diff --git a/.gitignore b/.gitignore index 64d11554..0b7b6935 100644 --- a/.gitignore +++ b/.gitignore @@ -40,3 +40,6 @@ node_modules/* #hashes file hashes.json + +#hiding git modules for travis work out +.gitignore diff --git a/ChangeLog b/ChangeLog index dcc94e3e..b4e62c3d 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,36 @@ +2012.07.*, Version 0.1.3 + +* Fixed bug with nodester (jitsu env.HOME make him go done). + +* Fixed bug: gzip do not working out, becouse accessible flag settet +up to _controller object. + +* To every panel added scroll bars which is hiding if do not needed. + +* Fixed bug with setting up flag in config.json: do not minimize js. + +* Fixed bugs many bugs in _anyload function (client.js). + +* Added function cssLoad to client.js + +* Added Client Side CodeMirror library with name Cloud Editor. + +* Fixed bug with building file table from json data, when we go around +and come back to root dir. +If name of a file or directory is more then 16 charactes it shows +like 'VERY-LONG-NAME..', so becaouse of on first came we load page +like no js, so json building from file table thet was genereted on +server. So if file to long we should take it's name from title +parameter of tag, it's wasn't, so folder navigation not every +time was possible. + +* Fixed bug with undefined size on root directory of Cloud Commander. +Now Cloud Commander writes size 0, if can't get size, and besides will +setted b char: "0b". + +* Added supporting of Russian language in directory names. + + 2012.07.14, Version 0.1.2 * Added suport of jitsu. diff --git a/README.md b/README.md index 3f9aaa81..00f0d856 100644 --- a/README.md +++ b/README.md @@ -42,9 +42,14 @@ There is a short list: Installing --------------- **Cloud Commander** installing is very easy. All you need it's just clone -repository from github. Install and start, just 3 commands: +repository from github. Just 2 commands: - git clone git://github.com/coderaiser/cloudcmd.git --recursive + git clone git://github.com/coderaiser/cloudcmd.git + cd cloudcmd +or + + npm i cloudcmd + mv node_modules/cloudcmd ./cloudcmd Starting --------------- @@ -75,8 +80,7 @@ assingned (and installed) module: [Minify] (https://github.com/coderaiser/minify Install addtitional modules: - git submodule init - git submodule update + npm i **Cloud Commander's Client Side** use module jquery for ajaxing. We could not use this module, but this way is fast: @@ -94,4 +98,12 @@ If you would like to contribute - send pull request to dev branch. Getting dev version of **Cloud Commander**: git clone cloudcmd --recursive + git checkout dev + +It is possible thet dev version Cloud Commander will needed dev version of Minify, +so to get it you should type a couple more commands: + + cd node_modules + rm -rf minify + git clone git://github.com/coderaiser/minify git checkout dev \ No newline at end of file diff --git a/client.js b/client.js index cadd6682..fe393801 100644 --- a/client.js +++ b/client.js @@ -17,6 +17,7 @@ var CloudClient={ init :function(){}, keyBinding :function(){},/* функция нажатий обработки клавишь */ + Editor :function(){},/* function loads and shows editor */ keyBinded :false,/* оброботка нажатий клавишь установлена*/ _loadDir :function(){}, /* @@ -137,7 +138,15 @@ CloudClient.keyBinding=(function(){ }); }); - +/* function loads and shows editor */ +CloudClient.Editor = (function(){ + /* loading CloudMirror plagin */ + CloudClient.jsload(CloudClient.LIBDIRCLIENT + 'editor.js',{ + onload:(function(){ + CloudCommander.Editor.Keys(); + }) + }); +}); /* * Функция привязываеться ко всем ссылкам и * загружает содержимое каталогов @@ -330,7 +339,20 @@ CloudClient.init=(function() /* устанавливаем размер высоты таблицы файлов * исходя из размеров разрешения экрана */ - + + /* выделяем строку с первым файлом */ + var lFmHeader=document.getElementsByClassName('fm_header'); + if(lFmHeader && lFmHeader[0].nextSibling) + lFmHeader[0].nextSibling.className=CloudClient.CURRENT_FILE; + + /* показываем элементы, которые будут работать только, если есть js */ + var lFM=document.getElementById('fm'); + if(lFM)lFM.className='localstorage'; + + /* если есть js - показываем правую панель*/ + var lRight=document.getElementById('right'); + if(lRight)lRight.className=lRight.className.replace('hidden',''); + /* формируем и округляем высоту экрана * при разрешениии 1024x1280: * 658 -> 700 @@ -339,28 +361,11 @@ CloudClient.init=(function() var lHeight=window.screen.height - (window.screen.height/3).toFixed(); lHeight=(lHeight/100).toFixed()*100; - var lFm=document.getElementById('fm'); - if(lFm)lFm.style.cssText='height:' + - lHeight + - 'px'; - - /* выделяем строку с первым файлом */ - var lFmHeader=document.getElementsByClassName('fm_header'); - if(lFmHeader && lFmHeader[0].nextSibling) - lFmHeader[0].nextSibling.className=CloudClient.CURRENT_FILE; - - /* показываем элементы, которые будут работать только, если есть js */ - var lFM=document.getElementById('fm'); - if(lFM)lFm.className='localstorage'; - - /* если есть js - показываем правую панель*/ - var lRight=document.getElementById('right'); - if(lRight)lRight.className=lRight.className.replace('hidden',''); - CloudClient.cssSet({id:'show_2panels', element:document.head, - inner:'#left{width:46%;}' - }); + inner:'#left{width:46%;}' + + '.panel{height:' + lHeight +'px' + }); }); /* функция меняет ссыки на ajax-овые */ @@ -432,9 +437,10 @@ CloudClient._changeLinks = function(pPanelID) * @pNeedRefresh - необходимость обновить данные о каталоге */ CloudClient._ajaxLoad=function(path, pNeedRefresh) -{ - /* Отображаем красивые пути */ - var lPath=path; +{ + /* Отображаем красивые пути */ + /* added supporting of russian language */ + var lPath=decodeURI(path); var lFS_s=CloudFunc.FS; if(lPath.indexOf(lFS_s)===0){ lPath=lPath.replace(lFS_s,''); @@ -586,24 +592,31 @@ CloudClient._anyload = function(pName,pSrc,pFunc,pStyle,pId,pElement) if(!document.getElementById(lID)) { var element = document.createElement(pName); - element.src = pSrc; + /* if working with external css + * using href in any other case + * using src + */ + pName === 'link' ? + element.href = pSrc + : element.src = pSrc; element.id=lID; if(arguments.length>=3){ /* if passed arguments function * then it's onload by default */ - if(typeof pFunc === 'function'){ - element.onload=pFunc; - /* if object - then onload or onerror */ - }else if (typeof pFunc === 'object'){ - if(pFunc.onload)element.onload = pFunc.onload; - if(pFunc.onerror)element.onerror=pFunc.onerror; - } - if(arguments.length>=4){ + if(pFunc) + if(typeof pFunc === 'function'){ + element.onload=pFunc; + /* if object - then onload or onerror */ + }else if (typeof pFunc === 'object'){ + if(pFunc.onload)element.onload = pFunc.onload; + if(pFunc.onerror)element.onerror=pFunc.onerror; + } + if(arguments.length >= 4 && pStyle){ element.style.cssText=pStyle; } } - pElement.appendChild(element); + (pElement || document.body).appendChild(element); return element; } /* если js-файл уже загружен @@ -632,9 +645,31 @@ CloudClient.cssSet = function(pParams_o){ pParams_o.func, pParams_o.style, pParams_o.id, - pParams_o.element?pParams_o.element:document.body); - lElem.innerHTML=pParams_o.inner; + pParams_o.element || document.head); + + pParams_o.inner && + (lElem.innerHTML = pParams_o.inner); }; +/* Function loads external css files + * @pParams_o - структура параметров, заполняеться таким + * образом: {src: ' ',func: '', id: '', element: '', inner: ''} + * все параметры опциональны + */ +CloudClient.cssLoad = function(pParams_o){ + var lElem=CloudClient._anyload('link', + pParams_o.src, + pParams_o.func, + pParams_o.style, + pParams_o.id, + pParams_o.element || document.head); + + lElem.rel = "stylesheet"; + + pParams_o.inner && + (lElem.innerHTML = pParams_o.inner); +}; + + /* * Функция генерирует JSON из html-таблицы файлов @@ -663,7 +698,22 @@ CloudClient._getJSONfromFileTable=function() var lIsDir=lLI[i].getElementsByClassName('mini-icon')[0] .className.replace('mini-icon ','')==='directory'?true:false; - var lName=lLI[i].getElementsByClassName('name')[0].textContent; + var lName=lLI[i].getElementsByClassName('name')[0]; + lName && + (lName = lName.getElementsByTagName('a')); + /* if found link to folder + * cheking is it a full name + * or short + */ + /* if short we got title + * if full - getting textConent + */ + lName.length && + (lName = lName[0]); + lName.title && + (lName = lName.title) || + (lName = lName.textContent); + /* если это папка - выводим слово dir вместо размера*/ var lSize=lIsDir?'dir':lLI[i].getElementsByClassName('size')[0].textContent; var lMode=lLI[i].getElementsByClassName('mode')[0].textContent; @@ -716,6 +766,7 @@ try{ CloudCommander.init(); /* привязываем клавиши к функциям */ CloudCommander.keyBinding(); + CloudCommander.Editor(); }; } catch(err){} \ No newline at end of file diff --git a/css/style.css b/css/style.css index d2c2e876..71caa2f9 100644 --- a/css/style.css +++ b/css/style.css @@ -137,8 +137,7 @@ background:url(/img/panel_refresh.png) 0 -15px no-repeat; background-position: 0 0; } #fm{ - height:90%; - overflow-y: scroll; + height:90%; } .fm_header{ font-weight: bold; @@ -162,7 +161,7 @@ background:url(/img/panel_refresh.png) 0 -15px no-repeat; float:right; } .panel{ - display: table; + overflow-y: auto; width:46%; } #keyspanel{ diff --git a/install-dev b/install-dev new file mode 100644 index 00000000..3e8d299d --- /dev/null +++ b/install-dev @@ -0,0 +1,6 @@ +git checkout dev +cd node_modules +git clone git://github.com/coderaiser/minify +cd minify +git checkout dev +cd ../../ \ No newline at end of file diff --git a/lib/client/editor.js b/lib/client/editor.js new file mode 100644 index 00000000..5a5ff18e --- /dev/null +++ b/lib/client/editor.js @@ -0,0 +1,130 @@ +var CloudCommander, CodeMirror; +CloudCommander.Editor = {}; +CloudCommander.Editor.CloudMirror = { + load: (function(){ + + CloudCommander.jsload('http://codemirror.net/lib/codemirror.js', load_all(this)); + + function load_all(pParent) { + return function(){ + CloudCommander.cssLoad({ + src : 'http://codemirror.net/lib/codemirror.css', + element : document.head + }); + + CloudCommander.cssLoad({ + src : 'http://codemirror.net/theme/night.css', + element : document.head + }); + + CloudCommander.cssSet({id:'editor', + inner : '.CodeMirror{' + + 'font-family:\'Droid Sans Mono\';' + + 'font-size:15px;' + + 'resize:vertical;' + + 'padding:20px;' + + '}' + + '.CodeMirror-scroll{' + + 'height: 660px;' + + '}' + + '.CodeMirror-scrollbar{' + + 'overflow-y:auto' + + '}' + }); + + var lShowEditor_f = function (){ + if (!document.getElementById('CloudEditor')) { + var lEditor=document.createElement('div'); + lEditor.id ='CloudEditor'; + lEditor.className = 'hidden'; + var lFM = document.getElementById('fm'); + + if(lFM){ + lFM.appendChild(lEditor); + + CodeMirror(lEditor,{ + mode : "xml", + htmlMode : true, + theme : 'night', + lineNumbers : true, + //переносим длинные строки + lineWrapping: true, + extraKeys: { + //Сохранение + "Esc": pParent.hide(pParent) + } + }); + }else console.log('Error. Something went wrong FM not found'); + } + }; + CloudCommander.jsload('http://codemirror.net/mode/xml/xml.js', lShowEditor_f); + }; + } + }), + show : (function(){ + /* if CloudEditor is not loaded - loading him */ + document.getElementById('CloudEditor') || + this.load(); + /* removing keyBinding if set */ + CloudCommander.keyBinded = false; + + var lLeft = this.getById('left'); + var lCloudEditor = this.getById('CloudEditor'); + + lLeft && + (lLeft.className = 'panel hidden'); + + lCloudEditor && + (lCloudEditor.className = ''); + }), + hide : (function(pParent) { + return function(){ + CloudCommander.keyBinded = true; + + var lLeft = pParent.getById('left'); + var lCloudEditor = pParent.getById('CloudEditor'); + + lCloudEditor && + (lCloudEditor.className = 'hidden'); + + lLeft && + (lLeft.className = 'panel'); + }; + }), + getById: function(pId){return document.getElementById(pId);}, + + getPanel: function(){ + var lCurrent = document.getElementsByClassName('current-file'); + lCurrent.length && + (lCurrent = lCurrent[0].parentElement); + + return lCurrent && lCurrent.id; + } +}; +CloudCommander.Editor.Keys = (function(){ + "use strict"; + + /* loading js and css of CodeMirror */ + CloudCommander.Editor.CloudMirror.load(); + + var key_event=function(event){ + + /* если клавиши можно обрабатывать */ + if(CloudCommander.keyBinded){ + /* if f4 pressed */ + if(event.keyCode===115){ + CloudCommander.Editor.CloudMirror.show(); + } + } + }; + + /* добавляем обработчик клавишь */ + if (document.addEventListener) + document.addEventListener('keydown', key_event,false); + + else + document.onkeypress=key_event; + + /* клавиши назначены*/ + CloudCommander.keyBinded=true; +}); \ No newline at end of file diff --git a/lib/client/keyBinding.js b/lib/client/keyBinding.js index a441671f..7f007e55 100644 --- a/lib/client/keyBinding.js +++ b/lib/client/keyBinding.js @@ -14,8 +14,7 @@ CloudCommander.keyBinding=(function(){ * контент снова (js,css) в готовой версии нет * необходимости. * - */ - //console.log(event.keyCode); + */ var lCurrentFile; var lName; /* если клавиши можно обрабатывать*/ @@ -108,17 +107,6 @@ CloudCommander.keyBinding=(function(){ var lATag=lName[0].getElementsByTagName('a'); /* если нету - выходим */ if(!lATag)return false; - /* получаем ссылку на каталог, - * что на уровень выше - */ - /* получаем имя каталога в котором находимся*/ - var lHref; - try{ - lHref=lCurrentFile.parentElement.getElementsByClassName('path')[0].textContent; - }catch(error){console.log('error');} - lHref=CloudFunc.removeLastSlash(lHref); - var lSubstr=lHref.substr(lHref,lHref.lastIndexOf('/')); - lHref=lHref.replace(lSubstr+'/',''); /* вызываем ajaxload привязанный через changelinks * пробулем нажать на ссылку, если не получиться diff --git a/lib/cloudfunc.js b/lib/cloudfunc.js index 43136dd8..fac98396 100644 --- a/lib/cloudfunc.js +++ b/lib/cloudfunc.js @@ -32,19 +32,6 @@ CloudFunc.removeLastSlash = function(pPath){ pPath.substr(pPath, pPath.length-1):pPath; else return pPath; }; -/* - * Функция меняет код символа пробела на пробел - * в переданной строке - * @pPath - строка - */ -CloudFunc.replaceSpaces = function(pPath){ - if(pPath.indexOf('%20')>0){ - do{ - pPath=pPath.replace('%20',' '); - }while(pPath.indexOf('%20')>0); - } - return pPath; -}; /* Функция возвращает заголовок веб страницы */ CloudFunc.setTitle = function(){ @@ -159,7 +146,7 @@ CloudFunc.convertPermissionsToNumberic= function(pPerm_s){ */ CloudFunc.getShortedSize=function(pSize){ /* if pSize=0 - return it */ - if(!pSize)return pSize; + if (pSize != pSize-0) return pSize; /* Константы размеров, что используются * внутри функции diff --git a/lib/server/object.js b/lib/server/object.js index 3db493aa..29355925 100644 --- a/lib/server/object.js +++ b/lib/server/object.js @@ -81,10 +81,7 @@ exports.Minify={ */ setAllowed :(function(pAllowed){ if(pAllowed){ - this._allowed.css=pAllowed.css; - this._allowed.js=pAllowed.js; - this._allowed.html=pAllowed.html; - this._allowed.img=pAllowed.img; + this._allowed=pAllowed; } }), @@ -101,7 +98,7 @@ exports.Minify={ lMinify = require('minify'); }catch(pError){ this._allowed={js:false,css:false,html:false}; - return console.log('Could not minify' + + return console.log('Could not minify ' + 'withou minify module\n' + 'for fixing type:\n' + 'git submodule init\n' + @@ -140,21 +137,23 @@ exports.Minify={ 'client.js' + ' changed. size:', (pFinalCode = pFinalCode - .replace('cloudfunc.js','cloudfunc.min.js') - .replace('keyBinding.js','keyBinding.min.js') - .replace('/lib/', lMinFolder) - .replace('/lib/client/', - lMinFolder)).length); + .replace('editor.js','editor.min.js') + .replace('cloudfunc.js','cloudfunc.min.js') + .replace('keyBinding.js','keyBinding.min.js') + .replace('/lib/', lMinFolder) + .replace('/lib/client/', + lMinFolder)).length); return pFinalCode; }; - var lOptimizeParams; + var lOptimizeParams = []; if (this._allowed.js) { lOptimizeParams=[{ 'client.js': lPostProcessing_f}, 'lib/cloudfunc.js', - 'lib/client/keyBinding.js']; + 'lib/client/keyBinding.js', + 'lib/client/editor.js']; } if (this._allowed.html) diff --git a/node_modules/minify b/node_modules/minify index d08b97db..8d386766 160000 --- a/node_modules/minify +++ b/node_modules/minify @@ -1 +1 @@ -Subproject commit d08b97db461c5e0227df178a699c376c963d48ee +Subproject commit 8d386766868e176add3772365e277e501b0a37dd diff --git a/package.json b/package.json index d6fdba5a..15b5a4d4 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "cloudcmd", - "version": "0.1.2", + "version": "0.1.3", "author": "coderaiser (https://github.com/coderaiser)", "description": "Two-panels file manager, totally writed on js.", "homepage": "https://github.com/coderaiser/cloudcmd", @@ -15,7 +15,7 @@ "node": "0.6.17", "subdomain": "cloudcmd", "dependencies": { - "minify": "0.1.2" + "minify": "0.1.3" }, "devDependencies": {}, "engines": { diff --git a/server.js b/server.js index 78e63684..8effda22 100644 --- a/server.js +++ b/server.js @@ -3,92 +3,91 @@ /* Обьект содержащий все функции и переменные * серверной части Cloud Commander'а */ -var CloudServer={ +var CloudServer = { /* main Cloud Commander configuration * readed from config.json if it's * exist */ - Config : { - "cache" : {"allowed" : true}, - "minification" : { - "js" : false, - "css" : false, - "html" : true, - "img" : false - }, - "server" : true + Config : { + "cache" : {"allowed" : true}, + "minification" : { + "js" : false, + "css" : false, + "html" : true, + "img" : false + }, + "server" : true }, /* функция, которая генерирует заголовки * файлов, отправляемые сервером клиенту */ - generateHeaders :function(){}, + generateHeaders : function () {}, /* функция высылает * данные клиенту */ - sendResponse :function(){}, + sendResponse : function () {}, /* Структура содержащая функции, * и переменные, в которых * говориться о поддерживаемых * браузером технологиях */ - BrowserSuport :{}, + BrowserSuport : {}, /* Обьект для работы с кэшем */ - Cashe :{}, + Cashe : {}, /* Обьект через который * выполняеться сжатие * скриптов и стилей */ - Minify :{}, + Minify : {}, /* Асоциативный масив обьектов для * работы с ответами сервера * высылаемыми на запрос о файле и * хранащий информацию в виде * Responces[name]=responce; */ - Responses :{}, + Responses : {}, /* ПЕРЕМЕННЫЕ */ /* Поддержка браузером JS*/ - NoJS :true, + NoJS : true, /* Поддержка gzip-сжатия * браузером */ - Gzip :undefined, + Gzip : undefined, /* КОНСТАНТЫ */ /* index.html */ - INDEX :'index.html', - LIBDIR :'./lib', - LIBDIRSERVER :'./lib/server', + INDEX : 'index.html', + LIBDIR : './lib', + LIBDIRSERVER : './lib/server', - Port :31337, /* server port */ - IP :'127.0.0.1' + Port : 31337, /* server port */ + IP : '127.0.0.1' }; -var LeftDir='/'; -var RightDir=LeftDir; +var LeftDir = '/'; +var RightDir = LeftDir; /* модуль для работы с путями*/ -var Path = require('path'); - +var Path = require('path'); var Fs = require('fs'); /* модуль для работы с файловой системой*/ - +var Querystring = require('querystring'); var Zlib; /* node v0.4 not contains zlib */ -try{ +try { Zlib = require('zlib'); /* модуль для сжатия данных gzip-ом*/ -}catch(error){ - Zlib=undefined; +} catch (error) { + Zlib = undefined; console.log('to use gzip-commpression' + 'you should install zlib module\n' + 'npm install zlib'); } /* добавляем модуль с функциями */ var CloudFunc; -try{ +try { CloudFunc = require(CloudServer.LIBDIR + '/cloudfunc'); @@ -125,22 +124,22 @@ CloudServer.init=(function(){ * not created, just init and * all logs writed to screen */ - if(process.argv[2]==='test'){ + if (process.argv[2] === 'test') { console.log(process.argv); - this.Config.server=false; - this.Config.logs=false; + this.Config.server = false; + this.Config.logs = false; } - if(this.Config.logs){ + if (this.Config.logs) { console.log('log param setted up in config.json\n' + 'from now all logs will be writed to log.txt'); this.writeLogsToFile(); } - }catch(pError){ + } catch (pError) { console.log('warning: configureation file config.json not found...\n' + 'using default values...\n' + JSON.stringify(CloudServer.Config)); - } + } /* Переменная в которой храниться кэш*/ this.Cache.setAllowed(CloudServer.Config.cache.allowed); @@ -154,8 +153,7 @@ CloudServer.init=(function(){ /* создаём сервер на порту 31337 */ -CloudServer.start=function() -{ +CloudServer.start = function () { this.init(); this.Port = process.env.PORT || /* c9 */ @@ -167,17 +165,17 @@ CloudServer.start=function() CloudServer.IP; /* if Cloud Server started on jitsu */ - if(!process.env.HOME.indexOf('/opt/haibu')){ - this.IP = '0.0.0.0'; + if(process.env.HOME && + !process.env.HOME.indexOf('/opt/haibu')) { + this.IP = '0.0.0.0'; } - /* server mode or testing mode */ - if(!process.argv[2] && this.Config.server){ + /* server mode or testing mode */ + if (!process.argv[2] && this.Config.server) { var http = require('http'); - try{ + try { http.createServer(this._controller).listen( - this.Port, - this.IP); + this.Port, this.IP); console.log('Cloud Commander server running at http://' + this.IP + @@ -252,7 +250,10 @@ CloudServer._controller=function(pReq, pRes) */ var url = require("url"); var pathname = url.parse(pReq.url).pathname; - console.log('pathname: '+pathname); + + /* added supporting of Russian language in directory names */ + pathname = Querystring.unescape(pathname); + console.log('pathname: ' + pathname); /* получаем поддерживаемые браузером кодировки*/ var lAcceptEncoding = pReq.headers['accept-encoding']; @@ -263,9 +264,8 @@ CloudServer._controller=function(pReq, pRes) if (lAcceptEncoding && lAcceptEncoding.match(/\bgzip\b/) && Zlib){ - this.Gzip=true; - }else - this.Gzip=false; + CloudServer.Gzip=true; + } /* путь в ссылке, который говорит * что js отключен */ @@ -369,17 +369,11 @@ CloudServer._controller=function(pReq, pRes) /* если в итоге путь пустой * делаем его корневым */ - if(pathname==='')pathname='/'; - - RightDir=pathname; - LeftDir=pathname; - - /* если встретиться пробел - - * меня код символа пробела на пробел - */ - - LeftDir=CloudFunc.replaceSpaces(LeftDir); - RightDir=CloudFunc.replaceSpaces(RightDir); + if (pathname==='') + pathname = '/'; + + LeftDir = pathname; + RightDir = LeftDir; /* Проверяем с папкой ли мы имеем дело */