From 8d0348968580ca664a196eceee3d4de357edddbd Mon Sep 17 00:00:00 2001 From: coderaiser Date: Mon, 6 Aug 2012 14:27:54 -0400 Subject: [PATCH] minor changes --- client.js | 1022 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 1022 insertions(+) create mode 100644 client.js diff --git a/client.js b/client.js new file mode 100644 index 00000000..8eaa54f1 --- /dev/null +++ b/client.js @@ -0,0 +1,1022 @@ +/* Функция которая возвратит обьект CloudCommander + * @window - обьект window + * @document - обьект document + * @CloudFunc - обьект содержащий общий функционал + * клиентский и серверный + */ + +var CloudCommander=(function(){ +"use strict"; + +/* Клиентский обьект, содержащий функциональную часть*/ +var CloudClient = { + /* Конструктор CloudClient, который + * выполняет весь функционал по + * инициализации + */ + init :function(){}, + + keyBinding :function(){},/* функция нажатий обработки клавишь*/ + Editor :function(){},/* function loads and shows editor */ + Viewer :function(){},/* function loads and shows viewer */ + Terminal :function(){},/* function loads and shows terminal*/ + keyBinded :false,/* оброботка нажатий клавишь установлена */ + _loadDir :function(){},/* Функция привязываеться ко всем + * ссылкам и + * загружает содержимое каталогов */ + + /* ОБЬЕКТЫ */ + /* Обьект для работы с кэшем */ + Cache :{}, + /* Object contain additional system functional */ + Util :{}, + + /* ПРИВАТНЫЕ ФУНКЦИИ */ + /* функция загружает json-данные о файловой системе */ + _ajaxLoad :function(){}, + /* Функция генерирует JSON из html-таблицы файлов */ + _getJSONfromFileTable :function(){}, + /* функция меняет ссыки на ajax-овые */ + _changeLinks :function(){}, + + /* КОНСТАНТЫ*/ + /* название css-класа текущего файла*/ + CURRENT_FILE :'current-file', + LIBDIR :'/lib/', + LIBDIRCLIENT :'/lib/client/', + /* height of Cloud Commander + * seting up in init() + */ + HEIGHT :0, + MIN_ONE_PANEL_WIDTH :1155 +}; + +/* short names used all the time functions */ +var getByClass = function(pClass){ + return document.getElementsByClassName(pClass); +}; + +var getById = function(pId){ + return document.getElementById(pId); +}; + +/* + * Обьект для работы с кэшем + * в него будут включены функции для + * работы с LocalStorage, webdb, + * indexed db etc. + */ +CloudClient.Cache={ + _allowed :true, /* приватный переключатель возможности работы с кэшем */ + /* функция проверяет возможно ли работать с кэшем каким-либо образом */ + isAllowed :function(){}, + /* Тип кэша, который доступен*/ + type :{}, + /* Функция устанавливает кэш, если выбранный вид поддерживаеться браузером*/ + set :function(){}, + /* Функция достаёт кэш, если выбранный вид поддерживаеться браузером*/ + get :function(){}, + /* функция чистит весь кэш для всех каталогов*/ + clear :function(){} +}; + + +/* функция проверяет поддерживаеться ли localStorage */ +CloudClient.Cache.isAllowed=(function(){ + if(window.localStorage && + localStorage.setItem && + localStorage.getItem){ + CloudClient.Cache._allowed=true; + }else + { + CloudClient.Cache._allowed=false; + /* загружаем PolyFill для localStorage, + * если он не поддерживаеться браузером + * https://gist.github.com/350433 + */ + /* + Util.jsload('https://raw.github.com/gist/350433/c9d3834ace63e5f5d7c8e1f6e3e2874d477cb9c1/gistfile1.js', + function(){CloudClient.Cache._allowed=true; + }); + */ + } +}); + /* если доступен localStorage и + * в нём есть нужная нам директория - + * записываем данные в него + */ +CloudClient.Cache.set = (function(pName, pData){ + if(CloudClient.Cache._allowed && pName && pData){ + localStorage.setItem(pName,pData); + } +}); +/* Если доступен Cache принимаем из него данные*/ +CloudClient.Cache.get = (function(pName){ + if(CloudClient.Cache._allowed && pName){ + return localStorage.getItem(pName); + } + else return null; +}); +/* Функция очищает кэш*/ +CloudClient.Cache.clear = (function(){ + if(CloudClient.Cache._allowed){ + localStorage.clear(); + } +}); + +/* Object contain additional system functional */ +CloudClient.Util = (function(){ + /* + * Function gets id by src + * from http://domain.com/1.js to + * 1_js + */ + this.getIdBySrc = function(pSrc){ + var lID=pSrc.replace(pSrc.substr(pSrc, + pSrc.lastIndexOf('/')+1), + ''); + /* убираем точку*/ + return lID.replace('.','_'); + }, + + /* + * Функция создаёт элемент и + * загружает файл с src. + * @pName - название тэга + * @pSrc - путь к файлу + * @pFunc - обьект, содержаий одну из функций + * или сразу две onload и onerror + * {onload: function(){}, onerror: function();} + * @pStyle - стиль + * @pId - id + * @pElement - элемент, дочерним которо будет этот + * @pParams_o = {name: '', src: ' ',func: '', style: '', id: '', parent: '', + async: false, inner: 'id{color:red, }, class:'', not_append: false} + */ + this.anyload = function(pParams_o){ + /* убираем путь к файлу, оставляя только название файла */ + var lID = pParams_o.id; + var lClass = pParams_o.className; + var lSrc = pParams_o.src; + var lFunc = pParams_o.func; + var lAsync = pParams_o.async; + + if(!lID) { + lID = this.getIdBySrc(lSrc); + } + + var element = getById(lID); + /* если скрипт еще не загружен */ + if(!element) + { + element = document.createElement(pParams_o.name); + element.id = lID; + if(lClass) + element.className = lClass; + /* if working with external css + * using href in any other case + * using src + */ + pParams_o.name === 'link' ? + element.href = lSrc + : element.src = lSrc; + + /* if passed arguments function + * then it's onload by default + */ + if(pParams_o.func) + if(typeof lFunc === 'function'){ + element.onload = lFunc; + /* if object - then onload or onerror */ + }else if (typeof lFunc === 'object') { + if(lFunc.onload && + typeof lFunc.onload === 'function') + element.onload = lFunc.onload; + + if(lFunc.onerror && + typeof lFunc.onerror === 'function') + element.onerror = (function(){ + (pParams_o.element || document.body) + .removeChild(element); + + lFunc.onerror(); + }); + } + + if(pParams_o.style){ + element.style.cssText=pParams_o.style; + } + + if(lAsync || lAsync === undefined) + element.async = true; + + if(!pParams_o.not_append) + (pParams_o.parent || document.body).appendChild(element); + + if(pParams_o.inner){ + element.innerHTML = pParams_o.inner; + } + } + /* если js-файл уже загружен + * запускаем функцию onload + */ + else if(lFunc && typeof lFunc==='function'){ + try{ + lFunc(); + }catch(error){console.log(error);} + } + return element; + }, + + /* Функция загружает js-файл */ + this.jsload = function(pSrc, pFunc, pStyle, pId, pAsync, pInner){ + this.anyload({ + name : 'script', + src : pSrc, + func : pFunc, + stle : pStyle, + id : pId, + async: pAsync, + inner: pInner + }); + }, + + /* Функция создаёт елемент style и записывает туда стили + * @pParams_o - структура параметров, заполняеться таким + * образом: {src: ' ',func: '', id: '', element: '', inner: ''} + * все параметры опциональны + */ + this.cssSet = function(pParams_o){ + pParams_o.name = 'style'; + pParams_o.parent = pParams_o.parent || document.head; + + return this.anyload(pParams_o); + + }, + /* Function loads external css files + * @pParams_o - структура параметров, заполняеться таким + * образом: {src: ' ',func: '', id: '', element: '', inner: ''} + * все параметры опциональны + */ + this.cssLoad = function(pParams_o){ + pParams_o.name = 'link'; + pParams_o.parent = pParams_o.parent || document.head; + var lElem = this.anyload(pParams_o); + + lElem && + (lElem.rel = 'stylesheet'); + + return lElem; + }; + + this.getById = function(pId){return document.getElementById(pId);}; + + this.getByClass = function(pClass){ + return document.getElementsByClassName(pClass); + }; + + this.getPanel = function(){ + var lCurrent = document.getElementsByClassName('current-file'); + lCurrent.length && + (lCurrent = lCurrent[0].parentElement); + + return lCurrent && lCurrent.id; + }; + + /* private members */ + var lLoadingImage; + var lErrorImage; + + /* Обьект, который содержит + * функции для отображения + * картинок + */ + var LImages_o = { + /* Функция создаёт картинку загрузки*/ + loading : function(){ + var lE = Util.getById('loading-image'); + if (!lE) + lE = Util.anyload({ + name : 'span', + className : 'icon loading', + id : 'loading-image', + not_append : true + }); + + lLoadingImage = lE; + + return lE; + }, + + /* Функция создаёт картинку ошибки загрузки*/ + error : function(){ + var lE = Util.getById('error-image'); + if (!lE) + lE = Util.anyload({ + name : 'span', + className : 'icon error', + id : 'error-image', + not_append : true + }); + + return lE; + } + }; + + var lThis = this; + this.Images = { + /* + * Function shows loading spinner + * @pElem - top element of screen + */ + showLoad : function(pElem){ + lLoadingImage = LImages_o.loading(); + lErrorImage = LImages_o.error(); + + lErrorImage.className = 'icon error hidden'; + + var lCurrent; + if(pElem) + lCurrent = pElem; + else + { + lCurrent = lThis.getByClass(CloudCommander.CURRENT_FILE); + lCurrent = lCurrent[0].firstChild.nextSibling; + } + + /* show loading icon * + * if it not showed */ + var lParent = lLoadingImage.parentElement; + if(!lParent || + (lParent && lParent !== lCurrent)) + lCurrent.appendChild(lLoadingImage); + + lLoadingImage.className = 'icon loading'; /* показываем загрузку*/ + }, + + hideLoad : function(){ + lLoadingImage = LImages_o.loading(); + lLoadingImage.className ='hidden'; + }, + + showError : function(jqXHR, textStatus, errorThrown){ + lLoadingImage = LImages_o.loading(); + + lErrorImage = LImages_o.error(); + + var lText = jqXHR.responseText; + + /* если файла не существует*/ + if(!lText.indexOf('Error: ENOENT, ')) + lText = lText.replace('Error: ENOENT, n','N'); + /* если не хватает прав для чтения файла*/ + else if(!lText.indexOf('Error: EACCES,')) + lText = lText.replace('Error: EACCES, p','P'); + + lErrorImage.className='icon error'; + lErrorImage.title = lText; + + var lParent = lLoadingImage.parentElement; + if(lParent) + lParent.appendChild(lErrorImage); + + lLoadingImage.className ='hidden'; + + console.log(lText); + } + }; + + this.getCurrentFile = function(){ + var lCurrent = lThis.getByClass(CloudCommander.CURRENT_FILE)[0]; + if(!lCurrent) + console.log('Error: Can not find Current File'); + + return lCurrent; + }; + + /* function getting panel active, or passive + * @pPanel_o = {active: true} + */ + this.getPanel = function(pActive){ + var lPanel; + + lPanel = lThis.getCurrentFile().parentElement; + + /* if two panels showed + * then always work with passive + * panel + */ + if(window.innerWidth > CloudCommander.MIN_ONE_PANEL_WIDTH) + pActive = {active: false}; + + /* if {active : false} getting passive panel */ + if(pActive && !pActive.active){ + var lId = lPanel.id === 'left' ? 'right' : 'left'; + lPanel = lThis.getById(lId); + } + + + if(!lPanel) + console.log('Error can not find Active Panel'); + + return lPanel; + }; + + this.showPanel = function(pActive){ + var lPanel = lThis.getPanel(pActive); + + if(lPanel) + lPanel.className = 'panel'; + }; + + this.hidePanel = function(pActive){ + var lPanel = lThis.getPanel(pActive); + + if(lPanel) + lPanel.className = 'panel hidden'; + }; +}); + + + +/* функция обработки нажатий клавишь */ +CloudClient.keyBinding=(function(){ + /* loading keyBinding module and start it */ + Util.jsload(CloudClient.LIBDIRCLIENT+'keyBinding.js',function(){ + CloudCommander.keyBinding(); + }); +}); + +/* function loads and shows editor */ +CloudClient.Editor = (function() { + /* loading CloudMirror plagin */ + Util.jsload(CloudClient.LIBDIRCLIENT + + 'editor.js',{ + onload:(function(){ + CloudCommander.Editor.Keys(); + }) + }); +}); + +/* function loads and shows viewer */ +CloudClient.Viewer = (function(){ + Util.jsload(CloudClient.LIBDIRCLIENT + + 'viewer.js',{ + onload: (function(){ + CloudCommander.Viewer.Keys(); + }) + }); +}); + +/* function loads and shows terminal */ +CloudClient.Terminal = (function(){ + Util.jsload(CloudClient.LIBDIRCLIENT + + 'terminal.js',{ + onload: (function(){ + CloudCommander.Terminal.Keys(); + }) + }); +}); + +/* + * Функция привязываеться ко всем ссылкам и + * загружает содержимое каталогов + */ +CloudClient._loadDir=(function(pLink,pNeedRefresh){ + /* @pElem - элемент, + * для которого нужно + * выполнить загрузку + */ + return function(){ + /* показываем гиф загрузки возле пути папки сверху*/ + /* ctrl+r нажата? */ + Util.Images.showLoad(pNeedRefresh ? this : null); + + var lCurrentFile=getByClass(CloudClient.CURRENT_FILE); + /* получаем имя каталога в котором находимся*/ + var lHref; + try{ + lHref=lCurrentFile[0].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+'/',''); + + /* загружаем содержимое каталога*/ + CloudClient._ajaxLoad(pLink, pNeedRefresh); + + /* получаем все элементы выделенной папки*/ + /* при этом, если мы нажали обновить + * или +R - ссылок мы ненайдём + * и заходить не будем + */ + var lA=this.getElementsByTagName('a'); + /* если нажали на ссылку на верхний каталог*/ + if(lA.length>0 && lA[0].textContent==='..' && + lHref!=='/'){ + /* функция устанавливает курсор на каталог + * с которого мы пришли, если мы поднялись + * в верх по файловой структуре + */ + CloudClient._currentToParent(lHref); + } + + /* что бы не переходить по ссылкам + * а грузить всё ajax'ом, + * возвращаем false на событие + * onclick + */ + return false; + }; + }); + + +/* + * Function edits file name + * + * @pParent - parent element + * @pEvent + */ +CloudClient._editFileName = (function(pParent){ +var lA = pParent.getElementsByTagName('a'); + if (lA.length && lA.textContent !== '..'){ + + lA[0].contentEditable = true; + CloudCommander.keyBinded = false; + + var lDocumentOnclick = document.onclick; + + /* setting event handler onclick + * if user clicks somewhere keyBinded + * backs + */ + document.onclick = (function(){ + var lA = pParent.getElementsByTagName('a'); + if (lA.length && lA.textContent !== '..') + lA[0].contentEditable = false; + + CloudCommander.keyBinded = true; + + /* backs old document.onclick + * and call it if it was + * setted up earlier + */ + document.onclick = lDocumentOnclick; + if(typeof lDocumentOnclick === 'function') + lDocumentOnclick(); + + }); + } +}); + +/* Функция устанавливает текущим файлом, тот + * на который кликнули единожды + */ +CloudClient._setCurrent=(function(){ + /* + * @pFromEnter - если мы сюда попали + * из события нажатия на энтер - + * вызоветься _loadDir + */ + return function(pFromEnter){ + var lCurrentFile=getByClass(CloudClient.CURRENT_FILE); + if(lCurrentFile && lCurrentFile.length > 0){ + /* если мы находимся не на + * пути и не на заголовках + */ + if(this.className!=='path' && + this.className!=='fm_header'){ + + if (this.className === CloudClient.CURRENT_FILE && + typeof pFromEnter !== 'boolean'){ + 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(lParent.className === CloudClient.CURRENT_FILE) + CloudClient._editFileName(lParent); + },400); + } + else{ + lCurrentFile[0].className=''; + /* устанавливаем курсор на файл, + * на который нажали */ + this.className = CloudClient.CURRENT_FILE; + } + } + } + /* если мы попали сюда с энтера*/ + if(pFromEnter===true){ + this.ondblclick(this); + }/* если мы попали сюда от клика мышки */ + else{pFromEnter.returnValue=false;} + + /* что бы не переходить по ссылкам + * а грузить всё ajax'ом, + * возвращаем false на событие + * onclick + */ + return false; + }; + }); + +/* функция устанавливает курсор на каталог + * с которого мы пришли, если мы поднялись + * в верх по файловой структуре + * @pDirName - имя каталога с которого мы пришли + */ +CloudClient._currentToParent = (function(pDirName){ + /* опредиляем в какой мы панели: + * правой или левой + */ + var lCurrentFile = getByClass(CloudClient.CURRENT_FILE); + var lPanel = lCurrentFile[0].parentElement; + + /* убираем слэш с имени каталога*/ + pDirName=pDirName.replace('/',''); + + var lRootDir = getById(pDirName + '(' + lPanel.id + ')'); + + /* if found li element with ID directory name + * set it to current file + */ + if(lRootDir){ + !(lCurrentFile[0].className = '') && + (lRootDir.className = CloudClient.CURRENT_FILE); + } +}); + +/* глобальные переменные */ +var CloudFunc, $, Util; + +/* Конструктор CloudClient, который + * выполняет весь функционал по + * инициализации + */ +CloudClient.init=(function() +{ + Util = new CloudClient.Util(); + + /* меняем title + * если js включен - имена папок отображать необязательно... + * а может и обязательно при переходе, можно будет это сделать + */ + var lTitle=document.getElementsByTagName('title'); + if(lTitle.length>0)lTitle[0].textContent='Cloud Commander'; + + /* загружаем jquery: */ + Util.jsload('//ajax.googleapis.com/ajax/libs/jquery/1.7.2/jquery.min.js',{ + onload: function(){ + $ = window.jQuery; + }, + + onerror: function(){ + Util.jsload('lib/client/jquery.js'); + + /* + * if could not load jquery from google server + * maybe we offline, load font from local + * directory + */ + Util.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");}' + }); + } + }); + + /* загружаем общие функции для клиента и сервера*/ + Util.jsload(CloudClient.LIBDIR+'cloudfunc.js',function(){ + /* берём из обьекта window общий с сервером функционал */ + CloudFunc=window.CloudFunc; + + /* меняем ссылки на ajax'овые*/ + CloudClient._changeLinks(CloudFunc.LEFTPANEL); + CloudClient._changeLinks(CloudFunc.RIGHTPANEL); + + /* устанавливаем переменную доступности кэша*/ + CloudClient.Cache.isAllowed(); + /* Устанавливаем кэш корневого каталога */ + if(!CloudClient.Cache.get('/'))CloudClient.Cache.set('/',CloudClient._getJSONfromFileTable()); + } + ); + + /* устанавливаем размер высоты таблицы файлов + * исходя из размеров разрешения экрана + */ + + /* выделяем строку с первым файлом */ + var lFmHeader=getByClass('fm_header'); + if(lFmHeader && lFmHeader[0].nextSibling) + lFmHeader[0].nextSibling.className=CloudClient.CURRENT_FILE; + + /* показываем элементы, которые будут работать только, если есть js */ + var lFM = getById('fm'); + if(lFM) + lFM.className='localstorage'; + + /* если есть js - показываем правую панель*/ + var lRight=getById('right'); + if(lRight)lRight.className=lRight.className.replace('hidden',''); + + /* формируем и округляем высоту экрана + * при разрешениии 1024x1280: + * 658 -> 700 + */ + + var lHeight = + window.screen.height - + (window.screen.height/3).toFixed(); + + lHeight=(lHeight/100).toFixed()*100; + + CloudClient.HEIGHT = lHeight; + + Util.cssSet({id:'show_2panels', + element:document.head, + inner:'#left{width:46%;}' + + '.panel{height:' + lHeight +'px' + }); +}); + +/* функция меняет ссыки на ajax-овые */ +CloudClient._changeLinks = function(pPanelID) +{ + /* назначаем кнопку очистить кэш и показываем её*/ + var lClearcache=getById('clear-cache'); + if(lClearcache)lClearcache.onclick=CloudClient.Cache.clear; + + /* меняем ссылки на ajax-запросы */ + var lPanel=getById(pPanelID); + var a=lPanel.getElementsByTagName('a'); + + /* Если нажмут на кнопку перезагрузить страниц - её нужно будет обязательно + * перезагрузить + */ + /* номер ссылки очистки кэша*/ + /* номер ссылки иконки обновления страницы */ + var lREFRESHICON=0; + + /* путь в ссылке, который говорит + * что js отключен + */ + var lNoJS_s = CloudFunc.NOJS; + var lFS_s = CloudFunc.FS; + + for(var i=0;i