/* Функция которая возвратит обьект 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