diff --git a/browser/animation/AnimationFrameEvent.ts b/browser/animation/AnimationFrameEvent.ts new file mode 100644 index 0000000..94b457e --- /dev/null +++ b/browser/animation/AnimationFrameEvent.ts @@ -0,0 +1,21 @@ +import { OnAnimationFrame } from './OnAnimationFrame'; +export class AnimationFrameEvent +{ + onAnimationFrame:OnAnimationFrame; + + _timeMS:number = 0; + get timeMS(){ return this._timeMS; } + + _lastTimeMS:number = 0; + get lastTimeMS(){ return this._lastTimeMS; } + + _deltaTimeMS:number = 0; + get deltaTimeMS(){ return this._deltaTimeMS; } + + update() + { + this._lastTimeMS = this._timeMS; + this._timeMS = Date.now(); + this._deltaTimeMS = this._timeMS - this._lastTimeMS; + } +} \ No newline at end of file diff --git a/browser/animation/OnAnimationFrame.ts b/browser/animation/OnAnimationFrame.ts new file mode 100644 index 0000000..157dffc --- /dev/null +++ b/browser/animation/OnAnimationFrame.ts @@ -0,0 +1,155 @@ +import { EventSlot } from '../events/EventSlot'; +import { AnimationFrameEvent } from './AnimationFrameEvent'; +import { sleep } from './sleep'; + +export type Idle = "Idle"; +export type Running = "Running"; +export type Stopping = "Stopping"; + +export type OnAnimationFrameStatus = Idle | Running | Stopping; + + +export class OnAnimationFrame extends EventSlot +{ + static readonly Idle:Idle = "Idle"; + static readonly Running:Running = "Running"; + static readonly Stopping:Stopping = "Stopping"; + + #status:OnAnimationFrameStatus = OnAnimationFrame.Idle; + get status(){ return this.#status; } + + #stopFlag = false; + #updater:()=>void = null; + #animationFrameEvent = new AnimationFrameEvent(); + + + constructor() + { + super(); + this.#animationFrameEvent.onAnimationFrame = this; + } + + doUntil( condition:()=>boolean, callback:( e:AnimationFrameEvent )=>void ) + { + let conditionalCallback = ( e:AnimationFrameEvent )=> + { + callback( e ); + + if ( ! condition() ) + { + this.removeListener( conditionalCallback ); + } + } + + this.addListener( conditionalCallback ); + } + + doOnceWhen( condition:()=>boolean, callback:( e:AnimationFrameEvent )=>void ) + { + let conditionalCallback = ( e:AnimationFrameEvent )=> + { + if ( ! condition() ) + { + return; + } + + callback( e ); + this.removeListener( conditionalCallback ); + + } + + this.addListener( conditionalCallback ); + } + + run() + { + if ( OnAnimationFrame.Running === this.#status ) + { + return; + } + + this._schedulRunning(); + } + + private async _schedulRunning() + { + if ( ! this.#updater ) + { + this._createUpdaterFunction(); + } + + if ( OnAnimationFrame.Idle === this.#status ) + { + this._startUpdaterLoop(); + return; + } + + let currentState = this.#status as OnAnimationFrameStatus; + let maxTries = 1000; + + while ( OnAnimationFrame.Idle !== currentState ) + { + maxTries--; + + if ( maxTries <= 0 ) + { + return Promise.reject(); + } + + await sleep( 1 ); + currentState = this.#status as OnAnimationFrameStatus; + + if ( OnAnimationFrame.Idle === currentState ) + { + this._startUpdaterLoop(); + return; + } + } + + + } + + private _startUpdaterLoop() + { + this.#stopFlag = false; + this.#status = OnAnimationFrame.Running; + this.#updater(); + } + + private _createUpdaterFunction() + { + this.#updater = + + ( ) => + { + this._update(); + + if ( this.#stopFlag ) + { + this.#stopFlag = false; + this.#status = OnAnimationFrame.Idle; + } + else + { + requestAnimationFrame( this.#updater ); + } + }; + } + + + + private _update() + { + + this.#animationFrameEvent.update(); + + this.dispatch( this.#animationFrameEvent ); + } + + stop() + { + this.#stopFlag = true; + } + + +} \ No newline at end of file diff --git a/browser/animation/TimePin.ts b/browser/animation/TimePin.ts new file mode 100644 index 0000000..fa25c4e --- /dev/null +++ b/browser/animation/TimePin.ts @@ -0,0 +1,32 @@ +import { sleep } from "./sleep"; + +export class TimePin +{ + protected _timeMS:number; + + now() + { + this._timeMS = new Date().getTime(); + } + + static create() + { + let pin = new TimePin(); + pin._timeMS = new Date().getTime(); + return pin; + } + + async forMS( durationMS:number, sleepStepsMS:number = 15 ):Promise + { + let endTime = this._timeMS + durationMS; + let currentTime = new Date().getTime(); + + while ( currentTime < endTime ) + { + await sleep( sleepStepsMS) ; + currentTime = new Date().getTime(); + } + + return Promise.resolve(); + } +} \ No newline at end of file diff --git a/browser/animation/nextFrame.ts b/browser/animation/nextFrame.ts new file mode 100644 index 0000000..a1958dd --- /dev/null +++ b/browser/animation/nextFrame.ts @@ -0,0 +1,11 @@ +export function nextFrame():Promise +{ + let promise = new Promise( + ( resolve, reject ) => + { + requestAnimationFrame( () => { resolve(); } ); + } + ); + + return promise; +} \ No newline at end of file diff --git a/browser/animation/sleep.ts b/browser/animation/sleep.ts new file mode 100644 index 0000000..20ee656 --- /dev/null +++ b/browser/animation/sleep.ts @@ -0,0 +1,11 @@ +export function sleep( timeMS:number ):Promise +{ + let promise = new Promise( + ( resolve, reject ) => + { + setTimeout( resolve, timeMS ); + } + ); + + return promise; +} \ No newline at end of file diff --git a/browser/events/ChangeEvent.ts b/browser/events/ChangeEvent.ts new file mode 100644 index 0000000..fdc3359 --- /dev/null +++ b/browser/events/ChangeEvent.ts @@ -0,0 +1,15 @@ + +export class ChangeEvent +{ + private _lastValue:T; + get lastValue() { return this._lastValue; } + private _newValue:T; + get newValue(){ return this._newValue; } + + constructor( lastValue:T, newValue:T ) + { + this._lastValue = lastValue; + this._newValue = newValue; + } + +} \ No newline at end of file diff --git a/browser/events/ComparableProperty.ts b/browser/events/ComparableProperty.ts new file mode 100644 index 0000000..992daba --- /dev/null +++ b/browser/events/ComparableProperty.ts @@ -0,0 +1,27 @@ +import { Property } from "./Property"; + +interface Comparable +{ + equals( other:T ):boolean +} + +export class ComparableProperty > extends Property +{ + isEqual( a:T, b:T ) + { + let valueA = a === null ? 0 : a === undefined ? 1 : 2; + let valueB = b === null ? 0 : b === undefined ? 1 : 2; + + if ( valueA !== valueB ) + { + return false; + } + + if ( valueA !== 2 ) + { + return true; + } + + return a.equals( b ); + } +} \ No newline at end of file diff --git a/browser/events/EventSlot.ts b/browser/events/EventSlot.ts new file mode 100644 index 0000000..0f93cc5 --- /dev/null +++ b/browser/events/EventSlot.ts @@ -0,0 +1,59 @@ +export type EventSlotCallback = ( eventData:T ) => void; + +export class EventSlot +{ + private _listeners:EventSlotCallback[] = []; + + + + addListener( callback:EventSlotCallback ) + { + this._listeners.push( callback ); + } + + once( callback:EventSlotCallback ) + { + let onceCallback = ( t:T ) => + { + try + { + callback( t ); + } + catch( e ) + { + console.error( "Error on callback", e ); + } + + this.removeListener( onceCallback ); + }; + + this.addListener( onceCallback ); + } + + removeListener( callback:EventSlotCallback ) + { + let index = this._listeners.indexOf( callback ); + + if ( index === -1 ) + { + return; + } + + this._listeners.splice( index, 1 ); + } + + removeAll() + { + this._listeners = []; + } + + + dispatch( eventData:T ) + { + for ( let listener of this._listeners ) + { + listener( eventData ); + } + } + +} \ No newline at end of file diff --git a/browser/events/ListProperty.ts b/browser/events/ListProperty.ts new file mode 100644 index 0000000..4bde9c2 --- /dev/null +++ b/browser/events/ListProperty.ts @@ -0,0 +1,148 @@ +import { Arrays } from "../tools/Arrays"; +import { EventSlot } from "./EventSlot"; + +export enum ListPropertyEventType +{ + ADDED, + REMOVED, + CHANGED, + SWAPPED +} + +export class ListPropertyEvent +{ + type:ListPropertyEventType; + indices:number[] = []; + elements:T[] = [] + + static create( type:ListPropertyEventType, indices:number[], elements:T[] ) + { + let e = new ListPropertyEvent(); + e.type = type; + e.indices = indices; + e.elements = elements; + + return e; + } + +} + +export class ListProperty extends EventSlot> +{ + #list:T[] = []; + + #dispatch( type:ListPropertyEventType, indices:number[], elements:T[] ) + { + this.dispatch( ListPropertyEvent.create( type, indices, elements ) ); + } + + #dispatchAdded( indices:number[], elements:T[] ) + { + this.#dispatch( ListPropertyEventType.ADDED, indices, elements ); + } + + #dispatchRemoved( indices:number[], elements:T[] ) + { + this.#dispatch( ListPropertyEventType.REMOVED, indices, elements ); + } + + #dispatchChanged( indices:number[], elements:T[] ) + { + this.#dispatch( ListPropertyEventType.CHANGED, indices, elements ); + } + + #dispatchSwapped( indices:number[], elements:T[] ) + { + this.#dispatch( ListPropertyEventType.SWAPPED, indices, elements ); + } + + get length() + { + return this.#list.length; + } + + indexOf( t:T ) + { + return this.#list.indexOf( t ); + } + + forAll( callback:( t:T, index:number ) => void ) + { + for ( let i = 0; i < this.#list.length; i++ ) + { + callback( this.#list[ i ], i ); + } + } + + add( t:T ) + { + this.#list.push( t ); + this.#dispatchAdded( [ this.#list.length - 1 ], [ t ] ); + } + + addAll( all:T[] ) + { + let last = this.#list.length; + this.#list = this.#list.concat( all ); + this.#dispatchAdded( [ last ], all ); + } + + get( index:number ) + { + return this.#list[ index ]; + } + + set( index:number, t:T ) + { + let before = this.#list[ index ]; + this.#list[ index ] = t; + this.#dispatchChanged( [ index ], [ before, t ] ); + } + + remove( index:number ) + { + let removal = this.#list[ index ]; + Arrays.removeAt( this.#list, index ); + + this.#dispatchRemoved( [ index ], [ removal ] ); + } + + removeFirst() + { + if ( this.length < 1 ) + { + return; + } + + this.remove( 0 ); + } + + removeLast() + { + if ( this.length < 1 ) + { + return; + } + + this.remove( this.length - 1 ); + } + + clear() + { + let all = this.#list; + this.#list = []; + + this.#dispatchRemoved( null, all ); + } + + swap( indexA:number, indexB:number ) + { + let elementA = this.#list[ indexA ]; + let elementB = this.#list[ indexB ]; + + this.#list[ indexA ] = elementB; + this.#list[ indexB ] = elementA; + + this.#dispatchSwapped( [ indexA, indexB ], [ elementB, elementA ] ); + } +} \ No newline at end of file diff --git a/browser/events/Property.ts b/browser/events/Property.ts new file mode 100644 index 0000000..3393792 --- /dev/null +++ b/browser/events/Property.ts @@ -0,0 +1,98 @@ +import { EventSlot } from './EventSlot'; +import { ChangeEvent } from './ChangeEvent'; +import { isClassOf } from '../tools/TypeUtilities'; + +export class Property extends EventSlot> +{ + protected _value:T; + private _silent:boolean = false; + + get isSilent() + { + return this._silent; + } + + constructor( initValue:T ) + { + super(); + this._value = initValue; + } + + toString() + { + return this.value + ""; + } + + get value(){ return this._value; } + + + set value( v:T ) + { + if ( this._silent ) + { + return; + } + + if ( this.isEqual( v, this._value ) ) + { + return; + } + + let valueBefore = this._value; + this._value = v; + + this.dispatch( new ChangeEvent( valueBefore, v ) ); + } + + forceDispatch() + { + this.dispatch( new ChangeEvent( this._value, this._value ) ); + } + + silent() + { + this._silent = true; + } + + unsilent() + { + this._silent = false; + } + + silentRecursively():boolean + { + let silent = this._silent; + this._silent = true; + return silent; + } + + unsilentRecursively( valueBefore:boolean ) + { + this._silent = valueBefore; + } + + setValueSilently( v:T ) + { + this._value = v; + } + + isEqual( a:T, b:T ) + { + return a === b; + } + + static resolveValue( value:T|Property, alternative:T = null ) + { + if ( value === null || value === undefined ) + { + return alternative; + } + + if ( isClassOf( value, Property ) ) + { + return ( value as Property ).value; + } + + return value as T; + } +} \ No newline at end of file diff --git a/browser/tools/TypeUtilities.ts b/browser/tools/TypeUtilities.ts new file mode 100644 index 0000000..dd58e75 --- /dev/null +++ b/browser/tools/TypeUtilities.ts @@ -0,0 +1,162 @@ +export type NullType = "null"; +export type UndefinedType = "undefined"; +export type PrimitiveType = "string" | "boolean" | "number"; +export type DataType = PrimitiveType | NullType | UndefinedType | "object" ; + +export type Action = (t:T)=>void; +export type Predicate = (t:T)=>boolean; +export type Func = ()=>R; +export type Func1 = ( i1:I1 ) => R; +export type Func2 = ( i1:I1, i2:I2 ) => R; +export type Func3 = ( i1:I1, i2:I2, i3:I3 ) => R; + + +export interface ClassConstructor +{ + prototype:PrototypeFunction +} + + +export interface PrototypeFunction +{ + isPrototypeOf:( value:any ) => boolean; +} + +export interface ObjectType +{ + constructor:ClassConstructor; +} + +export function isClassOf( value:any, classType:ClassConstructor ) +{ + return classType.prototype.isPrototypeOf( value ); +} + +export function castClass( value:any, classType:ClassConstructor, alternativeValue:T = null ) +{ + if ( isClassOf( value, classType ) ) + { + return value as T; + } + + return alternativeValue; +} + +export function getClass( value:any ):ClassConstructor +{ + if ( isPrimitive( value ) ) + { + return null; + } + + if ( isNoValue( value ) ) + { + return null; + } + + return value.constructor; +} + +export function hasOwnProp( value:any, name:string ) +{ + if ( ! value ) + { + return false; + } + + return Object.prototype.hasOwnProperty.call( value, name ); +} + +export function isNoValue( value:any ) +{ + if ( value === null ) + { + return true; + } + + if ( value === undefined ) + { + return true; + } + + return false; +} + +export function isFunction( value:any ) +{ + if ( isNoValue( value ) ) + { + return false; + } + + if ( "function" === typeof( value ) ) + { + return true; + } + + return false; +} + +export function getMemberName( parent:any, member:any ) +{ + for ( let it in parent ) + { + if ( parent[ it ] === member ) + { + return it; + } + } + + return null; +} + +export function isPrimitive( value:any ) +{ + if ( isNoValue( value ) ) + { + return false; + } + + let type = typeof( value ); + + if ( "string" === type ) + { + return true; + } + + if ( "number" === type ) + { + return true; + } + + if ( "boolean" === type ) + { + return true; + } + + return false; + +} + +export function className( value:any ) +{ + if ( value === null ) + { + return "null"; + } + + if ( value === undefined ) + { + return "undefined"; + } + + let type = typeof value; + + if ( type === "object" ) + { + return value.constructor.name; + } + + return type; + +} \ No newline at end of file diff --git a/browser/tools/sleep.ts b/browser/tools/sleep.ts new file mode 100644 index 0000000..ab725c9 --- /dev/null +++ b/browser/tools/sleep.ts @@ -0,0 +1,25 @@ +export function sleep( ms:number ):Promise +{ + let promise = new Promise( + ( resolve, reject )=> + { + setTimeout( ()=>{ resolve(); }, ms ); + } + ); + + return promise; +} + +export function waitAround( msMin:number, msMax:number ):Promise +{ + let ms = msMin + Math.random() * ( msMax - msMin ); + + let promise = new Promise( + ( resolve, reject )=> + { + setTimeout( ()=>{ resolve(); }, ms ); + } + ); + + return promise; +} \ No newline at end of file diff --git a/browser/xhttp/HTTPMethodType.ts b/browser/xhttp/HTTPMethodType.ts new file mode 100644 index 0000000..c3a2676 --- /dev/null +++ b/browser/xhttp/HTTPMethodType.ts @@ -0,0 +1,38 @@ +export type GET = "GET"; +export type HEAD = "HEAD"; +export type POST = "POST"; +export type PUT = "PUT"; +export type DELETE = "DELETE"; +export type CONNECT = "CONNECT"; +export type OPTIONS = "OPTIONS"; +export type TRACE = "TRACE"; +export type PATCH = "PATCH"; + +export type HTTPMethodType = + GET | HEAD | POST | PUT | DELETE | CONNECT | OPTIONS | TRACE | PATCH; + +export class HTTPMethodTypes +{ + static readonly GET:GET = "GET"; + static readonly HEAD:HEAD = "HEAD"; + static readonly POST:POST = "POST"; + static readonly PUT:PUT = "PUT"; + static readonly DELETE:DELETE = "DELETE"; + static readonly CONNECT:CONNECT = "CONNECT"; + static readonly OPTIONS:OPTIONS = "OPTIONS"; + static readonly TRACE:TRACE = "TRACE"; + static readonly PATCH:PATCH = "PATCH"; + + static readonly all:HTTPMethodType[] = + [ + "GET", + "HEAD", + "POST", + "PUT", + "DELETE", + "CONNECT", + "OPTIONS", + "TRACE", + "PATCH", + ]; +} \ No newline at end of file diff --git a/browser/xhttp/Loader.ts b/browser/xhttp/Loader.ts new file mode 100644 index 0000000..95ec571 --- /dev/null +++ b/browser/xhttp/Loader.ts @@ -0,0 +1,213 @@ +import { HTTPMethodType, HTTPMethodTypes } from "./HTTPMethodType"; + +export class Loader +{ + static async request( url:string, input:T ):Promise + { + let promise = new Promise + ( + ( resolve, reject ) => + { + let xhr = new XMLHttpRequest(); + + console.log( "loading", url ); + xhr.open( "POST", url, true ); + xhr.responseType = "text"; + xhr.onload= + () => + { + console.log( xhr.responseURL, xhr.responseText ); + + if ( xhr.status !== 200 || xhr.responseText.startsWith( "ERROR:" ) ) + { + reject( xhr.responseText ) + } + else + { + resolve( JSON.parse( xhr.responseText ) as O ); + } + + }; + + xhr.onerror=(e)=> + { + reject( e ); + } + + xhr.send( JSON.stringify( input ) ); + } + ); + + return promise; + } + + static async readDataURL( blob:Blob ):Promise + { + let promise = new Promise + ( + ( resolve, reject ) => + { + let fileReader = new FileReader(); + + fileReader.onload = ()=> + { + resolve( fileReader ); + } + + fileReader.onerror = ()=> + { + reject(); + } + + fileReader.onabort = ()=> + { + reject(); + } + + fileReader.readAsDataURL( blob ); + + } + ); + + return promise; + } + + static async loadJSON( url:string, method:HTTPMethodType = HTTPMethodTypes.GET):Promise + { + let promise = new Promise + ( + ( resolve, reject )=> + { + let xhr = new XMLHttpRequest(); + + xhr.open( method, url, true ); + xhr.responseType = "json"; + + console.log( "xhr", url, method ); + + xhr.onload=()=> + { + console.log( "onload" ); + let value = null; + + try + { + value = xhr.response; + } + catch ( e ) + { + reject( e ); + } + + resolve( value as T ); + }; + + xhr.onerror = ( e ) => + { + console.log( "onerror", e ); + reject( e ); + } + + xhr.send(); + } + ); + + return promise; + } + + + static loadText( url:string, method:"GET"|"POST" = "GET" ):Promise + { + let promise = new Promise + ( + ( resolve, reject ) => + { + let xhr = new XMLHttpRequest(); + xhr.open( method, url, true ); + + xhr.responseType = "text"; + + xhr.onload = () => + { + console.log( "load", url, xhr ); + + if ( xhr.status !== 200 ) + { + reject( xhr.response ) + } + else + { + resolve( xhr.responseText ); + } + + }; + + xhr.onerror = ( e )=> + { + console.log( "error", url, xhr ); + + reject( e ); + } + + xhr.send(); + } + ); + + return promise; + } + + static loadXML( url:string ):Promise + { + let promise = new Promise + ( + (resolve,reject)=> + { + let xhr = new XMLHttpRequest(); + xhr.open("GET",url,true); + xhr.responseType = "document"; + xhr.onload=()=> + { + resolve(xhr.responseXML); + }; + + xhr.onerror=(e)=> + { + reject(e); + } + + xhr.send(); + } + ); + + return promise; + } + + + static loadImage( url:string ):Promise + { + let promise = new Promise + ( + (resolve,reject)=> + { + let img = new Image(); + + img.onload = () => + { + resolve( img ); + }; + + img.onerror = ( e ) => + { + reject( e ); + } + + img.src = url; + } + ); + + return promise; + } + + + +} \ No newline at end of file diff --git a/node/log/LogColors.ts b/node/log/LogColors.ts new file mode 100644 index 0000000..1d58ca5 --- /dev/null +++ b/node/log/LogColors.ts @@ -0,0 +1,31 @@ +export class LogColors +{ + static readonly reset = "\x1b[0m"; + static readonly bright = "\x1b[1m"; + static readonly dim = "\x1b[2m"; + static readonly underscore = "\x1b[4m"; + static readonly blink = "\x1b[5m"; + static readonly reverse = "\x1b[7m"; + static readonly hidden = "\x1b[8m"; + + static readonly black_Foreground = "\x1b[30m"; + static readonly red_Foreground = "\x1b[31m"; + static readonly green_Foreground = "\x1b[32m"; + static readonly yellow_Foreground = "\x1b[33m"; + static readonly blue_Foreground = "\x1b[34m"; + static readonly magenta_Foreground = "\x1b[35m"; + static readonly cyan_Foreground = "\x1b[36m"; + static readonly white_Foreground = "\x1b[37m"; + static readonly gray_Foreground = "\x1b[90m"; + + static readonly black_Background = "\x1b[40m"; + static readonly red_Background = "\x1b[41m"; + static readonly green_Background = "\x1b[42m"; + static readonly yellow_Background = "\x1b[43m"; + static readonly blue_Background = "\x1b[44m"; + static readonly magenta_Background = "\x1b[45m"; + static readonly cyan_Background = "\x1b[46m"; + static readonly white_Background = "\x1b[47m"; + static readonly gray_Background = "\x1b[100m"; + +} \ No newline at end of file diff --git a/node/log/RJLog.ts b/node/log/RJLog.ts new file mode 100644 index 0000000..b401054 --- /dev/null +++ b/node/log/RJLog.ts @@ -0,0 +1,135 @@ + +import { RegExpUtility } from "../../browser/text/RegExpUtitlity"; +import { LogColors } from "./LogColors"; + +let pr = ( window as any ).process; + +export class RJLog +{ + static readonly errorColor = LogColors.red_Background; + static readonly errorMessageColor = LogColors.red_Foreground; + static readonly warnColor = LogColors.yellow_Background; + static readonly logColor = LogColors.gray_Background + static readonly resetColor = LogColors.reset; + + static readonly matcherWithFunction = /^\s+at\s(.+)\s\(.+?:(\d+:\d+)\)/; + static readonly matcherFile = /\(.+?\\(\w+)\.js:(\d+:\d+)\)/; + static readonly matcherAnonymous = /^\s+at\s(.+)\s\((.+)\)/; + + static readonly logAlwaysLineInfo = true; + + + static _parseLineResult( line:string ):RegExpExecArray + { + let result = RJLog.matcherWithFunction.exec( line ) || + RJLog.matcherFile.exec( line ) || + RJLog.matcherAnonymous.exec( line ); + + return result; + } + + static _parseLine( line:string ):string + { + let result = RJLog._parseLineResult( line ); + + if ( result ) + { + return " " + result[ 1 ] + "(" + result[ 2 ] + ") "; + } + + return line; + } + + static logError( e:Error ) + { + console.log( "\n" + RJLog._formatErrorMessage( e.stack ) ); + } + + static _formatErrorMessage( stackTrace:string, color:string = RJLog.errorMessageColor ) + { + let lines = RegExpUtility.splitLines( stackTrace ); + let output:string[] = [ color ]; + + lines.forEach( + ( line, index ) => + { + let lineInfo = RJLog._parseLine( line ); + + output.push( lineInfo ); + + if ( index !== lines.length - 1 ) + { + output.push( "\n" ); + } + + } + ) + + output.push( RJLog.resetColor ); + + return output.join( "" ); + } + + static getLineInfo( color:string = RJLog.logColor, stackTrace?:string, lineIndex:number = 3 ) + { + stackTrace = stackTrace || ( new Error().stack + "" ); + + let lines = RegExpUtility.splitLines( stackTrace ); + + let result:RegExpExecArray = null; + + while ( ! result && lineIndex < lines.length ) + { + let line = lines[ lineIndex ]; + + result = RJLog._parseLineResult( line ) ; + + lineIndex ++; + } + + + if ( ! result ) + { + console.log( stackTrace ); + return color + " " + RJLog.resetColor ; + } + + return color + " " + result[ 1 ] + "(" + result[ 2 ] + ") " + RJLog.resetColor; + } + + + static error( ...params:any[] ) + { + if ( RJLog.logAlwaysLineInfo || typeof pr === "object" ) + { + let lineInfo = RJLog.getLineInfo( RJLog.errorColor ); + console.log( "\n" + lineInfo ); + } + + console.error.apply( console, params ); + + } + + static warn( ...params:any[] ) + { + if ( RJLog.logAlwaysLineInfo || typeof pr === "object" ) + { + let lineInfo = RJLog.getLineInfo( RJLog.warnColor ); + console.log( "\n" + lineInfo ); + } + + console.warn.apply( console, params ); + } + + + static log( ...params:any[] ) + { + if ( RJLog.logAlwaysLineInfo || typeof pr === "object" ) + { + let lineInfo = RJLog.getLineInfo(); + console.log( "\n" + lineInfo ); + } + + console.log.apply( console, params ); + } +} \ No newline at end of file diff --git a/node/tsconfig.json b/node/tsconfig.json index f2e3a69..c15a3d2 100644 --- a/node/tsconfig.json +++ b/node/tsconfig.json @@ -1,7 +1,10 @@ { - "compilerOptions": { + "compilerOptions": + { "lib": ["ES2020"], "types": ["node"], // Includes Node.js types }, + + "include": ["./*"] } \ No newline at end of file