/*
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' });
}