123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368 |
- class ClassPage {
- static ModeCookieName = 'doczilla-js-docs-class-page-mode';
- static Mode = {
- Tabs: 'mode-tabs',
- List: 'mode-list'
- };
- static StyleClasses = {
- Selected: 'selected',
- Active: 'active',
- CtrlPressed: 'ctrl-pressed',
- ParentsBranch: 'parents-branch',
- ClassItem: 'class-item',
- ClassName: 'class-name',
- ClassIcon: 'class-icon',
- Filler: 'filler',
- FullSourcePrompt: 'full-source-prompt',
- FullSourcePromptText: 'full-source-prompt-text',
- FullSourcePromptButton: 'full-source-prompt-button',
- Spacing: 'spacing',
- CmLink: 'cm-link',
- Hidden: 'hidden'
- };
- static Attributes = {
- DataTab: 'data-tab',
- DataDisplayMode: 'data-display-mode',
- DataClassName: 'data-class-name',
- OnClick: 'onclick'
- };
- static Messages = {
- NoMixins: 'This class has no mixins.',
- NoChildren: 'This class has no child classes.',
- NoMixedIn: 'This class is not mixed in any classes.',
- NoParents: 'This is a base class, which has no parent classes.',
- ShowFullSourceText: 'There were found another entities in the source file of this class. Would you like to see full source file?',
- HideFullSourceText: 'Full source file shown. Would you like to hide all entities except the target class?',
- PromptButtonText: 'OK',
- CmLinkTipPrefix: 'Ctrl+Click to go to class'
- };
- start() {
- const tabElements = this.tabElements = {
- Editor: DOM.get('.tab.editor'),
- Methods: DOM.get('.tab.methods'),
- Parents: DOM.get('.tab.parents'),
- Properties: DOM.get('.tab.properties'),
- Mixins: DOM.get('.tab.mixins'),
- Children: DOM.get('.tab.children'),
- MixedIn: DOM.get('.tab.mixedin')
- };
-
- const contentElements = this.contentElements = {
- Editor: DOM.get('.content-tab#editor'),
- Methods: DOM.get('.content-tab#methods'),
- Parents: DOM.get('.content-tab#parents'),
- Properties: DOM.get('.content-tab#properties'),
- Mixins: DOM.get('.content-tab#mixins'),
- Children: DOM.get('.content-tab#children'),
- MixedIn: DOM.get('.content-tab#mixedin')
- };
- const rightContainer = this.rightContainer = DOM.get('.right');
- const selectedTab = this.selectedTab = tabElements[Url.getHash()] || tabElements.Editor;
- const activeContent = this.activeContent = contentElements[Url.getHash()] || contentElements.Editor;
- const modeCookieValue = DOM.getCookieProperty(App.CookieName, ClassPage.ModeCookieName);
-
- if(!modeCookieValue)
- DOM.setCookieProperty(App.CookieName, ClassPage.ModeCookieName, ClassPage.Mode.Tabs);
- const mode = this.mode = modeCookieValue || ClassPage.Mode.Tabs;
- const tabsModeButton = this.tabsModeButton = DOM.get('.display-mode-button.mode-tabs');
- const listModeButton = this.listModeButton = DOM.get('.display-mode-button.mode-list');
- (mode === ClassPage.Mode.Tabs ? tabsModeButton : listModeButton).addClass(ClassPage.StyleClasses.Selected);
- rightContainer.addClass(mode);
- this.renderContent();
- this.selectTab(selectedTab);
- this.activateContent(activeContent);
- this.codeMirrorEditor.eachLine((lineHandle) => {
- this.markExtend(lineHandle);
- this.markMixins(lineHandle);
- });
- this.registerEventListeners();
- return this;
- }
- switchMode(mode) {
- this.rightContainer.removeClass(this.mode);
- this.mode = mode;
- DOM.setCookieProperty(App.CookieName, ClassPage.ModeCookieName, mode);
- this.rightContainer.addClass(mode);
- this.codeMirrorEditor.refresh();
- }
- selectTab(tab) {
- tab = typeof tab === 'string' ? this.tabElements[tab] : tab;
- const selectedTab = this.selectedTab;
- let filler = selectedTab.getFirstChild('.filler');
- if(!filler)
- filler = DOM.create({ tag: DOM.Tags.Div, cls: ClassPage.StyleClasses.Filler });
- selectedTab.removeClass(ClassPage.StyleClasses.Selected);
- this.selectedTab = tab.addClass(ClassPage.StyleClasses.Selected);
- this.selectedTab.append(filler);
- }
- activateContent(content) {
- content = typeof content === 'string' ? this.contentElements[content] : content;
- this.activeContent.removeClass(ClassPage.StyleClasses.Active);
- this.activeContent = content.addClass(ClassPage.StyleClasses.Active);
- if(content === this.contentElements.Editor)
- this.codeMirrorEditor.refresh();
- }
- registerEventListeners() {
- this.registerTabsEventListeners();
- this.registerModeButtonsEventListeners();
- }
- registerTabsEventListeners() {
- const tabElements = this.tabElements;
- for(const tabName of Object.keys(tabElements)) {
- tabElements[tabName].on(DOM.Events.Click, this.onTabClick.bind(this));
- }
- }
- registerModeButtonsEventListeners() {
- this.tabsModeButton.on(DOM.Events.Click, this.onModeButtonClick.bind(this));
- this.listModeButton.on(DOM.Events.Click, this.onModeButtonClick.bind(this));
- }
- prepareSource() {
- const className = Class.name.replaceAll('.', '\\.');
- const classRx = new RegExp(`Z8\\.define\\(\'${className}\',\\s*\\{(?:.|[\r\n])+?^\\}\\);?`, 'gm');
- const classSource = this.classSource = ClassSource.match(classRx)[0];
- this.sourceHasAnotherEntities = ClassSource.trim() !== classSource;
- return classSource;
- }
- renderContent() {
- this.renderEditor();
- this.renderParents();
- this.renderMixins();
- this.renderChildren();
- this.renderMixedIn();
- }
- renderEditor() {
- this.codeMirrorEditor = CodeMirror(DOM.get('#editor').get(), {
- value: this.prepareSource(ClassSource),
- mode: 'javascript',
- theme: 'darcula',
- readOnly: true,
- lineNumbers: true,
- matchBrackets: true,
- scrollbarStyle: 'overlay'
- });
- if(this.sourceHasAnotherEntities)
- this.renderFullSourcePrompt();
- this.codeMirrorEditorElement = DOM.get('.CodeMirror');
- }
- renderMixins() {
- const mixinsElement = this.contentElements.Mixins;
- if(Class.mixins.length == 0) {
- DOM.create({ tag: DOM.Tags.Div, style: 'font-size: 24px;', innerHTML: ClassPage.Messages.NoMixins }, mixinsElement);
- return;
- }
- this.renderClassItems(Class.mixins, mixinsElement);
- }
- renderChildren() {
- const childrenElement = this.contentElements.Children;
- if(Class.children.length == 0) {
- DOM.create({ tag: DOM.Tags.Div, style: 'font-size: 24px;', innerHTML: ClassPage.Messages.NoChildren }, childrenElement);
- return;
- }
- this.renderClassItems(Class.children, childrenElement);
- }
- renderMixedIn() {
- const mixedInElement = this.contentElements.MixedIn;
- if(Class.mixedIn.length == 0) {
- DOM.create({ tag: DOM.Tags.Div, style: 'font-size: 24px;', innerHTML: ClassPage.Messages.NoMixedIn }, mixedInElement);
- return;
- }
- this.renderClassItems(Class.mixedIn, mixedInElement);
- }
- renderFullSourcePrompt() {
- const editorContent = this.contentElements.Editor;
- const prompt = DOM.create({ tag: DOM.Tags.Div, cls: ClassPage.StyleClasses.FullSourcePrompt }, editorContent);
- const text = DOM.create({ tag: DOM.Tags.Div, cls: ClassPage.StyleClasses.FullSourcePromptText, innerHTML: ClassPage.Messages.ShowFullSourceText }, prompt);
- const button = DOM.create({ tag: DOM.Tags.Div, cls: ClassPage.StyleClasses.FullSourcePromptButton, innerHTML: ClassPage.Messages.PromptButtonText }, prompt);
- const onButtonClick = (e) => {
- text.setInnerHTML(this.switchFullSource() ? ClassPage.Messages.HideFullSourceText : ClassPage.Messages.ShowFullSourceText);
- };
- button.on(DOM.Events.Click, onButtonClick.bind(this));
- }
- switchFullSource() {
- const show = this.fullSourceCodeShown = !this.fullSourceCodeShown;
- this.codeMirrorEditor.setValue(show ? ClassSource : this.classSource);
- if(show)
- this.findAndScrollToTargetClass();
- this.codeMirrorEditor.refresh();
- return show;
- }
- findAndScrollToTargetClass() {
- const className = Class.name.replaceAll('.', '\\.');
- const editor = this.codeMirrorEditor;
- const defineRx = new RegExp(`Z8\\.define\\(\'${className}\',`);
- editor.eachLine((lineHandle) => {
- const text = lineHandle.text;
- const match = text.match(defineRx);
- if(match) {
- editor.scrollIntoView({ line: lineHandle.lineNo(), ch: 0 });
- return;
- }
- });
- }
- renderParents() {
- const parentsContent = this.contentElements.Parents;
- const parentsContainer = DOM.create({ tag: DOM.Tags.Div, cls: ClassPage.StyleClasses.ParentsBranch }, parentsContent);
- if(Class.parentsBranch.length == 0) {
- DOM.create({ tag: DOM.Tags.Div, style: 'font-size: 24px;', innerHTML: ClassPage.Messages.NoParents }, parentsContent);
- return;
- }
- this.renderClassItems(Class.parentsBranch, parentsContainer, true);
- }
- renderClassItems(itemsList, container, withIndent) {
- let indent = 0;
- if(!withIndent)
- itemsList = itemsList.sort((a, b) => a.toLowerCase().localeCompare(b.toLowerCase()));
- for(const cls of itemsList) {
- const icon = DOM.create({ tag: DOM.Tags.Div, cls: ClassPage.StyleClasses.ClassIcon });
- const name = DOM.create({ tag: DOM.Tags.Div, cls: ClassPage.StyleClasses.ClassName, innerHTML: cls });
- const cn = [icon, name];
- if(indent > 0 && withIndent)
- cn.unshift(DOM.create({ tag: DOM.Tags.Div, cls: ClassPage.StyleClasses.Spacing, style: `width: ${10 * indent}px;` }));
- DOM.create({ tag: DOM.Tags.Div, cls: `${ClassPage.StyleClasses.ClassItem}${ withIndent ? ' indent' : '' }`, attr: { [ClassPage.Attributes.DataClassName]: cls }, cn: cn }, container).on(DOM.Events.Click, this.onClassClick.bind(this));
- indent++;
- }
- }
- markExtend(lineHandle) {
- const editor = this.codeMirrorEditor;
- const text = lineHandle.text;
- const match = text.match(/extend:\s*['"]?([\w\.]+)['"]?/);
- if (match) {
- const className = match[1];
- const from = { line: lineHandle.lineNo(), ch: match.index + 8 };
- const to = { line: lineHandle.lineNo(), ch: match.index + match[0].length };
- editor.markText(from, to, {
- className: ClassPage.StyleClasses.CmLink,
- title: `${ClassPage.Messages.CmLinkTipPrefix} ${className}`,
- attributes: {
- [ClassPage.Attributes.DataClassName]: className,
- [ClassPage.Attributes.OnClick]: 'window.page.onClassLinkClick(this);'
- }
- });
- }
- }
- markMixins(lineHandle) {
- const editor = this.codeMirrorEditor;
- const text = lineHandle.text;
- const match = text.match(/mixins:\s*(\[.*?\]|\w+)/);
- if (match) {
- let mixins = match[1].replace(/\[|\]/g, "").split(/\s*,\s*/);
- const mixinsStr = match[1].replace(/\[|\]/g, "");
- mixins = mixinsStr.split(/\s*,\s*/);
- const startIndex = match.index + match[0].indexOf(mixinsStr);
- for (var i = 0; i < mixins.length; i++) {
- const className = mixins[i].trim().replace(/^['"]|['"]$/g, "");
- const classIndex = mixinsStr.indexOf(className);
- const from = { line: lineHandle.lineNo(), ch: startIndex + classIndex };
- const to = { line: lineHandle.lineNo(), ch: startIndex + classIndex + className.length };
- editor.markText(from, to, {
- className: ClassPage.StyleClasses.CmLink,
- title: `${ClassPage.Messages.CmLinkTipPrefix} ${className}`,
- attributes: {
- [ClassPage.Attributes.DataClassName]: className,
- [ClassPage.Attributes.OnClick]: 'window.page.onClassLinkClick(this);'
- }
- });
- }
- }
- }
- onClassLinkClick(fragment) {
- if(this.codeMirrorEditorElement.hasClass(ClassPage.StyleClasses.CtrlPressed))
- Url.goTo(`/class/${fragment.getAttribute(ClassPage.Attributes.DataClassName)}`);
- }
- onTabClick(e) {
- const element = CDElement.get(e.target);
- if(element.hasClass(ClassPage.StyleClasses.Selected))
- return;
- const tabName = element.getAttribute(ClassPage.Attributes.DataTab);
- this.selectTab(tabName);
- this.activateContent(tabName);
- Url.setHash(tabName).updateLocation();
- }
- onClassClick(e) {
- let element = CDElement.get(e.target);
- while(!element.hasClass(ClassPage.StyleClasses.ClassItem))
- element = element.getParent();
- Url.goTo(`/class/${element.getAttribute(ClassPage.Attributes.DataClassName)}`);
- }
- onModeButtonClick(e) {
- const button = CDElement.get(e.target);
- const mode = button.getAttribute(ClassPage.Attributes.DataDisplayMode);
- this.switchMode(mode);
- button.addClass(ClassPage.StyleClasses.Selected);
- (mode === ClassPage.Mode.Tabs ? this.listModeButton : this.tabsModeButton).removeClass(ClassPage.StyleClasses.Selected);
- }
- };
- window_.on(DOM.Events.Load, (e) => {
- window.page = new ClassPage().start();
- });
- window_.on(DOM.Events.KeyDown, (e) => {
- if(window.page && e.key === 'Control')
- window.page.codeMirrorEditorElement.addClass(ClassPage.StyleClasses.CtrlPressed);
- });
- window_.on(DOM.Events.KeyUp, (e) => {
- if(window.page && e.key === 'Control')
- window.page.codeMirrorEditorElement.removeClass(ClassPage.StyleClasses.CtrlPressed);
- });
|