/* CATLAIR_LICENCE_TEXT */ /* Default JS script for Pusa on Catlair. https://pusa.dev Script must be loaded with "defer" attribute. Warning! This file is not recommended to be modified. */ /* CATLAIR_LICENCE_TEXT */ /* The Pusa It is the front-back of Pusa core. It works with PusaBack over ajax. ┌──>── Request( URL,Element,Event,GET,POST ) ──>──┐ │ │ ╔═════╧═════╗ ╔═════╧════╗ ║ PusaFront ║ (ajax) ║ PusaBack ║ ╚═════╤═════╝ ╚═════╤════╝ │ │ └──<────── Responce( commands and content ) ───<──┘ */ var JSON_TYPE = 'JSON'; /* Debug level */ var DEBUG_OFF = 'Off'; var DEBUG_ON = 'On'; class TPusa { constructor() { this.Events = []; /* Event timers accumulator. Contain actual timer for each event. */ this.Requests = []; this.Groups = []; /* List of groups */ this.Debug = DEBUG_OFF; this.LastRequests = []; /* Antispam events history. Each event registrated in this array. */ this.EventTimeouts = []; /* Timeots in milliseconds for event { mousmove: 1000 }*/ this.Pile = []; /* Pile of stings for *Pile* methods */ this.CountJob = 0; /* Count Pusa jobs for indicator */ this.Arguments = []; /* Array of arguments */ this.Focus = []; } /* Create and return new Pusa */ static Create() { if( !window.PusaState ) { window.PusaState = {}; /* Create the Pusa state */ window.PusaObjects = {}; /* List of classes and objects with public propertyes */ } return new TPusa(); } /* The Pusa request. 1. Recive array of parameters. 2. Create timer for controlling events 3. Send event to back-end. */ Request ( APrm /* Named array of arguments. Look at TPusa.Send method */ ) { if( MatchData( APrm.Filter, APrm.Event ) ) { if( APrm.Timeout ) { /* The event timer start for sending */ var Key = ( APrm.Event ? APrm.Event.type + '.' : '' ) + APrm.Class + '.' + APrm.Method + '.' + ( APrm.Element && APrm.Element.id ? APrm.Element.id + '.' : '' ) + ( APrm.Element && APrm.Element.className ? APrm.Element.className + '.' : '' ) ; if( this.Events[ Key ] ) { clearInterval( this.Events[ Key ]); } this.Events[ Key ] = setTimeout ( () => this.Send( APrm ), APrm.Timeout ); } else { /* Direct send */ this.Send( APrm ); } } return this; } /* Send Pusa request */ Send ( APrm /* Array of parametes: string Event - DOM Event from event element; string Element - DOM element, called the event; string Class - Pusa class controllername for call; string Method - Method name in Pusa controller; int Timeout - Request Timeout in milliseconds. 0 - the request will be sent immeidiately; array Events - Array of rules with name of XMLHttpRequest evends and callback class and method for pusa: string Event - name of event ( loadend, progress, error etc ); string Class - Pusa class controller name for call back; string Method - Method name in Pusa controller for call back. */ ) { var EventName = APrm.Event ? APrm.Event.type : null; var IDRequest = APrm.IDRequest ? APrm.IDRequest : clGUID(); var Data = null; /* Create FormData object */ var AttributesString = null; /* Strint element attributes */ var ElementString = null; /* String element propertyes */ var Data = new FormData(); /* Create the Request */ var Request = this.RequestNew ({ Element : APrm.Element, EventName : EventName, ID : IDRequest }); if( Request ) { /* Set requested arguments */ if( APrm.Arguments ) { for( let Key in APrm.Arguments ) { Data.append ( Key, ( typeof APrm.Arguments[ Key ] === 'object' ) ? JSON.stringify( APrm.Arguments[ Key ]) : APrm.Arguments[ Key ] ) } } /* Build income parameters */ Data.append( 'IDRequest' , IDRequest ); Data.append( 'TypeContent' , JSON_TYPE ); Data.append( 'Pusa' , APrm.Class ); Data.append( 'Method' , APrm.Method ); Data.append( 'URL' , window.location.href ); /* Get class */ let Class = window.PusaObjects[ APrm.Class ]; if( Class ) { Data.append ( 'Propertyes', JSON.stringify( Class ) ); } /* If element exists in parameters then collect element attributes for request */ if( APrm.Element && APrm.Element instanceof File ) { Data.append( 'Blob', APrm.Element ); } Request.Self = APrm.Element; /* Set users callback for Request events */ if( APrm.Events ) { APrm.Events.forEach ( Rule => { let EventName = Rule[ 'Event' ]; let Class = Rule[ 'Class' ]; let Method = Rule[ 'Method' ]; if( EventName && Class && Method ) { Request.upload.addEventListener ( EventName, e => { this.Request ({ Event : e, /* Current event from XMLHttpRequest*/ Class : Class, /* Class from current rule */ Method : Method, /* Method from current rule */ Element : APrm.Element }) } ); } } ) } /* Set callback for request */ Request.addEventListener ( 'loadend', Event => { this.JobEnd(); this.RequestRemove( Request ); /* Get pure content */ var Content = Event.target.response; var Result = null; /* Define type content */ try { Result = JSON.parse( Content ); Request.Result = Result; } catch( e ) { this.Warning( 'Error in JSON result' ); } if( Result && Result.Header ) { /* Processing Pusa answer */ this.Run ( Request.Result.Pusa, /* Send commands */ Request.Self, /* Send self element */ null, /* No focus elements */ Event, /* Current load end event */ APrm /* Request parameters for debuging */ ); /* Write error message to log if message is exists */ if( Result.Header.Code != 'Ok' ) { this.Warning( Result.Header.Message ); } } else { this.Warning( 'Unknown answer.' ); } } ); /* Set callback for the request error event */ Request.addEventListener ( 'error', () => { this.JobEnd(); this.Warning( 'Pusa request error' ); this.RequestRemove( Request ); } ); /* Set callback for the request abort event */ Request.addEventListener ( 'abort', () => { this.RequestRemove( Request ); } ); this.Info( 'Pusa AJAX call ' + APrm.Class + '.' + APrm.Method ); this.JobBegin(); /* Send request with Data form and URL */ Request.open( 'POST', '?pusa=' + APrm.Class + '.' + APrm.Method ); Request.send( Data ); } return this; }; /* Select elements for focus */ SelectElements ( ASelf, /* Self element */ AFrom, /* Array of DOM elements for start searching */ APrm /* Searching argumets */ ) { var Result = []; /* The pushing element subfunction in to focus list if element is not exists in focus list. */ function Push( AElement ) { if( Result.indexOf( AElement ) == -1 ) { Result.push( AElement ); } } /* Заполнение From текущим элементов если массив пуст */ AFrom = AFrom ? AFrom : [ ASelf ]; switch( APrm.Target ) { /* Set the self element as focus. It will be the only focus element */ case 'Self': if( ASelf ) Push( ASelf ); break; /* Set DOM document.body as focus. It will be the only focus element */ case 'Body': Push( window.document.body ); break; /* Set window as focus. It will be the only focus element */ case 'Window': Push( window ); break; /* Each focus element set its parent as focus */ case 'Parent': for( var i in AFrom ) { Push( AFrom[ i ].parentNode ); } break; case 'Parents': for( var i in AFrom ) { AFrom[ i ].ParentsFilter( APrm.Filter, e => Push( e )); } break; case 'ParentFirst': for( var i in AFrom ) { AFrom[ i ].ParentFilter( APrm.Filter, e => Push( e )); } break; case 'Children': for( var i in AFrom ) { AFrom[ i ].Children( APrm.Filter, e => Push( e )); } break; case 'ChildrenOfThis': for( var i in AFrom ) { AFrom[ i ].Children( APrm.Filter, e =>( e ), 1 ); } break; case 'ChildFirst': for( var i in AFrom ) { let e = AFrom[ i ].fistChild; if( e ) Push( e ); } break; case 'ChildLast': for( var i in AFrom ) { let e = AFrom[ i ].lastChild; if( e ) Push( e ); } break; case 'Last': for( var i in AFrom ) { Push( AFrom[ i ].parentNode.lastChild ); } break; case 'First': for( var i in AFrom ) { Push( AFrom[ i ].parentNode.firstChild ); } break; case 'Prev': for( var i in AFrom ) { let e = AFrom[ i ].previousSibling; if( !e ) e = AFrom[ i ].parentNode.lastChild; Push( e ); } break; case 'Next': for( var i in AFrom ) { let e = AFrom[ i ].nextSibling; if( !e ) e = AFrom[ i ].parentNode.firstChild; Push( e ); } break; } return Result; } /* Processing the pusa commands */ Run ( ACommands, /* Array Pusa commands list */ ASelf, /* Self element */ AFocus, /* List of focus elements */ AEvent, /* Event object for Self element */ ARequest /* Parent request */ ) { var that = this; /* That pointer at this for SetFocus and CommandWarning functions */ ASelf = ASelf ? ASelf : document.body; var Focuses = []; /* Array of focus lists for FocusStore FocusRestore */ var Focus = AFocus ? AFocus : [ ASelf ]; /* Array of focus objects */ var Index = 0; /* Display command warning at console */ function CommandWarning( AMessage ) { that.Warning ( AMessage + '.' + ( ARequest && ARequest.Class ? ' Class [' + ARequest.Class : '' ) + ']' + ( ARequest && ARequest.Method ? ' Method [' + ARequest.Method : '' ) + ']' + ' Directive [' + Index + '] ' + JSON.stringify( ACommands[ Index ]) ); } function SetFocus( $AFocus ) { Focus = $AFocus; } function FocusIsFull() { if( Focus.length == 0 ) { CommandWarning( 'Focus is empty' ); } return Focus.length != 0; } /* Loop for the list of commands received from Pusa-Back */ ACommands.forEach ( Line => { /* Get new command */ var Command = Line[ 0 ] ? Line[ 0 ] : ''; /* Get arguments for command */ var Prm = Line[ 1 ] ? Line[ 1 ] : {}; switch( Command ) { default: CommandWarning( 'Unknown directive' ); break; /* Receive and output message to console */ case 'Debug': this.Debug = Prm[ 'Level' ]; break; /* Store copy of current focus list to buffer */ case 'FocusPush': Focuses.push( Focus.slice() ); break; /* Restore focus list from buffer */ case 'FocusPop': if( Focuses.length > 0 ) { if( Prm[ 'Skip' ]) { Focuses.pop(); } else { Focus = Focuses.pop(); } } else { Focus = []; CommandWarning( 'Focus stack is empty' ); } break; /* Set property for Focus */ case 'Set': if( Prm.Values && FocusIsFull()) { Focus.forEach ( e => { if( typeof e === 'object' ) { for( let Key in Prm.Values ) { /* Set property to argument list */ e[ Key ] = Prm.Values[ Key ]; } } } ); } break; /* Move value from JS, Focus property or Focus method to arguments list */ case 'Arg': /* Set static values to argument list */ if( Prm.Statics ) { for( let Key in Prm.Statics ) { this.Arguments[ Key ] = Prm.Statics[ Key ]; } } /* Read fields form form or files as arguments */ if( Prm.Fields && FocusIsFull() ) { Focus.forEach ( e => { if( typeof e === 'object' ) { /* Send form */ if( e.tagName == 'FORM' ) { for( let i = 0; i < e.elements.length; i++ ) { let Field = e.elements[ i ]; let Key = Field.name; switch( Field.type) { case 'checkbox': this.Arguments[ Key ] = Field.checked; break; case 'file': /* Files upload */ if( Prm.Class && Prm.Method ) { let IDGroup = clGUID(); this.Arguments[ Key ] = this.SendFiles ( Field, IDGroup, Prm.Class, Prm.Method, Prm.Command ); this.Arguments[ 'IDGroup' ] = IDGroup; } else { CommandWarning( 'No class or method for files upload' ); } break; case 'radio': if( Field.checked ) { this.Arguments[ Key ] = Field.value; } break; case 'button': /* Skip the button */ break; default: this.Arguments[ Key ] = Field.value; break; } } /* Elements loop */ } /* Is Form */ } } ); } if( Prm.Name ) { if( Prm.JS ) { /* Set JS code result to argument list */ this.Arguments[ Prm.Name ] = eval( Prm.JS ); } /* Read argument from event if this exists */ if( Prm.Event && AEvent ) { this.Arguments[ Prm.Name ] = AEvent[ Prm.Event ]; } if(( Prm.Property || Prm.Method ) && FocusIsFull() ) { Focus.forEach ( e => { if( typeof e === 'object' ) { if( Prm.Property ) { /* Set property to argument list */ this.Arguments[ Prm.Name ] = e[ Prm.Property ]; } if( Prm.Method && typeof e[ Prm.Method ] === 'function' ) { /* Set method result to argument list */ this.Arguments[ Prm.Name ] = e[ Prm.Method ].apply( e, Prm.Arguments ); } else { CommandWarning( 'Method not exists' ); } } } ); } } break; /* Call JS method for focus elements */ case 'Call': if( Prm.Method && FocusIsFull() ) { Focus.forEach ( e => { if ( typeof e == 'object' && e[ Prm.Method ] && typeof e[ Prm.Method ] == 'function' ) { /* Set method result to argument list */ e[ Prm.Method ].apply( e, Prm.Arguments ); } } ); } break; /* Execute code js */ case 'JS': eval( Prm ); break; case 'Loop': if( Prm.Tasks && typeof Prm.Tasks === 'object' && Prm.Commands ) { Prm.Tasks.forEach ( r => { /* Create new commands form commands template */ var Commands = JSON.parse( JSON.stringify( Prm.Commands )); /* Replace in commands */ ReplaceInObject( Commands, r ); /* Execute commands with current event object */ TPusa.Create().Run ( Commands, /* Array of commands */ null, /* No self element */ Focus, /* Set current focus */ AEvent /* Set current event */ ); } ) } break; /* Set event on focus elements */ case 'Event': /* Set default values */ if( !Prm.Event ) Prm.Event = 'click'; if( !Prm.Timeout ) Prm.Timeout = 0; if( FocusIsFull() ) { /* Add listeners for focused DOM elements */ Focus.forEach ( e => { if( typeof e === 'object' ) { if( e.ReplaceEventListener ) { e.ReplaceEventListener ( Prm.Event, event => { /* Event clousure */ /* Call commands */ if( MatchData( Prm.Filter, event )) { /* Execute Pusa commands if exists */ let Arguments = []; if( Prm.Commands ) { Arguments = TPusa.Create() .Run( Prm.Commands, e, null, event ) .Arguments; } /* Send callback to PusaBack */ if( Prm.Class && Prm.Method && Prm.Event ) { this.Request ({ Event : event, Class : Prm.Class, Method : Prm.Method, Timeout : Prm.Timeout, Element : e, Filter : Prm.Filter, Arguments : Arguments }); } /* Call JS */ if( Prm.JS ) { eval( Prm.JS ); } } /* Stop floating event in DOM if catch is true */ if( Prm.Catch ) event.stopPropagation(); } ); } else { e.addEventListener ( Prm.Event, event => { /* Start closure */ let Arguments = []; if( Prm.Commands ) { Arguments = TPusa.Create() .Run( Prm.Commands, e, null ) .Arguments; } this.Request ({ Event : event, Class : Prm.Class, Method : Prm.Method, Timeout : Prm.Timeout, Element : e, Filter : Prm.Filter, Arguments : Arguments }) } ); } } /* e is object */ } ); } /* Focus is full */ break; /* Select and focus to DOM elements from current focus elements */ case 'Focus': if( Prm.Target ) { SetFocus ( this.SelectElements ( ASelf, Focus, { Target : Prm.Target, Filter : Prm.Filter } ) ); } /* Move property from Focus objects to Focus objects */ if( ( Prm.Property || Prm.Method ) && FocusIsFull() ) { var NewFocus = []; Focus.forEach ( e => { if( Prm.Property && typeof e === 'object' && e[ Prm.Property ] ) { /* Set property to focus list */ NewFocus.push( e[ Prm.Property ] ); } if( Prm.Method && typeof e === 'object' && e[ Prm.Method ] ) { /* Set method result to focus list */ NewFocus.push( e[ Prm.Method ].apply( e, Prm.Arguments )); } } ); SetFocus( NewFocus ); } break; /* Create DOM elements Elements will be added if does not exists with the passed ID */ case 'Create': if( FocusIsFull() ) { /* New elements array */ var NewElements = []; /* Loop for focused elements */ Focus.forEach ( e => { /* let Exists = e.Children( [ '=', Prm.ID, '@id' ], null, 1 ); */ let Exists = []; if( Exists.length == 0 ) { /* Create new element. */ var New = document.createElement( Prm.TagName ); /* Set default ID */ NewElements.push( New ); switch( Prm.Position ) { default: case 'Last' : e.append( New ); break; case 'First' : e.prepend( New ); break; case 'Before' : e.before( New ); break; case 'After' : e.after( New ); break; } } else { Exists.forEach( l => NewElements.push( l ) ); } } ); /* Set focus to the selected elements */ Focus = NewElements; } break; /* Conditions */ case 'If': let Condition = null; switch( Prm.Condition ) { case 'FocusIsEmpty': Condition = Focus.length == 0; break; default: CommandWarning( 'Unknown condition' ); break; } if( Condition === true && Prm.True ) { Focus = TPusa.Create().Run( Prm.True, ASelf, Focus, AEvent ).Focus; } if( Condition === false && Prm.False ) { Focus = TPusa.Create().Run( Prm.False, ASelf, Focus, AEvent ).Focus; } break; /* Timer */ case 'Timer': if( FocusIsFull() ) { Focus.forEach ( e => { /* Default timer name */ let TimerName = 'Timer.' + Prm.ID + '.' + Prm.Class + '.' + Prm.Method; /* Clear timeout if exists */ if( e[ TimerName ]) { clearTimeout( e[ TimerName ] ); } if( ! Prm.Stop ) { if( Prm.Timeout ) { /* Set timeout */ e[ TimerName ] = setTimeout ( () => { /* Execute Pusa commands if exists */ let Arguments = []; if( Prm.Commands ) { Arguments = TPusa.Create() .Run( Prm.Commands, e, null, AEvent ) .Arguments; } if( Prm.Class && Prm.Method ) { this.Send ({ Element : e, Class : Prm.Class, Method : Prm.Method, Events : [], Arguments : Arguments }); } }, Prm.Timeout ) } } } ); } break; /* Replace any text in element attributes */ case 'Replace': if( FocusIsFull()) { Focus.forEach ( e => { /* Attributes loop */ for( var i = 0; i < e.attributes.length; i++ ) { var s = e.attributes[i].value; /* Replace loop */ for( var Key in Prm.Values ) { var r = new RegExp( Key ); s = s.replace( r, Prm.Values[ Key ]); } e.attributes[i].value = s; } } ); } break; /* Set CSS attributes */ case 'CSSAttr': clCSSRuleBySelector ( Prm.Name, ( Rule ) => { for( var Key in Prm.Values ) { Rule.style[ Key ] = Prm.Values[ Key ]; } } ); break; /* */ case 'PileFrom': if( Prm.Name ) { /* Set value to pile */ if( Prm.Value ) { this.WritePile( Prm.Name, Prm.Value, Prm.Operator ); } /* Set focus property to pile */ if( Prm.Property && FocusIsFull()) { Focus.forEach ( e => { this.WritePile( Prm.Name, e[ Prm.Property ], Prm.Operator ); } ); } /* Set focus property to pile */ if( Prm.Method && FocusIsFull()) { Focus.forEach ( e => { if( Prm.Method && e[ Prm.Method ]) { this.WritePile ( Prm.Name, e[ Prm.Method ].apply( e, Prm.Arguments ), Prm.Operator ); } } ); } } break; /* Write pile content to focus property elements or like an method argument */ case 'PileTo': if( Prm.Name && FocusIsFull() ) { Focus.forEach ( e => { if( Prm.Property ) { e[ Prm.Property ] = this.Pile[ Prm.Name ]; } if( Prm.Method && e[ Prm.Method ]) { e[ Prm.Method ].apply( e, [ this.Pile[ Prm.Name ]]); } } ); } break; /* */ case 'PileEqual': if( FocusIsFull() ) { Focus.forEach ( e => { if( this.Pile[ Prm.Name ] == Prm.Value ) { TPusa.Create().Run( Prm.Equal, e, null, AEvent ); } else { TPusa.Create().Run( Prm.NotEqual, e, null, AEvent ); } } ); } break; /* Read state to arguments */ case 'ReadState': let State = window.PusaState[ Prm.Group ]; if( State ) { for( let Line in State ) { this.Arguments[ Line ] = State[ Line ]; } } break; /* Set state */ case 'SetState': let Key = Prm.ID ? Prm.ID : Prm.Class; if( !window.PusaObjects[ Key ]) { /* Create new object by id */ window.PusaObjects[ Key ] = { Class : Prm.Class, Public : {} }; } for( let Value in Prm.Values ) { /* Wirite property */ window.PusaObjects[ Key ][ 'Public' ][ Value ] = Prm.Values[ Value ]; } break; } Index++; } ); /* Set focus for Pusa */ this.Focus = Focus; return this; } /* Write to pile any content */ WritePile ( AName, /* Name of pile */ AValue, /* Content for writing */ AOperator /* Writing operator add set */ ) { switch( AOperator ) { case 'Set': this.Pile[ AName ] = AValue; break; case 'Add': this.Pile[ AName ] += AValue; break; } return this; } /* Send files from file element */ SendFiles ( AElement, /* File HTML element */ AIDGroup, /* */ AClass, /* Class for call */ AMethod, /* Method for call */ ACommands ) { let Files = []; let c = AElement.files.length; for( let i = 0; i < c; i++ ) { let IDFile = clGUID(); let File = AElement.files[ i ]; let Arguments = TPusa.Create().Run( ACommands ).Arguments; Arguments.IDGroup = AIDGroup; Arguments.Count = c; /* Send file */ this.Send ({ Element : File, Class : AClass, Method : AMethod, IDRequest : IDFile, Arguments : Arguments }); Files.push( IDFile ); } return Files; } /************************************************************************** Requests */ /* Create new XMLHTTPRequest and store it in to global array Requests */ RequestNew( APrm ) { var Result = null; if( this.RequestCheck( APrm.EventName )) { /* Close all previous request for current parameter without ID */ this.RequestsClose ({ EventName : APrm.EventName, Element : APrm.Element, IDGroup : APrm.IDGroup }); /* Create new request */ Result = new XMLHttpRequest(); /* Set parameters for new request */ Result.Params = APrm; /* Push new request in to list */ this.Requests.push( Result ); } return Result; } /* Antispam check request by event name */ RequestCheck( AEventName ) { var Result = true; var Now = Date.now(); /* Define antispam timeout */ AEventName = AEventName ? AEventName : 'any'; var Timeout = this.EventTimeouts[ AEventName ]; if( !Timeout ) Timeout = 0; /* Check antispam timeout */ var LastRequest = this.LastRequests[ AEventName ]; if( LastRequest && LastRequest + Timeout > Now ) { Result = false; } else { this.LastRequests[ AEventName ] = Now; } return Result; } /* Close requests by params */ RequestsClose( APrm ) { var Requests = this.RequestsFind( APrm ); for( Request of Requests ) Request.abort(); return true; } /* Return list of Requests by conditions */ RequestsFind( APrm ) { var Result = []; for( Request of this.Requests ) { if ( ( !APrm.Element || APrm.Element && ( APrm.Element == Request.Params.Element )) && ( !APrm.EventName || APrm.EventName && ( APrm.EventName == Request.Params.EventName )) && ( !APrm.IDGroup || APrm.IDGroup && ( APrm.IDGroup == Request.Params.IDGroup )) && ( !APrm.ID || APrm.ID && ( APrm.ID == Request.Params.ID )) ) { Result.push( Request ); } } return Result; } /* Remove XMLHTTPRequest from request list */ RequestRemove( ARequest ) { let Index = this.Requests.indexOf( ARequest ); this.Requests.splice( Index, 1 ); return this; } /************************************************************************** Loggger and jobs */ Info( AMsg ) { if( this.Debug == DEBUG_ON ) { console.info( AMsg ); } return this; } Warning( AMsg ) { if( this.Debug == DEBUG_ON ) { console.warn( AMsg ); } return this; } /* Create or existing return visual indicator */ GetIndicator() { var Result = document.getElementById( 'clIndicator' ); if( !Result ) { /* Create job indicator */ Result = document.createElement( 'div' ); Result.id = 'clIndicator'; Result.className = 'PostIndicator'; Result.style.display = 'none'; Result.style.opacity = '0'; document.body.append( Result ); } return Result; } /* Begin job. Start new job and enable indicator. */ JobBegin() { var Indicator = this.GetIndicator(); this.CountJob++; if( this.CountJob == 1 && Indicator.Timer == null ) { Indicator.Timer = setTimeout ( () => { Indicator.style.display = null; Indicator.style.opacity = '1'; Indicator.Timer = null; }, 500 ); } Indicator.innerHTML = this.CountJob; return this; } /* Job end. End currentn job and dec indicator. */ JobEnd() { var Indicator = this.GetIndicator(); this.CountJob--; Indicator.innerHTML = this.CountJob; if( this.CountJob < 1) { /* Hide indicator*/ Indicator.style.display = 'none'; Indicator.style.opacity = '0'; this.CountJob = 0; /* Clear timer if it was set */ if( Indicator.Timer ) { clearTimeout( Indicator.Timer ); Indicator.Timer = null; } } return this; } } /* CATLAIR_LICENCE_TEXT */ /* This module contains Pusa methods. Все функции являются копиями имеющихся модулей. При внесении изменений в любую из частей pusa_inline.js необходимо вносить изменения в основную библитеку откуда был заимстован код. still@itserv.ru */ /* Return GUIDform identifier */ function clGUID() { return 'xxxxxxxx-xxxx-xxxx-yxxx-xxxxxxxxxxxx'.replace ( /[xy]/g, function( c ) { var r = Math.random()*16|0, v = c == 'x' ? r : (r&0x3|0x8); return v.toString( 16 ); } ); } /* clString.js */ /* Перебирает все свойства в массиве и возвращает только значения - boolean - number - string - symbol Исключаются пустые значения, массивы объекты и прочее. */ function GetValuesOnly ( AObject, /* Объект для обхода */ AInclude, /* Массив имен включаемых значений. Невходящие будут исключены. */ AExclude /* Массив имен исключаемых значений. Входящие будут исключены. */ ) { var Result = {}; if( !AInclude ) AInclude = []; if( !AExclude ) AExclude = []; for( var Key in AObject ) { if ( Key != 'webkitStorageInfo' /* Chromium creates the warning message */ ) { try { switch( typeof( AObject[ Key ] )) { case 'boolean': case 'number': case 'string': case 'symbol': if ( ( AInclude.length == 0 || AInclude.indexOf( Key ) > -1 ) && ( AExclude.length == 0 || AExclude.indexOf( Key ) == -1 ) ) { Result[ Key ] = AObject[ Key ]; } break; } } catch( e ) { /* selectionStart attribute rase exception at IOS */ } } } return Result; } /* Find parent */ Element.prototype.ParentFilter = function ( AFilter, /* Finding value by selector */ ACallback /* Cllback for each parent */ ) { var Result = null; var e = this; while( e && e.nodeType === 1 && !Result ) { if( MatchData( AFilter, e ) ) { Result = e; if( ACallback ) ACallback( Result ); } e = e.parentNode; } return Result; }; /* Find parents */ Element.prototype.ParentsFilter = function ( AFilter, /* Finding value by selector */ ACallback /* Cllback for each parent */ ) { var Result = []; var e = this; while( e && e.nodeType === 1 ) { if( MatchData( AFilter, e ) ) { Result.push( e ); } e = e.parentNode; } if( ACallback ) Result.forEach( e => ACallback( e ) ); return Result; }; /* clDOM.js */ /* DOM recursion for children */ Element.prototype.ChildRecursion = function( AParams, AMaxDepth, ADepth ) { AMaxDepth = AMaxDepth ? AMaxDepth : 0; ADepth = ADepth ? ADepth : 0; if ( AParams.OnBefore ) AParams.OnBefore( this, AParams ); if( AMaxDepth == 0 || AMaxDepth > ADepth ) { for (var i in this.childNodes ) { var iNode=this.childNodes[i]; if (iNode.nodeType == 1) iNode.ChildRecursion( AParams, AMaxDepth, ADepth+1 ); } } if ( AParams.OnAfter ) AParams.OnAfter( this, AParams ); return this; }; /* Find children */ Element.prototype.Children = function ( AFilter, /* Finding value by selector */ ACallback, /* Cllback for each parent */ AMaxDepth ) { var Result = []; AMaxDepth = AMaxDepth ? AMaxDepth : 0; this.ChildRecursion ( { OnBefore: e => { if( MatchData( AFilter, e )) { Result.push( e ); } } }, AMaxDepth ); if( ACallback ) Result.forEach( e => ACallback( e ) ); return Result; }; /* */ Element.prototype.ReplaceEventListener = function ( AEvent, /* Event name */ AFunction /* Callback function */ ) { /* Remove */ if( this.LastEventListener ) { /* Remove previous listener */ this.removeEventListener( AEvent, this.LastEventListener ); } /* Store event listener for removing in future */ this.LastEventListener = AFunction; /* Add new listener */ this.addEventListener( AEvent, AFunction ); return this; }; /* Call ACallback for CSS rule by selector name https://developer.mozilla.org/en-US/docs/Web/API/Document/styleSheets */ function clCSSRuleBySelector( ASelector, ACallback ) { if( ACallback ) { for( const Sheet of document.styleSheets ) { for( const Rule of Sheet.rules ) { if( Rule.selectorText == ASelector ) { ACallback( Rule ); } } } } } function MatchData ( AFilter, /* Conditions in format [ 'Operator', Arguments, ... ] */ AData /* array of named values */ ) { /* Return simple value */ return Match ( AFilter, function( AArg ) { let Result = AArg; if( AArg.substr( 0,1 ) == '@' ) { let Key = AArg.slice( 1 ); Result = AData[ Key ]; } return Result; } ); } /* Match conditions in data. Return true or false if AData are matcing AFilter Filter example: (0) true == true [ '=', true, true ] (1) Data['DataKey'] == 'Value' [ '=', '@DataKey', 'Value' ] (2) Data[ 'Key1' ] == 'Value1' && Data[ 'Key2' ] != 'Value2' [ '&&', [ [ '=', '@Key1', 'Value1' ], [ '!=', 'Key2', 'Value2' ] ] ] */ function Match ( AFilter, /* Conditions in format [ 'Operator', Arguments, ... ] */ ACallback /* callback function function(AArg){return AArg;}*/ ) { /* Value check */ let Val = function( AArg ) { let Result = AArg; switch( typeof( AArg )) { case 'string': Result = ACallback ? ACallback( AArg ) : AArg; break; case 'object': Result = Match( AArg, ACallback ); break; } return Result; }; let Result = false; if( AFilter ) { let Operator = AFilter[ 0 ]; /* Prepare parameters */ switch( Operator ) { case '=': case '<>': case '>': case '<': case '<=': case '>=': case 'in': case '+': case '-': case '*': case '/': var a1 = Val( AFilter[ 1 ] ); var a2 = Val( AFilter[ 2 ] ); break; } /* Do operation with parameters and operator */ switch( Operator ) { case 'in' : Result = a2.split( ' ' ).indexOf( a1 ) > -1; break; case '=' : Result = a1 == a2; break; case '!=' : Result = a1 != a2; break; case '>' : Result = a1 > a2; break; case '<' : Result = a1 < a2; break; case '<=' : Result = a1 <= a2; break; case '>=' : Result = a1 >= a2; break; case '+' : Result = a1 + a2; break; case '-' : Result = a1 - a2; break; case '*' : Result = a1 * a2; break; case '/' : Result = a1 / a2; break; case 'or': Result = false; for( let i = 1; i < AFilter.length; i++ ) { Result = Result || Val( AFilter[i] ); } break; case 'and': Result = true; for( let i = 1; i < AFilter.length; i++ ) { Result = Result && Val( AFilter[i] ); } break; } } else { Result = true; } return Result; } function ReplaceInObject ( AObject, AValues ) { for( var Prop in AObject ) { var Value = AObject[ Prop ]; switch( typeof Value ) { case 'object': ReplaceInObject( Value, AValues ); break; case 'string': if( Value.indexOf( '%' ) >= 0 ) { let Content = AObject[ Prop ]; for( var Key in AValues ) { let Value = ( AValues[ Key ] === null ? '' : AValues[ Key ] ); Content = Content.replaceAll( '%' + Key + '%', Value ); } AObject[ Prop ] = Content; } break; } } } /* Move params from onbect in to DOM */ function clValuesFromObject( AObject, AConteiner, ACreate ) { for (var Key in AObject) { var Element = AConteiner.ChildByID( Key ); if( !Element && ACreate) { Element = document.createElement( 'input' ); Element.type = 'hidden'; Element.name = Key; AConteiner.append( Element ); } if (Element != null) { try { var KeyValue = decodeURIComponent( AObject[ Key ]); } catch (ex) { var KeyValue = null; } if ( KeyValue != null && KeyValue != 'null' ) { switch( Element.tagName ) { case 'INPUT': switch( Element.type ) { case 'checkbox': Element.checked = AObject[Key] == 'on' || AObject[Key] == 'true' || AObject[Key] == '1'; break; case 'text': Element.value = KeyValue; break; case 'hidden': Element.value = KeyValue; break; } break; case 'SELECT': Element.value = KeyValue; break; case 'TEXTAREA': Element.innerHTML = KeyValue; break; default: Element.innerHTML = KeyValue; break; } } } } } /* Check the container PusaDirectives. It can container the pusa directives from method Main.Init. You can create Main.Init at: site/[YOUR_SITE]/php/pusa/Main.php - for your Pusa controller otherwise it will be called: php/pusa/Main.php - default template for Pusa controller */ let StaticContainer = document.getElementById( 'PusaStaticCreated' ); if( StaticContainer ) { let StaticList = StaticContainer.innerHTML.split( ' ' ); StaticContainer.remove(); } let Container = document.getElementById( 'PusaDirectives' ); if( Container ) { /* Extract directives from tag PusaDirectives */ let Directives = JSON.parse( decodeURIComponent( Container.innerHTML)); /* Remove container with directives. It is no longer needed. */ Container.remove(); /* If the array of directives exists, it is run */ if( Directives ) { TPusa.Create().Run( Directives ); } } else { /* Request init method from Back It create new Pusa, and calls Pusa's method Main.Init after that. */ TPusa.Create().Request({ Class: 'Main', Method: 'Init' }); }