Файл index.js
require.config({
paths: {
modulesTree: 'lib/require/modules-tree'
, main: 'app/main/controller'
}
, urlArgs: '_=' + Math.random()
});
require(
[
'modulesTree'
, 'main'
]
, function(
modulesTree
, main
) {
main();
if (console && console.log) {
console.log('Функция формирования дерева зависимостей для каждого модуля');
window.requirejsModules.generateModuleDependenciesTree();
console.log('Отображение имен всех загруженных модулей');
for (var key in window.requirejsModules.list) {console.log(key);}
console.log('Отображение списка зависимостей всех модулей');
console.log(window.requirejsModules.generateModuleDependenciesList());
console.log('Отображение списка путей к файлам модулей');
console.log(window.requirejsModules.generateModulePathList());
console.log('Отображение дерева зависимостей модулей друг от друга');
console.log(window.requirejsModules.drawTreeGraph(window.requirejsModules.generateModuleDependenciesTreeObject())); //, '', {unicode: false}));
console.log('Отображение вложенности файлов и папок друг в друга');
console.log(window.requirejsModules.drawTreeGraph(window.requirejsModules.generateModulePathTreeObject()));
console.log('Отображение зависимостей данного модуля в дереве файлов и папок');
console.log(window.requirejsModules.drawModulePathTreeGraphWithDependencies('InterfaceBuilderController'));
console.log('Отображение включения данного модуля в другие модули в дереве файлов и папок');
console.log(window.requirejsModules.drawModulePathTreeGraphWithDependencies('jquery', {includeThisModule: true}));
}
var windowWidth = 1300
, windowHeight = 500
, top = Math.round( (window.screen.availWidth - windowWidth) / 4 )
, left = Math.round( (window.screen.height - windowHeight) / 4 )
, windowSpecs = [
'width=' + windowWidth
, 'height=' + windowHeight
, 'top=' + (top > 0 ? top : 0)
, 'left=' + (left > 0 ? left : 0)
, 'scrollbars=yes'
, 'resizable=yes'
].join(', ')
, moduleWindow;
if (moduleWindow === undefined) {moduleWindow = window.open('modulemap.html', '_blank', windowSpecs);}
}
);
Файл modules-tree.js
// Функция формирования списка загруженных модулей
requirejs.onResourceLoad = function (context, module, moduleDependenciesArray) {
if (!window.requirejsModules) {
window.requirejsModules = {
list: {}
, generateModuleDependenciesTree: generateModuleDependenciesTree
, generateModuleDependenciesTreeObject: generateModuleDependenciesTreeObject
, generateModuleDependenciesList: generateModuleDependenciesList
, generateModulePathList: generateModulePathList
, generateModulePathTreeObject: generateModulePathTreeObject
, drawModulePathTreeGraphWithDependencies: drawModulePathTreeGraphWithDependencies
, drawTreeGraph: drawTreeGraph
};
}
if (!window.requirejsModules.list[module.name]) {
window.requirejsModules.list[module.name] = {
moduleName: module.name
, modulePath: (function(){
var url = '';
if (module.url) {url = module.url.split('?')[0];}
if (module.name.search('.html') === -1) {
return url.replace('./', '');
} else {
return 'js/' + module.name;
}
})()
, moduleDependenciesNames: []
, moduleDependenciesTree: {}
};
}
var i, len;
if (moduleDependenciesArray) {
for (
i = 0, len = moduleDependenciesArray.length;
i < len;
i++
) {
window.requirejsModules.list[module.name].moduleDependenciesNames.push(moduleDependenciesArray[i].name);
}
}
};
// Функция формирования дерева зависимостей для каждого модуля
function generateModuleDependenciesTree () {
var modulesList = window.requirejsModules.list
, moduleName
, currentModule
, i
, len
, currentDependencyName;
for (moduleName in modulesList) {
if (modulesList.hasOwnProperty(moduleName)) {
currentModule = modulesList[moduleName];
for (
i = 0, len = currentModule.moduleDependenciesNames.length;
i < len;
i++
) {
currentDependencyName = currentModule.moduleDependenciesNames[i];
currentModule.moduleDependenciesTree[currentDependencyName] = modulesList[currentDependencyName];
}
}
}
}
// Функция формирования иерархического объекта, состоящего из имен модулей, для построения дерева вложенности модулей
function generateModuleDependenciesTreeObject (mainModuleName) {
generateModuleDependenciesTree();
if (mainModuleName === undefined) {mainModuleName = 'main';}
var moduleDependenciesTreeObject = buildModuleObject(window.requirejsModules.list[mainModuleName]);
function buildModuleObject (module) {
var moduleObject
, moduleDependencyName;
if (module !== undefined) {
moduleObject = {
name: module.moduleName
, path: module.modulePath
, dependencies: []
};
for (moduleDependencyName in module.moduleDependenciesTree) {
moduleObject.dependencies.push(buildModuleObject(module.moduleDependenciesTree[moduleDependencyName]));
}
moduleObject.dependencies.sort(function(a, b){a = a.name; b = b.name; if (a > b) {return 1;} else if (a < b) {return -1;} else {return 0;}});
} else {
moduleObject = {
name: ''
, path: ''
, dependencies: []
};
}
return moduleObject;
}
return moduleDependenciesTreeObject;
}
// Функция формирования списка зависимостей всех модулей
function generateModuleDependenciesList () {
var dependenciesList = []
, modulesList = window.requirejsModules.list
, moduleName
, currentModule
, i
, len;
for (moduleName in modulesList) {
if (modulesList.hasOwnProperty(moduleName)) {
currentModule = modulesList[moduleName];
len = currentModule.moduleDependenciesNames.length;
if (len) {
for (i = 0; i < len; i++) {
dependenciesList.push(moduleName + ' < ' + currentModule.moduleDependenciesNames[i]);
}
} else {
dependenciesList.push(currentModule.moduleName);
}
}
}
dependenciesList.sort(function(a, b){if (a > b) {return 1;} else if (a < b) {return -1;} else {return 0;}});
return dependenciesList.join('\n');
}
// Функция формирования списка путей к файлам модулей
function generateModulePathList (mainModuleName, includeThisModule) {
var moduleTypeMark = function () {return '';};
if (mainModuleName !== undefined) {
if (includeThisModule === true) {
moduleTypeMark = function (currentModule) {
if (currentModule.moduleName === mainModuleName) {
return '@'; // @ - main module
} else if (isCurrentModuleIncludeMainModule(currentModule.moduleName)) {
return '*'; // * - dependency
} else {
return ''; // '' - other
}
function isCurrentModuleIncludeMainModule (currentModuleName) {
var moduleDependenciesNames = window.requirejsModules.list[currentModuleName].moduleDependenciesNames
, i
, len = moduleDependenciesNames.length;
for (i = 0; i < len; i++) {
if (moduleDependenciesNames[i] === mainModuleName) {
return true;
}
}
return false;
}
};
} else {
moduleTypeMark = function (currentModule) {
if (currentModule.moduleName === mainModuleName) {
return '@'; // @ - main module
} else if (isDependency(currentModule.modulePath)) {
return '*'; // * - dependency
} else {
return ''; // '' - other
}
function isDependency (currentModulePath) {
var moduleDependenciesTreeObject = generateModuleDependenciesTreeObject(mainModuleName)
, dependencies = moduleDependenciesTreeObject.dependencies
, i
, len = dependencies.length;
for (i = 0; i < len; i++) {
if (dependencies[i].path === currentModulePath) {
return true;
}
}
return false;
}
};
}
}
var pathList = []
, modulesList = window.requirejsModules.list
, moduleName
, currentModule;
for (moduleName in modulesList) {
if (modulesList.hasOwnProperty(moduleName)) {
currentModule = modulesList[moduleName];
pathList.push(currentModule.modulePath + moduleTypeMark(currentModule));
}
}
pathList.sort(function(a, b){if (a > b) {return 1;} else if (a < b) {return -1;} else {return 0;}});
return pathList.join('\n');
}
// Функция формирования иерархического объекта, состоящего из путей к модулям, для построения дерева вложенности файлов и папок
function generateModulePathTreeObject (mainModuleName, includeThisModule) {
var pathList = generateModulePathList(mainModuleName, includeThisModule).split('\n');
return convertArrayOfDelimitedStringsIntoHierarchicalObject(pathList, '/');
}
// Функция преобразования массива из строк в иерархический объект вложенности элементов друг в друга
function convertArrayOfDelimitedStringsIntoHierarchicalObject (input, delimiter) {
// input = ['Fred-Jim-Bob', 'Fred-Jim', 'Fred-Thomas-Rob', 'Fred'];
// delimiter = '-';
var output = []
, i, j, k
, inputLength
, chain, chainLength
, currentNode, currentNodeLength
, wantedNode
, lastNode
, newNode;
for (i = 0, inputLength = input.length; i < inputLength; i++) {
chain = input[i].split(delimiter);
currentNode = output;
for (j = 0, chainLength = chain.length; j < chainLength; j++) {
wantedNode = chain[j];
lastNode = currentNode;
for (k = 0, currentNodeLength = currentNode.length; k < currentNodeLength; k++) {
if (currentNode[k].name === wantedNode) {
currentNode = currentNode[k].dependencies;
break;
}
}
// If we couldn't find an item in this list of dependencies
// that has the right name, create one:
if (lastNode === currentNode) {
currentNode[k] = {name: wantedNode, dependencies: [], path: input[i]}; // Добавлен path для того, чтобы выделять элементы в дереве
newNode = currentNode[k];
currentNode = newNode.dependencies;
}
}
}
return output.length > 0 ? output[0] : {};
}
// Функция построения дерева вложенности файлов и папок с отображением зависимостей модулей друг от друга
function drawModulePathTreeGraphWithDependencies (mainModuleName, options) {
if (options === undefined) {options = {};}
function chr (s) {
var chars = {
'─' : '-' // ─ box drawing light horizontal
, '│' : '|' // │ box drawing light vertical
, '┐' : '\\' // ┐ box drawing light down and left
, '┘' : '/' // ┘ box drawing light up and left
, '┌' : '/' // ┌ box drawing light down and right
, '└' : '\\' // └ box drawing light up and right
, '┤' : '+' // ┤ box drawing light vertical and left
, '├' : '+' // ├ box drawing light vertical and right
, '┬' : '+' // ┬ box drawing light down and horizontal
, '┴' : '+' // ┴ box drawing light up and horizontal
};
if (options.unicode === false) {return chars[s];} else {return s;}
}
var pathsTreeArray = (drawTreeGraph(generateModulePathTreeObject(mainModuleName, options.includeThisModule), '||')).split('||').slice(1)
, i
, j
, len = pathsTreeArray.length
, repeat
, charactersInRow
, maxCharactersInRow = 0
, dependencyIndexes = []
, dependencyIndexesLength;
for (i = 0; i < len; i++) {
pathsTreeArray[i] = pathsTreeArray[i].replace('\n', '');
if (pathsTreeArray[i].indexOf('*') > -1) {
dependencyIndexes.push(i);
}
if (pathsTreeArray[i].indexOf('@') > -1) {
dependencyIndexes.push(i);
pathsTreeArray[i] = pathsTreeArray[i].replace('@', ' ]*');
pathsTreeArray[i] = pathsTreeArray[i].split(' ');
pathsTreeArray[i][pathsTreeArray[i].length - 2] = '[ ' + pathsTreeArray[i][pathsTreeArray[i].length - 2];
pathsTreeArray[i] = pathsTreeArray[i].join(' ');
}
charactersInRow = pathsTreeArray[i].length;
if (charactersInRow > maxCharactersInRow) {
maxCharactersInRow = charactersInRow;
}
}
maxCharactersInRow += 2;
dependencyIndexesLength = dependencyIndexes.length;
if (dependencyIndexesLength > 0) {
if (dependencyIndexesLength === 1) {
pathsTreeArray[dependencyIndexes[0]] = pathsTreeArray[dependencyIndexes[0]].replace('*', '');
} else if (dependencyIndexesLength === 2) {
for (i = dependencyIndexes[0]; i <= dependencyIndexes[1]; i++) {
if (i === dependencyIndexes[0]) {
pathsTreeArray[i] = pathsTreeArray[i].replace('*', '');
charactersInRow = pathsTreeArray[i].length;
repeat = maxCharactersInRow - charactersInRow;
for (j = 0; j < repeat; j++) {
if (j === 0) {
pathsTreeArray[i] += ' ';
} else if (j === repeat - 1) {
pathsTreeArray[i] += chr('┐');
} else {
pathsTreeArray[i] += chr('─');
}
}
} else if (i === dependencyIndexes[1]) {
pathsTreeArray[i] = pathsTreeArray[i].replace('*', '');
charactersInRow = pathsTreeArray[i].length;
repeat = maxCharactersInRow - charactersInRow;
for (j = 0; j < repeat; j++) {
if (j === 0) {
pathsTreeArray[i] += ' ';
} else if (j === repeat - 1) {
pathsTreeArray[i] += chr('┘');
} else {
pathsTreeArray[i] += chr('─');
}
}
} else {
charactersInRow = pathsTreeArray[i].length;
repeat = maxCharactersInRow - charactersInRow;
for (j = 0; j < repeat; j++) {
if (j === repeat - 1) {
pathsTreeArray[i] += chr('│');
} else {
pathsTreeArray[i] += ' ';
}
}
}
}
} else if (dependencyIndexesLength > 2) {
for (i = dependencyIndexes[0]; i <= dependencyIndexes[dependencyIndexesLength - 1]; i++) {
if (i === dependencyIndexes[0]) {
pathsTreeArray[i] = pathsTreeArray[i].replace('*', '');
charactersInRow = pathsTreeArray[i].length;
repeat = maxCharactersInRow - charactersInRow;
for (j = 0; j < repeat; j++) {
if (j === 0) {
pathsTreeArray[i] += ' ';
} else if (j === repeat - 1) {
pathsTreeArray[i] += chr('┐');
} else {
pathsTreeArray[i] += chr('─');
}
}
} else if (i === dependencyIndexes[dependencyIndexesLength - 1]) {
pathsTreeArray[i] = pathsTreeArray[i].replace('*', '');
charactersInRow = pathsTreeArray[i].length;
repeat = maxCharactersInRow - charactersInRow;
for (j = 0; j < repeat; j++) {
if (j === 0) {
pathsTreeArray[i] += ' ';
} else if (j === repeat - 1) {
pathsTreeArray[i] += chr('┘');
} else {
pathsTreeArray[i] += chr('─');
}
}
} else if ((function(){
var middleDependencyIndexes = dependencyIndexes.slice(1, - 1);
for (var z = 0, len = middleDependencyIndexes.length; z < len; z++) {
if (i === middleDependencyIndexes[z]) {return true;}
}
return false;
})()) {
pathsTreeArray[i] = pathsTreeArray[i].replace('*', '');
charactersInRow = pathsTreeArray[i].length;
repeat = maxCharactersInRow - charactersInRow;
for (j = 0; j < repeat; j++) {
if (j === 0) {
pathsTreeArray[i] += ' ';
} else if (j === repeat - 1) {
pathsTreeArray[i] += chr('┤');
} else {
pathsTreeArray[i] += chr('─');
}
}
} else {
charactersInRow = pathsTreeArray[i].length;
repeat = maxCharactersInRow - charactersInRow;
for (j = 0; j < repeat; j++) {
if (j === repeat - 1) {
pathsTreeArray[i] += chr('│');
} else {
pathsTreeArray[i] += ' ';
}
}
}
}
}
}
for (i = 0; i < len; i++) {
pathsTreeArray[i] += '\n';
}
return pathsTreeArray.join('');
}
// Функция построения дерева потомков элемента
function drawTreeGraph (obj, prefix, options) {
if (typeof obj === 'string') {obj = {name: obj};}
if (prefix === undefined) {prefix = '';}
if (options === undefined) {options = {};}
function chr (s) {
var chars = {
'─' : '-' // ─ box drawing light horizontal
, '│' : '|' // │ box drawing light vertical
, '┐' : '\\' // ┐ box drawing light down and left
, '┘' : '/' // ┘ box drawing light up and left
, '┌' : '/' // ┌ box drawing light down and right
, '└' : '\\' // └ box drawing light up and right
, '┤' : '+' // ┤ box drawing light vertical and left
, '├' : '+' // ├ box drawing light vertical and right
, '┬' : '+' // ┬ box drawing light down and horizontal
, '┴' : '+' // ┴ box drawing light up and horizontal
};
if (options.unicode === false) {return chars[s];} else {return s;}
}
var dependencies = obj.dependencies || []
, lines = (obj.name || '').split('\n')
, splitter = '\n' + prefix + (dependencies.length ? chr('│') : ' ') + ' ';
return prefix
+ lines.join(splitter) + '\n'
+ dependencies.map(function (module, index) {
var last = (index === dependencies.length - 1)
, more = (module.dependencies && module.dependencies.length)
, prefix_ = prefix + (last ? ' ' : chr('│')) + ' ';
return prefix
+ (last ? chr('└') : chr('├')) + chr('─')
+ (more ? chr('┬') : chr('─')) + ' '
+ drawTreeGraph(module, prefix_, options).slice(prefix.length + 2);
}).join('');
}
Файл modulemap.html
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<meta http-equiv="X-UA-Compatible" content="IE=8" />
<meta http-equiv="cache-control" content="max-age=0" />
<meta http-equiv="cache-control" content="no-cache" />
<meta http-equiv="expires" content="0" />
<meta http-equiv="expires" content="Tue, 01 Jan 1980 1:00:00 GMT" />
<meta http-equiv="pragma" content="no-cache" />
<link href="style/lib/modulemap/modulemap.css?v=1.0.0" rel="stylesheet" type="text/css" />
<link href="style/lib/syntaxhighlighter/shCore.css?v=1.0.0" rel="stylesheet" type="text/css" />
<link href="style/lib/syntaxhighlighter/shCoreDefault.css?v=1.0.0" rel="stylesheet" type="text/css" />
<link href="style/lib/syntaxhighlighter/shCoreForModuleMap.css?v=1.0.0" rel="stylesheet" type="text/css" />
<style type="text/css">.syntaxhighlighter {margin-bottom: 0;}</style>
<script src="js/lib/syntaxhighlighter/shCore-with-fix.js?v=1.0.0" type="text/javascript"></script>
<script src="js/lib/syntaxhighlighter/shBrushJScript.js?v=1.0.0" type="text/javascript"></script>
<script src="js/lib/jquery/jquery.js?v=1.0.0" type="text/javascript"></script>
<script src="js/lib/js-beautify/beautify.js?v=1.0.0" type="text/javascript"></script>
<script data-main="js/lib/modulemap/modulemap.js?v=1.0.0" src="js/lib/require/require.js?v=1.0.0" type="text/javascript"></script>
<!--<script src="js/lib/modulemap/modulemap.js?v=1.0.0" type="text/javascript"></script>-->
<title>Аудит : Карта модулей</title>
</head>
<body>
<table id="header"></table>
<div id="grid" class="hidden">
<table id="module-grid-section-control">
<tr>
<td id="module-grid-section-control-empty"></td>
<td id="module-grid-section-visibility-control">
<div class="visibility-filter">
<div id="tree-section-visibility" class="visible"></div><div id="map-section-visibility" class="visible"></div><div id="file-section-visibility" class="visible"></div>
</div>
</td>
<td id="module-grid-section-control-empty"></td>
</tr>
</table>
<table class="module-grid">
<tr>
<td class="tree-section">
<div class="module-filter">
<div>Показывать от чего зависит модуль</div><div>Показывать куда входит модуль</div>
</div>
<div class="folder-tree">
<ul class="list-tree"></ul>
</div>
</td>
<td class="map-section">
<div class="map-filter">
<div>Горизонтальная схема</div><div>Вертикальная схема</div>
</div>
<div id="module-tree"></div>
</td>
<td class="file-section">
<div class="file-filter">
<div>Исходный код</div><div>Переформатированный код</div>
</div>
<div id="file-code">
<p id="file-code-hint">Кликните на модуль для отображения его кода</p>
</div>
</td>
</tr>
</table>
</div>
</body>
</html>
Файл modulemap.css
/*****************************************************************\
Reset CSS
\*****************************************************************/
html {
font-size: 100.01%; /* Bug fix for Opera */
}
body {
width: 99%;
min-width: 1200px;
margin: 10px;
margin-left: auto;
margin-right: auto;
padding: 0;
text-align: center;
font-family: Tahoma, Verdana, Arial, Helvetica, sans-serif;
font-size: 62.5%;
background-color: #ffffff;
color: #333333;
}
div,h1,h2,h3,h4,h5,h6,p,img,ol,ul,li,table,th,td,form,select,fieldset {
margin: 0;
padding: 0;
text-align: left;
}
article, aside, details, figcaption, figure, footer, header, hgroup, menu, nav, section {
display: block;
}
ol,ul {
list-style: none;
}
table {
border: 0; /* border="0" */
border-collapse: collapse; /* cellspacing="0" */
}
table tr th {
padding: 0; /* cellpadding="0" */
}
table tr td {
padding: 0; /* cellpadding="0" */
}
input,textarea,select {
font-family: Tahoma, Verdana, Arial, Helvetica, sans-serif;
font-size: 1.0em;
color: #333333;
}
/* don't show the x for text inputs */
input[type=text]::-ms-clear {
width : 0;
height: 0;
}
/* don't show the eye for password inputs */
input[type=password]::-ms-reveal {
width : 0;
height: 0;
}
textarea {
resize: none;
}
a img {
border: none;
}
a {
color: #333333;
text-decoration: underline;
}
a:link {
color: #333333;
text-decoration: underline;
}
a:visited {
color: #333333;
text-decoration: none;
}
a:hover {
color: #333333;
text-decoration: none;
}
a:active {
color: #333333;
text-decoration: none;
}
a:active,a:focus,img {
outline: 0; /* Firefox anchor border remove */
}
/*****************************************************************\
Common classes
\*****************************************************************/
/* Hidden element */
.hidden {
display: none !important;
}
/*****************************************************************\
Simple button
\*****************************************************************/
div.btn {
display: inline-block;
min-height: 16px;
padding: 3px 9px;
background-color: #2497e4;
text-align: center;
color: #ffffff;
white-space: nowrap;
cursor: pointer;
}
div.btn:hover {
background-color: #2e82ca;
}
div.btn-icon {
padding-left: 27px;
background-repeat: no-repeat;
background-position: 9px 4px;
}
div.btn-disabled {
background-color: #bfbfbf !important;
cursor: default !important;
}
/*****************************************************************\
List tree
\*****************************************************************/
ul.list-tree ul {
margin: 0;
padding: 0;
margin-left: -24px;
padding-left: 24px;
}
ul.list-tree ul ul {
background: url(img/list-tree/list-item-contents.png) repeat-y left;
}
ul.list-tree li.last > ul {
background-image: none;
}
ul.list-tree li {
position: relative;
margin: 0;
padding: 0;
line-height: 24px;
background: url(img/list-tree/list-item-root.png) no-repeat top left;
}
ul.list-tree li li {
padding-left: 24px;
background-image: url(img/list-tree/list-item.png);
}
ul.list-tree li.last {
background-image: url(img/list-tree/list-item-last.png);
}
ul.list-tree li.opened {
background-image: url(img/list-tree/list-item-open.png);
}
ul.list-tree li.closed {
background-image: url(img/list-tree/list-item.png);
}
ul.list-tree li.last.opened {
background-image: url(img/list-tree/list-item-last-open.png);
}
ul.list-tree li.last.closed {
background-image: url(img/list-tree/list-item-last.png);
}
ul.list-tree li.semiclosed {
background-image: url(img/list-tree/list-item-open.png);
}
ul.list-tree li.last.semiclosed {
background-image: url(img/list-tree/list-item-last-open.png);
}
ul.list-tree li.unclosed {
background-image: url(img/list-tree/list-item-open.png);
}
ul.list-tree li.last.unclosed {
background-image: url(img/list-tree/list-item-last-open.png);
}
ul.list-tree li.root {
background-image: url(img/list-tree/list-item-root.png);
}
ul.list-tree li.root.closed {
background-image: none;
}
ul.list-tree li.root.single {
background-image: none;
}
ul.list-tree span.element {
display: inline-block;
margin-left: 15px;
padding: 0px 5px;
vertical-align: top;
cursor: pointer;
white-space: nowrap;
}
ul.list-tree span.element:hover {
background: #2b7fed;
color: #ffffff;
}
ul.list-tree span.active {
background-color: #3c90fe !important;
color: #ffffff;
cursor: default;
}
ul.list-tree li span.selected {
background-color: #59adfe;
color: #ffffff;
}
ul.list-tree li span.button {
position: absolute;
display: block;
width: 11px;
height: 11px;
margin-top: 7px;
background-image: url(img/list-tree/button.png);
background-repeat: no-repeat;
background-position: top left;
}
ul.list-tree li.opened > span.button {
background-image: url(img/list-tree/button-open.png);
cursor: pointer;
}
ul.list-tree li.closed > span.button {
background-image: url(img/list-tree/button-closed.png);
cursor: pointer;
}
ul.list-tree li.semiclosed > span.button {
background-image: url(img/list-tree/button-closed.png);
cursor: pointer;
}
ul.list-tree li span.text {
margin-left: 2px;
}
/*****************************************************************\
Header
\*****************************************************************/
table#header {
width: 100%;
}
/*****************************************************************\
Grid
\*****************************************************************/
div#grid {
position: relative;
background-color: #ffffff;
font-size: 1.2em;
}
/*****************************************************************\
Module grid section control
\*****************************************************************/
table#module-grid-section-control {
width: 100%;
}
table#module-grid-section-control tr td {
padding: 5px;
border: 1px solid #0c6ed5;
}
table#module-grid-section-control tr td#module-grid-section-control-empty {
width: auto;
}
table#module-grid-section-control tr td#module-grid-section-visibility-control {
width: 100px;
text-align: center;
}
/*****************************************************************\
Module grid section control > Visibility filter
\*****************************************************************/
div.visibility-filter {
width: 100px;
height: 23px;
}
div.visibility-filter div {
display: inline-block;
width: 30px;
height: 23px;
background-image: url(img/icons/sections.gif);
background-repeat: no-repeat;
cursor: pointer;
}
div.visibility-filter div#tree-section-visibility {
background-position: 0px 0px;
}
div.visibility-filter div#map-section-visibility {
margin-left: 5px;
background-position: -30px 0px;
}
div.visibility-filter div#file-section-visibility {
margin-left: 5px;
background-position: -60px 0px;
}
div.visibility-filter div.visible {
background-color: #0c6ed5;
}
div.visibility-filter div.invisible {
background-color: #c0c0c0;
}
div.visibility-filter div:hover {
background-color: #0055b5;
}
/*****************************************************************\
Module grid
\*****************************************************************/
table.module-grid {
width: 100%;
}
table.module-grid tr td {
border: 1px solid #0c6ed5;
border-top: none;
vertical-align: top;
}
table.module-grid tr td.tree-section {
width: auto;
text-align: center;
}
table.module-grid tr td.map-section {
width: 40%;
text-align: center;
}
table.module-grid tr td.file-section {
width: 30%;
text-align: center;
}
table.module-grid tr td.width-auto {
width: auto !important;
}
table.module-grid tr td.width-50 {
width: 50% !important;
}
table.module-grid tr td.width-70 {
width: 100% !important;
}
table.module-grid tr td.width-100 {
width: 100% !important;
}
/*****************************************************************\
Module grid > Module filter
\*****************************************************************/
div.module-filter {
display: inline-block;
margin-top: 15px;
margin-left: 5px;
margin-right: 5px;
border-left: 1px solid #0c6ed5;
white-space: nowrap;
}
div.module-filter div {
display: inline-block;
padding: 4px 15px;
border-top: 1px solid #0c6ed5;
border-bottom: 1px solid #0c6ed5;
border-right: 1px solid #0c6ed5;
text-align: center;
white-space: nowrap;
color: #2497e4;
cursor: pointer;
}
div.module-filter div:hover {
color: #0c6ed5;
}
div.module-filter div.active {
background-color: #0c6ed5;
color: #ffffff;
cursor: default;
}
div.module-filter div.active:hover {
background-color: #0c6ed5;
color: #ffffff;
}
/*****************************************************************\
Module grid > Folder tree
\*****************************************************************/
div.folder-tree {
overflow: auto;
margin-top: 15px;
margin-left: 5px;
margin-right: 5px;
}
div.folder-tree ul.list-tree span.element span.icon {
display: inline-block;
width: 15px;
height: 15px;
line-height: 0;
margin-right: 2px;
vertical-align: text-top;
}
div.folder-tree ul.list-tree span.element span.folder {
background-image: url(img/icons/folder-small.gif);
}
div.folder-tree ul.list-tree span.element span.file {
background-image: url(img/icons/file-small.gif);
}
div.folder-tree ul.list-tree span.associated-file {
background-color: #5bafed;
color: #ffffff;
}
div.folder-tree ul.list-tree span.element:hover {
background-color: #ffffff;
color: #333333;
cursor: default;
}
div.folder-tree ul.list-tree span.selectable:hover {
background-color: #2b7fed;
color: #ffffff;
cursor: pointer;
}
/*****************************************************************\
Module grid > File connection line
\*****************************************************************/
div.connection-line {
overflow: hidden;
position: absolute;
width: 1px;
height: 1px;
top: 0px;
left: 0px;
background-color: #c0c0c0;
cursor: default;
}
/*****************************************************************\
Module grid > Map filter
\*****************************************************************/
div.map-filter {
display: inline-block;
margin-top: 15px;
margin-left: 5px;
margin-right: 5px;
border-left: 1px solid #0c6ed5;
white-space: nowrap;
}
div.map-filter div {
display: inline-block;
padding: 4px 15px;
border-top: 1px solid #0c6ed5;
border-bottom: 1px solid #0c6ed5;
border-right: 1px solid #0c6ed5;
text-align: center;
white-space: nowrap;
color: #2497e4;
cursor: pointer;
}
div.map-filter div:hover {
color: #0c6ed5;
}
div.map-filter div.active {
background-color: #0c6ed5;
color: #ffffff;
cursor: default;
}
div.map-filter div.active:hover {
background-color: #0c6ed5;
color: #ffffff;
}
/*****************************************************************\
Module grid > Module tree
\*****************************************************************/
div#module-tree {
position: relative;
overflow: auto;
margin-top: 15px;
margin-left: 5px;
margin-right: 5px;
}
/* Стиль оформления развернутого блока */
div#module-tree div.node {
position: absolute;
overflow: hidden;
line-height: 30px;
background-color: #ffffff;
border: 1px solid #c0c0c0;
vertical-align: middle;
text-align: center;
white-space: nowrap;
cursor: pointer;
z-index: 100;
}
/* Стиль оформления свернутого блока */
div#module-tree div.node-collapsed {
position: absolute;
overflow: hidden;
line-height: 30px;
background-color: #ffffff;
border: 1px solid #c0c0c0;
vertical-align: middle;
text-align: center;
white-space: nowrap;
cursor: pointer;
z-index: 100;
border: 1px solid #0c6ed5;
}
/* Соединительная линия между блоками */
div#module-tree div.node-line {
position: absolute;
overflow: hidden;
background-color: #c0c0c0;
}
/* Стиль выделенного блока */
div#module-tree div.active {
background-color: #3c90fe !important;
color: #ffffff;
}
/* Стиль блока с наведенным на него крусором */
div#module-tree div.node:hover {
background: #2b7fed;
color: #ffffff;
}
div#module-tree div.node-collapsed:hover {
background: #2b7fed;
color: #ffffff;
}
/*****************************************************************\
Module grid > File filter
\*****************************************************************/
div.file-filter {
display: inline-block;
margin-top: 15px;
margin-left: 5px;
margin-right: 5px;
border-left: 1px solid #0c6ed5;
white-space: nowrap;
}
div.file-filter div {
display: inline-block;
padding: 4px 15px;
border-top: 1px solid #0c6ed5;
border-bottom: 1px solid #0c6ed5;
border-right: 1px solid #0c6ed5;
text-align: center;
white-space: nowrap;
color: #2497e4;
cursor: pointer;
}
div.file-filter div:hover {
color: #0c6ed5;
}
div.file-filter div.active {
background-color: #0c6ed5;
color: #ffffff;
cursor: default;
}
div.file-filter div.active:hover {
background-color: #0c6ed5;
color: #ffffff;
}
/*****************************************************************\
Module grid > File code
\*****************************************************************/
div#file-code {
position: relative;
overflow: auto;
margin-top: 15px;
margin-left: 5px;
margin-right: 5px;
}
div#file-code p#file-code-hint {
margin-left: auto;
margin-right: auto;
text-align: center;
}
/* Временные стили */
div.folder-tree,
div#module-tree,
div#file-code {height: 650px;}
Файл modulemap.js
require.config({
baseUrl: 'js'
, paths: {
text: 'lib/require/text'
}
});
$(document).ready(function(){
// Содержимое модулей
var files = {};
loadAllFilesSourceCode();
// Функция загрузки кода всех файлов модуля
function loadAllFilesSourceCode () {
var modulesList = window.opener.requirejsModules.list
, key
, path;
for (key in modulesList) {
path = modulesList[key].modulePath;
if (!files.hasOwnProperty(path)) {
files[path] = $.ajax({type: 'GET', url: path, async: false}).responseText;
}
/*
path = path.substring(3);
if (!files.hasOwnProperty(path)) {
require(['text!' + path], function(code){
files[path] = code;
});
}
*/
}
}
// Функция загрузки кода файла модуля
/* Функция устарела
function loadFileSourceCode (path) {
path = path.substring(3);
if (!files.hasOwnProperty(path)) {
require(['text!' + path], function(code){
files[path] = code;
showFileSourceCode(path);
});
} else {
showFileSourceCode(path);
}
}
*/
// Функция отображения кода модуля
function showFileSourceCode () {
var selectedFilePath
, fileCode;
if ($('.folder-tree .selectable').hasClass('active')) {
selectedFilePath = $('.folder-tree .active').attr('path');
fileCode = files[selectedFilePath];
if ($('.file-filter div').first().hasClass('active')) {
/*
fileCode = fileCode.replace(/</gmi, '<').replace(/\r\n/gmi, '<br/>').replace(/ /gmi, ' ');
$('#file-code').empty().html(fileCode);
*/
fileCode = fileCode.replace(/</gmi, '<');
fileCode = '<pre class="brush: javascript">' + fileCode + '</pre>';
$('#file-code').empty().html(fileCode);
SyntaxHighlighter.highlight(); // SyntaxHighlighter загружен в файле index.html перед загрузкой всех модулей
$('.syntaxhighlighter .toolbar').remove();
$('.syntaxhighlighter').attr('style', 'width: 1500px !important;');
} else {
fileCode = js_beautify(fileCode, {
indent_size: 4
, indent_char: ' '
, max_preserve_newlines: -1
, preserve_newlines: false
, keep_array_indentation: false
, break_chained_methods: false
, indent_scripts: 'normal'
, brace_style: 'collapse'
, space_before_conditional: true
, unescape_strings: false
, jslint_happy: false
, end_with_newline: false
, wrap_line_length: 0
, indent_inner_html: false
, comma_first: true
, e4x: false
});
fileCode = fileCode.replace(/</gmi, '<');
fileCode = '<pre class="brush: javascript">' + fileCode + '</pre>';
$('#file-code').empty().html(fileCode);
SyntaxHighlighter.highlight(); // SyntaxHighlighter загружен в файле index.html перед загрузкой всех модулей
$('.syntaxhighlighter .toolbar').remove();
$('.syntaxhighlighter').attr('style', 'width: 1500px !important;');
}
}
}
// Показать содержимое страницы
$('#grid').removeClass('hidden');
// Выделить первую кнопку фильтра модулей
$('.module-filter div:first').addClass('active');
// Изменить размеры блоков согласно размеру окна пользователя
function resize () {
var windowHeight = $(window).height()
, bodyMarginTop = 11
, bodyMarginBottom = 11
, moduleGridSectionControlHeight = $('#module-grid-section-control').height()
, gridContentHeight = 0
, gridContentMinHeight = 245
, moduleFilterMarginTop = 15
, moduleFilterHeight = 0
, folderTreeHeight = 0
, folderTreeMarginTop = 15
, folderTreeMarginBottom = 5
, mapFilterMarginTop = 15
, mapFilterHeight = 0
, moduleTreeHight = 0
, moduleTreeMarginTop = 15
, moduleTreeMarginBottom = 5
, fileFilterMarginTop = 15
, fileFilterHeight = 0
, fileCodeHight = 0
, fileCodeMarginTop = 15
, fileCodeMarginBottom = 5;
gridContentHeight = windowHeight - bodyMarginTop - bodyMarginBottom - moduleGridSectionControlHeight;
if (gridContentHeight < gridContentMinHeight) {gridContentHeight = gridContentMinHeight;}
$('#grid').height(gridContentHeight);
$('table.module-grid tr td.tree-section').height(gridContentHeight);
moduleFilterHeight = $('.module-filter').first().height();
mapFilterHeight = $('.map-filter').first().height();
fileFilterHeight = $('.file-filter').first().height();
folderTreeHeight = gridContentHeight
- moduleFilterMarginTop
- moduleFilterHeight
- folderTreeMarginTop
- folderTreeMarginBottom;
moduleTreeHight = gridContentHeight
- mapFilterMarginTop
- mapFilterHeight
- moduleTreeMarginTop
- moduleTreeMarginBottom;
fileCodeHight = gridContentHeight
- fileCodeMarginTop
- fileFilterHeight
- fileCodeMarginTop
- fileCodeMarginBottom;
$('.folder-tree').height(folderTreeHeight);
$('#module-tree').height(moduleTreeHight);
$('#file-code').height(fileCodeHight);
var treeSectionWidth = 0
, mapSectionWidth = 0
, marginRight = 2
, result;
if (!$('.tree-section').hasClass('hidden')) {treeSectionWidth = $('#grid .tree-section').width();}
if (!$('.map-section').hasClass('hidden')) {mapSectionWidth = $('#grid .map-section').width();}
if (treeSectionWidth === 0 && mapSectionWidth === 0) {marginRight = -12;}
if (treeSectionWidth === 0 && mapSectionWidth !== 0) {marginRight = -12;}
if (treeSectionWidth !== 0 && mapSectionWidth === 0) {marginRight = -13;}
if (treeSectionWidth !== 0 && mapSectionWidth !== 0) {marginRight = 2;}
result = $('#header').width() - treeSectionWidth - mapSectionWidth + marginRight;
$('#file-code').css({'width': result + 'px'});
}
resize();
$(window).resize(resize);
/*
// Активировать изменение ширины колонок таблицы
function initColumnWidthResize (id) {
var border = $('#' + id + ' .border-section')
, borderWidth = border.width()
, treeSection = $('#' + id + ' .tree-section')
, mapSection = $('#' + id + ' .map-section')
, treeSectionMinWidth = 457
, mapSectionMinWidth = 385
, tableOffsetLeft
, folderTree = $('#' + id + ' .folder-tree')
, folderTreeSumOfMarginLeftAndRight = 10;
border.on('mousedown.border', startBorderMove);
function startBorderMove () {
setTableOffsetLeft();
$(document).on('mousemove.border', moveBorder).on('mouseup.border', endBorderMove);
return false;
}
function setTableOffsetLeft () {
tableOffsetLeft = border.parent().parent().offset().left;
}
function moveBorder (event) {
setBorderPosition(event.pageX - tableOffsetLeft - borderWidth / 2);
return false;
}
function endBorderMove () {
$(document).off('mousemove.border', moveBorder).off('mouseup.border', endBorderMove);
return false;
}
function setBorderPosition (x) {
if (x < treeSectionMinWidth) {
border.prev().css({'width': treeSectionMinWidth + 'px'});
folderTree.css({'width': (treeSectionMinWidth - folderTreeSumOfMarginLeftAndRight) + 'px'});
} else if (x > treeSection.width() + borderWidth + mapSection.width() - mapSectionMinWidth) {
border.prev().css({'width': treeSection.width() + borderWidth + mapSection.width() - mapSectionMinWidth + 'px'});
folderTree.css({'width': (treeSection.width() + borderWidth + mapSection.width() - mapSectionMinWidth - folderTreeSumOfMarginLeftAndRight) + 'px'});
} else {
border.prev().css({'width': x + 'px'});
folderTree.css({'width': (x - folderTreeSumOfMarginLeftAndRight) + 'px'});
}
}
}
initColumnWidthResize('grid');
*/
// Активировать фильтр дерева папок и файлов
function initFolderFilter () {
$('.module-filter div').first().addClass('active');
// Клик по любой кнопке фильтра
$('.module-filter div').click(function(){
// Выделение кнопки и отображение соответствующего ей блока с деревом
if ($(this).hasClass('active')) {return;}
$('.module-filter div').removeClass('active');
$(this).addClass('active');
var selectedFilePath
, selectedModuleName
, moduleList = window.opener.requirejsModules.list
, moduleDependenciesTreeObject
, moduleDependecies
, moduleDependeciesLength
, moduleDependenciesNames
, moduleDependenciesNamesLength
, key
, i;
if ($('.folder-tree .selectable').hasClass('active')) {
$('.folder-tree .selectable').removeClass('associated-file');
selectedFilePath = $('.folder-tree .active').attr('path');
showFileSourceCode();
for (key in moduleList) {
if (moduleList[key].modulePath === selectedFilePath) {
selectedModuleName = moduleList[key].moduleName;
break;
}
}
moduleDependenciesTreeObject = window.opener.requirejsModules.generateModuleDependenciesTreeObject(selectedModuleName);
if ($(this).index() === 0) {
// Выделить модули, от которых зависит данный модуль
moduleDependecies = moduleDependenciesTreeObject.dependencies;
moduleDependeciesLength = moduleDependecies.length;
for (i = 0; i < moduleDependeciesLength; i++) {
$('.folder-tree .selectable').filter('[path="' + moduleDependecies[i].path + '"]').addClass('associated-file');
}
} else if ($(this).index() === 1) {
// Выделить модули, куда входит данный модуль
for (key in moduleList) {
moduleDependenciesNames = moduleList[key].moduleDependenciesNames;
moduleDependenciesNamesLength = moduleDependenciesNames.length;
for (i = 0; i < moduleDependenciesNamesLength; i++) {
if (moduleDependenciesNames[i] === selectedModuleName) {
$('.folder-tree .selectable').filter('[path="' + moduleList[key].modulePath + '"]').addClass('associated-file');
break;
}
}
}
}
// Нарисовать соединительные линии для выделенных файлов
drawFileConnectionLines();
}
});
}
initFolderFilter();
// Обновить блок с деревом
function updateFolderTree (data) {
if ($.isEmptyObject(data)) {return;}
var tree = '<ul class="list-tree">';
if (data.hasOwnProperty('name')) {
tree += '<li class="' + (data.dependencies.length > 0 ? 'unclosed ' : '') + 'root"><span class="button"></span>'
+ '<span class="element' + (data.dependencies.length > 0 ? '' : ' selectable') + '"' + (data.dependencies.length > 0 ? '' : ' path="' + data.path + '"') + '>'
+ '<span class="icon ' + (data.dependencies.length > 0 ? 'folder' : 'file') + '"></span>'
+ '<span class="text">' + data.name + '</span>'
+ '</span>';
if (data.dependencies.length > 0) {
tree += buildTree(data.dependencies);
}
tree += '</li>';
}
tree += '</ul>';
$('.folder-tree').empty().append(tree);
$('.list-tree li:last-child').not('.root').addClass('last');
if (data.dependencies.length === 0) {
$('.list-tree li').addClass('single');
}
function buildTree (data) {
var tree = '<ul>'
, length = data.length
, i
, last = length - 1;
for (i = 0; i < length; i++) {
tree += '<li class="' + (data[i].dependencies.length > 0 ? 'unclosed ' : '') + (i === last ? 'last' : '') + '"><span class="button"></span>'
+ '<span class="element' + (data[i].dependencies.length > 0 ? '' : ' selectable') + '"' + (data[i].dependencies.length > 0 ? '' : ' path="' + data[i].path + '"') + '>'
+ '<span class="icon ' + (data[i].dependencies.length > 0 ? 'folder' : 'file') + '"></span>'
+ '<span class="text">' + data[i].name + '</span>'
+ '</span>';
if (data[i].dependencies.length > 0) {
tree += buildTree(data[i].dependencies);
}
tree += '</li>';
}
tree += '</ul>';
return tree;
}
}
var modulePathTreeObject = window.opener.requirejsModules.generateModulePathTreeObject();
//window.opener.console.log(window.opener.requirejsModules.generateModulePathTreeObject());
updateFolderTree(modulePathTreeObject);
// Клик по файлам в дереве папок
$(document).on('click', '.folder-tree .selectable', function(){
if ($(this).hasClass('active')) {return;}
$('.folder-tree .selectable').removeClass('active');
$(this).addClass('active');
var selectedFilePath
, selectedModuleName
, moduleList = window.opener.requirejsModules.list
, moduleDependenciesTreeObject
, moduleDependecies
, moduleDependeciesLength
, moduleDependenciesNames
, moduleDependenciesNamesLength
, key
, i;
if ($('.folder-tree .selectable').hasClass('active')) {
$('.folder-tree .selectable').removeClass('associated-file');
selectedFilePath = $('.folder-tree .active').attr('path');
showFileSourceCode();
for (key in moduleList) {
if (moduleList[key].modulePath === selectedFilePath) {
selectedModuleName = moduleList[key].moduleName;
break;
}
}
moduleDependenciesTreeObject = window.opener.requirejsModules.generateModuleDependenciesTreeObject(selectedModuleName);
if ($('.module-filter .active').index() === 0) {
// Выделить модули, от которых зависит данный модуль
moduleDependecies = moduleDependenciesTreeObject.dependencies;
moduleDependeciesLength = moduleDependecies.length;
for (i = 0; i < moduleDependeciesLength; i++) {
$('.folder-tree .selectable').filter('[path="' + moduleDependecies[i].path + '"]').addClass('associated-file');
}
} else if ($('.module-filter .active').index() === 1) {
// Выделить модули, куда входит данный модуль
for (key in moduleList) {
moduleDependenciesNames = moduleList[key].moduleDependenciesNames;
moduleDependenciesNamesLength = moduleDependenciesNames.length;
for (i = 0; i < moduleDependenciesNamesLength; i++) {
if (moduleDependenciesNames[i] === selectedModuleName) {
$('.folder-tree .selectable').filter('[path="' + moduleList[key].modulePath + '"]').addClass('associated-file');
break;
}
}
}
}
// Нарисовать соединительные линии для выделенных файлов
drawFileConnectionLines();
// Выделить модуль в дереве модулей
$('#module-tree div').removeClass('active');
$('#module-tree div').filter('[path="' + selectedFilePath + '"]').addClass('active');
}
});
// Функция для определения положения элемента относиттльно заданного элемента
// Пример использования: $('#innerElement').offsetRelativeTo('#outerElement').left;
jQuery.fn.offsetRelativeTo = function (el) {
var $el = $(el)
, o1 = this.offset()
, o2 = $el.offset();
o1.top = o1.top - o2.top - $el.scrollTop();
o1.left = o1.left - o2.left - $el.scrollLeft();
return o1;
};
// Функция отрисовки соединительных линиц для выделенных файлов в дереве папок
function drawFileConnectionLines () {
// Удалить все соединительные линии
$('.folder-tree .connection-line').remove();
// Рассчитать отступ слева для каждого выделенного файла
var allElements = $('.folder-tree .element')
, allElementsLength = allElements.length
, offsetTop
, offsetLeft
, width
, horizontalConnectionLineBeginCoordinate
, horizontalConnectionLineEndCoordinate
, maxHorizontalConnectionLineEndCoordinate = 0
, associatedFilesParameters = []
, associatedFilesParametersLength
, i;
for (i = 0; i < allElementsLength; i++) {
offsetTop = allElements.eq(i).offsetRelativeTo('.folder-tree:first').top; // Рассчитать отступ сверху каждого выделенного файла
offsetLeft = allElements.eq(i).offsetRelativeTo('.folder-tree:first').left; // Рассчитать отступ слева каждого выделенного файла
width = allElements.eq(i).width(); // Рассчитать ширину каждого выделенного файла
horizontalConnectionLineBeginCoordinate = offsetLeft + width + 10; // Сложить отступ слева, ширину каждого выделенного файла и расстояние до начальной точки горизонтальной соединительной линии, получив тем самым начальную точку
horizontalConnectionLineEndCoordinate = horizontalConnectionLineBeginCoordinate + 20; // Сложить отступ слева, ширину каждого выделенного файла и расстояние до конечной точки горизонтальной соединительной линии, получив тем самым конечную точку
// Выбрать самую дальнюю координату конечной точки
if (maxHorizontalConnectionLineEndCoordinate < horizontalConnectionLineEndCoordinate) {
maxHorizontalConnectionLineEndCoordinate = horizontalConnectionLineEndCoordinate;
}
if (
allElements.eq(i).hasClass('associated-file')
|| allElements.eq(i).hasClass('active')
) {
associatedFilesParameters.push({
offsetTop: offsetTop
, offsetLeft: offsetLeft
, width: width
, horizontalConnectionLineBeginCoordinate: horizontalConnectionLineBeginCoordinate
, horizontalConnectionLineEndCoordinate: horizontalConnectionLineEndCoordinate
, index: i
});
}
}
// Для каждого выделенного файла рассчиать ширину горизонтальной соединительной линии, отступ её от текста элемента и добавить горизонтальную линию в выделенный файл
associatedFilesParametersLength = associatedFilesParameters.length;
if (associatedFilesParametersLength > 1) {
for (i = 0; i < associatedFilesParametersLength; i++) {
allElements.eq(associatedFilesParameters[i].index).append('<div class="connection-line" style="width: ' + (maxHorizontalConnectionLineEndCoordinate - associatedFilesParameters[i].horizontalConnectionLineBeginCoordinate) + 'px; top: 12px; left: ' + (associatedFilesParameters[i].width + 53) + 'px;"></div>');
}
// Рассчитать расстояние по высоте между первым и последним выделенным файлом
// Добавить в первый выделенный файл элемент div с высотой от первого до последнего выделенного файла
allElements.eq(associatedFilesParameters[0].index).append('<div class="connection-line" style="height: ' + (associatedFilesParameters[associatedFilesParameters.length - 1].offsetTop - associatedFilesParameters[0].offsetTop + 1) + 'px; top: 12px; left: ' + (associatedFilesParameters[0].width + 53 + maxHorizontalConnectionLineEndCoordinate - associatedFilesParameters[0].horizontalConnectionLineBeginCoordinate) + 'px;"></div>');
}
}
// Активировать фильтр карты модулей
function initMapFilter () {
$('.map-filter div').first().addClass('active');
// Клик по любой кнопке фильтра
$('.map-filter div').click(function(){
// Выделение кнопки и отображение соответствующего ей блока в дереве
if ($(this).hasClass('active')) {return;}
$('.map-filter div').removeClass('active');
$(this).addClass('active');
changeModuleMapLayout();
var selectedFilePath;
if ($('.folder-tree .selectable').hasClass('active')) {
selectedFilePath = $('.folder-tree .active').attr('path');
showFileSourceCode();
// Выделить модуль в дереве модулей
$('#module-tree div').filter('[path="' + selectedFilePath + '"]').addClass('active');
}
});
}
initMapFilter();
var moduleDependenciesTreeObject = window.opener.requirejsModules.generateModuleDependenciesTreeObject();
//window.opener.console.log(window.opener.requirejsModules.generateModuleDependenciesTreeObject());
// Нарисовать дерево в первый раз
drawModuleMap();
// Изменить отрисовку дерева модулей
function changeModuleMapLayout () {
drawModuleMap();
}
// Обновить дерево на экране
function drawModuleMap () {
drawTree({
container: document.getElementById('module-tree')
, rootNode: moduleDependenciesTreeObject
, layout: (function(){
if ($('.map-filter div').eq(0).hasClass('active')) {
return 'horizontal';
} else {
return 'vertical';
}
})() // 'horizontal' or 'vertical'
, onNodeClick: nodeSingleAndDoubleClick
});
}
// Определение одиночного или двойного
function nodeSingleAndDoubleClick () {
var clicks = 0;
return function () {
var self = this;
clicks++;
if (clicks === 1) {
setTimeout(function(){
if (clicks === 1) {
nodeClick(self);
} else {
nodeDoubleClick(self);
}
clicks = 0;
}, 300);
}
};
}
// Одиночный клик на блоке элемента
function nodeClick (self) {
if ($(self).hasClass('active')) {return;}
$('.node').removeClass('active');
$('.node-collapsed').removeClass('active');
$(self).addClass('active');
var selectedFilePath
, selectedModuleName
, moduleList = window.opener.requirejsModules.list
, moduleDependenciesTreeObject
, moduleDependecies
, moduleDependeciesLength
, moduleDependenciesNames
, moduleDependenciesNamesLength
, key
, i;
if ($('.node').hasClass('active') || $('.node-collapsed').hasClass('active')) {
selectedFilePath = $('#module-tree .active').attr('path');
// Выделить модуль в дереве модулей
$('#module-tree div').filter('[path="' + selectedFilePath + '"]').addClass('active');
// Выделить файлы в дереве папок
$('.folder-tree .selectable').removeClass('active').removeClass('associated-file');
$('.folder-tree .selectable').filter('[path="' + selectedFilePath + '"]').addClass('active');
showFileSourceCode();
for (key in moduleList) {
if (moduleList[key].modulePath === selectedFilePath) {
selectedModuleName = moduleList[key].moduleName;
break;
}
}
moduleDependenciesTreeObject = window.opener.requirejsModules.generateModuleDependenciesTreeObject(selectedModuleName);
if ($('.module-filter .active').index() === 0) {
// Выделить модули, от которых зависит данный модуль
moduleDependecies = moduleDependenciesTreeObject.dependencies;
moduleDependeciesLength = moduleDependecies.length;
for (i = 0; i < moduleDependeciesLength; i++) {
$('.folder-tree .selectable').filter('[path="' + moduleDependecies[i].path + '"]').addClass('associated-file');
}
} else if ($('.module-filter .active').index() === 1) {
// Выделить модули, куда входит данный модуль
for (key in moduleList) {
moduleDependenciesNames = moduleList[key].moduleDependenciesNames;
moduleDependenciesNamesLength = moduleDependenciesNames.length;
for (i = 0; i < moduleDependenciesNamesLength; i++) {
if (moduleDependenciesNames[i] === selectedModuleName) {
$('.folder-tree .selectable').filter('[path="' + moduleList[key].modulePath + '"]').addClass('associated-file');
break;
}
}
}
}
// Нарисовать соединительные линии для выделенных файлов
drawFileConnectionLines();
}
}
// Двойной клик на блоке элемента
function nodeDoubleClick (self) {
if (self.node.dependencies && self.node.dependencies.length > 0) { // Если блок имеет потомков
self.node.collapsed = !self.node.collapsed;
drawModuleMap();
var selectedFilePath;
if ($('.folder-tree .selectable').hasClass('active')) {
selectedFilePath = $('.folder-tree .active').attr('path');
showFileSourceCode();
// Выделить модуль в дереве модулей
$('#module-tree div').filter('[path="' + selectedFilePath + '"]').addClass('active');
}
}
}
// Нарисовать дерево
function drawTree (args) {
// Подготовить элементы дерева для отрисовки на экране
prepareNode({node: args.rootNode});
// Построить вертикальное или горизонтальное дерево
if (args.layout === 'vertical') {
performLayoutV(args.rootNode); // Построить вертикальное дерево
} else {
performLayoutH(args.rootNode); // Построить горизонтальное дерево
}
// Нарисовать контейнеры элементов дерева
args.container.innerHTML = ''; // Удалить все элементы из блока DIV, в который будет вставлено наше дерево
drawNode(args.rootNode, args.container, args);
// Нарисовать соединительные линии между блоками элементов
drawLines(args.rootNode, args.container);
}
// Проверить раскрыт ли узел и имеет ли он потомков.
function isNodeCollapsedAndHasChildren (node) {
if (!node.collapsed && node.dependencies && node.dependencies.length > 0) { // Элемент имеет потомков и раскрыт
return true;
}
return false;
}
// Подготовить элементы дерева для отрисовки на экране
function prepareNode (args) {
var node = args.node // Узел
, level = args.level // Уровень вложенности узла
, parentNode = args.parentNode // Родительский узел
, leftNode = args.leftNode // Самый левый (первый) узел потомок
, rightLimits = args.rightLimits // Массив узлов потомков
, nodesLength // Количество потомков в данному узле
, i;
if (level === undefined) {level = 0;}
if (parentNode === undefined) {parentNode = null;}
if (leftNode === undefined) {leftNode = null;} // Самый левый (первый) узел потомок
if (rightLimits === undefined) {rightLimits = [];} // Массив элементов потомков
node.level = level;
node.parentNode = parentNode;
node.leftNode = leftNode;
if (isNodeCollapsedAndHasChildren(node)) { // Элемент имеет потомков и раскрыт
nodesLength = node.dependencies.length;
for (i = 0; i < nodesLength; i++) {
leftNode = null;
if (i === 0 && rightLimits[level] !== undefined) {
leftNode = rightLimits[level];
}
if (i > 0) {
leftNode = node.dependencies[i - 1]; // Получить самый левый (первый) узел потомок
}
if (i === (nodesLength - 1)) {
rightLimits[level] = node.dependencies[i]; // Получить самый правый (последний) узел потомок
}
// Приступить к обработке следующего элемента дерева
prepareNode({
node: node.dependencies[i]
, level: level + 1
, parentNode: node
, leftNode: leftNode
, rightLimits: rightLimits
});
}
}
}
// Построить вертикальное дерево
function performLayoutV (node) {
var nodeHeight = 30 // Высота блока узла в "px"
, nodeWidth = 170 // Ширина блока узла в "px"
, nodeMarginLeft = 50 // Отступ слева для блока узла в "px"
, nodeMarginTop = 15 // было 30 // Отступ сверху для блока узла в "px"
, nodeTop = 0 // Начальная координат верхней стороны родительского узла
, nodesLength // Количество потомков в данному узле
, childrenHeight = 0 // Высота, занимаемая потомками данного узла
, i;
// Перед посроением данного узла мы предварительно строим узлы всех его мотомков
if (isNodeCollapsedAndHasChildren(node)) { // Элемент имеет потомков и раскрыт
nodesLength = node.dependencies.length;
// Сначала построить вертикальное дерево для потомков данного узла
for (i = 0; i < nodesLength; i++) {
performLayoutV(node.dependencies[i]); // Построить вертикальное дерево для потомков данного узла
}
// Затем построить вертикальное дерево для самого родительского узла
// Родительский узел всегда находится в центре перед своими потомками
// Высота, занимаемая потомками, равна сумме координаты верхней стороны крайнего нижнего (последнего) потомка с его высотой
// минус координата верхней стороны крайнего верхнего (первого) потомка
childrenHeight = (node.dependencies[nodesLength - 1].top + node.dependencies[nodesLength - 1].height) - node.dependencies[0].top;
// Начальная координат верхней стороны родительского узла, размещаемого в центре перед потомками,
// равна сумме начальной координаты верхней стороны крайнего верхнего (первого) потомка и половине высоты, занимаемой
// всеми потомками данного узла минус половина высоты самого родительского узла
nodeTop = (node.dependencies[0].top + (childrenHeight / 2)) - (nodeHeight / 2);
// Верний элемент находится над предыдущим сестринским элементом?
// В этом случае переместить его вниз
var newTop
, diff;
if (node.leftNode && ((node.leftNode.top + node.leftNode.height + nodeMarginTop) > nodeTop)) {
newTop = node.leftNode.top + node.leftNode.height + nodeMarginTop;
diff = newTop - nodeTop;
// Также переместить моих потомков вниз
moveBottom(node.dependencies, diff);
nodeTop = newTop;
}
} else {
// Мой верх находится за верхним сестринским элементом
if (node.leftNode) {
nodeTop = node.leftNode.top + node.leftNode.height + nodeMarginTop;
}
}
node.top = nodeTop;
// Left зависит только от уровня вложенности level
node.left = (nodeMarginLeft * (node.level + 1)) + (nodeWidth * (node.level + 1));
// Размер блока всегда постоянен
node.height = nodeHeight;
node.width = nodeWidth;
// Рассчитать соединительные точки
// Потомок: Где линии выходят для соединения узла с его потомками
var pointX = node.left + nodeWidth
, pointY = nodeTop + (nodeHeight / 2);
node.childrenConnectorPoint = {
x: pointX
, y: pointY
, layout: 'vertical'
};
// Родитель: Где линия, которая соединяет это узел с его родительским концом
pointX = node.left;
pointY = nodeTop + (nodeHeight / 2);
node.parentConnectorPoint = {
x: pointX
, y: pointY
, layout: 'vertical'
};
}
// Переместить всех потомков узла вниз
function moveBottom (nodes, distance) {
var nodesLength = nodes.length
, node
, i;
for (i = 0; i < nodesLength; i++) {
node = nodes[i];
node.top += distance;
if (node.parentConnectorPoint) {node.parentConnectorPoint.y += distance;}
if (node.childrenConnectorPoint) {node.childrenConnectorPoint.y += distance;}
if (node.dependencies) {
moveBottom(node.dependencies, distance);
}
}
}
// Построить горизонтальное дерево
function performLayoutH (node) {
var nodeHeight = 30 // Высота блока узла в "px"
, nodeWidth = 170 // Ширина блока узла в "px"
, nodeMarginLeft = 10 // было 50 // Отступ слева для блока узла в "px"
, nodeMarginTop = 50 // было 30 // Отступ сверху для блока узла в "px"
, nodeLeft = 0 // Начальная координат левой стороны родительского узла
, nodesLength // Количество потомков в данному узле
, childrenWidth = 0 // Ширина, занимаемая потомками данного узла
, i;
// Перед построением данного узла мы предварительно строим узлы всех его мотомков
if (isNodeCollapsedAndHasChildren(node)) { // Элемент имеет потомков и раскрыт
nodesLength = node.dependencies.length;
// Сначала построить горизонтальное дерево для потомков данного узла
for (i = 0; i < nodesLength; i++) {
performLayoutH(node.dependencies[i]); // Построить горизонтальное дерево для потомков данного узла
}
// Затем построить горизонтальное дерево для самого родительского узла
// Родительский узел всегда находится в центре над своими потомками
// Ширина, занимаемая потомками, равна сумме координаты левой стороны крайнего правого (последнего) потомка с его шириной
// минус координата левой стороны крайнего левого (первого) потомка
childrenWidth = (node.dependencies[nodesLength - 1].left + node.dependencies[nodesLength - 1].width) - node.dependencies[0].left;
// Начальная координат левой стороны родительского узла, размещаемого в центре над потомками,
// равна сумме начальной координаты левой стороны крайнего левого (первого) потомка и половине ширины, занимаемой
// всеми потомками данного узла минус половина ширина самого родительского узла
nodeLeft = (node.dependencies[0].left + (childrenWidth / 2)) - (nodeWidth / 2);
// Левый элемент находится перед моим левый узлом?
// В этом случае переместить его вправо
var newLeft
, diff;
if (node.leftNode && ((node.leftNode.left + node.leftNode.width + nodeMarginLeft) > nodeLeft)) {
newLeft = node.leftNode.left + node.leftNode.width + nodeMarginLeft;
diff = newLeft - nodeLeft;
// Также переместить моих потомков вправо
moveRight(node.dependencies, diff);
nodeLeft = newLeft;
}
} else {
// Мое лево находится за левым сестринским элементом
if (node.leftNode) {
nodeLeft = node.leftNode.left + node.leftNode.width + nodeMarginLeft;
}
}
node.left = nodeLeft;
// Top зависит только от уровня вложенности level
node.top = (nodeMarginTop * (node.level + 1)) + (nodeHeight * (node.level + 1));
// Размер блока всегда постоянен
node.height = nodeHeight;
node.width = nodeWidth;
// Рассчитать соединительные точки
// Потомок: Где линии выходят для соединения узла с его потомками
var pointX = nodeLeft + (nodeWidth / 2)
, pointY = node.top + nodeHeight;
node.childrenConnectorPoint = {
x: pointX
, y: pointY
, layout: 'horizontal'
};
// Родитель: Где линия, которая соединяет это узел с его родительским концом
pointX = nodeLeft + (nodeWidth / 2);
pointY = node.top;
node.parentConnectorPoint = {
x: pointX
, y: pointY
, layout: 'horizontal'
};
}
// Переместить всех потомков узла вправо
function moveRight (nodes, distance) {
var nodesLength = nodes.length
, node
, i;
for (i = 0; i < nodesLength; i++) {
node = nodes[i];
node.left += distance;
if (node.parentConnectorPoint) {node.parentConnectorPoint.x += distance;}
if (node.childrenConnectorPoint) {node.childrenConnectorPoint.x += distance;}
if (node.dependencies) {
moveRight(node.dependencies, distance);
}
}
}
// Нарисовать контейнеры элементов
function drawNode (node, container, options) {
// Создать блок DIV для узла и задать ему значение ширины, высоты и положения на экране
var nodeDiv = document.createElement('div');
nodeDiv.style.top = node.top + 'px';
nodeDiv.style.left = node.left + 'px';
nodeDiv.style.width = node.width + 'px';
nodeDiv.style.height = node.height + 'px';
if (node.collapsed) {
nodeDiv.className = 'node-collapsed'; // Если узел свернут
} else {
nodeDiv.className = 'node'; // Если узел развернут
}
nodeDiv.setAttribute('path', node.path);
// Добавить в блок DIV узла индивидуальный класс и содержимое, если они были заданы
if (node.className) {nodeDiv.className = node.className;}
if (node.name) {nodeDiv.innerHTML = node.name;}
nodeDiv.node = node;
// Навесить на блок DIV события клика мыши по блоку
if (options.onNodeClick) {nodeDiv.onclick = options.onNodeClick();}
// Добавить созданный блок в контейнер
container.appendChild(nodeDiv);
// Нарисовать потомков
var nodesLength // Количество потомков в данному узле
, i;
if (isNodeCollapsedAndHasChildren(node)) { // Элемент имеет потомков и раскрыт
nodesLength = node.dependencies.length;
for (i = 0; i < nodesLength; i++) {
drawNode(node.dependencies[i], container, options);
}
}
}
// Нарисовать соединительные линии между блоками элементов
function drawLines (node, container) {
var nodesLength // Количество потомков в данному узле
, j;
if (isNodeCollapsedAndHasChildren(node)) { // Элемент имеет потомков и раскрыт
nodesLength = node.dependencies.length;
if (node.childrenConnectorPoint.layout === 'vertical') { // Вертикальная схема расположения блоков
for (j = 0; j < nodesLength; j++) {
// Нарисовать вертикальную соединительную линию
drawLineV(container, node.childrenConnectorPoint, node.dependencies[j].parentConnectorPoint);
drawLines(node.dependencies[j], container); // Нарисовать соединительные линии для потомков данного блока
}
} else {
for (j = 0; j < nodesLength; j++) { //Горизонтальная схема расположения блоков
// Нарисовать горизонтальную соединительную линию
drawLineH(container, node.childrenConnectorPoint, node.dependencies[j].parentConnectorPoint);
drawLines(node.dependencies[j], container); // Нарисовать соединительные линии для потомков данного блока
}
}
}
}
// Нарисовать вертикальную соединительную линию
function drawLineV (container, startPoint, endPoint) {
var midX = (startPoint.x + ((endPoint.x - startPoint.x) / 2)); // Середина - это половина пути между началом и концом точки X
// Начальный сегмент вертикальной соединительной линии
drawLineSegment(container, startPoint.x, startPoint.y, midX, startPoint.y, 1);
// Промежуточный сегмент вертикальной соединительной линии
var imsStartY = startPoint.y < endPoint.y ? startPoint.y : endPoint.y // Самое нижнее значение будет стартовой точкой
, imsEndY = startPoint.y > endPoint.y ? startPoint.y : endPoint.y; // Самое высокое значение будет конечной точкой
drawLineSegment(container, midX, imsStartY, midX, imsEndY, 1);
// Конечный сегмент вертикальной соединительной линии
drawLineSegment(container, midX, endPoint.y, endPoint.x, endPoint.y, 1);
}
// Нарисовать горизонтальную соединительную линию
function drawLineH (container, startPoint, endPoint) {
var midY = (startPoint.y + ((endPoint.y - startPoint.y) / 2)); // Середина - это половина пути между началом и концом точки Y
// Начальный сегмент горизонтальной соединительной линии
drawLineSegment(container, startPoint.x, startPoint.y, startPoint.x, midY, 1);
// Промежуточный сегмент горизонтальной соединительной линии
var imsStartX = startPoint.x < endPoint.x ? startPoint.x : endPoint.x // Самое нижнее значение будет стартовой точкой
, imsEndX = startPoint.x > endPoint.x ? startPoint.x : endPoint.x; // Самое высокое значение будет конечной точкой
drawLineSegment(container, imsStartX, midY, imsEndX, midY, 1);
// Конечный сегмент горизонтальной соединительной линии
drawLineSegment(container, endPoint.x, midY, endPoint.x, endPoint.y, 1);
}
// Нарисовать сегмент соединительной линии
function drawLineSegment (container, startX, startY, endX, endY, lineWidth) {
// Создать блок DIV, который будет изображать соединительную линию
var lineDiv = document.createElement('div');
lineDiv.style.top = startY + 'px';
lineDiv.style.left = startX + 'px';
if (startX === endX) { // Вертикальная линия
lineDiv.style.width = lineWidth + 'px';
lineDiv.style.height = (endY - startY) + 'px';
} else{ // Горизонтальная линия
lineDiv.style.width = (endX - startX) + 'px';
lineDiv.style.height = lineWidth + 'px';
}
lineDiv.className = 'node-line';
container.appendChild(lineDiv); // Добавить соединительную линию к блоку элемента
}
// Активировать фильтр кода файлов
function initFileFilter () {
$('.file-filter div').first().addClass('active');
// Клик по любой кнопке фильтра
$('.file-filter div').click(function(){
// Выделение кнопки и отображение соответствующего ей формата кода
if ($(this).hasClass('active')) {return;}
$('.file-filter div').removeClass('active');
$(this).addClass('active');
if ($('.folder-tree .selectable').hasClass('active')) {
showFileSourceCode();
}
});
}
initFileFilter();
// Активировать фильтр секций
function initSectionsFilter () {
$('#tree-section-visibility').click(function(){
if ($(this).hasClass('visible')) {
$(this).removeClass('visible').addClass('invisible');
$('.tree-section').addClass('hidden');
} else if ($(this).hasClass('invisible')) {
$(this).removeClass('invisible').addClass('visible');
$('.tree-section').removeClass('hidden');
}
setSectionsWidth();
});
$('#map-section-visibility').click(function(){
if ($(this).hasClass('visible')) {
$(this).removeClass('visible').addClass('invisible');
$('.map-section').addClass('hidden');
} else if ($(this).hasClass('invisible')) {
$(this).removeClass('invisible').addClass('visible');
$('.map-section').removeClass('hidden');
}
setSectionsWidth();
});
$('#file-section-visibility').click(function(){
if ($(this).hasClass('visible')) {
$(this).removeClass('visible').addClass('invisible');
$('.file-section').addClass('hidden');
} else if ($(this).hasClass('invisible')) {
$(this).removeClass('invisible').addClass('visible');
$('.file-section').removeClass('hidden');
}
setSectionsWidth();
});
function setSectionsWidth () {
// Показаны все секции
$('.tree-section').removeClass('width-auto').removeClass('width-50').removeClass('width-70').removeClass('width-100');
$('.map-section').removeClass('width-auto').removeClass('width-50').removeClass('width-70').removeClass('width-100');
$('.file-section').removeClass('width-auto').removeClass('width-50').removeClass('width-70').removeClass('width-100');
if (
!$('.tree-section').hasClass('hidden')
&& (($('.map-section').hasClass('hidden') && !$('.file-section').hasClass('hidden')) || (!$('.map-section').hasClass('hidden') && $('.file-section').hasClass('hidden')))
) { // Показана только секция с файлам и любая другая секция
$('.tree-section').addClass('width-auto');
$('.map-section').addClass('width-70');
$('.file-section').addClass('width-70');
} else if (
$('.tree-section').hasClass('hidden')
&& !$('.map-section').hasClass('hidden')
&& !$('.file-section').hasClass('hidden')
) { // Cекция с файлам скрыта и показы секции с картой и кодом
$('.map-section').addClass('width-50');
$('.file-section').addClass('width-50');
} else if (
(!$('.tree-section').hasClass('hidden') && $('.map-section').hasClass('hidden') && $('.file-section').hasClass('hidden'))
|| ($('.tree-section').hasClass('hidden') && !$('.map-section').hasClass('hidden') && $('.file-section').hasClass('hidden'))
|| ($('.tree-section').hasClass('hidden') && $('.map-section').hasClass('hidden') && !$('.file-section').hasClass('hidden'))
) { // Показана только одна любая секция
$('.tree-section').addClass('width-100');
$('.map-section').addClass('width-100');
$('.file-section').addClass('width-100');
}
resize();
// Скрыты все секции
}
}
initSectionsFilter();
});
/*
Код из HTML-файла
<div>Shift+Click to Add Node</div>
<div>Double Click to Expand or Collapse</div>
<div><select id="dlLayout" onchange="ChangeLayout()">
<option value="horizontal">Horizontal</option>
<option value="vertical">Vertical</option>
</select></div>
<div class="Container" id="dvTreeContainer"></div>
*/
/*
// Корневой элемент
var rootNode = {Content: 'Корневой элемент', Nodes:[]};
// Первый уровень
rootNode.Nodes[0] = {Content: 'N1.1'};
rootNode.Nodes[1] = {Content: 'N1.15', ToolTip: 'Всплывающая подсказка 1.15'};
rootNode.Nodes[2] = {Content: 'N1.2'};
rootNode.Nodes[3] = {Content: 'N1.3'};
rootNode.Nodes[4] = {Content: 'N1.4'};
// Второй уровень
rootNode.Nodes[2].Nodes = [
{Content : 'N1.2.1', Collapsed: true} // Этот узел отрисовывается свернутым
, {Content : 'N1.2.2'}
, {Content : 'N1.2.3', Class : 'SpecialNode', ToolTip: 'Кликни!'} // Этот узел раскрашен подругому
, {Content : 'N1.2.4'}
];
rootNode.Nodes[3].Nodes = [
{Content : 'N1.2.1', Collapsed: true} // Этот узел отрисовывается свернутым
, {Content : 'N1.2.2'}
, {Content : 'N1.2.3', Class : 'SpecialNode', ToolTip: 'Кликни!'} // Этот узел раскрашен подругому
, {Content : 'N1.2.4'}
];
// Третий уровень
rootNode.Nodes[2].Nodes[0].Nodes = [
{Content: 'N1.2.1.1' }
, {Content: 'N1.2.1.2'}
, {Content: 'N1.2.1.3'}
, {Content: 'N1.2.1.4'}
, {Content: 'N1.2.1.5'}
, {Content: 'N1.2.1.6'}
, {Content: 'N1.2.1.7'}
];
rootNode.Nodes[2].Nodes[1].Nodes = [
{Content: 'N1.2.1.1' }
, {Content: 'N1.2.1.2'}
, {Content: 'N1.2.1.3'}
, {Content: 'N1.2.1.4'}
, {Content: 'N1.2.1.5'}
, {Content: 'N1.2.1.6'}
, {Content: 'N1.2.1.7'}
];
rootNode.Nodes[2].Nodes[2].Nodes = [
{Content: 'N1.2.4.1'}
, {Content: 'N1.2.4.2'}
, {Content: 'N1.2.4.3'}
, {Content: 'N1.2.4.4'}
, {Content: 'N1.2.4.5'}
, {Content: 'N1.2.4.6'}
, {Content: 'N1.2.4.7'}
];
rootNode.Nodes[2].Nodes[3].Nodes = [
{Content: 'N1.2.4.1'}
, {Content: 'N1.2.4.2'}
, {Content: 'N1.2.4.3'}
, {Content: 'N1.2.4.4'}
, {Content: 'N1.2.4.5'}
, {Content: 'N1.2.4.6'}
, {Content: 'N1.2.4.7'}
];
// Нарисовать дерево в первый раз
refreshTree();
// Изменить отрисовку дерева при изменении опции в селекторе
document.getElementById('dlLayout').onchange = function () {
changeLayout();
};
// Изменить отрисовку дерева
function changeLayout () {
refreshTree();
}
// Обновить дерево на экране
function refreshTree () {
drawTree({
container: document.getElementById('dvTreeContainer')
, rootNode: rootNode
, layout: document.getElementById('dlLayout').value
, onNodeClick: nodeClick
, onNodeDoubleClick: nodeDoubleClick
});
}
// Одиночный клик на блоке элемента
function nodeClick (event) {
var newNodeIndex; // Индекс добавленного потомка в блок
if (this.node.name === 'N1.2.3') {alert(this.node.nontent);}
// Добавить в блок нового потомка
if (event && event.shiftKey) {
// Добавить в блок нового потомка, если блок раскрыт
if (!this.node.collapsed) {
if (!this.node.dependencies) {this.node.dependencies = [];}
newNodeIndex = this.node.dependencies.length;
this.node.dependencies[newNodeIndex] = {};
this.node.dependencies[newNodeIndex].name = this.node.name + '.' + (newNodeIndex + 1);
refreshTree();
}
}
}
// Двойной клик на блоке элемента
function nodeDoubleClick () {
if (this.node.dependencies && this.node.dependencies.length > 0) { // Если блок имеет потомков
this.node.collapsed = !this.node.collapsed;
refreshTree();
}
}
// Нарисовать дерево
function drawTree (args) {
// Подготовить элементы дерева для отрисовки на экране
prepareNode({node: args.rootNode});
// Построить вертикальное или горизонтальное дерево
if (args.layout === 'vertical') {
performLayoutV(args.rootNode); // Построить вертикальное дерево
} else {
performLayoutH(args.rootNode); // Построить горизонтальное дерево
}
// Нарисовать контейнеры элементов дерева
args.container.innerHTML = ''; // Удалить все элементы из блока DIV, в который будет вставлено наше дерево
drawNode(args.rootNode, args.container, args);
// Нарисовать соединительные линии между блоками элементов
drawLines(args.rootNode, args.container);
}
// Проверить раскрыт ли узел и имеет ли он потомков.
function isNodeCollapsedAndHasChildren (node) {
if (!node.collapsed && node.dependencies && node.dependencies.length > 0) { // Элемент имеет потомков и раскрыт
return true;
}
return false;
}
// Подготовить элементы дерева для отрисовки на экране
function prepareNode (args) {
var node = args.node // Узел
, level = args.level // Уровень вложенности узла
, parentNode = args.parentNode // Родительский узел
, leftNode = args.leftNode // Самый левый (первый) узел потомок
, rightLimits = args.rightLimits // Массив узлов потомков
, nodesLength // Количество потомков в данному узле
, i;
if (level === undefined) {level = 0;}
if (parentNode === undefined) {parentNode = null;}
if (leftNode === undefined) {leftNode = null;} // Самый левый (первый) узел потомок
if (rightLimits === undefined) {rightLimits = [];} // Массив элементов потомков
node.level = level;
node.parentNode = parentNode;
node.leftNode = leftNode;
if (isNodeCollapsedAndHasChildren(node)) { // Элемент имеет потомков и раскрыт
nodesLength = node.dependencies.length;
for (i = 0; i < nodesLength; i++) {
leftNode = null;
if (i === 0 && rightLimits[level] !== undefined) {
leftNode = rightLimits[level];
}
if (i > 0) {
leftNode = node.dependencies[i - 1]; // Получить самый левый (первый) узел потомок
}
if (i === (nodesLength - 1)) {
rightLimits[level] = node.dependencies[i]; // Получить самый правый (последний) узел потомок
}
// Приступить к обработке следующего элемента дерева
prepareNode({
node: node.dependencies[i]
, level: level + 1
, parentNode: node
, leftNode: leftNode
, rightLimits: rightLimits
});
}
}
}
// Построить вертикальное дерево
function performLayoutV (node) {
var nodeHeight = 30 // Высота блока узла в "px"
, nodeWidth = 170 // Ширина блока узла в "px"
, nodeMarginLeft = 50 // Отступ слева для блока узла в "px"
, nodeMarginTop = 30 // Отступ сверху для блока узла в "px"
, nodeTop = 0 // Начальная координат верхней стороны родительского узла
, nodesLength // Количество потомков в данному узле
, childrenHeight = 0 // Высота, занимаемая потомками данного узла
, i;
// Перед посроением данного узла мы предварительно строим узлы всех его мотомков
if (isNodeCollapsedAndHasChildren(node)) { // Элемент имеет потомков и раскрыт
nodesLength = node.dependencies.length;
// Сначала построить вертикальное дерево для потомков данного узла
for (i = 0; i < nodesLength; i++) {
performLayoutV(node.dependencies[i]); // Построить вертикальное дерево для потомков данного узла
}
// Затем построить вертикальное дерево для самого родительского узла
// Родительский узел всегда находится в центре перед своими потомками
// Высота, занимаемая потомками, равна сумме координаты верхней стороны крайнего нижнего (последнего) потомка с его высотой
// минус координата верхней стороны крайнего верхнего (первого) потомка
childrenHeight = (node.dependencies[nodesLength - 1].top + node.dependencies[nodesLength - 1].height) - node.dependencies[0].top;
// Начальная координат верхней стороны родительского узла, размещаемого в центре перед потомками,
// равна сумме начальной координаты верхней стороны крайнего верхнего (первого) потомка и половине высоты, занимаемой
// всеми потомками данного узла минус половина высоты самого родительского узла
nodeTop = (node.dependencies[0].top + (childrenHeight / 2)) - (nodeHeight / 2);
// Верний элемент находится над предыдущим сестринским элементом?
// В этом случае переместить его вниз
var newTop
, diff;
if (node.leftNode && ((node.leftNode.top + node.leftNode.height + nodeMarginTop) > nodeTop)) {
newTop = node.leftNode.top + node.leftNode.height + nodeMarginTop;
diff = newTop - nodeTop;
// Также переместить моих потомков вниз
moveBottom(node.dependencies, diff);
nodeTop = newTop;
}
} else {
// Мой верх находится за верхним сестринским элементом
if (node.leftNode) {
nodeTop = node.leftNode.top + node.leftNode.height + nodeMarginTop;
}
}
node.top = nodeTop;
// Left зависит только от уровня вложенности level
node.left = (nodeMarginLeft * (node.level + 1)) + (nodeWidth * (node.level + 1));
// Размер блока всегда постоянен
node.height = nodeHeight;
node.width = nodeWidth;
// Рассчитать соединительные точки
// Потомок: Где линии выходят для соединения узла с его потомками
var pointX = node.left + nodeWidth
, pointY = nodeTop + (nodeHeight / 2);
node.childrenConnectorPoint = {
x: pointX
, y: pointY
, layout: 'vertical'
};
// Родитель: Где линия, которая соединяет это узел с его родительским концом
pointX = node.left;
pointY = nodeTop + (nodeHeight / 2);
node.parentConnectorPoint = {
x: pointX
, y: pointY
, layout: 'vertical'
};
}
// Переместить всех потомков узла вниз
function moveBottom (nodes, distance) {
var nodesLength = nodes.length
, node
, i;
for (i = 0; i < nodesLength; i++) {
node = nodes[i];
node.top += distance;
if (node.parentConnectorPoint) {node.parentConnectorPoint.y += distance;}
if (node.childrenConnectorPoint) {node.childrenConnectorPoint.y += distance;}
if (node.dependencies) {
moveBottom(node.dependencies, distance);
}
}
}
// Построить горизонтальное дерево
function performLayoutH (node) {
var nodeHeight = 30 // Высота блока узла в "px"
, nodeWidth = 170 // Ширина блока узла в "px"
, nodeMarginLeft = 50 // Отступ слева для блока узла в "px"
, nodeMarginTop = 30 // Отступ сверху для блока узла в "px"
, nodeLeft = 0 // Начальная координат левой стороны родительского узла
, nodesLength // Количество потомков в данному узле
, childrenWidth = 0 // Ширина, занимаемая потомками данного узла
, i;
// Перед построением данного узла мы предварительно строим узлы всех его мотомков
if (isNodeCollapsedAndHasChildren(node)) { // Элемент имеет потомков и раскрыт
nodesLength = node.dependencies.length;
// Сначала построить горизонтальное дерево для потомков данного узла
for (i = 0; i < nodesLength; i++) {
performLayoutH(node.dependencies[i]); // Построить горизонтальное дерево для потомков данного узла
}
// Затем построить горизонтальное дерево для самого родительского узла
// Родительский узел всегда находится в центре над своими потомками
// Ширина, занимаемая потомками, равна сумме координаты левой стороны крайнего правого (последнего) потомка с его шириной
// минус координата левой стороны крайнего левого (первого) потомка
childrenWidth = (node.dependencies[nodesLength - 1].left + node.dependencies[nodesLength - 1].width) - node.dependencies[0].left;
// Начальная координат левой стороны родительского узла, размещаемого в центре над потомками,
// равна сумме начальной координаты левой стороны крайнего левого (первого) потомка и половине ширины, занимаемой
// всеми потомками данного узла минус половина ширина самого родительского узла
nodeLeft = (node.dependencies[0].left + (childrenWidth / 2)) - (nodeWidth / 2);
// Левый элемент находится перед моим левый узлом?
// В этом случае переместить его вправо
var newLeft
, diff;
if (node.leftNode && ((node.leftNode.left + node.leftNode.width + nodeMarginLeft) > nodeLeft)) {
newLeft = node.leftNode.left + node.leftNode.width + nodeMarginLeft;
diff = newLeft - nodeLeft;
// Также переместить моих потомков вправо
moveRight(node.dependencies, diff);
nodeLeft = newLeft;
}
} else {
// Мое лево находится за левым сестринским элементом
if (node.leftNode) {
nodeLeft = node.leftNode.left + node.leftNode.width + nodeMarginLeft;
}
}
node.left = nodeLeft;
// Top зависит только от уровня вложенности level
node.top = (nodeMarginTop * (node.level + 1)) + (nodeHeight * (node.level + 1));
// Размер блока всегда постоянен
node.height = nodeHeight;
node.width = nodeWidth;
// Рассчитать соединительные точки
// Потомок: Где линии выходят для соединения узла с его потомками
var pointX = nodeLeft + (nodeWidth / 2)
, pointY = node.top + nodeHeight;
node.childrenConnectorPoint = {
x: pointX
, y: pointY
, layout: 'horizontal'
};
// Родитель: Где линия, которая соединяет это узел с его родительским концом
pointX = nodeLeft + (nodeWidth / 2);
pointY = node.top;
node.parentConnectorPoint = {
x: pointX
, y: pointY
, layout: 'horizontal'
};
}
// Переместить всех потомков узла вправо
function moveRight (nodes, distance) {
var nodesLength = nodes.length
, node
, i;
for (i = 0; i < nodesLength; i++) {
node = nodes[i];
node.left += distance;
if (node.parentConnectorPoint) {node.parentConnectorPoint.x += distance;}
if (node.childrenConnectorPoint) {node.childrenConnectorPoint.x += distance;}
if (node.dependencies) {
moveRight(node.dependencies, distance);
}
}
}
// Нарисовать контейнеры элементов
function drawNode (node, container, options) {
// Создать блок DIV для узла и задать ему значение ширины, высоты и положения на экране
var nodeDiv = document.createElement('div');
nodeDiv.style.top = node.top + 'px';
nodeDiv.style.left = node.left + 'px';
nodeDiv.style.width = node.width + 'px';
nodeDiv.style.height = node.height + 'px';
if (node.collapsed) {
nodeDiv.className = 'node-collapsed'; // Если узел свернут
} else {
nodeDiv.className = 'node'; // Если узел развернут
}
// Добавить в блок DIV узла класс, содержимое и вспылвающую подсказку title, если они были заданы
if (node.className) {nodeDiv.className = node.className;}
if (node.tooltip) {nodeDiv.setAttribute('title', node.tooltip);}
if (node.name) {nodeDiv.innerHTML = node.name;}
nodeDiv.Node = node;
// Навесить на блок DIV события клика мыши по блоку
if (options.onNodeClick) {nodeDiv.onclick = options.onNodeClick;}
if (options.onNodeDoubleClick) {nodeDiv.ondblclick = options.onNodeDoubleClick;}
// Навесить на блок DIV события наведения курсора мыши на блок
nodeDiv.onmouseover = function () { // Курсор мыши над элементом
this.prevClassName = this.className;
this.className = 'node-hover';
};
nodeDiv.onmouseout = function () { // Курсор мыши ушел с элемента
if (this.prevClassName) {
this.className = this.prevClassName;
this.prevClassName = null;
}
};
// Добавить созданный блок в контейнер
container.appendChild(nodeDiv);
// Нарисовать потомков
var nodesLength // Количество потомков в данному узле
, i;
if (isNodeCollapsedAndHasChildren(node)) { // Элемент имеет потомков и раскрыт
nodesLength = node.dependencies.length;
for (i = 0; i < nodesLength; i++) {
drawNode(node.dependencies[i], container, options);
}
}
}
// Нарисовать соединительные линии между блоками элементов
function drawLines (node, container) {
var nodesLength // Количество потомков в данному узле
, j;
if (isNodeCollapsedAndHasChildren(node)) { // Элемент имеет потомков и раскрыт
nodesLength = node.dependencies.length;
if (node.childrenConnectorPoint.layout === 'vertical') { // Вертикальная схема расположения блоков
for (j = 0; j < nodesLength; j++) {
// Нарисовать вертикальную соединительную линию
drawLineV(container, node.childrenConnectorPoint, node.dependencies[j].parentConnectorPoint);
drawLines(node.dependencies[j], container); // Нарисовать соединительные линии для потомков данного блока
}
} else {
for (j = 0; j < nodesLength; j++) { //Горизонтальная схема расположения блоков
// Нарисовать горизонтальную соединительную линию
drawLineH(container, node.childrenConnectorPoint, node.dependencies[j].parentConnectorPoint);
drawLines(node.dependencies[j], container); // Нарисовать соединительные линии для потомков данного блока
}
}
}
}
// Нарисовать вертикальную соединительную линию
function drawLineV (container, startPoint, endPoint) {
var midX = (startPoint.x + ((endPoint.x - startPoint.x) / 2)); // Середина - это половина пути между началом и концом точки X
// Начальный сегмент вертикальной соединительной линии
drawLineSegment(container, startPoint.x, startPoint.y, midX, startPoint.y, 1);
// Промежуточный сегмент вертикальной соединительной линии
var imsStartY = startPoint.y < endPoint.y ? startPoint.y : endPoint.y // Самое нижнее значение будет стартовой точкой
, imsEndY = startPoint.y > endPoint.y ? startPoint.y : endPoint.y; // Самое высокое значение будет конечной точкой
drawLineSegment(container, midX, imsStartY, midX, imsEndY, 1);
// Конечный сегмент вертикальной соединительной линии
drawLineSegment(container, midX, endPoint.y, endPoint.x, endPoint.y, 1);
}
// Нарисовать горизонтальную соединительную линию
function drawLineH (container, startPoint, endPoint) {
var midY = (startPoint.y + ((endPoint.y - startPoint.y) / 2)); // Середина - это половина пути между началом и концом точки Y
// Начальный сегмент горизонтальной соединительной линии
drawLineSegment(container, startPoint.x, startPoint.y, startPoint.x, midY, 1);
// Промежуточный сегмент горизонтальной соединительной линии
var imsStartX = startPoint.x < endPoint.x ? startPoint.x : endPoint.x // Самое нижнее значение будет стартовой точкой
, imsEndX = startPoint.x > endPoint.x ? startPoint.x : endPoint.x; // Самое высокое значение будет конечной точкой
drawLineSegment(container, imsStartX, midY, imsEndX, midY, 1);
// Конечный сегмент горизонтальной соединительной линии
drawLineSegment(container, endPoint.x, midY, endPoint.x, endPoint.y, 1);
}
// Нарисовать сегмент соединительной линии
function drawLineSegment (container, startX, startY, endX, endY, lineWidth) {
// Создать блок DIV, который будет изображать соединительную линию
var lineDiv = document.createElement('div');
lineDiv.style.top = startY + 'px';
lineDiv.style.left = startX + 'px';
if (startX === endX) { // Вертикальная линия
lineDiv.style.width = lineWidth + 'px';
lineDiv.style.height = (endY - startY) + 'px';
} else{ // Горизонтальная линия
lineDiv.style.width = (endX - startX) + 'px';
lineDiv.style.height = lineWidth + 'px';
}
lineDiv.className = 'node-line';
container.appendChild(lineDiv); // Добавить соединительную линию к блоку элемента
}
*/
Комментариев нет:
Отправить комментарий