Browse Source

v1.0.6: statics navigation, session max age increased, session resave, 'Copy HTML link' context menu option

CrazyDoctor 4 months ago
parent
commit
71b410eee0

+ 1 - 0
node_scripts/uglifyStatics.mjs

@@ -77,6 +77,7 @@ class Minifier {
 					'lineNo',
 					'onClassLinkClick',
 					'onPropertyClick',
+					'line',
 					// <<< CodeMirror
 
 					// >>> Server data

+ 1 - 1
package.json

@@ -1,6 +1,6 @@
 {
 	"name": "doczilla_js_docs",
-	"version": "1.0.5",
+	"version": "1.0.6",
 	"dependencies": {
 		"@types/sqlite3": "^3.1.11",
 		"@types/ws": "^8.5.10",

+ 2 - 1
src/index.ts

@@ -66,7 +66,8 @@ class ServerApp {
 			options: {
 				static: path.resolve(__dirname, './static'),
 				sessionSecret: Guid.new(),
-				adminPassword: SHA256.hash(ServerConfig.adminPassword)
+				adminPassword: SHA256.hash(ServerConfig.adminPassword),
+				sessionMaxAge: 21600000
 			}
 		}).init().then((server) => {
 			server.start(() => {

+ 15 - 0
src/middlewares/SessionRenewMiddleware.ts

@@ -0,0 +1,15 @@
+import { Middleware } from 'org.crazydoctor.expressts';
+import {NextFunction, Request, Response} from 'express';
+import { ISession } from '../session/ISession';
+
+class SessionRenewMiddleware extends Middleware {
+	protected action = (req: Request, res: Response, next: NextFunction): any => {
+		const session: ISession = req.session as ISession;
+		session.lastAccess = new Date().getTime();
+		next();
+	};
+	protected order = 1;
+	protected route = null;
+}
+
+export default SessionRenewMiddleware;

+ 1 - 0
src/session/ISession.ts

@@ -5,4 +5,5 @@ export interface ISession extends Session {
 	isEditor: boolean;
 	login: string | null;
 	returnTo?: string;
+	lastAccess: number;
 }

+ 4 - 0
static/CDClientLib/CDClientLib.js

@@ -313,6 +313,10 @@ class Url {
 		location.reload();
 	}
 
+	static getPath() {
+		return new Url().getPath();
+	}
+
 	static getFullPath() {
 		const url = new Url().url;
 		return `${url.origin}${url.pathname}`;

+ 107 - 28
static/page/class/script.js

@@ -108,6 +108,8 @@ class ClassPage {
 		PropertyItem: 'property-item'
 	};
 
+	static __static__ = '__static__';
+
 	start() {
 		if(typeof Class === 'string') {
 			return this;
@@ -307,6 +309,9 @@ class ClassPage {
 
 	// TODO: refactor! Make PropertyItem class
 	renderPropertiesType(properties, type, headerText, container, initiallyHidden, isMethods) {
+		const isStatics = type === ClassPage.PropertyType.Statics;
+		const isInherited = type === ClassPage.PropertyType.Inherited;
+
 		properties = properties[type];
 
 		if(!properties || properties.length == 0)
@@ -325,7 +330,7 @@ class ClassPage {
 					|| element.getTag() === DOM.Tags.A;
 		};
 
-		if(type !== ClassPage.PropertyType.Inherited) {
+		if(!isInherited) {
 			this.documentable += properties.length;
 		} else {
 			const inheritedCommentsQuery = this.inheritedCommentsQuery;
@@ -349,7 +354,8 @@ class ClassPage {
 		}
 
 		for(const property of properties) {
-			const propertyItem = DOM.create({ tag: DOM.Tags.Div, cls: 'property-item', attr: { [ClassPage.Attributes.DataPropertyName]: property.key, [ClassPage.Attributes.DataPropertyType]: property.type }}, propertiesList).on(DOM.Events.MouseDown, (e) => {
+			const key = isStatics ? `${ClassPage.__static__}${property.key}` : property.key;
+			const propertyItem = DOM.create({ tag: DOM.Tags.Div, cls: 'property-item', attr: { [ClassPage.Attributes.DataPropertyName]: key, [ClassPage.Attributes.DataPropertyType]: property.type }}, propertiesList).on(DOM.Events.MouseDown, (e) => {
 				const element = CDElement.get(e.target);
 
 				if(e.buttons === DOM.MouseButtons.Right) {
@@ -362,9 +368,9 @@ class ClassPage {
 					if(propertyItemClickable(element))
 						return;
 					if(type === ClassPage.PropertyType.Inherited) {
-						Url.goTo(`/class/${property.nearestParent}#${isMethods ? 'Methods' : 'Properties'}:${property.key}`);
+						Url.goTo(`/class/${property.nearestParent}#${isMethods ? 'Methods' : 'Properties'}:${key}`);
 					} else {
-						this.searchPropertyInEditor(isMethods, property.dynamic, property.key);
+						this.searchPropertyInEditor(isMethods, property.dynamic, key);
 					}
 				}
 			});
@@ -401,7 +407,7 @@ class ClassPage {
 			const itemCommentText = DOM.create({ tag: DOM.Tags.Div, innerHTML: 'Comment:' });
 			const itemCommentCn = [itemCommentText];
 
-			const loadedComment = Comments[type === ClassPage.PropertyType.Statics ? `__static__${property.key}` : property.key];
+			const loadedComment = Comments[type === ClassPage.PropertyType.Statics ? `${ClassPage.__static__}${property.key}` : property.key];
 			const loadedCommentText = loadedComment && typeof loadedComment === 'object' ? loadedComment.text : '';
 
 			const hasComment = loadedComment && typeof loadedComment === 'object' && loadedCommentText.length > 0;
@@ -413,7 +419,7 @@ class ClassPage {
 				propertyItem.setAttribute(ClassPage.Attributes.DataPropertyInherited, 'true');
 			}
 
-			this.propertyItemElements[type === ClassPage.PropertyType.Statics ? `__static__${property.key}` : property.key] = propertyItem;
+			this.propertyItemElements[type === ClassPage.PropertyType.Statics ? `${ClassPage.__static__}${property.key}` : property.key] = propertyItem;
 			
 			if(isEditor) {
 				const itemCommentInput = DOM.create({ tag: DOM.Tags.Textarea, cls: 'property-item-comment-input hidden', attr: { 'placeholder': 'Not commented yet...'} }).setValue(CDUtils.br2nl(loadedCommentText));
@@ -436,7 +442,7 @@ class ClassPage {
 						if(commentContent === CDUtils.br2nl(itemCommentStatic.getValue()) || commentContent === '' && itemCommentStatic.hasClass('empty'))
 							return;
 
-						const propertyName = `${type === ClassPage.PropertyType.Statics ? '__static__' : ''}${property.key}`;
+						const propertyName = `${type === ClassPage.PropertyType.Statics ? ClassPage.__static__ : ''}${property.key}`;
 						const className = Class[ClassPage.ClassProperties.Name];
 						const classRoot = Class[ClassPage.ClassProperties.Root];
 
@@ -623,13 +629,15 @@ class ClassPage {
 	}
 
 	markContentInEditor() {
+		const staticsRange = this.getStaticsRange();
+
 		this.codeMirrorEditor.cmEachLine((lineHandle) => {
 			this.markExtend(lineHandle);
 			this.markMixins(lineHandle);
 			this.markZ8Locales(lineHandle);
 			this.markNew(lineHandle);
 			this.markThis(lineHandle);
-			this.markProperties(lineHandle);
+			this.markProperties(lineHandle, staticsRange);
 		});
 	}
 
@@ -793,24 +801,30 @@ class ClassPage {
 		}
 	}
 
-	markProperties(lineHandle) {
+	markProperties(lineHandle, staticsRange) {
 		const editor = this.codeMirrorEditor;
 		const text = lineHandle.text;
+		const lineNo = lineHandle.lineNo();
 		const regexp = /\t([\w]+):/g;
+
+		const isStatic = lineNo >= staticsRange.from && lineNo <= staticsRange.to;
+
 		let match;
 		while ((match = regexp.exec(text)) !== null) {
-			const propertyName = match[1];
-			const from = { line: lineHandle.lineNo(), ch: match.index + 1 };
-			const to = { line: lineHandle.lineNo(), ch: match.index + match[0].length - 1 };
+			const propertyName = isStatic ? `${ClassPage.__static__}${match[1]}` : match[1];
+			const from = { line: lineNo, ch: match.index + 1 };
+			const to = { line: lineNo, ch: match.index + match[0].length - 1 };
 			
 			const foundProperty = this.findClassProperty(propertyName);
 
 			if(!foundProperty)
 				continue;
-			
+
+			const titlePropertyName = isStatic ? `${Class[ClassPage.ClassProperties.ShortName] || Class[ClassPage.ClassProperties.Name]}.${propertyName.replace(ClassPage.__static__, '')}` : propertyName;
+
 			editor.markText(from, to, {
 				className: 'cm-this-prop',
-				title: `Ctrl+Click to go to ${foundProperty.type === 'method' ? 'method' : 'property'} '${propertyName}'`,
+				title: `Ctrl+Click to go to ${foundProperty.type === 'method' ? 'method' : 'property'} '${titlePropertyName}'`,
 				attributes: {
 					[ClassPage.Attributes.DataPropertyName]: propertyName,
 					[ClassPage.Attributes.DataPropertyType]: foundProperty.type === 'method' ? 'Methods' : 'Properties',
@@ -821,20 +835,63 @@ class ClassPage {
 		}
 	}
 
+	getStaticsRange() {
+		const range = { from: -1, to: -1 };
+		let staticsParenthesisFlag = -1;
+		let found = false;
+
+		this.codeMirrorEditor.cmEachLine((lineHandle) => {
+			if(found)
+				return;
+
+			const text = lineHandle.text;
+			let ignoreFirstParenthesis = false;
+
+			if(staticsParenthesisFlag === -1 && text.match(/statics:\s*\{/)) {
+				staticsParenthesisFlag = 1;
+				ignoreFirstParenthesis = true;
+				range.from = lineHandle.lineNo();
+			}
+
+			if(staticsParenthesisFlag > 0) {
+				for(let i = 0; i < text.length; i++) {
+					if(text.charAt(i) === '{' && ignoreFirstParenthesis)
+						ignoreFirstParenthesis = false;
+					else if (text.charAt(i) === '{')
+						staticsParenthesisFlag++;
+					else if(text.charAt(i) === '}')
+						staticsParenthesisFlag--;
+				}
+			}
+
+			if(staticsParenthesisFlag === 0) {
+				range.to = lineHandle.lineNo();
+				found = true;
+			}
+		});
+
+		return range;
+	}
+
 	shortNameExists(shortName) {
 		return Object.keys(ClassList).map((key) => ClassList[key]).filter((item) => item[ClassPage.ClassProperties.ShortName] === shortName).length > 0;
 	}
 
 	findClassProperty(propertyName) {
+		if(propertyName.startsWith(ClassPage.__static__)) {
+			propertyName = propertyName.slice(10);
+			const statics = Class[ClassPage.ClassProperties.Statics];
+			const foundStatic = statics.filter((prop) => prop.key === propertyName)[0];
+			return foundStatic;
+		}
+
 		const dynamicProperties = Class[ClassPage.ClassProperties.DynamicProperties];
 		const properties = Class[ClassPage.ClassProperties.Properties];
-		const statics = Class[ClassPage.ClassProperties.Statics];
 
-		const foundStatic = statics.filter((prop) => prop.key === propertyName)[0];
 		const foundDynamic = dynamicProperties.filter((prop) => prop.key === propertyName)[0];
 		const foundProperty = properties.filter((prop) => prop.key === propertyName)[0];
 
-		return foundStatic || foundDynamic || foundProperty;
+		return foundDynamic || foundProperty;
 	}
 
 	onClassLinkClick(fragment) {
@@ -871,7 +928,7 @@ class ClassPage {
 	}
 
 	onClassClick(e) {
-		let element = CDElement.get(e.target);
+		let element = CDElement.get(e.target); 
 		while(!element.hasClass(ClassPage.StyleClasses.ClassItem))
 			element = element.getParent();
 		Url.goTo(`/class/${element.getAttribute(ClassPage.Attributes.DataClassName)}`);
@@ -885,28 +942,42 @@ class ClassPage {
 		(mode === ClassPage.Mode.Tabs ? this.listModeButton : this.tabsModeButton).removeClass(ClassPage.StyleClasses.Selected);
 	}
 
-	searchInEditor(...queries) {
+	searchInEditor(isStatic, ...queries) {
+		this.switchFullSource(true);
+
 		const editor = this.codeMirrorEditor;
+		const staticsRange = this.getStaticsRange();
+		const staticsStart = staticsRange.from;
+		const staticsEnd = staticsRange.to;
+
 		for(const query of queries) {
 			const cursor = editor.getSearchCursor(query, CodeMirror.Pos(editor.cmFirstLine(), 0), { caseFold: false, multiline: true });
-			if(cursor.find(false)) {
-				this.switchFullSource(true);
-				this.openTab('Editor');
-				editor.setSelection(cursor.from(), cursor.to());
-				editor.scrollIntoView({from: cursor.from(), to: cursor.to()}, 100);
-				return;
+			while(cursor.find()) {
+				const from = cursor.from();
+				const to = cursor.to();
+				const lineIndex = from.line;
+
+				if((isStatic && lineIndex >= staticsStart && lineIndex <= staticsEnd) || (!isStatic && (lineIndex < staticsStart || lineIndex > staticsEnd))) {
+					this.openTab('Editor');
+					editor.setSelection(from, to);
+					editor.scrollIntoView({ from: from, to: to }, 100);
+					return;
+				}
 			}
 		}
 	}
 
 	searchPropertyInEditor(isMethod, isDynamic, propertyName) {
+		const isStatic = propertyName.startsWith(ClassPage.__static__);
+		propertyName = isStatic ? propertyName.slice(10) : propertyName;
+
 		if(isMethod) {
-			this.searchInEditor(`${propertyName}: function`);
+			this.searchInEditor(isStatic, `${propertyName}: function`);
 		} else {
 			if(isDynamic)
-				this.searchInEditor(`this.${propertyName} =`, `this.${propertyName}`);
+				this.searchInEditor(false, `this.${propertyName} =`, `this.${propertyName}`);
 			else
-				this.searchInEditor(`${propertyName}: `);
+				this.searchInEditor(isStatic, `${propertyName}: `);
 		}
 	}
 
@@ -1063,6 +1134,7 @@ class ClassPage {
 			const propertyItemParent = target.getAttribute(ClassPage.Attributes.DataPropertyParent);
 			const propertyItemDynamic = target.getAttribute(ClassPage.Attributes.DataPropertyDynamic);
 			const propertyItemInhertied = target.getAttribute(ClassPage.Attributes.DataPropertyInherited);
+			const propertyVisualName = target.getFirstChild('.property-item-name-span').getValue();
 
 			if(propertyItemInhertied !== 'true') {
 				this.createContextMenuItem('ShowInEditor', 'Show in Editor', () => {
@@ -1085,6 +1157,13 @@ class ClassPage {
 			this.createContextMenuItem('CopyLink', 'Copy link', () => {
 				DOM.copyToClipboard(`${Url.getFullPath()}#${propertyItemType === 'method' ? 'Methods' : 'Properties'}:${propertyItemName}`);
 			});
+			this.createContextMenuDelimiter();
+			this.createContextMenuItem('CopyHtmlLink', 'Copy HTML link', () => {
+				let linkText = propertyVisualName;
+				if(propertyItemName.startsWith(ClassPage.__static__))
+						linkText = `${Class[ClassPage.ClassProperties.ShortName] || Class[ClassPage.ClassProperties.Name]}.${propertyVisualName}`;
+				DOM.copyToClipboard(`<a href="${Url.getPath()}#${propertyItemType === 'method' ? 'Methods' : 'Properties'}:${propertyItemName}">${linkText}</a>`);
+			});
 			break;
 		}