class ClassListModule {
static SearchQuery = '';
static ModeCookieName = 'doczilla-js-docs-class-list-mode';
static OpenedFoldersCookieName = 'doczilla-js-docs-class-list-opened-folders';
static Mode = {
Structurized: 'structurized',
Unstructurized: 'unstructurized'
};
static _Mode = ClassListModule.Mode.Structurized;
static setQuery(query, reload) {
ClassListModule.SearchQuery = query.toLowerCase();
if(reload)
ClassListModule.reload();
}
static load() {
ClassListModule.classListElement = ClassListModule.classListElement ? ClassListModule.classListElement : DOM.get('.class-list');
DOM.get('.class-list-mode-button.structurized').switchClass('selected', ClassListModule._Mode === ClassListModule.Mode.Structurized);
DOM.get('.class-list-mode-button.unstructurized').switchClass('selected', ClassListModule._Mode === ClassListModule.Mode.Unstructurized);
ClassListModule.loadClasses(ClassListModule._Mode);
}
static loadClasses(mode) {
let classList = {};
if(mode === ClassListModule.Mode.Structurized) {
for(const root of RepoNames) {
classList[root] = { type: 'dir', contents: {} };
Object.keys(ClassList).filter((key) => {
return ClassList[key].filePath.indexOf(root) === 0 && (ClassListModule.SearchQuery.length == 0 || key.toLowerCase().includes(ClassListModule.SearchQuery) || (ClassList[key].shortName || '').toLowerCase().includes(ClassListModule.SearchQuery));
}).sort((a, b) => a.toLowerCase().localeCompare(b.toLowerCase())).map((key) => {
classList[root].contents[key] = { type: 'cls', class: ClassList[key] };
});
}
classList = ClassListModule.transformStructure(classList);
Object.keys(classList).forEach((key) => {
if(Object.keys(classList[key].contents).length > 0)
ClassListModule.renderDir(key, key, classList[key].contents, ClassListModule.classListElement);
});
} else {
Object.keys(ClassList).sort((a, b) => {
return a.toLowerCase().localeCompare(b.toLowerCase());
}).filter((key) => {
return ClassListModule.SearchQuery.length == 0 || key.toLowerCase().includes(ClassListModule.SearchQuery) || (ClassList[key].shortName || '').toLowerCase().includes(ClassListModule.SearchQuery);
}).map((key) => { classList[key] = ClassList[key]; });
for(const className of Object.keys(classList)) {
const icon = DOM.create({ tag: 'div', cls: 'class-icon' });
const name = DOM.create({ tag: 'div', cls: 'class-name', innerHTML: className });
DOM.create({
tag: 'div',
cls: `class-item ${ Class ? (Class.name === className ? 'selected' : '') : '' }`,
cn: [icon, name],
attr: {
'data-class-name': className,
'data-class-shortname': classList[className].shortName,
'title': className
}
}, ClassListModule.classListElement).on('mousedown', ClassListModule.onListItemMouseDown);
}
}
}
static renderDir(dirName, dirPath, dirContents, parentContainer) {
if(ClassListModule.SearchQuery.length > 0 && Object.keys(dirContents).length === 0)
return;
const dirCollapsedIconEl = DOM.create({ tag: 'div', cls: 'dir-collapsed-icon' });
const dirIconEl = DOM.create({ tag: 'div', cls: 'dir-icon' });
const dirNameEl = DOM.create({ tag: 'div', cls: 'dir-name', cn: [dirCollapsedIconEl, dirIconEl], innerHTML: dirName }).on('mousedown', ClassListModule.onListItemMouseDown);
const dirContentEl = DOM.create({ tag: 'div', cls: 'dir-content' });
const dirItem = DOM.create({ tag: 'div', cls: `dir-item ${ Class && Class.isPackage ? (Class.root + '/' + Class.name.replaceAll('.', '/') === dirPath ? 'selected' : '') : '' }`, cn: [dirNameEl, dirContentEl], attr: { 'data-dir-path': dirPath } }, parentContainer);
const opened = (DOM.getCookieProperty(App.CookieName, ClassListModule.OpenedFoldersCookieName) || []).indexOf(dirPath) > -1;
if(CDUtils.isEmpty(ClassListModule.SearchQuery) && !opened)
dirItem.addClass('collapsed');
Object.keys(dirContents).sort((a, b) => {
return -dirContents[a].type.localeCompare(dirContents[b].type); // 'dir' > 'cls'
}).forEach((key) => {
const item = dirContents[key];
if(item.type === 'cls') {
ClassListModule.renderClass(item.class, dirContentEl);
} else {
ClassListModule.renderDir(key, `${dirPath}/${key}`, item.contents, dirContentEl);
}
});
}
static renderClass(cls, container) {
const className = cls.name;
const classShortName = cls.shortName;
const icon = DOM.create({ tag: 'div', cls: 'class-icon' });
const name = DOM.create({ tag: 'div', cls: 'class-name', innerHTML: className });
DOM.create({
tag: 'div',
cls: `class-item ${ Class ? (Class.name === className ? 'selected' : '') : '' }`,
cn: [icon, name],
attr: {
'data-class-name': className,
'data-class-shortname': classShortName,
'title': className
}
}, container).on('mousedown', ClassListModule.onListItemMouseDown);
}
static transformStructure(obj) {
const newObj = {};
function recursiveTransform(parent, path, type, cls) {
const [currentKey, ...rest] = path.split('.');
if (!currentKey) return;
if (!parent[currentKey]) {
parent[currentKey] = {};
parent[currentKey].type = rest.length === 0 ? type : 'dir';
parent[currentKey].contents = {};
}
if (rest.length === 0) {
parent[currentKey].type = type;
parent[currentKey].class = cls;
} else {
recursiveTransform(parent[currentKey].contents, rest.join('.'), type, cls);
}
}
function sortContents(contents) {
const sortedKeys = Object.keys(contents).sort((a, b) => {
const typeA = contents[a].type === 'dir' ? 0 : 1;
const typeB = contents[b].type === 'dir' ? 0 : 1;
if (typeA !== typeB) return typeA - typeB;
return a.localeCompare(b);
});
const sortedContents = {};
sortedKeys.forEach(key => {
sortedContents[key] = contents[key];
});
return sortedContents;
}
for (const rootKey in obj) {
if (obj.hasOwnProperty(rootKey)) {
const { type, contents } = obj[rootKey];
newObj[rootKey] = { type: 'dir', contents: sortContents({}) };
for (const classKey in contents) {
if (contents.hasOwnProperty(classKey)) {
const { type, class: cls } = contents[classKey];
recursiveTransform(newObj[rootKey].contents, classKey, type, cls);
}
}
}
}
return newObj;
}
static onListItemClick(e) {
let target = CDElement.get(e.target);
while(!target.hasClass('class-item') && !target.hasClass('dir-name'))
target = target.getParent();
if(target.hasClass('class-item')) {
ClassListModule.goToClass(target);
} else if(target.hasClass('dir-name')) {
ClassListModule.onDirClick(target);
}
}
static onDirClick(target) {
const parent = target.getParent();
const dataDirPath = parent.getAttribute('data-dir-path');
parent.switchClass('collapsed');
if(!CDUtils.isEmpty(ClassListModule.SearchQuery))
return;
let openedFoldersCookieValue = DOM.getCookieProperty(App.CookieName, ClassListModule.OpenedFoldersCookieName) || [];
if(parent.hasClass('collapsed')) {
openedFoldersCookieValue.splice(openedFoldersCookieValue.indexOf(dataDirPath), 1);
} else {
openedFoldersCookieValue.push(dataDirPath);
}
DOM.setCookieProperty(App.CookieName, ClassListModule.OpenedFoldersCookieName, openedFoldersCookieValue, 24);
}
static getDirPath(target) {
return CDElement.get(target).getAttribute('data-dir-path');
}
static getClassName(target) {
return CDElement.get(target).getAttribute('data-class-name');
}
static getPackageName(target) {
const path = ClassListModule.getPackagePath(target).split('/');
return path[path.length - 1];
}
static getClassPath(target) {
return `/class/${ClassListModule.getClassName(target)}`;
}
static getPackagePath(target) {
const replaceSlashes = (str) => {
let firstSlash = str.indexOf('/');
if (firstSlash === -1) {
return str;
}
let beforeFirstSlash = str.slice(0, firstSlash + 1);
let afterFirstSlash = str.slice(firstSlash + 1).replace(/\//g, '.');
return beforeFirstSlash + afterFirstSlash;
}
const dirPath = replaceSlashes(ClassListModule.getDirPath(target));
return `/package/${dirPath}`;
}
static goToClass(target, newTab) {
Url.goTo(ClassListModule.getClassPath(target), newTab);
}
static goToPackage(target, newTab) {
Url.goTo(ClassListModule.getPackagePath(target), newTab);
}
static onModeButtonClick(e) {
const target = CDElement.get(e.target);
const mode = target.getAttribute('data-mode');
ClassListModule._Mode = mode;
DOM.setCookieProperty(App.CookieName, ClassListModule.ModeCookieName, mode);
ClassListModule.reload();
}
static clear() {
DOM.get('.class-list').getChildrenRecursive().forEach((item) => {
if(item.hasClass('class-item') || item.hasClass('dir-name'))
item.un('mousedown', ClassListModule.onListItemMouseDown)
item.remove();
});
}
static reload() {
ClassListModule.clear();
ClassListModule.load();
}
static updateSources() {
fetch('/updateSources', {
method: 'POST'
});
}
static onListItemMouseDown(e) {
const element = CDElement.get(e.target);
if(e.buttons === DOM.MouseButtons.Left) {
ClassListModule.onListItemClick(e);
} else if(e.buttons === DOM.MouseButtons.Right) {
setTimeout(() => {
ClassListModule.showContextMenu(element, { x: e.pageX, y: e.pageY });
}, 10);
}
}
static showContextMenu(target, pos) {
while(!target.hasClass('class-item') && !target.hasClass('dir-name'))
target = target.getParent();
const contextMenu = ContextMenu.init();
if(target.hasClass('class-item')) {
contextMenu.addItem('Open', 'Open', () => {
ClassListModule.goToClass(target, false);
});
contextMenu.addItem('OpenInNewTab', 'Open in a new tab', () => {
ClassListModule.goToClass(target, true);
});
contextMenu.addDelimiter();
contextMenu.addItem('CopyLink', 'Copy link', () => {
DOM.copyToClipboard(Url.setPath(ClassListModule.getClassPath(target)).setHash('').toString());
});
contextMenu.addItem('CopyHtmlLink', 'Copy HTML link', () => {
DOM.copyToClipboard(`${ClassListModule.getClassName(target)}`);
});
} else if(target.hasClass('dir-name') && !target.getParent().getParent().hasClass('class-list')) {
contextMenu.addItem('Open', 'Open', () => {
ClassListModule.goToPackage(target.getParent(), false);
});
contextMenu.addItem('OpenInNewTab', 'Open in a new tab', () => {
ClassListModule.goToPackage(target.getParent(), true);
});
contextMenu.addDelimiter();
contextMenu.addItem('CopyLink', 'Copy link', () => {
DOM.copyToClipboard(Url.setPath(ClassListModule.getPackagePath(target.getParent())).setHash('').toString());
});
contextMenu.addItem('CopyHtmlLink', 'Copy HTML link', () => {
DOM.copyToClipboard(`${ClassListModule.getPackageName(target.getParent())}`);
});
}
contextMenu.show(pos);
}
static init() {
DOM.get('.class-list-mode-button.structurized').on(DOM.Events.Click, ClassListModule.onModeButtonClick);
DOM.get('.class-list-mode-button.unstructurized').on(DOM.Events.Click, ClassListModule.onModeButtonClick);
DOM.get('.faq-button').on(DOM.Events.Click, (e) => { Url.goTo('/faq'); });
const refreshButton = DOM.get('.refresh-button');
if(refreshButton) {
refreshButton.on(DOM.Events.Click, (e) => {
if(confirm('Are you sure you want to update sources? It will take approximately 3-5 minutes. The system will not be available until the process finished.')) {
ClassListModule.updateSources();
Url.goTo('/');
}
});
}
const statisticsButton = DOM.get('.statistics-button');
if(statisticsButton) {
statisticsButton.on(DOM.Events.Click, (e) => {
Url.goTo('/statistics');
});
}
const modeCookieValue = DOM.getCookieProperty(App.CookieName, ClassListModule.ModeCookieName);
ClassListModule._Mode = modeCookieValue || ClassListModule.Mode.Structurized;
if(!modeCookieValue)
DOM.setCookieProperty(App.CookieName, ClassListModule.ModeCookieName, ClassListModule.Mode.Structurized);
ClassListModule.load();
ClassListModule.classListElement.scrollTo('.class-item.selected');
}
}
window_.on('load', (e) => {
ClassListModule.init();
});