From 49931f0ea5443966c8b27fcb2649e3fa55675a81 Mon Sep 17 00:00:00 2001 From: coderaiser Date: Wed, 12 Dec 2012 10:35:17 +0200 Subject: [PATCH 001/347] minor changes --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index aba2f45f..e2325caa 100644 --- a/README.md +++ b/README.md @@ -190,7 +190,7 @@ so to get it you should type a couple more commands: 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. From 982e0d959edea1e6aa3b630a6b0632e7bf201b4b Mon Sep 17 00:00:00 2001 From: coderaiser Date: Thu, 13 Dec 2012 05:37:02 -0500 Subject: [PATCH 002/347] changed the way of getting github application id --- ChangeLog | 6 ++++++ config.json | 2 +- lib/client/storage/_github.js | 10 +++------- lib/server/rest.js | 20 +------------------- 4 files changed, 11 insertions(+), 27 deletions(-) diff --git a/ChangeLog b/ChangeLog index 605c4426..fdac7986 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,9 @@ +2012.*.*, Version 0.1.9 + +* Changed the way of getting github application id +(now it's just from config, rest api removed). + + 2012.12.12, Version 0.1.8 * Added ability to shutdown Cloud Commander 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/_github.js b/lib/client/storage/_github.js index 1bbb5e2f..0f582e6b 100644 --- a/lib/client/storage/_github.js +++ b/lib/client/storage/_github.js @@ -43,13 +43,9 @@ var CloudCommander, Util, DOM, $, Github, cb; } function setConfig(pCallBack){ - - DOM.ajax({ - url : GitHubIdURL, - success : function(pData){ - GitHub_ID = pData; - Util.exec(pCallBack); - } + cloudcmd.getConfig(function(pConfig){ + GitHub_ID = pConfig.github_key; + Util.exec(pCallBack); }); } diff --git a/lib/server/rest.js b/lib/server/rest.js index 260296d2..f9a7b1e8 100644 --- a/lib/server/rest.js +++ b/lib/server/rest.js @@ -105,30 +105,12 @@ */ function onGET(pParams){ var lResult = {error: 'command not found'}, - lCmd = pParams.command, - lConfig = main.config, - lEnv = process.env, - lEnvKey, - lConfigKey; + lCmd = pParams.command; switch(lCmd){ case '': lResult = {info: 'Cloud Commander API v1'}; break; - - case 'github_key': - lEnvKey = lEnv.github_key; - lConfigKey = lConfig.github_key, - - lResult = lEnvKey || lConfigKey; - break; - - case 'dropbox_chooser_key': - lEnvKey = lEnv.dropbox_chooser_key; - lConfigKey = lConfig.dropbox_chooser_key; - - lResult = lEnvKey || lConfigKey; - break; case 'kill': pParams.data = { From ef71f4a57710a701b2296340dee05e0dd99462d0 Mon Sep 17 00:00:00 2001 From: coderaiser Date: Thu, 13 Dec 2012 10:32:53 -0500 Subject: [PATCH 003/347] added ability to upload files to GDrive --- ChangeLog | 1 + lib/client.js | 1 + lib/client/menu.js | 136 +++++++++++++++++++++++----------- lib/client/storage/_github.js | 3 +- lib/util.js | 26 +++++++ modules.json | 1 + 6 files changed, 122 insertions(+), 46 deletions(-) diff --git a/ChangeLog b/ChangeLog index fdac7986..9a799c12 100644 --- a/ChangeLog +++ b/ChangeLog @@ -3,6 +3,7 @@ * Changed the way of getting github application id (now it's just from config, rest api removed). +* Added ability to upload files to GDrive. 2012.12.12, Version 0.1.8 diff --git a/lib/client.js b/lib/client.js index ae3e3767..d2632079 100644 --- a/lib/client.js +++ b/lib/client.js @@ -362,6 +362,7 @@ function initModules(pCallBack){ lNames = {}; lNames[lStorage + '_dropbox'] = 'DropBox', lNames[lStorage + '_github' ] = 'GitHub', + lNames[lStorage + '_gdrive' ] = 'GDrive', lDisableMenuFunc(); diff --git a/lib/client/menu.js b/lib/client/menu.js index 6babe02e..92db3f1f 100644 --- a/lib/client/menu.js +++ b/lib/client/menu.js @@ -49,23 +49,32 @@ var CloudCommander, Util, DOM, CloudFunc, $; // define the elements of the menu items: { - view: {name: 'View', callback: function(key, opt){ - showEditor(true); - }}, + view: { + name : 'View', + callback : function(key, opt){ + showEditor(true); + } + }, - edit: {name: 'Edit', callback: function(key, opt){ - showEditor(); - }}, + edit: { + name : 'Edit', + callback : function(key, opt){ + showEditor(); + } + }, - delete: {name: 'Delete', + delete: { + name: 'Delete', callback: function(key, opt){ DOM.promptRemoveCurrent(); - }}, + } + }, upload: { - name: "Upload to", + name: 'Upload to', items: { - 'upload_to_gist': {name: 'Gist', + 'gist': { + name: 'Gist', callback: function(key, opt){ var lCurrent = DOM.getCurrentFile(), lPath = DOM.getCurrentPath(), @@ -93,42 +102,81 @@ var CloudCommander, Util, DOM, CloudFunc, $; Util.log('Uploading to gist...'); } }, - }, - }, - download: {name: 'Download',callback: function(key, opt){ - DOM.Images.showLoad(); - - var lCurrent = DOM.getCurrentFile(), - lLink = DOM.getByTag('a', lCurrent)[0].href; - - console.log('downloading file ' + lLink +'...'); - - lLink = lLink + '?download'; - - lLink = Util.removeStr( lLink, CloudFunc.NOJS ); - var lId = DOM.getIdBySrc(lLink); - - if(!DOM.getById(lId)){ - var lDownload = DOM.anyload({ - name : 'iframe', - async : false, - className : 'hidden', - src : lLink, - func : function(){ - DOM.Images.hideLoad(); - } - }); - DOM.Images.hideLoad(); - setTimeout(function() { - document.body.removeChild(lDownload); - }, 10000); + 'gdrive': { + name: 'GDrive', + + callback: function(key, opt){ + + + var lCurrent = DOM.getCurrentFile(), + lPath = DOM.getCurrentPath(), + lName = DOM.getCurrentName(lCurrent); + + DOM.ajax({ + url : lPath, + error : DOM.Images.showError, + success : function(data, textStatus, jqXHR){ + if( Util.isObject(data) ) + data = JSON.stringify(data, null, 4); + var lData = { + data: data, + name: lName + }; + + var lGDrive = cloudcmd.GDrive; + + if('init' in lGDrive) + lGDrive.init(lData); + else + Util.exec(cloudcmd.GDrive, lData); + } + }); + + Util.log('Uploading to gdrive...'); + } + } + } + }, + + + download: { + name: 'Download', + callback: function(key, opt){ + DOM.Images.showLoad(); + + var lCurrent = DOM.getCurrentFile(), + lLink = DOM.getByTag('a', lCurrent)[0].href; + + console.log('downloading file ' + lLink +'...'); + + lLink = lLink + '?download'; + + lLink = Util.removeStr( lLink, CloudFunc.NOJS ); + var lId = DOM.getIdBySrc(lLink); + + if(!DOM.getById(lId)){ + var lDownload = DOM.anyload({ + name : 'iframe', + async : false, + className : 'hidden', + src : lLink, + func : function(){ + DOM.Images.hideLoad(); + } + }); + + DOM.Images.hideLoad(); + setTimeout(function() { + document.body.removeChild(lDownload); + }, 10000); + } + else + DOM.Images.showError({ + responseText: 'Error: You trying to' + + 'download same file to often'}); } - else - DOM.Images.showError({ - responseText: 'Error: You trying to' + - 'download same file to often'}); - }} + } } }; } diff --git a/lib/client/storage/_github.js b/lib/client/storage/_github.js index 0f582e6b..18f48277 100644 --- a/lib/client/storage/_github.js +++ b/lib/client/storage/_github.js @@ -9,7 +9,6 @@ var CloudCommander, Util, DOM, $, Github, cb; APIURL = '/api/v1', AuthURL = APIURL + '/auth', - GitHubIdURL = APIURL + '/github_key', GitHub_ID, GithubLocal, @@ -103,7 +102,7 @@ var CloudCommander, Util, DOM, $, Github, cb; Util.exec(pCallBack); } - /* PUBLICK FUNCTIONS */ + /* PUBLIC FUNCTIONS */ GithubStore.basicLogin = function(pUser, pPasswd){ GithubLocal = new Github({ username: pUser, diff --git a/lib/util.js b/lib/util.js index 5c7ec2ca..498c1a27 100644 --- a/lib/util.js +++ b/lib/util.js @@ -290,6 +290,32 @@ var Util, exports; return lRet; }; + /** + * set timout before callback would be called + * @param pArgs {func, callback, time} + */ + Util.setTimeout = function(pArgs){ + var lDone, + lFunc = pArgs.func, + lTime = pArgs.time || 1000, + lCallBack = function(pArgument){ + if(!lDone){ + lDone = Util.exec(pArgs.callback, pArgument); + } + }; + + var lTimeoutFunc = function(){ + setTimeout(function(){ + Util.exec(lFunc, lCallBack); + if(!lDone) + lTimeoutFunc(); + }, lTime); + }; + + lTimeoutFunc(); + }; + + /** * function execute param function in * try...catch block diff --git a/modules.json b/modules.json index 46d42f3a..02aad66f 100644 --- a/modules.json +++ b/modules.json @@ -4,5 +4,6 @@ "viewer", "storage/_github", "storage/_dropbox", + "storage/_gdrive", "terminal" ] \ No newline at end of file From b4132849257acc37fa7d54a889498b6dfab330be Mon Sep 17 00:00:00 2001 From: coderaiser Date: Thu, 13 Dec 2012 10:53:31 -0500 Subject: [PATCH 004/347] added functions DOM.getCurrentFileContent(pCallBack [, pCurrentFile]) and Util.setTimeout(pFunction [, pCallBack, pTime]) --- ChangeLog | 3 ++ lib/client/dom.js | 17 +++++++++- lib/client/menu.js | 82 +++++++++++++++++++--------------------------- lib/util.js | 4 +-- 4 files changed, 54 insertions(+), 52 deletions(-) diff --git a/ChangeLog b/ChangeLog index 9a799c12..30d4ff9b 100644 --- a/ChangeLog +++ b/ChangeLog @@ -5,6 +5,9 @@ * Added ability to upload files to GDrive. +* Added functions DOM.getCurrentFileContent(pCallBack [, pCurrentFile]) +and Util.setTimeout(pFunction [, pCallBack, pTime]) + 2012.12.12, Version 0.1.8 * Added ability to shutdown Cloud Commander diff --git a/lib/client/dom.js b/lib/client/dom.js index 13272b85..2b344b99 100644 --- a/lib/client/dom.js +++ b/lib/client/dom.js @@ -758,7 +758,22 @@ var CloudCommander, Util, DOM, CloudFunc; } return lCurrent; }; - + + /** + * unified way to get current file content + * + * @pCurrentFile + */ + DOM.getCurrentFileContent = function(pCallBack, pCurrentFile){ + var lPath = DOM.getCurrentPath(pCurrentFile), + lRet = DOM.ajax({ + url : lPath, + error : DOM.Images.showError, + success : Util.retExec(pCallBack) + }); + + return lRet; + }; /** * unified way to get RefreshButton diff --git a/lib/client/menu.js b/lib/client/menu.js index 92db3f1f..09b12f19 100644 --- a/lib/client/menu.js +++ b/lib/client/menu.js @@ -76,29 +76,22 @@ var CloudCommander, Util, DOM, CloudFunc, $; 'gist': { name: 'Gist', callback: function(key, opt){ - var lCurrent = DOM.getCurrentFile(), - lPath = DOM.getCurrentPath(), - lName = DOM.getCurrentName(lCurrent); + DOM.getCurrentFileContent(function(pData){ + var lName = DOM.getCurrentName(); + if( Util.isObject(pData) ) + pData = JSON.stringify(pData, null, 4); - DOM.ajax({ - url : lPath, - error : DOM.Images.showError, - success : function(data, textStatus, jqXHR){ - if( Util.isObject(data) ) - data = JSON.stringify(data, null, 4); - - var lGitHub = cloudcmd.GitHub; - - if('init' in lGitHub) - lGitHub.createGist(data, lName); - else - Util.exec(cloudcmd.GitHub, - function(){ - lGitHub.createGist(data, lName); - }); - } + var lGitHub = cloudcmd.GitHub; + + if('init' in lGitHub) + lGitHub.createGist(pData, lName); + else + Util.exec(cloudcmd.GitHub, + function(){ + lGitHub.createGist(pData, lName); + }); }); - + Util.log('Uploading to gist...'); } }, @@ -107,39 +100,30 @@ var CloudCommander, Util, DOM, CloudFunc, $; name: 'GDrive', callback: function(key, opt){ - - - var lCurrent = DOM.getCurrentFile(), - lPath = DOM.getCurrentPath(), - lName = DOM.getCurrentName(lCurrent); - - DOM.ajax({ - url : lPath, - error : DOM.Images.showError, - success : function(data, textStatus, jqXHR){ - if( Util.isObject(data) ) - data = JSON.stringify(data, null, 4); - var lData = { - data: data, - name: lName - }; - - var lGDrive = cloudcmd.GDrive; + DOM.getCurrentFileContent(function(data){ + var lName = DOM.getCurrentName(); + + if( Util.isObject(data) ) + data = JSON.stringify(data, null, 4); + var lData = { + data: data, + name: lName + }; - if('init' in lGDrive) - lGDrive.init(lData); - else - Util.exec(cloudcmd.GDrive, lData); - } - }); - - Util.log('Uploading to gdrive...'); - } + var lGDrive = cloudcmd.GDrive; + + if('init' in lGDrive) + lGDrive.init(lData); + else + Util.exec(cloudcmd.GDrive, lData); + }); + + Util.log('Uploading to gdrive...'); } } + } }, - download: { name: 'Download', callback: function(key, opt){ diff --git a/lib/util.js b/lib/util.js index 498c1a27..5c8e6b16 100644 --- a/lib/util.js +++ b/lib/util.js @@ -242,8 +242,8 @@ var Util, exports; * @param pArg */ Util.retExec = function(pCallBack, pArg){ - return function(){ - Util.exec(pCallBack, pArg); + return function(pArgument){ + Util.exec(pCallBack, pArg || pArgument); }; }; From 1d91b6807098c7780f0a442151176ec9b01cf0ad Mon Sep 17 00:00:00 2001 From: coderaiser Date: Thu, 13 Dec 2012 14:11:21 -0500 Subject: [PATCH 005/347] refactored --- lib/client/dom.js | 33 +++++++++++++++++++++++--------- lib/client/editor/_codemirror.js | 8 +++----- lib/client/menu.js | 19 +++++++----------- lib/client/viewer.js | 2 +- 4 files changed, 35 insertions(+), 27 deletions(-) diff --git a/lib/client/dom.js b/lib/client/dom.js index 2b344b99..45b4fb6c 100644 --- a/lib/client/dom.js +++ b/lib/client/dom.js @@ -25,7 +25,7 @@ var CloudCommander, Util, DOM, CloudFunc; 'could not be none' }); - if(lRet_b) + if(lRet_b) DOM.removeClass(pCurrentFile, getCurrentFile()); return lRet_b; @@ -758,19 +758,34 @@ var CloudCommander, Util, DOM, CloudFunc; } return lCurrent; }; - + /** * unified way to get current file content * + * @pCallBack - callback function or data struct {sucess, error} * @pCurrentFile */ - DOM.getCurrentFileContent = function(pCallBack, pCurrentFile){ - var lPath = DOM.getCurrentPath(pCurrentFile), - lRet = DOM.ajax({ - url : lPath, - error : DOM.Images.showError, - success : Util.retExec(pCallBack) - }); + DOM.getCurrentFileContent = function(pParams, pCurrentFile){ + var lRet, + lParams = pParams ? pParams : {}, + lPath = DOM.getCurrentPath(pCurrentFile), + lError = function(){ + Util.exec(pParams.error); + DOM.Images.showError(); + }; + + if( Util.isFunction(lParams) ) + lParams = { + error : lError, + success : Util.retExec(pParams) + }; + else + lParams.error = lError; + + if(!lParams.url) + lParams.url = lPath; + + lRet = DOM.ajax(lParams); return lRet; }; diff --git a/lib/client/editor/_codemirror.js b/lib/client/editor/_codemirror.js index 82141574..836465c0 100644 --- a/lib/client/editor/_codemirror.js +++ b/lib/client/editor/_codemirror.js @@ -146,14 +146,12 @@ var CloudCommander, Util, DOM, CodeMirror; 400); /* reading data from current file */ - DOM.ajax({ - url:lPath, - error: function(jqXHR, textStatus, errorThrown){ + DOM.getCurrentFileContent({ + error : function(){ Loading = false; - return DOM.Images.showError(jqXHR); }, - success:function(data, textStatus, jqXHR){ + success : function(data){ /* if we got json - show it */ if(typeof data === 'object') data = JSON.stringify(data, null, 4); diff --git a/lib/client/menu.js b/lib/client/menu.js index 09b12f19..c8656795 100644 --- a/lib/client/menu.js +++ b/lib/client/menu.js @@ -129,25 +129,20 @@ var CloudCommander, Util, DOM, CloudFunc, $; callback: function(key, opt){ DOM.Images.showLoad(); - var lCurrent = DOM.getCurrentFile(), - lLink = DOM.getByTag('a', lCurrent)[0].href; + var lPath = DOM.getCurrentPath(), + lId = DOM.getIdBySrc(lPath); - console.log('downloading file ' + lLink +'...'); + Util.log('downloading file ' + lPath +'...'); - lLink = lLink + '?download'; - - lLink = Util.removeStr( lLink, CloudFunc.NOJS ); - var lId = DOM.getIdBySrc(lLink); + lPath = lPath + '?download'; if(!DOM.getById(lId)){ var lDownload = DOM.anyload({ name : 'iframe', async : false, className : 'hidden', - src : lLink, - func : function(){ - DOM.Images.hideLoad(); - } + src : lPath, + func : Util.retFunc(DOM.Images.hideLoad) }); DOM.Images.hideLoad(); @@ -155,7 +150,7 @@ var CloudCommander, Util, DOM, CloudFunc, $; document.body.removeChild(lDownload); }, 10000); } - else + else DOM.Images.showError({ responseText: 'Error: You trying to' + 'download same file to often'}); diff --git a/lib/client/viewer.js b/lib/client/viewer.js index d2c62cb6..d3f56e7f 100644 --- a/lib/client/viewer.js +++ b/lib/client/viewer.js @@ -131,7 +131,7 @@ var CloudCommander, Util, DOM, CloudFunc, $; */ FancyBox.loadData = function(pA, pCallBack){ var lLink = pA.href; - + /* убираем адрес хоста */ lLink = lLink.replace(cloudcmd.HOST, ''); From 221f5693cf8355f8df630f0764e57dc00eefa163 Mon Sep 17 00:00:00 2001 From: coderaiser Date: Thu, 13 Dec 2012 14:22:02 -0500 Subject: [PATCH 006/347] refactored --- lib/client/dom.js | 17 +++++++---------- 1 file changed, 7 insertions(+), 10 deletions(-) diff --git a/lib/client/dom.js b/lib/client/dom.js index 45b4fb6c..47545756 100644 --- a/lib/client/dom.js +++ b/lib/client/dom.js @@ -769,18 +769,15 @@ var CloudCommander, Util, DOM, CloudFunc; var lRet, lParams = pParams ? pParams : {}, lPath = DOM.getCurrentPath(pCurrentFile), - lError = function(){ - Util.exec(pParams.error); - DOM.Images.showError(); + lErrorWas = pParams.error, + lError = function(jqXHR){ + Util.exec(lErrorWas); + DOM.Images.showError(jqXHR); }; - if( Util.isFunction(lParams) ) - lParams = { - error : lError, - success : Util.retExec(pParams) - }; - else - lParams.error = lError; + lParams.success = Util.retExec(pParams); + + lParams.error = lError; if(!lParams.url) lParams.url = lPath; From 1d993ead06ca145168db0411ba045fd65ee14d97 Mon Sep 17 00:00:00 2001 From: coderaiser Date: Fri, 14 Dec 2012 11:05:39 +0200 Subject: [PATCH 007/347] fixed diskpart scenario --- lib/server/win/getvolumes.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/server/win/getvolumes.txt b/lib/server/win/getvolumes.txt index 4b3673c5..7736c7fd 100644 --- a/lib/server/win/getvolumes.txt +++ b/lib/server/win/getvolumes.txt @@ -1 +1 @@ -list volumes +list volume From 506fedcd1b0e2dbc996e7c977eb8fdf69fce9ce9 Mon Sep 17 00:00:00 2001 From: coderaiser Date: Fri, 14 Dec 2012 05:10:15 -0500 Subject: [PATCH 008/347] added functions in win.js for parsing diskpart output --- ChangeLog | 3 ++ lib/server/appcache.js | 2 +- lib/server/auth.js | 2 +- lib/server/rest.js | 2 +- lib/server/update.js | 4 +-- lib/server/win.js | 76 ++++++++++++++++++++++++++++++++++++++++-- 6 files changed, 82 insertions(+), 7 deletions(-) diff --git a/ChangeLog b/ChangeLog index 30d4ff9b..aa4d3e4d 100644 --- a/ChangeLog +++ b/ChangeLog @@ -8,6 +8,9 @@ * Added functions DOM.getCurrentFileContent(pCallBack [, pCurrentFile]) and Util.setTimeout(pFunction [, pCallBack, pTime]) +* Added functions in win.js for parsing diskpart output. + + 2012.12.12, Version 0.1.8 * Added ability to shutdown Cloud Commander diff --git a/lib/server/appcache.js b/lib/server/appcache.js index e30028a2..00360dfc 100644 --- a/lib/server/appcache.js +++ b/lib/server/appcache.js @@ -10,7 +10,7 @@ '# used for work with Aplication Cache.' + '\n' + '# If you wont to see at work set appcache: true' + '\n' + '# in config.json and start cloudcmd.js' + '\n' + - '# http://github.com/coderaiser/cloudcmd' + '\n'); + '# http://coderaiser.github.com/cloudcmd' + '\n'); var main = global.cloudcmd.main, fs = main.fs, diff --git a/lib/server/auth.js b/lib/server/auth.js index 04f56c83..ce0b284c 100644 --- a/lib/server/auth.js +++ b/lib/server/auth.js @@ -12,7 +12,7 @@ '# parameters in config.json or environment' + '\n' + '# and start cloudcmd.js or just do' + '\n' + '# require(\'auth.js\').auth(pCode, pCallBack)' + '\n' + - '# http://github.com/coderaiser/cloudcmd' + '\n'); + '# http://coderaiser.github.com/cloudcmd' + '\n'); var main = global.cloudcmd.main, diff --git a/lib/server/rest.js b/lib/server/rest.js index f9a7b1e8..c963251f 100644 --- a/lib/server/rest.js +++ b/lib/server/rest.js @@ -11,7 +11,7 @@ '# 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'); + '# http://coderaiser.github.com/cloudcmd' + '\n'); var main = global.cloudcmd.main, Util = main.util, diff --git a/lib/server/update.js b/lib/server/update.js index 0413be55..e0ac486a 100644 --- a/lib/server/update.js +++ b/lib/server/update.js @@ -10,7 +10,7 @@ '# Module is part of Cloud Commander,' + '\n' + '# used for work update thru git.' + '\n' + '# If you wont to see at work install git' + '\n' + - '# http://github.com/coderaiser/cloudcmd' + '\n'); + '# http://coderaiser.github.com/cloudcmd' + '\n'); var main = global.cloudcmd.main, mainpackage = main.mainpackage, @@ -35,7 +35,7 @@ * @param pStdout * @param pStderr */ - function pull(pError, pStdout, pStderr){ + function pull(pError, pStdout, pStderr){ var lExec; if(!pError){ diff --git a/lib/server/win.js b/lib/server/win.js index 87655191..6d02489e 100644 --- a/lib/server/win.js +++ b/lib/server/win.js @@ -1,3 +1,75 @@ -/* Library contain windows specific functions +/* + * Library contain windows specific functions * like getting information about volumes - */ \ No newline at end of file + */ + +(function(){ + "use strict"; + + if(!global.cloudcmd) + return console.log( + '# win.js' + '\n' + + '# -----------' + '\n' + + '# Module is part of Cloud Commander,' + '\n' + + '# used for work with windows specific' + '\n' + + '# functions. Woud be work on win32 only.' + '\n' + + '# http://coderaiser.github.com/cloudcmd' + '\n'); + + var main = global.cloudcmd.main, + + SRVDIR = main.SRVDIR, + BATDIR = SRVDIR + 'win\\', + GETVOLUMES = BATDIR + 'getvolumes', + BAT = GETVOLUMES + '.bat', + SCENARIO = GETVOLUMES + '.txt', + StdOut, + exec = main.child_process.exec, + Util = main.util; + + + /** + * get position of current name of volume + * @param pNumber = number of volume + */ + function getPosition(pNumber){ + var lRet, + lstrPattern = 'Том '; + + lRet = StdOut.indexOf(lstrPattern + pNumber); + + return lRet; + } + + /** + * get name of volume + * @param pPosition - current char position + */ + function getVolumeName(pPosition){ + var lRet, + lCharPosition = 10; + + lRet = StdOut[pPosition + lCharPosition]; + + return lRet; + } + + + exec(BAT + ' -s ' + SCENARIO, processOuput); + + + function processOuput(pError, pStdout, pStderr){ + StdOut = pStdout; + if(!pError){ + var lVolumes = [], + i = 0, + lNum = getPosition(i); + + do{ + lVolumes[i] = getVolumeName(lNum); + lNum = getPosition(++i); + }while(lNum > 0); + } + else + Util.log(pError); + } +})(); From 033e0314a624094a20e351ebb8bfcb1696bf7c04 Mon Sep 17 00:00:00 2001 From: coderaiser Date: Fri, 14 Dec 2012 05:49:54 -0500 Subject: [PATCH 009/347] minor changes --- lib/server/win.js | 103 +++++++++++++++++++++++++--------------------- 1 file changed, 57 insertions(+), 46 deletions(-) diff --git a/lib/server/win.js b/lib/server/win.js index 6d02489e..f7302321 100644 --- a/lib/server/win.js +++ b/lib/server/win.js @@ -16,60 +16,71 @@ '# http://coderaiser.github.com/cloudcmd' + '\n'); var main = global.cloudcmd.main, + Charset ={ + UNICODE : 65001, + WIN32 : 866 + }, - SRVDIR = main.SRVDIR, - BATDIR = SRVDIR + 'win\\', - GETVOLUMES = BATDIR + 'getvolumes', - BAT = GETVOLUMES + '.bat', - SCENARIO = GETVOLUMES + '.txt', - StdOut, exec = main.child_process.exec, Util = main.util; - /** - * get position of current name of volume - * @param pNumber = number of volume - */ - function getPosition(pNumber){ - var lRet, - lstrPattern = 'Том '; + exports.GetVolumes = function(pCallBack){ + var SRVDIR = '.\\', + BATDIR = SRVDIR + 'win\\', + SCENARIO = BATDIR + 'getvolumes.txt', + lCHCP = 'chcp ' + Charset.Unicode, + lDiskPart = 'diskpart -s' + SCENARIO; - lRet = StdOut.indexOf(lstrPattern + pNumber); - - return lRet; - } + exec(lCHCP + ' && ' + lDiskPart, retProcessOuput(pCallBack)); + }; - /** - * get name of volume - * @param pPosition - current char position - */ - function getVolumeName(pPosition){ - var lRet, - lCharPosition = 10; + + function retProcessOuput(pCallBack){ + return function(pError, pStdout, pStderr){ + /** + * get position of current name of volume + * @param pNumber = number of volume + */ + var getPosition = function(pNumber){ + var lRet, + lstrPattern = 'Том '; + + lRet = pStdout.indexOf(lstrPattern + pNumber); + + return lRet; + }; - lRet = StdOut[pPosition + lCharPosition]; - - return lRet; - } - - - exec(BAT + ' -s ' + SCENARIO, processOuput); - - - function processOuput(pError, pStdout, pStderr){ - StdOut = pStdout; - if(!pError){ - var lVolumes = [], - i = 0, - lNum = getPosition(i); + /** + * get name of volume + * @param pPosition - current char position + */ + var getVolumeName = function (pPosition){ + var lRet, + lCharPosition = 10; + + lRet = pStdout[pPosition + lCharPosition]; + + return lRet; + }; - do{ - lVolumes[i] = getVolumeName(lNum); - lNum = getPosition(++i); - }while(lNum > 0); - } - else - Util.log(pError); + var lVolumes = []; + + exec('chcp ' + Charset.WIN32); + + if(!pError){ + var i = 0, + lNum = getPosition(i); + + do{ + lVolumes[i] = getVolumeName(lNum); + lNum = getPosition(++i); + }while(lNum > 0); + } + else + Util.log(pError); + + Util.exec(pCallBack, lVolumes); + }; } })(); From a69b9d340624ef2d321e19849fb8c9bb04d759d1 Mon Sep 17 00:00:00 2001 From: coderaiser Date: Fri, 14 Dec 2012 06:17:22 -0500 Subject: [PATCH 010/347] minor changes --- lib/server/win.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/server/win.js b/lib/server/win.js index f7302321..f1987cbb 100644 --- a/lib/server/win.js +++ b/lib/server/win.js @@ -25,11 +25,11 @@ Util = main.util; - exports.GetVolumes = function(pCallBack){ + exports.getVolumes = function(pCallBack){ var SRVDIR = '.\\', BATDIR = SRVDIR + 'win\\', SCENARIO = BATDIR + 'getvolumes.txt', - lCHCP = 'chcp ' + Charset.Unicode, + lCHCP = 'chcp ' + Charset.UNICODE, lDiskPart = 'diskpart -s' + SCENARIO; exec(lCHCP + ' && ' + lDiskPart, retProcessOuput(pCallBack)); @@ -65,7 +65,7 @@ }; var lVolumes = []; - + exec('chcp ' + Charset.WIN32); if(!pError){ From fb05d31173ca18bcc50a318e4d6ac28d0f301448 Mon Sep 17 00:00:00 2001 From: coderaiser Date: Fri, 14 Dec 2012 06:27:33 -0500 Subject: [PATCH 011/347] minor changes --- lib/server/win.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/server/win.js b/lib/server/win.js index f1987cbb..8690f3af 100644 --- a/lib/server/win.js +++ b/lib/server/win.js @@ -30,7 +30,7 @@ BATDIR = SRVDIR + 'win\\', SCENARIO = BATDIR + 'getvolumes.txt', lCHCP = 'chcp ' + Charset.UNICODE, - lDiskPart = 'diskpart -s' + SCENARIO; + lDiskPart = 'diskpart -s ' + SCENARIO; exec(lCHCP + ' && ' + lDiskPart, retProcessOuput(pCallBack)); }; From 8f0ae56e785ab4a485f2da9ed423ede5bed2fa16 Mon Sep 17 00:00:00 2001 From: coderaiser Date: Fri, 14 Dec 2012 06:39:41 -0500 Subject: [PATCH 012/347] added getVolumes function --- lib/server/main.js | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/lib/server/main.js b/lib/server/main.js index d189191e..df8214d6 100644 --- a/lib/server/main.js +++ b/lib/server/main.js @@ -44,10 +44,11 @@ /* current dir + 2 levels up */ exports.WIN32 = ISWIN32 = isWin32(); exports.SLASH = SLASH = ISWIN32 ? '\\' : '/', - + exports.SRVDIR = SRVDIR = __dirname + SLASH, exports.LIBDIR = LIBDIR = path.normalize(SRVDIR + '../'), exports.DIR = DIR = path.normalize(LIBDIR + '../'), + exports.Volumes = getVolumes(); /* Functions */ exports.generateHeaders = generateHeaders, @@ -205,4 +206,18 @@ return lRet; } + + /** + * get volumes if win32 or get nothing if nix + */ + function getVolumes(){ + var lRet = ISWIN32 ? [] : '/'; + + if(ISWIN32) + srvrequire('win').getVolumes(function(pVolumes){ + exports.VOLUMES = pVolumes; + }); + + return lRet; + } })(); From 334f89b6b62d37ff0251183d4d2d7ff1d070f068 Mon Sep 17 00:00:00 2001 From: coderaiser Date: Fri, 14 Dec 2012 06:43:34 -0500 Subject: [PATCH 013/347] minor changes --- lib/server/main.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/server/main.js b/lib/server/main.js index df8214d6..7fcad27f 100644 --- a/lib/server/main.js +++ b/lib/server/main.js @@ -48,7 +48,7 @@ exports.SRVDIR = SRVDIR = __dirname + SLASH, exports.LIBDIR = LIBDIR = path.normalize(SRVDIR + '../'), exports.DIR = DIR = path.normalize(LIBDIR + '../'), - exports.Volumes = getVolumes(); + exports.VOLUMES = getVolumes(), /* Functions */ exports.generateHeaders = generateHeaders, From 07394fd18f2eb3abb9f505f4c67827e26edb40ad Mon Sep 17 00:00:00 2001 From: coderaiser Date: Fri, 14 Dec 2012 06:57:09 -0500 Subject: [PATCH 014/347] minor changes --- lib/server/main.js | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/lib/server/main.js b/lib/server/main.js index 7fcad27f..f3ae13fb 100644 --- a/lib/server/main.js +++ b/lib/server/main.js @@ -48,7 +48,6 @@ exports.SRVDIR = SRVDIR = __dirname + SLASH, exports.LIBDIR = LIBDIR = path.normalize(SRVDIR + '../'), exports.DIR = DIR = path.normalize(LIBDIR + '../'), - exports.VOLUMES = getVolumes(), /* Functions */ exports.generateHeaders = generateHeaders, @@ -71,7 +70,6 @@ exports.config = rootrequire('config'); exports.mainpackage = rootrequire('package'); - /* Additional Modules */ /* * Any of loaded below modules could work with global var so @@ -81,6 +79,9 @@ */ global.cloudcmd.main = exports; + exports.VOLUMES = getVolumes(), + + /* Additional Modules */ exports.auth = srvrequire('auth').auth, exports.appcache = srvrequire('appcache'), exports.cache = srvrequire('cache').Cache, @@ -100,7 +101,7 @@ * @param {Strin} pSrc */ function mrequire(pSrc){ - var lModule, + var lModule, lError = Util.tryCatchLog(function(){ lModule = require(pSrc); }); @@ -212,12 +213,13 @@ */ function getVolumes(){ var lRet = ISWIN32 ? [] : '/'; - + if(ISWIN32) srvrequire('win').getVolumes(function(pVolumes){ + console.log(pVolumes); exports.VOLUMES = pVolumes; }); - + return lRet; } })(); From f6cbf99b07ca8c838937bec36cd78fe4f3b01e92 Mon Sep 17 00:00:00 2001 From: coderaiser Date: Fri, 14 Dec 2012 07:39:16 -0500 Subject: [PATCH 015/347] minor changes --- lib/server/win.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/server/win.js b/lib/server/win.js index 8690f3af..b86e42d4 100644 --- a/lib/server/win.js +++ b/lib/server/win.js @@ -30,7 +30,7 @@ BATDIR = SRVDIR + 'win\\', SCENARIO = BATDIR + 'getvolumes.txt', lCHCP = 'chcp ' + Charset.UNICODE, - lDiskPart = 'diskpart -s ' + SCENARIO; + lDiskPart = 'diskpart -s "' + SCENARIO + '"'; exec(lCHCP + ' && ' + lDiskPart, retProcessOuput(pCallBack)); }; From c502690b59dd9335f42616bdea38078b72489476 Mon Sep 17 00:00:00 2001 From: coderaiser Date: Fri, 14 Dec 2012 08:10:20 -0500 Subject: [PATCH 016/347] minor changes --- lib/server/main.js | 2 +- lib/server/win.js | 172 ++++++++++++++++++++++----------------------- 2 files changed, 87 insertions(+), 87 deletions(-) diff --git a/lib/server/main.js b/lib/server/main.js index f3ae13fb..67c2cd61 100644 --- a/lib/server/main.js +++ b/lib/server/main.js @@ -1,6 +1,6 @@ (function(){ "strict mode"; - + /* Global var accessible from any loaded module */ global.cloudcmd = {}; diff --git a/lib/server/win.js b/lib/server/win.js index b86e42d4..9a7b58e3 100644 --- a/lib/server/win.js +++ b/lib/server/win.js @@ -1,86 +1,86 @@ -/* - * Library contain windows specific functions - * like getting information about volumes - */ - -(function(){ - "use strict"; - - if(!global.cloudcmd) - return console.log( - '# win.js' + '\n' + - '# -----------' + '\n' + - '# Module is part of Cloud Commander,' + '\n' + - '# used for work with windows specific' + '\n' + - '# functions. Woud be work on win32 only.' + '\n' + - '# http://coderaiser.github.com/cloudcmd' + '\n'); - - var main = global.cloudcmd.main, - Charset ={ - UNICODE : 65001, - WIN32 : 866 - }, - - exec = main.child_process.exec, - Util = main.util; - - - exports.getVolumes = function(pCallBack){ - var SRVDIR = '.\\', - BATDIR = SRVDIR + 'win\\', - SCENARIO = BATDIR + 'getvolumes.txt', - lCHCP = 'chcp ' + Charset.UNICODE, - lDiskPart = 'diskpart -s "' + SCENARIO + '"'; - - exec(lCHCP + ' && ' + lDiskPart, retProcessOuput(pCallBack)); - }; - - - function retProcessOuput(pCallBack){ - return function(pError, pStdout, pStderr){ - /** - * get position of current name of volume - * @param pNumber = number of volume - */ - var getPosition = function(pNumber){ - var lRet, - lstrPattern = 'Том '; - - lRet = pStdout.indexOf(lstrPattern + pNumber); - - return lRet; - }; - - /** - * get name of volume - * @param pPosition - current char position - */ - var getVolumeName = function (pPosition){ - var lRet, - lCharPosition = 10; - - lRet = pStdout[pPosition + lCharPosition]; - - return lRet; - }; - - var lVolumes = []; - - exec('chcp ' + Charset.WIN32); - - if(!pError){ - var i = 0, - lNum = getPosition(i); - - do{ - lVolumes[i] = getVolumeName(lNum); - lNum = getPosition(++i); - }while(lNum > 0); - } - else - Util.log(pError); - - Util.exec(pCallBack, lVolumes); - }; - } -})(); +/* + * Library contain windows specific functions + * like getting information about volumes + */ + +(function(){ + "use strict"; + + if(!global.cloudcmd) + return console.log( + '# win.js' + '\n' + + '# -----------' + '\n' + + '# Module is part of Cloud Commander,' + '\n' + + '# used for work with windows specific' + '\n' + + '# functions. Woud be work on win32 only.' + '\n' + + '# http://coderaiser.github.com/cloudcmd' + '\n'); + + var main = global.cloudcmd.main, + Charset ={ + UNICODE : 65001, + WIN32 : 866 + }, + + exec = main.child_process.exec, + Util = main.util; + + + exports.getVolumes = function(pCallBack){ + var SRVDIR = main.SRVDIR, + BATDIR = SRVDIR + 'win\\', + SCENARIO = BATDIR + 'getvolumes.txt', + lCHCP = 'chcp ' + Charset.UNICODE, + lDiskPart = 'diskpart -s "' + SCENARIO + '"'; + + exec(lCHCP + ' && ' + lDiskPart, retProcessOuput(pCallBack)); + }; + + + function retProcessOuput(pCallBack){ + return function(pError, pStdout, pStderr){ + /** + * get position of current name of volume + * @param pNumber = number of volume + */ + var getPosition = function(pNumber){ + var lRet, + lstrPattern = 'Том '; + + lRet = pStdout.indexOf(lstrPattern + pNumber); + + return lRet; + }; + + /** + * get name of volume + * @param pPosition - current char position + */ + var getVolumeName = function (pPosition){ + var lRet, + lCharPosition = 10; + + lRet = pStdout[pPosition + lCharPosition]; + + return lRet; + }; + + var lVolumes = []; + + exec('chcp ' + Charset.WIN32); + + if(!pError){ + var i = 0, + lNum = getPosition(i); + + do{ + lVolumes[i] = getVolumeName(lNum); + lNum = getPosition(++i); + }while(lNum > 0); + } + else + Util.log(pError); + + Util.exec(pCallBack, lVolumes); + }; + } +})(); From 26be171334ac4d9d0158451e0599ac0fe6a29518 Mon Sep 17 00:00:00 2001 From: coderaiser Date: Fri, 14 Dec 2012 08:12:35 -0500 Subject: [PATCH 017/347] minor changes --- lib/server.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/lib/server.js b/lib/server.js index 04c36819..4a65d110 100644 --- a/lib/server.js +++ b/lib/server.js @@ -143,10 +143,10 @@ lConfig.port; this.IP = process.env.IP || /* c9 */ - this.Config.ip; - - if(!this.IP) - this.IP = main.WIN32 ? '127.0.0.1' : '0.0.0.0'; + this.Config.ip || + main.WIN32 ? + '127.0.0.1' : + '0.0.0.0'; /* server mode or testing mode */ if (lConfig.server) { From ce2a3e9e6f8925dfdacc8f9b81fa9caefd6faee6 Mon Sep 17 00:00:00 2001 From: coderaiser Date: Fri, 14 Dec 2012 08:13:50 -0500 Subject: [PATCH 018/347] minor changes --- lib/server.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/lib/server.js b/lib/server.js index 4a65d110..95698b18 100644 --- a/lib/server.js +++ b/lib/server.js @@ -165,8 +165,10 @@ console.log('Cloud Commander server running at http://' + this.IP + ':' + this.Port); }, this)); - if(lError) + if(lError){ console.log('Cloud Commander server could not started'); + console.log(lError); + } }else console.log('Cloud Commander testing mode'); }; From c487088c83a2c61d821605aadbb5e327eaed026b Mon Sep 17 00:00:00 2001 From: coderaiser Date: Fri, 14 Dec 2012 08:17:09 -0500 Subject: [PATCH 019/347] minor changes --- lib/server/socket.js | 40 +++++++++++++++++++++++----------------- 1 file changed, 23 insertions(+), 17 deletions(-) diff --git a/lib/server/socket.js b/lib/server/socket.js index a01346f5..5f21ee52 100644 --- a/lib/server/socket.js +++ b/lib/server/socket.js @@ -20,24 +20,30 @@ * @pServer {Object} started server object */ exports.listen = function(pServer){ - io = io.listen(pServer); + var lRet; + if(io){ + io = io.listen(pServer); + + /* number of connections */ + var lConnNum = 0; + + lRet = io.sockets.on('connection', function (socket){ + ++lConnNum; + socket.send('{"stdout":"client connected"}'); + + console.log('server connected'); + + if(!OnMessageFuncs[lConnNum]) + OnMessageFuncs[lConnNum] = onMessage(lConnNum, socket); + + var lConn_func = OnMessageFuncs[lConnNum]; + + socket.on('message', lConn_func); + + }); + } - /* number of connections */ - var lConnNum = 0; - io.sockets.on('connection', function (socket){ - ++lConnNum; - socket.send('{"stdout":"client connected"}'); - - console.log('server connected'); - - if(!OnMessageFuncs[lConnNum]) - OnMessageFuncs[lConnNum] = onMessage(lConnNum, socket); - - var lConn_func = OnMessageFuncs[lConnNum]; - - socket.on('message', lConn_func); - - }); + return lRet; }; /** From 673ef7e22929ef3443cc096b4a1bc460efef7e88 Mon Sep 17 00:00:00 2001 From: coderaiser Date: Fri, 14 Dec 2012 08:21:55 -0500 Subject: [PATCH 020/347] minor changes --- lib/server.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/lib/server.js b/lib/server.js index 95698b18..626e790e 100644 --- a/lib/server.js +++ b/lib/server.js @@ -155,16 +155,16 @@ this.Server = http.createServer(this._controller); this.Server.listen(this.Port, this.IP); + var lListen; if(lConfig.socket && CloudServer.Socket){ - CloudServer.Socket.listen(this.Server); - console.log('sockets running'); + lListen = CloudServer.Socket.listen(this.Server); } - else - console.log('sockets disabled'); + console.log('sockets' + lListen ? 'running' :'disabled'); console.log('Cloud Commander server running at http://' + this.IP + ':' + this.Port); }, this)); + if(lError){ console.log('Cloud Commander server could not started'); console.log(lError); From 24f31e9a9215f2b0f64bbbb94615fe4d74167d83 Mon Sep 17 00:00:00 2001 From: coderaiser Date: Fri, 14 Dec 2012 08:27:11 -0500 Subject: [PATCH 021/347] minor changes --- lib/server.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/server.js b/lib/server.js index 626e790e..6506bc8c 100644 --- a/lib/server.js +++ b/lib/server.js @@ -158,8 +158,8 @@ var lListen; if(lConfig.socket && CloudServer.Socket){ lListen = CloudServer.Socket.listen(this.Server); - } - console.log('sockets' + lListen ? 'running' :'disabled'); + } + console.log('sockets ' + (lListen ? 'running' : 'disabled')); console.log('Cloud Commander server running at http://' + this.IP + ':' + this.Port); From 12626049578d8215476f6c2b314b8103c13ca9ba Mon Sep 17 00:00:00 2001 From: coderaiser Date: Fri, 14 Dec 2012 08:29:27 -0500 Subject: [PATCH 022/347] refactored --- lib/server.js | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/lib/server.js b/lib/server.js index 6506bc8c..01a6846d 100644 --- a/lib/server.js +++ b/lib/server.js @@ -159,10 +159,8 @@ if(lConfig.socket && CloudServer.Socket){ lListen = CloudServer.Socket.listen(this.Server); } - console.log('sockets ' + (lListen ? 'running' : 'disabled')); - - console.log('Cloud Commander server running at http://' + - this.IP + ':' + this.Port); + console.log('* Sockets ' + (lListen ? 'running' : 'disabled') + '\n' + + '* Server running at http://' + this.IP + ':' + this.Port); }, this)); if(lError){ From b2cb7ff96004fa92a95bcefb4950a5b36a91a4cd Mon Sep 17 00:00:00 2001 From: coderaiser Date: Fri, 14 Dec 2012 08:33:01 -0500 Subject: [PATCH 023/347] minor changes --- lib/server.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/server.js b/lib/server.js index 01a6846d..2af55ae9 100644 --- a/lib/server.js +++ b/lib/server.js @@ -144,9 +144,9 @@ this.IP = process.env.IP || /* c9 */ this.Config.ip || - main.WIN32 ? + (main.WIN32 ? '127.0.0.1' : - '0.0.0.0'; + '0.0.0.0'); /* server mode or testing mode */ if (lConfig.server) { From 85656dc488a0994aa0650885156341acc5ed87da Mon Sep 17 00:00:00 2001 From: coderaiser Date: Fri, 14 Dec 2012 10:55:19 -0500 Subject: [PATCH 024/347] refactored --- lib/client.js | 28 +++------------- lib/client/dom.js | 55 +++++++++++++++++--------------- lib/client/editor/_codemirror.js | 25 ++++----------- lib/client/keyBinding.js | 34 ++------------------ lib/client/menu.js | 2 +- lib/client/viewer.js | 11 ++----- lib/server/win.js | 22 ++++++++----- 7 files changed, 60 insertions(+), 117 deletions(-) diff --git a/lib/client.js b/lib/client.js index d2632079..5c2433a8 100644 --- a/lib/client.js +++ b/lib/client.js @@ -236,32 +236,14 @@ CloudClient._setCurrent = function(){ * вызоветься _loadDir */ return function(pFromEnter){ - var lCurrentFile = DOM.getCurrentFile(); - if(lCurrentFile){/* устанавливаем курсор на файл, на который нажали */ - DOM.setCurrentFile(this); - //if (DOM.isCurrentFile(this) && - // !Util.isBoolean(pFromEnter)){ - //var lParent = this; - - //setTimeout(function(){ - /* waiting a few seconds - * and if classes still equal - * make file name editable - * in other case - * double click event happend - */ - // if(DOM.getCurrentFile() === lParent) - // CloudClient._editFileName(lParent); - // },1000); - //} - } + var lCurrent = DOM.getCurrentFile(); /* если мы попали сюда с энтера */ if(pFromEnter===true){ - var lResult = Util.exec( Util.bind(this.ondblclick, this) ); + var lResult = Util.exec( Util.bind(lCurrent.ondblclick, lCurrent) ); /* enter pressed on file */ if(!lResult){ - var lA = DOM.getCurrentLink(this); - Util.exec( Util.bind(lA.ondblclick, this) ); + var lA = DOM.getCurrentLink(lCurrent); + Util.exec( Util.bind(lA.ondblclick, lCurrent) ); } }/* если мы попали сюда от клика мышки */ else @@ -603,7 +585,7 @@ CloudClient._changeLinks = function(pPanelID){ a[i].onclick = CloudClient._loadDir(link); } else { - lLi.onclick = CloudClient._setCurrent(); + lLi.onclick = CloudClient._setCurrent(); lLi.onmousedown = lSetCurrentFile_f; a[i].ondragstart = lOnDragStart_f; diff --git a/lib/client/dom.js b/lib/client/dom.js index 47545756..d673e5fd 100644 --- a/lib/client/dom.js +++ b/lib/client/dom.js @@ -759,6 +759,16 @@ var CloudCommander, Util, DOM, CloudFunc; return lCurrent; }; + + DOM.getCurrentSize = function(pCurrentFile){ + var lRet, + lCurrent = pCurrentFile || DOM.getCurrentFile(), + lSize = DOM.getByClass('size', lCurrent); + lRet = lSize[0].textContent; + + return lRet; + }; + /** * unified way to get current file content * @@ -811,35 +821,28 @@ var CloudCommander, Util, DOM, CloudFunc; * unified way to set current file */ DOM.setCurrentFile = function(pCurrentFile){ - var lRet_b = true; + var lRet = true, + lCurrentFileWas = DOM.getCurrentFile(); - if(!pCurrentFile){ - DOM.addCloudStatus({ - code : -1, - msg : 'Error pCurrentFile in' + - 'setCurrentFile' + - 'could not be none' - }); + if(!pCurrentFile) + lRet = false; + + if(lRet){ + if (pCurrentFile.className === 'path') + pCurrentFile = pCurrentFile.nextSibling; - lRet_b = false; + if (pCurrentFile.className === 'fm_header') + pCurrentFile = pCurrentFile.nextSibling; + + if(lCurrentFileWas) + UnSetCurrentFile(lCurrentFileWas); + + DOM.addClass(pCurrentFile, getCurrentFile()); + + /* scrolling to current file */ + DOM.scrollIntoViewIfNeeded(pCurrentFile); } - 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; + return lRet; }; /** diff --git a/lib/client/editor/_codemirror.js b/lib/client/editor/_codemirror.js index 836465c0..b8c82fad 100644 --- a/lib/client/editor/_codemirror.js +++ b/lib/client/editor/_codemirror.js @@ -121,23 +121,11 @@ var CloudCommander, Util, DOM, CodeMirror; if(Loading) return; - /* getting link */ - var lCurrentFile = DOM.getCurrentFile(), - lPath = DOM.getCurrentPath(lCurrentFile); - - /* checking is this link is to directory */ - var lSize = DOM.getByClass('size', lCurrentFile); - if(lSize){ - lSize = lSize[0].textContent; - - /* when folder view - * is no need to edit - * data - */ - if (lSize === '') - ReadOnly = true; - - } + /* checking is this link is to directory + * when folder view is no need to edit data + */ + if ( DOM.getCurrentSize() === '' ) + ReadOnly = true; Loading = true; setTimeout(function(){ @@ -153,7 +141,7 @@ var CloudCommander, Util, DOM, CodeMirror; success : function(data){ /* if we got json - show it */ - if(typeof data === 'object') + if( Util.isObject(data) ) data = JSON.stringify(data, null, 4); var lHided = DOM.hidePanel(); @@ -184,7 +172,6 @@ var CloudCommander, Util, DOM, CodeMirror; DOM.showPanel(); } - /** * function calls all CodeMirror editor functions */ diff --git a/lib/client/keyBinding.js b/lib/client/keyBinding.js index 89f16980..9b83d422 100644 --- a/lib/client/keyBinding.js +++ b/lib/client/keyBinding.js @@ -260,38 +260,8 @@ var CloudCommander, Util, DOM; } /* если нажали Enter - открываем папку*/ - else if(lKeyCode === KEY.ENTER){ - /* если ненайдены выделенные файлы - выходим*/ - if(!lCurrentFile)return; - - /* из него достаём спан с именем файла*/ - lName = DOM.getByClass('name', lCurrentFile); - - /* если нету (что вряд ли) - выходим*/ - if(!lName)return false; - - /* достаём все ссылки*/ - var lATag = DOM.getByTag('a', lName[0]); - - /* если нету - выходим */ - if(!lATag)return false; - - /* вызываем ajaxload привязанный через changelinks - * пробуем нажать на ссылку, если не получиться - * (opera, ie), вызываем событие onclick, - */ - - if(lCurrentFile.onclick) - lCurrentFile.onclick(true); - else try{ - lATag[0].click(); - } - catch(error){ - console.log(error); - } - - event.preventDefault();//запрет на дальнейшее действие - } + else if(lKeyCode === KEY.ENTER) + Util.exec(lCurrentFile.onclick, true); /* если нажали +r * обновляем страницу, diff --git a/lib/client/menu.js b/lib/client/menu.js index c8656795..98c8b805 100644 --- a/lib/client/menu.js +++ b/lib/client/menu.js @@ -1,7 +1,7 @@ /* object contains jQuery-contextMenu * https://github.com/medialize/jQuery-contextMenu */ -var CloudCommander, Util, DOM, CloudFunc, $; +var CloudCommander, Util, DOM, $; (function(){ "use strict"; diff --git a/lib/client/viewer.js b/lib/client/viewer.js index d3f56e7f..6c0f44b0 100644 --- a/lib/client/viewer.js +++ b/lib/client/viewer.js @@ -138,17 +138,12 @@ var CloudCommander, Util, DOM, CloudFunc, $; if (lLink.indexOf(CloudFunc.NOJS) === CloudFunc.FS.length) lLink = Util.removeStr(lLink, CloudFunc.NOJS); - DOM.ajax({ + DOM.getCurrentFileContent({ url : lLink, - - error : function(jqXHR, textStatus, errorThrown){ + error : function(){ FancyBox.loading = false; - return DOM.Images.showError(jqXHR, textStatus, errorThrown); }, - - success :function(data, textStatus, jqXHR){ - Util.exec(pCallBack, data); - } + success : Util.retExec(pCallBack) }); }; diff --git a/lib/server/win.js b/lib/server/win.js index 9a7b58e3..3560b494 100644 --- a/lib/server/win.js +++ b/lib/server/win.js @@ -38,15 +38,15 @@ function retProcessOuput(pCallBack){ return function(pError, pStdout, pStderr){ + var lstrPattern = 'Том ', + lCharPosition = 10; + /** * get position of current name of volume * @param pNumber = number of volume */ var getPosition = function(pNumber){ - var lRet, - lstrPattern = 'Том '; - - lRet = pStdout.indexOf(lstrPattern + pNumber); + var lRet = pStdout.indexOf(lstrPattern + pNumber); return lRet; }; @@ -56,10 +56,7 @@ * @param pPosition - current char position */ var getVolumeName = function (pPosition){ - var lRet, - lCharPosition = 10; - - lRet = pStdout[pPosition + lCharPosition]; + var lRet = pStdout[pPosition + lCharPosition]; return lRet; }; @@ -72,6 +69,15 @@ var i = 0, lNum = getPosition(i); + /* if russian name not found + * try to search english name + */ + if(lNum < 0){ + lstrPattern = 'Volume ', + lCharPosition = 10 + 3; + lNum = getPosition(i); + } + do{ lVolumes[i] = getVolumeName(lNum); lNum = getPosition(++i); From fadd11a743a264c87f98c601cacd88a4479773b5 Mon Sep 17 00:00:00 2001 From: coderaiser Date: Sat, 15 Dec 2012 04:46:53 -0500 Subject: [PATCH 025/347] refactored --- lib/client.js | 35 +---------------------------------- lib/client/keyBinding.js | 2 +- 2 files changed, 2 insertions(+), 35 deletions(-) diff --git a/lib/client.js b/lib/client.js index 5c2433a8..71118c0f 100644 --- a/lib/client.js +++ b/lib/client.js @@ -225,39 +225,7 @@ CloudClient._editFileName = function(pParent){ } }; -/** - * Функция устанавливает текущим файлом, тот - * на который кликнули единожды - */ -CloudClient._setCurrent = function(){ - /* - * @pFromEnter - если мы сюда попали - * из события нажатия на энтер - - * вызоветься _loadDir - */ - return function(pFromEnter){ - var lCurrent = DOM.getCurrentFile(); - /* если мы попали сюда с энтера */ - if(pFromEnter===true){ - var lResult = Util.exec( Util.bind(lCurrent.ondblclick, lCurrent) ); - /* enter pressed on file */ - if(!lResult){ - var lA = DOM.getCurrentLink(lCurrent); - Util.exec( Util.bind(lA.ondblclick, lCurrent) ); - } - }/* если мы попали сюда от клика мышки */ - else - pFromEnter.returnValue = false; - - /* что бы не переходить по ссылкам - * а грузить всё ajax'ом, - * возвращаем false на событие - * onclick - */ - return true; - }; - }; - + /** функция устанавливает курсор на каталог * с которого мы пришли, если мы поднялись * в верх по файловой структуре @@ -585,7 +553,6 @@ CloudClient._changeLinks = function(pPanelID){ a[i].onclick = CloudClient._loadDir(link); } else { - lLi.onclick = CloudClient._setCurrent(); lLi.onmousedown = lSetCurrentFile_f; a[i].ondragstart = lOnDragStart_f; diff --git a/lib/client/keyBinding.js b/lib/client/keyBinding.js index 9b83d422..9e0b6b30 100644 --- a/lib/client/keyBinding.js +++ b/lib/client/keyBinding.js @@ -261,7 +261,7 @@ var CloudCommander, Util, DOM; /* если нажали Enter - открываем папку*/ else if(lKeyCode === KEY.ENTER) - Util.exec(lCurrentFile.onclick, true); + Util.exec(lCurrentFile.ondblclick, true); /* если нажали +r * обновляем страницу, From e07202cb2054e8692aaf70b6c8d7c98dedbab5a2 Mon Sep 17 00:00:00 2001 From: coderaiser Date: Sat, 15 Dec 2012 04:55:08 -0500 Subject: [PATCH 026/347] minor changes --- lib/client.js | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/lib/client.js b/lib/client.js index 71118c0f..427ce29f 100644 --- a/lib/client.js +++ b/lib/client.js @@ -139,9 +139,10 @@ CloudClient._loadDir = function(pLink,pNeedRefresh){ DOM.Images.showLoad(pNeedRefresh ? {top:true} : null); - var lPanel = DOM.getPanel(), - /* получаем имя каталога в котором находимся*/ - lHref = DOM.getByClass('path', lPanel); + var lPanel = DOM.getPanel(), + lCurrent = DOM.getCurrentFile(), + /* получаем имя каталога в котором находимся */ + lHref = DOM.getByClass('path', lPanel); lHref = lHref[0].textContent; @@ -157,7 +158,7 @@ CloudClient._loadDir = function(pLink,pNeedRefresh){ * или +R - ссылок мы ненайдём * и заходить не будем */ - var lA = DOM.getCurrentLink(this); + var lA = DOM.getCurrentLink(lCurrent); /* если нажали на ссылку на верхний каталог*/ if(lA && lA.textContent==='..' && lHref!=='/'){ From 91a4aa9cc31b7ce836223b9860edfc6818653750 Mon Sep 17 00:00:00 2001 From: coderaiser Date: Sat, 15 Dec 2012 05:30:25 -0500 Subject: [PATCH 027/347] added function getCurrentDir to DOM module --- ChangeLog | 2 ++ lib/client.js | 75 +++++++++++++---------------------------------- lib/client/dom.js | 19 ++++++++++++ lib/util.js | 8 +++++ 4 files changed, 49 insertions(+), 55 deletions(-) diff --git a/ChangeLog b/ChangeLog index aa4d3e4d..56e1d1ed 100644 --- a/ChangeLog +++ b/ChangeLog @@ -10,6 +10,8 @@ and Util.setTimeout(pFunction [, pCallBack, pTime]) * Added functions in win.js for parsing diskpart output. +* Added function getCurrentDir to DOM module. + 2012.12.12, Version 0.1.8 diff --git a/lib/client.js b/lib/client.js index 427ce29f..42aee3e6 100644 --- a/lib/client.js +++ b/lib/client.js @@ -22,7 +22,7 @@ var CloudClient = { Terminal : null, /* function loads and shows terminal*/ Menu : null, /* function loads and shows menu */ GoogleAnalytics : null, - + _loadDir : null, /* Функция привязываеться ко всем * ссылкам и * загружает содержимое каталогов */ @@ -126,63 +126,28 @@ CloudClient.GoogleAnalytics = function(){ /** * Функция привязываеться ко всем ссылкам и * загружает содержимое каталогов + * + * @param pLink - ссылка + * @param pNeedRefresh - необходимость обязательной загрузки данных с сервера */ -CloudClient._loadDir = function(pLink,pNeedRefresh){ - /* @pElem - элемент, - * для которого нужно - * выполнить загрузку - */ - return function(pEvent){ - var lRet = true; - /* показываем гиф загрузки возле пути папки сверху*/ - /* ctrl+r нажата? */ - +CloudClient._loadDir = function(pLink, pNeedRefresh){ + return function(){ + /* + * показываем гиф загрузки возле пути папки сверху + * ctrl+r нажата? + */ + DOM.Images.showLoad(pNeedRefresh ? {top:true} : null); - var lPanel = DOM.getPanel(), - lCurrent = DOM.getCurrentFile(), - /* получаем имя каталога в котором находимся */ - lHref = DOM.getByClass('path', lPanel); + var lParent = DOM.getCurrentLink().textContent, + lDir = DOM.getCurrentDir(); - lHref = lHref[0].textContent; - - lHref = CloudFunc.removeLastSlash(lHref); - var lSubstr = lHref.substr(lHref,lHref.lastIndexOf('/')); - lHref = lHref.replace(lSubstr+'/',''); - /* загружаем содержимое каталога */ CloudClient._ajaxLoad(pLink, { refresh: pNeedRefresh }); - /* получаем все элементы выделенной папки*/ - /* при этом, если мы нажали обновить - * или +R - ссылок мы ненайдём - * и заходить не будем - */ - var lA = DOM.getCurrentLink(lCurrent); - /* если нажали на ссылку на верхний каталог*/ - if(lA && lA.textContent==='..' && lHref!=='/'){ - - /* функция устанавливает курсор на каталог - * с которого мы пришли, если мы поднялись - * в верх по файловой структуре - */ - CloudClient._currentToParent(lHref); - } - - /* что бы не переходить по ссылкам - * а грузить всё ajax'ом, - * возвращаем false на событие - * onclick - */ - - Util.setValue({ - object : pEvent, - property: 'returnValue', - value : false - }); - - return lRet; + if(lParent === '..' && lDir !== '/') + CloudClient._currentToParent(lDir); }; }; @@ -211,7 +176,7 @@ CloudClient._editFileName = function(pParent){ var lA = DOM.getCurrentLink(pParent); if (lA && lA.textContent !== '..') lA.contentEditable = false; - + KeyBinding.set(); /* backs old document.onclick @@ -453,12 +418,11 @@ CloudClient.getConfig = function(pCallBack){ CloudClient._changeLinks = function(pPanelID){ /* назначаем кнопку очистить кэш и показываем её */ var lClearcache = getById('clear-cache'); - if(lClearcache) lClearcache.onclick = DOM.Cache.clear; /* меняем ссылки на ajax-запросы */ - var lPanel = getById(pPanelID), - a = lPanel.getElementsByTagName('a'), + var lPanel = getById(pPanelID), + a = lPanel.getElementsByTagName('a'), /* номер ссылки иконки обновления страницы */ lREFRESHICON = 0, @@ -529,7 +493,7 @@ CloudClient._changeLinks = function(pPanelID){ DOM.setCurrentFile(pElement); }, - + lUrl = cloudcmd.HOST; for(var i = 0, n = a.length; i < n ; i++) @@ -554,6 +518,7 @@ CloudClient._changeLinks = function(pPanelID){ a[i].onclick = CloudClient._loadDir(link); } else { + lLi.onclick = Util.retFalse; lLi.onmousedown = lSetCurrentFile_f; a[i].ondragstart = lOnDragStart_f; diff --git a/lib/client/dom.js b/lib/client/dom.js index d673e5fd..6d971f9d 100644 --- a/lib/client/dom.js +++ b/lib/client/dom.js @@ -741,6 +741,25 @@ var CloudCommander, Util, DOM, CloudFunc; } }; + /** + * get current direcotory path + */ + DOM.getCurrentDir = function(){ + var lRet, + lSubstr, + lPanel = DOM.getPanel(), + /* получаем имя каталога в котором находимся */ + lHref = DOM.getByClass('path', lPanel); + + lHref = lHref[0].textContent; + + lHref = CloudFunc.removeLastSlash(lHref); + lSubstr = lHref.substr(lHref , lHref.lastIndexOf('/')); + lRet = Util.removeStr(lHref, lSubstr + '/'); + + return lRet; + }; + /** * unified way to get current file * diff --git a/lib/util.js b/lib/util.js index 5c8e6b16..372c28ac 100644 --- a/lib/util.js +++ b/lib/util.js @@ -257,6 +257,14 @@ var Util, exports; return Util.exec(pCallBack, pArg); }; }; + /** + * function return false + */ + Util.retFalse = function(){ + var lRet = false; + + return lRet; + }; /** * return load functions thrue callbacks one-by-one From c42643b82077e4ff78cf9cc4344e3fc5c90d3ec6 Mon Sep 17 00:00:00 2001 From: coderaiser Date: Sat, 15 Dec 2012 05:46:27 -0500 Subject: [PATCH 028/347] fixed bug with terminal load --- ChangeLog | 2 ++ lib/client/dom.js | 2 +- lib/client/terminal.js | 2 +- 3 files changed, 4 insertions(+), 2 deletions(-) diff --git a/ChangeLog b/ChangeLog index 56e1d1ed..7ac274c0 100644 --- a/ChangeLog +++ b/ChangeLog @@ -12,6 +12,8 @@ and Util.setTimeout(pFunction [, pCallBack, pTime]) * Added function getCurrentDir to DOM module. +* Fixed bug with terminal load + 2012.12.12, Version 0.1.8 diff --git a/lib/client/dom.js b/lib/client/dom.js index 6d971f9d..b062b26e 100644 --- a/lib/client/dom.js +++ b/lib/client/dom.js @@ -620,7 +620,7 @@ var CloudCommander, Util, DOM, CloudFunc; * @param pCallBack */ DOM.socketLoad = function(pCallBack){ - DOM.jsload('lib/client/socket.js', pCallBack); + DOM.jsload('/lib/client/socket.js', pCallBack); }; /* DOM */ diff --git a/lib/client/terminal.js b/lib/client/terminal.js index ad11b84b..55626ce9 100644 --- a/lib/client/terminal.js +++ b/lib/client/terminal.js @@ -22,7 +22,7 @@ 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', From 36752d636e10e866adafb8546dd5f6321f636f1e Mon Sep 17 00:00:00 2001 From: coderaiser Date: Sat, 15 Dec 2012 09:34:00 -0500 Subject: [PATCH 029/347] refactored --- lib/server.js | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/lib/server.js b/lib/server.js index 2af55ae9..3bf397d6 100644 --- a/lib/server.js +++ b/lib/server.js @@ -467,16 +467,11 @@ */ var getFilesStat_f = function(pName){ return function(pError, pStat){ - - var fReturnFalse = function(){ - return false; - }; - if(pError) lStats[pName] = { 'mode':0, 'size':0, - 'isDirectory':fReturnFalse + 'isDirectory': Util.retFalse }; else @@ -517,8 +512,6 @@ size:'dir' }; - var fReturnFalse = function returnFalse(){return false;}; - for(var i = 0; i < pFiles.length; i++){ /* *Переводим права доступа в 8-ричную систему From d73bcca6cb38e1948a8fc1c42b88a136f7c4530f Mon Sep 17 00:00:00 2001 From: coderaiser Date: Sun, 16 Dec 2012 15:34:36 +0200 Subject: [PATCH 030/347] added logo title --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index e2325caa..a841f8b4 100644 --- a/README.md +++ b/README.md @@ -8,7 +8,7 @@ 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) +![Cloud Commander](https://raw.github.com/coderaiser/cloudcmd/dev/img/logo/cloudcmd.png "Cloud Commander") Benefits --------------- From b7dc833f3e7b2f7de9e9a21de778c8aac59060f1 Mon Sep 17 00:00:00 2001 From: coderaiser Date: Mon, 17 Dec 2012 04:31:55 -0500 Subject: [PATCH 031/347] added additional modules to readme --- README.md | 3 + lib/client/storage/_dropbox.js | 140 +++++++++++++++++++++++---------- modules.json | 1 + 3 files changed, 103 insertions(+), 41 deletions(-) diff --git a/README.md b/README.md index e2325caa..dabf4b9d 100644 --- a/README.md +++ b/README.md @@ -169,11 +169,14 @@ For extend main functionality Cloud Commander use next modules: - [FancyBox] [FancyBoxURL] - [jQuery-contextMenu] [jQuery-contextMenuURL] - [jquery.terminal] [jquery.terminalURL] +- [dropbox-js] [dropbox-jsURL] [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" +[githubURL]: https://github.com/michael/github +[dropbox-jsURL]: https://github.com/dropbox/dropbox-js Contributing --------------- diff --git a/lib/client/storage/_dropbox.js b/lib/client/storage/_dropbox.js index 5532e1a9..bedf6c3d 100644 --- a/lib/client/storage/_dropbox.js +++ b/lib/client/storage/_dropbox.js @@ -1,55 +1,113 @@ -var CloudCommander, DOM, Dropbox; -/* module for work with github */ +var CloudCommander, Util, DOM, gapi; (function(){ "use strict"; - var cloudcmd = CloudCommander, - CHOOSER_API = 'https://www.dropbox.com/static/api/1/dropbox.js', - CLIENT_ID, - DropBoxStore = {}, - options = { - linkType: "direct", - success: function(files) { - console.log("Here's the file link:" + files[0].link); - }, - cancel: function() { - console.log('Chose something'); - } - }; + var cloudcmd = CloudCommander, + GDrive = {}; - /* PRIVATE FUNCTIONS */ - /** - * function loads dropbox.js - */ - function load(){ - console.time('dropbox load'); - - cloudcmd.getConfig(function(pConfig){ - var lElement = DOM.anyload({ - src : CHOOSER_API, - not_append : true, - id : 'dropboxjs', - func : DropBoxStore.choose + function authorize(pData){ + /* https://developers.google.com/drive/credentials */ + Util.setTimeout({ + func : function(pCallBack){ + var lCLIENT_ID = '255175681917.apps.googleusercontent.com', + lSCOPES = 'https://www.googleapis.com/auth/drive', + lParams = { + 'client_id' : lCLIENT_ID, + 'scope' : lSCOPES, + 'immediate' : true + }; + + gapi.auth.authorize(lParams, pCallBack); + }, - }); - - var lDropBoxId = pConfig.dropbox_chooser_key; - lElement.setAttribute('data-app-key', lDropBoxId); - document.body.appendChild(lElement); - - console.timeEnd('dropbox load'); + callback : function(pAuthResult){ + var lRet; + if (pAuthResult && !pAuthResult.error){ + uploadFile(pData); + + lRet = true; + } + return lRet; + } }); } - DropBoxStore.choose = function(){ - Dropbox.choose(options); + function load(pData){ + var lUrl = 'https://apis.google.com/js/client.js'; + + DOM.jsload(lUrl, function(){ + authorize(pData); + }); + } + + /** + * Start the file upload. + * + * @param {Object} evt Arguments from the file selector. + */ + function uploadFile(pData) { + gapi.client.load('drive', 'v2', function() { + GDrive.uploadFile(pData); + }); + } + + /** + * Insert new file. + * + * @param {File} fileData {name, data} File object to read data from. + * @param {Function} callback Function to call when the request is complete. + */ + GDrive.uploadFile = function(pData, callback) { + var lData = pData.data, + lName = pData.name, + boundary = '-------314159265358979323846', + delimiter = "\r\n--" + boundary + "\r\n", + close_delim = "\r\n--" + boundary + "--", + + contentType = pData.type || 'application/octet-stream', + metadata = { + 'title' : lName, + 'mimeType' : contentType + }, + + base64Data = btoa(lData), + + multipartRequestBody = + delimiter + + 'Content-Type: application/json\r\n\r\n' + + JSON.stringify(metadata) + + delimiter + + 'Content-Type: ' + contentType + '\r\n' + + 'Content-Transfer-Encoding: base64\r\n' + + '\r\n' + + base64Data + + close_delim; + + var request = gapi.client.request({ + 'path': '/upload/drive/v2/files', + 'method': 'POST', + 'params': {'uploadType': 'multipart'}, + 'headers': { + 'Content-Type': 'multipart/mixed; boundary="' + boundary + '"' + }, + + 'body': multipartRequestBody + }); + + if (!callback) + callback = function(file) { + console.log(file); + }; + + request.execute(callback); }; - DropBoxStore.init = function(){ - load(); + + GDrive.init = function(pData){ + load(pData); }; - cloudcmd.DropBox = DropBoxStore; -})(); + cloudcmd.GDrive = GDrive; +})(); \ No newline at end of file diff --git a/modules.json b/modules.json index 02aad66f..30766d4f 100644 --- a/modules.json +++ b/modules.json @@ -4,6 +4,7 @@ "viewer", "storage/_github", "storage/_dropbox", + "storage/_dropbox_chooser", "storage/_gdrive", "terminal" ] \ No newline at end of file From 90b572aeef3a8f7f793c10ffa6636863ca7004c2 Mon Sep 17 00:00:00 2001 From: coderaiser Date: Mon, 17 Dec 2012 11:34:55 +0200 Subject: [PATCH 032/347] shorted links --- README.md | 25 +++++++++++++------------ 1 file changed, 13 insertions(+), 12 deletions(-) diff --git a/README.md b/README.md index ae527505..98346c8b 100644 --- a/README.md +++ b/README.md @@ -2,13 +2,13 @@ Cloud Commander [![Build Status](https://secure.travis-ci.org/coderaiser/cloudcm =============== **Cloud Commander** - user friendly cloud file manager. DEMO: -[cloudfoundry] (http://cloudcmd.cloudfoundry.com "cloudfoundry"), -[appfog] (http://cloudcmd.aws.af.cm "appfog"). +[cloudfoundry] (//cloudcmd.cloudfoundry.com "cloudfoundry"), +[appfog] (//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) +Google PageSpeed Score : [100](//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 "Cloud Commander") +![Cloud Commander](//raw.github.com/coderaiser/cloudcmd/dev/img/logo/cloudcmd.png "Cloud Commander") Benefits --------------- @@ -45,7 +45,7 @@ There is a short list: Viewer's hot keys --------------- -- **Shift + F3** - open viewer window +- **Shift + F3** - open viewer window - **Esc** - close viewer window Editor's hot keys @@ -56,7 +56,7 @@ Editor's hot keys Documentation --------------- -JS Doc documentation could be found in [http://jsdoc.info/coderaiser/cloudcmd/](http://jsdoc.info/coderaiser/cloudcmd/) +JS Doc documentation could be found in [http://jsdoc.info/coderaiser/cloudcmd/](//jsdoc.info/coderaiser/cloudcmd/) Installing --------------- @@ -169,14 +169,15 @@ For extend main functionality Cloud Commander use next modules: - [FancyBox] [FancyBoxURL] - [jQuery-contextMenu] [jQuery-contextMenuURL] - [jquery.terminal] [jquery.terminalURL] +- [github] [githubURL] - [dropbox-js] [dropbox-jsURL] -[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" -[githubURL]: https://github.com/michael/github -[dropbox-jsURL]: https://github.com/dropbox/dropbox-js +[CodeMirrorURL]: //github.com/marijnh/CodeMirror "CodeMirror" +[FancyBoxURL]: //github.com/fancyapps/fancyBox "FancyBox" +[jQuery-contextMenuURL]: //github.com/medialize/jQuery-contextMenu "jQuery-contextMenu" +[jquery.terminalURL]: //github.com/jcubic/jquery.terminal "jquery.terminal" +[githubURL]: //github.com/michael/github +[dropbox-jsURL]: //github.com/dropbox/dropbox-js Contributing --------------- From 9d82e5574d4f732edeec7513296414d0b09284e3 Mon Sep 17 00:00:00 2001 From: coderaiser Date: Mon, 17 Dec 2012 04:42:15 -0500 Subject: [PATCH 033/347] from now api url on client readed from config.json --- ChangeLog | 4 +++- lib/client/storage/_github.js | 15 ++++++++------- 2 files changed, 11 insertions(+), 8 deletions(-) diff --git a/ChangeLog b/ChangeLog index 7ac274c0..9c6b8fe6 100644 --- a/ChangeLog +++ b/ChangeLog @@ -12,7 +12,9 @@ and Util.setTimeout(pFunction [, pCallBack, pTime]) * Added function getCurrentDir to DOM module. -* Fixed bug with terminal load +* Fixed bug with terminal load. + +* From now api url on client readed from config.json. 2012.12.12, Version 0.1.8 diff --git a/lib/client/storage/_github.js b/lib/client/storage/_github.js index 18f48277..b6cece81 100644 --- a/lib/client/storage/_github.js +++ b/lib/client/storage/_github.js @@ -3,11 +3,11 @@ var CloudCommander, Util, DOM, $, Github, cb; (function(){ "use strict"; - + var cloudcmd = CloudCommander, Cache = DOM.Cache, - APIURL = '/api/v1', + APIURL, AuthURL = APIURL + '/auth', GitHub_ID, @@ -36,14 +36,15 @@ var CloudCommander, Util, DOM, $, Github, cb; DOM.anyLoadInParallel(lFiles, function(){ console.timeEnd('github load'); DOM.Images.hideLoad(); - + Util.exec(pCallBack); - }); + }); } function setConfig(pCallBack){ cloudcmd.getConfig(function(pConfig){ GitHub_ID = pConfig.github_key; + APIURL = pConfig.api_url; Util.exec(pCallBack); }); } @@ -86,7 +87,7 @@ var CloudCommander, Util, DOM, $, Github, cb; function getUserData(pCallBack){ var lName, - + lShowUserInfo = function(pError, pData){ if(!pError){ lName = pData.name; @@ -96,7 +97,7 @@ var CloudCommander, Util, DOM, $, Github, cb; else DOM.Cache.remove('token'); }; - + User.show(null, lShowUserInfo); Util.exec(pCallBack); @@ -136,7 +137,7 @@ var CloudCommander, Util, DOM, $, Github, cb; public: true }; - + lFiles[pFileName] ={ content: pContent }; From 922fd50b5b63282d55247a1d2891cbaa4757ca31 Mon Sep 17 00:00:00 2001 From: coderaiser Date: Mon, 17 Dec 2012 05:49:18 -0500 Subject: [PATCH 034/347] renamed fm_header to fm-header --- ChangeLog | 3 +++ config.json | 2 +- css/style.css | 35 +++++++++++++---------------------- lib/client.js | 11 +++++------ lib/client/dom.js | 2 +- lib/client/menu.js | 7 ++++++- lib/client/storage/_github.js | 11 +++++++++-- lib/cloudfunc.js | 2 +- lib/util.js | 2 +- test/test.js | 2 +- 10 files changed, 41 insertions(+), 36 deletions(-) diff --git a/ChangeLog b/ChangeLog index 9c6b8fe6..07834c91 100644 --- a/ChangeLog +++ b/ChangeLog @@ -16,6 +16,9 @@ and Util.setTimeout(pFunction [, pCallBack, pTime]) * From now api url on client readed from config.json. +* If choosen upload to -> Gist, and file in json have +not '.json' extension, it's added to the and of filename. + 2012.12.12, Version 0.1.8 diff --git a/config.json b/config.json index f21f6665..2dd79a63 100644 --- a/config.json +++ b/config.json @@ -4,7 +4,7 @@ "cache" : {"allowed" : false}, "minification" : { "js" : false, - "css" : true, + "css" : false, "html" : true, "img" : true }, diff --git a/css/style.css b/css/style.css index d930bd50..98cd2071 100644 --- a/css/style.css +++ b/css/style.css @@ -59,9 +59,6 @@ body{ text-shadow:black 0 2px 1px; } .path-icon:hover{ - /* - color:#45D827; - */ cursor:pointer; } .path-icon:active{ @@ -111,13 +108,14 @@ body{ background-color: white; border: 1.5px solid rgba(49,123,249,.40); } + .cmd-button:hover{ border: 1.5px solid rgb(0,0,0); } .cmd-button:active{ color: white; - background-color: rgb(49,123,249); + background-color: rgb(49,123,249); } .clear-cache{ @@ -126,19 +124,13 @@ body{ background:url(/img/console_clear.png) -4px -4px no-repeat; } .clear-cache:active{ - /* - background-position-y: -25px; - */ - top:5px; -} - -.settings:before{ - content:'k'; -} + top:5px; +} + .links{ color:red; } - + .mini-icon { position: relative; top: 2px; @@ -177,7 +169,7 @@ body{ height: 90%; margin: 26px 26px 0 26px; } -.fm_header{ +.fm-header{ font-weight: bold; } #path{ @@ -233,8 +225,6 @@ body{ */ text-align: right; } -.owner{ -} .mode{ float: right; width: 25%; @@ -252,14 +242,12 @@ button{ width:10%; } a{ - text-decoration:none; + text-decoration:none; } a:hover, a:active { color: #06e; cursor:pointer; - outline: 0; } -a:focus { outline: thin dotted; } /* Если размер окна очень маленький * располагаем имя и атрибуты файла @@ -276,6 +264,7 @@ a:focus { outline: thin dotted; } } /* текущий файл под курсором */ .current-file{ + background-color: rgb(49, 123, 249); background-color: rgba(49, 123, 249, .40); color:white; } @@ -297,12 +286,14 @@ a:focus { outline: thin dotted; } position: relative; top: -17px; + color: rgb(246, 224, 124); color: rgba(246, 224, 124, 0.56); } .directory::before{ content: '\f216'; } .text-file::before{ + color: rgb(26, 224, 124); color: rgba(26, 224, 124, 0.56); content: '\f211'; } @@ -353,7 +344,7 @@ a:focus { outline: thin dotted; } } } @media only screen and (min-width: 601px) and (max-width: 785px){ - .panel{ + .panel{ width:94% !important; } #right{ @@ -362,7 +353,7 @@ a:focus { outline: thin dotted; } } @media only screen and (min-width:786px) and (max-width: 1155px){ - .panel{ + .panel{ width:94% !important; } /* если правая панель не помещаеться - прячем её */ diff --git a/lib/client.js b/lib/client.js index 42aee3e6..3d67708e 100644 --- a/lib/client.js +++ b/lib/client.js @@ -361,15 +361,14 @@ function baseInit(pCallBack){ if( !DOM.Cache.get('/') ) DOM.Cache.set('/', cloudcmd._getJSONfromFileTable()); }); - + /* устанавливаем размер высоты таблицы файлов * исходя из размеров разрешения экрана */ - + /* выделяем строку с первым файлом */ - var lFmHeader = getByClass('fm_header'); - if(lFmHeader && lFmHeader[0].nextSibling) - DOM.setCurrentFile(lFmHeader[0].nextSibling); + var lFmHeader = getByClass('fm-header'); + DOM.setCurrentFile(lFmHeader[0].nextSibling); /* показываем элементы, которые будут работать только, если есть js */ var lFM = getById('fm'); @@ -383,7 +382,7 @@ function baseInit(pCallBack){ var lHeight = window.screen.height; lHeight = lHeight - (lHeight/3).toFixed(); - + lHeight = (lHeight / 100).toFixed() * 100; cloudcmd.HEIGHT = lHeight; diff --git a/lib/client/dom.js b/lib/client/dom.js index b062b26e..6de8ffaa 100644 --- a/lib/client/dom.js +++ b/lib/client/dom.js @@ -850,7 +850,7 @@ var CloudCommander, Util, DOM, CloudFunc; if (pCurrentFile.className === 'path') pCurrentFile = pCurrentFile.nextSibling; - if (pCurrentFile.className === 'fm_header') + if (pCurrentFile.className === 'fm-header') pCurrentFile = pCurrentFile.nextSibling; if(lCurrentFileWas) diff --git a/lib/client/menu.js b/lib/client/menu.js index 98c8b805..ea34ed92 100644 --- a/lib/client/menu.js +++ b/lib/client/menu.js @@ -78,8 +78,13 @@ var CloudCommander, Util, DOM, $; callback: function(key, opt){ DOM.getCurrentFileContent(function(pData){ var lName = DOM.getCurrentName(); - if( Util.isObject(pData) ) + if( Util.isObject(pData) ){ pData = JSON.stringify(pData, null, 4); + + var lExt = '.json'; + if( !Util.checkExtension(lName, lExt) ) + lName += lExt; + } var lGitHub = cloudcmd.GitHub; diff --git a/lib/client/storage/_github.js b/lib/client/storage/_github.js index b6cece81..24f426f3 100644 --- a/lib/client/storage/_github.js +++ b/lib/client/storage/_github.js @@ -8,7 +8,7 @@ var CloudCommander, Util, DOM, $, Github, cb; Cache = DOM.Cache, APIURL, - AuthURL = APIURL + '/auth', + AuthURL, GitHub_ID, GithubLocal, @@ -45,6 +45,8 @@ var CloudCommander, Util, DOM, $, Github, cb; cloudcmd.getConfig(function(pConfig){ GitHub_ID = pConfig.github_key; APIURL = pConfig.api_url; + AuthURL = APIURL + '/auth'; + Util.exec(pCallBack); }); } @@ -126,6 +128,7 @@ var CloudCommander, Util, DOM, $, Github, cb; */ cloudcmd.GitHub.createGist = function(pContent, pFileName){ if(pContent){ + DOM.Images.showLoad(); if(!pFileName) pFileName = Util.getDate(); @@ -144,7 +147,11 @@ var CloudCommander, Util, DOM, $, Github, cb; lOptions.files = lFiles; - lGist.create(lOptions, cb); + lGist.create(lOptions, function(pError, pData){ + DOM.Images.hideLoad(); + console.log(pError || pData); + console.log(pData && pData.html_url); + }); } return pContent; diff --git a/lib/cloudfunc.js b/lib/cloudfunc.js index 8c9454eb..333bfacf 100644 --- a/lib/cloudfunc.js +++ b/lib/cloudfunc.js @@ -278,7 +278,7 @@ var CloudFunc, exports; */ CloudFunc._getFileTableHeader = function(pFileTableTitles) { - var lHeader='
  • '; + var lHeader='
  • '; lHeader+=''; for(var i=0;i pExt.length) { + if (typeof pExt === 'string' && pName.length > pExt.length) { var lExtNum = pName.lastIndexOf(pExt), /* последнее вхождение расширения*/ lExtSub = lLength - lExtNum; /* длина расширения*/ diff --git a/test/test.js b/test/test.js index 242d48d8..5449061a 100644 --- a/test/test.js +++ b/test/test.js @@ -39,7 +39,7 @@ '/X11/' + '' + '
  • ' + - '
  • ' + + '
  • ' + '' + 'name' + 'size' + From 2edcdc481069c3866eb8cbc7f2a744e4ff0b23dd Mon Sep 17 00:00:00 2001 From: coderaiser Date: Mon, 17 Dec 2012 06:27:12 -0500 Subject: [PATCH 035/347] fixed bug in not fully functional browsers, jquery loaded after ie.js, should be before --- ChangeLog | 3 +++ lib/client.js | 9 +++++---- lib/client/dom.js | 15 +++++++++++++-- lib/client/ie.js | 6 +++--- test/.jshintrc | 2 +- 5 files changed, 25 insertions(+), 10 deletions(-) diff --git a/ChangeLog b/ChangeLog index 07834c91..1eb3dcab 100644 --- a/ChangeLog +++ b/ChangeLog @@ -19,6 +19,9 @@ and Util.setTimeout(pFunction [, pCallBack, pTime]) * If choosen upload to -> Gist, and file in json have not '.json' extension, it's added to the and of filename. +* Fixed bug in not fully functional browsers, +jquery loaded after ie.js, should be before. + 2012.12.12, Version 0.1.8 diff --git a/lib/client.js b/lib/client.js index 3d67708e..63fa1851 100644 --- a/lib/client.js +++ b/lib/client.js @@ -238,10 +238,11 @@ CloudClient.init = function(){ if(!document.body.scrollIntoViewIfNeeded){ this.OLD_BROWSER = true; - DOM.jsload(CloudClient.LIBDIRCLIENT + 'ie.js', - function(){ - DOM.jqueryLoad( lFunc ); - }); + var lSrc = CloudClient.LIBDIRCLIENT + 'ie.js'; + + DOM.jqueryLoad( + DOM.retJSLoad(lSrc, lFunc) + ); }else lFunc(); }; diff --git a/lib/client/dom.js b/lib/client/dom.js index 6de8ffaa..8f03bc4e 100644 --- a/lib/client/dom.js +++ b/lib/client/dom.js @@ -535,7 +535,7 @@ var CloudCommander, Util, DOM, CloudFunc; * @param pFunc */ DOM.jsload = function(pSrc, pFunc){ - if(pSrc instanceof Array){ + if( Util.isArray(pSrc) ){ for(var i=0; i < pSrc.length; i++) pSrc[i].name = 'script'; @@ -549,6 +549,17 @@ var CloudCommander, Util, DOM, CloudFunc; }); }, + /** + * returns jsload functions + */ + DOM.retJSLoad = function(pSrc, pFunc){ + var lRet = function(){ + return DOM.jsload(pSrc, pFunc); + }; + + return lRet; + }, + /** * Функция создаёт елемент style и записывает туда стили * @param pParams_o - структура параметров, заполняеться таким @@ -559,7 +570,7 @@ var CloudCommander, Util, DOM, CloudFunc; pParams_o.name = 'style'; pParams_o.parent = pParams_o.parent || document.head; - return DOM.anyload(pParams_o); + return DOM.anyload(pParams_o); }, /** diff --git a/lib/client/ie.js b/lib/client/ie.js index 45136ac7..89bda3c3 100644 --- a/lib/client/ie.js +++ b/lib/client/ie.js @@ -77,14 +77,14 @@ var Util, DOM, $; https://gist.github.com/2581101 */ centerIfNeeded = arguments.length === 0 ? true : !!centerIfNeeded; - + var parent = pElement.parentNode, parentComputedStyle = window.getComputedStyle(parent, null), parentBorderTopWidth = - parseInt(parentComputedStyle.getPropertyValue('border-top-width')), + parseInt(parentComputedStyle.getPropertyValue('border-top-width'), 10), parentBorderLeftWidth = - parseInt(parentComputedStyle.getPropertyValue('border-left-width')), + parseInt(parentComputedStyle.getPropertyValue('border-left-width'), 10), overTop = pElement.offsetTop - parent.offsetTop < parent.scrollTop, overBottom = diff --git a/test/.jshintrc b/test/.jshintrc index f648a0a4..071acb2f 100644 --- a/test/.jshintrc +++ b/test/.jshintrc @@ -6,7 +6,7 @@ "es5" : true, "forin" : true, "globalstrict" : true, - "jquery" : true, + "jquery" : false, "newcap" : true, "noarg" : true, "node" : true, From cb87e5c32797ac1270540ee3e1c71d427c22e0ad Mon Sep 17 00:00:00 2001 From: coderaiser Date: Mon, 17 Dec 2012 06:38:51 -0500 Subject: [PATCH 036/347] fixed bug with closing terminal and opening viewer: inside event function varible called event do not exist (everithing ok on webkit) --- ChangeLog | 3 +++ lib/client/terminal.js | 8 ++++---- lib/client/viewer.js | 8 ++++---- 3 files changed, 11 insertions(+), 8 deletions(-) diff --git a/ChangeLog b/ChangeLog index 1eb3dcab..46a23f05 100644 --- a/ChangeLog +++ b/ChangeLog @@ -22,6 +22,9 @@ not '.json' extension, it's added to the and of filename. * Fixed bug in not fully functional browsers, jquery loaded after ie.js, should be before. +* Fixed bug with closing terminal and opening viewer: +inside event function varible called event do not exist +(everithing ok on webkit). 2012.12.12, Version 0.1.8 diff --git a/lib/client/terminal.js b/lib/client/terminal.js index 55626ce9..536763a4 100644 --- a/lib/client/terminal.js +++ b/lib/client/terminal.js @@ -107,15 +107,15 @@ var CloudCommander, Util, DOM, $; ]) ); /* добавляем обработчик клавишь */ - DOM.addKeyListener(function(){ + DOM.addKeyListener(function(pEvent){ /* если клавиши можно обрабатывать */ if(Hidden && KeyBinding.get() && - event.keyCode === cloudcmd.KEY.TRA){ + pEvent.keyCode === cloudcmd.KEY.TRA){ JqueryTerminal.show(); - event.preventDefault(); + pEvent.preventDefault(); } - else if(!Hidden && event.keyCode === cloudcmd.KEY.ESC) + else if(!Hidden && pEvent.keyCode === cloudcmd.KEY.ESC) JqueryTerminal.hide(); }); }; diff --git a/lib/client/viewer.js b/lib/client/viewer.js index 6c0f44b0..6a476d8b 100644 --- a/lib/client/viewer.js +++ b/lib/client/viewer.js @@ -193,16 +193,16 @@ var CloudCommander, Util, DOM, CloudFunc, $; FancyBox.show( DOM.getCurrentFile() ); }; - var lKeyListener = function(){ + var lKeyListener = function(pEvent){ var lF3 = cloudcmd.KEY.F3, lKeyBinded = KeyBinding.get(), - lKey = event.keyCode, - lShift = event.shiftKey; + lKey = pEvent.keyCode, + lShift = pEvent.shiftKey; /* если клавиши можно обрабатывать */ if( lKeyBinded && lKey === lF3 && lShift ){ lView(); - event.preventDefault(); + pEvent.preventDefault(); } }; From 1a2b74990ac0231954105f533ec7cddfb8fadacc Mon Sep 17 00:00:00 2001 From: coderaiser Date: Mon, 17 Dec 2012 06:54:22 -0500 Subject: [PATCH 037/347] fixed jshint errors --- lib/client/dom.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/client/dom.js b/lib/client/dom.js index 8f03bc4e..2aab90ac 100644 --- a/lib/client/dom.js +++ b/lib/client/dom.js @@ -14,7 +14,7 @@ var CloudCommander, Util, DOM, CloudFunc; /** * private function thet unset currentfile */ - function UnSetCurrentFile(pCurrentFile){ + function unSetCurrentFile(pCurrentFile){ var lRet_b = DOM.isCurrentFile(pCurrentFile); if(!pCurrentFile) @@ -865,7 +865,7 @@ var CloudCommander, Util, DOM, CloudFunc; pCurrentFile = pCurrentFile.nextSibling; if(lCurrentFileWas) - UnSetCurrentFile(lCurrentFileWas); + unSetCurrentFile(lCurrentFileWas); DOM.addClass(pCurrentFile, getCurrentFile()); From 9eed915772460c9fc4748fd7bfbd729e07d7cef4 Mon Sep 17 00:00:00 2001 From: coderaiser Date: Mon, 17 Dec 2012 10:52:33 -0500 Subject: [PATCH 038/347] added ability to upload files to dropbox --- ChangeLog | 3 + config.json | 2 + lib/client/menu.js | 72 +- lib/client/storage/_dropbox.js | 181 ++- lib/client/storage/_dropbox_chooser.js | 55 + lib/client/storage/_gdrive.js | 113 ++ lib/client/storage/_github.js | 17 +- lib/client/storage/dropbox/LICENSE.txt | 19 + lib/client/storage/dropbox/README.md | 73 ++ .../storage/dropbox/doc/auth_drivers.md | 167 +++ lib/client/storage/dropbox/doc/coffee_faq.md | 84 ++ lib/client/storage/dropbox/doc/development.md | 89 ++ .../storage/dropbox/doc/getting_started.md | 272 ++++ lib/client/storage/dropbox/lib/README.md | 10 + lib/client/storage/dropbox/lib/dropbox.min.js | 5 + lib/client/storage/dropbox/package.json | 46 + .../storage/dropbox/src/000-dropbox.coffee | 6 + .../storage/dropbox/src/api_error.coffee | 49 + lib/client/storage/dropbox/src/base64.coffee | 72 ++ lib/client/storage/dropbox/src/client.coffee | 1104 +++++++++++++++++ lib/client/storage/dropbox/src/drivers.coffee | 477 +++++++ lib/client/storage/dropbox/src/hmac.coffee | 180 +++ lib/client/storage/dropbox/src/oauth.coffee | 162 +++ lib/client/storage/dropbox/src/prod.coffee | 36 + .../storage/dropbox/src/pulled_changes.coffee | 100 ++ .../storage/dropbox/src/references.coffee | 86 ++ lib/client/storage/dropbox/src/stat.coffee | 127 ++ .../storage/dropbox/src/user_info.coffee | 79 ++ lib/client/storage/dropbox/src/xhr.coffee | 450 +++++++ .../storage/dropbox/src/zzz-export.coffee | 21 + lib/client/storage/dropbox/txt.vimrc.txt | 6 + 31 files changed, 4034 insertions(+), 129 deletions(-) create mode 100644 lib/client/storage/_dropbox_chooser.js create mode 100644 lib/client/storage/_gdrive.js create mode 100644 lib/client/storage/dropbox/LICENSE.txt create mode 100644 lib/client/storage/dropbox/README.md create mode 100644 lib/client/storage/dropbox/doc/auth_drivers.md create mode 100644 lib/client/storage/dropbox/doc/coffee_faq.md create mode 100644 lib/client/storage/dropbox/doc/development.md create mode 100644 lib/client/storage/dropbox/doc/getting_started.md create mode 100644 lib/client/storage/dropbox/lib/README.md create mode 100644 lib/client/storage/dropbox/lib/dropbox.min.js create mode 100644 lib/client/storage/dropbox/package.json create mode 100644 lib/client/storage/dropbox/src/000-dropbox.coffee create mode 100644 lib/client/storage/dropbox/src/api_error.coffee create mode 100644 lib/client/storage/dropbox/src/base64.coffee create mode 100644 lib/client/storage/dropbox/src/client.coffee create mode 100644 lib/client/storage/dropbox/src/drivers.coffee create mode 100644 lib/client/storage/dropbox/src/hmac.coffee create mode 100644 lib/client/storage/dropbox/src/oauth.coffee create mode 100644 lib/client/storage/dropbox/src/prod.coffee create mode 100644 lib/client/storage/dropbox/src/pulled_changes.coffee create mode 100644 lib/client/storage/dropbox/src/references.coffee create mode 100644 lib/client/storage/dropbox/src/stat.coffee create mode 100644 lib/client/storage/dropbox/src/user_info.coffee create mode 100644 lib/client/storage/dropbox/src/xhr.coffee create mode 100644 lib/client/storage/dropbox/src/zzz-export.coffee create mode 100644 lib/client/storage/dropbox/txt.vimrc.txt diff --git a/ChangeLog b/ChangeLog index 46a23f05..96288fd6 100644 --- a/ChangeLog +++ b/ChangeLog @@ -26,6 +26,9 @@ jquery loaded after ie.js, should be before. inside event function varible called event do not exist (everithing ok on webkit). +* Added ability to upload files to dropbox. + + 2012.12.12, Version 0.1.8 * Added ability to shutdown Cloud Commander diff --git a/config.json b/config.json index 2dd79a63..c014fd05 100644 --- a/config.json +++ b/config.json @@ -11,6 +11,8 @@ "github_key" : "891c251b925e4e967fa9", "github_secret" : "afe9bed1e810c5dc44c4c2a953fc6efb1e5b0545", "dropbox_key" : "0nd3ssnp5fp7tqs", + "dropbox_secret" : "r61lxpchmk8l06o", + "dropbox_encoded_key" : "DkMz4FYHQTA=|GW6pf2dONkrGvckMwBsl1V1vysrCPktPiUWN7UpDjw==", "dropbox_chooser_key" : "o7d6llji052vijk", "logs" : false, "show_keys_panel" : true, diff --git a/lib/client/menu.js b/lib/client/menu.js index ea34ed92..164f4cc6 100644 --- a/lib/client/menu.js +++ b/lib/client/menu.js @@ -34,6 +34,24 @@ var CloudCommander, Util, DOM, $; } } + function getContent(pCallBack){ + return DOM.getCurrentFileContent(function(pData){ + var lName = DOM.getCurrentName(); + if( Util.isObject(pData) ){ + pData = JSON.stringify(pData, null, 4); + + var lExt = '.json'; + if( !Util.checkExtension(lName, lExt) ) + lName += lExt; + } + + Util.exec(pCallBack, { + data: pData, + name: lName + }); + }); + } + /** function return configureation for menu */ function getConfig (){ return{ @@ -76,24 +94,17 @@ var CloudCommander, Util, DOM, $; 'gist': { name: 'Gist', callback: function(key, opt){ - DOM.getCurrentFileContent(function(pData){ - var lName = DOM.getCurrentName(); - if( Util.isObject(pData) ){ - pData = JSON.stringify(pData, null, 4); - - var lExt = '.json'; - if( !Util.checkExtension(lName, lExt) ) - lName += lExt; - } - - var lGitHub = cloudcmd.GitHub; + getContent(function(pParams){ + var lGitHub = cloudcmd.GitHub, + lData = pParams.data, + lName = pParams.name; if('init' in lGitHub) - lGitHub.createGist(pData, lName); + lGitHub.createGist(lData, lName); else Util.exec(cloudcmd.GitHub, function(){ - lGitHub.createGist(pData, lName); + lGitHub.createGist(lData, lName); }); }); @@ -103,28 +114,37 @@ var CloudCommander, Util, DOM, $; 'gdrive': { name: 'GDrive', - callback: function(key, opt){ - DOM.getCurrentFileContent(function(data){ - var lName = DOM.getCurrentName(); - - if( Util.isObject(data) ) - data = JSON.stringify(data, null, 4); - var lData = { - data: data, - name: lName - }; - + getContent(function(pParams){ var lGDrive = cloudcmd.GDrive; if('init' in lGDrive) - lGDrive.init(lData); + lGDrive.init(pParams); else - Util.exec(cloudcmd.GDrive, lData); + Util.exec(cloudcmd.GDrive, pParams); }); Util.log('Uploading to gdrive...'); } + }, + 'dropbox':{ + name: 'DropBox', + callback: function(key, opt){ + getContent(function(pParams){ + var lDropBox = cloudcmd.DropBox, + lData = pParams.data, + lName = pParams.name; + + if('init' in lDropBox) + lDropBox.uploadFile(lData, lName); + else + Util.exec(lDropBox, function(){ + cloudcmd.DropBox.uploadFile(lData, lName); + }); + }); + + Util.log('Uploading to dropbox...'); + } } } }, diff --git a/lib/client/storage/_dropbox.js b/lib/client/storage/_dropbox.js index bedf6c3d..d1c8b9ee 100644 --- a/lib/client/storage/_dropbox.js +++ b/lib/client/storage/_dropbox.js @@ -1,113 +1,102 @@ -var CloudCommander, Util, DOM, gapi; +var CloudCommander, Util, DOM, CloudFunc, Dropbox, cb, Client; +/* module for work with github */ (function(){ "use strict"; - var cloudcmd = CloudCommander, - GDrive = {}; - + var cloudcmd = CloudCommander, + //Client, + DropBoxStore = {}; - function authorize(pData){ - /* https://developers.google.com/drive/credentials */ - Util.setTimeout({ - func : function(pCallBack){ - var lCLIENT_ID = '255175681917.apps.googleusercontent.com', - lSCOPES = 'https://www.googleapis.com/auth/drive', - lParams = { - 'client_id' : lCLIENT_ID, - 'scope' : lSCOPES, - 'immediate' : true - }; - - gapi.auth.authorize(lParams, pCallBack); - }, - - callback : function(pAuthResult){ - var lRet; - if (pAuthResult && !pAuthResult.error){ - uploadFile(pData); - - lRet = true; - } - return lRet; - } - }); - } + /* temporary callback function for work with github */ + cb = function (err, data){ console.log(err || data);}; - function load(pData){ - var lUrl = 'https://apis.google.com/js/client.js'; - - DOM.jsload(lUrl, function(){ - authorize(pData); - }); - } - - /** - * Start the file upload. - * - * @param {Object} evt Arguments from the file selector. - */ - function uploadFile(pData) { - gapi.client.load('drive', 'v2', function() { - GDrive.uploadFile(pData); - }); - } + /* PRIVATE FUNCTIONS */ /** - * Insert new file. - * - * @param {File} fileData {name, data} File object to read data from. - * @param {Function} callback Function to call when the request is complete. + * function loads dropbox.js */ - GDrive.uploadFile = function(pData, callback) { - var lData = pData.data, - lName = pData.name, - boundary = '-------314159265358979323846', - delimiter = "\r\n--" + boundary + "\r\n", - close_delim = "\r\n--" + boundary + "--", - - contentType = pData.type || 'application/octet-stream', - metadata = { - 'title' : lName, - 'mimeType' : contentType - }, - - base64Data = btoa(lData), - - multipartRequestBody = - delimiter + - 'Content-Type: application/json\r\n\r\n' + - JSON.stringify(metadata) + - delimiter + - 'Content-Type: ' + contentType + '\r\n' + - 'Content-Transfer-Encoding: base64\r\n' + - '\r\n' + - base64Data + - close_delim; - - var request = gapi.client.request({ - 'path': '/upload/drive/v2/files', - 'method': 'POST', - 'params': {'uploadType': 'multipart'}, - 'headers': { - 'Content-Type': 'multipart/mixed; boundary="' + boundary + '"' - }, - - 'body': multipartRequestBody + function load(pCallBack){ + console.time('dropbox load'); + + var lSrc = '//cdnjs.cloudflare.com/ajax/libs/dropbox.js/0.7.1/dropbox.min.js', + lLocal = CloudFunc.LIBDIRCLIENT + 'storage/dropbox/lib/dropbox.min.js', + lOnload = function(){ + console.timeEnd('dropbox load'); + DOM.Images.hideLoad(); + + Util.exec(pCallBack); + }; + + DOM.jsload(lSrc, { + onload : lOnload, + error : DOM.retJSLoad(lLocal, lOnload) }); - if (!callback) - callback = function(file) { - console.log(file); + } + + function getUserData(pCallBack){ + var lName, + lShowUserInfo = function(pError, pData){ + if(!pError){ + lName = pData.name; + console.log('Hello ' + lName + ' :)!'); + } }; - request.execute(callback); + Client.getUserInfo(lShowUserInfo); + + Util.exec(pCallBack); + } + /** + * function logins on dropbox + * + * @param pData = {key, secret} + */ + DropBoxStore.login = function(pCallBack){ + cloudcmd.getConfig(function(pConfig){ + Client = new Dropbox.Client({ + key: pConfig.dropbox_encoded_key + }); + Client.authDriver(new Dropbox.Drivers.Redirect({rememberUser: true})); + + Client.authenticate(function(pError, pClient) { + Util.log(pError); + Client = pClient; + Util.exec(pCallBack); + }); + + }); + + }; + /** + * upload file to DropBox + */ + DropBoxStore.uploadFile = function(pContent, pFileName){ + if(pContent){ + DOM.Images.showLoad(); + if(!pFileName) + pFileName = Util.getDate(); + + Client.writeFile(pFileName, pContent, function(pError, pData){ + DOM.Images.hideLoad(); + console.log(pError || pData); + }); + } + + return pContent; }; - - GDrive.init = function(pData){ - load(pData); + DropBoxStore.init = function(pCallBack){ + Util.loadOnLoad([ + Util.retExec(pCallBack), + getUserData, + DropBoxStore.login, + load + ]); + + cloudcmd.DropBox.init = null; }; - cloudcmd.GDrive = GDrive; -})(); \ No newline at end of file + cloudcmd.DropBox = DropBoxStore; +})(); diff --git a/lib/client/storage/_dropbox_chooser.js b/lib/client/storage/_dropbox_chooser.js new file mode 100644 index 00000000..5532e1a9 --- /dev/null +++ b/lib/client/storage/_dropbox_chooser.js @@ -0,0 +1,55 @@ +var CloudCommander, DOM, Dropbox; +/* module for work with github */ + +(function(){ + "use strict"; + + var cloudcmd = CloudCommander, + CHOOSER_API = 'https://www.dropbox.com/static/api/1/dropbox.js', + CLIENT_ID, + DropBoxStore = {}, + options = { + linkType: "direct", + success: function(files) { + console.log("Here's the file link:" + files[0].link); + }, + cancel: function() { + console.log('Chose something'); + } + }; + + /* PRIVATE FUNCTIONS */ + + /** + * function loads dropbox.js + */ + function load(){ + console.time('dropbox load'); + + cloudcmd.getConfig(function(pConfig){ + var lElement = DOM.anyload({ + src : CHOOSER_API, + not_append : true, + id : 'dropboxjs', + func : DropBoxStore.choose + + }); + + var lDropBoxId = pConfig.dropbox_chooser_key; + lElement.setAttribute('data-app-key', lDropBoxId); + document.body.appendChild(lElement); + + console.timeEnd('dropbox load'); + }); + } + + DropBoxStore.choose = function(){ + Dropbox.choose(options); + }; + + DropBoxStore.init = function(){ + load(); + }; + + cloudcmd.DropBox = DropBoxStore; +})(); diff --git a/lib/client/storage/_gdrive.js b/lib/client/storage/_gdrive.js new file mode 100644 index 00000000..bedf6c3d --- /dev/null +++ b/lib/client/storage/_gdrive.js @@ -0,0 +1,113 @@ +var CloudCommander, Util, DOM, gapi; + +(function(){ + "use strict"; + + var cloudcmd = CloudCommander, + GDrive = {}; + + + function authorize(pData){ + /* https://developers.google.com/drive/credentials */ + Util.setTimeout({ + func : function(pCallBack){ + var lCLIENT_ID = '255175681917.apps.googleusercontent.com', + lSCOPES = 'https://www.googleapis.com/auth/drive', + lParams = { + 'client_id' : lCLIENT_ID, + 'scope' : lSCOPES, + 'immediate' : true + }; + + gapi.auth.authorize(lParams, pCallBack); + }, + + callback : function(pAuthResult){ + var lRet; + if (pAuthResult && !pAuthResult.error){ + uploadFile(pData); + + lRet = true; + } + return lRet; + } + }); + } + + function load(pData){ + var lUrl = 'https://apis.google.com/js/client.js'; + + DOM.jsload(lUrl, function(){ + authorize(pData); + }); + } + + /** + * Start the file upload. + * + * @param {Object} evt Arguments from the file selector. + */ + function uploadFile(pData) { + gapi.client.load('drive', 'v2', function() { + GDrive.uploadFile(pData); + }); + } + + /** + * Insert new file. + * + * @param {File} fileData {name, data} File object to read data from. + * @param {Function} callback Function to call when the request is complete. + */ + GDrive.uploadFile = function(pData, callback) { + var lData = pData.data, + lName = pData.name, + boundary = '-------314159265358979323846', + delimiter = "\r\n--" + boundary + "\r\n", + close_delim = "\r\n--" + boundary + "--", + + contentType = pData.type || 'application/octet-stream', + metadata = { + 'title' : lName, + 'mimeType' : contentType + }, + + base64Data = btoa(lData), + + multipartRequestBody = + delimiter + + 'Content-Type: application/json\r\n\r\n' + + JSON.stringify(metadata) + + delimiter + + 'Content-Type: ' + contentType + '\r\n' + + 'Content-Transfer-Encoding: base64\r\n' + + '\r\n' + + base64Data + + close_delim; + + var request = gapi.client.request({ + 'path': '/upload/drive/v2/files', + 'method': 'POST', + 'params': {'uploadType': 'multipart'}, + 'headers': { + 'Content-Type': 'multipart/mixed; boundary="' + boundary + '"' + }, + + 'body': multipartRequestBody + }); + + if (!callback) + callback = function(file) { + console.log(file); + }; + + request.execute(callback); + }; + + + GDrive.init = function(pData){ + load(pData); + }; + + cloudcmd.GDrive = GDrive; +})(); \ No newline at end of file diff --git a/lib/client/storage/_github.js b/lib/client/storage/_github.js index 24f426f3..0f7ba500 100644 --- a/lib/client/storage/_github.js +++ b/lib/client/storage/_github.js @@ -62,11 +62,7 @@ var CloudCommander, Util, DOM, $, Github, cb; if ( Util.isContainStr(lCode, '?code=') ){ lCode = lCode.replace('?code=',''); - DOM.ajax({ - type : 'put', - url : AuthURL, - data: lCode, - success: function(pData){ + var lSuccess = function(pData){ if(pData && pData.token){ lToken = pData.token; @@ -76,8 +72,15 @@ var CloudCommander, Util, DOM, $, Github, cb; } else Util.log("Worning: token not getted..."); - } - }); + }, + lData = { + type : 'put', + url : AuthURL, + data : lCode, + success : lSuccess + }; + + DOM.ajax(lData); } else //window.open('welcome.html', 'welcome','width=300,height=200,menubar=yes,status=yes')"> diff --git a/lib/client/storage/dropbox/LICENSE.txt b/lib/client/storage/dropbox/LICENSE.txt new file mode 100644 index 00000000..03350194 --- /dev/null +++ b/lib/client/storage/dropbox/LICENSE.txt @@ -0,0 +1,19 @@ +Copyright (c) 2012 Dropbox, Inc., http://www.dropbox.com + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +of the Software, and to permit persons to whom the Software is furnished to do +so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/lib/client/storage/dropbox/README.md b/lib/client/storage/dropbox/README.md new file mode 100644 index 00000000..cbca9f8b --- /dev/null +++ b/lib/client/storage/dropbox/README.md @@ -0,0 +1,73 @@ +# Client Library for the Dropbox API + +This is a JavaScript client library for the Dropbox API, +[written in CoffeeScript](https://github.com/dropbox/dropbox-js/blob/master/doc/coffee_faq.md), +suitable for use in both modern browsers and in server-side code running under +[node.js](http://nodejs.org/). + + +## Supported Platforms + +This library is tested against the following JavaScript platforms + +* [node.js](http://nodejs.org/) 0.6 and 0.8 +* [Chrome](https://www.google.com/chrome) 23 +* [Firefox](www.mozilla.org/firefox) 17 +* Internet Explorer 9 + +Keep in mind that the versions above are not hard requirements. + + +## Installation and Usage + +The +[getting started guide](https://github.com/dropbox/dropbox-js/blob/master/doc/getting_started.md) +will help you get your first dropbox.js application up and running. + +Peruse the source code of the +[sample apps](https://github.com/dropbox/dropbox-js/tree/master/samples), +and borrow as much as you need. + +The +[dropbox.js API reference](http://coffeedoc.info/github/dropbox/dropbox-js/master/class_index.html) +can be a good place to bookmark while building your application. + +If you run into a problem, take a look at +[the dropbox.js GitHub issue list](https://github.com/dropbox/dropbox-js/issues). +Please open a new issue if your problem wasn't already reported. + + +## Development + +This library is written in CoffeeScript. +[This document](https://github.com/dropbox/dropbox-js/blob/master/doc/coffee_faq.md) +can help you understand if that matters to you. + +The +[development guide](https://github.com/dropbox/dropbox-js/blob/master/doc/development.md) +will make your life easier if you need to change the source code. + + +## Platform-Specific Issues + +This lists the most serious problems that you might run into while using +`dropbox.js`. See +[the GitHub issue list](https://github.com/dropbox/dropbox-js/issues) for a +full list of outstanding problems. + +### node.js + +Reading and writing binary files is currently broken. + +### Internet Explorer 9 + +The library only works when used from `https://` pages, due to +[these issues](http://blogs.msdn.com/b/ieinternals/archive/2010/05/13/xdomainrequest-restrictions-limitations-and-workarounds.aspx). + +Reading and writing binary files is unsupported. + + +## Copyright and License + +The library is Copyright (c) 2012 Dropbox Inc., and distributed under the MIT +License. diff --git a/lib/client/storage/dropbox/doc/auth_drivers.md b/lib/client/storage/dropbox/doc/auth_drivers.md new file mode 100644 index 00000000..5507f058 --- /dev/null +++ b/lib/client/storage/dropbox/doc/auth_drivers.md @@ -0,0 +1,167 @@ +# Authentication Drivers + +This document explains the structure and functionality of a `dropbox.js` OAuth +driver, and describes the drivers that ship with the library. + +## The OAuth Driver Interface + +An OAuth driver is a JavaScript object that implements the methods documented +in the +[Dropbox.AuthDriver class](http://coffeedoc.info/github/dropbox/dropbox-js/master/classes/Dropbox/AuthDriver.html). +This class exists solely for the purpose of documenting these methods. + +A simple driver can get away with implementing `url` and `doAuthorize`. The +following example shows an awfully unusable node.js driver that asks the user +to visit the authorization URL in a browser. + +```javascript +var util = require("util"); +var simpleDriver = { + url: function() { return ""; }, + doAuthorize: function(authUrl, token, tokenSecret, callback) { + util.print("Visit the following in a browser, then press Enter\n" + + authUrl + "\n"); + var onEnterKey = function() { + process.stdin.removeListener("data", onEnterKey); + callback(token); + } + process.stdin.addListener("data", onEnterKey); + process.stdin.resume(); + } +}; +``` + +Complex drivers can take control of the OAuth process by implementing +`onAuthStateChange`. Implementations of this method should read the `authState` +field of the `Dropbox.Client` instance they are given to make decisions. +Implementations should call the `credentials` and `setCredentials` methods on +the client to control the OAuth process. + +See the +[Dropbox.Drivers.Redirect source](https://github.com/dropbox/dropbox-js/blob/master/src/drivers.coffee) +for a sample implementation of `onAuthStateChange`. + + +### The OAuth Process Steps + +The `authenticate` method in `Dropbox.Client` implements the OAuth process as a +finite state machine (FSM). The current state is available in the `authState` +field. + +The authentication FSM has the following states. + +* `Dropbox.Client.RESET` is the initial state, where the client has no OAuth +tokens; after `onAuthStateChange` is triggered, the client will attempt to +obtain an OAuth request token +* `Dropbox.Client.REQUEST` indicates that the client has obtained an OAuth +request token; after `onAuthStateChange` is triggered, the client will call +`doAuthorize` on the OAuth driver, to get the OAuth request token authorized by +the user +* `Dropbox.Client.AUTHORIZED` is reached after the `doAuthorize` calls its +callback, indicating that the user has authorized the OAuth request token; +after `onAuthStateChange` is triggered, the client will attempt to exchange the +request token for an OAuth access token +* `Dropbox.Client.DONE` indicates that the OAuth process has completed, and the +client has an OAuth access token that can be used in API calls; after +`onAuthStateChange` is triggered, `authorize` will call its callback function, +and report success +* `Dropbox.Client.SIGNED_OFF` is reached when the client's `signOut` method is +called, after the API call succeeds; after `onAuthStateChange` is triggered, +`signOut` will call its callback function, and report success +* `Dropbox.Client.ERROR` is reached if any of the Dropbox API calls used by +`authorize` or `signOut` results in an error; after `onAuthStateChange` is +triggered, `authorize` or `signOut` will call its callback function and report +the error + + +## Built-in OAuth Drivers + +`dropbox.js` ships with the OAuth drivers below. + +### Dropbox.Drivers.Redirect + +The recommended built-in driver for browser applications completes the OAuth +token authorization step by redirecting the browser to the Dropbox page that +performs the authorization and having that page redirect back to the +application page. + +This driver's constructor takes the following options. + +* `useQuery` should be set to true for applications that use the URL fragment +(the part after `#`) to store state information +* `rememberUser` can be set to true to have the driver store the user's OAuth +token in `localStorage`, so the user doesn't have to authorize the application +on every request + +Although it seems that `rememberUser` should be true by default, it brings a +couple of drawbacks. The user's token will still be valid after signing off the +Dropbox web site, so your application will still recognize the user and access +their Dropbox. This behavior is unintuitive to users. A reasonable compromise +for apps that use `rememberUser` is to provide a `Sign out` button that calls +the `signOut` method on the app's `Dropbox.Client` instance. + +The +[checkbox.js](https://github.com/dropbox/dropbox-js/tree/master/samples/checkbox.js) +sample application uses `rememberUser`, and implements signing off as described +above. + + +### Dropbox.Drivers.Popup + +This driver may be useful for browser applications that can't handle the +redirections peformed by `Dropbox.Drivers.Redirect`. This driver avoids +changing the location of the application's browser window by popping up a +separate window, and loading the Dropbox authorization page in that window. + +The popup method has a couple of serious drawbacks. Most browsers will not +display the popup window by default, and instead will show a hard-to-notice +warning that the user must interact with to display the popup. The driver's +code for communicating between the popup and the main application window does +not work on IE9 and below, so applications that use it will only work on +Chrome, Firefox and IE10+. + +If the drawbacks above are more acceptable than restructuring your application +to handle redirects, create a page on your site that contains the +[receiver code](https://github.com/dropbox/dropbox-js/blob/master/test/html/oauth_receiver.html), +and point the `Dropbox.Drivers.Popup` constructor to it. + +```javascript +client.authDriver(new Dropbox.Drivers.Popup({receiverUrl: "https://url.to/receiver.html"})); +``` + +The popup driver adds a `#` (fragment hash) to the receiver URL if necessary, +to ensure that the user's Dropbox uid and OAuth token are passed to the +receiver in a URL fragment. This measure may improve your users' privacy, as it +reduces the chance that their uid or token ends up in a server log. + +If you have a good reason to disable the behavior above, set the `noFragment` +option to true. + +```javascript +client.authDriver(new Dropbox.Drivers.Popup({receiverUrl: "https://url.to/receiver.html", noFragment: true})); +``` + + +### Dropbox.Drivers.NodeServer + +This driver is designed for use in the automated test suites of node.js +applications. It completes the OAuth token authorization step by opening the +Dropbox authorization page in a new browser window, and "catches" the OAuth +redirection by setting up a small server using the `https` built-in node.js +library. + +The driver's constructor takes the following options. + +* `port` is the HTTP port number; the default is 8192, and works well with the +Chrome extension described below +* `favicon` is a path to a file that will be served in response to requests to +`/favicon.ico`; setting this to a proper image will avoid some warnings in the +browsers' consoles + +To fully automate your test suite, you need to load up the Chrome extension +bundled in the `dropbox.js` source tree. The extension automatically clicks on +the "Authorize" button in the Dropbox token authorization page, and closes the +page after the token authorization process completes. Follow the steps in the +[development guide](https://github.com/dropbox/dropbox-js/blob/master/doc/development.md) +to build and install the extension. + diff --git a/lib/client/storage/dropbox/doc/coffee_faq.md b/lib/client/storage/dropbox/doc/coffee_faq.md new file mode 100644 index 00000000..b4814244 --- /dev/null +++ b/lib/client/storage/dropbox/doc/coffee_faq.md @@ -0,0 +1,84 @@ +# dropbox.js and CoffeeScript FAQ + +dropbox.js is written in [CoffeeScript](http://coffeescript.org/), which +compiles into very readable JavaScript. This document addresses the concerns +that are commonly raised by JavaScript developers that do not use or wish to +learn CoffeeScript. + + +## Do I need to learn CoffeeScript to use the library? + +**No.** + +The examples in the +[getting started guide](https://github.com/dropbox/dropbox-js/blob/master/doc/getting_started.md) +are all written in JavaScript. The +[dropbox.js API reference](http://coffeedoc.info/github/dropbox/dropbox-js/master/class_index.html) +covers the entire library, so you should not need to read the library source +code to understand how to use it. + +_Please open an issue if the documentation is unclear!_ + +The +[sample apps](https://github.com/dropbox/dropbox-js/tree/master/samples), +are written in CoffeeScript. Please use the `Try CoffeeScript` button on the +[CoffeeScript](http://coffeescript.org/) home page to quickly compile the +sample CoffeeScript into very readable JavaScript. + + +## Do I need to learn CoffeeScript to know how dropbox.js works? + +**No.** + +You can follow the +[development guide](https://github.com/dropbox/dropbox-js/blob/master/doc/development.md) +to build the un-minified JavaScript library in `lib/dropbox.js` and then use +your editor's find feature to get to the source code for the methods that you +are interested in. + +The building instructions in the development guide do not require familiarity +with CoffeeScript. + + +## Do I need to learn CoffeeScript to modify dropbox.js? + +**Yes, but you might not need to modify the library.** + +You do need to learn CoffeeScript to change the `dropbox.js` source code. At +the same time, you can take advantage of the library hooks and the dynamic +nature of the JavaScript language to change the behavior of `dropbox.js` +without touching the source code. + +* You can implement your OAuth strategy. +* You can add methods to the prototype classes such as `Dropbox.Client` to +implement custom operations. _Please open an issue if you think your addition +is generally useful!_ +* You can replace internal classes such as `Dropbox.Xhr` (or selectively +replace methods) with wrappers that tweak the original behavior + + +## Can I contribute to dropbox.js without learning CoffeeScript? + +**Yes.** + +Most of the development time is spent on API design, developing tests, +documentation and sample code. Contributing a good testing strategy with a bug +report can save us 90% of the development time. A feature request that also +includes a well thought-out API change proposal and testing strategy can also +save us 90-95% of the implementation time. + +At the same time, _please open issues for bugs and feature requests even if you +don't have time to include any of the above_. Knowing of a problem is the first +step towards fixing it. + +Last, please share your constructive suggestions on how to make `dropbox.js` +easier to use for JavaScript developers that don't speak CoffeeScript. + + +## Can I complain to get dropbox.js to switch away from CoffeeScript? + +**No.** + +At the moment, 100% of the library's development comes from unpaid, voluntary +efforts. Switching to JavaScript would reduce the efficiency of these efforts, +and it would kill developer motivation. diff --git a/lib/client/storage/dropbox/doc/development.md b/lib/client/storage/dropbox/doc/development.md new file mode 100644 index 00000000..1429d64b --- /dev/null +++ b/lib/client/storage/dropbox/doc/development.md @@ -0,0 +1,89 @@ +# dropbox.js Development + +Read this document if you want to build `dropbox.js` or modify its source code. +If you want to write applications using dropbox.js, check out the +[Getting Started](getting_started.md). + +The library is written using [CoffeeScript](http://coffeescript.org/), built +using [cake](http://coffeescript.org/documentation/docs/cake.html), minified +using [uglify.js](https://github.com/mishoo/UglifyJS/), tested using +[mocha](http://visionmedia.github.com/mocha/) and +[chai.js](http://chaijs.com/), and packaged using [npm](https://npmjs.org/). + +If you don't "speak" CoffeeScript, +[this document](https://github.com/dropbox/dropbox-js/blob/master/doc/coffee_faq.md) +might address some of your concerns. + + +## Dev Environment Setup + +Install [node.js](http://nodejs.org/#download) to get `npm` (the node +package manager), then use it to install the libraries required by the test +suite. + +```bash +git clone https://github.com/dropbox/dropbox-js.git +cd dropbox-js +npm install +``` + +## Build + +Run `npm pack` and ignore any deprecation warnings that might come up. + +```bash +npm pack +``` + +The build output is in the `lib/` directory. `dropbox.js` is the compiled +library that ships in the npm package, and `dropbox.min.js` is a minified +version, optimized for browser apps. + + +## Test + +First, you will need to obtain a couple of Dropbox tokens that will be used by +the automated tests. + +```bash +cake tokens +``` + +Re-run the command above if the tests fail due to authentication errors. + +Once you have Dropbox tokens, you can run the test suite in node.js or in your +default browser. + +```bash +cake test +cake webtest +``` + +The library is automatically re-built when running tests, so you don't need to +run `npm pack`. Please run the tests in both node.js and a browser before +submitting pull requests. + +The tests store all their data in folders named along the lines of +`js tests.0.ac1n6lgs0e3lerk9`. If tests fail, you might have to clean up these +folders yourself. + + +## Testing Chrome Extension + +The test suite opens up a couple of Dropbox authorization pages, and a page +that cannot close itself. dropbox.js ships with a Google Chrome extension that +can fully automate the testing process on Chrome. + +The extension is written in CoffeeScript, so you will have to compile it. + +```bash +cake extension +``` + +After compilation, have Chrome load the unpacked extension at +`test/chrome_extension` and click on the scary-looking toolbar icon to activate +the extension. The icon's color should turn red, to indicate that it is active. + +The extension performs some checks to prevent against attacks. However, for +best results, you should disable the automation (by clicking on the extension +icon) when you're not testing dropbox.js. diff --git a/lib/client/storage/dropbox/doc/getting_started.md b/lib/client/storage/dropbox/doc/getting_started.md new file mode 100644 index 00000000..8fdea598 --- /dev/null +++ b/lib/client/storage/dropbox/doc/getting_started.md @@ -0,0 +1,272 @@ +# Getting Started + +This is a guide to writing your first dropbox.js application. + + +## Library Setup + +This section describes how to get the library hooked up into your application. + +### Browser Applications + +To get started right away, place this snippet in your page's ``. + +```html + +``` + +The snippet is not a typo. [cdnjs](https://cdnjs.com) recommends using +[protocol-relative URLs](http://paulirish.com/2010/the-protocol-relative-url/). + +To get the latest development build of dropbox.js, follow the steps in the +[development guide](https://github.com/dropbox/dropbox-js/blob/master/doc/development.md). + + +#### "Powered by Dropbox" Static Web Apps + +Before writing any source code, use the +[console app](https://dl-web.dropbox.com/spa/pjlfdak1tmznswp/powered_by.js/public/index.html) +to set up your Dropbox. After adding an application, place the source code at +`/Apps/Static Web Apps/my_awesome_app/public`. You should find a pre-generated +`index.html` file in there. + +### node.js Applications + +First, install the `dropbox` [npm](https://npmjs.org/) package. + +```bash +npm install dropbox +``` + +Once the npm package is installed, the following `require` statement lets you +access the same API as browser applications + +```javascript +var Dropbox = require("dropbox"); +``` + + +## Initialization + +[Register your application](https://www.dropbox.com/developers/apps) to obtain +an API key. Read the brief +[API core concepts intro](https://www.dropbox.com/developers/start/core). + +Once you have an API key, use it to create a `Dropbox.Client`. + +```javascript +var client = new Dropbox.Client({ + key: "your-key-here", secret: "your-secret-here", sandbox: true +}); +``` + +If your application requires full Dropbox access, leave out the `sandbox: true` +parameter. + + +### Browser and Open-Source Applications + +The Dropbox API guidelines ask that the API key and secret is never exposed in +cleartext. This is an issue for browser-side and open-source applications. + +To meet this requirement, +[encode your API key](https://dl-web.dropbox.com/spa/pjlfdak1tmznswp/api_keys.js/public/index.html) +and pass the encoded key string to the `Dropbox.Client` constructor. + +```javascript +var client = new Dropbox.Client({ + key: "encoded-key-string|it-is-really-really-long", sandbox: true +}); +``` + + +## Authentication + +Before you can make any API calls, you need to authenticate your application's +user with Dropbox, and have them authorize your app's to access their Dropbox. + +This process follows the [OAuth 1.0](http://tools.ietf.org/html/rfc5849) +protocol, which entails sending the user to a Web page on `www.dropbox.com`, +and then having them redirected back to your application. Each Web application +has its requirements, so `dropbox.js` lets you customize the authentication +process by implementing an +[OAuth driver](https://github.com/dropbox/dropbox-js/blob/master/src/drivers.coffee). + +At the same time, dropbox.js ships with a couple of OAuth drivers, and you +should take advantage of them as you prototype your application. + +Read the +[authentication doc](https://github.com/dropbox/dropbox-js/blob/master/doc/auth_drivers.md) +for further information about writing an OAuth driver, and to learn about all +the drivers that ship with `dropbox.js`. + +### Browser Setup + +The following snippet will set up the recommended driver. + +```javascript +client.authDriver(new Dropbox.Drivers.Redirect()); +``` + +The +[authentication doc](https://github.com/dropbox/dropbox-js/blob/master/doc/auth_drivers.md) +describes some useful options that you can pass to the +`Dropbox.Drivers.Redirect` constructor. + +### node.js Setup + +Single-process node.js applications should create one driver to authenticate +all the clients. + +```javascript +client.authDriver(new Dropbox.Drivers.NodeServer(8191)); +``` + +The +[authentication doc](https://github.com/dropbox/dropbox-js/blob/master/doc/auth_drivers.md) +has useful tips on using the `NodeServer` driver. + +### Shared Code + +After setting up an OAuth driver, authenticating the user is one method call +away. + +```javascript +client.authenticate(function(error, client) { + if (error) { + // Replace with a call to your own error-handling code. + // + // Don't forget to return from the callback, so you don't execute the code + // that assumes everything went well. + return showError(error); + } + + // Replace with a call to your own application code. + // + // The user authorized your app, and everything went well. + // client is a Dropbox.Client instance that you can use to make API calls. + doSomethingCool(client); +}); +``` + +## Error Handlng + +When Dropbox API calls fail, dropbox.js methods pass a `Dropbox.Error` instance +as the first parameter in their callbacks. This parameter is named `error` in +all the code snippets on this page. + +If `error` is a truthy value, you should either recover from the error, or +notify the user that an error occurred. The `status` field in the +`Dropbox.Error` instance contains the HTTP error code, which should be one of +the +[error codes in the REST API](https://www.dropbox.com/developers/reference/api#error-handling). + +The snippet below is a template for an extensive error handler. + +```javascript +var showError = function(error) { + if (window.console) { // Skip the "if" in node.js code. + console.error(error); + } + + switch (error.status) { + case 401: + // If you're using dropbox.js, the only cause behind this error is that + // the user token expired. + // Get the user through the authentication flow again. + break; + + case 404: + // The file or folder you tried to access is not in the user's Dropbox. + // Handling this error is specific to your application. + break; + + case 507: + // The user is over their Dropbox quota. + // Tell them their Dropbox is full. Refreshing the page won't help. + break; + + case 503: + // Too many API requests. Tell the user to try again later. + // Long-term, optimize your code to use fewer API calls. + break; + + case 400: // Bad input parameter + case 403: // Bad OAuth request. + case 405: // Request method not expected + default: + // Caused by a bug in dropbox.js, in your application, or in Dropbox. + // Tell the user an error occurred, ask them to refresh the page. + } +}; +``` + + +## The Fun Part + +Authentication was the hard part of the API integration, and error handling was +the most boring part. Now that these are both behind us, you can interact +with the user's Dropbox and focus on coding up your application! + +The following sections have some commonly used code snippets. The +[Dropbox.Client API reference](http://coffeedoc.info/github/dropbox/dropbox-js/master/classes/Dropbox/Client.html) +will help you navigate less common scenarios, and the +[Dropbox REST API reference](https://www.dropbox.com/developers/reference/api) +describes the underlying HTTP protocol, and can come in handy when debugging +your application, or if you want to extend dropbox.js. + +### User Info + +```javascript +client.getUserInfo(function(error, userInfo) { + if (error) { + return showError(error); // Something went wrong. + } + + alert("Hello, " + userInfo.name + "!"); +}); +``` + +### Write a File + +```javascript +client.writeFile("hello_world.txt", "Hello, world!\n", function(error, stat) { + if (error) { + return showError(error); // Something went wrong. + } + + alert("File saved as revision " + stat.revisionTag); +}); +``` + +### Read a File + +```javascript +client.readFile("hello_world.txt", function(error, data) { + if (error) { + return showError(error); // Something went wrong. + } + + alert(data); // data has the file's contents +}); +``` + +### List a Directory's Contents + +```javascript +client.readdir("/", function(error, entries) { + if (error) { + return showError(error); // Something went wrong. + } + + alert("Your Dropbox contains " + entries.join(", ")); +}); +``` + +### Sample Applications + +Check out the +[sample apps](https://github.com/dropbox/dropbox-js/tree/master/samples) +to see how all these concepts play out together. + diff --git a/lib/client/storage/dropbox/lib/README.md b/lib/client/storage/dropbox/lib/README.md new file mode 100644 index 00000000..6a273731 --- /dev/null +++ b/lib/client/storage/dropbox/lib/README.md @@ -0,0 +1,10 @@ +# dropbox.js Build Directory + +dropbox.js is written in [CoffeeScript](http://coffeescript.org/), and its +source code is in the `src/` directory. The `lib/` directory contains the +compiled JavaScript produced by the build process. + +The +[development guide](https://github.com/dropbox/dropbox-js/blob/master/doc/development.md) +contains a step-by-step guide to the library's build process, and will help you +populate this directory. diff --git a/lib/client/storage/dropbox/lib/dropbox.min.js b/lib/client/storage/dropbox/lib/dropbox.min.js new file mode 100644 index 00000000..86ef3b5f --- /dev/null +++ b/lib/client/storage/dropbox/lib/dropbox.min.js @@ -0,0 +1,5 @@ +/* + * dropbox 0.7.1 + */ +(function(){var t,e,r,n,o,i,s,a,h,u,l,p,c,d,f,y,v,m,g,w,S,b,T={}.hasOwnProperty,E=function(t,e){function r(){this.constructor=t}for(var n in e)T.call(e,n)&&(t[n]=e[n]);return r.prototype=e.prototype,t.prototype=new r,t.__super__=e.prototype,t};if(t=function(){function t(t){this.client=new e(t)}return t}(),t.ApiError=function(){function t(t,e,r){var n;if(this.method=e,this.url=r,this.status=t.status,n=t.responseType?t.response||t.responseText:t.responseText)try{this.responseText=""+n,this.response=JSON.parse(n)}catch(o){this.response=null}else this.responseText="(no response)",this.response=null}return t.prototype.toString=function(){return"Dropbox API error "+this.status+" from "+this.method+" "+this.url+" :: "+this.responseText},t.prototype.inspect=function(){return""+this},t}(),"undefined"!=typeof window&&null!==window?window.atob&&window.btoa?(h=function(t){return window.atob(t)},d=function(t){return window.btoa(t)}):(l="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/",f=function(t,e,r){var n,o;for(o=3-e,t<<=8*o,n=3;n>=o;)r.push(l.charAt(63&t>>6*n)),n-=1;for(n=e;3>n;)r.push("="),n+=1;return null},u=function(t,e,r){var n,o;for(o=4-e,t<<=6*o,n=2;n>=o;)r.push(String.fromCharCode(255&t>>8*n)),n-=1;return null},d=function(t){var e,r,n,o,i,s;for(o=[],e=0,r=0,n=i=0,s=t.length;s>=0?s>i:i>s;n=s>=0?++i:--i)e=e<<8|t.charCodeAt(n),r+=1,3===r&&(f(e,r,o),e=r=0);return r>0&&f(e,r,o),o.join("")},h=function(t){var e,r,n,o,i,s,a;for(i=[],e=0,n=0,o=s=0,a=t.length;(a>=0?a>s:s>a)&&(r=t.charAt(o),"="!==r);o=a>=0?++s:--s)e=e<<6|l.indexOf(r),n+=1,4===n&&(u(e,n,i),e=n=0);return n>0&&u(e,n,i),i.join("")}):(h=function(t){var e,r;return e=new Buffer(t,"base64"),function(){var t,n,o;for(o=[],r=t=0,n=e.length;n>=0?n>t:t>n;r=n>=0?++t:--t)o.push(String.fromCharCode(e[r]));return o}().join("")},d=function(t){var e,r;return e=new Buffer(function(){var e,n,o;for(o=[],r=e=0,n=t.length;n>=0?n>e:e>n;r=n>=0?++e:--e)o.push(t.charCodeAt(r));return o}()),e.toString("base64")}),t.Client=function(){function r(e){this.sandbox=e.sandbox||!1,this.apiServer=e.server||this.defaultApiServer(),this.authServer=e.authServer||this.defaultAuthServer(),this.fileServer=e.fileServer||this.defaultFileServer(),this.downloadServer=e.downloadServer||this.defaultDownloadServer(),this.oauth=new t.Oauth(e),this.driver=null,this.filter=null,this.uid=null,this.authState=null,this.authError=null,this._credentials=null,this.setCredentials(e),this.setupUrls()}return r.prototype.authDriver=function(t){return this.driver=t,this},r.prototype.xhrFilter=function(t){return this.filter=t,this},r.prototype.dropboxUid=function(){return this.uid},r.prototype.credentials=function(){return this._credentials||this.computeCredentials(),this._credentials},r.prototype.authenticate=function(r){var n,o,i=this;return n=null,o=function(){var s;if(n!==i.authState&&(n=i.authState,i.driver.onAuthStateChange))return i.driver.onAuthStateChange(i,o);switch(i.authState){case e.RESET:return i.requestToken(function(t,r){var n,s;return t?(i.authError=t,i.authState=e.ERROR):(n=r.oauth_token,s=r.oauth_token_secret,i.oauth.setToken(n,s),i.authState=e.REQUEST),i._credentials=null,o()});case e.REQUEST:return s=i.authorizeUrl(i.oauth.token),i.driver.doAuthorize(s,i.oauth.token,i.oauth.tokenSecret,function(){return i.authState=e.AUTHORIZED,i._credentials=null,o()});case e.AUTHORIZED:return i.getAccessToken(function(t,r){return t?(i.authError=t,i.authState=e.ERROR):(i.oauth.setToken(r.oauth_token,r.oauth_token_secret),i.uid=r.uid,i.authState=e.DONE),i._credentials=null,o()});case e.DONE:return r(null,i);case t.SIGNED_OFF:return i.reset(),o();case e.ERROR:return r(i.authError)}},o(),this},r.prototype.signOut=function(r){var n,o=this;return n=new t.Xhr("POST",this.urls.signOut),n.signWithOauth(this.oauth),this.dispatchXhr(n,function(t){return t?r(t):(o.reset(),o.authState=e.SIGNED_OFF,o.driver.onAuthStateChange?o.driver.onAuthStateChange(o,function(){return r(t)}):r(t))})},r.prototype.signOff=function(t){return this.signOut(t)},r.prototype.getUserInfo=function(e){var r;return r=new t.Xhr("GET",this.urls.accountInfo),r.signWithOauth(this.oauth),this.dispatchXhr(r,function(r,n){return e(r,t.UserInfo.parse(n),n)})},r.prototype.readFile=function(e,r,n){var o,i,s,a,h,u,l;return n||"function"!=typeof r||(n=r,r=null),i={},u=null,o=null,a=null,r&&(r.versionTag?i.rev=r.versionTag:r.rev&&(i.rev=r.rev),r.blob&&(u="blob"),r.binary&&(u="b"),r.length?(null!=r.start?(h=r.start,s=r.start+r.length-1):(h="",s=r.length),a="bytes="+h+"-"+s):null!=r.start&&(a="bytes="+r.start+"-"),r.modifiedSince&&(o=new Date(r.modifiedSince).toUTCString())),l=new t.Xhr("GET",""+this.urls.getFile+"/"+this.urlEncodePath(e)),l.setParams(i).signWithOauth(this.oauth).setResponseType(u),o&&l.setHeader("If-Modified-Since",o),a&&l.setHeader("Range",a),this.dispatchXhr(l,function(e,r,o){return n(e,r,t.Stat.parse(o))})},r.prototype.writeFile=function(e,r,n,o){var i;return o||"function"!=typeof n||(o=n,n=null),i=t.Xhr.canSendForms&&"object"==typeof r,i?this.writeFileUsingForm(e,r,n,o):this.writeFileUsingPut(e,r,n,o)},r.prototype.writeFileUsingForm=function(e,r,n,o){var i,s,a,h;return a=e.lastIndexOf("/"),-1===a?(i=e,e=""):(i=e.substring(a),e=e.substring(0,a)),s={file:i},n&&(n.noOverwrite&&(s.overwrite="false"),n.lastVersionTag?s.parent_rev=n.lastVersionTag:(n.parentRev||n.parent_rev)&&(s.parent_rev=n.parentRev||n.parent_rev)),h=new t.Xhr("POST",""+this.urls.postFile+"/"+this.urlEncodePath(e)),h.setParams(s).signWithOauth(this.oauth).setFileField("file",i,r,"application/octet-stream"),delete s.file,this.dispatchXhr(h,function(e,r){return o(e,t.Stat.parse(r))})},r.prototype.writeFileUsingPut=function(e,r,n,o){var i,s;return i={},n&&(n.noOverwrite&&(i.overwrite="false"),n.lastVersionTag?i.parent_rev=n.lastVersionTag:(n.parentRev||n.parent_rev)&&(i.parent_rev=n.parentRev||n.parent_rev)),s=new t.Xhr("POST",""+this.urls.putFile+"/"+this.urlEncodePath(e)),s.setBody(r).setParams(i).signWithOauth(this.oauth),this.dispatchXhr(s,function(e,r){return o(e,t.Stat.parse(r))})},r.prototype.stat=function(e,r,n){var o,i;return n||"function"!=typeof r||(n=r,r=null),o={},r&&(null!=r.version&&(o.rev=r.version),(r.removed||r.deleted)&&(o.include_deleted="true"),r.readDir&&(o.list="true",r.readDir!==!0&&(o.file_limit=""+r.readDir)),r.cacheHash&&(o.hash=r.cacheHash)),o.include_deleted||(o.include_deleted="false"),o.list||(o.list="false"),i=new t.Xhr("GET",""+this.urls.metadata+"/"+this.urlEncodePath(e)),i.setParams(o).signWithOauth(this.oauth),this.dispatchXhr(i,function(e,r){var o,i,s;return s=t.Stat.parse(r),o=(null!=r?r.contents:void 0)?function(){var e,n,o,s;for(o=r.contents,s=[],e=0,n=o.length;n>e;e++)i=o[e],s.push(t.Stat.parse(i));return s}():void 0,n(e,s,o)})},r.prototype.readdir=function(t,e,r){var n;return r||"function"!=typeof e||(r=e,e=null),n={readDir:!0},e&&(null!=e.limit&&(n.readDir=e.limit),e.versionTag&&(n.versionTag=e.versionTag)),this.stat(t,n,function(t,e,n){var o,i;return o=n?function(){var t,e,r;for(r=[],t=0,e=n.length;e>t;t++)i=n[t],r.push(i.name);return r}():null,r(t,o,e,n)})},r.prototype.metadata=function(t,e,r){return this.stat(t,e,r)},r.prototype.makeUrl=function(e,r,n){var o,i,s,a,h;return n||"function"!=typeof r||(n=r,r=null),i=r&&(r["long"]||r.longUrl||r.downloadHack)?{short_url:"false"}:{},e=this.urlEncodePath(e),s=""+this.urls.shares+"/"+e,o=!1,a=!1,r&&(r.downloadHack?(o=!0,a=!0):r.download&&(o=!0,s=""+this.urls.media+"/"+e)),h=new t.Xhr("POST",s).setParams(i).signWithOauth(this.oauth),this.dispatchXhr(h,function(e,r){return a&&r&&r.url&&(r.url=r.url.replace(this.authServer,this.downloadServer)),n(e,t.PublicUrl.parse(r,o))})},r.prototype.history=function(e,r,n){var o,i;return n||"function"!=typeof r||(n=r,r=null),o={},r&&null!=r.limit&&(o.rev_limit=r.limit),i=new t.Xhr("GET",""+this.urls.revisions+"/"+this.urlEncodePath(e)),i.setParams(o).signWithOauth(this.oauth),this.dispatchXhr(i,function(e,r){var o,i;return i=r?function(){var e,n,i;for(i=[],e=0,n=r.length;n>e;e++)o=r[e],i.push(t.Stat.parse(o));return i}():void 0,n(e,i)})},r.prototype.revisions=function(t,e,r){return this.history(t,e,r)},r.prototype.thumbnailUrl=function(t,e){var r;return r=this.thumbnailXhr(t,e),r.paramsToUrl().url},r.prototype.readThumbnail=function(e,r,n){var o,i;return n||"function"!=typeof r||(n=r,r=null),o="b",r&&r.blob&&(o="blob"),i=this.thumbnailXhr(e,r),i.setResponseType(o),this.dispatchXhr(i,function(e,r,o){return n(e,r,t.Stat.parse(o))})},r.prototype.thumbnailXhr=function(e,r){var n,o;return n={},r&&(r.format?n.format=r.format:r.png&&(n.format="png"),r.size&&(n.size=r.size)),o=new t.Xhr("GET",""+this.urls.thumbnails+"/"+this.urlEncodePath(e)),o.setParams(n).signWithOauth(this.oauth)},r.prototype.revertFile=function(e,r,n){var o;return o=new t.Xhr("POST",""+this.urls.restore+"/"+this.urlEncodePath(e)),o.setParams({rev:r}).signWithOauth(this.oauth),this.dispatchXhr(o,function(e,r){return n(e,t.Stat.parse(r))})},r.prototype.restore=function(t,e,r){return this.revertFile(t,e,r)},r.prototype.findByName=function(e,r,n,o){var i,s;return o||"function"!=typeof n||(o=n,n=null),i={query:r},n&&(null!=n.limit&&(i.file_limit=n.limit),(n.removed||n.deleted)&&(i.include_deleted=!0)),s=new t.Xhr("GET",""+this.urls.search+"/"+this.urlEncodePath(e)),s.setParams(i).signWithOauth(this.oauth),this.dispatchXhr(s,function(e,r){var n,i;return i=r?function(){var e,o,i;for(i=[],e=0,o=r.length;o>e;e++)n=r[e],i.push(t.Stat.parse(n));return i}():void 0,o(e,i)})},r.prototype.search=function(t,e,r,n){return this.findByName(t,e,r,n)},r.prototype.makeCopyReference=function(e,r){var n;return n=new t.Xhr("GET",""+this.urls.copyRef+"/"+this.urlEncodePath(e)),n.signWithOauth(this.oauth),this.dispatchXhr(n,function(e,n){return r(e,t.CopyReference.parse(n))})},r.prototype.copyRef=function(t,e){return this.makeCopyReference(t,e)},r.prototype.pullChanges=function(e,r){var n,o;return r||"function"!=typeof e||(r=e,e=null),n=e?e.cursorTag?{cursor:e.cursorTag}:{cursor:e}:{},o=new t.Xhr("POST",this.urls.delta),o.setParams(n).signWithOauth(this.oauth),this.dispatchXhr(o,function(e,n){return r(e,t.PulledChanges.parse(n))})},r.prototype.delta=function(t,e){return this.pullChanges(t,e)},r.prototype.mkdir=function(e,r){var n;return n=new t.Xhr("POST",this.urls.fileopsCreateFolder),n.setParams({root:this.fileRoot,path:this.normalizePath(e)}).signWithOauth(this.oauth),this.dispatchXhr(n,function(e,n){return r(e,t.Stat.parse(n))})},r.prototype.remove=function(e,r){var n;return n=new t.Xhr("POST",this.urls.fileopsDelete),n.setParams({root:this.fileRoot,path:this.normalizePath(e)}).signWithOauth(this.oauth),this.dispatchXhr(n,function(e,n){return r(e,t.Stat.parse(n))})},r.prototype.unlink=function(t,e){return this.remove(t,e)},r.prototype["delete"]=function(t,e){return this.remove(t,e)},r.prototype.copy=function(e,r,n){var o,i,s;return n||"function"!=typeof o||(n=o,o=null),i={root:this.fileRoot,to_path:this.normalizePath(r)},e instanceof t.CopyReference?i.from_copy_ref=e.tag:i.from_path=this.normalizePath(e),s=new t.Xhr("POST",this.urls.fileopsCopy),s.setParams(i).signWithOauth(this.oauth),this.dispatchXhr(s,function(e,r){return n(e,t.Stat.parse(r))})},r.prototype.move=function(e,r,n){var o,i;return n||"function"!=typeof o||(n=o,o=null),i=new t.Xhr("POST",this.urls.fileopsMove),i.setParams({root:this.fileRoot,from_path:this.normalizePath(e),to_path:this.normalizePath(r)}).signWithOauth(this.oauth),this.dispatchXhr(i,function(e,r){return n(e,t.Stat.parse(r))})},r.prototype.reset=function(){return this.uid=null,this.oauth.setToken(null,""),this.authState=e.RESET,this.authError=null,this._credentials=null,this},r.prototype.setCredentials=function(t){return this.oauth.reset(t),this.uid=t.uid||null,this.authState=t.authState?t.authState:t.token?e.DONE:e.RESET,this.authError=null,this._credentials=null,this},r.prototype.appHash=function(){return this.oauth.appHash()},r.prototype.setupUrls=function(){return this.fileRoot=this.sandbox?"sandbox":"dropbox",this.urls={requestToken:""+this.apiServer+"/1/oauth/request_token",authorize:""+this.authServer+"/1/oauth/authorize",accessToken:""+this.apiServer+"/1/oauth/access_token",signOut:""+this.apiServer+"/1/unlink_access_token",accountInfo:""+this.apiServer+"/1/account/info",getFile:""+this.fileServer+"/1/files/"+this.fileRoot,postFile:""+this.fileServer+"/1/files/"+this.fileRoot,putFile:""+this.fileServer+"/1/files_put/"+this.fileRoot,metadata:""+this.apiServer+"/1/metadata/"+this.fileRoot,delta:""+this.apiServer+"/1/delta",revisions:""+this.apiServer+"/1/revisions/"+this.fileRoot,restore:""+this.apiServer+"/1/restore/"+this.fileRoot,search:""+this.apiServer+"/1/search/"+this.fileRoot,shares:""+this.apiServer+"/1/shares/"+this.fileRoot,media:""+this.apiServer+"/1/media/"+this.fileRoot,copyRef:""+this.apiServer+"/1/copy_ref/"+this.fileRoot,thumbnails:""+this.fileServer+"/1/thumbnails/"+this.fileRoot,fileopsCopy:""+this.apiServer+"/1/fileops/copy",fileopsCreateFolder:""+this.apiServer+"/1/fileops/create_folder",fileopsDelete:""+this.apiServer+"/1/fileops/delete",fileopsMove:""+this.apiServer+"/1/fileops/move"}},r.ERROR=0,r.RESET=1,r.REQUEST=2,r.AUTHORIZED=3,r.DONE=4,r.SIGNED_OFF=5,r.prototype.urlEncodePath=function(e){return t.Xhr.urlEncodeValue(this.normalizePath(e)).replace(/%2F/gi,"/")},r.prototype.normalizePath=function(t){var e;if("/"===t.substring(0,1)){for(e=1;"/"===t.substring(e,e+1);)e+=1;return t.substring(e)}return t},r.prototype.requestToken=function(e){var r;return r=new t.Xhr("POST",this.urls.requestToken).signWithOauth(this.oauth),this.dispatchXhr(r,e)},r.prototype.authorizeUrl=function(e){var r;return r={oauth_token:e,oauth_callback:this.driver.url()},""+this.urls.authorize+"?"+t.Xhr.urlEncode(r)},r.prototype.getAccessToken=function(e){var r;return r=new t.Xhr("POST",this.urls.accessToken).signWithOauth(this.oauth),this.dispatchXhr(r,e)},r.prototype.dispatchXhr=function(t,e){var r;return t.setCallback(e),t.prepare(),r=t.xhr,this.filter&&!this.filter(r,t)?r:(t.send(),r)},r.prototype.defaultApiServer=function(){return"https://api.dropbox.com"},r.prototype.defaultAuthServer=function(){return this.apiServer.replace("api.","www.")},r.prototype.defaultFileServer=function(){return this.apiServer.replace("api.","api-content.")},r.prototype.defaultDownloadServer=function(){return this.apiServer.replace("api.","dl.")},r.prototype.computeCredentials=function(){var t;return t={key:this.oauth.key,sandbox:this.sandbox},this.oauth.secret&&(t.secret=this.oauth.secret),this.oauth.token&&(t.token=this.oauth.token,t.tokenSecret=this.oauth.tokenSecret),this.uid&&(t.uid=this.uid),this.authState!==e.ERROR&&this.authState!==e.RESET&&this.authState!==e.DONE&&this.authState!==e.SIGNED_OFF&&(t.authState=this.authState),this.apiServer!==this.defaultApiServer()&&(t.server=this.apiServer),this.authServer!==this.defaultAuthServer()&&(t.authServer=this.authServer),this.fileServer!==this.defaultFileServer()&&(t.fileServer=this.fileServer),this.downloadServer!==this.defaultDownloadServer()&&(t.downloadServer=this.downloadServer),this._credentials=t},r}(),e=t.Client,t.AuthDriver=function(){function t(){}return t.prototype.url=function(){return"https://some.url"},t.prototype.doAuthorize=function(t,e,r,n){return n("oauth-token")},t.prototype.onAuthStateChange=function(t,e){return e()},t}(),t.Drivers={},t.Drivers.BrowserBase=function(){function t(t){this.rememberUser=(null!=t?t.rememberUser:void 0)||!1,this.scope=(null!=t?t.scope:void 0)||"default"}return t.prototype.onAuthStateChange=function(t,r){var n,o=this;switch(this.setStorageKey(t),t.authState){case e.RESET:return(n=this.loadCredentials())?n.authState?(t.setCredentials(n),r()):this.rememberUser?(t.setCredentials(n),t.getUserInfo(function(e){return e&&(t.reset(),o.forgetCredentials()),r()})):(this.forgetCredentials(),r()):r();case e.REQUEST:return this.storeCredentials(t.credentials()),r();case e.DONE:return this.rememberUser?this.storeCredentials(t.credentials()):this.forgetCredentials(),r();case e.SIGNED_OFF:return this.forgetCredentials(),r();case e.ERROR:return this.forgetCredentials(),r();default:return r()}},t.prototype.setStorageKey=function(t){return this.storageKey="dropbox-auth:"+this.scope+":"+t.appHash(),this},t.prototype.storeCredentials=function(t){return localStorage.setItem(this.storageKey,JSON.stringify(t))},t.prototype.loadCredentials=function(){var t;if(t=localStorage.getItem(this.storageKey),!t)return null;try{return JSON.parse(t)}catch(e){return null}},t.prototype.forgetCredentials=function(){return localStorage.removeItem(this.storageKey)},t.currentLocation=function(){return window.location.href},t}(),t.Drivers.Redirect=function(r){function n(t){n.__super__.constructor.call(this,t),this.useQuery=(null!=t?t.useQuery:void 0)||!1,this.receiverUrl=this.computeUrl(t),this.tokenRe=RegExp("(#|\\?|&)oauth_token=([^&#]+)(&|#|$)")}return E(n,r),n.prototype.onAuthStateChange=function(t,r){var o;return this.setStorageKey(t),t.authState===e.RESET&&(o=this.loadCredentials())&&o.authState&&(o.token===this.locationToken()&&o.authState===e.REQUEST?(o.authState=e.AUTHORIZED,this.storeCredentials(o)):this.forgetCredentials()),n.__super__.onAuthStateChange.call(this,t,r)},n.prototype.url=function(){return this.receiverUrl},n.prototype.doAuthorize=function(t){return window.location.assign(t)},n.prototype.computeUrl=function(){var e,r,n,o;return o="_dropboxjs_scope="+encodeURIComponent(this.scope),r=t.Drivers.BrowserBase.currentLocation(),-1===r.indexOf("#")?e=null:(n=r.split("#",2),r=n[0],e=n[1]),this.useQuery?r+=-1===r.indexOf("?")?"?"+o:"&"+o:e="?"+o,e?r+"#"+e:r},n.prototype.locationToken=function(){var e,r,n;return e=t.Drivers.BrowserBase.currentLocation(),n="_dropboxjs_scope="+encodeURIComponent(this.scope)+"&",-1===("function"==typeof e.indexOf?e.indexOf(n):void 0)?null:(r=this.tokenRe.exec(e),r?decodeURIComponent(r[2]):null)},n}(t.Drivers.BrowserBase),t.Drivers.Popup=function(r){function n(t){n.__super__.constructor.call(this,t),this.receiverUrl=this.computeUrl(t),this.tokenRe=RegExp("(#|\\?|&)oauth_token=([^&#]+)(&|#|$)")}return E(n,r),n.prototype.onAuthStateChange=function(t,r){var o;return this.setStorageKey(t),t.authState===e.RESET&&(o=this.loadCredentials())&&o.authState&&this.forgetCredentials(),n.__super__.onAuthStateChange.call(this,t,r)},n.prototype.doAuthorize=function(t,e,r,n){return this.listenForMessage(e,n),this.openWindow(t)},n.prototype.url=function(){return this.receiverUrl},n.prototype.computeUrl=function(e){var r;if(e){if(e.receiverUrl)return e.noFragment||-1!==e.receiverUrl.indexOf("#")?e.receiverUrl:e.receiverUrl+"#";if(e.receiverFile)return r=t.Drivers.BrowserBase.currentLocation().split("/"),r[r.length-1]=e.receiverFile,e.noFragment?r.join("/"):r.join("/")+"#"}return t.Drivers.BrowserBase.currentLocation()},n.prototype.openWindow=function(t){return window.open(t,"_dropboxOauthSigninWindow",this.popupWindowSpec(980,980))},n.prototype.popupWindowSpec=function(t,e){var r,n,o,i,s,a,h,u,l,p;return s=null!=(h=window.screenX)?h:window.screenLeft,a=null!=(u=window.screenY)?u:window.screenTop,i=null!=(l=window.outerWidth)?l:document.documentElement.clientWidth,r=null!=(p=window.outerHeight)?p:document.documentElement.clientHeight,n=Math.round(s+(i-t)/2),o=Math.round(a+(r-e)/2.5),"width="+t+",height="+e+","+("left="+n+",top="+o)+"dialog=yes,dependent=yes,scrollbars=yes,location=yes"},n.prototype.listenForMessage=function(t,e){var r,n;return n=this.tokenRe,r=function(o){var i;return i=n.exec(""+o.data),i&&decodeURIComponent(i[2])===t?(window.removeEventListener("message",r),e()):void 0},window.addEventListener("message",r,!1)},n}(t.Drivers.BrowserBase),t.Drivers.NodeServer=function(){function t(t){this.port=(null!=t?t.port:void 0)||8912,this.faviconFile=(null!=t?t.favicon:void 0)||null,this.fs=require("fs"),this.http=require("http"),this.open=require("open"),this.callbacks={},this.urlRe=RegExp("^/oauth_callback\\?"),this.tokenRe=RegExp("(\\?|&)oauth_token=([^&]+)(&|$)"),this.createApp()}return t.prototype.url=function(){return"http://localhost:"+this.port+"/oauth_callback"},t.prototype.doAuthorize=function(t,e,r,n){return this.callbacks[e]=n,this.openBrowser(t)},t.prototype.openBrowser=function(t){if(!t.match(/^https?:\/\//))throw Error("Not a http/https URL: "+t);return this.open(t)},t.prototype.createApp=function(){var t=this;return this.app=this.http.createServer(function(e,r){return t.doRequest(e,r)}),this.app.listen(this.port)},t.prototype.closeServer=function(){return this.app.close()},t.prototype.doRequest=function(t,e){var r,n,o,i=this;return this.urlRe.exec(t.url)&&(n=this.tokenRe.exec(t.url),n&&(o=decodeURIComponent(n[2]),this.callbacks[o]&&(this.callbacks[o](),delete this.callbacks[o]))),r="",t.on("data",function(t){return r+=t}),t.on("end",function(){return i.faviconFile&&"/favicon.ico"===t.url?i.sendFavicon(e):i.closeBrowser(e)})},t.prototype.closeBrowser=function(t){var e;return e='\n\n

    Please close this window.

    ',t.writeHead(200,{"Content-Length":e.length,"Content-Type":"text/html"}),t.write(e),t.end},t.prototype.sendFavicon=function(t){return this.fs.readFile(this.faviconFile,function(e,r){return t.writeHead(200,{"Content-Length":r.length,"Content-Type":"image/x-icon"}),t.write(r),t.end})},t}(),p=function(t,e){return a(m(S(t),S(e),t.length,e.length))},c=function(t){return a(w(S(t),t.length))},("undefined"==typeof window||null===window)&&(y=require("crypto"),p=function(t,e){var r;return r=y.createHmac("sha1",e),r.update(t),r.digest("base64")},c=function(t){var e;return e=y.createHash("sha1"),e.update(t),e.digest("base64")}),m=function(t,e,r,n){var o,i,s,a;return e.length>16&&(e=w(e,n)),s=function(){var t,r;for(r=[],i=t=0;16>t;i=++t)r.push(909522486^e[i]);return r}(),a=function(){var t,r;for(r=[],i=t=0;16>t;i=++t)r.push(1549556828^e[i]);return r}(),o=w(s.concat(t),64+r),w(a.concat(o),84)},w=function(t,e){var r,n,o,i,a,h,u,l,p,c,d,f,y,v,m,w,S,b;for(t[e>>2]|=1<<31-((3&e)<<3),t[(e+8>>6<<4)+15]=e<<3,w=Array(80),r=1732584193,o=-271733879,a=-1732584194,u=271733878,p=-1009589776,f=0,m=t.length;m>f;){for(n=r,i=o,h=a,l=u,c=p,y=b=0;80>b;y=++b)w[y]=16>y?t[f+y]:g(w[y-3]^w[y-8]^w[y-14]^w[y-16],1),20>y?(d=o&a|~o&u,v=1518500249):40>y?(d=o^a^u,v=1859775393):60>y?(d=o&a|o&u|a&u,v=-1894007588):(d=o^a^u,v=-899497514),S=s(s(g(r,5),d),s(s(p,w[y]),v)),p=u,u=a,a=g(o,30),o=r,r=S;r=s(r,n),o=s(o,i),a=s(a,h),u=s(u,l),p=s(p,c),f+=16}return[r,o,a,u,p]},g=function(t,e){return t<>>32-e},s=function(t,e){var r,n;return n=(65535&t)+(65535&e),r=(t>>16)+(e>>16)+(n>>16),r<<16|65535&n},a=function(t){var e,r,n,o,i;for(o="",e=0,n=4*t.length;n>e;)r=e,i=(255&t[r>>2]>>(3-(3&r)<<3))<<16,r+=1,i|=(255&t[r>>2]>>(3-(3&r)<<3))<<8,r+=1,i|=255&t[r>>2]>>(3-(3&r)<<3),o+=b[63&i>>18],o+=b[63&i>>12],e+=1,o+=e>=n?"=":b[63&i>>6],e+=1,o+=e>=n?"=":b[63&i],e+=1;return o},b="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/",S=function(t){var e,r,n,o,i;for(e=[],n=255,r=o=0,i=t.length;i>=0?i>o:o>i;r=i>=0?++o:--o)e[r>>2]|=(t.charCodeAt(r)&n)<<(3-(3&r)<<3);return e},t.Oauth=function(){function e(t){this.key=this.k=null,this.secret=this.s=null,this.token=null,this.tokenSecret=null,this._appHash=null,this.reset(t)}return e.prototype.reset=function(t){var e,r,n,o;if(t.secret)this.k=this.key=t.key,this.s=this.secret=t.secret,this._appHash=null;else if(t.key)this.key=t.key,this.secret=null,n=h(v(this.key).split("|",2)[1]),o=n.split("?",2),e=o[0],r=o[1],this.k=decodeURIComponent(e),this.s=decodeURIComponent(r),this._appHash=null;else if(!this.k)throw Error("No API key supplied");return t.token?this.setToken(t.token,t.tokenSecret):this.setToken(null,"")},e.prototype.setToken=function(e,r){if(e&&!r)throw Error("No secret supplied with the user token");return this.token=e,this.tokenSecret=r||"",this.hmacKey=t.Xhr.urlEncodeValue(this.s)+"&"+t.Xhr.urlEncodeValue(r),null},e.prototype.authHeader=function(e,r,n){var o,i,s,a,h,u;this.addAuthParams(e,r,n),i=[];for(s in n)a=n[s],"oauth_"===s.substring(0,6)&&i.push(s);for(i.sort(),o=[],h=0,u=i.length;u>h;h++)s=i[h],o.push(t.Xhr.urlEncodeValue(s)+'="'+t.Xhr.urlEncodeValue(n[s])+'"'),delete n[s];return"OAuth "+o.join(",")},e.prototype.addAuthParams=function(t,e,r){return this.boilerplateParams(r),r.oauth_signature=this.signature(t,e,r),r},e.prototype.boilerplateParams=function(t){return t.oauth_consumer_key=this.k,t.oauth_nonce=this.nonce(),t.oauth_signature_method="HMAC-SHA1",this.token&&(t.oauth_token=this.token),t.oauth_timestamp=Math.floor(Date.now()/1e3),t.oauth_version="1.0",t},e.prototype.nonce=function(){return Date.now().toString(36)+Math.random().toString(36)},e.prototype.signature=function(e,r,n){var o;return o=e.toUpperCase()+"&"+t.Xhr.urlEncodeValue(r)+"&"+t.Xhr.urlEncodeValue(t.Xhr.urlEncode(n)),p(o,this.hmacKey)},e.prototype.appHash=function(){return this._appHash?this._appHash:this._appHash=c(this.k).replace(/\=/g,"")},e}(),null==Date.now&&(Date.now=function(){return(new Date).getTime()}),v=function(t,e){var r,n,o,i,s,a,u,l,p,c,f,y;for(e?(e=[encodeURIComponent(t),encodeURIComponent(e)].join("?"),t=function(){var e,n,o;for(o=[],r=e=0,n=t.length/2;n>=0?n>e:e>n;r=n>=0?++e:--e)o.push(16*(15&t.charCodeAt(2*r))+(15&t.charCodeAt(2*r+1)));return o}()):(c=t.split("|",2),t=c[0],e=c[1],t=h(t),t=function(){var e,n,o;for(o=[],r=e=0,n=t.length;n>=0?n>e:e>n;r=n>=0?++e:--e)o.push(t.charCodeAt(r));return o}(),e=h(e)),i=function(){for(y=[],l=0;256>l;l++)y.push(l);return y}.apply(this),a=0,s=p=0;256>p;s=++p)a=(a+i[r]+t[s%t.length])%256,f=[i[a],i[s]],i[s]=f[0],i[a]=f[1];return s=a=0,o=function(){var t,r,o,h;for(h=[],u=t=0,r=e.length;r>=0?r>t:t>r;u=r>=0?++t:--t)s=(s+1)%256,a=(a+i[s])%256,o=[i[a],i[s]],i[s]=o[0],i[a]=o[1],n=i[(i[s]+i[a])%256],h.push(String.fromCharCode((n^e.charCodeAt(u))%256));return h}(),t=function(){var e,n,o;for(o=[],r=e=0,n=t.length;n>=0?n>e:e>n;r=n>=0?++e:--e)o.push(String.fromCharCode(t[r]));return o}(),[d(t.join("")),d(o.join(""))].join("|")},t.PulledChanges=function(){function e(e){var r;this.blankSlate=e.reset||!1,this.cursorTag=e.cursor,this.shouldPullAgain=e.has_more,this.shouldBackOff=!this.shouldPullAgain,this.changes=e.cursor&&e.cursor.length?function(){var n,o,i,s;for(i=e.entries,s=[],n=0,o=i.length;o>n;n++)r=i[n],s.push(t.PullChange.parse(r));return s}():[]}return e.parse=function(e){return e&&"object"==typeof e?new t.PulledChanges(e):e},e.prototype.blankSlate=void 0,e.prototype.cursorTag=void 0,e.prototype.changes=void 0,e.prototype.shouldPullAgain=void 0,e.prototype.shouldBackOff=void 0,e}(),t.PullChange=function(){function e(e){this.path=e[0],this.stat=t.Stat.parse(e[1]),this.stat?this.wasRemoved=!1:(this.stat=null,this.wasRemoved=!0)}return e.parse=function(e){return e&&"object"==typeof e?new t.PullChange(e):e},e.prototype.path=void 0,e.prototype.wasRemoved=void 0,e.prototype.stat=void 0,e}(),t.PublicUrl=function(){function e(t,e){this.url=t.url,this.expiresAt=new Date(Date.parse(t.expires)),this.isDirect=e===!0?!0:e===!1?!1:864e5>=Date.now()-this.expiresAt,this.isPreview=!this.isDirect}return e.parse=function(e,r){return e&&"object"==typeof e?new t.PublicUrl(e,r):e},e.prototype.url=void 0,e.prototype.expiresAt=void 0,e.prototype.isDirect=void 0,e.prototype.isPreview=void 0,e}(),t.CopyReference=function(){function e(t){"object"==typeof t?(this.tag=t.copy_ref,this.expiresAt=new Date(Date.parse(t.expires))):(this.tag=t,this.expiresAt=new Date)}return e.parse=function(e){return!e||"object"!=typeof e&&"string"!=typeof e?e:new t.CopyReference(e)},e.prototype.tag=void 0,e.prototype.expiresAt=void 0,e}(),t.Stat=function(){function e(t){var e,r,n,o;switch(this.path=t.path,"/"!==this.path.substring(0,1)&&(this.path="/"+this.path),e=this.path.length-1,e>=0&&"/"===this.path.substring(e)&&(this.path=this.path.substring(0,e)),r=this.path.lastIndexOf("/"),this.name=this.path.substring(r+1),this.isFolder=t.is_dir||!1,this.isFile=!this.isFolder,this.isRemoved=t.is_deleted||!1,this.typeIcon=t.icon,this.modifiedAt=(null!=(n=t.modified)?n.length:void 0)?new Date(Date.parse(t.modified)):null,this.clientModifiedAt=(null!=(o=t.client_mtime)?o.length:void 0)?new Date(Date.parse(t.client_mtime)):null,t.root){case"dropbox":this.inAppFolder=!1;break;case"app_folder":this.inAppFolder=!0;break;default:this.inAppFolder=null}this.size=t.bytes||0,this.humanSize=t.size||"",this.hasThumbnail=t.thumb_exists||!1,this.isFolder?(this.versionTag=t.hash,this.mimeType=t.mime_type||"inode/directory"):(this.versionTag=t.rev,this.mimeType=t.mime_type||"application/octet-stream")}return e.parse=function(e){return e&&"object"==typeof e?new t.Stat(e):e},e.prototype.path=null,e.prototype.name=null,e.prototype.inAppFolder=null,e.prototype.isFolder=null,e.prototype.isFile=null,e.prototype.isRemoved=null,e.prototype.typeIcon=null,e.prototype.versionTag=null,e.prototype.mimeType=null,e.prototype.size=null,e.prototype.humanSize=null,e.prototype.hasThumbnail=null,e.prototype.modifiedAt=null,e.prototype.clientModifiedAt=null,e}(),t.UserInfo=function(){function e(t){var e;this.name=t.display_name,this.email=t.email,this.countryCode=t.country||null,this.uid=""+t.uid,t.public_app_url?(this.publicAppUrl=t.public_app_url,e=this.publicAppUrl.length-1,e>=0&&"/"===this.publicAppUrl.substring(e)&&(this.publicAppUrl=this.publicAppUrl.substring(0,e))):this.publicAppUrl=null,this.referralUrl=t.referral_link,this.quota=t.quota_info.quota,this.privateBytes=t.quota_info.normal||0,this.sharedBytes=t.quota_info.shared||0,this.usedQuota=this.privateBytes+this.sharedBytes}return e.parse=function(e){return e&&"object"==typeof e?new t.UserInfo(e):e},e.prototype.name=null,e.prototype.email=null,e.prototype.countryCode=null,e.prototype.uid=null,e.prototype.referralUrl=null,e.prototype.publicAppUrl=null,e.prototype.quota=null,e.prototype.usedQuota=null,e.prototype.privateBytes=null,e.prototype.sharedBytes=null,e}(),"undefined"!=typeof window&&null!==window?(!window.XDomainRequest||"withCredentials"in new XMLHttpRequest?(i=window.XMLHttpRequest,o=!1,r=-1===window.navigator.userAgent.indexOf("Firefox")):(i=window.XDomainRequest,o=!0,r=!1),n=!0):(i=require("xmlhttprequest").XMLHttpRequest,o=!1,r=!1,n=!1),t.Xhr=function(){function e(t,e){this.method=t,this.isGet="GET"===this.method,this.url=e,this.headers={},this.params=null,this.body=null,this.preflight=!(this.isGet||"POST"===this.method),this.signed=!1,this.responseType=null,this.callback=null,this.xhr=null}return e.Request=i,e.ieMode=o,e.canSendForms=r,e.doesPreflight=n,e.prototype.setParams=function(t){if(this.signed)throw Error("setParams called after addOauthParams or addOauthHeader");if(this.params)throw Error("setParams cannot be called twice");return this.params=t,this},e.prototype.setCallback=function(t){return this.callback=t,this},e.prototype.signWithOauth=function(e){return t.Xhr.ieMode||t.Xhr.doesPreflight&&!this.preflight?this.addOauthParams(e):this.addOauthHeader(e)},e.prototype.addOauthParams=function(t){if(this.signed)throw Error("Request already has an OAuth signature");return this.params||(this.params={}),t.addAuthParams(this.method,this.url,this.params),this.signed=!0,this},e.prototype.addOauthHeader=function(t){if(this.signed)throw Error("Request already has an OAuth signature");return this.params||(this.params={}),this.signed=!0,this.setHeader("Authorization",t.authHeader(this.method,this.url,this.params))},e.prototype.setBody=function(t){if(this.isGet)throw Error("setBody cannot be called on GET requests");if(null!==this.body)throw Error("Request already has a body");return this.preflight||"undefined"!=typeof FormData&&null!==FormData&&t instanceof FormData||(this.preflight=!0),this.body=t,this},e.prototype.setResponseType=function(t){return this.responseType=t,this},e.prototype.setHeader=function(t,e){var r;if(this.headers[t])throw r=this.headers[t],Error("HTTP header "+t+" already set to "+r);if("Content-Type"===t)throw Error("Content-Type is automatically computed based on setBody");return this.preflight=!0,this.headers[t]=e,this},e.prototype.setFileField=function(t,e,r,n){var o,i;if(null!==this.body)throw Error("Request already has a body"); +if(this.isGet)throw Error("paramsToBody cannot be called on GET requests");return i="object"==typeof r&&("undefined"!=typeof Blob&&null!==Blob&&r instanceof Blob||"undefined"!=typeof File&&null!==File&&r instanceof File),i?(this.body=new FormData,this.body.append(t,r,e)):(n||(n="application/octet-stream"),o=this.multipartBoundary(),this.headers["Content-Type"]="multipart/form-data; boundary="+o,this.body=["--",o,"\r\n",'Content-Disposition: form-data; name="',t,'"; filename="',e,'"\r\n',"Content-Type: ",n,"\r\n","Content-Transfer-Encoding: binary\r\n\r\n",r,"\r\n","--",o,"--","\r\n"].join(""))},e.prototype.multipartBoundary=function(){return[Date.now().toString(36),Math.random().toString(36)].join("----")},e.prototype.paramsToUrl=function(){var e;return this.params&&(e=t.Xhr.urlEncode(this.params),0!==e.length&&(this.url=[this.url,"?",e].join("")),this.params=null),this},e.prototype.paramsToBody=function(){if(this.params){if(null!==this.body)throw Error("Request already has a body");if(this.isGet)throw Error("paramsToBody cannot be called on GET requests");this.headers["Content-Type"]="application/x-www-form-urlencoded",this.body=t.Xhr.urlEncode(this.params),this.params=null}return this},e.prototype.prepare=function(){var e,r,n,o,i=this;if(r=t.Xhr.ieMode,this.isGet||null!==this.body||r?(this.paramsToUrl(),null!==this.body&&"string"==typeof this.body&&(this.headers["Content-Type"]="text/plain; charset=utf8")):this.paramsToBody(),this.xhr=new t.Xhr.Request,r?(this.xhr.onload=function(){return i.onLoad()},this.xhr.onerror=function(){return i.onError()}):this.xhr.onreadystatechange=function(){return i.onReadyStateChange()},this.xhr.open(this.method,this.url,!0),!r){o=this.headers;for(e in o)T.call(o,e)&&(n=o[e],this.xhr.setRequestHeader(e,n))}return this.responseType&&("b"===this.responseType?this.xhr.overrideMimeType&&this.xhr.overrideMimeType("text/plain; charset=x-user-defined"):this.xhr.responseType=this.responseType),this},e.prototype.send=function(t){return this.callback=t||this.callback,null!==this.body?this.xhr.send(this.body):this.xhr.send(),this},e.urlEncode=function(t){var e,r,n;e=[];for(r in t)n=t[r],e.push(this.urlEncodeValue(r)+"="+this.urlEncodeValue(n));return e.sort().join("&")},e.urlEncodeValue=function(t){return encodeURIComponent(""+t).replace(/\!/g,"%21").replace(/'/g,"%27").replace(/\(/g,"%28").replace(/\)/g,"%29").replace(/\*/g,"%2A")},e.urlDecode=function(t){var e,r,n,o,i,s;for(r={},s=t.split("&"),o=0,i=s.length;i>o;o++)n=s[o],e=n.split("="),r[decodeURIComponent(e[0])]=decodeURIComponent(e[1]);return r},e.prototype.onReadyStateChange=function(){var e,r,n,o,i,s,a,h,u;if(4!==this.xhr.readyState)return!0;if(200>this.xhr.status||this.xhr.status>=300)return e=new t.ApiError(this.xhr,this.method,this.url),this.callback(e),!0;if(s=this.xhr.getResponseHeader("x-dropbox-metadata"),null!=s?s.length:void 0)try{i=JSON.parse(s)}catch(l){i=void 0}else i=void 0;if(this.responseType){if("b"===this.responseType){for(n=null!=this.xhr.responseText?this.xhr.responseText:this.xhr.response,r=[],o=h=0,u=n.length;u>=0?u>h:h>u;o=u>=0?++h:--h)r.push(String.fromCharCode(255&n.charCodeAt(o)));a=r.join(""),this.callback(null,a,i)}else this.callback(null,this.xhr.response,i);return!0}switch(a=null!=this.xhr.responseText?this.xhr.responseText:this.xhr.response,this.xhr.getResponseHeader("Content-Type")){case"application/x-www-form-urlencoded":this.callback(null,t.Xhr.urlDecode(a),i);break;case"application/json":case"text/javascript":this.callback(null,JSON.parse(a),i);break;default:this.callback(null,a,i)}return!0},e.prototype.onLoad=function(){var e;switch(e=this.xhr.responseText,this.xhr.contentType){case"application/x-www-form-urlencoded":this.callback(null,t.Xhr.urlDecode(e),void 0);break;case"application/json":case"text/javascript":this.callback(null,JSON.parse(e),void 0);break;default:this.callback(null,e,void 0)}return!0},e.prototype.onError=function(){var e;return e=new t.ApiError(this.xhr,this.method,this.url),this.callback(e),!0},e}(),null!=("undefined"!=typeof module&&null!==module?module.exports:void 0))module.exports=t;else{if("undefined"==typeof window||null===window)throw Error("This library only supports node.js and modern browsers.");window.Dropbox=t}t.atob=h,t.btoa=d,t.hmac=p,t.sha1=c,t.encodeKey=v}).call(this); \ No newline at end of file diff --git a/lib/client/storage/dropbox/package.json b/lib/client/storage/dropbox/package.json new file mode 100644 index 00000000..dfa72764 --- /dev/null +++ b/lib/client/storage/dropbox/package.json @@ -0,0 +1,46 @@ +{ + "name": "dropbox", + "version": "0.7.2", + "description": "Client library for the Dropbox API", + "keywords": ["dropbox", "filesystem", "storage"], + "homepage": "http://github.com/dropbox/dropbox-js", + "author": "Victor Costan (http://www.costan.us)", + "license": "MIT", + "contributors": [ + "Aakanksha Sarda " + ], + "repository": { + "type": "git", + "url": "https://github.com/dropbox/dropbox-js.git" + }, + "engines": { + "node": ">= 0.6" + }, + "dependencies": { + "open": ">= 0.0.2", + "xmlhttprequest": ">= 1.5.0" + }, + "devDependencies": { + "async": ">= 0.1.22", + "chai": ">= 1.4.0", + "codo": ">= 1.5.2", + "coffee-script": ">= 1.4.0", + "express": ">= 3.0.4", + "mocha": ">= 1.7.4", + "remove": ">= 0.1.5", + "sinon": ">= 1.5.2", + "sinon-chai": ">= 2.2.0", + "uglify-js": ">= 2.2.2" + }, + "main": "lib/dropbox.js", + "directories": { + "doc": "doc", + "lib": "lib", + "src": "src", + "test": "test" + }, + "scripts": { + "prepublish": "cake build", + "test": "cake test" + } +} diff --git a/lib/client/storage/dropbox/src/000-dropbox.coffee b/lib/client/storage/dropbox/src/000-dropbox.coffee new file mode 100644 index 00000000..183d00c2 --- /dev/null +++ b/lib/client/storage/dropbox/src/000-dropbox.coffee @@ -0,0 +1,6 @@ +# Main entry point to the Dropbox API. +class Dropbox + constructor: (options) -> + @client = new DropboxClient options + + # NOTE: this is not yet implemented. diff --git a/lib/client/storage/dropbox/src/api_error.coffee b/lib/client/storage/dropbox/src/api_error.coffee new file mode 100644 index 00000000..131fb9ec --- /dev/null +++ b/lib/client/storage/dropbox/src/api_error.coffee @@ -0,0 +1,49 @@ +# Information about a failed call to the Dropbox API. +class Dropbox.ApiError + # @property {Number} the HTTP error code (e.g., 403) + status: undefined + + # @property {String} the HTTP method of the failed request (e.g., 'GET') + method: undefined + + # @property {String} the URL of the failed request + url: undefined + + # @property {?String} the body of the HTTP error response; can be null if + # the error was caused by a network failure or by a security issue + responseText: undefined + + # @property {?Object} the result of parsing the JSON in the HTTP error + # response; can be null if the API server didn't return JSON, or if the + # HTTP response body is unavailable + response: undefined + + # Wraps a failed XHR call to the Dropbox API. + # + # @param {String} method the HTTP verb of the API request (e.g., 'GET') + # @param {String} url the URL of the API request + # @param {XMLHttpRequest} xhr the XMLHttpRequest instance of the failed + # request + constructor: (xhr, @method, @url) -> + @status = xhr.status + if xhr.responseType + text = xhr.response or xhr.responseText + else + text = xhr.responseText + if text + try + @responseText = text.toString() + @response = JSON.parse text + catch e + @response = null + else + @responseText = '(no response)' + @response = null + + # Used when the error is printed out by developers. + toString: -> + "Dropbox API error #{@status} from #{@method} #{@url} :: #{@responseText}" + + # Used by some testing frameworks. + inspect: -> + @toString() diff --git a/lib/client/storage/dropbox/src/base64.coffee b/lib/client/storage/dropbox/src/base64.coffee new file mode 100644 index 00000000..fa165d3a --- /dev/null +++ b/lib/client/storage/dropbox/src/base64.coffee @@ -0,0 +1,72 @@ +# node.js implementation of atob and btoa + +if window? + if window.atob and window.btoa + atob = (string) -> window.atob string + btoa = (base64) -> window.btoa base64 + else + # IE < 10 doesn't implement the standard atob / btoa functions. + base64Digits = + 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/' + + btoaNibble = (accumulator, bytes, result) -> + limit = 3 - bytes + accumulator <<= limit * 8 + i = 3 + while i >= limit + result.push base64Digits.charAt((accumulator >> (i * 6)) & 0x3F) + i -= 1 + i = bytes + while i < 3 + result.push '=' + i += 1 + null + atobNibble = (accumulator, digits, result) -> + limit = 4 - digits + accumulator <<= limit * 6 + i = 2 + while i >= limit + result.push String.fromCharCode((accumulator >> (8 * i)) & 0xFF) + i -= 1 + null + + btoa = (string) -> + result = [] + accumulator = 0 + bytes = 0 + for i in [0...string.length] + accumulator = (accumulator << 8) | string.charCodeAt(i) + bytes += 1 + if bytes is 3 + btoaNibble accumulator, bytes, result + accumulator = bytes = 0 + + if bytes > 0 + btoaNibble accumulator, bytes, result + result.join '' + + atob = (base64) -> + result = [] + accumulator = 0 + digits = 0 + for i in [0...base64.length] + digit = base64.charAt i + break if digit is '=' + accumulator = (accumulator << 6) | base64Digits.indexOf(digit) + digits += 1 + if digits is 4 + atobNibble accumulator, digits, result + accumulator = digits = 0 + + if digits > 0 + atobNibble accumulator, digits, result + result.join '' + +else + # NOTE: the npm packages atob and btoa don't do base64-encoding correctly. + atob = (arg) -> + buffer = new Buffer arg, 'base64' + (String.fromCharCode(buffer[i]) for i in [0...buffer.length]).join '' + btoa = (arg) -> + buffer = new Buffer(arg.charCodeAt(i) for i in [0...arg.length]) + buffer.toString 'base64' diff --git a/lib/client/storage/dropbox/src/client.coffee b/lib/client/storage/dropbox/src/client.coffee new file mode 100644 index 00000000..c465e4bc --- /dev/null +++ b/lib/client/storage/dropbox/src/client.coffee @@ -0,0 +1,1104 @@ +# Represents a user accessing the application. +class Dropbox.Client + # Dropbox client representing an application. + # + # For an optimal user experience, applications should use a single client for + # all Dropbox interactions. + # + # @param {Object} options the application type and API key + # @option options {Boolean} sandbox true for applications that request + # sandbox access (access to a single folder exclusive to the app) + # @option options {String} key the Dropbox application's key; browser-side + # applications should use Dropbox.encodeKey to obtain an encoded + # key string, and pass it as the key option + # @option options {String} secret the Dropbox application's secret; + # browser-side applications should not use the secret option; instead, + # they should pass the result of Dropbox.encodeKey as the key option + # @option options {String} token if set, the user's access token + # @option options {String} tokenSecret if set, the secret for the user's + # access token + # @option options {String} uid if set, the user's Dropbox UID + # @option options {Number} authState if set, indicates that the token and + # tokenSecret are in an intermediate state in the authentication process; + # this option should never be set by hand, however it may be returned by + # calls to credentials() + constructor: (options) -> + @sandbox = options.sandbox or false + @apiServer = options.server or @defaultApiServer() + @authServer = options.authServer or @defaultAuthServer() + @fileServer = options.fileServer or @defaultFileServer() + @downloadServer = options.downloadServer or @defaultDownloadServer() + + @oauth = new Dropbox.Oauth options + @driver = null + @filter = null + @uid = null + @authState = null + @authError = null + @_credentials = null + @setCredentials options + + @setupUrls() + + # Plugs in the OAuth / application integration code. + # + # @param {DropboxAuthDriver} driver provides the integration between the + # application and the Dropbox OAuth flow; most applications should be + # able to use instances of Dropbox.Driver.Redirect, Dropbox.Driver.Popup, + # or Dropbox.Driver.NodeServer + # @return {Dropbox.Client} this, for easy call chaining + authDriver: (driver) -> + @driver = driver + @ + + # Plugs in a filter for all XMLHttpRequests issued by this client. + # + # Whenever possible, filter implementations should only use the native + # XMHttpRequest object received as the first argument. The Dropbox.Xhr API + # implemented by the second argument is not yet stabilized. + # + # @param {function(XMLHttpRequest, Dropbox.Xhr): boolean} filter called every + # time the client is about to send a network request; the filter can + # inspect and modify the XMLHttpRequest; if the filter returns a falsey + # value, the XMLHttpRequest will not be sent + # @return {Drobpox.Client} this, for easy call chaining + xhrFilter: (filter) -> + @filter = filter + @ + + # The authenticated user's Dropbx user ID. + # + # This user ID is guaranteed to be consistent across API calls from the same + # application (not across applications, though). + # + # @return {?String} a short ID that identifies the user, or null if no user + # is authenticated + dropboxUid: -> + @uid + + # Get the client's OAuth credentials. + # + # @param {?Object} the result of a prior call to credentials() + # @return {Object} a plain object whose properties can be passed to the + # Dropbox.Client constructor to reuse this client's login credentials + credentials: () -> + @computeCredentials() unless @_credentials + @_credentials + + # Authenticates the app's user to Dropbox' API server. + # + # @param {function(?Dropbox.ApiError, ?Dropbox.Client)} callback called when + # the authentication completes; if successful, the second parameter is + # this client and the first parameter is null + # @return {Dropbox.Client} this, for easy call chaining + authenticate: (callback) -> + oldAuthState = null + + # Advances the authentication FSM by one step. + _fsmStep = => + if oldAuthState isnt @authState + oldAuthState = @authState + if @driver.onAuthStateChange + return @driver.onAuthStateChange(@, _fsmStep) + + switch @authState + when DropboxClient.RESET # No user credentials -> request token. + @requestToken (error, data) => + if error + @authError = error + @authState = DropboxClient.ERROR + else + token = data.oauth_token + tokenSecret = data.oauth_token_secret + @oauth.setToken token, tokenSecret + @authState = DropboxClient.REQUEST + @_credentials = null + _fsmStep() + + when DropboxClient.REQUEST # Have request token, get it authorized. + authUrl = @authorizeUrl @oauth.token + @driver.doAuthorize authUrl, @oauth.token, @oauth.tokenSecret, => + @authState = DropboxClient.AUTHORIZED + @_credentials = null + _fsmStep() + + when DropboxClient.AUTHORIZED + # Request token authorized, switch it for an access token. + @getAccessToken (error, data) => + if error + @authError = error + @authState = DropboxClient.ERROR + else + @oauth.setToken data.oauth_token, data.oauth_token_secret + @uid = data.uid + @authState = DropboxClient.DONE + @_credentials = null + _fsmStep() + + when DropboxClient.DONE # We have an access token. + callback null, @ + + when Dropbox.SIGNED_OFF # The user signed off, restart the flow. + @reset() + _fsmStep() + + when DropboxClient.ERROR # An error occurred during authentication. + callback @authError + + _fsmStep() # Start up the state machine. + @ + + # Revokes the user's Dropbox credentials. + # + # This should be called when the user explictly signs off from your + # application, to meet the users' expectation that after they sign off, their + # access tokens will be persisted on the machine. + # + # @param {function(?Dropbox.ApiError)} callback called when + # the authentication completes; if successful, the error parameter is + # null + # @return {XMLHttpRequest} the XHR object used for this API call + signOut: (callback) -> + xhr = new Dropbox.Xhr 'POST', @urls.signOut + xhr.signWithOauth @oauth + @dispatchXhr xhr, (error) => + return callback(error) if error + + @reset() + @authState = DropboxClient.SIGNED_OFF + if @driver.onAuthStateChange + @driver.onAuthStateChange @, -> + callback error + else + callback error + + # Alias for signOut. + signOff: (callback) -> + @signOut callback + + # Retrieves information about the logged in user. + # + # @param {function(?Dropbox.ApiError, ?Dropbox.UserInfo, ?Object)} callback + # called with the result of the /account/info HTTP request; if the call + # succeeds, the second parameter is a Dropbox.UserInfo instance, the + # third parameter is the parsed JSON data behind the Dropbox.UserInfo + # instance, and the first parameter is null + # @return {XMLHttpRequest} the XHR object used for this API call + getUserInfo: (callback) -> + xhr = new Dropbox.Xhr 'GET', @urls.accountInfo + xhr.signWithOauth @oauth + @dispatchXhr xhr, (error, userData) -> + callback error, Dropbox.UserInfo.parse(userData), userData + + # Retrieves the contents of a file stored in Dropbox. + # + # Some options are silently ignored in Internet Explorer 9 and below, due to + # insufficient support in its proprietary XDomainRequest replacement for XHR. + # Currently, the options are: arrayBuffer, blob, length, start. + # + # @param {String} path the path of the file to be read, relative to the + # user's Dropbox or to the application's folder + # @param {?Object} options the advanced settings below; for the default + # settings, skip the argument or pass null + # @option options {String} versionTag the tag string for the desired version + # of the file contents; the most recent version is retrieved by default + # @option options {String} rev alias for "versionTag" that matches the HTTP + # API + # @option options {Boolean} arrayBuffer if true, the file's contents will be + # passed to the callback in an ArrayBuffer; this is a good method of + # reading non-UTF8 data, such as images; requires XHR Level 2 support, + # which is not available in IE <= 9 + # @option options {Boolean} blob if true, the file's contents will be + # passed to the callback in a Blob; this is a good method of reading + # non-UTF8 data, such as images; requires XHR Level 2 support, which is not + # available in IE <= 9 + # @option options {Boolean} binary if true, the file will be retrieved as a + # binary string; the default is an UTF-8 encoded string; this relies on + # browser hacks and should not be used if the environment supports the Blob + # API + # @option options {Number} length the number of bytes to be retrieved from + # the file; if the start option is not present, the last "length" bytes + # will be read (after issue #30 is closed); by default, the entire file is + # read + # @option options {Number} start the 0-based offset of the first byte to be + # retrieved; if the length option is not present, the bytes between + # "start" and the file's end will be read; by default, the entire + # file is read + # @param {function(?Dropbox.ApiError, ?String, ?Dropbox.Stat)} callback + # called with the result of the /files (GET) HTTP request; the second + # parameter is the contents of the file, the third parameter is a + # Dropbox.Stat instance describing the file, and the first parameter is + # null + # @return {XMLHttpRequest} the XHR object used for this API call + readFile: (path, options, callback) -> + if (not callback) and (typeof options is 'function') + callback = options + options = null + + params = {} + responseType = null + rangeHeader = null + if options + if options.versionTag + params.rev = options.versionTag + else if options.rev + params.rev = options.rev + + if options.arrayBuffer + responseType = 'arraybuffer' + else if options.blob + responseType = 'blob' + else if options.binary + responseType = 'b' # See the Dropbox.Xhr.request2 docs + + if options.length + if options.start? + rangeStart = options.start + rangeEnd = options.start + options.length - 1 + else + rangeStart = '' + rangeEnd = options.length + rangeHeader = "bytes=#{rangeStart}-#{rangeEnd}" + else if options.start? + rangeHeader = "bytes=#{options.start}-" + + xhr = new Dropbox.Xhr 'GET', "#{@urls.getFile}/#{@urlEncodePath(path)}" + xhr.setParams(params).signWithOauth(@oauth).setResponseType(responseType) + xhr.setHeader 'Range', rangeHeader if rangeHeader + @dispatchXhr xhr, (error, data, metadata) -> + callback error, data, Dropbox.Stat.parse(metadata) + + # Store a file into a user's Dropbox. + # + # @param {String} path the path of the file to be created, relative to the + # user's Dropbox or to the application's folder + # @param {String, ArrayBuffer, ArrayBufferView, Blob, File} data the contents + # written to the file; if a File is passed, its name is ignored + # @param {?Object} options the advanced settings below; for the default + # settings, skip the argument or pass null + # @option options {String} lastVersionTag the identifier string for the + # version of the file's contents that was last read by this program, used + # for conflict resolution; for best results, use the versionTag attribute + # value from the Dropbox.Stat instance provided by readFile + # @option options {String} parentRev alias for "lastVersionTag" that matches + # the HTTP API + # @option options {Boolean} noOverwrite if set, the write will not overwrite + # a file with the same name that already exsits; instead the contents + # will be written to a similarly named file (e.g. "notes (1).txt" + # instead of "notes.txt") + # @param {function(?Dropbox.ApiError, ?Dropbox.Stat)} callback called with + # the result of the /files (POST) HTTP request; the second paramter is a + # Dropbox.Stat instance describing the newly created file, and the first + # parameter is null + # @return {XMLHttpRequest} the XHR object used for this API call + writeFile: (path, data, options, callback) -> + if (not callback) and (typeof options is 'function') + callback = options + options = null + + useForm = Dropbox.Xhr.canSendForms and typeof data is 'object' + if useForm + @writeFileUsingForm path, data, options, callback + else + @writeFileUsingPut path, data, options, callback + + # writeFile implementation that uses the POST /files API. + # + # @private + # This method is more demanding in terms of CPU and browser support, but does + # not require CORS preflight, so it always completes in 1 HTTP request. + writeFileUsingForm: (path, data, options, callback) -> + # Break down the path into a file/folder name and the containing folder. + slashIndex = path.lastIndexOf '/' + if slashIndex is -1 + fileName = path + path = '' + else + fileName = path.substring slashIndex + path = path.substring 0, slashIndex + + params = { file: fileName } + if options + if options.noOverwrite + params.overwrite = 'false' + if options.lastVersionTag + params.parent_rev = options.lastVersionTag + else if options.parentRev or options.parent_rev + params.parent_rev = options.parentRev or options.parent_rev + # TODO: locale support would edit the params here + + xhr = new Dropbox.Xhr 'POST', "#{@urls.postFile}/#{@urlEncodePath(path)}" + xhr.setParams(params).signWithOauth(@oauth).setFileField('file', fileName, + data, 'application/octet-stream') + + # NOTE: the Dropbox API docs ask us to replace the 'file' parameter after + # signing the request; the hack below works as intended + delete params.file + + @dispatchXhr xhr, (error, metadata) -> + callback error, Dropbox.Stat.parse(metadata) + + # writeFile implementation that uses the /files_put API. + # + # @private + # This method is less demanding on CPU, and makes fewer assumptions about + # browser support, but it takes 2 HTTP requests for binary files, because it + # needs CORS preflight. + writeFileUsingPut: (path, data, options, callback) -> + params = {} + if options + if options.noOverwrite + params.overwrite = 'false' + if options.lastVersionTag + params.parent_rev = options.lastVersionTag + else if options.parentRev or options.parent_rev + params.parent_rev = options.parentRev or options.parent_rev + # TODO: locale support would edit the params here + xhr = new Dropbox.Xhr 'POST', "#{@urls.putFile}/#{@urlEncodePath(path)}" + xhr.setBody(data).setParams(params).signWithOauth(@oauth) + @dispatchXhr xhr, (error, metadata) -> + callback error, Dropbox.Stat.parse(metadata) + + # Reads the metadata of a file or folder in a user's Dropbox. + # + # @param {String} path the path to the file or folder whose metadata will be + # read, relative to the user's Dropbox or to the application's folder + # @param {?Object} options the advanced settings below; for the default + # settings, skip the argument or pass null + # @option options {Number} version if set, the call will return the metadata + # for the given revision of the file / folder; the latest version is used + # by default + # @option options {Boolean} removed if set to true, the results will include + # files and folders that were deleted from the user's Dropbox + # @option options {Boolean} deleted alias for "removed" that matches the HTTP + # API; using this alias is not recommended, because it may cause confusion + # with JavaScript's delete operation + # @option options {Boolean, Number} readDir only meaningful when stat-ing + # folders; if this is set, the API call will also retrieve the folder's + # contents, which is passed into the callback's third parameter; if this + # is a number, it specifies the maximum number of files and folders that + # should be returned; the default limit is 10,000 items; if the limit is + # exceeded, the call will fail with an error + # @option options {String} versionTag used for saving bandwidth when getting + # a folder's contents; if this value is specified and it matches the + # folder's contents, the call will fail with a 304 (Contents not changed) + # error code; a folder's version identifier can be obtained from the + # versionTag attribute of a Dropbox.Stat instance describing it + # @param {function(?Dropbox.ApiError, ?Dropbox.Stat, ?Array)} + # callback called with the result of the /metadata HTTP request; if the + # call succeeds, the second parameter is a Dropbox.Stat instance + # describing the file / folder, and the first parameter is null; + # if the readDir option is true and the call succeeds, the third + # parameter is an array of Dropbox.Stat instances describing the folder's + # entries + # @return {XMLHttpRequest} the XHR object used for this API call + stat: (path, options, callback) -> + if (not callback) and (typeof options is 'function') + callback = options + options = null + + params = {} + if options + if options.version? + params.rev = options.version + if options.removed or options.deleted + params.include_deleted = 'true' + if options.readDir + params.list = 'true' + if options.readDir isnt true + params.file_limit = options.readDir.toString() + if options.cacheHash + params.hash = options.cacheHash + params.include_deleted ||= 'false' + params.list ||= 'false' + # TODO: locale support would edit the params here + xhr = new Dropbox.Xhr 'GET', "#{@urls.metadata}/#{@urlEncodePath(path)}" + xhr.setParams(params).signWithOauth @oauth + @dispatchXhr xhr, (error, metadata) -> + stat = Dropbox.Stat.parse metadata + if metadata?.contents + entries = (Dropbox.Stat.parse(entry) for entry in metadata.contents) + else + entries = undefined + callback error, stat, entries + + # Lists the files and folders inside a folder in a user's Dropbox. + # + # @param {String} path the path to the folder whose contents will be + # retrieved, relative to the user's Dropbox or to the application's + # folder + # @param {?Object} options the advanced settings below; for the default + # settings, skip the argument or pass null + # @option options {Boolean} removed if set to true, the results will include + # files and folders that were deleted from the user's Dropbox + # @option options {Boolean} deleted alias for "removed" that matches the HTTP + # API; using this alias is not recommended, because it may cause confusion + # with JavaScript's delete operation + # @option options {Boolean, Number} limit the maximum number of files and + # folders that should be returned; the default limit is 10,000 items; if + # the limit is exceeded, the call will fail with an error + # @option options {String} versionTag used for saving bandwidth; if this + # option is specified, and its value matches the folder's version tag, + # the call will fail with a 304 (Contents not changed) error code + # instead of returning the contents; a folder's version identifier can be + # obtained from the versionTag attribute of a Dropbox.Stat instance + # describing it + # @param {function(?Dropbox.ApiError, ?Array, ?Dropbox.Stat, + # ?Array)} callback called with the result of the /metadata + # HTTP request; if the call succeeds, the second parameter is an array + # containing the names of the files and folders in the given folder, the + # third parameter is a Dropbox.Stat instance describing the folder, the + # fourth parameter is an array of Dropbox.Stat instances describing the + # folder's entries, and the first parameter is null + # @return {XMLHttpRequest} the XHR object used for this API call + readdir: (path, options, callback) -> + if (not callback) and (typeof options is 'function') + callback = options + options = null + + statOptions = { readDir: true } + if options + if options.limit? + statOptions.readDir = options.limit + if options.versionTag + statOptions.versionTag = options.versionTag + @stat path, statOptions, (error, stat, entry_stats) -> + if entry_stats + entries = (entry_stat.name for entry_stat in entry_stats) + else + entries = null + callback error, entries, stat, entry_stats + + # Alias for "stat" that matches the HTTP API. + metadata: (path, options, callback) -> + @stat path, options, callback + + # Creates a publicly readable URL to a file or folder in the user's Dropbox. + # + # @param {String} path the path to the file or folder that will be linked to; + # the path is relative to the user's Dropbox or to the application's + # folder + # @param {?Object} options the advanced settings below; for the default + # settings, skip the argument or pass null + # @option options {Boolean} download if set, the URL will be a direct + # download URL, instead of the usual Dropbox preview URLs; direct + # download URLs are short-lived (currently 4 hours), whereas regular URLs + # virtually have no expiration date (currently set to 2030); no direct + # download URLs can be generated for directories + # @option options {Boolean} downloadHack if set, a long-living download URL + # will be generated by asking for a preview URL and using the officially + # documented hack at https://www.dropbox.com/help/201 to turn the preview + # URL into a download URL + # @option options {Boolean} long if set, the URL will not be shortened using + # Dropbox's shortner; the download and downloadHack options imply long + # @option options {Boolean} longUrl synonym for long; makes life easy for + # RhinoJS users + # @param {function(?Dropbox.ApiError, ?Dropbox.PublicUrl)} callback called + # with the result of the /shares or /media HTTP request; if the call + # succeeds, the second parameter is a Dropbox.PublicUrl instance, and the + # first parameter is null + # @return {XMLHttpRequest} the XHR object used for this API call + makeUrl: (path, options, callback) -> + if (not callback) and (typeof options is 'function') + callback = options + options = null + + # NOTE: cannot use options.long; normally, the CoffeeScript compiler + # escapes keywords for us; although long isn't really a keyword, the + # Rhino VM thinks it is; this hack can be removed when the bug below + # is fixed: + # https://github.com/mozilla/rhino/issues/93 + if options and (options['long'] or options.longUrl or options.downloadHack) + params = { short_url: 'false' } + else + params = {} + + path = @urlEncodePath path + url = "#{@urls.shares}/#{path}" + isDirect = false + useDownloadHack = false + if options + if options.downloadHack + isDirect = true + useDownloadHack = true + else if options.download + isDirect = true + url = "#{@urls.media}/#{path}" + + # TODO: locale support would edit the params here + xhr = new Dropbox.Xhr('POST', url).setParams(params).signWithOauth(@oauth) + @dispatchXhr xhr, (error, urlData) -> + if useDownloadHack and urlData and urlData.url + urlData.url = urlData.url.replace(@authServer, @downloadServer) + callback error, Dropbox.PublicUrl.parse(urlData, isDirect) + + # Retrieves the revision history of a file in a user's Dropbox. + # + # @param {String} path the path to the file whose revision history will be + # retrieved, relative to the user's Dropbox or to the application's + # folder + # @param {?Object} options the advanced settings below; for the default + # settings, skip the argument or pass null + # @option options {Number} limit if specified, the call will return at most + # this many versions + # @param {function(?Dropbox.ApiError, ?Array)} callback called + # with the result of the /revisions HTTP request; if the call succeeds, + # the second parameter is an array with one Dropbox.Stat instance per + # file version, and the first parameter is null + # @return {XMLHttpRequest} the XHR object used for this API call + history: (path, options, callback) -> + if (not callback) and (typeof options is 'function') + callback = options + options = null + + params = {} + if options and options.limit? + params.rev_limit = options.limit + + xhr = new Dropbox.Xhr 'GET', "#{@urls.revisions}/#{@urlEncodePath(path)}" + xhr.setParams(params).signWithOauth(@oauth) + @dispatchXhr xhr, (error, versions) -> + if versions + stats = (Dropbox.Stat.parse(metadata) for metadata in versions) + else + stats = undefined + callback error, stats + + # Alias for "history" that matches the HTTP API. + revisions: (path, options, callback) -> + @history path, options, callback + + # Computes a URL that generates a thumbnail for a file in the user's Dropbox. + # + # @param {String} path the path to the file whose thumbnail image URL will be + # computed, relative to the user's Dropbox or to the application's + # folder + # @param {?Object} options the advanced settings below; for the default + # settings, skip the argument or pass null + # @option options {Boolean} png if true, the thumbnail's image will be a PNG + # file; the default thumbnail format is JPEG + # @option options {String} format value that gets passed directly to the API; + # this is intended for newly added formats that the API may not support; + # use options such as "png" when applicable + # @option options {String} sizeCode specifies the image's dimensions; this + # gets passed directly to the API; currently, the following values are + # supported: 'small' (32x32), 'medium' (64x64), 'large' (128x128), + # 's' (64x64), 'm' (128x128), 'l' (640x480), 'xl' (1024x768); the default + # value is "small" + # @return {String} a URL to an image that can be used as the thumbnail for + # the given file + thumbnailUrl: (path, options) -> + xhr = @thumbnailXhr path, options + xhr.paramsToUrl().url + + # Retrieves the image data of a thumbnail for a file in the user's Dropbox. + # + # This method is intended to be used with low-level painting APIs. Whenever + # possible, it is easier to place the result of thumbnailUrl in a DOM + # element, and rely on the browser to fetch the file. + # + # @param {String} path the path to the file whose thumbnail image URL will be + # computed, relative to the user's Dropbox or to the application's + # folder + # @param {?Object} options the advanced settings below; for the default + # settings, skip the argument or pass null + # @option options {Boolean} png if true, the thumbnail's image will be a PNG + # file; the default thumbnail format is JPEG + # @option options {String} format value that gets passed directly to the API; + # this is intended for newly added formats that the API may not support; + # use options such as "png" when applicable + # @option options {String} sizeCode specifies the image's dimensions; this + # gets passed directly to the API; currently, the following values are + # supported: 'small' (32x32), 'medium' (64x64), 'large' (128x128), + # 's' (64x64), 'm' (128x128), 'l' (640x480), 'xl' (1024x768); the default + # value is "small" + # @option options {Boolean} blob if true, the file will be retrieved as a + # Blob, instead of a String; this requires XHR Level 2 support, which is + # not available in IE <= 9 + # @param {function(?Dropbox.ApiError, ?Object, ?Dropbox.Stat)} callback + # called with the result of the /thumbnails HTTP request; if the call + # succeeds, the second parameter is the image data as a String or Blob, + # the third parameter is a Dropbox.Stat instance describing the + # thumbnailed file, and the first argument is null + # @return {XMLHttpRequest} the XHR object used for this API call + readThumbnail: (path, options, callback) -> + if (not callback) and (typeof options is 'function') + callback = options + options = null + + responseType = 'b' + if options + responseType = 'blob' if options.blob + + xhr = @thumbnailXhr path, options + xhr.setResponseType responseType + @dispatchXhr xhr, (error, data, metadata) -> + callback error, data, Dropbox.Stat.parse(metadata) + + # Sets up an XHR for reading a thumbnail for a file in the user's Dropbox. + # + # @see Dropbox.Client#thumbnailUrl + # @return {Dropbox.Xhr} an XHR request configured for fetching the thumbnail + thumbnailXhr: (path, options) -> + params = {} + if options + if options.format + params.format = options.format + else if options.png + params.format = 'png' + if options.size + # Can we do something nicer here? + params.size = options.size + + xhr = new Dropbox.Xhr 'GET', "#{@urls.thumbnails}/#{@urlEncodePath(path)}" + xhr.setParams(params).signWithOauth(@oauth) + + # Reverts a file's contents to a previous version. + # + # This is an atomic, bandwidth-optimized equivalent of reading the file + # contents at the given file version (readFile), and then using it to + # overwrite the file (writeFile). + # + # @param {String} path the path to the file whose contents will be reverted + # to a previous version, relative to the user's Dropbox or to the + # application's folder + # @param {String} versionTag the tag of the version that the file will be + # reverted to; maps to the "rev" parameter in the HTTP API + # @param {function(?Dropbox.ApiError, ?Dropbox.Stat)} callback called with + # the result of the /restore HTTP request; if the call succeeds, the + # second parameter is a Dropbox.Stat instance describing the file after + # the revert operation, and the first parameter is null + # @return {XMLHttpRequest} the XHR object used for this API call + revertFile: (path, versionTag, callback) -> + xhr = new Dropbox.Xhr 'POST', "#{@urls.restore}/#{@urlEncodePath(path)}" + xhr.setParams(rev: versionTag).signWithOauth @oauth + @dispatchXhr xhr, (error, metadata) -> + callback error, Dropbox.Stat.parse(metadata) + + # Alias for "revertFile" that matches the HTTP API. + restore: (path, versionTag, callback) -> + @revertFile path, versionTag, callback + + # Finds files / folders whose name match a pattern, in the user's Dropbox. + # + # @param {String} path the path to the file whose contents will be reverted + # to a previous version, relative to the user's Dropbox or to the + # application's folder + # @param {String} namePattern the string that file / folder names must + # contain in order to match the search criteria; + # @param {?Object} options the advanced settings below; for the default + # settings, skip the argument or pass null + # @option options {Number} limit if specified, the call will return at most + # this many versions + # @option options {Boolean} removed if set to true, the results will include + # files and folders that were deleted from the user's Dropbox; the default + # limit is the maximum value of 1,000 + # @option options {Boolean} deleted alias for "removed" that matches the HTTP + # API; using this alias is not recommended, because it may cause confusion + # with JavaScript's delete operation + # @param {function(?Dropbox.ApiError, ?Array)} callback called + # with the result of the /search HTTP request; if the call succeeds, the + # second parameter is an array with one Dropbox.Stat instance per search + # result, and the first parameter is null + # @return {XMLHttpRequest} the XHR object used for this API call + findByName: (path, namePattern, options, callback) -> + if (not callback) and (typeof options is 'function') + callback = options + options = null + + params = { query: namePattern } + if options + if options.limit? + params.file_limit = options.limit + if options.removed or options.deleted + params.include_deleted = true + + xhr = new Dropbox.Xhr 'GET', "#{@urls.search}/#{@urlEncodePath(path)}" + xhr.setParams(params).signWithOauth(@oauth) + @dispatchXhr xhr, (error, results) -> + if results + stats = (Dropbox.Stat.parse(metadata) for metadata in results) + else + stats = undefined + callback error, stats + + # Alias for "findByName" that matches the HTTP API. + search: (path, namePattern, options, callback) -> + @findByName path, namePattern, options, callback + + # Creates a reference used to copy a file to another user's Dropbox. + # + # @param {String} path the path to the file whose contents will be + # referenced, relative to the uesr's Dropbox or to the application's + # folder + # @param {function(?Dropbox.ApiError, ?Dropbox.CopyReference)} callback + # called with the result of the /copy_ref HTTP request; if the call + # succeeds, the second parameter is a Dropbox.CopyReference instance, and + # the first parameter is null + # @return {XMLHttpRequest} the XHR object used for this API call + makeCopyReference: (path, callback) -> + xhr = new Dropbox.Xhr 'GET', "#{@urls.copyRef}/#{@urlEncodePath(path)}" + xhr.signWithOauth @oauth + @dispatchXhr xhr, (error, refData) -> + callback error, Dropbox.CopyReference.parse(refData) + + # Alias for "makeCopyReference" that matches the HTTP API. + copyRef: (path, callback) -> + @makeCopyReference path, callback + + # Fetches a list of changes in the user's Dropbox since the last call. + # + # This method is intended to make full sync implementations easier and more + # performant. Each call returns a cursor that can be used in a future call + # to obtain all the changes that happened in the user's Dropbox (or + # application directory) between the two calls. + # + # @param {Dropbox.PulledChanges, String} cursorTag the result of a previous + # call to pullChanges, or a string containing a tag representing the + # Dropbox state that is used as the baseline for the change list; this + # should be obtained from a previous call to pullChanges, or be set to null + # / ommitted on the first call to pullChanges + # @param {function(?Dropbox.ApiError, ?Dropbox.PulledChanges)} callback + # called with the result of the /delta HTTP request; if the call + # succeeds, the second parameter is a Dropbox.PulledChanges describing + # the changes to the user's Dropbox since the pullChanges call that + # produced the given cursor, and the first parameter is null + # @return {XMLHttpRequest} the XHR object used for this API call + pullChanges: (cursor, callback) -> + if (not callback) and (typeof cursor is 'function') + callback = cursor + cursor = null + + if cursor + if cursor.cursorTag + params = { cursor: cursor.cursorTag } + else + params = { cursor: cursor } + else + params = {} + + xhr = new Dropbox.Xhr 'POST', @urls.delta + xhr.setParams(params).signWithOauth @oauth + @dispatchXhr xhr, (error, deltaInfo) -> + callback error, Dropbox.PulledChanges.parse(deltaInfo) + + # Alias for "pullChanges" that matches the HTTP API. + delta: (cursor, callback) -> + @pullChanges cursor, callback + + # Creates a folder in a user's Dropbox. + # + # @param {String} path the path of the folder that will be created, relative + # to the user's Dropbox or to the application's folder + # @param {function(?Dropbox.ApiError, ?Dropbox.Stat)} callback called with + # the result of the /fileops/create_folder HTTP request; if the call + # succeeds, the second parameter is a Dropbox.Stat instance describing + # the newly created folder, and the first parameter is null + # @return {XMLHttpRequest} the XHR object used for this API call + mkdir: (path, callback) -> + xhr = new Dropbox.Xhr 'POST', @urls.fileopsCreateFolder + xhr.setParams(root: @fileRoot, path: @normalizePath(path)). + signWithOauth(@oauth) + @dispatchXhr xhr, (error, metadata) -> + callback error, Dropbox.Stat.parse(metadata) + + # Removes a file or diretory from a user's Dropbox. + # + # @param {String} path the path of the file to be read, relative to the + # user's Dropbox or to the application's folder + # @param {function(?Dropbox.ApiError, ?Dropbox.Stat)} callback called with + # the result of the /fileops/delete HTTP request; if the call succeeds, + # the second parameter is a Dropbox.Stat instance describing the removed + # file or folder, and the first parameter is null + # @return {XMLHttpRequest} the XHR object used for this API call + remove: (path, callback) -> + xhr = new Dropbox.Xhr 'POST', @urls.fileopsDelete + xhr.setParams(root: @fileRoot, path: @normalizePath(path)). + signWithOauth(@oauth) + @dispatchXhr xhr, (error, metadata) -> + callback error, Dropbox.Stat.parse(metadata) + + # node.js-friendly alias for "remove". + unlink: (path, callback) -> + @remove path, callback + + # Alias for "remove" that matches the HTTP API. + delete: (path, callback) -> + @remove path, callback + + # Copies a file or folder in the user's Dropbox. + # + # This method's "from" parameter can be either a path or a copy reference + # obtained by a previous call to makeCopyRef. The method uses a crude + # heuristic to interpret the "from" string -- if it doesn't contain any + # slash (/) or dot (.) character, it is assumed to be a copy reference. The + # easiest way to work with it is to prepend "/" to every path passed to the + # method. The method will process paths that start with multiple /s + # correctly. + # + # @param {String, Dropbox.CopyReference} from the path of the file or folder + # that will be copied, or a Dropbox.CopyReference instance obtained by + # calling makeCopyRef or Dropbox.CopyReference.parse; if this is a path, it + # is relative to the user's Dropbox or to the application's folder + # @param {String} toPath the path that the file or folder will have after the + # method call; the path is relative to the user's Dropbox or to the + # application folder + # @param {function(?Dropbox.ApiError, ?Dropbox.Stat)} callback called with + # the result of the /fileops/copy HTTP request; if the call succeeds, the + # second parameter is a Dropbox.Stat instance describing the file or folder + # created by the copy operation, and the first parameter is null + # @return {XMLHttpRequest} the XHR object used for this API call + copy: (from, toPath, callback) -> + if (not callback) and (typeof options is 'function') + callback = options + options = null + + params = { root: @fileRoot, to_path: @normalizePath(toPath) } + if from instanceof Dropbox.CopyReference + params.from_copy_ref = from.tag + else + params.from_path = @normalizePath from + # TODO: locale support would edit the params here + + xhr = new Dropbox.Xhr 'POST', @urls.fileopsCopy + xhr.setParams(params).signWithOauth @oauth + @dispatchXhr xhr, (error, metadata) -> + callback error, Dropbox.Stat.parse(metadata) + + # Moves a file or folder to a different location in a user's Dropbox. + # + # @param {String} fromPath the path of the file or folder that will be moved, + # relative to the user's Dropbox or to the application's folder + # @param {String} toPath the path that the file or folder will have after + # the method call; the path is relative to the user's Dropbox or to the + # application's folder + # @param {function(?Dropbox.ApiError, ?Dropbox.Stat)} callback called with + # the result of the /fileops/move HTTP request; if the call succeeds, the + # second parameter is a Dropbox.Stat instance describing the moved + # file or folder at its new location, and the first parameter is + # null + # @return {XMLHttpRequest} the XHR object used for this API call + move: (fromPath, toPath, callback) -> + if (not callback) and (typeof options is 'function') + callback = options + options = null + + xhr = new Dropbox.Xhr 'POST', @urls.fileopsMove + xhr.setParams( + root: @fileRoot, from_path: @normalizePath(fromPath), + to_path: @normalizePath(toPath)).signWithOauth @oauth + @dispatchXhr xhr, (error, metadata) -> + callback error, Dropbox.Stat.parse(metadata) + + # Removes all login information. + # + # @return {Dropbox.Client} this, for easy call chaining + reset: -> + @uid = null + @oauth.setToken null, '' + @authState = DropboxClient.RESET + @authError = null + @_credentials = null + @ + + # Change the client's OAuth credentials. + # + # @param {?Object} the result of a prior call to credentials() + # @return {Dropbox.Client} this, for easy call chaining + setCredentials: (credentials) -> + @oauth.reset credentials + @uid = credentials.uid or null + if credentials.authState + @authState = credentials.authState + else + if credentials.token + @authState = DropboxClient.DONE + else + @authState = DropboxClient.RESET + @authError = null + @_credentials = null + @ + + # @return {String} a string that uniquely identifies the Dropbox application + # of this client + appHash: -> + @oauth.appHash() + + # Computes the URLs of all the Dropbox API calls. + # + # @private + # This is called by the constructor, and used by the other methods. It should + # not be used directly. + setupUrls: -> + @fileRoot = if @sandbox then 'sandbox' else 'dropbox' + + @urls = + # Authentication. + requestToken: "#{@apiServer}/1/oauth/request_token" + authorize: "#{@authServer}/1/oauth/authorize" + accessToken: "#{@apiServer}/1/oauth/access_token" + signOut: "#{@apiServer}/1/unlink_access_token" + + # Accounts. + accountInfo: "#{@apiServer}/1/account/info" + + # Files and metadata. + getFile: "#{@fileServer}/1/files/#{@fileRoot}" + postFile: "#{@fileServer}/1/files/#{@fileRoot}" + putFile: "#{@fileServer}/1/files_put/#{@fileRoot}" + metadata: "#{@apiServer}/1/metadata/#{@fileRoot}" + delta: "#{@apiServer}/1/delta" + revisions: "#{@apiServer}/1/revisions/#{@fileRoot}" + restore: "#{@apiServer}/1/restore/#{@fileRoot}" + search: "#{@apiServer}/1/search/#{@fileRoot}" + shares: "#{@apiServer}/1/shares/#{@fileRoot}" + media: "#{@apiServer}/1/media/#{@fileRoot}" + copyRef: "#{@apiServer}/1/copy_ref/#{@fileRoot}" + thumbnails: "#{@fileServer}/1/thumbnails/#{@fileRoot}" + + # File operations. + fileopsCopy: "#{@apiServer}/1/fileops/copy" + fileopsCreateFolder: "#{@apiServer}/1/fileops/create_folder" + fileopsDelete: "#{@apiServer}/1/fileops/delete" + fileopsMove: "#{@apiServer}/1/fileops/move" + + # authState value for a client that experienced an authentication error. + @ERROR: 0 + + # authState value for a properly initialized client with no user credentials. + @RESET: 1 + + # authState value for a client with a request token that must be authorized. + @REQUEST: 2 + + # authState value for a client whose request token was authorized. + @AUTHORIZED: 3 + + # authState value for a client that has an access token. + @DONE: 4 + + # authState value for a client that voluntarily invalidated its access token. + @SIGNED_OFF: 5 + + # Normalizes a Dropobx path and encodes it for inclusion in a request URL. + # + # @private + # This is called internally by the other client functions, and should not be + # used outside the {Dropbox.Client} class. + urlEncodePath: (path) -> + Dropbox.Xhr.urlEncodeValue(@normalizePath(path)).replace /%2F/gi, '/' + + # Normalizes a Dropbox path for API requests. + # + # @private + # This is an internal method. It is used by all the client methods that take + # paths as arguments. + # + # @param {String} path a path + normalizePath: (path) -> + if path.substring(0, 1) is '/' + i = 1 + while path.substring(i, i + 1) is '/' + i += 1 + path.substring i + else + path + + # Generates an OAuth request token. + # + # @private + # This a low-level method called by authorize. Users should call authorize. + # + # @param {function(error, data)} callback called with the result of the + # /oauth/request_token HTTP request + requestToken: (callback) -> + xhr = new Dropbox.Xhr('POST', @urls.requestToken).signWithOauth(@oauth) + @dispatchXhr xhr, callback + + # The URL for /oauth/authorize, embedding the user's token. + # + # @private + # This a low-level method called by authorize. Users should call authorize. + # + # @param {String} token the oauth_token obtained from an /oauth/request_token + # call + # @return {String} the URL that the user's browser should be redirected to in + # order to perform an /oauth/authorize request + authorizeUrl: (token) -> + params = { oauth_token: token, oauth_callback: @driver.url() } + "#{@urls.authorize}?" + Dropbox.Xhr.urlEncode(params) + + # Exchanges an OAuth request token with an access token. + # + # @private + # This a low-level method called by authorize. Users should call authorize. + # + # @param {function(error, data)} callback called with the result of the + # /oauth/access_token HTTP request + getAccessToken: (callback) -> + xhr = new Dropbox.Xhr('POST', @urls.accessToken).signWithOauth(@oauth) + @dispatchXhr xhr, callback + + # Prepares an XHR before it is sent to the server. + # + # @private + # This is a low-level method called by other client methods. + dispatchXhr: (xhr, callback) -> + xhr.setCallback callback + xhr.prepare() + nativeXhr = xhr.xhr + if @filter + return nativeXhr unless @filter(nativeXhr, xhr) + xhr.send() + nativeXhr + + # @private + # @return {String} the URL to the default value for the "server" option + defaultApiServer: -> + 'https://api.dropbox.com' + + # @private + # @return {String} the URL to the default value for the "authServer" option + defaultAuthServer: -> + @apiServer.replace 'api.', 'www.' + + # @private + # @return {String} the URL to the default value for the "fileServer" option + defaultFileServer: -> + @apiServer.replace 'api.', 'api-content.' + + # @private + # @return {String} the URL to the default value for the "downloadServer" + # option + defaultDownloadServer: -> + @apiServer.replace 'api.', 'dl.' + + # Computes the cached value returned by credentials. + # + # @private + # @see Dropbox.Client#computeCredentials + computeCredentials: -> + value = + key: @oauth.key + sandbox: @sandbox + value.secret = @oauth.secret if @oauth.secret + if @oauth.token + value.token = @oauth.token + value.tokenSecret = @oauth.tokenSecret + value.uid = @uid if @uid + if @authState isnt DropboxClient.ERROR and + @authState isnt DropboxClient.RESET and + @authState isnt DropboxClient.DONE and + @authState isnt DropboxClient.SIGNED_OFF + value.authState = @authState + if @apiServer isnt @defaultApiServer() + value.server = @apiServer + if @authServer isnt @defaultAuthServer() + value.authServer = @authServer + if @fileServer isnt @defaultFileServer() + value.fileServer = @fileServer + if @downloadServer isnt @defaultDownloadServer() + value.downloadServer = @downloadServer + @_credentials = value + +DropboxClient = Dropbox.Client diff --git a/lib/client/storage/dropbox/src/drivers.coffee b/lib/client/storage/dropbox/src/drivers.coffee new file mode 100644 index 00000000..d48184e4 --- /dev/null +++ b/lib/client/storage/dropbox/src/drivers.coffee @@ -0,0 +1,477 @@ +# Documentation for the interface to a Dropbox OAuth driver. +class Dropbox.AuthDriver + # The callback URL that should be supplied to the OAuth /authorize call. + # + # The driver must be able to intercept redirects to the returned URL, in + # order to know when a user has completed the authorization flow. + # + # @return {String} an absolute URL + url: -> + 'https://some.url' + + # Redirects users to /authorize and waits for them to complete the flow. + # + # This method is called when the OAuth process reaches the REQUEST state, + # meaning the client has a request token that must be authorized by the user. + # + # @param {String} authUrl the URL that users should be sent to in order to + # authorize the application's token; this points to a Web page on + # Dropbox' servers + # @param {String} token the OAuth token that the user is authorizing; this + # will be provided by the Dropbox servers as a query parameter when the + # user is redirected to the URL returned by the driver's url() method + # @param {String} tokenSecret the secret associated with the given OAuth + # token; the driver may store this together with the token + # @param {function()} callback called when users have completed the + # authorization flow; the driver should call this when Dropbox redirects + # users to the URL returned by the url() method, and the 'token' query + # parameter matches the value of the token parameter + doAuthorize: (authUrl, token, tokenSecret, callback) -> + callback 'oauth-token' + + # Called when there is some progress in the OAuth process. + # + # The OAuth process goes through the following states: + # + # * Dropbox.Client.RESET - the client has no OAuth token, and is about to + # ask for a request token + # * Dropbox.Client.REQUEST - the client has a request OAuth token, and the + # user must go to an URL on the Dropbox servers to authorize the token + # * Dropbox.Client.AUTHORIZED - the client has a request OAuth token that + # was authorized by the user, and is about to exchange it for an access + # token + # * Dropbox.Client.DONE - the client has an access OAuth token that can be + # used for all API calls; the OAuth process is complete, and the callback + # passed to authorize is about to be called + # * Dropbox.Client.SIGNED_OFF - the client's Dropbox.Client#signOut() was + # called, and the client's OAuth token was invalidated + # * Dropbox.Client.ERROR - the client encounered an error during the OAuth + # process; the callback passed to authorize is about to be called with the + # error information + # + # @param {Dropbox.Client} client the client performing the OAuth process + # @param {function()} callback called when onAuthStateChange acknowledges the + # state change + onAuthStateChange: (client, callback) -> + callback() + +# Namespace for authentication drivers. +Dropbox.Drivers = {} + +# Base class for drivers that run in the browser. +# +# Inheriting from this class makes a driver use HTML5 localStorage to preserve +# OAuth tokens across page reloads. +class Dropbox.Drivers.BrowserBase + # Sets up the OAuth driver. + # + # Subclasses should pass the options object they receive to the superclass + # constructor. + # + # @param {?Object} options the advanced settings below + # @option options {Boolean} rememberUser if true, the user's OAuth tokens are + # saved in localStorage; if you use this, you MUST provide a UI item that + # calls signOut() on Dropbox.Client, to let the user "log out" of the + # application + # @option options {String} scope embedded in the localStorage key that holds + # the authentication data; useful for having multiple OAuth tokens in a + # single application + constructor: (options) -> + @rememberUser = options?.rememberUser or false + @scope = options?.scope or 'default' + + # The magic happens here. + onAuthStateChange: (client, callback) -> + @setStorageKey client + + switch client.authState + when DropboxClient.RESET + @loadCredentials (credentials) => + return callback() unless credentials + + if credentials.authState # Incomplete authentication. + client.setCredentials credentials + return callback() + + # There is an old access token. Only use it if the app supports + # logout. + unless @rememberUser + @forgetCredentials() + return callback() + + # Verify that the old access token still works. + client.setCredentials credentials + client.getUserInfo (error) => + if error + client.reset() + @forgetCredentials callback + else + callback() + when DropboxClient.REQUEST + @storeCredentials client.credentials(), callback + when DropboxClient.DONE + if @rememberUser + return @storeCredentials(client.credentials(), callback) + @forgetCredentials callback + when DropboxClient.SIGNED_OFF + @forgetCredentials callback + when DropboxClient.ERROR + @forgetCredentials callback + else + callback() + @ + + # Computes the @storageKey used by loadCredentials and forgetCredentials. + # + # @private + # This is called by onAuthStateChange. + # + # @param {Dropbox.Client} client the client instance that is running the + # authorization process + # @return {Dropbox.Driver} this, for easy call chaining + setStorageKey: (client) -> + # NOTE: the storage key is dependent on the app hash so that multiple apps + # hosted off the same server don't step on eachother's toes + @storageKey = "dropbox-auth:#{@scope}:#{client.appHash()}" + @ + + # Stores a Dropbox.Client's credentials to localStorage. + # + # @private + # onAuthStateChange calls this method during the authentication flow. + # + # @param {Object} credentials the result of a Drobpox.Client#credentials call + # @param {function()} callback called when the storing operation is complete + # @return {Dropbox.Drivers.BrowserBase} this, for easy call chaining + storeCredentials: (credentials, callback) -> + localStorage.setItem @storageKey, JSON.stringify(credentials) + callback() + @ + + # Retrieves a token and secret from localStorage. + # + # @private + # onAuthStateChange calls this method during the authentication flow. + # + # @param {function(?Object)} callback supplied with the credentials object + # stored by a previous call to + # Dropbox.Drivers.BrowserBase#storeCredentials; null if no credentials were + # stored, or if the previously stored credentials were deleted + # @return {Dropbox.Drivers.BrowserBase} this, for easy call chaining + loadCredentials: (callback) -> + jsonString = localStorage.getItem @storageKey + unless jsonString + callback null + return @ + + try + callback JSON.parse(jsonString) + catch e + # Parse errors. + callback null + @ + + # Deletes information previously stored by a call to storeToken. + # + # @private + # onAuthStateChange calls this method during the authentication flow. + # + # @param {function()} callback called after the credentials are deleted + # @return {Dropbox.Drivers.BrowserBase} this, for easy call chaining + forgetCredentials: (callback) -> + localStorage.removeItem @storageKey + callback() + @ + + # Wrapper for window.location, for testing purposes. + # + # @return {String} the current page's URL + @currentLocation: -> + window.location.href + +# OAuth driver that uses a redirect and localStorage to complete the flow. +class Dropbox.Drivers.Redirect extends Dropbox.Drivers.BrowserBase + # Sets up the redirect-based OAuth driver. + # + # @param {?Object} options the advanced settings below + # @option options {Boolean} useQuery if true, the page will receive OAuth + # data as query parameters; by default, the page receives OAuth data in + # the fragment part of the URL (the string following the #, + # available as document.location.hash), to avoid confusing the server + # generating the page + # @option options {Boolean} rememberUser if true, the user's OAuth tokens are + # saved in localStorage; if you use this, you MUST provide a UI item that + # calls signOut() on Dropbox.Client, to let the user "log out" of the + # application + # @option options {String} scope embedded in the localStorage key that holds + # the authentication data; useful for having multiple OAuth tokens in a + # single application + constructor: (options) -> + super options + @useQuery = options?.useQuery or false + @receiverUrl = @computeUrl options + @tokenRe = new RegExp "(#|\\?|&)oauth_token=([^&#]+)(&|#|$)" + + # Forwards the authentication process from REQUEST to AUTHORIZED on redirect. + onAuthStateChange: (client, callback) -> + superCall = do => => super client, callback + @setStorageKey client + if client.authState is DropboxClient.RESET + @loadCredentials (credentials) => + if credentials and credentials.authState # Incomplete authentication. + if credentials.token is @locationToken() and + credentials.authState is DropboxClient.REQUEST + # locationToken matched, so the redirect happened + credentials.authState = DropboxClient.AUTHORIZED + return @storeCredentials credentials, superCall + else + # The authentication process broke down, start over. + return @forgetCredentials superCall + superCall() + else + superCall() + + # URL of the current page, since the user will be sent right back. + url: -> + @receiverUrl + + # Redirects to the authorize page. + doAuthorize: (authUrl) -> + window.location.assign authUrl + + # Pre-computes the return value of url. + computeUrl: -> + querySuffix = "_dropboxjs_scope=#{encodeURIComponent @scope}" + location = Dropbox.Drivers.BrowserBase.currentLocation() + if location.indexOf('#') is -1 + fragment = null + else + locationPair = location.split '#', 2 + location = locationPair[0] + fragment = locationPair[1] + if @useQuery + if location.indexOf('?') is -1 + location += "?#{querySuffix}" # No query string in the URL. + else + location += "&#{querySuffix}" # The URL already has a query string. + else + fragment = "?#{querySuffix}" + + if fragment + location + '#' + fragment + else + location + + # Figures out if the user completed the OAuth flow based on the current URL. + # + # @return {?String} the OAuth token that the user just authorized, or null if + # the user accessed this directly, without having authorized a token + locationToken: -> + location = Dropbox.Drivers.BrowserBase.currentLocation() + + # Check for the scope. + scopePattern = "_dropboxjs_scope=#{encodeURIComponent @scope}&" + return null if location.indexOf?(scopePattern) is -1 + + # Extract the token. + match = @tokenRe.exec location + if match then decodeURIComponent(match[2]) else null + +# OAuth driver that uses a popup window and postMessage to complete the flow. +class Dropbox.Drivers.Popup extends Dropbox.Drivers.BrowserBase + # Sets up a popup-based OAuth driver. + # + # @param {?Object} options one of the settings below; leave out the argument + # to use the current location for redirecting + # @option options {Boolean} rememberUser if true, the user's OAuth tokens are + # saved in localStorage; if you use this, you MUST provide a UI item that + # calls signOut() on Dropbox.Client, to let the user "log out" of the + # application + # @option options {String} scope embedded in the localStorage key that holds + # the authentication data; useful for having multiple OAuth tokens in a + # single application + # @option options {String} receiverUrl URL to the page that receives the + # /authorize redirect and performs the postMessage + # @option options {Boolean} noFragment if true, the receiverUrl will be used + # as given; by default, a hash "#" is appended to URLs that don't have + # one, so the OAuth token is received as a URL fragment and does not hit + # the file server + # @option options {String} receiverFile the URL to the receiver page will be + # computed by replacing the file name (everything after the last /) of + # the current location with this parameter's value + constructor: (options) -> + super options + @receiverUrl = @computeUrl options + @tokenRe = new RegExp "(#|\\?|&)oauth_token=([^&#]+)(&|#|$)" + + # Removes credentials stuck in the REQUEST stage. + onAuthStateChange: (client, callback) -> + superCall = do => => super client, callback + @setStorageKey client + if client.authState is DropboxClient.RESET + @loadCredentials (credentials) -> + if credentials and credentials.authState # Incomplete authentication. + # The authentication process broke down, start over. + return @forgetCredentials superCall + superCall() + else + superCall() + + # Shows the authorization URL in a pop-up, waits for it to send a message. + doAuthorize: (authUrl, token, tokenSecret, callback) -> + @listenForMessage token, callback + @openWindow authUrl + + # URL of the redirect receiver page, which posts a message back to this page. + url: -> + @receiverUrl + + # Pre-computes the return value of url. + computeUrl: (options) -> + if options + if options.receiverUrl + if options.noFragment or options.receiverUrl.indexOf('#') isnt -1 + return options.receiverUrl + else + return options.receiverUrl + '#' + else if options.receiverFile + fragments = Dropbox.Drivers.BrowserBase.currentLocation().split '/' + fragments[fragments.length - 1] = options.receiverFile + if options.noFragment + return fragments.join('/') + else + return fragments.join('/') + '#' + Dropbox.Drivers.BrowserBase.currentLocation() + + # Creates a popup window. + # + # @param {String} url the URL that will be loaded in the popup window + # @return {?DOMRef} reference to the opened window, or null if the call + # failed + openWindow: (url) -> + window.open url, '_dropboxOauthSigninWindow', @popupWindowSpec(980, 700) + + # Spec string for window.open to create a nice popup. + # + # @param {Number} popupWidth the desired width of the popup window + # @param {Number} popupHeight the desired height of the popup window + # @return {String} spec string for the popup window + popupWindowSpec: (popupWidth, popupHeight) -> + # Metrics for the current browser window. + x0 = window.screenX ? window.screenLeft + y0 = window.screenY ? window.screenTop + width = window.outerWidth ? document.documentElement.clientWidth + height = window.outerHeight ? document.documentElement.clientHeight + + # Computed popup window metrics. + popupLeft = Math.round x0 + (width - popupWidth) / 2 + popupTop = Math.round y0 + (height - popupHeight) / 2.5 + popupLeft = x0 if popupLeft < x0 + popupTop = y0 if popupTop < y0 + + # The specification string. + "width=#{popupWidth},height=#{popupHeight}," + + "left=#{popupLeft},top=#{popupTop}" + + 'dialog=yes,dependent=yes,scrollbars=yes,location=yes' + + # Listens for a postMessage from a previously opened popup window. + # + # @param {String} token the token string that must be received from the popup + # window + # @param {function()} called when the received message matches the token + listenForMessage: (token, callback) -> + listener = (event) => + match = @tokenRe.exec event.data.toString() + if match and decodeURIComponent(match[2]) is token + window.removeEventListener 'message', listener + callback() + window.addEventListener 'message', listener, false + + +# OAuth driver that redirects the browser to a node app to complete the flow. +# +# This is useful for testing node.js libraries and applications. +class Dropbox.Drivers.NodeServer + # Starts up the node app that intercepts the browser redirect. + # + # @param {?Object} options one or more of the options below + # @option options {Number} port the number of the TCP port that will receive + # HTTP requests + # @param {String} faviconFile the path to a file that will be served at + # /favicon.ico + constructor: (options) -> + @port = options?.port or 8912 + @faviconFile = options?.favicon or null + # Calling require in the constructor because this doesn't work in browsers. + @fs = require 'fs' + @http = require 'http' + @open = require 'open' + + @callbacks = {} + @urlRe = new RegExp "^/oauth_callback\\?" + @tokenRe = new RegExp "(\\?|&)oauth_token=([^&]+)(&|$)" + @createApp() + + # URL to the node.js OAuth callback handler. + url: -> + "http://localhost:#{@port}/oauth_callback" + + # Opens the token + doAuthorize: (authUrl, token, tokenSecret, callback) -> + @callbacks[token] = callback + @openBrowser authUrl + + # Opens the given URL in a browser. + openBrowser: (url) -> + unless url.match /^https?:\/\// + throw new Error("Not a http/https URL: #{url}") + @open url + + # Creates and starts up an HTTP server that will intercept the redirect. + createApp: -> + @app = @http.createServer (request, response) => + @doRequest request, response + @app.listen @port + + # Shuts down the HTTP server. + # + # The driver will become unusable after this call. + closeServer: -> + @app.close() + + # Reads out an /authorize callback. + doRequest: (request, response) -> + if @urlRe.exec request.url + match = @tokenRe.exec request.url + if match + token = decodeURIComponent match[2] + if @callbacks[token] + @callbacks[token]() + delete @callbacks[token] + data = '' + request.on 'data', (dataFragment) -> data += dataFragment + request.on 'end', => + if @faviconFile and (request.url is '/favicon.ico') + @sendFavicon response + else + @closeBrowser response + + # Renders a response that will close the browser window used for OAuth. + closeBrowser: (response) -> + closeHtml = """ + + +

    Please close this window.

    + """ + response.writeHead(200, + {'Content-Length': closeHtml.length, 'Content-Type': 'text/html' }) + response.write closeHtml + response.end + + # Renders the favicon file. + sendFavicon: (response) -> + @fs.readFile @faviconFile, (error, data) -> + response.writeHead(200, + { 'Content-Length': data.length, 'Content-Type': 'image/x-icon' }) + response.write data + response.end diff --git a/lib/client/storage/dropbox/src/hmac.coffee b/lib/client/storage/dropbox/src/hmac.coffee new file mode 100644 index 00000000..b26be463 --- /dev/null +++ b/lib/client/storage/dropbox/src/hmac.coffee @@ -0,0 +1,180 @@ +# HMAC-SHA1 implementation heavily inspired from +# http://pajhome.org.uk/crypt/md5/sha1.js + +# Base64-encoded HMAC-SHA1. +# +# @param {String} string the ASCII string to be signed +# @param {String} key the HMAC key +# @return {String} a base64-encoded HMAC of the given string and key +base64HmacSha1 = (string, key) -> + arrayToBase64 hmacSha1(stringToArray(string), stringToArray(key), + string.length, key.length) + +# Base64-encoded SHA1. +# +# @param {String} string the ASCII string to be hashed +# @return {String} a base64-encoded SHA1 hash of the given string +base64Sha1 = (string) -> + arrayToBase64 sha1(stringToArray(string), string.length) + +# SHA1 and HMAC-SHA1 versions that use the node.js builtin crypto. +unless window? + crypto = require 'crypto' + base64HmacSha1 = (string, key) -> + hmac = crypto.createHmac 'sha1', key + hmac.update string + hmac.digest 'base64' + base64Sha1 = (string) -> + hash = crypto.createHash 'sha1' + hash.update string + hash.digest 'base64' + +# HMAC-SHA1 implementation. +# +# @param {Array} string the HMAC input, as an array of 32-bit numbers +# @param {Array} key the HMAC input, as an array of 32-bit numbers +# @param {Number} length the length of the HMAC input, in bytes +# @return {Array} the HMAC output, as an array of 32-bit numbers +hmacSha1 = (string, key, length, keyLength) -> + if key.length > 16 + key = sha1 key, keyLength + + ipad = (key[i] ^ 0x36363636 for i in [0...16]) + opad = (key[i] ^ 0x5C5C5C5C for i in [0...16]) + + hash1 = sha1 ipad.concat(string), 64 + length + sha1 opad.concat(hash1), 64 + 20 + +# SHA1 implementation. +# +# @param {Array} string the SHA1 input, as an array of 32-bit numbers; the +# computation trashes the array +# @param {Number} length the number of bytes in the SHA1 input; used in the +# SHA1 padding algorithm +# @return {Array} the SHA1 output, as an array of 32-bit numbers +sha1 = (string, length) -> + string[length >> 2] |= 1 << (31 - ((length & 0x03) << 3)) + string[(((length + 8) >> 6) << 4) + 15] = length << 3 + + state = Array 80 + a = 1732584193 # 0x67452301 + b = -271733879 # 0xefcdab89 + c = -1732584194 # 0x98badcfe + d = 271733878 # 0x10325476 + e = -1009589776 # 0xc3d2e1f0 + + i = 0 + limit = string.length + # Uncomment the line below to debug packing. + # console.log string.map(xxx) + while i < limit + a0 = a + b0 = b + c0 = c + d0 = d + e0 = e + + for j in [0...80] + if j < 16 + state[j] = string[i + j] + else + state[j] = rotateLeft32 state[j - 3] ^ state[j - 8] ^ state[j - 14] ^ + state[j - 16], 1 + if j < 20 + ft = (b & c) | ((~b) & d) + kt = 1518500249 # 0x5a827999 + else if j < 40 + ft = b ^ c ^ d + kt = 1859775393 # 0x6ed9eba1 + else if j < 60 + ft = (b & c) | (b & d) | (c & d) + kt = -1894007588 # 0x8f1bbcdc + else + ft = b ^ c ^ d + kt = -899497514 # 0xca62c1d6 + t = add32 add32(rotateLeft32(a, 5), ft), add32(add32(e, state[j]), kt) + e = d + d = c + c = rotateLeft32 b, 30 + b = a + a = t + # Uncomment the line below to debug block computation. + # console.log [xxx(a), xxx(b), xxx(c), xxx(d), xxx(e)] + a = add32 a, a0 + b = add32 b, b0 + c = add32 c, c0 + d = add32 d, d0 + e = add32 e, e0 + i += 16 + # Uncomment the line below to see the input to the base64 encoder. + # console.log [xxx(a), xxx(b), xxx(c), xxx(d), xxx(e)] + [a, b, c, d, e] + +### +# Uncomment the definition below for debugging. +# +# Returns the hexadecimal representation of a 32-bit number. +xxx = (n) -> + if n < 0 + n = (1 << 30) * 4 + n + n.toString 16 +### + +# Rotates a 32-bit word. +# +# @param {Number} value the 32-bit number to be rotated +# @param {Number} count the number of bits (0..31) to rotate by +# @return {Number} the rotated value +rotateLeft32 = (value, count) -> + (value << count) | (value >>> (32 - count)) + +# 32-bit unsigned addition. +# +# @param {Number} a, b the 32-bit numbers to be added modulo 2^32 +# @return {Number} the 32-bit representation of a + b +add32 = (a, b) -> + low = (a & 0xFFFF) + (b & 0xFFFF) + high = (a >> 16) + (b >> 16) + (low >> 16) + (high << 16) | (low & 0xFFFF) + +# Converts a 32-bit number array into a base64-encoded string. +# +# @param {Array} an array of big-endian 32-bit numbers +# @return {String} base64 encoding of the given array of numbers +arrayToBase64 = (array) -> + string = "" + i = 0 + limit = array.length * 4 + while i < limit + i2 = i + trit = ((array[i2 >> 2] >> ((3 - (i2 & 3)) << 3)) & 0xFF) << 16 + i2 += 1 + trit |= ((array[i2 >> 2] >> ((3 - (i2 & 3)) << 3)) & 0xFF) << 8 + i2 += 1 + trit |= (array[i2 >> 2] >> ((3 - (i2 & 3)) << 3)) & 0xFF + + string += _base64Digits[(trit >> 18) & 0x3F] + string += _base64Digits[(trit >> 12) & 0x3F] + i += 1 + if i >= limit + string += '=' + else + string += _base64Digits[(trit >> 6) & 0x3F] + i += 1 + if i >= limit + string += '=' + else + string += _base64Digits[trit & 0x3F] + i += 1 + string + +_base64Digits = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; + +# Converts an ASCII string into array of 32-bit numbers. +stringToArray = (string) -> + array = [] + mask = 0xFF + for i in [0...string.length] + array[i >> 2] |= (string.charCodeAt(i) & mask) << ((3 - (i & 3)) << 3) + array + diff --git a/lib/client/storage/dropbox/src/oauth.coffee b/lib/client/storage/dropbox/src/oauth.coffee new file mode 100644 index 00000000..823f90a5 --- /dev/null +++ b/lib/client/storage/dropbox/src/oauth.coffee @@ -0,0 +1,162 @@ +# Stripped-down OAuth implementation that works with the Dropbox API server. +class Dropbox.Oauth + # Creates an Oauth instance that manages an application's keys and token. + # + # @param {Object} options the following properties + # @option options {String} key the Dropbox application's key (consumer key, + # in OAuth vocabulary); browser-side applications should use + # Dropbox.encodeKey to obtain an encoded key string, and pass it as the + # key option + # @option options {String} secret the Dropbox application's secret (consumer + # secret, in OAuth vocabulary); browser-side applications should not use + # the secret option; instead, they should pass the result of + # Dropbox.encodeKey as the key option + constructor: (options) -> + @key = @k = null + @secret = @s = null + @token = null + @tokenSecret = null + @_appHash = null + @reset options + + # Creates an Oauth instance that manages an application's keys and token. + # + # @see Dropbox.Oauth#constructor for options + reset: (options) -> + if options.secret + @k = @key = options.key + @s = @secret = options.secret + @_appHash = null + else if options.key + @key = options.key + @secret = null + secret = atob dropboxEncodeKey(@key).split('|', 2)[1] + [k, s] = secret.split '?', 2 + @k = decodeURIComponent k + @s = decodeURIComponent s + @_appHash = null + else + unless @k + throw new Error('No API key supplied') + + if options.token + @setToken options.token, options.tokenSecret + else + @setToken null, '' + + # Sets the OAuth token to be used for future requests. + setToken: (token, tokenSecret) -> + if token and (not tokenSecret) + throw new Error('No secret supplied with the user token') + + @token = token + @tokenSecret = tokenSecret || '' + + # This is part of signing, but it's set here so it can be cached. + @hmacKey = Dropbox.Xhr.urlEncodeValue(@s) + '&' + + Dropbox.Xhr.urlEncodeValue(tokenSecret) + null + + # Computes the value of the Authorization HTTP header. + # + # This method mutates the params object, and removes all the OAuth-related + # parameters from it. + # + # @param {String} method the HTTP method used to make the request ('GET', + # 'POST', etc) + # @param {String} url the HTTP URL (e.g. "http://www.example.com/photos") + # that receives the request + # @param {Object} params an associative array (hash) containing the HTTP + # request parameters; the parameters should include the oauth_ + # parameters generated by calling {Dropbox.Oauth#boilerplateParams} + # @return {String} the value to be used for the Authorization HTTP header + authHeader: (method, url, params) -> + @addAuthParams method, url, params + + # Collect all the OAuth parameters. + oauth_params = [] + for param, value of params + if param.substring(0, 6) == 'oauth_' + oauth_params.push param + oauth_params.sort() + + # Remove the parameters from the params hash and add them to the header. + header = [] + for param in oauth_params + header.push Dropbox.Xhr.urlEncodeValue(param) + '="' + + Dropbox.Xhr.urlEncodeValue(params[param]) + '"' + delete params[param] + + # NOTE: the space after the comma is optional in the OAuth spec, so we'll + # skip it to save some bandwidth + 'OAuth ' + header.join(',') + + # Generates OAuth-required HTTP parameters. + # + # This method mutates the params object, and adds the OAuth-related + # parameters to it. + # + # @param {String} method the HTTP method used to make the request ('GET', + # 'POST', etc) + # @param {String} url the HTTP URL (e.g. "http://www.example.com/photos") + # that receives the request + # @param {Object} params an associative array (hash) containing the HTTP + # request parameters; the parameters should include the oauth_ + # parameters generated by calling {Dropbox.Oauth#boilerplateParams} + # @return {String} the value to be used for the Authorization HTTP header + addAuthParams: (method, url, params) -> + # Augment params with OAuth parameters. + @boilerplateParams params + params.oauth_signature = @signature method, url, params + params + + # Adds boilerplate OAuth parameters to a request's parameter list. + # + # This should be called right before signing a request, to maximize the + # chances that the OAuth timestamp will be fresh. + # + # @param {Object} params an associative array (hash) containing the + # parameters for an OAuth request; the boilerplate parameters will be + # added to this hash + # @return {Object} params + boilerplateParams: (params) -> + params.oauth_consumer_key = @k + params.oauth_nonce = @nonce() + params.oauth_signature_method = 'HMAC-SHA1' + params.oauth_token = @token if @token + params.oauth_timestamp = Math.floor(Date.now() / 1000) + params.oauth_version = '1.0' + params + + # Generates a nonce for an OAuth request. + # + # @return {String} the nonce to be used as the oauth_nonce parameter + nonce: -> + Date.now().toString(36) + Math.random().toString(36) + + # Computes the signature for an OAuth request. + # + # @param {String} method the HTTP method used to make the request ('GET', + # 'POST', etc) + # @param {String} url the HTTP URL (e.g. "http://www.example.com/photos") + # that receives the request + # @param {Object} params an associative array (hash) containing the HTTP + # request parameters; the parameters should include the oauth_ + # parameters generated by calling {Dropbox.Oauth#boilerplateParams} + # @return {String} the signature, ready to be used as the oauth_signature + # OAuth parameter + signature: (method, url, params) -> + string = method.toUpperCase() + '&' + Dropbox.Xhr.urlEncodeValue(url) + + '&' + Dropbox.Xhr.urlEncodeValue(Dropbox.Xhr.urlEncode(params)) + base64HmacSha1 string, @hmacKey + + # @return {String} a string that uniquely identifies the OAuth application + appHash: -> + return @_appHash if @_appHash + @_appHash = base64Sha1(@k).replace(/\=/g, '') + + +# Polyfill for Internet Explorer 8. +unless Date.now? + Date.now = () -> + (new Date()).getTime() diff --git a/lib/client/storage/dropbox/src/prod.coffee b/lib/client/storage/dropbox/src/prod.coffee new file mode 100644 index 00000000..3cc177b9 --- /dev/null +++ b/lib/client/storage/dropbox/src/prod.coffee @@ -0,0 +1,36 @@ +# Necessary bits to get a browser-side app in production. + +# Packs up a key and secret into a string, to bring script kiddies some pain. +# +# @param {String} key the application's API key +# @param {String} secret the application's API secret +# @return {String} encoded key string that can be passed as the key option to +# the Dropbox.Client constructor +dropboxEncodeKey = (key, secret) -> + if secret + secret = [encodeURIComponent(key), encodeURIComponent(secret)].join('?') + key = for i in [0...(key.length / 2)] + ((key.charCodeAt(i * 2) & 15) * 16) + (key.charCodeAt(i * 2 + 1) & 15) + else + [key, secret] = key.split '|', 2 + key = atob key + key = (key.charCodeAt(i) for i in [0...key.length]) + secret = atob secret + + s = [0...256] + y = 0 + for x in [0...256] + y = (y + s[i] + key[x % key.length]) % 256 + [s[x], s[y]] = [s[y], s[x]] + + x = y = 0 + result = for z in [0...secret.length] + x = (x + 1) % 256 + y = (y + s[x]) % 256 + [s[x], s[y]] = [s[y], s[x]] + k = s[(s[x] + s[y]) % 256] + String.fromCharCode((k ^ secret.charCodeAt(z)) % 256) + + key = (String.fromCharCode(key[i]) for i in [0...key.length]) + [btoa(key.join('')), btoa(result.join(''))].join '|' + diff --git a/lib/client/storage/dropbox/src/pulled_changes.coffee b/lib/client/storage/dropbox/src/pulled_changes.coffee new file mode 100644 index 00000000..0a324d24 --- /dev/null +++ b/lib/client/storage/dropbox/src/pulled_changes.coffee @@ -0,0 +1,100 @@ +# Wraps the result of pullChanges, describing the changes in a user's Dropbox. +class Dropbox.PulledChanges + # Creates a new Dropbox.PulledChanges instance from a /delta API call result. + # + # @param {?Object} deltaInfo the parsed JSON of a /delta API call result + # @return {?Dropbox.PulledChanges} a Dropbox.PulledChanges instance wrapping + # the given information; if the parameter does not look like parsed JSON, + # it is returned as is + @parse: (deltaInfo) -> + if deltaInfo and typeof deltaInfo is 'object' + new Dropbox.PulledChanges deltaInfo + else + deltaInfo + + # @property {Boolean} if true, the application should reset its copy of the + # user's Dropbox before applying the changes described by this instance + blankSlate: undefined + + # @property {String} encodes a cursor in the list of changes to a user's + # Dropbox; a pullChanges call returns some changes at the cursor, and then + # advance the cursor to account for the returned changes; the new cursor is + # returned by pullChanges, and meant to be used by a subsequent pullChanges + # call + cursorTag: undefined + + # @property {Array an array with one entry for each + # change to the user's Dropbox returned by a pullChanges call. + changes: undefined + + # @property {Boolean} if true, the pullChanges call returned a subset of the + # available changes, and the application should repeat the call + # immediately to get more changes + shouldPullAgain: undefined + + # @property {Boolean} if true, the API call will not have any more changes + # available in the nearby future, so the application should wait for at + # least 5 miuntes before issuing another pullChanges request + shouldBackOff: undefined + + # Creates a new Dropbox.PulledChanges instance from a /delta API call result. + # + # @private + # This constructor is used by Dropbox.PulledChanges, and should not be called + # directly. + # + # @param {?Object} deltaInfo the parsed JSON of a /delta API call result + constructor: (deltaInfo) -> + @blankSlate = deltaInfo.reset or false + @cursorTag = deltaInfo.cursor + @shouldPullAgain = deltaInfo.has_more + @shouldBackOff = not @shouldPullAgain + if deltaInfo.cursor and deltaInfo.cursor.length + @changes = (Dropbox.PullChange.parse entry for entry in deltaInfo.entries) + else + @changes = [] + +# Wraps a single change in a pullChanges result. +class Dropbox.PullChange + # Creates a Dropbox.PullChange instance wrapping an entry in a /delta result. + # + # @param {?Object} entry the parsed JSON of a single entry in a /delta API + # call result + # @return {?Dropbox.PullChange} a Dropbox.PullChange instance wrapping the + # given entry of a /delta API call; if the parameter does not look like + # parsed JSON, it is returned as is + @parse: (entry) -> + if entry and typeof entry is 'object' + new Dropbox.PullChange entry + else + entry + + # @property {String} the path of the changed file or folder + path: undefined + + # @property {Boolean} if true, this change is a deletion of the file or folder + # at the change's path; if a folder is deleted, all its contents (files + # and sub-folders) were also be deleted; pullChanges might not return + # separate changes expressing for the files or sub-folders + wasRemoved: undefined + + # @property {?Dropbox.Stat} a Stat instance containing updated information for + # the file or folder; this is null if the change is a deletion + stat: undefined + + # Creates a Dropbox.PullChange instance wrapping an entry in a /delta result. + # + # @private + # This constructor is used by Dropbox.PullChange.parse, and should not be + # called directly. + # + # @param {?Object} entry the parsed JSON of a single entry in a /delta API + # call result + constructor: (entry) -> + @path = entry[0] + @stat = Dropbox.Stat.parse entry[1] + if @stat + @wasRemoved = false + else + @stat = null + @wasRemoved = true diff --git a/lib/client/storage/dropbox/src/references.coffee b/lib/client/storage/dropbox/src/references.coffee new file mode 100644 index 00000000..f5e62437 --- /dev/null +++ b/lib/client/storage/dropbox/src/references.coffee @@ -0,0 +1,86 @@ +# Wraps an URL to a Dropbox file or folder that can be publicly shared. +class Dropbox.PublicUrl + # Creates a PublicUrl instance from a raw API response. + # + # @param {?Object} urlData the parsed JSON describing a public URL + # @param {Boolean} isDirect true if this is a direct download link, false if + # is a file / folder preview link + # @return {?Dropbox.PublicUrl} a PublicUrl instance wrapping the given public + # link info; parameters that don't look like parsed JSON are returned as + # they are + @parse: (urlData, isDirect) -> + if urlData and typeof urlData is 'object' + new Dropbox.PublicUrl urlData, isDirect + else + urlData + + # @property {String} the public URL + url: undefined + + # @property {Date} after this time, the URL is not usable + expiresAt: undefined + + # @property {Boolean} true if this is a direct download URL, false for URLs to + # preview pages in the Dropbox web app; folders do not have direct link + # + isDirect: undefined + + # @property {Boolean} true if this is URL points to a file's preview page in + # Dropbox, false for direct links + isPreview: undefined + + # Creates a PublicUrl instance from a raw API response. + # + # @private + # This constructor is used by Dropbox.PublicUrl.parse, and should not be + # called directly. + # + # @param {?Object} urlData the parsed JSON describing a public URL + # @param {Boolean} isDirect true if this is a direct download link, false if + # is a file / folder preview link + constructor: (urlData, isDirect) -> + @url = urlData.url + @expiresAt = new Date Date.parse(urlData.expires) + + if isDirect is true + @isDirect = true + else if isDirect is false + @isDirect = false + else + @isDirect = Date.now() - @expiresAt <= 86400000 # 1 day + @isPreview = !@isDirect + +# Reference to a file that can be used to make a copy across users' Dropboxes. +class Dropbox.CopyReference + # Creates a CopyReference instance from a raw reference or API response. + # + # @param {?Object, ?String} refData the parsed JSON descring a copy + # reference, or the reference string + @parse: (refData) -> + if refData and (typeof refData is 'object' or typeof refData is 'string') + new Dropbox.CopyReference refData + else + refData + + # @property {String} the raw reference, for use with Dropbox APIs + tag: undefined + + # @property {Date} deadline for using the reference in a copy operation + expiresAt: undefined + + # Creates a CopyReference instance from a raw reference or API response. + # + # @private + # This constructor is used by Dropbox.CopyReference.parse, and should not be + # called directly. + # + # @param {?Object, ?String} refData the parsed JSON descring a copy + # reference, or the reference string + constructor: (refData) -> + if typeof refData is 'object' + @tag = refData.copy_ref + @expiresAt = new Date Date.parse(refData.expires) + else + @tag = refData + @expiresAt = new Date() + diff --git a/lib/client/storage/dropbox/src/stat.coffee b/lib/client/storage/dropbox/src/stat.coffee new file mode 100644 index 00000000..6fe45c44 --- /dev/null +++ b/lib/client/storage/dropbox/src/stat.coffee @@ -0,0 +1,127 @@ +# The result of stat-ing a file or directory in a user's Dropbox. +class Dropbox.Stat + # Creates a Stat instance from a raw "metadata" response. + # + # @param {?Object} metadata the result of parsing JSON API responses that are + # called "metadata" in the API documentation + # @return {?Dropbox.Stat} a Stat instance wrapping the given API response; + # parameters that aren't parsed JSON objects are returned as they are + @parse: (metadata) -> + if metadata and typeof metadata is 'object' + new Dropbox.Stat metadata + else + metadata + + # @property {String} the path of this file or folder, relative to the user's + # Dropbox or to the application's folder + path: null + + # @property {String} the name of this file or folder + name: null + + # @property {Boolean} if true, the file or folder's path is relative to the + # application's folder; otherwise, the path is relative to the user's + # Dropbox + inAppFolder: null + + # @property {Boolean} if true, this Stat instance describes a folder + isFolder: null + + # @property {Boolean} if true, this Stat instance describes a file + isFile: null + + # @property {Boolean} if true, the file or folder described by this Stat + # instance was from the user's Dropbox, and was obtained by an API call + # that returns deleted items + isRemoved: null + + # @property {String} name of an icon in Dropbox's icon library that most + # accurately represents this file or folder + # + # See the Dropbox API documentation to obtain the Dropbox icon library. + # https://www.dropbox.com/developers/reference/api#metadata + typeIcon: null + + # @property {String} an identifier for the contents of the described file or + # directories; this can used to be restored a file's contents to a + # previous version, or to save bandwidth by not retrieving the same + # folder contents twice + versionTag: null + + # @property {String} a guess of the MIME type representing the file or folder's + # contents + mimeType: null + + # @property {Number} the size of the file, in bytes; null for folders + size: null + + # @property {String} the size of the file, in a human-readable format, such as + # "225.4KB"; the format of this string is influenced by the API client's + # locale + humanSize: null + + # @property {Boolean} if false, the URL generated by thumbnailUrl does not + # point to a valid image, and should not be used + hasThumbnail: null + + # @property {Date} the file or folder's last modification time + modifiedAt: null + + # @property {?Date} the file or folder's last modification time, as reported + # by the Dropbox client that uploaded the file; this time should not be + # trusted, but can be used for UI (display, sorting); null if the server + # does not report any time + clientModifiedAt: null + + # Creates a Stat instance from a raw "metadata" response. + # + # @private + # This constructor is used by Dropbox.Stat.parse, and should not be called + # directly. + # + # @param {Object} metadata the result of parsing JSON API responses that are + # called "metadata" in the API documentation + constructor: (metadata) -> + @path = metadata.path + # Ensure there is a trailing /, to make path processing reliable. + @path = '/' + @path if @path.substring(0, 1) isnt '/' + # Strip any trailing /, to make path joining predictable. + lastIndex = @path.length - 1 + if lastIndex >= 0 and @path.substring(lastIndex) is '/' + @path = @path.substring 0, lastIndex + + nameSlash = @path.lastIndexOf '/' + @name = @path.substring nameSlash + 1 + + @isFolder = metadata.is_dir || false + @isFile = !@isFolder + @isRemoved = metadata.is_deleted || false + @typeIcon = metadata.icon + if metadata.modified?.length + @modifiedAt = new Date Date.parse(metadata.modified) + else + @modifiedAt = null + if metadata.client_mtime?.length + @clientModifiedAt = new Date Date.parse(metadata.client_mtime) + else + @clientModifiedAt = null + + switch metadata.root + when 'dropbox' + @inAppFolder = false + when 'app_folder' + @inAppFolder = true + else + # New "root" value that we're not aware of. + @inAppFolder = null + + @size = metadata.bytes or 0 + @humanSize = metadata.size or '' + @hasThumbnail = metadata.thumb_exists or false + + if @isFolder + @versionTag = metadata.hash + @mimeType = metadata.mime_type || 'inode/directory' + else + @versionTag = metadata.rev + @mimeType = metadata.mime_type || 'application/octet-stream' diff --git a/lib/client/storage/dropbox/src/user_info.coffee b/lib/client/storage/dropbox/src/user_info.coffee new file mode 100644 index 00000000..fa61f212 --- /dev/null +++ b/lib/client/storage/dropbox/src/user_info.coffee @@ -0,0 +1,79 @@ +# Information about a Dropbox user. +class Dropbox.UserInfo + # Creates a UserInfo instance from a raw API response. + # + # @param {?Object} userInfo the result of parsing a JSON API response that + # describes a user + # @return {Dropbox.UserInfo} a UserInfo instance wrapping the given API + # response; parameters that aren't parsed JSON objects are returned as + # the are + @parse: (userInfo) -> + if userInfo and typeof userInfo is 'object' + new Dropbox.UserInfo userInfo + else + userInfo + + # @property {String} the user's name, in a form that is fit for display + name: null + + # @property {?String} the user's email; this is not in the official API + # documentation, so it might not be supported + email: null + + # @property {?String} two-letter country code, or null if unavailable + countryCode: null + + # @property {String} unique ID for the user; this ID matches the unique ID + # returned by the authentication process + uid: null + + # @property {String} + referralUrl: null + + # Specific to applications whose access type is "public app folder". + # + # @property {String} prefix for URLs to the application's files + publicAppUrl: null + + # @property {Number} the maximum amount of bytes that the user can store + quota: null + + # @property {Number} the number of bytes taken up by the user's data + usedQuota: null + + # @property {Number} the number of bytes taken up by the user's data that is + # not shared with other users + privateBytes: null + + # @property {Number} the number of bytes taken up by the user's data that is + # shared with other users + sharedBytes: null + + # Creates a UserInfo instance from a raw API response. + # + # @private + # This constructor is used by Dropbox.UserInfo.parse, and should not be + # called directly. + # + # @param {?Object} userInfo the result of parsing a JSON API response that + # describes a user + constructor: (userInfo) -> + @name = userInfo.display_name + @email = userInfo.email + @countryCode = userInfo.country or null + @uid = userInfo.uid.toString() + if userInfo.public_app_url + @publicAppUrl = userInfo.public_app_url + lastIndex = @publicAppUrl.length - 1 + # Strip any trailing /, to make path joining predictable. + if lastIndex >= 0 and @publicAppUrl.substring(lastIndex) is '/' + @publicAppUrl = @publicAppUrl.substring 0, lastIndex + else + @publicAppUrl = null + + @referralUrl = userInfo.referral_link + @quota = userInfo.quota_info.quota + @privateBytes = userInfo.quota_info.normal or 0 + @sharedBytes = userInfo.quota_info.shared or 0 + @usedQuota = @privateBytes + @sharedBytes + diff --git a/lib/client/storage/dropbox/src/xhr.coffee b/lib/client/storage/dropbox/src/xhr.coffee new file mode 100644 index 00000000..ef630f67 --- /dev/null +++ b/lib/client/storage/dropbox/src/xhr.coffee @@ -0,0 +1,450 @@ +if window? + if window.XDomainRequest and not ('withCredentials' of new XMLHttpRequest()) + DropboxXhrRequest = window.XDomainRequest + DropboxXhrIeMode = true + # IE's XDR doesn't allow setting requests' Content-Type to anything other + # than text/plain, so it can't send _any_ forms. + DropboxXhrCanSendForms = false + else + DropboxXhrRequest = window.XMLHttpRequest + DropboxXhrIeMode = false + # Firefox doesn't support adding named files to FormData. + # https://bugzilla.mozilla.org/show_bug.cgi?id=690659 + DropboxXhrCanSendForms = + window.navigator.userAgent.indexOf('Firefox') is -1 + DropboxXhrDoesPreflight = true +else + # Node.js needs an adapter for the XHR API. + DropboxXhrRequest = require('xmlhttprequest').XMLHttpRequest + DropboxXhrIeMode = false + # Node.js doesn't have FormData. We wouldn't want to bother putting together + # upload forms in node.js anyway, because it doesn't do CORS preflight + # checks, so we can use PUT requests without a performance hit. + DropboxXhrCanSendForms = false + # Node.js is a server so it doesn't do annoying browser checks. + DropboxXhrDoesPreflight = false + +# ArrayBufferView isn't available in the global namespce. +# +# Using the hack suggested in +# https://code.google.com/p/chromium/issues/detail?id=60449 +if typeof Uint8Array is 'undefined' + DropboxXhrArrayBufferView = null +else + DropboxXhrArrayBufferView = + (new Uint8Array(0)).__proto__.__proto__.constructor + +# Dispatches low-level AJAX calls (XMLHttpRequests). +class Dropbox.Xhr + # The object used to perform AJAX requests (XMLHttpRequest). + @Request = DropboxXhrRequest + # Set to true when using the XDomainRequest API. + @ieMode = DropboxXhrIeMode + # Set to true if the platform has proper support for FormData. + @canSendForms = DropboxXhrCanSendForms + # Set to true if the platform performs CORS preflight checks. + @doesPreflight = DropboxXhrDoesPreflight + @ArrayBufferView = DropboxXhrArrayBufferView + + # Sets up an AJAX request. + # + # @param {String} method the HTTP method used to make the request ('GET', + # 'POST', 'PUT', etc.) + # @param {String} baseUrl the URL that receives the request; this URL might + # be modified, e.g. by appending parameters for GET requests + constructor: (@method, baseUrl) -> + @isGet = @method is 'GET' + @url = baseUrl + @headers = {} + @params = null + @body = null + @preflight = not (@isGet or (@method is 'POST')) + @signed = false + @responseType = null + @callback = null + @xhr = null + + # Sets the parameters (form field values) that will be sent with the request. + # + # @param {?Object} params an associative array (hash) containing the HTTP + # request parameters + # @return {Dropbox.Xhr} this, for easy call chaining + setParams: (params) -> + if @signed + throw new Error 'setParams called after addOauthParams or addOauthHeader' + if @params + throw new Error 'setParams cannot be called twice' + @params = params + @ + + # Sets the function called when the XHR completes. + # + # This function can also be set when calling Dropbox.Xhr#send. + # + # @param {function(?Dropbox.ApiError, ?Object, ?Object)} callback called when + # the XHR completes; if an error occurs, the first parameter will be a + # Dropbox.ApiError instance; otherwise, the second parameter will be an + # instance of the required response type (e.g., String, Blob), and the + # third parameter will be the JSON-parsed 'x-dropbox-metadata' header + # @return {Dropbox.Xhr} this, for easy call chaining + setCallback: (@callback) -> + @ + + # Ammends the request parameters to include an OAuth signature. + # + # The OAuth signature will become invalid if the parameters are changed after + # the signing process. + # + # This method automatically decides the best way to add the OAuth signature + # to the current request. Modifying the request in any way (e.g., by adding + # headers) might result in a valid signature that is applied in a sub-optimal + # fashion. For best results, call this right before Dropbox.Xhr#prepare. + # + signWithOauth: (oauth) -> + if Dropbox.Xhr.ieMode or (Dropbox.Xhr.doesPreflight and (not @preflight)) + @addOauthParams oauth + else + @addOauthHeader oauth + + # Ammends the request parameters to include an OAuth signature. + # + # The OAuth signature will become invalid if the parameters are changed after + # the signing process. + # + # @param {Dropbox.Oauth} oauth OAuth instance whose key and secret will be + # used to sign the request + # @return {Dropbox.Xhr} this, for easy call chaining + addOauthParams: (oauth) -> + if @signed + throw new Error 'Request already has an OAuth signature' + + @params or= {} + oauth.addAuthParams @method, @url, @params + @signed = true + @ + + # Adds an Authorize header containing an OAuth signature. + # + # The OAuth signature will become invalid if the parameters are changed after + # the signing process. + # + # @param {Dropbox.Oauth} oauth OAuth instance whose key and secret will be + # used to sign the request + # @return {Dropbox.Xhr} this, for easy call chaining + addOauthHeader: (oauth) -> + if @signed + throw new Error 'Request already has an OAuth signature' + + @params or= {} + @signed = true + @setHeader 'Authorization', oauth.authHeader(@method, @url, @params) + + # Sets the body (piece of data) that will be sent with the request. + # + # @param {String, Blob, ArrayBuffer} body the body to be sent in a request; + # GET requests cannot have a body + # @return {Dropbox.Xhr} this, for easy call chaining + setBody: (body) -> + if @isGet + throw new Error 'setBody cannot be called on GET requests' + if @body isnt null + throw new Error 'Request already has a body' + + unless @preflight + unless (typeof FormData isnt 'undefined') and (body instanceof FormData) + @preflight = true + + @body = body + @ + + # Sends off an AJAX request and requests a custom response type. + # + # This method requires XHR Level 2 support, which is not available in IE + # versions <= 9. If these browsers must be supported, it is recommended to + # check whether window.Blob is truthy. + # + # @param {String} responseType the value that will be assigned to the XHR's + # responseType property + # @return {Dropbox.Xhr} this, for easy call chaining + setResponseType: (@responseType) -> + @ + + # Sets the value of a custom HTTP header. + # + # Custom HTTP headers require a CORS preflight in browsers, so requests that + # use them will take more time to complete, especially on high-latency mobile + # connections. + # + # @param {String} headerName the name of the HTTP header + # @param {String} value the value that the header will be set to + # @return {Dropbox.Xhr} this, for easy call chaining + setHeader: (headerName, value) -> + if @headers[headerName] + oldValue = @headers[headerName] + throw new Error "HTTP header #{headerName} already set to #{oldValue}" + if headerName is 'Content-Type' + throw new Error 'Content-Type is automatically computed based on setBody' + @preflight = true + @headers[headerName] = value + @ + + # Simulates having an being sent with the request. + # + # @param {String} fieldName the name of the form field / parameter (not of + # the uploaded file) + # @param {String} fileName the name of the uploaded file (not the name of the + # form field / parameter) + # @param {String, Blob, File} fileData contents of the file to be uploaded + # @param {?String} contentType the MIME type of the file to be uploaded; if + # fileData is a Blob or File, its MIME type is used instead + setFileField: (fieldName, fileName, fileData, contentType) -> + if @body isnt null + throw new Error 'Request already has a body' + + if @isGet + throw new Error 'paramsToBody cannot be called on GET requests' + + if typeof(fileData) is 'object' and typeof Blob isnt 'undefined' + if ArrayBuffer? and fileData instanceof ArrayBuffer + fileData = new Uint8Array fileData + if Dropbox.Xhr.ArrayBufferView and + fileData instanceof Dropbox.Xhr.ArrayBufferView + contentType or= 'application/octet-stream' + fileData = new Blob [fileData], type: contentType + # Workaround for http://crbug.com/165095 + if typeof File isnt 'undefined' and fileData instanceof File + fileData = new Blob [fileData], type: fileData.type + #fileData = fileData + useFormData = fileData instanceof Blob + else + useFormData = false + + if useFormData + @body = new FormData() + @body.append fieldName, fileData, fileName + else + contentType or= 'application/octet-stream' + boundary = @multipartBoundary() + @headers['Content-Type'] = "multipart/form-data; boundary=#{boundary}" + @body = ['--', boundary, "\r\n", + 'Content-Disposition: form-data; name="', fieldName, + '"; filename="', fileName, "\"\r\n", + 'Content-Type: ', contentType, "\r\n", + "Content-Transfer-Encoding: binary\r\n\r\n", + fileData, + "\r\n", '--', boundary, '--', "\r\n"].join '' + + # @private + # @return {String} a nonce suitable for use as a part boundary in a multipart + # MIME message + multipartBoundary: -> + [Date.now().toString(36), Math.random().toString(36)].join '----' + + # Moves this request's parameters to its URL. + # + # @private + # @return {Dropbox.Xhr} this, for easy call chaining + paramsToUrl: -> + if @params + queryString = Dropbox.Xhr.urlEncode @params + if queryString.length isnt 0 + @url = [@url, '?', queryString].join '' + @params = null + @ + + # Moves this request's parameters to its body. + # + # @private + # @return {Dropbox.Xhr} this, for easy call chaining + paramsToBody: -> + if @params + if @body isnt null + throw new Error 'Request already has a body' + if @isGet + throw new Error 'paramsToBody cannot be called on GET requests' + @headers['Content-Type'] = 'application/x-www-form-urlencoded' + @body = Dropbox.Xhr.urlEncode @params + @params = null + @ + + # Sets up an XHR request. + # + # This method completely sets up a native XHR object and stops short of + # calling its send() method, so the API client has a chance of customizing + # the XHR. After customizing the XHR, Dropbox.Xhr#send should be called. + # + # + # @return {Dropbox.Xhr} this, for easy call chaining + prepare: -> + ieMode = Dropbox.Xhr.ieMode + if @isGet or @body isnt null or ieMode + @paramsToUrl() + if @body isnt null and typeof @body is 'string' + @headers['Content-Type'] = 'text/plain; charset=utf8' + else + @paramsToBody() + + @xhr = new Dropbox.Xhr.Request() + if ieMode + @xhr.onload = => @onLoad() + @xhr.onerror = => @onError() + else + @xhr.onreadystatechange = => @onReadyStateChange() + @xhr.open @method, @url, true + + unless ieMode + for own header, value of @headers + @xhr.setRequestHeader header, value + + if @responseType + if @responseType is 'b' + if @xhr.overrideMimeType + @xhr.overrideMimeType 'text/plain; charset=x-user-defined' + else + @xhr.responseType = @responseType + + @ + + # Fires off the prepared XHR request. + # + # Dropbox.Xhr#prepare should be called exactly once before this method. + # + # @param {function(?Dropbox.ApiError, ?Object, ?Object)} callback called when + # the XHR completes; if an error occurs, the first parameter will be a + # Dropbox.ApiError instance; otherwise, the second parameter will be an + # instance of the required response type (e.g., String, Blob), and the + # third parameter will be the JSON-parsed 'x-dropbox-metadata' header + # @return {Dropbox.Xhr} this, for easy call chaining + send: (callback) -> + @callback = callback or @callback + + if @body isnt null + body = @body + # send() in XHR doesn't like naked ArrayBuffers + if Dropbox.Xhr.ArrayBufferView and body instanceof ArrayBuffer + body = new Uint8Array body + + try + @xhr.send body + catch e + # Firefox doesn't support sending ArrayBufferViews. + # Node.js doesn't implement Blob. + if typeof Blob isnt 'undefined' and Dropbox.Xhr.ArrayBufferView and + body instanceof Dropbox.Xhr.ArrayBufferView + body = new Blob [body], type: 'application/octet-stream' + @xhr.send body + else + throw e + else + @xhr.send() + @ + + # Encodes an associative array (hash) into a x-www-form-urlencoded String. + # + # For consistency, the keys are sorted in alphabetical order in the encoded + # output. + # + # @param {Object} object the JavaScript object whose keys will be encoded + # @return {String} the object's keys and values, encoded using + # x-www-form-urlencoded + @urlEncode: (object) -> + chunks = [] + for key, value of object + chunks.push @urlEncodeValue(key) + '=' + @urlEncodeValue(value) + chunks.sort().join '&' + + # Encodes an object into a x-www-form-urlencoded key or value. + # + # @param {Object} object the object to be encoded; the encoding calls + # toString() on the object to obtain its string representation + # @return {String} encoded string, suitable for use as a key or value in an + # x-www-form-urlencoded string + @urlEncodeValue: (object) -> + encodeURIComponent(object.toString()).replace(/\!/g, '%21'). + replace(/'/g, '%27').replace(/\(/g, '%28').replace(/\)/g, '%29'). + replace(/\*/g, '%2A') + + # Decodes an x-www-form-urlencoded String into an associative array (hash). + # + # @param {String} string the x-www-form-urlencoded String to be decoded + # @return {Object} an associative array whose keys and values are all strings + @urlDecode: (string) -> + result = {} + for token in string.split '&' + kvp = token.split '=' + result[decodeURIComponent(kvp[0])] = decodeURIComponent kvp[1] + result + + # Handles the XHR readystate event. + onReadyStateChange: -> + return true if @xhr.readyState isnt 4 # XMLHttpRequest.DONE is 4 + + if @xhr.status < 200 or @xhr.status >= 300 + apiError = new Dropbox.ApiError @xhr, @method, @url + @callback apiError + return true + + metadataJson = @xhr.getResponseHeader 'x-dropbox-metadata' + if metadataJson?.length + try + metadata = JSON.parse metadataJson + catch e + # Make sure the app doesn't crash if the server goes crazy. + metadata = undefined + else + metadata = undefined + + if @responseType + if @responseType is 'b' + dirtyText = if @xhr.responseText? + @xhr.responseText + else + @xhr.response + ### + jsString = ['["'] + for i in [0...dirtyText.length] + hexByte = (dirtyText.charCodeAt(i) & 0xFF).toString(16) + if hexByte.length is 2 + jsString.push "\\u00#{hexByte}" + else + jsString.push "\\u000#{hexByte}" + jsString.push '"]' + console.log jsString + text = JSON.parse(jsString.join(''))[0] + ### + bytes = [] + for i in [0...dirtyText.length] + bytes.push String.fromCharCode(dirtyText.charCodeAt(i) & 0xFF) + text = bytes.join '' + @callback null, text, metadata + else + @callback null, @xhr.response, metadata + return true + + text = if @xhr.responseText? then @xhr.responseText else @xhr.response + switch @xhr.getResponseHeader('Content-Type') + when 'application/x-www-form-urlencoded' + @callback null, Dropbox.Xhr.urlDecode(text), metadata + when 'application/json', 'text/javascript' + @callback null, JSON.parse(text), metadata + else + @callback null, text, metadata + true + + # Handles the XDomainRequest onload event. (IE 8, 9) + onLoad: -> + text = @xhr.responseText + switch @xhr.contentType + when 'application/x-www-form-urlencoded' + @callback null, Dropbox.Xhr.urlDecode(text), undefined + when 'application/json', 'text/javascript' + @callback null, JSON.parse(text), undefined + else + @callback null, text, undefined + true + + # Handles the XDomainRequest onload event. (IE 8, 9) + onError: -> + apiError = new Dropbox.ApiError @xhr, @method, @url + @callback apiError + return true diff --git a/lib/client/storage/dropbox/src/zzz-export.coffee b/lib/client/storage/dropbox/src/zzz-export.coffee new file mode 100644 index 00000000..45e47036 --- /dev/null +++ b/lib/client/storage/dropbox/src/zzz-export.coffee @@ -0,0 +1,21 @@ +# All changes to the global namespace happen here. + +# This file's name is set up in such a way that it will always show up last in +# the source directory. This makes coffee --join work as intended. + +if module?.exports? + # We're a node.js module, so export the Dropbox class. + module.exports = Dropbox +else if window? + # We're in a browser, so add Dropbox to the global namespace. + window.Dropbox = Dropbox +else + throw new Error('This library only supports node.js and modern browsers.') + +# These are mostly useful for testing. Clients shouldn't use internal stuff. +Dropbox.atob = atob +Dropbox.btoa = btoa +Dropbox.hmac = base64HmacSha1 +Dropbox.sha1 = base64Sha1 +Dropbox.encodeKey = dropboxEncodeKey + diff --git a/lib/client/storage/dropbox/txt.vimrc.txt b/lib/client/storage/dropbox/txt.vimrc.txt new file mode 100644 index 00000000..b6779843 --- /dev/null +++ b/lib/client/storage/dropbox/txt.vimrc.txt @@ -0,0 +1,6 @@ +" Indentation settings for the project: 2-space indentation, no tabs. +set tabstop=2 +set softtabstop=2 +set shiftwidth=2 +set expandtab + From 869cff58e564d22e48dc7ceaa1896ddd747dd7bb Mon Sep 17 00:00:00 2001 From: coderaiser Date: Mon, 17 Dec 2012 11:03:24 -0500 Subject: [PATCH 039/347] minor changes --- lib/client/menu.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/client/menu.js b/lib/client/menu.js index 164f4cc6..b74cb460 100644 --- a/lib/client/menu.js +++ b/lib/client/menu.js @@ -123,7 +123,7 @@ var CloudCommander, Util, DOM, $; else Util.exec(cloudcmd.GDrive, pParams); }); - + Util.log('Uploading to gdrive...'); } }, From 452042b75c31afcac4d40213098ff23416aa2f41 Mon Sep 17 00:00:00 2001 From: coderaiser Date: Tue, 18 Dec 2012 06:32:58 -0500 Subject: [PATCH 040/347] added ability to authorize on github thru new window, changed redirecting url to /auth and added rout function to cloudcmd.js --- ChangeLog | 3 ++ cloudcmd.js | 25 +++++++++++++++-- index.html | 46 ------------------------------- lib/client/storage/_github.js | 20 ++++++++------ lib/cloudfunc.js | 1 - lib/server.js | 52 +++++++++++++++++++++-------------- lib/server/main.js | 3 +- lib/server/minify.js | 5 ++-- shell/kill.js | 1 + 9 files changed, 76 insertions(+), 80 deletions(-) delete mode 100644 index.html diff --git a/ChangeLog b/ChangeLog index 96288fd6..4d981243 100644 --- a/ChangeLog +++ b/ChangeLog @@ -28,6 +28,9 @@ inside event function varible called event do not exist * Added ability to upload files to dropbox. +* Added ability to authorize on github thru new window, +changed redirecting url to /auth and added rout function +to cloudcmd.js 2012.12.12, Version 0.1.8 diff --git a/cloudcmd.js b/cloudcmd.js index 596e188a..c8b9d521 100644 --- a/cloudcmd.js +++ b/cloudcmd.js @@ -6,6 +6,7 @@ LIBDIR = main.LIBDIR, SRVDIR = main.SRVDIR, + CLIENTDIR = LIBDIR + 'client', path = main.path, fs = main.fs, @@ -23,9 +24,10 @@ readConfig(); Server.start(Config, { - index : indexProcessing, appcache : appCacheProcessing, - rest : rest + index : indexProcessing, + rest : rest, + route : route }); if(update) @@ -134,6 +136,25 @@ } } + /** + * routing of server queries + */ + function route(pParams){ + Util.log('* Routing'); + var lRet, + lName = pParams.name; + + if( Util.strCmp(lName, '/auth') ){ + Util.log('-> auth'); + pParams.name = main.HTMLDIR + lName + '.html'; + main.sendFile(pParams); + lRet = true; + } + + return lRet; + } + + /* function sets stdout to file log.txt */ function writeLogsToFile(){ var stdo = fs.createWriteStream('./log.txt'); diff --git a/index.html b/index.html deleted file mode 100644 index 85d13d8c..00000000 --- a/index.html +++ /dev/null @@ -1,46 +0,0 @@ - - - - - - - Cloud Commander - - - - - - - -
    -
    -
    - - - - - - - - -
    - - - - - - \ No newline at end of file diff --git a/lib/client/storage/_github.js b/lib/client/storage/_github.js index 0f7ba500..fc9a1bf8 100644 --- a/lib/client/storage/_github.js +++ b/lib/client/storage/_github.js @@ -51,15 +51,16 @@ var CloudCommander, Util, DOM, $, Github, cb; }); } - function init(pCallBack){ + + GithubStore.init = function(pCallBack, pCode){ var lToken = Cache.get('token'); if(lToken){ GithubStore.Login(lToken); Util.exec(pCallBack); } else{ - var lCode = window.location.search; - if ( Util.isContainStr(lCode, '?code=') ){ + var lCode = pCode || window.location.search; + if (pCode || Util.isContainStr(lCode, '?code=') ){ lCode = lCode.replace('?code=',''); var lSuccess = function(pData){ @@ -83,14 +84,17 @@ var CloudCommander, Util, DOM, $, Github, cb; DOM.ajax(lData); } else - //window.open('welcome.html', 'welcome','width=300,height=200,menubar=yes,status=yes')"> + window.open('https://github.com/login/oauth/authorize?client_id=' + + GitHub_ID + '&&scope=repo,user,gist', 'Cloud Commander Auth'); + /* window.location = 'https://github.com/login/oauth/authorize?client_id=' + GitHub_ID + '&&scope=repo,user,gist'; + */ } - } + }; - function getUserData(pCallBack){ + GithubStore.getUserData = function(pCallBack){ var lName, lShowUserInfo = function(pError, pData){ @@ -163,8 +167,8 @@ var CloudCommander, Util, DOM, $, Github, cb; cloudcmd.GitHub.init = function(pCallBack){ Util.loadOnLoad([ Util.retExec(pCallBack), - getUserData, - init, + GithubStore.getUserData, + GithubStore.init, setConfig, load ]); diff --git a/lib/cloudfunc.js b/lib/cloudfunc.js index 333bfacf..18b2e5ec 100644 --- a/lib/cloudfunc.js +++ b/lib/cloudfunc.js @@ -32,7 +32,6 @@ var CloudFunc, exports; /* length of longest file name */ CloudFunc.SHORTNAMELENGTH = 16; - /** * Функция убирает последний слеш, * если он - последний символ строки diff --git a/lib/server.js b/lib/server.js index 3bf397d6..f14e7c47 100644 --- a/lib/server.js +++ b/lib/server.js @@ -58,7 +58,7 @@ Server : {}, /* КОНСТАНТЫ */ - INDEX : main.DIR + 'index.html' + INDEX : main.DIR + 'html/index.html' }, DirPath = '/', @@ -134,6 +134,7 @@ CloudServer.indexProcessing = pProcessing.index; CloudServer.rest = pProcessing.rest; + CloudServer.route = pProcessing.route; this.init(pProcessing.appcache); @@ -182,9 +183,9 @@ { /* Читаем содержимое папки, переданное в url */ var lConfig = CloudServer.Config, - url = main.url, - lParsedUrl = url.parse(pReq.url), - pathname = lParsedUrl.pathname, + lURL = main.url, + lParsedUrl = lURL.parse(pReq.url), + lPath = lParsedUrl.pathname, /* varible contain one of queris: * download - change content-type for @@ -196,13 +197,13 @@ * query like this * ?json */ - lQuery = lParsedUrl.query; + lQuery = lParsedUrl.query; if(lQuery) console.log('query = ' + lQuery); /* added supporting of Russian language in directory names */ - pathname = Querystring.unescape(pathname); - console.log('pathname: ' + pathname); + lPath = Querystring.unescape(lPath); + console.log('pathname: ' + lPath); /* получаем поддерживаемые браузером кодировки*/ var lAcceptEncoding = pReq.headers['accept-encoding']; @@ -213,14 +214,14 @@ 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..."); + console.log("request for " + lPath + " received..."); if( lConfig.rest ){ var lRestWas = Util.exec(CloudServer.rest, { @@ -232,21 +233,32 @@ return; } + if( CloudServer.route){ + var lRouteWas = Util.exec(CloudServer.route, { + name : lPath, + request : pReq, + response : pRes + }); + + if(lRouteWas) + return; + } + /* если в пути нет информации ни о ФС, * ни об отсутствии js, * ни о том, что это корневой * каталог - загружаем файлы проэкта */ - if ( !Util.isContainStr(pathname, lFS_s) && - !Util.isContainStr(pathname, lNoJS_s) && - !Util.strCmp(pathname, '/') && + if ( !Util.isContainStr(lPath, lFS_s) && + !Util.isContainStr(lPath, lNoJS_s) && + !Util.strCmp(lPath, '/') && !Util.strCmp(lQuery, 'json') ) { /* если имена файлов проекта - загружаем их * * убираем слеш и читаем файл с текущец директории */ /* добавляем текующий каталог к пути */ - var lName = '.' + pathname; + var lName = '.' + lPath; console.log('reading ' + lName); /* watching is file changed */ @@ -346,16 +358,16 @@ * длиннее */ - if(pathname.indexOf(lNoJS_s) !== lFS_s.length && pathname !== '/') + if(lPath.indexOf(lNoJS_s) !== lFS_s.length && lPath !== '/') CloudServer.NoJS = false; else{ CloudServer.NoJS = true; - pathname = Util.removeStr(pathname, lNoJS_s); + lPath = Util.removeStr(lPath, lNoJS_s); } /* убираем индекс файловой системы */ - if(pathname.indexOf(lFS_s) === 0){ - pathname = Util.removeStr(pathname, lFS_s); + if(lPath.indexOf(lFS_s) === 0){ + lPath = Util.removeStr(lPath, lFS_s); /* если посетитель только зашел на сайт * no-js будет пустым, как и fs. @@ -375,10 +387,10 @@ /* если в итоге путь пустой * делаем его корневым */ - if (pathname === '') - pathname = '/'; + if (lPath === '') + lPath = '/'; - DirPath = pathname; + DirPath = lPath; CloudServer.Responses[DirPath] = pRes; diff --git a/lib/server/main.js b/lib/server/main.js index 67c2cd61..2de3ea00 100644 --- a/lib/server/main.js +++ b/lib/server/main.js @@ -48,6 +48,7 @@ exports.SRVDIR = SRVDIR = __dirname + SLASH, exports.LIBDIR = LIBDIR = path.normalize(SRVDIR + '../'), exports.DIR = DIR = path.normalize(LIBDIR + '../'), + exports.HTMLDIR = DIR + 'html/', /* Functions */ exports.generateHeaders = generateHeaders, @@ -180,7 +181,7 @@ * @param pGzip - данные сжаты gzip'ом */ function sendFile(pParams){ - var lRet = false, + var lRet, lName = pParams.name, lReq = pParams.request, lRes = pParams.response, diff --git a/lib/server/minify.js b/lib/server/minify.js index 3bcf6ff4..2db3fc90 100644 --- a/lib/server/minify.js +++ b/lib/server/minify.js @@ -5,11 +5,12 @@ var main = global.cloudcmd.main, DIR = main.DIR, - LIBDIR = main.LIBDIR; + LIBDIR = main.LIBDIR, + HTMLDIR = main.HTMLDIR; exports.Minify = { /* pathes to directories */ - INDEX : DIR + 'index.html', + INDEX : HTMLDIR + 'index.html', /* приватный переключатель минимизации */ _allowed :{ css : true, diff --git a/shell/kill.js b/shell/kill.js index 7b08dfb5..402593b3 100644 --- a/shell/kill.js +++ b/shell/kill.js @@ -1,3 +1,4 @@ + /* c9.io kill active node process */ (function(){ From d13c0de0b10ef1248525c303fec6ced37066f532 Mon Sep 17 00:00:00 2001 From: coderaiser Date: Tue, 18 Dec 2012 06:33:21 -0500 Subject: [PATCH 041/347] index.html moved to html dir --- html/auth.html | 7 +++++++ html/index.html | 46 ++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 53 insertions(+) create mode 100644 html/auth.html create mode 100644 html/index.html diff --git a/html/auth.html b/html/auth.html new file mode 100644 index 00000000..319102e5 --- /dev/null +++ b/html/auth.html @@ -0,0 +1,7 @@ + + + + + + + \ No newline at end of file diff --git a/html/index.html b/html/index.html new file mode 100644 index 00000000..85d13d8c --- /dev/null +++ b/html/index.html @@ -0,0 +1,46 @@ + + + + + + + Cloud Commander + + + + + + + +
    +
    +
    + + + + + + + + +
    + + + + + + \ No newline at end of file From c2df4431ebe7e3f1de5cc2500c2579f22ea0368e Mon Sep 17 00:00:00 2001 From: coderaiser Date: Tue, 18 Dec 2012 07:15:57 -0500 Subject: [PATCH 042/347] minor changes --- cloudcmd.js | 5 +++++ html/auth.html | 14 +++++++++++++- lib/client/storage/_github.js | 29 +++++++++++++++++++---------- 3 files changed, 37 insertions(+), 11 deletions(-) diff --git a/cloudcmd.js b/cloudcmd.js index c8b9d521..1ad7c4e3 100644 --- a/cloudcmd.js +++ b/cloudcmd.js @@ -149,6 +149,11 @@ pParams.name = main.HTMLDIR + lName + '.html'; main.sendFile(pParams); lRet = true; + }else if( Util.strCmp(lName, '/auth/github') ){ + Util.log('-> github'); + pParams.name = main.HTMLDIR + lName + '.html'; + main.sendFile(pParams); + lRet = true; } return lRet; diff --git a/html/auth.html b/html/auth.html index 319102e5..5390efe7 100644 --- a/html/auth.html +++ b/html/auth.html @@ -2,6 +2,18 @@ - + \ No newline at end of file diff --git a/lib/client/storage/_github.js b/lib/client/storage/_github.js index fc9a1bf8..1892a137 100644 --- a/lib/client/storage/_github.js +++ b/lib/client/storage/_github.js @@ -72,7 +72,7 @@ var CloudCommander, Util, DOM, $, Github, cb; Util.exec(pCallBack); } else - Util.log("Worning: token not getted..."); + Util.log('Worning: token not getted...'); }, lData = { type : 'put', @@ -83,14 +83,23 @@ var CloudCommander, Util, DOM, $, Github, cb; DOM.ajax(lData); } - else - window.open('https://github.com/login/oauth/authorize?client_id=' + - GitHub_ID + '&&scope=repo,user,gist', 'Cloud Commander Auth'); - /* - window.location = - 'https://github.com/login/oauth/authorize?client_id=' + - GitHub_ID + '&&scope=repo,user,gist'; - */ + else{ + + var lUrl = '//' + window.location.host + '/auth/github', + left = 140, + top = 187, + width = 1000, + height = 650, + + lOptions = 'left=' + left + + ',top=' + top + + ',width=' + width + + ',height=' + height + + ',personalbar=0,toolbar=0' + + ',scrollbars=1,resizable=1'; + + window.open(lUrl, 'Cloud Commander Auth', lOptions); + } } }; @@ -143,7 +152,7 @@ var CloudCommander, Util, DOM, $, Github, cb; lFiles = {}, lHost = CloudCommander.HOST, lOptions = { - description: "Uplouded by Cloud Commander from " + lHost, + description: 'Uplouded by Cloud Commander from ' + lHost, public: true }; From 898091ce2711c9319b4d19eec9b754b1d08f4947 Mon Sep 17 00:00:00 2001 From: coderaiser Date: Tue, 18 Dec 2012 07:18:01 -0500 Subject: [PATCH 043/347] added github.html --- html/auth/github.html | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) create mode 100644 html/auth/github.html diff --git a/html/auth/github.html b/html/auth/github.html new file mode 100644 index 00000000..97cc1a14 --- /dev/null +++ b/html/auth/github.html @@ -0,0 +1,22 @@ + + + + + + + \ No newline at end of file From 2ca465cd61d8aa9107455ba2187a452de5f65cf5 Mon Sep 17 00:00:00 2001 From: coderaiser Date: Thu, 20 Dec 2012 04:58:02 -0500 Subject: [PATCH 044/347] chenged description --- cloudcmd.js | 11 +++-- lib/client/menu.js | 82 ++++++++++++++++------------------- lib/client/storage/_github.js | 2 +- package.json | 2 +- 4 files changed, 47 insertions(+), 50 deletions(-) diff --git a/cloudcmd.js b/cloudcmd.js index 1ad7c4e3..be4384be 100644 --- a/cloudcmd.js +++ b/cloudcmd.js @@ -140,19 +140,24 @@ * routing of server queries */ function route(pParams){ - Util.log('* Routing'); var lRet, lName = pParams.name; if( Util.strCmp(lName, '/auth') ){ - Util.log('-> auth'); + Util.log('* Routing' + + '-> auth'); + pParams.name = main.HTMLDIR + lName + '.html'; main.sendFile(pParams); + lRet = true; }else if( Util.strCmp(lName, '/auth/github') ){ - Util.log('-> github'); + Util.log('* Routing' + + '-> github'); + pParams.name = main.HTMLDIR + lName + '.html'; main.sendFile(pParams); + lRet = true; } diff --git a/lib/client/menu.js b/lib/client/menu.js index b74cb460..abcb815e 100644 --- a/lib/client/menu.js +++ b/lib/client/menu.js @@ -34,7 +34,7 @@ var CloudCommander, Util, DOM, $; } } - function getContent(pCallBack){ + function getCurrentData(pCallBack){ return DOM.getCurrentFileContent(function(pData){ var lName = DOM.getCurrentName(); if( Util.isObject(pData) ){ @@ -52,7 +52,35 @@ var CloudCommander, Util, DOM, $; }); } - /** function return configureation for menu */ + /** + * function get menu item object for Upload To + */ + function getUploadToItem(pObjectName){ + var lObj = {}; + lObj.name = pObjectName; + + lObj.callback = function(key, opt){ + getCurrentData(function(pParams){ + var lObject = cloudcmd[pObjectName], + lName = pParams.name, + lData = pParams.data; + if('init' in lObject) + lObject.uploadFile(lData, lName); + else + Util.exec(lObject, function(){ + cloudcmd[pObjectName].uploadFile(lData, lName); + }); + }); + + Util.log('Uploading to ' + pObjectName+ '...'); + }; + + return lObj; + } + + /** + * function return configureation for menu + */ function getConfig (){ return{ // define which elements trigger this menu @@ -71,7 +99,7 @@ var CloudCommander, Util, DOM, $; name : 'View', callback : function(key, opt){ showEditor(true); - } + } }, edit: { @@ -85,37 +113,18 @@ var CloudCommander, Util, DOM, $; name: 'Delete', callback: function(key, opt){ DOM.promptRemoveCurrent(); - } + } }, upload: { name: 'Upload to', items: { - 'gist': { - name: 'Gist', - callback: function(key, opt){ - getContent(function(pParams){ - var lGitHub = cloudcmd.GitHub, - lData = pParams.data, - lName = pParams.name; - - if('init' in lGitHub) - lGitHub.createGist(lData, lName); - else - Util.exec(cloudcmd.GitHub, - function(){ - lGitHub.createGist(lData, lName); - }); - }); - - Util.log('Uploading to gist...'); - } - }, + 'GitHub': getUploadToItem('GitHub'), 'gdrive': { name: 'GDrive', callback: function(key, opt){ - getContent(function(pParams){ + getCurrentData(function(pParams){ var lGDrive = cloudcmd.GDrive; if('init' in lGDrive) @@ -127,25 +136,8 @@ var CloudCommander, Util, DOM, $; Util.log('Uploading to gdrive...'); } }, - 'dropbox':{ - name: 'DropBox', - callback: function(key, opt){ - getContent(function(pParams){ - var lDropBox = cloudcmd.DropBox, - lData = pParams.data, - lName = pParams.name; - - if('init' in lDropBox) - lDropBox.uploadFile(lData, lName); - else - Util.exec(lDropBox, function(){ - cloudcmd.DropBox.uploadFile(lData, lName); - }); - }); - - Util.log('Uploading to dropbox...'); - } - } + + 'DropBox': getUploadToItem('DropBox') } }, @@ -277,7 +269,7 @@ var CloudCommander, Util, DOM, $; DOM.Images.hideLoad(); if(Position && Position.x && Position.y) - $('li').contextMenu(Position); + $('li').contextMenu(Position); else $('li').contextMenu(); }; diff --git a/lib/client/storage/_github.js b/lib/client/storage/_github.js index 1892a137..2163dffa 100644 --- a/lib/client/storage/_github.js +++ b/lib/client/storage/_github.js @@ -142,7 +142,7 @@ var CloudCommander, Util, DOM, $, Github, cb; /** * function creates gist */ - cloudcmd.GitHub.createGist = function(pContent, pFileName){ + cloudcmd.GitHub.uploadFile = function(pContent, pFileName){ if(pContent){ DOM.Images.showLoad(); if(!pFileName) diff --git a/package.json b/package.json index f330fae7..df1f8408 100644 --- a/package.json +++ b/package.json @@ -2,7 +2,7 @@ "name": "cloudcmd", "version": "0.1.8", "author": "coderaiser (https://github.com/coderaiser)", - "description": "Two-panels file manager, totally writed on js.", + "description": "User friendly cloud file manager.", "homepage": "https://github.com/coderaiser/cloudcmd", "repository": { "type": "git", From 56a89226912370611174c7e0b3f69fa34bc935cd Mon Sep 17 00:00:00 2001 From: coderaiser Date: Thu, 20 Dec 2012 07:05:04 -0500 Subject: [PATCH 045/347] chenged description --- config.json | 4 ++-- lib/client/storage/_gdrive.js | 35 +++++++++++++---------------------- 2 files changed, 15 insertions(+), 24 deletions(-) diff --git a/config.json b/config.json index c014fd05..aa2b1a72 100644 --- a/config.json +++ b/config.json @@ -5,8 +5,8 @@ "minification" : { "js" : false, "css" : false, - "html" : true, - "img" : true + "html" : false, + "img" : false }, "github_key" : "891c251b925e4e967fa9", "github_secret" : "afe9bed1e810c5dc44c4c2a953fc6efb1e5b0545", diff --git a/lib/client/storage/_gdrive.js b/lib/client/storage/_gdrive.js index bedf6c3d..04319b49 100644 --- a/lib/client/storage/_gdrive.js +++ b/lib/client/storage/_gdrive.js @@ -9,29 +9,20 @@ var CloudCommander, Util, DOM, gapi; function authorize(pData){ /* https://developers.google.com/drive/credentials */ - Util.setTimeout({ - func : function(pCallBack){ - var lCLIENT_ID = '255175681917.apps.googleusercontent.com', - lSCOPES = 'https://www.googleapis.com/auth/drive', - lParams = { - 'client_id' : lCLIENT_ID, - 'scope' : lSCOPES, - 'immediate' : true - }; - - gapi.auth.authorize(lParams, pCallBack); - }, - - callback : function(pAuthResult){ - var lRet; - if (pAuthResult && !pAuthResult.error){ - uploadFile(pData); - - lRet = true; - } - return lRet; - } + var lCLIENT_ID = '255175681917.apps.googleusercontent.com', + lSCOPES = 'https://www.googleapis.com/auth/drive', + lParams = { + 'client_id' : lCLIENT_ID, + 'scope' : lSCOPES, + 'immediate' : false + }; + + setTimeout(function() { + gapi.auth.authorize(lParams, function(pAuthResult){ + if (pAuthResult && !pAuthResult.error) + uploadFile(pData); }); + }, 500); } function load(pData){ From bfc4baab0f61a30ffb21668d5959cb2a3c2a54ab Mon Sep 17 00:00:00 2001 From: coderaiser Date: Fri, 21 Dec 2012 08:41:49 -0500 Subject: [PATCH 046/347] DropBox, GDrive and GitHub modules now look the same --- ChangeLog | 3 + lib/client/dom.js | 20 +++++ lib/client/menu.js | 31 ++------ lib/client/storage/_dropbox.js | 31 ++++---- lib/client/storage/_gdrive.js | 76 +++++++++---------- lib/client/storage/_github.js | 36 ++++----- lib/client/storage/dropbox/lib/dropbox.min.js | 7 +- 7 files changed, 101 insertions(+), 103 deletions(-) diff --git a/ChangeLog b/ChangeLog index 4d981243..6dad0bbd 100644 --- a/ChangeLog +++ b/ChangeLog @@ -32,6 +32,9 @@ inside event function varible called event do not exist changed redirecting url to /auth and added rout function to cloudcmd.js +* DropBox, GDrive and GitHub modules now look the same. + + 2012.12.12, Version 0.1.8 * Added ability to shutdown Cloud Commander diff --git a/lib/client/dom.js b/lib/client/dom.js index 2aab90ac..c27351c6 100644 --- a/lib/client/dom.js +++ b/lib/client/dom.js @@ -1067,6 +1067,26 @@ var CloudCommander, Util, DOM, CloudFunc; return DOM.addClass(pElement, 'hidden'); }; + /** + * open window with URL + * @param pUrl + */ + DOM.openWindow = function(pUrl){ + var left = 140, + top = 187, + width = 1000, + height = 650, + + lOptions = 'left=' + left + + ',top=' + top + + ',width=' + width + + ',height=' + height + + ',personalbar=0,toolbar=0' + + ',scrollbars=1,resizable=1'; + + window.open(pUrl, 'Cloud Commander Auth', lOptions); + }; + /** * remove child of element * @param pChild diff --git a/lib/client/menu.js b/lib/client/menu.js index abcb815e..b1ee40ba 100644 --- a/lib/client/menu.js +++ b/lib/client/menu.js @@ -61,14 +61,13 @@ var CloudCommander, Util, DOM, $; lObj.callback = function(key, opt){ getCurrentData(function(pParams){ - var lObject = cloudcmd[pObjectName], - lName = pParams.name, - lData = pParams.data; + var lObject = cloudcmd[pObjectName]; + if('init' in lObject) - lObject.uploadFile(lData, lName); + lObject.uploadFile(pParams); else Util.exec(lObject, function(){ - cloudcmd[pObjectName].uploadFile(lData, lName); + cloudcmd[pObjectName].uploadFile(pParams); }); }); @@ -119,25 +118,9 @@ var CloudCommander, Util, DOM, $; upload: { name: 'Upload to', items: { - 'GitHub': getUploadToItem('GitHub'), - - 'gdrive': { - name: 'GDrive', - callback: function(key, opt){ - getCurrentData(function(pParams){ - var lGDrive = cloudcmd.GDrive; - - if('init' in lGDrive) - lGDrive.init(pParams); - else - Util.exec(cloudcmd.GDrive, pParams); - }); - - Util.log('Uploading to gdrive...'); - } - }, - - 'DropBox': getUploadToItem('DropBox') + 'GitHub' : getUploadToItem('GitHub'), + 'GDrive' : getUploadToItem('GDrive'), + 'DropBox' : getUploadToItem('DropBox') } }, diff --git a/lib/client/storage/_dropbox.js b/lib/client/storage/_dropbox.js index d1c8b9ee..d48f10e7 100644 --- a/lib/client/storage/_dropbox.js +++ b/lib/client/storage/_dropbox.js @@ -4,7 +4,7 @@ var CloudCommander, Util, DOM, CloudFunc, Dropbox, cb, Client; (function(){ "use strict"; - var cloudcmd = CloudCommander, + var CloudCmd = CloudCommander, //Client, DropBoxStore = {}; @@ -19,9 +19,10 @@ var CloudCommander, Util, DOM, CloudFunc, Dropbox, cb, Client; function load(pCallBack){ console.time('dropbox load'); - var lSrc = '//cdnjs.cloudflare.com/ajax/libs/dropbox.js/0.7.1/dropbox.min.js', - lLocal = CloudFunc.LIBDIRCLIENT + 'storage/dropbox/lib/dropbox.min.js', - lOnload = function(){ + //var lSrc = '//cdnjs.cloudflare.com/ajax/libs/dropbox.js/0.7.1/dropbox.min.js', + var lSrc = CloudCmd.LIBDIRCLIENT + 'storage/dropbox/lib/dropbox.js', + lLocal = CloudCmd.LIBDIRCLIENT + 'storage/dropbox/lib/dropbox.min.js', + lOnload = function(){ console.timeEnd('dropbox load'); DOM.Images.hideLoad(); @@ -54,7 +55,7 @@ var CloudCommander, Util, DOM, CloudFunc, Dropbox, cb, Client; * @param pData = {key, secret} */ DropBoxStore.login = function(pCallBack){ - cloudcmd.getConfig(function(pConfig){ + CloudCmd.getConfig(function(pConfig){ Client = new Dropbox.Client({ key: pConfig.dropbox_encoded_key }); @@ -72,19 +73,23 @@ var CloudCommander, Util, DOM, CloudFunc, Dropbox, cb, Client; /** * upload file to DropBox */ - DropBoxStore.uploadFile = function(pContent, pFileName){ - if(pContent){ + DropBoxStore.uploadFile = function(pParams, pCallBack){ + var lContent = pParams.data, + lName = pParams.name; + + if(lContent){ DOM.Images.showLoad(); - if(!pFileName) - pFileName = Util.getDate(); + if(!lName) + lName = Util.getDate(); - Client.writeFile(pFileName, pContent, function(pError, pData){ + Client.writeFile(lName, lContent, function(pError, pData){ DOM.Images.hideLoad(); console.log(pError || pData); + Util.exec(pCallBack); }); } - return pContent; + return lContent; }; DropBoxStore.init = function(pCallBack){ @@ -95,8 +100,8 @@ var CloudCommander, Util, DOM, CloudFunc, Dropbox, cb, Client; load ]); - cloudcmd.DropBox.init = null; + CloudCmd.DropBox.init = null; }; - cloudcmd.DropBox = DropBoxStore; + CloudCmd.DropBox = DropBoxStore; })(); diff --git a/lib/client/storage/_gdrive.js b/lib/client/storage/_gdrive.js index 04319b49..c7322dd0 100644 --- a/lib/client/storage/_gdrive.js +++ b/lib/client/storage/_gdrive.js @@ -7,42 +7,37 @@ var CloudCommander, Util, DOM, gapi; GDrive = {}; - function authorize(pData){ - /* https://developers.google.com/drive/credentials */ - var lCLIENT_ID = '255175681917.apps.googleusercontent.com', - lSCOPES = 'https://www.googleapis.com/auth/drive', - lParams = { - 'client_id' : lCLIENT_ID, - 'scope' : lSCOPES, - 'immediate' : false - }; - - setTimeout(function() { - gapi.auth.authorize(lParams, function(pAuthResult){ - if (pAuthResult && !pAuthResult.error) - uploadFile(pData); - }); - }, 500); - } + /* PRIVATE FUNCTIONS */ - function load(pData){ + /** + * load google api library + */ + function load(pCallBack){ var lUrl = 'https://apis.google.com/js/client.js'; DOM.jsload(lUrl, function(){ - authorize(pData); + /* https://developers.google.com/drive/credentials */ + var lCLIENT_ID = '255175681917.apps.googleusercontent.com', + lSCOPES = 'https://www.googleapis.com/auth/drive', + lParams = { + 'client_id' : lCLIENT_ID, + 'scope' : lSCOPES, + 'immediate' : false + }; + + setTimeout(function() { + gapi.auth.authorize(lParams, function(pAuthResult){ + + if (pAuthResult && !pAuthResult.error) + gapi.client.load('drive', 'v2', function() { + Util.exec(pCallBack); + }); + }); + + }, 500); }); } - /** - * Start the file upload. - * - * @param {Object} evt Arguments from the file selector. - */ - function uploadFile(pData) { - gapi.client.load('drive', 'v2', function() { - GDrive.uploadFile(pData); - }); - } /** * Insert new file. @@ -50,20 +45,20 @@ var CloudCommander, Util, DOM, gapi; * @param {File} fileData {name, data} File object to read data from. * @param {Function} callback Function to call when the request is complete. */ - GDrive.uploadFile = function(pData, callback) { - var lData = pData.data, - lName = pData.name, + GDrive.uploadFile = function(pParams, pCallBack) { + var lContent = pParams.data, + lName = pParams.name, boundary = '-------314159265358979323846', delimiter = "\r\n--" + boundary + "\r\n", close_delim = "\r\n--" + boundary + "--", - contentType = pData.type || 'application/octet-stream', + contentType = pParams.type || 'application/octet-stream', metadata = { 'title' : lName, 'mimeType' : contentType }, - base64Data = btoa(lData), + base64Data = btoa(lContent), multipartRequestBody = delimiter + @@ -87,17 +82,20 @@ var CloudCommander, Util, DOM, gapi; 'body': multipartRequestBody }); - if (!callback) - callback = function(file) { + if (!pCallBack) + pCallBack = function(file) { console.log(file); }; - request.execute(callback); + request.execute(pCallBack); }; - GDrive.init = function(pData){ - load(pData); + GDrive.init = function(pCallBack){ + Util.loadOnLoad([ + Util.retExec(pCallBack), + load + ]); }; cloudcmd.GDrive = GDrive; diff --git a/lib/client/storage/_github.js b/lib/client/storage/_github.js index 2163dffa..9bd5292c 100644 --- a/lib/client/storage/_github.js +++ b/lib/client/storage/_github.js @@ -84,21 +84,8 @@ var CloudCommander, Util, DOM, $, Github, cb; DOM.ajax(lData); } else{ - - var lUrl = '//' + window.location.host + '/auth/github', - left = 140, - top = 187, - width = 1000, - height = 650, - - lOptions = 'left=' + left + - ',top=' + top + - ',width=' + width + - ',height=' + height + - ',personalbar=0,toolbar=0' + - ',scrollbars=1,resizable=1'; - - window.open(lUrl, 'Cloud Commander Auth', lOptions); + var lUrl = '//' + window.location.host + '/auth/github'; + DOM.openWindow(lUrl); } } }; @@ -142,11 +129,14 @@ var CloudCommander, Util, DOM, $, Github, cb; /** * function creates gist */ - cloudcmd.GitHub.uploadFile = function(pContent, pFileName){ - if(pContent){ + cloudcmd.GitHub.uploadFile = function(pParams, pCallBack){ + var lContent = pParams.data, + lName = pParams.name; + + if(lContent){ DOM.Images.showLoad(); - if(!pFileName) - pFileName = Util.getDate(); + if(!lName) + lName = Util.getDate(); var lGist = GithubLocal.getGist(), lFiles = {}, @@ -157,8 +147,8 @@ var CloudCommander, Util, DOM, $, Github, cb; public: true }; - lFiles[pFileName] ={ - content: pContent + lFiles[lName] ={ + content: lContent }; lOptions.files = lFiles; @@ -167,10 +157,12 @@ var CloudCommander, Util, DOM, $, Github, cb; DOM.Images.hideLoad(); console.log(pError || pData); console.log(pData && pData.html_url); + + Util.exec(pCallBack); }); } - return pContent; + return lContent; }; cloudcmd.GitHub.init = function(pCallBack){ diff --git a/lib/client/storage/dropbox/lib/dropbox.min.js b/lib/client/storage/dropbox/lib/dropbox.min.js index 86ef3b5f..bb51c44f 100644 --- a/lib/client/storage/dropbox/lib/dropbox.min.js +++ b/lib/client/storage/dropbox/lib/dropbox.min.js @@ -1,5 +1,2 @@ -/* - * dropbox 0.7.1 - */ -(function(){var t,e,r,n,o,i,s,a,h,u,l,p,c,d,f,y,v,m,g,w,S,b,T={}.hasOwnProperty,E=function(t,e){function r(){this.constructor=t}for(var n in e)T.call(e,n)&&(t[n]=e[n]);return r.prototype=e.prototype,t.prototype=new r,t.__super__=e.prototype,t};if(t=function(){function t(t){this.client=new e(t)}return t}(),t.ApiError=function(){function t(t,e,r){var n;if(this.method=e,this.url=r,this.status=t.status,n=t.responseType?t.response||t.responseText:t.responseText)try{this.responseText=""+n,this.response=JSON.parse(n)}catch(o){this.response=null}else this.responseText="(no response)",this.response=null}return t.prototype.toString=function(){return"Dropbox API error "+this.status+" from "+this.method+" "+this.url+" :: "+this.responseText},t.prototype.inspect=function(){return""+this},t}(),"undefined"!=typeof window&&null!==window?window.atob&&window.btoa?(h=function(t){return window.atob(t)},d=function(t){return window.btoa(t)}):(l="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/",f=function(t,e,r){var n,o;for(o=3-e,t<<=8*o,n=3;n>=o;)r.push(l.charAt(63&t>>6*n)),n-=1;for(n=e;3>n;)r.push("="),n+=1;return null},u=function(t,e,r){var n,o;for(o=4-e,t<<=6*o,n=2;n>=o;)r.push(String.fromCharCode(255&t>>8*n)),n-=1;return null},d=function(t){var e,r,n,o,i,s;for(o=[],e=0,r=0,n=i=0,s=t.length;s>=0?s>i:i>s;n=s>=0?++i:--i)e=e<<8|t.charCodeAt(n),r+=1,3===r&&(f(e,r,o),e=r=0);return r>0&&f(e,r,o),o.join("")},h=function(t){var e,r,n,o,i,s,a;for(i=[],e=0,n=0,o=s=0,a=t.length;(a>=0?a>s:s>a)&&(r=t.charAt(o),"="!==r);o=a>=0?++s:--s)e=e<<6|l.indexOf(r),n+=1,4===n&&(u(e,n,i),e=n=0);return n>0&&u(e,n,i),i.join("")}):(h=function(t){var e,r;return e=new Buffer(t,"base64"),function(){var t,n,o;for(o=[],r=t=0,n=e.length;n>=0?n>t:t>n;r=n>=0?++t:--t)o.push(String.fromCharCode(e[r]));return o}().join("")},d=function(t){var e,r;return e=new Buffer(function(){var e,n,o;for(o=[],r=e=0,n=t.length;n>=0?n>e:e>n;r=n>=0?++e:--e)o.push(t.charCodeAt(r));return o}()),e.toString("base64")}),t.Client=function(){function r(e){this.sandbox=e.sandbox||!1,this.apiServer=e.server||this.defaultApiServer(),this.authServer=e.authServer||this.defaultAuthServer(),this.fileServer=e.fileServer||this.defaultFileServer(),this.downloadServer=e.downloadServer||this.defaultDownloadServer(),this.oauth=new t.Oauth(e),this.driver=null,this.filter=null,this.uid=null,this.authState=null,this.authError=null,this._credentials=null,this.setCredentials(e),this.setupUrls()}return r.prototype.authDriver=function(t){return this.driver=t,this},r.prototype.xhrFilter=function(t){return this.filter=t,this},r.prototype.dropboxUid=function(){return this.uid},r.prototype.credentials=function(){return this._credentials||this.computeCredentials(),this._credentials},r.prototype.authenticate=function(r){var n,o,i=this;return n=null,o=function(){var s;if(n!==i.authState&&(n=i.authState,i.driver.onAuthStateChange))return i.driver.onAuthStateChange(i,o);switch(i.authState){case e.RESET:return i.requestToken(function(t,r){var n,s;return t?(i.authError=t,i.authState=e.ERROR):(n=r.oauth_token,s=r.oauth_token_secret,i.oauth.setToken(n,s),i.authState=e.REQUEST),i._credentials=null,o()});case e.REQUEST:return s=i.authorizeUrl(i.oauth.token),i.driver.doAuthorize(s,i.oauth.token,i.oauth.tokenSecret,function(){return i.authState=e.AUTHORIZED,i._credentials=null,o()});case e.AUTHORIZED:return i.getAccessToken(function(t,r){return t?(i.authError=t,i.authState=e.ERROR):(i.oauth.setToken(r.oauth_token,r.oauth_token_secret),i.uid=r.uid,i.authState=e.DONE),i._credentials=null,o()});case e.DONE:return r(null,i);case t.SIGNED_OFF:return i.reset(),o();case e.ERROR:return r(i.authError)}},o(),this},r.prototype.signOut=function(r){var n,o=this;return n=new t.Xhr("POST",this.urls.signOut),n.signWithOauth(this.oauth),this.dispatchXhr(n,function(t){return t?r(t):(o.reset(),o.authState=e.SIGNED_OFF,o.driver.onAuthStateChange?o.driver.onAuthStateChange(o,function(){return r(t)}):r(t))})},r.prototype.signOff=function(t){return this.signOut(t)},r.prototype.getUserInfo=function(e){var r;return r=new t.Xhr("GET",this.urls.accountInfo),r.signWithOauth(this.oauth),this.dispatchXhr(r,function(r,n){return e(r,t.UserInfo.parse(n),n)})},r.prototype.readFile=function(e,r,n){var o,i,s,a,h,u,l;return n||"function"!=typeof r||(n=r,r=null),i={},u=null,o=null,a=null,r&&(r.versionTag?i.rev=r.versionTag:r.rev&&(i.rev=r.rev),r.blob&&(u="blob"),r.binary&&(u="b"),r.length?(null!=r.start?(h=r.start,s=r.start+r.length-1):(h="",s=r.length),a="bytes="+h+"-"+s):null!=r.start&&(a="bytes="+r.start+"-"),r.modifiedSince&&(o=new Date(r.modifiedSince).toUTCString())),l=new t.Xhr("GET",""+this.urls.getFile+"/"+this.urlEncodePath(e)),l.setParams(i).signWithOauth(this.oauth).setResponseType(u),o&&l.setHeader("If-Modified-Since",o),a&&l.setHeader("Range",a),this.dispatchXhr(l,function(e,r,o){return n(e,r,t.Stat.parse(o))})},r.prototype.writeFile=function(e,r,n,o){var i;return o||"function"!=typeof n||(o=n,n=null),i=t.Xhr.canSendForms&&"object"==typeof r,i?this.writeFileUsingForm(e,r,n,o):this.writeFileUsingPut(e,r,n,o)},r.prototype.writeFileUsingForm=function(e,r,n,o){var i,s,a,h;return a=e.lastIndexOf("/"),-1===a?(i=e,e=""):(i=e.substring(a),e=e.substring(0,a)),s={file:i},n&&(n.noOverwrite&&(s.overwrite="false"),n.lastVersionTag?s.parent_rev=n.lastVersionTag:(n.parentRev||n.parent_rev)&&(s.parent_rev=n.parentRev||n.parent_rev)),h=new t.Xhr("POST",""+this.urls.postFile+"/"+this.urlEncodePath(e)),h.setParams(s).signWithOauth(this.oauth).setFileField("file",i,r,"application/octet-stream"),delete s.file,this.dispatchXhr(h,function(e,r){return o(e,t.Stat.parse(r))})},r.prototype.writeFileUsingPut=function(e,r,n,o){var i,s;return i={},n&&(n.noOverwrite&&(i.overwrite="false"),n.lastVersionTag?i.parent_rev=n.lastVersionTag:(n.parentRev||n.parent_rev)&&(i.parent_rev=n.parentRev||n.parent_rev)),s=new t.Xhr("POST",""+this.urls.putFile+"/"+this.urlEncodePath(e)),s.setBody(r).setParams(i).signWithOauth(this.oauth),this.dispatchXhr(s,function(e,r){return o(e,t.Stat.parse(r))})},r.prototype.stat=function(e,r,n){var o,i;return n||"function"!=typeof r||(n=r,r=null),o={},r&&(null!=r.version&&(o.rev=r.version),(r.removed||r.deleted)&&(o.include_deleted="true"),r.readDir&&(o.list="true",r.readDir!==!0&&(o.file_limit=""+r.readDir)),r.cacheHash&&(o.hash=r.cacheHash)),o.include_deleted||(o.include_deleted="false"),o.list||(o.list="false"),i=new t.Xhr("GET",""+this.urls.metadata+"/"+this.urlEncodePath(e)),i.setParams(o).signWithOauth(this.oauth),this.dispatchXhr(i,function(e,r){var o,i,s;return s=t.Stat.parse(r),o=(null!=r?r.contents:void 0)?function(){var e,n,o,s;for(o=r.contents,s=[],e=0,n=o.length;n>e;e++)i=o[e],s.push(t.Stat.parse(i));return s}():void 0,n(e,s,o)})},r.prototype.readdir=function(t,e,r){var n;return r||"function"!=typeof e||(r=e,e=null),n={readDir:!0},e&&(null!=e.limit&&(n.readDir=e.limit),e.versionTag&&(n.versionTag=e.versionTag)),this.stat(t,n,function(t,e,n){var o,i;return o=n?function(){var t,e,r;for(r=[],t=0,e=n.length;e>t;t++)i=n[t],r.push(i.name);return r}():null,r(t,o,e,n)})},r.prototype.metadata=function(t,e,r){return this.stat(t,e,r)},r.prototype.makeUrl=function(e,r,n){var o,i,s,a,h;return n||"function"!=typeof r||(n=r,r=null),i=r&&(r["long"]||r.longUrl||r.downloadHack)?{short_url:"false"}:{},e=this.urlEncodePath(e),s=""+this.urls.shares+"/"+e,o=!1,a=!1,r&&(r.downloadHack?(o=!0,a=!0):r.download&&(o=!0,s=""+this.urls.media+"/"+e)),h=new t.Xhr("POST",s).setParams(i).signWithOauth(this.oauth),this.dispatchXhr(h,function(e,r){return a&&r&&r.url&&(r.url=r.url.replace(this.authServer,this.downloadServer)),n(e,t.PublicUrl.parse(r,o))})},r.prototype.history=function(e,r,n){var o,i;return n||"function"!=typeof r||(n=r,r=null),o={},r&&null!=r.limit&&(o.rev_limit=r.limit),i=new t.Xhr("GET",""+this.urls.revisions+"/"+this.urlEncodePath(e)),i.setParams(o).signWithOauth(this.oauth),this.dispatchXhr(i,function(e,r){var o,i;return i=r?function(){var e,n,i;for(i=[],e=0,n=r.length;n>e;e++)o=r[e],i.push(t.Stat.parse(o));return i}():void 0,n(e,i)})},r.prototype.revisions=function(t,e,r){return this.history(t,e,r)},r.prototype.thumbnailUrl=function(t,e){var r;return r=this.thumbnailXhr(t,e),r.paramsToUrl().url},r.prototype.readThumbnail=function(e,r,n){var o,i;return n||"function"!=typeof r||(n=r,r=null),o="b",r&&r.blob&&(o="blob"),i=this.thumbnailXhr(e,r),i.setResponseType(o),this.dispatchXhr(i,function(e,r,o){return n(e,r,t.Stat.parse(o))})},r.prototype.thumbnailXhr=function(e,r){var n,o;return n={},r&&(r.format?n.format=r.format:r.png&&(n.format="png"),r.size&&(n.size=r.size)),o=new t.Xhr("GET",""+this.urls.thumbnails+"/"+this.urlEncodePath(e)),o.setParams(n).signWithOauth(this.oauth)},r.prototype.revertFile=function(e,r,n){var o;return o=new t.Xhr("POST",""+this.urls.restore+"/"+this.urlEncodePath(e)),o.setParams({rev:r}).signWithOauth(this.oauth),this.dispatchXhr(o,function(e,r){return n(e,t.Stat.parse(r))})},r.prototype.restore=function(t,e,r){return this.revertFile(t,e,r)},r.prototype.findByName=function(e,r,n,o){var i,s;return o||"function"!=typeof n||(o=n,n=null),i={query:r},n&&(null!=n.limit&&(i.file_limit=n.limit),(n.removed||n.deleted)&&(i.include_deleted=!0)),s=new t.Xhr("GET",""+this.urls.search+"/"+this.urlEncodePath(e)),s.setParams(i).signWithOauth(this.oauth),this.dispatchXhr(s,function(e,r){var n,i;return i=r?function(){var e,o,i;for(i=[],e=0,o=r.length;o>e;e++)n=r[e],i.push(t.Stat.parse(n));return i}():void 0,o(e,i)})},r.prototype.search=function(t,e,r,n){return this.findByName(t,e,r,n)},r.prototype.makeCopyReference=function(e,r){var n;return n=new t.Xhr("GET",""+this.urls.copyRef+"/"+this.urlEncodePath(e)),n.signWithOauth(this.oauth),this.dispatchXhr(n,function(e,n){return r(e,t.CopyReference.parse(n))})},r.prototype.copyRef=function(t,e){return this.makeCopyReference(t,e)},r.prototype.pullChanges=function(e,r){var n,o;return r||"function"!=typeof e||(r=e,e=null),n=e?e.cursorTag?{cursor:e.cursorTag}:{cursor:e}:{},o=new t.Xhr("POST",this.urls.delta),o.setParams(n).signWithOauth(this.oauth),this.dispatchXhr(o,function(e,n){return r(e,t.PulledChanges.parse(n))})},r.prototype.delta=function(t,e){return this.pullChanges(t,e)},r.prototype.mkdir=function(e,r){var n;return n=new t.Xhr("POST",this.urls.fileopsCreateFolder),n.setParams({root:this.fileRoot,path:this.normalizePath(e)}).signWithOauth(this.oauth),this.dispatchXhr(n,function(e,n){return r(e,t.Stat.parse(n))})},r.prototype.remove=function(e,r){var n;return n=new t.Xhr("POST",this.urls.fileopsDelete),n.setParams({root:this.fileRoot,path:this.normalizePath(e)}).signWithOauth(this.oauth),this.dispatchXhr(n,function(e,n){return r(e,t.Stat.parse(n))})},r.prototype.unlink=function(t,e){return this.remove(t,e)},r.prototype["delete"]=function(t,e){return this.remove(t,e)},r.prototype.copy=function(e,r,n){var o,i,s;return n||"function"!=typeof o||(n=o,o=null),i={root:this.fileRoot,to_path:this.normalizePath(r)},e instanceof t.CopyReference?i.from_copy_ref=e.tag:i.from_path=this.normalizePath(e),s=new t.Xhr("POST",this.urls.fileopsCopy),s.setParams(i).signWithOauth(this.oauth),this.dispatchXhr(s,function(e,r){return n(e,t.Stat.parse(r))})},r.prototype.move=function(e,r,n){var o,i;return n||"function"!=typeof o||(n=o,o=null),i=new t.Xhr("POST",this.urls.fileopsMove),i.setParams({root:this.fileRoot,from_path:this.normalizePath(e),to_path:this.normalizePath(r)}).signWithOauth(this.oauth),this.dispatchXhr(i,function(e,r){return n(e,t.Stat.parse(r))})},r.prototype.reset=function(){return this.uid=null,this.oauth.setToken(null,""),this.authState=e.RESET,this.authError=null,this._credentials=null,this},r.prototype.setCredentials=function(t){return this.oauth.reset(t),this.uid=t.uid||null,this.authState=t.authState?t.authState:t.token?e.DONE:e.RESET,this.authError=null,this._credentials=null,this},r.prototype.appHash=function(){return this.oauth.appHash()},r.prototype.setupUrls=function(){return this.fileRoot=this.sandbox?"sandbox":"dropbox",this.urls={requestToken:""+this.apiServer+"/1/oauth/request_token",authorize:""+this.authServer+"/1/oauth/authorize",accessToken:""+this.apiServer+"/1/oauth/access_token",signOut:""+this.apiServer+"/1/unlink_access_token",accountInfo:""+this.apiServer+"/1/account/info",getFile:""+this.fileServer+"/1/files/"+this.fileRoot,postFile:""+this.fileServer+"/1/files/"+this.fileRoot,putFile:""+this.fileServer+"/1/files_put/"+this.fileRoot,metadata:""+this.apiServer+"/1/metadata/"+this.fileRoot,delta:""+this.apiServer+"/1/delta",revisions:""+this.apiServer+"/1/revisions/"+this.fileRoot,restore:""+this.apiServer+"/1/restore/"+this.fileRoot,search:""+this.apiServer+"/1/search/"+this.fileRoot,shares:""+this.apiServer+"/1/shares/"+this.fileRoot,media:""+this.apiServer+"/1/media/"+this.fileRoot,copyRef:""+this.apiServer+"/1/copy_ref/"+this.fileRoot,thumbnails:""+this.fileServer+"/1/thumbnails/"+this.fileRoot,fileopsCopy:""+this.apiServer+"/1/fileops/copy",fileopsCreateFolder:""+this.apiServer+"/1/fileops/create_folder",fileopsDelete:""+this.apiServer+"/1/fileops/delete",fileopsMove:""+this.apiServer+"/1/fileops/move"}},r.ERROR=0,r.RESET=1,r.REQUEST=2,r.AUTHORIZED=3,r.DONE=4,r.SIGNED_OFF=5,r.prototype.urlEncodePath=function(e){return t.Xhr.urlEncodeValue(this.normalizePath(e)).replace(/%2F/gi,"/")},r.prototype.normalizePath=function(t){var e;if("/"===t.substring(0,1)){for(e=1;"/"===t.substring(e,e+1);)e+=1;return t.substring(e)}return t},r.prototype.requestToken=function(e){var r;return r=new t.Xhr("POST",this.urls.requestToken).signWithOauth(this.oauth),this.dispatchXhr(r,e)},r.prototype.authorizeUrl=function(e){var r;return r={oauth_token:e,oauth_callback:this.driver.url()},""+this.urls.authorize+"?"+t.Xhr.urlEncode(r)},r.prototype.getAccessToken=function(e){var r;return r=new t.Xhr("POST",this.urls.accessToken).signWithOauth(this.oauth),this.dispatchXhr(r,e)},r.prototype.dispatchXhr=function(t,e){var r;return t.setCallback(e),t.prepare(),r=t.xhr,this.filter&&!this.filter(r,t)?r:(t.send(),r)},r.prototype.defaultApiServer=function(){return"https://api.dropbox.com"},r.prototype.defaultAuthServer=function(){return this.apiServer.replace("api.","www.")},r.prototype.defaultFileServer=function(){return this.apiServer.replace("api.","api-content.")},r.prototype.defaultDownloadServer=function(){return this.apiServer.replace("api.","dl.")},r.prototype.computeCredentials=function(){var t;return t={key:this.oauth.key,sandbox:this.sandbox},this.oauth.secret&&(t.secret=this.oauth.secret),this.oauth.token&&(t.token=this.oauth.token,t.tokenSecret=this.oauth.tokenSecret),this.uid&&(t.uid=this.uid),this.authState!==e.ERROR&&this.authState!==e.RESET&&this.authState!==e.DONE&&this.authState!==e.SIGNED_OFF&&(t.authState=this.authState),this.apiServer!==this.defaultApiServer()&&(t.server=this.apiServer),this.authServer!==this.defaultAuthServer()&&(t.authServer=this.authServer),this.fileServer!==this.defaultFileServer()&&(t.fileServer=this.fileServer),this.downloadServer!==this.defaultDownloadServer()&&(t.downloadServer=this.downloadServer),this._credentials=t},r}(),e=t.Client,t.AuthDriver=function(){function t(){}return t.prototype.url=function(){return"https://some.url"},t.prototype.doAuthorize=function(t,e,r,n){return n("oauth-token")},t.prototype.onAuthStateChange=function(t,e){return e()},t}(),t.Drivers={},t.Drivers.BrowserBase=function(){function t(t){this.rememberUser=(null!=t?t.rememberUser:void 0)||!1,this.scope=(null!=t?t.scope:void 0)||"default"}return t.prototype.onAuthStateChange=function(t,r){var n,o=this;switch(this.setStorageKey(t),t.authState){case e.RESET:return(n=this.loadCredentials())?n.authState?(t.setCredentials(n),r()):this.rememberUser?(t.setCredentials(n),t.getUserInfo(function(e){return e&&(t.reset(),o.forgetCredentials()),r()})):(this.forgetCredentials(),r()):r();case e.REQUEST:return this.storeCredentials(t.credentials()),r();case e.DONE:return this.rememberUser?this.storeCredentials(t.credentials()):this.forgetCredentials(),r();case e.SIGNED_OFF:return this.forgetCredentials(),r();case e.ERROR:return this.forgetCredentials(),r();default:return r()}},t.prototype.setStorageKey=function(t){return this.storageKey="dropbox-auth:"+this.scope+":"+t.appHash(),this},t.prototype.storeCredentials=function(t){return localStorage.setItem(this.storageKey,JSON.stringify(t))},t.prototype.loadCredentials=function(){var t;if(t=localStorage.getItem(this.storageKey),!t)return null;try{return JSON.parse(t)}catch(e){return null}},t.prototype.forgetCredentials=function(){return localStorage.removeItem(this.storageKey)},t.currentLocation=function(){return window.location.href},t}(),t.Drivers.Redirect=function(r){function n(t){n.__super__.constructor.call(this,t),this.useQuery=(null!=t?t.useQuery:void 0)||!1,this.receiverUrl=this.computeUrl(t),this.tokenRe=RegExp("(#|\\?|&)oauth_token=([^&#]+)(&|#|$)")}return E(n,r),n.prototype.onAuthStateChange=function(t,r){var o;return this.setStorageKey(t),t.authState===e.RESET&&(o=this.loadCredentials())&&o.authState&&(o.token===this.locationToken()&&o.authState===e.REQUEST?(o.authState=e.AUTHORIZED,this.storeCredentials(o)):this.forgetCredentials()),n.__super__.onAuthStateChange.call(this,t,r)},n.prototype.url=function(){return this.receiverUrl},n.prototype.doAuthorize=function(t){return window.location.assign(t)},n.prototype.computeUrl=function(){var e,r,n,o;return o="_dropboxjs_scope="+encodeURIComponent(this.scope),r=t.Drivers.BrowserBase.currentLocation(),-1===r.indexOf("#")?e=null:(n=r.split("#",2),r=n[0],e=n[1]),this.useQuery?r+=-1===r.indexOf("?")?"?"+o:"&"+o:e="?"+o,e?r+"#"+e:r},n.prototype.locationToken=function(){var e,r,n;return e=t.Drivers.BrowserBase.currentLocation(),n="_dropboxjs_scope="+encodeURIComponent(this.scope)+"&",-1===("function"==typeof e.indexOf?e.indexOf(n):void 0)?null:(r=this.tokenRe.exec(e),r?decodeURIComponent(r[2]):null)},n}(t.Drivers.BrowserBase),t.Drivers.Popup=function(r){function n(t){n.__super__.constructor.call(this,t),this.receiverUrl=this.computeUrl(t),this.tokenRe=RegExp("(#|\\?|&)oauth_token=([^&#]+)(&|#|$)")}return E(n,r),n.prototype.onAuthStateChange=function(t,r){var o;return this.setStorageKey(t),t.authState===e.RESET&&(o=this.loadCredentials())&&o.authState&&this.forgetCredentials(),n.__super__.onAuthStateChange.call(this,t,r)},n.prototype.doAuthorize=function(t,e,r,n){return this.listenForMessage(e,n),this.openWindow(t)},n.prototype.url=function(){return this.receiverUrl},n.prototype.computeUrl=function(e){var r;if(e){if(e.receiverUrl)return e.noFragment||-1!==e.receiverUrl.indexOf("#")?e.receiverUrl:e.receiverUrl+"#";if(e.receiverFile)return r=t.Drivers.BrowserBase.currentLocation().split("/"),r[r.length-1]=e.receiverFile,e.noFragment?r.join("/"):r.join("/")+"#"}return t.Drivers.BrowserBase.currentLocation()},n.prototype.openWindow=function(t){return window.open(t,"_dropboxOauthSigninWindow",this.popupWindowSpec(980,980))},n.prototype.popupWindowSpec=function(t,e){var r,n,o,i,s,a,h,u,l,p;return s=null!=(h=window.screenX)?h:window.screenLeft,a=null!=(u=window.screenY)?u:window.screenTop,i=null!=(l=window.outerWidth)?l:document.documentElement.clientWidth,r=null!=(p=window.outerHeight)?p:document.documentElement.clientHeight,n=Math.round(s+(i-t)/2),o=Math.round(a+(r-e)/2.5),"width="+t+",height="+e+","+("left="+n+",top="+o)+"dialog=yes,dependent=yes,scrollbars=yes,location=yes"},n.prototype.listenForMessage=function(t,e){var r,n;return n=this.tokenRe,r=function(o){var i;return i=n.exec(""+o.data),i&&decodeURIComponent(i[2])===t?(window.removeEventListener("message",r),e()):void 0},window.addEventListener("message",r,!1)},n}(t.Drivers.BrowserBase),t.Drivers.NodeServer=function(){function t(t){this.port=(null!=t?t.port:void 0)||8912,this.faviconFile=(null!=t?t.favicon:void 0)||null,this.fs=require("fs"),this.http=require("http"),this.open=require("open"),this.callbacks={},this.urlRe=RegExp("^/oauth_callback\\?"),this.tokenRe=RegExp("(\\?|&)oauth_token=([^&]+)(&|$)"),this.createApp()}return t.prototype.url=function(){return"http://localhost:"+this.port+"/oauth_callback"},t.prototype.doAuthorize=function(t,e,r,n){return this.callbacks[e]=n,this.openBrowser(t)},t.prototype.openBrowser=function(t){if(!t.match(/^https?:\/\//))throw Error("Not a http/https URL: "+t);return this.open(t)},t.prototype.createApp=function(){var t=this;return this.app=this.http.createServer(function(e,r){return t.doRequest(e,r)}),this.app.listen(this.port)},t.prototype.closeServer=function(){return this.app.close()},t.prototype.doRequest=function(t,e){var r,n,o,i=this;return this.urlRe.exec(t.url)&&(n=this.tokenRe.exec(t.url),n&&(o=decodeURIComponent(n[2]),this.callbacks[o]&&(this.callbacks[o](),delete this.callbacks[o]))),r="",t.on("data",function(t){return r+=t}),t.on("end",function(){return i.faviconFile&&"/favicon.ico"===t.url?i.sendFavicon(e):i.closeBrowser(e)})},t.prototype.closeBrowser=function(t){var e;return e='\n\n

    Please close this window.

    ',t.writeHead(200,{"Content-Length":e.length,"Content-Type":"text/html"}),t.write(e),t.end},t.prototype.sendFavicon=function(t){return this.fs.readFile(this.faviconFile,function(e,r){return t.writeHead(200,{"Content-Length":r.length,"Content-Type":"image/x-icon"}),t.write(r),t.end})},t}(),p=function(t,e){return a(m(S(t),S(e),t.length,e.length))},c=function(t){return a(w(S(t),t.length))},("undefined"==typeof window||null===window)&&(y=require("crypto"),p=function(t,e){var r;return r=y.createHmac("sha1",e),r.update(t),r.digest("base64")},c=function(t){var e;return e=y.createHash("sha1"),e.update(t),e.digest("base64")}),m=function(t,e,r,n){var o,i,s,a;return e.length>16&&(e=w(e,n)),s=function(){var t,r;for(r=[],i=t=0;16>t;i=++t)r.push(909522486^e[i]);return r}(),a=function(){var t,r;for(r=[],i=t=0;16>t;i=++t)r.push(1549556828^e[i]);return r}(),o=w(s.concat(t),64+r),w(a.concat(o),84)},w=function(t,e){var r,n,o,i,a,h,u,l,p,c,d,f,y,v,m,w,S,b;for(t[e>>2]|=1<<31-((3&e)<<3),t[(e+8>>6<<4)+15]=e<<3,w=Array(80),r=1732584193,o=-271733879,a=-1732584194,u=271733878,p=-1009589776,f=0,m=t.length;m>f;){for(n=r,i=o,h=a,l=u,c=p,y=b=0;80>b;y=++b)w[y]=16>y?t[f+y]:g(w[y-3]^w[y-8]^w[y-14]^w[y-16],1),20>y?(d=o&a|~o&u,v=1518500249):40>y?(d=o^a^u,v=1859775393):60>y?(d=o&a|o&u|a&u,v=-1894007588):(d=o^a^u,v=-899497514),S=s(s(g(r,5),d),s(s(p,w[y]),v)),p=u,u=a,a=g(o,30),o=r,r=S;r=s(r,n),o=s(o,i),a=s(a,h),u=s(u,l),p=s(p,c),f+=16}return[r,o,a,u,p]},g=function(t,e){return t<>>32-e},s=function(t,e){var r,n;return n=(65535&t)+(65535&e),r=(t>>16)+(e>>16)+(n>>16),r<<16|65535&n},a=function(t){var e,r,n,o,i;for(o="",e=0,n=4*t.length;n>e;)r=e,i=(255&t[r>>2]>>(3-(3&r)<<3))<<16,r+=1,i|=(255&t[r>>2]>>(3-(3&r)<<3))<<8,r+=1,i|=255&t[r>>2]>>(3-(3&r)<<3),o+=b[63&i>>18],o+=b[63&i>>12],e+=1,o+=e>=n?"=":b[63&i>>6],e+=1,o+=e>=n?"=":b[63&i],e+=1;return o},b="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/",S=function(t){var e,r,n,o,i;for(e=[],n=255,r=o=0,i=t.length;i>=0?i>o:o>i;r=i>=0?++o:--o)e[r>>2]|=(t.charCodeAt(r)&n)<<(3-(3&r)<<3);return e},t.Oauth=function(){function e(t){this.key=this.k=null,this.secret=this.s=null,this.token=null,this.tokenSecret=null,this._appHash=null,this.reset(t)}return e.prototype.reset=function(t){var e,r,n,o;if(t.secret)this.k=this.key=t.key,this.s=this.secret=t.secret,this._appHash=null;else if(t.key)this.key=t.key,this.secret=null,n=h(v(this.key).split("|",2)[1]),o=n.split("?",2),e=o[0],r=o[1],this.k=decodeURIComponent(e),this.s=decodeURIComponent(r),this._appHash=null;else if(!this.k)throw Error("No API key supplied");return t.token?this.setToken(t.token,t.tokenSecret):this.setToken(null,"")},e.prototype.setToken=function(e,r){if(e&&!r)throw Error("No secret supplied with the user token");return this.token=e,this.tokenSecret=r||"",this.hmacKey=t.Xhr.urlEncodeValue(this.s)+"&"+t.Xhr.urlEncodeValue(r),null},e.prototype.authHeader=function(e,r,n){var o,i,s,a,h,u;this.addAuthParams(e,r,n),i=[];for(s in n)a=n[s],"oauth_"===s.substring(0,6)&&i.push(s);for(i.sort(),o=[],h=0,u=i.length;u>h;h++)s=i[h],o.push(t.Xhr.urlEncodeValue(s)+'="'+t.Xhr.urlEncodeValue(n[s])+'"'),delete n[s];return"OAuth "+o.join(",")},e.prototype.addAuthParams=function(t,e,r){return this.boilerplateParams(r),r.oauth_signature=this.signature(t,e,r),r},e.prototype.boilerplateParams=function(t){return t.oauth_consumer_key=this.k,t.oauth_nonce=this.nonce(),t.oauth_signature_method="HMAC-SHA1",this.token&&(t.oauth_token=this.token),t.oauth_timestamp=Math.floor(Date.now()/1e3),t.oauth_version="1.0",t},e.prototype.nonce=function(){return Date.now().toString(36)+Math.random().toString(36)},e.prototype.signature=function(e,r,n){var o;return o=e.toUpperCase()+"&"+t.Xhr.urlEncodeValue(r)+"&"+t.Xhr.urlEncodeValue(t.Xhr.urlEncode(n)),p(o,this.hmacKey)},e.prototype.appHash=function(){return this._appHash?this._appHash:this._appHash=c(this.k).replace(/\=/g,"")},e}(),null==Date.now&&(Date.now=function(){return(new Date).getTime()}),v=function(t,e){var r,n,o,i,s,a,u,l,p,c,f,y;for(e?(e=[encodeURIComponent(t),encodeURIComponent(e)].join("?"),t=function(){var e,n,o;for(o=[],r=e=0,n=t.length/2;n>=0?n>e:e>n;r=n>=0?++e:--e)o.push(16*(15&t.charCodeAt(2*r))+(15&t.charCodeAt(2*r+1)));return o}()):(c=t.split("|",2),t=c[0],e=c[1],t=h(t),t=function(){var e,n,o;for(o=[],r=e=0,n=t.length;n>=0?n>e:e>n;r=n>=0?++e:--e)o.push(t.charCodeAt(r));return o}(),e=h(e)),i=function(){for(y=[],l=0;256>l;l++)y.push(l);return y}.apply(this),a=0,s=p=0;256>p;s=++p)a=(a+i[r]+t[s%t.length])%256,f=[i[a],i[s]],i[s]=f[0],i[a]=f[1];return s=a=0,o=function(){var t,r,o,h;for(h=[],u=t=0,r=e.length;r>=0?r>t:t>r;u=r>=0?++t:--t)s=(s+1)%256,a=(a+i[s])%256,o=[i[a],i[s]],i[s]=o[0],i[a]=o[1],n=i[(i[s]+i[a])%256],h.push(String.fromCharCode((n^e.charCodeAt(u))%256));return h}(),t=function(){var e,n,o;for(o=[],r=e=0,n=t.length;n>=0?n>e:e>n;r=n>=0?++e:--e)o.push(String.fromCharCode(t[r]));return o}(),[d(t.join("")),d(o.join(""))].join("|")},t.PulledChanges=function(){function e(e){var r;this.blankSlate=e.reset||!1,this.cursorTag=e.cursor,this.shouldPullAgain=e.has_more,this.shouldBackOff=!this.shouldPullAgain,this.changes=e.cursor&&e.cursor.length?function(){var n,o,i,s;for(i=e.entries,s=[],n=0,o=i.length;o>n;n++)r=i[n],s.push(t.PullChange.parse(r));return s}():[]}return e.parse=function(e){return e&&"object"==typeof e?new t.PulledChanges(e):e},e.prototype.blankSlate=void 0,e.prototype.cursorTag=void 0,e.prototype.changes=void 0,e.prototype.shouldPullAgain=void 0,e.prototype.shouldBackOff=void 0,e}(),t.PullChange=function(){function e(e){this.path=e[0],this.stat=t.Stat.parse(e[1]),this.stat?this.wasRemoved=!1:(this.stat=null,this.wasRemoved=!0)}return e.parse=function(e){return e&&"object"==typeof e?new t.PullChange(e):e},e.prototype.path=void 0,e.prototype.wasRemoved=void 0,e.prototype.stat=void 0,e}(),t.PublicUrl=function(){function e(t,e){this.url=t.url,this.expiresAt=new Date(Date.parse(t.expires)),this.isDirect=e===!0?!0:e===!1?!1:864e5>=Date.now()-this.expiresAt,this.isPreview=!this.isDirect}return e.parse=function(e,r){return e&&"object"==typeof e?new t.PublicUrl(e,r):e},e.prototype.url=void 0,e.prototype.expiresAt=void 0,e.prototype.isDirect=void 0,e.prototype.isPreview=void 0,e}(),t.CopyReference=function(){function e(t){"object"==typeof t?(this.tag=t.copy_ref,this.expiresAt=new Date(Date.parse(t.expires))):(this.tag=t,this.expiresAt=new Date)}return e.parse=function(e){return!e||"object"!=typeof e&&"string"!=typeof e?e:new t.CopyReference(e)},e.prototype.tag=void 0,e.prototype.expiresAt=void 0,e}(),t.Stat=function(){function e(t){var e,r,n,o;switch(this.path=t.path,"/"!==this.path.substring(0,1)&&(this.path="/"+this.path),e=this.path.length-1,e>=0&&"/"===this.path.substring(e)&&(this.path=this.path.substring(0,e)),r=this.path.lastIndexOf("/"),this.name=this.path.substring(r+1),this.isFolder=t.is_dir||!1,this.isFile=!this.isFolder,this.isRemoved=t.is_deleted||!1,this.typeIcon=t.icon,this.modifiedAt=(null!=(n=t.modified)?n.length:void 0)?new Date(Date.parse(t.modified)):null,this.clientModifiedAt=(null!=(o=t.client_mtime)?o.length:void 0)?new Date(Date.parse(t.client_mtime)):null,t.root){case"dropbox":this.inAppFolder=!1;break;case"app_folder":this.inAppFolder=!0;break;default:this.inAppFolder=null}this.size=t.bytes||0,this.humanSize=t.size||"",this.hasThumbnail=t.thumb_exists||!1,this.isFolder?(this.versionTag=t.hash,this.mimeType=t.mime_type||"inode/directory"):(this.versionTag=t.rev,this.mimeType=t.mime_type||"application/octet-stream")}return e.parse=function(e){return e&&"object"==typeof e?new t.Stat(e):e},e.prototype.path=null,e.prototype.name=null,e.prototype.inAppFolder=null,e.prototype.isFolder=null,e.prototype.isFile=null,e.prototype.isRemoved=null,e.prototype.typeIcon=null,e.prototype.versionTag=null,e.prototype.mimeType=null,e.prototype.size=null,e.prototype.humanSize=null,e.prototype.hasThumbnail=null,e.prototype.modifiedAt=null,e.prototype.clientModifiedAt=null,e}(),t.UserInfo=function(){function e(t){var e;this.name=t.display_name,this.email=t.email,this.countryCode=t.country||null,this.uid=""+t.uid,t.public_app_url?(this.publicAppUrl=t.public_app_url,e=this.publicAppUrl.length-1,e>=0&&"/"===this.publicAppUrl.substring(e)&&(this.publicAppUrl=this.publicAppUrl.substring(0,e))):this.publicAppUrl=null,this.referralUrl=t.referral_link,this.quota=t.quota_info.quota,this.privateBytes=t.quota_info.normal||0,this.sharedBytes=t.quota_info.shared||0,this.usedQuota=this.privateBytes+this.sharedBytes}return e.parse=function(e){return e&&"object"==typeof e?new t.UserInfo(e):e},e.prototype.name=null,e.prototype.email=null,e.prototype.countryCode=null,e.prototype.uid=null,e.prototype.referralUrl=null,e.prototype.publicAppUrl=null,e.prototype.quota=null,e.prototype.usedQuota=null,e.prototype.privateBytes=null,e.prototype.sharedBytes=null,e}(),"undefined"!=typeof window&&null!==window?(!window.XDomainRequest||"withCredentials"in new XMLHttpRequest?(i=window.XMLHttpRequest,o=!1,r=-1===window.navigator.userAgent.indexOf("Firefox")):(i=window.XDomainRequest,o=!0,r=!1),n=!0):(i=require("xmlhttprequest").XMLHttpRequest,o=!1,r=!1,n=!1),t.Xhr=function(){function e(t,e){this.method=t,this.isGet="GET"===this.method,this.url=e,this.headers={},this.params=null,this.body=null,this.preflight=!(this.isGet||"POST"===this.method),this.signed=!1,this.responseType=null,this.callback=null,this.xhr=null}return e.Request=i,e.ieMode=o,e.canSendForms=r,e.doesPreflight=n,e.prototype.setParams=function(t){if(this.signed)throw Error("setParams called after addOauthParams or addOauthHeader");if(this.params)throw Error("setParams cannot be called twice");return this.params=t,this},e.prototype.setCallback=function(t){return this.callback=t,this},e.prototype.signWithOauth=function(e){return t.Xhr.ieMode||t.Xhr.doesPreflight&&!this.preflight?this.addOauthParams(e):this.addOauthHeader(e)},e.prototype.addOauthParams=function(t){if(this.signed)throw Error("Request already has an OAuth signature");return this.params||(this.params={}),t.addAuthParams(this.method,this.url,this.params),this.signed=!0,this},e.prototype.addOauthHeader=function(t){if(this.signed)throw Error("Request already has an OAuth signature");return this.params||(this.params={}),this.signed=!0,this.setHeader("Authorization",t.authHeader(this.method,this.url,this.params))},e.prototype.setBody=function(t){if(this.isGet)throw Error("setBody cannot be called on GET requests");if(null!==this.body)throw Error("Request already has a body");return this.preflight||"undefined"!=typeof FormData&&null!==FormData&&t instanceof FormData||(this.preflight=!0),this.body=t,this},e.prototype.setResponseType=function(t){return this.responseType=t,this},e.prototype.setHeader=function(t,e){var r;if(this.headers[t])throw r=this.headers[t],Error("HTTP header "+t+" already set to "+r);if("Content-Type"===t)throw Error("Content-Type is automatically computed based on setBody");return this.preflight=!0,this.headers[t]=e,this},e.prototype.setFileField=function(t,e,r,n){var o,i;if(null!==this.body)throw Error("Request already has a body"); -if(this.isGet)throw Error("paramsToBody cannot be called on GET requests");return i="object"==typeof r&&("undefined"!=typeof Blob&&null!==Blob&&r instanceof Blob||"undefined"!=typeof File&&null!==File&&r instanceof File),i?(this.body=new FormData,this.body.append(t,r,e)):(n||(n="application/octet-stream"),o=this.multipartBoundary(),this.headers["Content-Type"]="multipart/form-data; boundary="+o,this.body=["--",o,"\r\n",'Content-Disposition: form-data; name="',t,'"; filename="',e,'"\r\n',"Content-Type: ",n,"\r\n","Content-Transfer-Encoding: binary\r\n\r\n",r,"\r\n","--",o,"--","\r\n"].join(""))},e.prototype.multipartBoundary=function(){return[Date.now().toString(36),Math.random().toString(36)].join("----")},e.prototype.paramsToUrl=function(){var e;return this.params&&(e=t.Xhr.urlEncode(this.params),0!==e.length&&(this.url=[this.url,"?",e].join("")),this.params=null),this},e.prototype.paramsToBody=function(){if(this.params){if(null!==this.body)throw Error("Request already has a body");if(this.isGet)throw Error("paramsToBody cannot be called on GET requests");this.headers["Content-Type"]="application/x-www-form-urlencoded",this.body=t.Xhr.urlEncode(this.params),this.params=null}return this},e.prototype.prepare=function(){var e,r,n,o,i=this;if(r=t.Xhr.ieMode,this.isGet||null!==this.body||r?(this.paramsToUrl(),null!==this.body&&"string"==typeof this.body&&(this.headers["Content-Type"]="text/plain; charset=utf8")):this.paramsToBody(),this.xhr=new t.Xhr.Request,r?(this.xhr.onload=function(){return i.onLoad()},this.xhr.onerror=function(){return i.onError()}):this.xhr.onreadystatechange=function(){return i.onReadyStateChange()},this.xhr.open(this.method,this.url,!0),!r){o=this.headers;for(e in o)T.call(o,e)&&(n=o[e],this.xhr.setRequestHeader(e,n))}return this.responseType&&("b"===this.responseType?this.xhr.overrideMimeType&&this.xhr.overrideMimeType("text/plain; charset=x-user-defined"):this.xhr.responseType=this.responseType),this},e.prototype.send=function(t){return this.callback=t||this.callback,null!==this.body?this.xhr.send(this.body):this.xhr.send(),this},e.urlEncode=function(t){var e,r,n;e=[];for(r in t)n=t[r],e.push(this.urlEncodeValue(r)+"="+this.urlEncodeValue(n));return e.sort().join("&")},e.urlEncodeValue=function(t){return encodeURIComponent(""+t).replace(/\!/g,"%21").replace(/'/g,"%27").replace(/\(/g,"%28").replace(/\)/g,"%29").replace(/\*/g,"%2A")},e.urlDecode=function(t){var e,r,n,o,i,s;for(r={},s=t.split("&"),o=0,i=s.length;i>o;o++)n=s[o],e=n.split("="),r[decodeURIComponent(e[0])]=decodeURIComponent(e[1]);return r},e.prototype.onReadyStateChange=function(){var e,r,n,o,i,s,a,h,u;if(4!==this.xhr.readyState)return!0;if(200>this.xhr.status||this.xhr.status>=300)return e=new t.ApiError(this.xhr,this.method,this.url),this.callback(e),!0;if(s=this.xhr.getResponseHeader("x-dropbox-metadata"),null!=s?s.length:void 0)try{i=JSON.parse(s)}catch(l){i=void 0}else i=void 0;if(this.responseType){if("b"===this.responseType){for(n=null!=this.xhr.responseText?this.xhr.responseText:this.xhr.response,r=[],o=h=0,u=n.length;u>=0?u>h:h>u;o=u>=0?++h:--h)r.push(String.fromCharCode(255&n.charCodeAt(o)));a=r.join(""),this.callback(null,a,i)}else this.callback(null,this.xhr.response,i);return!0}switch(a=null!=this.xhr.responseText?this.xhr.responseText:this.xhr.response,this.xhr.getResponseHeader("Content-Type")){case"application/x-www-form-urlencoded":this.callback(null,t.Xhr.urlDecode(a),i);break;case"application/json":case"text/javascript":this.callback(null,JSON.parse(a),i);break;default:this.callback(null,a,i)}return!0},e.prototype.onLoad=function(){var e;switch(e=this.xhr.responseText,this.xhr.contentType){case"application/x-www-form-urlencoded":this.callback(null,t.Xhr.urlDecode(e),void 0);break;case"application/json":case"text/javascript":this.callback(null,JSON.parse(e),void 0);break;default:this.callback(null,e,void 0)}return!0},e.prototype.onError=function(){var e;return e=new t.ApiError(this.xhr,this.method,this.url),this.callback(e),!0},e}(),null!=("undefined"!=typeof module&&null!==module?module.exports:void 0))module.exports=t;else{if("undefined"==typeof window||null===window)throw Error("This library only supports node.js and modern browsers.");window.Dropbox=t}t.atob=h,t.btoa=d,t.hmac=p,t.sha1=c,t.encodeKey=v}).call(this); \ No newline at end of file +(function(){var t,e,r,n,o,i,s,a,h,u,l,p,c,d,f,y,v,m,g,w,S,b,T,_={}.hasOwnProperty,E=function(t,e){function r(){this.constructor=t}for(var n in e)_.call(e,n)&&(t[n]=e[n]);return r.prototype=e.prototype,t.prototype=new r,t.__super__=e.prototype,t};if(t=function(){function t(t){this.client=new e(t)}return t}(),t.ApiError=function(){function t(t,e,r){var n;if(this.method=e,this.url=r,this.status=t.status,n=t.responseType?t.response||t.responseText:t.responseText)try{this.responseText=""+n,this.response=JSON.parse(n)}catch(o){this.response=null}else this.responseText="(no response)",this.response=null}return t.prototype.status=void 0,t.prototype.method=void 0,t.prototype.url=void 0,t.prototype.responseText=void 0,t.prototype.response=void 0,t.prototype.toString=function(){return"Dropbox API error "+this.status+" from "+this.method+" "+this.url+" :: "+this.responseText},t.prototype.inspect=function(){return""+this},t}(),"undefined"!=typeof window&&null!==window?window.atob&&window.btoa?(u=function(t){return window.atob(t)},f=function(t){return window.btoa(t)}):(p="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/",y=function(t,e,r){var n,o;for(o=3-e,t<<=8*o,n=3;n>=o;)r.push(p.charAt(63&t>>6*n)),n-=1;for(n=e;3>n;)r.push("="),n+=1;return null},l=function(t,e,r){var n,o;for(o=4-e,t<<=6*o,n=2;n>=o;)r.push(String.fromCharCode(255&t>>8*n)),n-=1;return null},f=function(t){var e,r,n,o,i,s;for(o=[],e=0,r=0,n=i=0,s=t.length;s>=0?s>i:i>s;n=s>=0?++i:--i)e=e<<8|t.charCodeAt(n),r+=1,3===r&&(y(e,r,o),e=r=0);return r>0&&y(e,r,o),o.join("")},u=function(t){var e,r,n,o,i,s,a;for(i=[],e=0,n=0,o=s=0,a=t.length;(a>=0?a>s:s>a)&&(r=t.charAt(o),"="!==r);o=a>=0?++s:--s)e=e<<6|p.indexOf(r),n+=1,4===n&&(l(e,n,i),e=n=0);return n>0&&l(e,n,i),i.join("")}):(u=function(t){var e,r;return e=new Buffer(t,"base64"),function(){var t,n,o;for(o=[],r=t=0,n=e.length;n>=0?n>t:t>n;r=n>=0?++t:--t)o.push(String.fromCharCode(e[r]));return o}().join("")},f=function(t){var e,r;return e=new Buffer(function(){var e,n,o;for(o=[],r=e=0,n=t.length;n>=0?n>e:e>n;r=n>=0?++e:--e)o.push(t.charCodeAt(r));return o}()),e.toString("base64")}),t.Client=function(){function r(e){this.sandbox=e.sandbox||!1,this.apiServer=e.server||this.defaultApiServer(),this.authServer=e.authServer||this.defaultAuthServer(),this.fileServer=e.fileServer||this.defaultFileServer(),this.downloadServer=e.downloadServer||this.defaultDownloadServer(),this.oauth=new t.Oauth(e),this.driver=null,this.filter=null,this.uid=null,this.authState=null,this.authError=null,this._credentials=null,this.setCredentials(e),this.setupUrls()}return r.prototype.authDriver=function(t){return this.driver=t,this},r.prototype.xhrFilter=function(t){return this.filter=t,this},r.prototype.dropboxUid=function(){return this.uid},r.prototype.credentials=function(){return this._credentials||this.computeCredentials(),this._credentials},r.prototype.authenticate=function(r){var n,o,i=this;return n=null,o=function(){var s;if(n!==i.authState&&(n=i.authState,i.driver.onAuthStateChange))return i.driver.onAuthStateChange(i,o);switch(i.authState){case e.RESET:return i.requestToken(function(t,r){var n,s;return t?(i.authError=t,i.authState=e.ERROR):(n=r.oauth_token,s=r.oauth_token_secret,i.oauth.setToken(n,s),i.authState=e.REQUEST),i._credentials=null,o()});case e.REQUEST:return s=i.authorizeUrl(i.oauth.token),i.driver.doAuthorize(s,i.oauth.token,i.oauth.tokenSecret,function(){return i.authState=e.AUTHORIZED,i._credentials=null,o()});case e.AUTHORIZED:return i.getAccessToken(function(t,r){return t?(i.authError=t,i.authState=e.ERROR):(i.oauth.setToken(r.oauth_token,r.oauth_token_secret),i.uid=r.uid,i.authState=e.DONE),i._credentials=null,o()});case e.DONE:return r(null,i);case t.SIGNED_OFF:return i.reset(),o();case e.ERROR:return r(i.authError)}},o(),this},r.prototype.signOut=function(r){var n,o=this;return n=new t.Xhr("POST",this.urls.signOut),n.signWithOauth(this.oauth),this.dispatchXhr(n,function(t){return t?r(t):(o.reset(),o.authState=e.SIGNED_OFF,o.driver.onAuthStateChange?o.driver.onAuthStateChange(o,function(){return r(t)}):r(t))})},r.prototype.signOff=function(t){return this.signOut(t)},r.prototype.getUserInfo=function(e){var r;return r=new t.Xhr("GET",this.urls.accountInfo),r.signWithOauth(this.oauth),this.dispatchXhr(r,function(r,n){return e(r,t.UserInfo.parse(n),n)})},r.prototype.readFile=function(e,r,n){var o,i,s,a,h,u;return n||"function"!=typeof r||(n=r,r=null),o={},h=null,s=null,r&&(r.versionTag?o.rev=r.versionTag:r.rev&&(o.rev=r.rev),r.arrayBuffer?h="arraybuffer":r.blob?h="blob":r.binary&&(h="b"),r.length?(null!=r.start?(a=r.start,i=r.start+r.length-1):(a="",i=r.length),s="bytes="+a+"-"+i):null!=r.start&&(s="bytes="+r.start+"-")),u=new t.Xhr("GET",""+this.urls.getFile+"/"+this.urlEncodePath(e)),u.setParams(o).signWithOauth(this.oauth).setResponseType(h),s&&u.setHeader("Range",s),this.dispatchXhr(u,function(e,r,o){return n(e,r,t.Stat.parse(o))})},r.prototype.writeFile=function(e,r,n,o){var i;return o||"function"!=typeof n||(o=n,n=null),i=t.Xhr.canSendForms&&"object"==typeof r,i?this.writeFileUsingForm(e,r,n,o):this.writeFileUsingPut(e,r,n,o)},r.prototype.writeFileUsingForm=function(e,r,n,o){var i,s,a,h;return a=e.lastIndexOf("/"),-1===a?(i=e,e=""):(i=e.substring(a),e=e.substring(0,a)),s={file:i},n&&(n.noOverwrite&&(s.overwrite="false"),n.lastVersionTag?s.parent_rev=n.lastVersionTag:(n.parentRev||n.parent_rev)&&(s.parent_rev=n.parentRev||n.parent_rev)),h=new t.Xhr("POST",""+this.urls.postFile+"/"+this.urlEncodePath(e)),h.setParams(s).signWithOauth(this.oauth).setFileField("file",i,r,"application/octet-stream"),delete s.file,this.dispatchXhr(h,function(e,r){return o(e,t.Stat.parse(r))})},r.prototype.writeFileUsingPut=function(e,r,n,o){var i,s;return i={},n&&(n.noOverwrite&&(i.overwrite="false"),n.lastVersionTag?i.parent_rev=n.lastVersionTag:(n.parentRev||n.parent_rev)&&(i.parent_rev=n.parentRev||n.parent_rev)),s=new t.Xhr("POST",""+this.urls.putFile+"/"+this.urlEncodePath(e)),s.setBody(r).setParams(i).signWithOauth(this.oauth),this.dispatchXhr(s,function(e,r){return o(e,t.Stat.parse(r))})},r.prototype.stat=function(e,r,n){var o,i;return n||"function"!=typeof r||(n=r,r=null),o={},r&&(null!=r.version&&(o.rev=r.version),(r.removed||r.deleted)&&(o.include_deleted="true"),r.readDir&&(o.list="true",r.readDir!==!0&&(o.file_limit=""+r.readDir)),r.cacheHash&&(o.hash=r.cacheHash)),o.include_deleted||(o.include_deleted="false"),o.list||(o.list="false"),i=new t.Xhr("GET",""+this.urls.metadata+"/"+this.urlEncodePath(e)),i.setParams(o).signWithOauth(this.oauth),this.dispatchXhr(i,function(e,r){var o,i,s;return s=t.Stat.parse(r),o=(null!=r?r.contents:void 0)?function(){var e,n,o,s;for(o=r.contents,s=[],e=0,n=o.length;n>e;e++)i=o[e],s.push(t.Stat.parse(i));return s}():void 0,n(e,s,o)})},r.prototype.readdir=function(t,e,r){var n;return r||"function"!=typeof e||(r=e,e=null),n={readDir:!0},e&&(null!=e.limit&&(n.readDir=e.limit),e.versionTag&&(n.versionTag=e.versionTag)),this.stat(t,n,function(t,e,n){var o,i;return o=n?function(){var t,e,r;for(r=[],t=0,e=n.length;e>t;t++)i=n[t],r.push(i.name);return r}():null,r(t,o,e,n)})},r.prototype.metadata=function(t,e,r){return this.stat(t,e,r)},r.prototype.makeUrl=function(e,r,n){var o,i,s,a,h;return n||"function"!=typeof r||(n=r,r=null),i=r&&(r["long"]||r.longUrl||r.downloadHack)?{short_url:"false"}:{},e=this.urlEncodePath(e),s=""+this.urls.shares+"/"+e,o=!1,a=!1,r&&(r.downloadHack?(o=!0,a=!0):r.download&&(o=!0,s=""+this.urls.media+"/"+e)),h=new t.Xhr("POST",s).setParams(i).signWithOauth(this.oauth),this.dispatchXhr(h,function(e,r){return a&&r&&r.url&&(r.url=r.url.replace(this.authServer,this.downloadServer)),n(e,t.PublicUrl.parse(r,o))})},r.prototype.history=function(e,r,n){var o,i;return n||"function"!=typeof r||(n=r,r=null),o={},r&&null!=r.limit&&(o.rev_limit=r.limit),i=new t.Xhr("GET",""+this.urls.revisions+"/"+this.urlEncodePath(e)),i.setParams(o).signWithOauth(this.oauth),this.dispatchXhr(i,function(e,r){var o,i;return i=r?function(){var e,n,i;for(i=[],e=0,n=r.length;n>e;e++)o=r[e],i.push(t.Stat.parse(o));return i}():void 0,n(e,i)})},r.prototype.revisions=function(t,e,r){return this.history(t,e,r)},r.prototype.thumbnailUrl=function(t,e){var r;return r=this.thumbnailXhr(t,e),r.paramsToUrl().url},r.prototype.readThumbnail=function(e,r,n){var o,i;return n||"function"!=typeof r||(n=r,r=null),o="b",r&&r.blob&&(o="blob"),i=this.thumbnailXhr(e,r),i.setResponseType(o),this.dispatchXhr(i,function(e,r,o){return n(e,r,t.Stat.parse(o))})},r.prototype.thumbnailXhr=function(e,r){var n,o;return n={},r&&(r.format?n.format=r.format:r.png&&(n.format="png"),r.size&&(n.size=r.size)),o=new t.Xhr("GET",""+this.urls.thumbnails+"/"+this.urlEncodePath(e)),o.setParams(n).signWithOauth(this.oauth)},r.prototype.revertFile=function(e,r,n){var o;return o=new t.Xhr("POST",""+this.urls.restore+"/"+this.urlEncodePath(e)),o.setParams({rev:r}).signWithOauth(this.oauth),this.dispatchXhr(o,function(e,r){return n(e,t.Stat.parse(r))})},r.prototype.restore=function(t,e,r){return this.revertFile(t,e,r)},r.prototype.findByName=function(e,r,n,o){var i,s;return o||"function"!=typeof n||(o=n,n=null),i={query:r},n&&(null!=n.limit&&(i.file_limit=n.limit),(n.removed||n.deleted)&&(i.include_deleted=!0)),s=new t.Xhr("GET",""+this.urls.search+"/"+this.urlEncodePath(e)),s.setParams(i).signWithOauth(this.oauth),this.dispatchXhr(s,function(e,r){var n,i;return i=r?function(){var e,o,i;for(i=[],e=0,o=r.length;o>e;e++)n=r[e],i.push(t.Stat.parse(n));return i}():void 0,o(e,i)})},r.prototype.search=function(t,e,r,n){return this.findByName(t,e,r,n)},r.prototype.makeCopyReference=function(e,r){var n;return n=new t.Xhr("GET",""+this.urls.copyRef+"/"+this.urlEncodePath(e)),n.signWithOauth(this.oauth),this.dispatchXhr(n,function(e,n){return r(e,t.CopyReference.parse(n))})},r.prototype.copyRef=function(t,e){return this.makeCopyReference(t,e)},r.prototype.pullChanges=function(e,r){var n,o;return r||"function"!=typeof e||(r=e,e=null),n=e?e.cursorTag?{cursor:e.cursorTag}:{cursor:e}:{},o=new t.Xhr("POST",this.urls.delta),o.setParams(n).signWithOauth(this.oauth),this.dispatchXhr(o,function(e,n){return r(e,t.PulledChanges.parse(n))})},r.prototype.delta=function(t,e){return this.pullChanges(t,e)},r.prototype.mkdir=function(e,r){var n;return n=new t.Xhr("POST",this.urls.fileopsCreateFolder),n.setParams({root:this.fileRoot,path:this.normalizePath(e)}).signWithOauth(this.oauth),this.dispatchXhr(n,function(e,n){return r(e,t.Stat.parse(n))})},r.prototype.remove=function(e,r){var n;return n=new t.Xhr("POST",this.urls.fileopsDelete),n.setParams({root:this.fileRoot,path:this.normalizePath(e)}).signWithOauth(this.oauth),this.dispatchXhr(n,function(e,n){return r(e,t.Stat.parse(n))})},r.prototype.unlink=function(t,e){return this.remove(t,e)},r.prototype["delete"]=function(t,e){return this.remove(t,e)},r.prototype.copy=function(e,r,n){var o,i,s;return n||"function"!=typeof o||(n=o,o=null),i={root:this.fileRoot,to_path:this.normalizePath(r)},e instanceof t.CopyReference?i.from_copy_ref=e.tag:i.from_path=this.normalizePath(e),s=new t.Xhr("POST",this.urls.fileopsCopy),s.setParams(i).signWithOauth(this.oauth),this.dispatchXhr(s,function(e,r){return n(e,t.Stat.parse(r))})},r.prototype.move=function(e,r,n){var o,i;return n||"function"!=typeof o||(n=o,o=null),i=new t.Xhr("POST",this.urls.fileopsMove),i.setParams({root:this.fileRoot,from_path:this.normalizePath(e),to_path:this.normalizePath(r)}).signWithOauth(this.oauth),this.dispatchXhr(i,function(e,r){return n(e,t.Stat.parse(r))})},r.prototype.reset=function(){return this.uid=null,this.oauth.setToken(null,""),this.authState=e.RESET,this.authError=null,this._credentials=null,this},r.prototype.setCredentials=function(t){return this.oauth.reset(t),this.uid=t.uid||null,this.authState=t.authState?t.authState:t.token?e.DONE:e.RESET,this.authError=null,this._credentials=null,this},r.prototype.appHash=function(){return this.oauth.appHash()},r.prototype.setupUrls=function(){return this.fileRoot=this.sandbox?"sandbox":"dropbox",this.urls={requestToken:""+this.apiServer+"/1/oauth/request_token",authorize:""+this.authServer+"/1/oauth/authorize",accessToken:""+this.apiServer+"/1/oauth/access_token",signOut:""+this.apiServer+"/1/unlink_access_token",accountInfo:""+this.apiServer+"/1/account/info",getFile:""+this.fileServer+"/1/files/"+this.fileRoot,postFile:""+this.fileServer+"/1/files/"+this.fileRoot,putFile:""+this.fileServer+"/1/files_put/"+this.fileRoot,metadata:""+this.apiServer+"/1/metadata/"+this.fileRoot,delta:""+this.apiServer+"/1/delta",revisions:""+this.apiServer+"/1/revisions/"+this.fileRoot,restore:""+this.apiServer+"/1/restore/"+this.fileRoot,search:""+this.apiServer+"/1/search/"+this.fileRoot,shares:""+this.apiServer+"/1/shares/"+this.fileRoot,media:""+this.apiServer+"/1/media/"+this.fileRoot,copyRef:""+this.apiServer+"/1/copy_ref/"+this.fileRoot,thumbnails:""+this.fileServer+"/1/thumbnails/"+this.fileRoot,fileopsCopy:""+this.apiServer+"/1/fileops/copy",fileopsCreateFolder:""+this.apiServer+"/1/fileops/create_folder",fileopsDelete:""+this.apiServer+"/1/fileops/delete",fileopsMove:""+this.apiServer+"/1/fileops/move"}},r.ERROR=0,r.RESET=1,r.REQUEST=2,r.AUTHORIZED=3,r.DONE=4,r.SIGNED_OFF=5,r.prototype.urlEncodePath=function(e){return t.Xhr.urlEncodeValue(this.normalizePath(e)).replace(/%2F/gi,"/")},r.prototype.normalizePath=function(t){var e;if("/"===t.substring(0,1)){for(e=1;"/"===t.substring(e,e+1);)e+=1;return t.substring(e)}return t},r.prototype.requestToken=function(e){var r;return r=new t.Xhr("POST",this.urls.requestToken).signWithOauth(this.oauth),this.dispatchXhr(r,e)},r.prototype.authorizeUrl=function(e){var r;return r={oauth_token:e,oauth_callback:this.driver.url()},""+this.urls.authorize+"?"+t.Xhr.urlEncode(r)},r.prototype.getAccessToken=function(e){var r;return r=new t.Xhr("POST",this.urls.accessToken).signWithOauth(this.oauth),this.dispatchXhr(r,e)},r.prototype.dispatchXhr=function(t,e){var r;return t.setCallback(e),t.prepare(),r=t.xhr,this.filter&&!this.filter(r,t)?r:(t.send(),r)},r.prototype.defaultApiServer=function(){return"https://api.dropbox.com"},r.prototype.defaultAuthServer=function(){return this.apiServer.replace("api.","www.")},r.prototype.defaultFileServer=function(){return this.apiServer.replace("api.","api-content.")},r.prototype.defaultDownloadServer=function(){return this.apiServer.replace("api.","dl.")},r.prototype.computeCredentials=function(){var t;return t={key:this.oauth.key,sandbox:this.sandbox},this.oauth.secret&&(t.secret=this.oauth.secret),this.oauth.token&&(t.token=this.oauth.token,t.tokenSecret=this.oauth.tokenSecret),this.uid&&(t.uid=this.uid),this.authState!==e.ERROR&&this.authState!==e.RESET&&this.authState!==e.DONE&&this.authState!==e.SIGNED_OFF&&(t.authState=this.authState),this.apiServer!==this.defaultApiServer()&&(t.server=this.apiServer),this.authServer!==this.defaultAuthServer()&&(t.authServer=this.authServer),this.fileServer!==this.defaultFileServer()&&(t.fileServer=this.fileServer),this.downloadServer!==this.defaultDownloadServer()&&(t.downloadServer=this.downloadServer),this._credentials=t},r}(),e=t.Client,t.AuthDriver=function(){function t(){}return t.prototype.url=function(){return"https://some.url"},t.prototype.doAuthorize=function(t,e,r,n){return n("oauth-token")},t.prototype.onAuthStateChange=function(t,e){return e()},t}(),t.Drivers={},t.Drivers.BrowserBase=function(){function t(t){this.rememberUser=(null!=t?t.rememberUser:void 0)||!1,this.scope=(null!=t?t.scope:void 0)||"default"}return t.prototype.onAuthStateChange=function(t,r){var n=this;switch(this.setStorageKey(t),t.authState){case e.RESET:return this.loadCredentials(function(e){return e?e.authState?(t.setCredentials(e),r()):n.rememberUser?(t.setCredentials(e),t.getUserInfo(function(e){return e?(t.reset(),n.forgetCredentials(r)):r()})):(n.forgetCredentials(),r()):r()});case e.REQUEST:return this.storeCredentials(t.credentials(),r);case e.DONE:return this.rememberUser?this.storeCredentials(t.credentials(),r):this.forgetCredentials(r);case e.SIGNED_OFF:return this.forgetCredentials(r);case e.ERROR:return this.forgetCredentials(r);default:return r(),this}},t.prototype.setStorageKey=function(t){return this.storageKey="dropbox-auth:"+this.scope+":"+t.appHash(),this},t.prototype.storeCredentials=function(t,e){return localStorage.setItem(this.storageKey,JSON.stringify(t)),e(),this},t.prototype.loadCredentials=function(t){var e;if(e=localStorage.getItem(this.storageKey),!e)return t(null),this;try{t(JSON.parse(e))}catch(r){t(null)}return this},t.prototype.forgetCredentials=function(t){return localStorage.removeItem(this.storageKey),t(),this},t.currentLocation=function(){return window.location.href},t}(),t.Drivers.Redirect=function(r){function n(t){n.__super__.constructor.call(this,t),this.useQuery=(null!=t?t.useQuery:void 0)||!1,this.receiverUrl=this.computeUrl(t),this.tokenRe=RegExp("(#|\\?|&)oauth_token=([^&#]+)(&|#|$)")}return E(n,r),n.prototype.onAuthStateChange=function(t,r){var o,i=this;return o=function(){return function(){return n.__super__.onAuthStateChange.call(i,t,r)}}(),this.setStorageKey(t),t.authState===e.RESET?this.loadCredentials(function(t){return t&&t.authState?t.token===i.locationToken()&&t.authState===e.REQUEST?(t.authState=e.AUTHORIZED,i.storeCredentials(t,o)):i.forgetCredentials(o):o()}):o()},n.prototype.url=function(){return this.receiverUrl},n.prototype.doAuthorize=function(t){return window.location.assign(t)},n.prototype.computeUrl=function(){var e,r,n,o;return o="_dropboxjs_scope="+encodeURIComponent(this.scope),r=t.Drivers.BrowserBase.currentLocation(),-1===r.indexOf("#")?e=null:(n=r.split("#",2),r=n[0],e=n[1]),this.useQuery?r+=-1===r.indexOf("?")?"?"+o:"&"+o:e="?"+o,e?r+"#"+e:r},n.prototype.locationToken=function(){var e,r,n;return e=t.Drivers.BrowserBase.currentLocation(),n="_dropboxjs_scope="+encodeURIComponent(this.scope)+"&",-1===("function"==typeof e.indexOf?e.indexOf(n):void 0)?null:(r=this.tokenRe.exec(e),r?decodeURIComponent(r[2]):null)},n}(t.Drivers.BrowserBase),t.Drivers.Popup=function(r){function n(t){n.__super__.constructor.call(this,t),this.receiverUrl=this.computeUrl(t),this.tokenRe=RegExp("(#|\\?|&)oauth_token=([^&#]+)(&|#|$)")}return E(n,r),n.prototype.onAuthStateChange=function(t,r){var o,i=this;return o=function(){return function(){return n.__super__.onAuthStateChange.call(i,t,r)}}(),this.setStorageKey(t),t.authState===e.RESET?this.loadCredentials(function(t){return t&&t.authState?this.forgetCredentials(o):o()}):o()},n.prototype.doAuthorize=function(t,e,r,n){return this.listenForMessage(e,n),this.openWindow(t)},n.prototype.url=function(){return this.receiverUrl},n.prototype.computeUrl=function(e){var r;if(e){if(e.receiverUrl)return e.noFragment||-1!==e.receiverUrl.indexOf("#")?e.receiverUrl:e.receiverUrl+"#";if(e.receiverFile)return r=t.Drivers.BrowserBase.currentLocation().split("/"),r[r.length-1]=e.receiverFile,e.noFragment?r.join("/"):r.join("/")+"#"}return t.Drivers.BrowserBase.currentLocation()},n.prototype.openWindow=function(t){return window.open(t,"_dropboxOauthSigninWindow",this.popupWindowSpec(980,700))},n.prototype.popupWindowSpec=function(t,e){var r,n,o,i,s,a,h,u,l,p;return s=null!=(h=window.screenX)?h:window.screenLeft,a=null!=(u=window.screenY)?u:window.screenTop,i=null!=(l=window.outerWidth)?l:document.documentElement.clientWidth,r=null!=(p=window.outerHeight)?p:document.documentElement.clientHeight,n=Math.round(s+(i-t)/2),o=Math.round(a+(r-e)/2.5),s>n&&(n=s),a>o&&(o=a),"width="+t+",height="+e+","+("left="+n+",top="+o)+"dialog=yes,dependent=yes,scrollbars=yes,location=yes"},n.prototype.listenForMessage=function(t,e){var r,n=this;return r=function(o){var i;return i=n.tokenRe.exec(""+o.data),i&&decodeURIComponent(i[2])===t?(window.removeEventListener("message",r),e()):void 0},window.addEventListener("message",r,!1)},n}(t.Drivers.BrowserBase),t.Drivers.NodeServer=function(){function t(t){this.port=(null!=t?t.port:void 0)||8912,this.faviconFile=(null!=t?t.favicon:void 0)||null,this.fs=require("fs"),this.http=require("http"),this.open=require("open"),this.callbacks={},this.urlRe=RegExp("^/oauth_callback\\?"),this.tokenRe=RegExp("(\\?|&)oauth_token=([^&]+)(&|$)"),this.createApp()}return t.prototype.url=function(){return"http://localhost:"+this.port+"/oauth_callback"},t.prototype.doAuthorize=function(t,e,r,n){return this.callbacks[e]=n,this.openBrowser(t)},t.prototype.openBrowser=function(t){if(!t.match(/^https?:\/\//))throw Error("Not a http/https URL: "+t);return this.open(t)},t.prototype.createApp=function(){var t=this;return this.app=this.http.createServer(function(e,r){return t.doRequest(e,r)}),this.app.listen(this.port)},t.prototype.closeServer=function(){return this.app.close()},t.prototype.doRequest=function(t,e){var r,n,o,i=this;return this.urlRe.exec(t.url)&&(n=this.tokenRe.exec(t.url),n&&(o=decodeURIComponent(n[2]),this.callbacks[o]&&(this.callbacks[o](),delete this.callbacks[o]))),r="",t.on("data",function(t){return r+=t}),t.on("end",function(){return i.faviconFile&&"/favicon.ico"===t.url?i.sendFavicon(e):i.closeBrowser(e)})},t.prototype.closeBrowser=function(t){var e;return e='\n\n

    Please close this window.

    ',t.writeHead(200,{"Content-Length":e.length,"Content-Type":"text/html"}),t.write(e),t.end},t.prototype.sendFavicon=function(t){return this.fs.readFile(this.faviconFile,function(e,r){return t.writeHead(200,{"Content-Length":r.length,"Content-Type":"image/x-icon"}),t.write(r),t.end})},t}(),c=function(t,e){return h(g(b(t),b(e),t.length,e.length))},d=function(t){return h(S(b(t),t.length))},("undefined"==typeof window||null===window)&&(v=require("crypto"),c=function(t,e){var r;return r=v.createHmac("sha1",e),r.update(t),r.digest("base64")},d=function(t){var e;return e=v.createHash("sha1"),e.update(t),e.digest("base64")}),g=function(t,e,r,n){var o,i,s,a;return e.length>16&&(e=S(e,n)),s=function(){var t,r;for(r=[],i=t=0;16>t;i=++t)r.push(909522486^e[i]);return r}(),a=function(){var t,r;for(r=[],i=t=0;16>t;i=++t)r.push(1549556828^e[i]);return r}(),o=S(s.concat(t),64+r),S(a.concat(o),84)},S=function(t,e){var r,n,o,i,s,h,u,l,p,c,d,f,y,v,m,g,S,b;for(t[e>>2]|=1<<31-((3&e)<<3),t[(e+8>>6<<4)+15]=e<<3,g=Array(80),r=1732584193,o=-271733879,s=-1732584194,u=271733878,p=-1009589776,f=0,m=t.length;m>f;){for(n=r,i=o,h=s,l=u,c=p,y=b=0;80>b;y=++b)g[y]=16>y?t[f+y]:w(g[y-3]^g[y-8]^g[y-14]^g[y-16],1),20>y?(d=o&s|~o&u,v=1518500249):40>y?(d=o^s^u,v=1859775393):60>y?(d=o&s|o&u|s&u,v=-1894007588):(d=o^s^u,v=-899497514),S=a(a(w(r,5),d),a(a(p,g[y]),v)),p=u,u=s,s=w(o,30),o=r,r=S;r=a(r,n),o=a(o,i),s=a(s,h),u=a(u,l),p=a(p,c),f+=16}return[r,o,s,u,p]},w=function(t,e){return t<>>32-e},a=function(t,e){var r,n;return n=(65535&t)+(65535&e),r=(t>>16)+(e>>16)+(n>>16),r<<16|65535&n},h=function(t){var e,r,n,o,i;for(o="",e=0,n=4*t.length;n>e;)r=e,i=(255&t[r>>2]>>(3-(3&r)<<3))<<16,r+=1,i|=(255&t[r>>2]>>(3-(3&r)<<3))<<8,r+=1,i|=255&t[r>>2]>>(3-(3&r)<<3),o+=T[63&i>>18],o+=T[63&i>>12],e+=1,o+=e>=n?"=":T[63&i>>6],e+=1,o+=e>=n?"=":T[63&i],e+=1;return o},T="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/",b=function(t){var e,r,n,o,i;for(e=[],n=255,r=o=0,i=t.length;i>=0?i>o:o>i;r=i>=0?++o:--o)e[r>>2]|=(t.charCodeAt(r)&n)<<(3-(3&r)<<3);return e},t.Oauth=function(){function e(t){this.key=this.k=null,this.secret=this.s=null,this.token=null,this.tokenSecret=null,this._appHash=null,this.reset(t)}return e.prototype.reset=function(t){var e,r,n,o;if(t.secret)this.k=this.key=t.key,this.s=this.secret=t.secret,this._appHash=null;else if(t.key)this.key=t.key,this.secret=null,n=u(m(this.key).split("|",2)[1]),o=n.split("?",2),e=o[0],r=o[1],this.k=decodeURIComponent(e),this.s=decodeURIComponent(r),this._appHash=null;else if(!this.k)throw Error("No API key supplied");return t.token?this.setToken(t.token,t.tokenSecret):this.setToken(null,"")},e.prototype.setToken=function(e,r){if(e&&!r)throw Error("No secret supplied with the user token");return this.token=e,this.tokenSecret=r||"",this.hmacKey=t.Xhr.urlEncodeValue(this.s)+"&"+t.Xhr.urlEncodeValue(r),null},e.prototype.authHeader=function(e,r,n){var o,i,s,a,h,u;this.addAuthParams(e,r,n),i=[];for(s in n)a=n[s],"oauth_"===s.substring(0,6)&&i.push(s);for(i.sort(),o=[],h=0,u=i.length;u>h;h++)s=i[h],o.push(t.Xhr.urlEncodeValue(s)+'="'+t.Xhr.urlEncodeValue(n[s])+'"'),delete n[s];return"OAuth "+o.join(",")},e.prototype.addAuthParams=function(t,e,r){return this.boilerplateParams(r),r.oauth_signature=this.signature(t,e,r),r},e.prototype.boilerplateParams=function(t){return t.oauth_consumer_key=this.k,t.oauth_nonce=this.nonce(),t.oauth_signature_method="HMAC-SHA1",this.token&&(t.oauth_token=this.token),t.oauth_timestamp=Math.floor(Date.now()/1e3),t.oauth_version="1.0",t},e.prototype.nonce=function(){return Date.now().toString(36)+Math.random().toString(36)},e.prototype.signature=function(e,r,n){var o;return o=e.toUpperCase()+"&"+t.Xhr.urlEncodeValue(r)+"&"+t.Xhr.urlEncodeValue(t.Xhr.urlEncode(n)),c(o,this.hmacKey)},e.prototype.appHash=function(){return this._appHash?this._appHash:this._appHash=d(this.k).replace(/\=/g,"")},e}(),null==Date.now&&(Date.now=function(){return(new Date).getTime()}),m=function(t,e){var r,n,o,i,s,a,h,l,p,c,d,y;for(e?(e=[encodeURIComponent(t),encodeURIComponent(e)].join("?"),t=function(){var e,n,o;for(o=[],r=e=0,n=t.length/2;n>=0?n>e:e>n;r=n>=0?++e:--e)o.push(16*(15&t.charCodeAt(2*r))+(15&t.charCodeAt(2*r+1)));return o}()):(c=t.split("|",2),t=c[0],e=c[1],t=u(t),t=function(){var e,n,o;for(o=[],r=e=0,n=t.length;n>=0?n>e:e>n;r=n>=0?++e:--e)o.push(t.charCodeAt(r));return o}(),e=u(e)),i=function(){for(y=[],l=0;256>l;l++)y.push(l);return y}.apply(this),a=0,s=p=0;256>p;s=++p)a=(a+i[r]+t[s%t.length])%256,d=[i[a],i[s]],i[s]=d[0],i[a]=d[1];return s=a=0,o=function(){var t,r,o,u;for(u=[],h=t=0,r=e.length;r>=0?r>t:t>r;h=r>=0?++t:--t)s=(s+1)%256,a=(a+i[s])%256,o=[i[a],i[s]],i[s]=o[0],i[a]=o[1],n=i[(i[s]+i[a])%256],u.push(String.fromCharCode((n^e.charCodeAt(h))%256));return u}(),t=function(){var e,n,o;for(o=[],r=e=0,n=t.length;n>=0?n>e:e>n;r=n>=0?++e:--e)o.push(String.fromCharCode(t[r]));return o}(),[f(t.join("")),f(o.join(""))].join("|")},t.PulledChanges=function(){function e(e){var r;this.blankSlate=e.reset||!1,this.cursorTag=e.cursor,this.shouldPullAgain=e.has_more,this.shouldBackOff=!this.shouldPullAgain,this.changes=e.cursor&&e.cursor.length?function(){var n,o,i,s;for(i=e.entries,s=[],n=0,o=i.length;o>n;n++)r=i[n],s.push(t.PullChange.parse(r));return s}():[]}return e.parse=function(e){return e&&"object"==typeof e?new t.PulledChanges(e):e},e.prototype.blankSlate=void 0,e.prototype.cursorTag=void 0,e.prototype.changes=void 0,e.prototype.shouldPullAgain=void 0,e.prototype.shouldBackOff=void 0,e}(),t.PullChange=function(){function e(e){this.path=e[0],this.stat=t.Stat.parse(e[1]),this.stat?this.wasRemoved=!1:(this.stat=null,this.wasRemoved=!0)}return e.parse=function(e){return e&&"object"==typeof e?new t.PullChange(e):e},e.prototype.path=void 0,e.prototype.wasRemoved=void 0,e.prototype.stat=void 0,e}(),t.PublicUrl=function(){function e(t,e){this.url=t.url,this.expiresAt=new Date(Date.parse(t.expires)),this.isDirect=e===!0?!0:e===!1?!1:864e5>=Date.now()-this.expiresAt,this.isPreview=!this.isDirect}return e.parse=function(e,r){return e&&"object"==typeof e?new t.PublicUrl(e,r):e},e.prototype.url=void 0,e.prototype.expiresAt=void 0,e.prototype.isDirect=void 0,e.prototype.isPreview=void 0,e}(),t.CopyReference=function(){function e(t){"object"==typeof t?(this.tag=t.copy_ref,this.expiresAt=new Date(Date.parse(t.expires))):(this.tag=t,this.expiresAt=new Date)}return e.parse=function(e){return!e||"object"!=typeof e&&"string"!=typeof e?e:new t.CopyReference(e)},e.prototype.tag=void 0,e.prototype.expiresAt=void 0,e}(),t.Stat=function(){function e(t){var e,r,n,o;switch(this.path=t.path,"/"!==this.path.substring(0,1)&&(this.path="/"+this.path),e=this.path.length-1,e>=0&&"/"===this.path.substring(e)&&(this.path=this.path.substring(0,e)),r=this.path.lastIndexOf("/"),this.name=this.path.substring(r+1),this.isFolder=t.is_dir||!1,this.isFile=!this.isFolder,this.isRemoved=t.is_deleted||!1,this.typeIcon=t.icon,this.modifiedAt=(null!=(n=t.modified)?n.length:void 0)?new Date(Date.parse(t.modified)):null,this.clientModifiedAt=(null!=(o=t.client_mtime)?o.length:void 0)?new Date(Date.parse(t.client_mtime)):null,t.root){case"dropbox":this.inAppFolder=!1;break;case"app_folder":this.inAppFolder=!0;break;default:this.inAppFolder=null}this.size=t.bytes||0,this.humanSize=t.size||"",this.hasThumbnail=t.thumb_exists||!1,this.isFolder?(this.versionTag=t.hash,this.mimeType=t.mime_type||"inode/directory"):(this.versionTag=t.rev,this.mimeType=t.mime_type||"application/octet-stream")}return e.parse=function(e){return e&&"object"==typeof e?new t.Stat(e):e},e.prototype.path=null,e.prototype.name=null,e.prototype.inAppFolder=null,e.prototype.isFolder=null,e.prototype.isFile=null,e.prototype.isRemoved=null,e.prototype.typeIcon=null,e.prototype.versionTag=null,e.prototype.mimeType=null,e.prototype.size=null,e.prototype.humanSize=null,e.prototype.hasThumbnail=null,e.prototype.modifiedAt=null,e.prototype.clientModifiedAt=null,e}(),t.UserInfo=function(){function e(t){var e;this.name=t.display_name,this.email=t.email,this.countryCode=t.country||null,this.uid=""+t.uid,t.public_app_url?(this.publicAppUrl=t.public_app_url,e=this.publicAppUrl.length-1,e>=0&&"/"===this.publicAppUrl.substring(e)&&(this.publicAppUrl=this.publicAppUrl.substring(0,e))):this.publicAppUrl=null,this.referralUrl=t.referral_link,this.quota=t.quota_info.quota,this.privateBytes=t.quota_info.normal||0,this.sharedBytes=t.quota_info.shared||0,this.usedQuota=this.privateBytes+this.sharedBytes}return e.parse=function(e){return e&&"object"==typeof e?new t.UserInfo(e):e},e.prototype.name=null,e.prototype.email=null,e.prototype.countryCode=null,e.prototype.uid=null,e.prototype.referralUrl=null,e.prototype.publicAppUrl=null,e.prototype.quota=null,e.prototype.usedQuota=null,e.prototype.privateBytes=null,e.prototype.sharedBytes=null,e}(),"undefined"!=typeof window&&null!==window?(!window.XDomainRequest||"withCredentials"in new XMLHttpRequest?(s=window.XMLHttpRequest,i=!1,n=-1===window.navigator.userAgent.indexOf("Firefox")):(s=window.XDomainRequest,i=!0,n=!1),o=!0):(s=require("xmlhttprequest").XMLHttpRequest,i=!1,n=!1,o=!1),r="undefined"==typeof Uint8Array?null:new Uint8Array(0).__proto__.__proto__.constructor,t.Xhr=function(){function e(t,e){this.method=t,this.isGet="GET"===this.method,this.url=e,this.headers={},this.params=null,this.body=null,this.preflight=!(this.isGet||"POST"===this.method),this.signed=!1,this.responseType=null,this.callback=null,this.xhr=null}return e.Request=s,e.ieMode=i,e.canSendForms=n,e.doesPreflight=o,e.ArrayBufferView=r,e.prototype.setParams=function(t){if(this.signed)throw Error("setParams called after addOauthParams or addOauthHeader");if(this.params)throw Error("setParams cannot be called twice");return this.params=t,this},e.prototype.setCallback=function(t){return this.callback=t,this},e.prototype.signWithOauth=function(e){return t.Xhr.ieMode||t.Xhr.doesPreflight&&!this.preflight?this.addOauthParams(e):this.addOauthHeader(e)},e.prototype.addOauthParams=function(t){if(this.signed)throw Error("Request already has an OAuth signature");return this.params||(this.params={}),t.addAuthParams(this.method,this.url,this.params),this.signed=!0,this},e.prototype.addOauthHeader=function(t){if(this.signed)throw Error("Request already has an OAuth signature");return this.params||(this.params={}),this.signed=!0,this.setHeader("Authorization",t.authHeader(this.method,this.url,this.params))},e.prototype.setBody=function(t){if(this.isGet)throw Error("setBody cannot be called on GET requests");if(null!==this.body)throw Error("Request already has a body");return this.preflight||"undefined"!=typeof FormData&&t instanceof FormData||(this.preflight=!0),this.body=t,this},e.prototype.setResponseType=function(t){return this.responseType=t,this +},e.prototype.setHeader=function(t,e){var r;if(this.headers[t])throw r=this.headers[t],Error("HTTP header "+t+" already set to "+r);if("Content-Type"===t)throw Error("Content-Type is automatically computed based on setBody");return this.preflight=!0,this.headers[t]=e,this},e.prototype.setFileField=function(e,r,n,o){var i,s;if(null!==this.body)throw Error("Request already has a body");if(this.isGet)throw Error("paramsToBody cannot be called on GET requests");return"object"==typeof n&&"undefined"!=typeof Blob?("undefined"!=typeof ArrayBuffer&&null!==ArrayBuffer&&n instanceof ArrayBuffer&&(n=new Uint8Array(n)),t.Xhr.ArrayBufferView&&n instanceof t.Xhr.ArrayBufferView&&(o||(o="application/octet-stream"),n=new Blob([n],{type:o})),"undefined"!=typeof File&&n instanceof File&&(n=new Blob([n],{type:n.type})),s=n instanceof Blob):s=!1,s?(this.body=new FormData,this.body.append(e,n,r)):(o||(o="application/octet-stream"),i=this.multipartBoundary(),this.headers["Content-Type"]="multipart/form-data; boundary="+i,this.body=["--",i,"\r\n",'Content-Disposition: form-data; name="',e,'"; filename="',r,'"\r\n',"Content-Type: ",o,"\r\n","Content-Transfer-Encoding: binary\r\n\r\n",n,"\r\n","--",i,"--","\r\n"].join(""))},e.prototype.multipartBoundary=function(){return[Date.now().toString(36),Math.random().toString(36)].join("----")},e.prototype.paramsToUrl=function(){var e;return this.params&&(e=t.Xhr.urlEncode(this.params),0!==e.length&&(this.url=[this.url,"?",e].join("")),this.params=null),this},e.prototype.paramsToBody=function(){if(this.params){if(null!==this.body)throw Error("Request already has a body");if(this.isGet)throw Error("paramsToBody cannot be called on GET requests");this.headers["Content-Type"]="application/x-www-form-urlencoded",this.body=t.Xhr.urlEncode(this.params),this.params=null}return this},e.prototype.prepare=function(){var e,r,n,o,i=this;if(r=t.Xhr.ieMode,this.isGet||null!==this.body||r?(this.paramsToUrl(),null!==this.body&&"string"==typeof this.body&&(this.headers["Content-Type"]="text/plain; charset=utf8")):this.paramsToBody(),this.xhr=new t.Xhr.Request,r?(this.xhr.onload=function(){return i.onLoad()},this.xhr.onerror=function(){return i.onError()}):this.xhr.onreadystatechange=function(){return i.onReadyStateChange()},this.xhr.open(this.method,this.url,!0),!r){o=this.headers;for(e in o)_.call(o,e)&&(n=o[e],this.xhr.setRequestHeader(e,n))}return this.responseType&&("b"===this.responseType?this.xhr.overrideMimeType&&this.xhr.overrideMimeType("text/plain; charset=x-user-defined"):this.xhr.responseType=this.responseType),this},e.prototype.send=function(e){var r;if(this.callback=e||this.callback,null!==this.body){r=this.body,t.Xhr.ArrayBufferView&&r instanceof ArrayBuffer&&(r=new Uint8Array(r));try{this.xhr.send(r)}catch(n){if(!("undefined"!=typeof Blob&&t.Xhr.ArrayBufferView&&r instanceof t.Xhr.ArrayBufferView))throw n;r=new Blob([r],{type:"application/octet-stream"}),this.xhr.send(r)}}else this.xhr.send();return this},e.urlEncode=function(t){var e,r,n;e=[];for(r in t)n=t[r],e.push(this.urlEncodeValue(r)+"="+this.urlEncodeValue(n));return e.sort().join("&")},e.urlEncodeValue=function(t){return encodeURIComponent(""+t).replace(/\!/g,"%21").replace(/'/g,"%27").replace(/\(/g,"%28").replace(/\)/g,"%29").replace(/\*/g,"%2A")},e.urlDecode=function(t){var e,r,n,o,i,s;for(r={},s=t.split("&"),o=0,i=s.length;i>o;o++)n=s[o],e=n.split("="),r[decodeURIComponent(e[0])]=decodeURIComponent(e[1]);return r},e.prototype.onReadyStateChange=function(){var e,r,n,o,i,s,a,h,u;if(4!==this.xhr.readyState)return!0;if(200>this.xhr.status||this.xhr.status>=300)return e=new t.ApiError(this.xhr,this.method,this.url),this.callback(e),!0;if(s=this.xhr.getResponseHeader("x-dropbox-metadata"),null!=s?s.length:void 0)try{i=JSON.parse(s)}catch(l){i=void 0}else i=void 0;if(this.responseType){if("b"===this.responseType){for(n=null!=this.xhr.responseText?this.xhr.responseText:this.xhr.response,r=[],o=h=0,u=n.length;u>=0?u>h:h>u;o=u>=0?++h:--h)r.push(String.fromCharCode(255&n.charCodeAt(o)));a=r.join(""),this.callback(null,a,i)}else this.callback(null,this.xhr.response,i);return!0}switch(a=null!=this.xhr.responseText?this.xhr.responseText:this.xhr.response,this.xhr.getResponseHeader("Content-Type")){case"application/x-www-form-urlencoded":this.callback(null,t.Xhr.urlDecode(a),i);break;case"application/json":case"text/javascript":this.callback(null,JSON.parse(a),i);break;default:this.callback(null,a,i)}return!0},e.prototype.onLoad=function(){var e;switch(e=this.xhr.responseText,this.xhr.contentType){case"application/x-www-form-urlencoded":this.callback(null,t.Xhr.urlDecode(e),void 0);break;case"application/json":case"text/javascript":this.callback(null,JSON.parse(e),void 0);break;default:this.callback(null,e,void 0)}return!0},e.prototype.onError=function(){var e;return e=new t.ApiError(this.xhr,this.method,this.url),this.callback(e),!0},e}(),null!=("undefined"!=typeof module&&null!==module?module.exports:void 0))module.exports=t;else{if("undefined"==typeof window||null===window)throw Error("This library only supports node.js and modern browsers.");window.Dropbox=t}t.atob=u,t.btoa=f,t.hmac=c,t.sha1=d,t.encodeKey=m}).call(this); \ No newline at end of file From a6298b8917b938d469144a719b0dde2b97be0eac Mon Sep 17 00:00:00 2001 From: coderaiser Date: Fri, 21 Dec 2012 08:43:19 -0500 Subject: [PATCH 047/347] added js version of dropbox module --- lib/client/storage/dropbox/lib/dropbox.js | 2392 +++++++++++++++++++++ 1 file changed, 2392 insertions(+) create mode 100644 lib/client/storage/dropbox/lib/dropbox.js diff --git a/lib/client/storage/dropbox/lib/dropbox.js b/lib/client/storage/dropbox/lib/dropbox.js new file mode 100644 index 00000000..baa1119c --- /dev/null +++ b/lib/client/storage/dropbox/lib/dropbox.js @@ -0,0 +1,2392 @@ +// Generated by CoffeeScript 1.4.0 +(function() { + var Dropbox, DropboxClient, DropboxXhrArrayBufferView, DropboxXhrCanSendForms, DropboxXhrDoesPreflight, DropboxXhrIeMode, DropboxXhrRequest, add32, arrayToBase64, atob, atobNibble, base64Digits, base64HmacSha1, base64Sha1, btoa, btoaNibble, crypto, dropboxEncodeKey, hmacSha1, rotateLeft32, sha1, stringToArray, _base64Digits, + __hasProp = {}.hasOwnProperty, + __extends = function(child, parent) { for (var key in parent) { if (__hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; }; + + Dropbox = (function() { + + function Dropbox(options) { + this.client = new DropboxClient(options); + } + + return Dropbox; + + })(); + + Dropbox.ApiError = (function() { + + ApiError.prototype.status = void 0; + + ApiError.prototype.method = void 0; + + ApiError.prototype.url = void 0; + + ApiError.prototype.responseText = void 0; + + ApiError.prototype.response = void 0; + + function ApiError(xhr, method, url) { + var text; + this.method = method; + this.url = url; + this.status = xhr.status; + if (xhr.responseType) { + text = xhr.response || xhr.responseText; + } else { + text = xhr.responseText; + } + if (text) { + try { + this.responseText = text.toString(); + this.response = JSON.parse(text); + } catch (e) { + this.response = null; + } + } else { + this.responseText = '(no response)'; + this.response = null; + } + } + + ApiError.prototype.toString = function() { + return "Dropbox API error " + this.status + " from " + this.method + " " + this.url + " :: " + this.responseText; + }; + + ApiError.prototype.inspect = function() { + return this.toString(); + }; + + return ApiError; + + })(); + + if (typeof window !== "undefined" && window !== null) { + if (window.atob && window.btoa) { + atob = function(string) { + return window.atob(string); + }; + btoa = function(base64) { + return window.btoa(base64); + }; + } else { + base64Digits = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/'; + btoaNibble = function(accumulator, bytes, result) { + var i, limit; + limit = 3 - bytes; + accumulator <<= limit * 8; + i = 3; + while (i >= limit) { + result.push(base64Digits.charAt((accumulator >> (i * 6)) & 0x3F)); + i -= 1; + } + i = bytes; + while (i < 3) { + result.push('='); + i += 1; + } + return null; + }; + atobNibble = function(accumulator, digits, result) { + var i, limit; + limit = 4 - digits; + accumulator <<= limit * 6; + i = 2; + while (i >= limit) { + result.push(String.fromCharCode((accumulator >> (8 * i)) & 0xFF)); + i -= 1; + } + return null; + }; + btoa = function(string) { + var accumulator, bytes, i, result, _i, _ref; + result = []; + accumulator = 0; + bytes = 0; + for (i = _i = 0, _ref = string.length; 0 <= _ref ? _i < _ref : _i > _ref; i = 0 <= _ref ? ++_i : --_i) { + accumulator = (accumulator << 8) | string.charCodeAt(i); + bytes += 1; + if (bytes === 3) { + btoaNibble(accumulator, bytes, result); + accumulator = bytes = 0; + } + } + if (bytes > 0) { + btoaNibble(accumulator, bytes, result); + } + return result.join(''); + }; + atob = function(base64) { + var accumulator, digit, digits, i, result, _i, _ref; + result = []; + accumulator = 0; + digits = 0; + for (i = _i = 0, _ref = base64.length; 0 <= _ref ? _i < _ref : _i > _ref; i = 0 <= _ref ? ++_i : --_i) { + digit = base64.charAt(i); + if (digit === '=') { + break; + } + accumulator = (accumulator << 6) | base64Digits.indexOf(digit); + digits += 1; + if (digits === 4) { + atobNibble(accumulator, digits, result); + accumulator = digits = 0; + } + } + if (digits > 0) { + atobNibble(accumulator, digits, result); + } + return result.join(''); + }; + } + } else { + atob = function(arg) { + var buffer, i; + buffer = new Buffer(arg, 'base64'); + return ((function() { + var _i, _ref, _results; + _results = []; + for (i = _i = 0, _ref = buffer.length; 0 <= _ref ? _i < _ref : _i > _ref; i = 0 <= _ref ? ++_i : --_i) { + _results.push(String.fromCharCode(buffer[i])); + } + return _results; + })()).join(''); + }; + btoa = function(arg) { + var buffer, i; + buffer = new Buffer((function() { + var _i, _ref, _results; + _results = []; + for (i = _i = 0, _ref = arg.length; 0 <= _ref ? _i < _ref : _i > _ref; i = 0 <= _ref ? ++_i : --_i) { + _results.push(arg.charCodeAt(i)); + } + return _results; + })()); + return buffer.toString('base64'); + }; + } + + Dropbox.Client = (function() { + + function Client(options) { + this.sandbox = options.sandbox || false; + this.apiServer = options.server || this.defaultApiServer(); + this.authServer = options.authServer || this.defaultAuthServer(); + this.fileServer = options.fileServer || this.defaultFileServer(); + this.downloadServer = options.downloadServer || this.defaultDownloadServer(); + this.oauth = new Dropbox.Oauth(options); + this.driver = null; + this.filter = null; + this.uid = null; + this.authState = null; + this.authError = null; + this._credentials = null; + this.setCredentials(options); + this.setupUrls(); + } + + Client.prototype.authDriver = function(driver) { + this.driver = driver; + return this; + }; + + Client.prototype.xhrFilter = function(filter) { + this.filter = filter; + return this; + }; + + Client.prototype.dropboxUid = function() { + return this.uid; + }; + + Client.prototype.credentials = function() { + if (!this._credentials) { + this.computeCredentials(); + } + return this._credentials; + }; + + Client.prototype.authenticate = function(callback) { + var oldAuthState, _fsmStep, + _this = this; + oldAuthState = null; + _fsmStep = function() { + var authUrl; + if (oldAuthState !== _this.authState) { + oldAuthState = _this.authState; + if (_this.driver.onAuthStateChange) { + return _this.driver.onAuthStateChange(_this, _fsmStep); + } + } + switch (_this.authState) { + case DropboxClient.RESET: + return _this.requestToken(function(error, data) { + var token, tokenSecret; + if (error) { + _this.authError = error; + _this.authState = DropboxClient.ERROR; + } else { + token = data.oauth_token; + tokenSecret = data.oauth_token_secret; + _this.oauth.setToken(token, tokenSecret); + _this.authState = DropboxClient.REQUEST; + } + _this._credentials = null; + return _fsmStep(); + }); + case DropboxClient.REQUEST: + authUrl = _this.authorizeUrl(_this.oauth.token); + return _this.driver.doAuthorize(authUrl, _this.oauth.token, _this.oauth.tokenSecret, function() { + _this.authState = DropboxClient.AUTHORIZED; + _this._credentials = null; + return _fsmStep(); + }); + case DropboxClient.AUTHORIZED: + return _this.getAccessToken(function(error, data) { + if (error) { + _this.authError = error; + _this.authState = DropboxClient.ERROR; + } else { + _this.oauth.setToken(data.oauth_token, data.oauth_token_secret); + _this.uid = data.uid; + _this.authState = DropboxClient.DONE; + } + _this._credentials = null; + return _fsmStep(); + }); + case DropboxClient.DONE: + return callback(null, _this); + case Dropbox.SIGNED_OFF: + _this.reset(); + return _fsmStep(); + case DropboxClient.ERROR: + return callback(_this.authError); + } + }; + _fsmStep(); + return this; + }; + + Client.prototype.signOut = function(callback) { + var xhr, + _this = this; + xhr = new Dropbox.Xhr('POST', this.urls.signOut); + xhr.signWithOauth(this.oauth); + return this.dispatchXhr(xhr, function(error) { + if (error) { + return callback(error); + } + _this.reset(); + _this.authState = DropboxClient.SIGNED_OFF; + if (_this.driver.onAuthStateChange) { + return _this.driver.onAuthStateChange(_this, function() { + return callback(error); + }); + } else { + return callback(error); + } + }); + }; + + Client.prototype.signOff = function(callback) { + return this.signOut(callback); + }; + + Client.prototype.getUserInfo = function(callback) { + var xhr; + xhr = new Dropbox.Xhr('GET', this.urls.accountInfo); + xhr.signWithOauth(this.oauth); + return this.dispatchXhr(xhr, function(error, userData) { + return callback(error, Dropbox.UserInfo.parse(userData), userData); + }); + }; + + Client.prototype.readFile = function(path, options, callback) { + var params, rangeEnd, rangeHeader, rangeStart, responseType, xhr; + if ((!callback) && (typeof options === 'function')) { + callback = options; + options = null; + } + params = {}; + responseType = null; + rangeHeader = null; + if (options) { + if (options.versionTag) { + params.rev = options.versionTag; + } else if (options.rev) { + params.rev = options.rev; + } + if (options.arrayBuffer) { + responseType = 'arraybuffer'; + } else if (options.blob) { + responseType = 'blob'; + } else if (options.binary) { + responseType = 'b'; + } + if (options.length) { + if (options.start != null) { + rangeStart = options.start; + rangeEnd = options.start + options.length - 1; + } else { + rangeStart = ''; + rangeEnd = options.length; + } + rangeHeader = "bytes=" + rangeStart + "-" + rangeEnd; + } else if (options.start != null) { + rangeHeader = "bytes=" + options.start + "-"; + } + } + xhr = new Dropbox.Xhr('GET', "" + this.urls.getFile + "/" + (this.urlEncodePath(path))); + xhr.setParams(params).signWithOauth(this.oauth).setResponseType(responseType); + if (rangeHeader) { + xhr.setHeader('Range', rangeHeader); + } + return this.dispatchXhr(xhr, function(error, data, metadata) { + return callback(error, data, Dropbox.Stat.parse(metadata)); + }); + }; + + Client.prototype.writeFile = function(path, data, options, callback) { + var useForm; + if ((!callback) && (typeof options === 'function')) { + callback = options; + options = null; + } + useForm = Dropbox.Xhr.canSendForms && typeof data === 'object'; + if (useForm) { + return this.writeFileUsingForm(path, data, options, callback); + } else { + return this.writeFileUsingPut(path, data, options, callback); + } + }; + + Client.prototype.writeFileUsingForm = function(path, data, options, callback) { + var fileName, params, slashIndex, xhr; + slashIndex = path.lastIndexOf('/'); + if (slashIndex === -1) { + fileName = path; + path = ''; + } else { + fileName = path.substring(slashIndex); + path = path.substring(0, slashIndex); + } + params = { + file: fileName + }; + if (options) { + if (options.noOverwrite) { + params.overwrite = 'false'; + } + if (options.lastVersionTag) { + params.parent_rev = options.lastVersionTag; + } else if (options.parentRev || options.parent_rev) { + params.parent_rev = options.parentRev || options.parent_rev; + } + } + xhr = new Dropbox.Xhr('POST', "" + this.urls.postFile + "/" + (this.urlEncodePath(path))); + xhr.setParams(params).signWithOauth(this.oauth).setFileField('file', fileName, data, 'application/octet-stream'); + delete params.file; + return this.dispatchXhr(xhr, function(error, metadata) { + return callback(error, Dropbox.Stat.parse(metadata)); + }); + }; + + Client.prototype.writeFileUsingPut = function(path, data, options, callback) { + var params, xhr; + params = {}; + if (options) { + if (options.noOverwrite) { + params.overwrite = 'false'; + } + if (options.lastVersionTag) { + params.parent_rev = options.lastVersionTag; + } else if (options.parentRev || options.parent_rev) { + params.parent_rev = options.parentRev || options.parent_rev; + } + } + xhr = new Dropbox.Xhr('POST', "" + this.urls.putFile + "/" + (this.urlEncodePath(path))); + xhr.setBody(data).setParams(params).signWithOauth(this.oauth); + return this.dispatchXhr(xhr, function(error, metadata) { + return callback(error, Dropbox.Stat.parse(metadata)); + }); + }; + + Client.prototype.stat = function(path, options, callback) { + var params, xhr; + if ((!callback) && (typeof options === 'function')) { + callback = options; + options = null; + } + params = {}; + if (options) { + if (options.version != null) { + params.rev = options.version; + } + if (options.removed || options.deleted) { + params.include_deleted = 'true'; + } + if (options.readDir) { + params.list = 'true'; + if (options.readDir !== true) { + params.file_limit = options.readDir.toString(); + } + } + if (options.cacheHash) { + params.hash = options.cacheHash; + } + } + params.include_deleted || (params.include_deleted = 'false'); + params.list || (params.list = 'false'); + xhr = new Dropbox.Xhr('GET', "" + this.urls.metadata + "/" + (this.urlEncodePath(path))); + xhr.setParams(params).signWithOauth(this.oauth); + return this.dispatchXhr(xhr, function(error, metadata) { + var entries, entry, stat; + stat = Dropbox.Stat.parse(metadata); + if (metadata != null ? metadata.contents : void 0) { + entries = (function() { + var _i, _len, _ref, _results; + _ref = metadata.contents; + _results = []; + for (_i = 0, _len = _ref.length; _i < _len; _i++) { + entry = _ref[_i]; + _results.push(Dropbox.Stat.parse(entry)); + } + return _results; + })(); + } else { + entries = void 0; + } + return callback(error, stat, entries); + }); + }; + + Client.prototype.readdir = function(path, options, callback) { + var statOptions; + if ((!callback) && (typeof options === 'function')) { + callback = options; + options = null; + } + statOptions = { + readDir: true + }; + if (options) { + if (options.limit != null) { + statOptions.readDir = options.limit; + } + if (options.versionTag) { + statOptions.versionTag = options.versionTag; + } + } + return this.stat(path, statOptions, function(error, stat, entry_stats) { + var entries, entry_stat; + if (entry_stats) { + entries = (function() { + var _i, _len, _results; + _results = []; + for (_i = 0, _len = entry_stats.length; _i < _len; _i++) { + entry_stat = entry_stats[_i]; + _results.push(entry_stat.name); + } + return _results; + })(); + } else { + entries = null; + } + return callback(error, entries, stat, entry_stats); + }); + }; + + Client.prototype.metadata = function(path, options, callback) { + return this.stat(path, options, callback); + }; + + Client.prototype.makeUrl = function(path, options, callback) { + var isDirect, params, url, useDownloadHack, xhr; + if ((!callback) && (typeof options === 'function')) { + callback = options; + options = null; + } + if (options && (options['long'] || options.longUrl || options.downloadHack)) { + params = { + short_url: 'false' + }; + } else { + params = {}; + } + path = this.urlEncodePath(path); + url = "" + this.urls.shares + "/" + path; + isDirect = false; + useDownloadHack = false; + if (options) { + if (options.downloadHack) { + isDirect = true; + useDownloadHack = true; + } else if (options.download) { + isDirect = true; + url = "" + this.urls.media + "/" + path; + } + } + xhr = new Dropbox.Xhr('POST', url).setParams(params).signWithOauth(this.oauth); + return this.dispatchXhr(xhr, function(error, urlData) { + if (useDownloadHack && urlData && urlData.url) { + urlData.url = urlData.url.replace(this.authServer, this.downloadServer); + } + return callback(error, Dropbox.PublicUrl.parse(urlData, isDirect)); + }); + }; + + Client.prototype.history = function(path, options, callback) { + var params, xhr; + if ((!callback) && (typeof options === 'function')) { + callback = options; + options = null; + } + params = {}; + if (options && (options.limit != null)) { + params.rev_limit = options.limit; + } + xhr = new Dropbox.Xhr('GET', "" + this.urls.revisions + "/" + (this.urlEncodePath(path))); + xhr.setParams(params).signWithOauth(this.oauth); + return this.dispatchXhr(xhr, function(error, versions) { + var metadata, stats; + if (versions) { + stats = (function() { + var _i, _len, _results; + _results = []; + for (_i = 0, _len = versions.length; _i < _len; _i++) { + metadata = versions[_i]; + _results.push(Dropbox.Stat.parse(metadata)); + } + return _results; + })(); + } else { + stats = void 0; + } + return callback(error, stats); + }); + }; + + Client.prototype.revisions = function(path, options, callback) { + return this.history(path, options, callback); + }; + + Client.prototype.thumbnailUrl = function(path, options) { + var xhr; + xhr = this.thumbnailXhr(path, options); + return xhr.paramsToUrl().url; + }; + + Client.prototype.readThumbnail = function(path, options, callback) { + var responseType, xhr; + if ((!callback) && (typeof options === 'function')) { + callback = options; + options = null; + } + responseType = 'b'; + if (options) { + if (options.blob) { + responseType = 'blob'; + } + } + xhr = this.thumbnailXhr(path, options); + xhr.setResponseType(responseType); + return this.dispatchXhr(xhr, function(error, data, metadata) { + return callback(error, data, Dropbox.Stat.parse(metadata)); + }); + }; + + Client.prototype.thumbnailXhr = function(path, options) { + var params, xhr; + params = {}; + if (options) { + if (options.format) { + params.format = options.format; + } else if (options.png) { + params.format = 'png'; + } + if (options.size) { + params.size = options.size; + } + } + xhr = new Dropbox.Xhr('GET', "" + this.urls.thumbnails + "/" + (this.urlEncodePath(path))); + return xhr.setParams(params).signWithOauth(this.oauth); + }; + + Client.prototype.revertFile = function(path, versionTag, callback) { + var xhr; + xhr = new Dropbox.Xhr('POST', "" + this.urls.restore + "/" + (this.urlEncodePath(path))); + xhr.setParams({ + rev: versionTag + }).signWithOauth(this.oauth); + return this.dispatchXhr(xhr, function(error, metadata) { + return callback(error, Dropbox.Stat.parse(metadata)); + }); + }; + + Client.prototype.restore = function(path, versionTag, callback) { + return this.revertFile(path, versionTag, callback); + }; + + Client.prototype.findByName = function(path, namePattern, options, callback) { + var params, xhr; + if ((!callback) && (typeof options === 'function')) { + callback = options; + options = null; + } + params = { + query: namePattern + }; + if (options) { + if (options.limit != null) { + params.file_limit = options.limit; + } + if (options.removed || options.deleted) { + params.include_deleted = true; + } + } + xhr = new Dropbox.Xhr('GET', "" + this.urls.search + "/" + (this.urlEncodePath(path))); + xhr.setParams(params).signWithOauth(this.oauth); + return this.dispatchXhr(xhr, function(error, results) { + var metadata, stats; + if (results) { + stats = (function() { + var _i, _len, _results; + _results = []; + for (_i = 0, _len = results.length; _i < _len; _i++) { + metadata = results[_i]; + _results.push(Dropbox.Stat.parse(metadata)); + } + return _results; + })(); + } else { + stats = void 0; + } + return callback(error, stats); + }); + }; + + Client.prototype.search = function(path, namePattern, options, callback) { + return this.findByName(path, namePattern, options, callback); + }; + + Client.prototype.makeCopyReference = function(path, callback) { + var xhr; + xhr = new Dropbox.Xhr('GET', "" + this.urls.copyRef + "/" + (this.urlEncodePath(path))); + xhr.signWithOauth(this.oauth); + return this.dispatchXhr(xhr, function(error, refData) { + return callback(error, Dropbox.CopyReference.parse(refData)); + }); + }; + + Client.prototype.copyRef = function(path, callback) { + return this.makeCopyReference(path, callback); + }; + + Client.prototype.pullChanges = function(cursor, callback) { + var params, xhr; + if ((!callback) && (typeof cursor === 'function')) { + callback = cursor; + cursor = null; + } + if (cursor) { + if (cursor.cursorTag) { + params = { + cursor: cursor.cursorTag + }; + } else { + params = { + cursor: cursor + }; + } + } else { + params = {}; + } + xhr = new Dropbox.Xhr('POST', this.urls.delta); + xhr.setParams(params).signWithOauth(this.oauth); + return this.dispatchXhr(xhr, function(error, deltaInfo) { + return callback(error, Dropbox.PulledChanges.parse(deltaInfo)); + }); + }; + + Client.prototype.delta = function(cursor, callback) { + return this.pullChanges(cursor, callback); + }; + + Client.prototype.mkdir = function(path, callback) { + var xhr; + xhr = new Dropbox.Xhr('POST', this.urls.fileopsCreateFolder); + xhr.setParams({ + root: this.fileRoot, + path: this.normalizePath(path) + }).signWithOauth(this.oauth); + return this.dispatchXhr(xhr, function(error, metadata) { + return callback(error, Dropbox.Stat.parse(metadata)); + }); + }; + + Client.prototype.remove = function(path, callback) { + var xhr; + xhr = new Dropbox.Xhr('POST', this.urls.fileopsDelete); + xhr.setParams({ + root: this.fileRoot, + path: this.normalizePath(path) + }).signWithOauth(this.oauth); + return this.dispatchXhr(xhr, function(error, metadata) { + return callback(error, Dropbox.Stat.parse(metadata)); + }); + }; + + Client.prototype.unlink = function(path, callback) { + return this.remove(path, callback); + }; + + Client.prototype["delete"] = function(path, callback) { + return this.remove(path, callback); + }; + + Client.prototype.copy = function(from, toPath, callback) { + var options, params, xhr; + if ((!callback) && (typeof options === 'function')) { + callback = options; + options = null; + } + params = { + root: this.fileRoot, + to_path: this.normalizePath(toPath) + }; + if (from instanceof Dropbox.CopyReference) { + params.from_copy_ref = from.tag; + } else { + params.from_path = this.normalizePath(from); + } + xhr = new Dropbox.Xhr('POST', this.urls.fileopsCopy); + xhr.setParams(params).signWithOauth(this.oauth); + return this.dispatchXhr(xhr, function(error, metadata) { + return callback(error, Dropbox.Stat.parse(metadata)); + }); + }; + + Client.prototype.move = function(fromPath, toPath, callback) { + var options, xhr; + if ((!callback) && (typeof options === 'function')) { + callback = options; + options = null; + } + xhr = new Dropbox.Xhr('POST', this.urls.fileopsMove); + xhr.setParams({ + root: this.fileRoot, + from_path: this.normalizePath(fromPath), + to_path: this.normalizePath(toPath) + }).signWithOauth(this.oauth); + return this.dispatchXhr(xhr, function(error, metadata) { + return callback(error, Dropbox.Stat.parse(metadata)); + }); + }; + + Client.prototype.reset = function() { + this.uid = null; + this.oauth.setToken(null, ''); + this.authState = DropboxClient.RESET; + this.authError = null; + this._credentials = null; + return this; + }; + + Client.prototype.setCredentials = function(credentials) { + this.oauth.reset(credentials); + this.uid = credentials.uid || null; + if (credentials.authState) { + this.authState = credentials.authState; + } else { + if (credentials.token) { + this.authState = DropboxClient.DONE; + } else { + this.authState = DropboxClient.RESET; + } + } + this.authError = null; + this._credentials = null; + return this; + }; + + Client.prototype.appHash = function() { + return this.oauth.appHash(); + }; + + Client.prototype.setupUrls = function() { + this.fileRoot = this.sandbox ? 'sandbox' : 'dropbox'; + return this.urls = { + requestToken: "" + this.apiServer + "/1/oauth/request_token", + authorize: "" + this.authServer + "/1/oauth/authorize", + accessToken: "" + this.apiServer + "/1/oauth/access_token", + signOut: "" + this.apiServer + "/1/unlink_access_token", + accountInfo: "" + this.apiServer + "/1/account/info", + getFile: "" + this.fileServer + "/1/files/" + this.fileRoot, + postFile: "" + this.fileServer + "/1/files/" + this.fileRoot, + putFile: "" + this.fileServer + "/1/files_put/" + this.fileRoot, + metadata: "" + this.apiServer + "/1/metadata/" + this.fileRoot, + delta: "" + this.apiServer + "/1/delta", + revisions: "" + this.apiServer + "/1/revisions/" + this.fileRoot, + restore: "" + this.apiServer + "/1/restore/" + this.fileRoot, + search: "" + this.apiServer + "/1/search/" + this.fileRoot, + shares: "" + this.apiServer + "/1/shares/" + this.fileRoot, + media: "" + this.apiServer + "/1/media/" + this.fileRoot, + copyRef: "" + this.apiServer + "/1/copy_ref/" + this.fileRoot, + thumbnails: "" + this.fileServer + "/1/thumbnails/" + this.fileRoot, + fileopsCopy: "" + this.apiServer + "/1/fileops/copy", + fileopsCreateFolder: "" + this.apiServer + "/1/fileops/create_folder", + fileopsDelete: "" + this.apiServer + "/1/fileops/delete", + fileopsMove: "" + this.apiServer + "/1/fileops/move" + }; + }; + + Client.ERROR = 0; + + Client.RESET = 1; + + Client.REQUEST = 2; + + Client.AUTHORIZED = 3; + + Client.DONE = 4; + + Client.SIGNED_OFF = 5; + + Client.prototype.urlEncodePath = function(path) { + return Dropbox.Xhr.urlEncodeValue(this.normalizePath(path)).replace(/%2F/gi, '/'); + }; + + Client.prototype.normalizePath = function(path) { + var i; + if (path.substring(0, 1) === '/') { + i = 1; + while (path.substring(i, i + 1) === '/') { + i += 1; + } + return path.substring(i); + } else { + return path; + } + }; + + Client.prototype.requestToken = function(callback) { + var xhr; + xhr = new Dropbox.Xhr('POST', this.urls.requestToken).signWithOauth(this.oauth); + return this.dispatchXhr(xhr, callback); + }; + + Client.prototype.authorizeUrl = function(token) { + var params; + params = { + oauth_token: token, + oauth_callback: this.driver.url() + }; + return ("" + this.urls.authorize + "?") + Dropbox.Xhr.urlEncode(params); + }; + + Client.prototype.getAccessToken = function(callback) { + var xhr; + xhr = new Dropbox.Xhr('POST', this.urls.accessToken).signWithOauth(this.oauth); + return this.dispatchXhr(xhr, callback); + }; + + Client.prototype.dispatchXhr = function(xhr, callback) { + var nativeXhr; + xhr.setCallback(callback); + xhr.prepare(); + nativeXhr = xhr.xhr; + if (this.filter) { + if (!this.filter(nativeXhr, xhr)) { + return nativeXhr; + } + } + xhr.send(); + return nativeXhr; + }; + + Client.prototype.defaultApiServer = function() { + return 'https://api.dropbox.com'; + }; + + Client.prototype.defaultAuthServer = function() { + return this.apiServer.replace('api.', 'www.'); + }; + + Client.prototype.defaultFileServer = function() { + return this.apiServer.replace('api.', 'api-content.'); + }; + + Client.prototype.defaultDownloadServer = function() { + return this.apiServer.replace('api.', 'dl.'); + }; + + Client.prototype.computeCredentials = function() { + var value; + value = { + key: this.oauth.key, + sandbox: this.sandbox + }; + if (this.oauth.secret) { + value.secret = this.oauth.secret; + } + if (this.oauth.token) { + value.token = this.oauth.token; + value.tokenSecret = this.oauth.tokenSecret; + } + if (this.uid) { + value.uid = this.uid; + } + if (this.authState !== DropboxClient.ERROR && this.authState !== DropboxClient.RESET && this.authState !== DropboxClient.DONE && this.authState !== DropboxClient.SIGNED_OFF) { + value.authState = this.authState; + } + if (this.apiServer !== this.defaultApiServer()) { + value.server = this.apiServer; + } + if (this.authServer !== this.defaultAuthServer()) { + value.authServer = this.authServer; + } + if (this.fileServer !== this.defaultFileServer()) { + value.fileServer = this.fileServer; + } + if (this.downloadServer !== this.defaultDownloadServer()) { + value.downloadServer = this.downloadServer; + } + return this._credentials = value; + }; + + return Client; + + })(); + + DropboxClient = Dropbox.Client; + + Dropbox.AuthDriver = (function() { + + function AuthDriver() {} + + AuthDriver.prototype.url = function() { + return 'https://some.url'; + }; + + AuthDriver.prototype.doAuthorize = function(authUrl, token, tokenSecret, callback) { + return callback('oauth-token'); + }; + + AuthDriver.prototype.onAuthStateChange = function(client, callback) { + return callback(); + }; + + return AuthDriver; + + })(); + + Dropbox.Drivers = {}; + + Dropbox.Drivers.BrowserBase = (function() { + + function BrowserBase(options) { + this.rememberUser = (options != null ? options.rememberUser : void 0) || false; + this.scope = (options != null ? options.scope : void 0) || 'default'; + } + + BrowserBase.prototype.onAuthStateChange = function(client, callback) { + var _this = this; + this.setStorageKey(client); + switch (client.authState) { + case DropboxClient.RESET: + return this.loadCredentials(function(credentials) { + if (!credentials) { + return callback(); + } + if (credentials.authState) { + client.setCredentials(credentials); + return callback(); + } + if (!_this.rememberUser) { + _this.forgetCredentials(); + return callback(); + } + client.setCredentials(credentials); + return client.getUserInfo(function(error) { + if (error) { + client.reset(); + return _this.forgetCredentials(callback); + } else { + return callback(); + } + }); + }); + case DropboxClient.REQUEST: + return this.storeCredentials(client.credentials(), callback); + case DropboxClient.DONE: + if (this.rememberUser) { + return this.storeCredentials(client.credentials(), callback); + } + return this.forgetCredentials(callback); + case DropboxClient.SIGNED_OFF: + return this.forgetCredentials(callback); + case DropboxClient.ERROR: + return this.forgetCredentials(callback); + default: + callback(); + return this; + } + }; + + BrowserBase.prototype.setStorageKey = function(client) { + this.storageKey = "dropbox-auth:" + this.scope + ":" + (client.appHash()); + return this; + }; + + BrowserBase.prototype.storeCredentials = function(credentials, callback) { + localStorage.setItem(this.storageKey, JSON.stringify(credentials)); + callback(); + return this; + }; + + BrowserBase.prototype.loadCredentials = function(callback) { + var jsonString; + jsonString = localStorage.getItem(this.storageKey); + if (!jsonString) { + callback(null); + return this; + } + try { + callback(JSON.parse(jsonString)); + } catch (e) { + callback(null); + } + return this; + }; + + BrowserBase.prototype.forgetCredentials = function(callback) { + localStorage.removeItem(this.storageKey); + callback(); + return this; + }; + + BrowserBase.currentLocation = function() { + return window.location.href; + }; + + return BrowserBase; + + })(); + + Dropbox.Drivers.Redirect = (function(_super) { + + __extends(Redirect, _super); + + function Redirect(options) { + Redirect.__super__.constructor.call(this, options); + this.useQuery = (options != null ? options.useQuery : void 0) || false; + this.receiverUrl = this.computeUrl(options); + this.tokenRe = new RegExp("(#|\\?|&)oauth_token=([^&#]+)(&|#|$)"); + } + + Redirect.prototype.onAuthStateChange = function(client, callback) { + var superCall, + _this = this; + superCall = (function() { + return function() { + return Redirect.__super__.onAuthStateChange.call(_this, client, callback); + }; + })(); + this.setStorageKey(client); + if (client.authState === DropboxClient.RESET) { + return this.loadCredentials(function(credentials) { + if (credentials && credentials.authState) { + if (credentials.token === _this.locationToken() && credentials.authState === DropboxClient.REQUEST) { + credentials.authState = DropboxClient.AUTHORIZED; + return _this.storeCredentials(credentials, superCall); + } else { + return _this.forgetCredentials(superCall); + } + } + return superCall(); + }); + } else { + return superCall(); + } + }; + + Redirect.prototype.url = function() { + return this.receiverUrl; + }; + + Redirect.prototype.doAuthorize = function(authUrl) { + /* */ + return DOM.openWindow(authUrl); + /* return window.location.assign(authUrl); */ + }; + + Redirect.prototype.computeUrl = function() { + var fragment, location, locationPair, querySuffix; + querySuffix = "_dropboxjs_scope=" + (encodeURIComponent(this.scope)); + location = Dropbox.Drivers.BrowserBase.currentLocation(); + if (location.indexOf('#') === -1) { + fragment = null; + } else { + locationPair = location.split('#', 2); + location = locationPair[0]; + fragment = locationPair[1]; + } + if (this.useQuery) { + if (location.indexOf('?') === -1) { + location += "?" + querySuffix; + } else { + location += "&" + querySuffix; + } + } else { + fragment = "?" + querySuffix; + } + if (fragment) { + return location + '#' + fragment; + } else { + return location; + } + }; + + Redirect.prototype.locationToken = function() { + var location, match, scopePattern; + location = Dropbox.Drivers.BrowserBase.currentLocation(); + scopePattern = "_dropboxjs_scope=" + (encodeURIComponent(this.scope)) + "&"; + if ((typeof location.indexOf === "function" ? location.indexOf(scopePattern) : void 0) === -1) { + return null; + } + match = this.tokenRe.exec(location); + if (match) { + return decodeURIComponent(match[2]); + } else { + return null; + } + }; + + return Redirect; + + })(Dropbox.Drivers.BrowserBase); + + Dropbox.Drivers.Popup = (function(_super) { + + __extends(Popup, _super); + + function Popup(options) { + Popup.__super__.constructor.call(this, options); + this.receiverUrl = this.computeUrl(options); + this.tokenRe = new RegExp("(#|\\?|&)oauth_token=([^&#]+)(&|#|$)"); + } + + Popup.prototype.onAuthStateChange = function(client, callback) { + var superCall, + _this = this; + superCall = (function() { + return function() { + return Popup.__super__.onAuthStateChange.call(_this, client, callback); + }; + })(); + this.setStorageKey(client); + if (client.authState === DropboxClient.RESET) { + return this.loadCredentials(function(credentials) { + if (credentials && credentials.authState) { + return this.forgetCredentials(superCall); + } + return superCall(); + }); + } else { + return superCall(); + } + }; + + Popup.prototype.doAuthorize = function(authUrl, token, tokenSecret, callback) { + this.listenForMessage(token, callback); + return this.openWindow(authUrl); + }; + + Popup.prototype.url = function() { + return this.receiverUrl; + }; + + Popup.prototype.computeUrl = function(options) { + var fragments; + if (options) { + if (options.receiverUrl) { + if (options.noFragment || options.receiverUrl.indexOf('#') !== -1) { + return options.receiverUrl; + } else { + return options.receiverUrl + '#'; + } + } else if (options.receiverFile) { + fragments = Dropbox.Drivers.BrowserBase.currentLocation().split('/'); + fragments[fragments.length - 1] = options.receiverFile; + if (options.noFragment) { + return fragments.join('/'); + } else { + return fragments.join('/') + '#'; + } + } + } + return Dropbox.Drivers.BrowserBase.currentLocation(); + }; + + Popup.prototype.openWindow = function(url) { + return window.open(url, '_dropboxOauthSigninWindow', this.popupWindowSpec(980, 700)); + }; + + Popup.prototype.popupWindowSpec = function(popupWidth, popupHeight) { + var height, popupLeft, popupTop, width, x0, y0, _ref, _ref1, _ref2, _ref3; + x0 = (_ref = window.screenX) != null ? _ref : window.screenLeft; + y0 = (_ref1 = window.screenY) != null ? _ref1 : window.screenTop; + width = (_ref2 = window.outerWidth) != null ? _ref2 : document.documentElement.clientWidth; + height = (_ref3 = window.outerHeight) != null ? _ref3 : document.documentElement.clientHeight; + popupLeft = Math.round(x0 + (width - popupWidth) / 2); + popupTop = Math.round(y0 + (height - popupHeight) / 2.5); + if (popupLeft < x0) { + popupLeft = x0; + } + if (popupTop < y0) { + popupTop = y0; + } + return ("width=" + popupWidth + ",height=" + popupHeight + ",") + ("left=" + popupLeft + ",top=" + popupTop) + 'dialog=yes,dependent=yes,scrollbars=yes,location=yes'; + }; + + Popup.prototype.listenForMessage = function(token, callback) { + var listener, + _this = this; + listener = function(event) { + var match; + match = _this.tokenRe.exec(event.data.toString()); + if (match && decodeURIComponent(match[2]) === token) { + window.removeEventListener('message', listener); + return callback(); + } + }; + return window.addEventListener('message', listener, false); + }; + + return Popup; + + })(Dropbox.Drivers.BrowserBase); + + Dropbox.Drivers.NodeServer = (function() { + + function NodeServer(options) { + this.port = (options != null ? options.port : void 0) || 8912; + this.faviconFile = (options != null ? options.favicon : void 0) || null; + this.fs = require('fs'); + this.http = require('http'); + this.open = require('open'); + this.callbacks = {}; + this.urlRe = new RegExp("^/oauth_callback\\?"); + this.tokenRe = new RegExp("(\\?|&)oauth_token=([^&]+)(&|$)"); + this.createApp(); + } + + NodeServer.prototype.url = function() { + return "http://localhost:" + this.port + "/oauth_callback"; + }; + + NodeServer.prototype.doAuthorize = function(authUrl, token, tokenSecret, callback) { + this.callbacks[token] = callback; + return this.openBrowser(authUrl); + }; + + NodeServer.prototype.openBrowser = function(url) { + if (!url.match(/^https?:\/\//)) { + throw new Error("Not a http/https URL: " + url); + } + return this.open(url); + }; + + NodeServer.prototype.createApp = function() { + var _this = this; + this.app = this.http.createServer(function(request, response) { + return _this.doRequest(request, response); + }); + return this.app.listen(this.port); + }; + + NodeServer.prototype.closeServer = function() { + return this.app.close(); + }; + + NodeServer.prototype.doRequest = function(request, response) { + var data, match, token, + _this = this; + if (this.urlRe.exec(request.url)) { + match = this.tokenRe.exec(request.url); + if (match) { + token = decodeURIComponent(match[2]); + if (this.callbacks[token]) { + this.callbacks[token](); + delete this.callbacks[token]; + } + } + } + data = ''; + request.on('data', function(dataFragment) { + return data += dataFragment; + }); + return request.on('end', function() { + if (_this.faviconFile && (request.url === '/favicon.ico')) { + return _this.sendFavicon(response); + } else { + return _this.closeBrowser(response); + } + }); + }; + + NodeServer.prototype.closeBrowser = function(response) { + var closeHtml; + closeHtml = "\n\n

    Please close this window.

    "; + response.writeHead(200, { + 'Content-Length': closeHtml.length, + 'Content-Type': 'text/html' + }); + response.write(closeHtml); + return response.end; + }; + + NodeServer.prototype.sendFavicon = function(response) { + return this.fs.readFile(this.faviconFile, function(error, data) { + response.writeHead(200, { + 'Content-Length': data.length, + 'Content-Type': 'image/x-icon' + }); + response.write(data); + return response.end; + }); + }; + + return NodeServer; + + })(); + + base64HmacSha1 = function(string, key) { + return arrayToBase64(hmacSha1(stringToArray(string), stringToArray(key), string.length, key.length)); + }; + + base64Sha1 = function(string) { + return arrayToBase64(sha1(stringToArray(string), string.length)); + }; + + if (typeof window === "undefined" || window === null) { + crypto = require('crypto'); + base64HmacSha1 = function(string, key) { + var hmac; + hmac = crypto.createHmac('sha1', key); + hmac.update(string); + return hmac.digest('base64'); + }; + base64Sha1 = function(string) { + var hash; + hash = crypto.createHash('sha1'); + hash.update(string); + return hash.digest('base64'); + }; + } + + hmacSha1 = function(string, key, length, keyLength) { + var hash1, i, ipad, opad; + if (key.length > 16) { + key = sha1(key, keyLength); + } + ipad = (function() { + var _i, _results; + _results = []; + for (i = _i = 0; _i < 16; i = ++_i) { + _results.push(key[i] ^ 0x36363636); + } + return _results; + })(); + opad = (function() { + var _i, _results; + _results = []; + for (i = _i = 0; _i < 16; i = ++_i) { + _results.push(key[i] ^ 0x5C5C5C5C); + } + return _results; + })(); + hash1 = sha1(ipad.concat(string), 64 + length); + return sha1(opad.concat(hash1), 64 + 20); + }; + + sha1 = function(string, length) { + var a, a0, b, b0, c, c0, d, d0, e, e0, ft, i, j, kt, limit, state, t, _i; + string[length >> 2] |= 1 << (31 - ((length & 0x03) << 3)); + string[(((length + 8) >> 6) << 4) + 15] = length << 3; + state = Array(80); + a = 1732584193; + b = -271733879; + c = -1732584194; + d = 271733878; + e = -1009589776; + i = 0; + limit = string.length; + while (i < limit) { + a0 = a; + b0 = b; + c0 = c; + d0 = d; + e0 = e; + for (j = _i = 0; _i < 80; j = ++_i) { + if (j < 16) { + state[j] = string[i + j]; + } else { + state[j] = rotateLeft32(state[j - 3] ^ state[j - 8] ^ state[j - 14] ^ state[j - 16], 1); + } + if (j < 20) { + ft = (b & c) | ((~b) & d); + kt = 1518500249; + } else if (j < 40) { + ft = b ^ c ^ d; + kt = 1859775393; + } else if (j < 60) { + ft = (b & c) | (b & d) | (c & d); + kt = -1894007588; + } else { + ft = b ^ c ^ d; + kt = -899497514; + } + t = add32(add32(rotateLeft32(a, 5), ft), add32(add32(e, state[j]), kt)); + e = d; + d = c; + c = rotateLeft32(b, 30); + b = a; + a = t; + } + a = add32(a, a0); + b = add32(b, b0); + c = add32(c, c0); + d = add32(d, d0); + e = add32(e, e0); + i += 16; + } + return [a, b, c, d, e]; + }; + + /* + # Uncomment the definition below for debugging. + # + # Returns the hexadecimal representation of a 32-bit number. + xxx = (n) -> + if n < 0 + n = (1 << 30) * 4 + n + n.toString 16 + */ + + + rotateLeft32 = function(value, count) { + return (value << count) | (value >>> (32 - count)); + }; + + add32 = function(a, b) { + var high, low; + low = (a & 0xFFFF) + (b & 0xFFFF); + high = (a >> 16) + (b >> 16) + (low >> 16); + return (high << 16) | (low & 0xFFFF); + }; + + arrayToBase64 = function(array) { + var i, i2, limit, string, trit; + string = ""; + i = 0; + limit = array.length * 4; + while (i < limit) { + i2 = i; + trit = ((array[i2 >> 2] >> ((3 - (i2 & 3)) << 3)) & 0xFF) << 16; + i2 += 1; + trit |= ((array[i2 >> 2] >> ((3 - (i2 & 3)) << 3)) & 0xFF) << 8; + i2 += 1; + trit |= (array[i2 >> 2] >> ((3 - (i2 & 3)) << 3)) & 0xFF; + string += _base64Digits[(trit >> 18) & 0x3F]; + string += _base64Digits[(trit >> 12) & 0x3F]; + i += 1; + if (i >= limit) { + string += '='; + } else { + string += _base64Digits[(trit >> 6) & 0x3F]; + } + i += 1; + if (i >= limit) { + string += '='; + } else { + string += _base64Digits[trit & 0x3F]; + } + i += 1; + } + return string; + }; + + _base64Digits = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; + + stringToArray = function(string) { + var array, i, mask, _i, _ref; + array = []; + mask = 0xFF; + for (i = _i = 0, _ref = string.length; 0 <= _ref ? _i < _ref : _i > _ref; i = 0 <= _ref ? ++_i : --_i) { + array[i >> 2] |= (string.charCodeAt(i) & mask) << ((3 - (i & 3)) << 3); + } + return array; + }; + + Dropbox.Oauth = (function() { + + function Oauth(options) { + this.key = this.k = null; + this.secret = this.s = null; + this.token = null; + this.tokenSecret = null; + this._appHash = null; + this.reset(options); + } + + Oauth.prototype.reset = function(options) { + var k, s, secret, _ref; + if (options.secret) { + this.k = this.key = options.key; + this.s = this.secret = options.secret; + this._appHash = null; + } else if (options.key) { + this.key = options.key; + this.secret = null; + secret = atob(dropboxEncodeKey(this.key).split('|', 2)[1]); + _ref = secret.split('?', 2), k = _ref[0], s = _ref[1]; + this.k = decodeURIComponent(k); + this.s = decodeURIComponent(s); + this._appHash = null; + } else { + if (!this.k) { + throw new Error('No API key supplied'); + } + } + if (options.token) { + return this.setToken(options.token, options.tokenSecret); + } else { + return this.setToken(null, ''); + } + }; + + Oauth.prototype.setToken = function(token, tokenSecret) { + if (token && (!tokenSecret)) { + throw new Error('No secret supplied with the user token'); + } + this.token = token; + this.tokenSecret = tokenSecret || ''; + this.hmacKey = Dropbox.Xhr.urlEncodeValue(this.s) + '&' + Dropbox.Xhr.urlEncodeValue(tokenSecret); + return null; + }; + + Oauth.prototype.authHeader = function(method, url, params) { + var header, oauth_params, param, value, _i, _len; + this.addAuthParams(method, url, params); + oauth_params = []; + for (param in params) { + value = params[param]; + if (param.substring(0, 6) === 'oauth_') { + oauth_params.push(param); + } + } + oauth_params.sort(); + header = []; + for (_i = 0, _len = oauth_params.length; _i < _len; _i++) { + param = oauth_params[_i]; + header.push(Dropbox.Xhr.urlEncodeValue(param) + '="' + Dropbox.Xhr.urlEncodeValue(params[param]) + '"'); + delete params[param]; + } + return 'OAuth ' + header.join(','); + }; + + Oauth.prototype.addAuthParams = function(method, url, params) { + this.boilerplateParams(params); + params.oauth_signature = this.signature(method, url, params); + return params; + }; + + Oauth.prototype.boilerplateParams = function(params) { + params.oauth_consumer_key = this.k; + params.oauth_nonce = this.nonce(); + params.oauth_signature_method = 'HMAC-SHA1'; + if (this.token) { + params.oauth_token = this.token; + } + params.oauth_timestamp = Math.floor(Date.now() / 1000); + params.oauth_version = '1.0'; + return params; + }; + + Oauth.prototype.nonce = function() { + return Date.now().toString(36) + Math.random().toString(36); + }; + + Oauth.prototype.signature = function(method, url, params) { + var string; + string = method.toUpperCase() + '&' + Dropbox.Xhr.urlEncodeValue(url) + '&' + Dropbox.Xhr.urlEncodeValue(Dropbox.Xhr.urlEncode(params)); + return base64HmacSha1(string, this.hmacKey); + }; + + Oauth.prototype.appHash = function() { + if (this._appHash) { + return this._appHash; + } + return this._appHash = base64Sha1(this.k).replace(/\=/g, ''); + }; + + return Oauth; + + })(); + + if (Date.now == null) { + Date.now = function() { + return (new Date()).getTime(); + }; + } + + dropboxEncodeKey = function(key, secret) { + var i, k, result, s, x, y, z, _i, _j, _ref, _ref1, _results; + if (secret) { + secret = [encodeURIComponent(key), encodeURIComponent(secret)].join('?'); + key = (function() { + var _i, _ref, _results; + _results = []; + for (i = _i = 0, _ref = key.length / 2; 0 <= _ref ? _i < _ref : _i > _ref; i = 0 <= _ref ? ++_i : --_i) { + _results.push(((key.charCodeAt(i * 2) & 15) * 16) + (key.charCodeAt(i * 2 + 1) & 15)); + } + return _results; + })(); + } else { + _ref = key.split('|', 2), key = _ref[0], secret = _ref[1]; + key = atob(key); + key = (function() { + var _i, _ref1, _results; + _results = []; + for (i = _i = 0, _ref1 = key.length; 0 <= _ref1 ? _i < _ref1 : _i > _ref1; i = 0 <= _ref1 ? ++_i : --_i) { + _results.push(key.charCodeAt(i)); + } + return _results; + })(); + secret = atob(secret); + } + s = (function() { + _results = []; + for (_i = 0; _i < 256; _i++){ _results.push(_i); } + return _results; + }).apply(this); + y = 0; + for (x = _j = 0; _j < 256; x = ++_j) { + y = (y + s[i] + key[x % key.length]) % 256; + _ref1 = [s[y], s[x]], s[x] = _ref1[0], s[y] = _ref1[1]; + } + x = y = 0; + result = (function() { + var _k, _ref2, _ref3, _results1; + _results1 = []; + for (z = _k = 0, _ref2 = secret.length; 0 <= _ref2 ? _k < _ref2 : _k > _ref2; z = 0 <= _ref2 ? ++_k : --_k) { + x = (x + 1) % 256; + y = (y + s[x]) % 256; + _ref3 = [s[y], s[x]], s[x] = _ref3[0], s[y] = _ref3[1]; + k = s[(s[x] + s[y]) % 256]; + _results1.push(String.fromCharCode((k ^ secret.charCodeAt(z)) % 256)); + } + return _results1; + })(); + key = (function() { + var _k, _ref2, _results1; + _results1 = []; + for (i = _k = 0, _ref2 = key.length; 0 <= _ref2 ? _k < _ref2 : _k > _ref2; i = 0 <= _ref2 ? ++_k : --_k) { + _results1.push(String.fromCharCode(key[i])); + } + return _results1; + })(); + return [btoa(key.join('')), btoa(result.join(''))].join('|'); + }; + + Dropbox.PulledChanges = (function() { + + PulledChanges.parse = function(deltaInfo) { + if (deltaInfo && typeof deltaInfo === 'object') { + return new Dropbox.PulledChanges(deltaInfo); + } else { + return deltaInfo; + } + }; + + PulledChanges.prototype.blankSlate = void 0; + + PulledChanges.prototype.cursorTag = void 0; + + PulledChanges.prototype.changes = void 0; + + PulledChanges.prototype.shouldPullAgain = void 0; + + PulledChanges.prototype.shouldBackOff = void 0; + + function PulledChanges(deltaInfo) { + var entry; + this.blankSlate = deltaInfo.reset || false; + this.cursorTag = deltaInfo.cursor; + this.shouldPullAgain = deltaInfo.has_more; + this.shouldBackOff = !this.shouldPullAgain; + if (deltaInfo.cursor && deltaInfo.cursor.length) { + this.changes = (function() { + var _i, _len, _ref, _results; + _ref = deltaInfo.entries; + _results = []; + for (_i = 0, _len = _ref.length; _i < _len; _i++) { + entry = _ref[_i]; + _results.push(Dropbox.PullChange.parse(entry)); + } + return _results; + })(); + } else { + this.changes = []; + } + } + + return PulledChanges; + + })(); + + Dropbox.PullChange = (function() { + + PullChange.parse = function(entry) { + if (entry && typeof entry === 'object') { + return new Dropbox.PullChange(entry); + } else { + return entry; + } + }; + + PullChange.prototype.path = void 0; + + PullChange.prototype.wasRemoved = void 0; + + PullChange.prototype.stat = void 0; + + function PullChange(entry) { + this.path = entry[0]; + this.stat = Dropbox.Stat.parse(entry[1]); + if (this.stat) { + this.wasRemoved = false; + } else { + this.stat = null; + this.wasRemoved = true; + } + } + + return PullChange; + + })(); + + Dropbox.PublicUrl = (function() { + + PublicUrl.parse = function(urlData, isDirect) { + if (urlData && typeof urlData === 'object') { + return new Dropbox.PublicUrl(urlData, isDirect); + } else { + return urlData; + } + }; + + PublicUrl.prototype.url = void 0; + + PublicUrl.prototype.expiresAt = void 0; + + PublicUrl.prototype.isDirect = void 0; + + PublicUrl.prototype.isPreview = void 0; + + function PublicUrl(urlData, isDirect) { + this.url = urlData.url; + this.expiresAt = new Date(Date.parse(urlData.expires)); + if (isDirect === true) { + this.isDirect = true; + } else if (isDirect === false) { + this.isDirect = false; + } else { + this.isDirect = Date.now() - this.expiresAt <= 86400000; + } + this.isPreview = !this.isDirect; + } + + return PublicUrl; + + })(); + + Dropbox.CopyReference = (function() { + + CopyReference.parse = function(refData) { + if (refData && (typeof refData === 'object' || typeof refData === 'string')) { + return new Dropbox.CopyReference(refData); + } else { + return refData; + } + }; + + CopyReference.prototype.tag = void 0; + + CopyReference.prototype.expiresAt = void 0; + + function CopyReference(refData) { + if (typeof refData === 'object') { + this.tag = refData.copy_ref; + this.expiresAt = new Date(Date.parse(refData.expires)); + } else { + this.tag = refData; + this.expiresAt = new Date(); + } + } + + return CopyReference; + + })(); + + Dropbox.Stat = (function() { + + Stat.parse = function(metadata) { + if (metadata && typeof metadata === 'object') { + return new Dropbox.Stat(metadata); + } else { + return metadata; + } + }; + + Stat.prototype.path = null; + + Stat.prototype.name = null; + + Stat.prototype.inAppFolder = null; + + Stat.prototype.isFolder = null; + + Stat.prototype.isFile = null; + + Stat.prototype.isRemoved = null; + + Stat.prototype.typeIcon = null; + + Stat.prototype.versionTag = null; + + Stat.prototype.mimeType = null; + + Stat.prototype.size = null; + + Stat.prototype.humanSize = null; + + Stat.prototype.hasThumbnail = null; + + Stat.prototype.modifiedAt = null; + + Stat.prototype.clientModifiedAt = null; + + function Stat(metadata) { + var lastIndex, nameSlash, _ref, _ref1; + this.path = metadata.path; + if (this.path.substring(0, 1) !== '/') { + this.path = '/' + this.path; + } + lastIndex = this.path.length - 1; + if (lastIndex >= 0 && this.path.substring(lastIndex) === '/') { + this.path = this.path.substring(0, lastIndex); + } + nameSlash = this.path.lastIndexOf('/'); + this.name = this.path.substring(nameSlash + 1); + this.isFolder = metadata.is_dir || false; + this.isFile = !this.isFolder; + this.isRemoved = metadata.is_deleted || false; + this.typeIcon = metadata.icon; + if ((_ref = metadata.modified) != null ? _ref.length : void 0) { + this.modifiedAt = new Date(Date.parse(metadata.modified)); + } else { + this.modifiedAt = null; + } + if ((_ref1 = metadata.client_mtime) != null ? _ref1.length : void 0) { + this.clientModifiedAt = new Date(Date.parse(metadata.client_mtime)); + } else { + this.clientModifiedAt = null; + } + switch (metadata.root) { + case 'dropbox': + this.inAppFolder = false; + break; + case 'app_folder': + this.inAppFolder = true; + break; + default: + this.inAppFolder = null; + } + this.size = metadata.bytes || 0; + this.humanSize = metadata.size || ''; + this.hasThumbnail = metadata.thumb_exists || false; + if (this.isFolder) { + this.versionTag = metadata.hash; + this.mimeType = metadata.mime_type || 'inode/directory'; + } else { + this.versionTag = metadata.rev; + this.mimeType = metadata.mime_type || 'application/octet-stream'; + } + } + + return Stat; + + })(); + + Dropbox.UserInfo = (function() { + + UserInfo.parse = function(userInfo) { + if (userInfo && typeof userInfo === 'object') { + return new Dropbox.UserInfo(userInfo); + } else { + return userInfo; + } + }; + + UserInfo.prototype.name = null; + + UserInfo.prototype.email = null; + + UserInfo.prototype.countryCode = null; + + UserInfo.prototype.uid = null; + + UserInfo.prototype.referralUrl = null; + + UserInfo.prototype.publicAppUrl = null; + + UserInfo.prototype.quota = null; + + UserInfo.prototype.usedQuota = null; + + UserInfo.prototype.privateBytes = null; + + UserInfo.prototype.sharedBytes = null; + + function UserInfo(userInfo) { + var lastIndex; + this.name = userInfo.display_name; + this.email = userInfo.email; + this.countryCode = userInfo.country || null; + this.uid = userInfo.uid.toString(); + if (userInfo.public_app_url) { + this.publicAppUrl = userInfo.public_app_url; + lastIndex = this.publicAppUrl.length - 1; + if (lastIndex >= 0 && this.publicAppUrl.substring(lastIndex) === '/') { + this.publicAppUrl = this.publicAppUrl.substring(0, lastIndex); + } + } else { + this.publicAppUrl = null; + } + this.referralUrl = userInfo.referral_link; + this.quota = userInfo.quota_info.quota; + this.privateBytes = userInfo.quota_info.normal || 0; + this.sharedBytes = userInfo.quota_info.shared || 0; + this.usedQuota = this.privateBytes + this.sharedBytes; + } + + return UserInfo; + + })(); + + if (typeof window !== "undefined" && window !== null) { + if (window.XDomainRequest && !('withCredentials' in new XMLHttpRequest())) { + DropboxXhrRequest = window.XDomainRequest; + DropboxXhrIeMode = true; + DropboxXhrCanSendForms = false; + } else { + DropboxXhrRequest = window.XMLHttpRequest; + DropboxXhrIeMode = false; + DropboxXhrCanSendForms = window.navigator.userAgent.indexOf('Firefox') === -1; + } + DropboxXhrDoesPreflight = true; + } else { + DropboxXhrRequest = require('xmlhttprequest').XMLHttpRequest; + DropboxXhrIeMode = false; + DropboxXhrCanSendForms = false; + DropboxXhrDoesPreflight = false; + } + + if (typeof Uint8Array === 'undefined') { + DropboxXhrArrayBufferView = null; + } else { + DropboxXhrArrayBufferView = (new Uint8Array(0)).__proto__.__proto__.constructor; + } + + Dropbox.Xhr = (function() { + + Xhr.Request = DropboxXhrRequest; + + Xhr.ieMode = DropboxXhrIeMode; + + Xhr.canSendForms = DropboxXhrCanSendForms; + + Xhr.doesPreflight = DropboxXhrDoesPreflight; + + Xhr.ArrayBufferView = DropboxXhrArrayBufferView; + + function Xhr(method, baseUrl) { + this.method = method; + this.isGet = this.method === 'GET'; + this.url = baseUrl; + this.headers = {}; + this.params = null; + this.body = null; + this.preflight = !(this.isGet || (this.method === 'POST')); + this.signed = false; + this.responseType = null; + this.callback = null; + this.xhr = null; + } + + Xhr.prototype.setParams = function(params) { + if (this.signed) { + throw new Error('setParams called after addOauthParams or addOauthHeader'); + } + if (this.params) { + throw new Error('setParams cannot be called twice'); + } + this.params = params; + return this; + }; + + Xhr.prototype.setCallback = function(callback) { + this.callback = callback; + return this; + }; + + Xhr.prototype.signWithOauth = function(oauth) { + if (Dropbox.Xhr.ieMode || (Dropbox.Xhr.doesPreflight && (!this.preflight))) { + return this.addOauthParams(oauth); + } else { + return this.addOauthHeader(oauth); + } + }; + + Xhr.prototype.addOauthParams = function(oauth) { + if (this.signed) { + throw new Error('Request already has an OAuth signature'); + } + this.params || (this.params = {}); + oauth.addAuthParams(this.method, this.url, this.params); + this.signed = true; + return this; + }; + + Xhr.prototype.addOauthHeader = function(oauth) { + if (this.signed) { + throw new Error('Request already has an OAuth signature'); + } + this.params || (this.params = {}); + this.signed = true; + return this.setHeader('Authorization', oauth.authHeader(this.method, this.url, this.params)); + }; + + Xhr.prototype.setBody = function(body) { + if (this.isGet) { + throw new Error('setBody cannot be called on GET requests'); + } + if (this.body !== null) { + throw new Error('Request already has a body'); + } + if (!this.preflight) { + if (!((typeof FormData !== 'undefined') && (body instanceof FormData))) { + this.preflight = true; + } + } + this.body = body; + return this; + }; + + Xhr.prototype.setResponseType = function(responseType) { + this.responseType = responseType; + return this; + }; + + Xhr.prototype.setHeader = function(headerName, value) { + var oldValue; + if (this.headers[headerName]) { + oldValue = this.headers[headerName]; + throw new Error("HTTP header " + headerName + " already set to " + oldValue); + } + if (headerName === 'Content-Type') { + throw new Error('Content-Type is automatically computed based on setBody'); + } + this.preflight = true; + this.headers[headerName] = value; + return this; + }; + + Xhr.prototype.setFileField = function(fieldName, fileName, fileData, contentType) { + var boundary, useFormData; + if (this.body !== null) { + throw new Error('Request already has a body'); + } + if (this.isGet) { + throw new Error('paramsToBody cannot be called on GET requests'); + } + if (typeof fileData === 'object' && typeof Blob !== 'undefined') { + if ((typeof ArrayBuffer !== "undefined" && ArrayBuffer !== null) && fileData instanceof ArrayBuffer) { + fileData = new Uint8Array(fileData); + } + if (Dropbox.Xhr.ArrayBufferView && fileData instanceof Dropbox.Xhr.ArrayBufferView) { + contentType || (contentType = 'application/octet-stream'); + fileData = new Blob([fileData], { + type: contentType + }); + } + if (typeof File !== 'undefined' && fileData instanceof File) { + fileData = new Blob([fileData], { + type: fileData.type + }); + } + useFormData = fileData instanceof Blob; + } else { + useFormData = false; + } + if (useFormData) { + this.body = new FormData(); + return this.body.append(fieldName, fileData, fileName); + } else { + contentType || (contentType = 'application/octet-stream'); + boundary = this.multipartBoundary(); + this.headers['Content-Type'] = "multipart/form-data; boundary=" + boundary; + return this.body = ['--', boundary, "\r\n", 'Content-Disposition: form-data; name="', fieldName, '"; filename="', fileName, "\"\r\n", 'Content-Type: ', contentType, "\r\n", "Content-Transfer-Encoding: binary\r\n\r\n", fileData, "\r\n", '--', boundary, '--', "\r\n"].join(''); + } + }; + + Xhr.prototype.multipartBoundary = function() { + return [Date.now().toString(36), Math.random().toString(36)].join('----'); + }; + + Xhr.prototype.paramsToUrl = function() { + var queryString; + if (this.params) { + queryString = Dropbox.Xhr.urlEncode(this.params); + if (queryString.length !== 0) { + this.url = [this.url, '?', queryString].join(''); + } + this.params = null; + } + return this; + }; + + Xhr.prototype.paramsToBody = function() { + if (this.params) { + if (this.body !== null) { + throw new Error('Request already has a body'); + } + if (this.isGet) { + throw new Error('paramsToBody cannot be called on GET requests'); + } + this.headers['Content-Type'] = 'application/x-www-form-urlencoded'; + this.body = Dropbox.Xhr.urlEncode(this.params); + this.params = null; + } + return this; + }; + + Xhr.prototype.prepare = function() { + var header, ieMode, value, _ref, + _this = this; + ieMode = Dropbox.Xhr.ieMode; + if (this.isGet || this.body !== null || ieMode) { + this.paramsToUrl(); + if (this.body !== null && typeof this.body === 'string') { + this.headers['Content-Type'] = 'text/plain; charset=utf8'; + } + } else { + this.paramsToBody(); + } + this.xhr = new Dropbox.Xhr.Request(); + if (ieMode) { + this.xhr.onload = function() { + return _this.onLoad(); + }; + this.xhr.onerror = function() { + return _this.onError(); + }; + } else { + this.xhr.onreadystatechange = function() { + return _this.onReadyStateChange(); + }; + } + this.xhr.open(this.method, this.url, true); + if (!ieMode) { + _ref = this.headers; + for (header in _ref) { + if (!__hasProp.call(_ref, header)) continue; + value = _ref[header]; + this.xhr.setRequestHeader(header, value); + } + } + if (this.responseType) { + if (this.responseType === 'b') { + if (this.xhr.overrideMimeType) { + this.xhr.overrideMimeType('text/plain; charset=x-user-defined'); + } + } else { + this.xhr.responseType = this.responseType; + } + } + return this; + }; + + Xhr.prototype.send = function(callback) { + var body; + this.callback = callback || this.callback; + if (this.body !== null) { + body = this.body; + if (Dropbox.Xhr.ArrayBufferView && body instanceof ArrayBuffer) { + body = new Uint8Array(body); + } + try { + this.xhr.send(body); + } catch (e) { + if (typeof Blob !== 'undefined' && Dropbox.Xhr.ArrayBufferView && body instanceof Dropbox.Xhr.ArrayBufferView) { + body = new Blob([body], { + type: 'application/octet-stream' + }); + this.xhr.send(body); + } else { + throw e; + } + } + } else { + this.xhr.send(); + } + return this; + }; + + Xhr.urlEncode = function(object) { + var chunks, key, value; + chunks = []; + for (key in object) { + value = object[key]; + chunks.push(this.urlEncodeValue(key) + '=' + this.urlEncodeValue(value)); + } + return chunks.sort().join('&'); + }; + + Xhr.urlEncodeValue = function(object) { + return encodeURIComponent(object.toString()).replace(/\!/g, '%21').replace(/'/g, '%27').replace(/\(/g, '%28').replace(/\)/g, '%29').replace(/\*/g, '%2A'); + }; + + Xhr.urlDecode = function(string) { + var kvp, result, token, _i, _len, _ref; + result = {}; + _ref = string.split('&'); + for (_i = 0, _len = _ref.length; _i < _len; _i++) { + token = _ref[_i]; + kvp = token.split('='); + result[decodeURIComponent(kvp[0])] = decodeURIComponent(kvp[1]); + } + return result; + }; + + Xhr.prototype.onReadyStateChange = function() { + var apiError, bytes, dirtyText, i, metadata, metadataJson, text, _i, _ref; + if (this.xhr.readyState !== 4) { + return true; + } + if (this.xhr.status < 200 || this.xhr.status >= 300) { + apiError = new Dropbox.ApiError(this.xhr, this.method, this.url); + this.callback(apiError); + return true; + } + metadataJson = this.xhr.getResponseHeader('x-dropbox-metadata'); + if (metadataJson != null ? metadataJson.length : void 0) { + try { + metadata = JSON.parse(metadataJson); + } catch (e) { + metadata = void 0; + } + } else { + metadata = void 0; + } + if (this.responseType) { + if (this.responseType === 'b') { + dirtyText = this.xhr.responseText != null ? this.xhr.responseText : this.xhr.response; + /* + jsString = ['["'] + for i in [0...dirtyText.length] + hexByte = (dirtyText.charCodeAt(i) & 0xFF).toString(16) + if hexByte.length is 2 + jsString.push "\\u00#{hexByte}" + else + jsString.push "\\u000#{hexByte}" + jsString.push '"]' + console.log jsString + text = JSON.parse(jsString.join(''))[0] + */ + + bytes = []; + for (i = _i = 0, _ref = dirtyText.length; 0 <= _ref ? _i < _ref : _i > _ref; i = 0 <= _ref ? ++_i : --_i) { + bytes.push(String.fromCharCode(dirtyText.charCodeAt(i) & 0xFF)); + } + text = bytes.join(''); + this.callback(null, text, metadata); + } else { + this.callback(null, this.xhr.response, metadata); + } + return true; + } + text = this.xhr.responseText != null ? this.xhr.responseText : this.xhr.response; + switch (this.xhr.getResponseHeader('Content-Type')) { + case 'application/x-www-form-urlencoded': + this.callback(null, Dropbox.Xhr.urlDecode(text), metadata); + break; + case 'application/json': + case 'text/javascript': + this.callback(null, JSON.parse(text), metadata); + break; + default: + this.callback(null, text, metadata); + } + return true; + }; + + Xhr.prototype.onLoad = function() { + var text; + text = this.xhr.responseText; + switch (this.xhr.contentType) { + case 'application/x-www-form-urlencoded': + this.callback(null, Dropbox.Xhr.urlDecode(text), void 0); + break; + case 'application/json': + case 'text/javascript': + this.callback(null, JSON.parse(text), void 0); + break; + default: + this.callback(null, text, void 0); + } + return true; + }; + + Xhr.prototype.onError = function() { + var apiError; + apiError = new Dropbox.ApiError(this.xhr, this.method, this.url); + this.callback(apiError); + return true; + }; + + return Xhr; + + })(); + + if ((typeof module !== "undefined" && module !== null ? module.exports : void 0) != null) { + module.exports = Dropbox; + } else if (typeof window !== "undefined" && window !== null) { + window.Dropbox = Dropbox; + } else { + throw new Error('This library only supports node.js and modern browsers.'); + } + + Dropbox.atob = atob; + + Dropbox.btoa = btoa; + + Dropbox.hmac = base64HmacSha1; + + Dropbox.sha1 = base64Sha1; + + Dropbox.encodeKey = dropboxEncodeKey; + +}).call(this); From 8f1db519c775a7f787bf6a0adb24280ddc7952e3 Mon Sep 17 00:00:00 2001 From: coderaiser Date: Sat, 22 Dec 2012 10:49:00 -0500 Subject: [PATCH 048/347] refactored menu module --- html/auth.html | 4 +- lib/client/dom.js | 4 +- lib/client/menu.js | 151 +++++++++++++++++----------------- lib/client/storage/_github.js | 31 ++++--- 4 files changed, 98 insertions(+), 92 deletions(-) diff --git a/html/auth.html b/html/auth.html index 5390efe7..aa23df2b 100644 --- a/html/auth.html +++ b/html/auth.html @@ -7,8 +7,8 @@ "use strict"; if(window.opener){ - var lGitHub = window.opener.CloudCommander.Github; - lGitHub.init(lGitHub.getUserData, window.location.search); + var lGitHub = window.opener.CloudCommander.GitHub; + lGitHub.autorize(lGitHub.callback, window.location.search); window.close(); } diff --git a/lib/client/dom.js b/lib/client/dom.js index c27351c6..ce665c6a 100644 --- a/lib/client/dom.js +++ b/lib/client/dom.js @@ -1084,7 +1084,9 @@ var CloudCommander, Util, DOM, CloudFunc; ',personalbar=0,toolbar=0' + ',scrollbars=1,resizable=1'; - window.open(pUrl, 'Cloud Commander Auth', lOptions); + var lWind = window.open(pUrl, 'Cloud Commander Auth', lOptions); + if(!lWind) + Util.log('Pupup blocked!'); }; /** diff --git a/lib/client/menu.js b/lib/client/menu.js index b1ee40ba..62fdd3c4 100644 --- a/lib/client/menu.js +++ b/lib/client/menu.js @@ -57,26 +57,53 @@ var CloudCommander, Util, DOM, $; */ function getUploadToItem(pObjectName){ var lObj = {}; - lObj.name = pObjectName; - - lObj.callback = function(key, opt){ - getCurrentData(function(pParams){ - var lObject = cloudcmd[pObjectName]; - - if('init' in lObject) - lObject.uploadFile(pParams); - else - Util.exec(lObject, function(){ - cloudcmd[pObjectName].uploadFile(pParams); - }); - }); - - Util.log('Uploading to ' + pObjectName+ '...'); - }; + if( Util.isArray(pObjectName) ){ + + var n = pObjectName.length; + for(var i = 0; i < n; i++){ + var lStr = pObjectName[i]; + lObj[lStr] = getUploadToItem( lStr ); + } + } + else if( Util.isString(pObjectName) ){ + lObj.name = pObjectName; + + lObj.callback = function(key, opt){ + getCurrentData(function(pParams){ + var lObject = cloudcmd[pObjectName]; + + if('init' in lObject) + lObject.uploadFile(pParams); + else + Util.exec(lObject, function(){ + cloudcmd[pObjectName].uploadFile(pParams); + }); + }); + + Util.log('Uploading to ' + pObjectName+ '...'); + }; + } return lObj; } + /** + * get menu item + */ + function getItem(pName, pCallBack){ + var lRet = { + name : pName + }; + + if(Util.isFunction(pCallBack) ) + lRet.callback = pCallBack; + + else if (Util.isObject(pCallBack)) + lRet.items = pCallBack; + + return lRet; + } + /** * function return configureation for menu */ @@ -94,68 +121,40 @@ var CloudCommander, Util, DOM, $; // define the elements of the menu items: { - view: { - name : 'View', - callback : function(key, opt){ - showEditor(true); - } - }, - - edit: { - name : 'Edit', - callback : function(key, opt){ - showEditor(); - } - }, - - delete: { - name: 'Delete', - callback: function(key, opt){ - DOM.promptRemoveCurrent(); - } - }, - - upload: { - name: 'Upload to', - items: { - 'GitHub' : getUploadToItem('GitHub'), - 'GDrive' : getUploadToItem('GDrive'), - 'DropBox' : getUploadToItem('DropBox') - } - }, - - download: { - name: 'Download', - callback: function(key, opt){ - DOM.Images.showLoad(); + 'View' : getItem( 'View', Util.retExec(showEditor, true) ), + 'Edit' : getItem( 'Edit', Util.retExec(showEditor) ), + 'Delete' : getItem( 'Delete', Util.retExec(DOM.promptRemoveCurrent) ), + 'Upload to' : getItem( 'Upload to', + getUploadToItem( ['GitHub', 'GDrive', 'DropBox'] ) ), + 'Download' : getItem( 'Download', function(key, opt){ + DOM.Images.showLoad(); + + var lPath = DOM.getCurrentPath(), + lId = DOM.getIdBySrc(lPath); + + Util.log('downloading file ' + lPath +'...'); + + lPath = lPath + '?download'; + + if(!DOM.getById(lId)){ + var lDownload = DOM.anyload({ + name : 'iframe', + async : false, + className : 'hidden', + src : lPath, + func : Util.retFunc(DOM.Images.hideLoad) + }); - var lPath = DOM.getCurrentPath(), - lId = DOM.getIdBySrc(lPath); - - Util.log('downloading file ' + lPath +'...'); - - lPath = lPath + '?download'; - - if(!DOM.getById(lId)){ - var lDownload = DOM.anyload({ - name : 'iframe', - async : false, - className : 'hidden', - src : lPath, - func : Util.retFunc(DOM.Images.hideLoad) - }); - - DOM.Images.hideLoad(); - setTimeout(function() { - document.body.removeChild(lDownload); - }, 10000); - } - else - DOM.Images.showError({ - responseText: 'Error: You trying to' + - 'download same file to often'}); + DOM.Images.hideLoad(); + setTimeout(function() { + document.body.removeChild(lDownload); + }, 10000); } - } + else + DOM.Images.showError({ + responseText: 'Error: You trying to' + + 'download same file to often'}); + }) } }; } diff --git a/lib/client/storage/_github.js b/lib/client/storage/_github.js index 9bd5292c..47e7e24a 100644 --- a/lib/client/storage/_github.js +++ b/lib/client/storage/_github.js @@ -13,7 +13,7 @@ var CloudCommander, Util, DOM, $, Github, cb; GitHub_ID, GithubLocal, User, - GithubStore = {}; + GitHubStore = {}; /* temporary callback function for work with github */ cb = function (err, data){ console.log(err || data);}; @@ -52,10 +52,10 @@ var CloudCommander, Util, DOM, $, Github, cb; } - GithubStore.init = function(pCallBack, pCode){ + GitHubStore.autorize = function(pCallBack, pCode){ var lToken = Cache.get('token'); if(lToken){ - GithubStore.Login(lToken); + GitHubStore.Login(lToken); Util.exec(pCallBack); } else{ @@ -67,7 +67,7 @@ var CloudCommander, Util, DOM, $, Github, cb; if(pData && pData.token){ lToken = pData.token; - GithubStore.Login(lToken); + GitHubStore.Login(lToken); Cache.set('token', lToken); Util.exec(pCallBack); } @@ -90,7 +90,7 @@ var CloudCommander, Util, DOM, $, Github, cb; } }; - GithubStore.getUserData = function(pCallBack){ + GitHubStore.getUserData = function(pCallBack){ var lName, lShowUserInfo = function(pError, pData){ @@ -109,7 +109,7 @@ var CloudCommander, Util, DOM, $, Github, cb; } /* PUBLIC FUNCTIONS */ - GithubStore.basicLogin = function(pUser, pPasswd){ + GitHubStore.basicLogin = function(pUser, pPasswd){ GithubLocal = new Github({ username: pUser, password: pPasswd, @@ -117,7 +117,7 @@ var CloudCommander, Util, DOM, $, Github, cb; }); }; - GithubStore.Login = function(pToken){ + GitHubStore.Login = function(pToken){ Github = GithubLocal = new Github({ token : pToken, auth : 'oauth' @@ -129,7 +129,7 @@ var CloudCommander, Util, DOM, $, Github, cb; /** * function creates gist */ - cloudcmd.GitHub.uploadFile = function(pParams, pCallBack){ + GitHubStore.uploadFile = function(pParams, pCallBack){ var lContent = pParams.data, lName = pParams.name; @@ -165,17 +165,22 @@ var CloudCommander, Util, DOM, $, Github, cb; return lContent; }; - cloudcmd.GitHub.init = function(pCallBack){ + GitHubStore.init = function(pCallBack){ Util.loadOnLoad([ Util.retExec(pCallBack), - GithubStore.getUserData, - GithubStore.init, + GitHubStore.getUserData, + GitHubStore.autorize, setConfig, load ]); - cloudcmd.GitHub.init = null; + GitHubStore.callback = function(){ + Util.loadOnLoad([ + Util.retExec(pCallBack), + GitHubStore.getUserData, + ]); + }; }; - cloudcmd.Github = GithubStore; + cloudcmd.GitHub = GitHubStore; })(); From b4946182735c9303c5a2c61eeb29a0525b68d036 Mon Sep 17 00:00:00 2001 From: coderaiser Date: Sun, 23 Dec 2012 06:26:34 -0500 Subject: [PATCH 049/347] refactored --- lib/client/menu.js | 97 ++++++++++++++++++++++++++++++---------------- 1 file changed, 63 insertions(+), 34 deletions(-) diff --git a/lib/client/menu.js b/lib/client/menu.js index 62fdd3c4..eac93841 100644 --- a/lib/client/menu.js +++ b/lib/client/menu.js @@ -104,6 +104,34 @@ var CloudCommander, Util, DOM, $; return lRet; } + /** + * get all menu items + * pItems = [{pName, pFunc}] + */ + function getAllItems(pItems){ + var lRet = {}; + + if( Util.isArray(pItems) ){ + var lName, + lFunc, + i, n = pItems.length; + for(i = 0; i < n; i++){ + var lItem = pItems[i]; + if(lItem){ + lName = lItem.name; + + for(var lProp in lItem){ + lName = lProp; + lFunc = lItem[lProp]; + } + lRet[lName] = getItem(lName, lFunc); + } + } + } + + return lRet; + } + /** * function return configureation for menu */ @@ -120,42 +148,43 @@ var CloudCommander, Util, DOM, $; }, // define the elements of the menu - items: { - 'View' : getItem( 'View', Util.retExec(showEditor, true) ), - 'Edit' : getItem( 'Edit', Util.retExec(showEditor) ), - 'Delete' : getItem( 'Delete', Util.retExec(DOM.promptRemoveCurrent) ), - 'Upload to' : getItem( 'Upload to', - getUploadToItem( ['GitHub', 'GDrive', 'DropBox'] ) ), - 'Download' : getItem( 'Download', function(key, opt){ - DOM.Images.showLoad(); - - var lPath = DOM.getCurrentPath(), - lId = DOM.getIdBySrc(lPath); - - Util.log('downloading file ' + lPath +'...'); - - lPath = lPath + '?download'; - - if(!DOM.getById(lId)){ - var lDownload = DOM.anyload({ - name : 'iframe', - async : false, - className : 'hidden', - src : lPath, - func : Util.retFunc(DOM.Images.hideLoad) - }); + items: getAllItems([ + { 'View' : Util.retExec(showEditor, true) }, + { 'Edit' : Util.retExec(showEditor) }, + { 'Delete' : Util.retExec(DOM.promptRemoveCurrent) }, + { 'Upload to' : getUploadToItem( ['GitHub', 'GDrive', 'DropBox'] ) }, + { + 'Download' : function(key, opt){ + DOM.Images.showLoad(); - DOM.Images.hideLoad(); - setTimeout(function() { - document.body.removeChild(lDownload); - }, 10000); + var lPath = DOM.getCurrentPath(), + lId = DOM.getIdBySrc(lPath); + + Util.log('downloading file ' + lPath +'...'); + + lPath = lPath + '?download'; + + if(!DOM.getById(lId)){ + var lDownload = DOM.anyload({ + name : 'iframe', + async : false, + className : 'hidden', + src : lPath, + func : Util.retFunc(DOM.Images.hideLoad) + }); + + DOM.Images.hideLoad(); + setTimeout(function() { + document.body.removeChild(lDownload); + }, 10000); + } + else + DOM.Images.showError({ + responseText: 'Error: You trying to' + + 'download same file to often'}); } - else - DOM.Images.showError({ - responseText: 'Error: You trying to' + - 'download same file to often'}); - }) - } + } + ]) }; } From c2b169a130c5ec39fa916c0957d24901bb080251 Mon Sep 17 00:00:00 2001 From: coderaiser Date: Sun, 23 Dec 2012 06:42:04 -0500 Subject: [PATCH 050/347] refactored --- lib/client/menu.js | 40 ++++++++++++++-------------------------- 1 file changed, 14 insertions(+), 26 deletions(-) diff --git a/lib/client/menu.js b/lib/client/menu.js index eac93841..5a65ad6a 100644 --- a/lib/client/menu.js +++ b/lib/client/menu.js @@ -109,25 +109,15 @@ var CloudCommander, Util, DOM, $; * pItems = [{pName, pFunc}] */ function getAllItems(pItems){ - var lRet = {}; + var lRet = {}, + lName, + lFunc; - if( Util.isArray(pItems) ){ - var lName, - lFunc, - i, n = pItems.length; - for(i = 0; i < n; i++){ - var lItem = pItems[i]; - if(lItem){ - lName = lItem.name; - - for(var lProp in lItem){ - lName = lProp; - lFunc = lItem[lProp]; - } - lRet[lName] = getItem(lName, lFunc); - } + if(pItems) + for(lName in pItems){ + lFunc = pItems[lName]; + lRet[lName] = getItem(lName, lFunc); } - } return lRet; } @@ -148,13 +138,12 @@ var CloudCommander, Util, DOM, $; }, // define the elements of the menu - items: getAllItems([ - { 'View' : Util.retExec(showEditor, true) }, - { 'Edit' : Util.retExec(showEditor) }, - { 'Delete' : Util.retExec(DOM.promptRemoveCurrent) }, - { 'Upload to' : getUploadToItem( ['GitHub', 'GDrive', 'DropBox'] ) }, - { - 'Download' : function(key, opt){ + items: getAllItems({ + 'View' : Util.retExec(showEditor, true), + 'Edit' : Util.retExec(showEditor), + 'Delete' : Util.retExec(DOM.promptRemoveCurrent), + 'Upload to' : getUploadToItem( ['GitHub', 'GDrive', 'DropBox'] ), + 'Download' : function(key, opt){ DOM.Images.showLoad(); var lPath = DOM.getCurrentPath(), @@ -183,8 +172,7 @@ var CloudCommander, Util, DOM, $; responseText: 'Error: You trying to' + 'download same file to often'}); } - } - ]) + }) }; } From c9664e85e117b91ba8772123d5a7fcb5c23e427f Mon Sep 17 00:00:00 2001 From: coderaiser Date: Sun, 23 Dec 2012 07:47:41 -0500 Subject: [PATCH 051/347] fixed bug with pressing Esc when menu showed --- ChangeLog | 4 ++++ lib/client/menu.js | 5 ++--- lib/client/menu/contextMenu.js | 4 ++-- lib/util.js | 4 +++- 4 files changed, 11 insertions(+), 6 deletions(-) diff --git a/ChangeLog b/ChangeLog index 6dad0bbd..43d4305c 100644 --- a/ChangeLog +++ b/ChangeLog @@ -34,6 +34,10 @@ to cloudcmd.js * DropBox, GDrive and GitHub modules now look the same. +* Fixed bug with pressing Esc when menu showed. +We could not to now, when key was pressed becaouse of +stopPropogation function in jquery.contextMenu module. + 2012.12.12, Version 0.1.8 diff --git a/lib/client/menu.js b/lib/client/menu.js index 5a65ad6a..e8db3194 100644 --- a/lib/client/menu.js +++ b/lib/client/menu.js @@ -140,7 +140,7 @@ var CloudCommander, Util, DOM, $; // define the elements of the menu items: getAllItems({ 'View' : Util.retExec(showEditor, true), - 'Edit' : Util.retExec(showEditor), + 'Edit' : Util.retExec(showEditor, false), 'Delete' : Util.retExec(DOM.promptRemoveCurrent), 'Upload to' : getUploadToItem( ['GitHub', 'GDrive', 'DropBox'] ), 'Download' : function(key, opt){ @@ -246,8 +246,7 @@ var CloudCommander, Util, DOM, $; /* if document.onclick was set up * before us, it's best time to call it */ - if(typeof lFunc_f === 'function') - lFunc_f(); + Util.exec(lFunc_f); KeyBinding.set(); } diff --git a/lib/client/menu/contextMenu.js b/lib/client/menu/contextMenu.js index 2b4da541..53506e6d 100644 --- a/lib/client/menu/contextMenu.js +++ b/lib/client/menu/contextMenu.js @@ -106,7 +106,7 @@ var // currently active contextMenu trigger // x and y are given (by mouse event) var triggerIsFixed = opt.$trigger.parents().andSelf() .filter(function() { - return $(this).css('position') == "fixed"; + return $($this).css('position') == "fixed"; }).length; if (triggerIsFixed) { @@ -428,7 +428,7 @@ var // currently active contextMenu trigger e.preventDefault(); } - e.stopPropagation(); + //e.stopPropagation(); }, key: function(e) { var opt = $currentTrigger.data('contextMenu') || {}, diff --git a/lib/util.js b/lib/util.js index 0c9def50..2defb049 100644 --- a/lib/util.js +++ b/lib/util.js @@ -243,7 +243,9 @@ var Util, exports; */ Util.retExec = function(pCallBack, pArg){ return function(pArgument){ - Util.exec(pCallBack, pArg || pArgument); + if( !Util.isUndefined(pArg) ) + pArgument = pArg; + Util.exec(pCallBack, pArgument); }; }; From 99a324c7b8196599dd9e84cbdcdb949a1e77b7d9 Mon Sep 17 00:00:00 2001 From: coderaiser Date: Sun, 23 Dec 2012 10:21:22 -0500 Subject: [PATCH 052/347] minor changes --- ChangeLog | 10 ++++++++++ lib/client/menu/contextMenu.js | 9 +++------ 2 files changed, 13 insertions(+), 6 deletions(-) diff --git a/ChangeLog b/ChangeLog index 43d4305c..844718d9 100644 --- a/ChangeLog +++ b/ChangeLog @@ -37,6 +37,16 @@ to cloudcmd.js * Fixed bug with pressing Esc when menu showed. We could not to now, when key was pressed becaouse of stopPropogation function in jquery.contextMenu module. +Commented stopPropogration. +``` +keyStop: function(e, opt) { + if (!opt.isInput) { + e.preventDefault(); + } + + //e.stopPropagation(); +} +``` 2012.12.12, Version 0.1.8 diff --git a/lib/client/menu/contextMenu.js b/lib/client/menu/contextMenu.js index 53506e6d..fe6d51a7 100644 --- a/lib/client/menu/contextMenu.js +++ b/lib/client/menu/contextMenu.js @@ -93,8 +93,7 @@ var // currently active contextMenu trigger }, // position menu position: function(opt, x, y) { - var $this = this, - offset; + var offset; // determine contextMenu position if (!x && !y) { opt.determinePosition.call(this, opt.$menu); @@ -106,7 +105,7 @@ var // currently active contextMenu trigger // x and y are given (by mouse event) var triggerIsFixed = opt.$trigger.parents().andSelf() .filter(function() { - return $($this).css('position') == "fixed"; + return $(this).css('position') == "fixed"; }).length; if (triggerIsFixed) { @@ -431,9 +430,7 @@ var // currently active contextMenu trigger //e.stopPropagation(); }, key: function(e) { - var opt = $currentTrigger.data('contextMenu') || {}, - $children = opt.$menu.children(), - $round; + var opt = $currentTrigger.data('contextMenu') || {}; switch (e.keyCode) { case 9: From 90354f42135800d84b5d7adaf6da725e7d126d33 Mon Sep 17 00:00:00 2001 From: coderaiser Date: Sun, 23 Dec 2012 10:45:29 -0500 Subject: [PATCH 053/347] updated socket.io to v0.9.13 --- ChangeLog | 1 + package.json | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/ChangeLog b/ChangeLog index 844718d9..098303b0 100644 --- a/ChangeLog +++ b/ChangeLog @@ -47,6 +47,7 @@ keyStop: function(e, opt) { //e.stopPropagation(); } ``` +* Updated socket.io to v0.9.13. 2012.12.12, Version 0.1.8 diff --git a/package.json b/package.json index df1f8408..d840e4f4 100644 --- a/package.json +++ b/package.json @@ -16,7 +16,7 @@ "subdomain": "cloudcmd", "dependencies": { "minify": "0.1.8", - "socket.io": "0.9.10" + "socket.io": "0.9.13" }, "devDependencies": {}, "engines": { From 165b30edbdfa5cc2f4df6dcfc71ff94963d44045 Mon Sep 17 00:00:00 2001 From: coderaiser Date: Mon, 24 Dec 2012 08:40:57 -0500 Subject: [PATCH 054/347] minor changes --- lib/client/menu.js | 1 + lib/client/menu/contextMenu.js | 3 ++- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/lib/client/menu.js b/lib/client/menu.js index e8db3194..8b466bf6 100644 --- a/lib/client/menu.js +++ b/lib/client/menu.js @@ -190,6 +190,7 @@ var CloudCommander, Util, DOM, $; DOM.anyLoadInParallel(lFiles, function(){ console.timeEnd('menu load'); + $.contextMenu.handle.keyStop = $.noop; Util.exec(pCallBack); }); } diff --git a/lib/client/menu/contextMenu.js b/lib/client/menu/contextMenu.js index fe6d51a7..04438d80 100644 --- a/lib/client/menu/contextMenu.js +++ b/lib/client/menu/contextMenu.js @@ -427,7 +427,7 @@ var // currently active contextMenu trigger e.preventDefault(); } - //e.stopPropagation(); + e.stopPropagation(); }, key: function(e) { var opt = $currentTrigger.data('contextMenu') || {}; @@ -1578,5 +1578,6 @@ $.contextMenu.fromMenu = function(element) { // make defaults accessible $.contextMenu.defaults = defaults; $.contextMenu.types = types; +$.contextMenu.handle = handle; })(jQuery); From 2e1e9867677458a57ab3049bf6d4c7a7879254e1 Mon Sep 17 00:00:00 2001 From: coderaiser Date: Tue, 25 Dec 2012 08:20:22 -0500 Subject: [PATCH 055/347] updated jquery-terminal to v0.4.22 --- ChangeLog | 2 + lib/client/terminal/jquery-terminal/CHANGELOG | 170 ++-- lib/client/terminal/jquery-terminal/README | 138 +-- lib/client/terminal/jquery-terminal/README.in | 69 -- .../jquery-terminal/jquery.terminal.css | 139 +-- .../jquery-terminal/jquery.terminal.js | 914 ++++++++++++------ 6 files changed, 848 insertions(+), 584 deletions(-) delete mode 100644 lib/client/terminal/jquery-terminal/README.in diff --git a/ChangeLog b/ChangeLog index 098303b0..e15e6c98 100644 --- a/ChangeLog +++ b/ChangeLog @@ -49,6 +49,8 @@ keyStop: function(e, opt) { ``` * Updated socket.io to v0.9.13. +* Updated jquery-terminal to v0.4.22. + 2012.12.12, Version 0.1.8 diff --git a/lib/client/terminal/jquery-terminal/CHANGELOG b/lib/client/terminal/jquery-terminal/CHANGELOG index 2c53b6c6..305b1b9c 100644 --- a/lib/client/terminal/jquery-terminal/CHANGELOG +++ b/lib/client/terminal/jquery-terminal/CHANGELOG @@ -1,82 +1,88 @@ -0.4.17 fix IE formatting issue by adding cross-browser split -0.4.16 add reverse history search on CTRL+R - - fix cancel ajax call on CTRL+D -0.4.15 only one command from multiply commands is added to history - CTRL+D is handled even if exit is false -0.4.14 terminal don't add space after prompt (prompt need to add this space) - fix historyFilter - remove livequery -0.4.12 history return history object - add historyFilter - new event onCommandChange that execute scroll_to_bottom - add event onBeforeLogin -0.4.11 fix blank lines when echo longer strings -0.4.10 fix long line formatting and linebreak in the middle of formatting -0.4.9 fix wrap first line when prompt contain formatting -0.4.8 fix alt+d and ctrl+u -0.4.7 fix inserting special characters in Webkit on Windows -0.4.6 remove undocumented pipe operator - refreash prompt on resume -0.4.5 fix line wrapping when text contains tabulations -0.4.4 fix line wrapping with scrollbars -0.4.3 fix JSON-RPC when use without login -0.4.2 fix formatting when text contain empty lines -0.4.1 fix formatting when text contains newline characters -0.4 fix text formating when text splited into more then one line - you can pass nested objects as first argument - add tab completion with object passed as first argument -0.3.8 fix cursor manipulation when command contain new line characters -0.3.7 fix function terminal.login_name -0.3.6 fix switch between terminals - when terminal is not visible scroll to current terminal -0.3.5 fix scrolling in jQuery 1.6 -0.3.3 fixing PAGE UP/DOWN -0.3.2 fixing cursor in long lines -0.3.1 fixing small bugs, speed up resizing -0.3 fix resizing on start and issue with greetings - add formating strings to set style of text. - add to echo a function which will be called when terminal - is resized -0.3-RC2 fix manipulation of long line commands -0.3-RC1 add callbacks and new functions - you can now overwrite keyboard shortcuts - resizing recalculates lines lenght and redraw content - if you create plugin for elements that are not in the DOM - and then append it to DOM it's display corectly - put all dependencies in one file - Default greetings show terminal signature depending on - width of terminal - use Local Sorage for command line history if posible - remove access to command line (cmd plugin) and add interface - to allow interact with it - -0.2.3.9 fix append enter character (0x0D) to the command (thanks to marat - for reporting the bug) - -0.2.3.8 update mousewheel plugin which fix scrolling in Opera (Thanks for - Alexey Dubovtsev for reporting the bug) - -0.2.3.7 fix cursor in IE in tilda example - -0.2.3.6 fix json serialization in IE - -0.2.3.5 fix demos and clipboard textarea transparency in IE - -0.2.3.4 fix long lines in command line issue - -0.2.3.3 fix Terminal in Internet Exporer - -0.2.3.2 fix blank line issue (thanks to Chris Janicki for finding the - bug) and fix CTRL + Arrows scroll on CTRL+V - -0.2.3.1 allow CTRL+W CTRL+T - -0.2.3 fix for "(#$%.{" characters on Opera/Chrome, add cursor move - with CTRL+P, CTRL+N, CTRL+F, CTRL+B which also work in Chrome. - Fix Arrow Keys on Chrome (for cursor move and command line - history). Change License to LGPL3. - -0.2.2 fix down-arrow/open parentises issue in Opera and Chrome - -0.2.1 add support for paste from clipboard with CTRL+V (Copy to - clipboard is alway enabled on websites) +0.4.20 Add exec, greetings, onClear, onBlur, onFocus, onTerminalChange +0.4.19 add support for ANSI terminal formatting, fix cancelable ajax on + CTRL+D, replace emails with link mailto, remove formatting processing + from command line, add text glow option to formatting +0.4.18 fix scrollbar, better exceptions in chrome, replace urls with links + one style for font and color in root .terminal class +0.4.17 fix IE formatting issue by adding cross-browser split +0.4.16 add reverse history search on CTRL+R + + fix cancel ajax call on CTRL+D +0.4.15 only one command from multiply commands is added to history + CTRL+D is handled even if exit is false +0.4.14 terminal don't add space after prompt (prompt need to add this space) + fix historyFilter + remove livequery +0.4.12 history return history object + add historyFilter + new event onCommandChange that execute scroll_to_bottom + add event onBeforeLogin +0.4.11 fix blank lines when echo longer strings +0.4.10 fix long line formatting and linebreak in the middle of formatting +0.4.9 fix wrap first line when prompt contain formatting +0.4.8 fix alt+d and ctrl+u +0.4.7 fix inserting special characters in Webkit on Windows +0.4.6 remove undocumented pipe operator + refreash prompt on resume +0.4.5 fix line wrapping when text contains tabulations +0.4.4 fix line wrapping with scrollbars +0.4.3 fix JSON-RPC when use without login +0.4.2 fix formatting when text contain empty lines +0.4.1 fix formatting when text contains newline characters +0.4 fix text formating when text splited into more then one line + you can pass nested objects as first argument + add tab completion with object passed as first argument +0.3.8 fix cursor manipulation when command contain new line characters +0.3.7 fix function terminal.login_name +0.3.6 fix switch between terminals - when terminal is not visible scroll to current terminal +0.3.5 fix scrolling in jQuery 1.6 +0.3.3 fixing PAGE UP/DOWN +0.3.2 fixing cursor in long lines +0.3.1 fixing small bugs, speed up resizing +0.3 fix resizing on start and issue with greetings + add formating strings to set style of text. + add to echo a function which will be called when terminal + is resized +0.3-RC2 fix manipulation of long line commands +0.3-RC1 add callbacks and new functions + you can now overwrite keyboard shortcuts + resizing recalculates lines lenght and redraw content + if you create plugin for elements that are not in the DOM + and then append it to DOM it's display corectly + put all dependencies in one file + Default greetings show terminal signature depending on + width of terminal + use Local Sorage for command line history if posible + remove access to command line (cmd plugin) and add interface + to allow interact with it + +0.2.3.9 fix append enter character (0x0D) to the command (thanks to marat + for reporting the bug) + +0.2.3.8 update mousewheel plugin which fix scrolling in Opera (Thanks for + Alexey Dubovtsev for reporting the bug) + +0.2.3.7 fix cursor in IE in tilda example + +0.2.3.6 fix json serialization in IE + +0.2.3.5 fix demos and clipboard textarea transparency in IE + +0.2.3.4 fix long lines in command line issue + +0.2.3.3 fix Terminal in Internet Exporer + +0.2.3.2 fix blank line issue (thanks to Chris Janicki for finding the + bug) and fix CTRL + Arrows scroll on CTRL+V + +0.2.3.1 allow CTRL+W CTRL+T + +0.2.3 fix for "(#$%.{" characters on Opera/Chrome, add cursor move + with CTRL+P, CTRL+N, CTRL+F, CTRL+B which also work in Chrome. + Fix Arrow Keys on Chrome (for cursor move and command line + history). Change License to LGPL3. + +0.2.2 fix down-arrow/open parentises issue in Opera and Chrome + +0.2.1 add support for paste from clipboard with CTRL+V (Copy to + clipboard is alway enabled on websites) \ No newline at end of file diff --git a/lib/client/terminal/jquery-terminal/README b/lib/client/terminal/jquery-terminal/README index 6a31c538..16957789 100644 --- a/lib/client/terminal/jquery-terminal/README +++ b/lib/client/terminal/jquery-terminal/README @@ -1,69 +1,69 @@ - __ _____ ________ __ - / // _ /__ __ _____ ___ __ _/__ ___/__ ___ ______ __ __ __ ___ / / - __ / // // // // // _ // _// // / / // _ // _// // // \/ // _ \/ / - / / // // // // // ___// / / // / / // ___// / / / / // // /\ // // / /__ - \___//____ \\___//____//_/ _\_ / /_//____//_/ /_/ /_//_//_/ /_/ \__\_\___/ - \/ /____/ version 0.4.18 - - -http://terminal.jcubic.pl - -Licensed under GNU LGPL Version 3 license http://www.gnu.org/licenses/lgpl.html -Copyright (c) 2011 Jakub Jankiewicz - -JQuery Terminal Emulator is a plugin for creating command line interpreters in -your applications. It can automatically call JSON-RPC service when user type -commands or you can provide you own function in which you can parse user -command. It's ideal if you want to provide additional functionality for power -users. It can also be used to debug your aplication. - -Features: - - * You can create interpreter for your JSON-RPC service with one line - of code. - - * Support for authentication (you can provide function when user enter - login and password or if you use JSON-RPC it can automatically call - login function on the server and pass token to all functions) - - * Stack of interpreters - you can create commands that trigger additional - interpreters (eg. you can use couple of JSON-RPC service and run them - when user type command) - - * Command Tree - you can use nested objects. Each command will invoke a - function, if the value is an object it will create new interpreter and - use function from that object as commands. You can use as much nested - object/commands as you like. If the value is a string it will create - JSON-RPC service. - - * Support for command line history it use Local Storage if posible - - * Support for tab completion if you create terminal from an object - - * Include keyboard shortcut from bash like CTRL+A, CTRL+D, CTRL+E etc. - - * Multiply terminals on one page (every terminal can have different - command, it's own authentication function and it's own command history) - - * It catch all exceptions and display error messages in terminal - (you can see errors in your javascript and php code in terminal if they - are in interpreter function) - - -Example of usage (javascript interpreter) - -jQuery(function($, undefined) { - $('#term_demo').terminal(function(command, term) { - if (command !== '') { - var result = window.eval(command); - if (result != undefined) { - term.echo(String(result)); - } - } - }, { - greetings: 'Javascript Interpreter', - name: 'js_demo', - height: 200, - width: 450, - prompt: 'js> '}); -}); + __ _____ ________ __ + / // _ /__ __ _____ ___ __ _/__ ___/__ ___ ______ __ __ __ ___ / / + __ / // // // // // _ // _// // / / // _ // _// // // \/ // _ \/ / + / / // // // // // ___// / / // / / // ___// / / / / // // /\ // // / /__ + \___//____ \\___//____//_/ _\_ / /_//____//_/ /_/ /_//_//_/ /_/ \__\_\___/ + \/ /____/ version 0.4.22 + + +http://terminal.jcubic.pl + +Licensed under GNU LGPL Version 3 license http://www.gnu.org/licenses/lgpl.html +Copyright (c) 2011-2012 Jakub Jankiewicz + +JQuery Terminal Emulator is a plugin for creating command line interpreters in +your applications. It can automatically call JSON-RPC service when user type +commands or you can provide you own function in which you can parse user +command. It's ideal if you want to provide additional functionality for power +users. It can also be used to debug your aplication. + +Features: + + * You can create interpreter for your JSON-RPC service with one line + of code. + + * Support for authentication (you can provide function when user enter + login and password or if you use JSON-RPC it can automatically call + login function on the server and pass token to all functions) + + * Stack of interpreters - you can create commands that trigger additional + interpreters (eg. you can use couple of JSON-RPC service and run them + when user type command) + + * Command Tree - you can use nested objects. Each command will invoke a + function, if the value is an object it will create new interpreter and + use function from that object as commands. You can use as much nested + object/commands as you like. If the value is a string it will create + JSON-RPC service. + + * Support for command line history it use Local Storage if posible + + * Support for tab completion if you create terminal from an object + + * Include keyboard shortcut from bash like CTRL+A, CTRL+D, CTRL+E etc. + + * Multiply terminals on one page (every terminal can have different + command, it's own authentication function and it's own command history) + + * It catch all exceptions and display error messages in terminal + (you can see errors in your javascript and php code in terminal if they + are in interpreter function) + + +Example of usage (javascript interpreter) + +jQuery(function($, undefined) { + $('#term_demo').terminal(function(command, term) { + if (command !== '') { + var result = window.eval(command); + if (result != undefined) { + term.echo(String(result)); + } + } + }, { + greetings: 'Javascript Interpreter', + name: 'js_demo', + height: 200, + width: 450, + prompt: 'js> '}); +}); \ No newline at end of file diff --git a/lib/client/terminal/jquery-terminal/README.in b/lib/client/terminal/jquery-terminal/README.in deleted file mode 100644 index f7487dac..00000000 --- a/lib/client/terminal/jquery-terminal/README.in +++ /dev/null @@ -1,69 +0,0 @@ - __ _____ ________ __ - / // _ /__ __ _____ ___ __ _/__ ___/__ ___ ______ __ __ __ ___ / / - __ / // // // // // _ // _// // / / // _ // _// // // \/ // _ \/ / - / / // // // // // ___// / / // / / // ___// / / / / // // /\ // // / /__ - \___//____ \\___//____//_/ _\_ / /_//____//_/ /_/ /_//_//_/ /_/ \__\_\___/ - \/ /____/ version {{VER}} - - -http://terminal.jcubic.pl - -Licensed under GNU LGPL Version 3 license http://www.gnu.org/licenses/lgpl.html -Copyright (c) 2011 Jakub Jankiewicz - -JQuery Terminal Emulator is a plugin for creating command line interpreters in -your applications. It can automatically call JSON-RPC service when user type -commands or you can provide you own function in which you can parse user -command. It's ideal if you want to provide additional functionality for power -users. It can also be used to debug your aplication. - -Features: - - * You can create interpreter for your JSON-RPC service with one line - of code. - - * Support for authentication (you can provide function when user enter - login and password or if you use JSON-RPC it can automatically call - login function on the server and pass token to all functions) - - * Stack of interpreters - you can create commands that trigger additional - interpreters (eg. you can use couple of JSON-RPC service and run them - when user type command) - - * Command Tree - you can use nested objects. Each command will invoke a - function, if the value is an object it will create new interpreter and - use function from that object as commands. You can use as much nested - object/commands as you like. If the value is a string it will create - JSON-RPC service. - - * Support for command line history it use Local Storage if posible - - * Support for tab completion if you create terminal from an object - - * Include keyboard shortcut from bash like CTRL+A, CTRL+D, CTRL+E etc. - - * Multiply terminals on one page (every terminal can have different - command, it's own authentication function and it's own command history) - - * It catch all exceptions and display error messages in terminal - (you can see errors in your javascript and php code in terminal if they - are in interpreter function) - - -Example of usage (javascript interpreter) - -jQuery(function($, undefined) { - $('#term_demo').terminal(function(command, term) { - if (command !== '') { - var result = window.eval(command); - if (result != undefined) { - term.echo(String(result)); - } - } - }, { - greetings: 'Javascript Interpreter', - name: 'js_demo', - height: 200, - width: 450, - prompt: 'js> '}); -}); diff --git a/lib/client/terminal/jquery-terminal/jquery.terminal.css b/lib/client/terminal/jquery-terminal/jquery.terminal.css index f4391f9c..03f6bfa2 100644 --- a/lib/client/terminal/jquery-terminal/jquery.terminal.css +++ b/lib/client/terminal/jquery-terminal/jquery.terminal.css @@ -1,67 +1,72 @@ -.terminal .clipboard { - position: absolute; - bottom: 0; - left: 0; - opacity: 0.01; - filter: alpha(opacity = 0.01); - filter: progid:DXImageTransform.Microsoft.Alpha(opacity=0.01); - width: 2px; -} -.cmd > .clipboard { - position: fixed; -} -.terminal { - position: relative; - overflow: hidden; -} -.cmd { - padding: 0; - margin: 0; - height: 1.3em; -} -.terminal .terminal-output div { - display: block; -} -.terminal { - font-family: FreeMono, monospace; - color: #aaa; - background-color: #000; - line-height: 16px; - /* removing breaking the lines */ - white-space: nowrap; - overflow:hidden; -} -.terminal .cmd span { - float: left; -} -.terminal .cmd span.inverted { - background-color: #aaa; - color: #000; -} -.terminal div::-moz-selection, .terminal span::-moz-selection { - background-color: #aaa; - color: #000; -} -.terminal div::selection, .terminal span::selection { - background-color: #aaa; - color: #000; -} -.terminal .terminal-output div.error, .terminal .terminal-output div.error div { - color: red; -} -.tilda { - position: fixed; - top: 0; - left: 0; - width: 100%; - z-index: 1100; -} -.clear { - clear: both; -} -.terminal a { - color: #0F60FF; -} -.terminal a:hover { - color: red; -} +.terminal .clipboard { + position: absolute; + bottom: 0; + left: 0; + opacity: 0.01; + filter: alpha(opacity = 0.01); + filter: progid:DXImageTransform.Microsoft.Alpha(opacity=0.01); + width: 2px; +} +.cmd > .clipboard { + position: fixed; +} +.terminal { + padding: 10px; + position: relative; + overflow: hidden; +} +.cmd { + padding: 0; + margin: 0; + height: 1.3em; +} +.terminal .terminal-output div div, .terminal .prompt { + display: block; + line-height: 9px; + height: 14px; +} +.terminal { + font-family: FreeMono, monospace; + color: #aaa; + background-color: #000; + font-size: 12px; + line-height: 14px; +} +.terminal .terminal-output div span { + display: inline-block; +} +.terminal .cmd span { + display: inline-block; +} +.terminal .cmd span.inverted { + background-color: #aaa; + color: #000; +} +.terminal .terminal-output div div::-moz-selection, .terminal .terminal-output div span::-moz-selection { + background-color: #aaa; + color: #000; +} +.terminal .terminal-output div div::selection, .terminal .terminal-output div span::selection, +.terminal .cmd > span::selection, .terminal .prompt span::selection { + background-color: #aaa; + color: #000; +} +.terminal .terminal-output div.error, .terminal .terminal-output div.error div { + color: red; +} +.tilda { + position: fixed; + top: 0; + left: 0; + width: 100%; + z-index: 1100; +} +.clear { + clear: both; +} +.terminal a { + color: #0F60FF; +} +.terminal a:hover { + color: red; +} \ No newline at end of file diff --git a/lib/client/terminal/jquery-terminal/jquery.terminal.js b/lib/client/terminal/jquery-terminal/jquery.terminal.js index 749c83fd..b8af09fe 100644 --- a/lib/client/terminal/jquery-terminal/jquery.terminal.js +++ b/lib/client/terminal/jquery-terminal/jquery.terminal.js @@ -4,11 +4,11 @@ *| __ / // // // // // _ // _// // / / // _ // _// // // \/ // _ \/ / *| / / // // // // // ___// / / // / / // ___// / / / / // // /\ // // / /__ *| \___//____ \\___//____//_/ _\_ / /_//____//_/ /_/ /_//_//_/ /_/ \__\_\___/ - *| \/ /____/ version 0.4.17 + *| \/ /____/ version 0.4.22 * http://terminal.jcubic.pl * * Licensed under GNU LGPL Version 3 license - * Copyright (c) 2011 Jakub Jankiewicz + * Copyright (c) 2011-2012 Jakub Jankiewicz * * Includes: * @@ -22,7 +22,7 @@ * Copyright 2007-2012 Steven Levithan * Available under the MIT License * - * Date: Sun, 24 Jun 2012 13:42:07 +0000 + * Date: Thu, 15 Nov 2012 07:12:21 +0000 */ /* @@ -47,29 +47,30 @@ disable */ -// return true if value is in array -Array.prototype.has = function(val) { - "use strict"; - for (var i = this.length; i--;) { - if (this[i] === val) { - return true; - } - } - return false; -}; -// debug function -function get_stack(caller) { - "use strict"; - if (caller) { - return [caller.toString().match(/.*\n.*\n/)].concat(get_stack(caller.caller)); - } else { - return []; - } -} + (function($, undefined) { "use strict"; + + // map object to object + $.omap = function(o, fn) { + var result = {}; + $.each(o, function(k, v) { + result[k] = fn.call(o, k, v); + }); + return result; + }; + // debug function + function get_stack(caller) { + "use strict"; + if (caller) { + return [caller.toString().match(/.*\n.*\n/)]. + concat(get_stack(caller.caller)); + } else { + return []; + } + } // ---------------------------------------- // START Storage plugin // ---------------------------------------- @@ -233,15 +234,15 @@ function get_stack(caller) { fn.$timerID = fn.$timerID || this.guid++; var handler = function() { - if (belay && this.inProgress) { + if (belay && handler.inProgress) { return; } - this.inProgress = true; + handler.inProgress = true; if ((++counter > times && times !== 0) || fn.call(element, counter) === false) { jQuery.timer.remove(element, label, fn); } - this.inProgress = false; + handler.inProgress = false; }; handler.$timerID = fn.$timerID; @@ -323,10 +324,13 @@ function get_stack(caller) { // START CROSS BROWSER SPLIT // ---------------------------------------- - var split; + (function(undef) { - // Avoid running twice; that would break the `nativeSplit` reference - split = split || function (undef) { + // prevent double include + + if (!String.prototype.split.toString().match(/\[native/)) { + return; + } var nativeSplit = String.prototype.split, compliantExecNpcg = /()??/.exec("")[1] === undef, // NPCG: nonparticipating capturing group @@ -406,7 +410,7 @@ function get_stack(caller) { return self; - }(); + })(); // ----------------------------------------------------------------------- /* @@ -437,151 +441,9 @@ function get_stack(caller) { // ----------------------------------------------------------------------- - var format_split_re = /(\[\[[bius]*;[^;]*;[^\]]*\][^\]\[]*\])/g; - var format_re = /\[\[([bius]*);([^;]*);([^\]]*)\]([^\]\[]*)\]/g; - var color_hex_re = /#([0-9A-Fa-f]{3}|[0-9A-Fa-f]{6})/; - function encodeHTML(str) { - if (typeof str === 'string') { - // don't escape entities - str = str.replace(/&(?!#[0-9]+;|[a-zA-Z]+;)/g, '&'); - str = str.replace(//g, '>'); - // I don't think that it find \n - str = str.replace(/\n/g, '
    '); - str = str.replace(/ /g, ' '); - str = str.replace(/\t/g, '    '); - //support for formating foo[[u;;]bar]baz[[b;#fff;]quux]zzz - var splited = str.split(format_split_re); - //console.log($.json_stringify(splited)); - if (splited.length > 1) { - str = $.map(splited, function(text) { - if (text === '') { - return text; - } else if (text.substring(0,1) === '[') { - // use substring for IE quirks mode - [0] don't work - return text.replace(format_re, function(s, - style, - color, - background, - text) { - if (text === '') { - return ' '; - } - var style_str = ''; - if (style.indexOf('b') !== -1) { - style_str += 'font-weight:bold;'; - } - var text_decoration = 'text-decoration:'; - if (style.indexOf('u') !== -1) { - text_decoration += 'underline '; - } - if (style.indexOf('s') !== -1) { - text_decoration += 'line-through'; - } - if (style.indexOf('s') !== -1 || - style.indexOf('u') !== -1) { - style_str += text_decoration + ';'; - } - if (style.indexOf('i') !== -1) { - style_str += 'font-style:italic; '; - } - if (color.match(color_hex_re)) { - style_str += 'color:' + color + ';'; - } - if (background.match(color_hex_re)) { - style_str += 'background-color:' + background; - } - str = '' + text + - ''; - return str; - }); - } else { - return '' + text + ''; - } - }).join(''); - } - return str; - } else { - return ''; - } - } - // ----------------------------------------------------------------------- - //split string to array of strings with the same length and keep formatting - function get_formatted_lines(str, length) { - var array = str.split(/\n/g); - var re_format = /(\[\[[bius]*;[^;]*;[^\]]*\][^\]\[]*\]?)/g; - var re_begin = /(\[\[[bius]*;[^;]*;[^\]]*\])/; - var re_last = /\[\[[bius]*;?[^;]*;?[^\]]*\]?$/; - var formatting = false; - var in_text = false; - var braket = 0; - var prev_format = ''; - var result = []; - for (var i = 0, len = array.length; i < len; ++i) { - if (prev_format !== '') { - if (array[i] === '') { - result.push(prev_format + ']'); - continue; - } else { - array[i] = prev_format + array[i]; - prev_format = ''; - } - } else { - if (array[i] === '') { - result.push(''); - continue; - } - } - var line = array[i]; - var first_index = 0; - var count = 0; - for (var j=0, jlen=line.length; j' + $.terminal.strip(string) + '').text().length; } // ----------------------------------------------------------------------- @@ -772,7 +634,7 @@ function get_stack(caller) { $.extend(this, { append: function(item) { - if (enabled && bc.current() !== item) { + if (enabled) { bc.append(item); $.Storage.set(name + 'commands', $.json_stringify(bc.data())); } @@ -875,7 +737,7 @@ function get_stack(caller) { function get_splited_command_line(string) { /* string = str_repeat('x', prompt_len) + string; - var result = get_formatted_lines(string); + var result = $.terminal.split_equal(string); result[0] = result[0].substring(prompt_len); return result; */ @@ -889,31 +751,31 @@ function get_stack(caller) { function draw_cursor_line(string, position) { var len = string.length; if (position === len) { - before.html(encodeHTML(string)); + before.html($.terminal.encode(string)); cursor.html(' '); after.html(''); } else if (position === 0) { before.html(''); //fix for tilda in IE - cursor.html(encodeHTML(string.slice(0, 1))); - //cursor.html(encodeHTML(string[0])); - after.html(encodeHTML(string.slice(1))); + cursor.html($.terminal.encode(string.slice(0, 1))); + //cursor.html($.terminal.encode(string[0])); + after.html($.terminal.encode(string.slice(1))); } else { - var before_str = encodeHTML(string.slice(0, position)); + var before_str = $.terminal.encode(string.slice(0, position)); before.html(before_str); //fix for tilda in IE var c = string.slice(position, position + 1); //cursor.html(string[position])); - cursor.html(c === ' ' ? ' ' : encodeHTML(c)); + cursor.html(c === ' ' ? ' ' : $.terminal.encode(c)); if (position === string.length - 1) { after.html(''); } else { - after.html(encodeHTML(string.slice(position + 1))); + after.html($.terminal.encode(string.slice(position + 1))); } } } function div(string) { - return '
    ' + encodeHTML(string) + '
    '; + return '
    ' + $.terminal.encode(string) + '
    '; } function lines_after(lines) { var last_ins = after; @@ -1005,11 +867,11 @@ function get_stack(caller) { } else { // in the middle if (num_lines === 3) { - before.before('
    ' + encodeHTML(array[0]) + + before.before('
    ' + $.terminal.encode(array[0]) + '
    '); draw_cursor_line(array[1], position-first_len-1); after.after('
    ' + - encodeHTML(array[2]) + + $.terminal.encode(array[2]) + '
    '); } else { // more lines, cursor in the middle @@ -1055,11 +917,11 @@ function get_stack(caller) { return function() { if (typeof prompt === 'string') { prompt_len = skipFormattingCount(prompt); - prompt_node.html(encodeHTML(prompt)); + prompt_node.html($.terminal.format(prompt)); } else { prompt(function(string) { prompt_len = skipFormattingCount(string); - prompt_node.html(encodeHTML(string)); + prompt_node.html($.terminal.format(string)); }); } //change_num_chars(); @@ -1276,7 +1138,8 @@ function get_stack(caller) { return false; } /*else { if ((e.altKey && e.which === 68) || - (e.ctrlKey && [65, 66, 68, 69, 80, 78, 70].has(e.which)) || + (e.ctrlKey && + $.inArray(e.which, [65, 66, 68, 69, 80, 78, 70]) > -1) || // 68 === D [35, 36, 37, 38, 39, 40].has(e.which)) { return false; @@ -1361,6 +1224,14 @@ function get_stack(caller) { return position; } }, + visible: (function() { + var visible = self.visible; + return function() { + visible.apply(self, []); + redraw(); + draw_prompt(); + }; + })(), show: (function() { var show = self.show; return function() { @@ -1428,7 +1299,7 @@ function get_stack(caller) { } if (result === undefined || result) { if (enabled) { - if ([38, 32, 13, 0, 8].has(e.which) && + if ($.inArray(e.which, [38, 32, 13, 0, 8]) > -1 && e.keyCode !== 123 && // for F12 which === 0 //!(e.which === 40 && e.shiftKey || !(e.which === 38 && e.shiftKey)) { @@ -1460,6 +1331,307 @@ function get_stack(caller) { // characters return self; }; + + // ------------------------------------------------------------------------- + // :: TOOLS + // ------------------------------------------------------------------------- + + var format_split_re = /(\[\[[gbius]*;[^;]*;[^\]]*\](?:[^\]\[]*|\[*(?!\[)[^\]]*\][^\]]*)\])/g; + var format_re = /\[\[([gbius]*);([^;]*);([^;\]]*;|[^\]]*);?([^\]]*)\]([^\]\[]*|[^\[]*\[(?!\[)*[^\]]*\][^\]]*)\]/g; + var color_hex_re = /#([0-9A-Fa-f]{3}|[0-9A-Fa-f]{6})/; + var url_re = /(https?:((?!&[^;]+;)[^\s:"'<)])+)/g; + var email_regex = /((([^<>()[\]\\.,;:\s@\"]+(\.[^<>()[\]\\.,;:\s@\"]+)*)|(\".+\"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,})))/g; + $.terminal = { + // split text into lines with equal width and make each line be renderd + // separatly (text formating can be longer then a line). + split_equal: function(str, length) { + var array = str.split(/\n/g); + var re_format = /(\[\[[gbius]*;[^;]*;[^\]]*\][^\]\[]*\]?)/g; + var re_begin = /(\[\[[gbius]*;[^;]*;[^\]]*\])/; + var re_last = /\[\[[gbius]*;?[^;]*;?[^\]]*\]?$/; + var formatting = false; + var in_text = false; + var braket = 0; + var prev_format = ''; + var result = []; + for (var i = 0, len = array.length; i < len; ++i) { + if (prev_format !== '') { + if (array[i] === '') { + result.push(prev_format + ']'); + continue; + } else { + array[i] = prev_format + array[i]; + prev_format = ''; + } + } else { + if (array[i] === '') { + result.push(''); + continue; + } + } + var line = array[i]; + var first_index = 0; + var count = 0; + for (var j=0, jlen=line.length; j/g, '>') + // I don't think that it find \n + .replace(/\n/g, '
    ') + .replace(/ /g, ' ') + .replace(/\t/g, '    '); + }, + format: function(str) { + if (typeof str === 'string') { + str = $.terminal.encode($.terminal.from_ansi(str)); + //support for formating foo[[u;;]bar]baz[[b;#fff;]quux]zzz + var splited = str.split(format_split_re); + if (splited && splited.length > 1) { + str = $.map(splited, function(text) { + if (text === '') { + return text; + } else if (text.substring(0,1) === '[') { + // use substring for IE quirks mode - [0] don't work + return text.replace(format_re, function(s, + style, + color, + background, + _class, + text) { + if (text === '') { + return ' '; + } + var style_str = ''; + if (style.indexOf('b') !== -1) { + style_str += 'font-weight:bold;'; + } + var text_decoration = 'text-decoration:'; + if (style.indexOf('u') !== -1) { + text_decoration += 'underline '; + } + if (style.indexOf('s') !== -1) { + text_decoration += 'line-through'; + } + if (style.indexOf('s') !== -1 || + style.indexOf('u') !== -1) { + style_str += text_decoration + ';'; + } + if (style.indexOf('i') !== -1) { + style_str += 'font-style:italic;'; + } + if (color.match(color_hex_re)) { + style_str += 'color:' + color + ';'; + if (style.indexOf('g') !== -1) { + style_str += 'text-shadow: 0 0 5px ' + color + ';'; + } + } + if (background.match(color_hex_re)) { + style_str += 'background-color:' + background; + } + var result = '' + text + ''; + return result; + }); + } else { + return '' + text + ''; + } + }).join(''); + } + + return str.replace(url_re, function(link) { + var comma = link.match(/\.$/); + link = link.replace(/\.$/, ''); + return '' + link + '' + + (comma ? '.' : ''); + }).replace(email_regex, '$1'). + replace(/<\/span>/g, '
    '); + } else { + return ''; + } + }, + // remove formatting from text + strip: function(str) { + return str.replace(format_re, '$5'); + }, + // return active terminal + active: function() { + return terminals.front(); + }, + ansi_colors: { + normal: { + black: '#000', + red: '#AA0000', + green: '#008400', + yellow: '#AA5500', + blue: '#0000AA', + magenta: '#AA00AA', + cyan: '#00AAAA', + white: '#fff' + }, + bold: { + white: '#fff', + red: '#FF5555', + green: '#44D544', + yellow: '#FFFF55', + blue: '#5555FF', + magenta: '#FF55FF', + cyan: '#55FFFF', + black: '#000' + } + }, + from_ansi: (function() { + var color = { + 30: 'black', + 31: 'red', + 32: 'green', + 33: 'yellow', + 34: 'blue', + 35: 'magenta', + 36: 'cyan', + 37: 'white' + }; + var background = { + 40: 'black', + 41: 'red', + 42: 'green', + 43: 'yellow', + 44: 'blue', + 45: 'magenta', + 46: 'cyan', + 47: 'white' + }; + function format_ansi(code) { + var controls = code.split(';'); + var num; + var styles = []; + var output_color = ''; + var output_background = ''; + for(var i in controls) { + num = parseInt(controls[i], 10); + if (num === 1) { + styles.push('b'); + } + if (num === 4) { + styles.push('u'); + } + if (background[num]) { + output_background = background[num]; + } + if (color[num]) { + output_color = color[num]; + } + } + var normal = $.terminal.ansi_colors.normal; + var colors = normal; + for (var i=styles.length;i--;) { + if (styles[i] == 'b') { + if (output_color == '') { + output_color = 'white'; + } + colors = $.terminal.ansi_colors.bold; + break; + } + } + return '[[' + [styles.join(''), + colors[output_color], + normal[output_background] + ].join(';') + ']'; + } + return function(input) { + var splitted = input.split(/(\[[0-9;]*m)/g); + if (splitted.length == 1) { + return input; + } + var output = []; + //skip closing at the begining + if (splitted.length > 3 && splitted.slice(0,3).join('') == '[0m') { + splitted = splitted.slice(3); + } + var inside = false; + for (var i=0; i/, '')], ['jQuery Terminal Emulator version ' + version_string, @@ -1521,31 +1693,35 @@ function get_stack(caller) { var terminal_id = terminals.length(); var num_chars; // numer of chars in line var command_list = []; // for tab completion - var settings = { + var settings = $.extend({ name: '', prompt: '> ', history: true, exit: true, clear: true, enabled: true, + displayExceptions: true, cancelableAjax: true, login: null, tabcompletion: null, historyFilter: null, - onInit: null, - onExit: null, - keypress: null, - keydown: null - }; - if (options) { - if (options.width) { - self.width(options.width); - } - if (options.height) { - self.height(options.height); - } - $.extend(settings, options); + onInit: $.noop, + onClear: $.noop, + onBlur: $.noop, + onFocus: $.noop, + onTerminalChange: $.noop, + onExit: $.noop, + keypress: $.noop, + keydown: $.noop + }, options || {}); + + if (settings.width) { + self.width(settings.width); } + if (settings.height) { + self.height(settings.height); + } + var pause = !settings.enabled; if (self.length === 0) { throw 'Sorry, but terminal said that "' + self.selector + @@ -1561,10 +1737,22 @@ function get_stack(caller) { } output = $('
    ').addClass('terminal-output').appendTo(self); self.addClass('terminal').append('
    '); + self.click(function() { + self.find('textarea').focus(); + }); + /* + self.bind('touchstart.touchScroll', function() { + + }); + self.bind('touchmove.touchScroll', function() { + + }); + */ + //$('').hide().focus().appendTo(self); + //calculate numbers of characters function haveScrollbars() { return self.get(0).scrollHeight > self.innerHeight(); } - //calculate numbers of characters function get_num_chars() { var cursor = self.find('.cursor'); var cur_width = cursor.width(); @@ -1584,28 +1772,33 @@ function get_stack(caller) { // display Exception on terminal function display_exception(e, label) { - var message; - if (typeof e === 'string') { - message = e; - } else { - if (typeof e.fileName === 'string') { - message = e.fileName + ': ' + e.message; + if (settings.displayExceptions) { + var message; + if (typeof e === 'string') { + message = e; } else { - message = e.message; - } - } - self.error('[' + label + ']: ' + message); - self.pause(); - if (typeof e.fileName === 'string') { - //display filename and line which throw exeption - $.get(e.fileName, function(file) { - self.resume(); - var num = e.lineNumber - 1; - var line = file.split('\n')[num]; - if (line) { - self.error('[' + e.lineNumber + ']: ' + line); + if (typeof e.fileName === 'string') { + message = e.fileName + ': ' + e.message; + } else { + message = e.message; } - }); + } + self.error('[' + label + ']: ' + message); + if (typeof e.fileName === 'string') { + //display filename and line which throw exeption + self.pause(); + $.get(e.fileName, function(file) { + self.resume(); + var num = e.lineNumber - 1; + var line = file.split('\n')[num]; + if (line) { + self.error('[' + e.lineNumber + ']: ' + line); + } + }); + } + if (e.stack) { + self.error(e.stack); + } } } @@ -1628,7 +1821,6 @@ function get_stack(caller) { return true; } - function scroll_to_bottom() { var scrollHeight = self.prop ? self.prop('scrollHeight') : self.attr('scrollHeight'); @@ -1642,18 +1834,18 @@ function get_stack(caller) { // string can have line break //var array = string.split('\n'); // TODO: the way it should work - var array = get_formatted_lines(string, num_chars); + var array = $.terminal.split_equal(string, num_chars); div = $('
    '); for (i = 0, len = array.length; i < len; ++i) { if (array[i] === '' || array[i] === '\r') { div.append('
     
    '); } else { - $('
    ').html(encodeHTML(array[i])).appendTo(div); + $('
    ').html($.terminal.format(array[i])).appendTo(div); } } } else { - div = $('
    ').html(encodeHTML(string)); + div = $('
    ').html($.terminal.format(string)); } output.append(div); div.width('100%'); @@ -1683,31 +1875,57 @@ function get_stack(caller) { // TERMINAL METHODS // ---------------------------------------------------------- - $.extend(self, { + var dalyed_commands = []; + $.extend(self, $.omap({ clear: function() { output.html(''); command_line.set(''); lines = []; + try { + settings.onClear(self); + } catch (e) { + display_exception(e, 'onClear'); + throw e; + } self.attr({ scrollTop: 0}); return self; }, + exec: function(command, silent) { + if (pause) { + dalyed_commands.push([command, silent]); + } else { + commands(command, silent); + } + return self; + }, + commands: function() { + return interpreters.top().eval; + }, + greetings: function() { + show_greetings(); + return self; + }, paused: function() { return pause; }, pause: function() { if (command_line) { + pause = true; self.disable(); - command_line.hide(); + command_line.hidden(); } return self; }, resume: function() { - //console.log('resume on ' + options.prompt + '\n' + - // get_stack(arguments.callee.caller).join('')); if (command_line) { self.enable(); - command_line.show(); - + var original = dalyed_commands; + dalyed_commands = []; + while (original.length) { + var command = original.shift(); + self.exec.apply(self, command); + } + command_line.visible(); scroll_to_bottom(); } return self; @@ -1738,27 +1956,55 @@ function get_stack(caller) { // 100 provides buffer in viewport var x = next.offset().top - 50; $('html,body').animate({scrollTop: x}, 500); + try { + settings.onTerminalChange(next); + } catch (e) { + display_exception(e, 'onTerminalChange'); + throw e; + } return next; } } }, - focus: function(toggle) { - //console.log('focus on ' + options.prompt + '\n' + - // get_stack(arguments.callee.caller).join('')); - // TODO: one terminal should go out of focus - // TODO: add onFocus and onBlur + // silent used so events are not fired on init + focus: function(toggle, silent) { self.oneTime(1, function() { if (terminals.length() === 1) { if (toggle === false) { - self.disable(); + try { + if (!silent && settings.onBlur(self) !== false) { + self.disable(); + } + } catch (e) { + display_exception(e, 'onBlur'); + throw e; + } } else { - self.enable(); + try { + if (!silent && settings.onFocus(self) !== false) { + self.enable(); + } + } catch (e) { + display_exception(e, 'onFocus'); + throw e; + } } } else { if (toggle === false) { self.next(); } else { - terminals.front().disable(); + var front = terminals.front(); + if (front != self) { + front.disable(); + if (!silent) { + try { + settings.onTerminalChange(self); + } catch (e) { + display_exception(e, 'onTerminalChange'); + throw e; + } + } + } terminals.set(self); self.enable(); } @@ -1768,8 +2014,6 @@ function get_stack(caller) { return self; }, enable: function() { - //console.log('enable: ' + options.prompt + '\n' + - // get_stack(arguments.callee.caller).join('')); if (num_chars === undefined) { //enabling first time self.resize(); @@ -1818,7 +2062,7 @@ function get_stack(caller) { }, set_prompt: function(prompt) { if (validate('prompt', prompt)) { - if (prompt.constructor === Function) { + if (typeof prompt == 'function') { command_line.prompt(function(command) { prompt(command, self); }); @@ -1847,11 +2091,11 @@ function get_stack(caller) { return lines; } else { return $.map(lines, function(i, item) { - return typeof item === 'function' ? item() : item; - }).get().join('\n'); + return typeof item == 'function' ? item() : item; + }).join('\n'); } }, - resize: function(width, height) { + resize: function(width, height) { if (width && height) { self.width(width); self.height(height); @@ -1861,7 +2105,7 @@ function get_stack(caller) { var o = output.detach(); output.html(''); $.each(lines, function(i, line) { - draw_line(line && line.constructor === Function ? line() : line); + draw_line(line && typeof line == 'function' ? line() : line); }); self.prepend(o); scroll_to_bottom(); @@ -1869,14 +2113,17 @@ function get_stack(caller) { }, echo: function(line) { lines.push(line); - return draw_line(typeof line === 'function' ? line() : line); + draw_line(typeof line === 'function' ? line() : line); + on_scrollbar_show_resize(); + return self; }, error: function(message) { //echo red message - self.echo('[[;#f00;]' + escape_brackets(message) + ']'); + return self.echo('[[;#f00;]' + escape_brackets(message) + ']'); }, scroll: function(amount) { var pos; + amount = Math.round(amount); if (self.prop) { if (amount > self.prop('scrollTop') && amount > 0) { self.prop('scrollTop', 0); @@ -1905,16 +2152,16 @@ function get_stack(caller) { token: settings.login ? function() { var name = settings.name; return $.Storage.get('token' + (name ? '_' + name : '')); - } : null, + } : $.noop, login_name: settings.login ? function() { var name = settings.name; return $.Storage.get('login' + (name ? '_' + name : '')); - } : null, + } : $.noop, name: function() { return settings.name; }, push: function(_eval, options) { - if (!options.prompt || validate('prompt', options.prompt)) { + if (options && (!options.prompt || validate('prompt', options.prompt)) || !options) { if (typeof _eval === 'string') { _eval = make_json_rpc_eval_fun(options['eval'], self); } @@ -1923,6 +2170,13 @@ function get_stack(caller) { } return self; }, + reset: function() { + self.clear(); + while(interpreters.size() > 1) { + interpreters.pop(); + } + initialize(); + }, pop: function(string) { if (string !== undefined) { echo_command(string); @@ -1931,20 +2185,38 @@ function get_stack(caller) { if (settings.login) { logout(); if (typeof settings.onExit === 'function') { - settings.onExit(self); + try { + settings.onExit(self); + } catch (e) { + display_exception(e, 'onExit'); + throw e; + } } } } else { var current = interpreters.pop(); prepare_top_interpreter(); if (typeof current.onExit === 'function') { - current.onExit(self); + try { + current.onExit(self); + } catch (e) { + display_exception(e, 'onExit'); + throw e; + } } } return self; - } - }); + }, function(_, fun) { + // wrap all functions and display execptions + return function() { + try { + return fun.apply(this, Array.prototype.slice.apply(arguments)); + } catch(e) { + display_exception(e, 'TERMINAL'); + } + }; + })); //function constructor for eval function make_json_rpc_eval_fun(url, terminal) { @@ -2007,6 +2279,7 @@ function get_stack(caller) { //display prompt and last command function echo_command(command) { + command = command.replace(/\[/g, '[').replace(/\]/g, ']'); var prompt = command_line.prompt(); if (command_line.mask()) { command = command.replace(/./g, '*'); @@ -2022,24 +2295,27 @@ function get_stack(caller) { // wrapper over eval it implements exit and catch all exeptions // from user code and display them on terminal - function commands(command) { + function commands(command, silent) { try { var interpreter = interpreters.top(); - if (command === 'exit' && settings.exit) { if (interpreters.size() === 1) { if (settings.login) { logout(); } else { var msg = 'You can exit from main interpeter'; - echo_command(command); + if (!silent) { + echo_command(command); + } self.echo(msg); } } else { self.pop('exit'); } } else { - echo_command(command); + if (!silent) { + echo_command(command); + } if (command === 'clear' && settings.clear) { self.clear(); } else { @@ -2110,6 +2386,16 @@ function get_stack(caller) { //this function is call only when options.login function is defined //check for this is in self.pop method function logout() { + if (typeof settings.onBeforelogout === 'function') { + try { + if (settings.onBeforelogout(self) == false) { + return; + } + } catch (e) { + display_exception(e, 'onBeforelogout'); + throw e; + } + } var name = settings.name; name = (name ? '_' + name : ''); $.Storage.remove('token' + name, null); @@ -2118,6 +2404,14 @@ function get_stack(caller) { command_line.history().disable(); } login(); + if (typeof settings.onAfterlogout === 'function') { + try { + settings.onAfterlogout(self); + } catch (e) { + display_exception(e, 'onAfterlogout'); + throw e; + } + } } //function enable history, set prompt, run eval function @@ -2130,7 +2424,7 @@ function get_stack(caller) { } name += terminal_id; command_line.name(name); - if (interpreter.prompt.constructor === Function) { + if (typeof interpreter.prompt == 'function') { command_line.prompt(function(command) { interpreter.prompt(command, self); }); @@ -2149,26 +2443,43 @@ function get_stack(caller) { prepare_top_interpreter(); show_greetings(); if (typeof settings.onInit === 'function') { - settings.onInit(self); + try { + settings.onInit(self); + } catch (e) { + display_exception(e, 'OnInit'); + throw e; + } } } - var tab_count = 0; - var scrollBars = haveScrollbars(); - function key_down(e) { - var i; - // after text pasted into textarea in cmd plugin - self.oneTime(5, function() { + + // --------------------------------------------------------------------- + var on_scrollbar_show_resize = (function() { + var scrollBars = haveScrollbars(); + return function() { if (scrollBars !== haveScrollbars()) { // if scollbars appearance change we will have different // number of chars self.resize(); scrollBars = haveScrollbars(); } + }; + })(); + // --------------------------------------------------------------------- + // KEYDOWN EVENT HANDLER + // --------------------------------------------------------------------- + var tab_count = 0; + + function key_down(e) { + var i; + // after text pasted into textarea in cmd plugin + self.oneTime(5, function() { + on_scrollbar_show_resize(); }); + if (settings.keydown && settings.keydown(e, self) === false) { + return false; + } if (!self.paused()) { - if (settings.keydown && settings.keydown(e, self) === false) { - return false; - } + if (e.which !== 9) { // not a TAB tab_count = 0; } @@ -2228,34 +2539,38 @@ function get_stack(caller) { self.attr({scrollTop: self.attr('scrollHeight')}); } } else { - // cancel ajax requests - if (settings.cancelableAjax) { - if (e.which === 68 && e.ctrlKey) { // CTRL+D - for (i=requests.length; i--;) { - var r = requests[i]; - if (4 !== r.readyState) { - try { - r.abort(); - } catch(e) { - self.error('error in aborting ajax'); - } + if (e.which === 68 && e.ctrlKey) { // CTRL+D + for (i=requests.length; i--;) { + var r = requests[i]; + if (4 !== r.readyState) { + try { + r.abort(); + } catch(e) { + self.error('error in aborting ajax'); } } - self.resume(); - return false; } + self.resume(); + return false; } } } + // --------------------------------------------------------------------- // INIT CODE + // --------------------------------------------------------------------- var url; if (settings.login && typeof settings.onBeforeLogin === 'function') { - settings.onBeforeLogin(self); + try { + settings.onBeforeLogin(self); + } catch (e) { + display_exception(e, 'onBeforeLogin'); + throw e; + } } - if (init_eval.constructor === String) { + if (typeof init_eval == 'string') { url = init_eval; //url variable is use when making login function init_eval = make_json_rpc_eval_fun(init_eval, self); - } else if (init_eval.constructor === Array) { + } else if (typeof init_eval == 'object' && init_eval.constructor === Array) { throw "You can't use array as eval"; } else if (typeof init_eval === 'object') { // top commands @@ -2348,7 +2663,12 @@ function get_stack(caller) { } : null, onCommandChange: function(command) { if (typeof settings.onCommandChange === 'function') { - settings.onCommandChange(command, self); + try { + settings.onCommandChange(command, self); + } catch (e) { + display_exception(e, 'onCommandChange'); + throw e; + } } scroll_to_bottom(); }, @@ -2357,16 +2677,17 @@ function get_stack(caller) { //num_chars = get_num_chars(); terminals.append(self); if (settings.enabled === true) { - self.focus(); + self.focus(undefined, true); } else { self.disable(); } - $(window).resize(self.resize); self.click(function() { - self.focus(); + if (!(pause && terminals.length() > 1 && self === $.terminal.active())) { + self.focus(); + } }); - if (self.token && !self.token() && self.login_name && + if (options.login && self.token && !self.token() && self.login_name && !self.login_name()) { login(); } else { @@ -2388,5 +2709,4 @@ function get_stack(caller) { return self; }; //terminal plugin - })(jQuery); \ No newline at end of file From 7f11fb99ef867703b9b9f2f7b26a1f8c5b64ac2c Mon Sep 17 00:00:00 2001 From: coderaiser Date: Tue, 25 Dec 2012 08:28:05 -0500 Subject: [PATCH 056/347] removed unneeded styles --- lib/client/terminal.js | 2 +- lib/client/terminal/jquery-terminal/jquery.terminal.css | 8 +++++--- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/lib/client/terminal.js b/lib/client/terminal.js index 536763a4..49d8fdd0 100644 --- a/lib/client/terminal.js +++ b/lib/client/terminal.js @@ -71,7 +71,7 @@ var CloudCommander, Util, DOM, $; * functin show jquery-terminal */ JqueryTerminal.show = function(){ - DOM.Images.hideLoad(); + DOM.Images.hideLoad(); /* only if panel was hided */ if( DOM.hidePanel() ){ Hidden = false; diff --git a/lib/client/terminal/jquery-terminal/jquery.terminal.css b/lib/client/terminal/jquery-terminal/jquery.terminal.css index 03f6bfa2..8eb2cee7 100644 --- a/lib/client/terminal/jquery-terminal/jquery.terminal.css +++ b/lib/client/terminal/jquery-terminal/jquery.terminal.css @@ -22,15 +22,17 @@ } .terminal .terminal-output div div, .terminal .prompt { display: block; - line-height: 9px; + /* line-height: 9px; */ height: 14px; } .terminal { font-family: FreeMono, monospace; color: #aaa; background-color: #000; - font-size: 12px; - line-height: 14px; + /* font-size: 12px; */ + /* line-height: 14px; */ + line-height: 16px; + /* removing breaking the lines */ } .terminal .terminal-output div span { display: inline-block; From 5758822fe023c835e747e7585f9322ff65ea8f42 Mon Sep 17 00:00:00 2001 From: coderaiser Date: Tue, 25 Dec 2012 09:19:28 -0500 Subject: [PATCH 057/347] added Cakefile --- lib/client/storage/dropbox/Cakefile | 131 ++++++++++++++++++++++++++++ 1 file changed, 131 insertions(+) create mode 100644 lib/client/storage/dropbox/Cakefile diff --git a/lib/client/storage/dropbox/Cakefile b/lib/client/storage/dropbox/Cakefile new file mode 100644 index 00000000..4a530770 --- /dev/null +++ b/lib/client/storage/dropbox/Cakefile @@ -0,0 +1,131 @@ +async = require 'async' +{spawn, exec} = require 'child_process' +fs = require 'fs' +log = console.log +remove = require 'remove' + +# Node 0.6 compatibility hack. +unless fs.existsSync + path = require 'path' + fs.existsSync = (filePath) -> path.existsSync filePath + + +task 'build', -> + build() + +task 'test', -> + vendor -> + build -> + ssl_cert -> + tokens -> + run 'node_modules/mocha/bin/mocha --colors --slow 200 ' + + '--timeout 10000 --require test/js/helper.js test/js/*test.js' + +task 'webtest', -> + vendor -> + build -> + ssl_cert -> + tokens -> + webFileServer = require './test/js/web_file_server.js' + webFileServer.openBrowser() + +task 'cert', -> + remove.removeSync 'test/ssl', ignoreMissing: true + ssl_cert() + +task 'vendor', -> + remove.removeSync './test/vendor', ignoreMissing: true + vendor() + +task 'tokens', -> + remove.removeSync './test/.token', ignoreMissing: true + build -> + tokens -> + process.exit 0 + +task 'doc', -> + run 'node_modules/codo/bin/codo src' + +task 'extension', -> + run 'node_modules/coffee-script/bin/coffee ' + + '--compile test/chrome_extension/*.coffee' + +build = (callback) -> + commands = [] + # Compile without --join for decent error messages. + commands.push 'node_modules/coffee-script/bin/coffee --output tmp ' + + '--compile src/*.coffee' + commands.push 'node_modules/coffee-script/bin/coffee --output lib ' + + '--compile --join dropbox.js src/*.coffee' + # Minify the javascript, for browser distribution. + commands.push 'node_modules/uglify-js/bin/uglifyjs --compress --mangle ' + + '--output lib/dropbox.min.js lib/dropbox.js' + commands.push 'node_modules/coffee-script/bin/coffee --output test/js ' + + '--compile test/src/*.coffee' + async.forEachSeries commands, run, -> + callback() if callback + +ssl_cert = (callback) -> + fs.mkdirSync 'test/ssl' unless fs.existsSync 'test/ssl' + if fs.existsSync 'test/ssl/cert.pem' + callback() if callback? + return + + run 'openssl req -new -x509 -days 365 -nodes -batch ' + + '-out test/ssl/cert.pem -keyout test/ssl/cert.pem ' + + '-subj /O=dropbox.js/OU=Testing/CN=localhost ', callback + +vendor = (callback) -> + # All the files will be dumped here. + fs.mkdirSync 'test/vendor' unless fs.existsSync 'test/vendor' + + # Embed the binary test image into a 7-bit ASCII JavaScript. + bytes = fs.readFileSync 'test/binary/dropbox.png' + fragments = [] + for i in [0...bytes.length] + fragment = bytes.readUInt8(i).toString 16 + while fragment.length < 4 + fragment = '0' + fragment + fragments.push "\\u#{fragment}" + js = "window.testImageBytes = \"#{fragments.join('')}\";" + fs.writeFileSync 'test/vendor/favicon.js', js + + downloads = [ + # chai.js ships different builds for browsers vs node.js + ['http://chaijs.com/chai.js', 'test/vendor/chai.js'], + # sinon.js also ships special builds for browsers + ['http://sinonjs.org/releases/sinon.js', 'test/vendor/sinon.js'], + # ... and sinon.js ships an IE-only module + ['http://sinonjs.org/releases/sinon-ie.js', 'test/vendor/sinon-ie.js'] + ] + async.forEachSeries downloads, download, -> + callback() if callback + +tokens = (callback) -> + TokenStash = require './test/js/token_stash.js' + tokenStash = new TokenStash + (new TokenStash()).get -> + callback() if callback? + +run = (args...) -> + for a in args + switch typeof a + when 'string' then command = a + when 'object' + if a instanceof Array then params = a + else options = a + when 'function' then callback = a + + command += ' ' + params.join ' ' if params? + cmd = spawn '/bin/sh', ['-c', command], options + cmd.stdout.on 'data', (data) -> process.stdout.write data + cmd.stderr.on 'data', (data) -> process.stderr.write data + process.on 'SIGHUP', -> cmd.kill() + cmd.on 'exit', (code) -> callback() if callback? and code is 0 + +download = ([url, file], callback) -> + if fs.existsSync file + callback() if callback? + return + + run "curl -o #{file} #{url}", callback \ No newline at end of file From 6821f95f26cedc71bec75727bf7e9f78c9235888 Mon Sep 17 00:00:00 2001 From: coderaiser Date: Tue, 25 Dec 2012 09:22:12 -0500 Subject: [PATCH 058/347] minor changes --- .gitignore | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/.gitignore b/.gitignore index b4ad56f5..665a9e77 100644 --- a/.gitignore +++ b/.gitignore @@ -10,12 +10,6 @@ min #node modules folder node_modules -#moving out minify from cloudcmd submodules -#minify module -#!node_modules/ -#node_modules/* -#!node_modules/minify - #temporary files folder tmp @@ -28,5 +22,5 @@ apache-status #log file log.txt -#hashes file -hashes.json \ No newline at end of file +#cloudfoundry file +manifest.yml \ No newline at end of file From 3d46fae3acca0ec0e9b05397290aeb2c174a85cf Mon Sep 17 00:00:00 2001 From: coderaiser Date: Tue, 25 Dec 2012 10:02:26 -0500 Subject: [PATCH 059/347] refactored --- lib/client/viewer.js | 358 +++++++++++++++++-------------------------- 1 file changed, 144 insertions(+), 214 deletions(-) diff --git a/lib/client/viewer.js b/lib/client/viewer.js index 6a476d8b..c14fcf30 100644 --- a/lib/client/viewer.js +++ b/lib/client/viewer.js @@ -1,215 +1,145 @@ -var CloudCommander, Util, DOM, CloudFunc, $; -/* object contains viewer FancyBox - * https://github.com/fancyapps/fancyBox - */ -(function(){ - "use strict"; - - var cloudcmd = CloudCommander, - KeyBinding = CloudCommander.KeyBinding, - FancyBox = {}; - - cloudcmd.Viewer = { - get: (function(){ - return this.FancyBox; - }) - }; - - /* PRIVATE FUNCTIONS */ - - function set(){ - if(DOM.getByClass('fancybox').length) - return; - - /* get all file links */ - var lA = DOM.getByTag('a', DOM.getPanel(false) ), - lActiveA = DOM.getByTag('a', DOM.getPanel(true) ), - - lDblClick_f = function(pA){ - return function(){ - var lConfig = getConfig(); - lConfig.href = pA.href; - if(pA.rel) - $.fancybox(lConfig); - else - FancyBox.loadData(pA, FancyBox.onDataLoaded); - }; - }, - - lSetOnclick = function(pA){ - /* first two is not files nor folders*/ - for (var i = 2, n = pA.length; i < n; i++) { - - var lA = pA[i], - lName = lA.title || lA.textContent; - - lA.className = 'fancybox'; - if(Util.checkExtension(lName, ['png','jpg', 'gif','ico'])) - lA.rel = 'gallery'; - - lA.ondblclick = lDblClick_f(lA); - } - }; - - lSetOnclick(lA); - lSetOnclick(lActiveA); - } - - /** - * function return configureation for FancyBox open and - * onclick (it shoud be different objects) - */ - function getConfig(){ - return{ - /* function do her work - * before FauncyBox shows - */ - beforeShow : Util.retFunc( KeyBinding.unSet ), - - afterShow : function(){ - var lEditor = DOM.getById('CloudViewer'); - if(lEditor) - lEditor.focus(); - }, - - beforeClose : Util.retFunc( KeyBinding.set ), - - openEffect : 'none', - closeEffect : 'none', - autoSize : false, - height : window.innerHeight, - helpers : { - overlay : { - css : { - 'background' : 'rgba(255, 255, 255, 0.1)' - } - } - }, - padding : 0 - }; - } - - /** - * function loads css and js of FancyBox - * @pParent - this - * @pCallBack - executes, when everything loaded - */ - FancyBox.load = function(pCallBack){ - console.time('fancybox load'); - var lDir = cloudcmd.LIBDIRCLIENT + 'viewer/fancybox/', - lFiles = [ lDir + 'jquery.fancybox.css', - lDir + 'jquery.fancybox.js' ]; - - - DOM.anyLoadInParallel(lFiles, function(){ - console.timeEnd('fancybox load'); - pCallBack(); - }) - .cssSet({id:'viewer', - inner : '#CloudViewer{' + - 'font-size: 16px;' + - 'white-space :pre' + - '}' + - '#CloudViewer::selection{' + - /* - 'background: #fe57a1;' - 'color: #fff;' - */ - 'background: #b3d4fc;' + - 'text-shadow: none;' + - '}' - }); - - }; - - /** - * function loads data an put it to pSuccess_f - * @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, pCallBack){ - var lLink = pA.href; - - /* убираем адрес хоста */ - lLink = lLink.replace(cloudcmd.HOST, ''); - - if (lLink.indexOf(CloudFunc.NOJS) === CloudFunc.FS.length) - lLink = Util.removeStr(lLink, CloudFunc.NOJS); - - DOM.getCurrentFileContent({ - url : lLink, - error : function(){ - FancyBox.loading = false; - }, - success : Util.retExec(pCallBack) - }); - }; - - FancyBox.onDataLoaded = function(pData){ - var lConfig = getConfig(); - - /* if we got json - show it */ - if( Util.isObject(pData) ) - pData = JSON.stringify(pData, null, 4); - - $.fancybox('
    ' + pData + '
    ', lConfig); - - DOM.Images.hideLoad(); - }; - - /** - * function shows FancyBox - */ - FancyBox.show = function(pCallBack){ - set(); - - var lConfig = getConfig(), - lResult = Util.exec(pCallBack); - - if(!lResult){ - var lCurrentFile = DOM.getCurrentFile(), - lA = DOM.getByClass('fancybox', lCurrentFile)[0]; - - if(lA){ - if(lA.rel) - $.fancybox.open({ href : lA.href }, - lConfig); - else - FancyBox.loadData(lA, FancyBox.onDataLoaded); - } - } - }; - - cloudcmd.Viewer.init = function(){ - DOM.jqueryLoad( Util.retLoadOnLoad([ - FancyBox.show, - FancyBox.load - ])); - - var lView = function(){ - DOM.Images.showLoad(); - FancyBox.show( DOM.getCurrentFile() ); - }; - - var lKeyListener = function(pEvent){ - var lF3 = cloudcmd.KEY.F3, - lKeyBinded = KeyBinding.get(), - lKey = pEvent.keyCode, - lShift = pEvent.shiftKey; - - /* если клавиши можно обрабатывать */ - if( lKeyBinded && lKey === lF3 && lShift ){ - lView(); - pEvent.preventDefault(); - } - }; - - /* добавляем обработчик клавишь */ - DOM.addKeyListener(lKeyListener) - .setButtonKey('f3', lView); - }; - - cloudcmd.Viewer.FancyBox = FancyBox; +var CloudCommander, Util, DOM, CloudFunc, $; +/* object contains viewer FancyBox + * https://github.com/fancyapps/fancyBox + */ +(function(){ + "use strict"; + + var cloudcmd = CloudCommander, + KeyBinding = CloudCommander.KeyBinding, + FancyBox = {}; + + cloudcmd.Viewer = { + get: (function(){ + return this.FancyBox; + }) + }; + + /* PRIVATE FUNCTIONS */ + + + /** + * function return configureation for FancyBox open and + * onclick (it shoud be different objects) + */ + function getConfig(){ + return{ + /* function do her work + * before FauncyBox shows + */ + beforeShow : function(){ + DOM.Images.hideLoad(); + KeyBinding.unSet(); + }, + + afterShow : function(){ + var lEditor = DOM.getById('CloudViewer'); + if(lEditor) + lEditor.focus(); + }, + + beforeClose : Util.retFunc( KeyBinding.set ), + + openEffect : 'none', + closeEffect : 'none', + autoSize : false, + height : window.innerHeight, + helpers : { + overlay : { + css : { + 'background' : 'rgba(255, 255, 255, 0.1)' + } + } + }, + padding : 0 + }; + } + + /** + * function loads css and js of FancyBox + * @pParent - this + * @pCallBack - executes, when everything loaded + */ + FancyBox.load = function(pCallBack){ + console.time('fancybox load'); + var lDir = cloudcmd.LIBDIRCLIENT + 'viewer/fancybox/', + lFiles = [ lDir + 'jquery.fancybox.css', + lDir + 'jquery.fancybox.js' ]; + + + DOM.anyLoadInParallel(lFiles, function(){ + console.timeEnd('fancybox load'); + pCallBack(); + }) + .cssSet({id:'viewer', + inner : '#CloudViewer{' + + 'font-size: 16px;' + + 'white-space :pre' + + '}' + + '#CloudViewer::selection{' + + /* + 'background: #fe57a1;' + 'color: #fff;' + */ + 'background: #b3d4fc;' + + 'text-shadow: none;' + + '}' + }); + + }; + + + /** + * function shows FancyBox + */ + FancyBox.show = function(){ + var lConfig = getConfig(), + lPath = DOM.getCurrentPath(); + + if( Util.checkExtension(lPath, ['png','jpg', 'gif','ico']) ) + $.fancybox.open({ href : lPath }, lConfig); + else{ + + DOM.getCurrentFileContent({ + success : function(pData){ + if( Util.isObject(pData) ) + pData = JSON.stringify(pData, null, 4); + $.fancybox('
    ' + pData + '
    ', lConfig); + } + }); + } + }; + + cloudcmd.Viewer.init = function(){ + DOM.jqueryLoad( + Util.loadOnLoad([ + FancyBox.show, + FancyBox.load + ]) + ); + + var lView = function(){ + DOM.Images.showLoad(); + FancyBox.show( DOM.getCurrentFile() ); + }; + + var lKeyListener = function(pEvent){ + var lF3 = cloudcmd.KEY.F3, + lKeyBinded = KeyBinding.get(), + lKey = pEvent.keyCode, + lShift = pEvent.shiftKey; + + /* если клавиши можно обрабатывать */ + if( lKeyBinded && lKey === lF3 && lShift ){ + lView(); + pEvent.preventDefault(); + } + }; + + /* добавляем обработчик клавишь */ + DOM.addKeyListener(lKeyListener) + .setButtonKey('f3', lView); + }; + + cloudcmd.Viewer.FancyBox = FancyBox; })(); \ No newline at end of file From 88bac098f00c25f55cce1fb51f86800f7ef4693f Mon Sep 17 00:00:00 2001 From: coderaiser Date: Tue, 25 Dec 2012 10:40:46 -0500 Subject: [PATCH 060/347] removed loading spinner --- ChangeLog | 8 ++++++++ lib/client/viewer.js | 11 ++++------- lib/client/viewer/fancybox/jquery.fancybox.css | 4 ++-- 3 files changed, 14 insertions(+), 9 deletions(-) diff --git a/ChangeLog b/ChangeLog index e15e6c98..e3b5b10e 100644 --- a/ChangeLog +++ b/ChangeLog @@ -51,6 +51,14 @@ keyStop: function(e, opt) { * Updated jquery-terminal to v0.4.22. +* Refactore viewer module. + +* Removed loading spinner by commenting jquery.fancybox.css block +#fancybox-loading div { + width: 44px; + height: 44px; + background: url('fancybox_loading.gif') center center no-repeat; +} 2012.12.12, Version 0.1.8 diff --git a/lib/client/viewer.js b/lib/client/viewer.js index c14fcf30..4d1cac91 100644 --- a/lib/client/viewer.js +++ b/lib/client/viewer.js @@ -24,9 +24,7 @@ var CloudCommander, Util, DOM, CloudFunc, $; */ function getConfig(){ return{ - /* function do her work - * before FauncyBox shows - */ + afterLoad : $.fancybox.hideLoading, beforeShow : function(){ DOM.Images.hideLoad(); KeyBinding.unSet(); @@ -65,11 +63,10 @@ var CloudCommander, Util, DOM, CloudFunc, $; var lDir = cloudcmd.LIBDIRCLIENT + 'viewer/fancybox/', lFiles = [ lDir + 'jquery.fancybox.css', lDir + 'jquery.fancybox.js' ]; - - - DOM.anyLoadInParallel(lFiles, function(){ + + DOM.anyLoadOnLoad(lFiles, function(){ console.timeEnd('fancybox load'); - pCallBack(); + Util.exec( pCallBack ); }) .cssSet({id:'viewer', inner : '#CloudViewer{' + diff --git a/lib/client/viewer/fancybox/jquery.fancybox.css b/lib/client/viewer/fancybox/jquery.fancybox.css index d6ff8a17..2731d45b 100644 --- a/lib/client/viewer/fancybox/jquery.fancybox.css +++ b/lib/client/viewer/fancybox/jquery.fancybox.css @@ -90,13 +90,13 @@ cursor: pointer; z-index: 8060; } - +/* #fancybox-loading div { width: 44px; height: 44px; background: url('fancybox_loading.gif') center center no-repeat; } - +*/ .fancybox-close { position: absolute; top: -18px; From 143253adba8af881d3716a6413162d82b91783fd Mon Sep 17 00:00:00 2001 From: coderaiser Date: Tue, 25 Dec 2012 11:18:37 -0500 Subject: [PATCH 061/347] minor changes --- lib/client/viewer.js | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/lib/client/viewer.js b/lib/client/viewer.js index 4d1cac91..6a965a2a 100644 --- a/lib/client/viewer.js +++ b/lib/client/viewer.js @@ -23,8 +23,7 @@ var CloudCommander, Util, DOM, CloudFunc, $; * onclick (it shoud be different objects) */ function getConfig(){ - return{ - afterLoad : $.fancybox.hideLoading, + return{ beforeShow : function(){ DOM.Images.hideLoad(); KeyBinding.unSet(); From d7c4a0293e36e7d1570a64b4e0bcc4fe5a406970 Mon Sep 17 00:00:00 2001 From: coderaiser Date: Wed, 26 Dec 2012 07:42:09 -0500 Subject: [PATCH 062/347] updated CodeMirror to v2.37.01 --- ChangeLog | 3 + lib/client/editor/codemirror/codemirror.css | 350 +- lib/client/editor/codemirror/codemirror.js | 6355 +++++++++-------- .../editor/codemirror/mode/javascript.js | 828 +-- lib/client/editor/codemirror/mode/xml.js | 640 +- lib/client/editor/codemirror/package.json | 42 +- lib/client/editor/codemirror/theme/night.css | 42 +- lib/client/terminal.js | 2 +- 8 files changed, 4153 insertions(+), 4109 deletions(-) diff --git a/ChangeLog b/ChangeLog index e3b5b10e..32682fce 100644 --- a/ChangeLog +++ b/ChangeLog @@ -60,6 +60,9 @@ keyStop: function(e, opt) { background: url('fancybox_loading.gif') center center no-repeat; } +* Updated CodeMirror to v2.37.01. + + 2012.12.12, Version 0.1.8 * Added ability to shutdown Cloud Commander diff --git a/lib/client/editor/codemirror/codemirror.css b/lib/client/editor/codemirror/codemirror.css index 41b8d09e..59af303b 100644 --- a/lib/client/editor/codemirror/codemirror.css +++ b/lib/client/editor/codemirror/codemirror.css @@ -1,174 +1,176 @@ -.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; - } - -} +.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-negative {color: #d44;} +span.cm-positive {color: #292;} + +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; + } + +} \ No newline at end of file diff --git a/lib/client/editor/codemirror/codemirror.js b/lib/client/editor/codemirror/codemirror.js index e49481b5..800e2485 100644 --- a/lib/client/editor/codemirror/codemirror.js +++ b/lib/client/editor/codemirror/codemirror.js @@ -1,3165 +1,3190 @@ -// 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; -})(); +// 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; + else if (option == "showCursorWhenSelecting") updateSelection(); + 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); + }, + defaultTextHeight: function() { return textHeight(); }, + 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 == "head") start = sel.inverted; + if (start == "anchor") start = !sel.inverted; + if (start == "end") start = false; + 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}; + }, + scrollIntoView: function(pos) { + var coords = localCoords(pos ? clipPos(pos) : sel.inverted ? sel.from : sel.to); + scrollIntoView(coords.x, coords.y, coords.x, coords.yBot); + }, + + 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 (Math.abs(scrollbar.scrollTop - lastScrollTop) > 1) { + 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 (Math.abs(scroller.scrollTop - lastScrollTop) > 1) { + 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(clipPos(start), cur); + return; + } + startstart = clipPos(startstart); + startend = clipPos(startend); + 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; + 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_lt9) { 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" && (ie_lt9 || document.activeElement != input)) + 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 || options.showCursorWhenSelecting) { + cursor.style.top = headPos.y + "px"; + cursor.style.left = (options.lineWrapping ? Math.min(headPos.x, lineSpace.offsetWidth) : headPos.x) + "px"; + cursor.style.display = ""; + } else { + cursor.style.display = "none"; + } + if (!collapsed) { + 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 - 1) + "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); + selectionDiv.style.display = ""; + } else { + selectionDiv.style.display = "none"; + } + } + + 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, + showCursorWhenSelecting: false, + 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)) continue; + if (modeObj.hasOwnProperty(prop)) modeObj["_" + prop] = modeObj[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": "goPageDown", "Shift-Ctrl-V": "goPageUp", "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); + var form = textarea.form, realSubmit = form.submit; + textarea.form.submit = function wrappedSubmit() { + save(); + form.submit = realSubmit; + form.submit(); + 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/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 qtwebkit = webkit && /Qt\/\d+\.\d+/.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 opera_version = opera && navigator.userAgent.match(/Version\/(\d*\.\d*)/); + if (opera_version) opera_version = Number(opera_version[1]); + // Some browsers use the wrong event properties to signal cmd/ctrl on OS X + var flipCtrlCmd = mac && (qtwebkit || opera && (opera_version == null || opera_version < 12.11)); + + // 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; ; ++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; + } + var nonASCIISingleCaseWordChar = /[\u3040-\u309f\u30a0-\u30ff\u3400-\u4db5\u4e00-\u9fcc]/; + function isWordChar(ch) { + return /\w/.test(ch) || ch > "\x80" && + (ch.toUpperCase() != ch.toLowerCase() || nonASCIISingleCaseWordChar.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.37 +"; + + return CodeMirror; +})(); \ No newline at end of file diff --git a/lib/client/editor/codemirror/mode/javascript.js b/lib/client/editor/codemirror/mode/javascript.js index 37f6f873..2526b276 100644 --- a/lib/client/editor/codemirror/mode/javascript.js +++ b/lib/client/editor/codemirror/mode/javascript.js @@ -1,409 +1,419 @@ -// 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 }); +// 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) { + function inList(list) { + for (var v = list; v; v = v.next) + if (v.name == varname) return true; + return false; + } + var state = cx.state; + if (state.context) { + cx.marked = "def"; + if (inList(state.localVars)) return; + state.localVars = {name: varname, next: state.localVars}; + } else { + if (inList(state.globalVars)) return; + state.globalVars = {name: varname, next: state.globalVars}; + } + } + + // 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, + globalVars: parserConfig.globalVars, + 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: ":{}", + + jsonMode: jsonMode + }; +}); + +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 }); \ No newline at end of file diff --git a/lib/client/editor/codemirror/mode/xml.js b/lib/client/editor/codemirror/mode/xml.js index 860e368f..d761fcc9 100644 --- a/lib/client/editor/codemirror/mode/xml.js +++ b/lib/client/editor/codemirror/mode/xml.js @@ -1,318 +1,322 @@ -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 && /")); + 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 { + var isClose = stream.eat("/"); + tagName = ""; + var c; + while ((c = stream.eat(/[^\s\u00a0=<>\"\'\/?]/))) tagName += c; + if (!tagName) return "error"; + type = isClose ? "closeTag" : "openTag"; + 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"; + else if (type == "word") setStyle = "attribute"; + 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 && / Date: Wed, 26 Dec 2012 09:42:12 -0500 Subject: [PATCH 063/347] refactored --- lib/client/dom.js | 38 ++++++++++++++++++++++++++++++- lib/client/editor/_codemirror.js | 39 ++++++++++++-------------------- lib/client/menu.js | 35 ++++++++-------------------- lib/client/terminal.js | 15 ++++++------ lib/client/viewer.js | 29 +++++++++++------------- lib/util.js | 10 ++++++-- 6 files changed, 90 insertions(+), 76 deletions(-) diff --git a/lib/client/dom.js b/lib/client/dom.js index ce665c6a..c08515dd 100644 --- a/lib/client/dom.js +++ b/lib/client/dom.js @@ -631,7 +631,7 @@ var CloudCommander, Util, DOM, CloudFunc; * @param pCallBack */ DOM.socketLoad = function(pCallBack){ - DOM.jsload('/lib/client/socket.js', pCallBack); + DOM.jsload('/lib/client/socket.js', Util.retExec(pCallBack) ); }; /* DOM */ @@ -826,6 +826,42 @@ var CloudCommander, Util, DOM, CloudFunc; return lRet; }; + + /** + * unified way to get current file content + * + * @pCallBack - function({data, name}){} + * @pCurrentFile + */ + DOM.getCurrentData = function(pCallBack, pCurrentFile){ + var lParams, + lFunc = function(pData){ + var lName = DOM.getCurrentName(); + if( Util.isObject(pData) ){ + pData = JSON.stringify(pData, null, 4); + + var lExt = '.json'; + if( !Util.checkExtension(lName, lExt) ) + lName += lExt; + } + + Util.exec(pCallBack, { + data: pData, + name: lName + }); + }; + + if( !Util.isObject(pCallBack) ) + lParams = lFunc; + else + lParams = { + success : lFunc, + error : pCallBack.error + }; + + + return DOM.getCurrentFileContent(lParams, pCurrentFile); + }; /** * unified way to get RefreshButton diff --git a/lib/client/editor/_codemirror.js b/lib/client/editor/_codemirror.js index b8c82fad..6249c080 100644 --- a/lib/client/editor/_codemirror.js +++ b/lib/client/editor/_codemirror.js @@ -50,9 +50,9 @@ var CloudCommander, Util, DOM, CodeMirror; * function initialize CodeMirror * @param {value, callback} */ - function initCodeMirror(pData){ - if(!pData) - pData = {}; + function initCodeMirror(pParams){ + if(!pParams) + pParams = {}; if(!FM) FM = DOM.getFM(); @@ -68,7 +68,7 @@ var CloudCommander, Util, DOM, CodeMirror; CodeMirrorEditor.CodeMirror = new CodeMirror(CodeMirrorElement,{ mode : 'javascript', - value : pData.data, + value : pParams.data.data, theme : 'night', lineNumbers : true, //переносим длинные строки @@ -77,7 +77,7 @@ var CloudCommander, Util, DOM, CodeMirror; extraKeys: { //Сохранение 'Esc': function(){ - Util.exec(pData.callback); + Util.exec(pParams); DOM.remove(lCSS, document.head); } }, @@ -128,33 +128,22 @@ var CloudCommander, Util, DOM, CodeMirror; ReadOnly = true; Loading = true; - setTimeout(function(){ - Loading = false; - }, - 400); - + + var lFalseLoading = function(){ Loading = false; }; + + setTimeout(lFalseLoading, 400); /* reading data from current file */ - DOM.getCurrentFileContent({ - error : function(){ - Loading = false; - }, - + DOM.getCurrentData({ + error : lFalseLoading, success : function(data){ - /* if we got json - show it */ - if( Util.isObject(data) ) - data = JSON.stringify(data, null, 4); - - var lHided = DOM.hidePanel(); - if(lHided){ + if( DOM.hidePanel() ){ Util.exec(pCallBack, data); /* removing keyBinding if set */ KeyBinding.unSet(); - } - + } DOM.Images.hideLoad(); - - Loading = false; + lFalseLoading(); } }); } diff --git a/lib/client/menu.js b/lib/client/menu.js index 8b466bf6..2eb01458 100644 --- a/lib/client/menu.js +++ b/lib/client/menu.js @@ -34,24 +34,6 @@ var CloudCommander, Util, DOM, $; } } - function getCurrentData(pCallBack){ - return DOM.getCurrentFileContent(function(pData){ - var lName = DOM.getCurrentName(); - if( Util.isObject(pData) ){ - pData = JSON.stringify(pData, null, 4); - - var lExt = '.json'; - if( !Util.checkExtension(lName, lExt) ) - lName += lExt; - } - - Util.exec(pCallBack, { - data: pData, - name: lName - }); - }); - } - /** * function get menu item object for Upload To */ @@ -69,7 +51,7 @@ var CloudCommander, Util, DOM, $; lObj.name = pObjectName; lObj.callback = function(key, opt){ - getCurrentData(function(pParams){ + DOM.getCurrentData(function(pParams){ var lObject = cloudcmd[pObjectName]; if('init' in lObject) @@ -277,24 +259,27 @@ var CloudCommander, Util, DOM, $; Menu.init = function(pPosition){ Position = pPosition; - DOM.jqueryLoad( Util.retLoadOnLoad([ + Util.loadOnLoad([ Menu.show, - load - ])); + load, + DOM.jqueryLoad + ]); var key_event = function(pEvent){ + var lKEY = cloudcmd.KEY, + lKeyCode = pEvent.keyCode; /* если клавиши можно обрабатывать */ if( KeyBinding.get() ){ /* if shift + F10 pressed */ - if(pEvent.keyCode === cloudcmd.KEY.F10 && pEvent.shiftKey){ + if(lKeyCode === lKEY.F10 && pEvent.shiftKey){ var lCurrent = DOM.getCurrentFile(); if(lCurrent) - $(lCurrent).contextMenu(); + $(lCurrent).contextMenu(); pEvent.preventDefault(); } } - else if (pEvent.keyCode === cloudcmd.KEY.ESC) + else if (lKeyCode === lKEY.ESC) KeyBinding.set(); }; diff --git a/lib/client/terminal.js b/lib/client/terminal.js index c6fa27af..b8f4be71 100644 --- a/lib/client/terminal.js +++ b/lib/client/terminal.js @@ -29,7 +29,7 @@ var CloudCommander, Util, DOM, $; lDir + 'terminal.css' ]; - DOM.anyLoadInParallel(lFiles, function(){ + DOM.anyLoadOnLoad([lFiles], function(){ console.timeEnd('terminal load'); init(); @@ -48,7 +48,7 @@ var CloudCommander, Util, DOM, $; $(window).unbind('resize'); - Util.exec(pCallBack.callback || pCallBack); + Util.exec(pCallBack); }); } @@ -100,11 +100,12 @@ var CloudCommander, Util, DOM, $; */ cloudcmd.Terminal.init = function(){ /* loading js and css*/ - DOM.jqueryLoad( Util.retLoadOnLoad([ - JqueryTerminal.show, - load, - DOM.socketLoad - ]) ); + Util.loadOnLoad([ + JqueryTerminal.show, + load, + DOM.socketLoad, + DOM.jqueryLoad, + ]); /* добавляем обработчик клавишь */ DOM.addKeyListener(function(pEvent){ diff --git a/lib/client/viewer.js b/lib/client/viewer.js index 6a965a2a..a2db9670 100644 --- a/lib/client/viewer.js +++ b/lib/client/viewer.js @@ -63,7 +63,7 @@ var CloudCommander, Util, DOM, CloudFunc, $; lFiles = [ lDir + 'jquery.fancybox.css', lDir + 'jquery.fancybox.js' ]; - DOM.anyLoadOnLoad(lFiles, function(){ + DOM.anyLoadOnLoad([lFiles], function(){ console.timeEnd('fancybox load'); Util.exec( pCallBack ); }) @@ -94,25 +94,22 @@ var CloudCommander, Util, DOM, CloudFunc, $; if( Util.checkExtension(lPath, ['png','jpg', 'gif','ico']) ) $.fancybox.open({ href : lPath }, lConfig); - else{ - - DOM.getCurrentFileContent({ - success : function(pData){ - if( Util.isObject(pData) ) - pData = JSON.stringify(pData, null, 4); - $.fancybox('
    ' + pData + '
    ', lConfig); - } + else + DOM.getCurrentData(function(pParams){ + var lData = pParams.data; + $.fancybox('
    ' + + lData + + '
    ', + lConfig); }); - } }; cloudcmd.Viewer.init = function(){ - DOM.jqueryLoad( - Util.loadOnLoad([ - FancyBox.show, - FancyBox.load - ]) - ); + Util.loadOnLoad([ + FancyBox.show, + FancyBox.load, + DOM.jqueryLoad + ]); var lView = function(){ DOM.Images.showLoad(); diff --git a/lib/util.js b/lib/util.js index 2defb049..c7c30e60 100644 --- a/lib/util.js +++ b/lib/util.js @@ -398,8 +398,14 @@ var Util, exports; Util.exec = function(pCallBack, pArg){ var lRet = false; - if( Util.isFunction(pCallBack) ) - lRet = pCallBack(pArg); + if(pCallBack){ + if( Util.isFunction(pCallBack) ) + lRet = pCallBack(pArg); + else { + var lCallBack = pCallBack.callback || pCallBack.success; + lRet = Util.exec(lCallBack, pArg); + } + } return lRet; }; From b2463867d73e134b9d03891641507737aadda560 Mon Sep 17 00:00:00 2001 From: coderaiser Date: Wed, 26 Dec 2012 10:48:05 -0500 Subject: [PATCH 064/347] minor changes --- lib/client/editor/_codemirror.js | 16 +++++-------- lib/client/terminal.js | 23 ++++++++++-------- lib/client/viewer.js | 41 +++++++++++--------------------- 3 files changed, 33 insertions(+), 47 deletions(-) diff --git a/lib/client/editor/_codemirror.js b/lib/client/editor/_codemirror.js index 6249c080..c60727f2 100644 --- a/lib/client/editor/_codemirror.js +++ b/lib/client/editor/_codemirror.js @@ -51,9 +51,6 @@ var CloudCommander, Util, DOM, CodeMirror; * @param {value, callback} */ function initCodeMirror(pParams){ - if(!pParams) - pParams = {}; - if(!FM) FM = DOM.getFM(); @@ -68,7 +65,7 @@ var CloudCommander, Util, DOM, CodeMirror; CodeMirrorEditor.CodeMirror = new CodeMirror(CodeMirrorElement,{ mode : 'javascript', - value : pParams.data.data, + value : pParams && pParams.data && pParams.data.data, theme : 'night', lineNumbers : true, //переносим длинные строки @@ -106,7 +103,7 @@ var CloudCommander, Util, DOM, CodeMirror; console.timeEnd('codemirror load'); CodeMirrorLoaded = true; Util.exec(pCallBack); - }); + }); } /** @@ -134,14 +131,13 @@ var CloudCommander, Util, DOM, CodeMirror; setTimeout(lFalseLoading, 400); /* reading data from current file */ DOM.getCurrentData({ - error : lFalseLoading, - success : function(data){ + error : lFalseLoading, + success : function(data){ if( DOM.hidePanel() ){ Util.exec(pCallBack, data); - - /* removing keyBinding if set */ KeyBinding.unSet(); - } + } + DOM.Images.hideLoad(); lFalseLoading(); } diff --git a/lib/client/terminal.js b/lib/client/terminal.js index b8f4be71..ebae0946 100644 --- a/lib/client/terminal.js +++ b/lib/client/terminal.js @@ -89,7 +89,6 @@ var CloudCommander, Util, DOM, $; DOM.hide(TerminalId); DOM.showPanel(); - KeyBinding.set(); Term.pause(); @@ -108,18 +107,22 @@ var CloudCommander, Util, DOM, $; ]); /* добавляем обработчик клавишь */ - DOM.addKeyListener(function(pEvent){ + var lKeyHandler = function(pEvent){ + var lKEY = cloudcmd.KEY, + lKeyCode = pEvent.keyCode, + lKeyBinded = KeyBinding.get(); /* если клавиши можно обрабатывать */ - if(Hidden && KeyBinding.get() && - pEvent.keyCode === cloudcmd.KEY.TRA){ - JqueryTerminal.show(); - pEvent.preventDefault(); - } + if(Hidden && lKeyBinded && lKeyCode === lKEY.TRA){ + JqueryTerminal.show(); + pEvent.preventDefault(); + } - else if(!Hidden && pEvent.keyCode === cloudcmd.KEY.ESC) + else if(!Hidden && lKeyCode === lKEY.ESC) JqueryTerminal.hide(); - }); + }; + + DOM.addKeyListener(lKeyHandler); }; - cloudcmd.Terminal.JqueryTerminal = JqueryTerminal; + cloudcmd.Terminal.JqueryTerminal = JqueryTerminal; })(); diff --git a/lib/client/viewer.js b/lib/client/viewer.js index a2db9670..63e1cdb9 100644 --- a/lib/client/viewer.js +++ b/lib/client/viewer.js @@ -7,23 +7,9 @@ var CloudCommander, Util, DOM, CloudFunc, $; var cloudcmd = CloudCommander, KeyBinding = CloudCommander.KeyBinding, - FancyBox = {}; - - cloudcmd.Viewer = { - get: (function(){ - return this.FancyBox; - }) - }; - - /* PRIVATE FUNCTIONS */ - - - /** - * function return configureation for FancyBox open and - * onclick (it shoud be different objects) - */ - function getConfig(){ - return{ + FancyBox = {}, + + Config = { beforeShow : function(){ DOM.Images.hideLoad(); KeyBinding.unSet(); @@ -31,7 +17,7 @@ var CloudCommander, Util, DOM, CloudFunc, $; afterShow : function(){ var lEditor = DOM.getById('CloudViewer'); - if(lEditor) + if(lEditor) lEditor.focus(); }, @@ -50,7 +36,12 @@ var CloudCommander, Util, DOM, CloudFunc, $; }, padding : 0 }; - } + + cloudcmd.Viewer = { + get: (function(){ + return this.FancyBox; + }) + }; /** * function loads css and js of FancyBox @@ -88,19 +79,15 @@ var CloudCommander, Util, DOM, CloudFunc, $; /** * function shows FancyBox */ - FancyBox.show = function(){ - var lConfig = getConfig(), - lPath = DOM.getCurrentPath(); + FancyBox.show = function(){ + var lPath = DOM.getCurrentPath(); if( Util.checkExtension(lPath, ['png','jpg', 'gif','ico']) ) - $.fancybox.open({ href : lPath }, lConfig); + $.fancybox.open({ href : lPath }, Config); else DOM.getCurrentData(function(pParams){ - var lData = pParams.data; $.fancybox('
    ' + - lData + - '
    ', - lConfig); + pParams.data + '
    ', Config); }); }; From 02f8648c19f4946ac0cc96cd01c261035c20ec90 Mon Sep 17 00:00:00 2001 From: coderaiser Date: Thu, 27 Dec 2012 05:23:08 -0500 Subject: [PATCH 065/347] moving out doit from minify to cloudcmd --- cloudcmd.js | 38 +++++++++++++++- lib/server.js | 13 +++--- lib/server/minify.js | 102 ++++++++++++------------------------------- 3 files changed, 73 insertions(+), 80 deletions(-) diff --git a/cloudcmd.js b/cloudcmd.js index be4384be..2a8f5021 100644 --- a/cloudcmd.js +++ b/cloudcmd.js @@ -26,6 +26,7 @@ Server.start(Config, { appcache : appCacheProcessing, index : indexProcessing, + minimize : minimize, rest : rest, route : route }); @@ -38,7 +39,7 @@ */ function indexProcessing(pData){ var lReplace_s, - lData = pData.data, + lData = pData.data, lAdditional = pData.additional; /* @@ -88,6 +89,41 @@ lAppCache.createManifest(); } + /** + * Функция минимизирует css/js/html + * если установлены параметры минимизации + */ + function minimize(pAllowed){ + var lOptimizeParams = [], + lStyleCSS = DIR + 'css/style.css', + lResetCSS = DIR + 'css/reset.css', + lIndex = DIR + 'html/index.html', + + lMinify = Server.Minify; + + if (pAllowed.js) { + lOptimizeParams.push(LIBDIR + 'client.js'); + } + + if (pAllowed.html) + lOptimizeParams.push(lIndex); + + if (pAllowed.css) { + var lStyles = []; + + lStyles[0] = {}; + lStyles[0][lStyleCSS] = pAllowed.img; + lStyles[1] = {}; + lStyles[1][lResetCSS] = pAllowed.img; + + lOptimizeParams.push(lStyles[0]); + lOptimizeParams.push(lStyles[1]); + } + + if (lOptimizeParams.length) + lMinify.optimize(lOptimizeParams); + } + /** * rest interface * @pConnectionData {request, responce} diff --git a/lib/server.js b/lib/server.js index f14e7c47..a11dc42f 100644 --- a/lib/server.js +++ b/lib/server.js @@ -91,11 +91,13 @@ CloudServer.Socket = main.socket; /* базовая инициализация */ - CloudServer.init = (function(pAppCachProcessing){ + CloudServer.init = function(pAppCachProcessing){ var lConfig = this.Config, lMinify = this.Minify, lCache = this.Cache, - lAppCache = this.AppCache; + lAppCache = this.AppCache, + + lMinifyAllowed = lConfig.minification; /* Переменная в которой храниться кэш*/ lCache.setAllowed(lConfig.cache.allowed); @@ -103,15 +105,15 @@ /* Change default parameters of * js/css/html minification */ - lMinify.setAllowed(lConfig.minification); + lMinify.setAllowed(lMinifyAllowed); /* Если нужно минимизируем скрипты */ - lMinify._allowed = lMinify.doit(); + Util.exec(CloudServer.minimize, lMinifyAllowed); /* создаём файл app cache */ if( lConfig.appcache && lAppCache && lConfig.server ) Util.exec( pAppCachProcessing ); - }); + }; /** @@ -135,6 +137,7 @@ CloudServer.indexProcessing = pProcessing.index; CloudServer.rest = pProcessing.rest; CloudServer.route = pProcessing.route; + CloudServer.minimize = pProcessing.minimize; this.init(pProcessing.appcache); diff --git a/lib/server/minify.js b/lib/server/minify.js index 2db3fc90..9bca2be6 100644 --- a/lib/server/minify.js +++ b/lib/server/minify.js @@ -6,13 +6,15 @@ var main = global.cloudcmd.main, DIR = main.DIR, LIBDIR = main.LIBDIR, - HTMLDIR = main.HTMLDIR; + HTMLDIR = main.HTMLDIR, + + Minify = main.require('minify'); exports.Minify = { /* pathes to directories */ - INDEX : HTMLDIR + 'index.html', + INDEX : HTMLDIR + 'index.html', /* приватный переключатель минимизации */ - _allowed :{ + _allowed : { css : true, js : true, html : true, @@ -34,66 +36,13 @@ */ setAllowed :(function(pAllowed){ if(pAllowed){ - this._allowed = pAllowed; + this._allowed = pAllowed; } }), - /* - * Функция минимизирует css/js/html - * если установлены параметры минимизации - */ + doit :(function(){ - var lMinify = main.require('minify'); - - if(!lMinify){ - this._allowed = {js:false,css:false,html:false}; - - console.log('You coud install minify ' + - 'for better download spead:\n' + - 'npm i minify'); - - return this._allowed; - } - - /* - * temporary changed dir path, - * becouse directory lib is write - * protected by others by default - * so if node process is started - * from other user (root for example - * in nodester) we can not write - * minified versions - */ - this.MinFolder = lMinify.MinFolder; - - var lOptimizeParams = [], - lStyleCSS = DIR + 'css/style.css', - lResetCSS = DIR + 'css/reset.css'; - if (this._allowed.js) { - lOptimizeParams.push(LIBDIR + 'client.js'); - } - - if (this._allowed.html) - lOptimizeParams.push(this.INDEX); - - if (this._allowed.css) { - var lStyles = []; - - lStyles[0] = {}; - lStyles[0][lStyleCSS] = this._allowed.img; - lStyles[1] = {}; - lStyles[1][lResetCSS] = this._allowed.img; - - lOptimizeParams.push(lStyles[0]); - lOptimizeParams.push(lStyles[1]); - } - - if (lOptimizeParams.length) - lMinify.optimize(lOptimizeParams); - - this.Cache = lMinify.Cache; - - return this._allowed; + }), optimize: function(pName, pParams){ @@ -101,21 +50,26 @@ pParams.force = this.force; - if(this._allowed.css || - this._allowed.js || - this._allowed.html){ - var lMinify = main.require('minify'); + if(!this.MinFolder) + this.MinFolder = Minify.MinFolder; + if(!this.Cache) + this.Cache = Minify.Cache; + + if(this._allowed.css || this._allowed.js || this._allowed.html){ + if(Minify) + Minify.optimize(pName, pParams); + else{ + lResult = false; - if(lMinify) - lMinify.optimize(pName, pParams); - else{ - lResult = false; - - this._allowed = {js:false,css:false,html:false}; - console.log('Could not minify ' + - 'without minify module\n' + - 'npm i minify'); - } + this._allowed = { + js : false, + css : false, + html : false}; + + console.log('Could not minify ' + + 'without minify module\n' + + 'npm i minify'); + } } else lResult = false; @@ -124,6 +78,6 @@ /* minification folder name */ MinFolder : '', - Cache : {} + Cache : null }; })(); From 5b4f1d146b42de65b3e297e486a851927191d745 Mon Sep 17 00:00:00 2001 From: coderaiser Date: Thu, 27 Dec 2012 05:46:58 -0500 Subject: [PATCH 066/347] moving out doit from minify to cloudcmd --- cloudcmd.js | 2 +- config.json | 6 +++--- lib/server.js | 6 +++--- lib/server/minify.js | 8 +++----- 4 files changed, 10 insertions(+), 12 deletions(-) diff --git a/cloudcmd.js b/cloudcmd.js index 2a8f5021..7b606d51 100644 --- a/cloudcmd.js +++ b/cloudcmd.js @@ -99,7 +99,7 @@ lResetCSS = DIR + 'css/reset.css', lIndex = DIR + 'html/index.html', - lMinify = Server.Minify; + lMinify = Server.CloudServer.Minify; if (pAllowed.js) { lOptimizeParams.push(LIBDIR + 'client.js'); diff --git a/config.json b/config.json index aa2b1a72..88b0c179 100644 --- a/config.json +++ b/config.json @@ -3,9 +3,9 @@ "appcache" : false, "cache" : {"allowed" : false}, "minification" : { - "js" : false, - "css" : false, - "html" : false, + "js" : true, + "css" : true, + "html" : true, "img" : false }, "github_key" : "891c251b925e4e967fa9", diff --git a/lib/server.js b/lib/server.js index a11dc42f..a82e46b5 100644 --- a/lib/server.js +++ b/lib/server.js @@ -97,7 +97,7 @@ lCache = this.Cache, lAppCache = this.AppCache, - lMinifyAllowed = lConfig.minification; + lMinifyAllowed = lConfig.minification; /* Переменная в которой храниться кэш*/ lCache.setAllowed(lConfig.cache.allowed); @@ -299,8 +299,8 @@ lFromCache_o.cache = false; - lFileData = CloudServer.Minify.Cache[ - Path.basename(lName)]; + if(lMinify.Cache) + lFileData = lMinify.Cache[Path.basename(lName)]; } var lReadFileFunc_f = CloudServer.getReadFileFunc(lName), /* если там что-то есть передаём данные в функцию readFile */ diff --git a/lib/server/minify.js b/lib/server/minify.js index 9bca2be6..619255e8 100644 --- a/lib/server/minify.js +++ b/lib/server/minify.js @@ -39,15 +39,13 @@ this._allowed = pAllowed; } }), - - - doit :(function(){ - - }), optimize: function(pName, pParams){ var lResult = true; + if(!pParams) + pParams = {}; + pParams.force = this.force; if(!this.MinFolder) From 00f35dd006123abc2765bb926b45e99282681034 Mon Sep 17 00:00:00 2001 From: coderaiser Date: Thu, 27 Dec 2012 05:56:08 -0500 Subject: [PATCH 067/347] moving out doit from minify to cloudcmd --- cloudcmd.js | 2 +- lib/server/minify.js | 39 +++++++++++++++++++-------------------- 2 files changed, 20 insertions(+), 21 deletions(-) diff --git a/cloudcmd.js b/cloudcmd.js index 7b606d51..20714e69 100644 --- a/cloudcmd.js +++ b/cloudcmd.js @@ -123,7 +123,7 @@ if (lOptimizeParams.length) lMinify.optimize(lOptimizeParams); } - + /** * rest interface * @pConnectionData {request, responce} diff --git a/lib/server/minify.js b/lib/server/minify.js index 619255e8..7beba1a2 100644 --- a/lib/server/minify.js +++ b/lib/server/minify.js @@ -41,35 +41,34 @@ }), optimize: function(pName, pParams){ - var lResult = true; - - if(!pParams) - pParams = {}; - - pParams.force = this.force; - - if(!this.MinFolder) - this.MinFolder = Minify.MinFolder; - if(!this.Cache) - this.Cache = Minify.Cache; - - if(this._allowed.css || this._allowed.js || this._allowed.html){ - if(Minify) + var lResult; + if(Minify) + + if(!pParams) + pParams = {}; + + pParams.force = this.force; + + if(!this.MinFolder) + this.MinFolder = Minify.MinFolder; + if(!this.Cache) + this.Cache = Minify.Cache; + + if(this._allowed.css || this._allowed.js || this._allowed.html){ Minify.optimize(pName, pParams); + lResult = true; + } else{ - lResult = false; - this._allowed = { js : false, css : false, - html : false}; - + html : false + }; + console.log('Could not minify ' + 'without minify module\n' + 'npm i minify'); } - } - else lResult = false; return lResult; }, From fa74c05f5601260ee6db4d6cb109c495500177d1 Mon Sep 17 00:00:00 2001 From: coderaiser Date: Thu, 27 Dec 2012 05:58:56 -0500 Subject: [PATCH 068/347] minor changes --- lib/server/minify.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/server/minify.js b/lib/server/minify.js index 7beba1a2..d4873ce1 100644 --- a/lib/server/minify.js +++ b/lib/server/minify.js @@ -42,8 +42,7 @@ optimize: function(pName, pParams){ var lResult; - if(Minify) - + if(Minify){ if(!pParams) pParams = {}; @@ -69,6 +68,7 @@ 'without minify module\n' + 'npm i minify'); } + } return lResult; }, From a8b13f4357167ae4a20a5ec39865711a817b54a9 Mon Sep 17 00:00:00 2001 From: coderaiser Date: Thu, 27 Dec 2012 06:01:07 -0500 Subject: [PATCH 069/347] minor changes --- lib/server/minify.js | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/lib/server/minify.js b/lib/server/minify.js index d4873ce1..adfd7f29 100644 --- a/lib/server/minify.js +++ b/lib/server/minify.js @@ -57,17 +57,17 @@ Minify.optimize(pName, pParams); lResult = true; } - else{ - this._allowed = { - js : false, - css : false, - html : false - }; - - console.log('Could not minify ' + - 'without minify module\n' + - 'npm i minify'); - } + } + else{ + this._allowed = { + js : false, + css : false, + html : false + }; + + console.log('Could not minify ' + + 'without minify module\n' + + 'npm i minify'); } return lResult; From ac168d1a8641f8808db3bd71068b7c6e19f352e4 Mon Sep 17 00:00:00 2001 From: coderaiser Date: Thu, 27 Dec 2012 08:08:22 -0500 Subject: [PATCH 070/347] minor changes --- cloudcmd.js | 10 +++++++--- config.json | 2 +- lib/server.js | 3 +-- lib/server/minify.js | 3 ++- 4 files changed, 11 insertions(+), 7 deletions(-) diff --git a/cloudcmd.js b/cloudcmd.js index 20714e69..840cd07d 100644 --- a/cloudcmd.js +++ b/cloudcmd.js @@ -99,7 +99,11 @@ lResetCSS = DIR + 'css/reset.css', lIndex = DIR + 'html/index.html', - lMinify = Server.CloudServer.Minify; + lMinify = Server.CloudServer.Minify, + lCSSOptions = { + img : pAllowed.img, + merge : true + }; if (pAllowed.js) { lOptimizeParams.push(LIBDIR + 'client.js'); @@ -112,9 +116,9 @@ var lStyles = []; lStyles[0] = {}; - lStyles[0][lStyleCSS] = pAllowed.img; + lStyles[0][lStyleCSS] = lCSSOptions; lStyles[1] = {}; - lStyles[1][lResetCSS] = pAllowed.img; + lStyles[1][lResetCSS] = lCSSOptions; lOptimizeParams.push(lStyles[0]); lOptimizeParams.push(lStyles[1]); diff --git a/config.json b/config.json index 88b0c179..270056fa 100644 --- a/config.json +++ b/config.json @@ -6,7 +6,7 @@ "js" : true, "css" : true, "html" : true, - "img" : false + "img" : true }, "github_key" : "891c251b925e4e967fa9", "github_secret" : "afe9bed1e810c5dc44c4c2a953fc6efb1e5b0545", diff --git a/lib/server.js b/lib/server.js index a82e46b5..d59f6d91 100644 --- a/lib/server.js +++ b/lib/server.js @@ -334,14 +334,13 @@ (lCheck_f('css') && lMin_o.css) || (lCheck_f('html') && lMin_o.html); - if(lResult){ + if(lResult) lResult = CloudServer.Minify.optimize(lName, { cache: true, callback: function(pFileData){ lReadFileFunc_f(undefined, pFileData, false); } }); - } } if(!lResult) diff --git a/lib/server/minify.js b/lib/server/minify.js index adfd7f29..ca34629e 100644 --- a/lib/server/minify.js +++ b/lib/server/minify.js @@ -46,7 +46,8 @@ if(!pParams) pParams = {}; - pParams.force = this.force; + if(this.force) + pParams.force = this.force; if(!this.MinFolder) this.MinFolder = Minify.MinFolder; From 5f132a2a3fb13c3cac49682e523d03d2ce2ce818 Mon Sep 17 00:00:00 2001 From: coderaiser Date: Fri, 28 Dec 2012 05:09:03 -0500 Subject: [PATCH 071/347] updated jquery to v.1.8.3 --- ChangeLog | 2 + lib/client/dom.js | 3 +- lib/client/jquery.js | 1969 ++++++++++++++++++++++++------------------ 3 files changed, 1110 insertions(+), 864 deletions(-) diff --git a/ChangeLog b/ChangeLog index 32682fce..89a8914f 100644 --- a/ChangeLog +++ b/ChangeLog @@ -62,6 +62,8 @@ keyStop: function(e, opt) { * Updated CodeMirror to v2.37.01. +* Updated jquery to v.1.8.3. + 2012.12.12, Version 0.1.8 diff --git a/lib/client/dom.js b/lib/client/dom.js index c08515dd..cc98639a 100644 --- a/lib/client/dom.js +++ b/lib/client/dom.js @@ -308,7 +308,6 @@ var CloudCommander, Util, DOM, CloudFunc; return lID; }, - /** * create elements and load them to DOM-tree * one-by-one @@ -604,7 +603,7 @@ var CloudCommander, Util, DOM, CloudFunc; */ DOM.jqueryLoad = function(pCallBack){ /* загружаем jquery: */ - DOM.jsload('//ajax.googleapis.com/ajax/libs/jquery/1.8.0/jquery.min.js',{ + DOM.jsload('//ajax.googleapis.com/ajax/libs/jquery/1.8.3/jquery.min.js',{ onload: Util.retExec(pCallBack), onerror: function(){ diff --git a/lib/client/jquery.js b/lib/client/jquery.js index daa58a6b..6c5f4a69 100644 --- a/lib/client/jquery.js +++ b/lib/client/jquery.js @@ -1,5 +1,5 @@ /*! - * jQuery JavaScript Library v1.8.0 + * jQuery JavaScript Library v1.8.3 * http://jquery.com/ * * Includes Sizzle.js @@ -9,7 +9,7 @@ * Released under the MIT license * http://jquery.org/license * - * Date: Thu Aug 09 2012 16:24:48 GMT-0400 (Eastern Daylight Time) + * Date: Tue Nov 13 2012 08:20:33 GMT-0500 (Eastern Standard Time) */ (function( window, undefined ) { var @@ -51,8 +51,8 @@ var core_rnotwhite = /\S/, core_rspace = /\s+/, - // IE doesn't match non-breaking spaces with \s - rtrim = core_rnotwhite.test("\xA0") ? (/^[\s\xA0]+|[\s\xA0]+$/g) : /^\s+|\s+$/g, + // Make sure we trim BOM and NBSP (here's looking at you, Safari 5.0 and IE) + rtrim = /^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g, // A simple way to check for HTML strings // Prioritize #id over to avoid XSS via location.hash (#9521) @@ -186,7 +186,7 @@ jQuery.fn = jQuery.prototype = { selector: "", // The current version of jQuery being used - jquery: "1.8.0", + jquery: "1.8.3", // The default length of a jQuery object is 0 length: 0, @@ -573,7 +573,7 @@ jQuery.extend({ }, nodeName: function( elem, name ) { - return elem.nodeName && elem.nodeName.toUpperCase() === name.toUpperCase(); + return elem.nodeName && elem.nodeName.toLowerCase() === name.toLowerCase(); }, // args is for internal usage only @@ -619,7 +619,7 @@ jQuery.extend({ }, // Use native String.trim function wherever possible - trim: core_trim ? + trim: core_trim && !core_trim.call("\uFEFF\xA0") ? function( text ) { return text == null ? "" : @@ -630,7 +630,7 @@ jQuery.extend({ function( text ) { return text == null ? "" : - text.toString().replace( rtrim, "" ); + ( text + "" ).replace( rtrim, "" ); }, // results is for internal usage only @@ -776,7 +776,7 @@ jQuery.extend({ }; // Set the guid of unique handler to the same of original handler, so it can be removed - proxy.guid = fn.guid = fn.guid || proxy.guid || jQuery.guid++; + proxy.guid = fn.guid = fn.guid || jQuery.guid++; return proxy; }, @@ -844,9 +844,10 @@ jQuery.ready.promise = function( obj ) { readyList = jQuery.Deferred(); - // Catch cases where $(document).ready() is called after the - // browser event has already occurred. - if ( document.readyState === "complete" || ( document.readyState !== "loading" && document.addEventListener ) ) { + // Catch cases where $(document).ready() is called after the browser event has already occurred. + // we once tried to use readyState "interactive" here, but it caused issues like the one + // discovered by ChrisS here: http://bugs.jquery.com/ticket/12282#comment:15 + if ( document.readyState === "complete" ) { // Handle it asynchronously to allow scripts the opportunity to delay ready setTimeout( jQuery.ready, 1 ); @@ -997,9 +998,12 @@ jQuery.Callbacks = function( options ) { var start = list.length; (function add( args ) { jQuery.each( args, function( _, arg ) { - if ( jQuery.isFunction( arg ) && ( !options.unique || !self.has( arg ) ) ) { - list.push( arg ); - } else if ( arg && arg.length ) { + var type = jQuery.type( arg ); + if ( type === "function" ) { + if ( !options.unique || !self.has( arg ) ) { + list.push( arg ); + } + } else if ( arg && arg.length && type !== "string" ) { // Inspect recursively add( arg ); } @@ -1141,7 +1145,7 @@ jQuery.extend({ // Get a promise for this deferred // If obj is provided, the promise aspect is added to the object promise: function( obj ) { - return typeof obj === "object" ? jQuery.extend( obj, promise ) : promise; + return obj != null ? jQuery.extend( obj, promise ) : promise; } }, deferred = {}; @@ -1251,24 +1255,23 @@ jQuery.support = (function() { clickFn, div = document.createElement("div"); - // Preliminary tests + // Setup div.setAttribute( "className", "t" ); div.innerHTML = "
    a"; + // Support tests won't run in some limited or non-browser environments all = div.getElementsByTagName("*"); a = div.getElementsByTagName("a")[ 0 ]; - a.style.cssText = "top:1px;float:left;opacity:.5"; - - // Can't get basic test support - if ( !all || !all.length || !a ) { + if ( !all || !a || !all.length ) { return {}; } - // First batch of supports tests + // First batch of tests select = document.createElement("select"); opt = select.appendChild( document.createElement("option") ); input = div.getElementsByTagName("input")[ 0 ]; + a.style.cssText = "top:1px;float:left;opacity:.5"; support = { // IE strips leading whitespace when .innerHTML is used leadingWhitespace: ( div.firstChild.nodeType === 3 ), @@ -1310,7 +1313,7 @@ jQuery.support = (function() { // Test setAttribute on camelCase class. If it works, we need attrFixes when doing get/setAttribute (ie6/7) getSetAttribute: div.className !== "t", - // Tests for enctype support on a form(#6743) + // Tests for enctype support on a form (#6743) enctype: !!document.createElement("form").enctype, // Makes sure cloning an html5 element does not cause problems @@ -1452,10 +1455,8 @@ jQuery.support = (function() { support.boxSizing = ( div.offsetWidth === 4 ); support.doesNotIncludeMarginInBodyOffset = ( body.offsetTop !== 1 ); - // NOTE: To any future maintainer, window.getComputedStyle was used here - // instead of getComputedStyle because it gave a better gzip size. - // The difference between window.getComputedStyle and getComputedStyle is - // 7 bytes + // NOTE: To any future maintainer, we've window.getComputedStyle + // because jsdom on node.js will break without it. if ( window.getComputedStyle ) { support.pixelPosition = ( window.getComputedStyle( div, null ) || {} ).top !== "1%"; support.boxSizingReliable = ( window.getComputedStyle( div, null ) || { width: "4px" } ).width === "4px"; @@ -1505,7 +1506,7 @@ jQuery.support = (function() { return support; })(); -var rbrace = /^(?:\{.*\}|\[.*\])$/, +var rbrace = /(?:\{[\s\S]*\}|\[[\s\S]*\])$/, rmultiDash = /([A-Z])/g; jQuery.extend({ @@ -1513,7 +1514,7 @@ jQuery.extend({ deletedIds: [], - // Please use with caution + // Remove at next major release (1.9/2.0) uuid: 0, // Unique for each copy of jQuery on the page @@ -1565,7 +1566,7 @@ jQuery.extend({ // Only DOM nodes need a new unique ID for each element since their data // ends up in the global cache if ( isNode ) { - elem[ internalKey ] = id = jQuery.deletedIds.pop() || ++jQuery.uuid; + elem[ internalKey ] = id = jQuery.deletedIds.pop() || jQuery.guid++; } else { id = internalKey; } @@ -1739,7 +1740,7 @@ jQuery.fn.extend({ for ( l = attr.length; i < l; i++ ) { name = attr[i].name; - if ( name.indexOf( "data-" ) === 0 ) { + if ( !name.indexOf( "data-" ) ) { name = jQuery.camelCase( name.substring(5) ); dataAttr( elem, name, data[ name ] ); @@ -1868,6 +1869,7 @@ jQuery.extend({ type = type || "fx"; var queue = jQuery.queue( elem, type ), + startLength = queue.length, fn = queue.shift(), hooks = jQuery._queueHooks( elem, type ), next = function() { @@ -1877,6 +1879,7 @@ jQuery.extend({ // If the fx queue is dequeued, always remove the progress sentinel if ( fn === "inprogress" ) { fn = queue.shift(); + startLength--; } if ( fn ) { @@ -1891,7 +1894,8 @@ jQuery.extend({ delete hooks.stop; fn.call( elem, next, hooks ); } - if ( !queue.length && hooks ) { + + if ( !startLength && hooks ) { hooks.empty.fire(); } }, @@ -1977,7 +1981,8 @@ jQuery.fn.extend({ type = type || "fx"; while( i-- ) { - if ( (tmp = jQuery._data( elements[ i ], type + "queueHooks" )) && tmp.empty ) { + tmp = jQuery._data( elements[ i ], type + "queueHooks" ); + if ( tmp && tmp.empty ) { count++; tmp.empty.add( resolve ); } @@ -2045,7 +2050,7 @@ jQuery.fn.extend({ setClass = " " + elem.className + " "; for ( c = 0, cl = classNames.length; c < cl; c++ ) { - if ( !~setClass.indexOf( " " + classNames[ c ] + " " ) ) { + if ( setClass.indexOf( " " + classNames[ c ] + " " ) < 0 ) { setClass += classNames[ c ] + " "; } } @@ -2078,7 +2083,7 @@ jQuery.fn.extend({ // loop over each item in the removal list for ( c = 0, cl = removes.length; c < cl; c++ ) { // Remove until there is nothing to remove, - while ( className.indexOf(" " + removes[ c ] + " ") > -1 ) { + while ( className.indexOf(" " + removes[ c ] + " ") >= 0 ) { className = className.replace( " " + removes[ c ] + " " , " " ); } } @@ -2132,7 +2137,7 @@ jQuery.fn.extend({ i = 0, l = this.length; for ( ; i < l; i++ ) { - if ( this[i].nodeType === 1 && (" " + this[i].className + " ").replace(rclass, " ").indexOf( className ) > -1 ) { + if ( this[i].nodeType === 1 && (" " + this[i].className + " ").replace(rclass, " ").indexOf( className ) >= 0 ) { return true; } } @@ -2213,26 +2218,25 @@ jQuery.extend({ }, select: { get: function( elem ) { - var value, i, max, option, - index = elem.selectedIndex, - values = [], + var value, option, options = elem.options, - one = elem.type === "select-one"; - - // Nothing was selected - if ( index < 0 ) { - return null; - } + index = elem.selectedIndex, + one = elem.type === "select-one" || index < 0, + values = one ? null : [], + max = one ? index + 1 : options.length, + i = index < 0 ? + max : + one ? index : 0; // Loop through all the selected options - i = one ? index : 0; - max = one ? index + 1 : options.length; for ( ; i < max; i++ ) { option = options[ i ]; - // Don't return options that are disabled or in a disabled optgroup - if ( option.selected && (jQuery.support.optDisabled ? !option.disabled : option.getAttribute("disabled") === null) && - (!option.parentNode.disabled || !jQuery.nodeName( option.parentNode, "optgroup" )) ) { + // oldIE doesn't update selected after form reset (#2551) + if ( ( option.selected || i === index ) && + // Don't return options that are disabled or in a disabled optgroup + ( jQuery.support.optDisabled ? !option.disabled : option.getAttribute("disabled") === null ) && + ( !option.parentNode.disabled || !jQuery.nodeName( option.parentNode, "optgroup" ) ) ) { // Get the specific value for the option value = jQuery( option ).val(); @@ -2247,11 +2251,6 @@ jQuery.extend({ } } - // Fixes Bug #2551 -- select.val() broken in IE after form.reset() - if ( one && !values.length && options.length ) { - return jQuery( options[ index ] ).val(); - } - return values; }, @@ -2310,7 +2309,7 @@ jQuery.extend({ return ret; } else { - elem.setAttribute( name, "" + value ); + elem.setAttribute( name, value + "" ); return value; } @@ -2574,7 +2573,7 @@ if ( !jQuery.support.style ) { return elem.style.cssText.toLowerCase() || undefined; }, set: function( elem, value ) { - return ( elem.style.cssText = "" + value ); + return ( elem.style.cssText = value + "" ); } }; } @@ -2707,6 +2706,7 @@ jQuery.event = { handler: handler, guid: handler.guid, selector: selector, + needsContext: selector && jQuery.expr.match.needsContext.test( selector ), namespace: namespaces.join(".") }, handleObjIn ); @@ -2942,7 +2942,7 @@ jQuery.event = { } // Note that this is a bare JS function and not a jQuery handler handle = ontype && cur[ ontype ]; - if ( handle && jQuery.acceptData( cur ) && handle.apply( cur, data ) === false ) { + if ( handle && jQuery.acceptData( cur ) && handle.apply && handle.apply( cur, data ) === false ) { event.preventDefault(); } } @@ -2987,10 +2987,10 @@ jQuery.event = { // Make a writable jQuery.Event from the native event object event = jQuery.event.fix( event || window.event ); - var i, j, cur, jqcur, ret, selMatch, matched, matches, handleObj, sel, related, + var i, j, cur, ret, selMatch, matched, matches, handleObj, sel, related, handlers = ( (jQuery._data( this, "events" ) || {} )[ event.type ] || []), delegateCount = handlers.delegateCount, - args = [].slice.call( arguments ), + args = core_slice.call( arguments ), run_all = !event.exclusive && !event.namespace, special = jQuery.event.special[ event.type ] || {}, handlerQueue = []; @@ -3008,23 +3008,20 @@ jQuery.event = { // Avoid non-left-click bubbling in Firefox (#3861) if ( delegateCount && !(event.button && event.type === "click") ) { - // Pregenerate a single jQuery object for reuse with .is() - jqcur = jQuery(this); - jqcur.context = this; - for ( cur = event.target; cur != this; cur = cur.parentNode || this ) { - // Don't process clicks (ONLY) on disabled elements (#6911, #8165, #xxxx) + // Don't process clicks (ONLY) on disabled elements (#6911, #8165, #11382, #11764) if ( cur.disabled !== true || event.type !== "click" ) { selMatch = {}; matches = []; - jqcur[0] = cur; for ( i = 0; i < delegateCount; i++ ) { handleObj = handlers[ i ]; sel = handleObj.selector; if ( selMatch[ sel ] === undefined ) { - selMatch[ sel ] = jqcur.is( sel ); + selMatch[ sel ] = handleObj.needsContext ? + jQuery( sel, this ).index( cur ) >= 0 : + jQuery.find( sel, this, null, [ cur ] ).length; } if ( selMatch[ sel ] ) { matches.push( handleObj ); @@ -3165,11 +3162,6 @@ jQuery.event = { }, special: { - ready: { - // Make sure the ready event is setup - setup: jQuery.bindReady - }, - load: { // Prevent triggered image.load events from bubbling to window.load noBubble: true @@ -3236,7 +3228,7 @@ jQuery.removeEvent = document.removeEventListener ? if ( elem.detachEvent ) { - // #8545, #7054, preventing memory leaks for custom events in IE6-8 – + // #8545, #7054, preventing memory leaks for custom events in IE6-8 // detachEvent needed property on element, by name of that event, to properly expose it to GC if ( typeof elem[ name ] === "undefined" ) { elem[ name ] = null; @@ -3458,7 +3450,7 @@ if ( !jQuery.support.changeBubbles ) { teardown: function() { jQuery.event.remove( this, "._change" ); - return rformElems.test( this.nodeName ); + return !rformElems.test( this.nodeName ); } }; } @@ -3599,7 +3591,7 @@ jQuery.fn.extend({ }, undelegate: function( selector, types, fn ) { // ( namespace ) or ( selector, types [, fn] ) - return arguments.length == 1? this.off( selector, "**" ) : this.off( types, selector || "**", fn ); + return arguments.length === 1 ? this.off( selector, "**" ) : this.off( types, selector || "**", fn ); }, trigger: function( type, data ) { @@ -3670,30 +3662,73 @@ jQuery.each( ("blur focus focusin focusout load resize scroll unload click dblcl }); /*! * Sizzle CSS Selector Engine - * Copyright 2012 jQuery Foundation and other contributors - * Released under the MIT license - * http://sizzlejs.com/ + * Copyright 2012 jQuery Foundation and other contributors + * Released under the MIT license + * http://sizzlejs.com/ */ (function( window, undefined ) { var cachedruns, - dirruns, - sortOrder, - siblingCheck, assertGetIdNotName, + Expr, + getText, + isXML, + contains, + compile, + sortOrder, + hasDuplicate, + outermostContext, - document = window.document, - docElem = document.documentElement, - - strundefined = "undefined", - hasDuplicate = false, baseHasDuplicate = true, - done = 0, - slice = [].slice, - push = [].push, + strundefined = "undefined", expando = ( "sizcache" + Math.random() ).replace( ".", "" ), + Token = String, + document = window.document, + docElem = document.documentElement, + dirruns = 0, + done = 0, + pop = [].pop, + push = [].push, + slice = [].slice, + // Use a stripped-down indexOf if a native one is unavailable + indexOf = [].indexOf || function( elem ) { + var i = 0, + len = this.length; + for ( ; i < len; i++ ) { + if ( this[i] === elem ) { + return i; + } + } + return -1; + }, + + // Augment a function for special use by Sizzle + markFunction = function( fn, value ) { + fn[ expando ] = value == null || value; + return fn; + }, + + createCache = function() { + var cache = {}, + keys = []; + + return markFunction(function( key, value ) { + // Only keep the most recent entries + if ( keys.push( key ) > Expr.cacheLength ) { + delete cache[ keys.shift() ]; + } + + // Retrieve with (key + " ") to avoid collision with native Object.prototype properties (see Issue #157) + return (cache[ key + " " ] = value); + }, cache ); + }, + + classCache = createCache(), + tokenCache = createCache(), + compilerCache = createCache(), + // Regex // Whitespace characters http://www.w3.org/TR/css3-selectors/#whitespace @@ -3710,29 +3745,29 @@ var cachedruns, operators = "([*^$|!~]?=)", attributes = "\\[" + whitespace + "*(" + characterEncoding + ")" + whitespace + "*(?:" + operators + whitespace + "*(?:(['\"])((?:\\\\.|[^\\\\])*?)\\3|(" + identifier + ")|)|)" + whitespace + "*\\]", - pseudos = ":(" + characterEncoding + ")(?:\\((?:(['\"])((?:\\\\.|[^\\\\])*?)\\2|((?:[^,]|\\\\,|(?:,(?=[^\\[]*\\]))|(?:,(?=[^\\(]*\\))))*))\\)|)", - pos = ":(nth|eq|gt|lt|first|last|even|odd)(?:\\((\\d*)\\)|)(?=[^-]|$)", - combinators = whitespace + "*([\\x20\\t\\r\\n\\f>+~])" + whitespace + "*", - groups = "(?=[^\\x20\\t\\r\\n\\f])(?:\\\\.|" + attributes + "|" + pseudos.replace( 2, 7 ) + "|[^\\\\(),])+", + + // Prefer arguments not in parens/brackets, + // then attribute selectors and non-pseudos (denoted by :), + // then anything else + // These preferences are here to reduce the number of selectors + // needing tokenize in the PSEUDO preFilter + pseudos = ":(" + characterEncoding + ")(?:\\((?:(['\"])((?:\\\\.|[^\\\\])*?)\\2|([^()[\\]]*|(?:(?:" + attributes + ")|[^:]|\\\\.)*|.*))\\)|)", + + // For matchExpr.POS and matchExpr.needsContext + pos = ":(even|odd|eq|gt|lt|nth|first|last)(?:\\(" + whitespace + + "*((?:-\\d)?\\d*)" + whitespace + "*\\)|)(?=[^-]|$)", // Leading and non-escaped trailing whitespace, capturing some non-whitespace characters preceding the latter rtrim = new RegExp( "^" + whitespace + "+|((?:^|[^\\\\])(?:\\\\.)*)" + whitespace + "+$", "g" ), - rcombinators = new RegExp( "^" + combinators ), - - // All simple (non-comma) selectors, excluding insignifant trailing whitespace - rgroups = new RegExp( groups + "?(?=" + whitespace + "*,|$)", "g" ), - - // A selector, or everything after leading whitespace - // Optionally followed in either case by a ")" for terminating sub-selectors - rselector = new RegExp( "^(?:(?!,)(?:(?:^|,)" + whitespace + "*" + groups + ")*?|" + whitespace + "*(.*?))(\\)|$)" ), - - // All combinators and selector components (attribute test, tag, pseudo, etc.), the latter appearing together when consecutive - rtokens = new RegExp( groups.slice( 19, -6 ) + "\\x20\\t\\r\\n\\f>+~])+|" + combinators, "g" ), + rcomma = new RegExp( "^" + whitespace + "*," + whitespace + "*" ), + rcombinators = new RegExp( "^" + whitespace + "*([\\x20\\t\\r\\n\\f>+~])" + whitespace + "*" ), + rpseudo = new RegExp( pseudos ), // Easily-parseable/retrievable ID or TAG or CLASS selectors rquickExpr = /^(?:#([\w\-]+)|(\w+)|\.([\w\-]+))$/, + rnot = /^:not/, rsibling = /[\x20\t\r\n\f]*[+~]/, rendsWithNot = /:not\($/, @@ -3745,56 +3780,46 @@ var cachedruns, "ID": new RegExp( "^#(" + characterEncoding + ")" ), "CLASS": new RegExp( "^\\.(" + characterEncoding + ")" ), "NAME": new RegExp( "^\\[name=['\"]?(" + characterEncoding + ")['\"]?\\]" ), - "TAG": new RegExp( "^(" + characterEncoding.replace( "[-", "[-\\*" ) + ")" ), + "TAG": new RegExp( "^(" + characterEncoding.replace( "w", "w*" ) + ")" ), "ATTR": new RegExp( "^" + attributes ), "PSEUDO": new RegExp( "^" + pseudos ), - "CHILD": new RegExp( "^:(only|nth|last|first)-child(?:\\(" + whitespace + + "POS": new RegExp( pos, "i" ), + "CHILD": new RegExp( "^:(only|nth|first|last)-child(?:\\(" + whitespace + "*(even|odd|(([+-]|)(\\d*)n|)" + whitespace + "*(?:([+-]|)" + whitespace + "*(\\d+)|))" + whitespace + "*\\)|)", "i" ), - "POS": new RegExp( pos, "ig" ), // For use in libraries implementing .is() "needsContext": new RegExp( "^" + whitespace + "*[>+~]|" + pos, "i" ) }, - classCache = {}, - cachedClasses = [], - compilerCache = {}, - cachedSelectors = [], - - // Mark a function for use in filtering - markFunction = function( fn ) { - fn.sizzleFilter = true; - return fn; - }, - - // Returns a function to use in pseudos for input types - createInputFunction = function( type ) { - return function( elem ) { - // Check the input's nodeName and type - return elem.nodeName.toLowerCase() === "input" && elem.type === type; - }; - }, - - // Returns a function to use in pseudos for buttons - createButtonFunction = function( type ) { - return function( elem ) { - var name = elem.nodeName.toLowerCase(); - return (name === "input" || name === "button") && elem.type === type; - }; - }, + // Support // Used for testing something on an element assert = function( fn ) { - var pass = false, - div = document.createElement("div"); + var div = document.createElement("div"); + try { - pass = fn( div ); - } catch (e) {} - // release memory in IE - div = null; - return pass; + return fn( div ); + } catch (e) { + return false; + } finally { + // release memory in IE + div = null; + } }, + // Check if getElementsByTagName("*") returns only elements + assertTagNameNoComments = assert(function( div ) { + div.appendChild( document.createComment("") ); + return !div.getElementsByTagName("*").length; + }), + + // Check if getAttribute returns normalized href attributes + assertHrefNotNormalized = assert(function( div ) { + div.innerHTML = ""; + return div.firstChild && typeof div.firstChild.getAttribute !== strundefined && + div.firstChild.getAttribute("href") === "#"; + }), + // Check if attributes should be retrieved by attribute nodes assertAttributes = assert(function( div ) { div.innerHTML = ""; @@ -3803,6 +3828,19 @@ var cachedruns, return type !== "boolean" && type !== "string"; }), + // Check if getElementsByClassName can be trusted + assertUsableClassName = assert(function( div ) { + // Opera can't find a second classname (in 9.6) + div.innerHTML = ""; + if ( !div.getElementsByClassName || !div.getElementsByClassName("e").length ) { + return false; + } + + // Safari 3.2 caches class attributes and doesn't catch changes + div.lastChild.className = "e"; + return div.getElementsByClassName("e").length === 2; + }), + // Check if getElementById returns elements by name // Check if getElementsByName privileges form controls or returns elements by ID assertUsableName = assert(function( div ) { @@ -3814,58 +3852,45 @@ var cachedruns, // Test var pass = document.getElementsByName && // buggy browsers will return fewer than the correct 2 - document.getElementsByName( expando ).length === + document.getElementsByName( expando ).length === 2 + // buggy browsers will return more than the correct 0 - 2 + document.getElementsByName( expando + 0 ).length; + document.getElementsByName( expando + 0 ).length; assertGetIdNotName = !document.getElementById( expando ); // Cleanup docElem.removeChild( div ); return pass; - }), - - // Check if the browser returns only elements - // when doing getElementsByTagName("*") - assertTagNameNoComments = assert(function( div ) { - div.appendChild( document.createComment("") ); - return div.getElementsByTagName("*").length === 0; - }), - - // Check if getAttribute returns normalized href attributes - assertHrefNotNormalized = assert(function( div ) { - div.innerHTML = ""; - return div.firstChild && typeof div.firstChild.getAttribute !== strundefined && - div.firstChild.getAttribute("href") === "#"; - }), - - // Check if getElementsByClassName can be trusted - assertUsableClassName = assert(function( div ) { - // Opera can't find a second classname (in 9.6) - div.innerHTML = ""; - if ( !div.getElementsByClassName || div.getElementsByClassName("e").length === 0 ) { - return false; - } - - // Safari caches class attributes, doesn't catch changes (in 3.2) - div.lastChild.className = "e"; - return div.getElementsByClassName("e").length !== 1; }); -var Sizzle = function( selector, context, results, seed ) { +// If slice is not available, provide a backup +try { + slice.call( docElem.childNodes, 0 )[0].nodeType; +} catch ( e ) { + slice = function( i ) { + var elem, + results = []; + for ( ; (elem = this[i]); i++ ) { + results.push( elem ); + } + return results; + }; +} + +function Sizzle( selector, context, results, seed ) { results = results || []; context = context || document; var match, elem, xml, m, nodeType = context.nodeType; - if ( nodeType !== 1 && nodeType !== 9 ) { - return []; - } - if ( !selector || typeof selector !== "string" ) { return results; } + if ( nodeType !== 1 && nodeType !== 9 ) { + return []; + } + xml = isXML( context ); if ( !xml && !seed ) { @@ -3909,21 +3934,157 @@ var Sizzle = function( selector, context, results, seed ) { } // All others - return select( selector, context, results, seed, xml ); + return select( selector.replace( rtrim, "$1" ), context, results, seed, xml ); +} + +Sizzle.matches = function( expr, elements ) { + return Sizzle( expr, null, null, elements ); }; -var Expr = Sizzle.selectors = { +Sizzle.matchesSelector = function( elem, expr ) { + return Sizzle( expr, null, null, [ elem ] ).length > 0; +}; + +// Returns a function to use in pseudos for input types +function createInputPseudo( type ) { + return function( elem ) { + var name = elem.nodeName.toLowerCase(); + return name === "input" && elem.type === type; + }; +} + +// Returns a function to use in pseudos for buttons +function createButtonPseudo( type ) { + return function( elem ) { + var name = elem.nodeName.toLowerCase(); + return (name === "input" || name === "button") && elem.type === type; + }; +} + +// Returns a function to use in pseudos for positionals +function createPositionalPseudo( fn ) { + return markFunction(function( argument ) { + argument = +argument; + return markFunction(function( seed, matches ) { + var j, + matchIndexes = fn( [], seed.length, argument ), + i = matchIndexes.length; + + // Match elements found at the specified indexes + while ( i-- ) { + if ( seed[ (j = matchIndexes[i]) ] ) { + seed[j] = !(matches[j] = seed[j]); + } + } + }); + }); +} + +/** + * Utility function for retrieving the text value of an array of DOM nodes + * @param {Array|Element} elem + */ +getText = Sizzle.getText = function( elem ) { + var node, + ret = "", + i = 0, + nodeType = elem.nodeType; + + if ( nodeType ) { + if ( nodeType === 1 || nodeType === 9 || nodeType === 11 ) { + // Use textContent for elements + // innerText usage removed for consistency of new lines (see #11153) + if ( typeof elem.textContent === "string" ) { + return elem.textContent; + } else { + // Traverse its children + for ( elem = elem.firstChild; elem; elem = elem.nextSibling ) { + ret += getText( elem ); + } + } + } else if ( nodeType === 3 || nodeType === 4 ) { + return elem.nodeValue; + } + // Do not include comment or processing instruction nodes + } else { + + // If no nodeType, this is expected to be an array + for ( ; (node = elem[i]); i++ ) { + // Do not traverse comment nodes + ret += getText( node ); + } + } + return ret; +}; + +isXML = Sizzle.isXML = function( elem ) { + // documentElement is verified for cases where it doesn't yet exist + // (such as loading iframes in IE - #4833) + var documentElement = elem && (elem.ownerDocument || elem).documentElement; + return documentElement ? documentElement.nodeName !== "HTML" : false; +}; + +// Element contains another +contains = Sizzle.contains = docElem.contains ? + function( a, b ) { + var adown = a.nodeType === 9 ? a.documentElement : a, + bup = b && b.parentNode; + return a === bup || !!( bup && bup.nodeType === 1 && adown.contains && adown.contains(bup) ); + } : + docElem.compareDocumentPosition ? + function( a, b ) { + return b && !!( a.compareDocumentPosition( b ) & 16 ); + } : + function( a, b ) { + while ( (b = b.parentNode) ) { + if ( b === a ) { + return true; + } + } + return false; + }; + +Sizzle.attr = function( elem, name ) { + var val, + xml = isXML( elem ); + + if ( !xml ) { + name = name.toLowerCase(); + } + if ( (val = Expr.attrHandle[ name ]) ) { + return val( elem ); + } + if ( xml || assertAttributes ) { + return elem.getAttribute( name ); + } + val = elem.getAttributeNode( name ); + return val ? + typeof elem[ name ] === "boolean" ? + elem[ name ] ? name : null : + val.specified ? val.value : null : + null; +}; + +Expr = Sizzle.selectors = { // Can be adjusted by the user cacheLength: 50, + createPseudo: markFunction, + match: matchExpr, - order: [ "ID", "TAG" ], - - attrHandle: {}, - - createPseudo: markFunction, + // IE6/7 return a modified href + attrHandle: assertHrefNotNormalized ? + {} : + { + "href": function( elem ) { + return elem.getAttribute( "href", 2 ); + }, + "type": function( elem ) { + return elem.getAttribute("type"); + } + }, find: { "ID": assertGetIdNotName ? @@ -3971,7 +4132,19 @@ var Expr = Sizzle.selectors = { return tmp; } return results; + }, + + "NAME": assertUsableName && function( tag, context ) { + if ( typeof context.getElementsByName !== strundefined ) { + return context.getElementsByName( name ); } + }, + + "CLASS": assertUsableClassName && function( className, context, xml ) { + if ( typeof context.getElementsByClassName !== strundefined && !xml ) { + return context.getElementsByClassName( className ); + } + } }, relative: { @@ -3996,7 +4169,7 @@ var Expr = Sizzle.selectors = { }, "CHILD": function( match ) { - /* matches from matchExpr.CHILD + /* matches from matchExpr["CHILD"] 1 type (only|nth|...) 2 argument (even|odd|\d*|\d*n([+-]\d+)?|...) 3 xn-component of xn+y argument ([+-]?\d*n|) @@ -4027,24 +4200,30 @@ var Expr = Sizzle.selectors = { }, "PSEUDO": function( match ) { - var argument, - unquoted = match[4]; - + var unquoted, excess; if ( matchExpr["CHILD"].test( match[0] ) ) { return null; } - // Relinquish our claim on characters in `unquoted` from a closing parenthesis on - if ( unquoted && (argument = rselector.exec( unquoted )) && argument.pop() ) { + if ( match[3] ) { + match[2] = match[3]; + } else if ( (unquoted = match[4]) ) { + // Only check arguments that contain a pseudo + if ( rpseudo.test(unquoted) && + // Get excess from tokenize (recursively) + (excess = tokenize( unquoted, true )) && + // advance to the next closing parenthesis + (excess = unquoted.indexOf( ")", unquoted.length - excess ) - unquoted.length) ) { - match[0] = match[0].slice( 0, argument[0].length - unquoted.length - 1 ); - unquoted = argument[0].slice( 0, -1 ); + // excess is a negative index + unquoted = unquoted.slice( 0, excess ); + match[0] = match[0].slice( 0, excess ); + } + match[2] = unquoted; } - // Quoted or unquoted, we have the full argument // Return only captures needed by the pseudo filter method (type and argument) - match.splice( 2, 3, unquoted || match[3] ); - return match; + return match.slice( 0, 3 ); } }, @@ -4076,91 +4255,65 @@ var Expr = Sizzle.selectors = { }, "CLASS": function( className ) { - var pattern = classCache[ className ]; - if ( !pattern ) { - pattern = classCache[ className ] = new RegExp( "(^|" + whitespace + ")" + className + "(" + whitespace + "|$)" ); - cachedClasses.push( className ); - // Avoid too large of a cache - if ( cachedClasses.length > Expr.cacheLength ) { - delete classCache[ cachedClasses.shift() ]; - } - } - return function( elem ) { - return pattern.test( elem.className || (typeof elem.getAttribute !== strundefined && elem.getAttribute("class")) || "" ); - }; + var pattern = classCache[ expando ][ className + " " ]; + + return pattern || + (pattern = new RegExp( "(^|" + whitespace + ")" + className + "(" + whitespace + "|$)" )) && + classCache( className, function( elem ) { + return pattern.test( elem.className || (typeof elem.getAttribute !== strundefined && elem.getAttribute("class")) || "" ); + }); }, "ATTR": function( name, operator, check ) { - if ( !operator ) { - return function( elem ) { - return Sizzle.attr( elem, name ) != null; - }; - } - - return function( elem ) { - var result = Sizzle.attr( elem, name ), - value = result + ""; + return function( elem, context ) { + var result = Sizzle.attr( elem, name ); if ( result == null ) { return operator === "!="; } - - switch ( operator ) { - case "=": - return value === check; - case "!=": - return value !== check; - case "^=": - return check && value.indexOf( check ) === 0; - case "*=": - return check && value.indexOf( check ) > -1; - case "$=": - return check && value.substr( value.length - check.length ) === check; - case "~=": - return ( " " + value + " " ).indexOf( check ) > -1; - case "|=": - return value === check || value.substr( 0, check.length + 1 ) === check + "-"; + if ( !operator ) { + return true; } + + result += ""; + + return operator === "=" ? result === check : + operator === "!=" ? result !== check : + operator === "^=" ? check && result.indexOf( check ) === 0 : + operator === "*=" ? check && result.indexOf( check ) > -1 : + operator === "$=" ? check && result.substr( result.length - check.length ) === check : + operator === "~=" ? ( " " + result + " " ).indexOf( check ) > -1 : + operator === "|=" ? result === check || result.substr( 0, check.length + 1 ) === check + "-" : + false; }; }, "CHILD": function( type, argument, first, last ) { if ( type === "nth" ) { - var doneName = done++; - return function( elem ) { - var parent, diff, - count = 0, - node = elem; + var node, diff, + parent = elem.parentNode; if ( first === 1 && last === 0 ) { return true; } - parent = elem.parentNode; - - if ( parent && (parent[ expando ] !== doneName || !elem.sizset) ) { + if ( parent ) { + diff = 0; for ( node = parent.firstChild; node; node = node.nextSibling ) { if ( node.nodeType === 1 ) { - node.sizset = ++count; - if ( node === elem ) { + diff++; + if ( elem === node ) { break; } } } - - parent[ expando ] = doneName; } - diff = elem.sizset - last; - - if ( first === 0 ) { - return diff === 0; - - } else { - return ( diff % first === 0 && diff / first >= 0 ); - } + // Incorporate the offset (or cast to NaN), then check against cycle size + diff -= last; + return diff === first || ( diff % first === 0 && diff / first >= 0 ); }; } @@ -4195,35 +4348,82 @@ var Expr = Sizzle.selectors = { }; }, - "PSEUDO": function( pseudo, argument, context, xml ) { + "PSEUDO": function( pseudo, argument ) { // pseudo-class names are case-insensitive // http://www.w3.org/TR/selectors/#pseudo-classes // Prioritize by case sensitivity in case custom pseudos are added with uppercase letters - var fn = Expr.pseudos[ pseudo ] || Expr.pseudos[ pseudo.toLowerCase() ]; + // Remember that setFilters inherits from pseudos + var args, + fn = Expr.pseudos[ pseudo ] || Expr.setFilters[ pseudo.toLowerCase() ] || + Sizzle.error( "unsupported pseudo: " + pseudo ); - if ( !fn ) { - Sizzle.error( "unsupported pseudo: " + pseudo ); - } - - // The user may set fn.sizzleFilter to indicate - // that arguments are needed to create the filter function + // The user may use createPseudo to indicate that + // arguments are needed to create the filter function // just as Sizzle does - if ( !fn.sizzleFilter ) { - return fn; + if ( fn[ expando ] ) { + return fn( argument ); } - return fn( argument, context, xml ); + // But maintain support for old signatures + if ( fn.length > 1 ) { + args = [ pseudo, pseudo, "", argument ]; + return Expr.setFilters.hasOwnProperty( pseudo.toLowerCase() ) ? + markFunction(function( seed, matches ) { + var idx, + matched = fn( seed, argument ), + i = matched.length; + while ( i-- ) { + idx = indexOf.call( seed, matched[i] ); + seed[ idx ] = !( matches[ idx ] = matched[i] ); + } + }) : + function( elem ) { + return fn( elem, 0, args ); + }; + } + + return fn; } }, pseudos: { - "not": markFunction(function( selector, context, xml ) { + "not": markFunction(function( selector ) { // Trim the selector passed to compile // to avoid treating leading and trailing // spaces as combinators - var matcher = compile( selector.replace( rtrim, "$1" ), context, xml ); + var input = [], + results = [], + matcher = compile( selector.replace( rtrim, "$1" ) ); + + return matcher[ expando ] ? + markFunction(function( seed, matches, context, xml ) { + var elem, + unmatched = matcher( seed, null, xml, [] ), + i = seed.length; + + // Match elements unmatched by `matcher` + while ( i-- ) { + if ( (elem = unmatched[i]) ) { + seed[i] = !(matches[i] = elem); + } + } + }) : + function( elem, context, xml ) { + input[0] = elem; + matcher( input, null, xml, results ); + return !results.pop(); + }; + }), + + "has": markFunction(function( selector ) { return function( elem ) { - return !matcher( elem ); + return Sizzle( selector, elem ).length > 0; + }; + }), + + "contains": markFunction(function( text ) { + return function( elem ) { + return ( elem.textContent || elem.innerText || getText( elem ) ).indexOf( text ) > -1; }; }), @@ -4273,18 +4473,6 @@ var Expr = Sizzle.selectors = { return true; }, - "contains": markFunction(function( text ) { - return function( elem ) { - return ( elem.textContent || elem.innerText || getText( elem ) ).indexOf( text ) > -1; - }; - }), - - "has": markFunction(function( selector ) { - return function( elem ) { - return Sizzle( selector, elem ).length > 0; - }; - }), - "header": function( elem ) { return rheader.test( elem.nodeName ); }, @@ -4299,14 +4487,14 @@ var Expr = Sizzle.selectors = { }, // Input types - "radio": createInputFunction("radio"), - "checkbox": createInputFunction("checkbox"), - "file": createInputFunction("file"), - "password": createInputFunction("password"), - "image": createInputFunction("image"), + "radio": createInputPseudo("radio"), + "checkbox": createInputPseudo("checkbox"), + "file": createInputPseudo("file"), + "password": createInputPseudo("password"), + "image": createInputPseudo("image"), - "submit": createButtonFunction("submit"), - "reset": createButtonFunction("reset"), + "submit": createButtonPseudo("submit"), + "reset": createButtonPseudo("reset"), "button": function( elem ) { var name = elem.nodeName.toLowerCase(); @@ -4319,210 +4507,76 @@ var Expr = Sizzle.selectors = { "focus": function( elem ) { var doc = elem.ownerDocument; - return elem === doc.activeElement && (!doc.hasFocus || doc.hasFocus()) && !!(elem.type || elem.href); + return elem === doc.activeElement && (!doc.hasFocus || doc.hasFocus()) && !!(elem.type || elem.href || ~elem.tabIndex); }, "active": function( elem ) { return elem === elem.ownerDocument.activeElement; - } - }, - - setFilters: { - "first": function( elements, argument, not ) { - return not ? elements.slice( 1 ) : [ elements[0] ]; }, - "last": function( elements, argument, not ) { - var elem = elements.pop(); - return not ? elements : [ elem ]; - }, + // Positional types + "first": createPositionalPseudo(function() { + return [ 0 ]; + }), - "even": function( elements, argument, not ) { - var results = [], - i = not ? 1 : 0, - len = elements.length; - for ( ; i < len; i = i + 2 ) { - results.push( elements[i] ); + "last": createPositionalPseudo(function( matchIndexes, length ) { + return [ length - 1 ]; + }), + + "eq": createPositionalPseudo(function( matchIndexes, length, argument ) { + return [ argument < 0 ? argument + length : argument ]; + }), + + "even": createPositionalPseudo(function( matchIndexes, length ) { + for ( var i = 0; i < length; i += 2 ) { + matchIndexes.push( i ); } - return results; - }, + return matchIndexes; + }), - "odd": function( elements, argument, not ) { - var results = [], - i = not ? 0 : 1, - len = elements.length; - for ( ; i < len; i = i + 2 ) { - results.push( elements[i] ); + "odd": createPositionalPseudo(function( matchIndexes, length ) { + for ( var i = 1; i < length; i += 2 ) { + matchIndexes.push( i ); } - return results; - }, + return matchIndexes; + }), - "lt": function( elements, argument, not ) { - return not ? elements.slice( +argument ) : elements.slice( 0, +argument ); - }, + "lt": createPositionalPseudo(function( matchIndexes, length, argument ) { + for ( var i = argument < 0 ? argument + length : argument; --i >= 0; ) { + matchIndexes.push( i ); + } + return matchIndexes; + }), - "gt": function( elements, argument, not ) { - return not ? elements.slice( 0, +argument + 1 ) : elements.slice( +argument + 1 ); - }, - - "eq": function( elements, argument, not ) { - var elem = elements.splice( +argument, 1 ); - return not ? elements : elem; - } + "gt": createPositionalPseudo(function( matchIndexes, length, argument ) { + for ( var i = argument < 0 ? argument + length : argument; ++i < length; ) { + matchIndexes.push( i ); + } + return matchIndexes; + }) } }; -// Deprecated -Expr.setFilters["nth"] = Expr.setFilters["eq"]; +function siblingCheck( a, b, ret ) { + if ( a === b ) { + return ret; + } -// Back-compat -Expr.filters = Expr.pseudos; + var cur = a.nextSibling; -// IE6/7 return a modified href -if ( !assertHrefNotNormalized ) { - Expr.attrHandle = { - "href": function( elem ) { - return elem.getAttribute( "href", 2 ); - }, - "type": function( elem ) { - return elem.getAttribute("type"); + while ( cur ) { + if ( cur === b ) { + return -1; } - }; + + cur = cur.nextSibling; + } + + return 1; } -// Add getElementsByName if usable -if ( assertUsableName ) { - Expr.order.push("NAME"); - Expr.find["NAME"] = function( name, context ) { - if ( typeof context.getElementsByName !== strundefined ) { - return context.getElementsByName( name ); - } - }; -} - -// Add getElementsByClassName if usable -if ( assertUsableClassName ) { - Expr.order.splice( 1, 0, "CLASS" ); - Expr.find["CLASS"] = function( className, context, xml ) { - if ( typeof context.getElementsByClassName !== strundefined && !xml ) { - return context.getElementsByClassName( className ); - } - }; -} - -// If slice is not available, provide a backup -try { - slice.call( docElem.childNodes, 0 )[0].nodeType; -} catch ( e ) { - slice = function( i ) { - var elem, results = []; - for ( ; (elem = this[i]); i++ ) { - results.push( elem ); - } - return results; - }; -} - -var isXML = Sizzle.isXML = function( elem ) { - // documentElement is verified for cases where it doesn't yet exist - // (such as loading iframes in IE - #4833) - var documentElement = elem && (elem.ownerDocument || elem).documentElement; - return documentElement ? documentElement.nodeName !== "HTML" : false; -}; - -// Element contains another -var contains = Sizzle.contains = docElem.compareDocumentPosition ? +sortOrder = docElem.compareDocumentPosition ? function( a, b ) { - return !!( a.compareDocumentPosition( b ) & 16 ); - } : - docElem.contains ? - function( a, b ) { - var adown = a.nodeType === 9 ? a.documentElement : a, - bup = b.parentNode; - return a === bup || !!( bup && bup.nodeType === 1 && adown.contains && adown.contains(bup) ); - } : - function( a, b ) { - while ( (b = b.parentNode) ) { - if ( b === a ) { - return true; - } - } - return false; - }; - -/** - * Utility function for retrieving the text value of an array of DOM nodes - * @param {Array|Element} elem - */ -var getText = Sizzle.getText = function( elem ) { - var node, - ret = "", - i = 0, - nodeType = elem.nodeType; - - if ( nodeType ) { - if ( nodeType === 1 || nodeType === 9 || nodeType === 11 ) { - // Use textContent for elements - // innerText usage removed for consistency of new lines (see #11153) - if ( typeof elem.textContent === "string" ) { - return elem.textContent; - } else { - // Traverse its children - for ( elem = elem.firstChild; elem; elem = elem.nextSibling ) { - ret += getText( elem ); - } - } - } else if ( nodeType === 3 || nodeType === 4 ) { - return elem.nodeValue; - } - // Do not include comment or processing instruction nodes - } else { - - // If no nodeType, this is expected to be an array - for ( ; (node = elem[i]); i++ ) { - // Do not traverse comment nodes - ret += getText( node ); - } - } - return ret; -}; - -Sizzle.attr = function( elem, name ) { - var attr, - xml = isXML( elem ); - - if ( !xml ) { - name = name.toLowerCase(); - } - if ( Expr.attrHandle[ name ] ) { - return Expr.attrHandle[ name ]( elem ); - } - if ( assertAttributes || xml ) { - return elem.getAttribute( name ); - } - attr = elem.getAttributeNode( name ); - return attr ? - typeof elem[ name ] === "boolean" ? - elem[ name ] ? name : null : - attr.specified ? attr.value : null : - null; -}; - -Sizzle.error = function( msg ) { - throw new Error( "Syntax error, unrecognized expression: " + msg ); -}; - -// Check if the JavaScript engine is using some sort of -// optimization where it does not always call our comparision -// function. If that is the case, discard the hasDuplicate value. -// Thus far that includes Google Chrome. -[0, 0].sort(function() { - return (baseHasDuplicate = 0); -}); - - -if ( docElem.compareDocumentPosition ) { - sortOrder = function( a, b ) { if ( a === b ) { hasDuplicate = true; return 0; @@ -4532,10 +4586,8 @@ if ( docElem.compareDocumentPosition ) { a.compareDocumentPosition : a.compareDocumentPosition(b) & 4 ) ? -1 : 1; - }; - -} else { - sortOrder = function( a, b ) { + } : + function( a, b ) { // The nodes are identical, we can exit early if ( a === b ) { hasDuplicate = true; @@ -4595,397 +4647,532 @@ if ( docElem.compareDocumentPosition ) { siblingCheck( ap[i], b, 1 ); }; - siblingCheck = function( a, b, ret ) { - if ( a === b ) { - return ret; - } - - var cur = a.nextSibling; - - while ( cur ) { - if ( cur === b ) { - return -1; - } - - cur = cur.nextSibling; - } - - return 1; - }; -} +// Always assume the presence of duplicates if sort doesn't +// pass them to our comparison function (as in Google Chrome). +[0, 0].sort( sortOrder ); +baseHasDuplicate = !hasDuplicate; // Document sorting and removing duplicates Sizzle.uniqueSort = function( results ) { var elem, - i = 1; + duplicates = [], + i = 1, + j = 0; - if ( sortOrder ) { - hasDuplicate = baseHasDuplicate; - results.sort( sortOrder ); + hasDuplicate = baseHasDuplicate; + results.sort( sortOrder ); - if ( hasDuplicate ) { - for ( ; (elem = results[i]); i++ ) { - if ( elem === results[ i - 1 ] ) { - results.splice( i--, 1 ); - } + if ( hasDuplicate ) { + for ( ; (elem = results[i]); i++ ) { + if ( elem === results[ i - 1 ] ) { + j = duplicates.push( i ); } } + while ( j-- ) { + results.splice( duplicates[ j ], 1 ); + } } return results; }; -function multipleContexts( selector, contexts, results, seed ) { - var i = 0, - len = contexts.length; - for ( ; i < len; i++ ) { - Sizzle( selector, contexts[i], results, seed ); - } -} +Sizzle.error = function( msg ) { + throw new Error( "Syntax error, unrecognized expression: " + msg ); +}; -function handlePOSGroup( selector, posfilter, argument, contexts, seed, not ) { - var results, - fn = Expr.setFilters[ posfilter.toLowerCase() ]; +function tokenize( selector, parseOnly ) { + var matched, match, tokens, type, + soFar, groups, preFilters, + cached = tokenCache[ expando ][ selector + " " ]; - if ( !fn ) { - Sizzle.error( posfilter ); + if ( cached ) { + return parseOnly ? 0 : cached.slice( 0 ); } - if ( selector || !(results = seed) ) { - multipleContexts( selector || "*", contexts, (results = []), seed ); - } + soFar = selector; + groups = []; + preFilters = Expr.preFilter; - return results.length > 0 ? fn( results, argument, not ) : []; -} + while ( soFar ) { -function handlePOS( selector, context, results, seed, groups ) { - var match, not, anchor, ret, elements, currentContexts, part, lastIndex, - i = 0, - len = groups.length, - rpos = matchExpr["POS"], - // This is generated here in case matchExpr["POS"] is extended - rposgroups = new RegExp( "^" + rpos.source + "(?!" + whitespace + ")", "i" ), - // This is for making sure non-participating - // matching groups are represented cross-browser (IE6-8) - setUndefined = function() { - var i = 1, - len = arguments.length - 2; - for ( ; i < len; i++ ) { - if ( arguments[i] === undefined ) { - match[i] = undefined; - } + // Comma and first run + if ( !matched || (match = rcomma.exec( soFar )) ) { + if ( match ) { + // Don't consume trailing commas as valid + soFar = soFar.slice( match[0].length ) || soFar; } - }; + groups.push( tokens = [] ); + } - for ( ; i < len; i++ ) { - // Reset regex index to 0 - rpos.exec(""); - selector = groups[i]; - ret = []; - anchor = 0; - elements = seed; - while ( (match = rpos.exec( selector )) ) { - lastIndex = rpos.lastIndex = match.index + match[0].length; - if ( lastIndex > anchor ) { - part = selector.slice( anchor, match.index ); - anchor = lastIndex; - currentContexts = [ context ]; + matched = false; - if ( rcombinators.test(part) ) { - if ( elements ) { - currentContexts = elements; - } - elements = seed; - } + // Combinators + if ( (match = rcombinators.exec( soFar )) ) { + tokens.push( matched = new Token( match.shift() ) ); + soFar = soFar.slice( matched.length ); - if ( (not = rendsWithNot.test( part )) ) { - part = part.slice( 0, -5 ).replace( rcombinators, "$&*" ); - } + // Cast descendant combinators to space + matched.type = match[0].replace( rtrim, " " ); + } - if ( match.length > 1 ) { - match[0].replace( rposgroups, setUndefined ); - } - elements = handlePOSGroup( part, match[1], match[2], currentContexts, elements, not ); + // Filters + for ( type in Expr.filter ) { + if ( (match = matchExpr[ type ].exec( soFar )) && (!preFilters[ type ] || + (match = preFilters[ type ]( match ))) ) { + + tokens.push( matched = new Token( match.shift() ) ); + soFar = soFar.slice( matched.length ); + matched.type = type; + matched.matches = match; } } - if ( elements ) { - ret = ret.concat( elements ); - - if ( (part = selector.slice( anchor )) && part !== ")" ) { - if ( rcombinators.test(part) ) { - multipleContexts( part, ret, results, seed ); - } else { - Sizzle( part, context, results, seed ? seed.concat(elements) : elements ); - } - } else { - push.apply( results, ret ); - } - } else { - Sizzle( selector, context, results, seed ); + if ( !matched ) { + break; } } - // Do not sort if this is a single filter - return len === 1 ? results : Sizzle.uniqueSort( results ); + // Return the length of the invalid excess + // if we're just parsing + // Otherwise, throw an error or return tokens + return parseOnly ? + soFar.length : + soFar ? + Sizzle.error( selector ) : + // Cache the tokens + tokenCache( selector, groups ).slice( 0 ); } -function tokenize( selector, context, xml ) { - var tokens, soFar, type, - groups = [], - i = 0, - - // Catch obvious selector issues: terminal ")"; nonempty fallback match - // rselector never fails to match *something* - match = rselector.exec( selector ), - matched = !match.pop() && !match.pop(), - selectorGroups = matched && selector.match( rgroups ) || [""], - - preFilters = Expr.preFilter, - filters = Expr.filter, - checkContext = !xml && context !== document; - - for ( ; (soFar = selectorGroups[i]) != null && matched; i++ ) { - groups.push( tokens = [] ); - - // Need to make sure we're within a narrower context if necessary - // Adding a descendant combinator will generate what is needed - if ( checkContext ) { - soFar = " " + soFar; - } - - while ( soFar ) { - matched = false; - - // Combinators - if ( (match = rcombinators.exec( soFar )) ) { - soFar = soFar.slice( match[0].length ); - - // Cast descendant combinators to space - matched = tokens.push({ part: match.pop().replace( rtrim, " " ), captures: match }); - } - - // Filters - for ( type in filters ) { - if ( (match = matchExpr[ type ].exec( soFar )) && (!preFilters[ type ] || - (match = preFilters[ type ]( match, context, xml )) ) ) { - - soFar = soFar.slice( match.shift().length ); - matched = tokens.push({ part: type, captures: match }); - } - } - - if ( !matched ) { - break; - } - } - } - - if ( !matched ) { - Sizzle.error( selector ); - } - - return groups; -} - -function addCombinator( matcher, combinator, context ) { +function addCombinator( matcher, combinator, base ) { var dir = combinator.dir, + checkNonElements = base && combinator.dir === "parentNode", doneName = done++; - if ( !matcher ) { - // If there is no matcher to check, check against the context - matcher = function( elem ) { - return elem === context; - }; - } return combinator.first ? - function( elem, context ) { + // Check against closest ancestor/preceding element + function( elem, context, xml ) { while ( (elem = elem[ dir ]) ) { - if ( elem.nodeType === 1 ) { - return matcher( elem, context ) && elem; + if ( checkNonElements || elem.nodeType === 1 ) { + return matcher( elem, context, xml ); } } } : - function( elem, context ) { - var cache, - dirkey = doneName + "." + dirruns, - cachedkey = dirkey + "." + cachedruns; - while ( (elem = elem[ dir ]) ) { - if ( elem.nodeType === 1 ) { - if ( (cache = elem[ expando ]) === cachedkey ) { - return elem.sizset; - } else if ( typeof cache === "string" && cache.indexOf(dirkey) === 0 ) { - if ( elem.sizset ) { + + // Check against all ancestor/preceding elements + function( elem, context, xml ) { + // We can't set arbitrary data on XML nodes, so they don't benefit from dir caching + if ( !xml ) { + var cache, + dirkey = dirruns + " " + doneName + " ", + cachedkey = dirkey + cachedruns; + while ( (elem = elem[ dir ]) ) { + if ( checkNonElements || elem.nodeType === 1 ) { + if ( (cache = elem[ expando ]) === cachedkey ) { + return elem.sizset; + } else if ( typeof cache === "string" && cache.indexOf(dirkey) === 0 ) { + if ( elem.sizset ) { + return elem; + } + } else { + elem[ expando ] = cachedkey; + if ( matcher( elem, context, xml ) ) { + elem.sizset = true; + return elem; + } + elem.sizset = false; + } + } + } + } else { + while ( (elem = elem[ dir ]) ) { + if ( checkNonElements || elem.nodeType === 1 ) { + if ( matcher( elem, context, xml ) ) { return elem; } - } else { - elem[ expando ] = cachedkey; - if ( matcher( elem, context ) ) { - elem.sizset = true; - return elem; - } - elem.sizset = false; } } } }; } -function addMatcher( higher, deeper ) { - return higher ? - function( elem, context ) { - var result = deeper( elem, context ); - return result && higher( result === true ? elem : result, context ); +function elementMatcher( matchers ) { + return matchers.length > 1 ? + function( elem, context, xml ) { + var i = matchers.length; + while ( i-- ) { + if ( !matchers[i]( elem, context, xml ) ) { + return false; + } + } + return true; } : - deeper; + matchers[0]; } -// ["TAG", ">", "ID", " ", "CLASS"] -function matcherFromTokens( tokens, context, xml ) { - var token, matcher, - i = 0; +function condense( unmatched, map, filter, context, xml ) { + var elem, + newUnmatched = [], + i = 0, + len = unmatched.length, + mapped = map != null; - for ( ; (token = tokens[i]); i++ ) { - if ( Expr.relative[ token.part ] ) { - matcher = addCombinator( matcher, Expr.relative[ token.part ], context ); - } else { - token.captures.push( context, xml ); - matcher = addMatcher( matcher, Expr.filter[ token.part ].apply( null, token.captures ) ); - } - } - - return matcher; -} - -function matcherFromGroupMatchers( matchers ) { - return function( elem, context ) { - var matcher, - j = 0; - for ( ; (matcher = matchers[j]); j++ ) { - if ( matcher(elem, context) ) { - return true; + for ( ; i < len; i++ ) { + if ( (elem = unmatched[i]) ) { + if ( !filter || filter( elem, context, xml ) ) { + newUnmatched.push( elem ); + if ( mapped ) { + map.push( i ); + } } } - return false; - }; + } + + return newUnmatched; } -var compile = Sizzle.compile = function( selector, context, xml ) { - var tokens, group, i, - cached = compilerCache[ selector ]; +function setMatcher( preFilter, selector, matcher, postFilter, postFinder, postSelector ) { + if ( postFilter && !postFilter[ expando ] ) { + postFilter = setMatcher( postFilter ); + } + if ( postFinder && !postFinder[ expando ] ) { + postFinder = setMatcher( postFinder, postSelector ); + } + return markFunction(function( seed, results, context, xml ) { + var temp, i, elem, + preMap = [], + postMap = [], + preexisting = results.length, - // Return a cached group function if already generated (context dependent) - if ( cached && cached.context === context ) { - return cached; + // Get initial elements from seed or context + elems = seed || multipleContexts( selector || "*", context.nodeType ? [ context ] : context, [] ), + + // Prefilter to get matcher input, preserving a map for seed-results synchronization + matcherIn = preFilter && ( seed || !selector ) ? + condense( elems, preMap, preFilter, context, xml ) : + elems, + + matcherOut = matcher ? + // If we have a postFinder, or filtered seed, or non-seed postFilter or preexisting results, + postFinder || ( seed ? preFilter : preexisting || postFilter ) ? + + // ...intermediate processing is necessary + [] : + + // ...otherwise use results directly + results : + matcherIn; + + // Find primary matches + if ( matcher ) { + matcher( matcherIn, matcherOut, context, xml ); + } + + // Apply postFilter + if ( postFilter ) { + temp = condense( matcherOut, postMap ); + postFilter( temp, [], context, xml ); + + // Un-match failing elements by moving them back to matcherIn + i = temp.length; + while ( i-- ) { + if ( (elem = temp[i]) ) { + matcherOut[ postMap[i] ] = !(matcherIn[ postMap[i] ] = elem); + } + } + } + + if ( seed ) { + if ( postFinder || preFilter ) { + if ( postFinder ) { + // Get the final matcherOut by condensing this intermediate into postFinder contexts + temp = []; + i = matcherOut.length; + while ( i-- ) { + if ( (elem = matcherOut[i]) ) { + // Restore matcherIn since elem is not yet a final match + temp.push( (matcherIn[i] = elem) ); + } + } + postFinder( null, (matcherOut = []), temp, xml ); + } + + // Move matched elements from seed to results to keep them synchronized + i = matcherOut.length; + while ( i-- ) { + if ( (elem = matcherOut[i]) && + (temp = postFinder ? indexOf.call( seed, elem ) : preMap[i]) > -1 ) { + + seed[temp] = !(results[temp] = elem); + } + } + } + + // Add elements to results, through postFinder if defined + } else { + matcherOut = condense( + matcherOut === results ? + matcherOut.splice( preexisting, matcherOut.length ) : + matcherOut + ); + if ( postFinder ) { + postFinder( null, results, matcherOut, xml ); + } else { + push.apply( results, matcherOut ); + } + } + }); +} + +function matcherFromTokens( tokens ) { + var checkContext, matcher, j, + len = tokens.length, + leadingRelative = Expr.relative[ tokens[0].type ], + implicitRelative = leadingRelative || Expr.relative[" "], + i = leadingRelative ? 1 : 0, + + // The foundational matcher ensures that elements are reachable from top-level context(s) + matchContext = addCombinator( function( elem ) { + return elem === checkContext; + }, implicitRelative, true ), + matchAnyContext = addCombinator( function( elem ) { + return indexOf.call( checkContext, elem ) > -1; + }, implicitRelative, true ), + matchers = [ function( elem, context, xml ) { + return ( !leadingRelative && ( xml || context !== outermostContext ) ) || ( + (checkContext = context).nodeType ? + matchContext( elem, context, xml ) : + matchAnyContext( elem, context, xml ) ); + } ]; + + for ( ; i < len; i++ ) { + if ( (matcher = Expr.relative[ tokens[i].type ]) ) { + matchers = [ addCombinator( elementMatcher( matchers ), matcher ) ]; + } else { + matcher = Expr.filter[ tokens[i].type ].apply( null, tokens[i].matches ); + + // Return special upon seeing a positional matcher + if ( matcher[ expando ] ) { + // Find the next relative operator (if any) for proper handling + j = ++i; + for ( ; j < len; j++ ) { + if ( Expr.relative[ tokens[j].type ] ) { + break; + } + } + return setMatcher( + i > 1 && elementMatcher( matchers ), + i > 1 && tokens.slice( 0, i - 1 ).join("").replace( rtrim, "$1" ), + matcher, + i < j && matcherFromTokens( tokens.slice( i, j ) ), + j < len && matcherFromTokens( (tokens = tokens.slice( j )) ), + j < len && tokens.join("") + ); + } + matchers.push( matcher ); + } } - // Generate a function of recursive functions that can be used to check each element - group = tokenize( selector, context, xml ); - for ( i = 0; (tokens = group[i]); i++ ) { - group[i] = matcherFromTokens( tokens, context, xml ); - } + return elementMatcher( matchers ); +} - // Cache the compiled function - cached = compilerCache[ selector ] = matcherFromGroupMatchers( group ); - cached.context = context; - cached.runs = cached.dirruns = 0; - cachedSelectors.push( selector ); - // Ensure only the most recent are cached - if ( cachedSelectors.length > Expr.cacheLength ) { - delete compilerCache[ cachedSelectors.shift() ]; +function matcherFromGroupMatchers( elementMatchers, setMatchers ) { + var bySet = setMatchers.length > 0, + byElement = elementMatchers.length > 0, + superMatcher = function( seed, context, xml, results, expandContext ) { + var elem, j, matcher, + setMatched = [], + matchedCount = 0, + i = "0", + unmatched = seed && [], + outermost = expandContext != null, + contextBackup = outermostContext, + // We must always have either seed elements or context + elems = seed || byElement && Expr.find["TAG"]( "*", expandContext && context.parentNode || context ), + // Nested matchers should use non-integer dirruns + dirrunsUnique = (dirruns += contextBackup == null ? 1 : Math.E); + + if ( outermost ) { + outermostContext = context !== document && context; + cachedruns = superMatcher.el; + } + + // Add elements passing elementMatchers directly to results + for ( ; (elem = elems[i]) != null; i++ ) { + if ( byElement && elem ) { + for ( j = 0; (matcher = elementMatchers[j]); j++ ) { + if ( matcher( elem, context, xml ) ) { + results.push( elem ); + break; + } + } + if ( outermost ) { + dirruns = dirrunsUnique; + cachedruns = ++superMatcher.el; + } + } + + // Track unmatched elements for set filters + if ( bySet ) { + // They will have gone through all possible matchers + if ( (elem = !matcher && elem) ) { + matchedCount--; + } + + // Lengthen the array for every element, matched or not + if ( seed ) { + unmatched.push( elem ); + } + } + } + + // Apply set filters to unmatched elements + matchedCount += i; + if ( bySet && i !== matchedCount ) { + for ( j = 0; (matcher = setMatchers[j]); j++ ) { + matcher( unmatched, setMatched, context, xml ); + } + + if ( seed ) { + // Reintegrate element matches to eliminate the need for sorting + if ( matchedCount > 0 ) { + while ( i-- ) { + if ( !(unmatched[i] || setMatched[i]) ) { + setMatched[i] = pop.call( results ); + } + } + } + + // Discard index placeholder values to get only actual matches + setMatched = condense( setMatched ); + } + + // Add matches to results + push.apply( results, setMatched ); + + // Seedless set matches succeeding multiple successful matchers stipulate sorting + if ( outermost && !seed && setMatched.length > 0 && + ( matchedCount + setMatchers.length ) > 1 ) { + + Sizzle.uniqueSort( results ); + } + } + + // Override manipulation of globals by nested matchers + if ( outermost ) { + dirruns = dirrunsUnique; + outermostContext = contextBackup; + } + + return unmatched; + }; + + superMatcher.el = 0; + return bySet ? + markFunction( superMatcher ) : + superMatcher; +} + +compile = Sizzle.compile = function( selector, group /* Internal Use Only */ ) { + var i, + setMatchers = [], + elementMatchers = [], + cached = compilerCache[ expando ][ selector + " " ]; + + if ( !cached ) { + // Generate a function of recursive functions that can be used to check each element + if ( !group ) { + group = tokenize( selector ); + } + i = group.length; + while ( i-- ) { + cached = matcherFromTokens( group[i] ); + if ( cached[ expando ] ) { + setMatchers.push( cached ); + } else { + elementMatchers.push( cached ); + } + } + + // Cache the compiled function + cached = compilerCache( selector, matcherFromGroupMatchers( elementMatchers, setMatchers ) ); } return cached; }; -Sizzle.matches = function( expr, elements ) { - return Sizzle( expr, null, null, elements ); -}; - -Sizzle.matchesSelector = function( elem, expr ) { - return Sizzle( expr, null, null, [ elem ] ).length > 0; -}; - -var select = function( selector, context, results, seed, xml ) { - // Remove excessive whitespace - selector = selector.replace( rtrim, "$1" ); - var elements, matcher, i, len, elem, token, - type, findContext, notTokens, - match = selector.match( rgroups ), - tokens = selector.match( rtokens ), - contextNodeType = context.nodeType; - - // POS handling - if ( matchExpr["POS"].test(selector) ) { - return handlePOS( selector, context, results, seed, match ); +function multipleContexts( selector, contexts, results ) { + var i = 0, + len = contexts.length; + for ( ; i < len; i++ ) { + Sizzle( selector, contexts[i], results ); } + return results; +} - if ( seed ) { - elements = slice.call( seed, 0 ); +function select( selector, context, results, seed, xml ) { + var i, tokens, token, type, find, + match = tokenize( selector ), + j = match.length; - // To maintain document order, only narrow the - // set if there is one group - } else if ( match && match.length === 1 ) { + if ( !seed ) { + // Try to minimize operations if there is only one group + if ( match.length === 1 ) { - // Take a shortcut and set the context if the root selector is an ID - if ( tokens.length > 1 && contextNodeType === 9 && !xml && - (match = matchExpr["ID"].exec( tokens[0] )) ) { + // Take a shortcut and set the context if the root selector is an ID + tokens = match[0] = match[0].slice( 0 ); + if ( tokens.length > 2 && (token = tokens[0]).type === "ID" && + context.nodeType === 9 && !xml && + Expr.relative[ tokens[1].type ] ) { - context = Expr.find["ID"]( match[1], context, xml )[0]; - if ( !context ) { - return results; - } - - selector = selector.slice( tokens.shift().length ); - } - - findContext = ( (match = rsibling.exec( tokens[0] )) && !match.index && context.parentNode ) || context; - - // Get the last token, excluding :not - notTokens = tokens.pop(); - token = notTokens.split(":not")[0]; - - for ( i = 0, len = Expr.order.length; i < len; i++ ) { - type = Expr.order[i]; - - if ( (match = matchExpr[ type ].exec( token )) ) { - elements = Expr.find[ type ]( (match[1] || "").replace( rbackslash, "" ), findContext, xml ); - - if ( elements == null ) { - continue; + context = Expr.find["ID"]( token.matches[0].replace( rbackslash, "" ), context, xml )[0]; + if ( !context ) { + return results; } - if ( token === notTokens ) { - selector = selector.slice( 0, selector.length - notTokens.length ) + - token.replace( matchExpr[ type ], "" ); + selector = selector.slice( tokens.shift().length ); + } - if ( !selector ) { - push.apply( results, slice.call(elements, 0) ); + // Fetch a seed set for right-to-left matching + for ( i = matchExpr["POS"].test( selector ) ? -1 : tokens.length - 1; i >= 0; i-- ) { + token = tokens[i]; + + // Abort if we hit a combinator + if ( Expr.relative[ (type = token.type) ] ) { + break; + } + if ( (find = Expr.find[ type ]) ) { + // Search, expanding context for leading sibling combinators + if ( (seed = find( + token.matches[0].replace( rbackslash, "" ), + rsibling.test( tokens[0].type ) && context.parentNode || context, + xml + )) ) { + + // If seed is empty or no tokens remain, we can return early + tokens.splice( i, 1 ); + selector = seed.length && tokens.join(""); + if ( !selector ) { + push.apply( results, slice.call( seed, 0 ) ); + return results; + } + + break; } } - break; - } - } - } - - // Only loop over the given elements once - // If selector is empty, we're already done - if ( selector ) { - matcher = compile( selector, context, xml ); - dirruns = matcher.dirruns++; - - if ( elements == null ) { - elements = Expr.find["TAG"]( "*", (rsibling.test( selector ) && context.parentNode) || context ); - } - for ( i = 0; (elem = elements[i]); i++ ) { - cachedruns = matcher.runs++; - if ( matcher(elem, context) ) { - results.push( elem ); } } } + // Compile and execute a filtering function + // Provide `match` to avoid retokenization if we modified the selector above + compile( selector, match )( + seed, + context, + xml, + results, + rsibling.test( selector ) + ); return results; -}; +} if ( document.querySelectorAll ) { (function() { @@ -4993,11 +5180,15 @@ if ( document.querySelectorAll ) { oldSelect = select, rescape = /'|\\/g, rattributeQuotes = /\=[\x20\t\r\n\f]*([^'"\]]*)[\x20\t\r\n\f]*\]/g, - rbuggyQSA = [], + + // qSa(:focus) reports false when true (Chrome 21), no need to also add to buggyMatches since matches checks buggyQSA + // A support test would require too much code (would include document ready) + rbuggyQSA = [ ":focus" ], + // matchesSelector(:active) reports false when true (IE9/Opera 11.5) // A support test would require too much code (would include document ready) // just skip matchesSelector for :active - rbuggyMatches = [":active"], + rbuggyMatches = [ ":active" ], matches = docElem.matchesSelector || docElem.mozMatchesSelector || docElem.webkitMatchesSelector || @@ -5007,7 +5198,12 @@ if ( document.querySelectorAll ) { // Build QSA regex // Regex strategy adopted from Diego Perini assert(function( div ) { - div.innerHTML = ""; + // Select is set to empty string on purpose + // This is to test IE's treatment of not explictly + // setting a boolean content attribute, + // since its presence should be enough + // http://bugs.jquery.com/ticket/12359 + div.innerHTML = ""; // IE8 - Some boolean attributes are not treated correctly if ( !div.querySelectorAll("[selected]").length ) { @@ -5033,42 +5229,52 @@ if ( document.querySelectorAll ) { // FF 3.5 - :enabled/:disabled and hidden elements (hidden elements are still enabled) // IE8 throws error here (do not put tests after this one) - div.innerHTML = ""; + div.innerHTML = ""; if ( !div.querySelectorAll(":enabled").length ) { rbuggyQSA.push(":enabled", ":disabled"); } }); - rbuggyQSA = rbuggyQSA.length && new RegExp( rbuggyQSA.join("|") ); + // rbuggyQSA always contains :focus, so no need for a length check + rbuggyQSA = /* rbuggyQSA.length && */ new RegExp( rbuggyQSA.join("|") ); select = function( selector, context, results, seed, xml ) { // Only use querySelectorAll when not filtering, // when this is not xml, // and when no QSA bugs apply - if ( !seed && !xml && (!rbuggyQSA || !rbuggyQSA.test( selector )) ) { - if ( context.nodeType === 9 ) { - try { - push.apply( results, slice.call(context.querySelectorAll( selector ), 0) ); - return results; - } catch(qsaError) {} + if ( !seed && !xml && !rbuggyQSA.test( selector ) ) { + var groups, i, + old = true, + nid = expando, + newContext = context, + newSelector = context.nodeType === 9 && selector; + // qSA works strangely on Element-rooted queries // We can work around this by specifying an extra ID on the root // and working up from there (Thanks to Andrew Dupont for the technique) // IE 8 doesn't work on object elements - } else if ( context.nodeType === 1 && context.nodeName.toLowerCase() !== "object" ) { - var old = context.getAttribute("id"), - nid = old || expando, - newContext = rsibling.test( selector ) && context.parentNode || context; + if ( context.nodeType === 1 && context.nodeName.toLowerCase() !== "object" ) { + groups = tokenize( selector ); - if ( old ) { - nid = nid.replace( rescape, "\\$&" ); + if ( (old = context.getAttribute("id")) ) { + nid = old.replace( rescape, "\\$&" ); } else { context.setAttribute( "id", nid ); } + nid = "[id='" + nid + "'] "; + i = groups.length; + while ( i-- ) { + groups[i] = nid + groups[i].join(""); + } + newContext = rsibling.test( selector ) && context.parentNode || context; + newSelector = groups.join(","); + } + + if ( newSelector ) { try { push.apply( results, slice.call( newContext.querySelectorAll( - selector.replace( rgroups, "[id='" + nid + "'] $&" ) + newSelector ), 0 ) ); return results; } catch(qsaError) { @@ -5093,11 +5299,11 @@ if ( document.querySelectorAll ) { // Gecko does not error, returns false instead try { matches.call( div, "[test!='']:sizzle" ); - rbuggyMatches.push( Expr.match.PSEUDO ); + rbuggyMatches.push( "!=", pseudos ); } catch ( e ) {} }); - // rbuggyMatches always contains :active, so no need for a length check + // rbuggyMatches always contains :active and :focus, so no need for a length check rbuggyMatches = /* rbuggyMatches.length && */ new RegExp( rbuggyMatches.join("|") ); Sizzle.matchesSelector = function( elem, expr ) { @@ -5105,7 +5311,7 @@ if ( document.querySelectorAll ) { expr = expr.replace( rattributeQuotes, "='$1']" ); // rbuggyMatches always contains :active, so no need for an existence check - if ( !isXML( elem ) && !rbuggyMatches.test( expr ) && (!rbuggyQSA || !rbuggyQSA.test( expr )) ) { + if ( !isXML( elem ) && !rbuggyMatches.test( expr ) && !rbuggyQSA.test( expr ) ) { try { var ret = matches.call( elem, expr ); @@ -5125,6 +5331,14 @@ if ( document.querySelectorAll ) { })(); } +// Deprecated +Expr.pseudos["nth"] = Expr.pseudos["eq"]; + +// Back-compat +function setFilters() {} +Expr.filters = setFilters.prototype = Expr.pseudos; +Expr.setFilters = new setFilters(); + // Override sizzle attribute retrieval Sizzle.attr = jQuery.attr; jQuery.find = Sizzle; @@ -5923,15 +6137,11 @@ jQuery.buildFragment = function( args, context, scripts ) { first = args[ 0 ]; // Set context from what may come in as undefined or a jQuery collection or a node + // Updated to fix #12266 where accessing context[0] could throw an exception in IE9/10 & + // also doubles as fix for #8950 where plain objects caused createDocumentFragment exception context = context || document; - context = (context[0] || context).ownerDocument || context[0] || context; - - // Ensure that an attr object doesn't incorrectly stand in as a document object - // Chrome and Firefox seem to allow this to occur and will throw exception - // Fixes #8950 - if ( typeof context.createDocumentFragment === "undefined" ) { - context = document; - } + context = !context.nodeType && context[0] || context; + context = context.ownerDocument || context; // Only cache "small" (1/2 KB) HTML strings that are associated with the main document // Cloning options loses the selected state, so don't cache them @@ -6076,8 +6286,8 @@ jQuery.extend({ }, clean: function( elems, context, fragment, scripts ) { - var j, safe, elem, tag, wrap, depth, div, hasBody, tbody, len, handleScript, jsTags, - i = 0, + var i, j, elem, tag, wrap, depth, div, hasBody, tbody, len, handleScript, jsTags, + safe = context === document && safeFragment, ret = []; // Ensure that context is a document @@ -6086,7 +6296,7 @@ jQuery.extend({ } // Use the already-created safe fragment if context permits - for ( safe = context === document && safeFragment; (elem = elems[i]) != null; i++ ) { + for ( i = 0; (elem = elems[i]) != null; i++ ) { if ( typeof elem === "number" ) { elem += ""; } @@ -6102,7 +6312,8 @@ jQuery.extend({ } else { // Ensure a safe container in which to render the html safe = safe || createSafeFragment( context ); - div = div || safe.appendChild( context.createElement("div") ); + div = context.createElement("div"); + safe.appendChild( div ); // Fix "XHTML"-style tags in all browsers elem = elem.replace(rxhtmlTag, "<$1>"); @@ -6145,21 +6356,20 @@ jQuery.extend({ elem = div.childNodes; - // Remember the top-level container for proper cleanup - div = safe.lastChild; + // Take out of fragment container (we need a fresh div each time) + div.parentNode.removeChild( div ); } } if ( elem.nodeType ) { ret.push( elem ); } else { - ret = jQuery.merge( ret, elem ); + jQuery.merge( ret, elem ); } } // Fix #11356: Clear elements from safeFragment if ( div ) { - safe.removeChild( div ); elem = div = safe = null; } @@ -6294,9 +6504,10 @@ if ( matched.browser ) { browser.version = matched.version; } -// Deprecated, use jQuery.browser.webkit instead -// Maintained for back-compat only -if ( browser.webkit ) { +// Chrome is Webkit, but Webkit is also Safari. +if ( browser.chrome ) { + browser.webkit = true; +} else if ( browser.webkit ) { browser.safari = true; } @@ -6322,23 +6533,25 @@ jQuery.sub = function() { var rootjQuerySub = jQuerySub(document); return jQuerySub; }; - + })(); var curCSS, iframe, iframeDoc, ralpha = /alpha\([^)]*\)/i, ropacity = /opacity=([^)]*)/, rposition = /^(top|right|bottom|left)$/, + // swappable if display is none or starts with table except "table", "table-cell", or "table-caption" + // see here for display values: https://developer.mozilla.org/en-US/docs/CSS/display + rdisplayswap = /^(none|table(?!-c[ea]).+)/, rmargin = /^margin/, rnumsplit = new RegExp( "^(" + core_pnum + ")(.*)$", "i" ), rnumnonpx = new RegExp( "^(" + core_pnum + ")(?!px)[a-z%]+$", "i" ), rrelNum = new RegExp( "^([-+])=(" + core_pnum + ")", "i" ), - elemdisplay = {}, + elemdisplay = { BODY: "block" }, cssShow = { position: "absolute", visibility: "hidden", display: "block" }, cssNormalTransform = { letterSpacing: 0, - fontWeight: 400, - lineHeight: 1 + fontWeight: 400 }, cssExpand = [ "Top", "Right", "Bottom", "Left" ], @@ -6604,18 +6817,20 @@ jQuery.extend({ } }); -// NOTE: To any future maintainer, we've used both window.getComputedStyle -// and getComputedStyle here to produce a better gzip size +// NOTE: To any future maintainer, we've window.getComputedStyle +// because jsdom on node.js will break without it. if ( window.getComputedStyle ) { curCSS = function( elem, name ) { var ret, width, minWidth, maxWidth, - computed = getComputedStyle( elem, null ), + computed = window.getComputedStyle( elem, null ), style = elem.style; if ( computed ) { - ret = computed[ name ]; - if ( ret === "" && !jQuery.contains( elem.ownerDocument.documentElement, elem ) ) { + // getPropertyValue is only needed for .css('filter') in IE9, see #12537 + ret = computed.getPropertyValue( name ) || computed[ name ]; + + if ( ret === "" && !jQuery.contains( elem.ownerDocument, elem ) ) { ret = jQuery.style( elem, name ); } @@ -6738,7 +6953,10 @@ function getWidthOrHeight( elem, name, extra ) { valueIsBorderBox = true, isBorderBox = jQuery.support.boxSizing && jQuery.css( elem, "boxSizing" ) === "border-box"; - if ( val <= 0 ) { + // some non-html elements return undefined for offsetWidth, so check for null/undefined + // svg - https://bugzilla.mozilla.org/show_bug.cgi?id=649285 + // MathML - https://bugzilla.mozilla.org/show_bug.cgi?id=491668 + if ( val <= 0 || val == null ) { // Fall back to computed then uncomputed css if necessary val = curCSS( elem, name ); if ( val < 0 || val == null ) { @@ -6817,12 +7035,14 @@ jQuery.each([ "height", "width" ], function( i, name ) { jQuery.cssHooks[ name ] = { get: function( elem, computed, extra ) { if ( computed ) { - if ( elem.offsetWidth !== 0 || curCSS( elem, "display" ) !== "none" ) { - return getWidthOrHeight( elem, name, extra ); - } else { + // certain elements can have dimension info if we invisibly show them + // however, it must have a current display style that would benefit from this + if ( elem.offsetWidth === 0 && rdisplayswap.test( curCSS( elem, "display" ) ) ) { return jQuery.swap( elem, cssShow, function() { return getWidthOrHeight( elem, name, extra ); }); + } else { + return getWidthOrHeight( elem, name, extra ); } } }, @@ -7056,10 +7276,10 @@ function buildParams( prefix, obj, traditional, add ) { add( prefix, obj ); } } -var // Document location - ajaxLocation, - // Document location segments +var + // Document location ajaxLocParts, + ajaxLocation, rhash = /#.*$/, rheaders = /^(.*?):[ \t]*([^\r\n]*)\r?$/mg, // IE leaves an \r character at EOL @@ -7228,7 +7448,7 @@ jQuery.fn.load = function( url, params, callback ) { params = undefined; // Otherwise, build a param string - } else if ( typeof params === "object" ) { + } else if ( params && typeof params === "object" ) { type = "POST"; } @@ -7576,7 +7796,7 @@ jQuery.extend({ // Set data for the fake xhr object jqXHR.status = status; - jqXHR.statusText = "" + ( nativeStatusText || statusText ); + jqXHR.statusText = ( nativeStatusText || statusText ) + ""; // Success/Error if ( isSuccess ) { @@ -7636,11 +7856,11 @@ jQuery.extend({ // Extract dataTypes list s.dataTypes = jQuery.trim( s.dataType || "*" ).toLowerCase().split( core_rspace ); - // Determine if a cross-domain request is in order + // A cross-domain request is in order when we have a protocol:host:port mismatch if ( s.crossDomain == null ) { parts = rurl.exec( s.url.toLowerCase() ); s.crossDomain = !!( parts && - ( parts[ 1 ] != ajaxLocParts[ 1 ] || parts[ 2 ] != ajaxLocParts[ 2 ] || + ( parts[ 1 ] !== ajaxLocParts[ 1 ] || parts[ 2 ] !== ajaxLocParts[ 2 ] || ( parts[ 3 ] || ( parts[ 1 ] === "http:" ? 80 : 443 ) ) != ( ajaxLocParts[ 3 ] || ( ajaxLocParts[ 1 ] === "http:" ? 80 : 443 ) ) ) ); @@ -8262,7 +8482,7 @@ if ( jQuery.support.ajax ) { // on any attempt to access responseText (#11426) try { responses.text = xhr.responseText; - } catch( _ ) { + } catch( e ) { } // Firefox throws an exception when accessing @@ -8338,12 +8558,13 @@ var fxNow, timerId, animationPrefilters = [ defaultPrefilter ], tweeners = { "*": [function( prop, value ) { - var end, unit, prevScale, + var end, unit, tween = this.createTween( prop, value ), parts = rfxnum.exec( value ), target = tween.cur(), start = +target || 0, - scale = 1; + scale = 1, + maxIterations = 20; if ( parts ) { end = +parts[2]; @@ -8359,17 +8580,15 @@ var fxNow, timerId, do { // If previous iteration zeroed out, double until we get *something* // Use a string for doubling factor so we don't accidentally see scale as unchanged below - prevScale = scale = scale || ".5"; + scale = scale || ".5"; // Adjust and apply start = start / scale; jQuery.style( tween.elem, prop, start + unit ); - // Update scale, tolerating zeroes from tween.cur() - scale = tween.cur() / target; - - // Stop looping if we've hit the mark or scale is unchanged - } while ( scale !== 1 && scale !== prevScale ); + // Update scale, tolerating zero or NaN from tween.cur() + // And breaking the loop if scale is unchanged or perfect, or if we've just had enough + } while ( scale !== (scale = tween.cur() / target) && scale !== 1 && --maxIterations ); } tween.unit = unit; @@ -8416,7 +8635,9 @@ function Animation( elem, properties, options ) { tick = function() { var currentTime = fxNow || createFxNow(), remaining = Math.max( 0, animation.startTime + animation.duration - currentTime ), - percent = 1 - ( remaining / animation.duration || 0 ), + // archaic crash bug won't allow us to use 1 - ( 0.5 || 0 ) (#12497) + temp = remaining / animation.duration || 0, + percent = 1 - temp, index = 0, length = animation.tweens.length; @@ -8568,7 +8789,7 @@ jQuery.Animation = jQuery.extend( Animation, { }); function defaultPrefilter( elem, props, opts ) { - var index, prop, value, length, dataShow, tween, hooks, oldfire, + var index, prop, value, length, dataShow, toggle, tween, hooks, oldfire, anim = this, style = elem.style, orig = {}, @@ -8642,6 +8863,7 @@ function defaultPrefilter( elem, props, opts ) { value = props[ index ]; if ( rfxtypes.exec( value ) ) { delete props[ index ]; + toggle = toggle || value === "toggle"; if ( value === ( hidden ? "hide" : "show" ) ) { continue; } @@ -8652,6 +8874,14 @@ function defaultPrefilter( elem, props, opts ) { length = handled.length; if ( length ) { dataShow = jQuery._data( elem, "fxshow" ) || jQuery._data( elem, "fxshow", {} ); + if ( "hidden" in dataShow ) { + hidden = dataShow.hidden; + } + + // store state if its toggle - enables .stop().toggle() to "reverse" + if ( toggle ) { + dataShow.hidden = !hidden; + } if ( hidden ) { jQuery( elem ).show(); } else { @@ -8709,7 +8939,13 @@ Tween.prototype = { var eased, hooks = Tween.propHooks[ this.prop ]; - this.pos = eased = jQuery.easing[ this.easing ]( percent, this.options.duration * percent, 0, 1, this.options.duration ); + if ( this.options.duration ) { + this.pos = eased = jQuery.easing[ this.easing ]( + percent, this.options.duration * percent, 0, 1, this.options.duration + ); + } else { + this.pos = eased = percent; + } this.now = ( this.end - this.start ) * eased + this.start; if ( this.options.step ) { @@ -8867,6 +9103,7 @@ function genFx( type, includeWidth ) { // if we include width, step value is 1 to do all cssExpand values, // if we don't include width, step value is 2 to skip over Left and Right + includeWidth = includeWidth? 1 : 0; for( ; i < 4 ; i += 2 - includeWidth ) { which = cssExpand[ i ]; attrs[ "margin" + which ] = attrs[ "padding" + which ] = type; @@ -8941,6 +9178,8 @@ jQuery.fx.tick = function() { timers = jQuery.timers, i = 0; + fxNow = jQuery.now(); + for ( ; i < timers.length; i++ ) { timer = timers[ i ]; // Checks the timer has not already been removed @@ -8952,6 +9191,7 @@ jQuery.fx.tick = function() { if ( !timers.length ) { jQuery.fx.stop(); } + fxNow = undefined; }; jQuery.fx.timer = function( timer ) { @@ -8995,7 +9235,8 @@ jQuery.fn.offset = function( options ) { }); } - var box, docElem, body, win, clientTop, clientLeft, scrollTop, scrollLeft, top, left, + var docElem, body, win, clientTop, clientLeft, scrollTop, scrollLeft, + box = { top: 0, left: 0 }, elem = this[ 0 ], doc = elem && elem.ownerDocument; @@ -9009,21 +9250,25 @@ jQuery.fn.offset = function( options ) { docElem = doc.documentElement; - // Make sure we're not dealing with a disconnected DOM node + // Make sure it's not a disconnected DOM node if ( !jQuery.contains( docElem, elem ) ) { - return { top: 0, left: 0 }; + return box; } - box = elem.getBoundingClientRect(); + // If we don't have gBCR, just use 0,0 rather than error + // BlackBerry 5, iOS 3 (original iPhone) + if ( typeof elem.getBoundingClientRect !== "undefined" ) { + box = elem.getBoundingClientRect(); + } win = getWindow( doc ); clientTop = docElem.clientTop || body.clientTop || 0; clientLeft = docElem.clientLeft || body.clientLeft || 0; scrollTop = win.pageYOffset || docElem.scrollTop; scrollLeft = win.pageXOffset || docElem.scrollLeft; - top = box.top + scrollTop - clientTop; - left = box.left + scrollLeft - clientLeft; - - return { top: top, left: left }; + return { + top: box.top + scrollTop - clientTop, + left: box.left + scrollLeft - clientLeft + }; }; jQuery.offset = { @@ -9201,7 +9446,7 @@ jQuery.each( { Height: "height", Width: "width" }, function( name, type ) { // Set width or height on the element jQuery.style( elem, type, value, extra ); - }, type, chainable ? margin : undefined, chainable ); + }, type, chainable ? margin : undefined, chainable, null ); }; }); }); From 3ebb520327a72c3809c924d3e24cef47f4a008e3 Mon Sep 17 00:00:00 2001 From: coderaiser Date: Fri, 28 Dec 2012 06:07:02 -0500 Subject: [PATCH 072/347] refactored --- config.json | 2 +- lib/client/dom.js | 147 +++++++++++++++++++--------------------------- lib/server.js | 8 +-- 3 files changed, 66 insertions(+), 91 deletions(-) diff --git a/config.json b/config.json index 270056fa..a0a1012c 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/dom.js b/lib/client/dom.js index cc98639a..4d616147 100644 --- a/lib/client/dom.js +++ b/lib/client/dom.js @@ -7,7 +7,7 @@ var CloudCommander, Util, DOM, CloudFunc; /* PRIVATE */ - function getCurrentFile(){ + function getCurrentFileClass(){ return CloudCommander.CURRENT_FILE; } @@ -26,7 +26,7 @@ var CloudCommander, Util, DOM, CloudFunc; }); if(lRet_b) - DOM.removeClass(pCurrentFile, getCurrentFile()); + DOM.removeClass(pCurrentFile, getCurrentFileClass()); return lRet_b; } @@ -34,45 +34,38 @@ var CloudCommander, Util, DOM, CloudFunc; /* private members */ var XMLHTTP, Title, - LoadingImage, - ErrorImage, /* Обьект, который содержит * функции для отображения * картинок */ - Images_o = { + Images = function (){ + var getImage = function(pName){ + var lId = pName + '-image', + lE = DOM.getById(lId); + if (!lE) + lE = DOM.anyload({ + name : 'span', + className : 'icon ' + pName, + id : lId, + not_append : true + }); + + return lE; + }; /* Функция создаёт картинку загрузки*/ - 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; + this.loading = function(){ + return getImage('loading'); + }; - 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; - } + this.error = function(){ + return getImage('error'); + }; }; + Images = new Images(); + /** * add class to current element * @param pElement @@ -659,7 +652,7 @@ var CloudCommander, Util, DOM, CloudFunc; * @param pElement - element */ DOM.getByClass = function(pClass, pElement){ - return (pElement || document).getElementsByClassName(pClass); + return (pElement || document).getElementsByClassName(pClass); }; @@ -669,41 +662,25 @@ var CloudCommander, Util, DOM, CloudFunc; * pPosition = {top: true}; */ showLoad : function(pPosition){ - var lRet_b = true; + var lRet_b, + lLoadingImage = Images.loading(), + lErrorImage = Images.error(); - LoadingImage = Images_o.loading(); - ErrorImage = Images_o.error(); - - DOM.hide(ErrorImage); + DOM.hide(lErrorImage); 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); /* показываем загрузку*/ - } + if(pPosition && pPosition.top) + lCurrent = DOM.getRefreshButton().parentElement; + else + lCurrent = DOM.getCurrentFile().firstChild.nextSibling; + + /* show loading icon if it not showed */ + + var lParent = lLoadingImage.parentElement; + if(!lParent || (lParent && lParent !== lCurrent)) + lCurrent.appendChild(lLoadingImage); + + lRet_b = DOM.show(lLoadingImage); /* показываем загрузку*/ return lRet_b; }, @@ -712,40 +689,38 @@ var CloudCommander, Util, DOM, CloudFunc; * hide load image */ hideLoad : function(){ - LoadingImage = Images_o.loading(); - DOM.hide(LoadingImage); + + DOM.hide( Images.loading() ); }, /** * show error image (usualy after error on ajax request) */ showError : function(jqXHR, textStatus, errorThrown){ - LoadingImage = Images_o.loading(); - - ErrorImage = Images_o.error(); + var lLoadingImage = Images.loading(), + lErrorImage = Images.error(), + lResponce = jqXHR.responseText, + lStatusText = jqXHR.statusText, + lStatus = jqXHR.status, + lText = (lStatus === 404 ? lResponce : lStatusText); - var lText; - if(jqXHR.status === 404) - lText = jqXHR.responseText; - else - lText = jqXHR.statusText; - /* если файла не существует*/ - if(!lText.indexOf('Error: ENOENT, ')) + if( Util.isContainStr(lText, 'Error: ENOENT, ') ) lText = lText.replace('Error: ENOENT, n','N'); /* если не хватает прав для чтения файла*/ - else if(!lText.indexOf('Error: EACCES,')) - lText = lText.replace('Error: EACCES, p','P'); + else if( Util.isContainStr(lText, 'Error: EACCES,') ) + lText = lText.replace('Error: EACCES, p','P'); + - DOM.show(ErrorImage); - ErrorImage.title = lText; + DOM.show(lErrorImage); + lErrorImage.title = lText; - var lParent = LoadingImage.parentElement; + var lParent = lLoadingImage.parentElement; if(lParent) - lParent.appendChild(ErrorImage); + lParent.appendChild(lErrorImage); - DOM.hide(LoadingImage); + DOM.hide(lLoadingImage); console.log(lText); } @@ -776,7 +751,7 @@ var CloudCommander, Util, DOM, CloudFunc; * @pCurrentFile */ DOM.getCurrentFile = function(pCurrentFile){ - var lCurrent = DOM.getByClass( pCurrentFile || getCurrentFile() )[0]; + var lCurrent = DOM.getByClass( pCurrentFile || getCurrentFileClass() )[0]; if(!lCurrent){ DOM.addCloudStatus({ code : -1, @@ -902,7 +877,7 @@ var CloudCommander, Util, DOM, CloudFunc; if(lCurrentFileWas) unSetCurrentFile(lCurrentFileWas); - DOM.addClass(pCurrentFile, getCurrentFile()); + DOM.addClass(pCurrentFile, getCurrentFileClass()); /* scrolling to current file */ DOM.scrollIntoViewIfNeeded(pCurrentFile); @@ -968,7 +943,7 @@ var CloudCommander, Util, DOM, CloudFunc; }); var lCurrentFileClass = pCurrentFile.className, - lIsCurrent = lCurrentFileClass.indexOf(getCurrentFile()) >= 0; + lIsCurrent = lCurrentFileClass.indexOf(getCurrentFileClass()) >= 0; return lIsCurrent; }; diff --git a/lib/server.js b/lib/server.js index d59f6d91..0579b309 100644 --- a/lib/server.js +++ b/lib/server.js @@ -3,10 +3,10 @@ var main = global.cloudcmd.main, - /* - * Обьект содержащий все функции и переменные - * серверной части Cloud Commander'а - */ + /* + * Обьект содержащий все функции и переменные + * серверной части Cloud Commander'а + */ CloudServer = { /* base configuration */ Config : { From 11237bbed89b110e29b03f56196a14d2f8b3ec1a Mon Sep 17 00:00:00 2001 From: coderaiser Date: Fri, 28 Dec 2012 09:19:43 -0500 Subject: [PATCH 073/347] refactored --- cloudcmd.js | 5 +- lib/client.js | 137 ++++++++++++++++++++++---------------------------- 2 files changed, 62 insertions(+), 80 deletions(-) diff --git a/cloudcmd.js b/cloudcmd.js index 840cd07d..3dfc687c 100644 --- a/cloudcmd.js +++ b/cloudcmd.js @@ -113,11 +113,8 @@ lOptimizeParams.push(lIndex); if (pAllowed.css) { - var lStyles = []; - - lStyles[0] = {}; + var lStyles = [{}, {}]; lStyles[0][lStyleCSS] = lCSSOptions; - lStyles[1] = {}; lStyles[1][lResetCSS] = lCSSOptions; lOptimizeParams.push(lStyles[0]); diff --git a/lib/client.js b/lib/client.js index 63fa1851..1bb70da0 100644 --- a/lib/client.js +++ b/lib/client.js @@ -3,11 +3,11 @@ * клиентский и серверный */ -var Util, DOM, CloudFunc, CloudCommander = (function(){ +var Util, DOM, CloudFunc, $, KeyBinding, CloudCommander = (function(){ "use strict"; /* Клиентский обьект, содержащий функциональную часть*/ -var CloudClient = { +var CloudCmd = { /* Конструктор CloudClient, который выполняет * весь функционал по инициализации */ @@ -56,18 +56,6 @@ var CloudClient = { return lLocation.protocol + '//' + lLocation.host; })() }; - - - -var cloudcmd = CloudClient, - -/* глобальные переменные */ - $, KeyBinding, - -/* short names used all the time functions */ - getByClass, getById; - - /** * function load modules * @pParams = {name, path, func, dobefore, arg} @@ -97,24 +85,24 @@ var loadModule = function(pParams){ if( !Util.isContainStr(lPath, '.js') ) lPath += '.js'; - if(!cloudcmd[lName]) - cloudcmd[lName] = function(pArg){ + if(!CloudCmd[lName]) + CloudCmd[lName] = function(pArg){ Util.exec(lDoBefore); - return DOM.jsload(cloudcmd.LIBDIRCLIENT + lPath, lFunc || + return DOM.jsload(CloudCmd.LIBDIRCLIENT + lPath, lFunc || function(){ - Util.exec(cloudcmd[lName].init, pArg); + Util.exec(CloudCmd[lName].init, pArg); }); }; }; -CloudClient.GoogleAnalytics = function(){ +CloudCmd.GoogleAnalytics = function(){ /* google analytics */ var lFunc = document.onmousemove; document.onmousemove = function(){ setTimeout(function(){ - DOM.jsload(cloudcmd.LIBDIRCLIENT + 'google_analytics.js'); + DOM.jsload(CloudCmd.LIBDIRCLIENT + 'google_analytics.js'); },5000); Util.exec(lFunc); @@ -130,7 +118,7 @@ CloudClient.GoogleAnalytics = function(){ * @param pLink - ссылка * @param pNeedRefresh - необходимость обязательной загрузки данных с сервера */ -CloudClient._loadDir = function(pLink, pNeedRefresh){ +CloudCmd._loadDir = function(pLink, pNeedRefresh){ return function(){ /* * показываем гиф загрузки возле пути папки сверху @@ -143,11 +131,11 @@ CloudClient._loadDir = function(pLink, pNeedRefresh){ lDir = DOM.getCurrentDir(); /* загружаем содержимое каталога */ - CloudClient._ajaxLoad(pLink, { refresh: pNeedRefresh }); + CloudCmd._ajaxLoad(pLink, { refresh: pNeedRefresh }); /* если нажали на ссылку на верхний каталог*/ if(lParent === '..' && lDir !== '/') - CloudClient._currentToParent(lDir); + CloudCmd._currentToParent(lDir); }; }; @@ -158,7 +146,7 @@ CloudClient._loadDir = function(pLink, pNeedRefresh){ * @param pParent - parent element * @param pEvent */ -CloudClient._editFileName = function(pParent){ +CloudCmd._editFileName = function(pParent){ var lA = DOM.getCurrentLink(pParent); if (lA && lA.textContent !== '..'){ @@ -197,7 +185,7 @@ CloudClient._editFileName = function(pParent){ * в верх по файловой структуре * @param pDirName - имя каталога с которого мы пришли */ -CloudClient._currentToParent = function(pDirName){ +CloudCmd._currentToParent = function(pDirName){ /* опредиляем в какой мы панели: * правой или левой */ @@ -206,7 +194,7 @@ CloudClient._currentToParent = function(pDirName){ /* убираем слэш с имени каталога*/ pDirName = pDirName.replace('/',''); - var lRootDir = getById(pDirName + '(' + lPanel.id + ')'); + var lRootDir = DOM.getById(pDirName + '(' + lPanel.id + ')'); /* if found li element with ID directory name * set it to current file @@ -221,7 +209,7 @@ CloudClient._currentToParent = function(pDirName){ * выполняет весь функционал по * инициализации */ -CloudClient.init = function(){ +CloudCmd.init = function(){ var lFunc = function(){ Util.loadOnLoad([ initKeysPanel, @@ -229,16 +217,12 @@ CloudClient.init = function(){ baseInit ]); }; - - getByClass = DOM.getByClass; - getById = DOM.getById; - - + //Util.socketLoad(); if(!document.body.scrollIntoViewIfNeeded){ this.OLD_BROWSER = true; - var lSrc = CloudClient.LIBDIRCLIENT + 'ie.js'; + var lSrc = CloudCmd.LIBDIRCLIENT + 'ie.js'; DOM.jqueryLoad( DOM.retJSLoad(lSrc, lFunc) @@ -252,7 +236,7 @@ function initModules(pCallBack){ /* привязываем клавиши к функциям */ path : 'keyBinding.js', func : function(){ - KeyBinding = cloudcmd.KeyBinding; + KeyBinding = CloudCmd.KeyBinding; KeyBinding.init(); } }); @@ -266,7 +250,7 @@ function initModules(pCallBack){ var lFunc = document.oncontextmenu; document.oncontextmenu = function(){ Util.exec(lFunc); - return cloudcmd.Menu.ENABLED || false; + return CloudCmd.Menu.ENABLED || false; }; }, @@ -306,8 +290,8 @@ function initKeysPanel(pCallBack){ null, null, /* f1 */ null, /* f2 */ - cloudcmd.Viewer, /* f3 */ - cloudcmd.Editor, /* f4 */ + CloudCmd.Viewer, /* f3 */ + CloudCmd.Editor, /* f4 */ null, /* f5 */ null, /* f6 */ null, /* f7 */ @@ -316,13 +300,13 @@ function initKeysPanel(pCallBack){ for(var i = 1; i <= 8; i++){ var lButton = 'f' + i, - lEl = getById('f' + i); + lEl = DOM.getById('f' + i); lEl.onclick = lFuncs[i]; lKeysPanel[lButton] = lEl; } - cloudcmd.KeysPanel = lKeysPanel; + CloudCmd.KeysPanel = lKeysPanel; Util.exec(pCallBack); } @@ -339,12 +323,12 @@ function baseInit(pCallBack){ } /* загружаем общие функции для клиента и сервера */ - DOM.jsload(cloudcmd.LIBDIR + 'cloudfunc.js',function(){ + DOM.jsload(CloudCmd.LIBDIR + 'cloudfunc.js',function(){ DOM.addListener("popstate", function(pEvent) { var lPath = pEvent.state; if(lPath) - cloudcmd._ajaxLoad(lPath, {nohistory: true}); + CloudCmd._ajaxLoad(lPath, {nohistory: true}); return true; }); @@ -353,14 +337,14 @@ function baseInit(pCallBack){ CloudFunc = window.CloudFunc; /* меняем ссылки на ajax'овые */ - cloudcmd._changeLinks(CloudFunc.LEFTPANEL); - cloudcmd._changeLinks(CloudFunc.RIGHTPANEL); + CloudCmd._changeLinks(CloudFunc.LEFTPANEL); + CloudCmd._changeLinks(CloudFunc.RIGHTPANEL); /* устанавливаем переменную доступности кэша */ DOM.Cache.isAllowed(); /* Устанавливаем кэш корневого каталога */ if( !DOM.Cache.get('/') ) - DOM.Cache.set('/', cloudcmd._getJSONfromFileTable()); + DOM.Cache.set('/', CloudCmd._getJSONfromFileTable()); }); /* устанавливаем размер высоты таблицы файлов @@ -368,11 +352,11 @@ function baseInit(pCallBack){ */ /* выделяем строку с первым файлом */ - var lFmHeader = getByClass('fm-header'); + var lFmHeader = DOM.getByClass('fm-header'); DOM.setCurrentFile(lFmHeader[0].nextSibling); /* показываем элементы, которые будут работать только, если есть js */ - var lFM = getById('fm'); + var lFM = DOM.getById('fm'); if(lFM) lFM.className='localstorage'; @@ -386,7 +370,7 @@ function baseInit(pCallBack){ lHeight = (lHeight / 100).toFixed() * 100; - cloudcmd.HEIGHT = lHeight; + CloudCmd.HEIGHT = lHeight; DOM.cssSet({id:'cloudcmd', inner: @@ -396,32 +380,32 @@ function baseInit(pCallBack){ }); Util.exec(pCallBack); - cloudcmd.KeyBinding(); + CloudCmd.KeyBinding(); } -CloudClient.getConfig = function(pCallBack){ - if(!cloudcmd.Config) +CloudCmd.getConfig = function(pCallBack){ + if(!CloudCmd.Config) return DOM.ajax({ url:'/config.json', success: function(pConfig){ - cloudcmd.Config = pConfig; + CloudCmd.Config = pConfig; Util.exec(pCallBack, pConfig); } }); else - Util.exec(pCallBack, cloudcmd.Config); + Util.exec(pCallBack, CloudCmd.Config); }; /* функция меняет ссыки на ajax-овые */ -CloudClient._changeLinks = function(pPanelID){ +CloudCmd._changeLinks = function(pPanelID){ /* назначаем кнопку очистить кэш и показываем её */ - var lClearcache = getById('clear-cache'); + var lClearcache = DOM.getById('clear-cache'); lClearcache.onclick = DOM.Cache.clear; /* меняем ссылки на ajax-запросы */ - var lPanel = getById(pPanelID), + var lPanel = DOM.getById(pPanelID), a = lPanel.getElementsByTagName('a'), /* номер ссылки иконки обновления страницы */ @@ -445,8 +429,8 @@ CloudClient._changeLinks = function(pPanelID){ var lTarget = pEvent.currentTarget || pEvent.target; DOM.setCurrentFile(lTarget); - if(Util.isFunction(cloudcmd.Menu) ){ - cloudcmd.Menu({ + if(Util.isFunction(CloudCmd.Menu) ){ + CloudCmd.Menu({ x: pEvent.x, y: pEvent.y }); @@ -494,7 +478,7 @@ CloudClient._changeLinks = function(pPanelID){ DOM.setCurrentFile(pElement); }, - lUrl = cloudcmd.HOST; + lUrl = CloudCmd.HOST; for(var i = 0, n = a.length; i < n ; i++) { @@ -503,7 +487,7 @@ CloudClient._changeLinks = function(pPanelID){ /* ставим загрузку гифа на клик*/ if(i === lREFRESHICON){ - a[i].onclick = CloudClient._loadDir(link, true); + a[i].onclick = CloudCmd._loadDir(link, true); a[i].parentElement.onclick = a[i].onclick; } @@ -515,7 +499,7 @@ CloudClient._changeLinks = function(pPanelID){ /* if we in path changing onclick events */ if (lLi.className === 'path') { - a[i].onclick = CloudClient._loadDir(link); + a[i].onclick = CloudCmd._loadDir(link); } else { lLi.onclick = Util.retFalse; @@ -529,10 +513,10 @@ CloudClient._changeLinks = function(pPanelID){ /* если ссылка на папку, а не файл */ if(a[i].target !== '_blank'){ - lLi.ondblclick = CloudClient._loadDir(link); + lLi.ondblclick = CloudCmd._loadDir(link); DOM.addListener('touchend', - CloudClient._loadDir(link), + CloudCmd._loadDir(link), false, lLi ); @@ -552,7 +536,7 @@ CloudClient._changeLinks = function(pPanelID){ * @param pOptions * { refresh, nohistory } - необходимость обновить данные о каталоге */ -CloudClient._ajaxLoad = function(pFullPath, pOptions){ +CloudCmd._ajaxLoad = function(pFullPath, pOptions){ if(!pOptions) pOptions = {}; /* Отображаем красивые пути */ @@ -614,8 +598,8 @@ CloudClient._ajaxLoad = function(pFullPath, pOptions){ }else lJSON = JSON.parse(lJSON); - CloudClient._createFileTable(lPanel, lJSON); - CloudClient._changeLinks(lPanel); + CloudCmd._createFileTable(lPanel, lJSON); + CloudCmd._changeLinks(lPanel); return; @@ -633,8 +617,8 @@ CloudClient._ajaxLoad = function(pFullPath, pOptions){ if(!jqXHR.responseText.indexOf('Error:')) return DOM.showError(jqXHR); - CloudClient._createFileTable(lPanel, data); - CloudClient._changeLinks(lPanel); + CloudCmd._createFileTable(lPanel, data); + CloudCmd._changeLinks(lPanel); /* Сохраняем структуру каталогов в localStorage, * если он поддерживаеться браузером @@ -659,13 +643,14 @@ CloudClient._ajaxLoad = function(pFullPath, pOptions){ * @param pEleme - родительский элемент * @param pJSON - данные о файлах */ -CloudClient._createFileTable = function(pElem, pJSON){ - var lElem = getById(pElem); +CloudCmd._createFileTable = function(pElem, pJSON){ + var lElem = DOM.getById(pElem); /* getting current element if was refresh */ - var lPath = getByClass('path', lElem); - var lWasRefresh_b = lPath[0].textContent === pJSON[0].path; - var lCurrent; + var lPath = DOM.getByClass('path', lElem), + lWasRefresh_b = lPath[0].textContent === pJSON[0].path, + lCurrent; + if(lWasRefresh_b) lCurrent = DOM.getCurrentFile(); @@ -698,9 +683,9 @@ CloudClient._createFileTable = function(pElem, pJSON){ * Функция генерирует JSON из html-таблицы файлов и * используеться при первом заходе в корень */ -CloudClient._getJSONfromFileTable = function(){ - var lLeft = getById('left'), - lPath = getByClass('path')[0].textContent, +CloudCmd._getJSONfromFileTable = function(){ + var lLeft = DOM.getById('left'), + lPath = DOM.getByClass('path')[0].textContent, lFileTable = [{ path:lPath, @@ -764,7 +749,7 @@ CloudClient._getJSONfromFileTable = function(){ return JSON.stringify(lFileTable); }; -return CloudClient; +return CloudCmd; })(); window.onload = function(){ From 1b70aee241f6df32eea41b1f40f3259537c3e412 Mon Sep 17 00:00:00 2001 From: coderaiser Date: Fri, 28 Dec 2012 09:54:08 -0500 Subject: [PATCH 074/347] minor changes --- cloudcmd.js | 6 +++--- lib/client.js | 12 ++++++------ 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/cloudcmd.js b/cloudcmd.js index 3dfc687c..806ada9c 100644 --- a/cloudcmd.js +++ b/cloudcmd.js @@ -207,9 +207,9 @@ var stdo = fs.createWriteStream('./log.txt'); process.stdout.write = (function(write) { - return function(string, encoding, fd) { - stdo.write(string); - }; + return function(string, encoding, fd) { + stdo.write(string); + }; })(process.stdout.write); } })(); \ No newline at end of file diff --git a/lib/client.js b/lib/client.js index 1bb70da0..560c7026 100644 --- a/lib/client.js +++ b/lib/client.js @@ -349,7 +349,7 @@ function baseInit(pCallBack){ /* устанавливаем размер высоты таблицы файлов * исходя из размеров разрешения экрана - */ + */ /* выделяем строку с первым файлом */ var lFmHeader = DOM.getByClass('fm-header'); @@ -357,8 +357,7 @@ function baseInit(pCallBack){ /* показываем элементы, которые будут работать только, если есть js */ var lFM = DOM.getById('fm'); - if(lFM) - lFM.className='localstorage'; + lFM.className='localstorage'; /* формируем и округляем высоту экрана * при разрешениии 1024x1280: @@ -369,10 +368,11 @@ function baseInit(pCallBack){ lHeight = lHeight - (lHeight/3).toFixed(); lHeight = (lHeight / 100).toFixed() * 100; - + CloudCmd.HEIGHT = lHeight; - - DOM.cssSet({id:'cloudcmd', + + DOM.cssSet({ + id:'cloudcmd', inner: '.panel{' + 'height:' + lHeight +'px;' + From ddec118dd3d5746917d9c8786c949c637a0c03c5 Mon Sep 17 00:00:00 2001 From: coderaiser Date: Fri, 28 Dec 2012 10:03:02 -0500 Subject: [PATCH 075/347] minor changes --- cloudcmd.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/cloudcmd.js b/cloudcmd.js index 806ada9c..59da7d88 100644 --- a/cloudcmd.js +++ b/cloudcmd.js @@ -49,16 +49,13 @@ */ if(srv.Minify._allowed.css){ var lPath = '/' + srv.Minify.MinFolder.replace(DIR, ''); - lReplace_s = ''; - lData = Util.removeStr(lData, lReplace_s) .replace('/css/style.css', lPath + 'all.min.css'); } - lReplace_s = '
    '; - /* меняем title */ + lReplace_s = '
    '; lData = lData.replace(lReplace_s, lReplace_s + lAdditional) .replace('Cloud Commander', '' + CloudFunc.getTitle() + ''); @@ -75,6 +72,9 @@ } + /** + * init and process of appcache if it allowed in config + */ function appCacheProcessing(){ var lAppCache = srv.AppCache, From d5df066ba830ddd4471f355f6446a769b822cc65 Mon Sep 17 00:00:00 2001 From: coderaiser Date: Mon, 31 Dec 2012 05:13:19 -0500 Subject: [PATCH 076/347] improved optimizing size of menu.js from 2539 to 2444 --- ChangeLog | 2 ++ config.json | 2 +- lib/client/menu.js | 6 +++--- 3 files changed, 6 insertions(+), 4 deletions(-) diff --git a/ChangeLog b/ChangeLog index 89a8914f..463161af 100644 --- a/ChangeLog +++ b/ChangeLog @@ -64,6 +64,8 @@ keyStop: function(e, opt) { * Updated jquery to v.1.8.3. +* Improved optimizing size of menu.js from 2539 to 2444. + 2012.12.12, Version 0.1.8 diff --git a/config.json b/config.json index a0a1012c..270056fa 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 diff --git a/lib/client/menu.js b/lib/client/menu.js index 2eb01458..0fd28fb2 100644 --- a/lib/client/menu.js +++ b/lib/client/menu.js @@ -1,8 +1,8 @@ /* object contains jQuery-contextMenu * https://github.com/medialize/jQuery-contextMenu */ -var CloudCommander, Util, DOM, $; -(function(){ +//var CloudCommander, Util, DOM, $; +(function(CloudCommander, Util, DOM, $){ "use strict"; var cloudcmd = CloudCommander, @@ -288,4 +288,4 @@ var CloudCommander, Util, DOM, $; }; cloudcmd.Menu = Menu; -})(); \ No newline at end of file +})(CloudCommander, Util, DOM, $); \ No newline at end of file From a02fec0182d900bbfb604063ba9cfe4ed53d3eff Mon Sep 17 00:00:00 2001 From: coderaiser Date: Thu, 3 Jan 2013 09:40:32 -0500 Subject: [PATCH 077/347] improved scripts compression --- lib/client/dom.js | 10 +++++----- lib/client/ie.js | 4 ++-- lib/util.js | 9 ++++----- 3 files changed, 11 insertions(+), 12 deletions(-) diff --git a/lib/client/dom.js b/lib/client/dom.js index 4d616147..c6a70cb9 100644 --- a/lib/client/dom.js +++ b/lib/client/dom.js @@ -1,10 +1,10 @@ -var CloudCommander, Util, DOM, CloudFunc; +var CloudCommander, Util, + DOM = {}, + CloudFunc; -(function(){ +(function(Util, DOM){ "use strict"; - DOM = {}; - /* PRIVATE */ function getCurrentFileClass(){ @@ -1204,4 +1204,4 @@ var CloudCommander, Util, DOM, CloudFunc; DOM.addCloudStatus = function(pStatus){ DOM.CloudStatus[DOM.CloudStatus.length] = pStatus; }; -})(); \ No newline at end of file +})(Util, DOM); \ No newline at end of file diff --git a/lib/client/ie.js b/lib/client/ie.js index 89bda3c3..bf9e0488 100644 --- a/lib/client/ie.js +++ b/lib/client/ie.js @@ -1,7 +1,7 @@ /* script, fixes ie */ var Util, DOM, $; -(function(){ +(function(Util, DOM, $){ "use strict"; /* setting head ie6 - ie8 */ @@ -218,4 +218,4 @@ var Util, DOM, $; } -})(); \ No newline at end of file +})(Util, DOM, $); \ No newline at end of file diff --git a/lib/util.js b/lib/util.js index c7c30e60..8772fbe0 100644 --- a/lib/util.js +++ b/lib/util.js @@ -3,12 +3,11 @@ * Module contain additional system functional */ var Util, exports; +Util = exports || {}; -(function(){ +(function(Util){ "use strict"; - - Util = exports || {}; - + var Scope = exports ? global : window; /** setting function context @@ -440,4 +439,4 @@ var Util, exports; return lRet; }; -})(); \ No newline at end of file +})(Util, exports); \ No newline at end of file From 940b6e6418dc0a275d99bf3459e23184c9317741 Mon Sep 17 00:00:00 2001 From: coderaiser Date: Thu, 3 Jan 2013 09:50:07 -0500 Subject: [PATCH 078/347] improved scripts compression --- config.json | 2 +- lib/client/google_analytics.js | 7 ++++--- lib/client/ie.js | 10 +++++----- lib/client/keyBinding.js | 5 +++-- lib/client/menu.js | 6 +++--- lib/client/socket.js | 4 ++-- lib/client/terminal.js | 7 ++++--- lib/client/viewer.js | 7 ++++--- 8 files changed, 26 insertions(+), 22 deletions(-) diff --git a/config.json b/config.json index 270056fa..a0a1012c 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/google_analytics.js b/lib/client/google_analytics.js index 9e07312e..ba3d0d85 100644 --- a/lib/client/google_analytics.js +++ b/lib/client/google_analytics.js @@ -1,6 +1,6 @@ var DOM, _gaq; -(function(){ - "use strict"; +(function(DOM, _gaq){ + 'use strict'; /* setting google analitics tracking code */ _gaq = [['_setAccount', 'UA-33536569-2'], ['_trackPageview']]; @@ -20,4 +20,5 @@ var DOM, _gaq; }; DOM.jsload('http://google-analytics.com/ga.js'); -})(); \ No newline at end of file + +})(DOM, _gaq); \ No newline at end of file diff --git a/lib/client/ie.js b/lib/client/ie.js index bf9e0488..fb18acee 100644 --- a/lib/client/ie.js +++ b/lib/client/ie.js @@ -12,13 +12,13 @@ var Util, DOM, $; {name: '', src: ' ',func: '', style: '', id: '', parent: '', async: false, inner: 'id{color:red, }, class:'', not_append: false} */ - DOM.cssSet = function(pParams_o){ + DOM.cssSet = function(pParams_o){ var lElement = ' + + +

    fancyBox

    + +

    This is a demonstration. More information and examples: www.fancyapps.com/fancybox/

    + +

    Simple image gallery

    +

    + + + + + + + +

    + +

    Different effects

    +

    + + + + + + + +

    + +

    Various types

    +

    + fancyBox will try to guess content type from href attribute but you can specify it directly by adding classname (fancybox.image, fancybox.iframe, etc). +

    + + + + +

    + Ajax example will not run from your local computer and requires a server to run. +

    + +

    Button helper

    +

    + + + + + + + +

    + +

    Thumbnail helper

    +

    + + + + + + + +

    + +

    Media helper

    +

    + Will not run from your local computer, requires a server to run. +

    + + + +

    Open manually

    + + +

    + Photo Credit: Instagrammer @whitjohns +

    + + \ No newline at end of file diff --git a/lib/client/viewer/fancyBox/lib/jquery-1.9.0.min.js b/lib/client/viewer/fancyBox/lib/jquery-1.9.0.min.js new file mode 100644 index 00000000..50d1b22f --- /dev/null +++ b/lib/client/viewer/fancyBox/lib/jquery-1.9.0.min.js @@ -0,0 +1,4 @@ +/*! jQuery v1.9.0 | (c) 2005, 2012 jQuery Foundation, Inc. | jquery.org/license */(function(e,t){"use strict";function n(e){var t=e.length,n=st.type(e);return st.isWindow(e)?!1:1===e.nodeType&&t?!0:"array"===n||"function"!==n&&(0===t||"number"==typeof t&&t>0&&t-1 in e)}function r(e){var t=Tt[e]={};return st.each(e.match(lt)||[],function(e,n){t[n]=!0}),t}function i(e,n,r,i){if(st.acceptData(e)){var o,a,s=st.expando,u="string"==typeof n,l=e.nodeType,c=l?st.cache:e,f=l?e[s]:e[s]&&s;if(f&&c[f]&&(i||c[f].data)||!u||r!==t)return f||(l?e[s]=f=K.pop()||st.guid++:f=s),c[f]||(c[f]={},l||(c[f].toJSON=st.noop)),("object"==typeof n||"function"==typeof n)&&(i?c[f]=st.extend(c[f],n):c[f].data=st.extend(c[f].data,n)),o=c[f],i||(o.data||(o.data={}),o=o.data),r!==t&&(o[st.camelCase(n)]=r),u?(a=o[n],null==a&&(a=o[st.camelCase(n)])):a=o,a}}function o(e,t,n){if(st.acceptData(e)){var r,i,o,a=e.nodeType,u=a?st.cache:e,l=a?e[st.expando]:st.expando;if(u[l]){if(t&&(r=n?u[l]:u[l].data)){st.isArray(t)?t=t.concat(st.map(t,st.camelCase)):t in r?t=[t]:(t=st.camelCase(t),t=t in r?[t]:t.split(" "));for(i=0,o=t.length;o>i;i++)delete r[t[i]];if(!(n?s:st.isEmptyObject)(r))return}(n||(delete u[l].data,s(u[l])))&&(a?st.cleanData([e],!0):st.support.deleteExpando||u!=u.window?delete u[l]:u[l]=null)}}}function a(e,n,r){if(r===t&&1===e.nodeType){var i="data-"+n.replace(Nt,"-$1").toLowerCase();if(r=e.getAttribute(i),"string"==typeof r){try{r="true"===r?!0:"false"===r?!1:"null"===r?null:+r+""===r?+r:wt.test(r)?st.parseJSON(r):r}catch(o){}st.data(e,n,r)}else r=t}return r}function s(e){var t;for(t in e)if(("data"!==t||!st.isEmptyObject(e[t]))&&"toJSON"!==t)return!1;return!0}function u(){return!0}function l(){return!1}function c(e,t){do e=e[t];while(e&&1!==e.nodeType);return e}function f(e,t,n){if(t=t||0,st.isFunction(t))return st.grep(e,function(e,r){var i=!!t.call(e,r,e);return i===n});if(t.nodeType)return st.grep(e,function(e){return e===t===n});if("string"==typeof t){var r=st.grep(e,function(e){return 1===e.nodeType});if(Wt.test(t))return st.filter(t,r,!n);t=st.filter(t,r)}return st.grep(e,function(e){return st.inArray(e,t)>=0===n})}function p(e){var t=zt.split("|"),n=e.createDocumentFragment();if(n.createElement)for(;t.length;)n.createElement(t.pop());return n}function d(e,t){return e.getElementsByTagName(t)[0]||e.appendChild(e.ownerDocument.createElement(t))}function h(e){var t=e.getAttributeNode("type");return e.type=(t&&t.specified)+"/"+e.type,e}function g(e){var t=nn.exec(e.type);return t?e.type=t[1]:e.removeAttribute("type"),e}function m(e,t){for(var n,r=0;null!=(n=e[r]);r++)st._data(n,"globalEval",!t||st._data(t[r],"globalEval"))}function y(e,t){if(1===t.nodeType&&st.hasData(e)){var n,r,i,o=st._data(e),a=st._data(t,o),s=o.events;if(s){delete a.handle,a.events={};for(n in s)for(r=0,i=s[n].length;i>r;r++)st.event.add(t,n,s[n][r])}a.data&&(a.data=st.extend({},a.data))}}function v(e,t){var n,r,i;if(1===t.nodeType){if(n=t.nodeName.toLowerCase(),!st.support.noCloneEvent&&t[st.expando]){r=st._data(t);for(i in r.events)st.removeEvent(t,i,r.handle);t.removeAttribute(st.expando)}"script"===n&&t.text!==e.text?(h(t).text=e.text,g(t)):"object"===n?(t.parentNode&&(t.outerHTML=e.outerHTML),st.support.html5Clone&&e.innerHTML&&!st.trim(t.innerHTML)&&(t.innerHTML=e.innerHTML)):"input"===n&&Zt.test(e.type)?(t.defaultChecked=t.checked=e.checked,t.value!==e.value&&(t.value=e.value)):"option"===n?t.defaultSelected=t.selected=e.defaultSelected:("input"===n||"textarea"===n)&&(t.defaultValue=e.defaultValue)}}function b(e,n){var r,i,o=0,a=e.getElementsByTagName!==t?e.getElementsByTagName(n||"*"):e.querySelectorAll!==t?e.querySelectorAll(n||"*"):t;if(!a)for(a=[],r=e.childNodes||e;null!=(i=r[o]);o++)!n||st.nodeName(i,n)?a.push(i):st.merge(a,b(i,n));return n===t||n&&st.nodeName(e,n)?st.merge([e],a):a}function x(e){Zt.test(e.type)&&(e.defaultChecked=e.checked)}function T(e,t){if(t in e)return t;for(var n=t.charAt(0).toUpperCase()+t.slice(1),r=t,i=Nn.length;i--;)if(t=Nn[i]+n,t in e)return t;return r}function w(e,t){return e=t||e,"none"===st.css(e,"display")||!st.contains(e.ownerDocument,e)}function N(e,t){for(var n,r=[],i=0,o=e.length;o>i;i++)n=e[i],n.style&&(r[i]=st._data(n,"olddisplay"),t?(r[i]||"none"!==n.style.display||(n.style.display=""),""===n.style.display&&w(n)&&(r[i]=st._data(n,"olddisplay",S(n.nodeName)))):r[i]||w(n)||st._data(n,"olddisplay",st.css(n,"display")));for(i=0;o>i;i++)n=e[i],n.style&&(t&&"none"!==n.style.display&&""!==n.style.display||(n.style.display=t?r[i]||"":"none"));return e}function C(e,t,n){var r=mn.exec(t);return r?Math.max(0,r[1]-(n||0))+(r[2]||"px"):t}function k(e,t,n,r,i){for(var o=n===(r?"border":"content")?4:"width"===t?1:0,a=0;4>o;o+=2)"margin"===n&&(a+=st.css(e,n+wn[o],!0,i)),r?("content"===n&&(a-=st.css(e,"padding"+wn[o],!0,i)),"margin"!==n&&(a-=st.css(e,"border"+wn[o]+"Width",!0,i))):(a+=st.css(e,"padding"+wn[o],!0,i),"padding"!==n&&(a+=st.css(e,"border"+wn[o]+"Width",!0,i)));return a}function E(e,t,n){var r=!0,i="width"===t?e.offsetWidth:e.offsetHeight,o=ln(e),a=st.support.boxSizing&&"border-box"===st.css(e,"boxSizing",!1,o);if(0>=i||null==i){if(i=un(e,t,o),(0>i||null==i)&&(i=e.style[t]),yn.test(i))return i;r=a&&(st.support.boxSizingReliable||i===e.style[t]),i=parseFloat(i)||0}return i+k(e,t,n||(a?"border":"content"),r,o)+"px"}function S(e){var t=V,n=bn[e];return n||(n=A(e,t),"none"!==n&&n||(cn=(cn||st("', + iframe : '', error : '

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

    ', closeBtn : '', next : '', @@ -1442,7 +1443,7 @@ // Create a close button if (current.closeBtn) { - $(current.tpl.closeBtn).appendTo(F.skin).bind( isTouch ? 'touchstart.fb' : 'click.fb', function(e) { + $(current.tpl.closeBtn).appendTo(F.skin).bind('click.fb', function(e) { e.preventDefault(); F.close(); @@ -1656,10 +1657,7 @@ F.wrap.css(startPos).animate(endPos, { duration : current.nextSpeed, easing : current.nextEasing, - complete : function() { - // This helps FireFox to properly render the box - setTimeout(F._afterZoomIn, 20); - } + complete : F._afterZoomIn }); } }, @@ -1780,7 +1778,7 @@ this.overlay.width(width).height('100%'); // jQuery does not return reliable result for IE - if ($.browser.msie) { + if (IE) { offsetWidth = Math.max(document.documentElement.offsetWidth, document.body.offsetWidth); if (D.width() > offsetWidth) { @@ -1887,7 +1885,7 @@ title.appendTo('body'); - if ($.browser.msie) { + if (IE) { title.width( title.width() ); } diff --git a/lib/client/viewer/fancyBox/source/jquery.fancybox.pack.js b/lib/client/viewer/fancyBox/source/jquery.fancybox.pack.js new file mode 100644 index 00000000..9f6a628e --- /dev/null +++ b/lib/client/viewer/fancyBox/source/jquery.fancybox.pack.js @@ -0,0 +1,45 @@ +/*! fancyBox v2.1.4 fancyapps.com | fancyapps.com/fancybox/#license */ +(function(C,z,f,r){var q=f(C),n=f(z),b=f.fancybox=function(){b.open.apply(this,arguments)},H=navigator.userAgent.match(/msie/),w=null,s=z.createTouch!==r,t=function(a){return a&&a.hasOwnProperty&&a instanceof f},p=function(a){return a&&"string"===f.type(a)},F=function(a){return p(a)&&0
    ',image:'',iframe:'",error:'

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

    ',closeBtn:'',next:'',prev:''},openEffect:"fade",openSpeed:250,openEasing:"swing",openOpacity:!0, +openMethod:"zoomIn",closeEffect:"fade",closeSpeed:250,closeEasing:"swing",closeOpacity:!0,closeMethod:"zoomOut",nextEffect:"elastic",nextSpeed:250,nextEasing:"swing",nextMethod:"changeIn",prevEffect:"elastic",prevSpeed:250,prevEasing:"swing",prevMethod:"changeOut",helpers:{overlay:!0,title:!0},onCancel:f.noop,beforeLoad:f.noop,afterLoad:f.noop,beforeShow:f.noop,afterShow:f.noop,beforeChange:f.noop,beforeClose:f.noop,afterClose:f.noop},group:{},opts:{},previous:null,coming:null,current:null,isActive:!1, +isOpen:!1,isOpened:!1,wrap:null,skin:null,outer:null,inner:null,player:{timer:null,isActive:!1},ajaxLoad:null,imgPreload:null,transitions:{},helpers:{},open:function(a,d){if(a&&(f.isPlainObject(d)||(d={}),!1!==b.close(!0)))return f.isArray(a)||(a=t(a)?f(a).get():[a]),f.each(a,function(e,c){var k={},g,h,j,m,l;"object"===f.type(c)&&(c.nodeType&&(c=f(c)),t(c)?(k={href:c.data("fancybox-href")||c.attr("href"),title:c.data("fancybox-title")||c.attr("title"),isDom:!0,element:c},f.metadata&&f.extend(!0,k, +c.metadata())):k=c);g=d.href||k.href||(p(c)?c:null);h=d.title!==r?d.title:k.title||"";m=(j=d.content||k.content)?"html":d.type||k.type;!m&&k.isDom&&(m=c.data("fancybox-type"),m||(m=(m=c.prop("class").match(/fancybox\.(\w+)/))?m[1]:null));p(g)&&(m||(b.isImage(g)?m="image":b.isSWF(g)?m="swf":"#"===g.charAt(0)?m="inline":p(c)&&(m="html",j=c)),"ajax"===m&&(l=g.split(/\s+/,2),g=l.shift(),l=l.shift()));j||("inline"===m?g?j=f(p(g)?g.replace(/.*(?=#[^\s]+$)/,""):g):k.isDom&&(j=c):"html"===m?j=g:!m&&(!g&& +k.isDom)&&(m="inline",j=c));f.extend(k,{href:g,type:m,content:j,title:h,selector:l});a[e]=k}),b.opts=f.extend(!0,{},b.defaults,d),d.keys!==r&&(b.opts.keys=d.keys?f.extend({},b.defaults.keys,d.keys):!1),b.group=a,b._start(b.opts.index)},cancel:function(){var a=b.coming;a&&!1!==b.trigger("onCancel")&&(b.hideLoading(),b.ajaxLoad&&b.ajaxLoad.abort(),b.ajaxLoad=null,b.imgPreload&&(b.imgPreload.onload=b.imgPreload.onerror=null),a.wrap&&a.wrap.stop(!0,!0).trigger("onReset").remove(),b.coming=null,b.current|| +b._afterZoomOut(a))},close:function(a){b.cancel();!1!==b.trigger("beforeClose")&&(b.unbindEvents(),b.isActive&&(!b.isOpen||!0===a?(f(".fancybox-wrap").stop(!0).trigger("onReset").remove(),b._afterZoomOut()):(b.isOpen=b.isOpened=!1,b.isClosing=!0,f(".fancybox-item, .fancybox-nav").remove(),b.wrap.stop(!0,!0).removeClass("fancybox-opened"),b.transitions[b.current.closeMethod]())))},play:function(a){var d=function(){clearTimeout(b.player.timer)},e=function(){d();b.current&&b.player.isActive&&(b.player.timer= +setTimeout(b.next,b.current.playSpeed))},c=function(){d();f("body").unbind(".player");b.player.isActive=!1;b.trigger("onPlayEnd")};if(!0===a||!b.player.isActive&&!1!==a){if(b.current&&(b.current.loop||b.current.index=c.index?"next":"prev"],b.router=e||"jumpto",c.loop&&(0>a&&(a=c.group.length+a%c.group.length),a%=c.group.length),c.group[a]!==r&&(b.cancel(),b._start(a)))},reposition:function(a,d){var e=b.current,c=e?e.wrap:null,k;c&&(k=b._getPosition(d),a&&"scroll"===a.type?(delete k.position,c.stop(!0,!0).animate(k,200)):(c.css(k),e.pos=f.extend({}, +e.dim,k)))},update:function(a){var d=a&&a.type,e=!d||"orientationchange"===d;e&&(clearTimeout(w),w=null);b.isOpen&&!w&&(w=setTimeout(function(){var c=b.current;c&&!b.isClosing&&(b.wrap.removeClass("fancybox-tmp"),(e||"load"===d||"resize"===d&&c.autoResize)&&b._setDimension(),"scroll"===d&&c.canShrink||b.reposition(a),b.trigger("onUpdate"),w=null)},e&&!s?0:300))},toggle:function(a){b.isOpen&&(b.current.fitToView="boolean"===f.type(a)?a:!b.current.fitToView,s&&(b.wrap.removeAttr("style").addClass("fancybox-tmp"), +b.trigger("onUpdate")),b.update())},hideLoading:function(){n.unbind(".loading");f("#fancybox-loading").remove()},showLoading:function(){var a,d;b.hideLoading();a=f('
    ').click(b.cancel).appendTo("body");n.bind("keydown.loading",function(a){if(27===(a.which||a.keyCode))a.preventDefault(),b.cancel()});b.defaults.fixed||(d=b.getViewport(),a.css({position:"absolute",top:0.5*d.h+d.y,left:0.5*d.w+d.x}))},getViewport:function(){var a=b.current&&b.current.locked|| +!1,d={x:q.scrollLeft(),y:q.scrollTop()};a?(d.w=a[0].clientWidth,d.h=a[0].clientHeight):(d.w=s&&C.innerWidth?C.innerWidth:q.width(),d.h=s&&C.innerHeight?C.innerHeight:q.height());return d},unbindEvents:function(){b.wrap&&t(b.wrap)&&b.wrap.unbind(".fb");n.unbind(".fb");q.unbind(".fb")},bindEvents:function(){var a=b.current,d;a&&(q.bind("orientationchange.fb"+(s?"":" resize.fb")+(a.autoCenter&&!a.locked?" scroll.fb":""),b.update),(d=a.keys)&&n.bind("keydown.fb",function(e){var c=e.which||e.keyCode,k= +e.target||e.srcElement;if(27===c&&b.coming)return!1;!e.ctrlKey&&(!e.altKey&&!e.shiftKey&&!e.metaKey&&(!k||!k.type&&!f(k).is("[contenteditable]")))&&f.each(d,function(d,k){if(1h[0].clientWidth||h[0].clientHeight&&h[0].scrollHeight>h[0].clientHeight),h=f(h).parent();if(0!==c&&!j&&1g||0>k)b.next(0>g?"up":"right");d.preventDefault()}}))},trigger:function(a,d){var e,c=d||b.coming||b.current;if(c){f.isFunction(c[a])&&(e=c[a].apply(c,Array.prototype.slice.call(arguments,1)));if(!1===e)return!1;c.helpers&&f.each(c.helpers,function(d, +e){e&&(b.helpers[d]&&f.isFunction(b.helpers[d][a]))&&(e=f.extend(!0,{},b.helpers[d].defaults,e),b.helpers[d][a](e,c))});f.event.trigger(a+".fb")}},isImage:function(a){return p(a)&&a.match(/(^data:image\/.*,)|(\.(jp(e|g|eg)|gif|png|bmp|webp)((\?|#).*)?$)/i)},isSWF:function(a){return p(a)&&a.match(/\.(swf)((\?|#).*)?$/i)},_start:function(a){var d={},e,c;a=l(a);e=b.group[a]||null;if(!e)return!1;d=f.extend(!0,{},b.opts,e);e=d.margin;c=d.padding;"number"===f.type(e)&&(d.margin=[e,e,e,e]);"number"===f.type(c)&& +(d.padding=[c,c,c,c]);d.modal&&f.extend(!0,d,{closeBtn:!1,closeClick:!1,nextClick:!1,arrows:!1,mouseWheel:!1,keys:null,helpers:{overlay:{closeClick:!1}}});d.autoSize&&(d.autoWidth=d.autoHeight=!0);"auto"===d.width&&(d.autoWidth=!0);"auto"===d.height&&(d.autoHeight=!0);d.group=b.group;d.index=a;b.coming=d;if(!1===b.trigger("beforeLoad"))b.coming=null;else{c=d.type;e=d.href;if(!c)return b.coming=null,b.current&&b.router&&"jumpto"!==b.router?(b.current.index=a,b[b.router](b.direction)):!1;b.isActive= +!0;if("image"===c||"swf"===c)d.autoHeight=d.autoWidth=!1,d.scrolling="visible";"image"===c&&(d.aspectRatio=!0);"iframe"===c&&s&&(d.scrolling="scroll");d.wrap=f(d.tpl.wrap).addClass("fancybox-"+(s?"mobile":"desktop")+" fancybox-type-"+c+" fancybox-tmp "+d.wrapCSS).appendTo(d.parent||"body");f.extend(d,{skin:f(".fancybox-skin",d.wrap),outer:f(".fancybox-outer",d.wrap),inner:f(".fancybox-inner",d.wrap)});f.each(["Top","Right","Bottom","Left"],function(a,b){d.skin.css("padding"+b,x(d.padding[a]))});b.trigger("onReady"); +if("inline"===c||"html"===c){if(!d.content||!d.content.length)return b._error("content")}else if(!e)return b._error("href");"image"===c?b._loadImage():"ajax"===c?b._loadAjax():"iframe"===c?b._loadIframe():b._afterLoad()}},_error:function(a){f.extend(b.coming,{type:"html",autoWidth:!0,autoHeight:!0,minWidth:0,minHeight:0,scrolling:"no",hasError:a,content:b.coming.tpl.error});b._afterLoad()},_loadImage:function(){var a=b.imgPreload=new Image;a.onload=function(){this.onload=this.onerror=null;b.coming.width= +this.width;b.coming.height=this.height;b._afterLoad()};a.onerror=function(){this.onload=this.onerror=null;b._error("image")};a.src=b.coming.href;!0!==a.complete&&b.showLoading()},_loadAjax:function(){var a=b.coming;b.showLoading();b.ajaxLoad=f.ajax(f.extend({},a.ajax,{url:a.href,error:function(a,e){b.coming&&"abort"!==e?b._error("ajax",a):b.hideLoading()},success:function(d,e){"success"===e&&(a.content=d,b._afterLoad())}}))},_loadIframe:function(){var a=b.coming,d=f(a.tpl.iframe.replace(/\{rnd\}/g, +(new Date).getTime())).attr("scrolling",s?"auto":a.iframe.scrolling).attr("src",a.href);f(a.wrap).bind("onReset",function(){try{f(this).find("iframe").hide().attr("src","//about:blank").end().empty()}catch(a){}});a.iframe.preload&&(b.showLoading(),d.one("load",function(){f(this).data("ready",1);s||f(this).bind("load.fb",b.update);f(this).parents(".fancybox-wrap").width("100%").removeClass("fancybox-tmp").show();b._afterLoad()}));a.content=d.appendTo(a.inner);a.iframe.preload||b._afterLoad()},_preloadImages:function(){var a= +b.group,d=b.current,e=a.length,c=d.preload?Math.min(d.preload,e-1):0,f,g;for(g=1;g<=c;g+=1)f=a[(d.index+g)%e],"image"===f.type&&f.href&&((new Image).src=f.href)},_afterLoad:function(){var a=b.coming,d=b.current,e,c,k,g,h;b.hideLoading();if(a&&!1!==b.isActive)if(!1===b.trigger("afterLoad",a,d))a.wrap.stop(!0).trigger("onReset").remove(),b.coming=null;else{d&&(b.trigger("beforeChange",d),d.wrap.stop(!0).removeClass("fancybox-opened").find(".fancybox-item, .fancybox-nav").remove());b.unbindEvents(); +e=a.content;c=a.type;k=a.scrolling;f.extend(b,{wrap:a.wrap,skin:a.skin,outer:a.outer,inner:a.inner,current:a,previous:d});g=a.href;switch(c){case "inline":case "ajax":case "html":a.selector?e=f("
    ").html(e).find(a.selector):t(e)&&(e.data("fancybox-placeholder")||e.data("fancybox-placeholder",f('
    ').insertAfter(e).hide()),e=e.show().detach(),a.wrap.bind("onReset",function(){f(this).find(e).length&&e.hide().replaceAll(e.data("fancybox-placeholder")).data("fancybox-placeholder", +!1)}));break;case "image":e=a.tpl.image.replace("{href}",g);break;case "swf":e='',h="",f.each(a.swf,function(a,b){e+='';h+=" "+a+'="'+b+'"'}),e+='"}(!t(e)||!e.parent().is(a.inner))&&a.inner.append(e);b.trigger("beforeShow"); +a.inner.css("overflow","yes"===k?"scroll":"no"===k?"hidden":k);b._setDimension();b.reposition();b.isOpen=!1;b.coming=null;b.bindEvents();if(b.isOpened){if(d.prevMethod)b.transitions[d.prevMethod]()}else f(".fancybox-wrap").not(a.wrap).stop(!0).trigger("onReset").remove();b.transitions[b.isOpened?a.nextMethod:a.openMethod]();b._preloadImages()}},_setDimension:function(){var a=b.getViewport(),d=0,e=!1,c=!1,e=b.wrap,k=b.skin,g=b.inner,h=b.current,c=h.width,j=h.height,m=h.minWidth,u=h.minHeight,n=h.maxWidth, +v=h.maxHeight,s=h.scrolling,q=h.scrollOutside?h.scrollbarWidth:0,y=h.margin,p=l(y[1]+y[3]),r=l(y[0]+y[2]),z,A,t,D,B,G,C,E,w;e.add(k).add(g).width("auto").height("auto").removeClass("fancybox-tmp");y=l(k.outerWidth(!0)-k.width());z=l(k.outerHeight(!0)-k.height());A=p+y;t=r+z;D=F(c)?(a.w-A)*l(c)/100:c;B=F(j)?(a.h-t)*l(j)/100:j;if("iframe"===h.type){if(w=h.content,h.autoHeight&&1===w.data("ready"))try{w[0].contentWindow.document.location&&(g.width(D).height(9999),G=w.contents().find("body"),q&&G.css("overflow-x", +"hidden"),B=G.height())}catch(H){}}else if(h.autoWidth||h.autoHeight)g.addClass("fancybox-tmp"),h.autoWidth||g.width(D),h.autoHeight||g.height(B),h.autoWidth&&(D=g.width()),h.autoHeight&&(B=g.height()),g.removeClass("fancybox-tmp");c=l(D);j=l(B);E=D/B;m=l(F(m)?l(m,"w")-A:m);n=l(F(n)?l(n,"w")-A:n);u=l(F(u)?l(u,"h")-t:u);v=l(F(v)?l(v,"h")-t:v);G=n;C=v;h.fitToView&&(n=Math.min(a.w-A,n),v=Math.min(a.h-t,v));A=a.w-p;r=a.h-r;h.aspectRatio?(c>n&&(c=n,j=l(c/E)),j>v&&(j=v,c=l(j*E)),cA||p>r)&&(c>m&&j>u)&&!(19n&&(c=n,j=l(c/E)),g.width(c).height(j),e.width(c+y),a=e.width(),p=e.height();else c=Math.max(m,Math.min(c,c-(a-A))),j=Math.max(u,Math.min(j,j-(p-r)));q&&("auto"===s&&jA||p>r)&&c>m&&j>u;c=h.aspectRatio?cu&&j
    ').appendTo("body"); +this.fixed=!1;a.fixed&&b.defaults.fixed&&(this.overlay.addClass("fancybox-overlay-fixed"),this.fixed=!0)},open:function(a){var d=this;a=f.extend({},this.defaults,a);this.overlay?this.overlay.unbind(".overlay").width("auto").height("auto"):this.create(a);this.fixed||(q.bind("resize.overlay",f.proxy(this.update,this)),this.update());a.closeClick&&this.overlay.bind("click.overlay",function(a){f(a.target).hasClass("fancybox-overlay")&&(b.isActive?b.close():d.close())});this.overlay.css(a.css).show()}, +close:function(){f(".fancybox-overlay").remove();q.unbind("resize.overlay");this.overlay=null;!1!==this.margin&&(f("body").css("margin-right",this.margin),this.margin=!1);this.el&&this.el.removeClass("fancybox-lock")},update:function(){var a="100%",b;this.overlay.width(a).height("100%");H?(b=Math.max(z.documentElement.offsetWidth,z.body.offsetWidth),n.width()>b&&(a=n.width())):n.width()>q.width()&&(a=n.width());this.overlay.width(a).height(n.height())},onReady:function(a,b){f(".fancybox-overlay").stop(!0, +!0);this.overlay||(this.margin=n.height()>q.height()||"scroll"===f("body").css("overflow-y")?f("body").css("margin-right"):!1,this.el=z.all&&!z.querySelector?f("html"):f("body"),this.create(a));a.locked&&this.fixed&&(b.locked=this.overlay.append(b.wrap),b.fixed=!1);!0===a.showEarly&&this.beforeShow.apply(this,arguments)},beforeShow:function(a,b){b.locked&&(this.el.addClass("fancybox-lock"),!1!==this.margin&&f("body").css("margin-right",l(this.margin)+b.scrollbarWidth));this.open(a)},onUpdate:function(){this.fixed|| +this.update()},afterClose:function(a){this.overlay&&!b.isActive&&this.overlay.fadeOut(a.speedOut,f.proxy(this.close,this))}};b.helpers.title={defaults:{type:"float",position:"bottom"},beforeShow:function(a){var d=b.current,e=d.title,c=a.type;f.isFunction(e)&&(e=e.call(d.element,d));if(p(e)&&""!==f.trim(e)){d=f('
    '+e+"
    ");switch(c){case "inside":c=b.skin;break;case "outside":c=b.wrap;break;case "over":c=b.inner;break;default:c=b.skin,d.appendTo("body"), +H&&d.width(d.width()),d.wrapInner(''),b.current.margin[2]+=Math.abs(l(d.css("margin-bottom")))}d["top"===a.position?"prependTo":"appendTo"](c)}}};f.fn.fancybox=function(a){var d,e=f(this),c=this.selector||"",k=function(g){var h=f(this).blur(),j=d,k,l;!g.ctrlKey&&(!g.altKey&&!g.shiftKey&&!g.metaKey)&&!h.is(".fancybox-wrap")&&(k=a.groupAttr||"data-fancybox-group",l=h.attr(k),l||(k="rel",l=h.get(0)[k]),l&&(""!==l&&"nofollow"!==l)&&(h=c.length?f(c):e,h=h.filter("["+k+'="'+l+ +'"]'),j=h.index(this)),a.index=j,!1!==b.open(h,a)&&g.preventDefault())};a=a||{};d=a.index||0;!c||!1===a.live?e.unbind("click.fb-start").bind("click.fb-start",k):n.undelegate(c,"click.fb-start").delegate(c+":not('.fancybox-item, .fancybox-nav')","click.fb-start",k);this.filter("[data-fancybox-start=1]").trigger("click");return this};n.ready(function(){f.scrollbarWidth===r&&(f.scrollbarWidth=function(){var a=f('
    ').appendTo("body"),b=a.children(), +b=b.innerWidth()-b.height(99).innerWidth();a.remove();return b});if(f.support.fixedPosition===r){var a=f.support,d=f('
    ').appendTo("body"),e=20===d[0].offsetTop||15===d[0].offsetTop;d.remove();a.fixedPosition=e}f.extend(b.defaults,{scrollbarWidth:f.scrollbarWidth(),fixed:f.support.fixedPosition,parent:f("body")})})})(window,document,jQuery); \ No newline at end of file diff --git a/lib/client/viewer/fancybox/LICENSE b/lib/client/viewer/fancybox/LICENSE deleted file mode 100644 index 17706910..00000000 --- a/lib/client/viewer/fancybox/LICENSE +++ /dev/null @@ -1,6 +0,0 @@ -FancyBox licensed under Creative Commons Attribution-NonCommercial 3.0 license. -You are free to use fancyBox for your personal or non-profit website projects. -You can get the author's permission to use fancyBox for commercial websites by -paying a fee. - -http://www.fancyapps.com/fancybox/#license \ No newline at end of file diff --git a/lib/client/viewer/fancybox/fancybox_overlay.png b/lib/client/viewer/fancybox/fancybox_overlay.png deleted file mode 100644 index 74e0ea11eb2cd603ea4456b5c05c2288732d0b54..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 923 zcmaJ=J#W)M7xB34Q^8fpZ$C36K{j1UCmT*ro-Pu+wcE%^M wa61#WKK%aawYU-wx=wSH|CpTo{By7|z02aD^{ZTFmyt;_d+0E|2sl>h($ diff --git a/lib/util.js b/lib/util.js index 75d1f11e..fc51632d 100644 --- a/lib/util.js +++ b/lib/util.js @@ -46,14 +46,14 @@ Util = exports || {}; * длинны расширения - * имеет смысл продолжать */ - if (typeof pExt === 'string' && pName.length > pExt.length) { + if (Util.isString(pExt) && pName.length > pExt.length) { var lExtNum = pName.lastIndexOf(pExt), /* последнее вхождение расширения*/ lExtSub = lLength - lExtNum; /* длина расширения*/ - + /* если pExt - расширение pName */ lRet = lExtSub === pExt.length; - }else if(typeof pExt === 'object' && pExt.length){ + }else if(Util.isObject(pExt) && pExt.length){ for(var i=0; i < pName.length; i++){ lRet = Util.checkExtension(pName, pExt[i]); From 96499963513fa744c986e33388c345109f19e434 Mon Sep 17 00:00:00 2001 From: coderaiser Date: Thu, 17 Jan 2013 04:54:38 -0500 Subject: [PATCH 098/347] minor changes --- lib/client/dom.js | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/lib/client/dom.js b/lib/client/dom.js index ccb24aae..1c54553e 100644 --- a/lib/client/dom.js +++ b/lib/client/dom.js @@ -128,8 +128,8 @@ var CloudCommander, Util, XMLHTTP.send(lData); if( !Util.isFunction(lSuccess_f) ){ - console.log('error in DOM.ajax onSuccess:', pParams); - console.log(pParams); + Util.log('error in DOM.ajax onSuccess:', pParams); + Util.log(pParams); } XMLHTTP.onreadystatechange = function(pEvent){ @@ -711,7 +711,7 @@ var CloudCommander, Util, /* если не хватает прав для чтения файла*/ else if( Util.isContainStr(lText, 'Error: EACCES,') ) lText = lText.replace('Error: EACCES, p','P'); - + DOM.show(lErrorImage); lErrorImage.title = lText; @@ -722,7 +722,7 @@ var CloudCommander, Util, DOM.hide(lLoadingImage); - console.log(lText); + Util.log(lText); } }; @@ -1031,7 +1031,7 @@ var CloudCommander, Util, if(!lPanel) - console.log('Error can not find Active Panel'); + Util.log('Error can not find Active Panel'); return lPanel; }; From 8fb97f24ca887174c7d840817b73ce3cd9b34c42 Mon Sep 17 00:00:00 2001 From: coderaiser Date: Thu, 17 Jan 2013 06:15:12 -0500 Subject: [PATCH 099/347] added replaceStr function --- lib/util.js | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/lib/util.js b/lib/util.js index fc51632d..5e43be92 100644 --- a/lib/util.js +++ b/lib/util.js @@ -134,7 +134,12 @@ Util = exports || {}; Util.removeStr = function(pStr, pSubStr){ return pStr.replace(pSubStr,''); }; - + + Util.replaceStr = function(pStr, pFrom, pTo){ + var lRet = pStr.replace(new RegExp(pFrom, 'g'), pTo); + return lRet; + }; + /** * invoke a couple of functions in paralel * From e8929c7d478bfc6406e2463546975e6bf934cd55 Mon Sep 17 00:00:00 2001 From: coderaiser Date: Thu, 17 Jan 2013 06:47:15 -0500 Subject: [PATCH 100/347] refactored --- lib/client.js | 4 +- lib/client/dom.js | 93 +++++++++++++++++------------------------------ 2 files changed, 34 insertions(+), 63 deletions(-) diff --git a/lib/client.js b/lib/client.js index f132b3d1..5fb44638 100644 --- a/lib/client.js +++ b/lib/client.js @@ -41,8 +41,6 @@ var CloudCmd = { _changeLinks : null, /* КОНСТАНТЫ*/ - /* название css-класа текущего файла*/ - CURRENT_FILE : 'current-file', LIBDIR : '/lib/', LIBDIRCLIENT : '/lib/client/', /* height of Cloud Commander @@ -65,7 +63,7 @@ CloudCmd.GoogleAnalytics = function(){ document.onmousemove = function(){ setTimeout(function(){ DOM.jsload(CloudCmd.LIBDIRCLIENT + 'google_analytics.js'); - },5000); + }, 5000); Util.exec(lFunc); diff --git a/lib/client/dom.js b/lib/client/dom.js index 1c54553e..765f5fe2 100644 --- a/lib/client/dom.js +++ b/lib/client/dom.js @@ -7,32 +7,10 @@ var CloudCommander, Util, /* PRIVATE */ - function getCurrentFileClass(){ - 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, getCurrentFileClass()); - - return lRet_b; - } - /* private members */ - var XMLHTTP, + var /* название css-класа текущего файла*/ + CURRENT_FILE = 'current-file', + XMLHTTP, Title, /* Обьект, который содержит @@ -66,6 +44,26 @@ var CloudCommander, Util, Images = new Images(); + /** + * 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, CURRENT_FILE); + + return lRet_b; + } + /** * add class to current element * @param pElement @@ -109,7 +107,7 @@ var CloudCommander, Util, * @param pListener * @param pUseCapture */ - DOM.addKeyListener = function(pListener, pUseCapture){ + DOM.addKeyListener = function(pListener, pUseCapture){ return DOM.addListener('keydown', pListener, pUseCapture); }; @@ -750,17 +748,10 @@ var CloudCommander, Util, * * @pCurrentFile */ - DOM.getCurrentFile = function(pCurrentFile){ - var lCurrent = DOM.getByClass( pCurrentFile || getCurrentFileClass() )[0]; - if(!lCurrent){ - DOM.addCloudStatus({ - code : -1, - msg : 'Error: can not find ' + - 'CurrentFile ' + - 'in getCurrentFile' - }); - } - return lCurrent; + DOM.getCurrentFile = function(){ + var lRet = DOM.getByClass(CURRENT_FILE )[0]; + + return lRet; }; @@ -877,7 +868,7 @@ var CloudCommander, Util, if(lCurrentFileWas) unSetCurrentFile(lCurrentFileWas); - DOM.addClass(pCurrentFile, getCurrentFileClass()); + DOM.addClass(pCurrentFile, CURRENT_FILE); /* scrolling to current file */ DOM.scrollIntoViewIfNeeded(pCurrentFile); @@ -934,16 +925,8 @@ var CloudCommander, Util, * @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(getCurrentFileClass()) >= 0; + lIsCurrent = lCurrentFileClass.indexOf(CURRENT_FILE) >= 0; return lIsCurrent; }; @@ -960,13 +943,7 @@ var CloudCommander, Util, lRet = lLink.length > 0 ? lLink[0] : -1; - if(!lRet) - DOM.addCloudStatus({ - code : -1, - msg : 'Error current element do not contain links' - }); - - return lRet; + return lRet; }; /** @@ -993,12 +970,8 @@ var CloudCommander, Util, 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; + if(lLink) + lLink = lLink.textContent; return lLink; }; From 5d9b96f7c3e65e787a8fc536e44378b2d8b1c3b3 Mon Sep 17 00:00:00 2001 From: coderaiser Date: Thu, 17 Jan 2013 08:02:16 -0500 Subject: [PATCH 101/347] improved modules.json format and parsing --- ChangeLog | 2 ++ lib/client.js | 44 +++++++++++++++++++++++++++++--------------- modules.json | 26 ++++++++++++++++++++------ 3 files changed, 51 insertions(+), 21 deletions(-) diff --git a/ChangeLog b/ChangeLog index c81f2d7e..1e4ff7f6 100644 --- a/ChangeLog +++ b/ChangeLog @@ -85,6 +85,8 @@ clicked on menu item. * Updated jquery to v1.9.0. +* Improved modules.json format and parsing. + 2012.12.12, Version 0.1.8 diff --git a/lib/client.js b/lib/client.js index 5fb44638..43af1e94 100644 --- a/lib/client.js +++ b/lib/client.js @@ -239,7 +239,9 @@ function initModules(pCallBack){ }); CloudCmd.getModules(function(pModules){ - var lStorage = 'storage/', + pModules = pModules || []; + + var lStorage = 'storage', lShowLoadFunc = Util.retFunc( DOM.Images.showLoad ), lDisableMenuFunc = function(){ var lFunc = document.oncontextmenu; @@ -255,24 +257,36 @@ function initModules(pCallBack){ 'viewer' : lShowLoadFunc }, - lNames = {}; - lNames[lStorage + '_dropbox'] = 'DropBox', - lNames[lStorage + '_github' ] = 'GitHub', - lNames[lStorage + '_gdrive' ] = 'GDrive', - lNames[lStorage + '_vk' ] = 'VK', + lLoad = function(pName, pPath, pDoBefore){ + loadModule({ + path : pPath, + name : pName, + dobefore : pDoBefore + }); + }; lDisableMenuFunc(); - if( Util.isArray(pModules) ) - for(var i = 0, n = pModules.length; i < n ; i++){ - var lModule = pModules[i]; - - loadModule({ - path : lModule, - dobefore : lDoBefore[lModule], - name : lNames[lModule] - }); + + + for(var i = 0, n = pModules.length; i < n ; i++){ + var lModule = pModules[i]; + + if(Util.isObject(lModule)){ + for(var lProp in lModule) + if(lProp === lStorage){ + var m = lModule[lProp].length, + lMod = lModule[lProp]; + + for(var j = 0; j < m; j++) + lLoad(lMod[j].name, lStorage + '/' + lMod[j].path); + + break; + } } + else + lLoad(null, lModule, lDoBefore[lModule]); + } Util.exec(pCallBack); }); diff --git a/modules.json b/modules.json index d7c68ff6..73595b98 100644 --- a/modules.json +++ b/modules.json @@ -2,10 +2,24 @@ "editor/_codemirror", "menu", "viewer", - "storage/_github", - "storage/_dropbox", - "storage/_dropbox_chooser", - "storage/_gdrive", - "storage/_vk", - "terminal" + "terminal",{ + "storage":[ + { + "name":"DropBox", + "path":"_dropbox" + }, + { + "name":"GitHub", + "path":"_github" + }, + { + "name":"GDrive", + "path":"_gdrive" + }, + { + "name":"VK", + "path":"_vk" + } + ] + } ] \ No newline at end of file From 75022497728f9b3f3eb3ca4f40192a70d3885685 Mon Sep 17 00:00:00 2001 From: coderaiser Date: Thu, 17 Jan 2013 08:07:28 -0500 Subject: [PATCH 102/347] minor changes --- lib/util.js | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/lib/util.js b/lib/util.js index 5e43be92..f28ab4b3 100644 --- a/lib/util.js +++ b/lib/util.js @@ -96,7 +96,7 @@ Util = exports || {}; var lArg = pArg, lConsole = Scope.console, lDate = '[' + Util.getDate() + '] '; - + if(lConsole && pArg) lConsole.log(lDate, lArg); @@ -136,7 +136,11 @@ Util = exports || {}; }; Util.replaceStr = function(pStr, pFrom, pTo){ - var lRet = pStr.replace(new RegExp(pFrom, 'g'), pTo); + var lRet; + + if(pStr && pFrom && pTo) + lRet = pStr.replace(new RegExp(pFrom, 'g'), pTo); + return lRet; }; From 04ca006c1147a96ac5b3d9feca28eb6c188c12d3 Mon Sep 17 00:00:00 2001 From: coderaiser Date: Thu, 17 Jan 2013 08:41:12 -0500 Subject: [PATCH 103/347] added ability to read storage modules information from menu module --- ChangeLog | 2 ++ lib/client/menu.js | 45 +++++++++++++++++++++++++++++++++++++-------- 2 files changed, 39 insertions(+), 8 deletions(-) diff --git a/ChangeLog b/ChangeLog index 1e4ff7f6..efc5195b 100644 --- a/ChangeLog +++ b/ChangeLog @@ -87,6 +87,8 @@ clicked on menu item. * Improved modules.json format and parsing. +* Added ability to read storage modules information from menu module. + 2012.12.12, Version 0.1.8 diff --git a/lib/client/menu.js b/lib/client/menu.js index 727a27e7..1ffa55fd 100644 --- a/lib/client/menu.js +++ b/lib/client/menu.js @@ -8,8 +8,9 @@ var CloudCommander, Util, DOM, $; var KeyBinding = CloudCmd.KeyBinding, MenuSeted = false, Menu = {}, - Position; - + Position, + UploadToItemNames; + Menu.dir = '/lib/client/menu/'; /* enable and disable menu constant */ @@ -33,17 +34,43 @@ var CloudCommander, Util, DOM, $; } } + /* function read data from modules.json + * and build array of menu items of "upload to" + * menu + */ + function setUploadToItemNames(pCallBack){ + CloudCmd.getModules(function(pModules){ + var lStorage = 'storage', + lItems = pModules[lStorage]; + + for(var i = 0, n = pModules.length; i < n; i++ ){ + lItems = pModules[i][lStorage]; + + if(lItems) + break; + } + + lItems = lItems || []; + UploadToItemNames = []; + + for(i = 0, n = lItems.length; i < n; i++) + UploadToItemNames[i] = lItems[i].name; + + Util.exec(pCallBack); + }); + } + /** * function get menu item object for Upload To */ - function getUploadToItem(pObjectName){ + function getUploadToItems(pObjectName){ var lObj = {}; if( Util.isArray(pObjectName) ){ var n = pObjectName.length; for(var i = 0; i < n; i++){ var lStr = pObjectName[i]; - lObj[lStr] = getUploadToItem( lStr ); + lObj[lStr] = getUploadToItems( lStr ); } } else if( Util.isString(pObjectName) ){ @@ -123,13 +150,14 @@ var CloudCommander, Util, DOM, $; 'View' : Util.retExec(showEditor, true), 'Edit' : Util.retExec(showEditor, false), 'Delete' : Util.retExec(DOM.promptRemoveCurrent), - 'Upload to' : getUploadToItem( + 'Upload to' : getUploadToItems(UploadToItemNames) + /* [ 'DropBox', 'GDrive', 'GitHub', 'VK' - ]), + ])*/, 'Download' : function(key, opt){ DOM.Images.showLoad(); @@ -225,8 +253,8 @@ var CloudCommander, Util, DOM, $; var lElement = document.elementFromPoint(pEvent.x, pEvent.y), lTag = lElement.tagName, lParent; - - if(lTag === 'A' || lTag === 'SPAN'){ + + if(lTag === 'A' || lTag === 'SPAN'){ if (lElement.tagName === 'A') lParent = lElement.parentElement.parentElement; else if(lElement.tagName === 'SPAN') @@ -275,6 +303,7 @@ var CloudCommander, Util, DOM, $; Util.loadOnLoad([ Menu.show, + setUploadToItemNames, load, DOM.jqueryLoad ]); From 02c0b7e871e7721d49f5e289cd2bb72a22b6b4f7 Mon Sep 17 00:00:00 2001 From: coderaiser Date: Thu, 17 Jan 2013 10:50:21 -0500 Subject: [PATCH 104/347] added easy template renderrer Util.render --- ChangeLog | 2 ++ lib/client/dom.js | 1 + lib/util.js | 46 ++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 49 insertions(+) diff --git a/ChangeLog b/ChangeLog index efc5195b..6b9bdce8 100644 --- a/ChangeLog +++ b/ChangeLog @@ -89,6 +89,8 @@ clicked on menu item. * Added ability to read storage modules information from menu module. +* Added easy template renderrer system Util.render. + 2012.12.12, Version 0.1.8 diff --git a/lib/client/dom.js b/lib/client/dom.js index 765f5fe2..cdfaa25e 100644 --- a/lib/client/dom.js +++ b/lib/client/dom.js @@ -550,6 +550,7 @@ var CloudCommander, Util, return lRet; }, + /** * Функция создаёт елемент style и записывает туда стили * @param pParams_o - структура параметров, заполняеться таким diff --git a/lib/util.js b/lib/util.js index f28ab4b3..f7f9f60c 100644 --- a/lib/util.js +++ b/lib/util.js @@ -135,6 +135,13 @@ Util = exports || {}; return pStr.replace(pSubStr,''); }; + /** + * function replase pFrom to pTo in pStr + * @pStr + * @pFrom + * @pTo + */ + Util.replaceStr = function(pStr, pFrom, pTo){ var lRet; @@ -144,6 +151,45 @@ Util = exports || {}; return lRet; }; + /** + * function render template with view + * @pTempl + * @pView + */ + Util.render = function(pTempl, pView){ + var lRet = Util.ownRender(pTempl, pView, ['{', '}']); + + return lRet; + }; + + /** + * function render template with view and own symbols + * @pTempl + * @pView + * @pSymbols + */ + Util.ownRender = function(pTempl, pView, pSymbols){ + if(!pSymbols) + pSymbols = ['{', '}']; + + var lRet = pTempl, + lFirstChar, + lSecondChar; + + lFirstChar = pSymbols[0]; + lSecondChar = pSymbols[1] || lFirstChar; + + for(var lVar in pView){ + var lStr = pView[lVar]; + lStr = Util.exec(lStr) || lStr; + + lRet = Util.replaceStr(lRet, lFirstChar + lVar + lSecondChar, lStr); + } + + return lRet; + }; + + /** * invoke a couple of functions in paralel * From 7c9268bd14795ef75e7df71f86abc495ec3da4f2 Mon Sep 17 00:00:00 2001 From: coderaiser Date: Fri, 18 Jan 2013 04:19:36 -0500 Subject: [PATCH 105/347] added functions DOM.parseJSON and DOM.stringifyJSON --- ChangeLog | 2 ++ lib/client.js | 13 ++----------- lib/client/dom.js | 34 ++++++++++++++++++++++++++++++---- lib/client/ie.js | 34 ++++++++++++++++++++++++++++++++++ lib/util.js | 2 +- 5 files changed, 69 insertions(+), 16 deletions(-) diff --git a/ChangeLog b/ChangeLog index 6b9bdce8..92bd8c06 100644 --- a/ChangeLog +++ b/ChangeLog @@ -91,6 +91,8 @@ clicked on menu item. * Added easy template renderrer system Util.render. +* Added functions DOM.parseJSON and DOM.stringifyJSON + 2012.12.12, Version 0.1.8 diff --git a/lib/client.js b/lib/client.js index 43af1e94..c74a3917 100644 --- a/lib/client.js +++ b/lib/client.js @@ -596,26 +596,17 @@ CloudCmd._ajaxLoad = function(pFullPath, pOptions){ /* опредиляем в какой мы панели: * правой или левой */ - var lPanel = DOM.getPanel().id, - lError; + var lPanel = DOM.getPanel().id; if(!pOptions.refresh && lPanel){ var lJSON = DOM.Cache.get(lPath); if (lJSON){ /* переводим из текста в JSON */ - if(window && !window.JSON){ - lError = Util.tryCatchLog(function(){ - lJSON = eval('('+lJSON+')'); - }); - - }else - lJSON = JSON.parse(lJSON); - + lJSON = DOM.parseJSON(lJSON); CloudCmd._createFileTable(lPanel, lJSON); CloudCmd._changeLinks(lPanel); - return; } } diff --git a/lib/client/dom.js b/lib/client/dom.js index cdfaa25e..9f62f561 100644 --- a/lib/client/dom.js +++ b/lib/client/dom.js @@ -135,13 +135,13 @@ var CloudCommander, Util, var lJqXHR = pEvent.target, lType = XMLHTTP.getResponseHeader('content-type'); - if (XMLHTTP.status === 200 /* OK */){ + if (XMLHTTP.status === 200 /* OK */){ var lData = lJqXHR.response; - /* If it's json - parse it as json */ + /* 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); + lData = DOM.parseJSON(lJqXHR.response); }); if( Util.log(lResult) ) @@ -804,7 +804,7 @@ var CloudCommander, Util, lFunc = function(pData){ var lName = DOM.getCurrentName(); if( Util.isObject(pData) ){ - pData = JSON.stringify(pData, null, 4); + pData = DOM.stringifyJSON(pData); var lExt = '.json'; if( !Util.checkExtension(lName, lExt) ) @@ -849,6 +849,32 @@ var CloudCommander, Util, return lRefresh; }; + /** + * @pJSON + */ + DOM.parseJSON = function(pJSON){ + var lRet; + + Util.tryCatchLog(function(){ + lRet = JSON.parse(pJSON); + }); + + return lRet; + }; + + /** + * pObj + */ + DOM.stringifyJSON = function(pObj){ + var lRet; + + Util.tryCatLog(function(){ + lRet = JSON.stringify(pObj, null, 4); + }); + + return lRet; + }; + /** * unified way to set current file */ diff --git a/lib/client/ie.js b/lib/client/ie.js index fb18acee..8742f409 100644 --- a/lib/client/ie.js +++ b/lib/client/ie.js @@ -152,6 +152,40 @@ var Util, DOM, $; if(!window.XMLHttpRequest) DOM.ajax = $.ajax; + if(!window.JSON){ + DOM.parseJSON = $.parseJSON; + + /* https://gist.github.com/754454 */ + DOM.stringifyJSON = function(pObj){ + var lRet; + + if (!Util.isObject(pObj) || pObj === null) { + // simple data type + if (Util.isString(pObj)) pObj = '"' + pObj + '"'; + lRet = String(pObj); + } else { + // recurse array or object + var n, v, json = [], isArray = Util.isArray(pObj); + + for (n in pObj) { + v = pObj[n]; + + if (pObj.hasOwnProperty(n)) { + if (Util.isString(v)) + v = '"' + v + '"'; + else if (Util.isObject(v) && v !== null) + v = DOM.stringifyJSON(v); + + json.push((isArray ? "" : '"' + n + '":') + String(v)); + } + } + lRet = (isArray ? "[" : "{") + String(json) + (isArray ? "]" : "}"); + } + + return lRet; + }; + } + if(!window.localStorage){ DOM.Cache = function(){ /* приватный переключатель возможности работы с кэшем */ diff --git a/lib/util.js b/lib/util.js index f7f9f60c..b5f712e8 100644 --- a/lib/util.js +++ b/lib/util.js @@ -96,7 +96,7 @@ Util = exports || {}; var lArg = pArg, lConsole = Scope.console, lDate = '[' + Util.getDate() + '] '; - + if(lConsole && pArg) lConsole.log(lDate, lArg); From c0291bdb7aa70a131f9bdb922a25e8d524146195 Mon Sep 17 00:00:00 2001 From: coderaiser Date: Fri, 18 Jan 2013 06:56:55 -0500 Subject: [PATCH 106/347] minor changes --- lib/client.js | 6 +++--- lib/client/dom.js | 29 ++--------------------------- lib/client/ie.js | 8 ++++---- lib/util.js | 26 ++++++++++++++++++++++++++ 4 files changed, 35 insertions(+), 34 deletions(-) diff --git a/lib/client.js b/lib/client.js index c74a3917..28637dac 100644 --- a/lib/client.js +++ b/lib/client.js @@ -603,7 +603,7 @@ CloudCmd._ajaxLoad = function(pFullPath, pOptions){ if (lJSON){ /* переводим из текста в JSON */ - lJSON = DOM.parseJSON(lJSON); + lJSON = Util.parseJSON(lJSON); CloudCmd._createFileTable(lPanel, lJSON); CloudCmd._changeLinks(lPanel); @@ -631,7 +631,7 @@ CloudCmd._ajaxLoad = function(pFullPath, pOptions){ /* переводим таблицу файлов в строку, для * сохранения в localStorage */ - var lJSON_s = JSON.stringify(data); + var lJSON_s = Util.stringifyJSON(data); Util.log(lJSON_s.length); /* если размер данных не очень бошьой @@ -749,7 +749,7 @@ CloudCmd._getJSONfromFileTable = function(){ mode: lMode }; } - return JSON.stringify(lFileTable); + return Util.stringifyJSON(lFileTable); }; return CloudCmd; diff --git a/lib/client/dom.js b/lib/client/dom.js index 9f62f561..2518832f 100644 --- a/lib/client/dom.js +++ b/lib/client/dom.js @@ -141,7 +141,7 @@ var CloudCommander, Util, /* If it's json - parse it as json */ if(lType && Util.isContainStr(lType, 'application/json') ){ var lResult = Util.tryCatch(function(){ - lData = DOM.parseJSON(lJqXHR.response); + lData = Util.parseJSON(lJqXHR.response); }); if( Util.log(lResult) ) @@ -804,7 +804,7 @@ var CloudCommander, Util, lFunc = function(pData){ var lName = DOM.getCurrentName(); if( Util.isObject(pData) ){ - pData = DOM.stringifyJSON(pData); + pData = Util.stringifyJSON(pData); var lExt = '.json'; if( !Util.checkExtension(lName, lExt) ) @@ -849,31 +849,6 @@ var CloudCommander, Util, return lRefresh; }; - /** - * @pJSON - */ - DOM.parseJSON = function(pJSON){ - var lRet; - - Util.tryCatchLog(function(){ - lRet = JSON.parse(pJSON); - }); - - return lRet; - }; - - /** - * pObj - */ - DOM.stringifyJSON = function(pObj){ - var lRet; - - Util.tryCatLog(function(){ - lRet = JSON.stringify(pObj, null, 4); - }); - - return lRet; - }; /** * unified way to set current file diff --git a/lib/client/ie.js b/lib/client/ie.js index 8742f409..46454924 100644 --- a/lib/client/ie.js +++ b/lib/client/ie.js @@ -153,10 +153,10 @@ var Util, DOM, $; DOM.ajax = $.ajax; if(!window.JSON){ - DOM.parseJSON = $.parseJSON; + Util.parseJSON = $.parseJSON; /* https://gist.github.com/754454 */ - DOM.stringifyJSON = function(pObj){ + Util.stringifyJSON = function(pObj){ var lRet; if (!Util.isObject(pObj) || pObj === null) { @@ -187,7 +187,7 @@ var Util, DOM, $; } if(!window.localStorage){ - DOM.Cache = function(){ + var Cache = function(){ /* приватный переключатель возможности работы с кэшем */ var CacheAllowed, Data = {}; @@ -248,7 +248,7 @@ var Util, DOM, $; }; }; - DOM.Cache = new DOM.Cache(); + DOM.Cache = new Cache(); } diff --git a/lib/util.js b/lib/util.js index b5f712e8..c1af6b16 100644 --- a/lib/util.js +++ b/lib/util.js @@ -65,6 +65,32 @@ Util = exports || {}; return lRet; }; + /** + * @pJSON + */ + Util.parseJSON = function(pJSON){ + var lRet; + + Util.tryCatchLog(function(){ + lRet = JSON.parse(pJSON); + }); + + return lRet; + }; + + /** + * pObj + */ + Util.stringifyJSON = function(pObj){ + var lRet; + + Util.tryCatchLog(function(){ + lRet = JSON.stringify(pObj, null, 4); + }); + + return lRet; + }; + /* STRINGS */ /** * function check is strings are equal From 4d8962b78ca2fd4f7de7f6dbd4f99aafd8da49d0 Mon Sep 17 00:00:00 2001 From: coderaiser Date: Fri, 18 Jan 2013 07:28:38 -0500 Subject: [PATCH 107/347] minor changes --- lib/server/ischanged.js | 77 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 77 insertions(+) create mode 100644 lib/server/ischanged.js diff --git a/lib/server/ischanged.js b/lib/server/ischanged.js new file mode 100644 index 00000000..8fb17504 --- /dev/null +++ b/lib/server/ischanged.js @@ -0,0 +1,77 @@ +(function(){ + 'use strict'; + + var main = require('./main'), //global.cloudcmd.main, + DIR = main.DIR, + + fs = main.fs, + path = main.path, + Util = main.util, + + /* object contains hashes of files*/ + CHANGESNAME = DIR + 'changes', + CHANGES_JSON = CHANGESNAME + '.json', + + Times = main.require(CHANGESNAME) || [], + TimesChanged; + + function isFileChanged(pFileName, pLastFile_b, pCallBack){ + var lReadedTime; + + var i, n = Times.length; + for(i = 0; i < n; i++){ + var lData = Times[i]; + + /* if founded row with file name - save hash */ + if(lData.name === pFileName){ + lReadedTime = lData.time; + break; + } + } + + fs.stat(pFileName, function(pError, pStat){ + var lTimeChanged; + + if(!pError){ + var lFileTime = pStat.mtime.getTime(); + + if(lReadedTime !== lFileTime){ + Times[i] = { + name: pFileName, + time: lFileTime + }; + + lTimeChanged = + TimesChanged = true; + } + + if(pLastFile_b && TimesChanged) + writeFile(CHANGES_JSON, Util.stringifyJSON(Times)); + } + else{ + Util.log(pError); + lTimeChanged = false; + } + + Util.exec(pCallBack, lTimeChanged); + }); + } + + /* + * Функция записывает файла + * и выводит ошибку или сообщает, + * что файл успешно записан + */ + function writeFile(pFileName, pData){ + fs.writeFile(pFileName, pData, function(pError){ + if(pError) + Util.log(pError); + else + Util.log('minify: file ' + path.basename(pFileName) + ' writed...'); + }); + } + + isFileChanged(DIR + 'favicon.ico', true, function(pData){ + console.log(pData) + }); +})(); \ No newline at end of file From 3fb6fa393577117b472851fb0d6d3c694fa5d41f Mon Sep 17 00:00:00 2001 From: coderaiser Date: Fri, 18 Jan 2013 11:00:34 -0500 Subject: [PATCH 108/347] Improved modules.json --- ChangeLog | 2 ++ lib/client.js | 8 ++++++-- lib/client/menu.js | 6 +----- modules.json | 21 +++++---------------- 4 files changed, 14 insertions(+), 23 deletions(-) diff --git a/ChangeLog b/ChangeLog index 92bd8c06..3ac003c4 100644 --- a/ChangeLog +++ b/ChangeLog @@ -93,6 +93,8 @@ clicked on menu item. * Added functions DOM.parseJSON and DOM.stringifyJSON +* Improved modules.json. + 2012.12.12, Version 0.1.8 diff --git a/lib/client.js b/lib/client.js index 28637dac..0c69fedf 100644 --- a/lib/client.js +++ b/lib/client.js @@ -278,8 +278,12 @@ function initModules(pCallBack){ var m = lModule[lProp].length, lMod = lModule[lProp]; - for(var j = 0; j < m; j++) - lLoad(lMod[j].name, lStorage + '/' + lMod[j].path); + for(var j = 0; j < m; j++){ + var lName = lMod[j], + lPath = lStorage + '/_' + lName.toLowerCase(); + + lLoad(lName, lPath); + } break; } diff --git a/lib/client/menu.js b/lib/client/menu.js index 1ffa55fd..a36c781c 100644 --- a/lib/client/menu.js +++ b/lib/client/menu.js @@ -50,11 +50,7 @@ var CloudCommander, Util, DOM, $; break; } - lItems = lItems || []; - UploadToItemNames = []; - - for(i = 0, n = lItems.length; i < n; i++) - UploadToItemNames[i] = lItems[i].name; + UploadToItemNames = lItems || []; Util.exec(pCallBack); }); diff --git a/modules.json b/modules.json index 73595b98..026bf9d7 100644 --- a/modules.json +++ b/modules.json @@ -4,22 +4,11 @@ "viewer", "terminal",{ "storage":[ - { - "name":"DropBox", - "path":"_dropbox" - }, - { - "name":"GitHub", - "path":"_github" - }, - { - "name":"GDrive", - "path":"_gdrive" - }, - { - "name":"VK", - "path":"_vk" - } + "DropBox", + "GitHub", + "GDrive", + "VK", + "SkyDrive" ] } ] \ No newline at end of file From 5251cdd9bc642b3ae7043fcc1fec2ede07a03c75 Mon Sep 17 00:00:00 2001 From: coderaiser Date: Mon, 21 Jan 2013 04:06:15 -0500 Subject: [PATCH 109/347] refactored --- lib/client/menu.js | 23 ++++++++++++----------- 1 file changed, 12 insertions(+), 11 deletions(-) diff --git a/lib/client/menu.js b/lib/client/menu.js index a36c781c..c891e824 100644 --- a/lib/client/menu.js +++ b/lib/client/menu.js @@ -76,11 +76,13 @@ var CloudCommander, Util, DOM, $; DOM.getCurrentData(function(pParams){ var lObject = CloudCmd[pObjectName]; - if('init' in lObject) - lObject.uploadFile(pParams); - else - Util.exec(lObject, function(){ + Util.ifExec('init' in lObject, + function(){ CloudCmd[pObjectName].uploadFile(pParams); + }, + + function(pCallBack){ + Util.exec(lObject, pCallBack); }); }); @@ -157,8 +159,8 @@ var CloudCommander, Util, DOM, $; 'Download' : function(key, opt){ DOM.Images.showLoad(); - var lPath = DOM.getCurrentPath(), - lId = DOM.getIdBySrc(lPath); + var lPath = DOM.getCurrentPath(), + lId = DOM.getIdBySrc(lPath); Util.log('downloading file ' + lPath +'...'); @@ -284,13 +286,12 @@ var CloudCommander, Util, DOM, $; */ Menu.show = function(){ set(); - DOM.Images.hideLoad(); - if(Position && Position.x && Position.y) - $('li').contextMenu(Position); - else - $('li').contextMenu(); + if(Position && !Position.x ) + Position = null; + + $('li').contextMenu(Position); }; /* key binding function */ From 43bf32fcf7bade559cfdc0f203660d7bbab76f39 Mon Sep 17 00:00:00 2001 From: coderaiser Date: Mon, 21 Jan 2013 09:53:00 -0500 Subject: [PATCH 110/347] refactored --- lib/client/dom.js | 77 +++++++++++++++++++++++++++++------------------ lib/client/ie.js | 6 ++++ 2 files changed, 54 insertions(+), 29 deletions(-) diff --git a/lib/client/dom.js b/lib/client/dom.js index 2518832f..8f35728e 100644 --- a/lib/client/dom.js +++ b/lib/client/dom.js @@ -102,6 +102,26 @@ var CloudCommander, Util, return lRet; }; + /** + * safe remove event listener + * @param pType + * @param pListener + * @param pUseCapture + * @param pElement {document by default} + */ + DOM.removeListener = function(pType, pListener, pUseCapture, pElement){ + var lRet = this; + + (pElement || window).removeEventListener( + pType, + pListener, + pUseCapture || false + ); + + return lRet; + }; + + /** * safe add event keydown listener * @param pListener @@ -417,8 +437,9 @@ var CloudCommander, Util, lFunc = pParams_o.func, lOnError, lAsync = pParams_o.async, - lParent = pParams_o.parent, + lParent = pParams_o.parent || document.body, lInner = pParams_o.inner, + lStyle = pParams_o.style, lNotAppend = pParams_o.not_append; if ( Util.isObject(lFunc) ){ @@ -429,10 +450,10 @@ var CloudCommander, Util, if(!lID && lSrc) lID = DOM.getIdBySrc(lSrc); - var element = DOM.getById(lID); + var lElement = DOM.getById(lID); /* если скрипт еще не загружен */ - if(!element){ + if(!lElement){ if(!lName && lSrc){ var lDot = lSrc.lastIndexOf('.'), @@ -449,23 +470,23 @@ var CloudCommander, Util, return {code: -1, text: 'name can not be empty'}; } } - element = document.createElement(lName); + lElement = document.createElement(lName); if(lID) - element.id = lID; + lElement.id = lID; if(lClass) - element.className = lClass; + lElement.className = lClass; /* if working with external css * using href in any other case * using src */ if(lName === 'link'){ - element.href = lSrc; - element.rel = 'stylesheet'; + lElement.href = lSrc; + lElement.rel = 'stylesheet'; }else - element.src = lSrc; + lElement.src = lSrc; /* * if passed arguments function @@ -474,16 +495,13 @@ var CloudCommander, Util, * 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); + var lLoad = function(pEvent){ + DOM.removeListener('load', lLoad, false, lElement); + Util.exec(lFunc, pEvent); + }, + + lError = function(){ + lParent.removeChild(lElement); DOM.Images.showError({ responseText: 'file ' + @@ -493,21 +511,22 @@ var CloudCommander, Util, }); Util.exec(lOnError); - }); + }; - if(pParams_o.style){ - element.style.cssText = pParams_o.style; - } + DOM.addListener('load', lLoad, false, lElement); + DOM.addListener('error', lError, false, lElement); + + if(lStyle) + lElement.style.cssText = lStyle; if(lAsync || lAsync === undefined) - element.async = true; + lElement.async = true; if(!lNotAppend) - (lParent || document.body).appendChild(element); + lParent.appendChild(lElement); - if(lInner){ - element.innerHTML = lInner; - } + if(lInner) + lElement.innerHTML = lInner; } /* если js-файл уже загружен * запускаем функцию onload @@ -515,7 +534,7 @@ var CloudCommander, Util, else Util.exec(lFunc); - return element; + return lElement; }, /** diff --git a/lib/client/ie.js b/lib/client/ie.js index 46454924..6ac58860 100644 --- a/lib/client/ie.js +++ b/lib/client/ie.js @@ -58,6 +58,12 @@ var Util, DOM, $; return lRet; }; + if(document.removeEventListener){ + DOM.removeListener = function(pType, pListener, pCapture, pElement){ + $(pElement || window).unbind(pType, pListener); + }; + } + if(!document.getElementsByClassName){ DOM.getByClass = function(pClass, pElement){ var lClass = '.' + pClass, From fd2c227cf7b0678bfc4785ae75c2411df2e7e578 Mon Sep 17 00:00:00 2001 From: coderaiser Date: Mon, 21 Jan 2013 10:18:43 -0500 Subject: [PATCH 111/347] refactored --- lib/client.js | 8 ++++---- lib/client/dom.js | 40 ++++++++++++++++++++++++++++++++-------- 2 files changed, 36 insertions(+), 12 deletions(-) diff --git a/lib/client.js b/lib/client.js index 0c69fedf..37aa6a81 100644 --- a/lib/client.js +++ b/lib/client.js @@ -759,12 +759,12 @@ CloudCmd._getJSONfromFileTable = function(){ return CloudCmd; })(); -window.onload = function(){ - 'use strict'; - + +DOM.addOneTimeListener('load', function cloudcmdLoad(){ + DOM.removeListener('load', cloudcmdLoad); /* базовая инициализация*/ CloudCommander.init(); /* загружаем Google Analytics */ CloudCommander.GoogleAnalytics(); -}; +}); diff --git a/lib/client/dom.js b/lib/client/dom.js index 8f35728e..bcea7207 100644 --- a/lib/client/dom.js +++ b/lib/client/dom.js @@ -70,17 +70,19 @@ var CloudCommander, Util, * @param pClass */ DOM.addClass = function(pElement, pClass){ - var lRet_b = true; + var lRet; - var lClassList = pElement.classList; - if(lClassList){ - if( !lClassList.contains(pClass) ) - lClassList.add(pClass); - else - lRet_b = false; + if(pElement){ + var lClassList = pElement.classList; + + if(lClassList){ + if( !lClassList.contains(pClass) ) + lClassList.add(pClass); + lRet = true; + } } - return lRet_b; + return lRet; }; /** @@ -102,6 +104,26 @@ var CloudCommander, Util, return lRet; }; + /** + * safe add event listener + * @param pType + * @param pListener + * @param pUseCapture + * @param pElement {document by default} + */ + DOM.addOneTimeListener = function(pType, pListener, pUseCapture, pElement){ + var lRet = this; + + DOM.addListener(pType, + function oneTime(pEvent){ + DOM.removeListener(pType, oneTime, pUseCapture, pElement); + pListener(pEvent); + }, + pUseCapture, pElement); + + return lRet; + }; + /** * safe remove event listener * @param pType @@ -497,6 +519,8 @@ var CloudCommander, Util, var lLoad = function(pEvent){ DOM.removeListener('load', lLoad, false, lElement); + DOM.removeListener('error', lError, false, lElement); + Util.exec(lFunc, pEvent); }, From 5006aa500f9b9f0478f5e680f0a0cff6765725b5 Mon Sep 17 00:00:00 2001 From: coderaiser Date: Mon, 21 Jan 2013 13:52:26 -0500 Subject: [PATCH 112/347] refactored --- lib/client/ie.js | 17 ++++------------- 1 file changed, 4 insertions(+), 13 deletions(-) diff --git a/lib/client/ie.js b/lib/client/ie.js index 6ac58860..0ea6d9a0 100644 --- a/lib/client/ie.js +++ b/lib/client/ie.js @@ -6,7 +6,7 @@ var Util, DOM, $; /* setting head ie6 - ie8 */ if(!document.head){ - document.head = document.getElementsByTagName("head")[0]; + document.head = $('head')[0]; /* {name: '', src: ' ',func: '', style: '', id: '', parent: '', @@ -45,22 +45,13 @@ var Util, DOM, $; * @param pType * @param pListener */ - DOM.addListener = function(pType, pListener){ - var lRet = this, - lType = 'on' + pType, - lFunc = document[lType]; - - document[lType] = function(){ - Util.exec(lFunc); - pListener(); - }; - - return lRet; + DOM.addListener = function(pType, pListener, pCapture, pElement){ + return $(pElement || window).bind(pType, null, pListener); }; if(document.removeEventListener){ DOM.removeListener = function(pType, pListener, pCapture, pElement){ - $(pElement || window).unbind(pType, pListener); + return $(pElement || window).unbind(pType, pListener); }; } From 57e76145495fcf6f4f28d3dabd7ce486fc93bf87 Mon Sep 17 00:00:00 2001 From: coderaiser Date: Mon, 21 Jan 2013 13:53:59 -0500 Subject: [PATCH 113/347] refactored --- lib/client/ie.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/client/ie.js b/lib/client/ie.js index 0ea6d9a0..b58e60b6 100644 --- a/lib/client/ie.js +++ b/lib/client/ie.js @@ -170,7 +170,7 @@ var Util, DOM, $; if (pObj.hasOwnProperty(n)) { if (Util.isString(v)) v = '"' + v + '"'; - else if (Util.isObject(v) && v !== null) + else if (v && Util.isObject(v)) v = DOM.stringifyJSON(v); json.push((isArray ? "" : '"' + n + '":') + String(v)); From bc6bbb0b34d46384a27d1fd5519b6144a2620731 Mon Sep 17 00:00:00 2001 From: coderaiser Date: Tue, 22 Jan 2013 06:38:31 -0500 Subject: [PATCH 114/347] refactored --- html/index.html | 8 ++++---- lib/client.js | 5 ++--- lib/client/dom.js | 2 +- lib/client/ie.js | 16 +++++++++------- lib/server/main.js | 6 +++--- lib/util.js | 2 +- 6 files changed, 20 insertions(+), 19 deletions(-) diff --git a/html/index.html b/html/index.html index 85d13d8c..6bba869f 100644 --- a/html/index.html +++ b/html/index.html @@ -36,11 +36,11 @@
    - - + \ No newline at end of file diff --git a/lib/client.js b/lib/client.js index 37aa6a81..55e45545 100644 --- a/lib/client.js +++ b/lib/client.js @@ -324,7 +324,7 @@ function initKeysPanel(pCallBack){ } function baseInit(pCallBack){ - if(applicationCache){ + if(window.applicationCache){ var lFunc = applicationCache.onupdateready; applicationCache.onupdateready = function(){ @@ -760,8 +760,7 @@ return CloudCmd; })(); -DOM.addOneTimeListener('load', function cloudcmdLoad(){ - DOM.removeListener('load', cloudcmdLoad); +DOM.addOneTimeListener('load', function(){ /* базовая инициализация*/ CloudCommander.init(); diff --git a/lib/client/dom.js b/lib/client/dom.js index bcea7207..a5b97984 100644 --- a/lib/client/dom.js +++ b/lib/client/dom.js @@ -3,7 +3,7 @@ var CloudCommander, Util, CloudFunc; (function(Util, DOM){ - "use strict"; + 'use strict'; /* PRIVATE */ diff --git a/lib/client/ie.js b/lib/client/ie.js index b58e60b6..f943a0cc 100644 --- a/lib/client/ie.js +++ b/lib/client/ie.js @@ -1,9 +1,12 @@ /* script, fixes ie */ -var Util, DOM, $; +//var Util, DOM, jQuery; (function(Util, DOM, $){ - "use strict"; + 'use strict'; + if(!window.XMLHttpRequest || !document.head) + DOM.ajax = $.ajax; + /* setting head ie6 - ie8 */ if(!document.head){ document.head = $('head')[0]; @@ -49,7 +52,7 @@ var Util, DOM, $; return $(pElement || window).bind(pType, null, pListener); }; - if(document.removeEventListener){ + if(!document.removeEventListener){ DOM.removeListener = function(pType, pListener, pCapture, pElement){ return $(pElement || window).unbind(pType, pListener); }; @@ -70,6 +73,8 @@ var Util, DOM, $; /* function polyfill webkit standart function */ DOM.scrollIntoViewIfNeeded = function(pElement, centerIfNeeded){ + if(!window.getComputedStyle) + return; /* https://gist.github.com/2581101 */ @@ -146,9 +151,6 @@ var Util, DOM, $; }; } - if(!window.XMLHttpRequest) - DOM.ajax = $.ajax; - if(!window.JSON){ Util.parseJSON = $.parseJSON; @@ -249,4 +251,4 @@ var Util, DOM, $; } -})(Util, DOM, $); \ No newline at end of file +})(Util, DOM, jQuery); \ No newline at end of file diff --git a/lib/server/main.js b/lib/server/main.js index 2de3ea00..ee49127a 100644 --- a/lib/server/main.js +++ b/lib/server/main.js @@ -132,10 +132,10 @@ * @param pGzip - данные сжаты gzip'ом */ function generateHeaders(pName, pGzip, pQuery){ - var lType = '', - lCacheControl = 0, + var lRet, + lType = '', lContentEncoding = '', - lRet, + lCacheControl = 0, lDot = pName.lastIndexOf('.'), lExt = pName.substr(lDot); diff --git a/lib/util.js b/lib/util.js index c1af6b16..03db79c9 100644 --- a/lib/util.js +++ b/lib/util.js @@ -6,7 +6,7 @@ var Util, exports; Util = exports || {}; (function(Util){ - "use strict"; + 'use strict'; var Scope = exports ? global : window; From 0da117af05099a75afa0b6e462fc08e91cfa0c35 Mon Sep 17 00:00:00 2001 From: coderaiser Date: Tue, 22 Jan 2013 09:21:19 -0500 Subject: [PATCH 115/347] minor changes --- html/index.html | 1 + lib/client.js | 1 - lib/client/dom.js | 16 ++++++++-------- lib/client/ie.js | 24 +++++++++++++++++++++--- lib/client/terminal.js | 8 +++++--- lib/server/main.js | 4 ++-- 6 files changed, 37 insertions(+), 17 deletions(-) diff --git a/html/index.html b/html/index.html index 6bba869f..51033780 100644 --- a/html/index.html +++ b/html/index.html @@ -40,6 +40,7 @@ + diff --git a/lib/client.js b/lib/client.js index 55e45545..1f5887ae 100644 --- a/lib/client.js +++ b/lib/client.js @@ -536,7 +536,6 @@ CloudCmd._changeLinks = function(pPanelID){ DOM.addListener('touchend', CloudCmd._loadDir(link), - false, lLi ); } diff --git a/lib/client/dom.js b/lib/client/dom.js index a5b97984..5a5df352 100644 --- a/lib/client/dom.js +++ b/lib/client/dom.js @@ -92,7 +92,7 @@ var CloudCommander, Util, * @param pUseCapture * @param pElement {document by default} */ - DOM.addListener = function(pType, pListener, pUseCapture, pElement){ + DOM.addListener = function(pType, pListener, pElement, pUseCapture){ var lRet = this; (pElement || window).addEventListener( @@ -111,12 +111,12 @@ var CloudCommander, Util, * @param pUseCapture * @param pElement {document by default} */ - DOM.addOneTimeListener = function(pType, pListener, pUseCapture, pElement){ + DOM.addOneTimeListener = function(pType, pListener, pElement, pUseCapture){ var lRet = this; DOM.addListener(pType, function oneTime(pEvent){ - DOM.removeListener(pType, oneTime, pUseCapture, pElement); + DOM.removeListener(pType, oneTime, pElement, pUseCapture); pListener(pEvent); }, pUseCapture, pElement); @@ -131,7 +131,7 @@ var CloudCommander, Util, * @param pUseCapture * @param pElement {document by default} */ - DOM.removeListener = function(pType, pListener, pUseCapture, pElement){ + DOM.removeListener = function(pType, pListener, pElement, pUseCapture){ var lRet = this; (pElement || window).removeEventListener( @@ -149,8 +149,8 @@ var CloudCommander, Util, * @param pListener * @param pUseCapture */ - DOM.addKeyListener = function(pListener, pUseCapture){ - return DOM.addListener('keydown', pListener, pUseCapture); + DOM.addKeyListener = function(pListener, pElement, pUseCapture){ + return DOM.addListener('keydown', pListener, pElement, pUseCapture); }; /** @@ -537,8 +537,8 @@ var CloudCommander, Util, Util.exec(lOnError); }; - DOM.addListener('load', lLoad, false, lElement); - DOM.addListener('error', lError, false, lElement); + DOM.addListener('load', lLoad, lElement); + DOM.addListener('error', lError,lElement); if(lStyle) lElement.style.cssText = lStyle; diff --git a/lib/client/ie.js b/lib/client/ie.js index f943a0cc..25df0b02 100644 --- a/lib/client/ie.js +++ b/lib/client/ie.js @@ -32,7 +32,11 @@ /* setting function context (this) */ Util.bind = function(pFunction, pContext){ - return $.proxy(pFunction, pContext); + var lRet; + + lRet = $.proxy(pFunction, pContext); + + return lRet; }; /* @@ -49,12 +53,26 @@ * @param pListener */ DOM.addListener = function(pType, pListener, pCapture, pElement){ - return $(pElement || window).bind(pType, null, pListener); + var lRet; + + if(!pElement) + pElement = window; + + lRet = $(pElement).bind(pType, null, pListener); + + return lRet; }; if(!document.removeEventListener){ DOM.removeListener = function(pType, pListener, pCapture, pElement){ - return $(pElement || window).unbind(pType, pListener); + var lRet; + + if(!pElement) + pElement = window; + + $(pElement).unbind(pType, pListener); + + return lRet; }; } diff --git a/lib/client/terminal.js b/lib/client/terminal.js index d5b99c38..bc05c261 100644 --- a/lib/client/terminal.js +++ b/lib/client/terminal.js @@ -19,15 +19,17 @@ var CloudCommander, Util, DOM, $; */ function load(pCallBack){ console.time('terminal load'); - + var lDir = '/lib/client/terminal/jquery-terminal/jquery.', lFiles = [ lDir + 'terminal.js', lDir + 'mousewheel.js', lDir + 'terminal.css' - ]; + ], + lJqueryMigrate = '//code.jquery.com/jquery-migrate-1.0.0.js'; + /* //github.com/jquery/jquery-migrate/ */ - DOM.anyLoadOnLoad([lFiles], function(){ + DOM.anyLoadOnLoad([lFiles, lJqueryMigrate], function(){ console.timeEnd('terminal load'); init(); $(function($, undefined) { diff --git a/lib/server/main.js b/lib/server/main.js index ee49127a..381b9aaa 100644 --- a/lib/server/main.js +++ b/lib/server/main.js @@ -186,8 +186,8 @@ lReq = pParams.request, lRes = pParams.response, - lEnc = lReq.headers['accept-encoding'] || '', - lGzip = lEnc.match(/\bgzip\b/), + lEnc = lReq.headers['accept-encoding'] || '', + lGzip = lEnc.match(/\bgzip\b/), lReadStream = fs.createReadStream(lName, { 'bufferSize': 4 * 1024 From c05aa0be8ed1767e5d6522ee8fd7a4d40b485c26 Mon Sep 17 00:00:00 2001 From: coderaiser Date: Tue, 22 Jan 2013 09:22:47 -0500 Subject: [PATCH 116/347] added migrate plugin for jquery --- ChangeLog | 2 + .../jquery-terminal/jquery-migrate-1.0.0.js | 498 ++++++++++++++++++ 2 files changed, 500 insertions(+) create mode 100644 lib/client/terminal/jquery-terminal/jquery-migrate-1.0.0.js diff --git a/ChangeLog b/ChangeLog index 3ac003c4..4c4b04b4 100644 --- a/ChangeLog +++ b/ChangeLog @@ -95,6 +95,8 @@ clicked on menu item. * Improved modules.json. +* Added migrate plugin for jquery. + 2012.12.12, Version 0.1.8 diff --git a/lib/client/terminal/jquery-terminal/jquery-migrate-1.0.0.js b/lib/client/terminal/jquery-terminal/jquery-migrate-1.0.0.js new file mode 100644 index 00000000..27111d80 --- /dev/null +++ b/lib/client/terminal/jquery-terminal/jquery-migrate-1.0.0.js @@ -0,0 +1,498 @@ +/*! + * jQuery Migrate - v1.0.0 - 2013-01-14 + * https://github.com/jquery/jquery-migrate + * Copyright 2005, 2013 jQuery Foundation, Inc. and other contributors; Licensed MIT + */ +(function( jQuery, window, undefined ) { +"use strict"; + + +var warnedAbout = {}; + +// List of warnings already given; public read only +jQuery.migrateWarnings = []; + +// Set to true to prevent console output; migrateWarnings still maintained +// jQuery.migrateMute = false; + +// Forget any warnings we've already given; public +jQuery.migrateReset = function() { + warnedAbout = {}; + jQuery.migrateWarnings.length = 0; +}; + +function migrateWarn( msg) { + if ( !warnedAbout[ msg ] ) { + warnedAbout[ msg ] = true; + jQuery.migrateWarnings.push( msg ); + if ( window.console && console.warn && !jQuery.migrateMute ) { + console.warn( "JQMIGRATE: " + msg ); + } + } +} + +function migrateWarnProp( obj, prop, value, msg ) { + if ( Object.defineProperty ) { + // On ES5 browsers (non-oldIE), warn if the code tries to get prop; + // allow property to be overwritten in case some other plugin wants it + try { + Object.defineProperty( obj, prop, { + configurable: true, + enumerable: true, + get: function() { + migrateWarn( msg ); + return value; + }, + set: function( newValue ) { + migrateWarn( msg ); + value = newValue; + } + }); + return; + } catch( err ) { + // IE8 is a dope about Object.defineProperty, can't warn there + } + } + + // Non-ES5 (or broken) browser; just set the property + jQuery._definePropertyBroken = true; + obj[ prop ] = value; +} + +if ( document.compatMode === "BackCompat" ) { + // jQuery has never supported or tested Quirks Mode + migrateWarn( "jQuery is not compatible with Quirks Mode" ); +} + + +var attrFn = {}, + attr = jQuery.attr, + valueAttrGet = jQuery.attrHooks.value && jQuery.attrHooks.value.get || + function() { return null; }, + valueAttrSet = jQuery.attrHooks.value && jQuery.attrHooks.value.set || + function() { return undefined; }, + rnoType = /^(?:input|button)$/i, + rnoAttrNodeType = /^[238]$/, + rboolean = /^(?:autofocus|autoplay|async|checked|controls|defer|disabled|hidden|loop|multiple|open|readonly|required|scoped|selected)$/i, + ruseDefault = /^(?:checked|selected)$/i; + +// jQuery.attrFn +migrateWarnProp( jQuery, "attrFn", attrFn, "jQuery.attrFn is deprecated" ); + +jQuery.attr = function( elem, name, value, pass ) { + var lowerName = name.toLowerCase(), + nType = elem && elem.nodeType; + + if ( pass ) { + migrateWarn("jQuery.fn.attr( props, pass ) is deprecated"); + if ( elem && !rnoAttrNodeType.test( nType ) && jQuery.isFunction( jQuery.fn[ name ] ) ) { + return jQuery( elem )[ name ]( value ); + } + } + + // Warn if user tries to set `type` since it breaks on IE 6/7/8 + if ( name === "type" && value !== undefined && rnoType.test( elem.nodeName ) ) { + migrateWarn("Can't change the 'type' of an input or button in IE 6/7/8"); + } + + // Restore boolHook for boolean property/attribute synchronization + if ( !jQuery.attrHooks[ lowerName ] && rboolean.test( lowerName ) ) { + jQuery.attrHooks[ lowerName ] = { + get: function( elem, name ) { + // Align boolean attributes with corresponding properties + // Fall back to attribute presence where some booleans are not supported + var attrNode, + property = jQuery.prop( elem, name ); + return property === true || typeof property !== "boolean" && + ( attrNode = elem.getAttributeNode(name) ) && attrNode.nodeValue !== false ? + + name.toLowerCase() : + undefined; + }, + set: function( elem, value, name ) { + var propName; + if ( value === false ) { + // Remove boolean attributes when set to false + jQuery.removeAttr( elem, name ); + } else { + // value is true since we know at this point it's type boolean and not false + // Set boolean attributes to the same name and set the DOM property + propName = jQuery.propFix[ name ] || name; + if ( propName in elem ) { + // Only set the IDL specifically if it already exists on the element + elem[ propName ] = true; + } + + elem.setAttribute( name, name.toLowerCase() ); + } + return name; + } + }; + + // Warn only for attributes that can remain distinct from their properties post-1.9 + if ( ruseDefault.test( lowerName ) ) { + migrateWarn( "jQuery.fn.attr(" + lowerName + ") may use property instead of attribute" ); + } + } + + return attr.call( jQuery, elem, name, value ); +}; + +// attrHooks: value +jQuery.attrHooks.value = { + get: function( elem, name ) { + var nodeName = ( elem.nodeName || "" ).toLowerCase(); + if ( nodeName === "button" ) { + return valueAttrGet.apply( this, arguments ); + } + if ( nodeName !== "input" && nodeName !== "option" ) { + migrateWarn("property-based jQuery.fn.attr('value') is deprecated"); + } + return name in elem ? + elem.value : + null; + }, + set: function( elem, value ) { + var nodeName = ( elem.nodeName || "" ).toLowerCase(); + if ( nodeName === "button" ) { + return valueAttrSet.apply( this, arguments ); + } + if ( nodeName !== "input" && nodeName !== "option" ) { + migrateWarn("property-based jQuery.fn.attr('value', val) is deprecated"); + } + // Does not return so that setAttribute is also used + elem.value = value; + } +}; + + +var matched, browser, + oldInit = jQuery.fn.init, + // Note this does NOT include the # XSS fix from 1.7! + rquickExpr = /^(?:.*(<[\w\W]+>)[^>]*|#([\w\-]*))$/; + +// $(html) "looks like html" rule change +jQuery.fn.init = function( selector, context, rootjQuery ) { + var match; + + if ( selector && typeof selector === "string" && !jQuery.isPlainObject( context ) && + (match = rquickExpr.exec( selector )) && match[1] ) { + // This is an HTML string according to the "old" rules; is it still? + if ( selector.charAt( 0 ) !== "<" ) { + migrateWarn("$(html) HTML strings must start with '<' character"); + } + // Now process using loose rules; let pre-1.8 play too + if ( context && context.context ) { + // jQuery object as context; parseHTML expects a DOM object + context = context.context; + } + if ( jQuery.parseHTML ) { + return oldInit.call( this, jQuery.parseHTML( jQuery.trim(selector), context, true ), + context, rootjQuery ); + } + } + return oldInit.apply( this, arguments ); +}; +jQuery.fn.init.prototype = jQuery.fn; + +jQuery.uaMatch = function( ua ) { + ua = ua.toLowerCase(); + + var match = /(chrome)[ \/]([\w.]+)/.exec( ua ) || + /(webkit)[ \/]([\w.]+)/.exec( ua ) || + /(opera)(?:.*version|)[ \/]([\w.]+)/.exec( ua ) || + /(msie) ([\w.]+)/.exec( ua ) || + ua.indexOf("compatible") < 0 && /(mozilla)(?:.*? rv:([\w.]+)|)/.exec( ua ) || + []; + + return { + browser: match[ 1 ] || "", + version: match[ 2 ] || "0" + }; +}; + +matched = jQuery.uaMatch( navigator.userAgent ); +browser = {}; + +if ( matched.browser ) { + browser[ matched.browser ] = true; + browser.version = matched.version; +} + +// Chrome is Webkit, but Webkit is also Safari. +if ( browser.chrome ) { + browser.webkit = true; +} else if ( browser.webkit ) { + browser.safari = true; +} + +jQuery.browser = browser; + +// Warn if the code tries to get jQuery.browser +migrateWarnProp( jQuery, "browser", browser, "jQuery.browser is deprecated" ); + +jQuery.sub = function() { + function jQuerySub( selector, context ) { + return new jQuerySub.fn.init( selector, context ); + } + jQuery.extend( true, jQuerySub, this ); + jQuerySub.superclass = this; + jQuerySub.fn = jQuerySub.prototype = this(); + jQuerySub.fn.constructor = jQuerySub; + jQuerySub.sub = this.sub; + jQuerySub.fn.init = function init( selector, context ) { + if ( context && context instanceof jQuery && !(context instanceof jQuerySub) ) { + context = jQuerySub( context ); + } + + return jQuery.fn.init.call( this, selector, context, rootjQuerySub ); + }; + jQuerySub.fn.init.prototype = jQuerySub.fn; + var rootjQuerySub = jQuerySub(document); + migrateWarn( "jQuery.sub() is deprecated" ); + return jQuerySub; +}; + + +var oldFnData = jQuery.fn.data; + +jQuery.fn.data = function( name ) { + var ret, evt, + elem = this[0]; + + // Handles 1.7 which has this behavior and 1.8 which doesn't + if ( elem && name === "events" && arguments.length === 1 ) { + ret = jQuery.data( elem, name ); + evt = jQuery._data( elem, name ); + if ( ( ret === undefined || ret === evt ) && evt !== undefined ) { + migrateWarn("Use of jQuery.fn.data('events') is deprecated"); + return evt; + } + } + return oldFnData.apply( this, arguments ); +}; + + +var rscriptType = /\/(java|ecma)script/i, + oldSelf = jQuery.fn.andSelf || jQuery.fn.addBack, + oldFragment = jQuery.buildFragment; + +jQuery.fn.andSelf = function() { + migrateWarn("jQuery.fn.andSelf() replaced by jQuery.fn.addBack()"); + return oldSelf.apply( this, arguments ); +}; + +// Since jQuery.clean is used internally on older versions, we only shim if it's missing +if ( !jQuery.clean ) { + jQuery.clean = function( elems, context, fragment, scripts ) { + // Set context per 1.8 logic + context = context || document; + context = !context.nodeType && context[0] || context; + context = context.ownerDocument || context; + + migrateWarn("jQuery.clean() is deprecated"); + + var i, elem, handleScript, jsTags, + ret = []; + + jQuery.merge( ret, jQuery.buildFragment( elems, context ).childNodes ); + + // Complex logic lifted directly from jQuery 1.8 + if ( fragment ) { + // Special handling of each script element + handleScript = function( elem ) { + // Check if we consider it executable + if ( !elem.type || rscriptType.test( elem.type ) ) { + // Detach the script and store it in the scripts array (if provided) or the fragment + // Return truthy to indicate that it has been handled + return scripts ? + scripts.push( elem.parentNode ? elem.parentNode.removeChild( elem ) : elem ) : + fragment.appendChild( elem ); + } + }; + + for ( i = 0; (elem = ret[i]) != null; i++ ) { + // Check if we're done after handling an executable script + if ( !( jQuery.nodeName( elem, "script" ) && handleScript( elem ) ) ) { + // Append to fragment and handle embedded scripts + fragment.appendChild( elem ); + if ( typeof elem.getElementsByTagName !== "undefined" ) { + // handleScript alters the DOM, so use jQuery.merge to ensure snapshot iteration + jsTags = jQuery.grep( jQuery.merge( [], elem.getElementsByTagName("script") ), handleScript ); + + // Splice the scripts into ret after their former ancestor and advance our index beyond them + ret.splice.apply( ret, [i + 1, 0].concat( jsTags ) ); + i += jsTags.length; + } + } + } + } + + return ret; + }; +} + +jQuery.buildFragment = function( elems, context, scripts, selection ) { + var ret, + warning = "jQuery.buildFragment() is deprecated"; + + // Set context per 1.8 logic + context = context || document; + context = !context.nodeType && context[0] || context; + context = context.ownerDocument || context; + + try { + ret = oldFragment.call( jQuery, elems, context, scripts, selection ); + + // jQuery < 1.8 required arrayish context; jQuery 1.9 fails on it + } catch( x ) { + ret = oldFragment.call( jQuery, elems, context.nodeType ? [ context ] : context[ 0 ], scripts, selection ); + + // Success from tweaking context means buildFragment was called by the user + migrateWarn( warning ); + } + + // jQuery < 1.9 returned an object instead of the fragment itself + if ( !ret.fragment ) { + migrateWarnProp( ret, "fragment", ret, warning ); + migrateWarnProp( ret, "cacheable", false, warning ); + } + + return ret; +}; + +var eventAdd = jQuery.event.add, + eventRemove = jQuery.event.remove, + eventTrigger = jQuery.event.trigger, + oldToggle = jQuery.fn.toggle, + oldLive = jQuery.fn.live, + oldDie = jQuery.fn.die, + ajaxEvents = "ajaxStart|ajaxStop|ajaxSend|ajaxComplete|ajaxError|ajaxSuccess", + rajaxEvent = new RegExp( "\\b(?:" + ajaxEvents + ")\\b" ), + rhoverHack = /(?:^|\s)hover(\.\S+|)\b/, + hoverHack = function( events ) { + if ( typeof( events ) != "string" || jQuery.event.special.hover ) { + return events; + } + if ( rhoverHack.test( events ) ) { + migrateWarn("'hover' pseudo-event is deprecated, use 'mouseenter mouseleave'"); + } + return events && events.replace( rhoverHack, "mouseenter$1 mouseleave$1" ); + }; + +// Event props removed in 1.9, put them back if needed; no practical way to warn them +if ( jQuery.event.props && jQuery.event.props[ 0 ] !== "attrChange" ) { + jQuery.event.props.unshift( "attrChange", "attrName", "relatedNode", "srcElement" ); +} + +// Undocumented jQuery.event.handle was "deprecated" in jQuery 1.7 +migrateWarnProp( jQuery.event, "handle", jQuery.event.dispatch, "jQuery.event.handle is undocumented and deprecated" ); + +// Support for 'hover' pseudo-event and ajax event warnings +jQuery.event.add = function( elem, types, handler, data, selector ){ + if ( elem !== document && rajaxEvent.test( types ) ) { + migrateWarn( "AJAX events should be attached to document: " + types ); + } + eventAdd.call( this, elem, hoverHack( types || "" ), handler, data, selector ); +}; +jQuery.event.remove = function( elem, types, handler, selector, mappedTypes ){ + eventRemove.call( this, elem, hoverHack( types ) || "", handler, selector, mappedTypes ); +}; + +jQuery.fn.error = function() { + var args = Array.prototype.slice.call( arguments, 0); + migrateWarn("jQuery.fn.error() is deprecated"); + args.splice( 0, 0, "error" ); + if ( arguments.length ) { + return this.bind.apply( this, args ); + } + // error event should not bubble to window, although it does pre-1.7 + this.triggerHandler.apply( this, args ); + return this; +}; + +jQuery.fn.toggle = function( fn, fn2 ) { + + // Don't mess with animation or css toggles + if ( !jQuery.isFunction( fn ) || !jQuery.isFunction( fn2 ) ) { + return oldToggle.apply( this, arguments ); + } + migrateWarn("jQuery.fn.toggle(handler, handler...) is deprecated"); + + // Save reference to arguments for access in closure + var args = arguments, + guid = fn.guid || jQuery.guid++, + i = 0, + toggler = function( event ) { + // Figure out which function to execute + var lastToggle = ( jQuery._data( this, "lastToggle" + fn.guid ) || 0 ) % i; + jQuery._data( this, "lastToggle" + fn.guid, lastToggle + 1 ); + + // Make sure that clicks stop + event.preventDefault(); + + // and execute the function + return args[ lastToggle ].apply( this, arguments ) || false; + }; + + // link all the functions, so any of them can unbind this click handler + toggler.guid = guid; + while ( i < args.length ) { + args[ i++ ].guid = guid; + } + + return this.click( toggler ); +}; + +jQuery.fn.live = function( types, data, fn ) { + migrateWarn("jQuery.fn.live() is deprecated"); + if ( oldLive ) { + return oldLive.apply( this, arguments ); + } + jQuery( this.context ).on( types, this.selector, data, fn ); + return this; +}; + +jQuery.fn.die = function( types, fn ) { + migrateWarn("jQuery.fn.die() is deprecated"); + if ( oldDie ) { + return oldDie.apply( this, arguments ); + } + jQuery( this.context ).off( types, this.selector || "**", fn ); + return this; +}; + +// Turn global events into document-triggered events +jQuery.event.trigger = function( event, data, elem, onlyHandlers ){ + if ( !elem & !rajaxEvent.test( event ) ) { + migrateWarn( "Global events are undocumented and deprecated" ); + } + return eventTrigger.call( this, event, data, elem || document, onlyHandlers ); +}; +jQuery.each( ajaxEvents.split("|"), + function( _, name ) { + jQuery.event.special[ name ] = { + setup: function() { + var elem = this; + + // The document needs no shimming; must be !== for oldIE + if ( elem !== document ) { + jQuery.event.add( document, name + "." + jQuery.guid, function() { + jQuery.event.trigger( name, null, elem, true ); + }); + jQuery._data( this, name, jQuery.guid++ ); + } + return false; + }, + teardown: function() { + if ( this !== document ) { + jQuery.event.remove( document, name + "." + jQuery._data( this, name ) ); + } + return false; + } + }; + } +); + + +})( jQuery, window ); From ebb85f3feb510f7ac8ccbde156940461b878633e Mon Sep 17 00:00:00 2001 From: coderaiser Date: Tue, 22 Jan 2013 09:41:49 -0500 Subject: [PATCH 117/347] minor changes --- lib/util.js | 38 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 38 insertions(+) diff --git a/lib/util.js b/lib/util.js index 03db79c9..4ee0ed25 100644 --- a/lib/util.js +++ b/lib/util.js @@ -491,6 +491,18 @@ Util = exports || {}; return lRet; }; + /** + * exec function if it exist in object + * @pArg + */ + Util.execIfExist = function(pObj, pName, pArg){ + var lRet; + if(pObj) + lRet = Util.exec(pObj[pName], pArg); + + return lRet; + }; + /** * function do conditional save exec of function * @param pCondition @@ -525,6 +537,32 @@ Util = exports || {}; return lRet; }; + + + /** + * start timer + * @pArg + */ + Util.time = function(pArg){ + var lRet, + lConsole = Scope.console; + + lRet = Util.execIfExist(lConsole, 'time', pArg); + + return lRet; + }; + /** + * stop timer + * @pArg + */ + Util.timeEnd = function(pArg){ + var lRet, + lConsole = Scope.console; + + lRet = Util.execIfExist(lConsole, 'timeEnd', pArg); + + return this; + }; /** * Gets current date in format yy.mm.dd hh:mm:ss */ From cbce8a48166b6420e2b4c8119c69f1647460c46b Mon Sep 17 00:00:00 2001 From: coderaiser Date: Wed, 23 Jan 2013 03:21:00 -0500 Subject: [PATCH 118/347] removed getting data from Minify cache --- ChangeLog | 2 ++ lib/client.js | 13 +------------ lib/client/dom.js | 3 ++- lib/server.js | 27 +++------------------------ lib/util.js | 2 +- 5 files changed, 9 insertions(+), 38 deletions(-) diff --git a/ChangeLog b/ChangeLog index 4c4b04b4..0736f133 100644 --- a/ChangeLog +++ b/ChangeLog @@ -97,6 +97,8 @@ clicked on menu item. * Added migrate plugin for jquery. +* Removed getting data from Minify cache. + 2012.12.12, Version 0.1.8 diff --git a/lib/client.js b/lib/client.js index 1f5887ae..d856dcf8 100644 --- a/lib/client.js +++ b/lib/client.js @@ -114,27 +114,16 @@ CloudCmd._editFileName = function(pParent){ lA.contentEditable = true; KeyBinding.unSet(); - var lDocumentOnclick = document.onclick; - /* setting event handler onclick * if user clicks somewhere keyBinded * backs */ - document.onclick = (function(){ + DOM.addOneTimeListener('click', function(){ var lA = DOM.getCurrentLink(pParent); if (lA && lA.textContent !== '..') lA.contentEditable = false; KeyBinding.set(); - - /* backs old document.onclick - * and call it if it was - * setted up earlier - */ - document.onclick = lDocumentOnclick; - - Util.exec(lDocumentOnclick); - }); } }; diff --git a/lib/client/dom.js b/lib/client/dom.js index 5a5df352..f1559716 100644 --- a/lib/client/dom.js +++ b/lib/client/dom.js @@ -649,7 +649,8 @@ var CloudCommander, Util, * maybe we offline, load font from local * directory */ - DOM.cssSet({id:'local-droids-font', + DOM.cssSet({ + id :'local-droids-font', element : document.head, inner : '@font-face {font-family: "Droid Sans Mono";' + 'font-style: normal;font-weight: normal;' + diff --git a/lib/server.js b/lib/server.js index 8406e2e4..8aaa57ee 100644 --- a/lib/server.js +++ b/lib/server.js @@ -265,7 +265,7 @@ Util.log('reading ' + lName); /* watching is file changed */ - if(lConfig.appcache) + if(lConfig.appcache) CloudServer.AppCache.watch(lName); /* сохраняем указатель на response и имя */ @@ -282,43 +282,22 @@ CloudServer.Gzip?(lName+'_gzip') : lName); Util.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){ - Util.log('trying to read data from Minify.Cache'); - - lFromCache_o.cache = false; - - if(lMinify.Cache) - lFileData = lMinify.Cache[Path.basename(lName)]; - } var lReadFileFunc_f = CloudServer.getReadFileFunc(lName), /* если там что-то есть передаём данные в функцию readFile */ lResult = lFileData; if(lResult){ - /* if file readed not from cache - - * he readed from minified cache - */ - lFromCache_o.minify = !lFromCache_o.cache; - Util.log(lName + ' readed from cache'); /* передаём данные с кэша, * если gzip включен - сжатые * в обратном случае - несжатые */ - lReadFileFunc_f(undefined, lFileData, lFromCache_o); + lReadFileFunc_f(undefined, lFileData, {'cache': true}); } /* if file not in one of caches * and minimizing setted then minimize it @@ -654,7 +633,7 @@ * @pData - данные * @pFromCache_o - прочитано с файла, * или из одного из кешей - * Пример {cache: false, minify: true} + * Пример {cache: false} */ var lReadFile = function(pError, pData, pFromCache_o){ var lSrv = CloudServer; diff --git a/lib/util.js b/lib/util.js index 4ee0ed25..e0bf8bf0 100644 --- a/lib/util.js +++ b/lib/util.js @@ -158,7 +158,7 @@ Util = exports || {}; * @param pSubStr */ Util.removeStr = function(pStr, pSubStr){ - return pStr.replace(pSubStr,''); + return pStr.replace(pSubStr, ''); }; /** From 49e67a64ac43ea0ac5d57afbb8cfdde085ea262c Mon Sep 17 00:00:00 2001 From: coderaiser Date: Wed, 23 Jan 2013 03:21:46 -0500 Subject: [PATCH 119/347] removed getting data from Minify cache --- lib/server/minify.js | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/lib/server/minify.js b/lib/server/minify.js index fcfce9f5..2485c1f2 100644 --- a/lib/server/minify.js +++ b/lib/server/minify.js @@ -52,9 +52,6 @@ if(!this.MinFolder) this.MinFolder = Minify.MinFolder; - if(!this.Cache) - this.Cache = Minify.Cache; - if(this._allowed.css || this._allowed.js || this._allowed.html){ Minify.optimize(pName, pParams); lResult = true; @@ -76,7 +73,6 @@ }, /* minification folder name */ - MinFolder : '', - Cache : null + MinFolder : '' }; })(); From de0038d042a6475d3fa6c234c185fe3e4ca2b139 Mon Sep 17 00:00:00 2001 From: coderaiser Date: Wed, 23 Jan 2013 07:25:36 -0500 Subject: [PATCH 120/347] moved jsons to json folder --- ChangeLog | 2 ++ config.json => json/config.json | 2 +- modules.json => json/modules.json | 0 lib/client.js | 6 +++--- lib/server.js | 12 +++++++++--- lib/server/ischanged.js | 2 +- lib/server/main.js | 4 ++-- 7 files changed, 18 insertions(+), 10 deletions(-) rename config.json => json/config.json (94%) rename modules.json => json/modules.json (100%) diff --git a/ChangeLog b/ChangeLog index 0736f133..cd638b6c 100644 --- a/ChangeLog +++ b/ChangeLog @@ -99,6 +99,8 @@ clicked on menu item. * Removed getting data from Minify cache. +* Moved jsons to json folder. + 2012.12.12, Version 0.1.8 diff --git a/config.json b/json/config.json similarity index 94% rename from config.json rename to json/config.json index 2daffa62..6e9dd506 100644 --- a/config.json +++ b/json/config.json @@ -3,7 +3,7 @@ "appcache" : false, "cache" : {"allowed" : false}, "minification" : { - "js" : false, + "js" : true, "css" : true, "html" : true, "img" : true diff --git a/modules.json b/json/modules.json similarity index 100% rename from modules.json rename to json/modules.json diff --git a/lib/client.js b/lib/client.js index d856dcf8..a0c20d5d 100644 --- a/lib/client.js +++ b/lib/client.js @@ -4,7 +4,7 @@ */ var Util, DOM, CloudFunc, $, KeyBinding, CloudCommander = (function(){ -"use strict"; +'use strict'; var Config, Modules; @@ -388,7 +388,7 @@ function baseInit(pCallBack){ CloudCmd.getConfig = function(pCallBack){ Util.ifExec(Config, pCallBack, function(pCallBack){ DOM.ajax({ - url:'/config.json', + url:'/json/config.json', success: function(pConfig){ Config = pConfig; Util.exec(pCallBack, pConfig); @@ -400,7 +400,7 @@ CloudCmd.getConfig = function(pCallBack){ CloudCmd.getModules = function(pCallBack){ Util.ifExec(Modules, pCallBack, function(pCallBack){ DOM.ajax({ - url:'/modules.json', + url:'/json/modules.json', success: Util.retExec(pCallBack) }); }); diff --git a/lib/server.js b/lib/server.js index 8aaa57ee..ea9d5ee2 100644 --- a/lib/server.js +++ b/lib/server.js @@ -68,8 +68,9 @@ LIBDIR = main.LIBDIR, SRVDIR = main.SRVDIR, - /* модуль для работы с путями*/ + /* модуль для работы с путями*/ Path = main.path, + crypto = main.crypto, Fs = main.fs, /* модуль для работы с файловой системой*/ Querystring = main.querystring, @@ -391,9 +392,14 @@ * меняем название html-файла и * загружаем сжатый html-файл в дальнейшем */ + + var lMinFileName = CloudServer.Minify.MinFolder + + crypto.createHash('sha1') + .update(main.DIR + 'html/index.html') + .digest('hex') + '.html'; + CloudServer.INDEX = (CloudServer.Minify._allowed.html ? - CloudServer.Minify.MinFolder + 'index.min.html' - : CloudServer.INDEX); + lMinFileName : CloudServer.INDEX); /* * сохраним указатель на response diff --git a/lib/server/ischanged.js b/lib/server/ischanged.js index 8fb17504..883c54b9 100644 --- a/lib/server/ischanged.js +++ b/lib/server/ischanged.js @@ -71,7 +71,7 @@ }); } - isFileChanged(DIR + 'favicon.ico', true, function(pData){ + isFileChanged(DIR + 'manifest.yml', true, function(pData){ console.log(pData) }); })(); \ No newline at end of file diff --git a/lib/server/main.js b/lib/server/main.js index 381b9aaa..2418c2a8 100644 --- a/lib/server/main.js +++ b/lib/server/main.js @@ -1,5 +1,5 @@ (function(){ - "strict mode"; + 'strict mode'; /* Global var accessible from any loaded module */ global.cloudcmd = {}; @@ -68,7 +68,7 @@ exports.zlib = zlib = mrequire('zlib'), /* Main Information */ - exports.config = rootrequire('config'); + exports.config = rootrequire('json/config'); exports.mainpackage = rootrequire('package'); From ad228bad9d9d0cf05a7b78aa706c478d629bdd5d Mon Sep 17 00:00:00 2001 From: coderaiser Date: Wed, 23 Jan 2013 07:53:40 -0500 Subject: [PATCH 121/347] refactored --- lib/client.js | 66 +++++++++++++++++++---------------------- lib/client/dom.js | 9 ++++++ lib/server/ischanged.js | 16 +++++----- 3 files changed, 48 insertions(+), 43 deletions(-) diff --git a/lib/client.js b/lib/client.js index a0c20d5d..c8d94990 100644 --- a/lib/client.js +++ b/lib/client.js @@ -289,22 +289,22 @@ function initKeysPanel(pCallBack){ var lKeysPanel = {}, lFuncs =[ - null, - null, /* f1 */ - null, /* f2 */ - CloudCmd.Viewer, /* f3 */ - CloudCmd.Editor, /* f4 */ - null, /* f5 */ - null, /* f6 */ - null, /* f7 */ - DOM.promptRemoveCurrent,/* f8 */ - ]; + null, + null, /* f1 */ + null, /* f2 */ + CloudCmd.Viewer, /* f3 */ + CloudCmd.Editor, /* f4 */ + null, /* f5 */ + null, /* f6 */ + null, /* f7 */ + DOM.promptRemoveCurrent,/* f8 */ + ]; for(var i = 1; i <= 8; i++){ var lButton = 'f' + i, lEl = DOM.getById('f' + i); - lEl.onclick = lFuncs[i]; + DOM.addClickListener(lFuncs[i], lEl); lKeysPanel[lButton] = lEl; } @@ -411,8 +411,8 @@ CloudCmd.getModules = function(pCallBack){ CloudCmd._changeLinks = function(pPanelID){ /* назначаем кнопку очистить кэш и показываем её */ var lClearcache = DOM.getById('clear-cache'); - lClearcache.onclick = DOM.Cache.clear; - + DOM.addListener('click', DOM.Cache.clear, lClearcache); + /* меняем ссылки на ajax-запросы */ var lPanel = DOM.getById(pPanelID), a = lPanel.getElementsByTagName('a'), @@ -491,45 +491,41 @@ CloudCmd._changeLinks = function(pPanelID){ for(var i = 0, n = a.length; i < n ; i++){ /* убираем адрес хоста*/ - var link = a[i].href.replace(lUrl,''); + var ai = a[i], + link = ai.href.replace(lUrl, ''), + lNEADREFRESH = i === lREFRESHICON, + lLoadDir = CloudCmd._loadDir(link, lNEADREFRESH); /* ставим загрузку гифа на клик*/ - if(i === lREFRESHICON){ - a[i].onclick = CloudCmd._loadDir(link, true); - - a[i].parentElement.onclick = a[i].onclick; + if(lNEADREFRESH){ + DOM.addClickListener( lLoadDir, ai ); + DOM.addClickListener( lLoadDir, ai.parentElement ); } /* устанавливаем обработчики на строку * на двойное нажатие на левую кнопку мышки */ else{ - var lLi = a[i].parentElement.parentElement; + var lLi = ai.parentElement.parentElement; /* if we in path changing onclick events */ - if (lLi.className === 'path') { - a[i].onclick = CloudCmd._loadDir(link); - } + if (lLi.className === 'path') + DOM.addClickListener( lLoadDir, ai ); else { - lLi.onclick = Util.retFalse; - lLi.onmousedown = lSetCurrentFile_f; - a[i].ondragstart = lOnDragStart_f; - + DOM.addClickListener( Util.retFalse, lLi); + DOM.addListener('mousedown', lSetCurrentFile_f, lLi); + DOM.addListener('ondragstart', lOnDragStart_f, ai); /* if right button clicked menu will * loads and shows */ - lLi.oncontextmenu = lOnContextMenu_f; + DOM.addListener('contextmenu', lOnContextMenu_f, lLi); /* если ссылка на папку, а не файл */ - if(a[i].target !== '_blank'){ - lLi.ondblclick = CloudCmd._loadDir(link); - - DOM.addListener('touchend', - CloudCmd._loadDir(link), - lLi - ); + if(ai.target !== '_blank'){ + DOM.addListener('dblclick', lLoadDir, lLi); + DOM.addListener('touchend', lLoadDir, lLi); } - lLi.id = (a[i].title ? a[i].title : a[i].textContent) + + lLi.id = (ai.title ? ai.title : ai.textContent) + '(' + pPanelID + ')'; } } diff --git a/lib/client/dom.js b/lib/client/dom.js index f1559716..d445a96c 100644 --- a/lib/client/dom.js +++ b/lib/client/dom.js @@ -153,6 +153,15 @@ var CloudCommander, Util, return DOM.addListener('keydown', pListener, pElement, pUseCapture); }; + /** + * safe add event click listener + * @param pListener + * @param pUseCapture + */ + DOM.addClickListener = function(pListener, pElement, pUseCapture){ + return DOM.addListener('click', pListener, pElement, pUseCapture); + }; + /** * load file countent thrue ajax */ diff --git a/lib/server/ischanged.js b/lib/server/ischanged.js index 883c54b9..1b752838 100644 --- a/lib/server/ischanged.js +++ b/lib/server/ischanged.js @@ -9,13 +9,13 @@ Util = main.util, /* object contains hashes of files*/ - CHANGESNAME = DIR + 'changes', + CHANGESNAME = DIR + 'json/changes', CHANGES_JSON = CHANGESNAME + '.json', Times = main.require(CHANGESNAME) || [], - TimesChanged; + GTimesChanged; - function isFileChanged(pFileName, pLastFile_b, pCallBack){ + exports.isFileChanged = function(pFileName, pLastFile_b, pCallBack){ var lReadedTime; var i, n = Times.length; @@ -42,20 +42,20 @@ }; lTimeChanged = - TimesChanged = true; + GTimesChanged = true; } - if(pLastFile_b && TimesChanged) + if(pLastFile_b && GTimesChanged) writeFile(CHANGES_JSON, Util.stringifyJSON(Times)); } else{ Util.log(pError); - lTimeChanged = false; + lTimeChanged = true; } Util.exec(pCallBack, lTimeChanged); }); - } + }; /* * Функция записывает файла @@ -72,6 +72,6 @@ } isFileChanged(DIR + 'manifest.yml', true, function(pData){ - console.log(pData) + console.log(pData); }); })(); \ No newline at end of file From a8c727a31f6e90040a088f7bc1219ee61ed8c0ec Mon Sep 17 00:00:00 2001 From: coderaiser Date: Wed, 23 Jan 2013 07:56:55 -0500 Subject: [PATCH 122/347] refactored --- lib/client/menu.js | 13 +++---------- 1 file changed, 3 insertions(+), 10 deletions(-) diff --git a/lib/client/menu.js b/lib/client/menu.js index c891e824..72ec76f4 100644 --- a/lib/client/menu.js +++ b/lib/client/menu.js @@ -211,8 +211,6 @@ var CloudCommander, Util, DOM, $; function set(){ if(!MenuSeted){ $.contextMenu(getConfig()); - - var lFunc_f = document.onclick; /* * Menu works in some crazy way so need a * little hack to get every thing work out. @@ -225,7 +223,7 @@ var CloudCommander, Util, DOM, $; * is not going on. All magic happening in * DOM tree */ - document.onclick = function(pEvent){ + DOM.addClickListener(function(pEvent){ /* if clicked on menu item */ var lClassName = pEvent.target.parentElement.className; switch(lClassName){ @@ -235,7 +233,7 @@ var CloudCommander, Util, DOM, $; return; } - if(pEvent && pEvent.x && pEvent.y){ + if(pEvent && pEvent.x){ var lLayer = DOM.getById('context-menu-layer'); if(lLayer){ var lStyle; @@ -266,15 +264,10 @@ var CloudCommander, Util, DOM, $; if(lLayer && lStyle) lLayer.style.cssText = lStyle; - /* if document.onclick was set up - * before us, it's best time to call it - */ - Util.exec(lFunc_f); - KeyBinding.set(); } } - }; + }); MenuSeted = true; } From 6b0eab1bba030d67c7f76240d56cb9f5a0d6046f Mon Sep 17 00:00:00 2001 From: coderaiser Date: Wed, 23 Jan 2013 09:42:33 -0500 Subject: [PATCH 123/347] fixed bug with working links in path panel and with clicks on files --- ChangeLog | 3 +++ json/config.json | 2 +- lib/client.js | 14 ++++++++------ lib/client/dom.js | 9 +++++++++ 4 files changed, 21 insertions(+), 7 deletions(-) diff --git a/ChangeLog b/ChangeLog index cd638b6c..2efb3fb3 100644 --- a/ChangeLog +++ b/ChangeLog @@ -101,6 +101,9 @@ clicked on menu item. * Moved jsons to json folder. +* Fixed bug with working links in path panel and +with clicks on files. + 2012.12.12, Version 0.1.8 diff --git a/json/config.json b/json/config.json index 6e9dd506..2daffa62 100644 --- a/json/config.json +++ b/json/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.js b/lib/client.js index c8d94990..c0ada80e 100644 --- a/lib/client.js +++ b/lib/client.js @@ -79,11 +79,9 @@ CloudCmd.GoogleAnalytics = function(){ * @param pNeedRefresh - необходимость обязательной загрузки данных с сервера */ CloudCmd._loadDir = function(pLink, pNeedRefresh){ - return function(){ - /* - * показываем гиф загрузки возле пути папки сверху - * ctrl+r нажата? - */ + return function(pEvent){ + /* показываем гиф загрузки возле пути папки сверху + * ctrl+r нажата? */ DOM.Images.showLoad(pNeedRefresh ? {top:true} : null); @@ -96,6 +94,8 @@ CloudCmd._loadDir = function(pLink, pNeedRefresh){ /* если нажали на ссылку на верхний каталог*/ if(lParent === '..' && lDir !== '/') CloudCmd._currentToParent(lDir); + + pEvent.preventDefault(); }; }; @@ -511,7 +511,9 @@ CloudCmd._changeLinks = function(pPanelID){ if (lLi.className === 'path') DOM.addClickListener( lLoadDir, ai ); else { - DOM.addClickListener( Util.retFalse, lLi); + DOM.addClickListener( function(pEvent){ + pEvent.preventDefault(); + }, ai); DOM.addListener('mousedown', lSetCurrentFile_f, lLi); DOM.addListener('ondragstart', lOnDragStart_f, ai); /* if right button clicked menu will diff --git a/lib/client/dom.js b/lib/client/dom.js index d445a96c..e4be7f9c 100644 --- a/lib/client/dom.js +++ b/lib/client/dom.js @@ -1064,6 +1064,15 @@ var CloudCommander, Util, return lPanel; }; + /** prevent default event */ + DOM.preventDefault = function(pEvent){ + var lRet; + + lRet = Util.exec(pEvent.preventDefault); + + return lRet; + }; + DOM.show = function(pElement){ DOM.removeClass(pElement, 'hidden'); }; From a18ac6fa29539d84cc923584768acabf0f9b720f Mon Sep 17 00:00:00 2001 From: coderaiser Date: Wed, 23 Jan 2013 10:08:19 -0500 Subject: [PATCH 124/347] refactored --- lib/client.js | 22 +++++++++++----------- lib/client/dom.js | 5 +++-- 2 files changed, 14 insertions(+), 13 deletions(-) diff --git a/lib/client.js b/lib/client.js index c0ada80e..0b574e82 100644 --- a/lib/client.js +++ b/lib/client.js @@ -85,11 +85,14 @@ CloudCmd._loadDir = function(pLink, pNeedRefresh){ DOM.Images.showLoad(pNeedRefresh ? {top:true} : null); - var lParent = DOM.getCurrentLink().textContent, - lDir = DOM.getCurrentDir(); + var lCurrentLink = DOM.getCurrentLink(), + lHref = lCurrentLink.href, + lParent = lCurrentLink.textContent, + lLink = pLink || Util.removeStr(lHref, CloudCmd.HOST), + lDir = DOM.getCurrentDir(); /* загружаем содержимое каталога */ - CloudCmd._ajaxLoad(pLink, { refresh: pNeedRefresh }); + CloudCmd._ajaxLoad(lLink, { refresh: pNeedRefresh }); /* если нажали на ссылку на верхний каталог*/ if(lParent === '..' && lDir !== '/') @@ -489,6 +492,7 @@ CloudCmd._changeLinks = function(pPanelID){ lUrl = CloudCmd.HOST; + var lLoadDirOnce = CloudCmd._loadDir(); for(var i = 0, n = a.length; i < n ; i++){ /* убираем адрес хоста*/ var ai = a[i], @@ -497,10 +501,8 @@ CloudCmd._changeLinks = function(pPanelID){ lLoadDir = CloudCmd._loadDir(link, lNEADREFRESH); /* ставим загрузку гифа на клик*/ - if(lNEADREFRESH){ - DOM.addClickListener( lLoadDir, ai ); + if(lNEADREFRESH) DOM.addClickListener( lLoadDir, ai.parentElement ); - } /* устанавливаем обработчики на строку * на двойное нажатие на левую кнопку мышки */ @@ -511,9 +513,7 @@ CloudCmd._changeLinks = function(pPanelID){ if (lLi.className === 'path') DOM.addClickListener( lLoadDir, ai ); else { - DOM.addClickListener( function(pEvent){ - pEvent.preventDefault(); - }, ai); + DOM.addClickListener( DOM.preventDefault, lLi); DOM.addListener('mousedown', lSetCurrentFile_f, lLi); DOM.addListener('ondragstart', lOnDragStart_f, ai); /* if right button clicked menu will @@ -523,8 +523,8 @@ CloudCmd._changeLinks = function(pPanelID){ /* если ссылка на папку, а не файл */ if(ai.target !== '_blank'){ - DOM.addListener('dblclick', lLoadDir, lLi); - DOM.addListener('touchend', lLoadDir, lLi); + DOM.addListener('dblclick', lLoadDirOnce, lLi); + DOM.addListener('touchend', lLoadDirOnce, lLi); } lLi.id = (ai.title ? ai.title : ai.textContent) + diff --git a/lib/client/dom.js b/lib/client/dom.js index e4be7f9c..e74233b3 100644 --- a/lib/client/dom.js +++ b/lib/client/dom.js @@ -1066,9 +1066,10 @@ var CloudCommander, Util, /** prevent default event */ DOM.preventDefault = function(pEvent){ - var lRet; + var lRet, + lFunc = Util.bind(pEvent.preventDefault, pEvent); - lRet = Util.exec(pEvent.preventDefault); + lRet = Util.exec(lFunc); return lRet; }; From ba1c37b520900b99fc4e8227d2bd9781240a217f Mon Sep 17 00:00:00 2001 From: coderaiser Date: Thu, 24 Jan 2013 06:15:07 -0500 Subject: [PATCH 125/347] refactored --- css/style.css | 13 +++++++++-- json/config.json | 2 +- lib/client.js | 2 +- lib/client/dom.js | 50 ++++++++++++++++++++++++++++++++++++---- lib/client/keyBinding.js | 46 ++++++++++++++++++------------------ 5 files changed, 81 insertions(+), 32 deletions(-) diff --git a/css/style.css b/css/style.css index 98cd2071..f2071518 100644 --- a/css/style.css +++ b/css/style.css @@ -13,6 +13,13 @@ } */ /* символьный шрифт от гитхаба*/ +@font-face { + font-family: "GeneralFoundicons"; + src: url('http://github.com/zurb/foundation-icons/blob/master/foundation_icons_general/fonts/general_foundicons.woff?raw=true') format('woff'); + font-weight: normal; + font-style: normal; +} + @font-face { font-family: 'Fontello'; @@ -71,7 +78,8 @@ body{ width:16px; height:16px; margin-left:0.5%; - font-family: 'Octicons Regular'; + /* font-family: 'Octicons Regular'; */ + font-family: 'GeneralFoundicons'; font-size:16px; } .error::before{ @@ -79,7 +87,8 @@ body{ bottom : 4px; color:rgb(222, 41, 41); cursor:default; - content:'\f026'; + /* content:'\f026'; */ + /* content:'\f013'; */ } .loading{ position:relative; diff --git a/json/config.json b/json/config.json index 2daffa62..03f32d5e 100644 --- a/json/config.json +++ b/json/config.json @@ -4,7 +4,7 @@ "cache" : {"allowed" : false}, "minification" : { "js" : false, - "css" : true, + "css" : false, "html" : true, "img" : true }, diff --git a/lib/client.js b/lib/client.js index 0b574e82..db176008 100644 --- a/lib/client.js +++ b/lib/client.js @@ -98,7 +98,7 @@ CloudCmd._loadDir = function(pLink, pNeedRefresh){ if(lParent === '..' && lDir !== '/') CloudCmd._currentToParent(lDir); - pEvent.preventDefault(); + DOM.preventDefault(); }; }; diff --git a/lib/client/dom.js b/lib/client/dom.js index e74233b3..076bc71a 100644 --- a/lib/client/dom.js +++ b/lib/client/dom.js @@ -9,7 +9,8 @@ var CloudCommander, Util, /* private members */ var /* название css-класа текущего файла*/ - CURRENT_FILE = 'current-file', + CURRENT_FILE = 'current-file', + Listeners = [], XMLHTTP, Title, @@ -44,6 +45,19 @@ var CloudCommander, Util, Images = new Images(); + function removeListenerFromList(pElement){ + var lRet; + + for(var i = 0, n = Listeners.length; i < n; i++){ + if(Listeners[i].element === pElement){ + Listeners[i] = null; + break; + } + } + + return lRet; + } + /** * private function thet unset currentfile */ @@ -93,14 +107,21 @@ var CloudCommander, Util, * @param pElement {document by default} */ DOM.addListener = function(pType, pListener, pElement, pUseCapture){ - var lRet = this; + var lRet = this, + lElement = (pElement || window); - (pElement || window).addEventListener( + + lElement.addEventListener( pType, pListener, pUseCapture || false ); + Listeners.push({ + element : lElement, + callback: pListener + }); + return lRet; }; @@ -158,10 +179,28 @@ var CloudCommander, Util, * @param pListener * @param pUseCapture */ - DOM.addClickListener = function(pListener, pElement, pUseCapture){ + DOM.addClickListener = function(pListener, pElement, pUseCapture){ return DOM.addListener('click', pListener, pElement, pUseCapture); }; + /** + * getListener for element + * + * @param pElement + */ + DOM.getListener = function(pElement){ + var lRet; + + for(var i = 0, n = Listeners.length; i < n; i++){ + if(Listeners[i].element === pElement){ + lRet = Listeners[i].callback; + break; + } + } + + return lRet; + }; + /** * load file countent thrue ajax */ @@ -1067,7 +1106,8 @@ var CloudCommander, Util, /** prevent default event */ DOM.preventDefault = function(pEvent){ var lRet, - lFunc = Util.bind(pEvent.preventDefault, pEvent); + lPreventDefault = pEvent && pEvent.preventDefault, + lFunc = Util.bind(lPreventDefault, pEvent); lRet = Util.exec(lFunc); diff --git a/lib/client/keyBinding.js b/lib/client/keyBinding.js index 050820b4..9bf1daff 100644 --- a/lib/client/keyBinding.js +++ b/lib/client/keyBinding.js @@ -1,15 +1,14 @@ var CloudCommander, Util, DOM; -(function(CloudCommander, Util, DOM){ +(function(CloudCmd, Util, DOM){ "use strict"; DOM.Images.hideLoad(); - var cloudcmd = CloudCommander, /* private property set or set key binding */ - keyBinded; + var keyBinded; /* Key constants*/ - cloudcmd.KEY = { + CloudCmd.KEY = { TAB : 9, ENTER : 13, ESC : 27, @@ -41,11 +40,11 @@ var CloudCommander, Util, DOM; TRA : 192 /* Typewritten Reverse Apostrophe (`) */ }; - var KEY = cloudcmd.KEY; + var KEY = CloudCmd.KEY; - cloudcmd.KeyBinding = {}; + CloudCmd.KeyBinding = {}; - var KeyBinding = cloudcmd.KeyBinding; + var KeyBinding = CloudCmd.KeyBinding; KeyBinding.get = function(){return keyBinded;}; @@ -75,14 +74,14 @@ var CloudCommander, Util, DOM; DOM.Images.showLoad({top: true}); - Util.exec(cloudcmd.Config); + Util.exec(CloudCmd.Config); } else if(lKeyCode === KEY.G && event.altKey) - Util.exec(cloudcmd.GitHub); + Util.exec(CloudCmd.GitHub); else if(lKeyCode === KEY.D && event.altKey){ - Util.exec(cloudcmd.DropBox); + Util.exec(CloudCmd.DropBox); event.preventDefault(); } @@ -121,7 +120,7 @@ var CloudCommander, Util, DOM; /* if f3 or shift+f3 or alt+f3 pressed */ else if(lKeyCode === KEY.F3){ - var lEditor = cloudcmd[event.shiftKey ? + var lEditor = CloudCmd[event.shiftKey ? 'Viewer' : 'Editor']; Util.exec(lEditor, true); @@ -133,19 +132,19 @@ var CloudCommander, Util, DOM; else if(lKeyCode === KEY.F4) { DOM.Images.showLoad(); - Util.exec(cloudcmd.Editor); + Util.exec(CloudCmd.Editor); event.preventDefault();//запрет на дальнейшее действие } else if(lKeyCode === KEY.F10 && event.shiftKey){ - Util.exec(cloudcmd.Menu); + Util.exec(CloudCmd.Menu); event.preventDefault();//запрет на дальнейшее действие } else if (lKeyCode === KEY.TRA){ DOM.Images.showLoad({top: true}); - Util.exec(cloudcmd.Terminal); + Util.exec(CloudCmd.Terminal); } /* навигация по таблице файлов*/ /* если нажали клавишу вверх*/ @@ -261,7 +260,7 @@ var CloudCommander, Util, DOM; /* если нажали Enter - открываем папку*/ else if(lKeyCode === KEY.ENTER) - Util.exec(lCurrentFile.ondblclick, true); + Util.exec(CloudCmd._loadDir()); /* если нажали +r * обновляем страницу, @@ -280,7 +279,7 @@ var CloudCommander, Util, DOM; * содержимого каталога */ var lRefreshIcon = DOM.getRefreshButton(); - if(lRefreshIcon){ + if(lRefreshIcon){ /* получаем название файла*/ var lSelectedName = DOM.getCurrentName(); @@ -289,9 +288,11 @@ var CloudCommander, Util, DOM; * ссылку, на которую повешен eventHandler * onclick */ - lRefreshIcon.onclick(); - cloudcmd._currentToParent(lSelectedName); - event.preventDefault();//запрет на дальнейшее действие + + Util.exec( DOM.getListener(lRefreshIcon) ); + CloudCmd._currentToParent(lSelectedName); + + DOM.preventDefault(); } } @@ -303,10 +304,9 @@ var CloudCommander, Util, DOM; 'press +q to remove all key-handlers'); var lClearCache = DOM.getById('clear-cache'); - if(lClearCache && lClearCache.onclick) - lClearCache.onclick(); - - event.preventDefault();//запрет на дальнейшее действие + Util.exec( DOM.getListener(lClearCache) ); + + DOM.preventDefault(); } /* если нажали +q From e75b5dab92da292343f2143a8b1acc63f7a3bd73 Mon Sep 17 00:00:00 2001 From: coderaiser Date: Thu, 24 Jan 2013 06:39:34 -0500 Subject: [PATCH 126/347] changed octicons font to foundicons --- ChangeLog | 2 ++ css/style.css | 8 ++------ font/fontello.woff | Bin 0 -> 3424 bytes font/general_foundicons.woff | Bin 0 -> 9728 bytes 4 files changed, 4 insertions(+), 6 deletions(-) create mode 100644 font/fontello.woff create mode 100644 font/general_foundicons.woff diff --git a/ChangeLog b/ChangeLog index 2efb3fb3..2bdc9440 100644 --- a/ChangeLog +++ b/ChangeLog @@ -104,6 +104,8 @@ clicked on menu item. * Fixed bug with working links in path panel and with clicks on files. +* Changed icons font from octicons to foundicons' + 2012.12.12, Version 0.1.8 diff --git a/css/style.css b/css/style.css index f2071518..76aa57a1 100644 --- a/css/style.css +++ b/css/style.css @@ -15,7 +15,7 @@ /* символьный шрифт от гитхаба*/ @font-face { font-family: "GeneralFoundicons"; - src: url('http://github.com/zurb/foundation-icons/blob/master/foundation_icons_general/fonts/general_foundicons.woff?raw=true') format('woff'); + src: url('/font/general_foundicons.woff') format('woff'); font-weight: normal; font-style: normal; } @@ -78,17 +78,13 @@ body{ width:16px; height:16px; margin-left:0.5%; - /* font-family: 'Octicons Regular'; */ font-family: 'GeneralFoundicons'; - font-size:16px; } .error::before{ position: relative; - bottom : 4px; color:rgb(222, 41, 41); cursor:default; - /* content:'\f026'; */ - /* content:'\f013'; */ + content:'\f013'; } .loading{ position:relative; diff --git a/font/fontello.woff b/font/fontello.woff new file mode 100644 index 0000000000000000000000000000000000000000..499d4eeaf81a16e08c8a079af51205262bd87984 GIT binary patch literal 3424 zcmY*c2{@Ep8-8cOSjLR9BoU#=ZyS3tw(JoqyBb?Y+1EirKKTq1$~t9D4Us)bW62&O zg(lgJrEDW>nE&G&8uQrw?j>gIo}VFrW;;uUwVIfZ9ut8-lPphz-EGcscok z+8W4-VBLx~6W(zN4ip9e=v`3b1c3%H0OppjyBDY>0s#Ce03Z&g(;gnWI|YD!S^fq3 zApQ%qyJzSvP*Vf|*geqq+QXcCwRkrtR{&tE0e!&ufbySpYAGHRL2Uu#LLkIgv|wR) zuRsC-upf;jb_8Az;I5~S3+Tsw6odN+Fm-sCmlFYu%dQ1-;Uhp0K)ttC9%C7F zcQ*5h8w7Cbh!Oxevbo}LlQ3siW;D(j2iM^gz6IGh0l*7@i{JqN*tc-|t<62n&0pGU za-yU2S#%V}-Q`?ep+roDyh3v{S~muoDENq$-8z{kv&GP?ZjEGrAWyFs-7+t1 z#h5yCx?@AL_xXst4LU`wV(?zbfE4=2CTnVu_`1~8suE27m9YJP=A;GH=Kwd*C;)y{ z0_n;1Z^6e6&`&GH?8mEc_F^2Y;wSmnej}>+#kv0KwXwXcoTY_<+3MezZW6$18TH4p*Apx>&M5SG_}P z@*YwY0k3Q+pb`^wx>TFKDym*=Z<$r6n0-P|02WC9gM-N$&gb)GS&o~>H&>qSP*Bz> zGG|D3s}@U(#CUO*pSh)_lsCWH{ZcYE-QOuQ|FMU?(*@%T(&fyKu7#UlVUmG`P+w-!-6CJ+tb#yYN*~DSu?=!_35r z#*n1!uE5{7hm85SdZAhZi5F6C)Gb&4FnrzU#Fr6?NGN@oJAyZ|XZB8~*@f%L%fS>b zm`}8wv%VYYBFI@Mr;Pk9i1oHUSgXeiqxH>$T*IG2U(Yv#)HC8|E{%B-llo&@Y~S=LOhMuNl7|OZpUK_A;AK8reQ(>J z3+5>l{ztjjz2E(k@_g#&)o9>h-#|cj9wkJQ`>AY z()j*uKGw^3C|*f5S)r#_O;zq>OfygKd4}#R(UV5XCh!}q4&I4>fK}8=L|Iu*{huog zmji{sH%*k{jOce(uXV<18h9x3<4Un?*l!sh<^WK^2MCYy9k4R4NdWr+)4jODxibQtO72y{N zn6*htL>y|ooSb$3bXs}K%(`)mQngV#JP@kQ1rVxLgi6s$0u9gJ*SWyeK)+aOeyJ z|Fs`#L|#xfrws(5Bk(XNpeyF{{uR4)zb zmX6#iHUN30u3`iYZJI0kXs^;r1{pb1dg=fD`Rmt<0XRwBqOesHK5y@VOe#Gf6WOS5#EH8D>1}{9RwU}?7rb{&S zk@O0~+uhfNZ9Z31ja7c6Yo_t!S!%+1{(|`{Q}?e$8a%<{jS(Bcw1|oAThYz1W@``B z3H-p?9iSOua~hGt8->Vt`+MzFRPlL-;>wz1K6OJk8ph46H4?EAJCC;XeZp&Gn#2t? z7=IxbD#xVRU+i~#p7kpF?RrKiLH`&+RVrE6CY4+drN7Tmk{JA}%H!o+c7EWJony6P zsY{S~%f}B+YjbD@^?m&xdVZ%9{_l*{#=kkXf@OR9X5XreCI=#v8&`5oq}t?T*k0Ji z$M`+CtE;Y{jlQTEsU}5m9&vs0B^~V^OM&ErDpUMJ@ z?Nv6m$=wxp?~#r%nbf;68}@l`jrpRbf%>S$}d+*X0TS%o)!%IJFZ zHz9QqcC)3L%MPoX<#7)<1>BzwhI9`lbu5a#UnISsv05$>IEna_rqpQCG{7~R4^^_+ z;&Z#qRJ`>>!pKw&A2F%LWp+nCQCOdMmYY>70r%QY@8xhwZMXIL8{ecaXWD9TT^C8- z-N_V7=_g-*Z5eJ#zm{W`o8=KMJ*4ev@+M(Os7E>9aZ2^f=&}C3ffm6jxP6Y^dgvOQ z!ST$zkE`(eRFA2_z}_fp-5OEC`ytIuM~9AAXWU0F0vz#98#kaIuy%*o(0e~*VKbz% zI7HMC6I168@fR7(*Vjxiv0jIf9rl#y$+w{jIVl4im&q=8OID72BS~6?hDYdAwO>61 zR!0tts$3KPVPOmGZkekkEX$)R#Q9uE5WP)$(D*Gmy;;ekh2?m#T*jv>qdU!A#p9Z_ zPXl6CmRi|1Ee^lNb^meh#!9w4h!lTVWs^kEjL*Jia`g`&-zI^(nW0>8N%R1IK2eh0 z-*S{qE_Rcs_q~5^Ry@|3Y~wHZ4##(vt|sN4b%uY&R$PHXArllXF310hq$fRLuhp3{ zjhjK$Oxy{4mn6(ZHD}saB@pXQ`7Rz43KA&X^^N5``6F#&5vO>iG)8-Es$rjHA4yE@ z>ib`=1WfnE^-fRkdQmW43QYh}jufxz=N#-0L(>?;zn4$Gy9;0CVe!~){)@h zY3JoOa{B4a#if0mTQWair=Y;-zTjyL*7c&{t{^swm@XSdly&Y`lR&o)FZZwXmAP+o zOaijzqZ*>FLQ;Y2OMWoH#_+MW$_w=R*z$(0`lPFyQ+_NzIz+NcYC9Jw*trTF)F&a8 zie#1KocgvGozlO*#CCekE)CCch9&V9R}k8GnK*_GEwl&RgSAd;yBJE@q>8w%*f6W{ z{xZRPP6mR+2X9n3BMP7Jhh!VB+ZpXOM_0A@kKA{#o zju%sYJ^iohsUG_1YS_*=b9rWCAt^~Jl4l|ddCme*Nd+`gI?K)Z6`6iQ6O zwMaZO;4m;AJ{Ku{7qW;AEkD`=&Oi*Kk3*kh6gY=gz{tsCn(bE6|BW&H*O%K$oRWB?R)&_ALVZ9dQw^fQVia du7HX9R>37$^eivcLm06Y@T5v6;1an0{|3GI{Qm#| literal 0 HcmV?d00001 diff --git a/font/general_foundicons.woff b/font/general_foundicons.woff new file mode 100644 index 0000000000000000000000000000000000000000..daab63141b07e0a1fa8efd931a98945b837bd4aa GIT binary patch literal 9728 zcmZvCWmFtZwCxP8fdq#D0}1Z#5@dn~cM0w?xVyW%y9Eis-3fyQAKcv~Sdhne?|c7V zb?sAqWLH<6>b3gEX*UHh7ytykHEI9=^S@Eh|3CTvYYHkX>;M3;_{}H&27_RTKdP~< zAp`(`(|yzJ-bTggtOa*tS7#6afbgG<_&>n)!&R6;%x&NNV{d%8H#i|_W%HQZc$mG# zxdQ-XZ!2IiU=#1uwlFm`dGoWr@i^bW{rIS}V)15tv%T2?q;L3)l7RrSuyuap0T14E z=QsT6f}kbZ*c-paQF_1WR{w#%oh;ba(EV+Fz~DEX>0ko zN!x<}&>n~Hf1CLK`dJNUO*V`GKrkz+2?rZ1eo#69GahjKzkai|ao&J{lz;$BEENKH zcyX*?vyM@Nfr}_09xEEi0gXN?l8Bg)Q22nY8{nwmFP%@!$`+snlmfB=I@d5A|9i&$ zAOP46N`F8MV#Ia={1n8!hIhclW`J`b!LCIxLI$M*jO;<`03#t#6OxgBIT=NH3j*#P z;1U7E48J7@Iz!xIrA4Z6LojjxafcnnAZ`VTGNLqoN22E;f$kamBDyks!ma}RB*G55 z-vZ#=@g@DYF9QQJfH5^U5*P_P9(d)?I3$E&6OG4ye_EfrjBi{hZ-ctG{J^A&c_d~f8qFBf7r3T$KOIvy8kD)QR@dnez@w824&ByolAGr z^E%o5z;v5F%rsFFL5ku(a_^35n?JYLfkw7o84z5m<2V=WU(*rNE0HdfZjn0GM?E1j6kvTH@l-^j8A-IR=7k2-(EDMi&Zl5?ZR#7^i~7Ka`RTW4N+$gw}J~OVROY zxLO{NKOb4|{7(*Ih0yv$vBBSsg(9?)rrS96XZENZ^0Cgp=>?uBcf9Mj7aJFl0QdsX z2Y3R?06lxA=!Ae z8TiA8qW6i6`fx;l`jOu2NZVix^=N}=+@IfjB*9SXer_Ra#>POzjJ6U?k{W+vI>Ouy z+@1#sGoHHy=WoxDyeI}9)Mpl*?B+0nPJ)ZgE6=KR?mW&sp7vKLlbw^8o>=ZbnUK;Y zN{Sst6_eKx>I&c#Czw9ut(7e*X$SXHDa;`~xkqlCPd2CMVA_90#*=i7z&5 z|Jy_6c>Y8uz};$jEkQmbFMF{TF`h+j>}pjk!ok~J%jMNld(P@-V&Lm%ZM8nJ$jq~t zNl5;iP2RXQ;4(se`ZRO3)xNUP&o^I)eVY2hSb_%eMdY4uTAVwPdW;kKS5~NOkZjm! zFaul~x&~eiuqAXu8+k#BbT1dzN|4TcwahBTs)Y2sszGPmsAJBicxw=RR84d z6U9ttSTtOZA9UX)V#}b3#yN?`=^FvLt{nfih>zGES??C^TZ=cS65U0XK#};7MlXi6 zRWA59@g8tWx6^aXNv|bm4h5sIoW+Fypt=s%(qkUs@G(Ejzc983Nkoubp)2eDAoVhp zARKNQM<*Yef10Q>_XzUUy?XZo@5?gMBctwan^3%|yw*_F;?$MDgw3F4Y3gm0+Ju(e zmx@d4_a%JyA7_%XxU>F;t)SdH!1K?7;~``|JO_6g1g25*1)umA1S{;;(9Rv17gS%7 zLmEEEOLrgvUvCRDqHDPd#ku2!6)PfPXAIAe#4U21EknbDp@(aCKh2qmej8yrZ%nkU z3z=*Y1r-Lq@+vLrj%hhr9lC(D{mK3n-1xrb`@8#p{=%Im*)}x4So`tEEDU2*OGW9` z;-UCbyF^AcdnX#gG0>-Xo74(?fsN zcJ))a0uTUg$RyfDXc~Bu1*Lt9)!B}J`)ERHAf6$GXEFbHEVIv)RiV7!L(@oe9gW0P z1e}tcmMksUvR?Nl2{jJRE2F2w2vtu7f6!J!aYLr)LU@?0wUzP=t;|_wH)gkW4Tm2_ z*}S^*W%l!EOT?7cEQiCv!g9>p$N_%38+nNJc`FLS3e9;`MFXII2_k;BEjAiuC znTdQPP~IJU)Hvo3E|v+fcqi))7%^57u# zH}y>)vaTW>8cMl|w!!mSu-tgGXWsy6JIQ2f!z6{!9=2tBoP|wp2FU8okF8+4w zjo64(>=Sirzwc3(QIk8)d9C-AtreNpUZAa11?0KxvU~o}2=8^~Cu0EYJM=bKguK-9 zw3#Y6oJowDPBwKq@*Vvcx1VkN$ECLh{lL>d%*&SoWvZ_4j?mBxi?H zNsVH8RkTHDyJIeDLK70=tdqZrGQnSBiRp2OpA&0boU;MXA*Dr3Pm~#3s4pIgNH2ig zbNH87snT$%Gs$hjqdi<*#Fj|quj0Zx_;RxGsV0k-2VcG9tu7i(LRYO218Y-3h>ZZM za(o#3(dDjF>b9oe_CT+qNm_T|^(17i_V~%E$z}7Sx&a@K{!63jtD8^!HO`7V7|Z72 zW5!P2HcgM?DRt{FlS~wLz;zhk!m;@Xb-Wz`*%vn|lGAz3MmU=-m9Ro;$Cf{ppXRPK zEjy-jH*II3)wdY(T#5OkfOJT@!3RyGQvUwpp8!`GtEg{z@FFcgS=4T0Zj$+H>owiD zgxHW|OjR#bxd$!#Nq?xhWa9axQ{pD{u^*og5ZLkq9c=jW9hUaN(Ng)5t;-svJ>f)e!)Tl%a%9jd)xw^O`Dk&?Ua*iyQ7c-KR#NlYZD^Ok_?3W8 zE3m3d>Q_9ucLd|K*fXQNKYgo)&S8T$TE}|uTP;dlpc-fy|alLpe-2Pzk zP1MK+D@=K^6{`lJdE&eRz&%Lgixb8HO7w4xoDvSk2%q95&33eL3B0T{hCdic0X~7R zGL6BLeWvb4h{ah)M}E^D<5lhk8Uzk*W>S_b zW@Xv1QcX0}=-as=`U2$`WlG#2{a$zeb+7}ZeLftYY@@)HRhXo|$X>TU%aybldkj;Y8WDnso8AJ*>5a#)l#@k&Zdy>PW1 zsc{Pi9)%Jn%rIB4+TZz{Z=}W9t^3}LBKP0Ca-Da5Mx7mau*ihN1ws!TX7KSD6-KF@ zvIm|2)U*+g@XBqg_f0#~h^72A%5~@s_{OiRs)QFbT%_dxK7TWxZ9-haMs!sBb83WS zVX4fuupF93zZx(w#o-A!L@I#jT&5gGqN_yNW=YvtAQ!Yo=q!PU$v<%`j%TJ-2|R#9 zLcj&e`)e|OaPe8-daks?1>{EsThq9x43y$b^#+iQJ7Tue91i=38{i`FVB~x?vTXJ! z|G8^+R!b2k_g>bHM(7D0)UEd+>{D#C%t^XCd_e6UwgZ=PvdG7XMUkJzIU2^d3a6UE zz7h(=H@yG!gxa2dkVY-uv`rMH*P|}vCy6sW|B;}M4-EZI^N|Px; zv<+J1ia#Mv#x;#VqGD16`kPIWCkkVuDP50Sb*;knGQ7yvD*J;&!Uc!kX-$>SJ+$rm zTw8KL9Z9p$SKC_BbDHFmN<`o!zfw+F1fM-kBuanU(@V9S`p%5>SN+rX?Xm~fig>0S z>Z>sG-c?R1y}+Cx1A<%8cMZ8fqFGM?kPxlQYV0H0{Z;-WcLl1Kh7;3o%n?tfavtQ1 zQw;YQCU%?OUPMqxv_cRPTz#Ob%E=J{6QJ?uKsOOICQs_r=>pItQnd^>xD8*dZAf{OsTJQluD z$qCItzuBV&RGxZucB+9dO&U`z^rT^K#d$tn8*b!S%9I<*#D zfmOp`KL#EMJX^Q)`pN-srswwS*Y0e-y=KJPl&FSmLn5{&{fEa)rOAuo9b={}HdHZ0`GMBQ@a;0P#FT^s`rE(>Fik`Fi@E>cqd&mg zG=U-5AdT%-QuP5+)ST5cU!_IRWnf8#se3M>*|hj{WwbiE>+SmCeAJ7@e@b!LUBDpg zSROOpzLs@9>(P21n1n& zUbrV&IF_^Xw$HjW3ki0bnGB zooSuIVL&#Y}NMh3wW)Y+m?qPfGYDG$;H(dW-My@v5 z+;w55qPM*%y35Basxd{?N5!;ouZ07ByfdqE>85l#v?_eSQK%R__4cD;h1``3!%wk!q^zRBE@Pn z`i#-YRM`v&HiGAQyHSRUW!~vH3=a)kML&1ix^j>7OsJGTKL)6LCBTo(<%e9%3ZbLl zW%Kj;?S#7i3;lF$2&MkH$A-FdUB70m`T8>mb{1q!4^pyUbSdo^FU=0A5dVha=A@L+y!vUMvJwmoWl1;M%+aJ*RU2oomqvY1`QCl-EN zt;;@vr0~8Z!{jwQ$nO+GLzj|YJ|B7902^|0+&uIfIBb`1biCtAvVJ0e$$Ejs3LPA` zN%Lgb%@$`BtQ!~i)NcDBK2zF%)^LX(+r-vu3^qk*PxPYm(?Mvtr62_Gd(qZpy>FAg z6U@=?Ylxcv%Y-~1;`Ay+X#9Fa7#i&o6FmeJQJ7L=OV#8?Y4)c77+Bg*!LP81ol`rN z*WM!h5pH4Mwba08Jr%a`TpYLFmxr{3_5P+W{ zZuWj{f~9Y)9{~u8;jzV7E}b**`8!sRsmPxxYT@r@VtSo@BzUGkb!^y3 z+^jiXbT_UXe?UibGx4fd(>CAf+a;Hb%zLG8H34<(?`NOf~5uu`Cf4C?eH z>uMdY=kHyI4Ze+7BQa$&c-uf9a(lQ%u(Cmxf3`R-ga3>!c$mnNUTB}%tJl%ht;;;S zn8)o_=bheSlsf3Uzks1p3{#B1;3l{)~B1@n!z(NM2H{p#_5%x zZ_x*8(aAVCp*#dnc2V*DKvoL&OE|d}AddY82crkN;w6TSXpl{URA|kfW_nPYVy%v{ z0BW^Y{$G5le?JMNhWl_U;(#NeoZ67NAkSq7g8BY_RUwqP@j158!G6dJ_;<<9yPM!t zA6|qXBosLqGelD6iZ7LcPzx#;HYf8O8X*?QQS_b;KRpBRcngXGuDsI9 zx~|Id+9zzD-CPN<1c3Po<0*n`d0TTmyxhuvu9jNQUhObCJI|7gfl`SZC-lKVC}Pc};Vf z+w1IEM+1IE{kzHD{}1A{Y8pmOW8Y=7I>x#T!I5+)4D#d&5GF~g8Fq3#q4w@RtIwi2 zj0rgF%uF_5T1TwizUny^MF@TgMdtW>^dV0JkcSK^mt{7REEJ?AN}ara?(LvXn&A8HftV*fvSANDSFBg-up2DD+*EFGvtjy3$L1coz zIxXd!r##=35#@>1`=H&8j;U98zV9x^H?%$=3pFrb@N>H7F9iLS7jODr?*Q-kjn$r0 zzz6VTt`(E2A0?KKaOerlcOLf#dEibxH~?)T_~K zV{0?+kmc?Pbvg)7&(zt5!zFH+<0XJrob zz{nP5n+LGeX&bxa0*rV=nUp<3Cn)Ct_s>qQHR)rp> z$YciD_>jOWU8123mCCdX-s4;k7gB@k6~o%cQ%eG7p}IWa?LC+kNRd4b=e6Wc=|4FwSLh`D#UDpm;c70M zc<vY$v0ti-P7862m^I1-PqU8^ex*h~cv));_A@S5i_B1#Kwlwk)Y2Dq zZLxGU&&%X)_Z23+t`4m`W-n1Tdzg}=`@>*cpCi(%(Br&i;sf~@oORTH4~HpufJbc} z96Euycbh1d=nWT%63H# zR3HaL@guNbANPJ?BqLD>Gs9`r6Nz3=<=okbZl>DpU=8!U;3P`^WnE+`3B~L?fzx3_ zYW1~b2&K|gci^+Apt8!7^tGj4zQji%hlN?C&(c1UT)dRd_6aWC5*YkzfP%qp><(4NNp z^r%7pjm;{RF|2*l})ppu3&`75n+_7Gq$J8S9EF5EA6+|2&&jLG(?wO zkgsruxwU=sAzA`d_jG&oNvwmq+W(@^4=GTu;#a&02N1{x%9K!G0@1@@xQ`C*WNVqG z9?{EC6ju6HZm=<%`}^SdrK>;S*l_qEpBp)o^0X+&m{EU*>O$dcqx`kSpmdc!}Nti8kN4(7_ldhAxMUJ0I063S;&?hnf?W^l=@NI07 z;<8kfcDlC^aj%rx=STUvow;O^_icZZOQOQFD$yXBQ7Y|Ud!Ej7>7UyQQViIs#(53= z1A|&q3L6G(Mz1o)Pr~ucZx`l@O;H;eajKo;Ql>Z!^{h$|sdLI+g&KdM?AInYPgHI6 zU2E3Yx2KYyhL}=$e!Xs7#hc-YNq7KL&!m5S_=iNaqt=6uzQgldN%6{<1Gn?Uj*Q=r zq$OJN3u;?vFU2?PENvCg>J^U_#KSDi+_BC!;IJ|_EY1M76k)baDH1@ccIQIc!%Pd}ZS_7~Tu99ty{8OnS}@+JnqhPE->wMu?0r@i?XpGvkkCNfHnoaPxU zmvonlIEZskQ67qrp&KFcO~#u8^^q3N0vs6RtHl{whxb>5{P%jiP53Z(4X%aw5sP5V zY5(<7TA(wogg44Gw4?I)pU0;$y5=rF!hDxtE-I^XF8N4Dyd-{gRU*M7Lk5i?5%RuwNlp;758s0v8Dms!DnYWSX_b9^nMz(%QY)f38`VlN#^pT+P>!Ca*uHIf%3oh`ZG@W83ev7$Z;4&ODBw{;b{ zDqo9+glp>1?MWjF0Rr{@3AxAfwGElr zamb%Co6yVl6$y!7f__#nw(V{yNHtyF1??vXFZrHP`tGB^y{^8Np z_hqxY6(Q522~;N&=D#{SwLI@LsFZ4OB}$d){o zqufD}j_@Cc#`{T1%g}d}waEK;b0r(-%_-MjPg}Z~tO3h)nd`guykqUK5Pga5k+cFN$MmD0&*Lrl4@~-=zJYU~x zDeU`bC8s238D-8dxg<+B4<@ER+P!`x>PjzuDXp;U{Y!9r*6j_g$pV)U-t zr#Fn?iq`%m*9QmZ-&uynnuWGJb(OKThX|&1ruf=Atq5FlhY03d7v%p>c8D-}h_HD0 z850g2Ag|f~xbTJNEdq9|1J>*T=RBX!_Y01wcb>T+-jMKwfR(oRJ2pZrUiv3OIbWnu z?MJ3hwSHB{EY|pq1L9)Qk-;sG2rFQ}7j?Lc@Qc4`h@5aMtn>r9IDtn+&Kv8L@bCD> zCa+%s)ArKBn$O_#@$c>iDD?q~`M(uR7*b9`L`Kx;TbWeepZ`p*z&E;ybGN5z_kJg< zC7`1PNi0kRkbzBaj9)&%qVVYq%P|c+FEEjZct%pKSrO6%F!`4eb z7%t(Dvg_HPWYnhvI++FQN~<&OLHIR0K)}*=rCs@VvvbL*Hi*cYUox%Lnp*YzT(0_! z-+-^d!CsXAnm(u8vl@l|v*h=tYdX5{^Re5K=e-k~83i1euT12V-<7$46@ub##7}C} zaYZv`zghA49o@}B_~7`6$y5=mFycK;P%*tXxagI1E8R~yBVW22N zcwX(G(_ELY(jTw|1E|Puy=M6`4XOs2uFx`mS3mcbnWK2rT+5f)nPVg zd1|)N@{NPZLZ66UY#|bDJ%VG2#^wR;K<2)STp$MafTW!BMra^yA-%0&AQf`&he#c) z@c-!-vTrwWxI7q?cHYwboM;_Axn}t;23!B~8S}XVOWoZG!wdR}4$TXT;IGsWzHC$3 zR(Ig7Kf$_q@|@f(G<0=NffdW#OysS`~HMUdDHwp4?d0ySUm}VBH8NnVX5wv z$;7A&DolO&DT4>y(O@@b5@a8;Hx~uL&=H?bJG{*esjfE=p5~v2{@L5-IC@oe)*SPm zjX0*Ql4aZ<3=7LB;JKcysnPCShxFB~*5mmQf0z4VJ@{0kqN&3zuqf=fbDMBG5S(ou zS$uzOz_Yz0q-?FLF+7Q?XpP{k%ZB7LZ7+%of;-1X)XKdrh>Iv&G{PfSWnBa<%vcrS z5P7#Ri^buDkQ$rXY9%Az;gB6#TgM<3-_%}mYza_SkOoE~8WL@Vz7(LijAhA(fkPkd z|G1z-D_{wcE0=qfZN5rsh8;>NWNk39&X%SbUe(!o*NQJ+p0?h-Hd+Ng`LV&7r2cOi z`2R^&zzUEMCTE*Y7ecoihj13e0#L4>UQ-jephhi^y_aKylUnD`g#Yfe$RCD|kH9N&6aB6qspPw7&OJ+w+G})<^)ia7GqI$#IM=fpRbzdU zf*p!E2=rVS=DrJQe;xPy6szo$e)oOA%woUf1Z%pX`II-Px2NkYi=&ML;Fq zLAJadK~GC9r4t*`$*sCZaSr$2D9Vr?f?ejh!Aalxv>|M?%lR&mjB znH6Jpsn~?Za;EeyNB_|0n;%GRf{1S$>~Jk78$QS;38D3z_SNjJElFB9gRct$x&l9U z?!fuIe*T{Hx~Y?xpLUZd{D>Vdm0;*kpLSYAL-*YS(qMfI9YZ7?g907H$le1q-rsP$!i%Jpe#0vZhqKeN44F-hBs8(@ zVX8TK6zUueNLh0HPNC3Y$(oIxR#9lO+UeS)i-5P2e$j;FJIkG5n0{*FuTwzZg%jYj zgC&k(vRJhq3;jzm7P9VhE#B`BoM6`Cc1TMh6>cVw%XOdQs#sVra&LlY)rT(8P%BGPgLcDpoc-iL8jyhv$SoAysw3XE=#$S(3O`k3dM z^EH2S$$@cLafe^gTQrTv6itz3Wp)TQsJx`)OgGG2{;8h}^(`~?)}O=U8At~J literal 0 HcmV?d00001 From 6c8f843a0f177b92eafdd620ebc532de74eec16b Mon Sep 17 00:00:00 2001 From: coderaiser Date: Thu, 24 Jan 2013 07:03:46 -0500 Subject: [PATCH 127/347] changed icons font from foundicons to fonteollo --- ChangeLog | 4 +++- css/style.css | 21 +++++++-------------- font/fontello.woff | Bin 3424 -> 3136 bytes 3 files changed, 10 insertions(+), 15 deletions(-) diff --git a/ChangeLog b/ChangeLog index 2bdc9440..9ec6d75f 100644 --- a/ChangeLog +++ b/ChangeLog @@ -104,7 +104,9 @@ clicked on menu item. * Fixed bug with working links in path panel and with clicks on files. -* Changed icons font from octicons to foundicons' +* Changed icons font from octicons to foundicons + +* Changed icons font from foundicons to fonteollo 2012.12.12, Version 0.1.8 diff --git a/css/style.css b/css/style.css index 76aa57a1..cb318fb9 100644 --- a/css/style.css +++ b/css/style.css @@ -2,16 +2,6 @@ @import url(//fonts.googleapis.com/css?family=Droid+Sans+Mono); */ -/* Foundation Icons General Enclosed */ -/* -@font-face { - font-family: 'FoundationIconsGeneralEnclosed'; - src: url('//dl.dropbox.com/u/78163899/mnemonia/fonts/foundation-icons-general-enclosed.woff') format('woff'); - font-weight: normal; - font-style: normal; - -} -*/ /* символьный шрифт от гитхаба*/ @font-face { font-family: "GeneralFoundicons"; @@ -20,7 +10,7 @@ font-style: normal; } - +/* http://fontello.com/ */ @font-face { font-family: 'Fontello'; font-style: normal; @@ -49,7 +39,7 @@ body{ .menu{ margin-bottom: 0; - font: 16px 'Octicons Regular'; + font: 16px 'Octicons Regular'; } .path-icon{ @@ -78,13 +68,16 @@ body{ width:16px; height:16px; margin-left:0.5%; - font-family: 'GeneralFoundicons'; + /* font-family: 'GeneralFoundicons'; */ + font-family: 'Fontello'; } .error::before{ position: relative; color:rgb(222, 41, 41); cursor:default; - content:'\f013'; + /* content:'\f013'; */ + content: '\2757'; + font-size:14px; } .loading{ position:relative; diff --git a/font/fontello.woff b/font/fontello.woff index 499d4eeaf81a16e08c8a079af51205262bd87984..5cf2f300c8b4f779e07c96206cef6a50308a1a33 100644 GIT binary patch literal 3136 zcmY*bc{r3^A3no0)+{00sIer=#4L8QWky*NgBQg+%rMrJEg^e&MI>vM7YR|a_L8Ns z%T}R{WJyWNQekA7@8SKvKfd!^_jS&FpZj-y=eeHekNb)RkqBS_8r=GTZ*LYb`nV7YL44Sh)RLe$%Jna7k zAIB&ZVfh2X3z_OuHSVs?kcXoS@?asT?ZnMJbB9DoV?g`}1Q|Ae)(Ur@fM5VP zMIgS9gZ$vEw=V_qabh5iXAdkYFgqXTV5l#rKEzS`SO#quoqb#(jSRiRUSB(x%%S2- zzWxCKa5X}ny?P__Iwf$)&lRfW`T}vgJv6q?76s=~$Ye4VM1N$ey=Qw#d!=L+^%7iA zV}}E=gWgZ4k9F7QMnx5|8R9?C zFjOi_BCZ;Xr^5tJvF1sne!kN&U3g@dsptQKiH?(VI}j&Wh>E zAY3G%4lYL#LLH72?~3FU5sfvCTPm7o40i8GHyH&g+18$^KGU=4R9sN0vm7&GihCeuDm5-O-rs^YCs`o%%o{XX zsi~z==}FP~CU;Z#DlC<6Rq(i)#5=VXB<3ElQWIM(zgrx_r?7;4u6Zw6i(KND(Rrc9 z-e)fK%(uMJCI7xVi(Cn0SFYuARd}9`{&J2DM+@4x?J_=j<_-07 z;hzYcZ_q~CWN#Ih+b(k%R>#(ldPje!v3_owrkd#e^ZY*07Y%fyzC z-$Z{s=UV-HYb0$Et>K(a>o)Ap5^lS~zp0G9sF!-hY{sh1EAh~$&{8>40hVyxPxSh| z3)pVKPZjc|@?NFcMLJ%I_Wl0E(w&Hft!oV3c>MfK?nsPpKi7)+CM=8;&OViXv|asP zv~Ryw7|AKt*TiSqN1nuAkzEtO_Wxr4A*m6MW$#7?Z$PXXFE)-m}@2 z*<38{r7Ye~d!p*$ozVQmqmID{=<&Vii2E9sUulsf{i*0=z}j1tBGT5-%+#>sq=HgY zWrAmCxtdfqJW}n;KP;=}KfSg2er7s`ZJ)xhj6J`vYE26KK9(WI`=h0@S)^c&{xN}4 zn7mk3MK={7*`E42LzBk|ZN;?Q&+weD4&0=kSB+OI>6nmO&!Fa&es7(#Ig^pGQBm)t z`*)mVNXX$gv5&ncpOqXcvcwOsu2d-y-rdUd8`CqK&nj+X%ex$8u-J)RGMY4KQ2iFb z*mRTj;*yOkt#1`5q@o>#D3+h>^C$ray(E0!A1bISJAQaoUM(l3!zA!pddfxVA;lnj zx0mH6fdN_T_TMVic5vG6b`mr_j|#P=ug>R_6|7F$v?1!*f@~)l=Xe!$zZP2Bs7`zt z@1v+FdG$na_qklyTqIV?jxJAUdX^2j3^^M3Pi3Pw&MN!1J_}czFTGXDw7q*mI%@}c zbJK@^dP-9+i1b>&Rn;TmGVC3fh|(*gd7h!k9@MPX*3taY9p9l2lxC$*@;jZinXe0S zY1(F2^IUW7U$4mxS=Qg~(j~Z5cK+3Fo;+iBu^)k$TheHd3VnmO#YZNx@%eaH zdBNA|r6Yext9(;z;uXoE^fn!=W}QCN_4#dsq6BCjIX2Hjr_J%{pyH<`HQ~*3@8OI* zWVCXhBqI$O{iv_$zzgYkatboq81l%L9jvzCX&3NgZO}ND~$zV6C zztv~o`+b3h%jhStU#;K8|r(b?t#u7lJx<>>00_60tX?%}adnqBfE@ zDV{Bf4e31}?NWESmDQpb>b;!^#RC+D)CNK3oc)82kq-<1=-;aI(q6EMLL4B!R@;KV)=*6Vz{n0@;~-ax9)#x%9F$r z6sRR_qB3%iYi0L?zZFb;sUI$~_Y^l6F%D-dk^D zC^vj(jaYb^idInDl611CF71d)otvbqUenCT*ua{ z-j5^EGs_>Ur^Fvli9T!f_y0E=kjcP>cnd59hvz=x{xPT=DaE}XFh;kT^Oy*lE%4J6 z$<{9h!it=g|H~6(vMoa|UNZ4|K8!v4DIS9>WKjc_R;;p6y72~*kPX0~#K(FC@WR;u zFUvUKg(SKCrv%0hgHjswZv%h;IB)=(p+E#BDp|0X5dQ>F7Te1$*HTYj?9k`CKK0|c z6oH@ZG?u_A=l$@YM|nc(IkAe<)*f`V$I>4OJxcC=*YhMb^(*Q%4Ybat)9O5|$R4z| z{QRqU3Q%Xnc5F5qyV)0|C=Zac{}9X5N_H2eMD{R(^M zNZx@H7d}q-iKl4TFP)oj_Y*EIII$rT(TPNaA}vvbB=|A-DWdT&T@@XN_BhdwL4Vss zq}S51dLhNM&!6a9Nwk6U!|q*4N#CRjCt?0|dWSvo(s6kyU2h7MbMK18jfCYtd!isf zLQ-YV-_Ekk6jjHdxx_L!B|VI9pzt;#-%gvVacG{k8Bo59dH6;6F|#;F!Of)guK`Cm zsel$(}c( zWFevWvAN=gdC!NR(#Dlh+*G0cZ|(wOm_yt|?zcdk50ArOaabI_wmR#xt5{C%ow^LM zCpjA8hH?U++=NZhv=Q`K+10>cH|?9)p%_g;Rl}r>P}Qu`mulg190V()I!-PGT(JfL qGpdn+C0N>3>nGUNAXtrRWcOGj9PCk;kv%jTVa)5*@Z_2U!012CE>4F4 literal 3424 zcmY*c2{@Ep8-8cOSjLR9BoU#=ZyS3tw(JoqyBb?Y+1EirKKTq1$~t9D4Us)bW62&O zg(lgJrEDW>nE&G&8uQrw?j>gIo}VFrW;;uUwVIfZ9ut8-lPphz-EGcscok z+8W4-VBLx~6W(zN4ip9e=v`3b1c3%H0OppjyBDY>0s#Ce03Z&g(;gnWI|YD!S^fq3 zApQ%qyJzSvP*Vf|*geqq+QXcCwRkrtR{&tE0e!&ufbySpYAGHRL2Uu#LLkIgv|wR) zuRsC-upf;jb_8Az;I5~S3+Tsw6odN+Fm-sCmlFYu%dQ1-;Uhp0K)ttC9%C7F zcQ*5h8w7Cbh!Oxevbo}LlQ3siW;D(j2iM^gz6IGh0l*7@i{JqN*tc-|t<62n&0pGU za-yU2S#%V}-Q`?ep+roDyh3v{S~muoDENq$-8z{kv&GP?ZjEGrAWyFs-7+t1 z#h5yCx?@AL_xXst4LU`wV(?zbfE4=2CTnVu_`1~8suE27m9YJP=A;GH=Kwd*C;)y{ z0_n;1Z^6e6&`&GH?8mEc_F^2Y;wSmnej}>+#kv0KwXwXcoTY_<+3MezZW6$18TH4p*Apx>&M5SG_}P z@*YwY0k3Q+pb`^wx>TFKDym*=Z<$r6n0-P|02WC9gM-N$&gb)GS&o~>H&>qSP*Bz> zGG|D3s}@U(#CUO*pSh)_lsCWH{ZcYE-QOuQ|FMU?(*@%T(&fyKu7#UlVUmG`P+w-!-6CJ+tb#yYN*~DSu?=!_35r z#*n1!uE5{7hm85SdZAhZi5F6C)Gb&4FnrzU#Fr6?NGN@oJAyZ|XZB8~*@f%L%fS>b zm`}8wv%VYYBFI@Mr;Pk9i1oHUSgXeiqxH>$T*IG2U(Yv#)HC8|E{%B-llo&@Y~S=LOhMuNl7|OZpUK_A;AK8reQ(>J z3+5>l{ztjjz2E(k@_g#&)o9>h-#|cj9wkJQ`>AY z()j*uKGw^3C|*f5S)r#_O;zq>OfygKd4}#R(UV5XCh!}q4&I4>fK}8=L|Iu*{huog zmji{sH%*k{jOce(uXV<18h9x3<4Un?*l!sh<^WK^2MCYy9k4R4NdWr+)4jODxibQtO72y{N zn6*htL>y|ooSb$3bXs}K%(`)mQngV#JP@kQ1rVxLgi6s$0u9gJ*SWyeK)+aOeyJ z|Fs`#L|#xfrws(5Bk(XNpeyF{{uR4)zb zmX6#iHUN30u3`iYZJI0kXs^;r1{pb1dg=fD`Rmt<0XRwBqOesHK5y@VOe#Gf6WOS5#EH8D>1}{9RwU}?7rb{&S zk@O0~+uhfNZ9Z31ja7c6Yo_t!S!%+1{(|`{Q}?e$8a%<{jS(Bcw1|oAThYz1W@``B z3H-p?9iSOua~hGt8->Vt`+MzFRPlL-;>wz1K6OJk8ph46H4?EAJCC;XeZp&Gn#2t? z7=IxbD#xVRU+i~#p7kpF?RrKiLH`&+RVrE6CY4+drN7Tmk{JA}%H!o+c7EWJony6P zsY{S~%f}B+YjbD@^?m&xdVZ%9{_l*{#=kkXf@OR9X5XreCI=#v8&`5oq}t?T*k0Ji z$M`+CtE;Y{jlQTEsU}5m9&vs0B^~V^OM&ErDpUMJ@ z?Nv6m$=wxp?~#r%nbf;68}@l`jrpRbf%>S$}d+*X0TS%o)!%IJFZ zHz9QqcC)3L%MPoX<#7)<1>BzwhI9`lbu5a#UnISsv05$>IEna_rqpQCG{7~R4^^_+ z;&Z#qRJ`>>!pKw&A2F%LWp+nCQCOdMmYY>70r%QY@8xhwZMXIL8{ecaXWD9TT^C8- z-N_V7=_g-*Z5eJ#zm{W`o8=KMJ*4ev@+M(Os7E>9aZ2^f=&}C3ffm6jxP6Y^dgvOQ z!ST$zkE`(eRFA2_z}_fp-5OEC`ytIuM~9AAXWU0F0vz#98#kaIuy%*o(0e~*VKbz% zI7HMC6I168@fR7(*Vjxiv0jIf9rl#y$+w{jIVl4im&q=8OID72BS~6?hDYdAwO>61 zR!0tts$3KPVPOmGZkekkEX$)R#Q9uE5WP)$(D*Gmy;;ekh2?m#T*jv>qdU!A#p9Z_ zPXl6CmRi|1Ee^lNb^meh#!9w4h!lTVWs^kEjL*Jia`g`&-zI^(nW0>8N%R1IK2eh0 z-*S{qE_Rcs_q~5^Ry@|3Y~wHZ4##(vt|sN4b%uY&R$PHXArllXF310hq$fRLuhp3{ zjhjK$Oxy{4mn6(ZHD}saB@pXQ`7Rz43KA&X^^N5``6F#&5vO>iG)8-Es$rjHA4yE@ z>ib`=1WfnE^-fRkdQmW43QYh}jufxz=N#-0L(>?;zn4$Gy9;0CVe!~){)@h zY3JoOa{B4a#if0mTQWair=Y;-zTjyL*7c&{t{^swm@XSdly&Y`lR&o)FZZwXmAP+o zOaijzqZ*>FLQ;Y2OMWoH#_+MW$_w=R*z$(0`lPFyQ+_NzIz+NcYC9Jw*trTF)F&a8 zie#1KocgvGozlO*#CCekE)CCch9&V9R}k8GnK*_GEwl&RgSAd;yBJE@q>8w%*f6W{ z{xZRPP6mR+2X9n3BMP7Jhh!VB+ZpXOM_0A@kKA{#o zju%sYJ^iohsUG_1YS_*=b9rWCAt^~Jl4l|ddCme*Nd+`gI?K)Z6`6iQ6O zwMaZO;4m;AJ{Ku{7qW;AEkD`=&Oi*Kk3*kh6gY=gz{tsCn(bE6|BW&H*O%K$oRWB?R)&_ALVZ9dQw^fQVia du7HX9R>37$^eivcLm06Y@T5v6;1an0{|3GI{Qm#| From cae40469a0daa3209423ff6ccdd63c234a1d425c Mon Sep 17 00:00:00 2001 From: coderaiser Date: Thu, 24 Jan 2013 07:36:07 -0500 Subject: [PATCH 128/347] refactored --- lib/client.js | 16 +++--- lib/client/keyBinding.js | 102 ++++++++++++++++++--------------------- 2 files changed, 57 insertions(+), 61 deletions(-) diff --git a/lib/client.js b/lib/client.js index db176008..c9d24fe7 100644 --- a/lib/client.js +++ b/lib/client.js @@ -91,14 +91,16 @@ CloudCmd._loadDir = function(pLink, pNeedRefresh){ lLink = pLink || Util.removeStr(lHref, CloudCmd.HOST), lDir = DOM.getCurrentDir(); - /* загружаем содержимое каталога */ - CloudCmd._ajaxLoad(lLink, { refresh: pNeedRefresh }); + if(pLink || lCurrentLink.target !== '_blank'){ + /* загружаем содержимое каталога */ + CloudCmd._ajaxLoad(lLink, { refresh: pNeedRefresh }); + + /* если нажали на ссылку на верхний каталог*/ + if(lParent === '..' && lDir !== '/') + CloudCmd._currentToParent(lDir); + } - /* если нажали на ссылку на верхний каталог*/ - if(lParent === '..' && lDir !== '/') - CloudCmd._currentToParent(lDir); - - DOM.preventDefault(); + DOM.preventDefault(pEvent); }; }; diff --git a/lib/client/keyBinding.js b/lib/client/keyBinding.js index 9bf1daff..ae8b4d60 100644 --- a/lib/client/keyBinding.js +++ b/lib/client/keyBinding.js @@ -60,16 +60,15 @@ var CloudCommander, Util, DOM; }; - var key_event = function(event){ + var key_event = function(pEvent){ /* получаем выдленный файл*/ - var lCurrentFile = DOM.getCurrentFile(), - lName, i; + var lCurrentFile = DOM.getCurrentFile(), i; /* если клавиши можно обрабатывать*/ - if(keyBinded && event){ - var lKeyCode = event.keyCode; + if(keyBinded && pEvent){ + var lKeyCode = pEvent.keyCode; /* open configuration window */ - if(lKeyCode === KEY.O && event.altKey){ + if(lKeyCode === KEY.O && pEvent.altKey){ console.log('openning config window...'); DOM.Images.showLoad({top: true}); @@ -77,12 +76,12 @@ var CloudCommander, Util, DOM; Util.exec(CloudCmd.Config); } - else if(lKeyCode === KEY.G && event.altKey) + else if(lKeyCode === KEY.G && pEvent.altKey) Util.exec(CloudCmd.GitHub); - else if(lKeyCode === KEY.D && event.altKey){ + else if(lKeyCode === KEY.D && pEvent.altKey){ Util.exec(CloudCmd.DropBox); - event.preventDefault(); + DOM.preventDefault(pEvent); } /* если нажали таб: @@ -113,20 +112,20 @@ var CloudCommander, Util, DOM; } }); - event.preventDefault();//запрет на дальнейшее действие + DOM.preventDefault(pEvent);//запрет на дальнейшее действие } else if(lKeyCode === KEY.Delete) DOM.promptRemoveCurrent(lCurrentFile); /* if f3 or shift+f3 or alt+f3 pressed */ else if(lKeyCode === KEY.F3){ - var lEditor = CloudCmd[event.shiftKey ? + var lEditor = CloudCmd[pEvent.shiftKey ? 'Viewer' : 'Editor']; Util.exec(lEditor, true); - event.preventDefault();//запрет на дальнейшее действие - } + DOM.preventDefault(pEvent); + } /* if f4 pressed */ else if(lKeyCode === KEY.F4) { @@ -134,12 +133,12 @@ var CloudCommander, Util, DOM; Util.exec(CloudCmd.Editor); - event.preventDefault();//запрет на дальнейшее действие + DOM.preventDefault(pEvent); } - else if(lKeyCode === KEY.F10 && event.shiftKey){ + else if(lKeyCode === KEY.F10 && pEvent.shiftKey){ Util.exec(CloudCmd.Menu); - - event.preventDefault();//запрет на дальнейшее действие + + DOM.preventDefault(pEvent); } else if (lKeyCode === KEY.TRA){ @@ -159,24 +158,23 @@ var CloudCommander, Util, DOM; lCurrentFile = lCurrentFile.previousSibling; if(lCurrentFile){ /* выделяем предыдущую строку*/ - DOM.setCurrentFile(lCurrentFile); + DOM.setCurrentFile(lCurrentFile); } - event.preventDefault();//запрет на дальнейшее действие + DOM.preventDefault(pEvent); } /* если нажали клавишу в низ*/ - else if(lKeyCode === KEY.DOWN){ + else if(lKeyCode === KEY.DOWN){ /* если ненайдены выделенные файлы - выходим*/ - if(!lCurrentFile)return; - - lCurrentFile = lCurrentFile.nextSibling; - /* если это не последняя строка */ if(lCurrentFile){ - /* выделяем следующую строку*/ - DOM.setCurrentFile(lCurrentFile); - - event.preventDefault();//запрет на дальнейшее действие + lCurrentFile = lCurrentFile.nextSibling; + /* если это не последняя строка */ + if(lCurrentFile){ + /* выделяем следующую строку*/ + DOM.setCurrentFile(lCurrentFile); + DOM.preventDefault(pEvent); + } } } @@ -185,19 +183,18 @@ var CloudCommander, Util, DOM; * элементу */ else if(lKeyCode === KEY.HOME){ - /* получаем первый элемент - * пропускаем путь и заголовки столбиков - * выделяем верхий файл - */ - lCurrentFile = lCurrentFile. - parentElement.firstChild; - - /* set current file and - * move scrollbar to top - */ - DOM.setCurrentFile(lCurrentFile); - - event.preventDefault();//запрет на дальнейшее действие + /* получаем первый элемент + * пропускаем путь и заголовки столбиков + * выделяем верхий файл + */ + lCurrentFile = lCurrentFile.parentElement.firstChild; + + /* set current file and + * move scrollbar to top + */ + DOM.setCurrentFile(lCurrentFile); + + DOM.preventDefault(pEvent); } /* если нажали клавишу End @@ -211,8 +208,8 @@ var CloudCommander, Util, DOM; /* set current file and * move scrollbar to bottom */ - DOM.setCurrentFile(lCurrentFile); - event.preventDefault();//запрет на дальнейшее действие + DOM.setCurrentFile(lCurrentFile); + DOM.preventDefault(pEvent); } /* если нажали клавишу page down @@ -227,8 +224,7 @@ var CloudCommander, Util, DOM; lCurrentFile = lCurrentFile.nextSibling; } DOM.setCurrentFile(lCurrentFile); - - event.preventDefault();//запрет на дальнейшее действие + DOM.preventDefault(pEvent); } /* если нажали клавишу page up @@ -254,14 +250,12 @@ var CloudCommander, Util, DOM; lC = lC.previousSibling; } DOM.setCurrentFile(lC); - - event.preventDefault();//запрет на дальнейшее действие + DOM.preventDefault(pEvent); } /* если нажали Enter - открываем папку*/ else if(lKeyCode === KEY.ENTER) Util.exec(CloudCmd._loadDir()); - /* если нажали +r * обновляем страницу, * загружаем содержимое каталога @@ -270,7 +264,7 @@ var CloudCommander, Util, DOM; * (обновляем кэш) */ else if(lKeyCode === KEY.R && - event.ctrlKey){ + pEvent.ctrlKey){ console.log('+r pressed\n' + 'reloading page...\n' + 'press +q to remove all key-handlers'); @@ -298,7 +292,7 @@ var CloudCommander, Util, DOM; /* если нажали +d чистим кэш */ else if(lKeyCode === KEY.D && - event.ctrlKey){ + pEvent.ctrlKey){ console.log('+d pressed\n' + 'clearing cache...\n' + 'press +q to remove all key-handlers'); @@ -313,7 +307,7 @@ var CloudCommander, Util, DOM; * убираем все обработчики * нажатий клавиш */ - else if(lKeyCode === KEY.Q && event.altKey){ + else if(lKeyCode === KEY.Q && pEvent.altKey){ console.log('+q pressed\n' + '+r reload key-handerl - removed' + '+s clear cache key-handler - removed'+ @@ -322,7 +316,7 @@ var CloudCommander, Util, DOM; /* обработчик нажатий клавиш снят*/ keyBinded = false; - event.preventDefault();//запрет на дальнейшее действие + pEvent.preventDefault();//запрет на дальнейшее действие } } @@ -330,7 +324,7 @@ var CloudCommander, Util, DOM; * устанавливаем все обработчики * нажатий клавиш */ - else if(event.keyCode === KEY.S && event.altKey){ + else if(pEvent.keyCode === KEY.S && pEvent.altKey){ /* обрабатываем нажатия на клавиши*/ keyBinded = true; @@ -339,7 +333,7 @@ var CloudCommander, Util, DOM; '+s clear cache key-handler - set\n' + 'press +q to remove them'); - event.preventDefault();//запрет на дальнейшее действие + pEvent.preventDefault();//запрет на дальнейшее действие } }; From 4df1d72332faade4c666e66c146611d399dbb3b5 Mon Sep 17 00:00:00 2001 From: coderaiser Date: Thu, 24 Jan 2013 09:27:12 -0500 Subject: [PATCH 129/347] refactored --- ChangeLog | 2 ++ css/style.css | 36 ++++++++++++++---------------------- font/Octicons.woff | Bin 41832 -> 0 bytes html/index.html | 11 ----------- lib/client.js | 28 ++++++++-------------------- 5 files changed, 24 insertions(+), 53 deletions(-) delete mode 100644 font/Octicons.woff diff --git a/ChangeLog b/ChangeLog index 9ec6d75f..bca108e7 100644 --- a/ChangeLog +++ b/ChangeLog @@ -108,6 +108,8 @@ with clicks on files. * Changed icons font from foundicons to fonteollo +* Removed Octicons font. + 2012.12.12, Version 0.1.8 diff --git a/css/style.css b/css/style.css index cb318fb9..d1b7736a 100644 --- a/css/style.css +++ b/css/style.css @@ -13,16 +13,10 @@ /* http://fontello.com/ */ @font-face { font-family: 'Fontello'; - font-style: normal; + src: url("/font/fontello.eot"); + src: url("/font/fontello.eot?#iefix") format('embedded-opentype'), url("/font/fontello.woff") format('woff'), url("/font/fontello.ttf") format('truetype'), url("/font/fontello.svg#cloudcmd") format('svg'); font-weight: normal; - src: url('/font/fontello.woff') format('woff'); -} - -@font-face { - font-family: 'Octicons Regular'; font-style: normal; - font-weight: normal; - src: local('Octicons Regular'), url('/font/Octicons.woff') format('woff'); } @font-face { @@ -37,11 +31,6 @@ body{ background-color:white; } -.menu{ - margin-bottom: 0; - font: 16px 'Octicons Regular'; -} - .path-icon{ position: relative; top: 3px; @@ -148,7 +137,7 @@ body{ * установления курсора */ .current-file > .mini-icon{ - left: -6px; + left: -6px; } /* freeupex */ .directory{ @@ -274,26 +263,29 @@ a:hover, a:active { color:white; } -/* меняем иконки на шрифтовые*/ + .fm-header{ + display:none; + } + + /* меняем иконки на шрифтовые*/ .mini-icon { - font: 60px 'Octicons Regular'; + color: rgb(246, 224, 124); + color: rgba(246, 224, 124, 0.56); + font: 60px 'Fontello'; width: 40%; height: 0; margin-left: 0; float: right; position: relative; - top: -17px; - - color: rgb(246, 224, 124); - color: rgba(246, 224, 124, 0.56); + top: 10px } .directory::before{ - content: '\f216'; + content: '\1f4c1'; } .text-file::before{ color: rgb(26, 224, 124); color: rgba(26, 224, 124, 0.56); - content: '\f211'; + content: '\1f4c4'; } .text-file{ background-image:none; diff --git a/font/Octicons.woff b/font/Octicons.woff deleted file mode 100644 index 79989eba838ee30608bd025e496f0484021a7e61..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 41832 zcmY&wDt4Ewr$&*IGNbCZQGbkY}=aHwlx#mww*8U{qCPzYwdM?uiR(Dsg zs_HuK^5WtEAmF|6Whsg(dAN-xt<)tZH=p$Pzx z+4$|l_zjNFxqU2)Z}D5(|K_;gAVdBDgRro5@c;n+Yck;1cRjo225aV_boO$$_B@X$Nqi zpaU{cQ!ZhBP?L~ShLHK2JAQBoh*@d?&I`|5Es(k|SHE3bSh>58vtz?%$phW4jh3&{{ zqP8i1t}~bw`DKhvP}$lREaP*Yc;U_wO*(F(q>dvkt^Sh?wJ&tcMOC?J@_DsSx4z-n zAed$<5OR4y%@d9Q0g(djZwuKt9X#@*QRN^Mav~lK_rdzuXOZBG1j=xyq!jO7SQEr> z{8DcohkU~qU#6_BJ%0bQCSIR=54JSV^Jv&A7P1kq5p@8xsIa)|OBl{f7ahdD(%Xz+ zQozAYtbaCC#6&GS^q1*@BRehv0BvyX+XN< zO67Hx*ARfE;#LrE06q#7M5@wAkY*U8U)O6~Ezqm7La)?-O;)W#8A-ukk;Jp?N-?EW zO2T$fck^NBQD|xnQj2@F|nufCm-L*a=G%zD<^{cf-4-SCD-YR&$3c@30;aRl zS3ConpVfgDI4?p5@SJzQ_02W~D_PHXA{A)jEY$>EQix!VB(fQ7v4}X;=HGt+MwwHa z+uDC-GgSOzumy<$P$n|aLyNG$#8o$g2k>G+Y;$n9xRN@IgaH#CtVJtLAx&F2Sh7D9 z_N8l+MVyF08S(rD$R2PM(3vztzX(%;T!-%}U0QNR-U5lCc&6?1_I{lI#LPD~uiM6} zIMp2I){P+myTA78he*(q0a3MPT|+Ns;*1qJavPZS#H^3e8jNaJ-8t10jzJyknw@sk z@P>zTZf~Sh_*^cTlDH}y>K~k^!*6Tc2*?5#+fY?$p$^sUxJl8CjLGT0)b(ifM!4Jr zKLd@^{CQ?fm^T}7R@f$16+2g@LD+Fn95(C`q%j)`I|xaYka!g<-fifqv9Ivrk6lPn z0$P7d2v%OK+MoX11FU;-X7V~n-A=LBV0if(3^n5FkVT1?X#iAh;5jq|E)HsGrn%PI zVL%!NSf6HA%|>y_lc}OQJQi5Vm8BF|$y?Y4j}@I#S@pr?6;kAjCs^4kM$=T#<_{-S z=&10bt4ab|mVCOH$WD~H#F~C8o}pTSRt@ijSowd@u`acCKFkGxkQ8mZydk$NFjT`k z5JWs|YhoSeSH{MGwM<~J+o__VQE4^dUt3yP*n#ht+t#lps1De%2l8E2&Cf5_kRexo zfbyBZrUUeS_Us}25}TvXW#wKSRGoHR)KVa+4Nb2=^zMBQ7@-5k{V4zpAl{_s(mhw( zaSy4RQ%CfTF6!HUYJLAG0&=GsE^H#u>xV0pe_ItQVGTB1hTu?8Kms7Un~TdB$mXlX z*eNtN5VT}RL4oiLwitrRpbX|pCxgCKLKOo~!7z$Rug!$}cwt)=R-FuyT0 z@lr7QL7Xr|@0M~WzykjFQSWVlejQ3asVuIvaq^L;p2CZ_bUOQ`I|4+6w2|&?ZgLh1 z-@DHy80}vq_Fr+TW+N^9vk@E(+|AJ!CXmaV8pL7GP~;0Liug)#7cY_EUld^3Jp_V~ zJbzUbvn9(K#9Y;dH*`fyn$|)S{Je!-DHrg?Tx$wm!FCZ)cHhO=^;A9>Y*7~bCg0se zmk*5$Cl%>3GbiiJ9zK_P5;Q}&WT7_5uYS>nw~58 zcSjg=bu#$u;sx0a+2hGuWqC;B%o7S4_g}>lJ_7c{K z_w(`V5ARJHtoplDtZyN15=g8MIa=8+`Hjn==TR$ls4!%{V{Dyc4ln{aFx@(1aR=~8YTPlb z(7(h<2m;_VI}a^)eyf{Q(z?LC2l*{Io{yiI6{M=VP3MUQm;O7G@{2CmJWDI9RuT&J zH_J1JKBhX_OL&(zpZcIjg2 zy>qOf_(N1YU{z~A24vCA4s?@O8J26AHn&>W57~1e z0`4RM4<<%e{ESh2o}^4B*Uw#>k7fGY4T|)0*|jiU-tZQ7ZkZC>+%D6q&vQls|Fe}h z4D=dx&CaWp7Q0RN+gZ_NV-D|r)Nr{=@7)-Sjv+N=Bilz^)69|Wg^0YDoItQME`3UzNf+C_E?zYyxHW+>k#XU!v0ev$NP=hC`dEf+v=`WFPSSV5A|h9NO}*?>`+H4TUCUMCtL&EDklfHb&|CP-Hf z81>PMSlL&xS%)Lz!2*3vvcZq9rMIitTMR&7hzeMsaAyJ*srP^iI^}+b`MtiqHE-R` z2A(%XRJCz%dwCx2A@V)nOq%Z9R8;H?Hm!gnZZ!BZes%l3;!-@)00!WDpWQ$9E5Awz z7rv^aTe7g~TI|}?x-L#)H=n>#>23wRxz_oKw8>8*-uPedZBLuDwJu00@BMA|_&>!c zEEwc{4DcGmf2N&Li{-di>iKcRU4#>wY7!54o*#^n% zhFplCPJp#YOR(Yu)UvF!hYwT4HJ6yUI;%yu&a7JRzy7fV(XWTb>X%Ss1t#*Np2^y0 zuu&mGTO<8~qMp18N?u0}Ox0M;Re@lAP7t;rNhj8*)@x0&JOJwdgbQ<2I(*iaU-QOz z2J=sor@LRy^*gIBZA%w;PUzuXZ{I`hs-|E1@v`sgoM`*H_FDKV0O{=P(d{pN9TTd& z6l<1yC=W~eb4J?!=B9;?(dX-pQDgJ(*FC*o&javnJ>+9-Dd*hl=19Q14i%yPU$`zJ z*yVjS$eun>ZwAZ;n>0P9_5+SqrcnHm7R2M#2$0ha=xt*M*jIqn;-osWuWOC~hEem7 z>^}v7X8Cwusrfb*!+&oO;Y{V)mtmkZ!0L3u?z}SVqQx|#9%{j)>*1;{pnLoM>p?{5 z(&p6YJc0`)>oH)vXVZiqchg@j>}fFJ^AWq zaoqk(cl}dVl+wTg0ZpSeZW?=^|K4bY#gToO0{4F z@RwU})DRr(u-<`<9sIvjhyIs@LJqY210)(?1f&fRS3Wv(_MQo{7cbTn4^Q^e-7&#H z;;7eV^lYMNHB$;BFef;Yuar@HuAq5;hB4BS!Vm|GV-%)PTj-jp*-#Fcx8n8~-^9H@ zId6bW`7i4Jw;h$SA3pk>b-+tsSzc|tc3@`G5neDiXD^6Cg}X|KRs6|ysA_4(4LtkbuRlY^k;5VCng^oNauzO9_lyh)h{W>F?5vPg zN`%Yo;IPw4=&Ipqskw)yK<9yCP40d`XpLD@fJa<~m5!cpCaApZf-3>x&J;)|0c9Wj zWE1W&$Pjm|zV1)=UxMJ~5Wlx0kB|3znaTMMmXM5fpLDGQN1io6JA5wQ)D#{1B3|oG z<+gdkYY3=j3UB&7*|6_MxeZ98}0b3ZDN^GXYsVR&zb{?UwGt3#q9owKM(7%?KTnloD5rOka3s;rl^~s zd&vLsF~tk38ardmk6~ygo*(Ns&$bL-Ar`Tw!MQ`Z@_`J7D;D@E!!$wwN0AvbM-cXgNmtkULlT0ix)ucAJJ9y(;<`q({_U`oRylswJ^vIGS+at`85azC-JK4YOs$m z3CCL!DBTh`fGnUjD|P_wN!&XDge7_Pv8SOAOzwbHalWPy+wm=Gr|$w$X5>l�1|-&CcA3ZKFiRw(%6`e#!J0CmFVh68L{ACG)dPk%AiZq}52{kGRf|E%0<-X{T)54#pkH#QEMLP(a_<1JRs znW>Le_XNn2zZ-`SMpuWvi5__(L8AbI_jTowx z(v=6~!lH&iuDDRXswR+pNzDaH%!=8bgoS+wfJ-)RIZFUUp$;hKnP7y@ZqovTVHPMc z4;^vlR&6t6m)|_<06{o+a6e9zn8w6@sZ93FSx<9T&&&K2XRecL5f6`2_-qFR8#LV$ z?(4+yxM=Hfq@r(CPKq`l;=Df?_arOPuPA z_=(FR5cK-v4NXTv?t79g$Xr43GnS{)Kq1+3j-DyLttumK%bO=(WgQJVG<$fmwUgpV zpx4LsCW7IxGSQJV!n(5v(t-KDbDJI$a>~k-3!~2^M)E9)WN(JZD_t`PQgg2K!YsQg z7rxFMyPXI3H`YKo`HXp_(sjpZDn1~9joQi^q$%I;)&_w%@d)by)S|$9u-FxpQtkI~ zbX72EPY|LtQqZ)d%ow$>Q~wBh6@|AyRug3u`TafUo@G*M0(JeXYN_^_tin1EIBa1D zt({>Fbu zJ4f`6%6?ETGmyr71tD(?ce(4FxH}ddC(hVA(IR2J3~dz$>5nb__liRfC}$xVEZMxT zSQBeLeh+>XIjcB)W1Xx+tHS&rZXl=|**s*TMQwnrH)ugXB_;H;5k3ci%NyP29V0Y~ zAmG(VQP;(CE#$@7Rk2G;Vqyd4v)q@#wr5)d65T{=X@K%+Csp~%*)p|%OjqoIA=M2v zs!d<0+8;|#ca{D+qO$A19Q}5*U%q3hrx-Q4k4GoH~{XHNU z;bug0B^miVHx?rn)!XhC79*-9R2tmF?3Q}GypN|>&fZ~{=;?Ia-F}cCMu5Ha^cRQT zv(BX(;()DEZf0%ycxwnmAtQgvJU>qH45&|2j06(R{qFgy)bHwnMK~0f&%w*yv+Q); z@%a?KO$J-&jyxyoVygE{?n|6viT?RfC^JF+$u#x31i6cpIL1x^TqjN)Yz%NLX3820 z94KU(-Nw)SitH4rcCu7vG7uFC_K~5Mk=w~lo9wH|$QT?bryDIy*B>_wiys*5NMh5& zPe^@jhlGTqey3%@)r$v<`$FD`nPDt>zwIzP%&RbSfd*}Qe!cp+o8eORIBI-%t~qoo6c%M zQ%q>9?2s+6Yd=nM=y#qpLkM%Vu&RC5ZLavw00^BfO(VQ8j4gut>nKK$3_mc?f2J4R z8%i-erU@Kv!Z-E+af4?QiR$*NKqa#Bq{cda>O7^f)XTcwm9{2W3>grn80j%!u?l4b z)7WCQ813>1^!K-HnRZ_c0u**1LLwPsP&J=CKy>Rbj5J+TnuvB~s?D(s$PY~laO8xL z;#5%GW7j5RIIh`%3g#M+yUBM4Fr3SbT+k4akdOf^p9F>qOO^qJx(%>v1T$cs6h&lU zckxcV<8AEgx|S1C-v-EIiq)7^R$m8qr7Yk%^%YXd(%4jSbosWDp|;aO-j6C@4;gOy zi{^HNYNwkC-wHCOUkb2rxLy2PsjON!YhDk6ue<^S zv68{@-r?bQ2+s%4;m1@fQ$o=tb%nRiswvim^8B9IL#+~*A2x!3AKGQ-ITwMR8=!S1 z8Sq@qTK^{F;4w1o3w873R>`%@j8ST0FBK@>?v4EJ{MNe&>q7g_1`%PE7;~4kqe0^R zw$(Spo5ZlRDW+vL+3^faeu(8nZF4vq!zyHcw6r$ zJJ8*_7xNOttL60Qlo~MSJ|7Okh{a*~&4-nKbcPB@Do9kUWpqiY(N>y#J8yg!@(U}#Yq#yOT15rC zxwYGsfJ++P;R`5W|1~F&T0G5RoRQqj{pyFy%7OZ>WwLh1ek;&%mfz=02w~Wnsutqf zF~{-%WWUZ)FUXT31=?hG1EZn3?_GoGtDA`vGLw5k7w%9=SJ$AY-lGBKX@mq~U7rd= zKE;m)VE;Ar2H0F=+Q{01dU$O%{K@Ep@RswYaS+{h?|peDecN?20gQZQoL zOZYnuDp(^&4L3_b*AS!pN51Rzurk-_M){R_?^^aYh!}ZOfy=McVW8sXlY)DMi4%6e z4E+Nn4fDr{o6+M8*7!tz0Z*L5hL|hmO%QkdL_?&RfpXl191WHTQ~YjJyy+x!wqjiF zBGstUT!WxFV1Tuk!aU6W)y2xbVnBB&+F6gh1(A-x-)HGV z`CqR8!=0y+`B1D&%BDg&yDjteATq#EU}^0BzKBMvZ)YW^ku!AhW7N;={pXaFz;{H98Vkn6#cOEg3HO zDT!DUt{$yo_+Q39d9C2{O~1$t<99Dbnv!pu#Vg{eB!t^ZLfSdTi!I}sci5oItO4^c z;Xqf*ge3TjkoJ^mmeWSVS#|x!h53iHy)F+|!NqpetI*m$m(ht$R1sVfcx#XLqkUKT zAU>aqChe9Zj$QKTaWXmxt_}cq5BJ-?Yt2awewu(25JQk6k!;!-tq*rdz(rM*n@Km9SiatXgm|io?LJrb%ECZ z&yDyQ7wVl~AZV7fGF#JiUibJkM~u~#;0|jcNqYi;6va%)aB_jkIlGh{PtsJpB~EoM z+Pb7co{Yl|%$K%@s+V3zj;0zz|KyHs!~Al`$+NDO1trX!BOs?#$heX7Dvg(GPMWYE zgk>xmR46bS7IWgICJ?+885Qj^`4p#NP5zwgOJFEq;(E9&UeJA0gUR}aq)v;Yje6J<0u+U9i(CC01QM0qwkj0*%B2Se*MFDMx{pwLZ9UF9s&qReuK zp#NtSR2M$J)oI9E)Y{TAkIZgk9ioUF-Fi$pa|RrqEbP4boNYjb&!X?Jnz366Ll9hM zE;%R}d#I^B>`&vX&>Yw}2MgA%FUt7q3y{2)aMFxV^%`>R`Z3auNY3c zm`u#|jK7wAKP0ZAVi4O2zBoa7=ok?Yku{{Yh3+Z@7hCty6^5ly3!Hx43VA{B#k>LJ zeVxFb)DpAaEWV!VE5!$h;w5v(0W!J{G(m&Mo3{=RA*NT<*=DY16RA({*F2iz3cPA0 zDG=skOklj37baMCze5oVV=gi@E7oaKt-z)_nKQ}LpgmL_S*IIMFi`db@jcAo1#frY zO(&ibla-_GHDE<=7%KbW1LvXy>!J)Qy7!ky;*ogI^;a8W-+Gr?>BA5<1|bL0Xs@77Tf5S`Fd7Xo8k1 zdrfS})okJ4L&nL=ZJ>H%_F~f7!7v+o&lzMqfia?>in!w#Z?c5{$ccv?k&x#|zs@$&|XLaa3^j@|eLoz(czp$_R0#A6c zUDtr95MH%QRB9**7ZUq=uwd~D5iH|U%{~#&A8Ed@qAU`hADC`MK&V}DHkPKLRN%h&?-jpP%*A(jiP}!*YDe()$|SUzeJrsJ{o|DN4LY~{IPSw zGhwoqdUSI&{!CwG>>0(ARh+nnTEK5@N-=YyE*dV5Vzhi{(~tm~@?#_zA1ZLI0s~-0 zMPLHZ(Wu8#V&F;)C6>o*f%4ErIhzU&+#|6!+sCD`=ivRLC-$fIe!BXvH^zBmcSMMO z+4gTY#ztv=QvQiy5p)Q7|EAoxn#fzSw8Jvx9$cOPHwP(%Z_Ha1(T+TSQ@izWKxTNt zM5d1zL7c}GQB{^k5SW`njV^;OE5J8~A}-5P?D01gwVZ0@+%PMq=&AHb>ra8ps^Lsu z@s62YfB087|EWt_0JXC~4POE#6Qs@EAN$9VD;(r;I;PiF%mtFMI$XfbZdD|QM)pC1W8H0wj8YM=Os;G z%+Jrq8MOz2>HuxMXB^rA`FIsy{3FqXKd)pW>R85&G57|zj)}pU9v#6)o;Pyk+Na%# znCgDIUu~bx4VnUtP8sfJHPSzy8`g>j$KBIS_RpDm`_a~{2jx#utw@1As4_Ij)0rg; zh+sHQmOqND${vInYiLpCy#qiNhO5+4L)--fy9pYFn|_8Q&f6-%s*$)lL=Co8Ct44^D-^ILa3AAcUthXR=Emv zBKC71&^jY^<`HBDVrTf|BLJAp1bB)$VsaKrf#6TKHT^a3_y1ny@5Apwuip~)p8Uc& zokKbOk2bw~3cnIRZ9j6m2d0#4MXV-lwGsTSwj>Hcb=@i_k+~g55S3&=eT@YbhPFg7K)B(*R6ftep-9y5}1nhe^dKx7#{Z7TNK}T z@$Y)n{jdxr_3(aJx(6)*i+*nYym#+;s=o|je);tKY|!qptp}OcM+FVNvX$)-l7HMN z+5j6B$sT;BRJ>FObqfZ1?ReRt-lvDA!?!Y}Lk>1%r0Cbr&u4tVH_P*7tk3D)5+$S44uJ=w5^E}aT%oKn>)f>K2a%ZyLb zga*{>wO4JZ)cfpSZgGJ{Rd`PEYhwSo93C@oGx9F{5o66;}GvA+j@9d_D^c;{9I+VFk?`m9zmhI~rLnrPnywubsd_>8gPNX4j0L zwRu!%u#yn{GQ`Sg;-$)U2p+-S0kh1&H6717^nnqNhXhdBzPP{Fy`b>$5C7mH8Fmmi z6bKO(mX>0p7oOx7wrWQO8cdgBxIYJJ3Fls-Id4^GZ{61)-Z$I@wxS(F-yC&(4cb2; z+M|D-68hA3cN?~U_U;To;t2Oq!1lc9GgvB^;mVyN@~t!dn_c!e*1z;THG8(s#2rS& zpIoq4!?F9ZWJB=FHOo6$Sg2%cq;#vRe)Q}1te6=d9=_$1Ve?^MW=~|ICW+v_$KOLz zBv`}zkE)qAd5<*MaVYx@v>nd<0G};B@GF4c;Ov(suMxg1-+(a!^{_2QIN0o<&YxB^ z=Z~Wqj*p|Co0}c(Tv!$|U5MfbIY0al=xOa`P6gOL;IDSR z_;|E1FD>nCtS+H>>7Q!q%bji5xthOv|{n|&1M4_P$t6~xjw%>3i-a~u_6?Ei4wGfj-;Db1agd~JyGenoJ3iglCcO24X{37vqPMGn%mA~|LQR->T zSUTt3K<)@$OVoYkJ;c0xzZoqXayQt&X#@Y%{VX0Ex$=5Z&h6NDxc>4!*eratKTEw` z^R#tewdxv3{~YIU-MNYPl`}V(;dNi6pN#NP6&a}y9PSC$9fIKy`7Kt~F0ec3O3){z zHcO7utAY{19GN6*GID8@4^JDanF$KfCI&55Qg$)O=MdARa0g|U2+y`Pxoq}ZKrGsn zI&g@s4?T%j)a6ivXDIGjMkTId-oc-==2^x_kt>K%c8*UVV*e^=ww!I~VM(uYK3xwW zzHwd1NcacON$ac5?^YE@4AJYa%LyFyAF56odx&!Kg$3!i=r;uam!GeQ53GzT2z{qS zGnPI)qzWlU7uM*=8e<>wRkJ}3wv;a%i7Fkxt#B7U9w?Zd^J8UJ0t+&v@`|j7apr_s zUDYh?z-l=iWEc;2XKas~Bui02}o$XOf+%Um!NE-6N%e-LNr zc|@iS1|x@ygEt*|g|2Gf8?|pLsAWp@dNWiaOlZ+lkT(Z4J5Y33I^DsQ`}k&_7}K*0 zHf?8kLMcRL7=8(&khbL~sE4-eV+{>rt-y!!u5y}bg>$*u#fup*_bksWb!al>*ZWXfc5Nz5R9NrM2AWX;W`K{0pDE-bI0RKIfuBxBJ4i zO6?!%T1@Gy2am5Y%?LAPud1&1Dt@Id6Lo{?hl#$RJ(L@Fh3ZE!o4FIZj$Ds*iMiuj zvls-nYdgtdR?3%p?$)xd3?nPHC4|{L9L^SRsc(lZ(72c(DYgqIoXywzX(fj8bqe#-6hyQdaTninhQUX zaQ=Gf`pc|u-uln1(#o#<&tvs^b|&Wr>BWt#CoUelZnW%XGGnv+;tE8&7#t#;hMcMg`5|97_#ame={t>TJh3y&xqlVQ~o1**3_s zbWiPK>I-x*NmdxhiQ|)l}9QA58pJ<5^Z3 zbXlt&B4^CzP`vF)eHZR1H`C+|Q`PEyUf>#EYUnD@Uq^#bZLF>?!0fX#U|4-7f%&zH z)!R7k_^d>FY{YS~_Px2%+&}{1%o5U+o;`^jMG(AkASrRX>Cquq8OEI~t`ZAUu)v81 zHCUDF*uD^iPs2uV&UO7N5(fyl3j<*Ph*Jb{g=}8blsJ-Q&o`L0(>EO!;_q@;C?*mH z9a{~K^*xw~C9IN#0FgT?R92zrBNiR;h1yo5QAyQBI=c&s4DNVn!Qmp9R* zIqODTytkrP6SZb<#>ghY%Tz@g-F_dp_Yuh|=_vhLhJ|7K>2C{C@s^k654@L-SK?2)FZC`R5fUPp z1XOkg7vQBr)}%4;6xg^56`l=G-Y|D&Na~!7us`@tpSp82B|HP`nXpF zXJjcbh`MV%k78-s#JQB~09wA=>eDBYHiWvWX{haYT$c(U%p13SA^CS1)AN(jp zbuL-H2<^X6XQ0!2)t=LkH+p&?TuDBTfnOJJk@fvB+h}(Z9@mz>pQ%Drh%uWT9mg|7 zWS(q1&zG6d8$Xg($dM5^7!$3VB1oRijL?^s>a!71;$$Uq&AbkyKx5aV8}z>R(s8_3 z@a&3)Vae<$w-iMTHQc)__L8TOKI4N)(}Zbksv$pBJT5gxVh?gez(ZHTcrH)m=|vck zGW{~psZZGznLM3|lEzmnq9~#}^f~R`n|ZwF^M3FC-i>9yd9+E0u;pKW)@|?C@3(P3 z@3-_2d$}VZWEFms{pWk3bgAu;pXNY}E&;9MD*(6hQo!f=lg49yq~|2bq$9c|J$s9f z#kXs!`)Te>_ighf-vi_T>~o~h`=a@baO21*ng5?s(`{l@MQP(9(KV{LqAjCXD|2OA z8Ha)hEo5?#R@uRVR(5Pnt4Z@xYXPu$TclKKI^1$*ve@glbVAd3#KAa!s@hB_-}evW zd7{EEcHsxXUWvZrd0|1zj1AyV`f3t!>|3$@{V@FJ)j(dW*mbqs-h`$@++4~|d`o0e6MyPq4Q-C8^wjmjX z`#_~(f-7(I7d6fb^o4CL)CIh{r7lK4@EV9j)S9F6 z=*+#_yNm_&n|v*lnMov&%Tb8UChrK!`Wh+MPX4P2O>@+Y+bf=e_=(kf3jF z2Cf*||7aCdgDMnNQNawM4|IbdFa)4&&cGtZ%$YLgj^K`e&LFs%LzCF5aIHv0cg+HE z){r(s5i>*g5K5LT!Q5b*%s78nm}y$W8E1sN$wUwRTNSTtg$Lb1px}ROW4hbWt-Vut zEchdTe?OjAB@W_5WdJ*tUaLHA3>Ci>9rzQD_!RL>TA(|TFwyCKIiB%jWUn}dGxlpF zHAX@icepQJC3Ht3%F^%rf|XOrwJ2$3%j3{?ya%ldOmxGZmo`mt8rQUI8mxUH9YPA+ zELfwmA`~+YX4in*p~^S0EN=?zD_-`xvlsYqSuoCJ0)7v;x4Yq(|E@+>&8f3kxEv}I3tH_gu(62 z5J)gN+a&|MXXAiQEzPQ|_vk#7C=kfX6sdlkB@f6vnZmkBgk^6c8Smt#?9g{=1*xv% z55^Cjb-(@>f7#?1Nk+*aBB?y*)n zH&D~_c+x!5182ao!WZpJpLXRrQ#`hy{?zeSW z-YmM^p!Ey%Hap6TPOb9}N7zF|*MXP7HmPro^~MK`Y0?7qAU@730F*r%>PasOk;A}n zPCAPlx5KN9Io3)f88ICK=hl`F%mD5=p*zmTIgO+n>fvX@uxwf`E^FxD^#uOelWA~} ztR_SVE6<#SN9n2KC_s6kCr{3uuZbtNCr$BBz=oe+{l2RkrTlrK5RF% zd#_Yt)T%-GI;BIJ?XXX^P~nv&2$ecD*$o_)1TOhz2-DOxYlWFkNq9`Yc{t2;S)}YsZlRdg~8> zrz{b=uf)EmyZHx)L9ftigHgDftJ7*hfqw4 zKoEb2>4yHUAPb(Fswfl`pO-;b7~k1h*F~hZ@3TA{RH<@QXeGg8Y8a!M3R(_e_c0$GGz7kGc55bCp7OUeyE0b2 z6=J1th|6X7F-SGeKIW%-_(A%l@rn-3Ythffge?@He^B}o=4hB)#@B`C!FzLv{vC56 z3)WQ&C{}6S_|7yUk*c{putPxGSeZ?_RnNn0{8M)Zjp12c`?pYC!_U_j^HgLR9Nfi4 zUMv@JQavOwuJh8rd;Y{Y{b>4ND$Rvty1aBoYYO(z83$h=W#+)<`t_1UR|sgb529!k zT)s}o3Zvo;#zx-AiaKBI02zPuNn$0Z+AquX#Z&*Mnn9+C;pNTEHhF`QXSc1S>kUy^ zT3EBP0A&I{-1JFP{%w|zSF}@Xv6+BP$~Z~wOzS{#m?}0xyH0D^tSt|T8s(Boz5o5n z|8LIg@lAx@h!{-N=N(BXb4kYiy>G7fS)kJcTu-S^==}oc=JP4$S9bw`!gsqFmC}xP zcD}W4`k>z_?ey_1W~uY;A|aHEm1QP|VBfJ%jO1y2-zQ%?mWppzIriHc1I>6u zx+^hI9D4${($(5*%Vt-GX4O5|O`#N3{Uf)w&OGKd6JOS>uwEm9lCFlvkD1b3Ajk3a zP?&Rwdp(IWxr8;WlqD=L!28jBp+nJAX3`16NY6-aiyM{f)BNuEi9m2PSDnrHVjH75oTjd{sZK;tr zmvUEKWN{+l51$)qn@cC*S{+uCLTAY;ltvIWHJ*08RPaLAjF7kffs);>S*VE0Kl&L& zgQ`^y=%mc2nXg!eq)^HK&O-WO*{fOuOUqw|9D~DH`VficXTM@D6vH^wYoW!aEt%g^ z8Yi_^sPwzAG>$?p+&_+f@S%PWR&**}(2 z>W1V#8HH?WYM;*Qlr=l0lGk1;j!m=mwZ;Dljpt8te%*I@Uv_qO3#xAKdoNxAub{AA zmL(r0!-H$p|9Q5V49b|0NDsCBn4PAW0xUEbx4% z%E<)D+j`Lfm7J6AF*rH@xfD9`QIJ02W{+tXmnWGvG$*@o4^oAf0s~N$;$cAe_xSr( zwNQ_MD?Z?u=1sb)m7%Y(p{JqYtCuJ3*2C1aLJ{#R)u$4uGB-5|IK%buK7y$IG|_!1 zR~(RZYXC{`K(T4L!f9s3veh-I3-tPti-zN7XqBNQVFUV_RWgk7kF}uKJKCZ?7Ku`e zK(Y1`jy6x!4g4`WOK^mjqcke7=NbOw3T)Ig&4+I6if3>9JK`FB!* zn5(QnbFRW3i^Yt_*~yhKo-4dkx&dC-{VJLpIlFgfwecR8i`HUROw+4iq9W!&k`#LO zNnF{kPi#Ti_^w^f)*mpDuAJ}O*xGHPT+VUsKndyOtlU%kbB|M8dBa<=lkiGwX`-H& zKnP_9EPPy8WBdN=Wev7gCmQo2)1-$|6uxwg~M{8ZOeA@({*PE4GAn+Q~i`THs#J$Ih-DITX1~y z0`^ip*I->}p&1X1e@zCZ# zW@96o^)*0stcmIp^QAhd0jd^lri%SdRH4`?+_#ez`R+ra5U3kl&tR88qb3Tg5Qk*U zZ8v)wMNw2YoYihIhIGc&?$?ZJx4g{z)A9M zfdZVCU8RiSCw|Ts_%Xil^CzD8xqenZrJw!zjnDHKKg46tKd=9_{;dAjuL&)*a*ql+ zCQM3;8}5c9>8p6#^J|ak6&^#lwOy}JT|s3L1i`q8go_n35@nIG&Q()FGZU&{;hQK) zV8zHUz%+C-WiJA!=k$S@KEx}`j`~KJZDzNVX4{!W$q%rWsr7Oj=AxCFD~oC&)aI;; zvAL2@2&p91_oBZlE}AV*MnFhSQ==$9_K<3DtB4gD6oQvH^EX8L;!}hS7iHMQO$qXJ zV_3&)3k_na8d@2g!^d#ZNWf_|wQ+KWhhT6fWZnLtdh}E@U0=&Nt_AF%8ssd9m-XCH z^+8F&L%s}$+c%8WaqKD!ny!28l}8q%JI0evC$$a;29I3_R~8@9zc1cjxnRKhcy={g z6nPk=AzPFJBgYM^0;#}BBjzSllWVALs6>?nW~JaDZP;PMej*O)C&EB>)RwioH7+hO zZd7ba`O*EKJcgT?;1}nm1EH_g%riF5dd3zWox|yEuw!S^YDO! z+u9vGap{+5maubwn#0oE`m6dg4tf9P-CQmwaQ6H6hkU_>zj@4r~bxPIKosF;-wXMxX5og zbWkerT0_-E(IsBrWf#A`&NirFURF;PaZ&aZD>#tb*U%JeC_(@XpdpC~IgN^Hb)m)4 zLfhMb1refNVd0`^`9xNq<#m-|Pf`@tW|zhRhjkv9u)U~N$)Fx_J5hfvXj^t-6@`YD z5JIHBXmo}}Ly&2Hhe+PG$f`Z7T1D|(1|5bN>EdADdD65=;6*@%q6c+NR2YqP(8^xF zX12&v_vT4o{mZt z72ZDl5KxYSidJ%pCFQV{oTBAJ_#2&XU>{%jUpkQ38@zLs0cWZ_EcM5Pb!ana^>XUB z;-%bpuM3&7Z7stAO^a&UxC@|*4qv;0zBU@w zn79yFdzUeUQ`&?Kg*>GRp{5RMgie|Oc|=8$n8}E58F}d_j!Lyv)=~x~S%DRE!`RWu3KB{IR*TnyJSXW_JKyJqDOC z`jwRNH6rJUG?7H%tDmRrDrzQ1{X6M0hPx?AhB8P+))?Mnq1wzm+tAClG~fG)Uj>L? z!EAXcxtykr39z+PSs^CdLSo05viWs%uy<&B5ik3SXF`41%{%dW zG_lk_x@8YuZZ9qkjLq%Et97`fG>b!Yy|XE*BWF{Pf?32q5%hXy&l3Hsxn1wcwC_k46rHQ z^cFatDrz(H`0moS!jpFt)g3Nv5svmK=pWvPo03ugVzHO2oi_P92zR$NVa=i$LpNad zXf`X!SYL&nQL*8H*wVn*`XDK!Y9pIO>^|C08UP0Tgq1Q}+9bDwb~TLf zQd7%BIpZWL@~#_;ntc$k)+n~pm5bEUJC30^a~n0K(Ub%>HR$SX75!7<6(Lo31Tg44q9;1>Oy{)u~xmgWQ_E1S4;e*VshRkps`d6jLxWAo-a{v2>rxMKVEE4IIb zf*03%UNU;9hWGqJb>o_z_SbGhNG0pKZ24s$iR%NNE8dD*Zprd_8jJHu^%KdS9xc? ze{sjaIX18(Z_2M2%o=~sQ;-oi7&gHWo(BP~zww5iU+h1hPi@_rDn}h@6Nhzi1LRl< z+@Y$xKSd8EbaW|&Xgi9&XdG;S_9!L8VdyAQ2?Ei>F)otks9?U(7ssJ}J#bf?8W+T) za95wmgf@Ty3)-zYan}khSPyXV3DPlQ`cfMgy#_Ek8*3MQd(CTVkcZW11VBj}@0L4Q zGb0VW?BFTKQ;mV*F7c2@ECy?@>}9$hHrPvQ4L0q?()>Fs=5NdlJE73d9MKN2DP{S? z&hUfH%=AHKw0gljdf0qv9z6-nBs1pGTQQIJazxLVQQxnCtCEZM1Iuogr-IS(*`&IusBTsYIoby_hhE-IIXv7n1?w#%veLE^Bt&lTyht#d z!`+2S+a%AzUGsBzIG=;N=7e~tE8^CG+T1+As{=b@CFDXN=0{h39fFH#?G0cIdtyCV zkyE^(M@UG7X3;y+hG_+%Rz5DR>u^!Ao{4YUD*JJY+NV>DV@D}96KNQj+(YyIli);V9-ul}3?VANx=O$ZMQ=L(su+!4rjnbx(v*wE70QsKM6?BW)Xzjv4X z7mbO6x!Q%jikrj+C8WcGlG}=%f<&4(sbw5fkj-R<)=^vHI-$Ya6{C>ECHzxfQe53i zsTFlAw*J$kAXuQ03g3vx&lcs5l*eN18%=tRv6&Gi4M|2%s8v?#8IC5TiR@6p9ZW_7 zccrqy@it4|{K0B5O3of>-QK5Cxn2m_lzZb-jF|{O-@9wAD3Jau?v=#7CgG1+yBEt_ zWKQY0crz*H=C%ICWtrYY+Bf#kyvyiZ;wr5ig?2_w(Z*Uc)MkgOj6t2HMkcUWKm|xC z7*fP2B8(jp9SbPxMIB5M)Vd_yOtqL=mSNYXSm)#x(v2{6QSA_!h$0Pq{U*1VuVBK0 z&2-4uPWeXRXat=H+#;t@+_&%~`S4pf%6;6To!8W-J49YlIN4BV%5z&yqdWXafa5M3 zcE9X=?fl&BnCK&6%`$eAJ7MLCos0eF?$aNrNFWNdbp__>2K4CDQ(ocsnIAZxm-q=2 zcA7JpumsRl+Mk_m*q?nX?tFK_{_NTH+p%vZz|K*R+fk9lvyJ?-S=@LXyJd}m#$;?A zn=6*`4RhtLTJ9oliJ@h&U1dwN*hf9b#XjnTa-lKaKmn2qw^3i)3HrTKyU2>zxU2BR zHiBN4#zl}?E<^jA5!Nm#RqwF_bVbtEcDgSw5Vwq5dT+?vs@RjekvgRxXWuvaq}L@$ z1ywN5I7KbVa7|oBq?#-u2TBUPeYB{#z;Ij2jb`kah>5vVxgzdje-JIO;?mAN2kjzv zhf+fKh#ox)#ii{FGw^}fY;HR>h((1iMKunXm52KdE_prFzD*m=P-7P@cT^TVE!90T z)@biM4Cnjyg9*2N5{|aaA&wGJ7~G!K22(hAU$$~x$iH-1h`r28?O_9%bY-_#?yKl~ zndb1>hg&ZO4q>rGT9%hvB6P6IDh`ZAv<{u`wS-owb~Z9B`(Ivm%fk`lY}YE=RdL4g z%vf7u|Bil^GS=5t_9IpZAjaJ#jQ5Lv6#eCX6axvg+|nhJZHuE7Zyl!SlEQ!8h}!OgdAw+(3-Wk0Hyc?f>Nk@--jDHFL}HjF=CJXf*?v{ zUoLvpp=S_SID-0p_M<@k%A0_4P zQBVdlbe?Lt!%6h_Ynv`r$m4&E?ET~9P^+b?vMb23dL??1wCQV-=t0spUyJ_ig!&Px zX}v+gc;*)B<3Fbu9rr+U-b&JOn}Y75gP`^A1UAxn@PM7HfrBora#&O1L5bjSU)t0sBzJe2ld+FO3MSeW0wi;k$(&(g6g zuK5TKK3Y7>otIs8-HmXf-GbZC+hq5P>7kL)3F_~o&0;l@X>1QFqKD<2BaD5-{^gcU>o#@E!RH_3=bc;*A2cx`G3>;!_WRCq&a#Yj zdTyBp;x%y#tB^UP230N@IS>9M?xt6-6jIco7wa=>Ub6u zWO$#z-Fo_Q6Nn^;m+1~rxW>jOaw0-Y(jQAcXb)?_N-n7RXyzEk6>ZQ!*cc_rWd&e; z-3KxfU<0)N*9t#uHBIhivEoUpNUXHkEg|d#x9FnmgHrL{l|+P{c*iSw^NDl!=U<^G zo>;|aq8yPNR7ZBd>4d~ioM5Y1bz>{reK9r$8Z36-+s5`_7x*yZqJ=6yQq-fxQ!IGK zkQaM=z>CLe-*AxNQ$^gNZ%0v`Rn#3W)Wtxrt-}qT)SU_z;iFx+x3nitCvA%}4y$u= zhLE+XSeV|jm5#*TelD*<=803$_TifB1Qs6(Qwoc=35|jv_R~7k+LqkDgLwQH@_of9 z_Sl`(c1*$hE^2gCQJhom+HwK!uhas7`)Af`Yr^_i!&tZ(vpwER0E8c|zqkkxHN2%- z9iwoHGmr%N73r5rg8QWPS@-KqAmII?E_IppQufy&b``so-N!z`PO<0N*8rEVvj55c zg|W0e&ZA=gHZ-7v<~A7zQ$sFh) zSF@YIMt;SpD@zw~FA}g2b|ut1lj^pjdaI(|gb}6dDgUhwILF<@IY1-m6&@uK1*CC>(9u1>9$(_u7w$J<_!nCYmB=;u026f&>^0 z2F6Woyp=mS?o{BkgyR;Ax7DJIThggmTypYo96z&K3{xW9VZr__fmW`FHyaG;7&N?? z?d4IMzTj}y_**Q#`Td_>e2o9yql=%qWV)`d#aC17_`<;chky55-+lMhZ++q9{(jCn z$8Nm(OZjiy|D~&M9P9j8^w?cDp1$MsjdvZ5evGXSEe^pC-y~UWvZXpTo~pLUHml^E z**DWK+PXA|A*R^7OM%?YpWP;z?9E$8y1GWTHCCBS+dg~Cc>S4o9{-1DKfY7(dX=3Y zfA$}4*?aQPc-Uh$d&1*~PVW8m9np4+t)|9iX^-A<_td4APE8&;^4In{$>s@ncZWST zsm>nHW`DcU<7xE#sqwkdKMs5O$?RJ8FnLz{fbS#zr$Mn4ai!}>+`Eg|g$=Xcb^*1x zuqX$u#p^LT=muo9g+N8yCFiQl7N@Uk;LzbquO!)ktxYuFrBsrP_WZOvSIe2Z+0hdn zo}5`cbPVx&rMxscKEHK+pI4t(V&+IV%1kb1Nn7Jmg2#D+cMMKY-c>8dmJyjaAUV@v zP(sFUkb+fEg3aH;qfAao9l-j`R_Qc%^LzLyelK^QKCS<|e)7Ao>L>Mom-t>@vvB=% zr_JsjsB=Y49&bauEnu;>>|9I-Y9=SHzGcr$`_+l8$*GJSAH3t?lXngt8|$m8YOvKh zu4(C?+kVBt^d4TrcR%(+eOdiOeffuvN&WD5u!);!3-?|8JE!6AOI!7~^uhgKs;lnq zbIb8fRgT7bNs_#c4GxDgU~*QEd2NBdxvPKc=W0N~#<5pN-;~blHP-j7_EpDoV zmF5pvvF0{_Tlm6<=2$mf1gY0*8_f7R7~$bwf{*+3H}z9*@I8YEuh>2}&~lBV)>dzF zCWkK{yyKBa?ijpmEN(z~=)i+_ZyT2bgFAP&NVdk-M1#4?Z*zi9)evZ@=B8>_Q?P$< zuIml`)HhB1ReCg`_|+fjNAw@H@l7X&ta4;v+hvdKdg@m1$j2wRWS0Be5w`99E`;rU zL;jEc?yWCeJJV|K+F4y^8c<9%t@S?1TIF|fi?4A2{;=QIs?KN_J*EAssT{Y@RRx@EQ`B-my)1uGN055DB@4!7?3 zL~m;gdJwlxwCQi_kLv#%%+2BX{w={f7EWI~nthr-&5am`gj=3jUikj^U$D@|XZKql zu+L4{abL+7fBv%x~1 zkhkqqpSf+Z3i(%V$Y`R(r9y{ zeB-TmqwVjET&(X_*6rBOGh!~SkF9FB<&GmfDdy5d=Rjb~>3k6wx6nb$}w55s{s??GtTZYS8CDsn9^|%=ZTL?c}GioP(w$W(n z#?3g4F#PP#r~~*3c9w#lFo@RR=Qvoz_z4!)I{XBcXa+wwXVfkDxjm!qz|UP7bvJ(Q z#X=@^7B_FF$lO8rVRy-(WNAlF!q481-`tr|4?EQp8TCX2H}1(G((++SH^4fp`zRt3 z%Vx&p`6IIQ@wPmx$wse5zsBw3^{=f$;B1!VX9O1s10(+*grQOv-$k zZ>{`~bJoKp6s^LH4A`Up$&gZLQ#6R{)64f*`V(&IbX)i{c)*AaQLY~!+r==}L&7>E zBhO4hJWnP&nCkNelhNA9uWmt{x`M)zsv2JRS3>Ys#laAXfLx9eWg@dhW^Oye?Jgz> zCfV#P8k)2EIWkpDIe30*n>}oweva~zEXnyaoqJu_ZTePzsB)h01>gqyxntZN!~HS5oG$OV{qUS>K|-so0oAPnImIkhSkUFA9If1Q z8&3>|>M|y<#Yg9*wMJleL0IJeG9LJPUJw{FT9$EqEpkX<{dy@p?O3iNBn#@}%chiC zoSK!h`XC^))dI_*SO{W)y3UTfCP%5;OMPd-fPMoN!c*_XDUTcz09g`$y8hDqz9ieT zkNWK@cGE1I)xQP5MThT`=5uTo`effe?5o+?Z0DdlAk-}R&Bau8&!fp%v?|U#llAm$ z%%V65cYqFlw{T22DcsL4EIwypL>oB-Qq_^sRv!jgQ4&eXllxH4mS$4XsoPQPxgy$z z<54W!)22_rzAvKn?Sy@QM(f{&iCz(H>>i3LS{< zQ&B|E(zc&QUfhT_gt#K%j5fSirgar0We3&m8*TqUUfUP4R>j`o@{uc;0LzLqS{3FJ zN>qw2QF(#XZf{i!o#>iwqI_R4C{r6YZNne19^K!m^%QJazY)&cA*+*4wGU`EKfQ(kE zF-p)%jTOU;9$7s$j;^isV6x8b+_h&fssqPC8!i}~Si9!X;d_o4CaQ=K-WO+Y+keN= zV>GH-!Z1;61a)UxI;yzzG`!I_`wT9Twpo0zER1nO-lu5^#Buig{D@|5q<7EAf4pzAop|QKh4npy|QPTyPeVdjIQbH z0{@&(@=u+g#Q2A999=NZT1YOv>INB5CIp1X7UzZs(almG0^;;wysU&`97VYi8eBR; zF@djwa`;L)|96R#1YrabO^k6-ho~eV`BGw00fn6dRosG*YduWf_^C!<3MlK zEJy3Uq)QN=BaJ*Fu_=Y>QuCYgnqtB-fHVdT)8eyKcPhB@p1b%4PVFp4&?aG$ShWYj1{oHs|1H8@Ur6Wb%| z4rJlR;M6Yi6mhs1M9!y7Z{`HaltuUD4PDBzG;3Z*>!oOGBpmM_Mu||CxfE;fYEBGN z>NE7hYWRM7e9EzT`z|~(mbtWX?RxvxTW+O2r`F(TH5hn|&Os;;E2gK31(Xr6g-^g8 z%O{{W6brfuKLTb)l=HDQV00i)ZCd{NxbO0NQ%5)yqmnKUy&$G~1nojfSm5%a!jm3E}D6ApGsN01X7e_)rQecojio2v)4`U`vEhP)2b;^k=AQ@^C{uoGiQD2SE3U4s4ESn_ z%UIDC?~fX6rsAsJ#>le+hd*q;?Mzp9_xkC_{cG1pSjGSLpNm>Y#w$DzY&KWdwwq-~ zIXmo*Hnsu$Rh_zR-o! z$B$5s62kG9V!{V2b~p`aR)R#d`BtjErz|k6pg`<0v3owLKb=nA6u*F%*CV8&S;69Z zQ!@R|&G8Br>-?+&VSWr{5kEXK%5n^3)sbP(NeXI6il<7b?7gH4mVC^f(}LB6XxZeF)WUqqEi>lsU_A`Q{#(I1-U9mDJ^^9ZgeVHs ztr_(e3d+@!Hx+()yAywJH~;-Rsn<+lr9Vn`LbmK1+AK z@ht9@o+W5xSemkQlR?fmxoC~#o;eh}=`&|j>G>XJ_|bk2$;Q}c$;CWZJaOJAsJTg7 z=}Ta4%=wxA*Y00RL1vuKux&-k;I^hga}E=e(`y2*X|R-)!pe^B02tmWZz)0AA=7dN zR?DDe6CtLjIO?gy^dw}x)+IPO45$#b3pL1Q!e!_pRiK=gpjFCh48MXdD_%kgMILM< zNk(#TIodca=tpBgRBj=7BIfwHJPrMHAnCtq&q1Y6ontIk{dZAii8qQ#)F^dLVrvzk8lfqpU?|LFw7VYzg&Zcr6O>NSFV7wC+)~g= z@c&<;{@_+8vXQqFf#G^Nn}rS&U@P}cgW8WUs801R`~#aPHWiGjci}N2it-bX-BJAZ zQT(xps)|^uXEN#;r*MvwaDDZ6uY9+<{_rr4zUhan zTb}#jb1l^mPapCTf$_zqgZ6D2tn3{)G!?tDW2}$!f2Y%jUQm=54y8v=*6F)(kzn7c zJ2|Qg`{A7_$2Y*>ec`r^8+2p5IU zniYjkGoJJZWeq~?jPQKeRR}ba&Fveux28rY^bzD2$|IM@#oP4VU46Vo%{wKvt?47Pkj@Qfr|Dm^x3F? zj2;{H%TdgEdI~);=x-Y!+v;3I{dPtRd{LI?I=_7mxGfA$qk#(mQpeo zd~4RtfZ&toLdvQFBrnGC-AHKZK}yEH9WK~^@UCMg@q&Gs3*!e4oj8pbJihOCxZsOX z^-CG`5%|ifJr9HM8{dMz^Pe)>i!aHt_9`3*)gvRXl3#g_^WV6m9)GBDkQr83 zPdr*@wV8t6V51v{v7gcRU?D`Ur7Y@$ZFf&RSO=$AZNWxU)EmNUZJ2VJm%wN0k^RC@tYxW~g6S2P;loHVb4@+hxQX9}8q?V1Ggb6zZ}?HzSssf@O3H?(gHH2nItAQk-0og?Fj zhNNzk=2}*dweci_p@M$qMTpi?R%4A4WNSDYt6_^suI7(fo^lAT4jt^b zR$!J^%{=|ezdm~B8(&S{y1px9vnk0|#jygot9befo4ImDS9efG4*Tmo_&vd1JGt*lxG?D`zPtX%6O?XJaH3CHQdaB&m zO6j)}<$xFRbdK@MMj{rHVomXMj?L2ukqoM=IwdLgLT6}TZRxsgJIFV)aXqwoWqbG7 zl)$y$xybk=0n)E^X?t(uC6e3r7|^*<8C%@8v>zJ7@sI0=d{Dl{erSzM)F>a{83EC30CUd`(cv9wf~{TR&L>+#;KY>y>$meM%M~oFZqACwOx13-O5CTr~B5=Lv<_em%)QP;-s>7w(i%{;MyqWWO z%gVD7W=MqQ_QUus5$ifwxF;t(t~$@8(y22}c2y4;o9C#07Nx|k&reT3f9rMQ^QuuX z%W>_{hm7noLIln8+!K7H39!Q90ZJj*4i zPUP-=z0_P2YFsmlDPU?)nk%xc9y7+~$oL}I-C{M6fps^?@+*Wm?455#)H@-E)-=}F zHwJdaSl^Ma8fbUMeh%_3IXe1{cfOG%i-rF$UZjsC*cGOUx_ie^ERWq=xA01zfpZrM zWe7}pJJFm3_Fjv9njuU`XD9U+)!V5p5>`U5 za%t{Hr01NFT&dM-)=>bYQKlYiLBI#qO+7a3(kiPG2+x1XR#e(+?nmrMb)8F#53JZ* zXtU;lMfz@n6&YKMBZJkO*HwvoZn+UGa)oY+Wkbxd?sYeIR!!A%q{x+eNMN6*(n$Rb zdn7hBZPb}24eW?o!UvcUE*ba{Q&9}GBxH=Nv`~}A(+2Hfk&R$IR_K|VfJ9oD1Z#lBE9KSPNgd`^CgmoQkn5ekv1R?h zVl0_7Snugem|P~QqAS3o|2Iyg`M&`3e|b+(dXrdMk=Sr{dj5TW!alsP-tbz^8z4eRjS+9TYUvKCY(+WcDz74}iAKacWI`X&r zp%wZe+st_w1r|Yqm2o#A=y`JMAP+^@FvkuRN(2NmP{_=DU9?%D?Obk81&2}AEMfxJ z#W}B^7DGYvAmlQF2qVS-=-qaaN_Yrf&2LnK1SK<7aw1FEz@2H<(xHLaaE#D;L1tZp?2hKdtU#7?pLRJaVI% z%FSGvOx~&-?KU4%an7x%5QM41joDy^sI&=(PDGd91yfl?J0@kTifhhY)rvf3YNK?a zq*SUeCJoA^h7g*-4eeSg5lp?>3MPSI6)S>`W&K!4r&b}?3YAhsOF+;-lSTvaaDypq z4c65+ij~aY6O@r8%wr4p#3+Y8VDr0f*4E0^X#24@ndKRcJfffry-*{_&8PacoJGT zFIAMFv!Z;QR%B45L(*N01K}}U2d2LxKgkWXeDBMG_B8hN!YDqI)8%4{ZfH&$UYE+KYU(X#j?Wh2edC*=DOQBR| zA)T@|#7o83Nf(78PiNO8HfFyo#hRP2kqac%o@>B>-(n?pI&3{5?zaj!KyTW1(lTrs z4o$-wllA;=;g9F^E3Z*Q{)!zLd;YLKuK$lqH)%&6CC!Jbna7;P)<9*pY15|CxAnii zNWH^KEi6 za@DZA^{*|#(X4?PcB_G6z~PEQG>Kw7*4>u}Mub5=A90Sg^CWyMHEpPcVNjOP+I?6h zL}>KoegX_YP$oBzs4nf!P?iP8M@U1_zC5HMD>NyOWn1$4EV(d`p>622WWebJ(^=v5 zbyT-^Er}(5NKu>@tzHHtv=2|gSQVB8i;J~&OV>oqk$)|_Ci1fBVMLQl4HJ~waKPT0 zT_h*+7Rf-aI04HPsEis{%yC{$#{uSyg>x;m*y;fBb0Xa)g7a5<(97mZ)%OmQiU})e zs>+K@3Tl&-DzdcGY#Sa$*F3DLOe!mPHg$cXRn^}U$07&RO`%<_Hmf%TM}`>@7l*OB z!Ro~{^p#dw8Rr+P0a1M3N)E^y0^`ufBN zE3Ua;jBXJX^VbNfb`hnquffy5a(qO|&bO>$pHB+)m#Q23AkX!z8)PA3`> zqb%O5iC{==Ls+6jUs)hgd z?!rIlU%C9&c~!)8(^gFY8y%FTA_(-#P=GN@u6&`r6;K zW9%%P@hdp>gNI&aW#`W?8S5B99Trp?>wXG;atsStK$~t{UAy2}6ImD_bSA}e=@q!b zb|J5uw*>}lLIRD@hukabpzHOra1(|${oI)}S7d1{Ck_#pRzb>g+>gp~bpmVA=j?)n zNR{fa>vd-!(%ID=L$X>}wJ#R1y7rFXiUZzV%WVF5EsG8_e>^E}^3^^t@&3^6>?@X|sdq6sYa6$rg_^1mvf-ZaG zG3EpHAkISJpv@hOFk4Q8jr+sFcr;w!%>`OFUTX8fMZUZvHJFwG_epM=_6_>H-czT% zUf+;+`pSOT_4#^x;cdvfzlA+Fv59RQV_$o#Z6Volk0{vw{lm(KFFpM5rTsts2_`fr zSFwBX^tM6Y9in)Lcj%|SlP7&`1HS1^o2I=(Ui01Wzq1i;rua^O|FzTpA-~VJkqth3 zr_bjf_VsSwlP3Q zls4bNuYCES@9>cSzVE$uAMS5_=l#Eh+k@0{8ug)NRE@x+=9^*t!uWT4RGo%Fvn8T! z#pJVT1a|5xr;2O;Fbntiuv*$*!k`)bFjK6J;l@f9XW_Un-V?TqjaI)c%xo5%Ptd&a z#yE&7TZt)D*CB@SAn3JWlRpa6%=!D@uKN95|8?uuzQE_4VMo!0qZ8j_NB_qyU)h*C zD>fZX9#}i!5F;$H@a}=F#*=r6~uu5z`mUOJ0yhH5N z|M!J=ik>@=Ji74r$I@o#Bt;KS9J|mIUig=R*E{`H4pDSe`OQ@Cgtz}NsT|%?Wz;4b zK7$g_*X}1-h}ccVIqarV!o4PR@syP)7_4Rtt@*OROvN&yG1<{WK)A7@OrtFuKLr;w8w>ntSwpSX% zr&vL>S^tnZFp!))Iy|tWxFp%~$20d|>>jZd?7uoBHk|%V|A582qyMS3QyY|YEZM-` zW#>npy8kC%{`#)nf&cl|LwofX^;_9VR@@S^T2Gb4{&buEp_T5ptGDx1s6Lszcj^oO zP=7}-prjR1>fcsU;@|S&>C&mSeM-7esYGmGL!Y$?VPRT0Af#-DrVzp!s#K^1C0(ct zB_I~S5GfGLW>Mo}<^nAUHHnFr!+-_XA`_*5A{&3-k`KP=~_11WIoEv3A z8e~kGat)W)JuR<#!iuN+!S;DfctUuZ{bynZbz@0}w)07rFw)YEEN~I2EQm!GKuP!I`C8mIZlDIE|^qKcZu+C^TFEV~63##L$ z;Mk>(#+Ev{rwPD&q;I%Z6NH8O-?Wx zw6+=i)~TMxFL*iV>5RHtPMtW7KkCFzd#ko*ADu&0<4^M7Jt4ssK27yiM3d*tg7|zH z#$b(`!z(o;U@VS}!&*DK9=7f|lE7H7Vz;fHSbOjN4?O$``r}TYfKfW~_!rQMP^47B z(POw(=t|xF=wrC$kuD8C_|Vzp7oQ-99o7k|$%OV0mP0*|(9A=i(JG_IPt!u}r_>4Z zR8j&(4hD!Iup4p}QTf$Ti~C^WaY*bX6;JuqQn0@Wb^}L5V^+C&UI5G92tLzOF^0}i z^lK)Gx@2AVHKBVQkH>RLXDTF3D+-5u*U^7|ot&mvUz)9&5(p}Y5Ml(wHWbp7{a(Rk zt&U|=a1nxW#ir1kIu!#+oV=!BC05t;w04u~DTKYnyp9EhS3{wD7OSD~6ZcJYENg*Y zgFOrV-Ntz(i%li+ijjFL69HS@A#lvE4w!$vLaG%dTVoW>5qlLN+z8JI>sBQjGJ~$-H`zMGP#r8mc?Vdy(Z<}krlH*Ro9Zxwwi1^xUk~nE z&EZD&rmbSf-e=eDe17Xs8hY2B?7REW*}98AfrER#$`8&wz2&dw#E!R`&wYLT?BVr3 zU&~K@fAr3_KWX|&^ZDlw{@?L<^V@6m|NiJX=#W<|&c;fowO~!(!edHvdyBcI#w3Z) zSe)TXrwxxDam#&{lI`=4iQo1fcG(K6svQpR!ek&|dPaQKdzVzyTv6#+xT9gD)Whpy zVHLM3Hi0VkYeBwlB>f{q_7^AH@KaeEB8Rq+kXj_s;JCI82NE z?%+C%=Gkaqe<;A{XI&P77l3@Iz;2gO?`tJYMZx-@;DA+Q5c(1Gm;RJcOjc#k_yE&p zI!d`=dwBFNquMyaawC=f56W}F(xL(^YMz^*jnyQqCo*#$+Qnotat4=jdg)s-l(AG~ zqtql&u`V`sM*D}5Yfq~R!ojYDHnAEcrN0Jbrl*{M&eTnkimSOi27(UH&*lE}_Nr?{ z$VzlaQv7a^g|eS@jZ$|!jOarkt$G4}m@zOYOPSBxkZVuNV$0^;kqHKj5RCSzSTeC8 zL9f`<-TOhE@kfhyKfxxS*j*g;`%6mp-@PDkvA~kXXWF>@RWaV&*Vnx8^XC5k&x`tk z7@K+vyWlPUK4m)fM-RMN-dj>yTKj%dXulAz(pLtX=qEw`I-3J4cT% z{Ehe>@tnR&tfk`g_u2Q@x90aSZR<910}||l?z_!2hU3t|?QEMs+{3OsObo2S>=OQ2 zNR=2hcD$03W6^VT&9VqF(@@nCsQS$r&16MjaM@rbM$=n#P!)sUi4)LZ>A+|r@}oGl zP6yHxBUy*$cW_SPR!J!`yNb%nt7-zl`i3TA@pR2eo)!ydi&P4o){4ryP-B=VRn-EQ zS_)0nS^`Z}HYJ&73D`pM=f&RA$~bgI9YJqAR>rnwn#|a1)|IV ze>51EvAhX`qr_OZ{2qS@108GIOvE09Xpl%MhkQsY{0_GGI~bXB$i2lubr?~1;MFF| z{v}$(<-fziOZ>9GL$r_y2+v|@HI(#=3`YEs47?nS)LO1vlb~-$v3Aac>j1L&Y66Td zOJ=UZA{1cCED`rs2;5r(>YPcm2kSa09ngXmwfJk1w*|c*U~vpt&>@03IbM&y##GHp zY^BV~D$xN6k5bEi%4(kM8xXCgBGJhrm(G9ZXC0QRqN2hwf0@tYbeK&0*~6bdJ1{gR z{_12Zb@Gm>-ec)vpW7jsT&$&Y^WnQTbv9cooOS-RCmg8udrAsT;+Sq*cuQ1>RV#fR ztO@H7v`h_I6LH}bSqjyCh8nut@{UVq-LJ({xn@RH8s`!G;5m1rjp>0*!a}#Pu#HDQiBqAK^?`GkaQRZ z0bY^$tA zet44DXn-F>sGxxtxHvy8q|#Z%s}>WfoU>7F+PUzZ)VP_4K#qlk3vvW+HDeAML@)RO zp`m6XFO;vxw&jmM#Kc9L3f`NtjKFIxA7$L$uYV!-e^7-tZfsx0J~U$jXP%zO{Ku3F zb{=h6Uk06|KH?&<{#*-v-`FgA z>!(^wOJpzW(1+j<6uFO;gytf$8Gc>K)wsBamsJbL=W`0nRd@pIDRT~b zlnq!bq-|Cj#~X|sVz5&^pAya%Cv7fTAzr}BvqdJ~;waHD7iniYrjE4v_3Y{2`P)4q zl-5>Lt3x!ZB`$Xu@`52hu`e4(9cHT{E$gS@EE7a?U3KpCnhF_dhHndVAi@p^1TyTcyjV$hw`i3kmmlRB1?^%n8_DZ!2t<2t(ELk zHJ~k@muvEdvC4Ih0-&~jlZ{0IP|uY-mjREk5^?d@3*9(*)L2Ui>#jtYZo^v(g z{5CWM4k*jQhfv3{4AT2!^vMT)4h0b;+Xfy#m&(;!4Lh|94d0`#f$3#23$pc^BDcb~ z(cEN*e{W4MCfahXU%Egwd>Gt;x*kfwiEasrngr*=xZo;8?@=j(o;R}J0q3{X@Wvqd=i5FqF_Nf+{U$c_R8ZsFm zaX1bG{AM~8{hFU|10tLf9HmxSF9>>ADYpArti_^`*x?M{(216epa*kp@;y2tFn$}K zctd1b;k+2CCSczAEm zZC0)<-p5B7n*N<>RJW1$Hyh;N*0<2>;U-#P7Wdc=#>HqNbx>$bNQj1Hx5M~+ zfu{tEwp52fh@&Roi;bw)&Cs8fCbiS&DMu_d`FfI98eV#hy#kIed*@7-A5YjnrZ#rm zjFvtUkP%H@(YQZ;wZPJ&r>TiP=TvMj?BuYBf?$YNtvvLa_2^UgOnSpU`LDpQADqyZ zAC_Hkw3o7<7UD&O~P<@4E9 z_u~=bN6>MU{3YJe(zd3%u?c}n00u9Y5X^05s$P>>0Ta*>Lk^xRB_5DUv85iHU?DbR zr25T`gma%sp-}aDXu2}G6@7Hcj$I*VI((hQ-k5q;7Oxf{Wh_nThO>mM=vAbw%nb4n zS)0TB^>b{Wwi8<>Y8dS%MiypBzh2UyJwM+Npz|8W8lyDD8c}}1y9M7lP-7!S@ z&nvlKbTYZ@ltrDvfon`6+`U1Dc(Zo?o2spNsr|NOEIEl~^hpqu=nN-shiV_JY+$$K^c z9M3_b;TQu&p&(n^YJ_e8`v;j1gi~rbLc&}B-I|`R#{%YsNX+yM^+jX+SfUy{{#lxe z#Mul9dPCma1htN6C_Y@1B|=>ebR9(vOrRa?_Y=4u=(|Ruea7J(szC6xwJYRML;mw= zZWH!JXK{E>^h?x?L2a;uh)?_9&S?oQQ756eV)Tu_@o3Kz1cIL4NQnB7uT^|N&-F$Y zA+_SWRg&S*HsVuHZ|n_v@2TU)zd4v4AcmBv0~Lw|ov z!3PR9xdCik^EvZdNC?$ys>#QJZ zuf?)$i2CSmPa7GB-LN;ibTs1myH?oXXboY?p?%yvVf@`=)_afUa@e_s&Pb_<7jOF}DntS?`5${)ly5n{M(%(ucDutFu zLsbseC(I)rtwUOHwWsP?BQ@FtsFXs0v0*1gbJj}i3O+X)1a+@X@TS34;3Y+EdbpydcL`Z^G&AB!{DF%l0x)sYQZ0g1*+4y zY0HaWtf>s^FBboc-#|d<5Sdo5x06d1=BWTSumImeyFwSRs%jF4BWNn#Q3#hqWH9JTm9oAO-H!-T8ZS;fbD0NN zAbnV-8ooCijn(M4(@5%p*uqW}DrD7AlWDe9jQ^!$77q;FnoT|X*1t%1)@LCC8m&0d zG60Af_)UbL!Le)<-SBck8m_9v+e0!e07(i%ZxbV!Ypc>p3sN;cYreel@HG;TVfUPN zT|3zw@kedK$_8iO<@)4@*xcqL?3cfBu~Q*(CNwarLCJWl5$d<}N;Y1{c=W;Z;Y&VZ zPrFB^f5nggyk{2h2zu=)e_SE5n|8n4Abte=&Fr1Dm|^GuN}(yl$jy#xG%u$(PAZv9 zzfC}w2#?1*qGfAu#^ z>T7D$YGN`^FmrS->nxLwg#u}NA$PhFU(D*d{M9vG%Wy=l%&4qIZ@}T=AlEb(Z@|6J z8B?pRY=~&>W1h-Ie4t9yR<~LDoQ1qtssdT6J5zMhIXYuG37xGT?e%(lYE#M<%E=b; zVX@a;9)QLQdSip3QXf1!)yL<^emJYnj~sb7ohcByf2(8$`z|z&ypu+Ww%`0dEqGVq zsE7UOLjedJvbwL`6F(_FV&3RZP^S0%b*GbAq z*Fe{uqqpLRR8&UL8fhKxADXZ(DF*NC*&WYR9JyIvX@Ii6NHyq%W*^P_-F(q(w%TLV^^GCX`H|Bh_!b7^6ftB=iGHsr9unil=YCy zpt%)3z6Nh{0s-oAtRtOlVoO_WPo7Y~YW@pTokcT|kLoE>a`+}=5V z?gw3s`Y3L1cIan*;k~dz_)O-q5g4bkizYo(@!?k#Vzn{u%E_&mgC35#JO9; zaW7=4R0z356jKASIO{8iQfqgSNy1U39?(%pMlhDzy-HBctz9k4-OZq9AKIX&QHfBq zd?*w5lJA({5`l_i>p~MU8*b*gZim^!0?KImT$U(3XViy!LC$V;HVBjGL zIfvz=505Jm9*`N9UyH_c8k*4=#8+n_v;7&TD$l0MhIHbFhsR(W;e(wD#v>-vcwIn6 z?3j^k;#9bWCfJy|$hMWbUeWD_8_7tr3D|Y2?BCF-7rjIru&lzJX9otcL;zKBT2gh9 zi6|Cm#_$M@EkrEzKAp|QBXF!*i6&1M?v-0WXH&KglPJ?)NPKq~gk5|vS=2Bp|LQlH zD`NE<2nN2NK+u*$%m?@9u;(Ul`g0R%P&<*?Kq#R6zU zK=0P(x|;h=P{ifWz<&H*vuoK@bUw42*dSRa7|s}fB;s&(0oC5BWfd6%S;{Lyz%1u` ztXRq5k@5{ai5b797?%Aqz96moO}9FdbWSqel*%`guwfurAFuLdf~Df3RCYr$P7QCJ zF;n*oZ-qbw7MoJAku4|Ga5r>7T%)2&$4=j~r@LP{goF{E5O=iKakHf#^m#VUJ>_X4 zT8*s-5ZC?_KM{=;v>l7EU!xG)R2bCcaZLEOU^^4j1UTm4q^V)i#qGQ(?IE<7SJ!fu{ls*!*W*y(d}Y)B`3?Bf&43Pm<)>00|$cds0eF4HH&J zL!TgFfJjv4(04)KDe%_p;q3rQo|)N+W`axEY})A2yZ=kK@smGqoJubpZI<1tc|V@u zFe;I2l8D|q4l@N$78yNfquf`O%mx1_tCpFVa~BF?T)Wg`gGhuJh~ScnBJ&g$NzeD| zId=Sv&hKa0oLU1i3fy$X(w%l}-u>Ep0h0s)NB4W)RzR{4HD1aiLyZ_KZGHYwbx>KC zb-ER%5K6~pwBo;)ZjXR6=WPRvWFL1=c!pgX(o=WR1fEf6@wOccQ-no1kcSMtpSbJI zg!s)hcG!{sN>B6S1nE%}nw@<`$;Lv@yZ-)Y)@Rdm{1ex=sB1 z2D0m*!BhpJ z3bHHG%XIG(qd5nwvS(r}LRm*qKU5W}h4uiuF@@QkBOH5UDLP4Jr&C}JZ;sSxs(_YF zN{7wY4w1~M?5Rp>I!YkAM}VY4d4i}VufraEiq|Sr^u6DBdvBDk41330?x%!j^Uee?P8SuRpWOnZA}FlZzut(`w*} zi8YT(f=2IV)A;iP?*v3CM4dY9LX6hd0K1C~E{HA<>5~u&|WE$hA5L$`G$f2s*Bx*^g@~B{&Pk>N`=8jPjF9 zj|qR8YuzH2y)bbWcK;s1fH#E*2#Cf9BOKH>6ZH^M0e9@=y3q_S#fTU}P-8UEP3CBb z8=C7=oBCUlC6|=-DHxQJ#fSmkUquW_%iQ(RD&rRuJ~>aIza1X7KaK-OJd|{O_Knf4 z7IBP1sEy#5kdo{k2V2bHelkR(7rlm1)p)Cy%BxSXF^Dn@89V%$^`3^orvjU)Tul>x{_fh}^f`v3CwQp@H87q}aghDq1Ko|Zoo zTv{&Zw8U_uSL4|0Py{o^YMj_>J$07q+isS(ho^{Vi20@)kd-8dqc`UlH_rbsJZ}Jf zlf2^L%w)=h#?dxM$){L?rgjLzkL1m|fkA66KKQ+SZ>l1{i6*~W`pYVK?PgV%vDA_o zbPFpo+yC;$m+!GFDhyN(%JzYiB;(4`!a8E$h8N|6zKB9*C=AW9#*P|2v5VN|U*Rvo z&`YI8!kwMfmPXK%MZ9Qf9zRK&zp(RcYw^L6GHohdrdN0IN}W4OZ~Oz_#2T)HY!zbe z?&=fRcbGYVx@?V~=|uCfW97Kk?R1&LQc&ApPQNq1GuvC(`$spsrAgfU@@3~#I9OUg zWh4&Eaq#+dY;0jrUnquUZp+L3Zt>)V@w_r{Vd1r092>W(&}c9X1mA`~pBCk8G43^- z-CAxAe-VoRmt+rN1@l^$bh)Z9aZ!cP|MFXlgDkGsFD71B=Y1auau=r6+4!9s?^^2` zk0OvGZVM%lKMuUGjKb3&uap^NGzuH47t8jGMl)A{(C`K$jDsTwteDNU04Z&jq+Ao^ z-tuByNAG2oXjN8oZrZ_gVb?h1m)cvD$B zF8Y3AO9rbS{Ae3^!=wMT;Ajsv(!S&3Sw38q`QiDD_%%Sab|&4Zz!BE&ty+o)#R?^{ zV{vb&f*jHNfjkQ{4gUs0AEt5Q+8ie(a2dVB!xSJ5gxOw{?;=1trK)&&(crkNf&MhN z6P5EGX4JUa&drH<6@LF4mMdce?T8BYJ3cjRAZY1~;wi1S-waAk&>C1@11rtzC>w*l+g-T+e_33P?t4+i8 zB(9a57LQNi<<;wK5gb_|yB1u^uw!ww?TpUY9FsEvW>#iqW>%myGv!?T8Q0r%$^0;AIJJUD-?+jLP7p_ZSUU--3Iz%jX%zD2 z|FPeFdyfhU2?+~|yOXYawL7Rvw%EJ~TEtXezY~v%aQtV20JYM$Hh_63T`^nLG@-3_#^4sQVHi&xaPLj^Gscq~o>L2MY&=?=1 zIV^}cLH*x`1XT`g0)qnc2Ky5(4IYe8kLZWQht!O0fr1Y}0eAw=P$f{mp`oBnqsL;P zVwhn3#r%eij(vl(h?|8MfG>cbLEu2h^$F%vI}r`hII#)|0!b_>2dNtADp@YsGPw}> zGR1F770PBRD5`JNvD9xgV44%!ZQ2_;8M+I4bb1bYO?prIAqIDb2SyggP9_m1Pi70| zO_nUyZ)`*CNbJoVwVYI(J)DPJFkF+|U>;3g5?(dlCq8sO624BpH-09575)qXOo4KN z--7?RF9ffJoP_g5ltjiwfudbvbYj`!2I7n24-&W%JQC}YDN@nW?lP0I8FFCx5rsv? z9wl#O8s$Y*Db-pv8nqyGI`v|WM$KF;GA)ocnfAD@f*up_O~1z=%h1fQ$0)_v&P2fE z!W3wFX72+I+v3{N(@M%J!0ODp+Gg6;-FDp$XbjHMkcIk3ua2)}GL5*(V?r`oR9&{c(o&jD}-pfATU@~8H-$*|iKP5jqe>VTkfY!j! zz{Vi=py;5CV4>iE;Hu!sFZf@aLexSsLheE-LbJkf!)+pnBFZC`B8Q@6qGqEbW1wQF zVkBbB{y7%<-@Jf2g`)i5PKNo1VW9B;BZi*;j|99Pkvyd~Ab}h48J?Gcis)ee4!T(a zIkGP$@1v4I7>DDCzEz=+Fp#!DCJ=?Zuf-9Ad!=DIQjP0&%j5Uv_BPDwAU_wVQSd~* zDXe%850}t>dY2G!K)>A%P`Cdn=9=i9eb1|J3~-D|xLp`T-v!whnMQTh2*zV81q%-k z<6f1532OKFFNDblt{8+1+J{7ckh%pA5pu&JarTj5b!HXAU|83pWViP-{v#$_^GU2W zY`rr0%UZ&CpM0|w?AkHFi%0riDQ!cic{R3qt$rYGM`g2wxNLNIS1DJ$8OAOs*7SYT zQTf%yElJ0UWm^0Iwu0Z7uXpo_bD` z@MkY9Lx-5`ElX34M$alzInmU2;S#=g^+06;b`j&nbUoY8abr9JIZSw_s_P%n9LRRa z|2a?Rh2bCR)`W6@y4CxC|NgzY^FAy#DrRrt!(SLp_zk#<>>an2x4N~pmG_3>cdBoq z7C`s7OIq!7Wqac=1HxYV{7#zWv zRKW->!Gs0DFmA!PCc&sNl|7^Bg8;T&DYktJwmnU@1NYiprCJLnJrA@MchVIPSbldL zeh)yKJ8_!_qK`X)j|ax1JNe@+6vhqa0ys+^oHh#1r3a@}fU|AE8OPv!0&sFB7@`MG zUjgUwgHzkUIX>XbM{ohg)7PY@tj|wrGf%nfPbqaz*{)9+SN{U0pOOonAf`|0J5PDy zSz-Z;t1$=i1k$NkI3o$4?AkVhuXckC+ufAg+#TE8gk3T>E+5*vT?#g?(A&eL4OiJO z2p#~h5$^**=Dqc%{}YnGHvB(_={oym=7aKU+53EueQ)!B!?n5#y$9yk^!M=~>)uAw z|AHHJmn#pBuPyKELC(FcrvDS}th!$CKM=nrypIG~_BQ-CY}olv|8yo_FlT!QdFZ`L zH~f*X;r3(GrET=m>vc28r;qf-^zKuj+Y)>$ld1L=6{zP^ykW4XDF!u1^e -
    diff --git a/lib/client.js b/lib/client.js index c9d24fe7..2a7f8a90 100644 --- a/lib/client.js +++ b/lib/client.js @@ -603,32 +603,20 @@ CloudCmd._ajaxLoad = function(pFullPath, pOptions){ } } - DOM.ajax({ + DOM.getCurrentFileContent({ url : lFSPath, - error : DOM.Images.showError, - - success :function(data, textStatus, jqXHR){ - /* если такой папки (или файла) нет - * прячем загрузку и показываем ошибку - */ - if(!jqXHR.responseText.indexOf('Error:')) - return DOM.showError(jqXHR); - - CloudCmd._createFileTable(lPanel, data); + success :function(pData){ + CloudCmd._createFileTable(lPanel, pData); CloudCmd._changeLinks(lPanel); - - /* Сохраняем структуру каталогов в localStorage, - * если он поддерживаеться браузером - */ /* переводим таблицу файлов в строку, для - * сохранения в localStorage - */ - var lJSON_s = Util.stringifyJSON(data); + * сохранения в localStorage + */ + var lJSON_s = Util.stringifyJSON(pData); Util.log(lJSON_s.length); /* если размер данных не очень бошьой - * сохраняем их в кэше - */ + * сохраняем их в кэше + */ if(lJSON_s.length < 50000 ) DOM.Cache.set(lPath, lJSON_s); } From 076c5a5761c76666bb79f1fa208d531cc779ff18 Mon Sep 17 00:00:00 2001 From: coderaiser Date: Thu, 24 Jan 2013 09:42:05 -0500 Subject: [PATCH 130/347] refactored --- lib/client.js | 15 +++++++-------- lib/client/dom.js | 11 ++++++++++- lib/client/google_analytics.js | 15 +++------------ 3 files changed, 20 insertions(+), 21 deletions(-) diff --git a/lib/client.js b/lib/client.js index 2a7f8a90..10f977dd 100644 --- a/lib/client.js +++ b/lib/client.js @@ -83,8 +83,6 @@ CloudCmd._loadDir = function(pLink, pNeedRefresh){ /* показываем гиф загрузки возле пути папки сверху * ctrl+r нажата? */ - DOM.Images.showLoad(pNeedRefresh ? {top:true} : null); - var lCurrentLink = DOM.getCurrentLink(), lHref = lCurrentLink.href, lParent = lCurrentLink.textContent, @@ -92,6 +90,8 @@ CloudCmd._loadDir = function(pLink, pNeedRefresh){ lDir = DOM.getCurrentDir(); if(pLink || lCurrentLink.target !== '_blank'){ + DOM.Images.showLoad(pNeedRefresh ? {top:true} : null); + /* загружаем содержимое каталога */ CloudCmd._ajaxLoad(lLink, { refresh: pNeedRefresh }); @@ -608,15 +608,14 @@ CloudCmd._ajaxLoad = function(pFullPath, pOptions){ success :function(pData){ CloudCmd._createFileTable(lPanel, pData); CloudCmd._changeLinks(lPanel); - /* переводим таблицу файлов в строку, для - * сохранения в localStorage - */ + + /* переводим таблицу файлов в строку, для * + * сохранения в localStorage */ var lJSON_s = Util.stringifyJSON(pData); Util.log(lJSON_s.length); - /* если размер данных не очень бошьой - * сохраняем их в кэше - */ + /* если размер данных не очень бошьой * + * сохраняем их в кэше */ if(lJSON_s.length < 50000 ) DOM.Cache.set(lPath, lJSON_s); } diff --git a/lib/client/dom.js b/lib/client/dom.js index 076bc71a..7d70274f 100644 --- a/lib/client/dom.js +++ b/lib/client/dom.js @@ -183,6 +183,15 @@ var CloudCommander, Util, return DOM.addListener('click', pListener, pElement, pUseCapture); }; + /** + * safe add event click listener + * @param pListener + * @param pUseCapture + */ + DOM.addErrorListener = function(pListener, pElement, pUseCapture){ + return DOM.addListener('error', pListener, pElement, pUseCapture); + }; + /** * getListener for element * @@ -586,7 +595,7 @@ var CloudCommander, Util, }; DOM.addListener('load', lLoad, lElement); - DOM.addListener('error', lError,lElement); + DOM.addErrorListener(lError,lElement); if(lStyle) lElement.style.cssText = lStyle; diff --git a/lib/client/google_analytics.js b/lib/client/google_analytics.js index ba3d0d85..a4f499fd 100644 --- a/lib/client/google_analytics.js +++ b/lib/client/google_analytics.js @@ -5,19 +5,10 @@ var DOM, _gaq; /* setting google analitics tracking code */ _gaq = [['_setAccount', 'UA-33536569-2'], ['_trackPageview']]; - var lOnError_f = window.onerror; - window.onerror = function(msg, url, line) { - var preventErrorAlert = true; - _gaq.push(['_trackEvent', - 'JS Error', - msg, + DOM.addErrorListener(function(msg, url, line) { + _gaq.push(['_trackEvent', 'JS Error', msg, navigator.userAgent + ' -> ' + url + " : " + line]); - - if(typeof lOnError_f === 'function') - lOnError_f(); - - return preventErrorAlert; - }; + }); DOM.jsload('http://google-analytics.com/ga.js'); From 536e2a627d482b1f122f50267929a07759542e1d Mon Sep 17 00:00:00 2001 From: coderaiser Date: Thu, 24 Jan 2013 09:49:32 -0500 Subject: [PATCH 131/347] refactored --- lib/client.js | 66 ++++++++++++++++++---------------------- lib/client/keyBinding.js | 13 +++----- 2 files changed, 35 insertions(+), 44 deletions(-) diff --git a/lib/client.js b/lib/client.js index 10f977dd..bf176028 100644 --- a/lib/client.js +++ b/lib/client.js @@ -57,18 +57,13 @@ var CloudCmd = { }; CloudCmd.GoogleAnalytics = function(){ - /* google analytics */ - var lFunc = document.onmousemove; - - document.onmousemove = function(){ + DOM.addOneTimeListener('mousemove', function(){ + var lUrl = CloudCmd.LIBDIRCLIENT + 'google_analytics.js'; + setTimeout(function(){ - DOM.jsload(CloudCmd.LIBDIRCLIENT + 'google_analytics.js'); + DOM.jsload(lUrl); }, 5000); - - Util.exec(lFunc); - - document.onmousemove = lFunc; - }; + }); }; /** @@ -79,30 +74,30 @@ CloudCmd.GoogleAnalytics = function(){ * @param pNeedRefresh - необходимость обязательной загрузки данных с сервера */ CloudCmd._loadDir = function(pLink, pNeedRefresh){ - return function(pEvent){ - /* показываем гиф загрузки возле пути папки сверху - * ctrl+r нажата? */ + return function(pEvent){ + /* показываем гиф загрузки возле пути папки сверху + * ctrl+r нажата? */ + + var lCurrentLink = DOM.getCurrentLink(), + lHref = lCurrentLink.href, + lParent = lCurrentLink.textContent, + lLink = pLink || Util.removeStr(lHref, CloudCmd.HOST), + lDir = DOM.getCurrentDir(); + + if(pLink || lCurrentLink.target !== '_blank'){ + DOM.Images.showLoad(pNeedRefresh ? {top:true} : null); - var lCurrentLink = DOM.getCurrentLink(), - lHref = lCurrentLink.href, - lParent = lCurrentLink.textContent, - lLink = pLink || Util.removeStr(lHref, CloudCmd.HOST), - lDir = DOM.getCurrentDir(); + /* загружаем содержимое каталога */ + CloudCmd._ajaxLoad(lLink, { refresh: pNeedRefresh }); - if(pLink || lCurrentLink.target !== '_blank'){ - DOM.Images.showLoad(pNeedRefresh ? {top:true} : null); - - /* загружаем содержимое каталога */ - CloudCmd._ajaxLoad(lLink, { refresh: pNeedRefresh }); - - /* если нажали на ссылку на верхний каталог*/ - if(lParent === '..' && lDir !== '/') - CloudCmd._currentToParent(lDir); - } - - DOM.preventDefault(pEvent); - }; + /* если нажали на ссылку на верхний каталог*/ + if(lParent === '..' && lDir !== '/') + CloudCmd._currentToParent(lDir); + } + + DOM.preventDefault(pEvent); }; +}; /** @@ -141,16 +136,15 @@ CloudCmd._editFileName = function(pParent){ */ CloudCmd._currentToParent = function(pDirName){ /* убираем слэш с имени каталога */ - pDirName = pDirName.replace('/',''); + pDirName = Util.removeStr(pDirName, '/'); /* опредиляем в какой мы панели: * * правой или левой */ var lPanel = DOM.getPanel(), lRootDir = DOM.getById(pDirName + '(' + lPanel.id + ')'); - /* if found li element with ID directory name - * set it to current file - */ + /* if found li element with ID directory name * + * set it to current file */ if(lRootDir){ DOM.setCurrentFile(lRootDir); DOM.scrollIntoViewIfNeeded(lRootDir, true); @@ -416,7 +410,7 @@ CloudCmd.getModules = function(pCallBack){ CloudCmd._changeLinks = function(pPanelID){ /* назначаем кнопку очистить кэш и показываем её */ var lClearcache = DOM.getById('clear-cache'); - DOM.addListener('click', DOM.Cache.clear, lClearcache); + DOM.addClickListener(DOM.Cache.clear, lClearcache); /* меняем ссылки на ajax-запросы */ var lPanel = DOM.getById(pPanelID), diff --git a/lib/client/keyBinding.js b/lib/client/keyBinding.js index ae8b4d60..9be094df 100644 --- a/lib/client/keyBinding.js +++ b/lib/client/keyBinding.js @@ -291,15 +291,12 @@ var CloudCommander, Util, DOM; } /* если нажали +d чистим кэш */ - else if(lKeyCode === KEY.D && - pEvent.ctrlKey){ - console.log('+d pressed\n' + - 'clearing cache...\n' + - 'press +q to remove all key-handlers'); - - var lClearCache = DOM.getById('clear-cache'); - Util.exec( DOM.getListener(lClearCache) ); + else if(lKeyCode === KEY.D && pEvent.ctrlKey){ + Util.log('+d pressed\n' + + 'clearing cache...\n' + + 'press +q to remove all key-handlers'); + DOM.Cache.clear(); DOM.preventDefault(); } From 3464e909a51fa2faa3de822ee8b3c873cc5b5053 Mon Sep 17 00:00:00 2001 From: coderaiser Date: Thu, 24 Jan 2013 11:06:42 -0500 Subject: [PATCH 132/347] added font files --- font/fontello.eot | Bin 0 -> 5068 bytes font/fontello.svg | 40 ++++++++++++++++++++++++++++++++++++++++ font/fontello.ttf | Bin 0 -> 4892 bytes 3 files changed, 40 insertions(+) create mode 100644 font/fontello.eot create mode 100644 font/fontello.svg create mode 100644 font/fontello.ttf diff --git a/font/fontello.eot b/font/fontello.eot new file mode 100644 index 0000000000000000000000000000000000000000..9ae403f0715efd1f87c417b85ce50405185e9abf GIT binary patch literal 5068 zcmds4TWlQV6+ZvW?9F$_SF(2Uug7+5@9bv1z9w1dX4h*690xlI$!@sqdcD3Rw%4+q zHW5;VRH)FZrBc=MfKVk6;-RXle-n^WwFsz?08yaoODkTG+CCsu(LMyh1Bv^c*yMTRP;4{gd#`+S%x=5w z(Lh;Rr954v1)w!NiE3zuazHDvu0fY{2Kpv#!IGzS=ppcIaoaYnct|J>h}Af%}Kq?`})y*3Nk%PXqH8 zHA>$xf(>1`*l1fHa2q`*IxeXvqXDXho4gE-?*th0H$|(|M;_ADfCToogDB} z(0&W+k4jQ}K!XB9duv?z4GjgXT(gUb1&SUxyR)O8L~YLL(jGFdbLoOlRGSDjA_MpF zQZL{_w~+-|W!nvV;0&%V0Esw}%jqyc z(;FT2{-H)+QI#xqH$`kkV_tXESLbz$i(ytGf~nut!T_Ss}~ zAKD#;?npU>rqLYuRY%D%#2g7xEn?m%I)f&iv{Z#jh;Gc3Nrp(Iq75z02d%!I&b`fD zExx{PSA)N4cyw%ZDBRlC+S2TFInA!#1HGcl+0xY9dPIcBnudE#-G-xgieW#u)L(iq zxAn+tV}~F&ANl>*A(400RMoha8Z|s0uW?;)RW)94SBr;0I(+G&>~>j-pL(1(UvRqX z#5t$u#-Cj^)$a3qs$G~p^*itCpK2dd9qpqBr&TS|m82Vn{s5+)MuwJj2&73f5X2di zNO+_BLD84LnSpc#b?$9zZfx*4sZP{7>--|z>eB0k>FYY6_l^$rwE4#F5=eBvXdUu} zTlKd(o3sI~t@HEuha3Z1Q>T8u>hmjghHQR*xNG13F>$)NTU>AP-i&zL!s2yr%X;5q zO`To)y4x`x4&?ES_BK|ljuC7lFYTuj(P_*LC&r)Gl<<%^TaBmN;c=hEz-V(-7)=bM z!=cZSu2<`^jt+;>vA<*gzE1DnJKA|zYO9PU$DYQ98jO~&#fKDH8k?O+p?9Qlcy!3& zYxL>95np4AZ{%;H{pN?_v4O)!`ft|sMUM1~>o;q}^>rAz;mLF2ABP8SO!Oa#^l88A zKeBLRLVT9Ic`3=$&tB4~@3D_wdGF!+$R|{-f5_(B_B3Rue~JvTA?~hvyravVXXu>a zV!rgYXu=@(EdHD2eV##AEyRUpZ$AYxWy6^a62i?y9_Rk+Nl>u7tzC> zr)&%MZ2I-C{}HtD8~mP|UMyp5tn_vn>(nhe%Ge;cI90|DY7pNqV<+s-V$6hrbBS;k zm9fCN^IREgI51x>W1WuCpUT*PpQB~$ASoUwV<+rCp*W7x4Z4hDb%mB_nYKwH+c@<@ zhVA1u3^Wgh1hd3i!Qq=hUJ}oXcw^aa*@7O$y=WiK+c>IM@yp|Q&f4Z zZkgzhTqru`W0G60RFO8|1B}apEWlkcL=Ad@iZnasL&RE1r(2)?5dAC?%gOH8D?Rc+3^6j0UH^jvcKRLSV|!QN!sYpf#G z#_vlOYir{OA5$KGiqqD5V&#mBRlvn+Jd|iPCAy3(z%Y7Z1=JR?-2*-o?^ZBI&wd|1 z?6wp#nL_`k((2LqTNT4Mag#ZrL|oBbSA-BKZa~$dJC4NZB&k|6Ce!fxQeB(VlgDyF$~_?t_bG6L09dvYB-&jYI4Ql?*-0|C`zNjZfXQki^aX55s8`;uQaVw?l2 zcekXXRrL%g(Gs#Nc;Jf}-`><`$q{>9^;@L$(hSE^sl;PQtuNY}#>8Fbc(G5kpjQKy zjGzYN-gLjLBU#c{(rs!PqswQ>my{h%< zdsDF(=ClXf1mYp0Ihj^Q91|65iTtI#N{go#Or^&&3z+#@JmUpUr?3z}%OEiJL~}AT z?ls|Ol84U|w|&7y$rU1CPk@4&_>}|8!olz2J?w(V9^r zku^&dFYRe&xvJ<>Kn;~6#w%q_^TB{DDwQ9$Oc~_#C$URKQo&-kXvX9o*)^m1x6Grv zW}97k^)}9!rGoxS%WY1_Ep^aez>xFU7BKn05*Wn}s&2$TWLJdeOQnE37qGZw5Hq*b z$HQ2N*v@t*Rwc!6pE*}eLyrfDL*(-O9dc~a%~c*o}9!!Ua3aR;(4WxqIhxZ z3ejPJ2>`($VOP1M?`9hb=X@%TBXmvokhg_PmXlnzdATm%6};P0iVesf#vZ>XM~~cB#v( z*whspG4;HSn0mqTD|b%m-Lsc?`wkFIU!ylqCW}Hmnfi?z|4L*fHpvMb&gRAyqtBB# wIdQeB+G+TUPBDS0^#s0QqE94?PJN=-%Njl#Sj7hdu4`iFab^6t=%9&z11<_Sg#Z8m literal 0 HcmV?d00001 diff --git a/font/fontello.svg b/font/fontello.svg new file mode 100644 index 00000000..0eb33d8e --- /dev/null +++ b/font/fontello.svg @@ -0,0 +1,40 @@ + + + + +Created by FontForge 20100429 at Thu Jan 24 11:50:44 2013 + By root +Copyright (C) 2012 by original authors @ fontello.com + + + + + + + + + + + + + diff --git a/font/fontello.ttf b/font/fontello.ttf new file mode 100644 index 0000000000000000000000000000000000000000..2868965d8d6901025ff86f916b807dcfe7299f4f GIT binary patch literal 4892 zcmds4TWlQV6+ZvW?9JCWzLK@$e?7KiduKQ6^)<-GAQIJV_W zn+T~wDpY9IQmJZrK&TQ3@laLOzX?dGT2!cz03m4AmsY$WwS7QUMf(s04n>$4Z_wX;|A2lHx?Ia%%FjRh-iy#z;osg^-`bwJ7RHeGO*|$xHuD?TU;EPwMEhBX zAL8oity}6tmhbic?nir4=$qmOx!|Xu{Swyim8AHP289MIxpn52E)5N=T(gU*1&SX! zx4WyKMsLpRk`^%LL~=OriEb02M`YkWSsvp#V3q}0<=739kOux>XOE@lAAeSUqXwfy z!_L$3dFUK7pu^LRH=LW_7dPH?ZZsN+pt+S~xDu3d=T#ZJ)B69WX%s*o2DInUD-ZR@ zdr_dSk$#ReA+%}IG;zNHB$7lPx61%cZ*bKIhZ+J6hlU!=fbf0v)?3S6UQLbEMT4~P>OrAvB0jC!X6^VvjJpiom39kFqdD-aPLN@UITE5;B>ZtCgCU)? zbcIQXZp@TPhDfF3`F%cbW9PTxB2aet?hJ!p(fBBL8*5hxC9f90@{5NAq#JZ=Zs>ZX_pyBycjpw4L zs^OxyT09EU(aVqKw#!od(C5DOvfEoH&bxg#|KO>q_FmXu?ZN7)-+f>IRQs6f=pa2Z zt!h!OB;7Fdhq3fDGBn#IP$tbl5LZkh<&SrQqA!0v1LX?pIMCYEu+QhFI#KJc3yNrq zN3RoRpzDy{J37?U8W=k+P-v%U845&O^mjTMwE?ZQtN@YINQ`MZZ!LE#r&;N@ut6dt?$Xkj;@2Udsz7rRx*47QS=I_Xq=8f(Li z`R6?)d?e0Q2sv()q0}6-DR|Qws#)v@E^FVji;ry%4l@$ zZ`fCZ*$OlVP(pJ5ZC-xPmPUW?`RlKL`&fPK6ROrf zV)Gqm8YnrpJ8GIu@MS^z%LcBN*d1_&qPZQpPw~>D@Bcsav#{ zu|ZyOri@**Pkg(K-LSuaITHrHON4Jx84G-OUMypcYUs5x*6AMleHk0@bE1r0B*nvJ z?1udZl%#dqpewXVE3`z*v`rG#CaE7XN)hbwD9}6@63h~7h1MvGx+I=Ecw^Za*@7O& zy$Ed$);t)iz+uYad5I{wzHw!9Woda^_9gpeG%^^K^H=0LOe<^IRhiw{US8kal5x4X zzP6oTU0o07*6~`-@v?*OOAcP}mC3EH?=0jl0nH%D0utY0_cQs0l^w{*a(tGXs40(} zqR1iwc@+1T{gtdyd|^VUHdL;92#%1( zXJ>6CIyN%q-Wi-6P7apDFvbPGue_%3yrLB4{lT{)OUcY)NC`_WNcH-xGJ4M|BlVCS zFa!RAE!FI-9Z>PKU#f9NSMJkB~^q$gc+~T+Hz4A3RzHQ?F=l^Au$?d zG@9{e(&@BcQ81l070ufDbULIoOTv%QlSK-yvC};9ZYZbh%PWWP!aFYLaMC@;>eWfSE|EYSvCetA-X*K1*IVXr3QAL{T-# zj4WiNsxcF0NL5+WXYHa#Or*P2ZQi^bQq|Vx#D6KaIqQ>C0b00lu-p3Mo*%E-XgYlAYkI%3dZa?@56^PmPRF0$bTxM zo|wPgG5jWOG$)iuDms2u2!ZB?R4vkRrOw)aO?1I z&NgKXs;Tzp+j*UbRJWzfpfU$Ss>hOY3M-_t_{{9MDGLwSUp8W%L#lVLrJ_ak3@FhY zaXNVDixuBF)Mv>NXJ7SOr1a7X$5yGtV@NHpIETi>adW)bCz_GffF)zdpu);v8Dz&p zDrkjUV;;=l3}deQjSNl5pkUDEv!n3 z|9Rz1{cnqfyTf}mW{&#vjT z8o&lQ{x$Y*;2aQ5jVcIE+)^Qk6Wp5=x+f=bj#s*ouy|dm6KG!2x=M5uU;;oeNLg2f zW0?eSEK|%mhM{|y!{A=#Fu0F745pbg25^!&3}%?a;1qKh+|QgN0JF?tFvlDQr7xkU0#_F=rgWW)6cia~Paw4ugj*71?X^!>p)bm>+S_2+*U>s)2bF z%43!q++$~0aoAZ0F?-%YOwCy;x<_4L#i8aM#MDIxF?GpOLwnR^RvhYzgP3~3K}@}9 z1(i3i^zPZqe0+z9rmxf6r|qH;&!m6h#eXGo5{KjjKF;RGRin?BIz4f%s@iP?i*7N2 lrS&v^!^EGqi*9|Q*vlGzHn55x2)M3`-KUiC{i2H|{t1R3BHRD~ literal 0 HcmV?d00001 From 594a0e92cc634d9e90fa73b722e226b69bdd445c Mon Sep 17 00:00:00 2001 From: coderaiser Date: Fri, 25 Jan 2013 06:52:54 -0500 Subject: [PATCH 133/347] refactored --- lib/client/dom.js | 2 +- lib/client/keyBinding.js | 74 +++++++++++----------------------------- 2 files changed, 20 insertions(+), 56 deletions(-) diff --git a/lib/client/dom.js b/lib/client/dom.js index 7d70274f..b74a47fa 100644 --- a/lib/client/dom.js +++ b/lib/client/dom.js @@ -903,7 +903,7 @@ var CloudCommander, Util, DOM.getCurrentData = function(pCallBack, pCurrentFile){ var lParams, lFunc = function(pData){ - var lName = DOM.getCurrentName(); + var lName = DOM.getCurrentName(pCurrentFile); if( Util.isObject(pData) ){ pData = Util.stringifyJSON(pData); diff --git a/lib/client/keyBinding.js b/lib/client/keyBinding.js index 9be094df..32f86fed 100644 --- a/lib/client/keyBinding.js +++ b/lib/client/keyBinding.js @@ -145,55 +145,26 @@ var CloudCommander, Util, DOM; DOM.Images.showLoad({top: true}); Util.exec(CloudCmd.Terminal); } - /* навигация по таблице файлов*/ - /* если нажали клавишу вверх*/ - else if(lKeyCode === KEY.UP){ - /* если ненайдены выделенные файлы - выходим*/ - if(!lCurrentFile) return; - - /* если это строка существет и - * если она не заголовок - * файловой таблицы - */ - lCurrentFile = lCurrentFile.previousSibling; - if(lCurrentFile){ - /* выделяем предыдущую строку*/ - DOM.setCurrentFile(lCurrentFile); - } - - DOM.preventDefault(pEvent); + /* навигация по таблице файлов * + * если нажали клавишу вверх * + * выделяем предыдущую строку */ + else if(lKeyCode === KEY.UP){ + DOM.setCurrentFile( lCurrentFile.previousSibling ); + DOM.preventDefault( pEvent ); } - /* если нажали клавишу в низ*/ + /* если нажали клавишу в низ * + * выделяем следующую строку */ else if(lKeyCode === KEY.DOWN){ - /* если ненайдены выделенные файлы - выходим*/ - if(lCurrentFile){ - lCurrentFile = lCurrentFile.nextSibling; - /* если это не последняя строка */ - if(lCurrentFile){ - /* выделяем следующую строку*/ - DOM.setCurrentFile(lCurrentFile); - DOM.preventDefault(pEvent); - } - } + DOM.setCurrentFile( lCurrentFile.nextSibling ); + DOM.preventDefault( pEvent ); } - /* если нажали клавишу Home - * переходим к самому верхнему - * элементу - */ + /* если нажали клавишу Home * + * переходим к самому верхнему * + * элементу */ else if(lKeyCode === KEY.HOME){ - /* получаем первый элемент - * пропускаем путь и заголовки столбиков - * выделяем верхий файл - */ - lCurrentFile = lCurrentFile.parentElement.firstChild; - - /* set current file and - * move scrollbar to top - */ - DOM.setCurrentFile(lCurrentFile); - + DOM.setCurrentFile( lCurrentFile.parentElement.firstChild ); DOM.preventDefault(pEvent); } @@ -201,15 +172,8 @@ var CloudCommander, Util, DOM; * выделяем последний элемент */ else if(lKeyCode === KEY.END){ - /* выделяем самый нижний файл */ - lCurrentFile = lCurrentFile. - parentElement.lastElementChild; - - /* set current file and - * move scrollbar to bottom - */ - DOM.setCurrentFile(lCurrentFile); - DOM.preventDefault(pEvent); + DOM.setCurrentFile( lCurrentFile.parentElement.lastElementChild ); + DOM.preventDefault( pEvent ); } /* если нажали клавишу page down @@ -256,6 +220,7 @@ var CloudCommander, Util, DOM; /* если нажали Enter - открываем папку*/ else if(lKeyCode === KEY.ENTER) Util.exec(CloudCmd._loadDir()); + /* если нажали +r * обновляем страницу, * загружаем содержимое каталога @@ -263,8 +228,7 @@ var CloudCommander, Util, DOM; * сервера, а не из кэша * (обновляем кэш) */ - else if(lKeyCode === KEY.R && - pEvent.ctrlKey){ + else if(lKeyCode === KEY.R && pEvent.ctrlKey){ console.log('+r pressed\n' + 'reloading page...\n' + 'press +q to remove all key-handlers'); @@ -275,7 +239,7 @@ var CloudCommander, Util, DOM; var lRefreshIcon = DOM.getRefreshButton(); if(lRefreshIcon){ /* получаем название файла*/ - var lSelectedName = DOM.getCurrentName(); + var lSelectedName = DOM.getCurrentName(lCurrentFile); /* если нашли элемент нажимаем него * а если не можем - нажимаем на From df16cf02dc01cd39b141ef75fd841bdc63d8bc6a Mon Sep 17 00:00:00 2001 From: coderaiser Date: Fri, 25 Jan 2013 07:09:46 -0500 Subject: [PATCH 134/347] refactored --- lib/client/dom.js | 28 +++++++++++++--------------- lib/client/menu.js | 7 ++----- 2 files changed, 15 insertions(+), 20 deletions(-) diff --git a/lib/client/dom.js b/lib/client/dom.js index b74a47fa..63ac6f84 100644 --- a/lib/client/dom.js +++ b/lib/client/dom.js @@ -850,8 +850,8 @@ var CloudCommander, Util, * * @pCurrentFile */ - DOM.getCurrentFile = function(){ - var lRet = DOM.getByClass(CURRENT_FILE )[0]; + DOM.getCurrentFile = function(pCurrentFile){ + var lRet = pCurrentFile || DOM.getByClass(CURRENT_FILE )[0]; return lRet; }; @@ -859,7 +859,7 @@ var CloudCommander, Util, DOM.getCurrentSize = function(pCurrentFile){ var lRet, - lCurrent = pCurrentFile || DOM.getCurrentFile(), + lCurrent = DOM.getCurrentFile(pCurrentFile), lSize = DOM.getByClass('size', lCurrent); lRet = lSize[0].textContent; @@ -955,13 +955,10 @@ var CloudCommander, Util, * unified way to set current file */ DOM.setCurrentFile = function(pCurrentFile){ - var lRet = true, + var lRet, lCurrentFileWas = DOM.getCurrentFile(); - if(!pCurrentFile) - lRet = false; - - if(lRet){ + if(pCurrentFile){ if (pCurrentFile.className === 'path') pCurrentFile = pCurrentFile.nextSibling; @@ -975,6 +972,8 @@ var CloudCommander, Util, /* scrolling to current file */ DOM.scrollIntoViewIfNeeded(pCurrentFile); + + lRet = true; } return lRet; }; @@ -1041,8 +1040,7 @@ var CloudCommander, Util, * @param pCurrentFile - current file by default */ DOM.getCurrentLink = function(pCurrentFile){ - var lLink = DOM.getByTag('a', - pCurrentFile || DOM.getCurrentFile()), + var lLink = DOM.getByTag( 'a', DOM.getCurrentFile(pCurrentFile) ), lRet = lLink.length > 0 ? lLink[0] : -1; @@ -1055,8 +1053,8 @@ var CloudCommander, Util, * @param pCurrentFile - current file by default */ DOM.getCurrentPath = function(pCurrentFile){ - var lCurrent = pCurrentFile || DOM.getCurrentFile(), - lPath = DOM.getCurrentLink(lCurrent).href; + var lCurrent = DOM.getCurrentFile( pCurrentFile ), + lPath = DOM.getCurrentLink( lCurrent ).href; /* убираем адрес хоста*/ lPath = Util.removeStr(lPath, CloudCommander.HOST); lPath = Util.removeStr(lPath, CloudFunc.NOJS); @@ -1070,8 +1068,8 @@ var CloudCommander, Util, * @param pCurrentFile */ DOM.getCurrentName = function(pCurrentFile){ - var lLink = DOM.getCurrentLink( - pCurrentFile || DOM.getCurrentFile()); + var lCurrent = DOM.getCurrentFile( pCurrentFile ), + lLink = DOM.getCurrentLink( lCurrent ); if(lLink) lLink = lLink.textContent; @@ -1089,7 +1087,7 @@ var CloudCommander, Util, /** 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 */ diff --git a/lib/client/menu.js b/lib/client/menu.js index 72ec76f4..c4821f0d 100644 --- a/lib/client/menu.js +++ b/lib/client/menu.js @@ -305,11 +305,8 @@ var CloudCommander, Util, DOM, $; if( KeyBinding.get() ){ /* if shift + F10 pressed */ if(lKeyCode === lKEY.F10 && pEvent.shiftKey){ - var lCurrent = DOM.getCurrentFile(); - if(lCurrent) - $(lCurrent).contextMenu(); - - pEvent.preventDefault(); + $( DOM.getCurrentFile() ).contextMenu(); + DOM.preventDefault(pEvent); } } else if (lKeyCode === lKEY.ESC) From f976cded52ed65d0d381a985465089f9ebfadcc7 Mon Sep 17 00:00:00 2001 From: coderaiser Date: Fri, 25 Jan 2013 07:32:12 -0500 Subject: [PATCH 135/347] refactored --- lib/client/dom.js | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/lib/client/dom.js b/lib/client/dom.js index 63ac6f84..49e4ad64 100644 --- a/lib/client/dom.js +++ b/lib/client/dom.js @@ -850,8 +850,8 @@ var CloudCommander, Util, * * @pCurrentFile */ - DOM.getCurrentFile = function(pCurrentFile){ - var lRet = pCurrentFile || DOM.getByClass(CURRENT_FILE )[0]; + DOM.getCurrentFile = function(){ + var lRet = DOM.getByClass(CURRENT_FILE )[0]; return lRet; }; @@ -859,9 +859,9 @@ var CloudCommander, Util, DOM.getCurrentSize = function(pCurrentFile){ var lRet, - lCurrent = DOM.getCurrentFile(pCurrentFile), - lSize = DOM.getByClass('size', lCurrent); - lRet = lSize[0].textContent; + lCurrent = pCurrentFile || DOM.getCurrentFile(), + lSize = DOM.getByClass('size', lCurrent); + lRet = lSize[0].textContent; return lRet; }; @@ -1040,7 +1040,7 @@ var CloudCommander, Util, * @param pCurrentFile - current file by default */ DOM.getCurrentLink = function(pCurrentFile){ - var lLink = DOM.getByTag( 'a', DOM.getCurrentFile(pCurrentFile) ), + var lLink = DOM.getByTag( 'a', pCurrentFile || DOM.getCurrentFile() ), lRet = lLink.length > 0 ? lLink[0] : -1; @@ -1053,7 +1053,7 @@ var CloudCommander, Util, * @param pCurrentFile - current file by default */ DOM.getCurrentPath = function(pCurrentFile){ - var lCurrent = DOM.getCurrentFile( pCurrentFile ), + var lCurrent = pCurrentFile || DOM.getCurrentFile(), lPath = DOM.getCurrentLink( lCurrent ).href; /* убираем адрес хоста*/ lPath = Util.removeStr(lPath, CloudCommander.HOST); @@ -1068,7 +1068,7 @@ var CloudCommander, Util, * @param pCurrentFile */ DOM.getCurrentName = function(pCurrentFile){ - var lCurrent = DOM.getCurrentFile( pCurrentFile ), + var lCurrent = pCurrentFile || DOM.getCurrentFile(), lLink = DOM.getCurrentLink( lCurrent ); if(lLink) From 85653ab09a0774e8746adf83fe4f763fd70d42a1 Mon Sep 17 00:00:00 2001 From: coderaiser Date: Fri, 25 Jan 2013 07:45:58 -0500 Subject: [PATCH 136/347] refactored --- lib/client.js | 39 +++++++++++---------------------------- 1 file changed, 11 insertions(+), 28 deletions(-) diff --git a/lib/client.js b/lib/client.js index bf176028..0c02848d 100644 --- a/lib/client.js +++ b/lib/client.js @@ -537,39 +537,22 @@ CloudCmd._changeLinks = function(pPanelID){ * @param pOptions * { refresh, nohistory } - необходимость обновить данные о каталоге */ -CloudCmd._ajaxLoad = function(pFullPath, pOptions){ +CloudCmd._ajaxLoad = function(pPath, pOptions){ if(!pOptions) pOptions = {}; - /* Отображаем красивые пути */ - /* added supporting of russian language */ - pFullPath = decodeURI(pFullPath); - var lPath = pFullPath, - lFSPath = pFullPath, - - lFS_s = CloudFunc.FS, - lNoJS_s = CloudFunc.NOJS; - /* - * убираем значения, которые, - * говорят об отсутствии js - */ - if(lPath.indexOf(lNoJS_s) === lFS_s.length) - lPath = lFSPath = Util.removeStr(lPath, lNoJS_s); + /* Отображаем красивые пути */ + var lFSPath = decodeURI(pPath); - if(lPath.indexOf(lFS_s) === 0){ - lPath = lPath.replace(lFS_s,''); - - if(lPath === '/') - pFullPath = '/'; - } - - Util.log ('reading dir: "' + lPath + '";'); + lFSPath = Util.removeStr( lFSPath, CloudFunc.NOJS ); + pPath = Util.removeStr( lFSPath, CloudFunc.FS ); + Util.log ('reading dir: "' + pPath + '";'); if(!pOptions.nohistory) - DOM.setHistory(pFullPath, null, pFullPath); + DOM.setHistory(pPath, null, pPath); - DOM.setTitle( CloudFunc.getTitle(lPath) ); + DOM.setTitle( CloudFunc.getTitle(pPath) ); /* если доступен localStorage и * в нём есть нужная нам директория - @@ -585,7 +568,7 @@ CloudCmd._ajaxLoad = function(pFullPath, pOptions){ var lPanel = DOM.getPanel().id; if(!pOptions.refresh && lPanel){ - var lJSON = DOM.Cache.get(lPath); + var lJSON = DOM.Cache.get(pPath); if (lJSON){ /* переводим из текста в JSON */ @@ -599,7 +582,7 @@ CloudCmd._ajaxLoad = function(pFullPath, pOptions){ DOM.getCurrentFileContent({ url : lFSPath, - success :function(pData){ + success : function(pData){ CloudCmd._createFileTable(lPanel, pData); CloudCmd._changeLinks(lPanel); @@ -611,7 +594,7 @@ CloudCmd._ajaxLoad = function(pFullPath, pOptions){ /* если размер данных не очень бошьой * * сохраняем их в кэше */ if(lJSON_s.length < 50000 ) - DOM.Cache.set(lPath, lJSON_s); + DOM.Cache.set(pPath, lJSON_s); } }); }; From 2ae8b8a040240aaf6261d0f272165d81c9f790a0 Mon Sep 17 00:00:00 2001 From: coderaiser Date: Mon, 28 Jan 2013 05:35:40 -0500 Subject: [PATCH 137/347] refactored --- cloudcmd.js | 5 ++- html/auth/github.html | 4 ++- json/config.json | 8 +++-- lib/client.js | 25 +++++++-------- lib/client/storage/_github.js | 58 ++++++++++++----------------------- lib/server.js | 4 +-- lib/server/auth.js | 15 ++++----- lib/server/ischanged.js | 2 +- lib/server/main.js | 4 ++- 9 files changed, 57 insertions(+), 68 deletions(-) diff --git a/cloudcmd.js b/cloudcmd.js index 3434530f..2d1d555d 100644 --- a/cloudcmd.js +++ b/cloudcmd.js @@ -104,9 +104,8 @@ merge : true }; - if (pAllowed.js) { + if (pAllowed.js) lOptimizeParams.push(LIBDIR + 'client.js'); - } if (pAllowed.html) lOptimizeParams.push(lIndex); @@ -121,7 +120,7 @@ } if (lOptimizeParams.length) - lMinify.optimize(lOptimizeParams); + lMinify.optimize(lOptimizeParams,{cache:true}); } /** diff --git a/html/auth/github.html b/html/auth/github.html index 1aa4ed8f..7ba927d0 100644 --- a/html/auth/github.html +++ b/html/auth/github.html @@ -5,11 +5,13 @@ \ No newline at end of file diff --git a/json/config.json b/json/config.json index d68b757e..6eb996bf 100644 --- a/json/config.json +++ b/json/config.json @@ -8,14 +8,6 @@ "html" : true, "img" : true }, - "github":{ - "key" : "891c251b925e4e967fa9", - "secret" : "afe9bed1e810c5dc44c4c2a953fc6efb1e5b0545" - }, - "dropbox_key" : "0nd3ssnp5fp7tqs", - "dropbox_secret" : "r61lxpchmk8l06o", - "dropbox_encoded_key" : "DkMz4FYHQTA=|GW6pf2dONkrGvckMwBsl1V1vysrCPktPiUWN7UpDjw==", - "dropbox_chooser_key" : "o7d6llji052vijk", "gdrive_id" : "255175681917", "vk_id" : "3336188", "logs" : false, diff --git a/json/modules.json b/json/modules.json index 026bf9d7..2275b95b 100644 --- a/json/modules.json +++ b/json/modules.json @@ -2,10 +2,23 @@ "editor/_codemirror", "menu", "viewer", - "terminal",{ - "storage":[ - "DropBox", - "GitHub", + "terminal", { + "name": "storage", + "data": [{ + "name": "DropBox", + "data":{ + "key" : "0nd3ssnp5fp7tqs", + "secret" : "r61lxpchmk8l06o", + "encodedKey" : "DkMz4FYHQTA=|GW6pf2dONkrGvckMwBsl1V1vysrCPktPiUWN7UpDjw==", + "chooserKey" : "o7d6llji052vijk" + } + }, { + "name": "GitHub", + "data": { + "key" : "891c251b925e4e967fa9", + "secret" : "afe9bed1e810c5dc44c4c2a953fc6efb1e5b0545" + } + }, "GDrive", "VK", "SkyDrive" diff --git a/lib/client.js b/lib/client.js index ad97296e..be63e052 100644 --- a/lib/client.js +++ b/lib/client.js @@ -230,19 +230,15 @@ function initModules(pCallBack){ CloudCmd.getModules(function(pModules){ pModules = pModules || []; + DOM.addListener('contextmenu', function(pEvent){ + CloudCmd.Menu.ENABLED || DOM.preventDefault(pEvent); + }, document); + var lStorage = 'storage', lShowLoadFunc = Util.retFunc( DOM.Images.showLoad ), - lDisableMenuFunc = function(){ - var lFunc = document.oncontextmenu; - document.oncontextmenu = function(){ - Util.exec(lFunc); - return CloudCmd.Menu.ENABLED || false; - }; - }, lDoBefore = { 'editor/_codemirror' : lShowLoadFunc, - 'menu' : lDisableMenuFunc, 'viewer' : lShowLoadFunc }, @@ -254,34 +250,26 @@ function initModules(pCallBack){ }); }; - lDisableMenuFunc(); - - - for(var i = 0, n = pModules.length; i < n ; i++){ var lModule = pModules[i]; - if(Util.isObject(lModule)){ - for(var lProp in lModule) - if(lProp === lStorage){ - var m = lModule[lProp].length, - lMod = lModule[lProp]; - - for(var j = 0; j < m; j++){ - var lName = lMod[j], - lPath = lStorage + '/_' + lName.toLowerCase(); - - lLoad(lName, lPath); - } - - break; - } - } - else + if(Util.isString(lModule)) lLoad(null, lModule, lDoBefore[lModule]); } + var lStorageObj = Util.findObjByNameInArr( pModules, lStorage ), + lMod = Util.getNamesFromObjArray( lStorageObj.data ); + + for(i = 0, n = lMod.length; i < n; i++){ + var lName = lMod[i], + lPath = lStorage + '/_' + lName.toLowerCase(); + + lLoad(lName, lPath); + } + + Util.exec(pCallBack); + }); } diff --git a/lib/client/menu.js b/lib/client/menu.js index c4821f0d..11ba9dee 100644 --- a/lib/client/menu.js +++ b/lib/client/menu.js @@ -40,17 +40,8 @@ var CloudCommander, Util, DOM, $; */ function setUploadToItemNames(pCallBack){ CloudCmd.getModules(function(pModules){ - var lStorage = 'storage', - lItems = pModules[lStorage]; - - for(var i = 0, n = pModules.length; i < n; i++ ){ - lItems = pModules[i][lStorage]; - - if(lItems) - break; - } - - UploadToItemNames = lItems || []; + var lStorageObj = Util.findObjByNameInArr( pModules, 'storage' ); + UploadToItemNames = Util.getNamesFromObjArray( lStorageObj.data ) || []; Util.exec(pCallBack); }); diff --git a/lib/client/storage/_dropbox.js b/lib/client/storage/_dropbox.js index cc3fec7e..001a1759 100644 --- a/lib/client/storage/_dropbox.js +++ b/lib/client/storage/_dropbox.js @@ -49,9 +49,13 @@ var CloudCommander, Util, DOM, Dropbox, cb, Client; * @param pData = {key, secret} */ DropBoxStore.login = function(pCallBack){ - CloudCmd.getConfig(function(pConfig){ + CloudCmd.getModules(function(pModules){ + var lStorage = Util.findObjByNameInArr(pModules, 'storage'), + lDropBox = Util.findObjByNameInArr(lStorage.data, 'DropBox'), + lDropBoxKey = lDropBox && lDropBox.data.encodedKey; + Client = new Dropbox.Client({ - key: pConfig.dropbox_encoded_key + key: lDropBoxKey }); //Client.authDriver(new Dropbox.Drivers.Redirect({rememberUser: true})); diff --git a/lib/client/storage/_github.js b/lib/client/storage/_github.js index 8a3ab44a..0efd0e21 100644 --- a/lib/client/storage/_github.js +++ b/lib/client/storage/_github.js @@ -5,9 +5,6 @@ var CloudCommander, Util, DOM, $, Github, cb; "use strict"; var Cache = DOM.Cache, - - APIURL, - AuthURL, GithubLocal, User, GitHubStore = {}; @@ -73,19 +70,15 @@ var CloudCommander, Util, DOM, $, Github, cb; } }; - GitHubStore.getUserData = function(pCallBack){ - var lName, - - lShowUserInfo = function(pError, pData){ - if(!pError){ - lName = pData.name; - Util.log('Hello ' + lName + ' :)!'); - } - else - DOM.Cache.remove('token'); - }; - - User.show(null, lShowUserInfo); + GitHubStore.getUserData = function(pCallBack){ + User.show(null, function(pError, pData){ + if(!pError){ + var lName = pData.name; + Util.log('Hello ' + lName + ' :)!'); + } + else + DOM.Cache.remove('token'); + }); Util.exec(pCallBack); }; diff --git a/lib/util.js b/lib/util.js index e0bf8bf0..ade393c6 100644 --- a/lib/util.js +++ b/lib/util.js @@ -119,12 +119,30 @@ Util = exports || {}; * @param pArg */ Util.log = function(pArg){ - var lArg = pArg, - lConsole = Scope.console, + var lConsole = Scope.console, lDate = '[' + Util.getDate() + '] '; if(lConsole && pArg) - lConsole.log(lDate, lArg); + lConsole.log(lDate, pArg); + + return pArg; + }; + + /** + * function log pArg if it's not empty + * @param pArg + */ + Util.logError = function(pArg){ + var lConsole = Scope.console, + lDate = '[' + Util.getDate() + '] '; + + if(lConsole && pArg){ + var lMsg = pArg.message; + if( lMsg ) + lDate += pArg.message + ' '; + + lConsole.error(lDate, pArg); + } return pArg; }; @@ -451,7 +469,7 @@ Util = exports || {}; lRet = Util.tryCatch(pTryFunc); - return Util.log(lRet); + return Util.logError(lRet); }; /** @@ -520,6 +538,58 @@ Util = exports || {}; return lRet; }; + /** + * get Object Property Names + * @pObj + */ + Util.getObjPropNames = function(pObj){ + var lRet = [], + i = 0; + + for(var lName in pObj) + lRet[i++] = lName; + + return lRet; + }; + + /** + * get values from Object Array name properties + * or + * @pObj + */ + Util.getNamesFromObjArray = function(pArr){ + var lRet = []; + + if(pArr) + for(var i = 0, n = pArr.length; i < n; i++) + lRet[i] = pArr[i].name || pArr[i]; + + return lRet; + }; + + /** + * find object by name in arrray + * or + * @pObj + */ + Util.findObjByNameInArr = function(pArr, pObjName){ + var lRet, + lSearch; + + if(pArr){ + for(var i = 0, n = pArr.length; i < n; i++ ){ + lSearch = pArr[i].name; + + if(lSearch === pObjName) + break; + } + + lRet = pArr[i]; + } + + return lRet; + }; + /** * Gets current time in format hh:mm:ss */ From 8914bb5282f34b7f76b1b1cb5cff845eb778617a Mon Sep 17 00:00:00 2001 From: coderaiser Date: Mon, 28 Jan 2013 09:16:37 -0500 Subject: [PATCH 139/347] refactored --- json/config.json | 2 -- json/modules.json | 20 ++++++++++++++------ lib/client/storage/_gdrive.js | 10 +++++++--- lib/client/storage/_github.js | 7 +++---- lib/server/auth.js | 6 ++++-- lib/server/main.js | 1 + 6 files changed, 29 insertions(+), 17 deletions(-) diff --git a/json/config.json b/json/config.json index 6eb996bf..939121ce 100644 --- a/json/config.json +++ b/json/config.json @@ -8,8 +8,6 @@ "html" : true, "img" : true }, - "gdrive_id" : "255175681917", - "vk_id" : "3336188", "logs" : false, "show_keys_panel" : true, "server" : true, diff --git a/json/modules.json b/json/modules.json index 2275b95b..bf7bc37a 100644 --- a/json/modules.json +++ b/json/modules.json @@ -7,10 +7,10 @@ "data": [{ "name": "DropBox", "data":{ - "key" : "0nd3ssnp5fp7tqs", - "secret" : "r61lxpchmk8l06o", - "encodedKey" : "DkMz4FYHQTA=|GW6pf2dONkrGvckMwBsl1V1vysrCPktPiUWN7UpDjw==", - "chooserKey" : "o7d6llji052vijk" + "key" : "0nd3ssnp5fp7tqs", + "secret" : "r61lxpchmk8l06o", + "encodedKey": "DkMz4FYHQTA=|GW6pf2dONkrGvckMwBsl1V1vysrCPktPiUWN7UpDjw==", + "chooserKey": "o7d6llji052vijk" } }, { "name": "GitHub", @@ -18,9 +18,17 @@ "key" : "891c251b925e4e967fa9", "secret" : "afe9bed1e810c5dc44c4c2a953fc6efb1e5b0545" } + }, { + "name": "GDrive", + "data": { + "id" : "255175681917" + } + }, { + "name": "VK", + "data": { + "id" : "3336188" + } }, - "GDrive", - "VK", "SkyDrive" ] } diff --git a/lib/client/storage/_gdrive.js b/lib/client/storage/_gdrive.js index 3a9c28cd..f01fefa0 100644 --- a/lib/client/storage/_gdrive.js +++ b/lib/client/storage/_gdrive.js @@ -16,9 +16,13 @@ var CloudCommander, Util, DOM, gapi; var lUrl = 'https://apis.google.com/js/client.js'; DOM.jsload(lUrl, function(){ - CloudCmd.getConfig(function(pConfig){ + CloudCmd.getModules(function(pModules){ + var lStorage = Util.findObjByNameInArr(pModules, 'storage'), + lGDrive = Util.findObjByNameInArr(lStorage.data, 'GDrive'), + GDriveId = lGDrive && lGDrive.data.id; /* https://developers.google.com/drive/credentials */ - var lCLIENT_ID = pConfig.gdrive_id + '.apps.googleusercontent.com', + + var lCLIENT_ID = GDriveId + '.apps.googleusercontent.com', lSCOPES = 'https://www.googleapis.com/auth/drive', lParams = { @@ -87,7 +91,7 @@ var CloudCommander, Util, DOM, gapi; if (!pCallBack) pCallBack = function(file) { - console.log(file); + Util.log(file); }; request.execute(pCallBack); diff --git a/lib/client/storage/_github.js b/lib/client/storage/_github.js index 0efd0e21..552fd481 100644 --- a/lib/client/storage/_github.js +++ b/lib/client/storage/_github.js @@ -10,7 +10,7 @@ var CloudCommander, Util, DOM, $, Github, cb; GitHubStore = {}; /* temporary callback function for work with github */ - cb = function (err, data){ console.log(err || data);}; + cb = function (err, data){ Util.log(err || data);}; /* PRIVATE FUNCTIONS */ @@ -118,7 +118,6 @@ var CloudCommander, Util, DOM, $, Github, cb; lHost = CloudCommander.HOST, lOptions = { description: 'Uplouded by Cloud Commander from ' + lHost, - public: true }; @@ -130,8 +129,8 @@ var CloudCommander, Util, DOM, $, Github, cb; lGist.create(lOptions, function(pError, pData){ DOM.Images.hideLoad(); - console.log(pError || pData); - console.log(pData && pData.html_url); + Util.log(pError || pData); + Util.log(pData && pData.html_url); Util.exec(pCallBack); }); diff --git a/lib/server/auth.js b/lib/server/auth.js index 0518eb01..b994834b 100644 --- a/lib/server/auth.js +++ b/lib/server/auth.js @@ -19,7 +19,7 @@ https = main.https, qs = main.querystring, - Config = main.config, + Modules = main.modules, Util = main.util, GithubAuth = { @@ -48,7 +48,9 @@ }; function authenticate(pCode, pCallBack) { - var lGitHub = Config.github, + var lStorage = Util.findObjByNameInArr(Modules, 'storage'), + lGitHub = Util.findObjByNameInArr(lStorage.data, 'GitHub'), + lId = lGitHub && lGitHub.key, lSecret = lGitHub && lGitHub.secret, lEnv = process.env, diff --git a/lib/server/main.js b/lib/server/main.js index f7aebd6b..223e072c 100644 --- a/lib/server/main.js +++ b/lib/server/main.js @@ -71,6 +71,7 @@ /* Main Information */ exports.config = mrequire(JSONDIR + 'config'); + exports.modules = mrequire(JSONDIR + 'modules'); exports.mainpackage = rootrequire('package'); From 1907d9e70042289df9d025643ef34cb23d232084 Mon Sep 17 00:00:00 2001 From: coderaiser Date: Tue, 29 Jan 2013 05:23:39 -0500 Subject: [PATCH 140/347] refactored --- lib/server/auth.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/server/auth.js b/lib/server/auth.js index b994834b..d9132612 100644 --- a/lib/server/auth.js +++ b/lib/server/auth.js @@ -75,12 +75,12 @@ res.setEncoding('utf8'); res.on('data', function (chunk) { body += chunk; }); res.on('end', function() { - pCallBack(qs.parse(body).access_token); + Util.exec(pCallBack, qs.parse(body).access_token); }); }); req.write(data); req.end(); - req.on('error', function(e) { pCallBack(e.message); }); + req.on('error', function(e) { Util.exec(pCallBack, e.message); }); } })(); From d3e749cee1e013e0fc6d28ca9438f213347666fb Mon Sep 17 00:00:00 2001 From: coderaiser Date: Tue, 29 Jan 2013 05:27:33 -0500 Subject: [PATCH 141/347] removed allowed from cache property in config --- json/config.json | 2 +- lib/server.js | 2 +- lib/server/cache.js | 29 +++++++++++++++-------------- 3 files changed, 17 insertions(+), 16 deletions(-) diff --git a/json/config.json b/json/config.json index 939121ce..30a5f000 100644 --- a/json/config.json +++ b/json/config.json @@ -1,7 +1,7 @@ { "api_url" : "/api/v1", "appcache" : false, - "cache" : {"allowed" : false}, + "cache" : false, "minification" : { "js" : false, "css" : false, diff --git a/lib/server.js b/lib/server.js index 9bc75496..7b4fa652 100644 --- a/lib/server.js +++ b/lib/server.js @@ -101,7 +101,7 @@ lMinifyAllowed = lConfig.minification; /* Переменная в которой храниться кэш*/ - lCache.setAllowed(lConfig.cache.allowed); + lCache.setAllowed(lConfig.cache); /* Change default parameters of * js/css/html minification diff --git a/lib/server/cache.js b/lib/server/cache.js index 054f3c66..f2c2cf2a 100644 --- a/lib/server/cache.js +++ b/lib/server/cache.js @@ -17,38 +17,39 @@ */ exports.Cache = { /* приватный переключатель возможности работы с кэшем */ - _allowed :true, + _allowed : true, /* данные в которых храняться файлы * в формате <поле> : <значение> * _data[name]=pData; * одному имени соответствуют * одни данные */ - _data :{}, + _data : {}, /* функция говорит можно ли работать с кэшем */ - isAllowed :(function(){ + isAllowed : (function(){ return this._allowed; }), /* функция устанавливает возможность работать с кэшем */ - setAllowed :(function(pAllowed){ - this._allowed=pAllowed; - }), + setAllowed : function(pAllowed){ + this._allowed = pAllowed; + }, /* Если доступен кэш * сохраняем в него данные */ - set :(function(pName, pData){ + set : function(pName, pData){ if(this._allowed && pName && pData){ this._data[pName]=pData; } - }), + }, /* Если доступен Cache принимаем из него данные*/ - get :(function(pName){ - if(this._allowed && pName){ - return this._data[pName]; - } - else return null; - }), + get : function(pName){ + var lRet; + if(this._allowed && pName) + lRet = this._data[pName]; + + return lRet; + }, /* Функция очищает кэш*/ clear :(function(){ From 7c4e727ed256593d31ea4a0bcbfd33226e8dc28e Mon Sep 17 00:00:00 2001 From: coderaiser Date: Tue, 29 Jan 2013 05:28:06 -0500 Subject: [PATCH 142/347] removed allowed from cache property in config --- ChangeLog | 2 ++ 1 file changed, 2 insertions(+) diff --git a/ChangeLog b/ChangeLog index bca108e7..a89eb006 100644 --- a/ChangeLog +++ b/ChangeLog @@ -110,6 +110,8 @@ with clicks on files. * Removed Octicons font. +* Removed allowed from cache property in config. + 2012.12.12, Version 0.1.8 From 2a4710091862763155409c8f1770e349995ece90 Mon Sep 17 00:00:00 2001 From: coderaiser Date: Tue, 29 Jan 2013 06:09:48 -0500 Subject: [PATCH 143/347] refactored --- html/auth/github.html | 2 +- lib/client.js | 2 +- lib/client/menu.js | 2 +- lib/server.js | 10 +++++++++- lib/util.js | 19 ++++--------------- 5 files changed, 16 insertions(+), 19 deletions(-) diff --git a/html/auth/github.html b/html/auth/github.html index 5d202d2b..01c5f847 100644 --- a/html/auth/github.html +++ b/html/auth/github.html @@ -13,7 +13,7 @@ cloudcmd.getModules(function(pModules){ var lStorage = Util.findObjByNameInArr(pModules, 'storage'), - lGitHub = Util.findObjByNameInArr(lStorage.data, 'GitHub'), + lGitHub = Util.findObjByNameInArr(lStorage, 'GitHub'), GitHub_ID = lGitHub && lGitHub.key; window.location = diff --git a/lib/client.js b/lib/client.js index be63e052..d3cf9f6a 100644 --- a/lib/client.js +++ b/lib/client.js @@ -258,7 +258,7 @@ function initModules(pCallBack){ } var lStorageObj = Util.findObjByNameInArr( pModules, lStorage ), - lMod = Util.getNamesFromObjArray( lStorageObj.data ); + lMod = Util.getNamesFromObjArray( lStorageObj ); for(i = 0, n = lMod.length; i < n; i++){ var lName = lMod[i], diff --git a/lib/client/menu.js b/lib/client/menu.js index 11ba9dee..cdd65227 100644 --- a/lib/client/menu.js +++ b/lib/client/menu.js @@ -41,7 +41,7 @@ var CloudCommander, Util, DOM, $; function setUploadToItemNames(pCallBack){ CloudCmd.getModules(function(pModules){ var lStorageObj = Util.findObjByNameInArr( pModules, 'storage' ); - UploadToItemNames = Util.getNamesFromObjArray( lStorageObj.data ) || []; + UploadToItemNames = Util.getNamesFromObjArray( lStorageObj ) || []; Util.exec(pCallBack); }); diff --git a/lib/server.js b/lib/server.js index 7b4fa652..939f37fa 100644 --- a/lib/server.js +++ b/lib/server.js @@ -1,6 +1,14 @@ (function(){ "use strict"; - + + if(!global.cloudcmd) + return console.log( + '# server.js' + '\n' + + '# -----------' + '\n' + + '# Module is part of Cloud Commander,' + '\n' + + '# easy to use web server.' + '\n' + + '# http://coderaiser.github.com/cloudcmd' + '\n'); + var main = global.cloudcmd.main, /* diff --git a/lib/util.js b/lib/util.js index ade393c6..259cfcfd 100644 --- a/lib/util.js +++ b/lib/util.js @@ -538,20 +538,6 @@ Util = exports || {}; return lRet; }; - /** - * get Object Property Names - * @pObj - */ - Util.getObjPropNames = function(pObj){ - var lRet = [], - i = 0; - - for(var lName in pObj) - lRet[i++] = lName; - - return lRet; - }; - /** * get values from Object Array name properties * or @@ -560,6 +546,9 @@ Util = exports || {}; Util.getNamesFromObjArray = function(pArr){ var lRet = []; + if(pArr && !Util.isArray(pArr)) + pArr = pArr.data; + if(pArr) for(var i = 0, n = pArr.length; i < n; i++) lRet[i] = pArr[i].name || pArr[i]; @@ -575,7 +564,7 @@ Util = exports || {}; Util.findObjByNameInArr = function(pArr, pObjName){ var lRet, lSearch; - + if(pArr){ for(var i = 0, n = pArr.length; i < n; i++ ){ lSearch = pArr[i].name; From 5f6f577d7e6a69d082988df14b43d0c0ab344a3e Mon Sep 17 00:00:00 2001 From: coderaiser Date: Tue, 29 Jan 2013 06:43:47 -0500 Subject: [PATCH 144/347] added ability to hide "Upload To" menu item --- ChangeLog | 3 ++ lib/client/menu.js | 94 ++++++++++++++++++++++++---------------------- 2 files changed, 53 insertions(+), 44 deletions(-) diff --git a/ChangeLog b/ChangeLog index a89eb006..d77d4631 100644 --- a/ChangeLog +++ b/ChangeLog @@ -112,6 +112,9 @@ with clicks on files. * Removed allowed from cache property in config. +* Added ability to hide "Upload To" menu item if +no storage modules setted up in modules.json. + 2012.12.12, Version 0.1.8 diff --git a/lib/client/menu.js b/lib/client/menu.js index cdd65227..5e8de2c5 100644 --- a/lib/client/menu.js +++ b/lib/client/menu.js @@ -112,18 +112,63 @@ var CloudCommander, Util, DOM, $; if(pItems) for(lName in pItems){ - lFunc = pItems[lName]; + lFunc = pItems[lName]; lRet[lName] = getItem(lName, lFunc); } return lRet; } + /** + * download menu item callback + */ + function downloadFromMenu(key, opt){ + DOM.Images.showLoad(); + + var lPath = DOM.getCurrentPath(), + lId = DOM.getIdBySrc(lPath); + + Util.log('downloading file ' + lPath +'...'); + + lPath = lPath + '?download'; + + if(!DOM.getById(lId)){ + var lDownload = DOM.anyload({ + name : 'iframe', + async : false, + className : 'hidden', + src : lPath, + func : Util.retFunc(DOM.Images.hideLoad) + }); + + DOM.Images.hideLoad(); + setTimeout(function() { + document.body.removeChild(lDownload); + }, 10000); + } + else + DOM.Images.showError({ + responseText: 'Error: You trying to' + + 'download same file to often'}); + } + /** * function return configureation for menu */ function getConfig (){ - return{ + var lRet, + lMenuItems = { + 'View' : Util.retExec(showEditor, true), + 'Edit' : Util.retExec(showEditor, false), + 'Delete' : Util.retExec(DOM.promptRemoveCurrent), + }; + + if(UploadToItemNames.length) + lMenuItems['Upload to'] = getUploadToItems(UploadToItemNames); + + lMenuItems.Download = Util.retExec(downloadFromMenu); + + lRet = { // define which elements trigger this menu selector: 'li', @@ -135,49 +180,10 @@ var CloudCommander, Util, DOM, $; }, // define the elements of the menu - items: getAllItems({ - 'View' : Util.retExec(showEditor, true), - 'Edit' : Util.retExec(showEditor, false), - 'Delete' : Util.retExec(DOM.promptRemoveCurrent), - 'Upload to' : getUploadToItems(UploadToItemNames) - /* - [ - 'DropBox', - 'GDrive', - 'GitHub', - 'VK' - ])*/, - 'Download' : function(key, opt){ - DOM.Images.showLoad(); - - var lPath = DOM.getCurrentPath(), - lId = DOM.getIdBySrc(lPath); - - Util.log('downloading file ' + lPath +'...'); - - lPath = lPath + '?download'; - - if(!DOM.getById(lId)){ - var lDownload = DOM.anyload({ - name : 'iframe', - async : false, - className : 'hidden', - src : lPath, - func : Util.retFunc(DOM.Images.hideLoad) - }); - - DOM.Images.hideLoad(); - setTimeout(function() { - document.body.removeChild(lDownload); - }, 10000); - } - else - DOM.Images.showError({ - responseText: 'Error: You trying to' + - 'download same file to often'}); - } - }) + items: getAllItems(lMenuItems) }; + + return lRet; } /** function loads css and js of Menu From dd29c4a8d63f63c07aba9080e59bb872845faacb Mon Sep 17 00:00:00 2001 From: coderaiser Date: Tue, 29 Jan 2013 10:13:25 -0500 Subject: [PATCH 145/347] minor changes --- README.md | 2 +- cloudcmd.js | 7 ++++--- json/config.json | 4 ++-- lib/server.js | 6 ++++-- lib/server/ischanged.js | 25 +++++++++++-------------- lib/server/minify.js | 24 +++++++++++++++++++++--- 6 files changed, 43 insertions(+), 25 deletions(-) diff --git a/README.md b/README.md index 98346c8b..dcce602b 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,5 @@ 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] (//cloudcmd.cloudfoundry.com "cloudfoundry"), diff --git a/cloudcmd.js b/cloudcmd.js index 2d1d555d..b35507a6 100644 --- a/cloudcmd.js +++ b/cloudcmd.js @@ -108,7 +108,8 @@ lOptimizeParams.push(LIBDIR + 'client.js'); if (pAllowed.html) - lOptimizeParams.push(lIndex); + //lOptimizeParams.push(lIndex); + lOptimizeParams = lIndex; if (pAllowed.css) { var lStyles = [{}, {}]; @@ -119,8 +120,8 @@ lOptimizeParams.push(lStyles[1]); } - if (lOptimizeParams.length) - lMinify.optimize(lOptimizeParams,{cache:true}); + //if (lOptimizeParams.length) + //lMinify.optimize(lOptimizeParams); } /** diff --git a/json/config.json b/json/config.json index 30a5f000..9a289e50 100644 --- a/json/config.json +++ b/json/config.json @@ -5,8 +5,8 @@ "minification" : { "js" : false, "css" : false, - "html" : true, - "img" : true + "html" : false, + "img" : false }, "logs" : false, "show_keys_panel" : true, diff --git a/lib/server.js b/lib/server.js index 939f37fa..1907b7b6 100644 --- a/lib/server.js +++ b/lib/server.js @@ -324,8 +324,10 @@ if(lResult) lResult = CloudServer.Minify.optimize(lName, { - cache: true, - callback: function(pFileData){ + name : lName, + request : pReq, + response : pRes, + callback : function(pFileData){ lReadFileFunc_f(undefined, pFileData, false); } }); diff --git a/lib/server/ischanged.js b/lib/server/ischanged.js index a33806e2..12966718 100644 --- a/lib/server/ischanged.js +++ b/lib/server/ischanged.js @@ -1,18 +1,18 @@ (function(){ 'use strict'; - var main = require('./main'), //global.cloudcmd.main, - DIR = main.DIR, + var main = require('./main'), //global.cloudcmd.main, + DIR = main.DIR, - fs = main.fs, - path = main.path, - Util = main.util, + fs = main.fs, + path = main.path, + Util = main.util, /* object contains hashes of files*/ - CHANGESNAME = DIR + 'json/changes', - CHANGES_JSON = CHANGESNAME + '.json', - - Times = main.require(CHANGESNAME) || [], + CHANGESNAME = DIR + 'json/changes', + CHANGES_JSON = CHANGESNAME + '.json', + + Times = main.require(CHANGESNAME) || [], GTimesChanged; exports.isFileChanged = function(pFileName, pLastFile_b, pCallBack){ @@ -29,6 +29,7 @@ } } + console.log(pFileName); fs.stat(pFileName, function(pError, pStat){ var lTimeChanged; @@ -42,7 +43,7 @@ }; lTimeChanged = - GTimesChanged = true; + GTimesChanged = true; } if(pLastFile_b && GTimesChanged) @@ -70,8 +71,4 @@ Util.log('minify: file ' + path.basename(pFileName) + ' writed...'); }); } - - exports.isFileChanged(DIR + 'manifest.yml', true, function(pData){ - console.log(pData); - }); })(); \ No newline at end of file diff --git a/lib/server/minify.js b/lib/server/minify.js index 2485c1f2..cb56bade 100644 --- a/lib/server/minify.js +++ b/lib/server/minify.js @@ -7,7 +7,7 @@ DIR = main.DIR, LIBDIR = main.LIBDIR, HTMLDIR = main.HTMLDIR, - + Util = main.util, Minify = main.require('minify'); exports.Minify = { @@ -53,7 +53,25 @@ this.MinFolder = Minify.MinFolder; if(this._allowed.css || this._allowed.js || this._allowed.html){ - Minify.optimize(pName, pParams); + main.srvrequire("ischanged").isFileChanged(pName, false, function(pChanged){ + if(!pChanged){ + Minify.optimize(pName, pParams); + }else{ + var lDot = pName.lastIndexOf('.'), + lExt = pName.substr(lDot); + + pName = Minify.MinFolder + main.crypto.createHash('sha1') + .update(pName) + .digest('hex') + lExt; + + console.log(pName); + + pParams.callback = null; + if(pParams.request && pParams.response) + main.sendFile(pParams); + } + }); + lResult = true; } } @@ -64,7 +82,7 @@ html : false }; - console.log('Could not minify ' + + Util.log('Could not minify ' + 'without minify module\n' + 'npm i minify'); } From 34a58fb84c823388f5559d6cbb6a2ec7b861130f Mon Sep 17 00:00:00 2001 From: coderaiser Date: Tue, 29 Jan 2013 10:24:15 -0500 Subject: [PATCH 146/347] minor changes --- lib/server/minify.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/server/minify.js b/lib/server/minify.js index cb56bade..dc70a546 100644 --- a/lib/server/minify.js +++ b/lib/server/minify.js @@ -54,7 +54,7 @@ if(this._allowed.css || this._allowed.js || this._allowed.html){ main.srvrequire("ischanged").isFileChanged(pName, false, function(pChanged){ - if(!pChanged){ + if(pChanged){ Minify.optimize(pName, pParams); }else{ var lDot = pName.lastIndexOf('.'), From 9c728bd7495759f16f0288540a4119f2e8d8a5df Mon Sep 17 00:00:00 2001 From: coderaiser Date: Tue, 29 Jan 2013 10:59:30 -0500 Subject: [PATCH 147/347] from now any file minifying only if last modified time was changed --- ChangeLog | 3 +++ json/config.json | 2 +- lib/server/minify.js | 1 + 3 files changed, 5 insertions(+), 1 deletion(-) diff --git a/ChangeLog b/ChangeLog index d77d4631..b38058cd 100644 --- a/ChangeLog +++ b/ChangeLog @@ -115,6 +115,9 @@ with clicks on files. * Added ability to hide "Upload To" menu item if no storage modules setted up in modules.json. +* From now any file minifying only if last modified +time was changed. + 2012.12.12, Version 0.1.8 diff --git a/json/config.json b/json/config.json index 9a289e50..cab6133b 100644 --- a/json/config.json +++ b/json/config.json @@ -3,7 +3,7 @@ "appcache" : false, "cache" : false, "minification" : { - "js" : false, + "js" : true, "css" : false, "html" : false, "img" : false diff --git a/lib/server/minify.js b/lib/server/minify.js index dc70a546..389ab5a1 100644 --- a/lib/server/minify.js +++ b/lib/server/minify.js @@ -67,6 +67,7 @@ console.log(pName); pParams.callback = null; + pParams.name = pName; if(pParams.request && pParams.response) main.sendFile(pParams); } From dd8481f2a42869ffc68455d8ec9dbcbd7987bb83 Mon Sep 17 00:00:00 2001 From: coderaiser Date: Wed, 30 Jan 2013 05:31:15 -0500 Subject: [PATCH 148/347] refactored --- cloudcmd.js | 3 +- lib/server.js | 66 ++++++++++++++++++++------------------------ lib/server/main.js | 44 ++++++++++++++++------------- lib/server/minify.js | 23 +++++---------- 4 files changed, 64 insertions(+), 72 deletions(-) diff --git a/cloudcmd.js b/cloudcmd.js index b35507a6..31783613 100644 --- a/cloudcmd.js +++ b/cloudcmd.js @@ -15,6 +15,7 @@ update = main.update, Server = main.require(LIBDIR + 'server'), + Minify = Server.Minify, srv = Server.CloudServer, Config = main.config; @@ -47,7 +48,7 @@ * меняем в index.html обычные css на * минифицированый */ - if(srv.Minify._allowed.css){ + if(Minify._allowed.css){ var lPath = '/' + srv.Minify.MinFolder.replace(DIR, ''); lReplace_s = ''; lData = Util.removeStr(lData, lReplace_s) diff --git a/lib/server.js b/lib/server.js index 1907b7b6..80f2ff23 100644 --- a/lib/server.js +++ b/lib/server.js @@ -78,11 +78,17 @@ /* модуль для работы с путями*/ Path = main.path, - crypto = main.crypto, Fs = main.fs, /* модуль для работы с файловой системой*/ Querystring = main.querystring, - - /* node v0.4 not contains zlib */ + + /* Обьект для работы с кэшем */ + Cache = main.cache, + + Minify = main.minify, + AppCache = main.appcache, + Socket = main.socket, + + /* node v0.4 not contains zlib */ Zlib = main.zlib; /* модуль для сжатия данных gzip-ом*/ if(!Zlib) Util.log('to use gzip-commpression' + @@ -91,36 +97,25 @@ /* добавляем модуль с функциями */ 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, - lMinifyAllowed = lConfig.minification; /* Переменная в которой храниться кэш*/ - lCache.setAllowed(lConfig.cache); + Cache.setAllowed(lConfig.cache); /* Change default parameters of * js/css/html minification */ - lMinify.setAllowed(lMinifyAllowed); + Minify.setAllowed(lMinifyAllowed); /* Если нужно минимизируем скрипты */ Util.exec(CloudServer.minimize, lMinifyAllowed); /* создаём файл app cache */ - if( lConfig.appcache && lAppCache && lConfig.server ) + if( lConfig.appcache && AppCache && lConfig.server ) Util.exec( pAppCachProcessing ); }; @@ -169,9 +164,9 @@ this.Server.listen(this.Port, this.IP); var lListen; - if(lConfig.socket && CloudServer.Socket){ - lListen = CloudServer.Socket.listen(this.Server); - } + if(lConfig.socket && Socket) + lListen = Socket.listen(this.Server); + Util.log('* Sockets ' + (lListen ? 'running' : 'disabled')); Util.log('* Server running at http://' + this.IP + ':' + this.Port); }, this)); @@ -287,7 +282,7 @@ * сжатый файл - если gzip-поддерживаеться браузером * не сжатый - в обратном случае */ - var lFileData = CloudServer.Cache.get( + var lFileData = Cache.get( CloudServer.Gzip?(lName+'_gzip') : lName); Util.log(Path.basename(lName)); @@ -311,7 +306,7 @@ /* if file not in one of caches * and minimizing setted then minimize it */ - else if(lName.indexOf('min') < 0 && CloudServer.Minify){ + else if(lName.indexOf('min') < 0 && Minify){ var lMin_o = lConfig.minification, lCheck_f = function(pExt){ @@ -323,8 +318,7 @@ (lCheck_f('html') && lMin_o.html); if(lResult) - lResult = CloudServer.Minify.optimize(lName, { - name : lName, + lResult = Minify.optimize(lName, { request : pReq, response : pRes, callback : function(pFileData){ @@ -403,12 +397,9 @@ * загружаем сжатый html-файл в дальнейшем */ - var lMinFileName = CloudServer.Minify.MinFolder + - crypto.createHash('sha1') - .update(main.DIR + 'html/index.html') - .digest('hex') + '.html'; + var lMinFileName = Minify.getName(main.DIR + 'html/index.html'); - CloudServer.INDEX = (CloudServer.Minify._allowed.html ? + CloudServer.INDEX = (Minify._allowed.html ? lMinFileName : CloudServer.INDEX); /* @@ -555,7 +546,7 @@ * с жатием или без, взависимости * от настроек */ - var lFileData = CloudServer.Cache.get(CloudServer.INDEX); + var lFileData = Cache.get(CloudServer.INDEX); /* если их нет там - вычитываем из файла*/ if(!lFileData) { Fs.readFile(CloudServer.INDEX, @@ -603,7 +594,7 @@ lIndexName = lSrv.INDEX; /* и сохраняем в кэш */ - lSrv.Cache.set(lIndexName, pIndex); + Cache.set(lIndexName, pIndex); pIndex = pIndex.toString(); @@ -710,10 +701,10 @@ * если установлена работа с кэшем * сохраняем сжатые данные */ - if(CloudServer.Cache.isAllowed){ + if(Cache.isAllowed){ /* устанавливаем кєш */ Util.log(pName+' gziped'); - CloudServer.Cache.set(pName+'_gzip', pResult); + Cache.set(pName+'_gzip', pResult); } CloudServer.sendResponse(pHeader, pResult, pName); } @@ -750,9 +741,12 @@ * @param pConfig * @param pProcessing {index, appcache, rest} */ - exports.start = function(pConfig, pProcessing){ + exports.start = function(pConfig, pProcessing){ CloudServer.start(pConfig, pProcessing); }; - exports.CloudServer = CloudServer; -})(); \ No newline at end of file + exports.CloudServer = CloudServer; + exports.Minify = Minify; + exports.AppCache = AppCache; + +})(); diff --git a/lib/server/main.js b/lib/server/main.js index 223e072c..649aaffb 100644 --- a/lib/server/main.js +++ b/lib/server/main.js @@ -185,28 +185,34 @@ */ function sendFile(pParams){ var lRet, + lName, lReq, lRes; + + if(pParams){ lName = pParams.name, lReq = pParams.request, - lRes = pParams.response, - - lEnc = lReq.headers['accept-encoding'] || '', - lGzip = lEnc.match(/\bgzip\b/), + lRes = pParams.response; + } + + if(lName && lRes && lReq){ + var lEnc = lReq.headers['accept-encoding'] || '', + lGzip = lEnc.match(/\bgzip\b/), + + lReadStream = fs.createReadStream(lName, { + 'bufferSize': 4 * 1024 + }); - lReadStream = fs.createReadStream(lName, { - 'bufferSize': 4 * 1024 - }); - - lReadStream.on('error', function(pError){ - lRes.writeHead(ERROR, 'OK'); - lRes.end(String(pError)); - }); - - lRes.writeHead(OK, generateHeaders(lName, lGzip) ); - - if (lGzip) - lReadStream = lReadStream.pipe( zlib.createGzip() ); - - lReadStream.pipe(lRes); + 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; } diff --git a/lib/server/minify.js b/lib/server/minify.js index 389ab5a1..477c407c 100644 --- a/lib/server/minify.js +++ b/lib/server/minify.js @@ -54,22 +54,11 @@ if(this._allowed.css || this._allowed.js || this._allowed.html){ main.srvrequire("ischanged").isFileChanged(pName, false, function(pChanged){ - if(pChanged){ + if(pChanged) Minify.optimize(pName, pParams); - }else{ - var lDot = pName.lastIndexOf('.'), - lExt = pName.substr(lDot); - - pName = Minify.MinFolder + main.crypto.createHash('sha1') - .update(pName) - .digest('hex') + lExt; - - console.log(pName); - - pParams.callback = null; - pParams.name = pName; - if(pParams.request && pParams.response) - main.sendFile(pParams); + else{ + pParams.name = Minify.getName(pName); + main.sendFile(pParams); } }); @@ -92,6 +81,8 @@ }, /* minification folder name */ - MinFolder : '' + MinFolder : '', + getName : Minify ? Minify.getName : Util.retFalse }; + })(); From 89112d465a37ea002e89c9519a062f5def975f03 Mon Sep 17 00:00:00 2001 From: coderaiser Date: Wed, 30 Jan 2013 09:38:20 -0500 Subject: [PATCH 149/347] removed ability to cache files in memory --- cloudcmd.js | 14 ++-- json/config.json | 7 +- lib/server.js | 150 +++++++++---------------------------------- lib/server/auth.js | 2 +- lib/server/main.js | 2 +- lib/server/minify.js | 62 +++++++++++------- lib/util.js | 15 +++++ 7 files changed, 96 insertions(+), 156 deletions(-) diff --git a/cloudcmd.js b/cloudcmd.js index 31783613..e204f201 100644 --- a/cloudcmd.js +++ b/cloudcmd.js @@ -23,6 +23,7 @@ * Win32 should be backslashes */ DIR = main.DIR; + console.log(main); readConfig(); Server.start(Config, { appcache : appCacheProcessing, @@ -49,7 +50,7 @@ * минифицированый */ if(Minify._allowed.css){ - var lPath = '/' + srv.Minify.MinFolder.replace(DIR, ''); + var lPath = '/' + Minify.MinFolder.replace(DIR, ''); lReplace_s = ''; lData = Util.removeStr(lData, lReplace_s) .replace('/css/style.css', lPath + 'all.min.css'); @@ -99,7 +100,6 @@ lResetCSS = DIR + 'css/reset.css', lIndex = DIR + 'html/index.html', - lMinify = Server.CloudServer.Minify, lCSSOptions = { img : pAllowed.img, merge : true @@ -109,8 +109,8 @@ lOptimizeParams.push(LIBDIR + 'client.js'); if (pAllowed.html) - //lOptimizeParams.push(lIndex); - lOptimizeParams = lIndex; + lOptimizeParams.push(lIndex); + //lOptimizeParams = lIndex; if (pAllowed.css) { var lStyles = [{}, {}]; @@ -121,8 +121,10 @@ lOptimizeParams.push(lStyles[1]); } - //if (lOptimizeParams.length) - //lMinify.optimize(lOptimizeParams); + if (lOptimizeParams.length) + Minify.optimize(lOptimizeParams, { + force:true + }); } /** diff --git a/json/config.json b/json/config.json index cab6133b..69ce0eb2 100644 --- a/json/config.json +++ b/json/config.json @@ -1,12 +1,11 @@ { "api_url" : "/api/v1", "appcache" : false, - "cache" : false, "minification" : { "js" : true, - "css" : false, - "html" : false, - "img" : false + "css" : true, + "html" : true, + "img" : true }, "logs" : false, "show_keys_panel" : true, diff --git a/lib/server.js b/lib/server.js index 80f2ff23..e588f64c 100644 --- a/lib/server.js +++ b/lib/server.js @@ -75,15 +75,12 @@ DIR = main.Dir, LIBDIR = main.LIBDIR, SRVDIR = main.SRVDIR, - + /* модуль для работы с путями*/ Path = main.path, Fs = main.fs, /* модуль для работы с файловой системой*/ Querystring = main.querystring, - /* Обьект для работы с кэшем */ - Cache = main.cache, - Minify = main.minify, AppCache = main.appcache, Socket = main.socket, @@ -103,9 +100,6 @@ var lConfig = this.Config, lMinifyAllowed = lConfig.minification; - /* Переменная в которой храниться кэш*/ - Cache.setAllowed(lConfig.cache); - /* Change default parameters of * js/css/html minification */ @@ -260,72 +254,28 @@ !Util.isContainStr(lPath, lNoJS_s) && !Util.strCmp(lPath, '/') && !Util.strCmp(lQuery, 'json') ) { - - /* если имена файлов проекта - загружаем их * - * убираем слеш и читаем файл с текущец директории */ - - /* добавляем текующий каталог к пути */ - var lName = '.' + lPath; - Util.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 = Cache.get( - CloudServer.Gzip?(lName+'_gzip') : lName); - - Util.log(Path.basename(lName)); - - /* object thet contains information - * about the source of file data - */ - var lReadFileFunc_f = CloudServer.getReadFileFunc(lName), - /* если там что-то есть передаём данные в функцию readFile */ - lResult = lFileData; - - if(lResult){ - Util.log(lName + ' readed from cache'); - - /* передаём данные с кэша, - * если gzip включен - сжатые - * в обратном случае - несжатые - */ - lReadFileFunc_f(undefined, lFileData, {'cache': true}); - } - /* if file not in one of caches - * and minimizing setted then minimize it - */ - else if(lName.indexOf('min') < 0 && Minify){ - var lMin_o = lConfig.minification, - - lCheck_f = function(pExt){ - return Util.checkExtension(lName,pExt); - }; - - lResult = (lCheck_f('js') && lMin_o.js) || - (lCheck_f('css') && lMin_o.css) || - (lCheck_f('html') && lMin_o.html); - - if(lResult) - lResult = Minify.optimize(lName, { - request : pReq, - response : pRes, - callback : function(pFileData){ - lReadFileFunc_f(undefined, pFileData, false); - } - }); - } + + /* если имена файлов проекта - загружаем их * + * убираем слеш и читаем файл с текущец директории */ + + /* добавляем текующий каталог к пути */ + var lName = '.' + lPath; + Util.log('reading ' + lName); + + /* watching is file changed */ + if(lConfig.appcache) + AppCache.watch(lName); + + Util.log(Path.basename(lName)); + + var lExt = Util.getExtension(lName), + lResult = lExt === '.js' || lExt === '.css' || lExt === '.html'; + + if(lResult) + lResult = Minify.optimize(lName, { + request : pReq, + response : pRes + }); if(!lResult) main.sendFile({ @@ -379,7 +329,6 @@ DirPath = lPath; CloudServer.Responses[DirPath] = pRes; - CloudServer.Statuses[DirPath] = OK; /* saving query of current file */ @@ -542,20 +491,9 @@ lList = '
      ' + lPanel + '
    ' + ''; - /* пробуем достать данные из кэша - * с жатием или без, взависимости - * от настроек - */ - var lFileData = 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{ + Fs.readFile(CloudServer.INDEX, CloudServer.indexReaded(lList)); + + }else{ DirPath = DirPath.substr(DirPath, DirPath.lastIndexOf('/') ); var lQuyery = CloudServer.Queries[DirPath]; @@ -593,9 +531,6 @@ var lSrv = CloudServer, lIndexName = lSrv.INDEX; - /* и сохраняем в кэш */ - Cache.set(lIndexName, pIndex); - pIndex = pIndex.toString(); @@ -638,34 +573,18 @@ /* * @pError - ошибка * @pData - данные - * @pFromCache_o - прочитано с файла, * или из одного из кешей - * Пример {cache: false} */ - var lReadFile = function(pError, pData, pFromCache_o){ + var lReadFile = function(pError, pData){ var lSrv = CloudServer; if (!pError){ Util.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) ) + if( lSrv.Gzip ) /* сжимаем содержимое */ Zlib.gzip(pData,lSrv.getGzipDataFunc(lHeader, pName)); else @@ -695,19 +614,8 @@ */ CloudServer.getGzipDataFunc = function(pHeader, pName){ return function(error, pResult){ - if(!error){ - /* отправляем сжатые данные - * вместе с заголовком - * если установлена работа с кэшем - * сохраняем сжатые данные - */ - if(Cache.isAllowed){ - /* устанавливаем кєш */ - Util.log(pName+' gziped'); - Cache.set(pName+'_gzip', pResult); - } + if(!error) CloudServer.sendResponse(pHeader, pResult, pName); - } else{ Util.log(error); CloudServer.sendResponse(pHeader, error); diff --git a/lib/server/auth.js b/lib/server/auth.js index d9132612..02b03f54 100644 --- a/lib/server/auth.js +++ b/lib/server/auth.js @@ -1,6 +1,6 @@ /* https://github.com/prose/gatekeeper */ (function(){ - "use strict"; + 'use strict'; if(!global.cloudcmd) return console.log( diff --git a/lib/server/main.js b/lib/server/main.js index 649aaffb..af9d5906 100644 --- a/lib/server/main.js +++ b/lib/server/main.js @@ -93,13 +93,13 @@ exports.rest = srvrequire('rest').api, exports.socket = srvrequire('socket'), exports.update = srvrequire('update'), + exports.ischanged = srvrequire('ischanged'); exports.minify = srvrequire('minify').Minify; /* * second initializing after all modules load, so global var is * totally filled of all information that should know all modules */ global.cloudcmd.main = exports; - /** * function do safe require of needed module * @param {Strin} pSrc diff --git a/lib/server/minify.js b/lib/server/minify.js index 477c407c..2f117471 100644 --- a/lib/server/minify.js +++ b/lib/server/minify.js @@ -1,14 +1,29 @@ -/* Обьект для сжатия скриптов и стилей по умолчанию - сжимаються */ +/* Обьект для сжатия скриптов и стилей */ (function(){ - "use strict"; + 'use strict'; - var main = global.cloudcmd.main, - DIR = main.DIR, - LIBDIR = main.LIBDIR, - HTMLDIR = main.HTMLDIR, - Util = main.util, - Minify = main.require('minify'); + if(!global.cloudcmd) + return console.log( + '# minify.js' + '\n' + + '# -----------' + '\n' + + '# Module is part of Cloud Commander,' + '\n' + + '# used for work with minification.' + '\n' + + '# If you wont to see at work set minify' + '\n' + + '# parameters in config.json or environment' + '\n' + + '# and start cloudcmd.js' + '\n' + + '# http://coderaiser.github.com/cloudcmd' + '\n'); + + var main = global.cloudcmd.main, + DIR = main.DIR, + LIBDIR = main.LIBDIR, + HTMLDIR = main.HTMLDIR, + Util = main.util, + Minify = main.require('minify'), + IsChanged = main.ischanged, + + COULD_NOT_MINIFY = 'Could not minify without minify module\n' + + 'npm i minify'; exports.Minify = { /* pathes to directories */ @@ -41,29 +56,32 @@ }), optimize: function(pName, pParams){ - var lResult; + var lRet; if(Minify){ if(!pParams) pParams = {}; - if(this.force) - pParams.force = this.force; + if( !Util.isObject(pName) ){ + pParams.name = Minify.getName(pName); + pParams.calback = function(){ + main.sendFile(pParams); + }; + } + + lRet = this._allowed.css || this._allowed.js || this._allowed.html; if(!this.MinFolder) this.MinFolder = Minify.MinFolder; - if(this._allowed.css || this._allowed.js || this._allowed.html){ - main.srvrequire("ischanged").isFileChanged(pName, false, function(pChanged){ + if(pParams.force) + Minify.optimize(pName, pParams); + else if(lRet) + IsChanged.isFileChanged(pName, false, function(pChanged){ if(pChanged) Minify.optimize(pName, pParams); - else{ - pParams.name = Minify.getName(pName); + else main.sendFile(pParams); - } }); - - lResult = true; - } } else{ this._allowed = { @@ -72,12 +90,10 @@ html : false }; - Util.log('Could not minify ' + - 'without minify module\n' + - 'npm i minify'); + Util.log(COULD_NOT_MINIFY); } - return lResult; + return lRet; }, /* minification folder name */ diff --git a/lib/util.js b/lib/util.js index 259cfcfd..2f4c31b9 100644 --- a/lib/util.js +++ b/lib/util.js @@ -538,6 +538,21 @@ Util = exports || {}; return lRet; }; + /** + * function gets file extension + * @param pFileName + * @return Ext + */ + Util.getExtension = function(pFileName){ + var lRet, lDot; + + if( Util.isString(pFileName) ) + lDot = pFileName.lastIndexOf('.'); + lRet = pFileName.substr(lDot); + + return lRet; + }; + /** * get values from Object Array name properties * or From 1fbe3f2166f0afdb477f9dcd63cf47fee931aa44 Mon Sep 17 00:00:00 2001 From: coderaiser Date: Wed, 30 Jan 2013 09:56:40 -0500 Subject: [PATCH 150/347] minor changes --- ChangeLog | 2 ++ cloudcmd.js | 1 - lib/server/minify.js | 21 ++++++++++++--------- 3 files changed, 14 insertions(+), 10 deletions(-) diff --git a/ChangeLog b/ChangeLog index b38058cd..3331bfd3 100644 --- a/ChangeLog +++ b/ChangeLog @@ -118,6 +118,8 @@ no storage modules setted up in modules.json. * From now any file minifying only if last modified time was changed. +* Removed ability to cache files in memory. + 2012.12.12, Version 0.1.8 diff --git a/cloudcmd.js b/cloudcmd.js index e204f201..694d87ce 100644 --- a/cloudcmd.js +++ b/cloudcmd.js @@ -23,7 +23,6 @@ * Win32 should be backslashes */ DIR = main.DIR; - console.log(main); readConfig(); Server.start(Config, { appcache : appCacheProcessing, diff --git a/lib/server/minify.js b/lib/server/minify.js index 2f117471..014c3b63 100644 --- a/lib/server/minify.js +++ b/lib/server/minify.js @@ -58,22 +58,25 @@ optimize: function(pName, pParams){ var lRet; if(Minify){ - if(!pParams) - pParams = {}; - if( !Util.isObject(pName) ){ - pParams.name = Minify.getName(pName); - pParams.calback = function(){ - main.sendFile(pParams); - }; - } + if( !Util.isObject(pName) ) + pParams.calback = function(pParams){ + var lRet = function(pData){ + if(pData){ + pParams.name = Minify.getName(pData.name); + main.sendFile(pParams); + } + }; + + return lRet; + }(pParams); lRet = this._allowed.css || this._allowed.js || this._allowed.html; if(!this.MinFolder) this.MinFolder = Minify.MinFolder; - if(pParams.force) + if(pParams && pParams.force) Minify.optimize(pName, pParams); else if(lRet) IsChanged.isFileChanged(pName, false, function(pChanged){ From 08717780be9f7bd2fb7315075b9f4dbcf284c3af Mon Sep 17 00:00:00 2001 From: coderaiser Date: Wed, 30 Jan 2013 10:05:29 -0500 Subject: [PATCH 151/347] minor changes --- lib/server/minify.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/server/minify.js b/lib/server/minify.js index 014c3b63..ccf419a1 100644 --- a/lib/server/minify.js +++ b/lib/server/minify.js @@ -60,7 +60,7 @@ if(Minify){ if( !Util.isObject(pName) ) - pParams.calback = function(pParams){ + pParams.callback = function(pParams){ var lRet = function(pData){ if(pData){ pParams.name = Minify.getName(pData.name); From 10d0c0cb251679871f636731b310a3a41f13081e Mon Sep 17 00:00:00 2001 From: coderaiser Date: Wed, 30 Jan 2013 10:20:24 -0500 Subject: [PATCH 152/347] minor changes --- lib/server/minify.js | 18 ++++++------------ 1 file changed, 6 insertions(+), 12 deletions(-) diff --git a/lib/server/minify.js b/lib/server/minify.js index ccf419a1..8f2fd45b 100644 --- a/lib/server/minify.js +++ b/lib/server/minify.js @@ -59,18 +59,12 @@ var lRet; if(Minify){ - if( !Util.isObject(pName) ) - pParams.callback = function(pParams){ - var lRet = function(pData){ - if(pData){ - pParams.name = Minify.getName(pData.name); - main.sendFile(pParams); - } - }; - - return lRet; - }(pParams); - + if( !Util.isObject(pName) ){ + pParams.name = Minify.getName(pName); + pParams.callback = Util.retExec(function(pParams){ + main.sendFile(pParams); + }, pParams); + } lRet = this._allowed.css || this._allowed.js || this._allowed.html; if(!this.MinFolder) From 117cfff228c61635a2a19cad4633ef155d33f1ae Mon Sep 17 00:00:00 2001 From: coderaiser Date: Wed, 30 Jan 2013 14:17:07 -0500 Subject: [PATCH 153/347] refactored --- cloudcmd.js | 3 +-- lib/server.js | 33 ++++++++++++++------------------- lib/server/minify.js | 4 ---- 3 files changed, 15 insertions(+), 25 deletions(-) diff --git a/cloudcmd.js b/cloudcmd.js index 694d87ce..2f3bc023 100644 --- a/cloudcmd.js +++ b/cloudcmd.js @@ -109,7 +109,6 @@ if (pAllowed.html) lOptimizeParams.push(lIndex); - //lOptimizeParams = lIndex; if (pAllowed.css) { var lStyles = [{}, {}]; @@ -122,7 +121,7 @@ if (lOptimizeParams.length) Minify.optimize(lOptimizeParams, { - force:true + force: true }); } diff --git a/lib/server.js b/lib/server.js index e588f64c..d67a8188 100644 --- a/lib/server.js +++ b/lib/server.js @@ -23,11 +23,6 @@ port : 80 }, - /* функция, которая генерирует заголовки - * файлов, отправляемые сервером клиенту - */ - generateHeaders : function () {}, - /* функция высылает * данные клиенту */ @@ -160,7 +155,7 @@ var lListen; if(lConfig.socket && Socket) lListen = Socket.listen(this.Server); - + Util.log('* Sockets ' + (lListen ? 'running' : 'disabled')); Util.log('* Server running at http://' + this.IP + ':' + this.Port); }, this)); @@ -397,7 +392,7 @@ Util.log(pError); CloudServer.Statuses[DirPath] = 404; - CloudServer.sendResponse('OK',pError.toString(), + CloudServer.sendResponse('OK', pError.toString(), DirPath); return; } @@ -405,7 +400,7 @@ /* Если мы не в корне добавляем слеш к будующим ссылкам */ if(DirPath !== '/') DirPath += '/'; - + pFiles = pFiles.sort(); var lCount = 0, @@ -418,9 +413,9 @@ return function(pError, pStat){ if(pError) lStats[pName] = { - 'mode':0, - 'size':0, - 'isDirectory': Util.retFalse + 'mode' : 0, + 'size' : 0, + 'isDirectory' : Util.retFalse }; else @@ -457,8 +452,8 @@ lList; lJSON[0] = { - path:DirPath, - size:'dir' + path : DirPath, + size : 'dir' }; for(var i = 0; i < pFiles.length; i++){ @@ -474,10 +469,10 @@ /* Если папка - выводим пиктограмму папки * * В противоположном случае - файла */ lJSONFile = { - 'name':pFiles[i], - 'size' : (lIsDir ? 'dir' : lStats.size), + 'name' : pFiles[i], + 'size' : lIsDir ? 'dir' : lStats.size, 'uid' : lStats.uid, - 'mode' : lMode}; + 'mode' : lMode}; lJSON[i+1] = lJSONFile; } @@ -538,8 +533,8 @@ lIndexProccessing = lSrv.indexProcessing; lProccessed = Util.exec(lIndexProccessing, { - data : pIndex, - additional : pList + data : pIndex, + additional : pList }); if(lProccessed) @@ -586,7 +581,7 @@ /* если браузер поддерживает gzip-сжатие - сжимаем данные*/ if( lSrv.Gzip ) /* сжимаем содержимое */ - Zlib.gzip(pData,lSrv.getGzipDataFunc(lHeader, pName)); + Zlib.gzip(pData, lSrv.getGzipDataFunc(lHeader, pName)); else /* высылаем несжатые данные */ lSrv.sendResponse(lHeader, pData, pName); diff --git a/lib/server/minify.js b/lib/server/minify.js index 8f2fd45b..de2aeea9 100644 --- a/lib/server/minify.js +++ b/lib/server/minify.js @@ -67,9 +67,6 @@ } lRet = this._allowed.css || this._allowed.js || this._allowed.html; - if(!this.MinFolder) - this.MinFolder = Minify.MinFolder; - if(pParams && pParams.force) Minify.optimize(pName, pParams); else if(lRet) @@ -94,7 +91,6 @@ }, /* minification folder name */ - MinFolder : '', getName : Minify ? Minify.getName : Util.retFalse }; From dc7b0673fd83e9ac5350c5ad638b38610572f497 Mon Sep 17 00:00:00 2001 From: coderaiser Date: Wed, 30 Jan 2013 14:19:10 -0500 Subject: [PATCH 154/347] refactored --- lib/server/minify.js | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/lib/server/minify.js b/lib/server/minify.js index de2aeea9..8f2fd45b 100644 --- a/lib/server/minify.js +++ b/lib/server/minify.js @@ -67,6 +67,9 @@ } lRet = this._allowed.css || this._allowed.js || this._allowed.html; + if(!this.MinFolder) + this.MinFolder = Minify.MinFolder; + if(pParams && pParams.force) Minify.optimize(pName, pParams); else if(lRet) @@ -91,6 +94,7 @@ }, /* minification folder name */ + MinFolder : '', getName : Minify ? Minify.getName : Util.retFalse }; From 75dafc40ffb9b522bad319b8e6de377e2d5ff057 Mon Sep 17 00:00:00 2001 From: coderaiser Date: Thu, 31 Jan 2013 04:19:16 -0500 Subject: [PATCH 155/347] moved extensions from main.js to json/ext.json --- ChangeLog | 2 + json/ext.json | 167 +++++++++++++++++++++++++++++++++++++++++++++ lib/server.js | 2 +- lib/server/main.js | 29 +++----- 4 files changed, 181 insertions(+), 19 deletions(-) create mode 100644 json/ext.json diff --git a/ChangeLog b/ChangeLog index 3331bfd3..abf1f564 100644 --- a/ChangeLog +++ b/ChangeLog @@ -120,6 +120,8 @@ time was changed. * Removed ability to cache files in memory. +* Moved extensions from main.js to json/ext.json. + 2012.12.12, Version 0.1.8 diff --git a/json/ext.json b/json/ext.json new file mode 100644 index 00000000..86c87580 --- /dev/null +++ b/json/ext.json @@ -0,0 +1,167 @@ +{ + ".3gp": "video/3gpp", + ".a": "application/octet-stream", + ".ai": "application/postscript", + ".aif": "audio/x-aiff", + ".aiff": "audio/x-aiff", + ".asc": "application/pgp-signature", + ".asf": "video/x-ms-asf", + ".asm": "text/x-asm", + ".asx": "video/x-ms-asf", + ".atom": "application/atom+xml", + ".au": "audio/basic", + ".avi": "video/x-msvideo", + ".bat": "application/x-msdownload", + ".bin": "application/octet-stream", + ".bmp": "image/bmp", + ".bz2": "application/x-bzip2", + ".c": "text/x-c", + ".cab": "application/vnd.ms-cab-compressed", + ".cc": "text/x-c", + ".chm": "application/vnd.ms-htmlhelp", + ".class": "application/octet-stream", + ".com": "application/x-msdownload", + ".conf": "text/plain", + ".cpp": "text/x-c", + ".crt": "application/x-x509-ca-cert", + ".css": "text/css", + ".csv": "text/csv", + ".cxx": "text/x-c", + ".deb": "application/x-debian-package", + ".der": "application/x-x509-ca-cert", + ".diff": "text/x-diff", + ".djv": "image/vnd.djvu", + ".djvu": "image/vnd.djvu", + ".dll": "application/x-msdownload", + ".dmg": "application/octet-stream", + ".doc": "application/msword", + ".dot": "application/msword", + ".dtd": "application/xml-dtd", + ".dvi": "application/x-dvi", + ".ear": "application/java-archive", + ".eml": "message/rfc822", + ".eps": "application/postscript", + ".exe": "application/x-msdownload", + ".f": "text/x-fortran", + ".f77": "text/x-fortran", + ".f90": "text/x-fortran", + ".flv": "video/x-flv", + ".for": "text/x-fortran", + ".gem": "application/octet-stream", + ".gemspec": "text/x-script.ruby", + ".gif": "image/gif", + ".gz": "application/x-gzip", + ".h": "text/x-c", + ".hh": "text/x-c", + ".htm": "text/html", + ".html": "text/html", + ".ico": "image/vnd.microsoft.icon", + ".ics": "text/calendar", + ".ifb": "text/calendar", + ".iso": "application/octet-stream", + ".jar": "application/java-archive", + ".java": "text/x-java-source", + ".jnlp": "application/x-java-jnlp-file", + ".jpeg": "image/jpeg", + ".jpg": "image/jpeg", + ".js": "application/javascript", + ".json": "application/json", + ".log": "text/plain", + ".m3u": "audio/x-mpegurl", + ".m4v": "video/mp4", + ".man": "text/troff", + ".mathml": "application/mathml+xml", + ".mbox": "application/mbox", + ".mdoc": "text/troff", + ".me": "text/troff", + ".mid": "audio/midi", + ".midi": "audio/midi", + ".mime": "message/rfc822", + ".mml": "application/mathml+xml", + ".mng": "video/x-mng", + ".mov": "video/quicktime", + ".mp3": "audio/mpeg", + ".mp4": "video/mp4", + ".mp4v": "video/mp4", + ".mpeg": "video/mpeg", + ".mpg": "video/mpeg", + ".ms": "text/troff", + ".msi": "application/x-msdownload", + ".odp": "application/vnd.oasis.opendocument.presentation", + ".ods": "application/vnd.oasis.opendocument.spreadsheet", + ".odt": "application/vnd.oasis.opendocument.text", + ".ogg": "application/ogg", + ".p": "text/x-pascal", + ".pas": "text/x-pascal", + ".pbm": "image/x-portable-bitmap", + ".pdf": "application/pdf", + ".pem": "application/x-x509-ca-cert", + ".pgm": "image/x-portable-graymap", + ".pgp": "application/pgp-encrypted", + ".pkg": "application/octet-stream", + ".pl": "text/x-script.perl", + ".pm": "text/x-script.perl-module", + ".png": "image/png", + ".pnm": "image/x-portable-anymap", + ".ppm": "image/x-portable-pixmap", + ".pps": "application/vnd.ms-powerpoint", + ".ppt": "application/vnd.ms-powerpoint", + ".ps": "application/postscript", + ".psd": "image/vnd.adobe.photoshop", + ".py": "text/x-script.python", + ".qt": "video/quicktime", + ".ra": "audio/x-pn-realaudio", + ".rake": "text/x-script.ruby", + ".ram": "audio/x-pn-realaudio", + ".rar": "application/x-rar-compressed", + ".rb": "text/x-script.ruby", + ".rdf": "application/rdf+xml", + ".roff": "text/troff", + ".rpm": "application/x-redhat-package-manager", + ".rss": "application/rss+xml", + ".rtf": "application/rtf", + ".ru": "text/x-script.ruby", + ".s": "text/x-asm", + ".sgm": "text/sgml", + ".sgml": "text/sgml", + ".sh": "application/x-sh", + ".sig": "application/pgp-signature", + ".snd": "audio/basic", + ".so": "application/octet-stream", + ".svg": "image/svg+xml", + ".svgz": "image/svg+xml", + ".swf": "application/x-shockwave-flash", + ".t": "text/troff", + ".tar": "application/x-tar", + ".tbz": "application/x-bzip-compressed-tar", + ".tcl": "application/x-tcl", + ".tex": "application/x-tex", + ".texi": "application/x-texinfo", + ".texinfo": "application/x-texinfo", + ".text": "text/plain", + ".tif": "image/tiff", + ".tiff": "image/tiff", + ".torrent": "application/x-bittorrent", + ".tr": "text/troff", + ".txt": "text/plain", + ".vcf": "text/x-vcard", + ".vcs": "text/x-vcalendar", + ".vrml": "model/vrml", + ".war": "application/java-archive", + ".wav": "audio/x-wav", + ".wma": "audio/x-ms-wma", + ".wmv": "video/x-ms-wmv", + ".wmx": "video/x-ms-wmx", + ".wrl": "model/vrml", + ".wsdl": "application/wsdl+xml", + ".xbm": "image/x-xbitmap", + ".xhtml": "application/xhtml+xml", + ".xls": "application/vnd.ms-excel", + ".xml": "application/xml", + ".xpm": "image/x-xpixmap", + ".xsl": "application/xml", + ".xslt": "application/xslt+xml", + ".yaml": "text/yaml", + ".yml": "text/yaml", + ".zip": "application/zip" +} \ No newline at end of file diff --git a/lib/server.js b/lib/server.js index d67a8188..ec24a739 100644 --- a/lib/server.js +++ b/lib/server.js @@ -293,7 +293,7 @@ CloudServer.NoJS = false; else{ CloudServer.NoJS = true; - lPath = Util.removeStr(lPath, lNoJS_s); + lPath = Util.removeStr(lPath, lNoJS_s); } /* убираем индекс файловой системы */ diff --git a/lib/server/main.js b/lib/server/main.js index af9d5906..c0f3d1ef 100644 --- a/lib/server/main.js +++ b/lib/server/main.js @@ -16,20 +16,10 @@ fs, path, zlib, + ext, OK = 200, - ERROR = 404, - Extensions = { - '.css' : 'text/css', - '.js' : 'text/javascript', - '.png' : 'image/png', - '.json' : 'application/json', - '.html' : 'text/html', - '.woff' : 'font/woff', - '.appcache' : 'text/cache-manifest', - '.mp3' : 'audio/mpeg' - }; - + FILE_NOT_FOUND = 404; /* Native Modules*/ exports.crypto = require('crypto'), @@ -65,13 +55,14 @@ exports.fs.exists = exports.fs.exists || exports.path.exists; /* Needed Modules */ - exports.util = Util = require(LIBDIR + 'util'), + exports.util = Util = require(LIBDIR + 'util'), - exports.zlib = zlib = mrequire('zlib'), + exports.zlib = zlib = mrequire('zlib'), /* Main Information */ - exports.config = mrequire(JSONDIR + 'config'); - exports.modules = mrequire(JSONDIR + 'modules'); + exports.config = jsonrequire('config'); + exports.modules = jsonrequire('modules'); + exports.ext = ext = jsonrequire('ext'); exports.mainpackage = rootrequire('package'); @@ -122,6 +113,8 @@ function srvrequire(pSrc){ return mrequire(SRVDIR + pSrc); } + function jsonrequire(pSrc){ return mrequire(JSONDIR + pSrc);} + /** * function check is current platform is win32 */ @@ -146,7 +139,7 @@ if( Util.strCmp(lExt, '.appcache') ) lCacheControl = 1; - lType = Extensions[lExt] || 'text/plain'; + lType = ext[lExt] || 'text/plain'; if( !Util.isContainStr(lType, 'img') ) lContentEncoding = '; charset=UTF-8'; @@ -202,7 +195,7 @@ }); lReadStream.on('error', function(pError){ - lRes.writeHead(ERROR, 'OK'); + lRes.writeHead(FILE_NOT_FOUND, 'OK'); lRes.end(String(pError)); }); From ebbc8d2ad4c04ec3bd7def2510658e95bcc998fa Mon Sep 17 00:00:00 2001 From: coderaiser Date: Thu, 31 Jan 2013 06:20:37 -0500 Subject: [PATCH 156/347] refactored --- json/config.json | 2 +- lib/client.js | 2 +- lib/server.js | 29 ++++++++++------------------- lib/util.js | 26 +++++++++++++++++++++++++- 4 files changed, 37 insertions(+), 22 deletions(-) diff --git a/json/config.json b/json/config.json index 69ce0eb2..428b8a2c 100644 --- a/json/config.json +++ b/json/config.json @@ -2,7 +2,7 @@ "api_url" : "/api/v1", "appcache" : false, "minification" : { - "js" : true, + "js" : false, "css" : true, "html" : true, "img" : true diff --git a/lib/client.js b/lib/client.js index d3cf9f6a..9ab7b0c5 100644 --- a/lib/client.js +++ b/lib/client.js @@ -452,7 +452,7 @@ CloudCmd._changeLinks = function(pPanelID){ lType = lElement.parentElement.nextSibling; if(lType && lType.textContent === ''){ - lLink = lLink.replace(lNoJS_s,''); + lLink = Util.removeStr(lLink, lNoJS_s); lName += '.json'; } diff --git a/lib/server.js b/lib/server.js index ec24a739..ef0a742f 100644 --- a/lib/server.js +++ b/lib/server.js @@ -262,10 +262,12 @@ AppCache.watch(lName); Util.log(Path.basename(lName)); + var lMin_o = lConfig.minification, + lExt = Util.getExtension(lName), + lResult = lExt === '.js' && lMin_o.js || + lExt === '.css' && lMin_o.css || + lExt === '.html' && lMin_o.html; - var lExt = Util.getExtension(lName), - lResult = lExt === '.js' || lExt === '.css' || lExt === '.html'; - if(lResult) lResult = Minify.optimize(lName, { request : pReq, @@ -279,18 +281,14 @@ response: pRes }); - }else{/* если мы имеем дело с файловой системой*/ - /* если путь не начинаеться с no-js - значит + }else{ + /* если мы имеем дело с файловой системой + * если путь не начинаеться с no-js - значит * js включен */ - /* убираем пометку cloud, без которой c9.io - * не работает поскольку путь из двух слешей - * (/fs/no-js/) - очень короткий, нужно - * длиннее - */ if(lPath.indexOf(lNoJS_s) !== lFS_s.length && lPath !== '/') - CloudServer.NoJS = false; + CloudServer.NoJS = false; else{ CloudServer.NoJS = true; lPath = Util.removeStr(lPath, lNoJS_s); @@ -313,15 +311,8 @@ if(lQuery === 'json') CloudServer.NoJS = false; - - /* если в итоге путь пустой - * делаем его корневым - */ - if (lPath === '') - lPath = '/'; - - DirPath = lPath; + DirPath = lPath || '/'; CloudServer.Responses[DirPath] = pRes; CloudServer.Statuses[DirPath] = OK; diff --git a/lib/util.js b/lib/util.js index 2f4c31b9..efc8582c 100644 --- a/lib/util.js +++ b/lib/util.js @@ -176,7 +176,31 @@ Util = exports || {}; * @param pSubStr */ Util.removeStr = function(pStr, pSubStr){ - return pStr.replace(pSubStr, ''); + var lRet; + if( Util.isString(pStr) ){ + var n = pSubStr.length; + + if( Util.isArray(pSubStr) ) + Util.fori(n, function(i){ + lRet = pStr = pStr.replace(pSubStr[i], ''); + }); + else + lRet = pStr.replace(pSubStr, ''); + } + + return lRet; + }; + + Util.for = function(pI, pN, pFunc){ + if(Util.isFunction(pFunc)) + for(var i = pI, n = pN; i < n; i++) + pFunc(i); + }; + + Util.fori = function(pN, pFunc){ + var lRet = Util.for(0, pN, pFunc); + + return lRet; }; /** From 3235c6a96eef96f318e983e0703a55a2d3f1c06a Mon Sep 17 00:00:00 2001 From: coderaiser Date: Thu, 31 Jan 2013 06:35:00 -0500 Subject: [PATCH 157/347] refactored --- lib/server.js | 11 +++++------ lib/server/main.js | 3 +-- 2 files changed, 6 insertions(+), 8 deletions(-) diff --git a/lib/server.js b/lib/server.js index ef0a742f..27ef516d 100644 --- a/lib/server.js +++ b/lib/server.js @@ -354,7 +354,7 @@ CloudServer._stated = function(pError, pStat){ if(pError){ CloudServer.Statuses[DirPath] = 404; - CloudServer.sendResponse('OK', pError.toString(), DirPath); + CloudServer.sendResponse(null, pError.toString(), DirPath); return; } @@ -383,12 +383,11 @@ Util.log(pError); CloudServer.Statuses[DirPath] = 404; - CloudServer.sendResponse('OK', pError.toString(), - DirPath); + CloudServer.sendResponse(null, pError.toString(), DirPath); return; } - /* Если мы не в корне добавляем слеш к будующим ссылкам */ + /* Если мы не в корне добавляем слеш к будующим ссылкам */ if(DirPath !== '/') DirPath += '/'; @@ -584,9 +583,9 @@ /* sending page not found */ lSrv.Statuses[pName] = 404; - lSrv.sendResponse('file not found', pError.toString(), pName); + lSrv.sendResponse(null, pError.toString(), pName); }else - lSrv.sendResponse('OK', 'passwd.json'); + lSrv.sendResponse(null, 'passwd.json'); } }; diff --git a/lib/server/main.js b/lib/server/main.js index c0f3d1ef..f85a7511 100644 --- a/lib/server/main.js +++ b/lib/server/main.js @@ -133,8 +133,7 @@ lContentEncoding = '', lCacheControl = 0, - lDot = pName.lastIndexOf('.'), - lExt = pName.substr(lDot); + lExt = Util.getExtension(pName); if( Util.strCmp(lExt, '.appcache') ) lCacheControl = 1; From 8a243ac235e3c4b7b029aa069e4764463aaf02d7 Mon Sep 17 00:00:00 2001 From: coderaiser Date: Thu, 31 Jan 2013 08:56:12 -0500 Subject: [PATCH 158/347] refactored --- json/config.json | 2 +- lib/server.js | 48 +++++++++++++++++++----------------------- lib/server/minify.js | 7 ++----- lib/util.js | 50 +++++++++++++++++++++++++------------------- 4 files changed, 53 insertions(+), 54 deletions(-) diff --git a/json/config.json b/json/config.json index 428b8a2c..69ce0eb2 100644 --- a/json/config.json +++ b/json/config.json @@ -2,7 +2,7 @@ "api_url" : "/api/v1", "appcache" : false, "minification" : { - "js" : false, + "js" : true, "css" : true, "html" : true, "img" : true diff --git a/lib/server.js b/lib/server.js index 27ef516d..2064cff7 100644 --- a/lib/server.js +++ b/lib/server.js @@ -262,24 +262,26 @@ AppCache.watch(lName); Util.log(Path.basename(lName)); - var lMin_o = lConfig.minification, + var lMin = lConfig.minification, lExt = Util.getExtension(lName), - lResult = lExt === '.js' && lMin_o.js || - lExt === '.css' && lMin_o.css || - lExt === '.html' && lMin_o.html; + lResult = lExt === '.js' && lMin.js || + lExt === '.css' && lMin.css || + lExt === '.html' && lMin.html; - if(lResult) - lResult = Minify.optimize(lName, { - request : pReq, - response : pRes - }); - if(!lResult) + Util.ifExec(lResult, function(){ main.sendFile({ name: lName, request: pReq, response: pRes }); + }, function(pCallBack){ + Minify.optimize(lName, { + request : pReq, + response : pRes, + callback : pCallBack + }); + }); }else{ /* если мы имеем дело с файловой системой @@ -312,6 +314,7 @@ if(lQuery === 'json') CloudServer.NoJS = false; + /* Если мы не в корне добавляем слеш к будующим ссылкам */ DirPath = lPath || '/'; CloudServer.Responses[DirPath] = pRes; @@ -332,10 +335,9 @@ * загружаем сжатый html-файл в дальнейшем */ - var lMinFileName = Minify.getName(main.DIR + 'html/index.html'); + var lMinName = Minify.getName(main.DIR + 'html/index.html'); - CloudServer.INDEX = (Minify._allowed.html ? - lMinFileName : CloudServer.INDEX); + CloudServer.INDEX = Minify._allowed.html ? lMinName : CloudServer.INDEX; /* * сохраним указатель на response @@ -352,22 +354,18 @@ * @param pStat */ CloudServer._stated = function(pError, pStat){ - if(pError){ - CloudServer.Statuses[DirPath] = 404; - CloudServer.sendResponse(null, pError.toString(), DirPath); - - return; - } - - /* если это каталог - читаем его содержимое */ - if(pStat){ + if(!pError && pStat){ + /* если это каталог - читаем его содержимое */ if(pStat.isDirectory()) Fs.readdir(DirPath, CloudServer._readDir); /* отдаём файл */ - else if(pStat.isFile()){ + else { Fs.readFile(DirPath, CloudServer.getReadFileFunc(DirPath)); Util.log('reading file: '+ DirPath); } + }else{ + CloudServer.Statuses[DirPath] = 404; + CloudServer.sendResponse(null, pError.toString(), DirPath); } }; @@ -387,10 +385,6 @@ return; } - /* Если мы не в корне добавляем слеш к будующим ссылкам */ - if(DirPath !== '/') - DirPath += '/'; - pFiles = pFiles.sort(); var lCount = 0, diff --git a/lib/server/minify.js b/lib/server/minify.js index 8f2fd45b..30978ed4 100644 --- a/lib/server/minify.js +++ b/lib/server/minify.js @@ -59,12 +59,9 @@ var lRet; if(Minify){ - if( !Util.isObject(pName) ){ + if( !Util.isObject(pName) ) pParams.name = Minify.getName(pName); - pParams.callback = Util.retExec(function(pParams){ - main.sendFile(pParams); - }, pParams); - } + lRet = this._allowed.css || this._allowed.js || this._allowed.html; if(!this.MinFolder) diff --git a/lib/util.js b/lib/util.js index efc8582c..d1448d6f 100644 --- a/lib/util.js +++ b/lib/util.js @@ -65,6 +65,28 @@ Util = exports || {}; return lRet; }; + /** for function + * @param pI + * @param pN + * @param pFunc + */ + Util.for = function(pI, pN, pFunc){ + if(Util.isFunction(pFunc)) + for(var i = pI, n = pN; i < n; i++){ + if(pFunc(i)) + break; + } + }; + + /** for function with i = 0 + * @param pN + * @param pFunc + Util.fori = function(pN, pFunc){ + var lRet = Util.for(0, pN, pFunc); + + return lRet; + }; + /** * @pJSON */ @@ -191,17 +213,6 @@ Util = exports || {}; return lRet; }; - Util.for = function(pI, pN, pFunc){ - if(Util.isFunction(pFunc)) - for(var i = pI, n = pN; i < n; i++) - pFunc(i); - }; - - Util.fori = function(pN, pFunc){ - var lRet = Util.for(0, pN, pFunc); - - return lRet; - }; /** * function replase pFrom to pTo in pStr @@ -570,9 +581,10 @@ Util = exports || {}; Util.getExtension = function(pFileName){ var lRet, lDot; - if( Util.isString(pFileName) ) + if( Util.isString(pFileName) ){ lDot = pFileName.lastIndexOf('.'); lRet = pFileName.substr(lDot); + } return lRet; }; @@ -589,8 +601,9 @@ Util = exports || {}; pArr = pArr.data; if(pArr) - for(var i = 0, n = pArr.length; i < n; i++) + Util.fori(pArr.length, function(i){ lRet[i] = pArr[i].name || pArr[i]; + }); return lRet; }; @@ -601,16 +614,11 @@ Util = exports || {}; * @pObj */ Util.findObjByNameInArr = function(pArr, pObjName){ - var lRet, - lSearch; + var lRet; if(pArr){ - for(var i = 0, n = pArr.length; i < n; i++ ){ - lSearch = pArr[i].name; - - if(lSearch === pObjName) - break; - } + for(var i = 0, n = pArr.length; i < n; i++ ) + if(pArr[i].name === pObjName) break; lRet = pArr[i]; } From 7a2ae160c7ca43016e3285d5996a850c3b08a53a Mon Sep 17 00:00:00 2001 From: coderaiser Date: Thu, 31 Jan 2013 09:02:12 -0500 Subject: [PATCH 159/347] refactored --- json/config.json | 2 +- lib/server.js | 11 +++++------ 2 files changed, 6 insertions(+), 7 deletions(-) diff --git a/json/config.json b/json/config.json index 69ce0eb2..a8cd4570 100644 --- a/json/config.json +++ b/json/config.json @@ -3,7 +3,7 @@ "appcache" : false, "minification" : { "js" : true, - "css" : true, + "css" : false, "html" : true, "img" : true }, diff --git a/lib/server.js b/lib/server.js index 2064cff7..fdc4161b 100644 --- a/lib/server.js +++ b/lib/server.js @@ -262,14 +262,13 @@ AppCache.watch(lName); Util.log(Path.basename(lName)); - var lMin = lConfig.minification, - lExt = Util.getExtension(lName), - lResult = lExt === '.js' && lMin.js || - lExt === '.css' && lMin.css || - lExt === '.html' && lMin.html; + var lExt = Util.getExtension(lName), + lResult = lExt === '.js' || + lExt === '.css' || + lExt === '.html'; - Util.ifExec(lResult, function(){ + Util.ifExec(!lResult, function(){ main.sendFile({ name: lName, request: pReq, From 3585d232aa3b3785f833dbaf3a83f0a7403bfef4 Mon Sep 17 00:00:00 2001 From: coderaiser Date: Thu, 31 Jan 2013 09:09:04 -0500 Subject: [PATCH 160/347] minor changes --- json/config.json | 6 +++--- lib/util.js | 1 + 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/json/config.json b/json/config.json index a8cd4570..05c7219d 100644 --- a/json/config.json +++ b/json/config.json @@ -2,9 +2,9 @@ "api_url" : "/api/v1", "appcache" : false, "minification" : { - "js" : true, - "css" : false, - "html" : true, + "js" : false, + "css" : true, + "html" : false, "img" : true }, "logs" : false, diff --git a/lib/util.js b/lib/util.js index d1448d6f..1997ef8a 100644 --- a/lib/util.js +++ b/lib/util.js @@ -81,6 +81,7 @@ Util = exports || {}; /** for function with i = 0 * @param pN * @param pFunc + */ Util.fori = function(pN, pFunc){ var lRet = Util.for(0, pN, pFunc); From ef1d00c4c93b57f30b5824aa0f5338c12584930f Mon Sep 17 00:00:00 2001 From: coderaiser Date: Thu, 31 Jan 2013 09:15:50 -0500 Subject: [PATCH 161/347] minor changes --- lib/server.js | 22 ++++++++++++---------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/lib/server.js b/lib/server.js index fdc4161b..4db81869 100644 --- a/lib/server.js +++ b/lib/server.js @@ -268,18 +268,20 @@ lExt === '.html'; - Util.ifExec(!lResult, function(){ + Util.ifExec(!lResult, function(pParams){ + var lSendName = pParams && pParams.name || lSendName; + main.sendFile({ - name: lName, - request: pReq, - response: pRes + name : lSendName, + request : pReq, + response : pRes + }); + }, function(pCallBack){ + Minify.optimize(lName, { + request : pReq, + response : pRes, + callback : pCallBack }); - }, function(pCallBack){ - Minify.optimize(lName, { - request : pReq, - response : pRes, - callback : pCallBack - }); }); }else{ From 4d10bba92f9cb54f38dd5f206e3fd0dbfe5cbc05 Mon Sep 17 00:00:00 2001 From: coderaiser Date: Thu, 31 Jan 2013 09:30:43 -0500 Subject: [PATCH 162/347] minor changes --- lib/server.js | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/lib/server.js b/lib/server.js index 4db81869..c7d83322 100644 --- a/lib/server.js +++ b/lib/server.js @@ -262,11 +262,11 @@ AppCache.watch(lName); Util.log(Path.basename(lName)); - var lExt = Util.getExtension(lName), - lResult = lExt === '.js' || - lExt === '.css' || - lExt === '.html'; - + var lMin = Minify._allowed, + lExt = Util.getExtension(lName), + lResult = lExt === '.js' && lMin.js || + lExt === '.css' && lMin.css || + lExt === '.html' && lMin.html; Util.ifExec(!lResult, function(pParams){ var lSendName = pParams && pParams.name || lSendName; From 3cffd2a81ec18c7b683ac391626cb8fd2338b8ac Mon Sep 17 00:00:00 2001 From: coderaiser Date: Thu, 31 Jan 2013 09:39:55 -0500 Subject: [PATCH 163/347] refactored --- json/config.json | 2 +- lib/server.js | 2 +- lib/server/minify.js | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/json/config.json b/json/config.json index 05c7219d..2e7884fb 100644 --- a/json/config.json +++ b/json/config.json @@ -2,7 +2,7 @@ "api_url" : "/api/v1", "appcache" : false, "minification" : { - "js" : false, + "js" : true, "css" : true, "html" : false, "img" : true diff --git a/lib/server.js b/lib/server.js index c7d83322..d044b07f 100644 --- a/lib/server.js +++ b/lib/server.js @@ -269,7 +269,7 @@ lExt === '.html' && lMin.html; Util.ifExec(!lResult, function(pParams){ - var lSendName = pParams && pParams.name || lSendName; + var lSendName = pParams && pParams.name || lName; main.sendFile({ name : lSendName, diff --git a/lib/server/minify.js b/lib/server/minify.js index 30978ed4..92f910b9 100644 --- a/lib/server/minify.js +++ b/lib/server/minify.js @@ -74,7 +74,7 @@ if(pChanged) Minify.optimize(pName, pParams); else - main.sendFile(pParams); + main.sendFile(); }); } else{ From 82665accf782dae10b698f9c0c8649667170da67 Mon Sep 17 00:00:00 2001 From: coderaiser Date: Thu, 31 Jan 2013 10:14:36 -0500 Subject: [PATCH 164/347] refactored --- cloudcmd.js | 2 +- lib/server.js | 4 ++-- lib/server/ischanged.js | 28 +++++++++++----------------- lib/server/minify.js | 12 +++++------- 4 files changed, 19 insertions(+), 27 deletions(-) diff --git a/cloudcmd.js b/cloudcmd.js index 2f3bc023..cd21d61f 100644 --- a/cloudcmd.js +++ b/cloudcmd.js @@ -48,7 +48,7 @@ * меняем в index.html обычные css на * минифицированый */ - if(Minify._allowed.css){ + if(Minify.allowed.css){ var lPath = '/' + Minify.MinFolder.replace(DIR, ''); lReplace_s = ''; lData = Util.removeStr(lData, lReplace_s) diff --git a/lib/server.js b/lib/server.js index d044b07f..6db5ba54 100644 --- a/lib/server.js +++ b/lib/server.js @@ -262,7 +262,7 @@ AppCache.watch(lName); Util.log(Path.basename(lName)); - var lMin = Minify._allowed, + var lMin = Minify.allowed, lExt = Util.getExtension(lName), lResult = lExt === '.js' && lMin.js || lExt === '.css' && lMin.css || @@ -338,7 +338,7 @@ var lMinName = Minify.getName(main.DIR + 'html/index.html'); - CloudServer.INDEX = Minify._allowed.html ? lMinName : CloudServer.INDEX; + CloudServer.INDEX = Minify.allowed.html ? lMinName : CloudServer.INDEX; /* * сохраним указатель на response diff --git a/lib/server/ischanged.js b/lib/server/ischanged.js index 12966718..5e99306e 100644 --- a/lib/server/ischanged.js +++ b/lib/server/ischanged.js @@ -12,10 +12,9 @@ CHANGESNAME = DIR + 'json/changes', CHANGES_JSON = CHANGESNAME + '.json', - Times = main.require(CHANGESNAME) || [], - GTimesChanged; + Times = main.require(CHANGESNAME) || []; - exports.isFileChanged = function(pFileName, pLastFile_b, pCallBack){ + exports.isFileChanged = function(pFileName, pCallBack){ var lReadedTime; var i, n = Times.length; @@ -36,23 +35,18 @@ if(!pError){ var lFileTime = pStat.mtime.getTime(); - if(lReadedTime !== lFileTime){ - Times[i] = { + if(lReadedTime !== lFileTime) + lTimeChanged = Times[i] = { name: pFileName, time: lFileTime }; - - lTimeChanged = - GTimesChanged = true; - } - - if(pLastFile_b && GTimesChanged) - writeFile(CHANGES_JSON, Util.stringifyJSON(Times)); - } - else{ - Util.log(pError); - lTimeChanged = true; } + else + lTimeChanged = Util.log(pError); + + console.log(lTimeChanged); + if(lTimeChanged) + writeFile(CHANGES_JSON, Util.stringifyJSON(Times)); Util.exec(pCallBack, lTimeChanged); }); @@ -68,7 +62,7 @@ if(pError) Util.log(pError); else - Util.log('minify: file ' + path.basename(pFileName) + ' writed...'); + Util.log('file ' + path.basename(pFileName) + ' writed...'); }); } })(); \ No newline at end of file diff --git a/lib/server/minify.js b/lib/server/minify.js index 92f910b9..0a1301b3 100644 --- a/lib/server/minify.js +++ b/lib/server/minify.js @@ -29,7 +29,7 @@ /* pathes to directories */ INDEX : HTMLDIR + 'index.html', /* приватный переключатель минимизации */ - _allowed : { + allowed : { css : true, js : true, html : true, @@ -51,18 +51,16 @@ */ setAllowed :(function(pAllowed){ if(pAllowed){ - this._allowed = pAllowed; + this.allowed = pAllowed; } }), optimize: function(pName, pParams){ var lRet; if(Minify){ + pParams.name = Minify.getName(pName); - if( !Util.isObject(pName) ) - pParams.name = Minify.getName(pName); - - lRet = this._allowed.css || this._allowed.js || this._allowed.html; + lRet = this.allowed.css || this.allowed.js || this.allowed.html; if(!this.MinFolder) this.MinFolder = Minify.MinFolder; @@ -78,7 +76,7 @@ }); } else{ - this._allowed = { + this.allowed = { js : false, css : false, html : false From 5578d42b9183ae1d8455e2389185d01d8aed7b78 Mon Sep 17 00:00:00 2001 From: coderaiser Date: Thu, 31 Jan 2013 10:23:26 -0500 Subject: [PATCH 165/347] refactored --- lib/server/minify.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/server/minify.js b/lib/server/minify.js index 0a1301b3..434a2028 100644 --- a/lib/server/minify.js +++ b/lib/server/minify.js @@ -68,7 +68,7 @@ if(pParams && pParams.force) Minify.optimize(pName, pParams); else if(lRet) - IsChanged.isFileChanged(pName, false, function(pChanged){ + IsChanged.isFileChanged(pName, function(pChanged){ if(pChanged) Minify.optimize(pName, pParams); else From d02bbbc04e9451806a42cad040b99e8042a1f0e7 Mon Sep 17 00:00:00 2001 From: coderaiser Date: Thu, 31 Jan 2013 10:30:19 -0500 Subject: [PATCH 166/347] refactored --- lib/server/ischanged.js | 1 - 1 file changed, 1 deletion(-) diff --git a/lib/server/ischanged.js b/lib/server/ischanged.js index 5e99306e..667b4461 100644 --- a/lib/server/ischanged.js +++ b/lib/server/ischanged.js @@ -28,7 +28,6 @@ } } - console.log(pFileName); fs.stat(pFileName, function(pError, pStat){ var lTimeChanged; From f34caa9628ba050ad7292c48a79bb4aa8acae015 Mon Sep 17 00:00:00 2001 From: coderaiser Date: Thu, 31 Jan 2013 10:36:15 -0500 Subject: [PATCH 167/347] refactored --- lib/server/ischanged.js | 1 - lib/server/minify.js | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/lib/server/ischanged.js b/lib/server/ischanged.js index 667b4461..04831d79 100644 --- a/lib/server/ischanged.js +++ b/lib/server/ischanged.js @@ -43,7 +43,6 @@ else lTimeChanged = Util.log(pError); - console.log(lTimeChanged); if(lTimeChanged) writeFile(CHANGES_JSON, Util.stringifyJSON(Times)); diff --git a/lib/server/minify.js b/lib/server/minify.js index 434a2028..21969f07 100644 --- a/lib/server/minify.js +++ b/lib/server/minify.js @@ -72,7 +72,7 @@ if(pChanged) Minify.optimize(pName, pParams); else - main.sendFile(); + main.sendFile(pParams); }); } else{ From 8079b03df95283db62fd4d2ed46c51b1a18250a4 Mon Sep 17 00:00:00 2001 From: coderaiser Date: Fri, 1 Feb 2013 03:55:43 -0500 Subject: [PATCH 168/347] minor changes --- json/config.json | 2 +- lib/server.js | 5 ++++- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/json/config.json b/json/config.json index 2e7884fb..05c7219d 100644 --- a/json/config.json +++ b/json/config.json @@ -2,7 +2,7 @@ "api_url" : "/api/v1", "appcache" : false, "minification" : { - "js" : true, + "js" : false, "css" : true, "html" : false, "img" : true diff --git a/lib/server.js b/lib/server.js index 6db5ba54..4a0aa386 100644 --- a/lib/server.js +++ b/lib/server.js @@ -360,7 +360,7 @@ if(pStat.isDirectory()) Fs.readdir(DirPath, CloudServer._readDir); /* отдаём файл */ - else { + else if(pStat.isFile()){ Fs.readFile(DirPath, CloudServer.getReadFileFunc(DirPath)); Util.log('reading file: '+ DirPath); } @@ -385,6 +385,9 @@ CloudServer.sendResponse(null, pError.toString(), DirPath); return; } + /* Если мы не в корне добавляем слеш к будующим ссылкам */ + if(DirPath !== '/') + DirPath += '/'; pFiles = pFiles.sort(); From 0fe8a351d8d5a95c8a4d22d11c1c4c5a999223ac Mon Sep 17 00:00:00 2001 From: coderaiser Date: Fri, 1 Feb 2013 10:45:09 -0500 Subject: [PATCH 169/347] added commander.js --- lib/server/commander.js | 472 ++++++++++++++++++++++++++++++++++++++++ lib/server/ischanged.js | 7 +- shell/kill.js | 2 +- 3 files changed, 476 insertions(+), 5 deletions(-) create mode 100644 lib/server/commander.js diff --git a/lib/server/commander.js b/lib/server/commander.js new file mode 100644 index 00000000..31d98451 --- /dev/null +++ b/lib/server/commander.js @@ -0,0 +1,472 @@ +(function(){ + 'use strict'; + + if(!global.cloudcmd) + return console.log( + '# commander.js' + '\n' + + '# -----------' + '\n' + + '# Module is part of Cloud Commander,' + '\n' + + '# used for getting dir content.' + '\n' + + '# and forming html content' + '\n' + + '# http://coderaiser.github.com/cloudcmd' + '\n'); + + var main = global.cloudcmd.main, + fs = main.fs, + zlib = main.zlib, + url = main.url, + CloudFunc = main.cloudfunc, + DIR = main.DIR, + LIBDIR = main.LIBDIR, + HTMLDIR = main.HTMLDIR, + Util = main.util, + + NOT_FOUND = 404, + OK = 200, + + NO_JS = CloudFunc.NoJS, + FS = CloudFunc.Fs, + INDEX = HTMLDIR + 'index.html'; + + + exports.sendContent = function(pParams){ + var lRet, + lReq, lRes; + + if(pParams){ + lReq = pParams.request, + lRes = pParams.response; + } + + if(lReq && lRes){ + var lPath = getCleanPath(lReq); + + fs.stat(lPath, function(pError, pStat){ + if(!pError) + if(pStat.isDirectory()) + fs.readdir(lPath, call(readDir, pParams) ); + else + fs.readFile(lPath, getReadFileFunc(lPath)); + else + sendResponse({ + status : NOT_FOUND, + data : pError.toString(), + request : lReq, + response: lRes + }); + }); + + lRet = true; + } + + return lRet; + }; + + + /** + * Функция читает ссылку или выводит информацию об ошибке + * @param pError + * @param pFiles + */ + function readDir(pParams){ + var lRet, + lError, lFiles, lHTTP, + lReq, lRes; + + if(pParams){ + lError = pParams.error; + lFiles = pParams.data; + lHTTP = pParams.params; + + if(lHTTP){ + lReq = lHTTP.request, + lRes = lHTTP.response; + lRet = true; + } + } + + if(lRet && !lError && lFiles){ + lFiles = lFiles.sort(); + + /* Получаем информацию о файлах */ + var n = lFiles.length, + lStats = {}, + lFill = function(){ + fillJSON(lStats, lFiles); + }; + + if(n){ + var i, lDirPath = getDirPath(lReq); + + for(i = 0; i < n; i++){ + var lName = lDirPath + lFiles[i], + lParams = { + name : lFiles[i], + callback : i == n-1 ? lFill : null, + stats : lStats, + }; + + fs.stat( lName, call(getFilesStat, lParams) ); + } + } + else + fillJSON(null, lFiles); + } + else + sendResponse({ + status : NOT_FOUND, + data : lError.toString(), + request : lReq, + response: lRes + }); + } + + /** async getting file states + * and putting it to lStats object + */ + function getFilesStat(pParams){ + var lError, lStat, lData, + lAllStats, lName, lCallBack; + + if(pParams){ + lError = pParams.error; + lStat = pParams.data; + lData = pParams.params; + + if(lData){ + lAllStats = lData.stats; + lName = lData.name; + lCallBack = lData.callback; + } + } + + if(lAllStats) + lAllStats[lName] = !lError ? lStat : { + 'mode' : 0, + 'size' : 0, + 'isDirectory' : Util.retFalse + }; + + Util.exec(lCallBack); + }; + + /** + * Function fill JSON by file stats + * + * @param pStats - object, contain file stats. + * example {'1.txt': stat} + * + * @param pFiles - array of files of current directory + */ + function fillJSON(pParams){ + var lFiles, lAllStats, lHTTP, + i, n, lReq, lRes; + + if(pParams){ + lFiles = pParams.files && (n = lFiles.length || 0); + lAllStats = pParams.stats; + lHTTP = pParams.data; + + if(lHTTP){ + lReq = lHTTP.request, + lRes = lHTTP.response; + } + } + /* данные о файлах в формате JSON*/ + var lJSON = [], + lJSONFile = {}, + lList, + lDirPath = getDirPath(lReq); + + lJSON[0] = { + path : lDirPath, + size : 'dir' + }; + + for( i = 0; i < n; i++){ + /* + *Переводим права доступа в 8-ричную систему + */ + var lName = lFiles[i], + + lMode = (lStats[lName].mode-0).toString(8), + lStats = lAllStats[lName], + lIsDir = lStats.isDirectory(); + + /* Если папка - выводим пиктограмму папки * + * В противоположном случае - файла */ + lJSONFile = { + 'name' : lFiles[i], + 'size' : lIsDir ? 'dir' : lStats.size, + 'uid' : lStats.uid, + 'mode' : lMode}; + + lJSON[i+1] = lJSONFile; + } + + /* если js недоступен + * или отключен выcылаем html-код + * и прописываем соответствующие заголовки + */ + if( noJS(lReq) ){ + var lPanel = CloudFunc.buildFromJSON(lJSON); + lList = '
      ' + lPanel + '
    ' + + ''; + + fs.readFile(INDEX, indexReaded(lList)); + + }else{ + lDirPath = lDirPath.substr(lDirPath, lDirPath.lastIndexOf('/') ); + + /* в обычном режиме(когда js включен + * высылаем json-структуру файлов + * с соответствующими заголовками + */ + lList = JSON.stringify(lJSON); + + /* если браузер поддерживает gzip-сжатие - сжимаем данные*/ + if( isGZIP(lReq) ){ + zlib.gzip(lList, call(lGzipCB, { + + })); + } + /* если не поддерживаеться - отсылаем данные без сжатия*/ + else + sendResponse({ + name : INDEX, + data : lList, + request : lReq, + response: lRes + }); + + } + } + + /** + *@param pList + */ + function indexReaded(pList){ + return function(pError, pIndex){ + if(pError){ + return Util.log(pError); + } + + pIndex = pIndex.toString(); + + + var lProccessed = Util.exec(lIndexProccessing, { + data : pIndex, + additional : pList + }); + + if(lProccessed) + pIndex = lProccessed; + + /* если браузер поддерживает gzip-сжатие - сжимаем данные*/ + if (isGZIP(lReq) ) { + zlib.gzip(pIndex, + call (gzipData, { + request : lReq, + response: lRes + })); + } + + /* если не поддерживаеться - отсылаем данные без сжатия*/ + else + sendResponse({ + name : INDEX, + data : pIndex, + request : lReq, + response: lRes + }); + }; + } + + /** + * Функция генерирует функцию считывания файла + * таким образом, что бы у нас было + * имя считываемого файла + * @param pName - полное имя файла + */ + function getReadFileFunc(pName){ + var lReadFile = function(pError, pData){ + if (!pError){ + Util.log('file ' + pName + ' readed'); + + + /* если браузер поддерживает gzip-сжатие - сжимаем данные*/ + if( isGZIP(lReq) ) + zlib.gzip(pData, call(gzipData, lParams)); + else + sendResponse({ + name : pName, + data : pData, + request : lReq, + response: lRes + }); + } + else + sendResponse({ + name : pName, + status : NOT_FOUND, + data : pError.toString(), + request : lReq, + response: lRes + }); + + return lReadFile; + }; + } + + /** + * Функция получает сжатые данные + * @param pHeader - заголовок файла + * @pName + */ + function gzipData(pParams){ + var lError, lResult, + lSendData, lData, lName, lReq, lRes; + + if(pParams){ + lError = pParams.error; + lResult = pParams.data; + + lData = pParams.params; + + if(lData){ + lReq = lSendData.request, + lRes = lSendData.response; + lName = lSendData.name; + } + } + + lSendData = { + request : lReq, + response: lRes + }; + + if(!lError){ + lSendData.name = lName; + lSendData.data = lResult; + } + else{ + lSendData.status = NOT_FOUND, + lSendData.data = lError.toString(), + } + + sendResponse(lSendData); + } + + /** + * Функция высылает ответ серверу + * @param pHead - заголовок + * @param Data - данные + * @param pName - имя отсылаемого файла + */ + function sendResponse(pParams){ + var lName, lData, lReq, lRes, lStatus; + + if(pParams){ + lName = pParams.name; + lData = pParams.data; + lReq = pParams.request, + lRes = pParams.response; + lStatus = pParams.status || OK; + } + + if(lStatus === NOT_FOUND) + Util.log(lData); + + if(lRes && lReq){ + /* varible contain one of queris: + * download - change content-type for + * make downloading process + * from client js + * json - /no-js/ will be removed, and + * if we will wont get directory + * content wi will set json + * query like this + * ?json + */ + var lPath = lName || getCleanPath(lReq), + lQuery = getQuery(lReq), + lGzip = isGZIP(lReq), + lHead = main.generateHeaders(lPath, lGzip, lQuery); + + lRes.writeHead(lStatus, lHead); + lRes.end(lData); + + Util.log(lPath + ' sended'); + } + } + + function getQuery(pReq){ + var lQuery, lParsedUrl; + + if(pReq){ + lParsedUrl = url.parse(pReq.url); + lQuery = lParsedUrl.query; + } + + return lQuery; + } + + function getPath(pReq){ + var lParsedUrl = url.parse(pReq.url), + lPath = lParsedUrl.pathname; + + return lPath; + } + + function getDirPath(pReq){ + var lPath = getPath(pReq); + + /* Если мы не в корне добавляем слеш к будующим ссылкам */ + if(lPath !== '/') + lPath += '/'; + + return lPath; + } + + function getCleanPath(pReq){ + var lPath = getPath(pReq), + lRet = Util.removeStr(lPath, [NO_JS, FS]) || '/'; + + return lRet; + } + + function call(pFunc, pParams){ + var lFunc = function(pError, pData){ + Util.exec(pFunc, { + error : pError, + data : pData, + params : pParams + }); + }; + + return lFunc; + } + + function noJS(pReq){ + var lNoJS, lPath; + + if(pReq){ + lPath = getPath(pReq); + + lNoJS = Util.isContainStr(lPath, CloudFunc.NoJS) + && lPath === '/' && getQuery() !== 'json'; + } + + return lNoJS; + } + + function isGZIP(pReq){ + var lEnc, lGZIP; + if(pReq){ + lEnc = pReq.headers['accept-encoding'] || '', + lGZIP = lEnc.match(/\bgzip\b/); + } + + return lGZIP; + } + +})(); \ No newline at end of file diff --git a/lib/server/ischanged.js b/lib/server/ischanged.js index 04831d79..9d0cedb8 100644 --- a/lib/server/ischanged.js +++ b/lib/server/ischanged.js @@ -1,15 +1,14 @@ (function(){ 'use strict'; - var main = require('./main'), //global.cloudcmd.main, - DIR = main.DIR, + var main = global.cloudcmd.main, + JSONDIR = main.JSONDIR, fs = main.fs, path = main.path, Util = main.util, - /* object contains hashes of files*/ - CHANGESNAME = DIR + 'json/changes', + CHANGESNAME = JSONDIR + 'changes', CHANGES_JSON = CHANGESNAME + '.json', Times = main.require(CHANGESNAME) || []; diff --git a/shell/kill.js b/shell/kill.js index 402593b3..30a659dc 100644 --- a/shell/kill.js +++ b/shell/kill.js @@ -2,7 +2,7 @@ /* c9.io kill active node process */ (function(){ - "use strict"; + 'use strict'; var exec = require('child_process').exec, lCmd = 'kill -9' + ' ' + /* kill finded process */ From 1e6d3e28aeac116b7f5799dd667952462b0ac40c Mon Sep 17 00:00:00 2001 From: coderaiser Date: Mon, 4 Feb 2013 06:17:09 -0500 Subject: [PATCH 170/347] refactored --- cloudcmd.js | 12 +- lib/server/commander.js | 434 ++++++++++++++++++---------------------- lib/server/main.js | 1 + lib/util.js | 62 +++++- 4 files changed, 272 insertions(+), 237 deletions(-) diff --git a/cloudcmd.js b/cloudcmd.js index cd21d61f..896788d3 100644 --- a/cloudcmd.js +++ b/cloudcmd.js @@ -196,7 +196,17 @@ main.sendFile(pParams); lRet = true; - } + }else if( Util.isContainStr(lName, [CloudFunc.Fs, CloudFunc.NoJS] ) && + Util.strCmp(lName, '/') && + Util.strCmp(lName, 'json') ) { + main.commander({ + request : pParams.request, + response : pParams.response, + indexProcessing : indexProcessing + }); + + lRet = true; + } return lRet; } diff --git a/lib/server/commander.js b/lib/server/commander.js index 31d98451..73375965 100644 --- a/lib/server/commander.js +++ b/lib/server/commander.js @@ -25,27 +25,147 @@ NO_JS = CloudFunc.NoJS, FS = CloudFunc.Fs, - INDEX = HTMLDIR + 'index.html'; - - - exports.sendContent = function(pParams){ - var lRet, - lReq, lRes; + INDEX = HTMLDIR + 'index.html', - if(pParams){ - lReq = pParams.request, - lRes = pParams.response; + IndexProcessingFunc = null; + + function getQuery(pReq){ + var lQuery, lParsedUrl; + + if(pReq){ + lParsedUrl = url.parse(pReq.url); + lQuery = lParsedUrl.query; } - if(lReq && lRes){ - var lPath = getCleanPath(lReq); + return lQuery; + } + + function getPath(pReq){ + var lParsedUrl = url.parse(pReq.url), + lPath = lParsedUrl.pathname; + + return lPath; + } + + function getDirPath(pReq){ + var lPath = getPath(pReq); + + /* Если мы не в корне добавляем слеш к будующим ссылкам */ + if(lPath !== '/') + lPath += '/'; + + return lPath; + } + + function getCleanPath(pReq){ + var lPath = getPath(pReq), + lRet = Util.removeStr(lPath, [NO_JS, FS]) || '/'; + + return lRet; + } + + function call(pFunc, pParams){ + var lFunc = function(pError, pData){ + Util.exec(pFunc, { + error : pError, + data : pData, + params : pParams + }); + }; + + return lFunc; + } + + function noJS(pReq){ + var lNoJS, lPath; + + if(pReq){ + lPath = getPath(pReq); + + lNoJS = Util.isContainStr(lPath, CloudFunc.NoJS) + && lPath === '/' && getQuery() !== 'json'; + } + + return lNoJS; + } + + function isGZIP(pReq){ + var lEnc, lGZIP; + if(pReq){ + lEnc = pReq.headers['accept-encoding'] || '', + lGZIP = lEnc.match(/\bgzip\b/); + } + + return lGZIP; + } + + /** + * Функция высылает ответ серверу + * @param pHead - заголовок + * @param Data - данные + * @param pName - имя отсылаемого файла + */ + function sendResponse(pParams){ + var lRet = Util.checkObjTrue(pParams, + ['name', 'data', 'request', 'response']); + + if(lRet){ + var p = pParams; + + var lPath = p.name || getCleanPath(p.request), + lQuery = getQuery(p.request), + /* download, json */ + lGzip = isGZIP(p.request), + lHead = main.generateHeaders(lPath, lGzip, lQuery); + + /* если браузер поддерживает gzip-сжатие - сжимаем данные*/ + Util.ifExec(!lGzip, + function(pParams){ + var lRet = Util.checkObj(pParams, ['status', 'data']); + + if(lRet){ + p.status = pParams.status; + p.data = pParams.data; + } + + p.response.writeHead(p.status || OK, lHead); + p.request.end(); + + Util.log(lPath + ' sended'); + Util.log( p.status === NOT_FOUND && p.data ); + }, + + function(pCallBack){ + zlib.gzip (call(gzipData, { + callback : pCallBack, + data : p.data + })); + }); + } + } + + exports.sendContent = function(pParams){ + var lRet = Util.checkObj(pParams, + ['processing'], + ['request', 'response']); + + if(lRet){ + var lReq = pParams.request, + lRes = pParams.response, + lPath = getCleanPath(lReq); + + IndexProcessingFunc = pParams.processing; fs.stat(lPath, function(pError, pStat){ if(!pError) if(pStat.isDirectory()) fs.readdir(lPath, call(readDir, pParams) ); else - fs.readFile(lPath, getReadFileFunc(lPath)); + main.sendFile({ + name : lPath, + request : lReq, + response : lRes + }); else sendResponse({ status : NOT_FOUND, @@ -147,7 +267,7 @@ }; Util.exec(lCallBack); - }; + } /** * Function fill JSON by file stats @@ -182,7 +302,7 @@ size : 'dir' }; - for( i = 0; i < n; i++){ + for( i = 0; i < n; i++ ){ /* *Переводим права доступа в 8-ричную систему */ @@ -212,7 +332,12 @@ lList = '
      ' + lPanel + '
    ' + ''; - fs.readFile(INDEX, indexReaded(lList)); + fs.readFile(INDEX, call(readFile, { + list : lList, + request : lReq, + response : lRes + }) + ); }else{ lDirPath = lDirPath.substr(lDirPath, lDirPath.lastIndexOf('/') ); @@ -222,99 +347,61 @@ * с соответствующими заголовками */ lList = JSON.stringify(lJSON); - - /* если браузер поддерживает gzip-сжатие - сжимаем данные*/ - if( isGZIP(lReq) ){ - zlib.gzip(lList, call(lGzipCB, { - - })); - } - /* если не поддерживаеться - отсылаем данные без сжатия*/ - else - sendResponse({ - name : INDEX, - data : lList, - request : lReq, - response: lRes - }); + + sendResponse({ + name : INDEX, + data : lList, + request : lReq, + response: lRes + }); } } - /** - *@param pList - */ - function indexReaded(pList){ - return function(pError, pIndex){ - if(pError){ - return Util.log(pError); - } - - pIndex = pIndex.toString(); - - - var lProccessed = Util.exec(lIndexProccessing, { - data : pIndex, - additional : pList - }); - - if(lProccessed) - pIndex = lProccessed; - - /* если браузер поддерживает gzip-сжатие - сжимаем данные*/ - if (isGZIP(lReq) ) { - zlib.gzip(pIndex, - call (gzipData, { - request : lReq, - response: lRes - })); - } - - /* если не поддерживаеться - отсылаем данные без сжатия*/ - else - sendResponse({ - name : INDEX, - data : pIndex, - request : lReq, - response: lRes - }); - }; - } /** * Функция генерирует функцию считывания файла - * таким образом, что бы у нас было + * таким образом, что бы у нас было * имя считываемого файла * @param pName - полное имя файла */ - function getReadFileFunc(pName){ - var lReadFile = function(pError, pData){ - if (!pError){ - Util.log('file ' + pName + ' readed'); + function readFile(pParams){ + var lRet = Util.checkObj(pParams, + ['error', 'data']); + + if(lRet) + lRet = Util.checkObj(pParams.params, + ['callback'], ['request', 'response', 'name', 'list']); + + if(lRet){ + var p = pParams, + c = pParams.params, + lParams = { + request : c.request, + response: c.response + }, - - /* если браузер поддерживает gzip-сжатие - сжимаем данные*/ - if( isGZIP(lReq) ) - zlib.gzip(pData, call(gzipData, lParams)); - else - sendResponse({ - name : pName, - data : pData, - request : lReq, - response: lRes - }); - } - else - sendResponse({ - name : pName, - status : NOT_FOUND, - data : pError.toString(), - request : lReq, - response: lRes + lProccessed = Util.exec(IndexProcessingFunc, { + data : p.data, + additional : c.list }); - return lReadFile; - }; + if(lProccessed) + p.data = lProccessed; + + if (!p.Error){ + lParams.name = c.Name; + lParams.data = p.data; + + Util.log('file ' + c.name + ' readed'); + } + else{ + lParams.status = NOT_FOUND; + lParams.data = p.error.toString(); + } + + sendResponse(lParams); + } } /** @@ -323,150 +410,29 @@ * @pName */ function gzipData(pParams){ - var lError, lResult, - lSendData, lData, lName, lReq, lRes; + var lRet = Util.checkObj(pParams, + ['error', 'data']); - if(pParams){ - lError = pParams.error; - lResult = pParams.data; + if(lRet) + lRet = Util.checkObj(pParams.params, + ['callback'], ['name']); + + if(lRet){ + var p = pParams, + c = pParams.params, + lData = {}; - lData = pParams.params; - - if(lData){ - lReq = lSendData.request, - lRes = lSendData.response; - lName = lSendData.name; + if(!p.error){ + lData.name = c.name; + lData.data = p.data; + } + else{ + lData.status = NOT_FOUND, + lData.data = p.error.toString(), } - } - lSendData = { - request : lReq, - response: lRes - }; - - if(!lError){ - lSendData.name = lName; - lSendData.data = lResult; + Util.exec(c.callback, lData); } - else{ - lSendData.status = NOT_FOUND, - lSendData.data = lError.toString(), - } - - sendResponse(lSendData); - } - - /** - * Функция высылает ответ серверу - * @param pHead - заголовок - * @param Data - данные - * @param pName - имя отсылаемого файла - */ - function sendResponse(pParams){ - var lName, lData, lReq, lRes, lStatus; - - if(pParams){ - lName = pParams.name; - lData = pParams.data; - lReq = pParams.request, - lRes = pParams.response; - lStatus = pParams.status || OK; - } - - if(lStatus === NOT_FOUND) - Util.log(lData); - - if(lRes && lReq){ - /* varible contain one of queris: - * download - change content-type for - * make downloading process - * from client js - * json - /no-js/ will be removed, and - * if we will wont get directory - * content wi will set json - * query like this - * ?json - */ - var lPath = lName || getCleanPath(lReq), - lQuery = getQuery(lReq), - lGzip = isGZIP(lReq), - lHead = main.generateHeaders(lPath, lGzip, lQuery); - - lRes.writeHead(lStatus, lHead); - lRes.end(lData); - - Util.log(lPath + ' sended'); - } - } - - function getQuery(pReq){ - var lQuery, lParsedUrl; - - if(pReq){ - lParsedUrl = url.parse(pReq.url); - lQuery = lParsedUrl.query; - } - - return lQuery; - } - - function getPath(pReq){ - var lParsedUrl = url.parse(pReq.url), - lPath = lParsedUrl.pathname; - - return lPath; - } - - function getDirPath(pReq){ - var lPath = getPath(pReq); - - /* Если мы не в корне добавляем слеш к будующим ссылкам */ - if(lPath !== '/') - lPath += '/'; - - return lPath; - } - - function getCleanPath(pReq){ - var lPath = getPath(pReq), - lRet = Util.removeStr(lPath, [NO_JS, FS]) || '/'; - - return lRet; - } - - function call(pFunc, pParams){ - var lFunc = function(pError, pData){ - Util.exec(pFunc, { - error : pError, - data : pData, - params : pParams - }); - }; - - return lFunc; - } - - function noJS(pReq){ - var lNoJS, lPath; - - if(pReq){ - lPath = getPath(pReq); - - lNoJS = Util.isContainStr(lPath, CloudFunc.NoJS) - && lPath === '/' && getQuery() !== 'json'; - } - - return lNoJS; - } - - function isGZIP(pReq){ - var lEnc, lGZIP; - if(pReq){ - lEnc = pReq.headers['accept-encoding'] || '', - lGZIP = lEnc.match(/\bgzip\b/); - } - - return lGZIP; } })(); \ No newline at end of file diff --git a/lib/server/main.js b/lib/server/main.js index f85a7511..5ce87e9d 100644 --- a/lib/server/main.js +++ b/lib/server/main.js @@ -86,6 +86,7 @@ exports.update = srvrequire('update'), exports.ischanged = srvrequire('ischanged'); exports.minify = srvrequire('minify').Minify; + exports.commander = srvrequire('commander'); /* * second initializing after all modules load, so global var is * totally filled of all information that should know all modules diff --git a/lib/util.js b/lib/util.js index 1997ef8a..bc06c648 100644 --- a/lib/util.js +++ b/lib/util.js @@ -39,7 +39,7 @@ Util = exports || {}; * @param pName - получает имя файла * @param pExt - расширение */ - Util.checkExtension = function(pName, pExt){ + Util.checkExtension = function(pName, pExt){ var lRet = false, lLength = pName.length; /* длина имени*/ /* если длина имени больше @@ -65,7 +65,65 @@ Util = exports || {}; return lRet; }; - /** for function + + /** + * Check is Properties exists and they are true if neaded + * + * @param pObj + * @param pPropArr + * @param pTrueArr + */ + Util.checkObj = function(pObj, pPropArr, pTrueArr){ + var lRet = Util.isObject(pObj), + i, n; + if( lRet ){ + lRet = Util.isArray(pPropArr); + if(lRet){ + n = pPropArr.length; + for(i = 0; i < n; i++){ + var lProp = pPropArr[i]; + lRet = pObj.hasOwnProperty( lProp ); + if(!lRet){ + Util.logError(lProp + ' not in Obj!'); + Util.log(pObj); + break; + } + } + } + + if( lRet && Util.isArray(pTrueArr) ) + lRet = Util.checkObjTrue( pObj, pTrueArr ); + } + + return lRet; + }; + + /** + * Check is Properties exists and they are true + * + * @param pObj + * @param pPropArr + * @param pTrueArr + */ + Util.checkObjTrue = function(pObj, pTrueArr){ + var lRet = Util.isObject(pObj), + i, n; + if( lRet ){ + lRet = Util.isArray(pTrueArr); + if(lRet){ + n = pTrueArr.length; + for(i = 0; i < n; i++){ + lRet = pObj[pTrueArr]; + if( !lRet) + break; + } + } + } + + return lRet; + }; + + /** for function * @param pI * @param pN * @param pFunc From da3a7617c3b193b1aeb2b09fbdb35edad142d3a7 Mon Sep 17 00:00:00 2001 From: coderaiser Date: Mon, 4 Feb 2013 06:27:15 -0500 Subject: [PATCH 171/347] refactored --- cloudcmd.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cloudcmd.js b/cloudcmd.js index 896788d3..228e6bdf 100644 --- a/cloudcmd.js +++ b/cloudcmd.js @@ -196,8 +196,8 @@ main.sendFile(pParams); lRet = true; - }else if( Util.isContainStr(lName, [CloudFunc.Fs, CloudFunc.NoJS] ) && - Util.strCmp(lName, '/') && + }else if( Util.isContainStr(lName, [CloudFunc.Fs, CloudFunc.NoJS] ) || + Util.strCmp(lName, '/') || Util.strCmp(lName, 'json') ) { main.commander({ request : pParams.request, From 2a874769659b9e7ed3acb0b82f3fa552ba1b7848 Mon Sep 17 00:00:00 2001 From: coderaiser Date: Mon, 4 Feb 2013 06:32:19 -0500 Subject: [PATCH 172/347] minor changes --- cloudcmd.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cloudcmd.js b/cloudcmd.js index 228e6bdf..7fdd9fef 100644 --- a/cloudcmd.js +++ b/cloudcmd.js @@ -199,7 +199,7 @@ }else if( Util.isContainStr(lName, [CloudFunc.Fs, CloudFunc.NoJS] ) || Util.strCmp(lName, '/') || Util.strCmp(lName, 'json') ) { - main.commander({ + main.commander.sendContent({ request : pParams.request, response : pParams.response, indexProcessing : indexProcessing From 81784ae1c3b75a151f43f444858d6fa8f2768023 Mon Sep 17 00:00:00 2001 From: coderaiser Date: Mon, 4 Feb 2013 06:38:24 -0500 Subject: [PATCH 173/347] minor changes --- lib/server/commander.js | 233 ++++++++++++++++++++-------------------- 1 file changed, 117 insertions(+), 116 deletions(-) diff --git a/lib/server/commander.js b/lib/server/commander.js index 73375965..c4eb7c01 100644 --- a/lib/server/commander.js +++ b/lib/server/commander.js @@ -29,120 +29,6 @@ IndexProcessingFunc = null; - function getQuery(pReq){ - var lQuery, lParsedUrl; - - if(pReq){ - lParsedUrl = url.parse(pReq.url); - lQuery = lParsedUrl.query; - } - - return lQuery; - } - - function getPath(pReq){ - var lParsedUrl = url.parse(pReq.url), - lPath = lParsedUrl.pathname; - - return lPath; - } - - function getDirPath(pReq){ - var lPath = getPath(pReq); - - /* Если мы не в корне добавляем слеш к будующим ссылкам */ - if(lPath !== '/') - lPath += '/'; - - return lPath; - } - - function getCleanPath(pReq){ - var lPath = getPath(pReq), - lRet = Util.removeStr(lPath, [NO_JS, FS]) || '/'; - - return lRet; - } - - function call(pFunc, pParams){ - var lFunc = function(pError, pData){ - Util.exec(pFunc, { - error : pError, - data : pData, - params : pParams - }); - }; - - return lFunc; - } - - function noJS(pReq){ - var lNoJS, lPath; - - if(pReq){ - lPath = getPath(pReq); - - lNoJS = Util.isContainStr(lPath, CloudFunc.NoJS) - && lPath === '/' && getQuery() !== 'json'; - } - - return lNoJS; - } - - function isGZIP(pReq){ - var lEnc, lGZIP; - if(pReq){ - lEnc = pReq.headers['accept-encoding'] || '', - lGZIP = lEnc.match(/\bgzip\b/); - } - - return lGZIP; - } - - /** - * Функция высылает ответ серверу - * @param pHead - заголовок - * @param Data - данные - * @param pName - имя отсылаемого файла - */ - function sendResponse(pParams){ - var lRet = Util.checkObjTrue(pParams, - ['name', 'data', 'request', 'response']); - - if(lRet){ - var p = pParams; - - var lPath = p.name || getCleanPath(p.request), - lQuery = getQuery(p.request), - /* download, json */ - lGzip = isGZIP(p.request), - lHead = main.generateHeaders(lPath, lGzip, lQuery); - - /* если браузер поддерживает gzip-сжатие - сжимаем данные*/ - Util.ifExec(!lGzip, - function(pParams){ - var lRet = Util.checkObj(pParams, ['status', 'data']); - - if(lRet){ - p.status = pParams.status; - p.data = pParams.data; - } - - p.response.writeHead(p.status || OK, lHead); - p.request.end(); - - Util.log(lPath + ' sended'); - Util.log( p.status === NOT_FOUND && p.data ); - }, - - function(pCallBack){ - zlib.gzip (call(gzipData, { - callback : pCallBack, - data : p.data - })); - }); - } - } exports.sendContent = function(pParams){ var lRet = Util.checkObj(pParams, @@ -427,12 +313,127 @@ lData.data = p.data; } else{ - lData.status = NOT_FOUND, - lData.data = p.error.toString(), + lData.status = NOT_FOUND; + lData.data = p.error.toString(); } Util.exec(c.callback, lData); } } + function getQuery(pReq){ + var lQuery, lParsedUrl; + + if(pReq){ + lParsedUrl = url.parse(pReq.url); + lQuery = lParsedUrl.query; + } + + return lQuery; + } + + function getPath(pReq){ + var lParsedUrl = url.parse(pReq.url), + lPath = lParsedUrl.pathname; + + return lPath; + } + + function getDirPath(pReq){ + var lPath = getPath(pReq); + + /* Если мы не в корне добавляем слеш к будующим ссылкам */ + if(lPath !== '/') + lPath += '/'; + + return lPath; + } + + function getCleanPath(pReq){ + var lPath = getPath(pReq), + lRet = Util.removeStr(lPath, [NO_JS, FS]) || '/'; + + return lRet; + } + + function call(pFunc, pParams){ + var lFunc = function(pError, pData){ + Util.exec(pFunc, { + error : pError, + data : pData, + params : pParams + }); + }; + + return lFunc; + } + + function noJS(pReq){ + var lNoJS, lPath; + + if(pReq){ + lPath = getPath(pReq); + + lNoJS = Util.isContainStr(lPath, CloudFunc.NoJS) + && lPath === '/' && getQuery() !== 'json'; + } + + return lNoJS; + } + + function isGZIP(pReq){ + var lEnc, lGZIP; + if(pReq){ + lEnc = pReq.headers['accept-encoding'] || '', + lGZIP = lEnc.match(/\bgzip\b/); + } + + return lGZIP; + } + + /** + * Функция высылает ответ серверу + * @param pHead - заголовок + * @param Data - данные + * @param pName - имя отсылаемого файла + */ + function sendResponse(pParams){ + var lRet = Util.checkObjTrue(pParams, + ['name', 'data', 'request', 'response']); + + if(lRet){ + var p = pParams; + + var lPath = p.name || getCleanPath(p.request), + lQuery = getQuery(p.request), + /* download, json */ + lGzip = isGZIP(p.request), + lHead = main.generateHeaders(lPath, lGzip, lQuery); + + /* если браузер поддерживает gzip-сжатие - сжимаем данные*/ + Util.ifExec(!lGzip, + function(pParams){ + var lRet = Util.checkObj(pParams, ['status', 'data']); + + if(lRet){ + p.status = pParams.status; + p.data = pParams.data; + } + + p.response.writeHead(p.status || OK, lHead); + p.request.end(); + + Util.log(lPath + ' sended'); + Util.log( p.status === NOT_FOUND && p.data ); + }, + + function(pCallBack){ + zlib.gzip (call(gzipData, { + callback : pCallBack, + data : p.data + })); + }); + } + } + })(); \ No newline at end of file From 99108ab40ade7aba285a8c26780696a511a1347d Mon Sep 17 00:00:00 2001 From: coderaiser Date: Mon, 4 Feb 2013 06:43:06 -0500 Subject: [PATCH 174/347] minor changes --- cloudcmd.js | 19 ++++++++++--------- lib/server/commander.js | 8 +++++--- 2 files changed, 15 insertions(+), 12 deletions(-) diff --git a/cloudcmd.js b/cloudcmd.js index 7fdd9fef..6c30dc4d 100644 --- a/cloudcmd.js +++ b/cloudcmd.js @@ -17,11 +17,14 @@ Server = main.require(LIBDIR + 'server'), Minify = Server.Minify, srv = Server.CloudServer, - Config = main.config; + Config = main.config, + + REQUEST = 'request', + RESPONSE = 'response'; /* reinit main dir os if we on * Win32 should be backslashes */ - DIR = main.DIR; + DIR = main.DIR, readConfig(); Server.start(Config, { @@ -199,14 +202,12 @@ }else if( Util.isContainStr(lName, [CloudFunc.Fs, CloudFunc.NoJS] ) || Util.strCmp(lName, '/') || Util.strCmp(lName, 'json') ) { - main.commander.sendContent({ - request : pParams.request, - response : pParams.response, - indexProcessing : indexProcessing + lRet = main.commander.sendContent({ + request : pParams[REQUEST], + response : pParams[RESPONSE], + processing : indexProcessing }); - - lRet = true; - } + } return lRet; } diff --git a/lib/server/commander.js b/lib/server/commander.js index c4eb7c01..3d830f81 100644 --- a/lib/server/commander.js +++ b/lib/server/commander.js @@ -26,6 +26,8 @@ NO_JS = CloudFunc.NoJS, FS = CloudFunc.Fs, INDEX = HTMLDIR + 'index.html', + REQUEST = 'request', + RESPONSE = 'response', IndexProcessingFunc = null; @@ -33,7 +35,7 @@ exports.sendContent = function(pParams){ var lRet = Util.checkObj(pParams, ['processing'], - ['request', 'response']); + [REQUEST, RESPONSE]); if(lRet){ var lReq = pParams.request, @@ -257,7 +259,7 @@ if(lRet) lRet = Util.checkObj(pParams.params, - ['callback'], ['request', 'response', 'name', 'list']); + ['callback'], [REQUEST, RESPONSE, 'name', 'list']); if(lRet){ var p = pParams, @@ -399,7 +401,7 @@ */ function sendResponse(pParams){ var lRet = Util.checkObjTrue(pParams, - ['name', 'data', 'request', 'response']); + ['name', 'data', REQUEST, RESPONSE]); if(lRet){ var p = pParams; From 53228519b08cb4b046de30ed24dc696dcb3de34e Mon Sep 17 00:00:00 2001 From: coderaiser Date: Mon, 4 Feb 2013 06:46:11 -0500 Subject: [PATCH 175/347] minor changes --- lib/util.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/lib/util.js b/lib/util.js index bc06c648..f796c196 100644 --- a/lib/util.js +++ b/lib/util.js @@ -113,7 +113,9 @@ Util = exports || {}; if(lRet){ n = pTrueArr.length; for(i = 0; i < n; i++){ - lRet = pObj[pTrueArr]; + var lProp = pTrueArr[i]; + lRet = pObj[lProp]; + if( !lRet) break; } From d80eae425bfaf5535ebe760efa570f9180f182a6 Mon Sep 17 00:00:00 2001 From: coderaiser Date: Mon, 4 Feb 2013 06:59:40 -0500 Subject: [PATCH 176/347] minor changes --- lib/server/commander.js | 25 +++++++++++++------------ 1 file changed, 13 insertions(+), 12 deletions(-) diff --git a/lib/server/commander.js b/lib/server/commander.js index 3d830f81..6ac56411 100644 --- a/lib/server/commander.js +++ b/lib/server/commander.js @@ -98,8 +98,15 @@ /* Получаем информацию о файлах */ var n = lFiles.length, lStats = {}, + lFilesData = { + files : lFiles, + stats : lStats, + request : lReq, + response : lRes + }, + lFill = function(){ - fillJSON(lStats, lFiles); + fillJSON(lFilesData); }; if(n){ @@ -117,7 +124,7 @@ } } else - fillJSON(null, lFiles); + fillJSON(lFilesData); } else sendResponse({ @@ -166,18 +173,14 @@ * @param pFiles - array of files of current directory */ function fillJSON(pParams){ - var lFiles, lAllStats, lHTTP, + var lFiles, lAllStats, i, n, lReq, lRes; if(pParams){ lFiles = pParams.files && (n = lFiles.length || 0); lAllStats = pParams.stats; - lHTTP = pParams.data; - - if(lHTTP){ - lReq = lHTTP.request, - lRes = lHTTP.response; - } + lReq = pParams.request, + lRes = pParams.response; } /* данные о файлах в формате JSON*/ var lJSON = [], @@ -191,9 +194,7 @@ }; for( i = 0; i < n; i++ ){ - /* - *Переводим права доступа в 8-ричную систему - */ + /* Переводим права доступа в 8-ричную систему */ var lName = lFiles[i], lMode = (lStats[lName].mode-0).toString(8), From 259612eafdf24f20ee6a6b9347b61864d7246b22 Mon Sep 17 00:00:00 2001 From: coderaiser Date: Mon, 4 Feb 2013 07:51:32 -0500 Subject: [PATCH 177/347] minor changes --- lib/server/commander.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/server/commander.js b/lib/server/commander.js index 6ac56411..b52840bc 100644 --- a/lib/server/commander.js +++ b/lib/server/commander.js @@ -177,7 +177,8 @@ i, n, lReq, lRes; if(pParams){ - lFiles = pParams.files && (n = lFiles.length || 0); + lFiles = pParams.files; + lFiles &&(n = lFiles.length || 0); lAllStats = pParams.stats; lReq = pParams.request, lRes = pParams.response; From ea6665433ea797f68607a7b6ce4a3abaaad22737 Mon Sep 17 00:00:00 2001 From: coderaiser Date: Mon, 4 Feb 2013 07:55:21 -0500 Subject: [PATCH 178/347] minor changes --- lib/server/commander.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/server/commander.js b/lib/server/commander.js index b52840bc..5c219127 100644 --- a/lib/server/commander.js +++ b/lib/server/commander.js @@ -198,8 +198,8 @@ /* Переводим права доступа в 8-ричную систему */ var lName = lFiles[i], - lMode = (lStats[lName].mode-0).toString(8), lStats = lAllStats[lName], + lMode = (lStats[lName].mode-0).toString(8), lIsDir = lStats.isDirectory(); /* Если папка - выводим пиктограмму папки * From af17e70499ba63408a330b8b27c4422a8251e085 Mon Sep 17 00:00:00 2001 From: coderaiser Date: Mon, 4 Feb 2013 07:58:01 -0500 Subject: [PATCH 179/347] minor changes --- lib/server/commander.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/server/commander.js b/lib/server/commander.js index 5c219127..878a7017 100644 --- a/lib/server/commander.js +++ b/lib/server/commander.js @@ -199,7 +199,7 @@ var lName = lFiles[i], lStats = lAllStats[lName], - lMode = (lStats[lName].mode-0).toString(8), + lMode = (lStats.mode-0).toString(8), lIsDir = lStats.isDirectory(); /* Если папка - выводим пиктограмму папки * From 76d62958d125ac2f4de2611e45aa28b39b7f13f2 Mon Sep 17 00:00:00 2001 From: coderaiser Date: Mon, 4 Feb 2013 08:00:38 -0500 Subject: [PATCH 180/347] minor changes --- lib/server/commander.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/server/commander.js b/lib/server/commander.js index 878a7017..1bed650c 100644 --- a/lib/server/commander.js +++ b/lib/server/commander.js @@ -325,7 +325,7 @@ } } - function getQuery(pReq){ + function getQuery(pReq){ var lQuery, lParsedUrl; if(pReq){ @@ -379,7 +379,7 @@ lPath = getPath(pReq); lNoJS = Util.isContainStr(lPath, CloudFunc.NoJS) - && lPath === '/' && getQuery() !== 'json'; + || lPath === '/' || getQuery() !== 'json'; } return lNoJS; From 7f395aa8801811f1b488ea8bc11325641a20fb7b Mon Sep 17 00:00:00 2001 From: coderaiser Date: Mon, 4 Feb 2013 08:02:08 -0500 Subject: [PATCH 181/347] minor changes --- lib/server/commander.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/server/commander.js b/lib/server/commander.js index 1bed650c..ad4af01c 100644 --- a/lib/server/commander.js +++ b/lib/server/commander.js @@ -237,9 +237,9 @@ * с соответствующими заголовками */ lList = JSON.stringify(lJSON); - + sendResponse({ - name : INDEX, + name : lDirPath + '.json', data : lList, request : lReq, response: lRes From 50d84bb239608f0d6c8f9df99bc01d2681a7a2f9 Mon Sep 17 00:00:00 2001 From: coderaiser Date: Mon, 4 Feb 2013 08:05:05 -0500 Subject: [PATCH 182/347] minor changes --- lib/server/commander.js | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/lib/server/commander.js b/lib/server/commander.js index ad4af01c..4627e84f 100644 --- a/lib/server/commander.js +++ b/lib/server/commander.js @@ -197,9 +197,9 @@ for( i = 0; i < n; i++ ){ /* Переводим права доступа в 8-ричную систему */ var lName = lFiles[i], - + lStats = lAllStats[lName], - lMode = (lStats.mode-0).toString(8), + lMode = (lStats.mode - 0).toString(8), lIsDir = lStats.isDirectory(); /* Если папка - выводим пиктограмму папки * @@ -208,7 +208,8 @@ 'name' : lFiles[i], 'size' : lIsDir ? 'dir' : lStats.size, 'uid' : lStats.uid, - 'mode' : lMode}; + 'mode' : lMode + }; lJSON[i+1] = lJSONFile; } @@ -223,9 +224,9 @@ ''; fs.readFile(INDEX, call(readFile, { - list : lList, - request : lReq, - response : lRes + list : lList, + request : lReq, + response : lRes }) ); From e2fadc1af039172c61100bce17cb2b8f3ff56fd3 Mon Sep 17 00:00:00 2001 From: coderaiser Date: Mon, 4 Feb 2013 08:10:10 -0500 Subject: [PATCH 183/347] minor changes --- lib/server/commander.js | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/lib/server/commander.js b/lib/server/commander.js index 4627e84f..1a8aa19a 100644 --- a/lib/server/commander.js +++ b/lib/server/commander.js @@ -257,12 +257,11 @@ * @param pName - полное имя файла */ function readFile(pParams){ - var lRet = Util.checkObj(pParams, - ['error', 'data']); + var lRet = Util.checkObj(pParams, + ['error', 'data']) && - if(lRet) - lRet = Util.checkObj(pParams.params, - ['callback'], [REQUEST, RESPONSE, 'name', 'list']); + Util.checkObjTrue(pParams.params, + [REQUEST, RESPONSE, 'name', 'list']); if(lRet){ var p = pParams, From c0014a13fa0a062996e7e9c2a81e7f6f6eb159ae Mon Sep 17 00:00:00 2001 From: coderaiser Date: Mon, 4 Feb 2013 08:11:49 -0500 Subject: [PATCH 184/347] minor changes --- lib/server/commander.js | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/lib/server/commander.js b/lib/server/commander.js index 1a8aa19a..028d7fd6 100644 --- a/lib/server/commander.js +++ b/lib/server/commander.js @@ -194,13 +194,16 @@ size : 'dir' }; + var lName, lStats, lMode, lIsDir; for( i = 0; i < n; i++ ){ /* Переводим права доступа в 8-ричную систему */ - var lName = lFiles[i], - - lStats = lAllStats[lName], + lName = lFiles[i], + lStats = lAllStats[lName], + + if(lStats){ lMode = (lStats.mode - 0).toString(8), lIsDir = lStats.isDirectory(); + } /* Если папка - выводим пиктограмму папки * * В противоположном случае - файла */ From 45e36c7a47a47aed4e157aaf923b395d58dc15b9 Mon Sep 17 00:00:00 2001 From: coderaiser Date: Mon, 4 Feb 2013 08:29:34 -0500 Subject: [PATCH 185/347] minor changes --- lib/server/commander.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/server/commander.js b/lib/server/commander.js index 028d7fd6..e4c500ef 100644 --- a/lib/server/commander.js +++ b/lib/server/commander.js @@ -198,7 +198,7 @@ for( i = 0; i < n; i++ ){ /* Переводим права доступа в 8-ричную систему */ lName = lFiles[i], - lStats = lAllStats[lName], + lStats = lAllStats[lName]; if(lStats){ lMode = (lStats.mode - 0).toString(8), From 011ebea0b09eacd7c96c39150f4c64f27c970761 Mon Sep 17 00:00:00 2001 From: coderaiser Date: Mon, 4 Feb 2013 08:40:55 -0500 Subject: [PATCH 186/347] minor changes --- lib/server/commander.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/lib/server/commander.js b/lib/server/commander.js index e4c500ef..466e219f 100644 --- a/lib/server/commander.js +++ b/lib/server/commander.js @@ -261,10 +261,10 @@ */ function readFile(pParams){ var lRet = Util.checkObj(pParams, - ['error', 'data']) && + ['error', 'data', 'params']) && Util.checkObjTrue(pParams.params, - [REQUEST, RESPONSE, 'name', 'list']); + [REQUEST, RESPONSE, 'list']); if(lRet){ var p = pParams, @@ -283,7 +283,7 @@ p.data = lProccessed; if (!p.Error){ - lParams.name = c.Name; + lParams.name = INDEX; lParams.data = p.data; Util.log('file ' + c.name + ' readed'); @@ -304,7 +304,7 @@ */ function gzipData(pParams){ var lRet = Util.checkObj(pParams, - ['error', 'data']); + ['error', 'data', 'params']); if(lRet) lRet = Util.checkObj(pParams.params, From ad96569e9d9e07ef41faae627e7dc74cdd1f7e9d Mon Sep 17 00:00:00 2001 From: coderaiser Date: Mon, 4 Feb 2013 09:43:27 -0500 Subject: [PATCH 187/347] refactored --- lib/server/commander.js | 44 ++++++++++++++++++++--------------------- 1 file changed, 21 insertions(+), 23 deletions(-) diff --git a/lib/server/commander.js b/lib/server/commander.js index 466e219f..22af20c3 100644 --- a/lib/server/commander.js +++ b/lib/server/commander.js @@ -115,8 +115,9 @@ for(i = 0; i < n; i++){ var lName = lDirPath + lFiles[i], lParams = { + callback : lFill, + count : n, name : lFiles[i], - callback : i == n-1 ? lFill : null, stats : lStats, }; @@ -139,31 +140,28 @@ * and putting it to lStats object */ function getFilesStat(pParams){ - var lError, lStat, lData, - lAllStats, lName, lCallBack; - - if(pParams){ - lError = pParams.error; - lStat = pParams.data; - lData = pParams.params; + var lRet = Util.checkObj(pParams, + ['error', 'data', 'params']) && - if(lData){ - lAllStats = lData.stats; - lName = lData.name; - lCallBack = lData.callback; - } + Util.checkObj(pParams.params, + ['callback'], ['stats', 'name', 'count']); + + if(lRet){ + var p = pParams, + c = p.params; + + if(c.stats) + c.stats[c.name] = !p.error ? p.data : { + 'mode' : 0, + 'size' : 0, + 'isDirectory' : Util.retFalse + }; + + if(c.count === Object.keys(c.stats).length) + Util.exec(c.callback); } - - if(lAllStats) - lAllStats[lName] = !lError ? lStat : { - 'mode' : 0, - 'size' : 0, - 'isDirectory' : Util.retFalse - }; - - Util.exec(lCallBack); } - + /** * Function fill JSON by file stats * From 6ddd3af1015baecf4ac93e0c030321cf81d64c97 Mon Sep 17 00:00:00 2001 From: coderaiser Date: Mon, 4 Feb 2013 09:47:46 -0500 Subject: [PATCH 188/347] refactored --- lib/server/commander.js | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/lib/server/commander.js b/lib/server/commander.js index 22af20c3..c7701c1f 100644 --- a/lib/server/commander.js +++ b/lib/server/commander.js @@ -266,8 +266,11 @@ if(lRet){ var p = pParams, - c = pParams.params, - lParams = { + c = pParams.params; + + p.data = p.data.toString(); + + var lParams = { request : c.request, response: c.response }, From 47e27bd8531951a5e447750badfe6f37c4a99b16 Mon Sep 17 00:00:00 2001 From: coderaiser Date: Mon, 4 Feb 2013 09:50:25 -0500 Subject: [PATCH 189/347] refactored --- lib/server/commander.js | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/lib/server/commander.js b/lib/server/commander.js index c7701c1f..b851edb1 100644 --- a/lib/server/commander.js +++ b/lib/server/commander.js @@ -309,17 +309,15 @@ if(lRet) lRet = Util.checkObj(pParams.params, - ['callback'], ['name']); + ['callback']); if(lRet){ var p = pParams, c = pParams.params, lData = {}; - if(!p.error){ - lData.name = c.name; + if(!p.error) lData.data = p.data; - } else{ lData.status = NOT_FOUND; lData.data = p.error.toString(); From 8679db23a58eb75fd0d01cf164dd3ec6ee66c5b4 Mon Sep 17 00:00:00 2001 From: coderaiser Date: Mon, 4 Feb 2013 09:52:15 -0500 Subject: [PATCH 190/347] refactored --- lib/server/commander.js | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/lib/server/commander.js b/lib/server/commander.js index b851edb1..f3e78139 100644 --- a/lib/server/commander.js +++ b/lib/server/commander.js @@ -314,16 +314,16 @@ if(lRet){ var p = pParams, c = pParams.params, - lData = {}; + lParams = {}; if(!p.error) - lData.data = p.data; + lParams.data = p.data; else{ - lData.status = NOT_FOUND; - lData.data = p.error.toString(); + lParams.status = NOT_FOUND; + lParams.data = p.error.toString(); } - Util.exec(c.callback, lData); + Util.exec(c.callback, lParams); } } From 5555a96f5da9a76d27e09ac8f048fdcddff4f3dd Mon Sep 17 00:00:00 2001 From: coderaiser Date: Mon, 4 Feb 2013 09:56:29 -0500 Subject: [PATCH 191/347] refactored --- lib/server/commander.js | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/lib/server/commander.js b/lib/server/commander.js index f3e78139..9fa949da 100644 --- a/lib/server/commander.js +++ b/lib/server/commander.js @@ -434,10 +434,9 @@ }, function(pCallBack){ - zlib.gzip (call(gzipData, { - callback : pCallBack, - data : p.data - })); + zlib.gzip (p.data, call(gzipData, { + callback : pCallBack + })); }); } } From 8cb3ecc099ded9f445d893b16c8a6a6221644369 Mon Sep 17 00:00:00 2001 From: coderaiser Date: Mon, 4 Feb 2013 09:57:36 -0500 Subject: [PATCH 192/347] minor changes --- lib/server/commander.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/server/commander.js b/lib/server/commander.js index 9fa949da..7b3fee62 100644 --- a/lib/server/commander.js +++ b/lib/server/commander.js @@ -419,7 +419,7 @@ /* если браузер поддерживает gzip-сжатие - сжимаем данные*/ Util.ifExec(!lGzip, function(pParams){ - var lRet = Util.checkObj(pParams, ['status', 'data']); + var lRet = Util.checkObj(pParams, ['data']); if(lRet){ p.status = pParams.status; From bdb9caa08314b53a45807da6a2eb83837acf086b Mon Sep 17 00:00:00 2001 From: coderaiser Date: Mon, 4 Feb 2013 09:58:52 -0500 Subject: [PATCH 193/347] minor changes --- lib/server/commander.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/server/commander.js b/lib/server/commander.js index 7b3fee62..d3860d52 100644 --- a/lib/server/commander.js +++ b/lib/server/commander.js @@ -427,7 +427,7 @@ } p.response.writeHead(p.status || OK, lHead); - p.request.end(); + p.response.end(); Util.log(lPath + ' sended'); Util.log( p.status === NOT_FOUND && p.data ); From b0c68f306de9ceecfb8a8088bb5a5ecb78e7c0c5 Mon Sep 17 00:00:00 2001 From: coderaiser Date: Mon, 4 Feb 2013 10:01:25 -0500 Subject: [PATCH 194/347] minor changes --- lib/server/commander.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/server/commander.js b/lib/server/commander.js index d3860d52..08433ebc 100644 --- a/lib/server/commander.js +++ b/lib/server/commander.js @@ -427,7 +427,7 @@ } p.response.writeHead(p.status || OK, lHead); - p.response.end(); + p.response.end(p.data); Util.log(lPath + ' sended'); Util.log( p.status === NOT_FOUND && p.data ); From 1eb21b98b2a8d21c878f40f6ecd28b34f49c1a18 Mon Sep 17 00:00:00 2001 From: coderaiser Date: Tue, 5 Feb 2013 03:40:14 -0500 Subject: [PATCH 195/347] refactored --- cloudcmd.js | 11 +- json/config.json | 2 +- lib/server.js | 445 +++------------------------------------- lib/server/commander.js | 3 +- 4 files changed, 43 insertions(+), 418 deletions(-) diff --git a/cloudcmd.js b/cloudcmd.js index 6c30dc4d..e1d373a5 100644 --- a/cloudcmd.js +++ b/cloudcmd.js @@ -20,11 +20,12 @@ Config = main.config, REQUEST = 'request', - RESPONSE = 'response'; + RESPONSE = 'response', + INDEX = DIR + 'html/index.html'; /* reinit main dir os if we on * Win32 should be backslashes */ - DIR = main.DIR, + DIR = main.DIR; readConfig(); Server.start(Config, { @@ -100,7 +101,6 @@ var lOptimizeParams = [], lStyleCSS = DIR + 'css/style.css', lResetCSS = DIR + 'css/reset.css', - lIndex = DIR + 'html/index.html', lCSSOptions = { img : pAllowed.img, @@ -111,7 +111,7 @@ lOptimizeParams.push(LIBDIR + 'client.js'); if (pAllowed.html) - lOptimizeParams.push(lIndex); + lOptimizeParams.push(INDEX); if (pAllowed.css) { var lStyles = [{}, {}]; @@ -205,7 +205,8 @@ lRet = main.commander.sendContent({ request : pParams[REQUEST], response : pParams[RESPONSE], - processing : indexProcessing + processing : indexProcessing, + index : Minify.allowed.html ? Minify.getName(INDEX) : INDEX }); } diff --git a/json/config.json b/json/config.json index 05c7219d..428b8a2c 100644 --- a/json/config.json +++ b/json/config.json @@ -4,7 +4,7 @@ "minification" : { "js" : false, "css" : true, - "html" : false, + "html" : true, "img" : true }, "logs" : false, diff --git a/lib/server.js b/lib/server.js index 4a0aa386..8c6da4f8 100644 --- a/lib/server.js +++ b/lib/server.js @@ -127,7 +127,6 @@ var lConfig = this.Config; - CloudServer.indexProcessing = pProcessing.index; CloudServer.rest = pProcessing.rest; CloudServer.route = pProcessing.route; CloudServer.minimize = pProcessing.minimize; @@ -181,41 +180,16 @@ var lConfig = CloudServer.Config, lURL = main.url, lParsedUrl = lURL.parse(pReq.url), - lPath = 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) - Util.log('query = ' + lQuery); - + lPath = lParsedUrl.pathname; + /* added supporting of Russian language in directory names */ lPath = Querystring.unescape(lPath); Util.log('pathname: ' + lPath); - - /* получаем поддерживаемые браузером кодировки*/ - 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; Util.log("request for " + lPath + " received..."); @@ -240,391 +214,40 @@ return; } - /* если в пути нет информации ни о ФС, - * ни об отсутствии js, - * ни о том, что это корневой - * каталог - загружаем файлы проэкта - */ - if ( !Util.isContainStr(lPath, lFS_s) && - !Util.isContainStr(lPath, lNoJS_s) && - !Util.strCmp(lPath, '/') && - !Util.strCmp(lQuery, 'json') ) { + /* если имена файлов проекта - загружаем их * + * убираем слеш и читаем файл с текущец директории */ + + /* добавляем текующий каталог к пути */ + var lName = '.' + lPath; + Util.log('reading ' + lName); + + /* watching is file changed */ + if(lConfig.appcache) + AppCache.watch(lName); + + Util.log(Path.basename(lName)); + + var lMin = Minify.allowed, + lExt = Util.getExtension(lName), + lResult = lExt === '.js' && lMin.js || + lExt === '.css' && lMin.css || + lExt === '.html' && lMin.html; + + Util.ifExec(!lResult, function(pParams){ + var lSendName = pParams && pParams.name || lName; - /* если имена файлов проекта - загружаем их * - * убираем слеш и читаем файл с текущец директории */ - - /* добавляем текующий каталог к пути */ - var lName = '.' + lPath; - Util.log('reading ' + lName); - - /* watching is file changed */ - if(lConfig.appcache) - AppCache.watch(lName); - - Util.log(Path.basename(lName)); - var lMin = Minify.allowed, - lExt = Util.getExtension(lName), - lResult = lExt === '.js' && lMin.js || - lExt === '.css' && lMin.css || - lExt === '.html' && lMin.html; - - Util.ifExec(!lResult, function(pParams){ - var lSendName = pParams && pParams.name || lName; - - main.sendFile({ - name : lSendName, - request : pReq, - response : pRes - }); - }, function(pCallBack){ - Minify.optimize(lName, { - request : pReq, - response : pRes, - callback : pCallBack - }); + main.sendFile({ + name : lSendName, + request : pReq, + response : pRes }); - - }else{ - /* если мы имеем дело с файловой системой - * если путь не начинаеться с no-js - значит - * js включен - */ - - if(lPath.indexOf(lNoJS_s) !== lFS_s.length && lPath !== '/') - CloudServer.NoJS = false; - else{ - CloudServer.NoJS = true; - lPath = Util.removeStr(lPath, lNoJS_s); - } - - /* убираем индекс файловой системы */ - if(lPath.indexOf(lFS_s) === 0){ - lPath = Util.removeStr(lPath, lFS_s); - - /* если посетитель только зашел на сайт - * no-js будет пустым, как и fs. - * Если в пути нету fs - посетитель только зашел на сайт - * загружаем его полностью. - */ - } - - /* if query json setted up - * load json data, no-js false. - */ - - if(lQuery === 'json') - CloudServer.NoJS = false; - - /* Если мы не в корне добавляем слеш к будующим ссылкам */ - DirPath = lPath || '/'; - - CloudServer.Responses[DirPath] = pRes; - CloudServer.Statuses[DirPath] = OK; - - /* saving query of current file */ - CloudServer.Queries[DirPath] = lQuery; - Util.log(lQuery); - - Util.log(DirPath); - - - /* читаем основные данные о файле */ - Fs.stat(DirPath, CloudServer._stated); - - /* если установлено сжатие - * меняем название html-файла и - * загружаем сжатый html-файл в дальнейшем - */ - - var lMinName = Minify.getName(main.DIR + 'html/index.html'); - - CloudServer.INDEX = Minify.allowed.html ? lMinName : 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 && pStat){ - /* если это каталог - читаем его содержимое */ - if(pStat.isDirectory()) - Fs.readdir(DirPath, CloudServer._readDir); - /* отдаём файл */ - else if(pStat.isFile()){ - Fs.readFile(DirPath, CloudServer.getReadFileFunc(DirPath)); - Util.log('reading file: '+ DirPath); - } - }else{ - CloudServer.Statuses[DirPath] = 404; - CloudServer.sendResponse(null, pError.toString(), DirPath); - } - }; - - - /** - * Функция читает ссылку или выводит информацию об ошибке - * @param pError - * @param pFiles - */ - CloudServer._readDir = function (pError, pFiles) - { - if(pError){ - Util.log(pError); - - CloudServer.Statuses[DirPath] = 404; - CloudServer.sendResponse(null, 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){ - if(pError) - lStats[pName] = { - 'mode' : 0, - 'size' : 0, - 'isDirectory' : Util.retFalse - }; - - else - lStats[pName] = pStat; - - /* if this file is last - moving next */ - if(++lCount === pFiles.length) - CloudServer._fillJSON(lStats, pFiles); - }; - }; - - if(pFiles.length) - for(var i = 0; i < pFiles.length; i++) - /* Получаем информацию о файле */ - Fs.stat(DirPath + pFiles[i], getFilesStat_f(pFiles[i])); - - else - CloudServer._fillJSON(null, pFiles); - }; - - /** - * Function fill JSON by file stats - * - * @param pStats - object, contain file stats. - * example {'1.txt': stat} - * - * @param pFiles - array of files of current directory - */ - CloudServer._fillJSON = function(pStats, pFiles){ - /* данные о файлах в формате JSON*/ - var lJSON = [], - lJSONFile = {}, - lHeader, /* заголовок ответа сервера */ - lList; - - lJSON[0] = { - path : DirPath, - size : 'dir' - }; - - 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 + '
    ' + - ''; - - Fs.readFile(CloudServer.INDEX, CloudServer.indexReaded(lList)); - - }else{ - DirPath = DirPath.substr(DirPath, DirPath.lastIndexOf('/') ); - - var lQuyery = CloudServer.Queries[DirPath]; - DirPath += '.json'; - CloudServer.Queries[DirPath] = lQuyery; - - /* в обычном режиме(когда js включен - * высылаем json-структуру файлов - * с соответствующими заголовками - */ - lList = JSON.stringify(lJSON); - lHeader = main.generateHeaders(DirPath, CloudServer.Gzip, lQuyery); - - /* если браузер поддерживает gzip-сжатие - сжимаем данные*/ - if(CloudServer.Gzip){ - var lGzipCB = CloudServer.getGzipDataFunc(lHeader, CloudServer.INDEX); - - Zlib.gzip(lList, lGzipCB); - } - /* если не поддерживаеться - отсылаем данные без сжатия*/ - else - CloudServer.sendResponse(lHeader, lList, CloudServer.INDEX); - } - }; - - /** - *@param pList - */ - CloudServer.indexReaded = function(pList){ - return function(pError, pIndex){ - if(pError){ - return Util.log(pError); - } - - var lSrv = CloudServer, - lIndexName = lSrv.INDEX; - - pIndex = pIndex.toString(); - - - var lProccessed, - lIndexProccessing = lSrv.indexProcessing; - - lProccessed = Util.exec(lIndexProccessing, { - data : pIndex, - additional : pList + }, function(pCallBack){ + Minify.optimize(lName, { + request : pReq, + response : pRes, + callback : pCallBack }); - - 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 - данные - * или из одного из кешей - */ - var lReadFile = function(pError, pData){ - var lSrv = CloudServer; - if (!pError){ - Util.log('file ' + pName + ' readed'); - - var lQuery = lSrv.Queries[pName], - lHeader = main.generateHeaders(pName, lSrv.Gzip, lQuery); - - /* если браузер поддерживает gzip-сжатие - сжимаем данные*/ - if( lSrv.Gzip ) - /* сжимаем содержимое */ - Zlib.gzip(pData, lSrv.getGzipDataFunc(lHeader, pName)); - else - /* высылаем несжатые данные */ - lSrv.sendResponse(lHeader, pData, pName); - } - else{ - Util.log(pError.path); - if(pError.path !== 'passwd.json'){ - Util.log(pError); - - /* sending page not found */ - lSrv.Statuses[pName] = 404; - lSrv.sendResponse(null, pError.toString(), pName); - }else - lSrv.sendResponse(null, 'passwd.json'); - } - }; - - return lReadFile; - }; - - /** - * Функция получает сжатые данные - * @param pHeader - заголовок файла - * @pName - */ - CloudServer.getGzipDataFunc = function(pHeader, pName){ - return function(error, pResult){ - if(!error) - CloudServer.sendResponse(pHeader, pResult, pName); - else{ - Util.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); - - Util.log(pName + ' sended'); - } + }); }; /** diff --git a/lib/server/commander.js b/lib/server/commander.js index 08433ebc..007fdb18 100644 --- a/lib/server/commander.js +++ b/lib/server/commander.js @@ -136,7 +136,8 @@ }); } - /** async getting file states + /** + * async getting file states * and putting it to lStats object */ function getFilesStat(pParams){ From 08b3cfabb85c5e0d6bfaf81ec710726fe6f673ed Mon Sep 17 00:00:00 2001 From: coderaiser Date: Tue, 5 Feb 2013 03:48:32 -0500 Subject: [PATCH 196/347] minor changes --- lib/server/commander.js | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/lib/server/commander.js b/lib/server/commander.js index 007fdb18..2c5dbd6a 100644 --- a/lib/server/commander.js +++ b/lib/server/commander.js @@ -25,7 +25,7 @@ NO_JS = CloudFunc.NoJS, FS = CloudFunc.Fs, - INDEX = HTMLDIR + 'index.html', + INDEX = null, REQUEST = 'request', RESPONSE = 'response', @@ -35,12 +35,12 @@ exports.sendContent = function(pParams){ var lRet = Util.checkObj(pParams, ['processing'], - [REQUEST, RESPONSE]); + [REQUEST, RESPONSE, 'index']); if(lRet){ - var lReq = pParams.request, - lRes = pParams.response, - lPath = getCleanPath(lReq); + var p = pParams, + lPath = getCleanPath(p.request), + INDEX = p.index; IndexProcessingFunc = pParams.processing; @@ -51,15 +51,15 @@ else main.sendFile({ name : lPath, - request : lReq, - response : lRes + request : p.request, + response : p.response }); else sendResponse({ status : NOT_FOUND, data : pError.toString(), - request : lReq, - response: lRes + request : p.request, + response : p.response }); }); From 5f52fecae113bac0cfbf76af94c93e48f6bbab83 Mon Sep 17 00:00:00 2001 From: coderaiser Date: Tue, 5 Feb 2013 03:50:08 -0500 Subject: [PATCH 197/347] minor changes --- lib/server/commander.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/server/commander.js b/lib/server/commander.js index 2c5dbd6a..19000af5 100644 --- a/lib/server/commander.js +++ b/lib/server/commander.js @@ -39,9 +39,9 @@ if(lRet){ var p = pParams, - lPath = getCleanPath(p.request), - INDEX = p.index; + lPath = getCleanPath(p.request); + INDEX = p.index; IndexProcessingFunc = pParams.processing; fs.stat(lPath, function(pError, pStat){ From 355a1b0f34e63e94bd7a6a3f9061dd5e88693ca0 Mon Sep 17 00:00:00 2001 From: coderaiser Date: Tue, 5 Feb 2013 03:56:03 -0500 Subject: [PATCH 198/347] minor changes --- cloudcmd.js | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/cloudcmd.js b/cloudcmd.js index e1d373a5..2bf47a7d 100644 --- a/cloudcmd.js +++ b/cloudcmd.js @@ -199,15 +199,17 @@ main.sendFile(pParams); lRet = true; - }else if( Util.isContainStr(lName, [CloudFunc.Fs, CloudFunc.NoJS] ) || - Util.strCmp(lName, '/') || - Util.strCmp(lName, 'json') ) { - lRet = main.commander.sendContent({ - request : pParams[REQUEST], - response : pParams[RESPONSE], - processing : indexProcessing, - index : Minify.allowed.html ? Minify.getName(INDEX) : INDEX - }); + }else if( Util.isContainStr(lName, CloudFunc.FS) || + Util.isContainStr(lName, CloudFunc.NO_JS ) || + Util.strCmp(lName, '/') || + Util.strCmp(lName, 'json') ){ + + lRet = main.commander.sendContent({ + request : pParams[REQUEST], + response : pParams[RESPONSE], + processing : indexProcessing, + index : Minify.allowed.html ? Minify.getName(INDEX) : INDEX + }); } return lRet; From 48912e0782833f2bdf06f2290d326bbc610d354e Mon Sep 17 00:00:00 2001 From: coderaiser Date: Tue, 5 Feb 2013 03:58:19 -0500 Subject: [PATCH 199/347] minor changes --- lib/server/commander.js | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/lib/server/commander.js b/lib/server/commander.js index 19000af5..0ec48551 100644 --- a/lib/server/commander.js +++ b/lib/server/commander.js @@ -23,8 +23,9 @@ NOT_FOUND = 404, OK = 200, - NO_JS = CloudFunc.NoJS, - FS = CloudFunc.Fs, + FS = CloudFunc.FS, + NO_JS = CloudFunc.NO_JS, + INDEX = null, REQUEST = 'request', RESPONSE = 'response', From a8e01b9777cd6f2c422cefb2ba222aaeb2e7aaf6 Mon Sep 17 00:00:00 2001 From: coderaiser Date: Tue, 5 Feb 2013 04:07:40 -0500 Subject: [PATCH 200/347] minor changes --- lib/server/commander.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/server/commander.js b/lib/server/commander.js index 0ec48551..fc597b41 100644 --- a/lib/server/commander.js +++ b/lib/server/commander.js @@ -382,8 +382,8 @@ if(pReq){ lPath = getPath(pReq); - lNoJS = Util.isContainStr(lPath, CloudFunc.NoJS) - || lPath === '/' || getQuery() !== 'json'; + lNoJS = Util.isContainStr(lPath, NO_JS) + || lPath === '/' || getQuery() == 'json'; } return lNoJS; From 41a5d618266d62cdb7a48e7c0d7cc735a4d285eb Mon Sep 17 00:00:00 2001 From: coderaiser Date: Tue, 5 Feb 2013 05:27:44 -0500 Subject: [PATCH 201/347] minor changes --- lib/server/commander.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/lib/server/commander.js b/lib/server/commander.js index fc597b41..5b303c0c 100644 --- a/lib/server/commander.js +++ b/lib/server/commander.js @@ -14,6 +14,7 @@ fs = main.fs, zlib = main.zlib, url = main.url, + querystring = main.querystring, CloudFunc = main.cloudfunc, DIR = main.DIR, LIBDIR = main.LIBDIR, @@ -344,6 +345,7 @@ var lParsedUrl = url.parse(pReq.url), lPath = lParsedUrl.pathname; + lPath = querystring.unescape(lPath); return lPath; } From e35b07c22416d7cce002bf0ea769c04d2e316024 Mon Sep 17 00:00:00 2001 From: coderaiser Date: Tue, 5 Feb 2013 05:31:04 -0500 Subject: [PATCH 202/347] minor changes --- lib/server/commander.js | 13 ++----------- 1 file changed, 2 insertions(+), 11 deletions(-) diff --git a/lib/server/commander.js b/lib/server/commander.js index 5b303c0c..74baea2b 100644 --- a/lib/server/commander.js +++ b/lib/server/commander.js @@ -112,7 +112,7 @@ }; if(n){ - var i, lDirPath = getDirPath(lReq); + var i, lDirPath = getCleanPath(lReq); for(i = 0; i < n; i++){ var lName = lDirPath + lFiles[i], @@ -188,7 +188,7 @@ var lJSON = [], lJSONFile = {}, lList, - lDirPath = getDirPath(lReq); + lDirPath = getCleanPath(lReq); lJSON[0] = { path : lDirPath, @@ -349,15 +349,6 @@ return lPath; } - function getDirPath(pReq){ - var lPath = getPath(pReq); - - /* Если мы не в корне добавляем слеш к будующим ссылкам */ - if(lPath !== '/') - lPath += '/'; - - return lPath; - } function getCleanPath(pReq){ var lPath = getPath(pReq), From 970b55f8a1f741e6d1961677c560b08b1e069124 Mon Sep 17 00:00:00 2001 From: coderaiser Date: Tue, 5 Feb 2013 05:39:32 -0500 Subject: [PATCH 203/347] minor changes --- cloudcmd.js | 2 +- lib/server/commander.js | 5 ++++- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/cloudcmd.js b/cloudcmd.js index 2bf47a7d..1d7a1a5f 100644 --- a/cloudcmd.js +++ b/cloudcmd.js @@ -21,7 +21,7 @@ REQUEST = 'request', RESPONSE = 'response', - INDEX = DIR + 'html/index.html'; + INDEX = DIR + 'html' + main.SLASH + 'index.html'; /* reinit main dir os if we on * Win32 should be backslashes */ diff --git a/lib/server/commander.js b/lib/server/commander.js index 74baea2b..2f77b923 100644 --- a/lib/server/commander.js +++ b/lib/server/commander.js @@ -352,7 +352,10 @@ function getCleanPath(pReq){ var lPath = getPath(pReq), - lRet = Util.removeStr(lPath, [NO_JS, FS]) || '/'; + lRet = Util.removeStr(lPath, [NO_JS, FS]) || main.SLASH; + + if(lRet !== '/') + lRet = lRet += main.SLASH; return lRet; } From 95ededd29236ea76286375b70a19660e35db2c4b Mon Sep 17 00:00:00 2001 From: coderaiser Date: Tue, 5 Feb 2013 05:43:53 -0500 Subject: [PATCH 204/347] minor changes --- lib/server/commander.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/server/commander.js b/lib/server/commander.js index 2f77b923..3f89f02f 100644 --- a/lib/server/commander.js +++ b/lib/server/commander.js @@ -355,7 +355,7 @@ lRet = Util.removeStr(lPath, [NO_JS, FS]) || main.SLASH; if(lRet !== '/') - lRet = lRet += main.SLASH; + lRet += '/'; return lRet; } From 5532219515c203b064e4946a4b5952f9a749b886 Mon Sep 17 00:00:00 2001 From: coderaiser Date: Tue, 5 Feb 2013 05:45:48 -0500 Subject: [PATCH 205/347] minor changes --- lib/server/commander.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/server/commander.js b/lib/server/commander.js index 3f89f02f..1a9f2994 100644 --- a/lib/server/commander.js +++ b/lib/server/commander.js @@ -25,7 +25,7 @@ OK = 200, FS = CloudFunc.FS, - NO_JS = CloudFunc.NO_JS, + NO_JS = CloudFunc.NOJS, INDEX = null, REQUEST = 'request', From 3b1973b1fc3633c36ae85b881996fbcc6705357a Mon Sep 17 00:00:00 2001 From: coderaiser Date: Tue, 5 Feb 2013 05:59:06 -0500 Subject: [PATCH 206/347] commander functions moved out to commander.js from server.js --- ChangeLog | 2 ++ lib/client.js | 1 - 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/ChangeLog b/ChangeLog index abf1f564..3fb84a23 100644 --- a/ChangeLog +++ b/ChangeLog @@ -122,6 +122,8 @@ time was changed. * Moved extensions from main.js to json/ext.json. +* Commander functions moved out to commander.js from server.js + 2012.12.12, Version 0.1.8 diff --git a/lib/client.js b/lib/client.js index 9ab7b0c5..9b6d73b0 100644 --- a/lib/client.js +++ b/lib/client.js @@ -534,7 +534,6 @@ CloudCmd._ajaxLoad = function(pPath, pOptions){ var lFSPath = decodeURI(pPath); lFSPath = Util.removeStr( lFSPath, CloudFunc.NOJS ); - pPath = Util.removeStr( lFSPath, CloudFunc.FS ); Util.log ('reading dir: "' + pPath + '";'); From bcebc1c9a9ed2af62e669df9e728ca29bb43eb89 Mon Sep 17 00:00:00 2001 From: coderaiser Date: Tue, 5 Feb 2013 06:01:48 -0500 Subject: [PATCH 207/347] minor changes --- cloudcmd.js | 4 ++-- lib/server.js | 44 ++------------------------------------------ 2 files changed, 4 insertions(+), 44 deletions(-) diff --git a/cloudcmd.js b/cloudcmd.js index 1d7a1a5f..aafe108c 100644 --- a/cloudcmd.js +++ b/cloudcmd.js @@ -65,10 +65,10 @@ .replace('Cloud Commander', '' + CloudFunc.getTitle() + ''); - if(!srv.Config.appcache) + if(!Config.appcache) lData = Util.removeStr(lData, ' manifest="/cloudcmd.appcache"'); - if(!srv.Config.show_keys_panel){ + if(!Config.show_keys_panel){ var lKeysPanel = '
    Date: Tue, 5 Feb 2013 06:55:20 -0500 Subject: [PATCH 208/347] minor changes --- cloudcmd.js | 8 +--- lib/server.js | 98 ++++++++++++++++++++-------------------------- lib/server/main.js | 2 + 3 files changed, 47 insertions(+), 61 deletions(-) diff --git a/cloudcmd.js b/cloudcmd.js index aafe108c..fd6e747c 100644 --- a/cloudcmd.js +++ b/cloudcmd.js @@ -188,17 +188,13 @@ '-> auth'); pParams.name = main.HTMLDIR + lName + '.html'; - main.sendFile(pParams); - - lRet = true; + lRet = main.sendFile(pParams); }else if( Util.strCmp(lName, '/auth/github') ){ Util.log('* Routing' + '-> github'); pParams.name = main.HTMLDIR + lName + '.html'; - main.sendFile(pParams); - - lRet = true; + lRet = main.sendFile(pParams); }else if( Util.isContainStr(lName, CloudFunc.FS) || Util.isContainStr(lName, CloudFunc.NO_JS ) || Util.strCmp(lName, '/') || diff --git a/lib/server.js b/lib/server.js index 8dbaa54d..8869f75b 100644 --- a/lib/server.js +++ b/lib/server.js @@ -108,7 +108,7 @@ if (lConfig.server) { var http = main.http, lError = Util.tryCatchLog(Util.bind(function(){ - this.Server = http.createServer(this._controller); + this.Server = http.createServer( controller ); this.Server.listen(this.Port, this.IP); var lListen; @@ -134,10 +134,11 @@ * @param req - запрос клиента (Request) * @param res - ответ сервера (Response) */ - CloudServer._controller = function(pReq, pRes) + function controller(pReq, pRes) { /* Читаем содержимое папки, переданное в url */ - var lConfig = CloudServer.Config, + var lRet, + lConfig = CloudServer.Config, lURL = main.url, lParsedUrl = lURL.parse(pReq.url), lPath = lParsedUrl.pathname; @@ -146,69 +147,56 @@ lPath = Querystring.unescape(lPath); Util.log('pathname: ' + lPath); - /* запоминаем поддерживает ли браузер - * gzip-сжатие при каждом обращении к серверу - * и доступен ли нам модуль zlib - */ - Util.log("request for " + lPath + " received..."); - - if( lConfig.rest ){ - var lRestWas = Util.exec(CloudServer.rest, { + + if( lConfig.rest ) + lRet = Util.exec(CloudServer.rest, { request : pReq, response : pRes }); - - if(lRestWas) - return; - } - if( CloudServer.route){ - var lRouteWas = Util.exec(CloudServer.route, { + if( !lRet && CloudServer.route) + lRet = Util.exec(CloudServer.route, { name : lPath, request : pReq, response : pRes }); + + if(!lRet){ + /* добавляем текующий каталог к пути */ + var lName = '.' + lPath; + Util.log('reading ' + lName); - if(lRouteWas) - return; + /* watching is file changed */ + if(lConfig.appcache) + AppCache.watch(lName); + + Util.log(Path.basename(lName)); + + var lMin = Minify.allowed, + lExt = Util.getExtension(lName), + lResult = lExt === '.js' && lMin.js || + lExt === '.css' && lMin.css || + lExt === '.html' && lMin.html; + + Util.ifExec(!lResult, + function(pParams){ + var lSendName = pParams && pParams.name || lName; + + main.sendFile({ + name : lSendName, + request : pReq, + response : pRes + }); + }, function(pCallBack){ + Minify.optimize(lName, { + request : pReq, + response : pRes, + callback : pCallBack + }); + }); } - - /* если имена файлов проекта - загружаем их * - * убираем слеш и читаем файл с текущец директории */ - - /* добавляем текующий каталог к пути */ - var lName = '.' + lPath; - Util.log('reading ' + lName); - - /* watching is file changed */ - if(lConfig.appcache) - AppCache.watch(lName); - - Util.log(Path.basename(lName)); - - var lMin = Minify.allowed, - lExt = Util.getExtension(lName), - lResult = lExt === '.js' && lMin.js || - lExt === '.css' && lMin.css || - lExt === '.html' && lMin.html; - - Util.ifExec(!lResult, function(pParams){ - var lSendName = pParams && pParams.name || lName; - - main.sendFile({ - name : lSendName, - request : pReq, - response : pRes - }); - }, function(pCallBack){ - Minify.optimize(lName, { - request : pReq, - response : pRes, - callback : pCallBack - }); - }); - }; + } /** * start server function diff --git a/lib/server/main.js b/lib/server/main.js index 5ce87e9d..451cb516 100644 --- a/lib/server/main.js +++ b/lib/server/main.js @@ -205,6 +205,8 @@ lReadStream = lReadStream.pipe( zlib.createGzip() ); lReadStream.pipe(lRes); + + lRet = true; } return lRet; From 866cf30790189ed89353ff9e99d2e90588458e52 Mon Sep 17 00:00:00 2001 From: coderaiser Date: Tue, 5 Feb 2013 08:48:18 -0500 Subject: [PATCH 209/347] refactored --- cloudcmd.js | 14 ++--- lib/server.js | 122 +++++++++++++++------------------------- lib/server/commander.js | 20 ++----- lib/util.js | 19 +++++++ 4 files changed, 73 insertions(+), 102 deletions(-) diff --git a/cloudcmd.js b/cloudcmd.js index fd6e747c..9ccbb2f6 100644 --- a/cloudcmd.js +++ b/cloudcmd.js @@ -11,12 +11,12 @@ path = main.path, fs = main.fs, CloudFunc = main.cloudfunc, + AppCache = main.appcache, Util = main.util, update = main.update, Server = main.require(LIBDIR + 'server'), - Minify = Server.Minify, - srv = Server.CloudServer, + Minify = main.minify, Config = main.config, REQUEST = 'request', @@ -30,7 +30,6 @@ readConfig(); Server.start(Config, { appcache : appCacheProcessing, - index : indexProcessing, minimize : minimize, rest : rest, route : route @@ -81,16 +80,15 @@ * init and process of appcache if it allowed in config */ function appCacheProcessing(){ - var lAppCache = srv.AppCache, - lFiles = [ + var lFiles = [ {'//themes.googleusercontent.com/static/fonts/droidsansmono/v4/ns-m2xQYezAtqh7ai59hJUYuTAAIFFn5GTWtryCmBQ4.woff' : './font/DroidSansMono.woff'}, {'//ajax.googleapis.com/ajax/libs/jquery/1.8.0/jquery.min.js' : './lib/client/jquery.js'}]; - if(srv.Minify._allowed.css) + if(Config.minification.css) lFiles.push('node_modules/minify/min/all.min.css'); - lAppCache.addFiles(lFiles); - lAppCache.createManifest(); + AppCache.addFiles(lFiles); + AppCache.createManifest(); } /** diff --git a/lib/server.js b/lib/server.js index 8869f75b..536ae120 100644 --- a/lib/server.js +++ b/lib/server.js @@ -11,24 +11,13 @@ var main = global.cloudcmd.main, - /* - * Обьект содержащий все функции и переменные - * серверной части Cloud Commander'а - */ - CloudServer = { /* base configuration */ - Config : { - server : true, - socket : true, - port : 80 + Config = { + server : true, + socket : true, + port : 80 }, - /* server varible */ - Server : {}, - - /* КОНСТАНТЫ */ - INDEX : main.DIR + 'html/index.html' - }, DIR = main.Dir, LIBDIR = main.LIBDIR, SRVDIR = main.SRVDIR, @@ -37,23 +26,18 @@ Path = main.path, Querystring = main.querystring, - Minify = main.minify, - AppCache = main.appcache, - Socket = main.socket, + Minify = main.minify, + AppCache = main.appcache, + Socket = main.socket, - /* node v0.4 not contains zlib */ - Zlib = main.zlib; /* модуль для сжатия данных gzip-ом*/ - if(!Zlib) - Util.log('to use gzip-commpression' + - 'you should use newer node version\n'); - - /* добавляем модуль с функциями */ - var Util = main.util; + http = main.http, + Util = main.util, + + Server, Rest, Route, Minimize, Port, IP; /* базовая инициализация */ - CloudServer.init = function(pAppCachProcessing){ - var lConfig = this.Config, - lMinifyAllowed = lConfig.minification; + function init(pAppCachProcessing){ + var lMinifyAllowed = Config.minification; /* Change default parameters of * js/css/html minification @@ -61,63 +45,59 @@ Minify.setAllowed(lMinifyAllowed); /* Если нужно минимизируем скрипты */ - Util.exec(CloudServer.minimize, lMinifyAllowed); + Util.exec(Minimize, lMinifyAllowed); /* создаём файл app cache */ - if( lConfig.appcache && AppCache && lConfig.server ) + if( Config.appcache && AppCache && Config.server ) Util.exec( pAppCachProcessing ); - }; + } /** - * Функция создаёт сервер + * start server function * @param pConfig + * @param pProcessing {index, appcache, rest} */ - CloudServer.start = function (pConfig, pProcessing) { + exports.start = function start(pConfig, pProcessing) { if(!pProcessing) pProcessing = {}; if(pConfig) - this.Config = pConfig; + Config = pConfig; else Util.log('warning: configuretion file config.json not found...\n' + 'using default values...\n' + - JSON.stringify(this.Config)); + JSON.stringify(Config)); - var lConfig = this.Config; + Rest = pProcessing.rest; + Route = pProcessing.route; + Minimize = pProcessing.minimize; - CloudServer.rest = pProcessing.rest; - CloudServer.route = pProcessing.route; - CloudServer.minimize = pProcessing.minimize; + init(pProcessing.appcache); - this.init(pProcessing.appcache); - - this.Port = process.env.PORT || /* c9 */ + Port = process.env.PORT || /* c9 */ process.env.app_port || /* nodester */ process.env.VCAP_APP_PORT || /* cloudfoundry */ - lConfig.port; + Config.port; - this.IP = process.env.IP || /* c9 */ - this.Config.ip || - (main.WIN32 ? - '127.0.0.1' : - '0.0.0.0'); + IP = process.env.IP || /* c9 */ + Config.ip || + (main.WIN32 ? '127.0.0.1' : '0.0.0.0'); /* server mode or testing mode */ - if (lConfig.server) { - var http = main.http, - lError = Util.tryCatchLog(Util.bind(function(){ - this.Server = http.createServer( controller ); - this.Server.listen(this.Port, this.IP); + if (Config.server) { + var lError = Util.tryCatchLog(function(){ + Server = http.createServer( controller ); + Server.listen(Port, IP); var lListen; - if(lConfig.socket && Socket) - lListen = Socket.listen(this.Server); + if(Config.socket && Socket) + lListen = Socket.listen(Server); Util.log('* Sockets ' + (lListen ? 'running' : 'disabled')); - Util.log('* Server running at http://' + this.IP + ':' + this.Port); - }, this)); + Util.log('* Server running at http://' + IP + ':' + Port); + }); if(lError){ Util.log('Cloud Commander server could not started'); @@ -125,7 +105,7 @@ } }else Util.log('Cloud Commander testing mode'); - }; + } /** @@ -138,7 +118,6 @@ { /* Читаем содержимое папки, переданное в url */ var lRet, - lConfig = CloudServer.Config, lURL = main.url, lParsedUrl = lURL.parse(pReq.url), lPath = lParsedUrl.pathname; @@ -149,14 +128,14 @@ Util.log("request for " + lPath + " received..."); - if( lConfig.rest ) - lRet = Util.exec(CloudServer.rest, { + if( Config.rest ) + lRet = Util.exec(Rest, { request : pReq, response : pRes }); - if( !lRet && CloudServer.route) - lRet = Util.exec(CloudServer.route, { + if( !lRet && Route) + lRet = Util.exec(Route, { name : lPath, request : pReq, response : pRes @@ -168,7 +147,7 @@ Util.log('reading ' + lName); /* watching is file changed */ - if(lConfig.appcache) + if(Config.appcache) AppCache.watch(lName); Util.log(Path.basename(lName)); @@ -198,17 +177,4 @@ } } - /** - * start server function - * @param pConfig - * @param pProcessing {index, appcache, rest} - */ - exports.start = function(pConfig, pProcessing){ - CloudServer.start(pConfig, pProcessing); - }; - - exports.CloudServer = CloudServer; - exports.Minify = Minify; - exports.AppCache = AppCache; - })(); diff --git a/lib/server/commander.js b/lib/server/commander.js index 1a9f2994..bb172372 100644 --- a/lib/server/commander.js +++ b/lib/server/commander.js @@ -49,7 +49,7 @@ fs.stat(lPath, function(pError, pStat){ if(!pError) if(pStat.isDirectory()) - fs.readdir(lPath, call(readDir, pParams) ); + fs.readdir(lPath, Util.call(readDir, pParams) ); else main.sendFile({ name : lPath, @@ -123,7 +123,7 @@ stats : lStats, }; - fs.stat( lName, call(getFilesStat, lParams) ); + fs.stat( lName, Util.call(getFilesStat, lParams) ); } } else @@ -227,7 +227,7 @@ lList = '
      ' + lPanel + '
    ' + ''; - fs.readFile(INDEX, call(readFile, { + fs.readFile(INDEX, Util.call(readFile, { list : lList, request : lReq, response : lRes @@ -360,18 +360,6 @@ return lRet; } - function call(pFunc, pParams){ - var lFunc = function(pError, pData){ - Util.exec(pFunc, { - error : pError, - data : pData, - params : pParams - }); - }; - - return lFunc; - } - function noJS(pReq){ var lNoJS, lPath; @@ -432,7 +420,7 @@ }, function(pCallBack){ - zlib.gzip (p.data, call(gzipData, { + zlib.gzip (p.data, Util.call(gzipData, { callback : pCallBack })); }); diff --git a/lib/util.js b/lib/util.js index f796c196..0cf49ef7 100644 --- a/lib/util.js +++ b/lib/util.js @@ -33,6 +33,25 @@ Util = exports || {}; return lRet; }; + /** + * callback for functions(pError, pData) + * thet moves on our parameters. + * + * @param pFunc + * @param pParams + */ + Util.call = function(pFunc, pParams){ + var lFunc = function(pError, pData){ + Util.exec(pFunc, { + error : pError, + data : pData, + params : pParams + }); + }; + + return lFunc; + }; + /** * Функция ищет в имени файла расширение * и если находит возвращает true From 3d34f15f4d706d08956678dcd2d249dff08b1470 Mon Sep 17 00:00:00 2001 From: coderaiser Date: Tue, 5 Feb 2013 09:16:16 -0500 Subject: [PATCH 210/347] refactored --- cloudcmd.js | 8 +-- lib/server.js | 109 +++++++++++++++++++++++++++++++++++-- lib/server/commander.js | 5 +- lib/server/main.js | 115 +++------------------------------------- lib/server/minify.js | 5 +- lib/server/rest.js | 5 +- 6 files changed, 126 insertions(+), 121 deletions(-) diff --git a/cloudcmd.js b/cloudcmd.js index 9ccbb2f6..5b250ca7 100644 --- a/cloudcmd.js +++ b/cloudcmd.js @@ -15,7 +15,7 @@ Util = main.util, update = main.update, - Server = main.require(LIBDIR + 'server'), + server = main.server, Minify = main.minify, Config = main.config, @@ -28,7 +28,7 @@ DIR = main.DIR; readConfig(); - Server.start(Config, { + server.start(Config, { appcache : appCacheProcessing, minimize : minimize, rest : rest, @@ -186,13 +186,13 @@ '-> auth'); pParams.name = main.HTMLDIR + lName + '.html'; - lRet = main.sendFile(pParams); + lRet = server.sendFile(pParams); }else if( Util.strCmp(lName, '/auth/github') ){ Util.log('* Routing' + '-> github'); pParams.name = main.HTMLDIR + lName + '.html'; - lRet = main.sendFile(pParams); + lRet = server.sendFile(pParams); }else if( Util.isContainStr(lName, CloudFunc.FS) || Util.isContainStr(lName, CloudFunc.NO_JS ) || Util.strCmp(lName, '/') || diff --git a/lib/server.js b/lib/server.js index 536ae120..eefea9ba 100644 --- a/lib/server.js +++ b/lib/server.js @@ -31,9 +31,15 @@ Socket = main.socket, http = main.http, + zlib = main.zlib, + fs = main.fs, Util = main.util, + ext = main.ext, - Server, Rest, Route, Minimize, Port, IP; + Server, Rest, Route, Minimize, Port, IP, + + OK = 200, + FILE_NOT_FOUND = 404; /* базовая инициализация */ function init(pAppCachProcessing){ @@ -58,7 +64,7 @@ * @param pConfig * @param pProcessing {index, appcache, rest} */ - exports.start = function start(pConfig, pProcessing) { + function start(pConfig, pProcessing) { if(!pProcessing) pProcessing = {}; @@ -105,7 +111,7 @@ } }else Util.log('Cloud Commander testing mode'); - } + }; /** @@ -162,7 +168,7 @@ function(pParams){ var lSendName = pParams && pParams.name || lName; - main.sendFile({ + sendFile({ name : lSendName, request : pReq, response : pRes @@ -177,4 +183,99 @@ } } + /** + * Функция создаёт заголовки файлов + * в зависимости от расширения файла + * перед отправкой их клиенту + * @param pName - имя файла + * @param pGzip - данные сжаты gzip'ом + */ + function generateHeaders(pName, pGzip, pQuery){ + var lRet, + lType = '', + lContentEncoding = '', + lCacheControl = 0, + + lExt = Util.getExtension(pName); + + if( Util.strCmp(lExt, '.appcache') ) + lCacheControl = 1; + + lType = ext[lExt] || 'text/plain'; + + if( !Util.isContainStr(lType, 'img') ) + lContentEncoding = '; charset=UTF-8'; + + if(Util.strCmp(pQuery, 'download') ) + lType = 'application/octet-stream'; + + + if(!lCacheControl) + lCacheControl = 31337 * 21; + + lRet = { + /* if type of file any, but img - + * then we shoud specify charset + */ + 'Content-Type': lType + lContentEncoding, + 'cache-control': 'max-age=' + lCacheControl, + 'last-modified': new Date().toString(), + /* https://developers.google.com/speed/docs/best-practices + /caching?hl=ru#LeverageProxyCaching */ + 'Vary': 'Accept-Encoding' + }; + + if(pGzip) + lRet['content-encoding'] = 'gzip'; + + return lRet; + } + + /** + * send file to client thru pipe + * and gzip it if client support + * + * @param pName - имя файла + * @param pGzip - данные сжаты gzip'ом + */ + function sendFile(pParams){ + var lRet, + lName, lReq, lRes; + + if(pParams){ + lName = pParams.name, + lReq = pParams.request, + lRes = pParams.response; + } + + if(lName && lRes && lReq){ + var lEnc = lReq.headers['accept-encoding'] || '', + lGzip = lEnc.match(/\bgzip\b/), + + lReadStream = fs.createReadStream(lName, { + 'bufferSize': 4 * 1024 + }); + + lReadStream.on('error', function(pError){ + lRes.writeHead(FILE_NOT_FOUND, 'OK'); + lRes.end(String(pError)); + }); + + lRes.writeHead(OK, generateHeaders(lName, lGzip) ); + + if (lGzip) + lReadStream = lReadStream.pipe( zlib.createGzip() ); + + lReadStream.pipe(lRes); + + lRet = true; + } + + return lRet; + } + + exports.generateHeaders = generateHeaders; + exports.sendFile = sendFile; + exports.start = start; + })(); diff --git a/lib/server/commander.js b/lib/server/commander.js index bb172372..316a2c0f 100644 --- a/lib/server/commander.js +++ b/lib/server/commander.js @@ -20,6 +20,7 @@ LIBDIR = main.LIBDIR, HTMLDIR = main.HTMLDIR, Util = main.util, + server = main.server, NOT_FOUND = 404, OK = 200, @@ -51,7 +52,7 @@ if(pStat.isDirectory()) fs.readdir(lPath, Util.call(readDir, pParams) ); else - main.sendFile({ + server.sendFile({ name : lPath, request : p.request, response : p.response @@ -400,7 +401,7 @@ lQuery = getQuery(p.request), /* download, json */ lGzip = isGZIP(p.request), - lHead = main.generateHeaders(lPath, lGzip, lQuery); + lHead = server.generateHeaders(lPath, lGzip, lQuery); /* если браузер поддерживает gzip-сжатие - сжимаем данные*/ Util.ifExec(!lGzip, diff --git a/lib/server/main.js b/lib/server/main.js index 451cb516..4142a734 100644 --- a/lib/server/main.js +++ b/lib/server/main.js @@ -13,18 +13,12 @@ SLASH, ISWIN32, - fs, - path, - zlib, - ext, - - OK = 200, - FILE_NOT_FOUND = 404; + path; /* Native Modules*/ exports.crypto = require('crypto'), exports.child_process = require('child_process'), - exports.fs = fs = require('fs'), + exports.fs = require('fs'), exports.http = require('http'), exports.https = require('https'), exports.path = path = require('path'), @@ -43,26 +37,23 @@ exports.JSONDIR = JSONDIR = DIR + 'json/', /* Functions */ - exports.generateHeaders = generateHeaders, - exports.sendFile = sendFile, - exports.require = mrequire, exports.librequire = librequire, exports.srvrequire = srvrequire, exports.rootrequire = rootrequire, /* compitability with old versions of node */ - exports.fs.exists = exports.fs.exists || exports.path.exists; + exports.fs.exists = exports.fs.exists || exports.path.exists; /* Needed Modules */ exports.util = Util = require(LIBDIR + 'util'), - exports.zlib = zlib = mrequire('zlib'), + exports.zlib = mrequire('zlib'), /* Main Information */ exports.config = jsonrequire('config'); exports.modules = jsonrequire('modules'); - exports.ext = ext = jsonrequire('ext'); + exports.ext = jsonrequire('ext'); exports.mainpackage = rootrequire('package'); @@ -77,15 +68,16 @@ exports.VOLUMES = getVolumes(), /* Additional Modules */ + exports.minify = srvrequire('minify').Minify; + exports.socket = srvrequire('socket'), + exports.server = librequire('server'), exports.auth = srvrequire('auth').auth, exports.appcache = srvrequire('appcache'), exports.cache = srvrequire('cache').Cache, exports.cloudfunc = librequire('cloudfunc'), exports.rest = srvrequire('rest').api, - exports.socket = srvrequire('socket'), exports.update = srvrequire('update'), exports.ischanged = srvrequire('ischanged'); - exports.minify = srvrequire('minify').Minify; exports.commander = srvrequire('commander'); /* * second initializing after all modules load, so global var is @@ -121,97 +113,6 @@ */ function isWin32(){ return process.platform === 'win32'; } - /** - * Функция создаёт заголовки файлов - * в зависимости от расширения файла - * перед отправкой их клиенту - * @param pName - имя файла - * @param pGzip - данные сжаты gzip'ом - */ - function generateHeaders(pName, pGzip, pQuery){ - var lRet, - lType = '', - lContentEncoding = '', - lCacheControl = 0, - - lExt = Util.getExtension(pName); - - if( Util.strCmp(lExt, '.appcache') ) - lCacheControl = 1; - - lType = ext[lExt] || 'text/plain'; - - if( !Util.isContainStr(lType, 'img') ) - lContentEncoding = '; charset=UTF-8'; - - if(Util.strCmp(pQuery, 'download') ) - lType = 'application/octet-stream'; - - - if(!lCacheControl) - lCacheControl = 31337 * 21; - - lRet = { - /* if type of file any, but img - - * then we shoud specify charset - */ - 'Content-Type': lType + lContentEncoding, - 'cache-control': 'max-age=' + lCacheControl, - 'last-modified': new Date().toString(), - /* https://developers.google.com/speed/docs/best-practices - /caching?hl=ru#LeverageProxyCaching */ - 'Vary': 'Accept-Encoding' - }; - - if(pGzip) - lRet['content-encoding'] = 'gzip'; - - return lRet; - } - - /** - * send file to client thru pipe - * and gzip it if client support - * - * @param pName - имя файла - * @param pGzip - данные сжаты gzip'ом - */ - function sendFile(pParams){ - var lRet, - lName, lReq, lRes; - - if(pParams){ - lName = pParams.name, - lReq = pParams.request, - lRes = pParams.response; - } - - if(lName && lRes && lReq){ - var lEnc = lReq.headers['accept-encoding'] || '', - lGzip = lEnc.match(/\bgzip\b/), - - lReadStream = fs.createReadStream(lName, { - 'bufferSize': 4 * 1024 - }); - - lReadStream.on('error', function(pError){ - lRes.writeHead(FILE_NOT_FOUND, 'OK'); - lRes.end(String(pError)); - }); - - lRes.writeHead(OK, generateHeaders(lName, lGzip) ); - - if (lGzip) - lReadStream = lReadStream.pipe( zlib.createGzip() ); - - lReadStream.pipe(lRes); - - lRet = true; - } - - return lRet; - } - /** * get volumes if win32 or get nothing if nix diff --git a/lib/server/minify.js b/lib/server/minify.js index 21969f07..7aeaff4c 100644 --- a/lib/server/minify.js +++ b/lib/server/minify.js @@ -19,7 +19,8 @@ LIBDIR = main.LIBDIR, HTMLDIR = main.HTMLDIR, Util = main.util, - Minify = main.require('minify'), + Minify = main.minify, + server = main.server, IsChanged = main.ischanged, COULD_NOT_MINIFY = 'Could not minify without minify module\n' + @@ -72,7 +73,7 @@ if(pChanged) Minify.optimize(pName, pParams); else - main.sendFile(pParams); + server.sendFile(pParams); }); } else{ diff --git a/lib/server/rest.js b/lib/server/rest.js index 6957bb31..d747c281 100644 --- a/lib/server/rest.js +++ b/lib/server/rest.js @@ -16,9 +16,10 @@ var main = global.cloudcmd.main, Util = main.util, Config = main.config, + server = main.server, APIURL = Config.api_url, OK = 200, - Header = main.generateHeaders('api.json', false); + Header = server.generateHeaders('api.json', false); /** * rest interface @@ -153,7 +154,7 @@ if( Util.isString(lFiles) ){ pParams.name = lFiles; - main.sendFile(pParams); + server.sendFile(pParams); lResult = null; } From e3789e3eb8bdfdd414a930a1aca10733d0fbb832 Mon Sep 17 00:00:00 2001 From: coderaiser Date: Tue, 5 Feb 2013 09:46:50 -0500 Subject: [PATCH 211/347] refactored --- cloudcmd.js | 6 +- json/config.json | 2 +- lib/server.js | 106 +---------------------- lib/server/commander.js | 110 ++++++++++++------------ lib/server/main.js | 180 +++++++++++++++++++++++++++++++--------- lib/server/minify.js | 5 +- lib/server/rest.js | 5 +- 7 files changed, 205 insertions(+), 209 deletions(-) diff --git a/cloudcmd.js b/cloudcmd.js index 5b250ca7..d4012c5d 100644 --- a/cloudcmd.js +++ b/cloudcmd.js @@ -15,7 +15,7 @@ Util = main.util, update = main.update, - server = main.server, + server = main.librequire('server'), Minify = main.minify, Config = main.config, @@ -186,13 +186,13 @@ '-> auth'); pParams.name = main.HTMLDIR + lName + '.html'; - lRet = server.sendFile(pParams); + lRet = main.sendFile(pParams); }else if( Util.strCmp(lName, '/auth/github') ){ Util.log('* Routing' + '-> github'); pParams.name = main.HTMLDIR + lName + '.html'; - lRet = server.sendFile(pParams); + lRet = main.sendFile(pParams); }else if( Util.isContainStr(lName, CloudFunc.FS) || Util.isContainStr(lName, CloudFunc.NO_JS ) || Util.strCmp(lName, '/') || diff --git a/json/config.json b/json/config.json index 428b8a2c..69ce0eb2 100644 --- a/json/config.json +++ b/json/config.json @@ -2,7 +2,7 @@ "api_url" : "/api/v1", "appcache" : false, "minification" : { - "js" : false, + "js" : true, "css" : true, "html" : true, "img" : true diff --git a/lib/server.js b/lib/server.js index eefea9ba..4328d18a 100644 --- a/lib/server.js +++ b/lib/server.js @@ -31,15 +31,9 @@ Socket = main.socket, http = main.http, - zlib = main.zlib, - fs = main.fs, Util = main.util, - ext = main.ext, - Server, Rest, Route, Minimize, Port, IP, - - OK = 200, - FILE_NOT_FOUND = 404; + Server, Rest, Route, Minimize, Port, IP; /* базовая инициализация */ function init(pAppCachProcessing){ @@ -111,7 +105,7 @@ } }else Util.log('Cloud Commander testing mode'); - }; + } /** @@ -168,7 +162,7 @@ function(pParams){ var lSendName = pParams && pParams.name || lName; - sendFile({ + main.sendFile({ name : lSendName, request : pReq, response : pRes @@ -183,99 +177,7 @@ } } - /** - * Функция создаёт заголовки файлов - * в зависимости от расширения файла - * перед отправкой их клиенту - * @param pName - имя файла - * @param pGzip - данные сжаты gzip'ом - */ - function generateHeaders(pName, pGzip, pQuery){ - var lRet, - lType = '', - lContentEncoding = '', - lCacheControl = 0, - - lExt = Util.getExtension(pName); - - if( Util.strCmp(lExt, '.appcache') ) - lCacheControl = 1; - - lType = ext[lExt] || 'text/plain'; - - if( !Util.isContainStr(lType, 'img') ) - lContentEncoding = '; charset=UTF-8'; - - if(Util.strCmp(pQuery, 'download') ) - lType = 'application/octet-stream'; - - - if(!lCacheControl) - lCacheControl = 31337 * 21; - - lRet = { - /* if type of file any, but img - - * then we shoud specify charset - */ - 'Content-Type': lType + lContentEncoding, - 'cache-control': 'max-age=' + lCacheControl, - 'last-modified': new Date().toString(), - /* https://developers.google.com/speed/docs/best-practices - /caching?hl=ru#LeverageProxyCaching */ - 'Vary': 'Accept-Encoding' - }; - - if(pGzip) - lRet['content-encoding'] = 'gzip'; - - return lRet; - } - - /** - * send file to client thru pipe - * and gzip it if client support - * - * @param pName - имя файла - * @param pGzip - данные сжаты gzip'ом - */ - function sendFile(pParams){ - var lRet, - lName, lReq, lRes; - - if(pParams){ - lName = pParams.name, - lReq = pParams.request, - lRes = pParams.response; - } - - if(lName && lRes && lReq){ - var lEnc = lReq.headers['accept-encoding'] || '', - lGzip = lEnc.match(/\bgzip\b/), - - lReadStream = fs.createReadStream(lName, { - 'bufferSize': 4 * 1024 - }); - - lReadStream.on('error', function(pError){ - lRes.writeHead(FILE_NOT_FOUND, 'OK'); - lRes.end(String(pError)); - }); - - lRes.writeHead(OK, generateHeaders(lName, lGzip) ); - - if (lGzip) - lReadStream = lReadStream.pipe( zlib.createGzip() ); - - lReadStream.pipe(lRes); - - lRet = true; - } - - return lRet; - } - - exports.generateHeaders = generateHeaders; - exports.sendFile = sendFile; + exports.start = start; })(); diff --git a/lib/server/commander.js b/lib/server/commander.js index 316a2c0f..d8c34654 100644 --- a/lib/server/commander.js +++ b/lib/server/commander.js @@ -12,17 +12,16 @@ var main = global.cloudcmd.main, fs = main.fs, - zlib = main.zlib, - url = main.url, - querystring = main.querystring, CloudFunc = main.cloudfunc, DIR = main.DIR, LIBDIR = main.LIBDIR, HTMLDIR = main.HTMLDIR, Util = main.util, - server = main.server, + url = main.url, + querystring = main.querystring, + zlib = main.zlib, - NOT_FOUND = 404, + FILE_NOT_FOUND = 404, OK = 200, FS = CloudFunc.FS, @@ -52,15 +51,15 @@ if(pStat.isDirectory()) fs.readdir(lPath, Util.call(readDir, pParams) ); else - server.sendFile({ + main.sendFile({ name : lPath, request : p.request, response : p.response }); else sendResponse({ - status : NOT_FOUND, - data : pError.toString(), + status : FILE_NOT_FOUND, + data : pError.toString(), request : p.request, response : p.response }); @@ -132,7 +131,7 @@ } else sendResponse({ - status : NOT_FOUND, + status : FILE_NOT_FOUND, data : lError.toString(), request : lReq, response: lRes @@ -294,15 +293,58 @@ Util.log('file ' + c.name + ' readed'); } else{ - lParams.status = NOT_FOUND; + lParams.status = FILE_NOT_FOUND; lParams.data = p.error.toString(); } sendResponse(lParams); } } + /** + * Функция высылает ответ серверу + * @param pHead - заголовок + * @param Data - данные + * @param pName - имя отсылаемого файла + */ + function sendResponse(pParams){ + var lRet = Util.checkObjTrue(pParams, + ['name', 'data', REQUEST, RESPONSE]); + + if(lRet){ + var p = pParams; + + var lPath = p.name || getCleanPath(p.request), + lQuery = getQuery(p.request), + /* download, json */ + lGzip = isGZIP(p.request), + lHead = main.generateHeaders(lPath, lGzip, lQuery); + + /* если браузер поддерживает gzip-сжатие - сжимаем данные*/ + Util.ifExec(!lGzip, + function(pParams){ + var lRet = Util.checkObj(pParams, ['data']); + + if(lRet){ + p.status = pParams.status; + p.data = pParams.data; + } + + p.response.writeHead(p.status || OK, lHead); + p.response.end(p.data); + + Util.log(lPath + ' sended'); + Util.log( p.status === FILE_NOT_FOUND && p.data ); + }, + + function(pCallBack){ + zlib.gzip (p.data, Util.call(gzipData, { + callback : pCallBack + })); + }); + } + } - /** + /** * Функция получает сжатые данные * @param pHeader - заголовок файла * @pName @@ -323,7 +365,7 @@ if(!p.error) lParams.data = p.data; else{ - lParams.status = NOT_FOUND; + lParams.status = FILE_NOT_FOUND; lParams.data = p.error.toString(); } @@ -384,48 +426,4 @@ return lGZIP; } - /** - * Функция высылает ответ серверу - * @param pHead - заголовок - * @param Data - данные - * @param pName - имя отсылаемого файла - */ - function sendResponse(pParams){ - var lRet = Util.checkObjTrue(pParams, - ['name', 'data', REQUEST, RESPONSE]); - - if(lRet){ - var p = pParams; - - var lPath = p.name || getCleanPath(p.request), - lQuery = getQuery(p.request), - /* download, json */ - lGzip = isGZIP(p.request), - lHead = server.generateHeaders(lPath, lGzip, lQuery); - - /* если браузер поддерживает gzip-сжатие - сжимаем данные*/ - Util.ifExec(!lGzip, - function(pParams){ - var lRet = Util.checkObj(pParams, ['data']); - - if(lRet){ - p.status = pParams.status; - p.data = pParams.data; - } - - p.response.writeHead(p.status || OK, lHead); - p.response.end(p.data); - - Util.log(lPath + ' sended'); - Util.log( p.status === NOT_FOUND && p.data ); - }, - - function(pCallBack){ - zlib.gzip (p.data, Util.call(gzipData, { - callback : pCallBack - })); - }); - } - } - })(); \ No newline at end of file diff --git a/lib/server/main.js b/lib/server/main.js index 4142a734..4d0b1242 100644 --- a/lib/server/main.js +++ b/lib/server/main.js @@ -12,49 +12,56 @@ SLASH, ISWIN32, + ext, + path, + fs, + zlib, - path; + OK = 200, + FILE_NOT_FOUND = 404; /* 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'), + exports.crypto = require('crypto'), + exports.child_process = require('child_process'), + exports.fs = fs = require('fs'), + exports.http = require('http'), + exports.https = require('https'), + exports.path = path = require('path'), + exports.url = require('url'), + exports.querystring = require('querystring'), /* Constants */ /* current dir + 2 levels up */ - exports.WIN32 = ISWIN32 = isWin32(); - exports.SLASH = SLASH = ISWIN32 ? '\\' : '/', + exports.WIN32 = ISWIN32 = isWin32(); + exports.SLASH = SLASH = ISWIN32 ? '\\' : '/', - exports.SRVDIR = SRVDIR = __dirname + SLASH, - exports.LIBDIR = LIBDIR = path.normalize(SRVDIR + '../'), - exports.DIR = DIR = path.normalize(LIBDIR + '../'), - exports.HTMLDIR = DIR + 'html/', - exports.JSONDIR = JSONDIR = DIR + 'json/', + exports.SRVDIR = SRVDIR = __dirname + SLASH, + exports.LIBDIR = LIBDIR = path.normalize(SRVDIR + '../'), + exports.DIR = DIR = path.normalize(LIBDIR + '../'), + exports.HTMLDIR = DIR + 'html/', + exports.JSONDIR = JSONDIR = DIR + 'json/', /* Functions */ - exports.require = mrequire, - exports.librequire = librequire, - exports.srvrequire = srvrequire, - exports.rootrequire = rootrequire, + exports.require = mrequire, + exports.librequire = librequire, + exports.srvrequire = srvrequire, + exports.rootrequire = rootrequire, + exports.generateHeaders = generateHeaders, + exports.sendFile = sendFile, /* compitability with old versions of node */ - exports.fs.exists = exports.fs.exists || exports.path.exists; + exports.fs.exists = exports.fs.exists || exports.path.exists; /* Needed Modules */ - exports.util = Util = require(LIBDIR + 'util'), + exports.util = Util = require(LIBDIR + 'util'), - exports.zlib = mrequire('zlib'), + exports.zlib = zlib = mrequire('zlib'), /* Main Information */ - exports.config = jsonrequire('config'); - exports.modules = jsonrequire('modules'); - exports.ext = jsonrequire('ext'); - exports.mainpackage = rootrequire('package'); + exports.config = jsonrequire('config'); + exports.modules = jsonrequire('modules'); + exports.ext = ext = jsonrequire('ext'); + exports.mainpackage = rootrequire('package'); /* @@ -63,22 +70,21 @@ * moudles do not depends on each other all needed information * for all modules is initialized hear. */ - global.cloudcmd.main = exports; + global.cloudcmd.main = exports; - exports.VOLUMES = getVolumes(), + exports.VOLUMES = getVolumes(), /* Additional Modules */ - exports.minify = srvrequire('minify').Minify; - exports.socket = srvrequire('socket'), - exports.server = librequire('server'), - exports.auth = srvrequire('auth').auth, - exports.appcache = srvrequire('appcache'), - exports.cache = srvrequire('cache').Cache, - exports.cloudfunc = librequire('cloudfunc'), - exports.rest = srvrequire('rest').api, - exports.update = srvrequire('update'), - exports.ischanged = srvrequire('ischanged'); - exports.commander = srvrequire('commander'); + exports.socket = srvrequire('socket'), + exports.auth = srvrequire('auth').auth, + exports.appcache = srvrequire('appcache'), + exports.cache = srvrequire('cache').Cache, + exports.cloudfunc = librequire('cloudfunc'), + exports.rest = srvrequire('rest').api, + exports.update = srvrequire('update'), + exports.ischanged = srvrequire('ischanged'); + exports.commander = srvrequire('commander'); + 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 @@ -113,7 +119,6 @@ */ function isWin32(){ return process.platform === 'win32'; } - /** * get volumes if win32 or get nothing if nix */ @@ -128,4 +133,97 @@ return lRet; } + + /** + * Функция создаёт заголовки файлов + * в зависимости от расширения файла + * перед отправкой их клиенту + * @param pName - имя файла + * @param pGzip - данные сжаты gzip'ом + */ + function generateHeaders(pName, pGzip, pQuery){ + var lRet, + lType = '', + lContentEncoding = '', + lCacheControl = 0, + + lExt = Util.getExtension(pName); + + if( Util.strCmp(lExt, '.appcache') ) + lCacheControl = 1; + + lType = ext[lExt] || 'text/plain'; + + if( !Util.isContainStr(lType, 'img') ) + lContentEncoding = '; charset=UTF-8'; + + if(Util.strCmp(pQuery, 'download') ) + lType = 'application/octet-stream'; + + + if(!lCacheControl) + lCacheControl = 31337 * 21; + + lRet = { + /* if type of file any, but img - + * then we shoud specify charset + */ + 'Content-Type': lType + lContentEncoding, + 'cache-control': 'max-age=' + lCacheControl, + 'last-modified': new Date().toString(), + /* https://developers.google.com/speed/docs/best-practices + /caching?hl=ru#LeverageProxyCaching */ + 'Vary': 'Accept-Encoding' + }; + + if(pGzip) + lRet['content-encoding'] = 'gzip'; + + return lRet; + } + + /** + * send file to client thru pipe + * and gzip it if client support + * + * @param pName - имя файла + * @param pGzip - данные сжаты gzip'ом + */ + function sendFile(pParams){ + var lRet, + lName, lReq, lRes; + + if(pParams){ + lName = pParams.name, + lReq = pParams.request, + lRes = pParams.response; + } + + if(lName && lRes && lReq){ + var lEnc = lReq.headers['accept-encoding'] || '', + lGzip = lEnc.match(/\bgzip\b/), + + lReadStream = fs.createReadStream(lName, { + 'bufferSize': 4 * 1024 + }); + + lReadStream.on('error', function(pError){ + lRes.writeHead(FILE_NOT_FOUND, 'OK'); + lRes.end(String(pError)); + }); + + lRes.writeHead(OK, generateHeaders(lName, lGzip) ); + + if (lGzip) + lReadStream = lReadStream.pipe( zlib.createGzip() ); + + lReadStream.pipe(lRes); + + lRet = true; + } + + return lRet; + } + + })(); diff --git a/lib/server/minify.js b/lib/server/minify.js index 7aeaff4c..21969f07 100644 --- a/lib/server/minify.js +++ b/lib/server/minify.js @@ -19,8 +19,7 @@ LIBDIR = main.LIBDIR, HTMLDIR = main.HTMLDIR, Util = main.util, - Minify = main.minify, - server = main.server, + Minify = main.require('minify'), IsChanged = main.ischanged, COULD_NOT_MINIFY = 'Could not minify without minify module\n' + @@ -73,7 +72,7 @@ if(pChanged) Minify.optimize(pName, pParams); else - server.sendFile(pParams); + main.sendFile(pParams); }); } else{ diff --git a/lib/server/rest.js b/lib/server/rest.js index d747c281..6957bb31 100644 --- a/lib/server/rest.js +++ b/lib/server/rest.js @@ -16,10 +16,9 @@ var main = global.cloudcmd.main, Util = main.util, Config = main.config, - server = main.server, APIURL = Config.api_url, OK = 200, - Header = server.generateHeaders('api.json', false); + Header = main.generateHeaders('api.json', false); /** * rest interface @@ -154,7 +153,7 @@ if( Util.isString(lFiles) ){ pParams.name = lFiles; - server.sendFile(pParams); + main.sendFile(pParams); lResult = null; } From 08c8ee6cc9b4d0e6c74a3cab05e058e740f7339a Mon Sep 17 00:00:00 2001 From: coderaiser Date: Tue, 5 Feb 2013 09:49:19 -0500 Subject: [PATCH 212/347] refactored --- lib/client.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/client.js b/lib/client.js index 9b6d73b0..be8f9438 100644 --- a/lib/client.js +++ b/lib/client.js @@ -534,8 +534,8 @@ CloudCmd._ajaxLoad = function(pPath, pOptions){ var lFSPath = decodeURI(pPath); lFSPath = Util.removeStr( lFSPath, CloudFunc.NOJS ); - - Util.log ('reading dir: "' + pPath + '";'); + var lCleanPath = Util.removeStr(lFSPath, CloudFunc.FS); + Util.log ('reading dir: "' + lCleanPath + '";'); if(!pOptions.nohistory) DOM.setHistory(pPath, null, pPath); From a8e453056e00e839929e4ffcc546fd3ff8735f5f Mon Sep 17 00:00:00 2001 From: coderaiser Date: Tue, 5 Feb 2013 10:42:15 -0500 Subject: [PATCH 213/347] minor changes --- README.md | 4 ---- 1 file changed, 4 deletions(-) diff --git a/README.md b/README.md index dcce602b..0f50a2a8 100644 --- a/README.md +++ b/README.md @@ -84,10 +84,6 @@ All main configuration could be done thrue config.json. "html" : true, "img" : false }, - "github_key" : "891c251b925e4e967fa9", - "github_secret" : "afe9bed1e810c5dc44c4c2a953fc6efb1e5b0545", - "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 */ From ac2364559a32a22fddd63c366109a00a487796d1 Mon Sep 17 00:00:00 2001 From: coderaiser Date: Tue, 5 Feb 2013 10:43:26 -0500 Subject: [PATCH 214/347] minor changes --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 0f50a2a8..d729f1c3 100644 --- a/README.md +++ b/README.md @@ -8,7 +8,7 @@ DEMO: Google PageSpeed Score : [100](//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](//raw.github.com/coderaiser/cloudcmd/dev/img/logo/cloudcmd.png "Cloud Commander") +![Cloud Commander](img/logo/cloudcmd.png "Cloud Commander") Benefits --------------- From 333680fcae9da2d851e770181255ff42da04f3a1 Mon Sep 17 00:00:00 2001 From: coderaiser Date: Tue, 5 Feb 2013 10:44:41 -0500 Subject: [PATCH 215/347] fixed links --- README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index d729f1c3..ad96c9e8 100644 --- a/README.md +++ b/README.md @@ -2,10 +2,10 @@ Cloud Commander [![Build Status](https://secure.travis-ci.org/coderaiser/cloudcm =============== **Cloud Commander** - user friendly cloud file manager. DEMO: -[cloudfoundry] (//cloudcmd.cloudfoundry.com "cloudfoundry"), -[appfog] (//cloudcmd.aws.af.cm "appfog"). +[cloudfoundry] (http://cloudcmd.cloudfoundry.com "cloudfoundry"), +[appfog] (http://cloudcmd.aws.af.cm "appfog"). -Google PageSpeed Score : [100](//developers.google.com/speed/pagespeed/insights#url=http_3A_2F_2Fcloudcmd.cloudfoundry.com_2F&mobile=false "score") (out of 100) +Google PageSpeed Score : [100](http://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](img/logo/cloudcmd.png "Cloud Commander") From 46f9cf7e877124399de39ced6987e7a5e276f783 Mon Sep 17 00:00:00 2001 From: coderaiser Date: Wed, 6 Feb 2013 04:00:37 -0500 Subject: [PATCH 216/347] added bin directory and minify cmd tool --- lib/server.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/server.js b/lib/server.js index 4328d18a..879b8861 100644 --- a/lib/server.js +++ b/lib/server.js @@ -171,7 +171,8 @@ Minify.optimize(lName, { request : pReq, response : pRes, - callback : pCallBack + callback : pCallBack, + returnName : true }); }); } From 41c5b1d8366275a39098b8acb8e391dc5a3d3fda Mon Sep 17 00:00:00 2001 From: coderaiser Date: Wed, 6 Feb 2013 08:02:16 -0500 Subject: [PATCH 217/347] minor changes --- cloudcmd.js | 445 +++++++++++++++++++++++++------------------------- shell/kill.js | 36 ++-- 2 files changed, 241 insertions(+), 240 deletions(-) diff --git a/cloudcmd.js b/cloudcmd.js index d4012c5d..df0fdd1b 100644 --- a/cloudcmd.js +++ b/cloudcmd.js @@ -1,223 +1,224 @@ -(function(){ - 'use strict'; - - var DIR = __dirname + '/', - main = require(DIR + 'lib/server/main'), - - LIBDIR = main.LIBDIR, - SRVDIR = main.SRVDIR, - CLIENTDIR = LIBDIR + 'client', - - path = main.path, - fs = main.fs, - CloudFunc = main.cloudfunc, - AppCache = main.appcache, - Util = main.util, - update = main.update, - - server = main.librequire('server'), - Minify = main.minify, - Config = main.config, - - REQUEST = 'request', - RESPONSE = 'response', - INDEX = DIR + 'html' + main.SLASH + 'index.html'; - - /* reinit main dir os if we on - * Win32 should be backslashes */ - DIR = main.DIR; - - readConfig(); - server.start(Config, { - appcache : appCacheProcessing, - minimize : minimize, - rest : rest, - route : route - }); - - if(update) - update.get(); - - /** - * additional processing of index file - */ - function indexProcessing(pData){ - var lReplace_s, - lData = pData.data, - lAdditional = pData.additional; - - /* - * если выбрана опция минимизировать скрипты - * меняем в index.html обычные css на - * минифицированый - */ - if(Minify.allowed.css){ - var lPath = '/' + Minify.MinFolder.replace(DIR, ''); - lReplace_s = ''; - lData = Util.removeStr(lData, lReplace_s) - .replace('/css/style.css', lPath + 'all.min.css'); - } - - /* меняем title */ - lReplace_s = '
    '; - lData = lData.replace(lReplace_s, lReplace_s + lAdditional) - .replace('Cloud Commander', - '' + CloudFunc.getTitle() + ''); - - if(!Config.appcache) - lData = Util.removeStr(lData, ' manifest="/cloudcmd.appcache"'); - - if(!Config.show_keys_panel){ - var lKeysPanel = '
    auth'); - - pParams.name = main.HTMLDIR + lName + '.html'; - lRet = main.sendFile(pParams); - }else if( Util.strCmp(lName, '/auth/github') ){ - Util.log('* Routing' + - '-> github'); - - pParams.name = main.HTMLDIR + lName + '.html'; - lRet = main.sendFile(pParams); - }else if( Util.isContainStr(lName, CloudFunc.FS) || - Util.isContainStr(lName, CloudFunc.NO_JS ) || - Util.strCmp(lName, '/') || - Util.strCmp(lName, 'json') ){ - - lRet = main.commander.sendContent({ - request : pParams[REQUEST], - response : pParams[RESPONSE], - processing : indexProcessing, - index : Minify.allowed.html ? Minify.getName(INDEX) : INDEX - }); - } - - return lRet; - } - - - /* function sets stdout to file log.txt */ - function writeLogsToFile(){ - var stdo = fs.createWriteStream('./log.txt'); - - process.stdout.write = (function(write) { - return function(string, encoding, fd) { - stdo.write(string); - }; - })(process.stdout.write); - } +(function(){ + 'use strict'; + + var DIR = __dirname + '/', + main = require(DIR + 'lib/server/main'), + + LIBDIR = main.LIBDIR, + SRVDIR = main.SRVDIR, + CLIENTDIR = LIBDIR + 'client', + HTMLDIR = main.HTMLDIR, + + path = main.path, + fs = main.fs, + CloudFunc = main.cloudfunc, + AppCache = main.appcache, + Util = main.util, + update = main.update, + + server = main.librequire('server'), + Minify = main.minify, + Config = main.config, + + REQUEST = 'request', + RESPONSE = 'response', + INDEX = HTMLDIR + 'index.html'; + + /* reinit main dir os if we on + * Win32 should be backslashes */ + DIR = main.DIR; + + readConfig(); + server.start(Config, { + appcache : appCacheProcessing, + minimize : minimize, + rest : rest, + route : route + }); + + if(update) + update.get(); + + /** + * additional processing of index file + */ + function indexProcessing(pData){ + var lReplace_s, + lData = pData.data, + lAdditional = pData.additional; + + /* + * если выбрана опция минимизировать скрипты + * меняем в index.html обычные css на + * минифицированый + */ + if(Minify.allowed.css){ + var lPath = '/' + Minify.MinFolder.replace(DIR, ''); + lReplace_s = ''; + lData = Util.removeStr(lData, lReplace_s) + .replace('/css/style.css', lPath + 'all.min.css'); + } + + /* меняем title */ + lReplace_s = '
    '; + lData = lData.replace(lReplace_s, lReplace_s + lAdditional) + .replace('Cloud Commander', + '' + CloudFunc.getTitle() + ''); + + if(!Config.appcache) + lData = Util.removeStr(lData, ' manifest="/cloudcmd.appcache"'); + + if(!Config.show_keys_panel){ + var lKeysPanel = '
    auth'); + + pParams.name = main.HTMLDIR + lName + '.html'; + lRet = main.sendFile(pParams); + }else if( Util.strCmp(lName, '/auth/github') ){ + Util.log('* Routing' + + '-> github'); + + pParams.name = main.HTMLDIR + lName + '.html'; + lRet = main.sendFile(pParams); + }else if( Util.isContainStr(lName, CloudFunc.FS) || + Util.isContainStr(lName, CloudFunc.NO_JS ) || + Util.strCmp(lName, '/') || + Util.strCmp(lName, 'json') ){ + + lRet = main.commander.sendContent({ + request : pParams[REQUEST], + response : pParams[RESPONSE], + processing : indexProcessing, + index : Minify.allowed.html ? Minify.getName(INDEX) : INDEX + }); + } + + return lRet; + } + + + /* function sets stdout to file log.txt */ + function writeLogsToFile(){ + var stdo = fs.createWriteStream('./log.txt'); + + process.stdout.write = (function(write) { + return function(string, encoding, fd) { + stdo.write(string); + }; + })(process.stdout.write); + } })(); \ No newline at end of file diff --git a/shell/kill.js b/shell/kill.js index 30a659dc..8d85033c 100644 --- a/shell/kill.js +++ b/shell/kill.js @@ -1,18 +1,18 @@ - -/* 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); - }); - -})(); +#!/usr/bin/env node +/* 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); + }); + +})(); From 5ecd0bf9eabe38b9793da435ee0f12ba16f59681 Mon Sep 17 00:00:00 2001 From: coderaiser Date: Wed, 6 Feb 2013 10:11:02 -0500 Subject: [PATCH 218/347] added params to closure for minifing result code --- lib/client.js | 1418 ++++++++++++++++++++++---------------------- lib/server/main.js | 453 +++++++------- 2 files changed, 933 insertions(+), 938 deletions(-) diff --git a/lib/client.js b/lib/client.js index be8f9438..ebeaa32c 100644 --- a/lib/client.js +++ b/lib/client.js @@ -1,709 +1,709 @@ -/* Функция которая возвратит обьект CloudCommander - * @CloudFunc - обьект содержащий общий функционал - * клиентский и серверный - */ - -var Util, DOM, CloudFunc, $, KeyBinding, CloudCommander = (function(){ -'use strict'; - -var Config, Modules; - -/* Клиентский обьект, содержащий функциональную часть*/ -var CloudCmd = { - /* Конструктор CloudClient, который выполняет - * весь функционал по инициализации - */ - init : null, /* start initialization */ - - KeyBinding : null, /* обьект обработки нажатий клавишь */ - KeysPanel : null, /* panel with key buttons f1-f8 */ - Editor : null, /* function loads and shows editor */ - Storage : null, /* function loads storage */ - Viewer : null, /* function loads and shows viewer */ - Terminal : null, /* function loads and shows terminal*/ - Menu : null, /* function loads and shows menu */ - GoogleAnalytics : null, - - _loadDir : null, /* Функция привязываеться ко всем - * ссылкам и - * загружает содержимое каталогов */ - - /* ОБЬЕКТЫ */ - - /* ПРИВАТНЫЕ ФУНКЦИИ */ - /* функция загружает json-данные о файловой системе */ - _ajaxLoad : null, - - /* Функция генерирует JSON из html-таблицы файлов */ - _getJSONfromFileTable : null, - - /* функция меняет ссыки на ajax-овые */ - _changeLinks : null, - - /* КОНСТАНТЫ*/ - LIBDIR : '/lib/', - LIBDIRCLIENT : '/lib/client/', - JSONDIR : '/json/', - /* height of Cloud Commander - * seting up in init() - */ - HEIGHT : 0, - MIN_ONE_PANEL_WIDTH : 1155, - OLD_BROWSER : false, - - HOST : (function(){ - var lLocation = document.location; - return lLocation.protocol + '//' + lLocation.host; - })() -}; - -CloudCmd.GoogleAnalytics = function(){ - DOM.addOneTimeListener('mousemove', function(){ - var lUrl = CloudCmd.LIBDIRCLIENT + 'google_analytics.js'; - - setTimeout(function(){ - DOM.jsload(lUrl); - }, 5000); - }); -}; - -/** - * Функция привязываеться ко всем ссылкам и - * загружает содержимое каталогов - * - * @param pLink - ссылка - * @param pNeedRefresh - необходимость обязательной загрузки данных с сервера - */ -CloudCmd._loadDir = function(pLink, pNeedRefresh){ - return function(pEvent){ - /* показываем гиф загрузки возле пути папки сверху - * ctrl+r нажата? */ - - var lCurrentLink = DOM.getCurrentLink(), - lHref = lCurrentLink.href, - lParent = lCurrentLink.textContent, - lLink = pLink || Util.removeStr(lHref, CloudCmd.HOST), - lDir = DOM.getCurrentDir(); - - if(pLink || lCurrentLink.target !== '_blank'){ - DOM.Images.showLoad(pNeedRefresh ? {top:true} : null); - - /* загружаем содержимое каталога */ - CloudCmd._ajaxLoad(lLink, { refresh: pNeedRefresh }); - - /* если нажали на ссылку на верхний каталог*/ - if(lParent === '..' && lDir !== '/') - CloudCmd._currentToParent(lDir); - } - - DOM.preventDefault(pEvent); - }; -}; - - -/** - * Function edits file name - * - * @param pParent - parent element - * @param pEvent - */ -CloudCmd._editFileName = function(pParent){ - var lA = DOM.getCurrentLink(pParent); - - if (lA && lA.textContent !== '..'){ - - lA.contentEditable = true; - KeyBinding.unSet(); - - /* setting event handler onclick - * if user clicks somewhere keyBinded - * backs - */ - DOM.addOneTimeListener('click', function(){ - var lA = DOM.getCurrentLink(pParent); - if (lA && lA.textContent !== '..') - lA.contentEditable = false; - - KeyBinding.set(); - }); - } -}; - - -/** функция устанавливает курсор на каталог - * с которого мы пришли, если мы поднялись - * в верх по файловой структуре - * @param pDirName - имя каталога с которого мы пришли - */ -CloudCmd._currentToParent = function(pDirName){ - /* убираем слэш с имени каталога */ - pDirName = Util.removeStr(pDirName, '/'); - - /* опредиляем в какой мы панели: * - * правой или левой */ - var lPanel = DOM.getPanel(), - lRootDir = DOM.getById(pDirName + '(' + lPanel.id + ')'); - - /* if found li element with ID directory name * - * set it to current file */ - if(lRootDir){ - DOM.setCurrentFile(lRootDir); - DOM.scrollIntoViewIfNeeded(lRootDir, true); - } -}; - -/** - * function load modules - * @pParams = {name, path, func, dobefore, arg} - */ -function loadModule(pParams){ - if(!pParams) return; - - var lName = pParams.name, - lPath = pParams.path, - lFunc = pParams.func, - lDoBefore = pParams.dobefore; - - if( Util.isString(pParams) ) - lPath = pParams; - - if(lPath && !lName){ - lName = lPath[0].toUpperCase() + lPath.substring(1); - lName = Util.removeStr(lName, '.js'); - - var lSlash = lName.indexOf('/'); - if(lSlash > 0){ - var lAfterSlash = lName.substr(lSlash); - lName = Util.removeStr(lName, lAfterSlash); - } - } - - if( !Util.isContainStr(lPath, '.js') ) - lPath += '.js'; - - if(!CloudCmd[lName]) - CloudCmd[lName] = function(pArg){ - Util.exec(lDoBefore); - - return DOM.jsload(CloudCmd.LIBDIRCLIENT + lPath, lFunc || - function(){ - Util.exec(CloudCmd[lName].init, pArg); - }); - }; -} -/** Конструктор CloudClient, который - * выполняет весь функционал по - * инициализации - */ -CloudCmd.init = function(){ - var lCallBack = function(){ - Util.loadOnLoad([ - initKeysPanel, - initModules, - baseInit - ]); - }, - lFunc = function(pCallBack){ - this.OLD_BROWSER = true; - var lSrc = CloudCmd.LIBDIRCLIENT + 'ie.js'; - - DOM.jqueryLoad( - DOM.retJSLoad(lSrc, pCallBack) - ); - }; - - //Util.socketLoad(); - - Util.ifExec(document.body.scrollIntoViewIfNeeded, lCallBack, lFunc); -}; - -function initModules(pCallBack){ - loadModule({ - /* привязываем клавиши к функциям */ - path : 'keyBinding.js', - func : function(){ - KeyBinding = CloudCmd.KeyBinding; - KeyBinding.init(); - } - }); - - CloudCmd.getModules(function(pModules){ - pModules = pModules || []; - - DOM.addListener('contextmenu', function(pEvent){ - CloudCmd.Menu.ENABLED || DOM.preventDefault(pEvent); - }, document); - - var lStorage = 'storage', - lShowLoadFunc = Util.retFunc( DOM.Images.showLoad ), - - lDoBefore = { - 'editor/_codemirror' : lShowLoadFunc, - 'viewer' : lShowLoadFunc - }, - - lLoad = function(pName, pPath, pDoBefore){ - loadModule({ - path : pPath, - name : pName, - dobefore : pDoBefore - }); - }; - - for(var i = 0, n = pModules.length; i < n ; i++){ - var lModule = pModules[i]; - - if(Util.isString(lModule)) - lLoad(null, lModule, lDoBefore[lModule]); - } - - var lStorageObj = Util.findObjByNameInArr( pModules, lStorage ), - lMod = Util.getNamesFromObjArray( lStorageObj ); - - for(i = 0, n = lMod.length; i < n; i++){ - var lName = lMod[i], - lPath = lStorage + '/_' + lName.toLowerCase(); - - lLoad(lName, lPath); - } - - - Util.exec(pCallBack); - - }); -} - -function initKeysPanel(pCallBack){ - var lKeysPanel = {}, - - lFuncs =[ - null, - null, /* f1 */ - null, /* f2 */ - CloudCmd.Viewer, /* f3 */ - CloudCmd.Editor, /* f4 */ - null, /* f5 */ - null, /* f6 */ - null, /* f7 */ - DOM.promptRemoveCurrent,/* f8 */ - ]; - - for(var i = 1; i <= 8; i++){ - var lButton = 'f' + i, - lEl = DOM.getById('f' + i); - - DOM.addClickListener(lFuncs[i], lEl); - lKeysPanel[lButton] = lEl; - } - - CloudCmd.KeysPanel = lKeysPanel; - Util.exec(pCallBack); -} - -function baseInit(pCallBack){ - if(window.applicationCache){ - var lFunc = applicationCache.onupdateready; - - applicationCache.onupdateready = function(){ - Util.log('app cacheed'); - location.reload(); - - Util.exec(lFunc); - }; - } - - /* загружаем общие функции для клиента и сервера */ - DOM.jsload(CloudCmd.LIBDIR + 'cloudfunc.js',function(){ - DOM.addListener("popstate", function(pEvent) { - var lPath = pEvent.state; - - if(lPath) - CloudCmd._ajaxLoad(lPath, {nohistory: true}); - - return true; - }); - - /* берём из обьекта window общий с сервером функционал */ - CloudFunc = window.CloudFunc; - - /* меняем ссылки на ajax'овые */ - CloudCmd._changeLinks(CloudFunc.LEFTPANEL); - CloudCmd._changeLinks(CloudFunc.RIGHTPANEL); - - /* устанавливаем переменную доступности кэша */ - DOM.Cache.isAllowed(); - /* Устанавливаем кэш корневого каталога */ - if( !DOM.Cache.get('/') ) - DOM.Cache.set('/', CloudCmd._getJSONfromFileTable()); - }); - - /* устанавливаем размер высоты таблицы файлов - * исходя из размеров разрешения экрана - */ - - /* выделяем строку с первым файлом */ - var lFmHeader = DOM.getByClass('fm-header'); - DOM.setCurrentFile(lFmHeader[0].nextSibling); - - /* показываем элементы, которые будут работать только, если есть js */ - var lFM = DOM.getById('fm'); - lFM.className='localstorage'; - - /* формируем и округляем высоту экрана - * при разрешениии 1024x1280: - * 658 -> 700 - */ - - var lHeight = window.screen.height; - lHeight = lHeight - (lHeight/3).toFixed(); - - lHeight = (lHeight / 100).toFixed() * 100; - - CloudCmd.HEIGHT = lHeight; - - DOM.cssSet({ - id:'cloudcmd', - inner: - '.panel{' + - 'height:' + lHeight +'px;' + - '}' - }); - - Util.exec(pCallBack); - CloudCmd.KeyBinding(); -} - -CloudCmd.getConfig = function(pCallBack){ - Util.ifExec(Config, pCallBack, function(pCallBack){ - DOM.ajax({ - url : CloudCmd.JSONDIR + 'config.json', - success : function(pConfig){ - Config = pConfig; - Util.exec(pCallBack, pConfig); - } - }); - }); -}; - -CloudCmd.getModules = function(pCallBack){ - Util.ifExec(Modules, pCallBack, function(pCallBack){ - DOM.ajax({ - url : CloudCmd.JSONDIR + 'modules.json', - success : Util.retExec(pCallBack) - }); - }); -}; - - -/* функция меняет ссыки на ajax-овые */ -CloudCmd._changeLinks = function(pPanelID){ - /* назначаем кнопку очистить кэш и показываем её */ - var lClearcache = DOM.getById('clear-cache'); - DOM.addClickListener(DOM.Cache.clear, lClearcache); - - /* меняем ссылки на ajax-запросы */ - var lPanel = DOM.getById(pPanelID), - a = lPanel.getElementsByTagName('a'), - - /* номер ссылки иконки обновления страницы */ - lREFRESHICON = 0, - - /* путь в ссылке, который говорит - * что js отключен - */ - lNoJS_s = CloudFunc.NOJS, - - /* right mouse click function varible */ - lOnContextMenu_f = function(pEvent){ - var lReturn_b = true; - - KeyBinding.unSet(); - - /* getting html element - * currentTarget - DOM event - * target - jquery event - */ - var lTarget = pEvent.currentTarget || pEvent.target; - DOM.setCurrentFile(lTarget); - - if(Util.isFunction(CloudCmd.Menu) ){ - CloudCmd.Menu({ - x: pEvent.x, - y: pEvent.y - }); - - /* disabling browsers menu*/ - lReturn_b = false; - DOM.Images.showLoad(); - } - - return lReturn_b; - }, - - /* drag and drop function varible - * download file from browser to descktop - * in Chrome (HTML5) - */ - lOnDragStart_f = function(pEvent){ - var lElement = pEvent.target, - lLink = lElement.href, - lName = lElement.textContent, - /* if it's directory - adding json extension */ - lType = lElement.parentElement.nextSibling; - - if(lType && lType.textContent === ''){ - lLink = Util.removeStr(lLink, lNoJS_s); - lName += '.json'; - } - - pEvent.dataTransfer.setData("DownloadURL", - 'application/octet-stream' + ':' + - lName + ':' + - lLink); - }, - - lSetCurrentFile_f = function(pEvent){ - var pElement = pEvent.target, - lTag = pElement.tagName; - - if(lTag !== 'LI') - do{ - pElement = pElement.parentElement; - lTag = pElement.tagName; - }while(lTag !== 'LI'); - - DOM.setCurrentFile(pElement); - }, - - lUrl = CloudCmd.HOST; - - var lLoadDirOnce = CloudCmd._loadDir(); - for(var i = 0, n = a.length; i < n ; i++){ - /* убираем адрес хоста*/ - var ai = a[i], - link = ai.href.replace(lUrl, ''), - lNEADREFRESH = i === lREFRESHICON, - lLoadDir = CloudCmd._loadDir(link, lNEADREFRESH); - - /* ставим загрузку гифа на клик*/ - if(lNEADREFRESH) - DOM.addClickListener( lLoadDir, ai.parentElement ); - - /* устанавливаем обработчики на строку - * на двойное нажатие на левую кнопку мышки */ - else{ - var lLi = ai.parentElement.parentElement; - - /* if we in path changing onclick events */ - if (lLi.className === 'path') - DOM.addClickListener( lLoadDir, ai ); - else { - DOM.addClickListener( DOM.preventDefault, lLi); - DOM.addListener('mousedown', lSetCurrentFile_f, lLi); - DOM.addListener('ondragstart', lOnDragStart_f, ai); - /* if right button clicked menu will - * loads and shows - */ - DOM.addListener('contextmenu', lOnContextMenu_f, lLi); - - /* если ссылка на папку, а не файл */ - if(ai.target !== '_blank'){ - DOM.addListener('dblclick', lLoadDirOnce, lLi); - DOM.addListener('touchend', lLoadDirOnce, lLi); - } - - lLi.id = (ai.title ? ai.title : ai.textContent) + - '(' + pPanelID + ')'; - } - } - } -}; - -/** - * Функция загружает json-данные о Файловой Системе - * через ajax-запрос. - * @param path - каталог для чтения - * @param pOptions - * { refresh, nohistory } - необходимость обновить данные о каталоге - */ -CloudCmd._ajaxLoad = function(pPath, pOptions){ - if(!pOptions) - pOptions = {}; - - /* Отображаем красивые пути */ - var lFSPath = decodeURI(pPath); - - lFSPath = Util.removeStr( lFSPath, CloudFunc.NOJS ); - var lCleanPath = Util.removeStr(lFSPath, CloudFunc.FS); - Util.log ('reading dir: "' + lCleanPath + '";'); - - if(!pOptions.nohistory) - DOM.setHistory(pPath, null, pPath); - - DOM.setTitle( CloudFunc.getTitle(pPath) ); - - /* если доступен localStorage и - * в нём есть нужная нам директория - - * читаем данные с него и - * выходим - * если стоит поле обязательной перезагрузки - - * перезагружаемся - */ - - /* опредиляем в какой мы панели: - * правой или левой - */ - var lPanel = DOM.getPanel().id; - - if(!pOptions.refresh && lPanel){ - var lJSON = DOM.Cache.get(pPath); - - if (lJSON){ - /* переводим из текста в JSON */ - lJSON = Util.parseJSON(lJSON); - CloudCmd._createFileTable(lPanel, lJSON); - CloudCmd._changeLinks(lPanel); - - return; - } - } - - DOM.getCurrentFileContent({ - url : lFSPath, - success : function(pData){ - CloudCmd._createFileTable(lPanel, pData); - CloudCmd._changeLinks(lPanel); - - /* переводим таблицу файлов в строку, для * - * сохранения в localStorage */ - var lJSON_s = Util.stringifyJSON(pData); - Util.log(lJSON_s.length); - - /* если размер данных не очень бошьой * - * сохраняем их в кэше */ - if(lJSON_s.length < 50000 ) - DOM.Cache.set(pPath, lJSON_s); - } - }); -}; - -/** - * Функция строит файловую таблицу - * @param pEleme - родительский элемент - * @param pJSON - данные о файлах - */ -CloudCmd._createFileTable = function(pElem, pJSON){ - var lElem = DOM.getById(pElem), - /* getting current element if was refresh */ - lPath = DOM.getByClass('path', lElem), - lWasRefresh_b = lPath[0].textContent === pJSON[0].path, - lCurrent; - - if(lWasRefresh_b) - lCurrent = DOM.getCurrentFile(); - - /* говорим построителю, - * что бы он в нужный момент - * выделил строку с первым файлом - */ - - /* очищаем панель */ - var i = lElem.childNodes.length; - while(i--) - lElem.removeChild(lElem.lastChild); - - /* заполняем панель новыми элементами */ - lElem.innerHTML = CloudFunc.buildFromJSON(pJSON, true); - - /* searching current file */ - if(lWasRefresh_b && lCurrent){ - for(i = 0; i < lElem.childNodes.length; i++) - if(lElem.childNodes[i].textContent === lCurrent.textContent){ - lCurrent = lElem.childNodes[i]; - break; - } - DOM.setCurrentFile(lCurrent); - } -}; - -/** - * Функция генерирует JSON из html-таблицы файлов и - * используеться при первом заходе в корень - */ -CloudCmd._getJSONfromFileTable = function(){ - var lLeft = DOM.getById('left'), - lPath = DOM.getByClass('path')[0].textContent, - - lFileTable = [{ - path:lPath, - size:'dir' - }], - - lLI = lLeft.getElementsByTagName('li'), - - j = 1; /* счётчик реальных файлов */ - - /* счётчик элементов файлов в DOM */ - /* Если путь отличный от корневного - * второй элемент li - это ссылка на верхний - * каталог '..' - */ - - /* пропускам Path и Header*/ - for(var i = 2, n = lLI.length; i < n; i++){ - var lChildren = lLI[i].children, - /* file attributes */ - lAttr = {}; - - /* getting all elements to lAttr object */ - for(var l = 0; l < lChildren.length; l++) - lAttr[lChildren[l].className] = lChildren[l]; - - /* mini-icon */ - var lIsDir = lAttr['mini-icon directory'] ? true : false, - - lName = lAttr.name; - if(lName) - lName = DOM.getByTag('a', lName); - - /* if found link to folder - * cheking is it a full name - * or short - */ - /* if short we got title - * if full - getting textConent - */ - if(lName.length) - lName = lName[0]; - - lName = lName.title || lName.textContent; - - /* если это папка - выводим слово dir вместо размера*/ - var lSize = lIsDir ? 'dir' : lAttr.size.textContent, - lMode = lAttr.mode.textContent; - - /* переводим права доступа в цыфровой вид - * для хранения в localStorage - */ - lMode = CloudFunc.convertPermissionsToNumberic(lMode); - - lFileTable[ j++ ]={ - name: lName, - size: lSize, - mode: lMode - }; - } - return Util.stringifyJSON(lFileTable); -}; - -return CloudCmd; -})(); - - -DOM.addOneTimeListener('load', function(){ - /* базовая инициализация*/ - CloudCommander.init(); - - /* загружаем Google Analytics */ - CloudCommander.GoogleAnalytics(); -}); +/* Функция которая возвратит обьект CloudCommander + * @CloudFunc - обьект содержащий общий функционал + * клиентский и серверный + */ + +var Util, DOM, CloudFunc, $, KeyBinding, CloudCommander = (function(Util, DOM){ +'use strict'; + +var Config, Modules; + +/* Клиентский обьект, содержащий функциональную часть*/ +var CloudCmd = { + /* Конструктор CloudClient, который выполняет + * весь функционал по инициализации + */ + init : null, /* start initialization */ + + KeyBinding : null, /* обьект обработки нажатий клавишь */ + KeysPanel : null, /* panel with key buttons f1-f8 */ + Editor : null, /* function loads and shows editor */ + Storage : null, /* function loads storage */ + Viewer : null, /* function loads and shows viewer */ + Terminal : null, /* function loads and shows terminal*/ + Menu : null, /* function loads and shows menu */ + GoogleAnalytics : null, + + _loadDir : null, /* Функция привязываеться ко всем + * ссылкам и + * загружает содержимое каталогов */ + + /* ОБЬЕКТЫ */ + + /* ПРИВАТНЫЕ ФУНКЦИИ */ + /* функция загружает json-данные о файловой системе */ + _ajaxLoad : null, + + /* Функция генерирует JSON из html-таблицы файлов */ + _getJSONfromFileTable : null, + + /* функция меняет ссыки на ajax-овые */ + _changeLinks : null, + + /* КОНСТАНТЫ*/ + LIBDIR : '/lib/', + LIBDIRCLIENT : '/lib/client/', + JSONDIR : '/json/', + /* height of Cloud Commander + * seting up in init() + */ + HEIGHT : 0, + MIN_ONE_PANEL_WIDTH : 1155, + OLD_BROWSER : false, + + HOST : (function(){ + var lLocation = document.location; + return lLocation.protocol + '//' + lLocation.host; + })() +}; + +CloudCmd.GoogleAnalytics = function(){ + DOM.addOneTimeListener('mousemove', function(){ + var lUrl = CloudCmd.LIBDIRCLIENT + 'google_analytics.js'; + + setTimeout(function(){ + DOM.jsload(lUrl); + }, 5000); + }); +}; + +/** + * Функция привязываеться ко всем ссылкам и + * загружает содержимое каталогов + * + * @param pLink - ссылка + * @param pNeedRefresh - необходимость обязательной загрузки данных с сервера + */ +CloudCmd._loadDir = function(pLink, pNeedRefresh){ + return function(pEvent){ + /* показываем гиф загрузки возле пути папки сверху + * ctrl+r нажата? */ + + var lCurrentLink = DOM.getCurrentLink(), + lHref = lCurrentLink.href, + lParent = lCurrentLink.textContent, + lLink = pLink || Util.removeStr(lHref, CloudCmd.HOST), + lDir = DOM.getCurrentDir(); + + if(pLink || lCurrentLink.target !== '_blank'){ + DOM.Images.showLoad(pNeedRefresh ? {top:true} : null); + + /* загружаем содержимое каталога */ + CloudCmd._ajaxLoad(lLink, { refresh: pNeedRefresh }); + + /* если нажали на ссылку на верхний каталог*/ + if(lParent === '..' && lDir !== '/') + CloudCmd._currentToParent(lDir); + } + + DOM.preventDefault(pEvent); + }; +}; + + +/** + * Function edits file name + * + * @param pParent - parent element + * @param pEvent + */ +CloudCmd._editFileName = function(pParent){ + var lA = DOM.getCurrentLink(pParent); + + if (lA && lA.textContent !== '..'){ + + lA.contentEditable = true; + KeyBinding.unSet(); + + /* setting event handler onclick + * if user clicks somewhere keyBinded + * backs + */ + DOM.addOneTimeListener('click', function(){ + var lA = DOM.getCurrentLink(pParent); + if (lA && lA.textContent !== '..') + lA.contentEditable = false; + + KeyBinding.set(); + }); + } +}; + + +/** функция устанавливает курсор на каталог + * с которого мы пришли, если мы поднялись + * в верх по файловой структуре + * @param pDirName - имя каталога с которого мы пришли + */ +CloudCmd._currentToParent = function(pDirName){ + /* убираем слэш с имени каталога */ + pDirName = Util.removeStr(pDirName, '/'); + + /* опредиляем в какой мы панели: * + * правой или левой */ + var lPanel = DOM.getPanel(), + lRootDir = DOM.getById(pDirName + '(' + lPanel.id + ')'); + + /* if found li element with ID directory name * + * set it to current file */ + if(lRootDir){ + DOM.setCurrentFile(lRootDir); + DOM.scrollIntoViewIfNeeded(lRootDir, true); + } +}; + +/** + * function load modules + * @pParams = {name, path, func, dobefore, arg} + */ +function loadModule(pParams){ + if(!pParams) return; + + var lName = pParams.name, + lPath = pParams.path, + lFunc = pParams.func, + lDoBefore = pParams.dobefore; + + if( Util.isString(pParams) ) + lPath = pParams; + + if(lPath && !lName){ + lName = lPath[0].toUpperCase() + lPath.substring(1); + lName = Util.removeStr(lName, '.js'); + + var lSlash = lName.indexOf('/'); + if(lSlash > 0){ + var lAfterSlash = lName.substr(lSlash); + lName = Util.removeStr(lName, lAfterSlash); + } + } + + if( !Util.isContainStr(lPath, '.js') ) + lPath += '.js'; + + if(!CloudCmd[lName]) + CloudCmd[lName] = function(pArg){ + Util.exec(lDoBefore); + + return DOM.jsload(CloudCmd.LIBDIRCLIENT + lPath, lFunc || + function(){ + Util.exec(CloudCmd[lName].init, pArg); + }); + }; +} +/** Конструктор CloudClient, который + * выполняет весь функционал по + * инициализации + */ +CloudCmd.init = function(){ + var lCallBack = function(){ + Util.loadOnLoad([ + initKeysPanel, + initModules, + baseInit + ]); + }, + lFunc = function(pCallBack){ + this.OLD_BROWSER = true; + var lSrc = CloudCmd.LIBDIRCLIENT + 'ie.js'; + + DOM.jqueryLoad( + DOM.retJSLoad(lSrc, pCallBack) + ); + }; + + //Util.socketLoad(); + + Util.ifExec(document.body.scrollIntoViewIfNeeded, lCallBack, lFunc); +}; + +function initModules(pCallBack){ + loadModule({ + /* привязываем клавиши к функциям */ + path : 'keyBinding.js', + func : function(){ + KeyBinding = CloudCmd.KeyBinding; + KeyBinding.init(); + } + }); + + CloudCmd.getModules(function(pModules){ + pModules = pModules || []; + + DOM.addListener('contextmenu', function(pEvent){ + CloudCmd.Menu.ENABLED || DOM.preventDefault(pEvent); + }, document); + + var lStorage = 'storage', + lShowLoadFunc = Util.retFunc( DOM.Images.showLoad ), + + lDoBefore = { + 'editor/_codemirror' : lShowLoadFunc, + 'viewer' : lShowLoadFunc + }, + + lLoad = function(pName, pPath, pDoBefore){ + loadModule({ + path : pPath, + name : pName, + dobefore : pDoBefore + }); + }; + + for(var i = 0, n = pModules.length; i < n ; i++){ + var lModule = pModules[i]; + + if(Util.isString(lModule)) + lLoad(null, lModule, lDoBefore[lModule]); + } + + var lStorageObj = Util.findObjByNameInArr( pModules, lStorage ), + lMod = Util.getNamesFromObjArray( lStorageObj ); + + for(i = 0, n = lMod.length; i < n; i++){ + var lName = lMod[i], + lPath = lStorage + '/_' + lName.toLowerCase(); + + lLoad(lName, lPath); + } + + + Util.exec(pCallBack); + + }); +} + +function initKeysPanel(pCallBack){ + var lKeysPanel = {}, + + lFuncs =[ + null, + null, /* f1 */ + null, /* f2 */ + CloudCmd.Viewer, /* f3 */ + CloudCmd.Editor, /* f4 */ + null, /* f5 */ + null, /* f6 */ + null, /* f7 */ + DOM.promptRemoveCurrent,/* f8 */ + ]; + + for(var i = 1; i <= 8; i++){ + var lButton = 'f' + i, + lEl = DOM.getById('f' + i); + + DOM.addClickListener(lFuncs[i], lEl); + lKeysPanel[lButton] = lEl; + } + + CloudCmd.KeysPanel = lKeysPanel; + Util.exec(pCallBack); +} + +function baseInit(pCallBack){ + if(window.applicationCache){ + var lFunc = applicationCache.onupdateready; + + applicationCache.onupdateready = function(){ + Util.log('app cacheed'); + location.reload(); + + Util.exec(lFunc); + }; + } + + /* загружаем общие функции для клиента и сервера */ + DOM.jsload(CloudCmd.LIBDIR + 'cloudfunc.js',function(){ + DOM.addListener("popstate", function(pEvent) { + var lPath = pEvent.state; + + if(lPath) + CloudCmd._ajaxLoad(lPath, {nohistory: true}); + + return true; + }); + + /* берём из обьекта window общий с сервером функционал */ + CloudFunc = window.CloudFunc; + + /* меняем ссылки на ajax'овые */ + CloudCmd._changeLinks(CloudFunc.LEFTPANEL); + CloudCmd._changeLinks(CloudFunc.RIGHTPANEL); + + /* устанавливаем переменную доступности кэша */ + DOM.Cache.isAllowed(); + /* Устанавливаем кэш корневого каталога */ + if( !DOM.Cache.get('/') ) + DOM.Cache.set('/', CloudCmd._getJSONfromFileTable()); + }); + + /* устанавливаем размер высоты таблицы файлов + * исходя из размеров разрешения экрана + */ + + /* выделяем строку с первым файлом */ + var lFmHeader = DOM.getByClass('fm-header'); + DOM.setCurrentFile(lFmHeader[0].nextSibling); + + /* показываем элементы, которые будут работать только, если есть js */ + var lFM = DOM.getById('fm'); + lFM.className='localstorage'; + + /* формируем и округляем высоту экрана + * при разрешениии 1024x1280: + * 658 -> 700 + */ + + var lHeight = window.screen.height; + lHeight = lHeight - (lHeight/3).toFixed(); + + lHeight = (lHeight / 100).toFixed() * 100; + + CloudCmd.HEIGHT = lHeight; + + DOM.cssSet({ + id:'cloudcmd', + inner: + '.panel{' + + 'height:' + lHeight +'px;' + + '}' + }); + + Util.exec(pCallBack); + CloudCmd.KeyBinding(); +} + +CloudCmd.getConfig = function(pCallBack){ + Util.ifExec(Config, pCallBack, function(pCallBack){ + DOM.ajax({ + url : CloudCmd.JSONDIR + 'config.json', + success : function(pConfig){ + Config = pConfig; + Util.exec(pCallBack, pConfig); + } + }); + }); +}; + +CloudCmd.getModules = function(pCallBack){ + Util.ifExec(Modules, pCallBack, function(pCallBack){ + DOM.ajax({ + url : CloudCmd.JSONDIR + 'modules.json', + success : Util.retExec(pCallBack) + }); + }); +}; + + +/* функция меняет ссыки на ajax-овые */ +CloudCmd._changeLinks = function(pPanelID){ + /* назначаем кнопку очистить кэш и показываем её */ + var lClearcache = DOM.getById('clear-cache'); + DOM.addClickListener(DOM.Cache.clear, lClearcache); + + /* меняем ссылки на ajax-запросы */ + var lPanel = DOM.getById(pPanelID), + a = lPanel.getElementsByTagName('a'), + + /* номер ссылки иконки обновления страницы */ + lREFRESHICON = 0, + + /* путь в ссылке, который говорит + * что js отключен + */ + lNoJS_s = CloudFunc.NOJS, + + /* right mouse click function varible */ + lOnContextMenu_f = function(pEvent){ + var lReturn_b = true; + + KeyBinding.unSet(); + + /* getting html element + * currentTarget - DOM event + * target - jquery event + */ + var lTarget = pEvent.currentTarget || pEvent.target; + DOM.setCurrentFile(lTarget); + + if(Util.isFunction(CloudCmd.Menu) ){ + CloudCmd.Menu({ + x: pEvent.x, + y: pEvent.y + }); + + /* disabling browsers menu*/ + lReturn_b = false; + DOM.Images.showLoad(); + } + + return lReturn_b; + }, + + /* drag and drop function varible + * download file from browser to descktop + * in Chrome (HTML5) + */ + lOnDragStart_f = function(pEvent){ + var lElement = pEvent.target, + lLink = lElement.href, + lName = lElement.textContent, + /* if it's directory - adding json extension */ + lType = lElement.parentElement.nextSibling; + + if(lType && lType.textContent === ''){ + lLink = Util.removeStr(lLink, lNoJS_s); + lName += '.json'; + } + + pEvent.dataTransfer.setData("DownloadURL", + 'application/octet-stream' + ':' + + lName + ':' + + lLink); + }, + + lSetCurrentFile_f = function(pEvent){ + var pElement = pEvent.target, + lTag = pElement.tagName; + + if(lTag !== 'LI') + do{ + pElement = pElement.parentElement; + lTag = pElement.tagName; + }while(lTag !== 'LI'); + + DOM.setCurrentFile(pElement); + }, + + lUrl = CloudCmd.HOST; + + var lLoadDirOnce = CloudCmd._loadDir(); + for(var i = 0, n = a.length; i < n ; i++){ + /* убираем адрес хоста*/ + var ai = a[i], + link = ai.href.replace(lUrl, ''), + lNEADREFRESH = i === lREFRESHICON, + lLoadDir = CloudCmd._loadDir(link, lNEADREFRESH); + + /* ставим загрузку гифа на клик*/ + if(lNEADREFRESH) + DOM.addClickListener( lLoadDir, ai.parentElement ); + + /* устанавливаем обработчики на строку + * на двойное нажатие на левую кнопку мышки */ + else{ + var lLi = ai.parentElement.parentElement; + + /* if we in path changing onclick events */ + if (lLi.className === 'path') + DOM.addClickListener( lLoadDir, ai ); + else { + DOM.addClickListener( DOM.preventDefault, lLi); + DOM.addListener('mousedown', lSetCurrentFile_f, lLi); + DOM.addListener('ondragstart', lOnDragStart_f, ai); + /* if right button clicked menu will + * loads and shows + */ + DOM.addListener('contextmenu', lOnContextMenu_f, lLi); + + /* если ссылка на папку, а не файл */ + if(ai.target !== '_blank'){ + DOM.addListener('dblclick', lLoadDirOnce, lLi); + DOM.addListener('touchend', lLoadDirOnce, lLi); + } + + lLi.id = (ai.title ? ai.title : ai.textContent) + + '(' + pPanelID + ')'; + } + } + } +}; + +/** + * Функция загружает json-данные о Файловой Системе + * через ajax-запрос. + * @param path - каталог для чтения + * @param pOptions + * { refresh, nohistory } - необходимость обновить данные о каталоге + */ +CloudCmd._ajaxLoad = function(pPath, pOptions){ + if(!pOptions) + pOptions = {}; + + /* Отображаем красивые пути */ + var lFSPath = decodeURI(pPath); + + lFSPath = Util.removeStr( lFSPath, CloudFunc.NOJS ); + var lCleanPath = Util.removeStr(lFSPath, CloudFunc.FS); + Util.log ('reading dir: "' + lCleanPath + '";'); + + if(!pOptions.nohistory) + DOM.setHistory(pPath, null, pPath); + + DOM.setTitle( CloudFunc.getTitle(pPath) ); + + /* если доступен localStorage и + * в нём есть нужная нам директория - + * читаем данные с него и + * выходим + * если стоит поле обязательной перезагрузки - + * перезагружаемся + */ + + /* опредиляем в какой мы панели: + * правой или левой + */ + var lPanel = DOM.getPanel().id; + + if(!pOptions.refresh && lPanel){ + var lJSON = DOM.Cache.get(pPath); + + if (lJSON){ + /* переводим из текста в JSON */ + lJSON = Util.parseJSON(lJSON); + CloudCmd._createFileTable(lPanel, lJSON); + CloudCmd._changeLinks(lPanel); + + return; + } + } + + DOM.getCurrentFileContent({ + url : lFSPath, + success : function(pData){ + CloudCmd._createFileTable(lPanel, pData); + CloudCmd._changeLinks(lPanel); + + /* переводим таблицу файлов в строку, для * + * сохранения в localStorage */ + var lJSON_s = Util.stringifyJSON(pData); + Util.log(lJSON_s.length); + + /* если размер данных не очень бошьой * + * сохраняем их в кэше */ + if(lJSON_s.length < 50000 ) + DOM.Cache.set(pPath, lJSON_s); + } + }); +}; + +/** + * Функция строит файловую таблицу + * @param pEleme - родительский элемент + * @param pJSON - данные о файлах + */ +CloudCmd._createFileTable = function(pElem, pJSON){ + var lElem = DOM.getById(pElem), + /* getting current element if was refresh */ + lPath = DOM.getByClass('path', lElem), + lWasRefresh_b = lPath[0].textContent === pJSON[0].path, + lCurrent; + + if(lWasRefresh_b) + lCurrent = DOM.getCurrentFile(); + + /* говорим построителю, + * что бы он в нужный момент + * выделил строку с первым файлом + */ + + /* очищаем панель */ + var i = lElem.childNodes.length; + while(i--) + lElem.removeChild(lElem.lastChild); + + /* заполняем панель новыми элементами */ + lElem.innerHTML = CloudFunc.buildFromJSON(pJSON, true); + + /* searching current file */ + if(lWasRefresh_b && lCurrent){ + for(i = 0; i < lElem.childNodes.length; i++) + if(lElem.childNodes[i].textContent === lCurrent.textContent){ + lCurrent = lElem.childNodes[i]; + break; + } + DOM.setCurrentFile(lCurrent); + } +}; + +/** + * Функция генерирует JSON из html-таблицы файлов и + * используеться при первом заходе в корень + */ +CloudCmd._getJSONfromFileTable = function(){ + var lLeft = DOM.getById('left'), + lPath = DOM.getByClass('path')[0].textContent, + + lFileTable = [{ + path:lPath, + size:'dir' + }], + + lLI = lLeft.getElementsByTagName('li'), + + j = 1; /* счётчик реальных файлов */ + + /* счётчик элементов файлов в DOM */ + /* Если путь отличный от корневного + * второй элемент li - это ссылка на верхний + * каталог '..' + */ + + /* пропускам Path и Header*/ + for(var i = 2, n = lLI.length; i < n; i++){ + var lChildren = lLI[i].children, + /* file attributes */ + lAttr = {}; + + /* getting all elements to lAttr object */ + for(var l = 0; l < lChildren.length; l++) + lAttr[lChildren[l].className] = lChildren[l]; + + /* mini-icon */ + var lIsDir = lAttr['mini-icon directory'] ? true : false, + + lName = lAttr.name; + if(lName) + lName = DOM.getByTag('a', lName); + + /* if found link to folder + * cheking is it a full name + * or short + */ + /* if short we got title + * if full - getting textConent + */ + if(lName.length) + lName = lName[0]; + + lName = lName.title || lName.textContent; + + /* если это папка - выводим слово dir вместо размера*/ + var lSize = lIsDir ? 'dir' : lAttr.size.textContent, + lMode = lAttr.mode.textContent; + + /* переводим права доступа в цыфровой вид + * для хранения в localStorage + */ + lMode = CloudFunc.convertPermissionsToNumberic(lMode); + + lFileTable[ j++ ]={ + name: lName, + size: lSize, + mode: lMode + }; + } + return Util.stringifyJSON(lFileTable); +}; + +return CloudCmd; +})(Util, DOM); + + +DOM.addOneTimeListener('load', function(){ + /* базовая инициализация*/ + CloudCommander.init(); + + /* загружаем Google Analytics */ + CloudCommander.GoogleAnalytics(); +}); diff --git a/lib/server/main.js b/lib/server/main.js index 4d0b1242..f385596d 100644 --- a/lib/server/main.js +++ b/lib/server/main.js @@ -1,229 +1,224 @@ -(function(){ - 'strict mode'; - - /* Global var accessible from any loaded module */ - global.cloudcmd = {}; - - var DIR, - LIBDIR, - SRVDIR, - JSONDIR, - Util, - - SLASH, - ISWIN32, - ext, - path, - fs, - zlib, - - OK = 200, - FILE_NOT_FOUND = 404; - - /* Native Modules*/ - exports.crypto = require('crypto'), - exports.child_process = require('child_process'), - exports.fs = fs = require('fs'), - exports.http = require('http'), - exports.https = require('https'), - exports.path = path = require('path'), - exports.url = require('url'), - exports.querystring = require('querystring'), - - /* Constants */ - /* current dir + 2 levels up */ - exports.WIN32 = ISWIN32 = isWin32(); - exports.SLASH = SLASH = ISWIN32 ? '\\' : '/', - - exports.SRVDIR = SRVDIR = __dirname + SLASH, - exports.LIBDIR = LIBDIR = path.normalize(SRVDIR + '../'), - exports.DIR = DIR = path.normalize(LIBDIR + '../'), - exports.HTMLDIR = DIR + 'html/', - exports.JSONDIR = JSONDIR = DIR + 'json/', - - /* Functions */ - exports.require = mrequire, - exports.librequire = librequire, - exports.srvrequire = srvrequire, - exports.rootrequire = rootrequire, - exports.generateHeaders = generateHeaders, - exports.sendFile = sendFile, - - /* compitability with old versions of node */ - exports.fs.exists = exports.fs.exists || exports.path.exists; - - /* Needed Modules */ - exports.util = Util = require(LIBDIR + 'util'), - - exports.zlib = zlib = mrequire('zlib'), - - /* Main Information */ - exports.config = jsonrequire('config'); - exports.modules = jsonrequire('modules'); - exports.ext = ext = jsonrequire('ext'); - exports.mainpackage = rootrequire('package'); - - - /* - * Any of loaded below modules could work with global var so - * it should be initialized first. Becouse of almost any of - * moudles do not depends on each other all needed information - * for all modules is initialized hear. - */ - global.cloudcmd.main = exports; - - exports.VOLUMES = getVolumes(), - - /* Additional Modules */ - exports.socket = srvrequire('socket'), - exports.auth = srvrequire('auth').auth, - exports.appcache = srvrequire('appcache'), - exports.cache = srvrequire('cache').Cache, - exports.cloudfunc = librequire('cloudfunc'), - exports.rest = srvrequire('rest').api, - exports.update = srvrequire('update'), - exports.ischanged = srvrequire('ischanged'); - exports.commander = srvrequire('commander'); - exports.minify = srvrequire('minify').Minify; - /* - * second initializing after all modules load, so global var is - * totally filled of all information that should know all modules - */ - global.cloudcmd.main = exports; - /** - * function do safe require of needed module - * @param {Strin} pSrc - */ - function mrequire(pSrc){ - var lModule, - lError = Util.tryCatchLog(function(){ - lModule = require(pSrc); - }); - - if(lError) - console.log(lError); - - return lModule; - } - - function rootrequire(pSrc){ return mrequire(DIR + pSrc); } - - function librequire(pSrc){ return mrequire(LIBDIR + pSrc); } - - function srvrequire(pSrc){ return mrequire(SRVDIR + pSrc); } - - function jsonrequire(pSrc){ return mrequire(JSONDIR + pSrc);} - - /** - * function check is current platform is win32 - */ - function isWin32(){ return process.platform === 'win32'; } - - /** - * get volumes if win32 or get nothing if nix - */ - function getVolumes(){ - var lRet = ISWIN32 ? [] : '/'; - - if(ISWIN32) - srvrequire('win').getVolumes(function(pVolumes){ - console.log(pVolumes); - exports.VOLUMES = pVolumes; - }); - - return lRet; - } - - /** - * Функция создаёт заголовки файлов - * в зависимости от расширения файла - * перед отправкой их клиенту - * @param pName - имя файла - * @param pGzip - данные сжаты gzip'ом - */ - function generateHeaders(pName, pGzip, pQuery){ - var lRet, - lType = '', - lContentEncoding = '', - lCacheControl = 0, - - lExt = Util.getExtension(pName); - - if( Util.strCmp(lExt, '.appcache') ) - lCacheControl = 1; - - lType = ext[lExt] || 'text/plain'; - - if( !Util.isContainStr(lType, 'img') ) - lContentEncoding = '; charset=UTF-8'; - - if(Util.strCmp(pQuery, 'download') ) - lType = 'application/octet-stream'; - - - if(!lCacheControl) - lCacheControl = 31337 * 21; - - lRet = { - /* if type of file any, but img - - * then we shoud specify charset - */ - 'Content-Type': lType + lContentEncoding, - 'cache-control': 'max-age=' + lCacheControl, - 'last-modified': new Date().toString(), - /* https://developers.google.com/speed/docs/best-practices - /caching?hl=ru#LeverageProxyCaching */ - 'Vary': 'Accept-Encoding' - }; - - if(pGzip) - lRet['content-encoding'] = 'gzip'; - - return lRet; - } - - /** - * send file to client thru pipe - * and gzip it if client support - * - * @param pName - имя файла - * @param pGzip - данные сжаты gzip'ом - */ - function sendFile(pParams){ - var lRet, - lName, lReq, lRes; - - if(pParams){ - lName = pParams.name, - lReq = pParams.request, - lRes = pParams.response; - } - - if(lName && lRes && lReq){ - var lEnc = lReq.headers['accept-encoding'] || '', - lGzip = lEnc.match(/\bgzip\b/), - - lReadStream = fs.createReadStream(lName, { - 'bufferSize': 4 * 1024 - }); - - lReadStream.on('error', function(pError){ - lRes.writeHead(FILE_NOT_FOUND, 'OK'); - lRes.end(String(pError)); - }); - - lRes.writeHead(OK, generateHeaders(lName, lGzip) ); - - if (lGzip) - lReadStream = lReadStream.pipe( zlib.createGzip() ); - - lReadStream.pipe(lRes); - - lRet = true; - } - - return lRet; - } - - -})(); +(function(){ + 'strict mode'; + + /* Global var accessible from any loaded module */ + global.cloudcmd = {}; + + var DIR, LIBDIR, SRVDIR, JSONDIR, + Util, + + SLASH, + ISWIN32, + ext, + path, fs, zlib, + + OK = 200, + FILE_NOT_FOUND = 404; + + /* Native Modules*/ + exports.crypto = require('crypto'), + exports.child_process = require('child_process'), + exports.fs = fs = require('fs'), + exports.http = require('http'), + exports.https = require('https'), + exports.path = path = require('path'), + exports.url = require('url'), + exports.querystring = require('querystring'), + + /* Constants */ + /* current dir + 2 levels up */ + exports.WIN32 = ISWIN32 = isWin32(); + exports.SLASH = SLASH = ISWIN32 ? '\\' : '/', + + exports.SRVDIR = SRVDIR = __dirname + SLASH, + exports.LIBDIR = LIBDIR = path.normalize(SRVDIR + '../'), + exports.DIR = DIR = path.normalize(LIBDIR + '../'), + exports.HTMLDIR = DIR + 'html' + SLASH, + exports.JSONDIR = JSONDIR = DIR + 'json' + SLASH, + + /* Functions */ + exports.require = mrequire, + exports.librequire = librequire, + exports.srvrequire = srvrequire, + exports.rootrequire = rootrequire, + exports.generateHeaders = generateHeaders, + exports.sendFile = sendFile, + + /* compitability with old versions of node */ + exports.fs.exists = exports.fs.exists || exports.path.exists; + + /* Needed Modules */ + exports.util = Util = require(LIBDIR + 'util'), + + exports.zlib = zlib = mrequire('zlib'), + + /* Main Information */ + exports.config = jsonrequire('config'); + exports.modules = jsonrequire('modules'); + exports.ext = ext = jsonrequire('ext'); + exports.mainpackage = rootrequire('package'); + + + /* + * Any of loaded below modules could work with global var so + * it should be initialized first. Becouse of almost any of + * moudles do not depends on each other all needed information + * for all modules is initialized hear. + */ + global.cloudcmd.main = exports; + + exports.VOLUMES = getVolumes(), + + /* Additional Modules */ + exports.socket = srvrequire('socket'), + exports.auth = srvrequire('auth').auth, + exports.appcache = srvrequire('appcache'), + exports.cache = srvrequire('cache').Cache, + exports.cloudfunc = librequire('cloudfunc'), + exports.rest = srvrequire('rest').api, + exports.update = srvrequire('update'), + exports.ischanged = srvrequire('ischanged'); + exports.commander = srvrequire('commander'); + exports.minify = srvrequire('minify').Minify; + /* + * second initializing after all modules load, so global var is + * totally filled of all information that should know all modules + */ + global.cloudcmd.main = exports; + /** + * function do safe require of needed module + * @param {Strin} pSrc + */ + function mrequire(pSrc){ + var lModule, + lError = Util.tryCatchLog(function(){ + lModule = require(pSrc); + }); + + if(lError) + console.log(lError); + + return lModule; + } + + function rootrequire(pSrc){ return mrequire(DIR + pSrc); } + + function librequire(pSrc){ return mrequire(LIBDIR + pSrc); } + + function srvrequire(pSrc){ return mrequire(SRVDIR + pSrc); } + + function jsonrequire(pSrc){ return mrequire(JSONDIR + pSrc);} + + /** + * function check is current platform is win32 + */ + function isWin32(){ return process.platform === 'win32'; } + + /** + * get volumes if win32 or get nothing if nix + */ + function getVolumes(){ + var lRet = ISWIN32 ? [] : '/'; + + if(ISWIN32) + srvrequire('win').getVolumes(function(pVolumes){ + console.log(pVolumes); + exports.VOLUMES = pVolumes; + }); + + return lRet; + } + + /** + * Функция создаёт заголовки файлов + * в зависимости от расширения файла + * перед отправкой их клиенту + * @param pName - имя файла + * @param pGzip - данные сжаты gzip'ом + */ + function generateHeaders(pName, pGzip, pQuery){ + var lRet, + lType = '', + lContentEncoding = '', + lCacheControl = 0, + + lExt = Util.getExtension(pName); + + if( Util.strCmp(lExt, '.appcache') ) + lCacheControl = 1; + + lType = ext[lExt] || 'text/plain'; + + if( !Util.isContainStr(lType, 'img') ) + lContentEncoding = '; charset=UTF-8'; + + if(Util.strCmp(pQuery, 'download') ) + lType = 'application/octet-stream'; + + + if(!lCacheControl) + lCacheControl = 31337 * 21; + + lRet = { + /* if type of file any, but img - + * then we shoud specify charset + */ + 'Content-Type': lType + lContentEncoding, + 'cache-control': 'max-age=' + lCacheControl, + 'last-modified': new Date().toString(), + /* https://developers.google.com/speed/docs/best-practices + /caching?hl=ru#LeverageProxyCaching */ + 'Vary': 'Accept-Encoding' + }; + + if(pGzip) + lRet['content-encoding'] = 'gzip'; + + return lRet; + } + + /** + * send file to client thru pipe + * and gzip it if client support + * + * @param pName - имя файла + * @param pGzip - данные сжаты gzip'ом + */ + function sendFile(pParams){ + var lRet, + lName, lReq, lRes; + + if(pParams){ + lName = pParams.name, + lReq = pParams.request, + lRes = pParams.response; + } + + if(lName && lRes && lReq){ + var lEnc = lReq.headers['accept-encoding'] || '', + lGzip = lEnc.match(/\bgzip\b/), + + lReadStream = fs.createReadStream(lName, { + 'bufferSize': 4 * 1024 + }); + + lReadStream.on('error', function(pError){ + lRes.writeHead(FILE_NOT_FOUND, 'OK'); + lRes.end(String(pError)); + }); + + lRes.writeHead(OK, generateHeaders(lName, lGzip) ); + + if (lGzip) + lReadStream = lReadStream.pipe( zlib.createGzip() ); + + lReadStream.pipe(lRes); + + lRet = true; + } + + return lRet; + } + + +})(); From d8394d969943aaddfe59b1bac12de554cca90084 Mon Sep 17 00:00:00 2001 From: coderaiser Date: Wed, 6 Feb 2013 10:31:50 -0500 Subject: [PATCH 219/347] minor changes --- cloudcmd.js | 22 +- json/config.json | 32 +- lib/util.js | 1556 +++++++++++++++++++++++----------------------- 3 files changed, 816 insertions(+), 794 deletions(-) diff --git a/cloudcmd.js b/cloudcmd.js index df0fdd1b..888b798f 100644 --- a/cloudcmd.js +++ b/cloudcmd.js @@ -22,7 +22,9 @@ REQUEST = 'request', RESPONSE = 'response', - INDEX = HTMLDIR + 'index.html'; + INDEX = HTMLDIR + 'index.html', + FS = CloudFunc.FS, + NO_JS = CloudFunc.NO_JS; /* reinit main dir os if we on * Win32 should be backslashes */ @@ -182,22 +184,14 @@ var lRet, lName = pParams.name; - if( Util.strCmp(lName, '/auth') ){ + if( Util.strCmp(lName, ['/auth', '/auth/github']) ){ Util.log('* Routing' + - '-> auth'); - + '-> ' + lName); pParams.name = main.HTMLDIR + lName + '.html'; lRet = main.sendFile(pParams); - }else if( Util.strCmp(lName, '/auth/github') ){ - Util.log('* Routing' + - '-> github'); - - pParams.name = main.HTMLDIR + lName + '.html'; - lRet = main.sendFile(pParams); - }else if( Util.isContainStr(lName, CloudFunc.FS) || - Util.isContainStr(lName, CloudFunc.NO_JS ) || - Util.strCmp(lName, '/') || - Util.strCmp(lName, 'json') ){ + } + else if( Util.isContainStr(lName, [FS, NO_JS]) || + Util.strCmp( lName, ['/', 'json']) ){ lRet = main.commander.sendContent({ request : pParams[REQUEST], diff --git a/json/config.json b/json/config.json index 69ce0eb2..a241eb45 100644 --- a/json/config.json +++ b/json/config.json @@ -1,17 +1,17 @@ -{ - "api_url" : "/api/v1", - "appcache" : false, - "minification" : { - "js" : true, - "css" : true, - "html" : true, - "img" : true - }, - "logs" : false, - "show_keys_panel" : true, - "server" : true, - "socket" : true, - "port" : 80, - "ip" : null, - "rest" : true +{ + "api_url" : "/api/v1", + "appcache" : false, + "minification" : { + "js" : false, + "css" : true, + "html" : false, + "img" : true + }, + "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/util.js b/lib/util.js index 0cf49ef7..87d5df34 100644 --- a/lib/util.js +++ b/lib/util.js @@ -1,765 +1,793 @@ -/* - * Licensed under MIT License http://www.opensource.org/licenses/mit-license - * Module contain additional system functional - */ -var Util, exports; -Util = exports || {}; - -(function(Util){ - 'use strict'; - - var Scope = exports ? global : window; - - /** setting function context - * @param {function} pFunction - * @param {object} pContext - */ - Util.bind = function(pFunction, pContext){ - var lRet = false; - - if( Util.isFunction(pFunction) ) - lRet = pFunction.bind(pContext); - - return lRet; - }; - - Util.breakpoint = function(){ - var lRet = Util.tryCatch(function(){ - debugger; - }); - - Util.log(lRet); - - return lRet; - }; - - /** - * callback for functions(pError, pData) - * thet moves on our parameters. - * - * @param pFunc - * @param pParams - */ - Util.call = function(pFunc, pParams){ - var lFunc = function(pError, pData){ - Util.exec(pFunc, { - error : pError, - data : pData, - params : pParams - }); - }; - - return lFunc; - }; - - /** - * Функция ищет в имени файла расширение - * и если находит возвращает true - * @param pName - получает имя файла - * @param pExt - расширение - */ - Util.checkExtension = function(pName, pExt){ - var lRet = false, - lLength = pName.length; /* длина имени*/ - /* если длина имени больше - * длинны расширения - - * имеет смысл продолжать - */ - if (Util.isString(pExt) && pName.length > pExt.length) { - var lExtNum = pName.lastIndexOf(pExt), /* последнее вхождение расширения*/ - lExtSub = lLength - lExtNum; /* длина расширения*/ - - /* если pExt - расширение pName */ - lRet = lExtSub === pExt.length; - - }else if(Util.isObject(pExt) && pExt.length){ - for(var i=0; i < pName.length; i++){ - lRet = Util.checkExtension(pName, pExt[i]); - - if(lRet) - break; - } - } - - return lRet; - }; - - - /** - * Check is Properties exists and they are true if neaded - * - * @param pObj - * @param pPropArr - * @param pTrueArr - */ - Util.checkObj = function(pObj, pPropArr, pTrueArr){ - var lRet = Util.isObject(pObj), - i, n; - if( lRet ){ - lRet = Util.isArray(pPropArr); - if(lRet){ - n = pPropArr.length; - for(i = 0; i < n; i++){ - var lProp = pPropArr[i]; - lRet = pObj.hasOwnProperty( lProp ); - if(!lRet){ - Util.logError(lProp + ' not in Obj!'); - Util.log(pObj); - break; - } - } - } - - if( lRet && Util.isArray(pTrueArr) ) - lRet = Util.checkObjTrue( pObj, pTrueArr ); - } - - return lRet; - }; - - /** - * Check is Properties exists and they are true - * - * @param pObj - * @param pPropArr - * @param pTrueArr - */ - Util.checkObjTrue = function(pObj, pTrueArr){ - var lRet = Util.isObject(pObj), - i, n; - if( lRet ){ - lRet = Util.isArray(pTrueArr); - if(lRet){ - n = pTrueArr.length; - for(i = 0; i < n; i++){ - var lProp = pTrueArr[i]; - lRet = pObj[lProp]; - - if( !lRet) - break; - } - } - } - - return lRet; - }; - - /** for function - * @param pI - * @param pN - * @param pFunc - */ - Util.for = function(pI, pN, pFunc){ - if(Util.isFunction(pFunc)) - for(var i = pI, n = pN; i < n; i++){ - if(pFunc(i)) - break; - } - }; - - /** for function with i = 0 - * @param pN - * @param pFunc - */ - Util.fori = function(pN, pFunc){ - var lRet = Util.for(0, pN, pFunc); - - return lRet; - }; - - /** - * @pJSON - */ - Util.parseJSON = function(pJSON){ - var lRet; - - Util.tryCatchLog(function(){ - lRet = JSON.parse(pJSON); - }); - - return lRet; - }; - - /** - * pObj - */ - Util.stringifyJSON = function(pObj){ - var lRet; - - Util.tryCatchLog(function(){ - lRet = JSON.stringify(pObj, null, 4); - }); - - return lRet; - }; - - /* STRINGS */ - /** - * function check is strings are equal - * @param pStr1 - * @param pStr2 - */ - Util.strCmp = function (pStr1, pStr2){ - return this.isContainStr(pStr1, pStr2) && - pStr1.length === pStr2.length; - }; - - /** - * function returns is pStr1 contains pStr2 - * @param pStr1 - * @param pStr2 - */ - - Util.isContainStr = function(pStr1, pStr2){ - return pStr1 && - pStr2 && - pStr1.indexOf(pStr2) >= 0; - }; - - /** - * function log pArg if it's not empty - * @param pArg - */ - Util.log = function(pArg){ - var lConsole = Scope.console, - lDate = '[' + Util.getDate() + '] '; - - if(lConsole && pArg) - lConsole.log(lDate, pArg); - - return pArg; - }; - - /** - * function log pArg if it's not empty - * @param pArg - */ - Util.logError = function(pArg){ - var lConsole = Scope.console, - lDate = '[' + Util.getDate() + '] '; - - if(lConsole && pArg){ - var lMsg = pArg.message; - if( lMsg ) - lDate += pArg.message + ' '; - - lConsole.error(lDate, pArg); - } - - return pArg; - }; - - /** - * load functions thrue callbacks one-by-one - * @param pFunc_a {Array} - array of functions - * @param pData - not necessarily - */ - Util.loadOnLoad = function(pFunc_a, pData){ - if( Util.isArray(pFunc_a) && pFunc_a.length) { - var lFunc_a = pFunc_a.slice(), - lFunc = lFunc_a.pop(), - lCallBack = function(pData){ - return Util.loadOnLoad(lFunc_a, pData); - }; - - if( !Util.isUndefined(pData) ) - pData = { - data : pData, - callback : lCallBack - }; - - Util.exec(lFunc , pData || lCallBack); - } - }; - - /** - * function remove substring from string - * @param pStr - * @param pSubStr - */ - Util.removeStr = function(pStr, pSubStr){ - var lRet; - if( Util.isString(pStr) ){ - var n = pSubStr.length; - - if( Util.isArray(pSubStr) ) - Util.fori(n, function(i){ - lRet = pStr = pStr.replace(pSubStr[i], ''); - }); - else - lRet = pStr.replace(pSubStr, ''); - } - - return lRet; - }; - - - /** - * function replase pFrom to pTo in pStr - * @pStr - * @pFrom - * @pTo - */ - - Util.replaceStr = function(pStr, pFrom, pTo){ - var lRet; - - if(pStr && pFrom && pTo) - lRet = pStr.replace(new RegExp(pFrom, 'g'), pTo); - - return lRet; - }; - - /** - * function render template with view - * @pTempl - * @pView - */ - Util.render = function(pTempl, pView){ - var lRet = Util.ownRender(pTempl, pView, ['{', '}']); - - return lRet; - }; - - /** - * function render template with view and own symbols - * @pTempl - * @pView - * @pSymbols - */ - Util.ownRender = function(pTempl, pView, pSymbols){ - if(!pSymbols) - pSymbols = ['{', '}']; - - var lRet = pTempl, - lFirstChar, - lSecondChar; - - lFirstChar = pSymbols[0]; - lSecondChar = pSymbols[1] || lFirstChar; - - for(var lVar in pView){ - var lStr = pView[lVar]; - lStr = Util.exec(lStr) || lStr; - - lRet = Util.replaceStr(lRet, lFirstChar + lVar + lSecondChar, lStr); - } - - return lRet; - }; - - - /** - * invoke a couple of functions in paralel - * - * @param {Array} pFuncs - * @param {function} pCallback - * - * Example: - * i >=0, pFuncs[i] = function(param, callback){} - */ - Util.paralelExec = function(pFuncs, pCallback){ - var done = []; - - /* add items to array done*/ - function addFunc(pNum){ - done.push(pNum); - } - - /* - * improve callback of funcs so - * we pop number of function and - * if it's last we call pCallBack - */ - function doneFunc(pParams){ - Util.exec(pParams.callback); - - var lNum = done.pop (pParams.number); - if(!lNum){ - Util.exec(pCallback); - } - } - - for(var i = 0, n = pFuncs.length; i < n; i++){ - addFunc(i); - - var lFunc = pFuncs[i].callback; - - pFuncs[i].callback = Util.retExec(doneFunc, { - number : i, - callback : lFunc - }); - } - }; - - /** - * functions check is pVarible is array - * @param pVarible - */ - Util.isArray = function(pVarible){ - return pVarible instanceof Array; - }; - - /** - * functions check is pVarible is boolean - * @param pVarible - */ - Util.isBoolean = function(pVarible){ - return Util.isType(pVarible, 'boolean'); - }; - - /** - * functions check is pVarible is function - * @param pVarible - */ - Util.isFunction = function(pVarible){ - return Util.isType(pVarible, 'function'); - }; - - /** - * functions check is pVarible is object - * @param pVarible - */ - Util.isObject = function(pVarible){ - return Util.isType(pVarible, 'object'); - }; - - /** - * functions check is pVarible is string - * @param pVarible - */ - Util.isString = function(pVarible){ - return Util.isType(pVarible, 'string'); - }; - - /** - * functions check is pVarible is string - * @param pVarible - */ - Util.isUndefined = function(pVarible){ - return Util.isType(pVarible, 'undefined'); - }; - - /** - * functions check is pVarible is pType - * @param pVarible - * @param pType - */ - Util.isType = function(pVarible, pType){ - return typeof pVarible === pType; - }; - - - /** - * return save exec function - * @param pCallBack - * @param pArg - */ - Util.retExec = function(pCallBack, pArg){ - return function(pArgument){ - if( !Util.isUndefined(pArg) ) - pArgument = pArg; - Util.exec(pCallBack, pArgument); - }; - }; - - /** - * return function wich exec function in params - * @param pCallBack - * @param pArg - */ - Util.retFunc = function(pCallBack, pArg){ - return function(){ - return Util.exec(pCallBack, pArg); - }; - }; - /** - * function return false - */ - Util.retFalse = function(){ - var lRet = false; - - return lRet; - }; - - /** - * return load functions thrue callbacks one-by-one - * @param pFunc_a {Array} - array of functions - * @param pData - not necessarily - */ - Util.retLoadOnLoad = function(pFunc_a, pData){ - return function(){ - Util.loadOnLoad(pFunc_a, pData); - }; - }; - - /** - * set value to property of object, if object exist - * @param pArgs {object, property, value} - */ - Util.setValue = function(pArgs){ - var lRet = false; - - if( Util.isObject(pArgs) ){ - var lObj = pArgs.object, - lProp = pArgs.property, - lVal = pArgs.lVal; - - if(lObj){ - lObj[lProp] = lVal; - lRet = true; - } - } - - return lRet; - }; - - /** - * set timout before callback would be called - * @param pArgs {func, callback, time} - */ - Util.setTimeout = function(pArgs){ - var lDone, - lFunc = pArgs.func, - lTime = pArgs.time || 1000, - lCallBack = function(pArgument){ - if(!lDone){ - lDone = Util.exec(pArgs.callback, pArgument); - } - }; - - var lTimeoutFunc = function(){ - setTimeout(function(){ - Util.exec(lFunc, lCallBack); - if(!lDone) - lTimeoutFunc(); - }, lTime); - }; - - lTimeoutFunc(); - }; - - - /** - * function execute param function in - * try...catch block - * - * @param pCallBack - */ - Util.tryCatch = function(pCallBack){ - var lRet; - try{ - lRet = pCallBack(); - } - catch(pError){ - lRet = pError; - } - - return lRet; - }; - - /** - * function execute param function in - * try...catch block and log result - * - * @param pTryFunc - */ - Util.tryCatchDebug = function(pTryFunc){ - var lRet = Util.tryCatch(pTryFunc); - - if(lRet) - Util.debug(); - - return lRet; - }; - - /** - * function execute param function in - * try...catch block and log result - * - * @param pTryFunc - */ - Util.tryCatchLog = function(pTryFunc){ - var lRet; - - lRet = Util.tryCatch(pTryFunc); - - return Util.logError(lRet); - }; - - /** - * function execute param function in - * try...catch block and log result - * - * @param pCallBack - */ - Util.tryCatchCall = function(pTryFunc, pCallBack){ - var lRet; - - lRet = Util.tryCatch(pTryFunc); - - if(lRet) - Util.exec(pCallBack, lRet); - - return lRet; - }; - - /** - * function do save exec of function - * @param pCallBack - * @param pArg - */ - Util.exec = function(pCallBack, pArg){ - var lRet; - - if(pCallBack){ - if( Util.isFunction(pCallBack) ) - lRet = pCallBack(pArg); - else { - var lCallBack = pCallBack.callback || pCallBack.success; - lRet = Util.exec(lCallBack, pArg); - } - } - - return lRet; - }; - - /** - * exec function if it exist in object - * @pArg - */ - Util.execIfExist = function(pObj, pName, pArg){ - var lRet; - if(pObj) - lRet = Util.exec(pObj[pName], pArg); - - return lRet; - }; - - /** - * function do conditional save exec of function - * @param pCondition - * @param pCallBack - * @param pFunc - */ - Util.ifExec = function(pCondition, pCallBack, pFunc){ - var lRet; - - if(pCondition) - Util.exec(pCallBack, pCondition); - else - Util.exec(pFunc, pCallBack); - - return lRet; - }; - - /** - * function gets file extension - * @param pFileName - * @return Ext - */ - Util.getExtension = function(pFileName){ - var lRet, lDot; - - if( Util.isString(pFileName) ){ - lDot = pFileName.lastIndexOf('.'); - lRet = pFileName.substr(lDot); - } - - return lRet; - }; - - /** - * get values from Object Array name properties - * or - * @pObj - */ - Util.getNamesFromObjArray = function(pArr){ - var lRet = []; - - if(pArr && !Util.isArray(pArr)) - pArr = pArr.data; - - if(pArr) - Util.fori(pArr.length, function(i){ - lRet[i] = pArr[i].name || pArr[i]; - }); - - return lRet; - }; - - /** - * find object by name in arrray - * or - * @pObj - */ - Util.findObjByNameInArr = function(pArr, pObjName){ - var lRet; - - if(pArr){ - for(var i = 0, n = pArr.length; i < n; i++ ) - if(pArr[i].name === pObjName) break; - - lRet = pArr[i]; - } - - return lRet; - }; - - /** - * Gets current time in format hh:mm:ss - */ - Util.getTime = function(){ - var lRet, - date = new Date(), - hours = date.getHours(), - minutes = date.getMinutes(), - seconds = date.getSeconds(); - - minutes = minutes < 10 ? '0' + minutes : minutes; - seconds = seconds < 10 ? '0' + seconds : seconds; - - lRet = hours + ":" + minutes + ":" + seconds; - - return lRet; - }; - - - /** - * start timer - * @pArg - */ - Util.time = function(pArg){ - var lRet, - lConsole = Scope.console; - - lRet = Util.execIfExist(lConsole, 'time', pArg); - - return lRet; - }; - /** - * stop timer - * @pArg - */ - Util.timeEnd = function(pArg){ - var lRet, - lConsole = Scope.console; - - lRet = Util.execIfExist(lConsole, 'timeEnd', pArg); - - return this; - }; - /** - * 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; - }; - +/* + * Licensed under MIT License http://www.opensource.org/licenses/mit-license + * Module contain additional system functional + */ +var Util, exports; +Util = exports || {}; + +(function(Util){ + 'use strict'; + + var Scope = exports ? global : window; + + /** setting function context + * @param {function} pFunction + * @param {object} pContext + */ + Util.bind = function(pFunction, pContext){ + var lRet = false; + + if( Util.isFunction(pFunction) ) + lRet = pFunction.bind(pContext); + + return lRet; + }; + + Util.breakpoint = function(){ + var lRet = Util.tryCatch(function(){ + debugger; + }); + + Util.log(lRet); + + return lRet; + }; + + /** + * callback for functions(pError, pData) + * thet moves on our parameters. + * + * @param pFunc + * @param pParams + */ + Util.call = function(pFunc, pParams){ + var lFunc = function(pError, pData){ + Util.exec(pFunc, { + error : pError, + data : pData, + params : pParams + }); + }; + + return lFunc; + }; + + /** + * Функция ищет в имени файла расширение + * и если находит возвращает true + * @param pName - получает имя файла + * @param pExt - расширение + */ + Util.checkExtension = function(pName, pExt){ + var lRet = false, + lLength = pName.length; /* длина имени*/ + /* если длина имени больше + * длинны расширения - + * имеет смысл продолжать + */ + if (Util.isString(pExt) && pName.length > pExt.length) { + var lExtNum = pName.lastIndexOf(pExt), /* последнее вхождение расширения*/ + lExtSub = lLength - lExtNum; /* длина расширения*/ + + /* если pExt - расширение pName */ + lRet = lExtSub === pExt.length; + + }else if(Util.isObject(pExt) && pExt.length){ + for(var i=0; i < pName.length; i++){ + lRet = Util.checkExtension(pName, pExt[i]); + + if(lRet) + break; + } + } + + return lRet; + }; + + + /** + * Check is Properties exists and they are true if neaded + * + * @param pObj + * @param pPropArr + * @param pTrueArr + */ + Util.checkObj = function(pObj, pPropArr, pTrueArr){ + var lRet = Util.isObject(pObj), + i, n; + if( lRet ){ + lRet = Util.isArray(pPropArr); + if(lRet){ + n = pPropArr.length; + for(i = 0; i < n; i++){ + var lProp = pPropArr[i]; + lRet = pObj.hasOwnProperty( lProp ); + if(!lRet){ + Util.logError(lProp + ' not in Obj!'); + Util.log(pObj); + break; + } + } + } + + if( lRet && Util.isArray(pTrueArr) ) + lRet = Util.checkObjTrue( pObj, pTrueArr ); + } + + return lRet; + }; + + /** + * Check is Properties exists and they are true + * + * @param pObj + * @param pPropArr + * @param pTrueArr + */ + Util.checkObjTrue = function(pObj, pTrueArr){ + var lRet = Util.isObject(pObj), + i, n; + if( lRet ){ + lRet = Util.isArray(pTrueArr); + if(lRet){ + n = pTrueArr.length; + for(i = 0; i < n; i++){ + var lProp = pTrueArr[i]; + lRet = pObj[lProp]; + + if( !lRet) + break; + } + } + } + + return lRet; + }; + + /** for function + * @param pI + * @param pN + * @param pFunc + */ + Util.for = function(pI, pN, pFunc){ + if(Util.isFunction(pFunc)) + for(var i = pI, n = pN; i < n; i++){ + if(pFunc(i)) + break; + } + }; + + /** for function with i = 0 + * @param pN + * @param pFunc + */ + Util.fori = function(pN, pFunc){ + var lRet = Util.for(0, pN, pFunc); + + return lRet; + }; + + /** + * @pJSON + */ + Util.parseJSON = function(pJSON){ + var lRet; + + Util.tryCatchLog(function(){ + lRet = JSON.parse(pJSON); + }); + + return lRet; + }; + + /** + * pObj + */ + Util.stringifyJSON = function(pObj){ + var lRet; + + Util.tryCatchLog(function(){ + lRet = JSON.stringify(pObj, null, 4); + }); + + return lRet; + }; + + /* STRINGS */ + /** + * function check is strings are equal + * @param pStr1 + * @param pStr2 + */ + Util.strCmp = function (pStr1, pStr2){ + var lRet = Util.isString(pStr1); + + if(lRet){ + if( Util.isArray(pStr2) ) + for(var i = 0, n = pStr2.length; i < n; i++){ + lRet = Util.strCmp( pStr1, pStr2[i] ); + + if(lRet) + break; + } + else if( Util.isString(pStr2) ) + lRet = Util.isContainStr(pStr1, pStr2) && + pStr1.length === pStr2.length; + } + + return lRet; + + }; + + /** + * function returns is pStr1 contains pStr2 + * @param pStr1 + * @param pStr2 + */ + + Util.isContainStr = function(pStr1, pStr2){ + var lRet = Util.isString(pStr1); + + + if( lRet ){ + if( Util.isArray(pStr2) ) + for(var i = 0, n = pStr2.length; i < n; i++){ + lRet = Util.strCmp( pStr1, pStr2[i] ); + + if(lRet) + break; + } + else if( Util.isString(pStr2) ) + lRet = pStr1.indexOf(pStr2) >= 0; + } + + return lRet; + }; + + /** + * function log pArg if it's not empty + * @param pArg + */ + Util.log = function(pArg){ + var lConsole = Scope.console, + lDate = '[' + Util.getDate() + '] '; + + if(lConsole && pArg) + lConsole.log(lDate, pArg); + + return pArg; + }; + + /** + * function log pArg if it's not empty + * @param pArg + */ + Util.logError = function(pArg){ + var lConsole = Scope.console, + lDate = '[' + Util.getDate() + '] '; + + if(lConsole && pArg){ + var lMsg = pArg.message; + if( lMsg ) + lDate += pArg.message + ' '; + + lConsole.error(lDate, pArg); + } + + return pArg; + }; + + /** + * load functions thrue callbacks one-by-one + * @param pFunc_a {Array} - array of functions + * @param pData - not necessarily + */ + Util.loadOnLoad = function(pFunc_a, pData){ + if( Util.isArray(pFunc_a) && pFunc_a.length) { + var lFunc_a = pFunc_a.slice(), + lFunc = lFunc_a.pop(), + lCallBack = function(pData){ + return Util.loadOnLoad(lFunc_a, pData); + }; + + if( !Util.isUndefined(pData) ) + pData = { + data : pData, + callback : lCallBack + }; + + Util.exec(lFunc , pData || lCallBack); + } + }; + + /** + * function remove substring from string + * @param pStr + * @param pSubStr + */ + Util.removeStr = function(pStr, pSubStr){ + var lRet; + if( Util.isString(pStr) ){ + var n = pSubStr.length; + + if( Util.isArray(pSubStr) ) + Util.fori(n, function(i){ + lRet = pStr = pStr.replace(pSubStr[i], ''); + }); + else + lRet = pStr.replace(pSubStr, ''); + } + + return lRet; + }; + + + /** + * function replase pFrom to pTo in pStr + * @pStr + * @pFrom + * @pTo + */ + + Util.replaceStr = function(pStr, pFrom, pTo){ + var lRet; + + if(pStr && pFrom && pTo) + lRet = pStr.replace(new RegExp(pFrom, 'g'), pTo); + + return lRet; + }; + + /** + * function render template with view + * @pTempl + * @pView + */ + Util.render = function(pTempl, pView){ + var lRet = Util.ownRender(pTempl, pView, ['{', '}']); + + return lRet; + }; + + /** + * function render template with view and own symbols + * @pTempl + * @pView + * @pSymbols + */ + Util.ownRender = function(pTempl, pView, pSymbols){ + if(!pSymbols) + pSymbols = ['{', '}']; + + var lRet = pTempl, + lFirstChar, + lSecondChar; + + lFirstChar = pSymbols[0]; + lSecondChar = pSymbols[1] || lFirstChar; + + for(var lVar in pView){ + var lStr = pView[lVar]; + lStr = Util.exec(lStr) || lStr; + + lRet = Util.replaceStr(lRet, lFirstChar + lVar + lSecondChar, lStr); + } + + return lRet; + }; + + + /** + * invoke a couple of functions in paralel + * + * @param {Array} pFuncs + * @param {function} pCallback + * + * Example: + * i >=0, pFuncs[i] = function(param, callback){} + */ + Util.paralelExec = function(pFuncs, pCallback){ + var done = []; + + /* add items to array done*/ + function addFunc(pNum){ + done.push(pNum); + } + + /* + * improve callback of funcs so + * we pop number of function and + * if it's last we call pCallBack + */ + function doneFunc(pParams){ + Util.exec(pParams.callback); + + var lNum = done.pop (pParams.number); + if(!lNum){ + Util.exec(pCallback); + } + } + + for(var i = 0, n = pFuncs.length; i < n; i++){ + addFunc(i); + + var lFunc = pFuncs[i].callback; + + pFuncs[i].callback = Util.retExec(doneFunc, { + number : i, + callback : lFunc + }); + } + }; + + /** + * functions check is pVarible is array + * @param pVarible + */ + Util.isArray = function(pVarible){ + return pVarible instanceof Array; + }; + + /** + * functions check is pVarible is boolean + * @param pVarible + */ + Util.isBoolean = function(pVarible){ + return Util.isType(pVarible, 'boolean'); + }; + + /** + * functions check is pVarible is function + * @param pVarible + */ + Util.isFunction = function(pVarible){ + return Util.isType(pVarible, 'function'); + }; + + /** + * functions check is pVarible is object + * @param pVarible + */ + Util.isObject = function(pVarible){ + return Util.isType(pVarible, 'object'); + }; + + /** + * functions check is pVarible is string + * @param pVarible + */ + Util.isString = function(pVarible){ + return Util.isType(pVarible, 'string'); + }; + + /** + * functions check is pVarible is string + * @param pVarible + */ + Util.isUndefined = function(pVarible){ + return Util.isType(pVarible, 'undefined'); + }; + + /** + * functions check is pVarible is pType + * @param pVarible + * @param pType + */ + Util.isType = function(pVarible, pType){ + return typeof pVarible === pType; + }; + + + /** + * return save exec function + * @param pCallBack + * @param pArg + */ + Util.retExec = function(pCallBack, pArg){ + return function(pArgument){ + if( !Util.isUndefined(pArg) ) + pArgument = pArg; + Util.exec(pCallBack, pArgument); + }; + }; + + /** + * return function wich exec function in params + * @param pCallBack + * @param pArg + */ + Util.retFunc = function(pCallBack, pArg){ + return function(){ + return Util.exec(pCallBack, pArg); + }; + }; + /** + * function return false + */ + Util.retFalse = function(){ + var lRet = false; + + return lRet; + }; + + /** + * return load functions thrue callbacks one-by-one + * @param pFunc_a {Array} - array of functions + * @param pData - not necessarily + */ + Util.retLoadOnLoad = function(pFunc_a, pData){ + return function(){ + Util.loadOnLoad(pFunc_a, pData); + }; + }; + + /** + * set value to property of object, if object exist + * @param pArgs {object, property, value} + */ + Util.setValue = function(pArgs){ + var lRet = false; + + if( Util.isObject(pArgs) ){ + var lObj = pArgs.object, + lProp = pArgs.property, + lVal = pArgs.lVal; + + if(lObj){ + lObj[lProp] = lVal; + lRet = true; + } + } + + return lRet; + }; + + /** + * set timout before callback would be called + * @param pArgs {func, callback, time} + */ + Util.setTimeout = function(pArgs){ + var lDone, + lFunc = pArgs.func, + lTime = pArgs.time || 1000, + lCallBack = function(pArgument){ + if(!lDone){ + lDone = Util.exec(pArgs.callback, pArgument); + } + }; + + var lTimeoutFunc = function(){ + setTimeout(function(){ + Util.exec(lFunc, lCallBack); + if(!lDone) + lTimeoutFunc(); + }, lTime); + }; + + lTimeoutFunc(); + }; + + + /** + * function execute param function in + * try...catch block + * + * @param pCallBack + */ + Util.tryCatch = function(pCallBack){ + var lRet; + try{ + lRet = pCallBack(); + } + catch(pError){ + lRet = pError; + } + + return lRet; + }; + + /** + * function execute param function in + * try...catch block and log result + * + * @param pTryFunc + */ + Util.tryCatchDebug = function(pTryFunc){ + var lRet = Util.tryCatch(pTryFunc); + + if(lRet) + Util.debug(); + + return lRet; + }; + + /** + * function execute param function in + * try...catch block and log result + * + * @param pTryFunc + */ + Util.tryCatchLog = function(pTryFunc){ + var lRet; + + lRet = Util.tryCatch(pTryFunc); + + return Util.logError(lRet); + }; + + /** + * function execute param function in + * try...catch block and log result + * + * @param pCallBack + */ + Util.tryCatchCall = function(pTryFunc, pCallBack){ + var lRet; + + lRet = Util.tryCatch(pTryFunc); + + if(lRet) + Util.exec(pCallBack, lRet); + + return lRet; + }; + + /** + * function do save exec of function + * @param pCallBack + * @param pArg + */ + Util.exec = function(pCallBack, pArg){ + var lRet; + + if(pCallBack){ + if( Util.isFunction(pCallBack) ) + lRet = pCallBack(pArg); + else { + var lCallBack = pCallBack.callback || pCallBack.success; + lRet = Util.exec(lCallBack, pArg); + } + } + + return lRet; + }; + + /** + * exec function if it exist in object + * @pArg + */ + Util.execIfExist = function(pObj, pName, pArg){ + var lRet; + if(pObj) + lRet = Util.exec(pObj[pName], pArg); + + return lRet; + }; + + /** + * function do conditional save exec of function + * @param pCondition + * @param pCallBack + * @param pFunc + */ + Util.ifExec = function(pCondition, pCallBack, pFunc){ + var lRet; + + if(pCondition) + Util.exec(pCallBack, pCondition); + else + Util.exec(pFunc, pCallBack); + + return lRet; + }; + + /** + * function gets file extension + * @param pFileName + * @return Ext + */ + Util.getExtension = function(pFileName){ + var lRet, lDot; + + if( Util.isString(pFileName) ){ + lDot = pFileName.lastIndexOf('.'); + lRet = pFileName.substr(lDot); + } + + return lRet; + }; + + /** + * get values from Object Array name properties + * or + * @pObj + */ + Util.getNamesFromObjArray = function(pArr){ + var lRet = []; + + if(pArr && !Util.isArray(pArr)) + pArr = pArr.data; + + if(pArr) + Util.fori(pArr.length, function(i){ + lRet[i] = pArr[i].name || pArr[i]; + }); + + return lRet; + }; + + /** + * find object by name in arrray + * or + * @pObj + */ + Util.findObjByNameInArr = function(pArr, pObjName){ + var lRet; + + if(pArr){ + for(var i = 0, n = pArr.length; i < n; i++ ) + if(pArr[i].name === pObjName) break; + + lRet = pArr[i]; + } + + return lRet; + }; + + /** + * Gets current time in format hh:mm:ss + */ + Util.getTime = function(){ + var lRet, + date = new Date(), + hours = date.getHours(), + minutes = date.getMinutes(), + seconds = date.getSeconds(); + + minutes = minutes < 10 ? '0' + minutes : minutes; + seconds = seconds < 10 ? '0' + seconds : seconds; + + lRet = hours + ":" + minutes + ":" + seconds; + + return lRet; + }; + + + /** + * start timer + * @pArg + */ + Util.time = function(pArg){ + var lRet, + lConsole = Scope.console; + + lRet = Util.execIfExist(lConsole, 'time', pArg); + + return lRet; + }; + /** + * stop timer + * @pArg + */ + Util.timeEnd = function(pArg){ + var lRet, + lConsole = Scope.console; + + lRet = Util.execIfExist(lConsole, 'timeEnd', pArg); + + return this; + }; + /** + * 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; + }; + })(Util, exports); \ No newline at end of file From ba6de2c905f1311271b99d3e85aacbf859ca9163 Mon Sep 17 00:00:00 2001 From: coderaiser Date: Wed, 6 Feb 2013 10:38:57 -0500 Subject: [PATCH 220/347] minor changes --- lib/util.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/util.js b/lib/util.js index 87d5df34..e84f168e 100644 --- a/lib/util.js +++ b/lib/util.js @@ -232,7 +232,7 @@ Util = exports || {}; if( lRet ){ if( Util.isArray(pStr2) ) for(var i = 0, n = pStr2.length; i < n; i++){ - lRet = Util.strCmp( pStr1, pStr2[i] ); + lRet = Util.isContainStr( pStr1, pStr2[i] ); if(lRet) break; From 86641207e1ae018980ccd563a9f0f60557166777 Mon Sep 17 00:00:00 2001 From: coderaiser Date: Wed, 6 Feb 2013 10:40:54 -0500 Subject: [PATCH 221/347] minor changes --- cloudcmd.js | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/cloudcmd.js b/cloudcmd.js index 888b798f..7277e7ee 100644 --- a/cloudcmd.js +++ b/cloudcmd.js @@ -23,8 +23,7 @@ REQUEST = 'request', RESPONSE = 'response', INDEX = HTMLDIR + 'index.html', - FS = CloudFunc.FS, - NO_JS = CloudFunc.NO_JS; + FS = CloudFunc.FS; /* reinit main dir os if we on * Win32 should be backslashes */ @@ -190,7 +189,7 @@ pParams.name = main.HTMLDIR + lName + '.html'; lRet = main.sendFile(pParams); } - else if( Util.isContainStr(lName, [FS, NO_JS]) || + else if( Util.isContainStr(lName, FS) || Util.strCmp( lName, ['/', 'json']) ){ lRet = main.commander.sendContent({ From 0dd8bb2a3ad2144c895e984472a491da266e08f0 Mon Sep 17 00:00:00 2001 From: coderaiser Date: Wed, 6 Feb 2013 14:04:39 -0500 Subject: [PATCH 222/347] fixed bug with old browsers suport --- ChangeLog | 2 ++ lib/client.js | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/ChangeLog b/ChangeLog index 3fb84a23..b2ed6afc 100644 --- a/ChangeLog +++ b/ChangeLog @@ -124,6 +124,8 @@ time was changed. * Commander functions moved out to commander.js from server.js +* Fixed bug with old browsers suport. + 2012.12.12, Version 0.1.8 diff --git a/lib/client.js b/lib/client.js index ebeaa32c..974b5c69 100644 --- a/lib/client.js +++ b/lib/client.js @@ -204,7 +204,7 @@ CloudCmd.init = function(){ ]); }, lFunc = function(pCallBack){ - this.OLD_BROWSER = true; + CloudCmd.OLD_BROWSER = true; var lSrc = CloudCmd.LIBDIRCLIENT + 'ie.js'; DOM.jqueryLoad( From eff5a6c9360fa969b4b2aa4665e9a915ebe77fa2 Mon Sep 17 00:00:00 2001 From: coderaiser Date: Wed, 6 Feb 2013 14:38:26 -0500 Subject: [PATCH 223/347] fixed bug with scrolling in opera and firefox --- ChangeLog | 2 + lib/client/dom.js | 2594 +++++++++++++++++++------------------- lib/client/ie.js | 543 ++++---- lib/client/keyBinding.js | 614 ++++----- 4 files changed, 1884 insertions(+), 1869 deletions(-) diff --git a/ChangeLog b/ChangeLog index b2ed6afc..7716e29f 100644 --- a/ChangeLog +++ b/ChangeLog @@ -126,6 +126,8 @@ time was changed. * Fixed bug with old browsers suport. +* Fixed bug with scrolling in opera and firefox. + 2012.12.12, Version 0.1.8 diff --git a/lib/client/dom.js b/lib/client/dom.js index 49e4ad64..9d28f9b3 100644 --- a/lib/client/dom.js +++ b/lib/client/dom.js @@ -1,1292 +1,1304 @@ -var CloudCommander, Util, - DOM = {}, - CloudFunc; - -(function(Util, DOM){ - 'use strict'; - - /* PRIVATE */ - - /* private members */ - var /* название css-класа текущего файла*/ - CURRENT_FILE = 'current-file', - Listeners = [], - XMLHTTP, - Title, - - /* Обьект, который содержит - * функции для отображения - * картинок - */ - Images = function (){ - var getImage = function(pName){ - var lId = pName + '-image', - lE = DOM.getById(lId); - if (!lE) - lE = DOM.anyload({ - name : 'span', - className : 'icon ' + pName, - id : lId, - not_append : true - }); - - return lE; - }; - /* Функция создаёт картинку загрузки*/ - this.loading = function(){ - return getImage('loading'); - }; - - /* Функция создаёт картинку ошибки загрузки*/ - this.error = function(){ - return getImage('error'); - }; - }; - - Images = new Images(); - - function removeListenerFromList(pElement){ - var lRet; - - for(var i = 0, n = Listeners.length; i < n; i++){ - if(Listeners[i].element === pElement){ - Listeners[i] = null; - break; - } - } - - return lRet; - } - - /** - * 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, CURRENT_FILE); - - return lRet_b; - } - - /** - * add class to current element - * @param pElement - * @param pClass - */ - DOM.addClass = function(pElement, pClass){ - var lRet; - - if(pElement){ - var lClassList = pElement.classList; - - if(lClassList){ - if( !lClassList.contains(pClass) ) - lClassList.add(pClass); - lRet = true; - } - } - - return lRet; - }; - - /** - * safe add event listener - * @param pType - * @param pListener - * @param pUseCapture - * @param pElement {document by default} - */ - DOM.addListener = function(pType, pListener, pElement, pUseCapture){ - var lRet = this, - lElement = (pElement || window); - - - lElement.addEventListener( - pType, - pListener, - pUseCapture || false - ); - - Listeners.push({ - element : lElement, - callback: pListener - }); - - return lRet; - }; - - /** - * safe add event listener - * @param pType - * @param pListener - * @param pUseCapture - * @param pElement {document by default} - */ - DOM.addOneTimeListener = function(pType, pListener, pElement, pUseCapture){ - var lRet = this; - - DOM.addListener(pType, - function oneTime(pEvent){ - DOM.removeListener(pType, oneTime, pElement, pUseCapture); - pListener(pEvent); - }, - pUseCapture, pElement); - - return lRet; - }; - - /** - * safe remove event listener - * @param pType - * @param pListener - * @param pUseCapture - * @param pElement {document by default} - */ - DOM.removeListener = function(pType, pListener, pElement, pUseCapture){ - var lRet = this; - - (pElement || window).removeEventListener( - pType, - pListener, - pUseCapture || false - ); - - return lRet; - }; - - - /** - * safe add event keydown listener - * @param pListener - * @param pUseCapture - */ - DOM.addKeyListener = function(pListener, pElement, pUseCapture){ - return DOM.addListener('keydown', pListener, pElement, pUseCapture); - }; - - /** - * safe add event click listener - * @param pListener - * @param pUseCapture - */ - DOM.addClickListener = function(pListener, pElement, pUseCapture){ - return DOM.addListener('click', pListener, pElement, pUseCapture); - }; - - /** - * safe add event click listener - * @param pListener - * @param pUseCapture - */ - DOM.addErrorListener = function(pListener, pElement, pUseCapture){ - return DOM.addListener('error', pListener, pElement, pUseCapture); - }; - - /** - * getListener for element - * - * @param pElement - */ - DOM.getListener = function(pElement){ - var lRet; - - for(var i = 0, n = Listeners.length; i < n; i++){ - if(Listeners[i].element === pElement){ - lRet = Listeners[i].callback; - break; - } - } - - return lRet; - }; - - /** - * 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) ){ - Util.log('error in DOM.ajax onSuccess:', pParams); - Util.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 = Util.parseJSON(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(); - - /** - * delete currentfile, prompt before it - * - * @pCurrentFile - */ - 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(lCurrent); - - return lRet; - }; - - /** - * 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 || document.body, - lInner = pParams_o.inner, - lStyle = pParams_o.style, - lNotAppend = pParams_o.not_append; - - if ( Util.isObject(lFunc) ){ - lOnError = lFunc.onerror; - lFunc = lFunc.onload; - } - /* убираем путь к файлу, оставляя только название файла */ - if(!lID && lSrc) - lID = DOM.getIdBySrc(lSrc); - - var lElement = DOM.getById(lID); - - /* если скрипт еще не загружен */ - if(!lElement){ - if(!lName && lSrc){ - - var lDot = lSrc.lastIndexOf('.'), - lExt = lSrc.substr(lDot); - switch(lExt){ - case '.js': - lName = 'script'; - break; - case '.css': - lName = 'link'; - lParent = document.head; - break; - default: - return {code: -1, text: 'name can not be empty'}; - } - } - lElement = document.createElement(lName); - - if(lID) - lElement.id = lID; - - if(lClass) - lElement.className = lClass; - - /* if working with external css - * using href in any other case - * using src - */ - if(lName === 'link'){ - lElement.href = lSrc; - lElement.rel = 'stylesheet'; - }else - lElement.src = lSrc; - - /* - * if passed arguments function - * then it's onload by default - * - * if object - then onload and onerror - */ - - var lLoad = function(pEvent){ - DOM.removeListener('load', lLoad, false, lElement); - DOM.removeListener('error', lError, false, lElement); - - Util.exec(lFunc, pEvent); - }, - - lError = function(){ - lParent.removeChild(lElement); - - DOM.Images.showError({ - responseText: 'file ' + - lSrc + - ' could not be loaded', - status : 404 - }); - - Util.exec(lOnError); - }; - - DOM.addListener('load', lLoad, lElement); - DOM.addErrorListener(lError,lElement); - - if(lStyle) - lElement.style.cssText = lStyle; - - if(lAsync || lAsync === undefined) - lElement.async = true; - - if(!lNotAppend) - lParent.appendChild(lElement); - - if(lInner) - lElement.innerHTML = lInner; - } - /* если js-файл уже загружен - * запускаем функцию onload - */ - else - Util.exec(lFunc); - - return lElement; - }, - - /** - * Функция загружает js-файл - * - * @param pSrc - * @param pFunc - */ - DOM.jsload = function(pSrc, pFunc){ - if( Util.isArray(pSrc) ){ - 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 - }); - }, - - /** - * returns jsload functions - */ - DOM.retJSLoad = function(pSrc, pFunc){ - var lRet = function(){ - return DOM.jsload(pSrc, pFunc); - }; - - return lRet; - }, - - - /** - * Функция создаёт елемент 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('//code.jquery.com/jquery-1.9.0.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', Util.retExec(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, - lLoadingImage = Images.loading(), - lErrorImage = Images.error(); - - DOM.hide(lErrorImage); - - var lCurrent; - if(pPosition && pPosition.top) - lCurrent = DOM.getRefreshButton().parentElement; - else - lCurrent = DOM.getCurrentFile().firstChild.nextSibling; - - /* show loading icon if it not showed */ - - var lParent = lLoadingImage.parentElement; - if(!lParent || (lParent && lParent !== lCurrent)) - lCurrent.appendChild(lLoadingImage); - - lRet_b = DOM.show(lLoadingImage); /* показываем загрузку*/ - - return lRet_b; - }, - - /** - * hide load image - */ - hideLoad : function(){ - - DOM.hide( Images.loading() ); - }, - - /** - * show error image (usualy after error on ajax request) - */ - showError : function(jqXHR, textStatus, errorThrown){ - var lLoadingImage = Images.loading(), - lErrorImage = Images.error(), - lResponce = jqXHR.responseText, - lStatusText = jqXHR.statusText, - lStatus = jqXHR.status, - lText = (lStatus === 404 ? lResponce : lStatusText); - - /* если файла не существует*/ - if( Util.isContainStr(lText, 'Error: ENOENT, ') ) - lText = lText.replace('Error: ENOENT, n','N'); - - /* если не хватает прав для чтения файла*/ - else if( Util.isContainStr(lText, 'Error: EACCES,') ) - lText = lText.replace('Error: EACCES, p','P'); - - - DOM.show(lErrorImage); - lErrorImage.title = lText; - - var lParent = lLoadingImage.parentElement; - if(lParent) - lParent.appendChild(lErrorImage); - - DOM.hide(lLoadingImage); - - Util.log(lText); - } - }; - - /** - * get current direcotory path - */ - DOM.getCurrentDir = function(){ - var lRet, - lSubstr, - lPanel = DOM.getPanel(), - /* получаем имя каталога в котором находимся */ - lHref = DOM.getByClass('path', lPanel); - - lHref = lHref[0].textContent; - - lHref = CloudFunc.removeLastSlash(lHref); - lSubstr = lHref.substr(lHref , lHref.lastIndexOf('/')); - lRet = Util.removeStr(lHref, lSubstr + '/'); - - return lRet; - }; - - /** - * unified way to get current file - * - * @pCurrentFile - */ - DOM.getCurrentFile = function(){ - var lRet = DOM.getByClass(CURRENT_FILE )[0]; - - return lRet; - }; - - - DOM.getCurrentSize = function(pCurrentFile){ - var lRet, - lCurrent = pCurrentFile || DOM.getCurrentFile(), - lSize = DOM.getByClass('size', lCurrent); - lRet = lSize[0].textContent; - - return lRet; - }; - - /** - * unified way to get current file content - * - * @pCallBack - callback function or data struct {sucess, error} - * @pCurrentFile - */ - DOM.getCurrentFileContent = function(pParams, pCurrentFile){ - var lRet, - lParams = pParams ? pParams : {}, - lPath = DOM.getCurrentPath(pCurrentFile), - lErrorWas = pParams.error, - lError = function(jqXHR){ - Util.exec(lErrorWas); - DOM.Images.showError(jqXHR); - }; - if( Util.isFunction(lParams) ) - lParams.success = Util.retExec(pParams); - - lParams.error = lError; - - if(!lParams.url) - lParams.url = lPath; - - lRet = DOM.ajax(lParams); - - return lRet; - }; - - /** - * unified way to get current file content - * - * @pCallBack - function({data, name}){} - * @pCurrentFile - */ - DOM.getCurrentData = function(pCallBack, pCurrentFile){ - var lParams, - lFunc = function(pData){ - var lName = DOM.getCurrentName(pCurrentFile); - if( Util.isObject(pData) ){ - pData = Util.stringifyJSON(pData); - - var lExt = '.json'; - if( !Util.checkExtension(lName, lExt) ) - lName += lExt; - } - - Util.exec(pCallBack, { - data: pData, - name: lName - }); - }; - - if( !Util.isObject(pCallBack) ) - lParams = lFunc; - else - lParams = { - success : lFunc, - error : pCallBack.error - }; - - - return DOM.getCurrentFileContent(lParams, pCurrentFile); - }; - - /** - * 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, - lCurrentFileWas = DOM.getCurrentFile(); - - if(pCurrentFile){ - if (pCurrentFile.className === 'path') - pCurrentFile = pCurrentFile.nextSibling; - - if (pCurrentFile.className === 'fm-header') - pCurrentFile = pCurrentFile.nextSibling; - - if(lCurrentFileWas) - unSetCurrentFile(lCurrentFileWas); - - DOM.addClass(pCurrentFile, CURRENT_FILE); - - /* scrolling to current file */ - DOM.scrollIntoViewIfNeeded(pCurrentFile); - - lRet = true; - } - return lRet; - }; - - /** - * 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; - }; - - /** - * 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 - * - * @param pCurrentFile - */ - DOM.isCurrentFile = function(pCurrentFile){ - var lCurrentFileClass = pCurrentFile.className, - lIsCurrent = lCurrentFileClass.indexOf(CURRENT_FILE) >= 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; - - 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 - * - * @param pCurrentFile - */ - DOM.getCurrentName = function(pCurrentFile){ - var lCurrent = pCurrentFile || DOM.getCurrentFile(), - lLink = DOM.getCurrentLink( lCurrent ); - - if(lLink) - 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) - Util.log('Error can not find Active Panel'); - - return lPanel; - }; - - /** prevent default event */ - DOM.preventDefault = function(pEvent){ - var lRet, - lPreventDefault = pEvent && pEvent.preventDefault, - lFunc = Util.bind(lPreventDefault, pEvent); - - lRet = Util.exec(lFunc); - - return lRet; - }; - - 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'); - }; - - /** - * open window with URL - * @param pUrl - */ - DOM.openWindow = function(pUrl){ - var left = 140, - top = 187, - width = 1000, - height = 650, - - lOptions = 'left=' + left + - ',top=' + top + - ',width=' + width + - ',height=' + height + - ',personalbar=0,toolbar=0' + - ',scrollbars=1,resizable=1'; - - var lWind = window.open(pUrl, 'Cloud Commander Auth', lOptions); - if(!lWind) - Util.log('Pupup blocked!'); - }; - - /** - * 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 lCurrent = pCurrent || DOM.getCurrentFile(), - lParent = lCurrent.parentElement, - lName = DOM.getCurrentName(lCurrent); - - if(lCurrent && lParent){ - if(lName !== '..'){ - var lNext = lCurrent.nextSibling; - var lPrevious = lCurrent.previousSibling; - if(lNext) - DOM.setCurrentFile(lNext); - else if(lPrevious) - DOM.setCurrentFile(lPrevious); - - lParent.removeChild(lCurrent); - } - 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 lCurrent; - }; - - /** - * 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(Util, DOM){ + 'use strict'; + + /* PRIVATE */ + + /* private members */ + var /* название css-класа текущего файла*/ + CURRENT_FILE = 'current-file', + Listeners = [], + XMLHTTP, + Title, + + /* Обьект, который содержит + * функции для отображения + * картинок + */ + Images = function (){ + var getImage = function(pName){ + var lId = pName + '-image', + lE = DOM.getById(lId); + if (!lE) + lE = DOM.anyload({ + name : 'span', + className : 'icon ' + pName, + id : lId, + not_append : true + }); + + return lE; + }; + /* Функция создаёт картинку загрузки*/ + this.loading = function(){ + return getImage('loading'); + }; + + /* Функция создаёт картинку ошибки загрузки*/ + this.error = function(){ + return getImage('error'); + }; + }; + + Images = new Images(); + + function removeListenerFromList(pElement){ + var lRet; + + for(var i = 0, n = Listeners.length; i < n; i++){ + if(Listeners[i].element === pElement){ + Listeners[i] = null; + break; + } + } + + return lRet; + } + + /** + * 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, CURRENT_FILE); + + return lRet_b; + } + + /** + * add class to current element + * @param pElement + * @param pClass + */ + DOM.addClass = function(pElement, pClass){ + var lRet; + + if(pElement){ + var lClassList = pElement.classList; + + if(lClassList){ + if( !lClassList.contains(pClass) ) + lClassList.add(pClass); + lRet = true; + } + } + + return lRet; + }; + + /** + * safe add event listener + * @param pType + * @param pListener + * @param pUseCapture + * @param pElement {document by default} + */ + DOM.addListener = function(pType, pListener, pElement, pUseCapture){ + var lRet = this, + lElement = (pElement || window); + + + lElement.addEventListener( + pType, + pListener, + pUseCapture || false + ); + + Listeners.push({ + element : lElement, + callback: pListener + }); + + return lRet; + }; + + /** + * safe add event listener + * @param pType + * @param pListener + * @param pUseCapture + * @param pElement {document by default} + */ + DOM.addOneTimeListener = function(pType, pListener, pElement, pUseCapture){ + var lRet = this; + + DOM.addListener(pType, + function oneTime(pEvent){ + DOM.removeListener(pType, oneTime, pElement, pUseCapture); + pListener(pEvent); + }, + pUseCapture, pElement); + + return lRet; + }; + + /** + * safe remove event listener + * @param pType + * @param pListener + * @param pUseCapture + * @param pElement {document by default} + */ + DOM.removeListener = function(pType, pListener, pElement, pUseCapture){ + var lRet = this; + + (pElement || window).removeEventListener( + pType, + pListener, + pUseCapture || false + ); + + return lRet; + }; + + + /** + * safe add event keydown listener + * @param pListener + * @param pUseCapture + */ + DOM.addKeyListener = function(pListener, pElement, pUseCapture){ + return DOM.addListener('keydown', pListener, pElement, pUseCapture); + }; + + /** + * safe add event click listener + * @param pListener + * @param pUseCapture + */ + DOM.addClickListener = function(pListener, pElement, pUseCapture){ + return DOM.addListener('click', pListener, pElement, pUseCapture); + }; + + /** + * safe add event click listener + * @param pListener + * @param pUseCapture + */ + DOM.addErrorListener = function(pListener, pElement, pUseCapture){ + return DOM.addListener('error', pListener, pElement, pUseCapture); + }; + + /** + * getListener for element + * + * @param pElement + */ + DOM.getListener = function(pElement){ + var lRet; + + for(var i = 0, n = Listeners.length; i < n; i++){ + if(Listeners[i].element === pElement){ + lRet = Listeners[i].callback; + break; + } + } + + return lRet; + }; + + /** + * 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) ){ + Util.log('error in DOM.ajax onSuccess:', pParams); + Util.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 = Util.parseJSON(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(); + + /** + * delete currentfile, prompt before it + * + * @pCurrentFile + */ + 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(lCurrent); + + return lRet; + }; + + /** + * 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 || document.body, + lInner = pParams_o.inner, + lStyle = pParams_o.style, + lNotAppend = pParams_o.not_append; + + if ( Util.isObject(lFunc) ){ + lOnError = lFunc.onerror; + lFunc = lFunc.onload; + } + /* убираем путь к файлу, оставляя только название файла */ + if(!lID && lSrc) + lID = DOM.getIdBySrc(lSrc); + + var lElement = DOM.getById(lID); + + /* если скрипт еще не загружен */ + if(!lElement){ + if(!lName && lSrc){ + + var lDot = lSrc.lastIndexOf('.'), + lExt = lSrc.substr(lDot); + switch(lExt){ + case '.js': + lName = 'script'; + break; + case '.css': + lName = 'link'; + lParent = document.head; + break; + default: + return {code: -1, text: 'name can not be empty'}; + } + } + lElement = document.createElement(lName); + + if(lID) + lElement.id = lID; + + if(lClass) + lElement.className = lClass; + + /* if working with external css + * using href in any other case + * using src + */ + if(lName === 'link'){ + lElement.href = lSrc; + lElement.rel = 'stylesheet'; + }else + lElement.src = lSrc; + + /* + * if passed arguments function + * then it's onload by default + * + * if object - then onload and onerror + */ + + var lLoad = function(pEvent){ + DOM.removeListener('load', lLoad, false, lElement); + DOM.removeListener('error', lError, false, lElement); + + Util.exec(lFunc, pEvent); + }, + + lError = function(){ + lParent.removeChild(lElement); + + DOM.Images.showError({ + responseText: 'file ' + + lSrc + + ' could not be loaded', + status : 404 + }); + + Util.exec(lOnError); + }; + + DOM.addListener('load', lLoad, lElement); + DOM.addErrorListener(lError,lElement); + + if(lStyle) + lElement.style.cssText = lStyle; + + if(lAsync || lAsync === undefined) + lElement.async = true; + + if(!lNotAppend) + lParent.appendChild(lElement); + + if(lInner) + lElement.innerHTML = lInner; + } + /* если js-файл уже загружен + * запускаем функцию onload + */ + else + Util.exec(lFunc); + + return lElement; + }, + + /** + * Функция загружает js-файл + * + * @param pSrc + * @param pFunc + */ + DOM.jsload = function(pSrc, pFunc){ + if( Util.isArray(pSrc) ){ + 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 + }); + }, + + /** + * returns jsload functions + */ + DOM.retJSLoad = function(pSrc, pFunc){ + var lRet = function(){ + return DOM.jsload(pSrc, pFunc); + }; + + return lRet; + }, + + + /** + * Функция создаёт елемент 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('//code.jquery.com/jquery-1.9.0.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', Util.retExec(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, + lLoadingImage = Images.loading(), + lErrorImage = Images.error(); + + DOM.hide(lErrorImage); + + var lCurrent; + if(pPosition && pPosition.top) + lCurrent = DOM.getRefreshButton().parentElement; + else + lCurrent = DOM.getCurrentFile().firstChild.nextSibling; + + /* show loading icon if it not showed */ + + var lParent = lLoadingImage.parentElement; + if(!lParent || (lParent && lParent !== lCurrent)) + lCurrent.appendChild(lLoadingImage); + + lRet_b = DOM.show(lLoadingImage); /* показываем загрузку*/ + + return lRet_b; + }, + + /** + * hide load image + */ + hideLoad : function(){ + + DOM.hide( Images.loading() ); + }, + + /** + * show error image (usualy after error on ajax request) + */ + showError : function(jqXHR, textStatus, errorThrown){ + var lLoadingImage = Images.loading(), + lErrorImage = Images.error(), + lResponce = jqXHR.responseText, + lStatusText = jqXHR.statusText, + lStatus = jqXHR.status, + lText = (lStatus === 404 ? lResponce : lStatusText); + + /* если файла не существует*/ + if( Util.isContainStr(lText, 'Error: ENOENT, ') ) + lText = lText.replace('Error: ENOENT, n','N'); + + /* если не хватает прав для чтения файла*/ + else if( Util.isContainStr(lText, 'Error: EACCES,') ) + lText = lText.replace('Error: EACCES, p','P'); + + + DOM.show(lErrorImage); + lErrorImage.title = lText; + + var lParent = lLoadingImage.parentElement; + if(lParent) + lParent.appendChild(lErrorImage); + + DOM.hide(lLoadingImage); + + Util.log(lText); + } + }; + + /** + * get current direcotory path + */ + DOM.getCurrentDir = function(){ + var lRet, + lSubstr, + lPanel = DOM.getPanel(), + /* получаем имя каталога в котором находимся */ + lHref = DOM.getByClass('path', lPanel); + + lHref = lHref[0].textContent; + + lHref = CloudFunc.removeLastSlash(lHref); + lSubstr = lHref.substr(lHref , lHref.lastIndexOf('/')); + lRet = Util.removeStr(lHref, lSubstr + '/'); + + return lRet; + }; + + /** + * unified way to get current file + * + * @pCurrentFile + */ + DOM.getCurrentFile = function(){ + var lRet = DOM.getByClass(CURRENT_FILE )[0]; + + return lRet; + }; + + + DOM.getCurrentSize = function(pCurrentFile){ + var lRet, + lCurrent = pCurrentFile || DOM.getCurrentFile(), + lSize = DOM.getByClass('size', lCurrent); + lRet = lSize[0].textContent; + + return lRet; + }; + + /** + * unified way to get current file content + * + * @pCallBack - callback function or data struct {sucess, error} + * @pCurrentFile + */ + DOM.getCurrentFileContent = function(pParams, pCurrentFile){ + var lRet, + lParams = pParams ? pParams : {}, + lPath = DOM.getCurrentPath(pCurrentFile), + lErrorWas = pParams.error, + lError = function(jqXHR){ + Util.exec(lErrorWas); + DOM.Images.showError(jqXHR); + }; + if( Util.isFunction(lParams) ) + lParams.success = Util.retExec(pParams); + + lParams.error = lError; + + if(!lParams.url) + lParams.url = lPath; + + lRet = DOM.ajax(lParams); + + return lRet; + }; + + /** + * unified way to get current file content + * + * @pCallBack - function({data, name}){} + * @pCurrentFile + */ + DOM.getCurrentData = function(pCallBack, pCurrentFile){ + var lParams, + lFunc = function(pData){ + var lName = DOM.getCurrentName(pCurrentFile); + if( Util.isObject(pData) ){ + pData = Util.stringifyJSON(pData); + + var lExt = '.json'; + if( !Util.checkExtension(lName, lExt) ) + lName += lExt; + } + + Util.exec(pCallBack, { + data: pData, + name: lName + }); + }; + + if( !Util.isObject(pCallBack) ) + lParams = lFunc; + else + lParams = { + success : lFunc, + error : pCallBack.error + }; + + + return DOM.getCurrentFileContent(lParams, pCurrentFile); + }; + + /** + * 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, + lCurrentFileWas = DOM.getCurrentFile(); + + if(pCurrentFile){ + if (pCurrentFile.className === 'path') + pCurrentFile = pCurrentFile.nextSibling; + + if (pCurrentFile.className === 'fm-header') + pCurrentFile = pCurrentFile.nextSibling; + + if(lCurrentFileWas) + unSetCurrentFile(lCurrentFileWas); + + DOM.addClass(pCurrentFile, CURRENT_FILE); + + /* scrolling to current file */ + DOM.scrollIntoViewIfNeeded(pCurrentFile); + + lRet = true; + } + return lRet; + }; + + /** + * 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; + }; + + /** + * 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 + * + * @param pCurrentFile + */ + DOM.isCurrentFile = function(pCurrentFile){ + var lCurrentFileClass = pCurrentFile.className, + lIsCurrent = lCurrentFileClass.indexOf(CURRENT_FILE) >= 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; + + 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 + * + * @param pCurrentFile + */ + DOM.getCurrentName = function(pCurrentFile){ + var lCurrent = pCurrentFile || DOM.getCurrentFile(), + lLink = DOM.getCurrentLink( lCurrent ); + + if(lLink) + 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) + Util.log('Error can not find Active Panel'); + + return lPanel; + }; + + /** prevent default event */ + DOM.preventDefault = function(pEvent){ + var lRet, + lPreventDefault = pEvent && pEvent.preventDefault, + lFunc = Util.bind(lPreventDefault, pEvent); + + lRet = Util.exec(lFunc); + + return lRet; + }; + + 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'); + }; + + /** + * open window with URL + * @param pUrl + */ + DOM.openWindow = function(pUrl){ + var left = 140, + top = 187, + width = 1000, + height = 650, + + lOptions = 'left=' + left + + ',top=' + top + + ',width=' + width + + ',height=' + height + + ',personalbar=0,toolbar=0' + + ',scrollbars=1,resizable=1'; + + var lWind = window.open(pUrl, 'Cloud Commander Auth', lOptions); + if(!lWind) + Util.log('Pupup blocked!'); + }; + + /** + * 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 lCurrent = pCurrent || DOM.getCurrentFile(), + lParent = lCurrent.parentElement, + lName = DOM.getCurrentName(lCurrent); + + if(lCurrent && lParent){ + if(lName !== '..'){ + var lNext = lCurrent.nextSibling; + var lPrevious = lCurrent.previousSibling; + if(lNext) + DOM.setCurrentFile(lNext); + else if(lPrevious) + DOM.setCurrentFile(lPrevious); + + lParent.removeChild(lCurrent); + } + 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 lCurrent; + }; + + /** + * 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; + }; + + /* scroll on one page*/ + DOM.scrollByPages = function(pElement, pPages){ + var lRet = true; + + if(pElement && pElement.scrollByPages && pPages) + pElement.scrollByPages(pPages); + 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; + }; })(Util, DOM); \ No newline at end of file diff --git a/lib/client/ie.js b/lib/client/ie.js index 25df0b02..b0c63dfc 100644 --- a/lib/client/ie.js +++ b/lib/client/ie.js @@ -1,272 +1,273 @@ -/* script, fixes ie */ -//var Util, DOM, jQuery; - -(function(Util, DOM, $){ - 'use strict'; - - if(!window.XMLHttpRequest || !document.head) - DOM.ajax = $.ajax; - - /* setting head ie6 - ie8 */ - if(!document.head){ - document.head = $('head')[0]; - - /* - {name: '', src: ' ',func: '', style: '', id: '', parent: '', - async: false, inner: 'id{color:red, }, class:'', not_append: false} - */ - DOM.cssSet = function(pParams_o){ - var lElement = ''; + + return $(lElement) + .appendTo(pParams_o.parent || document.head); + }; + } + + /* setting function context (this) */ + Util.bind = function(pFunction, pContext){ + var lRet; + + lRet = $.proxy(pFunction, pContext); + + return lRet; + }; + + /* + * typeof callback === "function" should not be used, + * as older browsers may report objects to be a function, + * which they are not + */ + Util.isFunction = $.isFunction; + + if (!document.addEventListener) + /** + * safe add event listener on ie + * @param pType + * @param pListener + */ + DOM.addListener = function(pType, pListener, pCapture, pElement){ + var lRet; + + if(!pElement) + pElement = window; + + lRet = $(pElement).bind(pType, null, pListener); + + return lRet; + }; + + if(!document.removeEventListener){ + DOM.removeListener = function(pType, pListener, pCapture, pElement){ + var lRet; + + if(!pElement) + pElement = window; + + $(pElement).unbind(pType, pListener); + + return lRet; + }; + } + + if(!document.getElementsByClassName){ + DOM.getByClass = function(pClass, pElement){ + var lClass = '.' + pClass, + lResult; + + if(pElement) + lResult = $(pElement).find(lClass); + else lResult = $.find(lClass); + + return lResult; + }; + } + + DOM.scrollByPages = Util.retFalse; + /* function polyfill webkit standart function */ + DOM.scrollIntoViewIfNeeded = function(pElement, centerIfNeeded){ + if(!window.getComputedStyle) + return; + /* + https://gist.github.com/2581101 + */ + centerIfNeeded = arguments.length === 0 ? true : !!centerIfNeeded; + + var parent = pElement.parentNode, + parentComputedStyle = window.getComputedStyle(parent, null), + parentBorderTopWidth = + parseInt(parentComputedStyle.getPropertyValue('border-top-width'), 10), + + parentBorderLeftWidth = + parseInt(parentComputedStyle.getPropertyValue('border-left-width'), 10), + + overTop = pElement.offsetTop - parent.offsetTop < parent.scrollTop, + overBottom = + (pElement.offsetTop - + parent.offsetTop + + pElement.clientHeight - + parentBorderTopWidth) > + (parent.scrollTop + parent.clientHeight), + + overLeft = pElement.offsetLeft - + parent.offsetLeft < parent.scrollLeft, + + overRight = + (pElement.offsetLeft - + parent.offsetLeft + + pElement.clientWidth - + parentBorderLeftWidth) > + (parent.scrollLeft + parent.clientWidth), + + alignWithTop = overTop && !overBottom; + + if ((overTop || overBottom) && centerIfNeeded) + parent.scrollTop = + pElement.offsetTop - + parent.offsetTop - + parent.clientHeight / 2 - + parentBorderTopWidth + + pElement.clientHeight / 2; + + if ((overLeft || overRight) && centerIfNeeded) + parent.scrollLeft = + pElement.offsetLeft - + parent.offsetLeft - + parent.clientWidth / 2 - + parentBorderLeftWidth + + pElement.clientWidth / 2; + + if ( (overTop || overBottom || overLeft || overRight) && + !centerIfNeeded) + pElement.scrollIntoView(alignWithTop); + }; + + if(!document.body.classList){ + DOM.addClass = function(pElement, pClass){ + var lSpaceChar = '', + lClassName = pElement.className, + lRet_b = true; + + if(lClassName) lSpaceChar = ' '; + + if( lClassName.indexOf(pClass) < 0 ) + pElement.className += lSpaceChar + pClass; + else + lRet_b = false; + }; + + DOM.removeClass = function(pElement, pClass){ + var lClassName = pElement.className; + + if(lClassName.length > pClass.length) + pElement.className = lClassName.replace(pClass, ''); + }; + } + + if(!window.JSON){ + Util.parseJSON = $.parseJSON; + + /* https://gist.github.com/754454 */ + Util.stringifyJSON = function(pObj){ + var lRet; + + if (!Util.isObject(pObj) || pObj === null) { + // simple data type + if (Util.isString(pObj)) pObj = '"' + pObj + '"'; + lRet = String(pObj); + } else { + // recurse array or object + var n, v, json = [], isArray = Util.isArray(pObj); + + for (n in pObj) { + v = pObj[n]; + + if (pObj.hasOwnProperty(n)) { + if (Util.isString(v)) + v = '"' + v + '"'; + else if (v && Util.isObject(v)) + v = DOM.stringifyJSON(v); + + json.push((isArray ? "" : '"' + n + '":') + String(v)); + } + } + lRet = (isArray ? "[" : "{") + String(json) + (isArray ? "]" : "}"); + } + + return lRet; + }; + } + + if(!window.localStorage){ + var 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 Cache(); + } + + })(Util, DOM, jQuery); \ No newline at end of file diff --git a/lib/client/keyBinding.js b/lib/client/keyBinding.js index 32f86fed..df348264 100644 --- a/lib/client/keyBinding.js +++ b/lib/client/keyBinding.js @@ -1,308 +1,308 @@ -var CloudCommander, Util, DOM; -(function(CloudCmd, Util, DOM){ - "use strict"; - - DOM.Images.hideLoad(); - - /* private property set or set key binding */ - var keyBinded; - - /* Key constants*/ - CloudCmd.KEY = { - TAB : 9, - ENTER : 13, - ESC : 27, - - PAGE_UP : 33, - PAGE_DOWN : 34, - END : 35, - HOME : 36, - UP : 38, - DOWN : 40, - - Delete : 46, - - D : 68, - - G : 71, - - O : 79, - Q : 81, - R : 82, - S : 83, - T : 84, - - F2 : 113, - F3 : 114, - F4 : 115, - F10 : 121, - - TRA : 192 /* Typewritten Reverse Apostrophe (`) */ - }; - - var KEY = CloudCmd.KEY; - - CloudCmd.KeyBinding = {}; - - var KeyBinding = CloudCmd.KeyBinding; - - KeyBinding.get = function(){return keyBinded;}; - - KeyBinding.set = function(){keyBinded = true;}; - - KeyBinding.unSet = function(){keyBinded = false;}; - - KeyBinding.init = function(){ - /* saving state of tabs varibles */ - var lTabPanel = { - left : 0, - right : 0 - - }; - - var key_event = function(pEvent){ - /* получаем выдленный файл*/ - var lCurrentFile = DOM.getCurrentFile(), i; - /* если клавиши можно обрабатывать*/ - if(keyBinded && pEvent){ - var lKeyCode = pEvent.keyCode; - - /* open configuration window */ - if(lKeyCode === KEY.O && pEvent.altKey){ - console.log('openning config window...'); - - DOM.Images.showLoad({top: true}); - - Util.exec(CloudCmd.Config); - } - - else if(lKeyCode === KEY.G && pEvent.altKey) - Util.exec(CloudCmd.GitHub); - - else if(lKeyCode === KEY.D && pEvent.altKey){ - Util.exec(CloudCmd.DropBox); - DOM.preventDefault(pEvent); - } - - /* если нажали таб: - * переносим курсор на - * правую панель, если - * мы были на левой и - * наоборот - */ - else if(lKeyCode === KEY.TAB){ - console.log('Tab pressed'); - - Util.tryCatchLog(function(){ - /* changing parent panel of curent-file */ - var lPanel = DOM.getPanel(), - lId = lPanel.id; - - lTabPanel[lId] = lCurrentFile; - - lPanel = DOM.getPanel({active:false}); - lId = lPanel.id; - - if(lTabPanel[lId]) - DOM.setCurrentFile(lTabPanel[lId]); - else{ - var lFirstFileOnList = DOM.getByTag('li', lPanel)[2]; - - DOM.setCurrentFile(lFirstFileOnList); - } - }); - - DOM.preventDefault(pEvent);//запрет на дальнейшее действие - } - else if(lKeyCode === KEY.Delete) - DOM.promptRemoveCurrent(lCurrentFile); - - /* if f3 or shift+f3 or alt+f3 pressed */ - else if(lKeyCode === KEY.F3){ - var lEditor = CloudCmd[pEvent.shiftKey ? - 'Viewer' : 'Editor']; - - Util.exec(lEditor, true); - - DOM.preventDefault(pEvent); - } - - /* if f4 pressed */ - else if(lKeyCode === KEY.F4) { - DOM.Images.showLoad(); - - Util.exec(CloudCmd.Editor); - - DOM.preventDefault(pEvent); - } - else if(lKeyCode === KEY.F10 && pEvent.shiftKey){ - Util.exec(CloudCmd.Menu); - - DOM.preventDefault(pEvent); - } - - else if (lKeyCode === KEY.TRA){ - DOM.Images.showLoad({top: true}); - Util.exec(CloudCmd.Terminal); - } - /* навигация по таблице файлов * - * если нажали клавишу вверх * - * выделяем предыдущую строку */ - else if(lKeyCode === KEY.UP){ - DOM.setCurrentFile( lCurrentFile.previousSibling ); - DOM.preventDefault( pEvent ); - } - - /* если нажали клавишу в низ * - * выделяем следующую строку */ - else if(lKeyCode === KEY.DOWN){ - DOM.setCurrentFile( lCurrentFile.nextSibling ); - DOM.preventDefault( pEvent ); - } - - /* если нажали клавишу Home * - * переходим к самому верхнему * - * элементу */ - else if(lKeyCode === KEY.HOME){ - DOM.setCurrentFile( lCurrentFile.parentElement.firstChild ); - DOM.preventDefault(pEvent); - } - - /* если нажали клавишу End - * выделяем последний элемент - */ - else if(lKeyCode === KEY.END){ - DOM.setCurrentFile( lCurrentFile.parentElement.lastElementChild ); - DOM.preventDefault( pEvent ); - } - - /* если нажали клавишу page down - * проматываем экран - */ - else if(lKeyCode === KEY.PAGE_DOWN){ - DOM.getPanel().scrollByPages(1); - - for(i=0; i<30; i++){ - if(!lCurrentFile.nextSibling) break; - - lCurrentFile = lCurrentFile.nextSibling; - } - DOM.setCurrentFile(lCurrentFile); - DOM.preventDefault(pEvent); - } - - /* если нажали клавишу page up - * проматываем экран - */ - else if(lKeyCode === KEY.PAGE_UP){ - DOM.getPanel().scrollByPages(-1); - - var lC = lCurrentFile, - tryCatch = function(pCurrentFile){ - Util.tryCatch(function(){ - return pCurrentFile - .previousSibling - .previousSibling - .previousSibling - .previousSibling; - }); - }; - - for(i = 0; i < 30; i++){ - if(!lC.previousSibling || tryCatch(lC) ) break; - - lC = lC.previousSibling; - } - DOM.setCurrentFile(lC); - DOM.preventDefault(pEvent); - } - - /* если нажали Enter - открываем папку*/ - else if(lKeyCode === KEY.ENTER) - Util.exec(CloudCmd._loadDir()); - - /* если нажали +r - * обновляем страницу, - * загружаем содержимое каталога - * при этом данные берём всегда с - * сервера, а не из кэша - * (обновляем кэш) - */ - else if(lKeyCode === KEY.R && pEvent.ctrlKey){ - console.log('+r pressed\n' + - 'reloading page...\n' + - 'press +q to remove all key-handlers'); - - /* Программно нажимаем на кнопку перезагрузки - * содержимого каталога - */ - var lRefreshIcon = DOM.getRefreshButton(); - if(lRefreshIcon){ - /* получаем название файла*/ - var lSelectedName = DOM.getCurrentName(lCurrentFile); - - /* если нашли элемент нажимаем него - * а если не можем - нажимаем на - * ссылку, на которую повешен eventHandler - * onclick - */ - - Util.exec( DOM.getListener(lRefreshIcon) ); - CloudCmd._currentToParent(lSelectedName); - - DOM.preventDefault(); - } - } - - /* если нажали +d чистим кэш */ - else if(lKeyCode === KEY.D && pEvent.ctrlKey){ - Util.log('+d pressed\n' + - 'clearing cache...\n' + - 'press +q to remove all key-handlers'); - - DOM.Cache.clear(); - DOM.preventDefault(); - } - - /* если нажали +q - * убираем все обработчики - * нажатий клавиш - */ - else if(lKeyCode === KEY.Q && pEvent.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; - - pEvent.preventDefault();//запрет на дальнейшее действие - } - } - - /* если нажали +s - * устанавливаем все обработчики - * нажатий клавиш - */ - else if(pEvent.keyCode === KEY.S && pEvent.altKey){ - /* обрабатываем нажатия на клавиши*/ - keyBinded = true; - - console.log('+s pressed\n' + - '+r reload key-handerl - set\n' + - '+s clear cache key-handler - set\n' + - 'press +q to remove them'); - - pEvent.preventDefault();//запрет на дальнейшее действие - } - }; - - /* добавляем обработчик клавишь */ - DOM.addKeyListener(key_event); - - /* клавиши назначены*/ - keyBinded = true; - }; - +var CloudCommander, Util, DOM; +(function(CloudCmd, Util, DOM){ + "use strict"; + + DOM.Images.hideLoad(); + + /* private property set or set key binding */ + var keyBinded; + + /* Key constants*/ + CloudCmd.KEY = { + TAB : 9, + ENTER : 13, + ESC : 27, + + PAGE_UP : 33, + PAGE_DOWN : 34, + END : 35, + HOME : 36, + UP : 38, + DOWN : 40, + + Delete : 46, + + D : 68, + + G : 71, + + O : 79, + Q : 81, + R : 82, + S : 83, + T : 84, + + F2 : 113, + F3 : 114, + F4 : 115, + F10 : 121, + + TRA : 192 /* Typewritten Reverse Apostrophe (`) */ + }; + + var KEY = CloudCmd.KEY; + + CloudCmd.KeyBinding = {}; + + var KeyBinding = CloudCmd.KeyBinding; + + KeyBinding.get = function(){return keyBinded;}; + + KeyBinding.set = function(){keyBinded = true;}; + + KeyBinding.unSet = function(){keyBinded = false;}; + + KeyBinding.init = function(){ + /* saving state of tabs varibles */ + var lTabPanel = { + left : 0, + right : 0 + + }; + + var key_event = function(pEvent){ + /* получаем выдленный файл*/ + var lCurrentFile = DOM.getCurrentFile(), i; + /* если клавиши можно обрабатывать*/ + if(keyBinded && pEvent){ + var lKeyCode = pEvent.keyCode; + + /* open configuration window */ + if(lKeyCode === KEY.O && pEvent.altKey){ + console.log('openning config window...'); + + DOM.Images.showLoad({top: true}); + + Util.exec(CloudCmd.Config); + } + + else if(lKeyCode === KEY.G && pEvent.altKey) + Util.exec(CloudCmd.GitHub); + + else if(lKeyCode === KEY.D && pEvent.altKey){ + Util.exec(CloudCmd.DropBox); + DOM.preventDefault(pEvent); + } + + /* если нажали таб: + * переносим курсор на + * правую панель, если + * мы были на левой и + * наоборот + */ + else if(lKeyCode === KEY.TAB){ + console.log('Tab pressed'); + + Util.tryCatchLog(function(){ + /* changing parent panel of curent-file */ + var lPanel = DOM.getPanel(), + lId = lPanel.id; + + lTabPanel[lId] = lCurrentFile; + + lPanel = DOM.getPanel({active:false}); + lId = lPanel.id; + + if(lTabPanel[lId]) + DOM.setCurrentFile(lTabPanel[lId]); + else{ + var lFirstFileOnList = DOM.getByTag('li', lPanel)[2]; + + DOM.setCurrentFile(lFirstFileOnList); + } + }); + + DOM.preventDefault(pEvent);//запрет на дальнейшее действие + } + else if(lKeyCode === KEY.Delete) + DOM.promptRemoveCurrent(lCurrentFile); + + /* if f3 or shift+f3 or alt+f3 pressed */ + else if(lKeyCode === KEY.F3){ + var lEditor = CloudCmd[pEvent.shiftKey ? + 'Viewer' : 'Editor']; + + Util.exec(lEditor, true); + + DOM.preventDefault(pEvent); + } + + /* if f4 pressed */ + else if(lKeyCode === KEY.F4) { + DOM.Images.showLoad(); + + Util.exec(CloudCmd.Editor); + + DOM.preventDefault(pEvent); + } + else if(lKeyCode === KEY.F10 && pEvent.shiftKey){ + Util.exec(CloudCmd.Menu); + + DOM.preventDefault(pEvent); + } + + else if (lKeyCode === KEY.TRA){ + DOM.Images.showLoad({top: true}); + Util.exec(CloudCmd.Terminal); + } + /* навигация по таблице файлов * + * если нажали клавишу вверх * + * выделяем предыдущую строку */ + else if(lKeyCode === KEY.UP){ + DOM.setCurrentFile( lCurrentFile.previousSibling ); + DOM.preventDefault( pEvent ); + } + + /* если нажали клавишу в низ * + * выделяем следующую строку */ + else if(lKeyCode === KEY.DOWN){ + DOM.setCurrentFile( lCurrentFile.nextSibling ); + DOM.preventDefault( pEvent ); + } + + /* если нажали клавишу Home * + * переходим к самому верхнему * + * элементу */ + else if(lKeyCode === KEY.HOME){ + DOM.setCurrentFile( lCurrentFile.parentElement.firstChild ); + DOM.preventDefault(pEvent); + } + + /* если нажали клавишу End + * выделяем последний элемент + */ + else if(lKeyCode === KEY.END){ + DOM.setCurrentFile( lCurrentFile.parentElement.lastElementChild ); + DOM.preventDefault( pEvent ); + } + + /* если нажали клавишу page down + * проматываем экран + */ + else if(lKeyCode === KEY.PAGE_DOWN){ + DOM.scrollByPages( DOM.getPanel(), 1 ); + + for(i=0; i<30; i++){ + if(!lCurrentFile.nextSibling) break; + + lCurrentFile = lCurrentFile.nextSibling; + } + DOM.setCurrentFile(lCurrentFile); + DOM.preventDefault(pEvent); + } + + /* если нажали клавишу page up + * проматываем экран + */ + else if(lKeyCode === KEY.PAGE_UP){ + DOM.scrollByPages( DOM.getPanel(), -1 ); + + var lC = lCurrentFile, + tryCatch = function(pCurrentFile){ + Util.tryCatch(function(){ + return pCurrentFile + .previousSibling + .previousSibling + .previousSibling + .previousSibling; + }); + }; + + for(i = 0; i < 30; i++){ + if(!lC.previousSibling || tryCatch(lC) ) break; + + lC = lC.previousSibling; + } + DOM.setCurrentFile(lC); + DOM.preventDefault(pEvent); + } + + /* если нажали Enter - открываем папку*/ + else if(lKeyCode === KEY.ENTER) + Util.exec(CloudCmd._loadDir()); + + /* если нажали +r + * обновляем страницу, + * загружаем содержимое каталога + * при этом данные берём всегда с + * сервера, а не из кэша + * (обновляем кэш) + */ + else if(lKeyCode === KEY.R && pEvent.ctrlKey){ + console.log('+r pressed\n' + + 'reloading page...\n' + + 'press +q to remove all key-handlers'); + + /* Программно нажимаем на кнопку перезагрузки + * содержимого каталога + */ + var lRefreshIcon = DOM.getRefreshButton(); + if(lRefreshIcon){ + /* получаем название файла*/ + var lSelectedName = DOM.getCurrentName(lCurrentFile); + + /* если нашли элемент нажимаем него + * а если не можем - нажимаем на + * ссылку, на которую повешен eventHandler + * onclick + */ + + Util.exec( DOM.getListener(lRefreshIcon) ); + CloudCmd._currentToParent(lSelectedName); + + DOM.preventDefault(); + } + } + + /* если нажали +d чистим кэш */ + else if(lKeyCode === KEY.D && pEvent.ctrlKey){ + Util.log('+d pressed\n' + + 'clearing cache...\n' + + 'press +q to remove all key-handlers'); + + DOM.Cache.clear(); + DOM.preventDefault(); + } + + /* если нажали +q + * убираем все обработчики + * нажатий клавиш + */ + else if(lKeyCode === KEY.Q && pEvent.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; + + pEvent.preventDefault();//запрет на дальнейшее действие + } + } + + /* если нажали +s + * устанавливаем все обработчики + * нажатий клавиш + */ + else if(pEvent.keyCode === KEY.S && pEvent.altKey){ + /* обрабатываем нажатия на клавиши*/ + keyBinded = true; + + console.log('+s pressed\n' + + '+r reload key-handerl - set\n' + + '+s clear cache key-handler - set\n' + + 'press +q to remove them'); + + pEvent.preventDefault();//запрет на дальнейшее действие + } + }; + + /* добавляем обработчик клавишь */ + DOM.addKeyListener(key_event); + + /* клавиши назначены*/ + keyBinded = true; + }; + })(CloudCommander, Util, DOM); \ No newline at end of file From 73620ed9f9f31ed5d2fc009061ac21030993900d Mon Sep 17 00:00:00 2001 From: coderaiser Date: Wed, 6 Feb 2013 14:47:15 -0500 Subject: [PATCH 224/347] fixed bug with (fake) deleting file --- ChangeLog | 2 ++ lib/client/dom.js | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/ChangeLog b/ChangeLog index 7716e29f..d3bbe83d 100644 --- a/ChangeLog +++ b/ChangeLog @@ -128,6 +128,8 @@ time was changed. * Fixed bug with scrolling in opera and firefox. +* Fixed bug with (fake) deleting file. + 2012.12.12, Version 0.1.8 diff --git a/lib/client/dom.js b/lib/client/dom.js index 9d28f9b3..a5cccde5 100644 --- a/lib/client/dom.js +++ b/lib/client/dom.js @@ -367,7 +367,7 @@ var CloudCommander, Util, lMsg = 'Are you sure thet you wont delete '; /* dom element passed and it is not event */ - if(pCurrentFile && !pCurrentFile.type) + if( Util.isObject(pCurrentFile) ) lCurrent = pCurrentFile; lName = DOM.getCurrentName(lCurrent); From 29f14ba8dd5ad7358b8e71af9dc34f9c33d4628f Mon Sep 17 00:00:00 2001 From: coderaiser Date: Wed, 6 Feb 2013 14:50:07 -0500 Subject: [PATCH 225/347] fixed bug with (fake) deleting file from bottom panel --- lib/client/dom.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/client/dom.js b/lib/client/dom.js index a5cccde5..20cd2396 100644 --- a/lib/client/dom.js +++ b/lib/client/dom.js @@ -367,7 +367,7 @@ var CloudCommander, Util, lMsg = 'Are you sure thet you wont delete '; /* dom element passed and it is not event */ - if( Util.isObject(pCurrentFile) ) + if( Util.isObject(pCurrentFile) && !pCurrentFile.type) lCurrent = pCurrentFile; lName = DOM.getCurrentName(lCurrent); From 6c89bb1f7f7bbce3e3ae5a10c0417f2a20bcea89 Mon Sep 17 00:00:00 2001 From: coderaiser Date: Wed, 6 Feb 2013 14:58:15 -0500 Subject: [PATCH 226/347] changed remove to delete --- html/index.html | 2 +- lib/client.js | 2 +- lib/client/dom.js | 6 +++--- lib/client/keyBinding.js | 2 +- lib/client/menu.js | 2 +- 5 files changed, 7 insertions(+), 7 deletions(-) diff --git a/html/index.html b/html/index.html index 58e47df7..c775c8b1 100644 --- a/html/index.html +++ b/html/index.html @@ -21,7 +21,7 @@ - +
    diff --git a/lib/client.js b/lib/client.js index 974b5c69..cc74fead 100644 --- a/lib/client.js +++ b/lib/client.js @@ -285,7 +285,7 @@ function initKeysPanel(pCallBack){ null, /* f5 */ null, /* f6 */ null, /* f7 */ - DOM.promptRemoveCurrent,/* f8 */ + DOM.promptDeleteCurrent,/* f8 */ ]; for(var i = 1; i <= 8; i++){ diff --git a/lib/client/dom.js b/lib/client/dom.js index 20cd2396..466e2286 100644 --- a/lib/client/dom.js +++ b/lib/client/dom.js @@ -360,7 +360,7 @@ var CloudCommander, Util, * * @pCurrentFile */ - DOM.promptRemoveCurrent = function(pCurrentFile){ + DOM.promptDeleteCurrent = function(pCurrentFile){ var lRet, lCurrent, lName, @@ -375,7 +375,7 @@ var CloudCommander, Util, lRet = confirm(lMsg + lName + '?'); if(lRet) - DOM.removeCurrent(lCurrent); + DOM.deleteCurrent(lCurrent); return lRet; }; @@ -1215,7 +1215,7 @@ var CloudCommander, Util, * remove current file from file table * @pCurrent */ - DOM.removeCurrent = function(pCurrent){ + DOM.deleteCurrent = function(pCurrent){ var lCurrent = pCurrent || DOM.getCurrentFile(), lParent = lCurrent.parentElement, lName = DOM.getCurrentName(lCurrent); diff --git a/lib/client/keyBinding.js b/lib/client/keyBinding.js index df348264..fdcf2d0c 100644 --- a/lib/client/keyBinding.js +++ b/lib/client/keyBinding.js @@ -115,7 +115,7 @@ var CloudCommander, Util, DOM; DOM.preventDefault(pEvent);//запрет на дальнейшее действие } else if(lKeyCode === KEY.Delete) - DOM.promptRemoveCurrent(lCurrentFile); + DOM.promptDeleteCurrent(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 5e8de2c5..e331db0e 100644 --- a/lib/client/menu.js +++ b/lib/client/menu.js @@ -160,7 +160,7 @@ var CloudCommander, Util, DOM, $; lMenuItems = { 'View' : Util.retExec(showEditor, true), 'Edit' : Util.retExec(showEditor, false), - 'Delete' : Util.retExec(DOM.promptRemoveCurrent), + 'Delete' : Util.retExec(DOM.promptDeleteCurrent), }; if(UploadToItemNames.length) From 9f9aaa034752de74fcb47d4ac4e05d2062e603cb Mon Sep 17 00:00:00 2001 From: coderaiser Date: Thu, 7 Feb 2013 08:48:09 -0500 Subject: [PATCH 227/347] minor changes --- json/config.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/json/config.json b/json/config.json index a241eb45..e53c7ca4 100644 --- a/json/config.json +++ b/json/config.json @@ -2,9 +2,9 @@ "api_url" : "/api/v1", "appcache" : false, "minification" : { - "js" : false, + "js" : true, "css" : true, - "html" : false, + "html" : true, "img" : true }, "logs" : false, From 20ee329417658427ae7600f99ac523046dd6e1f6 Mon Sep 17 00:00:00 2001 From: coderaiser Date: Thu, 7 Feb 2013 08:56:11 -0500 Subject: [PATCH 228/347] minor changes --- lib/server/commander.js | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/lib/server/commander.js b/lib/server/commander.js index d8c34654..82a42547 100644 --- a/lib/server/commander.js +++ b/lib/server/commander.js @@ -112,7 +112,7 @@ }; if(n){ - var i, lDirPath = getCleanPath(lReq); + var i, lDirPath = getDirPath(lReq); for(i = 0; i < n; i++){ var lName = lDirPath + lFiles[i], @@ -187,11 +187,10 @@ /* данные о файлах в формате JSON*/ var lJSON = [], lJSONFile = {}, - lList, - lDirPath = getCleanPath(lReq); + lList; lJSON[0] = { - path : lDirPath, + path : getDirPath(lReq), size : 'dir' }; @@ -397,6 +396,12 @@ var lPath = getPath(pReq), lRet = Util.removeStr(lPath, [NO_JS, FS]) || main.SLASH; + return lRet; + } + + function getDirPath(pReq){ + var lRet = getCleanPath(pReq); + if(lRet !== '/') lRet += '/'; From ff1e3eb112ea606b30993409ff9d6ad2479b11ee Mon Sep 17 00:00:00 2001 From: coderaiser Date: Thu, 7 Feb 2013 08:59:03 -0500 Subject: [PATCH 229/347] minor changes --- lib/server/commander.js | 867 ++++++++++++++++++++-------------------- 1 file changed, 434 insertions(+), 433 deletions(-) diff --git a/lib/server/commander.js b/lib/server/commander.js index 82a42547..40c04394 100644 --- a/lib/server/commander.js +++ b/lib/server/commander.js @@ -1,434 +1,435 @@ -(function(){ - 'use strict'; - - if(!global.cloudcmd) - return console.log( - '# commander.js' + '\n' + - '# -----------' + '\n' + - '# Module is part of Cloud Commander,' + '\n' + - '# used for getting dir content.' + '\n' + - '# and forming html content' + '\n' + - '# http://coderaiser.github.com/cloudcmd' + '\n'); - - var main = global.cloudcmd.main, - fs = main.fs, - CloudFunc = main.cloudfunc, - DIR = main.DIR, - LIBDIR = main.LIBDIR, - HTMLDIR = main.HTMLDIR, - Util = main.util, - url = main.url, - querystring = main.querystring, - zlib = main.zlib, - - FILE_NOT_FOUND = 404, - OK = 200, - - FS = CloudFunc.FS, - NO_JS = CloudFunc.NOJS, - - INDEX = null, - REQUEST = 'request', - RESPONSE = 'response', - - IndexProcessingFunc = null; - - - exports.sendContent = function(pParams){ - var lRet = Util.checkObj(pParams, - ['processing'], - [REQUEST, RESPONSE, 'index']); - - if(lRet){ - var p = pParams, - lPath = getCleanPath(p.request); - - INDEX = p.index; - IndexProcessingFunc = pParams.processing; - - fs.stat(lPath, function(pError, pStat){ - if(!pError) - if(pStat.isDirectory()) - fs.readdir(lPath, Util.call(readDir, pParams) ); - else - main.sendFile({ - name : lPath, - request : p.request, - response : p.response - }); - else - sendResponse({ - status : FILE_NOT_FOUND, - data : pError.toString(), - request : p.request, - response : p.response - }); - }); - - lRet = true; - } - - return lRet; - }; - - - /** - * Функция читает ссылку или выводит информацию об ошибке - * @param pError - * @param pFiles - */ - function readDir(pParams){ - var lRet, - lError, lFiles, lHTTP, - lReq, lRes; - - if(pParams){ - lError = pParams.error; - lFiles = pParams.data; - lHTTP = pParams.params; - - if(lHTTP){ - lReq = lHTTP.request, - lRes = lHTTP.response; - lRet = true; - } - } - - if(lRet && !lError && lFiles){ - lFiles = lFiles.sort(); - - /* Получаем информацию о файлах */ - var n = lFiles.length, - lStats = {}, - lFilesData = { - files : lFiles, - stats : lStats, - request : lReq, - response : lRes - }, - - lFill = function(){ - fillJSON(lFilesData); - }; - - if(n){ - var i, lDirPath = getDirPath(lReq); - - for(i = 0; i < n; i++){ - var lName = lDirPath + lFiles[i], - lParams = { - callback : lFill, - count : n, - name : lFiles[i], - stats : lStats, - }; - - fs.stat( lName, Util.call(getFilesStat, lParams) ); - } - } - else - fillJSON(lFilesData); - } - else - sendResponse({ - status : FILE_NOT_FOUND, - data : lError.toString(), - request : lReq, - response: lRes - }); - } - - /** - * async getting file states - * and putting it to lStats object - */ - function getFilesStat(pParams){ - var lRet = Util.checkObj(pParams, - ['error', 'data', 'params']) && - - Util.checkObj(pParams.params, - ['callback'], ['stats', 'name', 'count']); - - if(lRet){ - var p = pParams, - c = p.params; - - if(c.stats) - c.stats[c.name] = !p.error ? p.data : { - 'mode' : 0, - 'size' : 0, - 'isDirectory' : Util.retFalse - }; - - if(c.count === Object.keys(c.stats).length) - Util.exec(c.callback); - } - } - - /** - * Function fill JSON by file stats - * - * @param pStats - object, contain file stats. - * example {'1.txt': stat} - * - * @param pFiles - array of files of current directory - */ - function fillJSON(pParams){ - var lFiles, lAllStats, - i, n, lReq, lRes; - - if(pParams){ - lFiles = pParams.files; - lFiles &&(n = lFiles.length || 0); - lAllStats = pParams.stats; - lReq = pParams.request, - lRes = pParams.response; - } - /* данные о файлах в формате JSON*/ - var lJSON = [], - lJSONFile = {}, - lList; - - lJSON[0] = { - path : getDirPath(lReq), - size : 'dir' - }; - - var lName, lStats, lMode, lIsDir; - for( i = 0; i < n; i++ ){ - /* Переводим права доступа в 8-ричную систему */ - lName = lFiles[i], - lStats = lAllStats[lName]; - - if(lStats){ - lMode = (lStats.mode - 0).toString(8), - lIsDir = lStats.isDirectory(); - } - - /* Если папка - выводим пиктограмму папки * - * В противоположном случае - файла */ - lJSONFile = { - 'name' : lFiles[i], - 'size' : lIsDir ? 'dir' : lStats.size, - 'uid' : lStats.uid, - 'mode' : lMode - }; - - lJSON[i+1] = lJSONFile; - } - - /* если js недоступен - * или отключен выcылаем html-код - * и прописываем соответствующие заголовки - */ - if( noJS(lReq) ){ - var lPanel = CloudFunc.buildFromJSON(lJSON); - lList = '
      ' + lPanel + '
    ' + - ''; - - fs.readFile(INDEX, Util.call(readFile, { - list : lList, - request : lReq, - response : lRes - }) - ); - - }else{ - lDirPath = lDirPath.substr(lDirPath, lDirPath.lastIndexOf('/') ); - - /* в обычном режиме(когда js включен - * высылаем json-структуру файлов - * с соответствующими заголовками - */ - lList = JSON.stringify(lJSON); - - sendResponse({ - name : lDirPath + '.json', - data : lList, - request : lReq, - response: lRes - }); - - } - } - - - /** - * Функция генерирует функцию считывания файла - * таким образом, что бы у нас было - * имя считываемого файла - * @param pName - полное имя файла - */ - function readFile(pParams){ - var lRet = Util.checkObj(pParams, - ['error', 'data', 'params']) && - - Util.checkObjTrue(pParams.params, - [REQUEST, RESPONSE, 'list']); - - if(lRet){ - var p = pParams, - c = pParams.params; - - p.data = p.data.toString(); - - var lParams = { - request : c.request, - response: c.response - }, - - lProccessed = Util.exec(IndexProcessingFunc, { - data : p.data, - additional : c.list - }); - - if(lProccessed) - p.data = lProccessed; - - if (!p.Error){ - lParams.name = INDEX; - lParams.data = p.data; - - Util.log('file ' + c.name + ' readed'); - } - else{ - lParams.status = FILE_NOT_FOUND; - lParams.data = p.error.toString(); - } - - sendResponse(lParams); - } - } - /** - * Функция высылает ответ серверу - * @param pHead - заголовок - * @param Data - данные - * @param pName - имя отсылаемого файла - */ - function sendResponse(pParams){ - var lRet = Util.checkObjTrue(pParams, - ['name', 'data', REQUEST, RESPONSE]); - - if(lRet){ - var p = pParams; - - var lPath = p.name || getCleanPath(p.request), - lQuery = getQuery(p.request), - /* download, json */ - lGzip = isGZIP(p.request), - lHead = main.generateHeaders(lPath, lGzip, lQuery); - - /* если браузер поддерживает gzip-сжатие - сжимаем данные*/ - Util.ifExec(!lGzip, - function(pParams){ - var lRet = Util.checkObj(pParams, ['data']); - - if(lRet){ - p.status = pParams.status; - p.data = pParams.data; - } - - p.response.writeHead(p.status || OK, lHead); - p.response.end(p.data); - - Util.log(lPath + ' sended'); - Util.log( p.status === FILE_NOT_FOUND && p.data ); - }, - - function(pCallBack){ - zlib.gzip (p.data, Util.call(gzipData, { - callback : pCallBack - })); - }); - } - } - - /** - * Функция получает сжатые данные - * @param pHeader - заголовок файла - * @pName - */ - function gzipData(pParams){ - var lRet = Util.checkObj(pParams, - ['error', 'data', 'params']); - - if(lRet) - lRet = Util.checkObj(pParams.params, - ['callback']); - - if(lRet){ - var p = pParams, - c = pParams.params, - lParams = {}; - - if(!p.error) - lParams.data = p.data; - else{ - lParams.status = FILE_NOT_FOUND; - lParams.data = p.error.toString(); - } - - Util.exec(c.callback, lParams); - } - } - - function getQuery(pReq){ - var lQuery, lParsedUrl; - - if(pReq){ - lParsedUrl = url.parse(pReq.url); - lQuery = lParsedUrl.query; - } - - return lQuery; - } - - function getPath(pReq){ - var lParsedUrl = url.parse(pReq.url), - lPath = lParsedUrl.pathname; - - lPath = querystring.unescape(lPath); - return lPath; - } - - - function getCleanPath(pReq){ - var lPath = getPath(pReq), - lRet = Util.removeStr(lPath, [NO_JS, FS]) || main.SLASH; - - return lRet; - } - - function getDirPath(pReq){ - var lRet = getCleanPath(pReq); - - if(lRet !== '/') - lRet += '/'; - - return lRet; - } - - function noJS(pReq){ - var lNoJS, lPath; - - if(pReq){ - lPath = getPath(pReq); - - lNoJS = Util.isContainStr(lPath, NO_JS) - || lPath === '/' || getQuery() == 'json'; - } - - return lNoJS; - } - - function isGZIP(pReq){ - var lEnc, lGZIP; - if(pReq){ - lEnc = pReq.headers['accept-encoding'] || '', - lGZIP = lEnc.match(/\bgzip\b/); - } - - return lGZIP; - } - +(function(){ + 'use strict'; + + if(!global.cloudcmd) + return console.log( + '# commander.js' + '\n' + + '# -----------' + '\n' + + '# Module is part of Cloud Commander,' + '\n' + + '# used for getting dir content.' + '\n' + + '# and forming html content' + '\n' + + '# http://coderaiser.github.com/cloudcmd' + '\n'); + + var main = global.cloudcmd.main, + fs = main.fs, + CloudFunc = main.cloudfunc, + DIR = main.DIR, + LIBDIR = main.LIBDIR, + HTMLDIR = main.HTMLDIR, + Util = main.util, + url = main.url, + querystring = main.querystring, + zlib = main.zlib, + + FILE_NOT_FOUND = 404, + OK = 200, + + FS = CloudFunc.FS, + NO_JS = CloudFunc.NOJS, + + INDEX = null, + REQUEST = 'request', + RESPONSE = 'response', + + IndexProcessingFunc = null; + + + exports.sendContent = function(pParams){ + var lRet = Util.checkObj(pParams, + ['processing'], + [REQUEST, RESPONSE, 'index']); + + if(lRet){ + var p = pParams, + lPath = getCleanPath(p.request); + + INDEX = p.index; + IndexProcessingFunc = pParams.processing; + + fs.stat(lPath, function(pError, pStat){ + if(!pError) + if(pStat.isDirectory()) + fs.readdir(lPath, Util.call(readDir, pParams) ); + else + main.sendFile({ + name : lPath, + request : p.request, + response : p.response + }); + else + sendResponse({ + status : FILE_NOT_FOUND, + data : pError.toString(), + request : p.request, + response : p.response + }); + }); + + lRet = true; + } + + return lRet; + }; + + + /** + * Функция читает ссылку или выводит информацию об ошибке + * @param pError + * @param pFiles + */ + function readDir(pParams){ + var lRet, + lError, lFiles, lHTTP, + lReq, lRes; + + if(pParams){ + lError = pParams.error; + lFiles = pParams.data; + lHTTP = pParams.params; + + if(lHTTP){ + lReq = lHTTP.request, + lRes = lHTTP.response; + lRet = true; + } + } + + if(lRet && !lError && lFiles){ + lFiles = lFiles.sort(); + + /* Получаем информацию о файлах */ + var n = lFiles.length, + lStats = {}, + lFilesData = { + files : lFiles, + stats : lStats, + request : lReq, + response : lRes + }, + + lFill = function(){ + fillJSON(lFilesData); + }; + + if(n){ + var i, lDirPath = getDirPath(lReq); + + for(i = 0; i < n; i++){ + var lName = lDirPath + lFiles[i], + lParams = { + callback : lFill, + count : n, + name : lFiles[i], + stats : lStats, + }; + + fs.stat( lName, Util.call(getFilesStat, lParams) ); + } + } + else + fillJSON(lFilesData); + } + else + sendResponse({ + status : FILE_NOT_FOUND, + data : lError.toString(), + request : lReq, + response: lRes + }); + } + + /** + * async getting file states + * and putting it to lStats object + */ + function getFilesStat(pParams){ + var lRet = Util.checkObj(pParams, + ['error', 'data', 'params']) && + + Util.checkObj(pParams.params, + ['callback'], ['stats', 'name', 'count']); + + if(lRet){ + var p = pParams, + c = p.params; + + if(c.stats) + c.stats[c.name] = !p.error ? p.data : { + 'mode' : 0, + 'size' : 0, + 'isDirectory' : Util.retFalse + }; + + if(c.count === Object.keys(c.stats).length) + Util.exec(c.callback); + } + } + + /** + * Function fill JSON by file stats + * + * @param pStats - object, contain file stats. + * example {'1.txt': stat} + * + * @param pFiles - array of files of current directory + */ + function fillJSON(pParams){ + var lFiles, lAllStats, + i, n, lReq, lRes; + + if(pParams){ + lFiles = pParams.files; + lFiles &&(n = lFiles.length || 0); + lAllStats = pParams.stats; + lReq = pParams.request, + lRes = pParams.response; + } + /* данные о файлах в формате JSON*/ + var lJSON = [], + lJSONFile = {}, + lList, + lDirPath = getDirPath(lReq); + + lJSON[0] = { + path : lDirPath, + size : 'dir' + }; + + var lName, lStats, lMode, lIsDir; + for( i = 0; i < n; i++ ){ + /* Переводим права доступа в 8-ричную систему */ + lName = lFiles[i], + lStats = lAllStats[lName]; + + if(lStats){ + lMode = (lStats.mode - 0).toString(8), + lIsDir = lStats.isDirectory(); + } + + /* Если папка - выводим пиктограмму папки * + * В противоположном случае - файла */ + lJSONFile = { + 'name' : lFiles[i], + 'size' : lIsDir ? 'dir' : lStats.size, + 'uid' : lStats.uid, + 'mode' : lMode + }; + + lJSON[i+1] = lJSONFile; + } + + /* если js недоступен + * или отключен выcылаем html-код + * и прописываем соответствующие заголовки + */ + if( noJS(lReq) ){ + var lPanel = CloudFunc.buildFromJSON(lJSON); + lList = '
      ' + lPanel + '
    ' + + ''; + + fs.readFile(INDEX, Util.call(readFile, { + list : lList, + request : lReq, + response : lRes + }) + ); + + }else{ + lDirPath = lDirPath.substr(lDirPath, lDirPath.lastIndexOf('/') ); + + /* в обычном режиме(когда js включен + * высылаем json-структуру файлов + * с соответствующими заголовками + */ + lList = JSON.stringify(lJSON); + + sendResponse({ + name : lDirPath + '.json', + data : lList, + request : lReq, + response: lRes + }); + + } + } + + + /** + * Функция генерирует функцию считывания файла + * таким образом, что бы у нас было + * имя считываемого файла + * @param pName - полное имя файла + */ + function readFile(pParams){ + var lRet = Util.checkObj(pParams, + ['error', 'data', 'params']) && + + Util.checkObjTrue(pParams.params, + [REQUEST, RESPONSE, 'list']); + + if(lRet){ + var p = pParams, + c = pParams.params; + + p.data = p.data.toString(); + + var lParams = { + request : c.request, + response: c.response + }, + + lProccessed = Util.exec(IndexProcessingFunc, { + data : p.data, + additional : c.list + }); + + if(lProccessed) + p.data = lProccessed; + + if (!p.Error){ + lParams.name = INDEX; + lParams.data = p.data; + + Util.log('file ' + c.name + ' readed'); + } + else{ + lParams.status = FILE_NOT_FOUND; + lParams.data = p.error.toString(); + } + + sendResponse(lParams); + } + } + /** + * Функция высылает ответ серверу + * @param pHead - заголовок + * @param Data - данные + * @param pName - имя отсылаемого файла + */ + function sendResponse(pParams){ + var lRet = Util.checkObjTrue(pParams, + ['name', 'data', REQUEST, RESPONSE]); + + if(lRet){ + var p = pParams; + + var lPath = p.name || getCleanPath(p.request), + lQuery = getQuery(p.request), + /* download, json */ + lGzip = isGZIP(p.request), + lHead = main.generateHeaders(lPath, lGzip, lQuery); + + /* если браузер поддерживает gzip-сжатие - сжимаем данные*/ + Util.ifExec(!lGzip, + function(pParams){ + var lRet = Util.checkObj(pParams, ['data']); + + if(lRet){ + p.status = pParams.status; + p.data = pParams.data; + } + + p.response.writeHead(p.status || OK, lHead); + p.response.end(p.data); + + Util.log(lPath + ' sended'); + Util.log( p.status === FILE_NOT_FOUND && p.data ); + }, + + function(pCallBack){ + zlib.gzip (p.data, Util.call(gzipData, { + callback : pCallBack + })); + }); + } + } + + /** + * Функция получает сжатые данные + * @param pHeader - заголовок файла + * @pName + */ + function gzipData(pParams){ + var lRet = Util.checkObj(pParams, + ['error', 'data', 'params']); + + if(lRet) + lRet = Util.checkObj(pParams.params, + ['callback']); + + if(lRet){ + var p = pParams, + c = pParams.params, + lParams = {}; + + if(!p.error) + lParams.data = p.data; + else{ + lParams.status = FILE_NOT_FOUND; + lParams.data = p.error.toString(); + } + + Util.exec(c.callback, lParams); + } + } + + function getQuery(pReq){ + var lQuery, lParsedUrl; + + if(pReq){ + lParsedUrl = url.parse(pReq.url); + lQuery = lParsedUrl.query; + } + + return lQuery; + } + + function getPath(pReq){ + var lParsedUrl = url.parse(pReq.url), + lPath = lParsedUrl.pathname; + + lPath = querystring.unescape(lPath); + return lPath; + } + + + function getCleanPath(pReq){ + var lPath = getPath(pReq), + lRet = Util.removeStr(lPath, [NO_JS, FS]) || main.SLASH; + + return lRet; + } + + function getDirPath(pReq){ + var lRet = getCleanPath(pReq); + + if(lRet !== '/') + lRet += '/'; + + return lRet; + } + + function noJS(pReq){ + var lNoJS, lPath; + + if(pReq){ + lPath = getPath(pReq); + + lNoJS = Util.isContainStr(lPath, NO_JS) + || lPath === '/' || getQuery() == 'json'; + } + + return lNoJS; + } + + function isGZIP(pReq){ + var lEnc, lGZIP; + if(pReq){ + lEnc = pReq.headers['accept-encoding'] || '', + lGZIP = lEnc.match(/\bgzip\b/); + } + + return lGZIP; + } + })(); \ No newline at end of file From 3f3153ffa64d50f752680e8da623e125eeb4840c Mon Sep 17 00:00:00 2001 From: coderaiser Date: Thu, 7 Feb 2013 10:57:39 -0500 Subject: [PATCH 230/347] minor changes --- json/config.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/json/config.json b/json/config.json index e53c7ca4..609d9776 100644 --- a/json/config.json +++ b/json/config.json @@ -2,7 +2,7 @@ "api_url" : "/api/v1", "appcache" : false, "minification" : { - "js" : true, + "js" : false, "css" : true, "html" : true, "img" : true From 749011f24f64ac75a1b0deb2470da4a8a00982de Mon Sep 17 00:00:00 2001 From: coderaiser Date: Thu, 7 Feb 2013 11:07:10 -0500 Subject: [PATCH 231/347] fixed bug with dragstart --- lib/client.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/client.js b/lib/client.js index cc74fead..beb3518d 100644 --- a/lib/client.js +++ b/lib/client.js @@ -500,7 +500,7 @@ CloudCmd._changeLinks = function(pPanelID){ else { DOM.addClickListener( DOM.preventDefault, lLi); DOM.addListener('mousedown', lSetCurrentFile_f, lLi); - DOM.addListener('ondragstart', lOnDragStart_f, ai); + DOM.addListener('dragstart', lOnDragStart_f, ai); /* if right button clicked menu will * loads and shows */ From 8907b11fb83bfaf2da79782aae5c166151b48214 Mon Sep 17 00:00:00 2001 From: coderaiser Date: Thu, 7 Feb 2013 11:19:11 -0500 Subject: [PATCH 232/347] fixed bug with download from menu --- lib/server/commander.js | 14 ++------------ lib/server/main.js | 20 ++++++++++++++++---- 2 files changed, 18 insertions(+), 16 deletions(-) diff --git a/lib/server/commander.js b/lib/server/commander.js index 40c04394..41ef16cc 100644 --- a/lib/server/commander.js +++ b/lib/server/commander.js @@ -314,7 +314,7 @@ var p = pParams; var lPath = p.name || getCleanPath(p.request), - lQuery = getQuery(p.request), + lQuery = main.getQuery(p.request), /* download, json */ lGzip = isGZIP(p.request), lHead = main.generateHeaders(lPath, lGzip, lQuery); @@ -373,16 +373,6 @@ } } - function getQuery(pReq){ - var lQuery, lParsedUrl; - - if(pReq){ - lParsedUrl = url.parse(pReq.url); - lQuery = lParsedUrl.query; - } - - return lQuery; - } function getPath(pReq){ var lParsedUrl = url.parse(pReq.url), @@ -416,7 +406,7 @@ lPath = getPath(pReq); lNoJS = Util.isContainStr(lPath, NO_JS) - || lPath === '/' || getQuery() == 'json'; + || lPath === '/' || main.getQuery() == 'json'; } return lNoJS; diff --git a/lib/server/main.js b/lib/server/main.js index f385596d..24ea68f1 100644 --- a/lib/server/main.js +++ b/lib/server/main.js @@ -10,7 +10,7 @@ SLASH, ISWIN32, ext, - path, fs, zlib, + path, fs, zlib, url, OK = 200, FILE_NOT_FOUND = 404; @@ -22,7 +22,7 @@ exports.http = require('http'), exports.https = require('https'), exports.path = path = require('path'), - exports.url = require('url'), + exports.url = url = require('url'), exports.querystring = require('querystring'), /* Constants */ @@ -42,6 +42,7 @@ exports.srvrequire = srvrequire, exports.rootrequire = rootrequire, exports.generateHeaders = generateHeaders, + exports.getQuery = getQuery, exports.sendFile = sendFile, /* compitability with old versions of node */ @@ -186,12 +187,13 @@ */ function sendFile(pParams){ var lRet, - lName, lReq, lRes; + lName, lReq, lRes, lQuery; if(pParams){ lName = pParams.name, lReq = pParams.request, lRes = pParams.response; + lQuery = getQuery(lReq); } if(lName && lRes && lReq){ @@ -207,7 +209,7 @@ lRes.end(String(pError)); }); - lRes.writeHead(OK, generateHeaders(lName, lGzip) ); + lRes.writeHead(OK, generateHeaders(lName, lGzip, lQuery ) ); if (lGzip) lReadStream = lReadStream.pipe( zlib.createGzip() ); @@ -220,5 +222,15 @@ return lRet; } + function getQuery(pReq){ + var lQuery, lParsedUrl; + + if(pReq){ + lParsedUrl = url.parse(pReq.url); + lQuery = lParsedUrl.query; + } + + return lQuery; + } })(); From 8c4fb9c4ab4c3fa11f9c71de1b791670bcd1ded4 Mon Sep 17 00:00:00 2001 From: coderaiser Date: Fri, 8 Feb 2013 03:38:45 -0500 Subject: [PATCH 233/347] minor changes --- lib/client/menu.js | 635 +++++++++++++++++++++++---------------------- 1 file changed, 318 insertions(+), 317 deletions(-) diff --git a/lib/client/menu.js b/lib/client/menu.js index e331db0e..6ec4daac 100644 --- a/lib/client/menu.js +++ b/lib/client/menu.js @@ -1,318 +1,319 @@ -/* object contains jQuery-contextMenu - * https://github.com/medialize/jQuery-contextMenu - */ -var CloudCommander, Util, DOM, $; -(function(CloudCmd, Util, DOM){ - 'use strict'; - - var KeyBinding = CloudCmd.KeyBinding, - MenuSeted = false, - Menu = {}, - Position, - UploadToItemNames; - - Menu.dir = '/lib/client/menu/'; - - /* enable and disable menu constant */ - Menu.ENABLED = false; - - /* PRIVATE FUNCTIONS */ - - /** function shows editor - * @param pReadOnly - */ - function showEditor(pReadOnly){ - DOM.Images.showLoad(); - var lEditor = CloudCmd[pReadOnly ? 'Viewer' : 'Editor'], - - lResult = Util.exec(lEditor, pReadOnly); - - if(!lResult){ - lEditor = lEditor.get(); - if(lEditor) - Util.exec(lEditor.show); - } - } - - /* function read data from modules.json - * and build array of menu items of "upload to" - * menu - */ - function setUploadToItemNames(pCallBack){ - CloudCmd.getModules(function(pModules){ - var lStorageObj = Util.findObjByNameInArr( pModules, 'storage' ); - UploadToItemNames = Util.getNamesFromObjArray( lStorageObj ) || []; - - Util.exec(pCallBack); - }); - } - - /** - * function get menu item object for Upload To - */ - function getUploadToItems(pObjectName){ - var lObj = {}; - if( Util.isArray(pObjectName) ){ - - var n = pObjectName.length; - for(var i = 0; i < n; i++){ - var lStr = pObjectName[i]; - lObj[lStr] = getUploadToItems( lStr ); - } - } - else if( Util.isString(pObjectName) ){ - lObj.name = pObjectName; - - lObj.callback = function(key, opt){ - DOM.getCurrentData(function(pParams){ - var lObject = CloudCmd[pObjectName]; - - Util.ifExec('init' in lObject, - function(){ - CloudCmd[pObjectName].uploadFile(pParams); - }, - - function(pCallBack){ - Util.exec(lObject, pCallBack); - }); - }); - - Util.log('Uploading to ' + pObjectName+ '...'); - }; - } - - return lObj; - } - - /** - * get menu item - */ - function getItem(pName, pCallBack){ - var lRet = { - name : pName - }; - - if(Util.isFunction(pCallBack) ) - lRet.callback = pCallBack; - - else if (Util.isObject(pCallBack)) - lRet.items = pCallBack; - - return lRet; - } - - /** - * get all menu items - * pItems = [{pName, pFunc}] - */ - function getAllItems(pItems){ - var lRet = {}, - lName, - lFunc; - - if(pItems) - for(lName in pItems){ - lFunc = pItems[lName]; - lRet[lName] = getItem(lName, lFunc); - } - - return lRet; - } - - /** - * download menu item callback - */ - function downloadFromMenu(key, opt){ - DOM.Images.showLoad(); - - var lPath = DOM.getCurrentPath(), - lId = DOM.getIdBySrc(lPath); - - Util.log('downloading file ' + lPath +'...'); - - lPath = lPath + '?download'; - - if(!DOM.getById(lId)){ - var lDownload = DOM.anyload({ - name : 'iframe', - async : false, - className : 'hidden', - src : lPath, - func : Util.retFunc(DOM.Images.hideLoad) - }); - - DOM.Images.hideLoad(); - setTimeout(function() { - document.body.removeChild(lDownload); - }, 10000); - } - else - DOM.Images.showError({ - responseText: 'Error: You trying to' + - 'download same file to often'}); - } - - /** - * function return configureation for menu - */ - function getConfig (){ - var lRet, - lMenuItems = { - 'View' : Util.retExec(showEditor, true), - 'Edit' : Util.retExec(showEditor, false), - 'Delete' : Util.retExec(DOM.promptDeleteCurrent), - }; - - if(UploadToItemNames.length) - lMenuItems['Upload to'] = getUploadToItems(UploadToItemNames); - - lMenuItems.Download = Util.retExec(downloadFromMenu); - - lRet = { - // define which elements trigger this menu - selector: 'li', - - callback: function(key, options) { - var m = "clicked: " + key; - Util.log(m, options); - - KeyBinding.set(); - }, - - // define the elements of the menu - items: getAllItems(lMenuItems) - }; - - return lRet; - } - - /** function loads css and js of Menu - * @param pCallBack - */ - function load(pCallBack){ - console.time('menu load'); - - var lDir = Menu.dir, - lFiles = [ - lDir + 'contextMenu.js', - lDir + 'contextMenu.css' - ]; - - DOM.anyLoadInParallel(lFiles, function(){ - console.timeEnd('menu load'); - $.contextMenu.handle.keyStop = $.noop; - Util.exec(pCallBack); - }); - } - - function set(){ - if(!MenuSeted){ - $.contextMenu(getConfig()); - /* - * Menu works in some crazy way so need a - * little hack to get every thing work out. - * When menu shows up, it drawing invisible - * layer wich hides all elements of - * Cloud Commander so it could not handle - * onclick events. To get every thing work - * how expected we hide out invisible layer - * so for observer it is nothing special - * is not going on. All magic happening in - * DOM tree - */ - DOM.addClickListener(function(pEvent){ - /* if clicked on menu item */ - var lClassName = pEvent.target.parentElement.className; - switch(lClassName){ - case 'context-menu-item': - return; - case 'context-menu-list ': - return; - } - - if(pEvent && pEvent.x){ - var lLayer = DOM.getById('context-menu-layer'); - if(lLayer){ - var lStyle; - - if(lLayer) - lStyle = lLayer.style.cssText; - /* hide invisible menu layer */ - if(lStyle) - lLayer.style.cssText = lStyle - .replace('z-index: 1', 'z-index:-1'); - - /* get element by point */ - var lElement = document.elementFromPoint(pEvent.x, pEvent.y), - lTag = lElement.tagName, - lParent; - - if(lTag === 'A' || lTag === 'SPAN'){ - if (lElement.tagName === 'A') - lParent = lElement.parentElement.parentElement; - else if(lElement.tagName === 'SPAN') - lParent = lElement.parentElement; - - if(lParent.className === '') - DOM.setCurrentFile(lParent); - } - - /* show invisible menu layer */ - if(lLayer && lStyle) - lLayer.style.cssText = lStyle; - - KeyBinding.set(); - } - } - }); - - MenuSeted = true; - } - } - - - /** function shows menu for the first time - * right away after loading - */ - Menu.show = function(){ - set(); - DOM.Images.hideLoad(); - - if(Position && !Position.x ) - Position = null; - - $('li').contextMenu(Position); - }; - - /* key binding function */ - Menu.init = function(pPosition){ - Position = pPosition; - - Util.loadOnLoad([ - Menu.show, - setUploadToItemNames, - load, - DOM.jqueryLoad - ]); - - var key_event = function(pEvent){ - var lKEY = CloudCmd.KEY, - lKeyCode = pEvent.keyCode; - /* если клавиши можно обрабатывать */ - if( KeyBinding.get() ){ - /* if shift + F10 pressed */ - if(lKeyCode === lKEY.F10 && pEvent.shiftKey){ - $( DOM.getCurrentFile() ).contextMenu(); - DOM.preventDefault(pEvent); - } - } - else if (lKeyCode === lKEY.ESC) - KeyBinding.set(); - }; - - /* добавляем обработчик клавишь */ - DOM.addKeyListener( key_event ); - }; - - CloudCmd.Menu = Menu; +/* object contains jQuery-contextMenu + * https://github.com/medialize/jQuery-contextMenu + */ +var CloudCommander, Util, DOM, $; +(function(CloudCmd, Util, DOM){ + 'use strict'; + + var KeyBinding = CloudCmd.KeyBinding, + MenuSeted = false, + Menu = {}, + Position, + UploadToItemNames; + + Menu.dir = '/lib/client/menu/'; + + /* enable and disable menu constant */ + Menu.ENABLED = false; + + /* PRIVATE FUNCTIONS */ + + /** function shows editor + * @param pReadOnly + */ + function showEditor(pReadOnly){ + DOM.Images.showLoad(); + var lEditor = CloudCmd[pReadOnly ? 'Viewer' : 'Editor'], + + lResult = Util.exec(lEditor, pReadOnly); + + if(!lResult){ + lEditor = lEditor.get(); + if(lEditor) + Util.exec(lEditor.show); + } + } + + /* function read data from modules.json + * and build array of menu items of "upload to" + * menu + */ + function setUploadToItemNames(pCallBack){ + CloudCmd.getModules(function(pModules){ + var lStorageObj = Util.findObjByNameInArr( pModules, 'storage' ); + UploadToItemNames = Util.getNamesFromObjArray( lStorageObj ) || []; + + Util.exec(pCallBack); + }); + } + + /** + * function get menu item object for Upload To + */ + function getUploadToItems(pObjectName){ + var lObj = {}; + if( Util.isArray(pObjectName) ){ + + var n = pObjectName.length; + for(var i = 0; i < n; i++){ + var lStr = pObjectName[i]; + lObj[lStr] = getUploadToItems( lStr ); + } + } + else if( Util.isString(pObjectName) ){ + lObj.name = pObjectName; + + lObj.callback = function(key, opt){ + DOM.getCurrentData(function(pParams){ + var lObject = CloudCmd[pObjectName]; + + Util.ifExec('init' in lObject, + function(){ + CloudCmd[pObjectName].uploadFile(pParams); + }, + + function(pCallBack){ + Util.exec(lObject, pCallBack); + }); + }); + + Util.log('Uploading to ' + pObjectName+ '...'); + }; + } + + return lObj; + } + + /** + * get menu item + */ + function getItem(pName, pCallBack){ + var lRet = { + name : pName + }; + + if(Util.isFunction(pCallBack) ) + lRet.callback = pCallBack; + + else if (Util.isObject(pCallBack)) + lRet.items = pCallBack; + + return lRet; + } + + /** + * get all menu items + * pItems = [{pName, pFunc}] + */ + function getAllItems(pItems){ + var lRet = {}, + lName, + lFunc; + + if(pItems) + for(lName in pItems){ + lFunc = pItems[lName]; + lRet[lName] = getItem(lName, lFunc); + } + + return lRet; + } + + /** + * download menu item callback + */ + function downloadFromMenu(key, opt){ + DOM.Images.showLoad(); + + var lPath = DOM.getCurrentPath(), + lId = DOM.getIdBySrc(lPath); + + Util.log('downloading file ' + lPath +'...'); + + lPath = lPath + '?download'; + + if(!DOM.getById(lId)){ + var lDownload = DOM.anyload({ + name : 'iframe', + async : false, + className : 'hidden', + src : lPath, + func : Util.retFunc(DOM.Images.hideLoad) + }); + + DOM.Images.hideLoad(); + setTimeout(function() { + document.body.removeChild(lDownload); + }, 10000); + } + else + DOM.Images.showError({ + responseText: 'Error: You trying to' + + 'download same file to often'}); + } + + /** + * function return configureation for menu + */ + function getConfig (){ + var lRet, + lMenuItems = { + 'View' : Util.retExec(showEditor, true), + 'Edit' : Util.retExec(showEditor, false), + 'Delete' : Util.retExec(DOM.promptDeleteCurrent), + }; + + if(UploadToItemNames.length) + lMenuItems['Upload to'] = getUploadToItems(UploadToItemNames); + + lMenuItems.Download = Util.retExec(downloadFromMenu); + + lRet = { + // define which elements trigger this menu + selector: 'li', + + callback: function(key, options) { + var m = "clicked: " + key; + Util.log(m, options); + + KeyBinding.set(); + }, + + // define the elements of the menu + items: getAllItems(lMenuItems) + }; + + return lRet; + } + + /** function loads css and js of Menu + * @param pCallBack + */ + function load(pCallBack){ + console.time('menu load'); + + var lDir = Menu.dir, + lFiles = [ + lDir + 'contextMenu.js', + lDir + 'contextMenu.css' + ]; + + DOM.anyLoadInParallel(lFiles, function(){ + console.timeEnd('menu load'); + $.contextMenu.handle.keyStop = $.noop; + Util.exec(pCallBack); + }); + } + + function set(){ + if(!MenuSeted){ + $.contextMenu(getConfig()); + /* + * Menu works in some crazy way so need a + * little hack to get every thing work out. + * When menu shows up, it drawing invisible + * layer wich hides all elements of + * Cloud Commander so it could not handle + * onclick events. To get every thing work + * how expected we hide out invisible layer + * so for observer it is nothing special + * is not going on. All magic happening in + * DOM tree + */ + DOM.addClickListener(function(pEvent){ + /* if clicked on menu item */ + var lParent = pEvent.target.parentElement, + lClassName = lParent && lParent.className; + switch(lClassName){ + case 'context-menu-item': + return; + case 'context-menu-list ': + return; + } + + if(pEvent && pEvent.x){ + var lLayer = DOM.getById('context-menu-layer'); + if(lLayer){ + var lStyle; + + if(lLayer) + lStyle = lLayer.style.cssText; + /* hide invisible menu layer */ + if(lStyle) + lLayer.style.cssText = lStyle + .replace('z-index: 1', 'z-index:-1'); + + /* get element by point */ + var lElement = document.elementFromPoint(pEvent.x, pEvent.y), + lTag = lElement.tagName, + lParent; + + if(lTag === 'A' || lTag === 'SPAN'){ + if (lElement.tagName === 'A') + lParent = lElement.parentElement.parentElement; + else if(lElement.tagName === 'SPAN') + lParent = lElement.parentElement; + + if(lParent.className === '') + DOM.setCurrentFile(lParent); + } + + /* show invisible menu layer */ + if(lLayer && lStyle) + lLayer.style.cssText = lStyle; + + KeyBinding.set(); + } + } + }); + + MenuSeted = true; + } + } + + + /** function shows menu for the first time + * right away after loading + */ + Menu.show = function(){ + set(); + DOM.Images.hideLoad(); + + if(Position && !Position.x ) + Position = null; + + $('li').contextMenu(Position); + }; + + /* key binding function */ + Menu.init = function(pPosition){ + Position = pPosition; + + Util.loadOnLoad([ + Menu.show, + setUploadToItemNames, + load, + DOM.jqueryLoad + ]); + + var key_event = function(pEvent){ + var lKEY = CloudCmd.KEY, + lKeyCode = pEvent.keyCode; + /* если клавиши можно обрабатывать */ + if( KeyBinding.get() ){ + /* if shift + F10 pressed */ + if(lKeyCode === lKEY.F10 && pEvent.shiftKey){ + $( DOM.getCurrentFile() ).contextMenu(); + DOM.preventDefault(pEvent); + } + } + else if (lKeyCode === lKEY.ESC) + KeyBinding.set(); + }; + + /* добавляем обработчик клавишь */ + DOM.addKeyListener( key_event ); + }; + + CloudCmd.Menu = Menu; })(CloudCommander, Util, DOM); \ No newline at end of file From f8e47930bdf55b68b579bb9c2827fecb37018e9f Mon Sep 17 00:00:00 2001 From: coderaiser Date: Fri, 8 Feb 2013 03:59:20 -0500 Subject: [PATCH 234/347] minor changes --- lib/server/commander.js | 18 +++++++++--------- lib/util.js | 5 ++++- 2 files changed, 13 insertions(+), 10 deletions(-) diff --git a/lib/server/commander.js b/lib/server/commander.js index 41ef16cc..a5ce697e 100644 --- a/lib/server/commander.js +++ b/lib/server/commander.js @@ -58,6 +58,7 @@ }); else sendResponse({ + name : lPath, status : FILE_NOT_FOUND, data : pError.toString(), request : p.request, @@ -79,7 +80,7 @@ */ function readDir(pParams){ var lRet, - lError, lFiles, lHTTP, + lError, lFiles, lHTTP, lDirPath, lReq, lRes; if(pParams){ @@ -91,6 +92,7 @@ lReq = lHTTP.request, lRes = lHTTP.response; lRet = true; + lDirPath = getDirPath(lReq); } } @@ -112,9 +114,7 @@ }; if(n){ - var i, lDirPath = getDirPath(lReq); - - for(i = 0; i < n; i++){ + for(var i = 0; i < n; i++){ var lName = lDirPath + lFiles[i], lParams = { callback : lFill, @@ -131,10 +131,11 @@ } else sendResponse({ - status : FILE_NOT_FOUND, data : lError.toString(), + name : lDirPath, request : lReq, - response: lRes + response: lRes, + status : FILE_NOT_FOUND }); } @@ -279,17 +280,16 @@ }, lProccessed = Util.exec(IndexProcessingFunc, { + additional : c.list, data : p.data, - additional : c.list + name : INDEX }); if(lProccessed) p.data = lProccessed; if (!p.Error){ - lParams.name = INDEX; lParams.data = p.data; - Util.log('file ' + c.name + ' readed'); } else{ diff --git a/lib/util.js b/lib/util.js index e84f168e..aa72b706 100644 --- a/lib/util.js +++ b/lib/util.js @@ -135,8 +135,11 @@ Util = exports || {}; var lProp = pTrueArr[i]; lRet = pObj[lProp]; - if( !lRet) + if( !lRet){ + Util.logError(lProp + ' not true!'); + Util.log(pObj); break; + } } } } From 7f87bf8a74b19b4ce093e00e4d0ab1a4d30096f0 Mon Sep 17 00:00:00 2001 From: coderaiser Date: Fri, 8 Feb 2013 04:05:35 -0500 Subject: [PATCH 235/347] minor changes --- lib/server/commander.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/server/commander.js b/lib/server/commander.js index a5ce697e..a18d6b08 100644 --- a/lib/server/commander.js +++ b/lib/server/commander.js @@ -275,14 +275,14 @@ p.data = p.data.toString(); var lParams = { - request : c.request, - response: c.response + name : INDEX, + request : c.request, + response : c.response, }, lProccessed = Util.exec(IndexProcessingFunc, { additional : c.list, data : p.data, - name : INDEX }); if(lProccessed) From f984301464b49d41d3ce0e90681f10a2668d74b2 Mon Sep 17 00:00:00 2001 From: coderaiser Date: Fri, 8 Feb 2013 04:15:54 -0500 Subject: [PATCH 236/347] minor changes --- lib/server/commander.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/server/commander.js b/lib/server/commander.js index a18d6b08..f32d9931 100644 --- a/lib/server/commander.js +++ b/lib/server/commander.js @@ -325,7 +325,7 @@ var lRet = Util.checkObj(pParams, ['data']); if(lRet){ - p.status = pParams.status; + p.status = pParams.status || p.status; p.data = pParams.data; } From 9e41ed7df45973fa786c391cb4f721a9c631802c Mon Sep 17 00:00:00 2001 From: coderaiser Date: Fri, 8 Feb 2013 05:49:45 -0500 Subject: [PATCH 237/347] added appcache and woff properties --- json/ext.json | 4 +++- lib/client/dom.js | 20 +++++++++++++------- lib/util.js | 2 +- 3 files changed, 17 insertions(+), 9 deletions(-) diff --git a/json/ext.json b/json/ext.json index 86c87580..b22e0d20 100644 --- a/json/ext.json +++ b/json/ext.json @@ -4,6 +4,7 @@ ".ai": "application/postscript", ".aif": "audio/x-aiff", ".aiff": "audio/x-aiff", + ".appcache": "text/cache-manifest", ".asc": "application/pgp-signature", ".asf": "video/x-ms-asf", ".asm": "text/x-asm", @@ -68,7 +69,7 @@ ".json": "application/json", ".log": "text/plain", ".m3u": "audio/x-mpegurl", - ".m4v": "video/mp4", + ".m4v": "video/mp4", ".man": "text/troff", ".mathml": "application/mathml+xml", ".mbox": "application/mbox", @@ -152,6 +153,7 @@ ".wma": "audio/x-ms-wma", ".wmv": "video/x-ms-wmv", ".wmx": "video/x-ms-wmx", + ".woff": "font/woff", ".wrl": "model/vrml", ".wsdl": "application/wsdl+xml", ".xbm": "image/x-xbitmap", diff --git a/lib/client/dom.js b/lib/client/dom.js index 466e2286..6a720a60 100644 --- a/lib/client/dom.js +++ b/lib/client/dom.js @@ -387,15 +387,21 @@ var CloudCommander, Util, * Example: http://domain.com/1.js -> 1_js */ DOM.getIdBySrc = function(pSrc){ - var lID = pSrc.replace(pSrc.substr(pSrc, - pSrc.lastIndexOf('/')+1), - ''); + var lRet = Util.isString(pSrc); - /* убираем точки */ - while(lID.indexOf('.') > 0) - lID = lID.replace('.','_'); + if(lRet){ + var lNum = pSrc.lastIndexOf('/') + 1, + lSub = pSrc.substr(pSrc, lNum), + lID = Util.removeStr(pSrc, lSub ); + + /* убираем точки */ + while(lID.indexOf('.') > 0) + lID = lID.replace('.', '_'); + + lRet = lID; + } - return lID; + return lRet; }, /** diff --git a/lib/util.js b/lib/util.js index aa72b706..2f261ea3 100644 --- a/lib/util.js +++ b/lib/util.js @@ -310,7 +310,7 @@ Util = exports || {}; */ Util.removeStr = function(pStr, pSubStr){ var lRet; - if( Util.isString(pStr) ){ + if( Util.isString(pStr) && Util.isString(pSubStr) ){ var n = pSubStr.length; if( Util.isArray(pSubStr) ) From d758bebc7a2dfefb8d5540cd8700d8722deab563 Mon Sep 17 00:00:00 2001 From: coderaiser Date: Fri, 8 Feb 2013 06:00:11 -0500 Subject: [PATCH 238/347] minor changes --- lib/util.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/util.js b/lib/util.js index 2f261ea3..2f819bd5 100644 --- a/lib/util.js +++ b/lib/util.js @@ -310,7 +310,7 @@ Util = exports || {}; */ Util.removeStr = function(pStr, pSubStr){ var lRet; - if( Util.isString(pStr) && Util.isString(pSubStr) ){ + if( Util.isString(pStr) && pSubStr ){ var n = pSubStr.length; if( Util.isArray(pSubStr) ) From 84e14ce2ddfa56ab7f23946b801447e6277fac77 Mon Sep 17 00:00:00 2001 From: coderaiser Date: Fri, 8 Feb 2013 09:22:03 -0500 Subject: [PATCH 239/347] refactored --- lib/server/commander.js | 98 +++++++++++++++++++---------------------- lib/server/main.js | 12 ++--- 2 files changed, 52 insertions(+), 58 deletions(-) diff --git a/lib/server/commander.js b/lib/server/commander.js index f32d9931..44ec8a85 100644 --- a/lib/server/commander.js +++ b/lib/server/commander.js @@ -79,64 +79,58 @@ * @param pFiles */ function readDir(pParams){ - var lRet, - lError, lFiles, lHTTP, lDirPath, - lReq, lRes; - - if(pParams){ - lError = pParams.error; - lFiles = pParams.data; - lHTTP = pParams.params; - - if(lHTTP){ - lReq = lHTTP.request, - lRes = lHTTP.response; - lRet = true; - lDirPath = getDirPath(lReq); - } - } - - if(lRet && !lError && lFiles){ - lFiles = lFiles.sort(); - - /* Получаем информацию о файлах */ - var n = lFiles.length, - lStats = {}, - lFilesData = { - files : lFiles, - stats : lStats, - request : lReq, - response : lRes - }, + var lRet = Util.checkObj(pParams, + ['error', 'data', 'params']) && - lFill = function(){ - fillJSON(lFilesData); - }; - - if(n){ - for(var i = 0; i < n; i++){ - var lName = lDirPath + lFiles[i], - lParams = { - callback : lFill, - count : n, - name : lFiles[i], - stats : lStats, - }; + Util.checkObjTrue(pParams.params, + [REQUEST, RESPONSE]); + if(lRet){ + var p = pParams, + c = p.params, + lDirPath = getDirPath(c.request); + + if(!p.error && p.data){ + p.data = p.data.sort(); + + /* Получаем информацию о файлах */ + var n = p.data.length, + lStats = {}, + lFilesData = { + files : p.data, + stats : lStats, + request : c.request, + response : c.response + }, - fs.stat( lName, Util.call(getFilesStat, lParams) ); + lFill = function(){ + fillJSON(lFilesData); + }; + + if(n){ + for(var i = 0; i < n; i++){ + var lName = lDirPath + p.data[i], + lParams = { + callback : lFill, + count : n, + name : p.data[i], + stats : lStats, + }; + + fs.stat( lName, Util.call(getFilesStat, lParams) ); + } } + else + fillJSON(lFilesData); } else - fillJSON(lFilesData); + sendResponse({ + data : p.Error.toString(), + name : lDirPath, + request : c.request, + response: c.response, + status : FILE_NOT_FOUND + }); } - else - sendResponse({ - data : lError.toString(), - name : lDirPath, - request : lReq, - response: lRes, - status : FILE_NOT_FOUND - }); } /** diff --git a/lib/server/main.js b/lib/server/main.js index 24ea68f1..8d3cf430 100644 --- a/lib/server/main.js +++ b/lib/server/main.js @@ -150,6 +150,9 @@ lType = ext[lExt] || 'text/plain'; + /* if type of file any, but img - + * then we shoud specify charset + */ if( !Util.isContainStr(lType, 'img') ) lContentEncoding = '; charset=UTF-8'; @@ -159,16 +162,13 @@ 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(), + 'last-modified': new Date().toString(), /* https://developers.google.com/speed/docs/best-practices - /caching?hl=ru#LeverageProxyCaching */ + /caching?hl=ru#LeverageProxyCaching */ 'Vary': 'Accept-Encoding' }; From 696a4f3a70c447a42cfbee71d596dcb8381e9461 Mon Sep 17 00:00:00 2001 From: coderaiser Date: Fri, 8 Feb 2013 09:26:53 -0500 Subject: [PATCH 240/347] refactored --- lib/server/commander.js | 29 +++++++++++++++-------------- 1 file changed, 15 insertions(+), 14 deletions(-) diff --git a/lib/server/commander.js b/lib/server/commander.js index 44ec8a85..51d2b634 100644 --- a/lib/server/commander.js +++ b/lib/server/commander.js @@ -87,16 +87,17 @@ if(lRet){ var p = pParams, c = p.params, + lFiles = p.data, lDirPath = getDirPath(c.request); if(!p.error && p.data){ - p.data = p.data.sort(); + lFiles.data = lFiles.sort(); /* Получаем информацию о файлах */ - var n = p.data.length, + var n = lFiles.length, lStats = {}, lFilesData = { - files : p.data, + files : lFiles, stats : lStats, request : c.request, response : c.response @@ -108,11 +109,11 @@ if(n){ for(var i = 0; i < n; i++){ - var lName = lDirPath + p.data[i], + var lName = lDirPath + lFiles[i], lParams = { callback : lFill, count : n, - name : p.data[i], + name : lFiles[i], stats : lStats, }; @@ -256,11 +257,9 @@ * @param pName - полное имя файла */ function readFile(pParams){ - var lRet = Util.checkObj(pParams, - ['error', 'data', 'params']) && - - Util.checkObjTrue(pParams.params, - [REQUEST, RESPONSE, 'list']); + var lRet = checkParams(pParams) && + Util.checkObjTrue(pParams.params, + [REQUEST, RESPONSE, 'list']); if(lRet){ var p = pParams, @@ -344,12 +343,10 @@ * @pName */ function gzipData(pParams){ - var lRet = Util.checkObj(pParams, - ['error', 'data', 'params']); + var lRet = checkParams(pParams); if(lRet) - lRet = Util.checkObj(pParams.params, - ['callback']); + lRet = Util.checkObj(pParams.params, ['callback']); if(lRet){ var p = pParams, @@ -368,6 +365,10 @@ } + function checkParams(pParams){ + return Util.checkObj(pParams, ['error', 'data', 'params']); + } + function getPath(pReq){ var lParsedUrl = url.parse(pReq.url), lPath = lParsedUrl.pathname; From a99a99651811baaf9c492864db945648791c8eb8 Mon Sep 17 00:00:00 2001 From: coderaiser Date: Fri, 8 Feb 2013 10:16:47 -0500 Subject: [PATCH 241/347] refactored --- lib/server/commander.js | 24 +++++++++++++----------- 1 file changed, 13 insertions(+), 11 deletions(-) diff --git a/lib/server/commander.js b/lib/server/commander.js index 51d2b634..9a91aa49 100644 --- a/lib/server/commander.js +++ b/lib/server/commander.js @@ -79,10 +79,10 @@ * @param pFiles */ function readDir(pParams){ - var lRet = Util.checkObj(pParams, - ['error', 'data', 'params']) && - - Util.checkObjTrue(pParams.params, + var lRet = checkParams(pParams); + + if(lRet) + lRet = Util.checkObjTrue(pParams.params, [REQUEST, RESPONSE]); if(lRet){ var p = pParams, @@ -139,10 +139,10 @@ * and putting it to lStats object */ function getFilesStat(pParams){ - var lRet = Util.checkObj(pParams, - ['error', 'data', 'params']) && - - Util.checkObj(pParams.params, + var lRet = checkParams(pParams); + + if(lRet) + lRet = Util.checkObj(pParams.params, ['callback'], ['stats', 'name', 'count']); if(lRet){ @@ -245,7 +245,7 @@ request : lReq, response: lRes }); - + } } @@ -257,8 +257,10 @@ * @param pName - полное имя файла */ function readFile(pParams){ - var lRet = checkParams(pParams) && - Util.checkObjTrue(pParams.params, + var lRet = checkParams(pParams); + + if(lRet) + lRet = Util.checkObjTrue(pParams.params, [REQUEST, RESPONSE, 'list']); if(lRet){ From 0a7b095d996db6ecdf99920d2c7eec3cc0844d07 Mon Sep 17 00:00:00 2001 From: coderaiser Date: Fri, 8 Feb 2013 10:19:22 -0500 Subject: [PATCH 242/347] minor changes --- lib/server/commander.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/server/commander.js b/lib/server/commander.js index 9a91aa49..20085805 100644 --- a/lib/server/commander.js +++ b/lib/server/commander.js @@ -90,7 +90,7 @@ lFiles = p.data, lDirPath = getDirPath(c.request); - if(!p.error && p.data){ + if(!p.error && lFiles){ lFiles.data = lFiles.sort(); /* Получаем информацию о файлах */ From 9309ba1d1beea8a1ae9cc356e3053781dc031921 Mon Sep 17 00:00:00 2001 From: coderaiser Date: Fri, 8 Feb 2013 11:21:32 -0500 Subject: [PATCH 243/347] minor changes --- lib/client.js | 2 +- lib/client/dom.js | 4 ++ lib/client/menu.js | 121 +++++++++++++++++++++------------------- lib/server/commander.js | 6 +- lib/server/main.js | 94 ++++++++++++++++--------------- 5 files changed, 121 insertions(+), 106 deletions(-) diff --git a/lib/client.js b/lib/client.js index beb3518d..bd153f79 100644 --- a/lib/client.js +++ b/lib/client.js @@ -230,7 +230,7 @@ function initModules(pCallBack){ CloudCmd.getModules(function(pModules){ pModules = pModules || []; - DOM.addListener('contextmenu', function(pEvent){ + DOM.addContextMenuListener(function(pEvent){ CloudCmd.Menu.ENABLED || DOM.preventDefault(pEvent); }, document); diff --git a/lib/client/dom.js b/lib/client/dom.js index 6a720a60..d889539f 100644 --- a/lib/client/dom.js +++ b/lib/client/dom.js @@ -183,6 +183,10 @@ var CloudCommander, Util, return DOM.addListener('click', pListener, pElement, pUseCapture); }; + DOM.addContextMenuListener = function(pListener, pElement, pUseCapture){ + return DOM.addListener('contextmenu', pListener, pElement, pUseCapture); + }; + /** * safe add event click listener * @param pListener diff --git a/lib/client/menu.js b/lib/client/menu.js index 6ec4daac..3b6b7448 100644 --- a/lib/client/menu.js +++ b/lib/client/menu.js @@ -205,67 +205,72 @@ var CloudCommander, Util, DOM, $; }); } + /* + * Menu works in some crazy way so need a + * little hack to get every thing work out. + * When menu shows up, it drawing invisible + * layer wich hides all elements of + * Cloud Commander so it could not handle + * onclick events. To get every thing work + * how expected we hide out invisible layer + * so for observer it is nothing special + * is not going on. All magic happening in + * DOM tree + */ + function clickProcessing(pEvent){ + /* if clicked on menu item */ + var lParent = pEvent.target.parentElement, + lClassName = lParent && lParent.className; + switch(lClassName){ + case 'context-menu-item': + return; + case 'context-menu-list ': + return; + } + + if(pEvent && pEvent.x){ + var lLayer = DOM.getById('context-menu-layer'); + if(lLayer){ + var lStyle; + + if(lLayer) + lStyle = lLayer.style.cssText; + /* hide invisible menu layer */ + if(lStyle) + lLayer.style.cssText = lStyle + .replace('z-index: 1', 'z-index:-1'); + + /* get element by point */ + var lElement = document.elementFromPoint(pEvent.x, pEvent.y), + lTag = lElement.tagName; + + if(lTag === 'A' || lTag === 'SPAN'){ + if (lElement.tagName === 'A') + lParent = lElement.parentElement.parentElement; + else if(lElement.tagName === 'SPAN') + lParent = lElement.parentElement; + + if(lParent.className === '') + DOM.setCurrentFile(lParent); + } + + /* show invisible menu layer */ + if(lLayer && lStyle) + lLayer.style.cssText = lStyle; + + KeyBinding.set(); + + //DOM.addListener('contextmenu', clickProcessing, lLayer); + } + } + } + + function set(){ if(!MenuSeted){ $.contextMenu(getConfig()); - /* - * Menu works in some crazy way so need a - * little hack to get every thing work out. - * When menu shows up, it drawing invisible - * layer wich hides all elements of - * Cloud Commander so it could not handle - * onclick events. To get every thing work - * how expected we hide out invisible layer - * so for observer it is nothing special - * is not going on. All magic happening in - * DOM tree - */ - DOM.addClickListener(function(pEvent){ - /* if clicked on menu item */ - var lParent = pEvent.target.parentElement, - lClassName = lParent && lParent.className; - switch(lClassName){ - case 'context-menu-item': - return; - case 'context-menu-list ': - return; - } - - if(pEvent && pEvent.x){ - var lLayer = DOM.getById('context-menu-layer'); - if(lLayer){ - var lStyle; - - if(lLayer) - lStyle = lLayer.style.cssText; - /* hide invisible menu layer */ - if(lStyle) - lLayer.style.cssText = lStyle - .replace('z-index: 1', 'z-index:-1'); - - /* get element by point */ - var lElement = document.elementFromPoint(pEvent.x, pEvent.y), - lTag = lElement.tagName, - lParent; - - if(lTag === 'A' || lTag === 'SPAN'){ - if (lElement.tagName === 'A') - lParent = lElement.parentElement.parentElement; - else if(lElement.tagName === 'SPAN') - lParent = lElement.parentElement; - - if(lParent.className === '') - DOM.setCurrentFile(lParent); - } - - /* show invisible menu layer */ - if(lLayer && lStyle) - lLayer.style.cssText = lStyle; - - KeyBinding.set(); - } - } - }); + + DOM.addClickListener(clickProcessing); MenuSeted = true; } diff --git a/lib/server/commander.js b/lib/server/commander.js index 20085805..30a91116 100644 --- a/lib/server/commander.js +++ b/lib/server/commander.js @@ -21,8 +21,8 @@ querystring = main.querystring, zlib = main.zlib, - FILE_NOT_FOUND = 404, - OK = 200, + FILE_NOT_FOUND = main.FILE_NOT_FOUND, + OK = main.OK, FS = CloudFunc.FS, NO_JS = CloudFunc.NOJS, @@ -125,7 +125,7 @@ } else sendResponse({ - data : p.Error.toString(), + data : p.error.toString(), name : lDirPath, request : c.request, response: c.response, diff --git a/lib/server/main.js b/lib/server/main.js index 8d3cf430..6737266c 100644 --- a/lib/server/main.js +++ b/lib/server/main.js @@ -12,52 +12,58 @@ ext, path, fs, zlib, url, - OK = 200, - FILE_NOT_FOUND = 404; + OK, FILE_NOT_FOUND; + + /* Consts */ + + exports.OK = OK = 200; + exports.FILE_NOT_FOUND = 404; + exports.REQUEST = 'request'; + exports.RESPONSE = 'response'; /* Native Modules*/ - exports.crypto = require('crypto'), - exports.child_process = require('child_process'), - exports.fs = fs = require('fs'), - exports.http = require('http'), - exports.https = require('https'), - exports.path = path = require('path'), - exports.url = url = require('url'), - exports.querystring = require('querystring'), + exports.crypto = require('crypto'), + exports.child_process = require('child_process'), + exports.fs = fs = require('fs'), + exports.http = require('http'), + exports.https = require('https'), + exports.path = path = require('path'), + exports.url = url = require('url'), + exports.querystring = require('querystring'), /* Constants */ /* current dir + 2 levels up */ - exports.WIN32 = ISWIN32 = isWin32(); - exports.SLASH = SLASH = ISWIN32 ? '\\' : '/', + exports.WIN32 = ISWIN32 = isWin32(); + exports.SLASH = SLASH = ISWIN32 ? '\\' : '/', - exports.SRVDIR = SRVDIR = __dirname + SLASH, - exports.LIBDIR = LIBDIR = path.normalize(SRVDIR + '../'), - exports.DIR = DIR = path.normalize(LIBDIR + '../'), - exports.HTMLDIR = DIR + 'html' + SLASH, - exports.JSONDIR = JSONDIR = DIR + 'json' + SLASH, + exports.SRVDIR = SRVDIR = __dirname + SLASH, + exports.LIBDIR = LIBDIR = path.normalize(SRVDIR + '../'), + exports.DIR = DIR = path.normalize(LIBDIR + '../'), + exports.HTMLDIR = DIR + 'html' + SLASH, + exports.JSONDIR = JSONDIR = DIR + 'json' + SLASH, /* Functions */ - exports.require = mrequire, - exports.librequire = librequire, - exports.srvrequire = srvrequire, - exports.rootrequire = rootrequire, - exports.generateHeaders = generateHeaders, - exports.getQuery = getQuery, - exports.sendFile = sendFile, + exports.require = mrequire, + exports.librequire = librequire, + exports.srvrequire = srvrequire, + exports.rootrequire = rootrequire, + exports.generateHeaders = generateHeaders, + exports.getQuery = getQuery, + exports.sendFile = sendFile, /* compitability with old versions of node */ - exports.fs.exists = exports.fs.exists || exports.path.exists; + exports.fs.exists = exports.fs.exists || exports.path.exists; /* Needed Modules */ - exports.util = Util = require(LIBDIR + 'util'), + exports.util = Util = require(LIBDIR + 'util'), - exports.zlib = zlib = mrequire('zlib'), + exports.zlib = zlib = mrequire('zlib'), /* Main Information */ - exports.config = jsonrequire('config'); - exports.modules = jsonrequire('modules'); - exports.ext = ext = jsonrequire('ext'); - exports.mainpackage = rootrequire('package'); + exports.config = jsonrequire('config'); + exports.modules = jsonrequire('modules'); + exports.ext = ext = jsonrequire('ext'); + exports.mainpackage = rootrequire('package'); /* @@ -66,21 +72,21 @@ * moudles do not depends on each other all needed information * for all modules is initialized hear. */ - global.cloudcmd.main = exports; + global.cloudcmd.main = exports; - exports.VOLUMES = getVolumes(), + exports.VOLUMES = getVolumes(), /* Additional Modules */ - exports.socket = srvrequire('socket'), - exports.auth = srvrequire('auth').auth, - exports.appcache = srvrequire('appcache'), - exports.cache = srvrequire('cache').Cache, - exports.cloudfunc = librequire('cloudfunc'), - exports.rest = srvrequire('rest').api, - exports.update = srvrequire('update'), - exports.ischanged = srvrequire('ischanged'); - exports.commander = srvrequire('commander'); - exports.minify = srvrequire('minify').Minify; + exports.socket = srvrequire('socket'), + exports.auth = srvrequire('auth').auth, + exports.appcache = srvrequire('appcache'), + exports.cache = srvrequire('cache').Cache, + exports.cloudfunc = librequire('cloudfunc'), + exports.rest = srvrequire('rest').api, + exports.update = srvrequire('update'), + exports.ischanged = srvrequire('ischanged'); + exports.commander = srvrequire('commander'); + 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 @@ -202,7 +208,7 @@ lReadStream = fs.createReadStream(lName, { 'bufferSize': 4 * 1024 - }); + }); lReadStream.on('error', function(pError){ lRes.writeHead(FILE_NOT_FOUND, 'OK'); From 19b773f43c975dfce826a5a672d93c3efdab3dfa Mon Sep 17 00:00:00 2001 From: coderaiser Date: Sat, 9 Feb 2013 04:47:27 -0500 Subject: [PATCH 244/347] added ability show context menu on right click while menu is showing now --- ChangeLog | 3 +++ lib/client/menu.js | 10 +++++++--- 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/ChangeLog b/ChangeLog index d3bbe83d..635d7965 100644 --- a/ChangeLog +++ b/ChangeLog @@ -130,6 +130,9 @@ time was changed. * Fixed bug with (fake) deleting file. +* Added ability show context menu on right click +while menu is showing now. + 2012.12.12, Version 0.1.8 diff --git a/lib/client/menu.js b/lib/client/menu.js index 3b6b7448..7f50f090 100644 --- a/lib/client/menu.js +++ b/lib/client/menu.js @@ -180,7 +180,13 @@ var CloudCommander, Util, DOM, $; }, // define the elements of the menu - items: getAllItems(lMenuItems) + items: getAllItems(lMenuItems), + events:{ + hide: function(){ + var lLayer = DOM.getById('context-menu-layer'); + DOM.addOneTimeListener('contextmenu', clickProcessing, lLayer); + } + } }; return lRet; @@ -259,8 +265,6 @@ var CloudCommander, Util, DOM, $; lLayer.style.cssText = lStyle; KeyBinding.set(); - - //DOM.addListener('contextmenu', clickProcessing, lLayer); } } } From 896bd44fe143e8e95e7e48793cb2726eb20ce186 Mon Sep 17 00:00:00 2001 From: coderaiser Date: Sat, 9 Feb 2013 05:09:06 -0500 Subject: [PATCH 245/347] minor changes --- json/config.json | 2 +- shell/deploy.sh | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/json/config.json b/json/config.json index 609d9776..e53c7ca4 100644 --- a/json/config.json +++ b/json/config.json @@ -2,7 +2,7 @@ "api_url" : "/api/v1", "appcache" : false, "minification" : { - "js" : false, + "js" : true, "css" : true, "html" : true, "img" : true diff --git a/shell/deploy.sh b/shell/deploy.sh index 316dbf69..798ac07e 100755 --- a/shell/deploy.sh +++ b/shell/deploy.sh @@ -1,4 +1,3 @@ -cd .. echo 'appfog' af update echo 'http://cloudcmd.aws.af.cm/' From 2dcc8ed7220dc61de7f68863dd85b660b26d2987 Mon Sep 17 00:00:00 2001 From: coderaiser Date: Sat, 9 Feb 2013 15:40:51 -0500 Subject: [PATCH 246/347] minor changes --- json/config.json | 2 +- lib/client/google_analytics.js | 2 +- lib/client/menu.js | 1 - lib/server/main.js | 5 +++-- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/json/config.json b/json/config.json index e53c7ca4..609d9776 100644 --- a/json/config.json +++ b/json/config.json @@ -2,7 +2,7 @@ "api_url" : "/api/v1", "appcache" : false, "minification" : { - "js" : true, + "js" : false, "css" : true, "html" : true, "img" : true diff --git a/lib/client/google_analytics.js b/lib/client/google_analytics.js index a4f499fd..ed65f695 100644 --- a/lib/client/google_analytics.js +++ b/lib/client/google_analytics.js @@ -10,6 +10,6 @@ var DOM, _gaq; navigator.userAgent + ' -> ' + url + " : " + line]); }); - DOM.jsload('http://google-analytics.com/ga.js'); + DOM.jsload('//google-analytics.com/ga.js'); })(DOM, _gaq); \ No newline at end of file diff --git a/lib/client/menu.js b/lib/client/menu.js index 7f50f090..4d050f54 100644 --- a/lib/client/menu.js +++ b/lib/client/menu.js @@ -24,7 +24,6 @@ var CloudCommander, Util, DOM, $; function showEditor(pReadOnly){ DOM.Images.showLoad(); var lEditor = CloudCmd[pReadOnly ? 'Viewer' : 'Editor'], - lResult = Util.exec(lEditor, pReadOnly); if(!lResult){ diff --git a/lib/server/main.js b/lib/server/main.js index 6737266c..7cb1848d 100644 --- a/lib/server/main.js +++ b/lib/server/main.js @@ -193,12 +193,13 @@ */ function sendFile(pParams){ var lRet, - lName, lReq, lRes, lQuery; + lName, lReq, lRes, lQuery, lGziped; if(pParams){ lName = pParams.name, lReq = pParams.request, lRes = pParams.response; + lGziped = pParams.gziped; lQuery = getQuery(lReq); } @@ -217,7 +218,7 @@ lRes.writeHead(OK, generateHeaders(lName, lGzip, lQuery ) ); - if (lGzip) + if (lGzip && !lGziped) lReadStream = lReadStream.pipe( zlib.createGzip() ); lReadStream.pipe(lRes); From d1f209e018c03ce936ed9dbb6636f6aa3b60f8b2 Mon Sep 17 00:00:00 2001 From: coderaiser Date: Mon, 18 Feb 2013 04:50:51 -0500 Subject: [PATCH 247/347] added ability to remove /fs/no-js when go up to root directory --- ChangeLog | 3 +++ lib/client.js | 15 +++++++++------ 2 files changed, 12 insertions(+), 6 deletions(-) diff --git a/ChangeLog b/ChangeLog index 635d7965..9ec6842c 100644 --- a/ChangeLog +++ b/ChangeLog @@ -133,6 +133,9 @@ time was changed. * Added ability show context menu on right click while menu is showing now. +* Added ability to remove /fs/no-js when go up to root +directory. + 2012.12.12, Version 0.1.8 diff --git a/lib/client.js b/lib/client.js index bd153f79..0e6d99ea 100644 --- a/lib/client.js +++ b/lib/client.js @@ -528,19 +528,22 @@ CloudCmd._changeLinks = function(pPanelID){ */ CloudCmd._ajaxLoad = function(pPath, pOptions){ if(!pOptions) - pOptions = {}; + pOptions = {}; /* Отображаем красивые пути */ - var lFSPath = decodeURI(pPath); + var lFSPath = decodeURI(pPath); - lFSPath = Util.removeStr( lFSPath, CloudFunc.NOJS ); + lFSPath = Util.removeStr( lFSPath, CloudFunc.NOJS ); var lCleanPath = Util.removeStr(lFSPath, CloudFunc.FS); Util.log ('reading dir: "' + lCleanPath + '";'); - if(!pOptions.nohistory) - DOM.setHistory(pPath, null, pPath); - DOM.setTitle( CloudFunc.getTitle(pPath) ); + if(!pOptions.nohistory){ + pPath = lCleanPath === '/' ? '/' : pPath; + DOM.setHistory(pPath, null, pPath); + } + + DOM.setTitle( CloudFunc.getTitle(lCleanPath) ); /* если доступен localStorage и * в нём есть нужная нам директория - From cb020dbcca601945aa6234853a0cd826ebc8087f Mon Sep 17 00:00:00 2001 From: coderaiser Date: Mon, 18 Feb 2013 05:51:13 -0500 Subject: [PATCH 248/347] changed google page spead score becouse of reflow, on setting current file --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index ad96c9e8..9791205d 100644 --- a/README.md +++ b/README.md @@ -5,7 +5,7 @@ DEMO: [cloudfoundry] (http://cloudcmd.cloudfoundry.com "cloudfoundry"), [appfog] (http://cloudcmd.aws.af.cm "appfog"). -Google PageSpeed Score : [100](http://developers.google.com/speed/pagespeed/insights#url=http_3A_2F_2Fcloudcmd.cloudfoundry.com_2F&mobile=false "score") (out of 100) +Google PageSpeed Score : [99](http://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](img/logo/cloudcmd.png "Cloud Commander") From 02ce865eef63bc856a4d36b5fe3581fd7345fccd Mon Sep 17 00:00:00 2001 From: coderaiser Date: Mon, 18 Feb 2013 06:12:01 -0500 Subject: [PATCH 249/347] removed reflows maded by js --- ChangeLog | 2 + README.md | 2 +- json/config.json | 2 +- lib/client.js | 13 +- lib/cloudfunc.js | 893 +++++++++++++++++++++++------------------------ 5 files changed, 453 insertions(+), 459 deletions(-) diff --git a/ChangeLog b/ChangeLog index 9ec6842c..6971ee77 100644 --- a/ChangeLog +++ b/ChangeLog @@ -136,6 +136,8 @@ while menu is showing now. * Added ability to remove /fs/no-js when go up to root directory. +* Removed reflows maded by js. + 2012.12.12, Version 0.1.8 diff --git a/README.md b/README.md index 9791205d..ad96c9e8 100644 --- a/README.md +++ b/README.md @@ -5,7 +5,7 @@ DEMO: [cloudfoundry] (http://cloudcmd.cloudfoundry.com "cloudfoundry"), [appfog] (http://cloudcmd.aws.af.cm "appfog"). -Google PageSpeed Score : [99](http://developers.google.com/speed/pagespeed/insights#url=http_3A_2F_2Fcloudcmd.cloudfoundry.com_2F&mobile=false "score") (out of 100) +Google PageSpeed Score : [100](http://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](img/logo/cloudcmd.png "Cloud Commander") diff --git a/json/config.json b/json/config.json index 609d9776..e53c7ca4 100644 --- a/json/config.json +++ b/json/config.json @@ -2,7 +2,7 @@ "api_url" : "/api/v1", "appcache" : false, "minification" : { - "js" : false, + "js" : true, "css" : true, "html" : true, "img" : true diff --git a/lib/client.js b/lib/client.js index 0e6d99ea..ea909564 100644 --- a/lib/client.js +++ b/lib/client.js @@ -337,19 +337,14 @@ function baseInit(pCallBack){ DOM.Cache.set('/', CloudCmd._getJSONfromFileTable()); }); - /* устанавливаем размер высоты таблицы файлов - * исходя из размеров разрешения экрана - */ - - /* выделяем строку с первым файлом */ - var lFmHeader = DOM.getByClass('fm-header'); - DOM.setCurrentFile(lFmHeader[0].nextSibling); - /* показываем элементы, которые будут работать только, если есть js */ var lFM = DOM.getById('fm'); lFM.className='localstorage'; - /* формируем и округляем высоту экрана + /* устанавливаем размер высоты таблицы файлов + * исходя из размеров разрешения экрана + * + * формируем и округляем высоту экрана * при разрешениии 1024x1280: * 658 -> 700 */ diff --git a/lib/cloudfunc.js b/lib/cloudfunc.js index 18b2e5ec..e0e2faf8 100644 --- a/lib/cloudfunc.js +++ b/lib/cloudfunc.js @@ -1,449 +1,446 @@ -var CloudFunc, exports; - -(function(){ - "use strict"; - - /** - * Модуль, содержащий функции, которые - * будут работать и на клиенте и на сервере - */ - - CloudFunc = exports || {}; - - /* Путь с которым мы сейчас работаем */ - CloudFunc.Path = ''; - - /* КОНСТАНТЫ (общие для клиента и сервера)*/ - - /* название программы */ - CloudFunc.NAME = 'Cloud Commander'; - - /* если в ссылке будет эта строка - в браузере js отключен */ - CloudFunc.NOJS = '/no-js'; - CloudFunc.FS = '/fs'; - - /* название css-класа кнопки обновления файловой структуры*/ - CloudFunc.REFRESHICON = 'refresh-icon'; - - /* id панелей с файлами */ - CloudFunc.LEFTPANEL = 'left'; - CloudFunc.RIGHTPANEL = 'right'; - - /* length of longest file name */ - CloudFunc.SHORTNAMELENGTH = 16; - - /** - * Функция убирает последний слеш, - * если он - последний символ строки - */ - CloudFunc.removeLastSlash = function(pPath){ - if(typeof pPath==='string') - return (pPath.lastIndexOf('/') === pPath.length-1) ? - pPath.substr(pPath, pPath.length-1):pPath; - else return pPath; - }; - - /** Функция возвращает заголовок веб страницы - * @pPath - */ - CloudFunc.getTitle = function(pPath){ - if(!CloudFunc.Path) - CloudFunc.Path = '/'; - - return CloudFunc.NAME + ' - ' + (pPath || CloudFunc.Path); - - }; - /** - * Функция переводит права из цыфрового вида в символьный - * @param pPerm_s - строка с правами доступа - * к файлу в 8-миричной системе - */ - CloudFunc.convertPermissionsToSymbolic = function(pPerm_s){ - /* - S_IRUSR 0000400 protection: readable by owner - S_IWUSR 0000200 writable by owner - S_IXUSR 0000100 executable by owner - S_IRGRP 0000040 readable by group - S_IWGRP 0000020 writable by group - S_IXGRP 0000010 executable by group - S_IROTH 0000004 readable by all - S_IWOTH 0000002 writable by all - S_IXOTH 0000001 executable by all - */ - if(!pPerm_s) return; - - /* тип файла */ - var lType = pPerm_s.charAt(0); - - switch (lType-0) { - case 1: /* обычный файл */ - lType='-'; - break; - case 2: /* байт-ориентированное (символьное) устройство*/ - lType='c'; - break; - case 4: /* каталог */ - lType='d'; - break; - default: - lType='-'; - } - - /* оставляем последние 3 символа*/ - pPerm_s = pPerm_s.length> 5 ?pPerm_s.substr(3) : pPerm_s.substr(2); - - /* Рекомендации гугла советуют вместо string[3] - * использовать string.charAt(3) - */ - /* - http://google-styleguide.googlecode.com/svn/trunk/javascriptguide.xml?showone=Standards_features#Standards_features - - Always preferred over non-standards featuresFor - maximum portability and compatibility, always - prefer standards features over non-standards - features (e.g., string.charAt(3) over string[3] - and element access with DOM functions instead - of using an application-specific shorthand). - */ - /* Переводим в двоичную систему */ - var lOwner = (pPerm_s[0]-0).toString(2), - lGroup = (pPerm_s[1]-0).toString(2), - lAll = (pPerm_s[2]-0).toString(2), - /* - console.log(lOwner+' '+lGroup+' '+lAll); - */ - /* переводим в символьную систему*/ - lPermissions = //lType+' '+ - (lOwner[0]-0>0?'r':'-') + - (lOwner[1]-0>0?'w':'-') + - (lOwner[2]-0>0?'x':'-') + - ' ' + - (lGroup[0]-0>0?'r':'-') + - (lGroup[1]-0>0?'w':'-') + - (lGroup[2]-0>0?'x':'-') + - ' ' + - (lAll[0]-0>0?'r':'-') + - (lAll[1]-0>0?'w':'-') + - (lAll[2]-0>0?'x':'-'); - /* - console.log(lPermissions); - */ - return lPermissions; - }; - - /** - * Функция конвертирует права доступа к файлам из символьного вида - * в цыфровой - */ - CloudFunc.convertPermissionsToNumberic = function(pPerm_s){ - /* если передана правильная строка, конвертированная - * функциец convertPermissionsToSymbolic - */ - if(!pPerm_s || pPerm_s.length!==11)return pPerm_s; - - var lOwner= (pPerm_s[0] === 'r' ? 4 : 0) + - (pPerm_s[1] === 'w' ? 2 : 0) + - (pPerm_s[2] === 'x' ? 1 : 0), - - lGroup= (pPerm_s[4] === 'r' ? 4 : 0) + - (pPerm_s[5] === 'w' ? 2 : 0) + - (pPerm_s[6] === 'x' ? 1 : 0), - - lAll = (pPerm_s[8] === 'r' ? 4 : 0) + - (pPerm_s[9] === 'w' ? 2 : 0) + - (pPerm_s[10] === 'x' ? 1 : 0); - - /* добавляем 2 цыфры до 5 */ - return '00' + lOwner + lGroup + lAll; - }; - /** Функция получает короткие размеры - * конвертируя байт в килобайты, мегабойты, - * гигайбайты и терабайты - * @pSize - размер в байтах - */ - CloudFunc.getShortedSize = function(pSize){ - /* if pSize=0 - return it */ - if (pSize !== pSize-0) return pSize; - - /* Константы размеров, что используются - * внутри функции - */ - var l1BMAX = 1024; - var l1KBMAX = 1048576; - var l1MBMAX = 1073741824; - var l1GBMAX = 1099511627776; - var l1TBMAX = 1125899906842624; - - var lShorted; - - if (pSize < l1BMAX) lShorted = pSize + 'b'; - else if (pSize < l1KBMAX) lShorted = (pSize/l1BMAX) .toFixed(2) + 'kb'; - else if (pSize < l1MBMAX) lShorted = (pSize/l1KBMAX).toFixed(2) + 'mb'; - else if (pSize < l1GBMAX) lShorted = (pSize/l1MBMAX).toFixed(2) + 'gb'; - else if (pSize < l1TBMAX) lShorted = (pSize/l1GBMAX).toFixed(2) + 'tb'; - - return lShorted; - }; - - /** Функция парсит uid и имена пользователей - * из переданного в строке вычитаного файла /etc/passwd - * и возвращает массив обьектов имён и uid пользователей - * @pPasswd_s - строка, в которой находиться файл /etc/passwd - */ - CloudFunc.getUserUIDsAndNames = function(pPasswd_s){ - var lUsers = {name:'', uid:''}, - lUsersData = [], - i = 0; - do{ - /* получаем первую строку */ - var lLine = pPasswd_s.substr(pPasswd_s, pPasswd_s.indexOf('\n') + 1); - - if(lLine){ - - /* удаляем первую строку из /etc/passwd*/ - pPasswd_s = pPasswd_s.replace(lLine, ''); - - /* получаем первое слово строки */ - var lName = lLine.substr(lLine,lLine.indexOf(':')); - lLine = lLine.replace(lName+':x:',''); - - /* получаем uid*/ - var lUID = lLine.substr(lLine,lLine.indexOf(':')); - if((lUID - 0).toString()!=='NaN'){ - lUsers.name = lName; - lUsers.uid = lUID; - lUsersData[i++] = lUsers; - console.log('uid='+lUID+' name='+lName); - } - } - }while(pPasswd_s !== ''); - - return lUsersData; - }; - - /** Функция получает адреса каждого каталога в пути - * возвращаеться массив каталогов - * @param url - адрес каталога - */ - CloudFunc._getDirPath = function(url){ - var folders = [], - i = 0; - do{ - folders[i++] = url; - url = url.substr(url,url.lastIndexOf('/')); - }while(url !== ''); - - /* Формируем ссылки на каждый каталог в пути */ - var lHref = '', - lHrefEnd ='', - - lHtmlPath, - - /* путь в ссылке, который говорит что js отключен */ - lNoJS_s = CloudFunc.NOJS, - lFS_s = CloudFunc.FS; - /* корневой каталог */ - lHtmlPath = lHref + - lFS_s + - lNoJS_s + - lTitle + - '/' + - _l + - '/' + - lHrefEnd; - - for(i = folders.length - 1; i > 0; i--) - { - var lUrl=folders[i], - lShortName = lUrl.replace(lUrl.substr(lUrl,lUrl.lastIndexOf('/')+1),''); - if (i!==1) - lHtmlPath += lHref + - lFS_s + lNoJS_s + lUrl + - lTitle + lUrl + _l + - lShortName + - lHrefEnd + '/'; - else - lHtmlPath+=lShortName+'/'; - } - /* *** */ - return lHtmlPath; - }; - - /** - * Функция формирует заголовки столбиков - * @pFileTableTitles - массив названий столбиков - */ - CloudFunc._getFileTableHeader = function(pFileTableTitles) - { - var lHeader='
  • '; - lHeader+=''; - for(var i=0;i'+ - lStr+ - ''; - } - lHeader += '
  • '; - - return lHeader; - }; - - /** - * Функция строит таблицу файлв из JSON-информации о файлах - * @param pJSON - информация о файлах - * @param pKeyBinded - если клавиши назначены, выделяем верхний файл - * [{path:'путь',size:'dir'}, - * {name:'имя',size:'размер',mode:'права доступа'}] - */ - CloudFunc.buildFromJSON = function(pJSON, pKeyBinded) - { - var files; - /* - * Если мы на клиенте и нет JSON - - * через eval парсим. - * Если-же мы на сервере, - * или на клиенте всё есть - * парсим стандарным методом - * - * По скольку мы прописали заголовок application/json - * нет необходимости его конвертировать, - * но она есть, если мы вытягиваем данные из - * localStorage - */ - files = pJSON; - - /* сохраняем путь каталога в котором мы сейчас находимся*/ - var lPath = files[0].path; - - /* сохраняем путь */ - CloudFunc.Path = lPath; - - /* - * Строим путь каталога в котором мы находимся - * со всеми подкаталогами - */ - var lHtmlPath = CloudFunc._getDirPath(lPath), - - /* Убираем последний слэш - * с пути для кнопки обновить страницу - * если он есть - */ - lRefreshPath = CloudFunc.removeLastSlash(lPath), - - /* путь в ссылке, который говорит - * что js отключен - */ - lNoJS_s = CloudFunc.NOJS, - lFS_s = CloudFunc.FS, - - lFileTable = - '
  • '+ - '' + - '' + - '' + - '' + - '' + - '' + - '' + lHtmlPath + '' + - '
  • ', - - fileTableTitles = ['name','size','owner','mode']; - - lFileTable += CloudFunc._getFileTableHeader(fileTableTitles); - - /* Если мы не в корне */ - if(lPath !== '/'){ - /* ссылка на верхний каталог*/ - var lDotDot; - /* убираем последний слеш и каталог в котором мы сейчас находимся*/ - lDotDot = lPath.substr(lPath,lPath.lastIndexOf('/')); - lDotDot = lDotDot.substr(lDotDot,lDotDot.lastIndexOf('/')); - /* Если предыдущий каталог корневой */ - if(lDotDot === '')lDotDot = '/'; - - /* Сохраняем путь к каталогу верхнего уровня*/ - lFileTable += '
  • '+ - '' + - '' + - '' + - '' + ".." + - '' + - '<dir>'+ - '.' + - '' + - '
  • '; - } - - for(var i = 1, n = files.length; i < n; i++){ - lFileTable += '
  • '; - lFileTable += ''; - lFileTable += ''; - lFileTable += '' + - '' + files[i].name + - "" + - ''; - /* если папка - не выводим размер */ - lFileTable += '' + - (files[i].size === 'dir' ? - '<dir>' - /* если это файл - получаем - * короткий размер - */ - : CloudFunc.getShortedSize( - files[i].size)); - lFileTable += '' + - '' + - (!files[i].uid ? 'root' : files[i].uid) + - '' + - '' + - /* конвертируем названия разрешений - * из числового формата в буквенный - * при этом корневой каталог не трогаем - * по скольку в нём и так всё уже - * установлено еще на сервере - */ - (//lPath==='/'?files[i].mode: - CloudFunc - .convertPermissionsToSymbolic - (files[i].mode)) + - ''; - lFileTable += '
  • '; - } - - /* если клавиши назначены и - * мы в корневом каталоге и - * верхний файл еще не выделен - - * выделяем верхний файл - */ - if(pKeyBinded && lPath === '/'&& - lFileTable.indexOf('
  • ') < 0){ - lFileTable = lFileTable - .replace('
  • ', - '
  • '); - } - - return lFileTable; - }; +var CloudFunc, exports; + +(function(){ + "use strict"; + + /** + * Модуль, содержащий функции, которые + * будут работать и на клиенте и на сервере + */ + + CloudFunc = exports || {}; + + /* Путь с которым мы сейчас работаем */ + CloudFunc.Path = ''; + + /* КОНСТАНТЫ (общие для клиента и сервера)*/ + + /* название программы */ + CloudFunc.NAME = 'Cloud Commander'; + + /* если в ссылке будет эта строка - в браузере js отключен */ + CloudFunc.NOJS = '/no-js'; + CloudFunc.FS = '/fs'; + + /* название css-класа кнопки обновления файловой структуры*/ + CloudFunc.REFRESHICON = 'refresh-icon'; + + /* id панелей с файлами */ + CloudFunc.LEFTPANEL = 'left'; + CloudFunc.RIGHTPANEL = 'right'; + + /* length of longest file name */ + CloudFunc.SHORTNAMELENGTH = 16; + + /** + * Функция убирает последний слеш, + * если он - последний символ строки + */ + CloudFunc.removeLastSlash = function(pPath){ + if(typeof pPath==='string') + return (pPath.lastIndexOf('/') === pPath.length-1) ? + pPath.substr(pPath, pPath.length-1):pPath; + else return pPath; + }; + + /** Функция возвращает заголовок веб страницы + * @pPath + */ + CloudFunc.getTitle = function(pPath){ + if(!CloudFunc.Path) + CloudFunc.Path = '/'; + + return CloudFunc.NAME + ' - ' + (pPath || CloudFunc.Path); + + }; + /** + * Функция переводит права из цыфрового вида в символьный + * @param pPerm_s - строка с правами доступа + * к файлу в 8-миричной системе + */ + CloudFunc.convertPermissionsToSymbolic = function(pPerm_s){ + /* + S_IRUSR 0000400 protection: readable by owner + S_IWUSR 0000200 writable by owner + S_IXUSR 0000100 executable by owner + S_IRGRP 0000040 readable by group + S_IWGRP 0000020 writable by group + S_IXGRP 0000010 executable by group + S_IROTH 0000004 readable by all + S_IWOTH 0000002 writable by all + S_IXOTH 0000001 executable by all + */ + if(!pPerm_s) return; + + /* тип файла */ + var lType = pPerm_s.charAt(0); + + switch (lType-0) { + case 1: /* обычный файл */ + lType='-'; + break; + case 2: /* байт-ориентированное (символьное) устройство*/ + lType='c'; + break; + case 4: /* каталог */ + lType='d'; + break; + default: + lType='-'; + } + + /* оставляем последние 3 символа*/ + pPerm_s = pPerm_s.length> 5 ?pPerm_s.substr(3) : pPerm_s.substr(2); + + /* Рекомендации гугла советуют вместо string[3] + * использовать string.charAt(3) + */ + /* + http://google-styleguide.googlecode.com/svn/trunk/javascriptguide.xml?showone=Standards_features#Standards_features + + Always preferred over non-standards featuresFor + maximum portability and compatibility, always + prefer standards features over non-standards + features (e.g., string.charAt(3) over string[3] + and element access with DOM functions instead + of using an application-specific shorthand). + */ + /* Переводим в двоичную систему */ + var lOwner = (pPerm_s[0]-0).toString(2), + lGroup = (pPerm_s[1]-0).toString(2), + lAll = (pPerm_s[2]-0).toString(2), + /* + console.log(lOwner+' '+lGroup+' '+lAll); + */ + /* переводим в символьную систему*/ + lPermissions = //lType+' '+ + (lOwner[0]-0>0?'r':'-') + + (lOwner[1]-0>0?'w':'-') + + (lOwner[2]-0>0?'x':'-') + + ' ' + + (lGroup[0]-0>0?'r':'-') + + (lGroup[1]-0>0?'w':'-') + + (lGroup[2]-0>0?'x':'-') + + ' ' + + (lAll[0]-0>0?'r':'-') + + (lAll[1]-0>0?'w':'-') + + (lAll[2]-0>0?'x':'-'); + /* + console.log(lPermissions); + */ + return lPermissions; + }; + + /** + * Функция конвертирует права доступа к файлам из символьного вида + * в цыфровой + */ + CloudFunc.convertPermissionsToNumberic = function(pPerm_s){ + /* если передана правильная строка, конвертированная + * функциец convertPermissionsToSymbolic + */ + if(!pPerm_s || pPerm_s.length!==11)return pPerm_s; + + var lOwner= (pPerm_s[0] === 'r' ? 4 : 0) + + (pPerm_s[1] === 'w' ? 2 : 0) + + (pPerm_s[2] === 'x' ? 1 : 0), + + lGroup= (pPerm_s[4] === 'r' ? 4 : 0) + + (pPerm_s[5] === 'w' ? 2 : 0) + + (pPerm_s[6] === 'x' ? 1 : 0), + + lAll = (pPerm_s[8] === 'r' ? 4 : 0) + + (pPerm_s[9] === 'w' ? 2 : 0) + + (pPerm_s[10] === 'x' ? 1 : 0); + + /* добавляем 2 цыфры до 5 */ + return '00' + lOwner + lGroup + lAll; + }; + /** Функция получает короткие размеры + * конвертируя байт в килобайты, мегабойты, + * гигайбайты и терабайты + * @pSize - размер в байтах + */ + CloudFunc.getShortedSize = function(pSize){ + /* if pSize=0 - return it */ + if (pSize !== pSize-0) return pSize; + + /* Константы размеров, что используются + * внутри функции + */ + var l1BMAX = 1024; + var l1KBMAX = 1048576; + var l1MBMAX = 1073741824; + var l1GBMAX = 1099511627776; + var l1TBMAX = 1125899906842624; + + var lShorted; + + if (pSize < l1BMAX) lShorted = pSize + 'b'; + else if (pSize < l1KBMAX) lShorted = (pSize/l1BMAX) .toFixed(2) + 'kb'; + else if (pSize < l1MBMAX) lShorted = (pSize/l1KBMAX).toFixed(2) + 'mb'; + else if (pSize < l1GBMAX) lShorted = (pSize/l1MBMAX).toFixed(2) + 'gb'; + else if (pSize < l1TBMAX) lShorted = (pSize/l1GBMAX).toFixed(2) + 'tb'; + + return lShorted; + }; + + /** Функция парсит uid и имена пользователей + * из переданного в строке вычитаного файла /etc/passwd + * и возвращает массив обьектов имён и uid пользователей + * @pPasswd_s - строка, в которой находиться файл /etc/passwd + */ + CloudFunc.getUserUIDsAndNames = function(pPasswd_s){ + var lUsers = {name:'', uid:''}, + lUsersData = [], + i = 0; + do{ + /* получаем первую строку */ + var lLine = pPasswd_s.substr(pPasswd_s, pPasswd_s.indexOf('\n') + 1); + + if(lLine){ + + /* удаляем первую строку из /etc/passwd*/ + pPasswd_s = pPasswd_s.replace(lLine, ''); + + /* получаем первое слово строки */ + var lName = lLine.substr(lLine,lLine.indexOf(':')); + lLine = lLine.replace(lName+':x:',''); + + /* получаем uid*/ + var lUID = lLine.substr(lLine,lLine.indexOf(':')); + if((lUID - 0).toString()!=='NaN'){ + lUsers.name = lName; + lUsers.uid = lUID; + lUsersData[i++] = lUsers; + console.log('uid='+lUID+' name='+lName); + } + } + }while(pPasswd_s !== ''); + + return lUsersData; + }; + + /** Функция получает адреса каждого каталога в пути + * возвращаеться массив каталогов + * @param url - адрес каталога + */ + CloudFunc._getDirPath = function(url){ + var folders = [], + i = 0; + do{ + folders[i++] = url; + url = url.substr(url,url.lastIndexOf('/')); + }while(url !== ''); + + /* Формируем ссылки на каждый каталог в пути */ + var lHref = '', + lHrefEnd ='', + + lHtmlPath, + + /* путь в ссылке, который говорит что js отключен */ + lNoJS_s = CloudFunc.NOJS, + lFS_s = CloudFunc.FS; + /* корневой каталог */ + lHtmlPath = lHref + + lFS_s + + lNoJS_s + + lTitle + + '/' + + _l + + '/' + + lHrefEnd; + + for(i = folders.length - 1; i > 0; i--) + { + var lUrl=folders[i], + lShortName = lUrl.replace(lUrl.substr(lUrl,lUrl.lastIndexOf('/')+1),''); + if (i!==1) + lHtmlPath += lHref + + lFS_s + lNoJS_s + lUrl + + lTitle + lUrl + _l + + lShortName + + lHrefEnd + '/'; + else + lHtmlPath+=lShortName+'/'; + } + /* *** */ + return lHtmlPath; + }; + + /** + * Функция формирует заголовки столбиков + * @pFileTableTitles - массив названий столбиков + */ + CloudFunc._getFileTableHeader = function(pFileTableTitles) + { + var lHeader='
  • '; + lHeader+=''; + for(var i=0;i'+ + lStr+ + ''; + } + lHeader += '
  • '; + + return lHeader; + }; + + /** + * Функция строит таблицу файлв из JSON-информации о файлах + * @param pJSON - информация о файлах + * @param pKeyBinded - если клавиши назначены, выделяем верхний файл + * [{path:'путь',size:'dir'}, + * {name:'имя',size:'размер',mode:'права доступа'}] + */ + CloudFunc.buildFromJSON = function(pJSON, pKeyBinded) + { + var files; + /* + * Если мы на клиенте и нет JSON - + * через eval парсим. + * Если-же мы на сервере, + * или на клиенте всё есть + * парсим стандарным методом + * + * По скольку мы прописали заголовок application/json + * нет необходимости его конвертировать, + * но она есть, если мы вытягиваем данные из + * localStorage + */ + files = pJSON; + + /* сохраняем путь каталога в котором мы сейчас находимся*/ + var lPath = files[0].path; + + /* сохраняем путь */ + CloudFunc.Path = lPath; + + /* + * Строим путь каталога в котором мы находимся + * со всеми подкаталогами + */ + var lHtmlPath = CloudFunc._getDirPath(lPath), + + /* Убираем последний слэш + * с пути для кнопки обновить страницу + * если он есть + */ + lRefreshPath = CloudFunc.removeLastSlash(lPath), + + /* путь в ссылке, который говорит + * что js отключен + */ + lNoJS_s = CloudFunc.NOJS, + lFS_s = CloudFunc.FS, + + lFileTable = + '
  • '+ + '' + + '' + + '' + + '' + + '' + + '' + + '' + lHtmlPath + '' + + '
  • ', + + fileTableTitles = ['name','size','owner','mode']; + + lFileTable += CloudFunc._getFileTableHeader(fileTableTitles); + + /* Если мы не в корне */ + if(lPath !== '/'){ + /* ссылка на верхний каталог*/ + var lDotDot; + /* убираем последний слеш и каталог в котором мы сейчас находимся*/ + lDotDot = lPath.substr(lPath, lPath.lastIndexOf('/')); + lDotDot = lDotDot.substr(lDotDot, lDotDot.lastIndexOf('/')); + /* Если предыдущий каталог корневой */ + if(lDotDot === '')lDotDot = '/'; + + /* Сохраняем путь к каталогу верхнего уровня*/ + lFileTable += '
  • '+ + '' + + '' + + '' + + '' + ".." + + '' + + '<dir>'+ + '.' + + '' + + '
  • '; + } + + for(var i = 1, n = files.length; i < n; i++){ + lFileTable += '
  • '; + lFileTable += ''; + lFileTable += ''; + lFileTable += '' + + '' + files[i].name + + "" + + ''; + /* если папка - не выводим размер */ + lFileTable += '' + + (files[i].size === 'dir' ? + '<dir>' + /* если это файл - получаем + * короткий размер + */ + : CloudFunc.getShortedSize( + files[i].size)); + lFileTable += '' + + '' + + (!files[i].uid ? 'root' : files[i].uid) + + '' + + '' + + /* конвертируем названия разрешений + * из числового формата в буквенный + * при этом корневой каталог не трогаем + * по скольку в нём и так всё уже + * установлено еще на сервере + */ + (//lPath==='/'?files[i].mode: + CloudFunc + .convertPermissionsToSymbolic + (files[i].mode)) + + ''; + lFileTable += '
  • '; + } + + /* если клавиши назначены и + * мы в корневом каталоге и + * верхний файл еще не выделен - + * выделяем верхний файл + */ + if(lPath === '/') + lFileTable = lFileTable.replace('
  • ', + '
  • '); + + return lFileTable; + }; })(); \ No newline at end of file From 9cea6de023bc903dcc9379a95a038cf1f87e343b Mon Sep 17 00:00:00 2001 From: coderaiser Date: Mon, 18 Feb 2013 08:10:52 -0500 Subject: [PATCH 250/347] minor changes --- ChangeLog | 2 -- html/auth/github.html | 52 +++++++++++++++++++++---------------------- json/config.json | 2 +- lib/client.js | 7 ++++++ 4 files changed, 34 insertions(+), 29 deletions(-) diff --git a/ChangeLog b/ChangeLog index 6971ee77..9ec6842c 100644 --- a/ChangeLog +++ b/ChangeLog @@ -136,8 +136,6 @@ while menu is showing now. * Added ability to remove /fs/no-js when go up to root directory. -* Removed reflows maded by js. - 2012.12.12, Version 0.1.8 diff --git a/html/auth/github.html b/html/auth/github.html index 01c5f847..8a2e8a1e 100644 --- a/html/auth/github.html +++ b/html/auth/github.html @@ -1,27 +1,27 @@ - - - - - - + + + + + + \ No newline at end of file diff --git a/json/config.json b/json/config.json index e53c7ca4..609d9776 100644 --- a/json/config.json +++ b/json/config.json @@ -2,7 +2,7 @@ "api_url" : "/api/v1", "appcache" : false, "minification" : { - "js" : true, + "js" : false, "css" : true, "html" : true, "img" : true diff --git a/lib/client.js b/lib/client.js index ea909564..3ec032d4 100644 --- a/lib/client.js +++ b/lib/client.js @@ -337,6 +337,13 @@ function baseInit(pCallBack){ DOM.Cache.set('/', CloudCmd._getJSONfromFileTable()); }); + /* выделяем строку с первым файлом */ + var lFmHeader = DOM.getByClass('fm-header'); + if(lFmHeader && lFmHeader[0]){ + var lCurrent = lFmHeader[0].nextSibling; + DOM.setCurrentFile(lCurrent); + } + /* показываем элементы, которые будут работать только, если есть js */ var lFM = DOM.getById('fm'); lFM.className='localstorage'; From 8e586d3d092454b56acb145aee1dc9259aabbcd4 Mon Sep 17 00:00:00 2001 From: coderaiser Date: Mon, 18 Feb 2013 08:13:45 -0500 Subject: [PATCH 251/347] minor changes --- lib/client.js | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/lib/client.js b/lib/client.js index 3ec032d4..43aad27e 100644 --- a/lib/client.js +++ b/lib/client.js @@ -601,12 +601,8 @@ CloudCmd._createFileTable = function(pElem, pJSON){ var lElem = DOM.getById(pElem), /* getting current element if was refresh */ lPath = DOM.getByClass('path', lElem), - lWasRefresh_b = lPath[0].textContent === pJSON[0].path, - lCurrent; + lWasRefresh_b = lPath[0].textContent === pJSON[0].path; - if(lWasRefresh_b) - lCurrent = DOM.getCurrentFile(); - /* говорим построителю, * что бы он в нужный момент * выделил строку с первым файлом @@ -621,7 +617,8 @@ CloudCmd._createFileTable = function(pElem, pJSON){ lElem.innerHTML = CloudFunc.buildFromJSON(pJSON, true); /* searching current file */ - if(lWasRefresh_b && lCurrent){ + if(lWasRefresh_b){ + var lCurrent = DOM.getCurrentFile(); for(i = 0; i < lElem.childNodes.length; i++) if(lElem.childNodes[i].textContent === lCurrent.textContent){ lCurrent = lElem.childNodes[i]; From d19eb4d58a2f722b4ff63d852a9103a78da7f6fe Mon Sep 17 00:00:00 2001 From: coderaiser Date: Mon, 18 Feb 2013 08:21:57 -0500 Subject: [PATCH 252/347] minor changes --- ChangeLog | 2 ++ lib/client.js | 8 ++++---- lib/cloudfunc.js | 6 +++--- 3 files changed, 9 insertions(+), 7 deletions(-) diff --git a/ChangeLog b/ChangeLog index 9ec6842c..6971ee77 100644 --- a/ChangeLog +++ b/ChangeLog @@ -136,6 +136,8 @@ while menu is showing now. * Added ability to remove /fs/no-js when go up to root directory. +* Removed reflows maded by js. + 2012.12.12, Version 0.1.8 diff --git a/lib/client.js b/lib/client.js index 43aad27e..96bf874e 100644 --- a/lib/client.js +++ b/lib/client.js @@ -566,7 +566,7 @@ CloudCmd._ajaxLoad = function(pPath, pOptions){ if (lJSON){ /* переводим из текста в JSON */ lJSON = Util.parseJSON(lJSON); - CloudCmd._createFileTable(lPanel, lJSON); + CloudCmd._createFileTable(lPanel, lJSON, true); CloudCmd._changeLinks(lPanel); return; @@ -576,7 +576,7 @@ CloudCmd._ajaxLoad = function(pPath, pOptions){ DOM.getCurrentFileContent({ url : lFSPath, success : function(pData){ - CloudCmd._createFileTable(lPanel, pData); + CloudCmd._createFileTable(lPanel, pData, true); CloudCmd._changeLinks(lPanel); /* переводим таблицу файлов в строку, для * @@ -597,7 +597,7 @@ CloudCmd._ajaxLoad = function(pPath, pOptions){ * @param pEleme - родительский элемент * @param pJSON - данные о файлах */ -CloudCmd._createFileTable = function(pElem, pJSON){ +CloudCmd._createFileTable = function(pElem, pJSON, pSetCurrent){ var lElem = DOM.getById(pElem), /* getting current element if was refresh */ lPath = DOM.getByClass('path', lElem), @@ -614,7 +614,7 @@ CloudCmd._createFileTable = function(pElem, pJSON){ lElem.removeChild(lElem.lastChild); /* заполняем панель новыми элементами */ - lElem.innerHTML = CloudFunc.buildFromJSON(pJSON, true); + lElem.innerHTML = CloudFunc.buildFromJSON(pJSON, pSetCurrent); /* searching current file */ if(lWasRefresh_b){ diff --git a/lib/cloudfunc.js b/lib/cloudfunc.js index e0e2faf8..7b059b08 100644 --- a/lib/cloudfunc.js +++ b/lib/cloudfunc.js @@ -298,7 +298,7 @@ var CloudFunc, exports; * [{path:'путь',size:'dir'}, * {name:'имя',size:'размер',mode:'права доступа'}] */ - CloudFunc.buildFromJSON = function(pJSON, pKeyBinded) + CloudFunc.buildFromJSON = function(pJSON, pSetCurrent) { var files; /* @@ -368,7 +368,7 @@ var CloudFunc, exports; if(lDotDot === '')lDotDot = '/'; /* Сохраняем путь к каталогу верхнего уровня*/ - lFileTable += '
  • '+ + lFileTable += '
  • '+ '' + '' + '' + @@ -437,7 +437,7 @@ var CloudFunc, exports; * верхний файл еще не выделен - * выделяем верхний файл */ - if(lPath === '/') + if(pSetCurrent) lFileTable = lFileTable.replace('
  • ', '
  • '); From 47ea3491aea0f779d337f541277f9aca1dc69c25 Mon Sep 17 00:00:00 2001 From: coderaiser Date: Mon, 18 Feb 2013 09:01:15 -0500 Subject: [PATCH 253/347] minor changes --- html/auth/github.html | 16 ++++++++-------- json/config.json | 2 +- lib/client/storage/_dropbox.js | 4 ++-- lib/server/auth.js | 2 +- lib/util.js | 2 +- 5 files changed, 13 insertions(+), 13 deletions(-) diff --git a/html/auth/github.html b/html/auth/github.html index 8a2e8a1e..e95eb1fd 100644 --- a/html/auth/github.html +++ b/html/auth/github.html @@ -2,17 +2,17 @@ - \ No newline at end of file diff --git a/json/config.json b/json/config.json index 609d9776..e53c7ca4 100644 --- a/json/config.json +++ b/json/config.json @@ -2,7 +2,7 @@ "api_url" : "/api/v1", "appcache" : false, "minification" : { - "js" : false, + "js" : true, "css" : true, "html" : true, "img" : true diff --git a/lib/client/storage/_dropbox.js b/lib/client/storage/_dropbox.js index 001a1759..eefd8262 100644 --- a/lib/client/storage/_dropbox.js +++ b/lib/client/storage/_dropbox.js @@ -51,8 +51,8 @@ var CloudCommander, Util, DOM, Dropbox, cb, Client; DropBoxStore.login = function(pCallBack){ CloudCmd.getModules(function(pModules){ var lStorage = Util.findObjByNameInArr(pModules, 'storage'), - lDropBox = Util.findObjByNameInArr(lStorage.data, 'DropBox'), - lDropBoxKey = lDropBox && lDropBox.data.encodedKey; + lDropBox = Util.findObjByNameInArr(lStorage, 'DropBox'), + lDropBoxKey = lDropBox && lDropBox.encodedKey; Client = new Dropbox.Client({ key: lDropBoxKey diff --git a/lib/server/auth.js b/lib/server/auth.js index 02b03f54..efaa622f 100644 --- a/lib/server/auth.js +++ b/lib/server/auth.js @@ -49,7 +49,7 @@ function authenticate(pCode, pCallBack) { var lStorage = Util.findObjByNameInArr(Modules, 'storage'), - lGitHub = Util.findObjByNameInArr(lStorage.data, 'GitHub'), + lGitHub = Util.findObjByNameInArr(lStorage, 'GitHub'), lId = lGitHub && lGitHub.key, lSecret = lGitHub && lGitHub.secret, diff --git a/lib/util.js b/lib/util.js index 2f819bd5..1fc50240 100644 --- a/lib/util.js +++ b/lib/util.js @@ -731,7 +731,7 @@ Util = exports || {}; for(var i = 0, n = pArr.length; i < n; i++ ) if(pArr[i].name === pObjName) break; - lRet = pArr[i]; + lRet = pArr[i].data; } return lRet; From 4f1cb990c394c8d7a9cde6b0a1128e9ac58a8b22 Mon Sep 17 00:00:00 2001 From: coderaiser Date: Mon, 18 Feb 2013 09:46:35 -0500 Subject: [PATCH 254/347] minor changes --- cloudcmd.js | 43 ++++++++++++--------- json/config.json | 2 +- lib/cloudfunc.js | 83 ++++++++++++++++++----------------------- lib/server/commander.js | 2 +- 4 files changed, 64 insertions(+), 66 deletions(-) diff --git a/cloudcmd.js b/cloudcmd.js index 7277e7ee..6151b4e0 100644 --- a/cloudcmd.js +++ b/cloudcmd.js @@ -180,24 +180,33 @@ * routing of server queries */ function route(pParams){ - var lRet, - lName = pParams.name; + var lRet = Util.checkObjTrue( pParams, ['name', REQUEST, RESPONSE] ); - if( Util.strCmp(lName, ['/auth', '/auth/github']) ){ - Util.log('* Routing' + - '-> ' + lName); - pParams.name = main.HTMLDIR + lName + '.html'; - lRet = main.sendFile(pParams); - } - else if( Util.isContainStr(lName, FS) || - Util.strCmp( lName, ['/', 'json']) ){ - - lRet = main.commander.sendContent({ - request : pParams[REQUEST], - response : pParams[RESPONSE], - processing : indexProcessing, - index : Minify.allowed.html ? Minify.getName(INDEX) : INDEX - }); + if(lRet){ + var p = pParams; + + if( Util.strCmp(p.name, ['/auth', '/auth/github']) ){ + Util.log('* Routing' + + '-> ' + p.name); + pParams.name = main.HTMLDIR + p.name + '.html'; + lRet = main.sendFile( pParams ); + } + else if( Util.isContainStr(p.name, FS) || Util.strCmp( p.name, '/') ){ + if(main.getQuery() === '') + p.request.url += '?html'; + + var lName = Minify.allowed.html ? + Minify.getName(INDEX) : INDEX; + + lRet = main.commander.sendContent({ + request : p.request, + response : p.response, + processing : indexProcessing, + index : lName + }); + } + else + lRet = false; } return lRet; diff --git a/json/config.json b/json/config.json index e53c7ca4..609d9776 100644 --- a/json/config.json +++ b/json/config.json @@ -2,7 +2,7 @@ "api_url" : "/api/v1", "appcache" : false, "minification" : { - "js" : true, + "js" : false, "css" : true, "html" : true, "img" : true diff --git a/lib/cloudfunc.js b/lib/cloudfunc.js index 7b059b08..f7c4ea3d 100644 --- a/lib/cloudfunc.js +++ b/lib/cloudfunc.js @@ -300,45 +300,28 @@ var CloudFunc, exports; */ CloudFunc.buildFromJSON = function(pJSON, pSetCurrent) { - var files; - /* - * Если мы на клиенте и нет JSON - - * через eval парсим. - * Если-же мы на сервере, - * или на клиенте всё есть - * парсим стандарным методом - * - * По скольку мы прописали заголовок application/json - * нет необходимости его конвертировать, - * но она есть, если мы вытягиваем данные из - * localStorage - */ - files = pJSON; - - /* сохраняем путь каталога в котором мы сейчас находимся*/ - var lPath = files[0].path; - - /* сохраняем путь */ - CloudFunc.Path = lPath; - - /* - * Строим путь каталога в котором мы находимся - * со всеми подкаталогами - */ - var lHtmlPath = CloudFunc._getDirPath(lPath), - - /* Убираем последний слэш - * с пути для кнопки обновить страницу - * если он есть - */ + var files = pJSON, + /* сохраняем путь каталога в котором мы сейчас находимся*/ + lPath = files[0].path, + + /* + * Строим путь каталога в котором мы находимся + * со всеми подкаталогами + */ + lHtmlPath = CloudFunc._getDirPath(lPath), + + /* Убираем последний слэш + * с пути для кнопки обновить страницу + * если он есть + */ lRefreshPath = CloudFunc.removeLastSlash(lPath), - + /* путь в ссылке, который говорит * что js отключен */ lNoJS_s = CloudFunc.NOJS, lFS_s = CloudFunc.FS, - + lFileTable = '
  • '+ '' + '' + lHtmlPath + '' + '
  • ', - + fileTableTitles = ['name','size','owner','mode']; lFileTable += CloudFunc._getFileTableHeader(fileTableTitles); + /* сохраняем путь */ + CloudFunc.Path = lPath; + /* Если мы не в корне */ if(lPath !== '/'){ /* ссылка на верхний каталог*/ - var lDotDot; + var lDotDot, lLink; /* убираем последний слеш и каталог в котором мы сейчас находимся*/ lDotDot = lPath.substr(lPath, lPath.lastIndexOf('/')); lDotDot = lDotDot.substr(lDotDot, lDotDot.lastIndexOf('/')); /* Если предыдущий каталог корневой */ - if(lDotDot === '')lDotDot = '/'; + if(lDotDot === '') + lDotDot = '/'; + + lLink = lFS_s + lNoJS_s + lDotDot; + /* Сохраняем путь к каталогу верхнего уровня*/ lFileTable += '
  • '+ - '' + - '' + - '' + - '' + ".." + - '' + - '<dir>'+ - '.' + - '' + - '
  • '; + '' + + '' + + '' + + '' + ".." + + '' + + '<dir>' + + '.' + + '' + + '
  • '; } for(var i = 1, n = files.length; i < n; i++){ diff --git a/lib/server/commander.js b/lib/server/commander.js index 30a91116..96278d0c 100644 --- a/lib/server/commander.js +++ b/lib/server/commander.js @@ -403,7 +403,7 @@ lPath = getPath(pReq); lNoJS = Util.isContainStr(lPath, NO_JS) - || lPath === '/' || main.getQuery() == 'json'; + || lPath === '/' || main.getQuery(pReq) == 'json'; } return lNoJS; From c171912f17457df83e489e47a833b02b598f55d3 Mon Sep 17 00:00:00 2001 From: coderaiser Date: Mon, 18 Feb 2013 10:59:19 -0500 Subject: [PATCH 255/347] minor changes --- cloudcmd.js | 4 ++-- lib/cloudfunc.js | 21 ++++++++++----------- lib/server/commander.js | 5 ++--- lib/server/rest.js | 41 +++++++++++++++++++++++++++++++---------- 4 files changed, 45 insertions(+), 26 deletions(-) diff --git a/cloudcmd.js b/cloudcmd.js index 6151b4e0..b1830c86 100644 --- a/cloudcmd.js +++ b/cloudcmd.js @@ -192,8 +192,8 @@ lRet = main.sendFile( pParams ); } else if( Util.isContainStr(p.name, FS) || Util.strCmp( p.name, '/') ){ - if(main.getQuery() === '') - p.request.url += '?html'; + //if( !main.getQuery(p.request) ) + //p.request.url += '?html'; var lName = Minify.allowed.html ? Minify.getName(INDEX) : INDEX; diff --git a/lib/cloudfunc.js b/lib/cloudfunc.js index f7c4ea3d..1089a9df 100644 --- a/lib/cloudfunc.js +++ b/lib/cloudfunc.js @@ -358,17 +358,16 @@ var CloudFunc, exports; /* Сохраняем путь к каталогу верхнего уровня*/ - lFileTable += '
  • '+ - '' + - '' + - '' + - '' + ".." + - '' + - '<dir>' + - '.' + - '' + - '
  • '; + lFileTable += '
  • ' + + '' + + '' + + '' + ".." + + '' + + '<dir>' + + '.' + + '' + + '
  • '; } for(var i = 1, n = files.length; i < n; i++){ diff --git a/lib/server/commander.js b/lib/server/commander.js index 96278d0c..30f2b668 100644 --- a/lib/server/commander.js +++ b/lib/server/commander.js @@ -43,7 +43,7 @@ var p = pParams, lPath = getCleanPath(p.request); - INDEX = p.index; + INDEX = p.index; IndexProcessingFunc = pParams.processing; fs.stat(lPath, function(pError, pStat){ @@ -398,12 +398,11 @@ function noJS(pReq){ var lNoJS, lPath; - if(pReq){ lPath = getPath(pReq); lNoJS = Util.isContainStr(lPath, NO_JS) - || lPath === '/' || main.getQuery(pReq) == 'json'; + || lPath === '/' || main.getQuery(pReq) === 'html'; } return lNoJS; diff --git a/lib/server/rest.js b/lib/server/rest.js index 6957bb31..010e108a 100644 --- a/lib/server/rest.js +++ b/lib/server/rest.js @@ -22,12 +22,12 @@ /** * rest interface - * @pConnectionData {request, responce} + * @pParams {request, responce} */ - exports.api = function(pConnectionData){ - var lRet = false, - lReq = pConnectionData.request, - lRes = pConnectionData.response, + exports.api = function(pParams){ + var lRet, + lReq = pParams.request, + lRes = pParams.response, lUrl = lReq.url, lMethod = lReq.method; @@ -84,18 +84,38 @@ lCmd = Util.removeStr(lCmd, '/'); pParams.command = lCmd; } + if(lCmd === 'fs') + onFS(pParams); + else + switch(lMethod){ + case 'GET': + lResult = onGET(pParams); + break; + + case 'PUT': + lResult = onPUT(pParams); + break; + } + return lResult; + } + + function onFS(pParams){ + var lResult, + lMethod = pParams.method; switch(lMethod){ case 'GET': - lResult = onGET(pParams); + pParams.data = { + mesage: 'fs called' + }; + send(pParams); + lResult = true; break; case 'PUT': lResult = onPUT(pParams); break; } - - return lResult; } /** @@ -171,9 +191,10 @@ * @param pReq * @param pCallBack */ - function getBody(pReq, pCallBack){ + function getBody(pReq, pCallBack){ var lBody = ''; - pReq.on('data', function(chunk) { + + pReq.on('data', function(chunk){ lBody += chunk.toString(); }); From 307c22173e376e4ab17f9d47ef7dc288d0ce2278 Mon Sep 17 00:00:00 2001 From: coderaiser Date: Tue, 19 Feb 2013 04:03:52 -0500 Subject: [PATCH 256/347] fixed bug with setting current file after refresh and refreshing dir content --- ChangeLog | 3 +++ lib/client.js | 6 +++--- lib/client/keyBinding.js | 3 +-- 3 files changed, 7 insertions(+), 5 deletions(-) diff --git a/ChangeLog b/ChangeLog index 6971ee77..c241903b 100644 --- a/ChangeLog +++ b/ChangeLog @@ -138,6 +138,9 @@ directory. * Removed reflows maded by js. +* Fixed bug with setting current file +after refresh and refreshing dir content. + 2012.12.12, Version 0.1.8 diff --git a/lib/client.js b/lib/client.js index 96bf874e..f9682906 100644 --- a/lib/client.js +++ b/lib/client.js @@ -85,7 +85,7 @@ CloudCmd._loadDir = function(pLink, pNeedRefresh){ lLink = pLink || Util.removeStr(lHref, CloudCmd.HOST), lDir = DOM.getCurrentDir(); - if(pLink || lCurrentLink.target !== '_blank'){ + if(lLink || lCurrentLink.target !== '_blank'){ DOM.Images.showLoad(pNeedRefresh ? {top:true} : null); /* загружаем содержимое каталога */ @@ -536,7 +536,7 @@ CloudCmd._ajaxLoad = function(pPath, pOptions){ var lFSPath = decodeURI(pPath); lFSPath = Util.removeStr( lFSPath, CloudFunc.NOJS ); - var lCleanPath = Util.removeStr(lFSPath, CloudFunc.FS); + var lCleanPath = Util.removeStr(lFSPath, CloudFunc.FS) || '/'; Util.log ('reading dir: "' + lCleanPath + '";'); @@ -601,6 +601,7 @@ CloudCmd._createFileTable = function(pElem, pJSON, pSetCurrent){ var lElem = DOM.getById(pElem), /* getting current element if was refresh */ lPath = DOM.getByClass('path', lElem), + lCurrent = DOM.getCurrentFile(), lWasRefresh_b = lPath[0].textContent === pJSON[0].path; /* говорим построителю, @@ -618,7 +619,6 @@ CloudCmd._createFileTable = function(pElem, pJSON, pSetCurrent){ /* searching current file */ if(lWasRefresh_b){ - var lCurrent = DOM.getCurrentFile(); for(i = 0; i < lElem.childNodes.length; i++) if(lElem.childNodes[i].textContent === lCurrent.textContent){ lCurrent = lElem.childNodes[i]; diff --git a/lib/client/keyBinding.js b/lib/client/keyBinding.js index fdcf2d0c..651143ed 100644 --- a/lib/client/keyBinding.js +++ b/lib/client/keyBinding.js @@ -248,9 +248,8 @@ var CloudCommander, Util, DOM; */ Util.exec( DOM.getListener(lRefreshIcon) ); - CloudCmd._currentToParent(lSelectedName); - DOM.preventDefault(); + DOM.preventDefault(pEvent); } } From d8a053f67096945f008d60c02c3ec8992d119e8a Mon Sep 17 00:00:00 2001 From: coderaiser Date: Tue, 19 Feb 2013 05:33:19 -0500 Subject: [PATCH 257/347] Removed part of url thet says that js is disabled ("no-js") --- ChangeLog | 5 +++++ cloudcmd.js | 4 ++-- lib/client.js | 14 +++++--------- lib/client/dom.js | 3 +-- lib/client/editor/_ace.js | 17 ++++++----------- lib/cloudfunc.js | 26 +++++++------------------- lib/server/commander.js | 7 ++----- lib/util.js | 25 ++++++++++++++----------- 8 files changed, 42 insertions(+), 59 deletions(-) diff --git a/ChangeLog b/ChangeLog index c241903b..ca88af7f 100644 --- a/ChangeLog +++ b/ChangeLog @@ -141,6 +141,11 @@ directory. * Fixed bug with setting current file after refresh and refreshing dir content. +* Removed part of url thet says that js is disabled, from +now json data of file structure would be getted from +click event with ?json flag. "no-js" part of url +would not be supported anymore. + 2012.12.12, Version 0.1.8 diff --git a/cloudcmd.js b/cloudcmd.js index b1830c86..a32ea050 100644 --- a/cloudcmd.js +++ b/cloudcmd.js @@ -192,8 +192,8 @@ lRet = main.sendFile( pParams ); } else if( Util.isContainStr(p.name, FS) || Util.strCmp( p.name, '/') ){ - //if( !main.getQuery(p.request) ) - //p.request.url += '?html'; + if( !main.getQuery(p.request) ) + p.request.url += '?html'; var lName = Minify.allowed.html ? Minify.getName(INDEX) : INDEX; diff --git a/lib/client.js b/lib/client.js index f9682906..dd944682 100644 --- a/lib/client.js +++ b/lib/client.js @@ -85,6 +85,8 @@ CloudCmd._loadDir = function(pLink, pNeedRefresh){ lLink = pLink || Util.removeStr(lHref, CloudCmd.HOST), lDir = DOM.getCurrentDir(); + lLink += '?json'; + if(lLink || lCurrentLink.target !== '_blank'){ DOM.Images.showLoad(pNeedRefresh ? {top:true} : null); @@ -410,11 +412,6 @@ CloudCmd._changeLinks = function(pPanelID){ /* номер ссылки иконки обновления страницы */ lREFRESHICON = 0, - /* путь в ссылке, который говорит - * что js отключен - */ - lNoJS_s = CloudFunc.NOJS, - /* right mouse click function varible */ lOnContextMenu_f = function(pEvent){ var lReturn_b = true; @@ -453,10 +450,8 @@ CloudCmd._changeLinks = function(pPanelID){ /* if it's directory - adding json extension */ lType = lElement.parentElement.nextSibling; - if(lType && lType.textContent === ''){ - lLink = Util.removeStr(lLink, lNoJS_s); + if(lType && lType.textContent === '') lName += '.json'; - } pEvent.dataTransfer.setData("DownloadURL", 'application/octet-stream' + ':' + @@ -536,7 +531,8 @@ CloudCmd._ajaxLoad = function(pPath, pOptions){ var lFSPath = decodeURI(pPath); lFSPath = Util.removeStr( lFSPath, CloudFunc.NOJS ); - var lCleanPath = Util.removeStr(lFSPath, CloudFunc.FS) || '/'; + pPath = Util.removeStr( lFSPath, '?json' ); + var lCleanPath = Util.removeStr( pPath, CloudFunc.FS ) || '/'; Util.log ('reading dir: "' + lCleanPath + '";'); diff --git a/lib/client/dom.js b/lib/client/dom.js index d889539f..58c06507 100644 --- a/lib/client/dom.js +++ b/lib/client/dom.js @@ -1067,9 +1067,8 @@ var CloudCommander, Util, lPath = DOM.getCurrentLink( lCurrent ).href; /* убираем адрес хоста*/ lPath = Util.removeStr(lPath, CloudCommander.HOST); - lPath = Util.removeStr(lPath, CloudFunc.NOJS); - return lPath; + return lPath; }; /** diff --git a/lib/client/editor/_ace.js b/lib/client/editor/_ace.js index 6ff8cd18..75097f2f 100644 --- a/lib/client/editor/_ace.js +++ b/lib/client/editor/_ace.js @@ -107,17 +107,12 @@ var CloudCommander, CloudFunc, ace; /* if directory - load json * not html data */ - if (lSize === ''){ - if (lA.indexOf(CloudFunc.NOJS) === - CloudFunc.FS.length) { - lA = lA.replace(CloudFunc.NOJS, ''); - /* when folder view - * is no need to edit - * data - */ - ReadOnly = true; - } - } + if (lSize === '') + /* when folder view + * is no need to edit + * data + */ + ReadOnly = true; } this.loading = true; diff --git a/lib/cloudfunc.js b/lib/cloudfunc.js index 1089a9df..137cfbba 100644 --- a/lib/cloudfunc.js +++ b/lib/cloudfunc.js @@ -19,7 +19,6 @@ var CloudFunc, exports; CloudFunc.NAME = 'Cloud Commander'; /* если в ссылке будет эта строка - в браузере js отключен */ - CloudFunc.NOJS = '/no-js'; CloudFunc.FS = '/fs'; /* название css-класа кнопки обновления файловой структуры*/ @@ -238,16 +237,13 @@ var CloudFunc, exports; lTitle = '" title="', _l = '">', lHrefEnd ='', - + lHtmlPath, - /* путь в ссылке, который говорит что js отключен */ - lNoJS_s = CloudFunc.NOJS, lFS_s = CloudFunc.FS; /* корневой каталог */ lHtmlPath = lHref + lFS_s + - lNoJS_s + lTitle + '/' + _l + @@ -259,11 +255,9 @@ var CloudFunc, exports; var lUrl=folders[i], lShortName = lUrl.replace(lUrl.substr(lUrl,lUrl.lastIndexOf('/')+1),''); if (i!==1) - lHtmlPath += lHref + - lFS_s + lNoJS_s + lUrl + - lTitle + lUrl + _l + - lShortName + - lHrefEnd + '/'; + lHtmlPath += lHref + lFS_s + lUrl + + lTitle + lUrl + _l + + lShortName + lHrefEnd + '/'; else lHtmlPath+=lShortName+'/'; } @@ -315,11 +309,6 @@ var CloudFunc, exports; * если он есть */ lRefreshPath = CloudFunc.removeLastSlash(lPath), - - /* путь в ссылке, который говорит - * что js отключен - */ - lNoJS_s = CloudFunc.NOJS, lFS_s = CloudFunc.FS, lFileTable = @@ -330,7 +319,7 @@ var CloudFunc, exports; '' + '' + - '' + + '' + '' + '' + '' + lHtmlPath + '' + @@ -354,7 +343,7 @@ var CloudFunc, exports; if(lDotDot === '') lDotDot = '/'; - lLink = lFS_s + lNoJS_s + lDotDot; + lLink = lFS_s + lDotDot; /* Сохраняем путь к каталогу верхнего уровня*/ @@ -380,8 +369,7 @@ var CloudFunc, exports; '">'; lFileTable += ''; lFileTable += '' + - ' Date: Tue, 19 Feb 2013 05:41:09 -0500 Subject: [PATCH 258/347] minor changes --- lib/client.js | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/lib/client.js b/lib/client.js index dd944682..3b52ddf7 100644 --- a/lib/client.js +++ b/lib/client.js @@ -528,17 +528,17 @@ CloudCmd._ajaxLoad = function(pPath, pOptions){ pOptions = {}; /* Отображаем красивые пути */ - var lFSPath = decodeURI(pPath); - lFSPath = Util.removeStr( lFSPath, CloudFunc.NOJS ); - pPath = Util.removeStr( lFSPath, '?json' ); - var lCleanPath = Util.removeStr( pPath, CloudFunc.FS ) || '/'; + var lFSPath = decodeURI(pPath), + lNOJSPath = Util.removeStr( lFSPath, '?json' ), + lCleanPath = Util.removeStr( lNOJSPath, CloudFunc.FS ) || '/'; + Util.log ('reading dir: "' + lCleanPath + '";'); if(!pOptions.nohistory){ - pPath = lCleanPath === '/' ? '/' : pPath; - DOM.setHistory(pPath, null, pPath); + lNOJSPath = lCleanPath === '/' ? '/' : lNOJSPath; + DOM.setHistory(lNOJSPath, null, lNOJSPath); } DOM.setTitle( CloudFunc.getTitle(lCleanPath) ); From d8a58784e903cd8a7e5b327c0e4d9cd658c8580b Mon Sep 17 00:00:00 2001 From: coderaiser Date: Tue, 19 Feb 2013 05:51:27 -0500 Subject: [PATCH 259/347] minor changes --- lib/client.js | 51 ++++++++++++++++++++++------------------------- lib/client/dom.js | 2 +- 2 files changed, 25 insertions(+), 28 deletions(-) diff --git a/lib/client.js b/lib/client.js index 3b52ddf7..fbd8d591 100644 --- a/lib/client.js +++ b/lib/client.js @@ -543,20 +543,16 @@ CloudCmd._ajaxLoad = function(pPath, pOptions){ DOM.setTitle( CloudFunc.getTitle(lCleanPath) ); + var lPanel = DOM.getPanel().id; /* если доступен localStorage и * в нём есть нужная нам директория - * читаем данные с него и * выходим * если стоит поле обязательной перезагрузки - * перезагружаемся - */ - - /* опредиляем в какой мы панели: - * правой или левой - */ - var lPanel = DOM.getPanel().id; - - if(!pOptions.refresh && lPanel){ + */ + var lRet = pOptions.refresh; + if(!lRet){ var lJSON = DOM.Cache.get(pPath); if (lJSON){ @@ -564,28 +560,29 @@ CloudCmd._ajaxLoad = function(pPath, pOptions){ lJSON = Util.parseJSON(lJSON); CloudCmd._createFileTable(lPanel, lJSON, true); CloudCmd._changeLinks(lPanel); - - return; } + else + lRet = true; } - DOM.getCurrentFileContent({ - url : lFSPath, - success : function(pData){ - CloudCmd._createFileTable(lPanel, pData, true); - CloudCmd._changeLinks(lPanel); - - /* переводим таблицу файлов в строку, для * - * сохранения в localStorage */ - var lJSON_s = Util.stringifyJSON(pData); - Util.log(lJSON_s.length); - - /* если размер данных не очень бошьой * - * сохраняем их в кэше */ - if(lJSON_s.length < 50000 ) - DOM.Cache.set(pPath, lJSON_s); - } - }); + if(lRet) + DOM.getCurrentFileContent({ + url : lFSPath, + success : function(pData){ + CloudCmd._createFileTable(lPanel, pData, true); + CloudCmd._changeLinks(lPanel); + + /* переводим таблицу файлов в строку, для * + * сохранения в localStorage */ + var lJSON_s = Util.stringifyJSON(pData); + Util.log(lJSON_s.length); + + /* если размер данных не очень бошьой * + * сохраняем их в кэше */ + if(lJSON_s.length < 50000 ) + DOM.Cache.set(pPath, lJSON_s); + } + }); }; /** diff --git a/lib/client/dom.js b/lib/client/dom.js index 58c06507..25021a72 100644 --- a/lib/client/dom.js +++ b/lib/client/dom.js @@ -885,7 +885,7 @@ var CloudCommander, Util, DOM.getCurrentFileContent = function(pParams, pCurrentFile){ var lRet, lParams = pParams ? pParams : {}, - lPath = DOM.getCurrentPath(pCurrentFile), + lPath = DOM.getCurrentPath(pCurrentFile) + '?json', lErrorWas = pParams.error, lError = function(jqXHR){ Util.exec(lErrorWas); From 11abaf2df6189117ca99b248ca7de77a42f9463b Mon Sep 17 00:00:00 2001 From: coderaiser Date: Tue, 19 Feb 2013 05:52:49 -0500 Subject: [PATCH 260/347] minor changes --- lib/client/dom.js | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/lib/client/dom.js b/lib/client/dom.js index 25021a72..ddecac5b 100644 --- a/lib/client/dom.js +++ b/lib/client/dom.js @@ -885,6 +885,11 @@ var CloudCommander, Util, DOM.getCurrentFileContent = function(pParams, pCurrentFile){ var lRet, lParams = pParams ? pParams : {}, + /* adding ?json param + * if file would be ignored and + * if directory would be + * geted back in json format + */ lPath = DOM.getCurrentPath(pCurrentFile) + '?json', lErrorWas = pParams.error, lError = function(jqXHR){ From fa84e43fb2057093a1b45cdf7f7a3cf783751877 Mon Sep 17 00:00:00 2001 From: coderaiser Date: Tue, 19 Feb 2013 06:09:12 -0500 Subject: [PATCH 261/347] fixed link on doc --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index ad96c9e8..acf1df32 100644 --- a/README.md +++ b/README.md @@ -56,7 +56,7 @@ Editor's hot keys Documentation --------------- -JS Doc documentation could be found in [http://jsdoc.info/coderaiser/cloudcmd/](//jsdoc.info/coderaiser/cloudcmd/) +JS Doc documentation could be found in [http://jsdoc.info/coderaiser/cloudcmd/](http://jsdoc.info/coderaiser/cloudcmd/) Installing --------------- From 34ba09616325f92c8d6b4308be4202a38ffbce43 Mon Sep 17 00:00:00 2001 From: coderaiser Date: Tue, 19 Feb 2013 07:52:13 -0500 Subject: [PATCH 262/347] added ability to add ?json flag only if we work with dir --- ChangeLog | 2 ++ lib/client/dom.js | 40 +++++++++++++++++++++++++++------------- lib/server/commander.js | 18 +++--------------- 3 files changed, 32 insertions(+), 28 deletions(-) diff --git a/ChangeLog b/ChangeLog index ca88af7f..5eb6efe6 100644 --- a/ChangeLog +++ b/ChangeLog @@ -146,6 +146,8 @@ now json data of file structure would be getted from click event with ?json flag. "no-js" part of url would not be supported anymore. +* Added ability to add ?json flag only if we work with dir. + 2012.12.12, Version 0.1.8 diff --git a/lib/client/dom.js b/lib/client/dom.js index ddecac5b..bd586171 100644 --- a/lib/client/dom.js +++ b/lib/client/dom.js @@ -884,15 +884,11 @@ var CloudCommander, Util, */ DOM.getCurrentFileContent = function(pParams, pCurrentFile){ var lRet, - lParams = pParams ? pParams : {}, - /* adding ?json param - * if file would be ignored and - * if directory would be - * geted back in json format - */ - lPath = DOM.getCurrentPath(pCurrentFile) + '?json', - lErrorWas = pParams.error, - lError = function(jqXHR){ + lCurrentFile = pCurrentFile ? pCurrentFile : DOM.getCurrentFile(), + lParams = pParams ? pParams : {}, + lPath = DOM.getCurrentPath(lCurrentFile), + lErrorWas = pParams.error, + lError = function(jqXHR){ Util.exec(lErrorWas); DOM.Images.showError(jqXHR); }; @@ -901,6 +897,10 @@ var CloudCommander, Util, lParams.error = lError; + + if( DOM.isCurrentDir(lCurrentFile) ) + lPath += '?json'; + if(!lParams.url) lParams.url = lPath; @@ -916,9 +916,11 @@ var CloudCommander, Util, * @pCurrentFile */ DOM.getCurrentData = function(pCallBack, pCurrentFile){ + var lParams, + lCurrentFile = pCurrentFile ? pCurrentFile : DOM.getCurrentFile(), lFunc = function(pData){ - var lName = DOM.getCurrentName(pCurrentFile); + var lName = DOM.getCurrentName(lCurrentFile); if( Util.isObject(pData) ){ pData = Util.stringifyJSON(pData); @@ -942,7 +944,7 @@ var CloudCommander, Util, }; - return DOM.getCurrentFileContent(lParams, pCurrentFile); + return DOM.getCurrentFileContent(lParams, lCurrentFile); }; /** @@ -1042,12 +1044,24 @@ var CloudCommander, Util, * @param pCurrentFile */ DOM.isCurrentFile = function(pCurrentFile){ - var lCurrentFileClass = pCurrentFile.className, - lIsCurrent = lCurrentFileClass.indexOf(CURRENT_FILE) >= 0; + var lClass = pCurrentFile && pCurrentFile.className, + lIsCurrent = lClass && lClass.indexOf(CURRENT_FILE) >= 0; return lIsCurrent; }; + /** + * check is current file is a directory + * + * @param pCurrentFile + */ + DOM.isCurrentDir = function(pCurrentFile){ + var lSize = DOM.getCurrentSize(pCurrentFile), + lRet = lSize === ''; + + return lRet; + }; + /** * get link from current (or param) file diff --git a/lib/server/commander.js b/lib/server/commander.js index 3f9e2678..9c24a027 100644 --- a/lib/server/commander.js +++ b/lib/server/commander.js @@ -212,11 +212,8 @@ lJSON[i+1] = lJSONFile; } - /* если js недоступен - * или отключен выcылаем html-код - * и прописываем соответствующие заголовки - */ - if( noJS(lReq) ){ + /* если js отключен выcылаем html-код */ + if( main.getQuery(lReq) === 'html' ){ var lPanel = CloudFunc.buildFromJSON(lJSON); lList = '
      ' + lPanel + '
    ' + ''; @@ -385,6 +382,7 @@ return lRet; } + function getDirPath(pReq){ var lRet = getCleanPath(pReq); @@ -394,16 +392,6 @@ return lRet; } - function noJS(pReq){ - var lNoJS, lPath; - if(pReq){ - lPath = getPath(pReq); - - lNoJS = lPath === '/' || main.getQuery(pReq) === 'html'; - } - - return lNoJS; - } function isGZIP(pReq){ var lEnc, lGZIP; From 71f6fd57c46250408c1f5dae3943db289b6c161f Mon Sep 17 00:00:00 2001 From: coderaiser Date: Tue, 19 Feb 2013 08:11:29 -0500 Subject: [PATCH 263/347] minor changes --- cloudcmd.js | 7 ++++++- lib/client.js | 9 +++++---- 2 files changed, 11 insertions(+), 5 deletions(-) diff --git a/cloudcmd.js b/cloudcmd.js index a32ea050..52edd352 100644 --- a/cloudcmd.js +++ b/cloudcmd.js @@ -192,8 +192,13 @@ lRet = main.sendFile( pParams ); } else if( Util.isContainStr(p.name, FS) || Util.strCmp( p.name, '/') ){ - if( !main.getQuery(p.request) ) + var lQuery = main.getQuery(p.request); + + if( !lQuery ) p.request.url += '?html'; + else if(lQuery === '?download') + lQuery += '&&html'; + var lName = Minify.allowed.html ? Minify.getName(INDEX) : INDEX; diff --git a/lib/client.js b/lib/client.js index fbd8d591..ff652cab 100644 --- a/lib/client.js +++ b/lib/client.js @@ -446,12 +446,13 @@ CloudCmd._changeLinks = function(pPanelID){ lOnDragStart_f = function(pEvent){ var lElement = pEvent.target, lLink = lElement.href, - lName = lElement.textContent, - /* if it's directory - adding json extension */ - lType = lElement.parentElement.nextSibling; + lName = lElement.textContent; - if(lType && lType.textContent === '') + /* if it's directory - adding json extension */ + if( DOM.isCurrentDir() ){ lName += '.json'; + lLink += '?json'; + } pEvent.dataTransfer.setData("DownloadURL", 'application/octet-stream' + ':' + From 70f6db5cb25cacd4a90ccf665de81c858d3cc61b Mon Sep 17 00:00:00 2001 From: coderaiser Date: Tue, 19 Feb 2013 09:19:09 -0500 Subject: [PATCH 264/347] added ability not to change url if we have no rights for reading --- ChangeLog | 3 +++ cloudcmd.js | 10 +++------- lib/client.js | 10 ++++++++-- lib/client/dom.js | 6 +++--- 4 files changed, 17 insertions(+), 12 deletions(-) diff --git a/ChangeLog b/ChangeLog index 5eb6efe6..33597cb7 100644 --- a/ChangeLog +++ b/ChangeLog @@ -148,6 +148,9 @@ would not be supported anymore. * Added ability to add ?json flag only if we work with dir. +* Added ability not to change url if we have no rights +for reading. + 2012.12.12, Version 0.1.8 diff --git a/cloudcmd.js b/cloudcmd.js index 52edd352..adc14c4c 100644 --- a/cloudcmd.js +++ b/cloudcmd.js @@ -192,16 +192,12 @@ lRet = main.sendFile( pParams ); } else if( Util.isContainStr(p.name, FS) || Util.strCmp( p.name, '/') ){ - var lQuery = main.getQuery(p.request); + var lQuery = main.getQuery(p.request), + lName = Minify.allowed.html ? + Minify.getName(INDEX) : INDEX; if( !lQuery ) p.request.url += '?html'; - else if(lQuery === '?download') - lQuery += '&&html'; - - - var lName = Minify.allowed.html ? - Minify.getName(INDEX) : INDEX; lRet = main.commander.sendContent({ request : p.request, diff --git a/lib/client.js b/lib/client.js index ff652cab..f2f748f2 100644 --- a/lib/client.js +++ b/lib/client.js @@ -532,11 +532,12 @@ CloudCmd._ajaxLoad = function(pPath, pOptions){ var lFSPath = decodeURI(pPath), lNOJSPath = Util.removeStr( lFSPath, '?json' ), - lCleanPath = Util.removeStr( lNOJSPath, CloudFunc.FS ) || '/'; + lCleanPath = Util.removeStr( lNOJSPath, CloudFunc.FS ) || '/', + + lOldURL = window.location.pathname; Util.log ('reading dir: "' + lCleanPath + '";'); - if(!pOptions.nohistory){ lNOJSPath = lCleanPath === '/' ? '/' : lNOJSPath; DOM.setHistory(lNOJSPath, null, lNOJSPath); @@ -569,6 +570,11 @@ CloudCmd._ajaxLoad = function(pPath, pOptions){ if(lRet) DOM.getCurrentFileContent({ url : lFSPath, + + error : function(){ + DOM.setHistory(lOldURL, null, lOldURL); + }, + success : function(pData){ CloudCmd._createFileTable(lPanel, pData, true); CloudCmd._changeLinks(lPanel); diff --git a/lib/client/dom.js b/lib/client/dom.js index bd586171..03695be4 100644 --- a/lib/client/dom.js +++ b/lib/client/dom.js @@ -585,8 +585,8 @@ var CloudCommander, Util, */ var lLoad = function(pEvent){ - DOM.removeListener('load', lLoad, false, lElement); - DOM.removeListener('error', lError, false, lElement); + DOM.removeListener('load', lLoad, lElement); + DOM.removeListener('error', lError, lElement); Util.exec(lFunc, pEvent); }, @@ -605,7 +605,7 @@ var CloudCommander, Util, }; DOM.addListener('load', lLoad, lElement); - DOM.addErrorListener(lError,lElement); + DOM.addErrorListener(lError, lElement); if(lStyle) lElement.style.cssText = lStyle; From c1dd6513254d1dc630ca45244e194d1874dca938 Mon Sep 17 00:00:00 2001 From: coderaiser Date: Tue, 19 Feb 2013 09:22:50 -0500 Subject: [PATCH 265/347] fixed bug with pressing enter on file --- ChangeLog | 2 ++ lib/client.js | 2 +- lib/client/dom.js | 4 ++-- lib/client/keyBinding.js | 3 ++- 4 files changed, 7 insertions(+), 4 deletions(-) diff --git a/ChangeLog b/ChangeLog index 33597cb7..8fa38baa 100644 --- a/ChangeLog +++ b/ChangeLog @@ -151,6 +151,8 @@ would not be supported anymore. * Added ability not to change url if we have no rights for reading. +* Fixed bug with pressing enter on file. + 2012.12.12, Version 0.1.8 diff --git a/lib/client.js b/lib/client.js index f2f748f2..afdbd97d 100644 --- a/lib/client.js +++ b/lib/client.js @@ -449,7 +449,7 @@ CloudCmd._changeLinks = function(pPanelID){ lName = lElement.textContent; /* if it's directory - adding json extension */ - if( DOM.isCurrentDir() ){ + if( DOM.isCurrentIsDir() ){ lName += '.json'; lLink += '?json'; } diff --git a/lib/client/dom.js b/lib/client/dom.js index 03695be4..6c198f15 100644 --- a/lib/client/dom.js +++ b/lib/client/dom.js @@ -898,7 +898,7 @@ var CloudCommander, Util, lParams.error = lError; - if( DOM.isCurrentDir(lCurrentFile) ) + if( DOM.isCurrentIsDir(lCurrentFile) ) lPath += '?json'; if(!lParams.url) @@ -1055,7 +1055,7 @@ var CloudCommander, Util, * * @param pCurrentFile */ - DOM.isCurrentDir = function(pCurrentFile){ + DOM.isCurrentIsDir = function(pCurrentFile){ var lSize = DOM.getCurrentSize(pCurrentFile), lRet = lSize === ''; diff --git a/lib/client/keyBinding.js b/lib/client/keyBinding.js index 651143ed..beb39b29 100644 --- a/lib/client/keyBinding.js +++ b/lib/client/keyBinding.js @@ -219,7 +219,8 @@ var CloudCommander, Util, DOM; /* если нажали Enter - открываем папку*/ else if(lKeyCode === KEY.ENTER) - Util.exec(CloudCmd._loadDir()); + if(DOM.isCurrentIsDir()) + Util.exec(CloudCmd._loadDir()); /* если нажали +r * обновляем страницу, From fb3ed102e9c04bb790b0b2de68ac1faef75b1060 Mon Sep 17 00:00:00 2001 From: coderaiser Date: Wed, 20 Feb 2013 05:50:53 -0500 Subject: [PATCH 266/347] minor changes --- lib/client/keyBinding.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/client/keyBinding.js b/lib/client/keyBinding.js index beb39b29..76585b25 100644 --- a/lib/client/keyBinding.js +++ b/lib/client/keyBinding.js @@ -1,6 +1,6 @@ var CloudCommander, Util, DOM; (function(CloudCmd, Util, DOM){ - "use strict"; + 'use strict'; DOM.Images.hideLoad(); From 14f32dd34a499e606b9d2278c6d497f3c65fa29c Mon Sep 17 00:00:00 2001 From: coderaiser Date: Wed, 20 Feb 2013 05:52:43 -0500 Subject: [PATCH 267/347] minor changes --- lib/client/keyBinding.js | 2 -- 1 file changed, 2 deletions(-) diff --git a/lib/client/keyBinding.js b/lib/client/keyBinding.js index 76585b25..bfc0bf7d 100644 --- a/lib/client/keyBinding.js +++ b/lib/client/keyBinding.js @@ -2,8 +2,6 @@ var CloudCommander, Util, DOM; (function(CloudCmd, Util, DOM){ 'use strict'; - DOM.Images.hideLoad(); - /* private property set or set key binding */ var keyBinded; From 263684ac84cd3d57aa2aa577f7a299a71cae729e Mon Sep 17 00:00:00 2001 From: coderaiser Date: Wed, 20 Feb 2013 05:54:09 -0500 Subject: [PATCH 268/347] minor changes --- lib/client/keyBinding.js | 4 ---- 1 file changed, 4 deletions(-) diff --git a/lib/client/keyBinding.js b/lib/client/keyBinding.js index bfc0bf7d..60c45535 100644 --- a/lib/client/keyBinding.js +++ b/lib/client/keyBinding.js @@ -237,15 +237,11 @@ var CloudCommander, Util, DOM; */ var lRefreshIcon = DOM.getRefreshButton(); if(lRefreshIcon){ - /* получаем название файла*/ - var lSelectedName = DOM.getCurrentName(lCurrentFile); - /* если нашли элемент нажимаем него * а если не можем - нажимаем на * ссылку, на которую повешен eventHandler * onclick */ - Util.exec( DOM.getListener(lRefreshIcon) ); DOM.preventDefault(pEvent); From cec7abca94fd4b8773cfdfef05c5ffe17bb55307 Mon Sep 17 00:00:00 2001 From: coderaiser Date: Wed, 20 Feb 2013 06:29:03 -0500 Subject: [PATCH 269/347] updated jquery jQuery-contextMenu to v1.6.5 --- ChangeLog | 2 + lib/client/menu/README.md | 138 +- lib/client/menu/contextMenu.css | 284 +-- lib/client/menu/contextMenu.js | 3269 ++++++++++++++++--------------- lib/client/menu/package.json | 2 +- lib/client/menu/ui.position.js | 517 ----- 6 files changed, 1929 insertions(+), 2283 deletions(-) delete mode 100644 lib/client/menu/ui.position.js diff --git a/ChangeLog b/ChangeLog index 8fa38baa..9827acbe 100644 --- a/ChangeLog +++ b/ChangeLog @@ -153,6 +153,8 @@ for reading. * Fixed bug with pressing enter on file. +* Updated jquery jQuery-contextMenu to v1.6.5. + 2012.12.12, Version 0.1.8 diff --git a/lib/client/menu/README.md b/lib/client/menu/README.md index 5471c487..c3551ada 100644 --- a/lib/client/menu/README.md +++ b/lib/client/menu/README.md @@ -69,7 +69,7 @@ You're (obviously) able to use the context menu with your mouse. Once it is open * ⇞ (page up) captured and ignore to avoid page scrolling (for consistency with native menus) * ⇟ (page down) captured and ignore to avoid page scrolling (for consistency with native menus) * ↖ (home) first item in list, will skip disabled elements -* ↘ (end) last item in, will skip disabled elements +* ↘ (end) last item in list, will skip disabled elements Besides the obvious, browser also react to alphanumeric key strokes. Hitting r in a context menu will make Firefox (8) reload the page immediately. Chrome selects the option to see infos on the page, Safari selects the option to print the document. Awesome, right? Until trying the same on Windows I did not realize that the browsers were using the access-key for this. I would've preferred typing the first character of something, say "s" for "save" and then iterate through all the commands beginning with s. But that's me - what do I know about UX? Anyways, $.contextMenu now also supports accesskey handling. @@ -82,9 +82,9 @@ use [Google Closure Compiler](http://closure-compiler.appspot.com/home): // ==ClosureCompiler== // @compilation_level SIMPLE_OPTIMIZATIONS // @output_file_name contextMenu.js -// @code_url http://medialize.github.com/jQuery-contextMenu/jquery-1.7.1.min.js -// @code_url http://medialize.github.com/jQuery-contextMenu/jquery.ui.position.js -// @code_url http://medialize.github.com/jQuery-contextMenu/jquery.contextMenu.js +// @code_url http://medialize.github.com/jQuery-contextMenu/jquery-1.8.2.min.js +// @code_url http://medialize.github.com/jQuery-contextMenu/src/jquery.ui.position.js +// @code_url http://medialize.github.com/jQuery-contextMenu/src/jquery.contextMenu.js // ==/ClosureCompiler== ``` @@ -103,155 +103,213 @@ $.contextMenu is published under the [MIT license](http://www.opensource.org/lic ## Changelog ## -### 1.5.22 ### +### 1.6.5 (January 20th 2013) ### -* Fixing issue with animation and remove on hide (Issue #64) +* fixing "opening a second menu can break the layer" - ([Issue #105](https://github.com/medialize/jQuery-contextMenu/issues/105)) -### 1.5.21 ### +### 1.6.4 (January 19th 2013) ### -* Fixing backdrop would not remove on destroy (Issue #63) +* fixing [jQuery plugin manifest](https://github.com/medialize/jQuery-contextMenu/commit/413b1ecaba0aeb4e50f97cee35f7c367435e7830#commitcomment-2465216), again. yep. I'm that kind of a guy. :( -### 1.5.20 ### +### 1.6.3 (January 19th 2013) ### -* Fixing backdrop would not position properly in IE6 (Issue #59) -* Fixing nested input elements not accessible in Chrome / Safari (Issue #58) +* fixing [jQuery plugin manifest](https://github.com/medialize/jQuery-contextMenu/commit/413b1ecaba0aeb4e50f97cee35f7c367435e7830#commitcomment-2465216) + +### 1.6.2 (January 19th 2013) ### + +* fixing "menu won't close" regression introduced by 1.6.1 + +### 1.6.1 (January 19th 2013) ### + +* fixing potential html parsing problem +* upgrading to jQuery UI position v1.10.0 +* replaced `CRLF` by `LF` (no idea how this happened in the first place...) +* adding `options.reposition` to dis/allow simply relocating a menu instead of rebuilding it ([Issue #104](https://github.com/medialize/jQuery-contextMenu/issues/104)) + +### 1.6.0 (December 29th 2012) ### + +* adding [DOM Element bound context menus](http://medialize.github.com/jQuery-contextMenu/demo/on-dom-element.html) - ([Issue 88](https://github.com/medialize/jQuery-contextMenu/issues/88)) +* adding class `context-menu-active` to define state on active trigger element - ([Issue 92](https://github.com/medialize/jQuery-contextMenu/issues/92)) +* adding [demo for TouchSwipe](http://medialize.github.com/jQuery-contextMenu/demo/trigger-swipe.html) activation +* adding export of internal functions and event handlers - ([Issue 101](https://github.com/medialize/jQuery-contextMenu/issues/101)) +* fixing key "watch" might translate to Object.prototype.watch in callbacks map - ([Issue 93](https://github.com/medialize/jQuery-contextMenu/issues/93)) +* fixing menu and submenu width calculation - ([Issue 18](https://github.com/medialize/jQuery-contextMenu/issues/18)) +* fixing unused variables - ([Issue 100](https://github.com/medialize/jQuery-contextMenu/issues/100)) +* fixing iOS "click" compatibility problem - ([Issue 83](https://github.com/medialize/jQuery-contextMenu/issues/83)) +* fixing separators to not be clickable - ([Issue 85](https://github.com/medialize/jQuery-contextMenu/issues/85)) +* fixing issues with fixed positioned triggers ([Issue 95](https://github.com/medialize/jQuery-contextMenu/issues/95)) +* fixing word break problem - ([Issue 80](https://github.com/medialize/jQuery-contextMenu/issues/80)) + +### 1.5.25 (October 8th 2012) ### + +* upgrading to jQuery 1.8.2 ([Issue 78](https://github.com/medialize/jQuery-contextMenu/issues/78)) +* upgrading to jQuery UI position 1.9.0 RC1 ([Issue 78](https://github.com/medialize/jQuery-contextMenu/issues/78)) + +### 1.5.24 (August 30th 2012) ### + +* adding context menu options to input command events ([Issue 72](https://github.com/medialize/jQuery-contextMenu/issues/72), dtex) +* code cosmetics for JSLint + +### 1.5.23 (August 22nd 2012) ### + +* fixing reposition/close issue on scrolled documents ([Issue 69](https://github.com/medialize/jQuery-contextMenu/issues/69)) +* fixing jQuery reference ([Issue 68](https://github.com/medialize/jQuery-contextMenu/issues/68)) + +### 1.5.22 (July 16th 2012) ### + +* fixing issue with animation and remove on hide (Issue #64) + +### 1.5.21 (July 14th 2012) ### + +* fixing backdrop would not remove on destroy (Issue #63) + +### 1.5.20 (June 26th 2012) ### + +Note: git tag of version is `v1.6.20`?! + +* fixing backdrop would not position properly in IE6 (Issue #59) +* fixing nested input elements not accessible in Chrome / Safari (Issue #58) ### 1.5.19 ### +Note: git tag of version is missing...?! + * fixing sub-menu positioning when `$.ui.position` is not available (Issue #56) ### 1.5.18 ### +Note: git tag of version is missing...?! + * fixing html5 `` import (Issue #53) -### 1.5.17 ### +### 1.5.17 (June 4th 2012) ### * fixing `options` to default to `options.trigger = "right"` * fixing variable name typo (Within Issue #51) * fixing menu not closing while opening other menu (Within Issue #51) * adding workaround for `contextmenu`-bug in Firefox 12 (Within Issue #51) -### 1.5.16 ### +### 1.5.16 (May 29th 2012) ### * added vendor-prefixed user-select to CSS * fixed issue with z-indexing when `` is used as a trigger (Issue #49) -### 1.5.15 ### +### 1.5.15 (May 26th 2012) ### * allowing to directly open another element's menu while a menu is shown (Issue #48) * fixing autohide option that would not properly hide the menu -### 1.5.14 ### +### 1.5.14 (May 22nd 2012) ### * options.build() would break default options (Issue #47) * $.contextMenu('destroy') would not remove backdrop -### 1.5.13 ### +### 1.5.13 (May 4th 2012) ### * exposing $trigger to dynamically built custom menu-item types (Issue #42) * fixing repositioning of open menu (formerly accidental re-open) * adding asynchronous example * dropping ignoreRightClick in favor of proper event-type detection -### 1.5.12 ### +### 1.5.12 (May 2nd 2012) ### * prevent invoking callback of first item of a sub-menu when clicking on the sub-menu-item (Issue #41) -### 1.5.11 ### +### 1.5.11 (April 27th 2012) ### * providing `opt.$trigger` to show event (Issue #39) -### 1.5.10 ### +### 1.5.10 (April 21st 2012) ### * ignoreRightClick would not prevent right click when menu is already open (Issue #38) -### 1.5.9 ### +### 1.5.9 (March 10th 2012) ### * If build() did not return any items, an empty menu was shown (Issue #33) -### 1.5.8 ### +### 1.5.8 (January 28th 2012) ### * Capturing Page Up and Page Down keys to ignore like space (Issue #30) * Added Home / End keys to jump to first / last command of menu (Issue #29) * Bug hitting enter in an <input> would yield an error (Issue #28) -### 1.5.7 ### +### 1.5.7 (January 21st 2012) ### * Non-ASCII character in jquery.contextMenu.js caused compatibility issues in Rails (Issue #27) -### 1.5.6 ### +### 1.5.6 (January 8th 2012) ### * Bug contextmenu event was not passed to build() callback (Issue #24) * Bug sub-menu markers would not display properly in Safari and Chrome (Issue #25) -### 1.5.5 ### +### 1.5.5 (January 6th 2012) ### * Bug Internet Explorer would not close menu when giving input elements focus (Issue #23) -### 1.5.4 ### +### 1.5.4 (January 5th 2012) ## * Bug not set z-index of sub-menus might not overlap the main menu correctly (Issue #22) -### 1.5.3 ### +### 1.5.3 (January 1st 2012) ### * Bug `console.log is undefined` -### 1.5.2 ### +### 1.5.2 (December 25th 2012) ### * Bug sub-menus would not properly update their disabled states (Issue #16) [again…] * Bug sub-menus would not properly adjust width accoring to min-width and max-width (Issue #18) -### 1.5.1 ### +### 1.5.1 (December 18th 2011) ### * Bug sub-menus would not properly update their disabled states (Issue #16) -### 1.5 ### +### 1.5 (December 13th 2011) ### * Added [dynamic menu creation](http://medialize.github.com/jQuery-contextMenu/demo/dynamic-create.html) (Issue #15) -### 1.4.4 ### +### 1.4.4 (December 12th 2011) ### * Bug positioning <menu> when trigger element is `position:fixed` (Issue #14) -### 1.4.3 ### +### 1.4.3 (December 11th 2011) ### * Bug key handler would caputure all key strokes while menu was visible (essentially disabling F5 and co.) -### 1.4.2 ### +### 1.4.2 (December 6th 2011) ### * Bug opt.$trigger was not available to disabled callbacks * jQuery bumped to 1.7.1 -### 1.4.1 ### +### 1.4.1 (November 9th 2011) ### * Bug where <menu> imports would not pass action (click event) properly -### 1.4 ### +### 1.4 (November 7th 2011) ### * Upgraded to jQuery 1.7 (changed dependecy!) * Added internal events `contextmenu:focus`, `contextmenu:blur` and `contextmenu:hide` * Added custom <command> types * Bug where `className` wasn't properly set on <menu> -### 1.3 ### +### 1.3 (September 5th 2011) ### * Added support for accesskeys * Bug where two sub-menus could be open simultaneously -### 1.2.2 ### +### 1.2.2 (August 24th 2011) ### * Bug in HTML5 import -### 1.2.1 ### +### 1.2.1 (August 24th 2011) ### * Bug in HTML5 detection -### 1.2 ### +### 1.2 (August 24th 2011) ### * Added compatibility to <menuitem> for Firefox 8 * Upgraded to jQuery 1.6.2 -### 1.1 ### +### 1.1 (August 11th 2011) ### * Bug #1 TypeError on HTML5 action passthru * Bug #2 disbaled callback not invoked properly @@ -259,6 +317,6 @@ $.contextMenu is published under the [MIT license](http://www.opensource.org/lic * Feature #4 option to use a single callback for all commands, rather than registering the same function for each item * Option to ignore right-click (original "contextmenu" event trigger) for non-right-click triggers -### 1.0 ### +### 1.0 (July 7th 2011) ### * Initial $.contextMenu handler \ No newline at end of file diff --git a/lib/client/menu/contextMenu.css b/lib/client/menu/contextMenu.css index 04f7f015..ee58116f 100644 --- a/lib/client/menu/contextMenu.css +++ b/lib/client/menu/contextMenu.css @@ -1,142 +1,142 @@ -/*! - * jQuery contextMenu - Plugin for simple contextMenu handling - * - * Version: 1.5.22 - * - * Authors: Rodney Rehm, Addy Osmani (patches for FF) - * Web: http://medialize.github.com/jQuery-contextMenu/ - * - * Licensed under - * MIT License http://www.opensource.org/licenses/mit-license - * GPL v3 http://opensource.org/licenses/GPL-3.0 - * - */ - -.context-menu-list { - margin:0; - padding:0; - - min-width: 120px; - max-width: 250px; - display: inline-block; - position: absolute; - list-style-type: none; - - border: 1px solid #DDD; - background: #EEE; - - -webkit-box-shadow: 0 2px 5px rgba(0, 0, 0, 0.5); - -moz-box-shadow: 0 2px 5px rgba(0, 0, 0, 0.5); - -ms-box-shadow: 0 2px 5px rgba(0, 0, 0, 0.5); - -o-box-shadow: 0 2px 5px rgba(0, 0, 0, 0.5); - box-shadow: 0 2px 5px rgba(0, 0, 0, 0.5); - - font-family: Verdana, Arial, Helvetica, sans-serif; - font-size: 11px; -} - -.context-menu-item { - padding: 2px 2px 2px 24px; - background-color: #EEE; - position: relative; - -webkit-user-select: none; - -moz-user-select: -moz-none; - -ms-user-select: none; - user-select: none; -} - -.context-menu-separator { - padding-bottom:0; - border-bottom: 1px solid #DDD; -} - -.context-menu-item > label > input, -.context-menu-item > label > textarea { - -webkit-user-select: text; - -moz-user-select: text; - -ms-user-select: text; - user-select: text; -} - -.context-menu-item.hover { - cursor: pointer; - background-color: #39F; -} - -.context-menu-item.disabled { - color: #666; -} - -.context-menu-input.hover, -.context-menu-item.disabled.hover { - cursor: default; - background-color: #EEE; -} - -.context-menu-submenu:after { - content: ">"; - color: #666; - position: absolute; - top: 0; - right: 3px; - z-index: 1; -} - -/* icons - #protip: - In case you want to use sprites for icons (which I would suggest you do) have a look at - http://css-tricks.com/13224-pseudo-spriting/ to get an idea of how to implement - .context-menu-item.icon:before {} - */ -.context-menu-item.icon { min-height: 18px; background-repeat: no-repeat; background-position: 4px 2px; } -.context-menu-item.icon-edit { background-image: url(images/page_white_edit.png); } -.context-menu-item.icon-cut { background-image: url(images/cut.png); } -.context-menu-item.icon-copy { background-image: url(images/page_white_copy.png); } -.context-menu-item.icon-paste { background-image: url(images/page_white_paste.png); } -.context-menu-item.icon-delete { background-image: url(images/page_white_delete.png); } -.context-menu-item.icon-add { background-image: url(images/page_white_add.png); } -.context-menu-item.icon-quit { background-image: url(images/door.png); } - -/* vertically align inside labels */ -.context-menu-input > label > * { vertical-align: top; } - -/* position checkboxes and radios as icons */ -.context-menu-input > label > input[type="checkbox"], -.context-menu-input > label > input[type="radio"] { - margin-left: -17px; -} -.context-menu-input > label > span { - margin-left: 5px; -} - -.context-menu-input > label, -.context-menu-input > label > input[type="text"], -.context-menu-input > label > textarea, -.context-menu-input > label > select { - display: block; - width: 100%; - - -webkit-box-sizing: border-box; - -moz-box-sizing: border-box; - -ms-box-sizing: border-box; - -o-box-sizing: border-box; - box-sizing: border-box; -} - -.context-menu-input > label > textarea { - height: 100px; -} -.context-menu-item > .context-menu-list { - display: none; - /* re-positioned by js */ - right: -5px; - top: 5px; -} - -.context-menu-item.hover > .context-menu-list { - display: block; -} - -.context-menu-accesskey { - text-decoration: underline; -} +/*! + * jQuery contextMenu - Plugin for simple contextMenu handling + * + * Version: git-master + * + * Authors: Rodney Rehm, Addy Osmani (patches for FF) + * Web: http://medialize.github.com/jQuery-contextMenu/ + * + * Licensed under + * MIT License http://www.opensource.org/licenses/mit-license + * GPL v3 http://opensource.org/licenses/GPL-3.0 + * + */ + +.context-menu-list { + margin:0; + padding:0; + + min-width: 120px; + max-width: 250px; + display: inline-block; + position: absolute; + list-style-type: none; + + border: 1px solid #DDD; + background: #EEE; + + -webkit-box-shadow: 0 2px 5px rgba(0, 0, 0, 0.5); + -moz-box-shadow: 0 2px 5px rgba(0, 0, 0, 0.5); + -ms-box-shadow: 0 2px 5px rgba(0, 0, 0, 0.5); + -o-box-shadow: 0 2px 5px rgba(0, 0, 0, 0.5); + box-shadow: 0 2px 5px rgba(0, 0, 0, 0.5); + + font-family: Verdana, Arial, Helvetica, sans-serif; + font-size: 11px; +} + +.context-menu-item { + padding: 2px 2px 2px 24px; + background-color: #EEE; + position: relative; + -webkit-user-select: none; + -moz-user-select: -moz-none; + -ms-user-select: none; + user-select: none; +} + +.context-menu-separator { + padding-bottom:0; + border-bottom: 1px solid #DDD; +} + +.context-menu-item > label > input, +.context-menu-item > label > textarea { + -webkit-user-select: text; + -moz-user-select: text; + -ms-user-select: text; + user-select: text; +} + +.context-menu-item.hover { + cursor: pointer; + background-color: #39F; +} + +.context-menu-item.disabled { + color: #666; +} + +.context-menu-input.hover, +.context-menu-item.disabled.hover { + cursor: default; + background-color: #EEE; +} + +.context-menu-submenu:after { + content: ">"; + color: #666; + position: absolute; + top: 0; + right: 3px; + z-index: 1; +} + +/* icons + #protip: + In case you want to use sprites for icons (which I would suggest you do) have a look at + http://css-tricks.com/13224-pseudo-spriting/ to get an idea of how to implement + .context-menu-item.icon:before {} + */ +.context-menu-item.icon { min-height: 18px; background-repeat: no-repeat; background-position: 4px 2px; } +.context-menu-item.icon-edit { background-image: url(images/page_white_edit.png); } +.context-menu-item.icon-cut { background-image: url(images/cut.png); } +.context-menu-item.icon-copy { background-image: url(images/page_white_copy.png); } +.context-menu-item.icon-paste { background-image: url(images/page_white_paste.png); } +.context-menu-item.icon-delete { background-image: url(images/page_white_delete.png); } +.context-menu-item.icon-add { background-image: url(images/page_white_add.png); } +.context-menu-item.icon-quit { background-image: url(images/door.png); } + +/* vertically align inside labels */ +.context-menu-input > label > * { vertical-align: top; } + +/* position checkboxes and radios as icons */ +.context-menu-input > label > input[type="checkbox"], +.context-menu-input > label > input[type="radio"] { + margin-left: -17px; +} +.context-menu-input > label > span { + margin-left: 5px; +} + +.context-menu-input > label, +.context-menu-input > label > input[type="text"], +.context-menu-input > label > textarea, +.context-menu-input > label > select { + display: block; + width: 100%; + + -webkit-box-sizing: border-box; + -moz-box-sizing: border-box; + -ms-box-sizing: border-box; + -o-box-sizing: border-box; + box-sizing: border-box; +} + +.context-menu-input > label > textarea { + height: 100px; +} +.context-menu-item > .context-menu-list { + display: none; + /* re-positioned by js */ + right: -5px; + top: 5px; +} + +.context-menu-item.hover > .context-menu-list { + display: block; +} + +.context-menu-accesskey { + text-decoration: underline; +} diff --git a/lib/client/menu/contextMenu.js b/lib/client/menu/contextMenu.js index 04438d80..a903e822 100644 --- a/lib/client/menu/contextMenu.js +++ b/lib/client/menu/contextMenu.js @@ -1,1583 +1,1686 @@ -/*! - * jQuery contextMenu - Plugin for simple contextMenu handling - * - * Version: 1.5.22 - * - * Authors: Rodney Rehm, Addy Osmani (patches for FF) - * Web: http://medialize.github.com/jQuery-contextMenu/ - * - * Licensed under - * MIT License http://www.opensource.org/licenses/mit-license - * GPL v3 http://opensource.org/licenses/GPL-3.0 - * - */ - -(function($, undefined){ - - // TODO: - - // ARIA stuff: menuitem, menuitemcheckbox und menuitemradio - // create structure if $.support[htmlCommand || htmlMenuitem] and !opt.disableNative - -// determine html5 compatibility -$.support.htmlMenuitem = ('HTMLMenuItemElement' in window); -$.support.htmlCommand = ('HTMLCommandElement' in window); -$.support.eventSelectstart = ("onselectstart" in document.documentElement); -/* // should the need arise, test for css user-select -$.support.cssUserSelect = (function(){ - var t = false, - e = document.createElement('div'); - - $.each('Moz|Webkit|Khtml|O|ms|Icab|'.split('|'), function(i, prefix) { - var propCC = prefix + (prefix ? 'U' : 'u') + 'serSelect', - prop = (prefix ? ('-' + prefix.toLowerCase() + '-') : '') + 'user-select'; - - e.style.cssText = prop + ': text;'; - if (e.style[propCC] == 'text') { - t = true; - return false; - } - - return true; - }); - - return t; -})(); -*/ - -var // currently active contextMenu trigger - $currentTrigger = null, - // is contextMenu initialized with at least one menu? - initialized = false, - // window handle - $win = $(window), - // number of registered menus - counter = 0, - // mapping selector to namespace - namespaces = {}, - // mapping namespace to options - menus = {}, - // custom command type handlers - types = {}, - // default values - defaults = { - // selector of contextMenu trigger - selector: null, - // where to append the menu to - appendTo: null, - // method to trigger context menu ["right", "left", "hover"] - trigger: "right", - // hide menu when mouse leaves trigger / menu elements - autoHide: false, - // ms to wait before showing a hover-triggered context menu - delay: 200, - // determine position to show menu at - determinePosition: function($menu) { - // position to the lower middle of the trigger element - if ($.ui && $.ui.position) { - // .position() is provided as a jQuery UI utility - // (...and it won't work on hidden elements) - $menu.css('display', 'block').position({ - my: "center top", - at: "center bottom", - of: this, - offset: "0 5", - collision: "fit" - }).css('display', 'none'); - } else { - // determine contextMenu position - var offset = this.offset(); - offset.top += this.outerHeight(); - offset.left += this.outerWidth() / 2 - $menu.outerWidth() / 2; - $menu.css(offset); - } - }, - // position menu - position: function(opt, x, y) { - var offset; - // determine contextMenu position - if (!x && !y) { - opt.determinePosition.call(this, opt.$menu); - return; - } else if (x === "maintain" && y === "maintain") { - // x and y must not be changed (after re-show on command click) - offset = opt.$menu.position(); - } else { - // x and y are given (by mouse event) - var triggerIsFixed = opt.$trigger.parents().andSelf() - .filter(function() { - return $(this).css('position') == "fixed"; - }).length; - - if (triggerIsFixed) { - y -= $win.scrollTop(); - x -= $win.scrollLeft(); - } - offset = {top: y, left: x}; - } - - // correct offset if viewport demands it - var bottom = $win.scrollTop() + $win.height(), - right = $win.scrollLeft() + $win.width(), - height = opt.$menu.height(), - width = opt.$menu.width(); - - if (offset.top + height > bottom) { - offset.top -= height; - } - - if (offset.left + width > right) { - offset.left -= width; - } - - opt.$menu.css(offset); - }, - // position the sub-menu - positionSubmenu: function($menu) { - if ($.ui && $.ui.position) { - // .position() is provided as a jQuery UI utility - // (...and it won't work on hidden elements) - $menu.css('display', 'block').position({ - my: "left top", - at: "right top", - of: this, - collision: "fit" - }).css('display', ''); - } else { - // determine contextMenu position - var offset = { - top: 0, - left: this.outerWidth() - }; - $menu.css(offset); - } - }, - // offset to add to zIndex - zIndex: 1, - // show hide animation settings - animation: { - duration: 50, - show: 'slideDown', - hide: 'slideUp' - }, - // events - events: { - show: $.noop, - hide: $.noop - }, - // default callback - callback: null, - // list of contextMenu items - items: {} - }, - // mouse position for hover activation - hoveract = { - timer: null, - pageX: null, - pageY: null - }, - // determine zIndex - zindex = function($t) { - var zin = 0, - $tt = $t; - - while (true) { - zin = Math.max(zin, parseInt($tt.css('z-index'), 10) || 0); - $tt = $tt.parent(); - if (!$tt || !$tt.length || "html body".indexOf($tt.prop('nodeName').toLowerCase()) > -1 ) { - break; - } - } - - return zin; - }, - // event handlers - handle = { - // abort anything - abortevent: function(e){ - e.preventDefault(); - e.stopImmediatePropagation(); - }, - - // contextmenu show dispatcher - contextmenu: function(e) { - var $this = $(this); - - // disable actual context-menu - e.preventDefault(); - e.stopImmediatePropagation(); - - // abort native-triggered events unless we're triggering on right click - if (e.data.trigger != 'right' && e.originalEvent) { - return; - } - - if (!$this.hasClass('context-menu-disabled')) { - // theoretically need to fire a show event at - // http://www.whatwg.org/specs/web-apps/current-work/multipage/interactive-elements.html#context-menus - // var evt = jQuery.Event("show", { data: data, pageX: e.pageX, pageY: e.pageY, relatedTarget: this }); - // e.data.$menu.trigger(evt); - - $currentTrigger = $this; - if (e.data.build) { - var built = e.data.build($currentTrigger, e); - // abort if build() returned false - if (built === false) { - return; - } - - // dynamically build menu on invocation - e.data = $.extend(true, {}, defaults, e.data, built || {}); - - // abort if there are no items to display - if (!e.data.items || $.isEmptyObject(e.data.items)) { - // Note: jQuery captures and ignores errors from event handlers - if (window.console) { - (console.error || console.log)("No items specified to show in contextMenu"); - } - - throw new Error('No Items sepcified'); - } - - // backreference for custom command type creation - e.data.$trigger = $currentTrigger; - - op.create(e.data); - } - // show menu - op.show.call($this, e.data, e.pageX, e.pageY); - } - }, - // contextMenu left-click trigger - click: function(e) { - e.preventDefault(); - e.stopImmediatePropagation(); - $(this).trigger(jQuery.Event("contextmenu", { data: e.data, pageX: e.pageX, pageY: e.pageY })); - }, - // contextMenu right-click trigger - mousedown: function(e) { - // register mouse down - var $this = $(this); - - // hide any previous menus - if ($currentTrigger && $currentTrigger.length && !$currentTrigger.is($this)) { - $currentTrigger.data('contextMenu').$menu.trigger('contextmenu:hide'); - } - - // activate on right click - if (e.button == 2) { - $currentTrigger = $this.data('contextMenuActive', true); - } - }, - // contextMenu right-click trigger - mouseup: function(e) { - // show menu - var $this = $(this); - if ($this.data('contextMenuActive') && $currentTrigger && $currentTrigger.length && $currentTrigger.is($this) && !$this.hasClass('context-menu-disabled')) { - e.preventDefault(); - e.stopImmediatePropagation(); - $currentTrigger = $this; - $this.trigger(jQuery.Event("contextmenu", { data: e.data, pageX: e.pageX, pageY: e.pageY })); - } - - $this.removeData('contextMenuActive'); - }, - // contextMenu hover trigger - mouseenter: function(e) { - var $this = $(this), - $related = $(e.relatedTarget), - $document = $(document); - - // abort if we're coming from a menu - if ($related.is('.context-menu-list') || $related.closest('.context-menu-list').length) { - return; - } - - // abort if a menu is shown - if ($currentTrigger && $currentTrigger.length) { - return; - } - - hoveract.pageX = e.pageX; - hoveract.pageY = e.pageY; - hoveract.data = e.data; - $document.on('mousemove.contextMenuShow', handle.mousemove); - hoveract.timer = setTimeout(function() { - hoveract.timer = null; - $document.off('mousemove.contextMenuShow'); - $currentTrigger = $this; - $this.trigger(jQuery.Event("contextmenu", { data: hoveract.data, pageX: hoveract.pageX, pageY: hoveract.pageY })); - }, e.data.delay ); - }, - // contextMenu hover trigger - mousemove: function(e) { - hoveract.pageX = e.pageX; - hoveract.pageY = e.pageY; - }, - // contextMenu hover trigger - mouseleave: function(e) { - // abort if we're leaving for a menu - var $related = $(e.relatedTarget); - if ($related.is('.context-menu-list') || $related.closest('.context-menu-list').length) { - return; - } - - try { - clearTimeout(hoveract.timer); - } catch(e) {} - - hoveract.timer = null; - }, - - // click on layer to hide contextMenu - layerClick: function(e) { - var $this = $(this), - root = $this.data('contextMenuRoot'), - mouseup = false, - button = e.button, - x = e.pageX, - y = e.pageY, - target, - offset, - selectors; - - e.preventDefault(); - e.stopImmediatePropagation(); - - // This hack looks about as ugly as it is - // Firefox 12 (at least) fires the contextmenu event directly "after" mousedown - // for some reason `root.$layer.hide(); document.elementFromPoint()` causes this - // contextmenu event to be triggered on the uncovered element instead of on the - // layer (where every other sane browser, including Firefox nightly at the time) - // triggers the event. This workaround might be obsolete by September 2012. - $this.on('mouseup', function() { - mouseup = true; - }); - setTimeout(function() { - var $window, hideshow; - - // test if we need to reposition the menu - if ((root.trigger == 'left' && button == 0) || (root.trigger == 'right' && button == 2)) { - if (document.elementFromPoint) { - root.$layer.hide(); - target = document.elementFromPoint(x, y); - root.$layer.show(); - - selectors = []; - for (var s in namespaces) { - selectors.push(s); - } - - target = $(target).closest(selectors.join(', ')); - - if (target.length) { - if (target.is(root.$trigger[0])) { - root.position.call(root.$trigger, root, x, y); - return; - } - } - } else { - offset = root.$trigger.offset(); - $window = $(window); - // while this looks kinda awful, it's the best way to avoid - // unnecessarily calculating any positions - offset.top += $window.scrollTop(); - if (offset.top <= e.pageY) { - offset.left += $window.scrollLeft(); - if (offset.left <= e.pageX) { - offset.bottom = offset.top + root.$trigger.outerHeight(); - if (offset.bottom >= e.pageY) { - offset.right = offset.left + root.$trigger.outerWidth(); - if (offset.right >= e.pageX) { - // reposition - root.position.call(root.$trigger, root, x, y); - return; - } - } - } - } - } - } - - hideshow = function(e) { - if (e) { - e.preventDefault(); - e.stopImmediatePropagation(); - } - - root.$menu.trigger('contextmenu:hide'); - if (target && target.length) { - setTimeout(function() { - target.contextMenu({x: x, y: y}); - }, 50); - } - }; - - if (mouseup) { - // mouseup has already happened - hideshow(); - } else { - // remove only after mouseup has completed - $this.on('mouseup', hideshow); - } - }, 50); - }, - // key handled :hover - keyStop: function(e, opt) { - if (!opt.isInput) { - e.preventDefault(); - } - - e.stopPropagation(); - }, - key: function(e) { - var opt = $currentTrigger.data('contextMenu') || {}; - - switch (e.keyCode) { - case 9: - case 38: // up - handle.keyStop(e, opt); - // if keyCode is [38 (up)] or [9 (tab) with shift] - if (opt.isInput) { - if (e.keyCode == 9 && e.shiftKey) { - e.preventDefault(); - opt.$selected && opt.$selected.find('input, textarea, select').blur(); - opt.$menu.trigger('prevcommand'); - return; - } else if (e.keyCode == 38 && opt.$selected.find('input, textarea, select').prop('type') == 'checkbox') { - // checkboxes don't capture this key - e.preventDefault(); - return; - } - } else if (e.keyCode != 9 || e.shiftKey) { - opt.$menu.trigger('prevcommand'); - return; - } - - case 9: // tab - case 40: // down - handle.keyStop(e, opt); - if (opt.isInput) { - if (e.keyCode == 9) { - e.preventDefault(); - opt.$selected && opt.$selected.find('input, textarea, select').blur(); - opt.$menu.trigger('nextcommand'); - return; - } else if (e.keyCode == 40 && opt.$selected.find('input, textarea, select').prop('type') == 'checkbox') { - // checkboxes don't capture this key - e.preventDefault(); - return; - } - } else { - opt.$menu.trigger('nextcommand'); - return; - } - break; - - case 37: // left - handle.keyStop(e, opt); - if (opt.isInput || !opt.$selected || !opt.$selected.length) { - break; - } - - if (!opt.$selected.parent().hasClass('context-menu-root')) { - var $parent = opt.$selected.parent().parent(); - opt.$selected.trigger('contextmenu:blur'); - opt.$selected = $parent; - return; - } - break; - - case 39: // right - handle.keyStop(e, opt); - if (opt.isInput || !opt.$selected || !opt.$selected.length) { - break; - } - - var itemdata = opt.$selected.data('contextMenu') || {}; - if (itemdata.$menu && opt.$selected.hasClass('context-menu-submenu')) { - opt.$selected = null; - itemdata.$selected = null; - itemdata.$menu.trigger('nextcommand'); - return; - } - break; - - case 35: // end - case 36: // home - if (opt.$selected && opt.$selected.find('input, textarea, select').length) { - return; - } else { - (opt.$selected && opt.$selected.parent() || opt.$menu) - .children(':not(.disabled, .not-selectable)')[e.keyCode == 36 ? 'first' : 'last']() - .trigger('contextmenu:focus'); - e.preventDefault(); - return; - } - break; - - case 13: // enter - handle.keyStop(e, opt); - if (opt.isInput) { - if (opt.$selected && !opt.$selected.is('textarea, select')) { - e.preventDefault(); - return; - } - break; - } - opt.$selected && opt.$selected.trigger('mouseup'); - return; - - case 32: // space - case 33: // page up - case 34: // page down - // prevent browser from scrolling down while menu is visible - handle.keyStop(e, opt); - return; - - case 27: // esc - handle.keyStop(e, opt); - opt.$menu.trigger('contextmenu:hide'); - return; - - default: // 0-9, a-z - var k = (String.fromCharCode(e.keyCode)).toUpperCase(); - if (opt.accesskeys[k]) { - // according to the specs accesskeys must be invoked immediately - opt.accesskeys[k].$node.trigger(opt.accesskeys[k].$menu - ? 'contextmenu:focus' - : 'mouseup' - ); - return; - } - break; - } - // pass event to selected item, - // stop propagation to avoid endless recursion - e.stopPropagation(); - opt.$selected && opt.$selected.trigger(e); - }, - - // select previous possible command in menu - prevItem: function(e) { - e.stopPropagation(); - var opt = $(this).data('contextMenu') || {}; - - // obtain currently selected menu - if (opt.$selected) { - var $s = opt.$selected; - opt = opt.$selected.parent().data('contextMenu') || {}; - opt.$selected = $s; - } - - var $children = opt.$menu.children(), - $prev = !opt.$selected || !opt.$selected.prev().length ? $children.last() : opt.$selected.prev(), - $round = $prev; - - // skip disabled - while ($prev.hasClass('disabled') || $prev.hasClass('not-selectable')) { - if ($prev.prev().length) { - $prev = $prev.prev(); - } else { - $prev = $children.last(); - } - if ($prev.is($round)) { - // break endless loop - return; - } - } - - // leave current - if (opt.$selected) { - handle.itemMouseleave.call(opt.$selected.get(0), e); - } - - // activate next - handle.itemMouseenter.call($prev.get(0), e); - - // focus input - var $input = $prev.find('input, textarea, select'); - if ($input.length) { - $input.focus(); - } - }, - // select next possible command in menu - nextItem: function(e) { - e.stopPropagation(); - var opt = $(this).data('contextMenu') || {}; - - // obtain currently selected menu - if (opt.$selected) { - var $s = opt.$selected; - opt = opt.$selected.parent().data('contextMenu') || {}; - opt.$selected = $s; - } - - var $children = opt.$menu.children(), - $next = !opt.$selected || !opt.$selected.next().length ? $children.first() : opt.$selected.next(), - $round = $next; - - // skip disabled - while ($next.hasClass('disabled') || $next.hasClass('not-selectable')) { - if ($next.next().length) { - $next = $next.next(); - } else { - $next = $children.first(); - } - if ($next.is($round)) { - // break endless loop - return; - } - } - - // leave current - if (opt.$selected) { - handle.itemMouseleave.call(opt.$selected.get(0), e); - } - - // activate next - handle.itemMouseenter.call($next.get(0), e); - - // focus input - var $input = $next.find('input, textarea, select'); - if ($input.length) { - $input.focus(); - } - }, - - // flag that we're inside an input so the key handler can act accordingly - focusInput: function(e) { - var $this = $(this).closest('.context-menu-item'), - data = $this.data(), - opt = data.contextMenu, - root = data.contextMenuRoot; - - root.$selected = opt.$selected = $this; - root.isInput = opt.isInput = true; - }, - // flag that we're inside an input so the key handler can act accordingly - blurInput: function(e) { - var $this = $(this).closest('.context-menu-item'), - data = $this.data(), - opt = data.contextMenu, - root = data.contextMenuRoot; - - root.isInput = opt.isInput = false; - }, - - // :hover on menu - menuMouseenter: function(e) { - var root = $(this).data().contextMenuRoot; - root.hovering = true; - }, - // :hover on menu - menuMouseleave: function(e) { - var root = $(this).data().contextMenuRoot; - if (root.$layer && root.$layer.is(e.relatedTarget)) { - root.hovering = false; - } - }, - - // :hover done manually so key handling is possible - itemMouseenter: function(e) { - var $this = $(this), - data = $this.data(), - opt = data.contextMenu, - root = data.contextMenuRoot; - - root.hovering = true; - - // abort if we're re-entering - if (e && root.$layer && root.$layer.is(e.relatedTarget)) { - e.preventDefault(); - e.stopImmediatePropagation(); - } - - // make sure only one item is selected - (opt.$menu ? opt : root).$menu - .children('.hover').trigger('contextmenu:blur'); - - if ($this.hasClass('disabled') || $this.hasClass('not-selectable')) { - opt.$selected = null; - return; - } - - $this.trigger('contextmenu:focus'); - }, - // :hover done manually so key handling is possible - itemMouseleave: function(e) { - var $this = $(this), - data = $this.data(), - opt = data.contextMenu, - root = data.contextMenuRoot; - - if (root !== opt && root.$layer && root.$layer.is(e.relatedTarget)) { - root.$selected && root.$selected.trigger('contextmenu:blur'); - e.preventDefault(); - e.stopImmediatePropagation(); - root.$selected = opt.$selected = opt.$node; - return; - } - - $this.trigger('contextmenu:blur'); - }, - // contextMenu item click - itemClick: function(e) { - var $this = $(this), - data = $this.data(), - opt = data.contextMenu, - root = data.contextMenuRoot, - key = data.contextMenuKey, - callback; - - // abort if the key is unknown or disabled or is a menu - if (!opt.items[key] || $this.hasClass('disabled') || $this.hasClass('context-menu-submenu')) { - return; - } - - e.preventDefault(); - e.stopImmediatePropagation(); - - if ($.isFunction(root.callbacks[key])) { - // item-specific callback - callback = root.callbacks[key]; - } else if ($.isFunction(root.callback)) { - // default callback - callback = root.callback; - } else { - // no callback, no action - return; - } - - // hide menu if callback doesn't stop that - if (callback.call(root.$trigger, key, root) !== false) { - root.$menu.trigger('contextmenu:hide'); - } else if (root.$menu.parent().length) { - op.update.call(root.$trigger, root); - } - }, - // ignore click events on input elements - inputClick: function(e) { - e.stopImmediatePropagation(); - }, - - // hide - hideMenu: function(e, data) { - var root = $(this).data('contextMenuRoot'); - op.hide.call(root.$trigger, root, data && data.force); - }, - // focus - focusItem: function(e) { - e.stopPropagation(); - var $this = $(this), - data = $this.data(), - opt = data.contextMenu, - root = data.contextMenuRoot; - - $this.addClass('hover') - .siblings('.hover').trigger('contextmenu:blur'); - - // remember selected - opt.$selected = root.$selected = $this; - - // position sub-menu - do after show so dumb $.ui.position can keep up - if (opt.$node) { - root.positionSubmenu.call(opt.$node, opt.$menu); - } - }, - // blur - blurItem: function(e) { - e.stopPropagation(); - var $this = $(this), - data = $this.data(), - opt = data.contextMenu, - root = data.contextMenuRoot; - - $this.removeClass('hover'); - opt.$selected = null; - } - }, - // operations - op = { - show: function(opt, x, y) { - var $this = $(this), - offset, - css = {}; - - // hide any open menus - $('#context-menu-layer').trigger('mousedown'); - - // backreference for callbacks - opt.$trigger = $this; - - // show event - if (opt.events.show.call($this, opt) === false) { - $currentTrigger = null; - return; - } - - // create or update context menu - op.update.call($this, opt); - - // position menu - opt.position.call($this, opt, x, y); - - // make sure we're in front - if (opt.zIndex) { - css.zIndex = zindex($this) + opt.zIndex; - } - - // add layer - op.layer.call(opt.$menu, opt, css.zIndex); - - // adjust sub-menu zIndexes - opt.$menu.find('ul').css('zIndex', css.zIndex + 1); - - // position and show context menu - opt.$menu.css( css )[opt.animation.show](opt.animation.duration); - // make options available - $this.data('contextMenu', opt); - // register key handler - $(document).off('keydown.contextMenu').on('keydown.contextMenu', handle.key); - // register autoHide handler - if (opt.autoHide) { - // trigger element coordinates - var pos = $this.position(); - pos.right = pos.left + $this.outerWidth(); - pos.bottom = pos.top + this.outerHeight(); - // mouse position handler - $(document).on('mousemove.contextMenuAutoHide', function(e) { - if (opt.$layer && !opt.hovering && (!(e.pageX >= pos.left && e.pageX <= pos.right) || !(e.pageY >= pos.top && e.pageY <= pos.bottom))) { - // if mouse in menu... - opt.$menu.trigger('contextmenu:hide'); - } - }); - } - }, - hide: function(opt, force) { - var $this = $(this); - if (!opt) { - opt = $this.data('contextMenu') || {}; - } - - // hide event - if (!force && opt.events && opt.events.hide.call($this, opt) === false) { - return; - } - - if (opt.$layer) { - // keep layer for a bit so the contextmenu event can be aborted properly by opera - setTimeout((function($layer){ return function(){ - $layer.remove(); - }; - })(opt.$layer), 10); - - try { - delete opt.$layer; - } catch(e) { - opt.$layer = null; - } - } - - // remove handle - $currentTrigger = null; - // remove selected - opt.$menu.find('.hover').trigger('contextmenu:blur'); - opt.$selected = null; - // unregister key and mouse handlers - //$(document).off('.contextMenuAutoHide keydown.contextMenu'); // http://bugs.jquery.com/ticket/10705 - $(document).off('.contextMenuAutoHide').off('keydown.contextMenu'); - // hide menu - opt.$menu && opt.$menu[opt.animation.hide](opt.animation.duration, function (){ - // tear down dynamically built menu after animation is completed. - if (opt.build) { - opt.$menu.remove(); - $.each(opt, function(key, value) { - switch (key) { - case 'ns': - case 'selector': - case 'build': - case 'trigger': - return true; - - default: - opt[key] = undefined; - try { - delete opt[key]; - } catch (e) {} - return true; - } - }); - } - }); - }, - create: function(opt, root) { - if (root === undefined) { - root = opt; - } - // create contextMenu - opt.$menu = $('
      ').data({ - 'contextMenu': opt, - 'contextMenuRoot': root - }); - - $.each(['callbacks', 'commands', 'inputs'], function(i,k){ - opt[k] = {}; - if (!root[k]) { - root[k] = {}; - } - }); - - root.accesskeys || (root.accesskeys = {}); - - // create contextMenu items - $.each(opt.items, function(key, item){ - var $t = $('
    • '), - $label = null, - $input = null; - - item.$node = $t.data({ - 'contextMenu': opt, - 'contextMenuRoot': root, - 'contextMenuKey': key - }); - - // register accesskey - // NOTE: the accesskey attribute should be applicable to any element, but Safari5 and Chrome13 still can't do that - if (item.accesskey) { - var aks = splitAccesskey(item.accesskey); - for (var i=0, ak; ak = aks[i]; i++) { - if (!root.accesskeys[ak]) { - root.accesskeys[ak] = item; - item._name = item.name.replace(new RegExp('(' + ak + ')', 'i'), '$1'); - break; - } - } - } - - if (typeof item == "string") { - $t.addClass('context-menu-separator not-selectable'); - } else if (item.type && types[item.type]) { - // run custom type handler - types[item.type].call($t, item, opt, root); - // register commands - $.each([opt, root], function(i,k){ - k.commands[key] = item; - if ($.isFunction(item.callback)) { - k.callbacks[key] = item.callback; - } - }); - } else { - // add label for input - if (item.type == 'html') { - $t.addClass('context-menu-html not-selectable'); - } else if (item.type) { - $label = $('').appendTo($t); - $('').html(item._name || item.name).appendTo($label); - $t.addClass('context-menu-input'); - opt.hasTypes = true; - $.each([opt, root], function(i,k){ - k.commands[key] = item; - k.inputs[key] = item; - }); - } else if (item.items) { - item.type = 'sub'; - } - - switch (item.type) { - case 'text': - $input = $('') - .val(item.value || "").appendTo($label); - break; - - case 'textarea': - $input = $('') - .val(item.value || "").appendTo($label); - - if (item.height) { - $input.height(item.height); - } - break; - - case 'checkbox': - $input = $('') - .val(item.value || "").prop("checked", !!item.selected).prependTo($label); - break; - - case 'radio': - $input = $('') - .val(item.value || "").prop("checked", !!item.selected).prependTo($label); - break; - - case 'select': - $input = $(' - if (item.type && item.type != 'sub' && item.type != 'html') { - $input - .on('focus', handle.focusInput) - .on('blur', handle.blurInput); - - if (item.events) { - $input.on(item.events); - } - } - - // add icons - if (item.icon) { - $t.addClass("icon icon-" + item.icon); - } - } - - // cache contained elements - item.$input = $input; - item.$label = $label; - - // attach item to menu - $t.appendTo(opt.$menu); - - // Disable text selection - if (!opt.hasTypes && $.support.eventSelectstart) { - // browsers support user-select: none, - // IE has a special event for text-selection - // browsers supporting neither will not be preventing text-selection - $t.on('selectstart.disableTextSelect', handle.abortevent); - } - }); - // attach contextMenu to (to bypass any possible overflow:hidden issues on parents of the trigger element) - if (!opt.$node) { - opt.$menu.css('display', 'none').addClass('context-menu-root'); - } - opt.$menu.appendTo(opt.appendTo || document.body); - }, - update: function(opt, root) { - var $this = this; - if (root === undefined) { - root = opt; - // determine widths of submenus, as CSS won't grow them automatically - // position:absolute > position:absolute; min-width:100; max-width:200; results in width: 100; - // kinda sucks hard... - opt.$menu.find('ul').andSelf().css({position: 'static', display: 'block'}).each(function(){ - var $this = $(this); - $this.width($this.css('position', 'absolute').width()) - .css('position', 'static'); - }).css({position: '', display: ''}); - } - // re-check disabled for each item - opt.$menu.children().each(function(){ - var $item = $(this), - key = $item.data('contextMenuKey'), - item = opt.items[key], - disabled = ($.isFunction(item.disabled) && item.disabled.call($this, key, root)) || item.disabled === true; - - // dis- / enable item - $item[disabled ? 'addClass' : 'removeClass']('disabled'); - - if (item.type) { - // dis- / enable input elements - $item.find('input, select, textarea').prop('disabled', disabled); - - // update input states - switch (item.type) { - case 'text': - case 'textarea': - item.$input.val(item.value || ""); - break; - - case 'checkbox': - case 'radio': - item.$input.val(item.value || "").prop('checked', !!item.selected); - break; - - case 'select': - item.$input.val(item.selected || ""); - break; - } - } - - if (item.$menu) { - // update sub-menu - op.update.call($this, item, root); - } - }); - }, - layer: function(opt, zIndex) { - // add transparent layer for click area - // filter and background for Internet Explorer, Issue #23 - var $layer = opt.$layer = $('
      ') - .css({height: $win.height(), width: $win.width(), display: 'block'}) - .data('contextMenuRoot', opt) - .insertBefore(this) - .on('contextmenu', handle.abortevent) - .on('mousedown', handle.layerClick); - - // IE6 doesn't know position:fixed; - if (!$.support.fixedPosition) { - $layer.css({ - 'position' : 'absolute', - 'height' : $(document).height() - }); - } - - return $layer; - } - }; - -// split accesskey according to http://www.whatwg.org/specs/web-apps/current-work/multipage/editing.html#assigned-access-key -function splitAccesskey(val) { - var t = val.split(/\s+/), - keys = []; - - for (var i=0, k; k = t[i]; i++) { - k = k[0].toUpperCase(); // first character only - // theoretically non-accessible characters should be ignored, but different systems, different keyboard layouts, ... screw it. - // a map to look up already used access keys would be nice - keys.push(k); - } - - return keys; -} - -// handle contextMenu triggers -$.fn.contextMenu = function(operation) { - if (operation === undefined) { - this.first().trigger('contextmenu'); - } else if (operation.x && operation.y) { - this.first().trigger(jQuery.Event("contextmenu", {pageX: operation.x, pageY: operation.y})); - } else if (operation === "hide") { - var $menu = this.data('contextMenu').$menu; - $menu && $menu.trigger('contextmenu:hide'); - } else if (operation) { - this.removeClass('context-menu-disabled'); - } else if (!operation) { - this.addClass('context-menu-disabled'); - } - - return this; -}; - -// manage contextMenu instances -$.contextMenu = function(operation, options) { - if (typeof operation != 'string') { - options = operation; - operation = 'create'; - } - - if (typeof options == 'string') { - options = {selector: options}; - } else if (options === undefined) { - options = {}; - } - - // merge with default options - var o = $.extend(true, {}, defaults, options || {}), - $document = $(document); - - switch (operation) { - case 'create': - // no selector no joy - if (!o.selector) { - throw new Error('No selector specified'); - } - // make sure internal classes are not bound to - if (o.selector.match(/.context-menu-(list|item|input)($|\s)/)) { - throw new Error('Cannot bind to selector "' + o.selector + '" as it contains a reserved className'); - } - if (!o.build && (!o.items || $.isEmptyObject(o.items))) { - throw new Error('No Items sepcified'); - } - counter ++; - o.ns = '.contextMenu' + counter; - namespaces[o.selector] = o.ns; - menus[o.ns] = o; - - // default to right click - if (!o.trigger) { - o.trigger = 'right'; - } - - if (!initialized) { - // make sure item click is registered first - $document - .on({ - 'contextmenu:hide.contextMenu': handle.hideMenu, - 'prevcommand.contextMenu': handle.prevItem, - 'nextcommand.contextMenu': handle.nextItem, - 'contextmenu.contextMenu': handle.abortevent, - 'mouseenter.contextMenu': handle.menuMouseenter, - 'mouseleave.contextMenu': handle.menuMouseleave - }, '.context-menu-list') - .on('mouseup.contextMenu', '.context-menu-input', handle.inputClick) - .on({ - 'mouseup.contextMenu': handle.itemClick, - 'contextmenu:focus.contextMenu': handle.focusItem, - 'contextmenu:blur.contextMenu': handle.blurItem, - 'contextmenu.contextMenu': handle.abortevent, - 'mouseenter.contextMenu': handle.itemMouseenter, - 'mouseleave.contextMenu': handle.itemMouseleave - }, '.context-menu-item'); - - initialized = true; - } - - // engage native contextmenu event - $document - .on('contextmenu' + o.ns, o.selector, o, handle.contextmenu); - - switch (o.trigger) { - case 'hover': - $document - .on('mouseenter' + o.ns, o.selector, o, handle.mouseenter) - .on('mouseleave' + o.ns, o.selector, o, handle.mouseleave); - break; - - case 'left': - $document.on('click' + o.ns, o.selector, o, handle.click); - break; - /* - default: - // http://www.quirksmode.org/dom/events/contextmenu.html - $document - .on('mousedown' + o.ns, o.selector, o, handle.mousedown) - .on('mouseup' + o.ns, o.selector, o, handle.mouseup); - break; - */ - } - - // create menu - if (!o.build) { - op.create(o); - } - break; - - case 'destroy': - if (!o.selector) { - $document.off('.contextMenu .contextMenuAutoHide'); - $.each(namespaces, function(key, value) { - $document.off(value); - }); - - namespaces = {}; - menus = {}; - counter = 0; - initialized = false; - - $('#context-menu-layer, .context-menu-list').remove(); - } else if (namespaces[o.selector]) { - var $visibleMenu = $('.context-menu-list').filter(':visible'); - if ($visibleMenu.length && $visibleMenu.data().contextMenuRoot.$trigger.is(o.selector)) { - $visibleMenu.trigger('contextmenu:hide', {force: true}); - } - - try { - if (menus[namespaces[o.selector]].$menu) { - menus[namespaces[o.selector]].$menu.remove(); - } - - delete menus[namespaces[o.selector]]; - } catch(e) { - menus[namespaces[o.selector]] = null; - } - - $document.off(namespaces[o.selector]); - } - break; - - case 'html5': - // if or are not handled by the browser, - // or options was a bool true, - // initialize $.contextMenu for them - if ((!$.support.htmlCommand && !$.support.htmlMenuitem) || (typeof options == "boolean" && options)) { - $('menu[type="context"]').each(function() { - if (this.id) { - $.contextMenu({ - selector: '[contextmenu=' + this.id +']', - items: $.contextMenu.fromMenu(this) - }); - } - }).css('display', 'none'); - } - break; - - default: - throw new Error('Unknown operation "' + operation + '"'); - } - - return this; -}; - -// import values into commands -$.contextMenu.setInputValues = function(opt, data) { - if (data === undefined) { - data = {}; - } - - $.each(opt.inputs, function(key, item) { - switch (item.type) { - case 'text': - case 'textarea': - item.value = data[key] || ""; - break; - - case 'checkbox': - item.selected = data[key] ? true : false; - break; - - case 'radio': - item.selected = (data[item.radio] || "") == item.value ? true : false; - break; - - case 'select': - item.selected = data[key] || ""; - break; - } - }); -}; - -// export values from commands -$.contextMenu.getInputValues = function(opt, data) { - if (data === undefined) { - data = {}; - } - - $.each(opt.inputs, function(key, item) { - switch (item.type) { - case 'text': - case 'textarea': - case 'select': - data[key] = item.$input.val(); - break; - - case 'checkbox': - data[key] = item.$input.prop('checked'); - break; - - case 'radio': - if (item.$input.prop('checked')) { - data[item.radio] = item.value; - } - break; - } - }); - - return data; -}; - -// find