AltoBookingDeveloper

AltoBookingDeveloper

Unser Unternehmen entwickelt schlüsselfertige Websites und betreut diese für Mobiltelefone, Tablets und Computer.

 Wir nutzen moderne Technologien und bieten nicht nur die Entwicklung einfacher Webseiten – Visitenkarten oder Blogs – an, sondern auch vollwertige Online-Systeme mit Schnittstellen zu Zahlungsdiensten, Webdiensten und vielen weiteren notwendigen Komponenten wie Buchhaltung, Inhaltsverwaltung und Informationen über Geschäftsprodukte.

 Unsere langjährige Berufserfahrung ermöglicht es uns, Kundenprojekte effektiv umzusetzen. Wir entwickeln und veröffentlichen außerdem Open-Source-Projekte und unterstützen damit unsere geschätzten Kollegen sowie Einsteiger, die nach praktischen, sofort einsatzbereiten Lösungen suchen.

Unsere Leistungen

Wir unterstützen Sie dabei, Ihre Ideen präzise zu formulieren und alle erforderlichen Schritte umzusetzen – von der ersten Konzeptentwicklung bis hin zum vollständigen Projektabschluss.

Ganz gleich, ob Sie eine bestehende Website optimieren oder eine komplett neue Seite von Grund auf erstellen lassen möchten: Wir bieten Ihnen folgende Leistungen an:
  • Durchführung einer Website-Analyse zur Identifizierung von Problemen und – falls erforderlich – deren Behebung.
  • Entwicklung neuer Website-Bereiche – beispielsweise eines Kontaktformulars oder eines Impressums.
  • Optimierung der Suchmaschinenfreundlichkeit (SEO) Ihrer Website.
  • Falls Ihre Website derzeit einsprachig ist: Implementierung einer Sprachauswahl, einer mehrsprachigen Benutzeroberfläche sowie eines Content-Management-Systems, das die Verwaltung mehrsprachiger Inhalte ermöglicht.
  • Konzeption (einschließlich Funktionalität, Seitenstruktur und Benutzeroberfläche) sowie grafische Gestaltung einer neuen Website.
  • Entwurf und Implementierung von Datenbanken sowie von Modulen für eine benutzerfreundliche Verwaltung – insbesondere für das Content-Management der Website.
  • Implementierung von Systemen zur Besucherregistrierung und Authentifizierung, persönlichen Benutzerkonten, Kontaktformularen und ähnlichen Funktionen.
  • Entwicklung von E-Commerce-Komponenten, darunter:
    • Schnittstellenmodule für Payment-Gateways ( wir empfehlen Novalnet Innovation );
    • Module zur Workflow-Automatisierung – beispielsweise für die automatische Erstellung und den Versand von Auftragsbestätigungen und PDF-Rechnungen an Kunden;
    • B2B-Schnittstellen für Webservices ( z. B. Hotelbeds ).

Sollten Sie Interesse an unseren Angeboten haben – oder falls Sie die von Ihnen benötigte Leistung hier nicht explizit aufgeführt finden –, kontaktieren Sie uns bitte über den Bereich „Kontakt“.

Portfolio

Maßgeschneiderte Weblösungen, die überzeugen.

Online-Reisevermittlung Alto Booking
Hunde Hobbyzucht Beautiful Puppies
Tour operator Classic Travel
Tour operator (trademark Beriner Reisen) Berliner Reisen

Dies und das

Hier sind die Links zu unseren Open-Source-Projekten, die auf GitHub veröffentlicht sind.

CompactDOM ist eine JavaScript-Bibliothek (ein CompactDOM-Class), deren Methoden die Manipulation von DOM-Elementen vereinfachen. Das einzelne Zeichen _ dient als Alias für das CompactDOM-Objekt. Die aktuelle Version von CompactDOM kann von GitHub heruntergeladen werden. Ein CompactDOM-Tutorial finden Sie hier.
Nachfolgend ist der CompactDOM-Code für die index.js-Datei von dieser Website und zwei Codebeispiele in CompactDOM - ColorPicker и CrossPoint.

CompactDOM index.js-Datei von dieser Website
__.ready(() => {

	__.link("css/all.css,css/modal.css,css/button_to_up.css,css/selLanguages.css,themes/css/blackboard.css");

	const date = new Date();
	_(".footer-copy").content(`© ${date.getFullYear()} AltoBookingDeveloper. All rights reserved`);

	_("#modal").modal();
	__.scroll();

	var touch = __.env().tou;

	//----------------------------------
	__.laSelector({
		laSel:"a[href='#laSel']",
		laSelAct:"#laSelAct",
		laDef: _("#laSelAct").content().toLowerCase().substr(0, 2),
		dir:null,
		laListeFile:null
	});

	__.laSelector();

	//----------------------------------------------

	const refreshCaptcha = _("#refresh span");

	captchaRefresh = function(e){
		let et = e.type;
		let setClass = "refresh-hover";
		switch(et)
		{
			case "pointerover":
			case "mouseenter":
			case "touchstart":
			_(this).class(setClass, true);
			_(this).click(captchaRefresh);
			break;

			case "pointerout":
			case "mouseleave":
			case "touchend":
			_(this).class(setClass, false);
			break;

			case "click":
				const url = `captcha/image.php?t=${Date.now()}`
			    _("#captcha").attr(
			    {
			    	"src": url,
			    	"loading": "eager",
			    	"fetchPriority": "high"
			    });
				_("#code").val("");
				if(!touch)
					_(this).class(setClass, false);
			break;
		}
	};

	refreshCaptcha.mouseenter(captchaRefresh);
	refreshCaptcha.mouseleave(captchaRefresh);

	//-----------------------------------------------------
	const contactForm = _(".contact-form form");
	const code = _("#code");
	const email = _("#email");
	const bgFormFeldError = "background: rgba(255, 0, 0, 0.6)";

	_(".form-input").click(() => {code.css(false);});

	const formSubmitRsp = function(rsp, to, url){
		if(rsp)
		{
			if(rsp["captchaValid"] && rsp["emailValid"]){
				_(to).modal(`<div class=msg>${rsp["msg"]}</div>`);
				contactForm.d.reset();
				captchaRefresh();
			}
			else
			{
				if(!rsp["captchaValid"])
					code.css(bgFormFeldError);
			 	if(!rsp["emailValid"])
					email.css(bgFormFeldError);
			}
		}
	};

	var req =
	{
  		url:"?s=1",
  		method:"post",
  		responseType:"json",
  		to:"#modal",
  		func: formSubmitRsp
	};

	const formInput = _(".form-input,.form-textarea");

	formInput.click(function(){
		_(this).css(false);
	});


	const submitHandler = (e) => {
		e.preventDefault();
		// 	Form JS validation
		let valid = true;
		formInput.each((eL) => {
			let eLVal = eL.val();
			let expr = eL.attr("type")=="email"?"email":"text";
			switch (expr)
			{
			  case "email":
			  invalidBg = eLVal.match(
    			/^(([^<>()[\]\\.,;:\s@\"]+(\.[^<>()[\]\\.,;:\s@\"]+)*)|(\".+\"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/)==null?bgFormFeldError:false;
			  break;
			  case "text":
			  invalidBg = /\S/.test(eLVal)?false:bgFormFeldError;
			  break;
			}
			eL.css(invalidBg);
			if(valid)
				valid = !invalidBg;
		});

		//-------------------------------
		if(valid)
		{
			contactForm.send(req);
		}
	};

	contactForm.submit(submitHandler);

	//------------------------------------
	const video = _("video");
	video.each(function(vEl){
		vEl.d.onended = (e) => {
		    e.target.currentTime = 0;
	 		e.target.load();
		};
	});
//###############################################################

	const mobileToggle = _('#mobileToggle');
    const mobileMenu = _('#mobileMenu');
    const mobileMenuOverlay = _('#mobileMenuOverlay');
    const mobileMenuClose = _('#mobileMenuClose');
    const mobileMenuLinks = _('.mobile-menu-link');
    const navLinks = _('.nav-link');
    const body = _("body");

            // Function to open mobile menu
            function openMobileMenu() {
                mobileToggle.class('active',true);
                mobileMenu.class('active',true);
                mobileMenuOverlay.class('active',true);
                body.css('overflow:hidden'); // Prevent body scroll
            }

            // Function to close mobile menu
            function closeMobileMenu() {
                mobileToggle.class('active',false);
                mobileMenu.class('active',false);
                mobileMenuOverlay.class('active',false);
                body.css("overflow"); // Restore body scroll
            }

            // Toggle mobile menu when hamburger is clicked
            mobileToggle.click(function(e) {
                e.preventDefault();
                e.stopPropagation();

                if (mobileMenu.d.classList.contains('active')) {
                    closeMobileMenu();
                }
                else
                {
                    openMobileMenu();
                }
            });

            // Close mobile menu when close button is clicked
            mobileMenuClose.click(function(e) {
                e.preventDefault();
                closeMobileMenu();
            });

            // Close mobile menu when overlay is clicked
            mobileMenuOverlay.click(function() {
                closeMobileMenu();
            });

            // Close mobile menu when clicking on mobile menu links
            mobileMenuLinks.each(function(link){
                link.click(function() {
                    closeMobileMenu();

                    // Remove active class from all mobile links
                    mobileMenuLinks.each(function(l){ l.class('active',false);});
                    // Add active class to clicked link
                    _(this).class('active',true);

                    // Also update desktop nav active state
                    const href = _(this).attr('href');
                    navLinks.each(function(navLink){
                        navLink.class('active',false);
                        if (navLink.attr('href') === href) {
                            navLink.class('active',true);
                        }
                    });
                });
            });

            // Close mobile menu when clicking on desktop nav links
            navLinks.each(function(link){
                link.click(function() {
                    closeMobileMenu();

                    // Remove active class from all links
                    navLinks.each(function(l){l.class('active',false);});
                    // Add active class to clicked link (except CTA button)
                    if (!_(this).class('cta-button')) {
                        _(this).class('active',true);

                        // Also update mobile nav active state
                        const href = _(this).attr('href');
                        mobileMenuLinks.each(function(mobileLink){
                            mobileLink.class('active',false);
                            if (mobileLink.attr('href') === href) {
                                mobileLink.class('active',true);
                            }
                        });
                    }
                });
            });

            // Close mobile menu on escape key
            document.addEventListener('keydown', function(e) {
                if (e.key === 'Escape' && mobileMenu.class('active')) {
                    closeMobileMenu();
                }
            });

            // Navbar scroll effect - Remove auto-hide, keep it sticky
            window.addEventListener('scroll', function() {
                const navbar = _('.navbar-container');
                let scrollTop = window.pageYOffset || document.documentElement.scrollTop;

                // Add/remove scroll class for styling changes if needed
                if (scrollTop > 50) {
                    navbar.class('scrolled',true);
                }
                else
                {
                    navbar.class('scrolled',false);
                }
            });


            // Add hover effect to floating circles

            const floatingCircles = _('.floating-circle');
            floatingCircles.each(function(circle) {
                circle.mouseenter(function() {
                    this.style.transform = 'scale(1.2)';
                });

                circle.mouseleave(function() {
                    this.style.transform = 'scale(1)';
                });
            });

            // Smooth scrolling for navigation links
            _('a[href^="#"]').each(function(anchor){
                anchor.click(function (e) {
                    e.preventDefault();
                    const target = _(_(this).attr('href')).d;
                    if (target) {
                        target.scrollIntoView({
                            behavior: 'smooth',
                            block: 'start'
                        });
                    }
                });
            });

            // Handle window resize
            window.addEventListener('resize', function() {
                if (__.env().wbr > 992 && mobileMenu.class('active')) {
                    closeMobileMenu();
                }
            });
    //##############################################################################
});

ColorPicker Demo, auf GitHab
/*

Color Picker

CompactDOM script

Version: 1.0

Author: Vladimir Kheifets (kheifets.vladimir@online.de)

Copyright ©2022 Vladimir Kheifets All Rights Reserved

Online-tutorial of the Java Script Library CompactDOM:
https://www.alto-booking.com/developer/CompactDOM/

CompactDOM on GitHub:
https://github.com/VladimirKheifets/Java-Script-library-CompactDOM

*/

/*
The 'ready' method with default selelector 'window' calls an anonymous function
after the page is fully loaded
*/
__.ready( () => {
	//Defining 'head' and 'body' elements
	body = _("body");
	head = 	_("head");

	/*
	The 'create' method creates a child element 'title' in the parent element 'head'
	<title>Color picker</title>
	*/	
	head.create( "Color picker", {tag:"title"} );

	/*
	The 'link' method with default selelector 'head' creates  two child 
	elements 'link' in the parent element 'head'	
	<link href="css/index.css" rel="stylesheet" type="text/css">	
	<link href="css/modal.css" rel="stylesheet" type="text/css">
	*/
	__.link("css/index.css,css/modal.css");

	/*
	The 'create' method creates a child element 'meta' in the parent element 'head'
	<meta charset="utf-8">
	*/
	head.create(1,{ tag:"meta", charset:"utf-8" });

	/*
	The 'create' method creates a child element 'meta' in the parent element 'head'
	<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1,
	 user-scalable=no, user-scalable=0">
	*/
	head.create(1,
	{
		tag:"meta",
		name:"viewport",
		content:"width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no, user-scalable=0"
	});

	/*
	The 'create' method with default selelector 'body' creates a child element 'div' 
	in the parent element 'body' and defining 'div' element
	<div align="center"></div>
	*/
	div = __.create(1,{tag:"div", align:"center"});

	/*
	The 'create' method creates a child element 'div' in the parent element 'div'
	and defining 'divColorPicker' element
	<div id="color_picker"></div>
	*/
	divColorPicker = div.create(1,{tag:"div", id:"color_picker"});

	/*
	The 'create' method creates five child elements 'span' in the parent element 'divColorPicker'
	*/
	divColorPicker.create( 5, { tag:"span", class:"bgc", title:"Click me" } );

	/*
	The 'create' method creates a child element 'div' in the parent element 'div'
	and defining 'colorCode' element
	<div></div>
	*/
	colorCode = div.create( 1, {tag:"div"} );

	/*
	The 'hide' method makes the 'colorCode' element invisible.
	<div style="display:none"></div>
	*/
	colorCode.hide();

	// Defining 'colors' array
	colors = [ "FFFFFF", "F7F7F7", "FFF8F7", "FFFDDE", "F9FEF6" ];

	/*
	The 'each' method calls an anonymous function for each 'span' elements
	with selector '.bgc' (NodeList)
	this function defining  each element - 'el'  and index of element - 'ind'
	*/
	_(".bgc").each( ( el, ind ) => {

		/*
		The 'scc' method addes to each element the attribute style="background-color:"
  		The value of color for each attribute is determined from the 'colors[ind]'
		*/
		el.css("background-color:#"+colors[ind]);

		/*
		The 'create' method creates a child element 'input' in the each parent element 'el'
		and defining 'inp' elementcolorCode' element is created colorCodeContent
		*/
		inp = el.create(1,{tag:"input", type:"color", value:"#"+colors[ind]});

		//The 'on' method with the 'inp' selector handles the 'input' event and calls the anonymous function
		inp.on("input", (e) => {

			//The 'hide' method makes the 'colorCode' element unvisible.
			colorCode.hide();

			/*
			The 'val' method with the 'e.target' selector returns the color code
			and defines the 'color' variable.
			The 'toUpperCase' method of native js converts the value
			of the variable 'color' to uppercase
			*/
			color = _(e.target).val();
			color = color.toUpperCase();

			//Defining 'selectors' array
			selectors = [el, body, _modal];

			/*
			The 'scc' method for each selector from the "selector" array is
			addes attribute the style="background-color:", or
			changes the previously defined value of this attribute.
			*/
			for(i in selectors) selectors[i].css("background-color:" + color );


			//Defining 'modalContent' variable
			modalContent  = "<div>Selected color code: " + color + "</div>";
			modalContent += "<button>Copy code to clipboard</button>";
			modalContent += "<button>Send</button>";

			/*
			Method 'modal' with default selector '#modal'
			displays a modal window with the content from
			the variable 'modalContent'
			*/
			__.modal(modalContent);

			/*
			The 'click' method for the 'button' selector
			(two buttons in a modal window) handles the 'click'
			event and calls an anonymous function.
			*/

			_("button").click((eB) => {

				/*
				The 'content' method for the 'eB.target' selector
				returns the content of the element.
				*/

				if(_(eB.target).content() === "Send")
				{
					/*
					The "send" button was clicked

					Defining 'colorsCode' object
					*/
					colorsCode={};

					/*
					The 'each' method calls an anonymous function for each 'input' elements
					this function defining  each element - 'elmInput'  and index of element - 'ind'
					*/
					_("input").each((elmInput, indInp) => {

						/*
						The "val" method returns the value of each "input" element.
						Defining the properties of the 'colorsCode' object
						*/
						color = elmInput.val();
						color = color.toUpperCase();

						//Defining the properties(key:value) of the 'colorsCode' object
						colorsCode["color"+indInp] = color;
					});

					/*
					The "send" method creates a 'formData' object from
					the "colorsCode" object and send AJAX Post Request whith default
					request type - 'FormData' to server(url:"selectedColors.php")
					and returns AJAX Response with default response type - 'text'
					to callback function.
					In this function, the 'css' method changes the background color
					to 'white' and then the 'modal' method displays
					the AJAX Response in a modal window.
					*/
					__.send(
						{
							url:"selectedColors.php",
							method:"post",
							data:colorsCode,
							func:(rsp)=>{
								_modal.css("background-color:white");
								__.modal(rsp);
							}
						}
					);
				}
				else
				{
					/*
					The "Copy color code to clipboard" button was clicked

					The code of the selected color will be copied to the clipboard
					*/
					navigator.clipboard.writeText(color);

					//The content of the 'colorCode' element is created
					colorCodeContent = "<br>Color code in clipbord: " + color;
					colorCodeContent += "<button>Clear clipboard</button>";
					colorCode.content(colorCodeContent);

					/*
					The 'click' method for the 'button' selector
					(buttons in colorCodeContent) handles the 'click'
					event and calls an anonymous function.
					*/
					_("button").click(()=>{

						// Сlear clipboard data
					 	navigator.clipboard.writeText("");

					 	//The 'hide' method makes the 'colorCode' element unvisible.
					 	colorCode.hide();
					});

					// The 'show' method makes the colorCode element visible.
					colorCode.show();
				}

				// The 'modal' method with a parameter of '0' immediately closes the modal window.
				__.modal(0);
			});
		});
	});

	 /*
	 The 'modal' method without parameters only creates the HTML code of the modal window,
	 but does not display it.

	 <div id="modal" class="modal" style="opacity: 0; transition-property: opacity;
	 transition-duration: 600ms; transition-timing-function: cubic-bezier(0.02, 0.01, 0.47, 1);">
	 <div class="modal_close">×</div>
	 <div id="modal_content" class="modal_content"></div>
	 </div>
	 <div id="modal_gray_layer" class="modal_gray_layer"></div>
	*/
	__.modal();

	//Outputs a current html-document to the web console
	OutputsHTML = document.documentElement;
	if(outerHTML = OutputsHTML.outerHTML) OutputsHTML = outerHTML;
	console.log(OutputsHTML);
});
CrossPoint Demo, auf GitHab
/*

Cross Point

CompactDOM script

Version: 1.0, 2022-05-19

Author: Vladimir Kheifets (kheifets.vladimir@online.de)

Copyright ©2022 Vladimir Kheifets All Rights Reserved

Online-tutorial of the Java Script Library CompactDOM:
https://www.alto-booking.com/developer/CompactDOM/

CompactDOM on GitHub:
https://github.com/VladimirKheifets/Java-Script-library-CompactDOM

*/

__.laSelector("en");
dic = __.getDictionary(_dirLa);
//---------------------------------
  __.ready(() => {
  Env = __.env();
  __.on(Env.eor, ev => { __.reload();});
  //------------------------------------------------
  head = _("head");
  title = dic.title;
  head.create( title, {tag:"title"} );
  head.create(1,{ tag:"meta", charset:"utf-8" });
  head.create(1,
  {
    tag:"meta",
    name:"viewport",
    content:"width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no, user-scalable=0"
  });
  __.link("css/index.css,css/modal.css,css/button_to_up.css,css/selLanguages.css");
  ctx = [];
  lay = [];
  lines = {};
  inp = _("input");
  lab = _("label");

  //------------------------------------------------
  colors = {1:"blue", 2:"purple", 3:"red"};
  canvasWidthD = [300,500,500,600,860];
  canvasHeightD = [300,300,300,400,400];
  Env = __.env();
  canvasWidth = canvasWidthD[Env.dev];
  canvasHeight = canvasHeightD[Env.dev];
  gridCell = 10;
  X0 = canvasWidth/2 - 1;
  Y0 = canvasHeight/2 - 1;

  main = _("main");
  mainCanvasSt = "width:"+canvasWidth+"px;height:"+canvasHeight+"px"
  main.css(mainCanvasSt);
  _("header h1").content(title);

  //-- canvas -------------------------------------------------------
  main.create(3,
    {
      tag: "canvas",
      id: "lay",
      width: canvasWidth,
      height: canvasHeight,
      style:mainCanvasSt
    });
  elCanvas = _("canvas");
  //---LINE div --------------------------------------
    _("header div").each((el,ind) => {
      i = ind + 1;
      el.css("color:" + colors[i]);
      el.attr("id","dL"+i);
  });


 labContent=dic.switch.concat(dic.switch);
 _("label").each((el,ind)=>{el.content(labContent[ind])});

  //-- footer -------------------------------------------------------
  d = new Date();
  year = d.getFullYear();
  footer = "<p>© </p>" + year + " Alto Booking"
  footerSt="margin-top:"+ (canvasHeight+20) + "px";
  main.create(footer, {add:"after",tag:"footer"});//, style:footerSt
  //------------------------------------------------------------------

  nav = __.create(1, {tag:"nav"});
  nav.create(
    dic.button,
    {
      tag:"button",
      id:"but"
    }
  );

  navCSS = "top:"+(_("header").position().top+10)+"px";
  nav.css(navCSS);
  navHide = () =>{if(Env.dev<3) nav.hide();};
  res = __.create(1,{tag:"p"});
  res.css("height:55px");
  res.hide(100);
  if(Env.dev < 3)
  {
    menu  = main.create(1, {add:"after",tag:"div", id:"menu"});
    fileName=(Env.tou?"menuts":"menu")+".html";
    menu.include(fileName);
    nav.hide();
    res.css(navCSS);
    menu.click(()=>{
      if(nav.ishide())
        nav.show();
      else
        nav.hide();
      if(!res.ishide())
        res.hide(100);
    });
  }

  i=1;
  while(i<3)
  {
    aI = "a" + i;
    bI = "b" + i;
    lines[aI] = _("#" + aI);
    lines[bI] = _("#" + bI);
    _("#dL"+i +" input").each((el,ind) => {
      el.attr(
      {
        "type" : "radio",
        "id" : "inp" + i,
        "name" : "inp" + i,
        "value" : ind+1
      }
      );
    });
    i++;
  }

  // Defining canvas & layots
  elCanvas.each((el,ind) => {
    ctx[ind] = el.d.getContext('2d');
    lay[ind] = el;
    el.css("z-index:" + ind);
  });

  startSetting = () => {
    pXY = {
      width: canvasWidth,
      height: canvasHeight,
      1:{line:{}},
      2:{line:{}}
    };
    activesPoint = {1:"a", 2:"a"};
    Drawing = {1:true,2:true};
    lay[2].hide();
    _("span").each((el) => {el.content("")});
    but = [];
    _("button").each( (el, ind) => {
      but[ind] = el;
      if(ind>1) el.hide();
    });
    inp.each( (el, ind) => {
      if(ind == 0 || ind == 2) el.checked(2);
      el.css("opacity:0");
    });
    lab.each( (el, ind) => {
      if(ind == 1 || ind == 3) el.css("opacity:0");
    });
    res.css("opacity:0");
  };

  showLineSelector = (iL) => {
    inpEl = {1:[0,1], 2:[2,3]};
    labEl = {1:[1],2:[3]};
    inp.each( (el, ind) => {
      if(inpEl[iL].includes(ind))
        el.css("opacity:1");
    });
    lab.each( (el, ind) => {
      if(labEl[iL].includes(ind))
        el.css("opacity:1");
    });
  }



  drawingGrid = (cansasW, cansasH, sizeCell, c, color) => {
    c.strokeStyle = "#ddd";
    for (var x = sizeCell; x < cansasW; x += sizeCell) {
      c.moveTo(x, 0);
      c.lineTo(x, cansasH);
    }

    for (var y = sizeCell; y < cansasH; y += sizeCell) {
      c.moveTo(0, y);
      c.lineTo(cansasW, y);
    }
    c.stroke();
  }

  drawingAxes = (cansasW, cansasH, c, color) => {
    // Drawing axes, Decart 0 in canvas x=399, y=198
    // Axis X
    x1X = 10;
    y1X = cansasH/2;
    x2X = cansasW - 20;
    y2X = y1X;
    drawingLine(c, x1X, y1X, x2X, y2X, color);
    drawingLine(c, x2X-10, y2X+5, x2X, y2X, color);
    drawingLine(c, x2X-10, y2X-5, x2X, y2X, color);
    drawingText(c,'X', x2X+2, y2X+7, color);

    // Axis Y
    x1Y = cansasW/2;
    y1Y = x1X+10;
    x2Y = x1Y;
    y2Y = cansasH - 10;
    drawingLine(c, x1Y, y1Y, x2Y, y2Y, color);
    drawingLine(c, x2Y+5, y1Y+10, x1Y, y1Y, color);
    drawingLine(c, x2Y-5, y1Y+10, x1Y, y1Y, color);
    drawingText(c,'Y', x1Y-6, y1Y-3, color);
  }


  drawingLine = (c, x1, y1, x2, y2, color )=> {
    c.beginPath();
    c.strokeStyle = color;
    c.moveTo(x1, y1);
    c.lineTo(x2, y2);
    c.closePath();
    c.stroke();
  };

  drawingPoint = (c, x, y, color )=> {
    c.beginPath();
    c.fillStyle = color;
    c.arc(x, y, 3, 0, 2 * Math.PI);
    c.fill();
    c.closePath();
    c.stroke();
  };

  drawingText = (c, text, x, y, color, font )=> {
    if(__.u(font)) font = '16px arial'
    c.beginPath();
    c.fillStyle = color;
    c.font = font;
    c.fillText(text, x, y);
    c.fill();
    c.closePath();
    c.stroke();
  };

  clearCanvas = (c) => {c.clearRect(0, 0, canvasWidth, canvasHeight)};

  butShowHide = (s) => {
    i = 2;
    while(i<but.length)
    {
      if(s)
        but[i].show();
      else
        but[i].hide();
      i++;
    }
  };


  ePoint = (e) => {
    id = e.target.id;
    ind = parseInt(id.substr(3))-1;
    x = e.offsetX;
    y = e.offsetY;
    Xd = x - X0;
    Yd = Y0 - y;
  };

  getlineABC = (XY) => {
     if(__.a(XY))
     {
        x1 = XY[0];
        y1 = XY[1];
        x2 = XY[2];
        y2 = XY[3];
     }
     else
     {
      x1 = XY["Xad"];
      y1 = XY["Yad"];
      x2 = XY["Xbd"];
      y2 = XY["Ybd"];
     }
     A = y2 - y1;
     B = x1 - x2;
     C = -x1*y2 + y1*x2;
     return [A,B,C];
   };

  crossPoint = (ABC1, ABC2) => {
    A1 = ABC1[0];
    B1 = ABC1[1];
    C1 = ABC1[2];
    A2 = ABC2[0];
    B2 = ABC2[1];
    C2 = ABC2[2];
    Xcd = Math.round((B1*C2 - B2*C1)/(A2*B2 - A2*B1));
    Ycd = Math.round((A1*C2 - A2*C1)/(A2*B1 - A1*B2));
    Xc = Xcd + X0;
    Yc = Y0 - Ycd;
    return [Xc, Yc, Xcd, Ycd];
  };

  crossPointK = (XY1, XY2, X0, Y0 ) => {
    x1 = XY1["Xad"];
    y1 = XY1["Yad"];
    x2 = XY1["Xbd"];
    y2 = XY1["Ybd"];

    x3 = XY2["Xad"];
    y3 = XY2["Yad"];
    x4 = XY2["Xbd"];
    y4 = XY2["Ybd"];
    //-----------------------------------
    z1 = (x3-x1)*(y2-y1)-(y3-y1)*(x2-x1);
    z2 = (x4-x1)*(y2-y1)-(y4-y1)*(x2-x1);
    z3 = (x1-x3)*(y4-y3)-(y1-y3)*(x4-x3);
    z4 = (x2-x3)*(y4-y3)-(y2-y3)*(x4-x3);
    crossPointCheck = (z3*z4>0 || z1*z2>0)?false:true;
    //-----------------------------------
    if(crossPointCheck)
    {
      k1 = (y1 - y2) / (x1 - x2);
      b1 = y2 - k1 * x2;
      k2 = (y3 - y4) / (x3 - x4);
      b2 = y4 - k2 * x4;
      Xd = Math.round((b2 - b1)/(k1 - k2));
      Yd = Math.round(k1*Xd + b1);
      X = Xd + X0;
      Y = Y0 - Yd;
      return {
        crossPointCheck: true,
        X:X,
        Y:Y,
        Xd:Xd,
        Yd:Yd
      };
    }
    else
      return {
        crossPointCheck:false
    };
  };

  elCanvas.on('click', (e) => { //mouseclick or touchstart
    navHide();
    ePoint(e);
    if(Drawing[ind])
    {
      drawingPoint(ctx[ind], x, y, colors[ind]);
      if(activesPoint[ind]==="a")
      {
        activesPoint[ind] = "b";
        pXY[ind]["Xa"] = x;
        pXY[ind]["Ya"] = y;
        pXY[ind]["Xad"] = Xd;
        pXY[ind]["Yad"] = Yd;
      }
      else
      {
        pXY[ind]["Xb"] = x;
        pXY[ind]["Yb"] = y;
        pXY[ind]["Xbd"] = Xd;
        pXY[ind]["Ybd"] = Yd;
        drawingLine(ctx[ind], pXY[ind]["Xa"], pXY[ind]["Ya"], x, y, colors[ind]);
        lay[2].show();
        Drawing[ind] = false;
        if(ind == 2)
        {
          butShowHide(1);
          showLineSelector(1);
          showLineSelector(2);
        }
      }
    }
  });

  elCanvas.on('mousemove', (e) => {
    navHide();
    ePoint(e);
    if(Drawing[ind])
    {
      ab = activesPoint[ind];
      if(ab === "b")
      {
         clearCanvas(ctx[ind]);
         drawingPoint(ctx[ind], pXY[ind]["Xa"], pXY[ind]["Ya"], colors[ind]);
         drawingLine(ctx[ind], pXY[ind]["Xa"], pXY[ind]["Ya"], x, y, colors[ind]);
         contP = dic.points[1] + ": ";
      }
      else
        contP = " "+dic.points[0]+": ";
      lines[ab+ind].content(contP + "x = "+Xd+", y = "+Yd);
    }
  });

  inp.change((e) => {
    el = e.target;
    id = _(el).attr("id");
    i = id.substr(3);
    if(activesPoint[i]==="b")
    {
      val = parseInt(_(el).val());
      if(val === 2)
      {
        ABC1 = getlineABC(pXY[i]);
        cHalbW = canvasWidth/2;
        cHalbH = canvasHeight/2;
        ABC2a = getlineABC([-cHalbW, -cHalbH, -cHalbW, cHalbH]);
        ABC2b = getlineABC([cHalbW, -cHalbH, cHalbW, cHalbH]);
        XYca = crossPoint(ABC1, ABC2a);
        XYcb = crossPoint(ABC1, ABC2b);
        drawingLine(ctx[i], XYca[0], XYca[1],  XYcb[0], XYcb[1], colors[i]);
        pXY[i]["line"] = {
          Xa:  XYca[0],
          Ya:  XYca[1],
          Xad: XYca[2],
          Yad: XYca[3],
          Xb:  XYcb[0],
          Yb:  XYcb[1],
          Xbd: XYcb[2],
          Ybd: XYcb[3]
        };
      }
      else
      {
         clearCanvas(ctx[i]);
         drawingPoint(ctx[i], pXY[i]["Xa"], pXY[i]["Ya"], colors[i]);
         drawingLine(ctx[i], pXY[i]["Xa"], pXY[i]["Ya"], pXY[i]["Xb"], pXY[i]["Yb"], colors[i]);
         drawingPoint(ctx[i], pXY[i]["Xb"], pXY[i]["Yb"], colors[i]);
         delete pXY[i].line;
      }
    }
  });

  showResult = (XYcp, code) => {
    if(__.u(code)) code="PHP";
    res.show(1);
    if(XYcp["crossPointCheck"])
    {
      drawingPoint(ctx[2], XYcp["X"], XYcp["Y"], colors[3]);
      out = "Crossing point found.<br>";
      out += "Coordinates ";
      out += "X: "+XYcp["Xd"] + ", Y: " + XYcp["Yd"];
      out += "<br>calculated with " + code;
      res.content(out);
    }
    else
    {
      out = "Crossing point not found!<br>";
      out += "Please click the \"clear\"<br>button and try again.";
      res.content(out);
    }
  };

  printInfo = (rsp) => {
    txt=dic.structure;
      var   out = "<div>";
      if(rsp)
      {
        out += txt[0];
      }
      out += txt[1];
      if(rsp)
      {
        out += txt[2];
      }
      out += "</div>";
      return out;
  };

  _("button").click((e) => {
  id = e.target.id;
  i = parseInt(id.substr(3));
    switch(i)
    {
      case 1:
      obj=
      {
        url:_dirLa+"/help.html",
        method:"get",
        func:(rsp) => {
         __.modal(rsp);
        },
        debug:1
      }
      __.send(obj);
      break;

      case 2:
      clearCanvas(ctx[0]);
      if(gridOn)
      {
        gridOn = false
        drawingAxes(canvasWidth, canvasHeight, ctx[0], "#aaa");
      }
      else
      {
        gridOn = true;
        drawingGrid(canvasWidth, canvasHeight, 10, ctx[0], "#ddd");
        drawingAxes(canvasWidth, canvasHeight, ctx[0], "#aaa");
      }
      break;

      case 3:
      XY1 = (pXY[1].line.Xa)?pXY[1].line:pXY[1];
      XY2 = (pXY[2].line.Xb)?pXY[2].line:pXY[2];
      XYcp =  crossPointK(XY1, XY2, X0, Y0 );
      showResult(XYcp,"Java Script");
      console.log(XYcp);
      break;

      case 4:
      obj=
      {
        url:"crossPoint.php",
        method:"post",
        dataType: "json",
        responseType: "json",
        data:pXY,
        func: showResult
      }
      __.send(obj);
      break;

      case 5:
      obj=
      {
        url:"crossPoint.php?pr=1",
        method:"post",
        dataType: "json",
        data:pXY,
        func: (rsp) => {
          out = "<div class = 'outPr'>";
          out += rsp;
          out += printInfo(true);
          out += "</div>";
          __.modal(out);
        }
      }
      __.send(obj);
      break;

      case 6:
      obj=
      {
        url:"crossPoint.php?pr=2",
        method:"post",
        dataType: "json",
        data:pXY,
        func: (rsp) => {
          out = "<div class = 'outPr'>";
          out += rsp;
          out += printInfo();
          out += "</div>";
          __.modal(out);
        }
      }
      __.send(obj);
      break;

      case 7:
      clearCanvas(ctx[1]);
      clearCanvas(ctx[2]);
      startSetting();
      break;
    }
    navHide();
  });

  //----------------------------------------------
  gridOn = true;
  drawingGrid(canvasWidth, canvasHeight, gridCell, ctx[0], "#ddd");
  drawingAxes(canvasWidth, canvasHeight, ctx[0], "#aaa");
  startSetting();
  __.modal();
  __.scroll();
  __.laSelector();
});

Datumsintervall-Berechnung, implementiert in PHP, Python, C++, Java und ECMAScript, auf GitHab

Problemstellung

An unordered set of dates for one year is given.
It is required to obtain an ordered list of months and for each month ordered date intervals.
Dates must be specified in two formats:
- full month name and day;
- ISO date format.

For example:

Set of dates:

"January 8", "May 11", "January 3","January 1", "April 2", "January 4","January 5", "May 11","February 24", "April 1", "February 25", "February 26", "March 12", "January 7", "March 13", "January 3", "March 14", "February 24", "April 3","May 10", "May 12","February 23"

or

"2025-05-10", "2025-03-14","2025-05-11", "2025-01-04", "2025-03-13", "2025-01-08","2025-05-12", "2025-01-03",  "2025-04-03","2025-02-24", "2025-01-01", "2025-01-05", "2025-02-23",  "2025-02-25", "2025-02-26", "2025-03-12",

Result:

Intervals:

Days in January
  1
  3 - 5
  7 - 8

Days in February
23 - 26

Days in March
12 - 14

Days in April
1 - 3

Days in May
10 - 12

In order to compare the capabilities of programming languages, the task should be
implemented in PHP, Python, C++, Java and ECMAScript.

PHP Demo
<?PHP
/*
	Calculating date intervals in PHP
	Version: 2.0, 2025-02-22
	Author: Vladimir Kheifets vladimir.kheifets@online.de
	Copyright © 2025 Vladimir Kheifets All Rights Reserved
*/

function getIntervals($inData){

	$months = getMonths();

	$groups = [];

	foreach((array)$inData as $item){

		$pattern1 = "/^([a-zA-Z]+).+(\d+)$/";
		$pattern2 = "/^(\d{4})\-(\d{2})\-(\d{2})$/";
		if(preg_match($pattern1, $item, $out))
		{
			$month = array_search($out[1], $months);
			$day = intval($out[2]);
		}
		else if(preg_match($pattern2, $item, $out2))
		{
			$month = intval($out2[2])-1;
			$day = intval($out2[3]);
		}
		else
			return false;

		$groups[$month][] = $day;
	}

  if(empty($groups))
  	return false;

	ksort($groups);

	foreach($groups as $preff => $group){

		sort($group);

		//---------------------------------
		$interval = "";
        $numberBefor = 0;
		foreach($group as  $number)
		{
			if($numberBefor == 0)
				$interval = $number;
			else
			{
				if(($number - $numberBefor) > 1)
				{
					if($interval != $numberBefor)
						$interval .= " - $numberBefor";
					$intervals[$preff][] = $interval;
					$interval = $number;
				}
			}
            $numberBefor = $number;
		}
		if($interval != $number)
			$interval .= " - $number";
		$intervals[$preff][] = $interval;
	}
	return $intervals;
}

//---------------------------------------------------------

function viewIntervals($intervals){
	if($intervals)
	{
		$months = getMonths();
		$out = "-----------------------------------<br>";
		$out .= "Intervals:<br>";
		foreach($intervals  as $month => $interval){
			$out .= "Days in  {$months[$month]}:<br>";
			foreach($interval as $numberDays)
			{
				$out .= "$numberDays<br>";
			}
			$out .= "<br>";
		}
		echo $out;
	}
}

//---------------------------------------------------------

function getMonths(){
  return  array_map(fn (int $m): string => (DateTime::createFromFormat('m-d', "$m-1")
  -> format('F')),  range(1, 12),);
}

//---------------------------------------------------------

?>
<head>
    <meta charset="UTF-8">
    <title>Demo Intervals.php</title>
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <style>
    pre{
    	padding-left:20px;
    	font-family: arial;
    	font-size: 14px;
    }
    </style>
</head>
<body>
<pre>
<b>Demo Intervals.php</b>

<?

$data = ["January 8", "May 11", "January 3","January 1", "April 2", "January 4","January 5", "May 11","February 24", "April 1", "February 25", "February 26", "March 12", "January 7", "March 13", "January 3", "March 14", "February 24", "April 3","May 10", "May 12","February 23"];

echo "\$data ";
print_r($data);
$intervals = getIntervals($data);
viewIntervals($intervals);

//-------------------------------------------------------

$data = ["2025-05-10", "2025-03-14","2025-05-11", "2025-01-04", "2025-03-13", "2025-01-08","2025-05-12", "2025-01-03",  "2025-04-03","2025-02-24", "2025-01-01", "2025-01-05", "2025-02-23",  "2025-02-25", "2025-02-26", "2025-03-12",  "2025-04-01", "2025-04-02", "2025-01-07"];

echo "<hr>\$data ";
print_r($data);
$intervals = getIntervals($data);
viewIntervals($intervals);

/*

-----------------------------------
	Intervals:
	Days in January:
	1
	3 - 5
	7 - 8

	Days in February:
	23 - 26

	Days in March:
	12 - 14

	Days in April:
	1 - 3

	Days in May:
	10 - 12

	-----------------------------------
	Intervals:
	Days in January:
	01
	03 - 05
	07 - 08

	Days in February:
	23 - 26

	Days in March:
	12 - 14

	Days in April:
	01 - 03

	Days in May:
	10 - 12

*/
?>
</pre>
</body>
</html>

Python

"""
  Calculating date intervals in Python
  Version: 2.0, 2025-02-22
  Author: Vladimir Kheifets vladimir.kheifets@online.de
  Copyright © 2025 Vladimir Kheifets All Rights Reserved
"""

import re
import datetime
import calendar

def getintervals(inData):

  groups = {}
  patt1 = re.compile(r'^([a-zA-Z]+).+(\d+)$')
  patt2 = re.compile(r'^(\d{4})\-(\d{2})\-(\d{2})$')
  intervals = {}

  for item in inData:
    if re.match(patt1,item):
      out = patt1.findall(item)
      month = list(calendar.month_name).index(out[0][0]);
      day = int(out[0][1])
    elif re.match(patt2,item):
      out = patt2.findall(item)
      month = int(out[0][1])
      day = int(out[0][2])
    else:
      return intervals

    if month in groups:
      groups[month].append(day)
    else:
      groups[month]=[day]

  monthGroups = sorted(list(groups.keys()))

  for preff in monthGroups:
    group = groups[preff]
    group.sort()
    numberBefor = 0
    for number in group:
      val = str(number)
      if numberBefor == 0:
        interval = val
      else:
        if number - numberBefor > 1:
          valBefor = str(numberBefor)
          if interval != valBefor:
            interval += " - " + valBefor
          if preff in intervals:
            intervals[preff].append(interval)
          else:
            intervals[preff]=[interval]
          interval = val

      numberBefor = number

    if interval != val:
      interval += " - " + val;
    if preff in intervals:
       intervals[preff].append(interval)
    else:
       intervals[preff] = [interval]

  return intervals;

#--------------------------------------

def viewIntervals(intervals):
  out = "---------------------\n\n"
  out += "intervals:\n\n"

  for month in  intervals:
    out +=  "Days in  " +    calendar.month_name[month] + "\n"
    for numberDays in intervals[month]:
       out +=  numberDays + "\n"
    out +=  "\n"
  print(out)

#--------------------------------------

data = ["January 8", "May 11", "January 3","January 1", "April 2", "January 4","January 5", "May 11","February 24", "April 1", "February 25", "February 26", "March 12", "January 7", "March 13", "January 3", "March 14", "February 24", "April 3","May 10", "May 12","February 23"];

intervals = getintervals(data)
viewIntervals(intervals)

#------------------------------------------------------------

data = ["2025-05-10", "2025-03-14","2025-05-11", "2025-01-04", "2025-03-13", "2025-01-08","2025-05-12", "2025-01-03",  "2025-04-03","2025-02-24", "2025-01-01", "2025-01-05", "2025-02-23",  "2025-02-25", "2025-02-26", "2025-03-12",  "2025-04-01", "2025-04-02", "2025-01-07"];

intervals = getintervals(data)
viewIntervals(intervals)

"""
Output:

---------------------

intervals:

Days in  January
1
3 - 5
7 - 8

Days in  February
23 - 26

Days in  March
12 - 14

Days in  April
1 - 3

Days in  May
10 - 12


---------------------

intervals:

Days in  January
1
3 - 5
7 - 8

Days in  February
23 - 26

Days in  March
12 - 14

Days in  April
1 - 3

Days in  May
10 - 12

"""

C++

/*
    Calculating date intervals in C++
    Version: 2.0, 2025-02-22
    Author: Vladimir Kheifets vladimir.kheifets@online.de
    Copyright © 2025 Vladimir Kheifets All Rights Reserved
*/

#include <langinfo.h>
#include <locale.h>
#include <iostream>
#include <string>
#include <regex>
#include <map>

using namespace std;

//------------------------------------------------------------

vector<string> getMonths() {
   const nl_item nl_months[12] = {MON_1, MON_2, MON_3, MON_4, MON_5, MON_6,
                                   MON_7, MON_8, MON_9, MON_10, MON_11, MON_12};
    int i;
    vector<string> months;
    setlocale(LC_ALL, "");
    for (i = 0; i < 12; i++) {
        months.push_back(nl_langinfo(nl_months[i]));
    }
    return months;
}

//------------------------------------------------------------

int getMonthNumber(string month) {
    vector<string> months = getMonths();
    auto it = find(months.begin(), months.end(), month);
    return distance(months.begin(), it);
}

//------------------------------------------------------------

void viewIntervals( map< int, vector<string> > intervals){
    vector<string> months;
    months = getMonths();
    cout << "-----------------------------"  << endl << endl;
    cout << "Intervals:" << endl << endl;

    for (const auto& pair : intervals)
    {
        int mI = pair.first;
        string mN = "Days in " + months[mI] + ":";
        cout << mN << endl;
        vector interval = pair.second;
        for( string numberDay : interval)
        {
            cout << numberDay << endl;
        }
         cout <<  endl;
    }
}

//------------------------------------------------------------

map< int, vector <string> > getIntervals(vector<string> inData) {

   vector<string> months;
   months = getMonths();

    map< int, vector<int> > groups;
    string interval;
    string valBefor;
    string val;
    vector <int> group;
    map< int, vector <string> > intervals = {};

    regex rS1("^([a-zA-Z]+).+(\\d+)$");
    regex rS2("(\\d{4})\\-(\\d{2})\\-(\\d{2})");
    regex rR("\\ ");
    smatch m;

    for (string item : inData)
    {
        int mI;
        int d;

        if(regex_search(item, m, rS1))
        {
            mI = getMonthNumber(m[1]);
            d = stoi(m[2]);
        }
        else if(regex_search(item, m, rS2))
        {
             mI = stoi(m[2]) - 1;
             d = stoi(m[3]);
        }
        else
            return intervals;

        groups[mI].push_back(d);
    }

    for (const auto& pair : groups)
    {
        int month = pair.first;
        group = pair.second;
        sort(group.begin(), group.end());

        int numberBefor = 0;
        for (int number : group)
        {
            val = to_string(number);
            if (numberBefor == 0)
                interval = val;
            else
            {
                if ((number - numberBefor) > 1)
                {
                    valBefor = to_string(numberBefor);
                    if (interval != valBefor)
                        interval += " - " + valBefor;
                    intervals[month].push_back(interval);
                    interval = val;
                }
            }
            numberBefor = number;
        }

        if (interval != val)
            interval += " - " + val;
        intervals[month].push_back(interval);
    }

    return intervals;
}

//----------------------------------------------------------------------------

int main()
{
    vector<string> data = {"January 8","January 7", "January 3","January 1", "January 4","January 5", "February 23", "February 24", "February 25", "February 26", "March 12", "March 13", "March 14", "April 1", "April 2", "April 3","May 10", "May 11", "May 12"};

    map< int, vector <string> > intervals = getIntervals(data);

    viewIntervals(intervals);

    //------------------------------------------------------

    data = {"2025-01-08","2025-01-07", "2025-01-03","2025-01-01", "2025-01-04","2025-01-05", "2025-02-23", "2025-02-24", "2025-02-25", "2025-02-26", "2025-03-12", "2025-03-13", "2025-03-14", "2025-04-01", "2025-04-02", "2025-04-03","2025-05-10", "2025-05-11", "2025-05-12"};

    intervals = getIntervals(data);

    viewIntervals(intervals);
    /*
        Intervals:

        Days in January
        1
        3 - 5
        7 - 8

        Days in February
        23 - 26

        Days in March
        12 - 14

        Days in April
        1 - 3

        Days in May
        10 - 12
    */

    return 0;
}

Java

/*
    Calculating date intervals in C++
    Version: 2.0, 2025-02-22
    Author: Vladimir Kheifets vladimir.kheifets@online.de
    Copyright © 2025 Vladimir Kheifets All Rights Reserved
*/

import java.util.*;
import java.util.regex.*;
import java.util.function.Function;
import java.text.SimpleDateFormat;
import java.text.DateFormatSymbols;

public class Main {


    public static Map< Integer, Vector<String>> getIntervals(String[] inData)
    {
        Map< Integer, Vector<Integer> > groups = new HashMap<>();
        //List<String> intervals = new ArrayList<String>();
        Map< Integer, Vector<String> > intervals = new HashMap<>();

        String[] months = new DateFormatSymbols().getMonths();

        Pattern p1 = Pattern.compile("^([a-zA-Z]+).+(\\d+)$");
        Pattern p2 = Pattern.compile("(\\d{4})\\-(\\d{2})\\-(\\d{2})");
        String mN = "";
        int mI = 0;
        int d = 0;

        for(String item : inData)
        {
            Matcher m1 = p1.matcher(item);
            boolean b1 = m1.matches();
            Matcher m2 = p2.matcher(item);
            boolean b2 = m2.matches();

            if(b1)
            {
              mI = Arrays.asList(months).indexOf(m1.group(1));
              d = Integer.parseInt(m1.group(2));
            }
            else if (b2)
            {
              mI = Integer.parseInt(m2.group(2))-1;
              d = Integer.parseInt(m2.group(3));
            }
            else
              return intervals;

            if(groups.get(mI) == null)
                groups.put(mI, new Vector<Integer>());
            groups.get(mI).add(d);
        }

        groups.entrySet().forEach(entry -> {

          int month = entry.getKey();

          Vector<Integer> group = new Vector<Integer>();
          group = entry.getValue();
          Collections.sort(group);

          int numberBefor = 0;
          String interval = "";
          String val = "";

          intervals.put(month, new Vector<String>());
          for(int number : group)
          {
            val = Integer.toString(number);
            if (numberBefor == 0)
              interval = val;
            else
            {
                if (number - numberBefor > 1)
                {
                    String valBefor = Integer.toString(numberBefor);

                    if (!interval.equals(valBefor))
                        interval += " - " + valBefor;

                    //intervals[month].add(interval);
                    intervals.get(month).add(interval);
                    interval = val;
                }
            }

            numberBefor = number;

          }


          if (interval != val)
              interval += " - " + val;
           intervals.get(month).add(interval);

        });
        return intervals;
    }

    //------------------------------------------------------------------------------

    public static void viewIntervals(Map< Integer, Vector<String> > intervals){

      String[] months = new DateFormatSymbols().getMonths();

      System.out.println("------------------------\n");
      System.out.println("Intervals:\n");
      intervals.entrySet().forEach(entry -> {
          int i = entry.getKey();
           System.out.println("Days in " + months[i] + ":");
          Vector <String> interval = entry.getValue();
          for(String numberDay : interval)
           System.out.println(numberDay);
          System.out.println("");
      });
    }

    //------------------------------------------------------------------------------


    public static void main(String[] args) throws Exception{

    //------------------------------------------------------------------------------

     String[] data1 = {"January 8", "May 11", "January 3","January 1", "April 2", "January 4","January 5", "May 11","February 24", "April 1", "February 25", "February 26", "March 12", "January 7", "March 13", "January 3", "March 14", "February 24", "April 3","May 10", "May 12","February 23"};

      Map< Integer, Vector<String>> intervals = getIntervals(data1);

      viewIntervals(intervals);

      //---------------------------------------------------------------

    String[] data2 = {"2025-05-10", "2025-03-14","2025-05-11", "2025-01-04", "2025-03-13", "2025-01-08","2025-05-12", "2025-01-03",  "2025-04-03","2025-02-24", "2025-01-01", "2025-01-05", "2025-02-23",  "2025-02-25", "2025-02-26", "2025-03-12",  "2025-04-01", "2025-04-02", "2025-01-07"};

    intervals = getIntervals(data2);

    viewIntervals(intervals);

    /*
    ------------------------

    Intervals:

    Days in January:
    1
    3 - 5
    7 - 8

    Days in February:
    23 - 26

    Days in March:
    12 - 14

    Days in April:
    1 - 3

    Days in May:
    10 - 12

    ------------------------

    Intervals:

    Days in January:
    1
    3 - 5
    7 - 8

    Days in February:
    23 - 26

    Days in March:
    12 - 14

    Days in April:
    1 - 3

    Days in May:
    10 - 12

    */
  }
}

ECMAScript

/*
	Calculating date intervals in ECMAScript
	Version: 2.0, 2025-02-22
	Author: Vladimir Kheifets vladimir.kheifets@online.de
	Copyright © 2025 Vladimir Kheifets All Rights Reserved
*/

function getIntervals(inData){

	const pattern1 = /^([a-zA-Z]+).+(\d+)$/;
	const pattern2 = /^(\d{4})\-(\d{2})\-(\d{2})$/;

	const months = getMonths();

	let groups = {};

	for(let item of inData)
	{
		let out1 = pattern1.exec(item);
		let out2 = pattern2.exec(item);
		let day, month;
		if(out1)
		{
			day = parseInt(out1[2]);
			month = months.indexOf(out1[1]);
		}
		else if(out2)
		{
			day = parseInt(out2[3]);
			month = parseInt(out2[2]) - 1;
		}
		else
			return false;

		if(!groups[month])
			groups[month]=[];
		groups[month].push(day);
	}

	let intervals = {};

	for( let preff in groups)
	{
		let group = groups[preff];
		group.sort();

		let numberBefor = 0;

		intervals[preff] = [];
		for (var number of group)
		{
			if(numberBefor == 0)
				var interval = number;
			else
			{
				if((number - numberBefor) > 1)
				{
					if(interval != numberBefor)
						interval += ` - ${numberBefor}`;
					intervals[preff].push(interval);
					interval = number;
				}
			}
			numberBefor = number;

		}
		if(interval != number)
			interval += ` - ${number}`;
		intervals[preff].push(interval);
	}
	return intervals;
}

//---------------------------------------------------------

function viewIntervals(intervals){
	if(intervals)
	{
		months = getMonths();
		let out = "Intervals:\n\n";
		for(var key in  intervals )
		{
			out += `Days in  ${months[key]}:\n`;
			for(var interval of intervals[key] )
			{
				out += `${interval}\n`;
			}
			out += "\n";
		}
		alert(out);
	}
}

//---------------------------------------------------------

function getMonths(){
	return months = [...Array(11).keys()].map(key => new Date(0, key).
		toLocaleString("en",{ month: 'long' }));
}

//-----------------------------------------------------------

let data = ["January 8", "May 11", "January 3","January 1", "April 2", "January 4","January 5", "May 11","February 24", "April 1", "February 25", "February 26", "March 12", "January 7", "March 13", "January 3", "March 14", "February 24", "April 3","May 10", "May 12","February 23"];

let intervals = getIntervals(data);
viewIntervals(intervals);

//---------------------------------------------------------

data = ["2025-05-10", "2025-03-14","2025-05-11", "2025-01-04", "2025-03-13", "2025-01-08","2025-05-12", "2025-01-03",  "2025-04-03","2025-02-24", "2025-01-01", "2025-01-05", "2025-02-23",  "2025-02-25", "2025-02-26", "2025-03-12",  "2025-04-01", "2025-04-02", "2025-01-07"];

intervals = getIntervals(data);
viewIntervals(intervals);

/*
-----------------------------------
Intervals:

Days in January:
1
3 - 5
7 - 8

Days in February:
23 - 26

Days in March:
12 - 14

Days in April:
1 - 3

Days in May:
10 - 12

-----------------------------------
Intervals:

Days in January:
01
03 - 05
07 - 08

Days in February:
23 - 26

Days in March:
12 - 14

Days in April:
01 - 03

Days in May:
10 - 12

*/

Tabellen-Editor, Demo, auf GitHab

Datei index.php
<?PHP
/*
  PHP script tableEditor
  Version: 2.0, 2026-05-16
  Author: Vladimir Kheifets (vladimir.kheifets@online.de)
  Copyright (c) 2026 Vladimir Kheifets All Rights Reserved
  Demo:
  https://www.alto-booking.com/developer/table_editor/
*/

$inclFile = "../incl/db.php";
if(file_exists($inclFile))
  require_once($inclFile);
else
  $connect = new mysqli("127.0.0.1:3306","root","","test");

require_once("dbToolsClass.php");
require_once("tableEditorFunctions.php");
#####################################################################
$table = "myTable";
$dbt = new dbTools($connect, $table);
$tableHeader = $feldsInsUpd = $dbt -> fieldsName;
$columns = $dbt -> columns;
unset($feldsInsUpd[0]);
#####################################################################
if(isset($_POST["checkRow"]))
{
  foreach($_POST["checkRow"] as $ID)
  {
    $dbt -> delRow($ID);
  }
}
else if(isset($_POST["iRow"]))
{
  foreach ($_POST["iRow"] as $i =>$key)
  {
    $row = setRowToSave($key, $i, $columns);
    $dbt -> saveRow($row);
  }
}
#####################################################################
$selected = $dbt -> select();
$tableRows = getTableRows($selected, $tableHeader, $columns);
#####################################################################
?>
<html>
<head>
<title>Table editor</title>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="stylesheet" type="text/css" href="tableEditor.css" />
<script>
  columns = "<?=$columns?>";
  tableID = "<?=$table;?>";
</script>
<script type="text/javascript" src="tableEditor.js"></script>
</head>
<body>
<div align="center">
<form action="" method="post">
<table id="<?=$table?>" border="1">
<?
echo setTableRow($tableHeader,false);
foreach($tableRows as $iRow => $tableRow)
{
  echo setTableRow($tableRow, true, $iRow);
}
?>
</table>
<p>
<input type="submit" value="Save " />
<input type="button" value="Add row" />
<input type="button" value="Delete row" />
</p>
</form>
</div>
</body>
</html>

Datei myTable.sql
  --
  -- Table structure for table `myTable`
  --

  CREATE TABLE `myTable` (
    `ID` int NOT NULL,
    `A` varchar(200) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT NULL,
    `B` varchar(200) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT NULL,
    `C` varchar(200) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT NULL
  ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;

  ALTER TABLE `myTable`
    ADD UNIQUE KEY `request_id` (`ID`);

  ALTER TABLE `myTable`
    MODIFY `ID` int NOT NULL AUTO_INCREMENT;
  COMMIT;

Datei dbToolsClass.php
<?PHP
class dbTools{
  private $table;
  private $connect;
  private $keyName;
  public $fieldsName;
  public $columns;

  //-------------------------------------------------------------
   function __construct($connect, $table) {
    $this-> connect = $connect;
    $this -> table = $table;
    $this -> fieldsName = $this->getFieldsName();
  }
  //----------------------------------------------------------
  private function getFieldsName(){
    $connect = $this->connect;
    $table = $this->table;
    $query = "SHOW FIELDS FROM `$table`";
    $out = [];
    $stmt = $connect -> prepare($query);
    $stmt -> execute();
    $result = $stmt->get_result();
    foreach($result -> fetch_all(MYSQLI_NUM) as $row)
      $out[] = $row[0];
    $stmt->close();
    $this -> keyName =  $out[0];
    $this -> columns = count($out);
    return $out;
  }
  //----------------------------------------------------------
  public function delRow($key){
    $connect = $this->connect;
    $table = $this->table;
    $keyName = $this->keyName;
    $query = "DELETE FROM $table WHERE $keyName = ? ";
    $stmt = $connect -> prepare($query);
    $stmt -> execute([$key]);
    $stmt->close();
  }
  //-------------------------------------------------------
  public function select( $filter="",$filterValArr=null, $felds="*" ){
    $connect = $this->connect;
    $table = $this->table;
    $keyName = $this->keyName;

    if(strpos($filter,"?") !== false){
      if($filter == "?")
        $filter = "$keyName = ?";
      $filter = "WHERE $filter";
    }
    $query = "SELECT $felds FROM $table $filter";
    $stmt = $connect->prepare($query);
    $stmt->execute($filterValArr);
    $result = $stmt->get_result();
    $out = $result->fetch_all(MYSQLI_ASSOC);
    $stmt->close();
    return $out;
  }
  //----------------------------------------------------------
  public function saveRow($values, $felds=null){
    $connect = $this->connect;
    $table = $this->table;
    $keyName = $this->keyName;

    $feldsArr = $felds?explode(",",$felds):$this->fieldsName;
    if($this->is_key($values[0]))
    {
      unset($feldsArr[0]);
      $set = implode("=?,",$feldsArr)."=?";
      $query = "UPDATE $table SET $set WHERE $keyName = ?";
      $keyVal = $values[0];
      unset($values[0]);
      $values=array_merge($values,[$keyVal]);
    }
    else
    {
      if(!$felds)
        $felds = implode(",",$feldsArr);
      $par = implode(",",array_fill(0, count($feldsArr),"?"));
      $query = "INSERT INTO $table ($felds) VALUES ($par)";
    }
    $stmt = $connect->prepare($query);
    $stmt->execute($values);
    $result = $stmt->get_result();
    $stmt->close();
    return $result;
  }
  //----------------------------------------------------------
  private function is_key($value){
    $connect = $this->connect;
    $table = $this->table;
    $keyName = $this->keyName;
    $query = "SELECT count($keyName) as res FROM `$table` WHERE $keyName = ? ";
    $stmt = $connect->prepare($query);
    $stmt->execute([$value]);
    $stmt->bind_result($res);
    $stmt->fetch();
    $stmt->close();
    return $res;
  }
  //----------------------------------------------------------
}

Datei tableEditorFunctions.php
<?PHP
#####################################################################
function getTableRows($selected, $tableHeader, $columns){
  if(count($selected)>0)
  {
    $tableRows =[];
    foreach($selected as $row)
    {
      $buf = [];
      foreach($tableHeader as $col => $colName)
      {
        if($col==0) $ID = $row[$colName];
          $buf[] = $row[$colName];
      }
      $tableRows[$ID] = $buf;
    }
  }
  else
    $tableRows[0] = array_fill(0, $columns, '');
    return $tableRows;
}
#####################################################################
function setTdTag($value, $iCol=null){
  if(isset($iCol))
    return <<<HTML
    <td><input type = "text" name="col{$iCol}[]" value="$value"></td>
    HTML;
  else
    return <<<HTML
    <td>$value</td>
    HTML;
}
#####################################################################
function setTableRow($rowArray, $inpRow=false, $iRow = 0){
  $outHtml = "<tr>";
  foreach($rowArray as $iCol => $value)
  {
    if($iCol == 0)
    {
      if($inpRow)
      {
        $outHtml .= "<td><input name='checkRow[]' type='checkbox' value='$iRow'>";
        $outHtml .= "<input type ='hidden' name = 'iRow[]' value='$iRow'></td>";
      }
      else
        $outHtml .= "<td> </td>";
    }
    else
    {
      if($inpRow)
        $outHtml .= setTdTag($value, $iCol);
      else
         $outHtml .= setTdTag($value);
    }
  }
  $outHtml .= "<tr>";
  return $outHtml;
}
#####################################################################
function setRowToSave($key, $i, $columns){
  $row = [];
  $row[] = $key;
  for($j=1; $j<$columns; $j++)
    $row[]= $_POST["col".$j][$i];
  return $row;
}
#####################################################################

Datei tableEditor.js
/*
  JS script tableEditor
  Version: 2.0, 2026-05-16
  Author: Vladimir Kheifets (vladimir.kheifets@online.de)
  Copyright (c) 2026 Vladimir Kheifets All Rights Reserved
*/
window.addEventListener("load", () => {
  msg1 = "Do you really want to delete the table rows?";
  msg2 = "Select the table rows you want to delete";
  //--------------------------------------------------------
  addRow = function(){
    table = document.getElementById(tableID);
    newRow = table.insertRow(-1);
    newCell = newRow.insertCell(-1);
    colHTML = "<input name='checkRow[]' type='checkbox' value='0'>";
    colHTML += "<input type ='hidden' name = 'iRow[]' value='0'>";
    newCell.innerHTML = colHTML;
    for (var i = 1; i < columns; i++) {
      newCell = newRow.insertCell(-1);
      newCell.innerHTML = '<input type = "text" name="col'+i+'[]" value="">';
    }
  }
  //--------------------------------------------------------
  delRows = function(){
    if(!confirm(msg1)) return;
    table = document.getElementById(tableID);
    checkEl = document.querySelectorAll("input[type='checkbox']");
    checkCount = checkEl.length;
    goSubmit = false;
    noCheked = true;
    for (i = 0; i < checkCount; i++) {
      if(checkEl[i].checked)
      {
        noCheked = false;
        r = checkEl[i].parentNode.parentNode.rowIndex;
        if(checkEl[i].value == 0){
          table.deleteRow(r);
          checkCount = checkEl.length;
          if(checkCount==1)
            addRow();
        }
        else
          goSubmit = true;
      }
    }

    if(noCheked)
    {
      alert(msg2);
      return;
    }
    else if(goSubmit)
    {
      table.parentNode.submit();
    }
  }
  //-----------------------------------------------------------------
  buttonAdd = document.querySelectorAll("input[value='Add row']")[0];
  buttonAdd.addEventListener("click", addRow);
  //-----------------------------------------------------------------
  buttonDel = document.querySelectorAll("input[value='Delete row']")[0];
  buttonDel.addEventListener("click", delRows);
  //-----------------------------------------------------------------
});

Datei tableEditor.css
/*
  CSS tableEditor
  Version: 2.0, 2026-05-16
  Author: Vladimir Kheifets (vladimir.kheifets@online.de)
  Copyright (c) 2026 Vladimir Kheifets All Rights Reserved
*/

table{
  margin-top: 40px;
  border-collapse: collapse;
}

td{
  padding:0 2 0 2;
  text-align: center;
  width: 50px;
}

td + td{
  width: 150px;
}

input[type="text"]{
  border-width:0px;
  border:none;
}

p input{
  width: 150px;
  margin: 20 20 0 20;
}

Browserübergreifende Interpretation des HTML-Attributs „title“, Demo, Download title.zip 151Kb

Datei index.html
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>HTML title Attribute modified with CSS and JS</title>
<script type="text/javascript" src="CompactDOM.min.js"></script>
<script type="text/javascript" src="help.js" ></script>
</head>
<body>
<div align=center>
<input name="language" type="radio" title="L5" value="en">EN
<input name="language" type="radio" title="L6" value="de">DE
<input name="language" type="radio" title="L7" value="ru">RU
<hr>
<p><br><img src="DSC03899.jpg" title="R1" border=1 height="100"></p>
<p><input type="text" title="2" ></p>
<p><textarea title="R3" ></textarea></p>
<p><a title="T4" href="https://www.php.net" target="_blank">https://www.php.net</a></p>
<p>
<table border=1 width="100">
<tr>
	<td title="L8">100</td>
	<td title="9">212</td>
	<td title="R10">373,15</td>
</tr>
</table>
</p>
<p><input type="text" title="[R]The title is set to text in English<br>with the prefix [R] (right orientation)"></p>
<p><input type="text" title="[L]The title is set to text in English<br>with the prefix [L] (left orientation)"></p>
<p><input type="text" title="The title is set to text in English<br>without the prefix (defaul bottom orientation)"></p>
</div>
</body>
</html>

CompactDOM Datei help.js
//Create tags:
__.link("index.css,help.css");

//Create window.addEventListener("load", (event) => {
__.ready( () => {
	
	//Create  tag:<div></div> and defined element divHelp
	divHelp = __.create(1,{tag:"div"});
	
	//Defined eLWithAttrTitle - collection (list) of HTML elements with attribute title
	eLWithAttrTitle = _("[title]");
	
	//Defined selected language from GET["la"] or if udefinde language is "en"   
	language = location.href.split("la=")[1];
	if(!language) language = "en";

	//Defined languageRadio - collection (list) of HTML elements with attribute name='language'
	languageRadio = _("input[name='language']");

	//Looping through all elements from the languageRadio collection
	languageRadio.each(function(Elradio){
		
		// For each element, we get the value of the v
		// alue attribute into the laAttr variable		
		laAttr = Elradio.attr("value");

		if(laAttr == language)
		{
			// If the value of laAttr matches the value of the 
			// selected language, then the checked and disabled 
			// attributes are set for this element.
			Elradio.checked(true);
			Elradio.lock("disabled",true);
		}
		else
		{
			// For other elements сreate
			// addEventListener("click", (event) => {
			Elradio.click(function(){

				// The value of the value attribute of the checked
				// element is determined. Then the ntml get request is sent
				language = Elradio.val();
				location.assign("?la="+language);
			});
		}
	});
	//-------------------------------------------------
	showTitle = function(titleKey, El){
		/*
		Predefined:
		- textOfTitles (object) from AJAX response (in the createTitles function)
		The keys of this object are text indexes and the values are texts:
		- divHelp (object) HTML-element Div container for title output

		input:
		- titleKey - (string) title attribute value - [L|R|T|B|null][number]
		- El - (object) element with the title attribute
		*/

		/*
		The position of each element is determined in pos object:
		pos.left, pos.right, pos.top, pos.bottom, pos.height, pos.width
		*/
		pos  = El.position();

		/*
		Here the values are assigned to the LRTB variable, which determines the orientation of the divHelp element, and the ind variable, which determines the text key from the textOfTitles object.
		If the first character from titleKey is a number, then the variable LRTB defining
		the default value "B" and the variable ind the value with the variable titleKey.
		Otherwise, the LRTB variable is assigned the value of the first character from the titleKey variable and the ind variable the value of the following characters from the titleKey variable.
		*/

		patternD = /^\d{1,}$/;
		patternT1 = /^\[[LRTB]\]/;
		patternT2 = /^[LRTB]\d{1,}$/;
		LRTB = "B";
		if(patternD.test(titleKey))
		{
			ind = titleKey;
		}
		else if(patternT1.test(titleKey))
		{
			LRTB = titleKey.substr(1,1);
			contenDivHelp = titleKey.substr(3);
			ind = null;
		}
		else if(patternT2.test(titleKey))
		{
			LRTB = titleKey.substr(0,1);
			ind = titleKey.substr(1);
		}
		else
		{
			contenDivHelp = titleKey;
		}

		if(ind) contenDivHelp = textOfTitles[ind];

		//The content of the divHelp element is determined from the textOfTitles object
		divHelp.content(contenDivHelp);

		// The css method sets a CSS attribute for the divHelp element
		divHelp.css("display:block");

		//The position of the divHelp element is determined in posD object:
		//posD.left, posD.right, posD.top, posD.bottom, posD.height, posD.width
		posD = divHelp.position();

		//In switch(LRTB), the values of the variables cl, Y, T are determined.
		switch (LRTB){

		  case 'L':
		    cl = "left";
		    Y = pos.top+(pos.height)/2-20;
			X = pos.left - posD.width - 10;
		    break;

		  case 'R':
		  	cl = "right";
		  	Y = pos.top+(pos.height)/2-20;
			X = pos.right + 10;
		  	break;

		  case 'T':
		   	cl = "top";
		   	Y = pos.top - posD.height - 10;
			X = pos.left - 20;
		    break;

		  case 'B':
		   	cl = "bottom";
		   	Y = pos.bottom + 10;
			X = pos.left;
		    break;
		}

		// The attr method sets a class attribute for the divHelp element
		divHelp.attr("class","popup " + cl);

		// The css method sets a CSS attribute for the divHelp element
		divHelp.css("top:"+Y+"px; left:"+X+"px");

		// The show method sets the CSS property opacity:1
		// (with animation) for the divHelp element.
		divHelp.show(1);
	}
	//-------------------------------------------------
	createTitles = function(rsp){
		textOfTitles = rsp["texts"];
		eLWithAttrTitle.each(function(El){
			title = El.attr("title");
			El.attr({rem:"title"});
			El.attr("data-title",title);
			//-----------------------------------------
			El.mouseenter(function(){
				title = El.attr("data-title");
				showTitle(title, El);
			});
			//-----------------------------------------
			El.mouseleave(function(){
			  	divHelp.css("opacity: 0;display:none");
			});
			//-----------------------------------------
		});

	}
	//-------------------------------------------------
	__.send(
	{
		url:"languages/"+language+".json",
		method:"get",
		responseType:"json",
		func:createTitles
	}
	);
});

English Texts in Datei
{
	"texts":{
	"1":"<div style='line-height:30px'> Neuschwanstein Castle</div> <img src='DSC03899.jpg' height='300'> ",
	"2":"Enter your name<br>in Latin letters",
	"3":"Write about yourself here",
	"4":"PHP manual here",
	"5":"English",
	"6":"Deutsch",
	"7":"Русский",
	"8": "Degree Celsius",
	"9": "Fahrenheit",
	"10": "Kelvin"
	}
}

Datei index.css
body{
	font-family: arial;
	font-size: 16px;
}

body > div{
	padding-bottom: 50px;
}


table{
	margin: 20 0 20 0;
	border-collapse: collapse;
	border: 1px solid #CCC;
}

input[type="text"],textarea{
	margin-top: 10 0 0 0;
	width: 186px;
}

a{
	margin-top: 20px;
}

table td{
	min-width: 60px;
	text-align: center
}

Datei help.css
/* CSS Document Help*/

.popup {
	border: 1px solid #71653a;
	font-family:arial;
	font-size:16px !important;
	border-radius: 5px;
	padding: 10px;
	color: #71653a;
	background-color: #fff1be;	
	white-space: pre;	
	box-shadow: 0 1px 10px rgba(0, 0, 0, 0.2);    		 
	opacity: 0;
	transition-property: opacity;
	transition-duration: 600ms;
	transition-timing-function: cubic-bezier(0.02, 0.01, 0.47, 1);
	Z-INDEX: 999999;
	POSITION: absolute	
}
		
.popup:before, .popup:after {
    content: "";
    position: absolute;
}

.popup.left:before {
    border-left: 10px solid #71653a;
    border-top: 10px solid rgba(113, 101, 58, 0);
    border-bottom: 10px solid rgba(113, 101, 58, 0);
    right: -10px;
    top: 10px;
}

.popup.left:after {
    border-left: 10px solid #fff1be;
    border-top: 10px solid rgba(255, 241, 190, 0);
    border-bottom: 10px solid rgba(255, 241, 190, 0);
    right: -9px;
    top: 10px;
}

.popup.right:before {
    border-right: 10px solid #71653a;
    border-top: 10px solid rgba(113, 101, 58, 0);
    border-bottom: 10px solid rgba(113, 101, 58, 0);
    left: -10px;
    top: 10px;
}

.popup.right:after {
    border-right: 10px solid #fff1be;
    border-top: 10px solid rgba(255, 241, 190, 0);
    border-bottom: 10px solid rgba(255, 241, 190, 0);
    left: -9px;
    top: 10px;
}

.popup.top:before {
    border-left: 10px solid rgba(113, 101, 58, 0);
    border-right: 10px solid rgba(113, 101, 58, 0);
    border-top: 10px solid #71653a;
    left: 20px;
    bottom: -10px;
}

.popup.top:after {
    border-left: 10px solid rgba(255, 241, 190, 0);
    border-right: 10px solid rgba(255, 241, 190, 0);
    border-top: 10px solid #fff1be;
    left: 20px;
    bottom: -9px;
}

.popup.bottom:before {
    border-left: 10px solid rgba(113, 101, 58, 0);
    border-right: 10px solid rgba(113, 101, 58, 0);
    border-bottom: 10px solid #71653a;
    left: 20px;
    top: -10px;
}

.popup.bottom:after {
    border-left: 10px solid rgba(255, 241, 190, 0);
    border-right: 10px solid rgba(255, 241, 190, 0);
    border-bottom: 10px solid #fff1be;
    left: 20px;
    top: -9px;
}

Benutzerauthentifizierung Download auth.zip 4.53Kb

Datei tableUser.sql

--
-- Table structure for table `users`
--

CREATE TABLE `users` (
  `id` int NOT NULL,
  `login` varchar(10) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL,
  `password` char(64) NOT NULL,
  `email` varchar(250) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci DEFAULT NULL,
  `role` int NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;

--
-- Dumping data for table `users`
--

INSERT INTO `users` (`id`, `login`, `password`, `email`, `role`) VALUES
(1, 'demouser', '$2y$10$3cL2fIc.j0A0duG48E/7AObPpS871J5ASDjz79IqkS1Ke.o8vNT1C', 'demo.user@domen.com', 1);

--
-- Indexes for dumped tables
--

--
-- Indexes for table `users`
--
ALTER TABLE `users`
  ADD PRIMARY KEY (`id`);

--
-- AUTO_INCREMENT for dumped tables
--

--
-- AUTO_INCREMENT for table `users`
--
ALTER TABLE `users`
  MODIFY `id` int NOT NULL AUTO_INCREMENT, AUTO_INCREMENT=2;
COMMIT;

Ein-Faktor-Authentifizierung (SFA), Demo, User: Demouser  Password: test

Datei authSFA.php
<?php
/*
    Single-Factor Authentication (SFA) in PHP
    Version: 2.0, 2026-05-18
    Author: Vladimir Kheifets vladimir.kheifets@online.de
    Copyright © 2026 Vladimir Kheifets All Rights Reserved
*/

session_start();
$inclFile = "../incl/db.php";
if(file_exists($inclFile)){
  require_once($inclFile);
}
else
  $connect = new mysqli("127.0.0.1:3306","root","","test");

###############################################
if (filter_input(INPUT_POST, 'logout'))
{
    unset($_SESSION['user_id']);
    unset($_SESSION['role']);
}
else if ($login = filter_input(INPUT_POST, 'login') )
{
    $stmt = $connect->prepare("SELECT id, role, password FROM `users` WHERE login = ?");
    $stmt->execute([$login]);
    $stmt->bind_result($id, $role, $password);
    $stmt->fetch();
    if ($password)
    {
        if (password_verify($_POST['password'], $password))
        {
            $_SESSION['user_id'] = $id;
            $_SESSION['role'] = $role;
            $message = "";
            $out["error"]=0;
            $out["message"]="";
        }
        else
        {
           $out["error"]=2;
           $out["message"]="Incorrect password";
        }
    }
    else
    {
        $out["error"]=1;
        $out["message"]="Not found user";
    }
    header('Content-Type: application/json; charset=utf-8');
    echo json_encode($out);
    exit;
}
?>
<html>
<head>
<title>Single-Factor Authentication (SFA)</title>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0">
</head>
<body>
<?
if(isset($_SESSION['user_id']) AND isset($_SESSION['role']))
{
    echo "Successful Single-Factor Authentication(SFA)<pre>";
    print_r($_SESSION);
    echo <<<HTML
        <form method="post" action="">
        <input type="submit" name="logout" value="Logot">
        </form>
    HTML;
}
else
    echo <<<HTML
    <script type="text/javascript" src="authSFA.js"></script>
    <form id="log" method="post" action="">
        <input type="text" name="login" placeholder="Name" required>
        <input type="password" name="password" placeholder="Password" required>
        <input type="submit" value="Login">
        <div id="error"></div>
    </form>
    </body>
    </html>
    HTML;


Datei authSFA.js
/*
    Single-Factor Authentication (SFA) in JS
    Version: 2.0, 2026-05-18
    Author: Vladimir Kheifets vladimir.kheifets@online.de
    Copyright © 2026 Vladimir Kheifets All Rights Reserved
*/

window.addEventListener("load", () => {
    const form = document.getElementById("log");
    const err = document.getElementById("error");
    const inp = document.querySelectorAll("input");
    //------------------------------------------------
    for (i in [0,1]) {
        inp[i].addEventListener("click", (e) => {
            e.target.value = "";
            err.innerHTML = "";
        });
    }
    //------------------------------------------------
    const sendLogin = (e) => {        
        e.preventDefault();
        let fd = new FormData(form);
        fetch("?", {
          method: "POST",
          body: fd,
        })
        .then(response => response.json())
        .then((data) => {
          if(data["error"])
            err.innerHTML = data["message"];
          else
          {
            while (form.firstChild)
                form.removeChild(form.firstChild);
            form.submit();
          }
        });
    };
    //--------------------------------------------------
    form.addEventListener("submit", sendLogin);
    //--------------------------------------------------
});

Zwei-Faktor-Authentifizierung (2FA), Demo, User: Demouser  Password: test

Datei auth2FA.php
<?php
/*
    Two-Factor Authentication (2FA) in PHP
    Version: 2.0, 2026-05-18
    Author: Vladimir Kheifets vladimir.kheifets@online.de
    Copyright © 2026 Vladimir Kheifets All Rights Reserved
*/
session_start();
$inclFile = "../incl/db.php";
if(file_exists($inclFile)){
  require_once($inclFile);
}
else
  $connect = new mysqli("127.0.0.1:3306","root","","test");
//-----------------------------------------------------------------
if(filter_input(INPUT_POST, 'logout'))
{
    unset($_SESSION['user_id']);
    unset($_SESSION['role']);
}
else if ($login = filter_input(INPUT_POST, 'login')){
    $stmt = $connect->prepare("SELECT id FROM `users` WHERE login = ?");
    $stmt->execute([$login]);
    $stmt->bind_result($id);
    $stmt->fetch();
    if($id)
    {
        $_SESSION['user_id'] = $id;
        $out["error"] = 0;
        $out["message"]="";
    }
    else
    {
        $out["error"] = 1;
        $out["message"]="Not found user";
    }
    header('Content-Type: application/json; charset=utf-8');
    echo json_encode($out);
    exit;
}
else if($password = filter_input(INPUT_POST, 'password'))
{
    session_start();
    $conn = mysqli_connect('127.0.0.1:3306','root','','test');
    $stmt = $connect->prepare("SELECT role, password FROM `users` WHERE id = ?");
    $stmt->execute([$_SESSION['user_id']]);
    $stmt->bind_result($role, $passwordInDb);
    $stmt->fetch();
    if($passwordInDb)
    {
        if (password_verify($password, $passwordInDb))
        {
            $_SESSION['role'] = $role;
            $out["error"]=0;
        }
        else
        {
          $out["error"]=2;
          $out["message"]="Incorrect password";
        }
    }
    header('Content-Type: application/json; charset=utf-8');
    echo json_encode($out);
    exit;
}
echo <<<HTML
<html>
<head>
<title>Two-Factor Authentication (2FA)</title>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0">
</head>
<body>
HTML;

if(isset($_SESSION['user_id']) AND isset($_SESSION['role']))
{
    echo "Successful Two-Factor Authentication (2FA)<pre>";
    print_r($_SESSION);
    echo <<<HTML
    <form method="post" action="">
    <input type="submit" name="logout" value="Logot">
    </form>
    HTML;
}
else
    echo <<<HTML
    <script type="text/javascript" src="auth2FA.js"></script>
    <form id="log" method="post" action="">
        <input type="text" name="login" placeholder="Name" required>
        <input type="submit" id="go" value="Login">
        <div id="error"></div>
    </form>
    </body>
    </html>
    HTML;

Datei auth2FA.js
/*
    Two-Factor Authentication (2FA) in JS
    Version: 2.0, 2026-05-18
    Author: Vladimir Kheifets vladimir.kheifets@online.de
    Copyright © 2026 Vladimir Kheifets All Rights Reserved
*/

window.addEventListener("load", () => {
    const form = document.getElementById("log");
    const go = document.getElementById("go");
    const err = document.getElementById("error");
    const inp = document.querySelectorAll("input");
    //------------------------------------------------
    const goValue = { 1:"login", 2:"Continue" };
    const inpAttr = {
       type:"password", name:"password", placeholder:"Password"
    };

    const inpSet = (inpAttr) => {
        inp[0].value="";
        for(attr in inpAttr)
            inp[0].setAttribute(attr,inpAttr[attr]);
    }

    for (i in [0,1]) {
        inp[i].addEventListener("click", (e) => {
            e.target.value = "";
            err.innerHTML = "";
        });
    }
    //------------------------------------------------
    const sendLogin = (e) => {
      e.preventDefault();
      let fd = new FormData(form);
      fetch("?", {
      method: "POST",
      body: fd,
      })
      .then(response => response.json())
      .then((data) => {
        if(data["error"]){
            err.innerHTML = data["message"];
            go.value = goValue[step];
        }
        else
        {
            switch(step)
            {
                case 1:
                    inpSet(inpAttr);
                    go.value = goValue[2];
                    step++;
                    break;
                case 2:
                     while (form.firstChild)
                        form.removeChild(form.firstChild);
                    form.submit();
                    break;
            }
        }
       });
    };
    //------------------------------------------------
    var step = 1;
    form.addEventListener("submit", sendLogin);
    //------------------------------------------------
});

Berechnung der Zeit für Kontakte mit Kunden oder Partnern, Demo, Download timeCalculator.zip 35.7Kb

index.pnp
<?
/*
	Calculating the time of contacts with clients or partners
	PHP-Script index.php
	Version: 3.1, 2026-05-19
	Author: Vladimir Kheifets (kheifets.vladimir@online.de)
	Copyright (c) 2026 Vladimir Kheifets All Rights Reserved
*/

session_start();
########################################################################
$expires=time()+2592000;
$path="/";
$identifiers = timezone_identifiers_list();
$preffArr=["my","client"];
if(!empty($_COOKIE["se"]))
{
	$selected = (array)json_decode($_COOKIE["se"]);
	$laC=$selected["la"];
}
else if(!empty($_SESSION["se"]))
{
	$selected=$_SESSION["se"];
	$laC=$selected["la"];
}

foreach($preffArr as $preff)
		foreach(["Continent","City","BeginTime","EndTime"] as $i=>$name)
			${$preff.$name."Sel"}=isset($selected)?$selected[$preff][$i]:"";

if(isset($_GET["la"]) AND !empty($_COOKIE["se"]))
{
	$selected["la"] = $_GET["la"];
	$se=json_encode($selected);
	setcookie("se", $se, $expires,$path);
	$_SESSION["se"]=$selected;
}
$la=empty($_GET["la"])?empty($laC)?"en":$laC:$_GET["la"];
#########################################################################
$CookieLink = !empty($_COOKIE["se"])?"✔":"";
$CookieLink = $CookieLink." <span>Cookie</span>▼";
#########################################################################
$buf=file("dictionary/$la.txt");
$la_liste=file("dictionary/language.txt");
$defhref="https://www.alto-booking.com/timeCalculator/";
$href_suff="?la=";
$header_link="";
foreach($la_liste as $line)
{
	$tmp = explode("~",trim($line));
	$lang=$hreflang=$tmp[0];
	$language[$lang] = $tmp[1];
	if(isset($tmp[2]))	$hreflang.="-".$tmp[2];
	$href = $defhref.$href_suff.$lang;
	$header_link .= "<link rel=\"alternate\" hreflang=\"$hreflang\" href=\"$href\" />\n";
}
$la_active=$language[$la];
//--------------------------------------------
foreach($buf as $line)
{
	$tmp = explode("~",$line);
	$dic[$tmp[0]] = trim($tmp[1]);
}
$dicKeys = array_keys($dic);
$err_ArrKeys = ["0","1","2","3","msg1","my","client","unsel","time"];
$varArr=array_diff($dicKeys, $err_ArrKeys,["myD","clientD"]);
foreach($varArr as $keyDic) $$keyDic=$dic[$keyDic];
//----------------------------------------------------------------
foreach ($identifiers as $value)
{
	$tmp = explode("/",$value);
	if(isset($tmp[1]))
	{
		$city=$tmp[1];
		if(isset($tmp[2])) $city.="/".$tmp[2];
		$continentCity[$tmp[0]][] = $city;
	}
}
//-------------------------------------------
if(isset($_GET['Continent']) AND isset($_GET['idCity']))
{
	$Continent=$_GET['Continent'];
	echo json_encode($continentCity[$Continent]);
	exit;
}
if(isset($_GET['go']))
{
	extract($_POST);
	saveSelected();
	if(isset($_COOKIE["se"]))
	{
		saveSelected(true,$expires, $path);
	}
	$myTimeZone = "$myContinent/$myCity";
	$clientTimeZone = "$clientContinent/$clientCity";
	$myTimeZoneStr = str_replace("_"," ",$myTimeZone);
	$clientTimeZoneStr	= str_replace("_"," ",$clientTimeZone);
	date_default_timezone_set($myTimeZone);
	$d = new DateTime("now", new DateTimeZone($myTimeZone));
	$myTimeNow = getTime($d);
	echo DicReplaceV($myNow),"<br>";
	$d = new DateTime("now", new DateTimeZone($clientTimeZone));
	$clientTimeNow=getTime($d);
	echo DicReplaceV($clientNow),"<br>";
	$myTime=getOfficeTime($myBeginTime, $myEndTime, $myTimeZone);

	$clientTime=getOfficeTime($clientBeginTime, $clientEndTime, $clientTimeZone);
	if(@count($myTime)>0 AND @count($clientTime)>0)
		$res = array_intersect( $myTime, $clientTime);
	if(@count($res)>0)
	{
		$contactTimeBegin = min($res);
		$contactTimeEnd = max($res);
		echo DicReplaceV($contact),"<br>";
	}
	else
		echo DicReplaceV($contactNot),"<br>";
	$cookie_b=isset($_COOKIE["se"])?$cookieN:$cookieY;
	echo <<<EOF
	<div align="center">
	<div class="set-cookie">
	$cookie
	<div align="center"><p id="cookie_p">$cookie_b</p></div>
	</div>
	EOF;
	exit;
}
if(isset($_GET["_la"]))
{
	extract($_POST);
	saveSelected();
	if(isset($_COOKIE["se"]))
	{
		saveSelected(true,$expires, $path);
	}
	echo "<div class=\"la-liste\">";
	foreach($language as $laS =>$name)
	{
		echo <<<HTML
		<span id="la_$laS">$name</span>
		HTML;
	}
	echo "</div>";
	exit;
}
else if(isset($_GET["_cookie"]))
{
	$cookie_b=isset($_COOKIE["se"])?$cookieN:$cookieY;
	echo <<<EOF
	<div class="set-cookie">
	$cookie
	<div align="center"><p id="cookie_p">$cookie_b</p></div>
	</div>
	EOF;
	exit;
}
else if(isset($_GET["_cookie_p"]))
{
	$link="Cookie";
	if(isset($_COOKIE["se"]))
	{
		saveSelected(true, 0, $path);
	}
	else
	{
		extract($_POST);
		saveSelected(true, $expires, $path);
	}
	exit;
}
############################################################
foreach($preffArr as $preff)
{
	$ContinentSel=${$preff."ContinentSel"};
	${$preff."Continents"} = ArrToOption(array_keys($continentCity), $ContinentSel, $optContinents );
	${$preff."Cities"}=ArrToOption(null, ${$preff."CitySel"}, $optCities,1);
	${$preff."BeginTimes"}=ArrToOption(getTimeArr(5,22),${$preff."BeginTimeSel"},$timeBegin);
	${$preff."EndTimes"}=ArrToOption(getTimeArr(5,22),${$preff."EndTimeSel"},$timeEnd, );
}
echo <<<HTML
<html lang="$la" xml:lang="$la">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no, user-scalable=0" >
<meta name="description" content="$desc">
<meta name="keywords" content="$keyw">
<meta name="author" content="Vladimir Kheifets">
<meta name="google" value="notranslate"/>
$header_link
<link rel="stylesheet" href="css/timeCalculator.css" >
<title>$title</title>
</head>
<body class="notranslate">
<form>
<div align="center">
<div class="DinlB">
<div id="cookie">$CookieLink</div>
<div id="la"><span>$la_active</span>▼</div>
<h1 class="title">$title</h1>
HTML;
foreach($preffArr as $preff)
{
$divTitle=$dic[$preff."D"];
echo <<<HTML
<div class="select-block" id="{$preff}Time">
<div>$divTitle</div>
<select name="{$preff}Continent" id="{$preff}Continent">
${$preff."Continents"}
</select>
<select name="{$preff}City" id="{$preff}City">
${$preff."Cities"}
</select>
<select name="{$preff}BeginTime">
${$preff."BeginTimes"}
</select>
<select name="{$preff}EndTime">
${$preff."EndTimes"}
</select>
</div><br>
HTML;
}
$Y=date("Y");
echo <<<HTML
<button type="button">$button</button>
<div class="copy"><p>©</p>$Y Alto Booking</div>
</div>
</div>
</form>
<script>
//-------------------------------------------------------
var err_Arr={};
HTML;
foreach ($err_ArrKeys as $keyDic)
	echo "err_Arr[\"$keyDic\"]=\"{$dic[$keyDic]}\";";
echo "var optCities=\"{$dic['optCities']}\";";
echo <<<HTML
</script>
<script type="text/javascript" src="CompactDOM.min.js"></script>
<script type="text/javascript" src="timeCalculator.js"></script>
</body>
</html>
HTML;
//---------------------------------------------------
function  getOfficeTime($OfficeBeginTime, $OfficeEndTime, $zone){
  $Time=[];
  $TimeZone=new DateTimeZone($zone);
  $Begin = new DateTime("today $OfficeBeginTime", $TimeZone);
  $BeginT = $Begin->getTimestamp();
  $End = new DateTime("today $OfficeEndTime", $TimeZone);
  $EndT = $End->getTimestamp();
  $t=$BeginT;
  while ($t <= $EndT)
  {
    $Time[] = date("H:i",$t);
    $t = strtotime("+1 hour", $t);
  }
  return $Time;
}
//-------------------------------------------------
function getTime($d){
return $d->format('H:i')." GMT". $d->format('P');
}
//---------------------------------------------------
function ArrToOption($arr, $selected, $default="",$textRepl=null){
	if($default)
		$options="<option>$default</option>";
	if($arr)
	{
		foreach($arr as $value)
		{
			$sel=$value==$selected?"selected":"";
			if($textRepl)
				$options .= "<option $sel value='$value'>".str_replace("_"," ",$value)."</option>";
			else
				$options .= "<option $sel>$value</option>";
		}
	}
	return $options;
}
//---------------------------------------------------
function getTimeArr($begin,$end){
for ($time=$begin; $time <=$end ; $time++)
{
	$times[]=sprintf("%02d:00", $time);
}
return $times;
}
//---------------------------------------------------
function DicReplaceV($text){
    $pattern="{\{(.*?)\}}";
    preg_match_all($pattern,$text,$m, PREG_PATTERN_ORDER);
	$k=count($m[1]);
	for ($i=0; $i<$k; $i++)
	{
		$a=$m[0][$i];
		$b=$m[1][$i];
	    global $$b;
		$text = str_replace($a,$$b,$text);
	}
	return $text;
}
//---------------------------------------------------
function saveSelected($inCookie=null,$expires=null, $path=null){
	if($inCookie AND $expires==0)
	{
		setcookie("se", "", time()-7200 ,$path);
		unset ($_COOKIE["se"]);
		return;
	}
	global $la,$myContinent, $myCity, $myBeginTime, $myEndTime;
	global $clientContinent, $clientCity, $clientBeginTime, $clientEndTime;
	$selected["la"]=$la;
	$selected["my"]=[$myContinent,$myCity, $myBeginTime, $myEndTime];
	$selected["client"]=[$clientContinent, $clientCity, $clientBeginTime, $clientEndTime];
	if($inCookie)
	{
		$se=json_encode($selected);
		setcookie("se", $se, $expires,$path);
	}
	else
		$_SESSION["se"]=$selected;
}
?>

CompactDOM Datei timeCalculator.js
/*
	Calculating the time of contacts with clients or partners
	CompactDOM-Script timeCalculator.js
	Version: 3.1, 2026-05-19
	Author: Vladimir Kheifets (kheifets.vladimir@online.de)
	Copyright (c) 2026 Vladimir Kheifets All Rights Reserved
*/

__.ready(() => {
__.link("css/modal.css,css/button_to_up.css");

const myContinent = _("#myContinent");
const clientContinent = _("#clientContinent");
const myCity = _("#myCity");
const clientCity = _("#clientCity");
const preff = ["my","client"];

const viewCities = function (Cities,id){
	if(Cities && id)
	{			
		Cities.unshift(optCities);
		optCreat={};		
		Cities.forEach(function(item) {
			txt=__.ins("_",item," ");
			optCreat[item]=txt;	
		});		
		let elCities = _(id);
		elCities.content("");
		elCities.create(optCreat);
		elCities.lock(1, 0);
		elCities.css("background-color:#FFFFFF");
	}
	else
	{			
		evEl = _(this);		
		id = this.id.replace("Continent", "City");		
		Continent = evEl.value();				
		if(evEl.selected()>0)
		{
			url = "?Continent="+Continent+"&idCity="+id;
			par={
				url:url,
				to:"#"+id,
				func:viewCities,
				method:"get",
				responseType:"json"
			};			
			__.send(par);
		}
		else
		{
			let elCities = _("#"+id);
			elCities.content("");
			elCities.create([optCities]);
			elCities.lock(1,1);
			elCities.css("background-color:#F1F1F1");
		}
	}
};
//-------------------------------------------------------
const setCookie = function(){
	cookieInn =_("#cookie");
	txt = __.ins("se=",document.cookie)?"":"✔ ";
	txt += "<span>Cookie</span>▼"
	cookieInn.content(txt);
	url = __.url("_cookie_p=1");
	par={
		url:url,
		method:"post"
	};
	__.send(par);
	__.modal(0);
}
//-------------------------------------------------------
const goCalk = function (){
__.scroll(0);
//valid form-------------------------
msg=[];
preff.forEach(function(item) {
	AllSel = _("#"+item+"Time select").El;
	buf=[];
	msg[item]="";
	[0,1,2,3].forEach(function(i){
		if(_(AllSel[i]).selected()==0)	buf.push(err_Arr[i]);
	});
	if(buf[0])
	{
		msg[item]=err_Arr['unsel']+buf.join(', ')+". ";
	}
	FromInd=_(AllSel[2]).selected();
	ToInd=_(AllSel[3]).selected();
	if(FromInd>0 && ToInd>0 && FromInd >= ToInd)
		msg[item]+=err_Arr['time'];
	if(msg[item])	msg[item]=	err_Arr[item]+" "+msg[item]
});
if(msg['my'] || msg['client'])
{
	msg_text = err_Arr['msg1'] +"<br>";
	if(msg['my']) msg_text = msg_text+msg['my'];
	if(msg['my'] && msg['client']) msg_text += "<br>";
	if(msg['client']) msg_text+=msg['client'];	
	__.modal(msg_text);
	return;
}
//valid OK -------------------------------------------
//
    let url=__.url("go=1");
	par={
		url:url,
		method:"post",
		to:"#modal",
		func:viewModal
	};
	__.send(par);
};

const viewModal = function(rsp, to, req){
	_(to).modal(rsp);
	if(__.ins("_la",req))
	{
		setLa = function(){			
			la = this.id.substr(3);			
			_("form").attr({action:"?la="+la, method:"post"});
			__.send();
		}
		_("span[id^='la_']").click(setLa);
	}
	else
	{
		_("#cookie_p").click(setCookie);
	}
};
//---------------------------------------------------
const goSelector = function (){
	id = this.id;	
	get="_"+id+"=1";	
	url = __.url(get);	
	par={
		url:url,
		method:"post",
		to:"#modal",
		func:viewModal
	};
	__.send(par);
};
//-------------------------------------------------------

if(myContinent.selected()==0){
	myCity.lock(1,1);	
	myCity.css("background-color:#F1F1F1");
}
if(clientContinent.selected()==0)
{
	clientCity.lock(1,1);
	clientCity.css("background-color:#F1F1F1");
}
myContinent.change(viewCities);
clientContinent.change(viewCities);
_("button").click(goCalk);
_("#la").click(goSelector);
_("#cookie").click(goSelector);
__.scroll();
__.modal();
});

Ganzzahl-Arithmetik nach der „Spaltenmethode“, Demo, auf GitHab ,
Download сolumnOperations.zip 11.7Kb

index.php
<?
include_once("сolumnOperationsClass.php");
$сolOp = new сolumnOperations();
$title = "Columns operations of whole numbers";
$operations =
[
	"Addition of two numbers",
	"Addition of several numbers",
	"Multiplication of two numbers",
	"Subtraction of two numbers",
	"Division of two numbers"
];

$matheSymbols =
[
	"+",
	"×",
	"−",
	"÷"
];

$case = isset($_GET["case"])?$_GET["case"]:"0";
$ul = "<ul onChange = 'go()'>";
foreach($operations as $key => $val)
{
	$active = $key==$case?"class='active'":"onClick=go($key)";
	$ul .= "<li $active>$val</li>";
}
$ul .= "<li onClick = 'go($case)'>Generate other random operands</li>";
$ul .= "</ul>";

$trM = "";
switch ($case) {
	case 0:
		// Addition of two numberscode
		$matheSymbol = $matheSymbols[0];
		$augend =  rand(10, 100000);
		$addend = rand(10, 100000);
		$sum = $сolOp -> сolumnAddition($augend, $addend);
		$operands = $сolOp -> numberToDivStr($augend);
		$operands .= $сolOp -> numberToDivStr($addend);
		$result = $сolOp -> numberToDivStr($sum);
		$resultPHP = "of addition:";
		$sumPHP = $augend + $addend;
		$resultPHP .= "<br>$augend + $addend = $sumPHP";
		break;

	case 1:
		// Addition of several numbers
		$matheSymbol = $matheSymbols[0];
		for ($i=0; $i < 6; $i++)
			$numArr[$i] = rand(1, pow(10, $i+1));
		shuffle($numArr);
		//-------------------------------------------
		$sum = $сolOp -> сolumnAdditionArr($numArr);
		$result = $сolOp -> numberToDivStr($sum);

		$operands = "";
		foreach($numArr as $i => $number){
			$operands .= $сolOp -> numberToDivStr($number);
		}
		$sumFromArraySum = array_sum($numArr);
		$numArrToStr  = implode(" + ", $numArr);
		eval("\$sumPHP=$numArrToStr;");
		$resultPHP = "of addition:";
		$resultPHP .= "<br>$numArrToStr = $sumPHP";
		break;

	case 2:
		// Multiplication of two numbers
		$matheSymbol = $matheSymbols[1];
		$multiplier = rand(10, 100000);
		$multiplicand = rand(10, 100000);
		$product = $сolOp -> сolumnMultiplication($multiplier, $multiplicand);
		$strArr = $сolOp -> strArr;
		$operandsM = $сolOp -> numberToDivStr($multiplier);
		$operandsM .= $сolOp -> numberToDivStr($multiplicand);
		$result = $сolOp -> numberToDivStr($product);
		$operands = "";
		foreach($strArr as $i => $number)
			$operands .= $сolOp -> numberToDivStr($number, $i);
		$trM = <<<HTML
		<tr>
		<td> × </td>
		<td>$operandsM<hr></td>
		</tr>
		HTML;
		$matheSymbol = $matheSymbols[0];
		$resultPHP = "of multiplication:";
		$resultPHP .= "<br>$multiplier * $multiplicand = ".$multiplier * $multiplicand;
		break;

	case 3:
		// Subtraction of two numbers
		$matheSymbol = $matheSymbols[2];
		do{
			$minuend =  rand(10, 100000);
			$subtrahend = rand(10, 100000);
		} while ($subtrahend > $minuend);

		$differenz = $сolOp -> сolumnSubtraction($minuend, $subtrahend);
		$operands = $сolOp -> numberToDivStr($minuend);
		$operands .= $сolOp -> numberToDivStr($subtrahend);
		$result = $сolOp -> numberToDivStr($differenz);
		$resultPHP = "of subtraction:";
		$differenzPHP = $minuend - $subtrahend;
		$resultPHP .= "<br>$minuend - $subtrahend = $differenzPHP";
		break;

	case 4:
		// Division of two numbers
		do{
		   $dividend =  rand(1, 100000);
		   $divisor = rand(2, 100); //1000
		} while ($divisor > $dividend);

		$resultPHP = "of division:";
		$quotientPHP = $dividend / $divisor;
		$resultPHP .= "<br>$dividend / $divisor = $quotientPHP";
		$quotient = $сolOp -> сolumnDivision($dividend, $divisor, true);
		$divisionStepsToHTML =  $сolOp -> сolumnDivisionToHTML();
		$result = "";
		break;
}

echo <<<HTML
<html>
<head>
<title>Demo PHP class сolumnOperations</title>
<meta name="viewport" content="width=device-width,height=device-height,user-scalable=yes">
<link rel="stylesheet" type="text/css" href="index.css" />
<script>
function go(c){
	window.location.assign("?case="+c);
}
</script>
</head>
<body>
<table class="tabHeader" align="center" >
<tr>
<td class="tit">$title</td>
</tr>
<tr>
<td>$ul
<div>PHP result $resultPHP</div>
</td>
</tr>
</table>
HTML;

if($case == 4)
	echo $divisionStepsToHTML;
else
	echo <<<HTML
	<div align="center" class="tabCalculation">
	<table>
	$trM
	<tr>
	<td> $matheSymbol </td>
	<td>$operands<hr></td>
	</tr>
	<tr>
	<td> = </td>
	<td>$result</td>
	</tr>
	</table>
	</div>
	</body>
	</html>
	HTML;


сolumnOperationsClass.php
<?PHP
/*
	PHP class сolumnOperations
	Version: 2.0, 2023-11-20
	Author: Vladimir Kheifets (vladimir.kheifets.@online.de)
	Copyright (c) 2023 Vladimir Kheifets All Rights Reserved
	Demo:
	https://www.alto-booking.com/developer/columnOperations
*/

class сolumnOperations {

	public $strArr;
	private $numArr;
	private $maxColumn;
	private $dividend;
	private $divisor;
	private $stepDivisionRightShift;
	private $quotient;
	private $remainder;
	private $divisionAllStepArr;

	public function сolumnMultiplication($a, $b){

		/*
		Parameters (intenger):
		$a  - (integer) multiplier
		$b  - (integer) multiplicand

		Assigns a value to fields:
		public strArr (array)
		private numArr (array)

		Returns:
		product - (integer) multiplication result
		*/

		$aArr = array_reverse(str_split($a));
		$bArr = array_reverse(str_split($b));
		foreach($bArr as $i => $numB){
			$d = 0;
			$buf = "";
			foreach($aArr as $j => $numA){
				$m = $numB * $numA + $d;
				$tu = $this->getTensUnits($m);
				$d = $tu[0];
				$m = $tu[1];
				$buf .= $m;
			}
			if( $d>0 ) $buf .= $d;
			$resStr = strrev($buf);
			$interimResult = intval($resStr);
			$this->numArr[] = $interimResult * pow ( 10, $i );
			$this->strArr[] = $resStr;
		}
		return 	$this -> сolumnAdditionArr();
	}

	public function сolumnAddition($augend, $addend){

		/*
		Parameters:
		$augend - (integer)
		$addend - (integer)

		Assigns a value to private fields:
		maxColumn - (integer)

		Return:
		(integer) $sum
		*/

		$k = strlen((string) max($augend, $addend));
		$augendArr = $this -> getDigitReverseArr($augend, $k);
		$addenddArr = $this -> getDigitReverseArr($addend, $k);

		$sum = "";
		$d = 0;
		foreach($augendArr as $i => $digAaugend){
			$sum1 = $digAaugend + $addenddArr[$i] + $d;
			if($sum1 > 9)
			{
				$d = 1;
				$sum1 -= 10;
			}
			else
				$d=0;
			$sum .= $sum1;
			$sum1 = 0;
		}
		$sum .= $d;
		$sum = (int)strrev($sum);
		$this -> maxColumn = strlen($sum);
		return $sum;
	}


	public function сolumnAdditionArr($numArr = null){

		/*
		Parameter:
		$numArr - (integer) an array of summands
		or null, in case this method is called in the сolumnMultiplication method,
		gets the value from a private field:
		numArr

		Assigns a value to private fields:
		maxColumn - (integer)

		Returns:
		(integer) $sum
		*/

		if(is_null($numArr)) $numArr = $this-> numArr;
		$k = strlen((string) max($numArr));
		foreach($numArr as $i=>$num)
			$buf[] = $this -> getDigitReverseArr($num, $k);
		$sum = "";
		$d = 0;
		for ($i=0; $i < $k; $i++) {
			$sum1=0;
			foreach($buf as $j => $num)
			{
				$sum1 += $num[$i];
			}
			$sum1 += $d;
			$tu = $this -> getTensUnits($sum1);
			$d = $tu[0];
			$sum .= $tu[1];
		}
		$sum .= $d;
		$sum = (int)strrev($sum);
		$this -> maxColumn = strlen($sum);
		return $sum;
	}

	public function сolumnAdditionArr2($numArr = null){

		/*
		This method calls the сolumnAddition method to calculate the sum
		and it is an alternative to the сolumnAdditionArr method.

		Parameter:
		$numArr - (integer) an array of summands
		or null, in case this method is called in the сolumnMultiplication method,
		gets the value from a private field:
		numArr

		Assigns a value to private fields:
		maxColumn - (integer)

		Returns: (integer) $sum
		*/

		if(is_null($numArr)) $numArr = $this-> numArr;
		$sum = 0;
		foreach($numArr as $i => $addend){
			$sum = $this -> сolumnAddition($sum, $addend);
		}
		$this -> maxColumn = strlen($sum);
		return $sum;
	}

	public function сolumnSubtraction($minuend, $subtrahend){

		/*
		Parameters:
		$minuend - (integer)
		$subtrahend	- (integer)

		Assigns a value to private fields:
		maxColumn - (integer)

		Returns:
		$difference - (integer)
		*/

		if($subtrahend > $minuend) return;
		$k = strlen((string) max($minuend, $subtrahend));
		$minuendArr = $this -> getDigitReverseArr($minuend, $k);
		$subtrahendArr = $this -> getDigitReverseArr($subtrahend, $k);
		$difference = "";
		$d = 0;
		foreach ($subtrahendArr as $i => $digS) {
			$digM = $minuendArr[$i] - $d;

			if($digS > $digM)
			{
				$dDig = $digM - $digS + 10;
				$d = 1;
			}
			else
			{
				$dDig = $digM - $digS;
				$d = 0;
			}
			$difference .= $dDig;
		}
		$this -> maxColumn = max(strlen($difference), $k);
		return (int)strrev($difference);
	}

	public function сolumnDivision($dividend, $divisor, $withoutRemainder = false ){

		/*
		Parameters:
		$dividend - (integer)
		$divisor - (integer)
		$withoutRemainder (boolean):
		true - returns quotient (float) whith 15 digits
		false - returns  quotient (float) without of precision

		Assigns a value to public fields:
		quotient - (float)
		remainder - (integer)
		divisionAllStepArr - an array of intermediate results for all steps of division

		Returns:
		$quotient - (float)
		*/

		$divisionAllStepArr = [];
		$this -> dividend = $dividend;
		$this -> divisor = $divisor;
		$this -> stepDivisionRightShift = 0;
		if($divisor == 0) return "Error: Division by zero";
		$quotient = "";
		$i=0;
		$divisionAllStepArr = [];
		$lenDividend = strlen($dividend);
		$lenDivisor = strlen($divisor);
		$stepRemainder = 1;
		$stepSubtrahend=1;
		while($i<$lenDividend){
			if($i == 0)
			{
				$lenStepDividend = $divisor <= (int)substr($dividend,$i, $lenDivisor)?$lenDivisor:$lenDivisor + 1;
	   			$StepDividend = substr($dividend, $i, $lenStepDividend);
	   			$i += $lenStepDividend;
			}
			else
			{
				$StepDividend = $stepRemainder.substr($dividend, $i, 1);
				if($StepDividend < $divisor)
				{

					$quotient .= "0";
					$StepDividend = $stepRemainder.substr($dividend, $i, 2);
					$i += 2;
				}
				else
					$i += 1;
			}
			$divisionStepArr = $this -> longDivisionStep($StepDividend);
			$divisionAllStepArr[] = $divisionStepArr;
			extract($divisionStepArr);
			if($stepSubtrahend>0)
			$quotient .= $stepQuotient;
		}

		if($withoutRemainder)
		{
			if($stepRemainder>0)
			{
				$precision = 15 - strlen($quotient);
			   	$quotient .=".";
			   	$digits = 0;
			   	while($digits < $precision AND $stepRemainder>0){

					$StepDividend = $stepRemainder."0";
					if($StepDividend < $divisor )
					{
						$quotient .= "0";
						$StepDividend = $stepRemainder."00";
					}

			      $divisionStepArr = $this -> longDivisionStep($StepDividend);
			      $divisionAllStepArr[] = $divisionStepArr;
			      extract($divisionStepArr);
			      $quotient .= $stepQuotient;
			      $digits++;
			   }
				$quotient = round($quotient, $precision - 1);
			}
		}
		$quotient = (float) $quotient;
		$stepRemainder = (int) $stepRemainder;
		$this -> quotient = $quotient;
		$this -> remainder = $stepRemainder;
		$this -> divisionAllStepArr = $divisionAllStepArr;
		return $quotient;
	}

	public function numberToDivStr($num, $rowIndex = null ){

		/*
		Parameters:
		$num - (integer) the number that should be output in html
		$rowIndex - null or (integer)  row index for displaying a number with a digit shift
		null - only after calling the сolumnMultiplication method, must be defined in other cases

		Returns: (string) HTML - line in which each digit of a number is displayed in div tags.
		Complemented with empty ( ) div tags to the maximum number of digits in the sum or product
		*/

		if($num == 0)
		{
			$numArr[0] = 0;
			$numLen = 1;
		}
		else
		{
			$numArr = str_split($num);
			$numLen = strlen($num);
		}
		$maxColumn = $this -> maxColumn;
		$divStr = "";
		if($maxColumn)
		{
			$shiftLeft = $maxColumn - $numLen - 1 * $rowIndex;
			for ($i=0; $i < $shiftLeft; $i++) {
				$divStr .= "<div> </div>";
			}
		}
		foreach($numArr as $num)
		{
			$divStr .= "<div>$num</div>";
		}
		if($maxColumn)
		{
			$shiftRight = $maxColumn - $numLen - $shiftLeft;
			for ($i=0; $i < $shiftRight; $i++) {
				$divStr .= "<div> </div>";
			}
		}

		$divStr .= "<br>";
		return $divStr;
	}

	public function getTensUnits($n){

		/*
		Parameter:
		$n - (integer) the number 0 - 99
		Return: (integer) an array in which the 0-element contains tens,
		and the 1-element contains units of a given number.
		*/

		return $n>9?str_split($n):[0, $n];
	}

	public function getDigitReverseArr($num, $maxDig){
		/*
		Parameter:
		$num - (integer) a number that must be converted into an array digit by digit
		$maxDig - (integer) maximum number of elements in the created array
		Return:
		An array containing the digits of the number in reverse order, padded with 0 values
		*/
		return array_pad(array_reverse(str_split($num)), $maxDig, 0);
	}

	private function longDivisionStep($dividend){

		/*
			Parameter:
			$dividend - (integer)

			Assigns a value to private fields:
			stepDivisionRightShift

			Returnts an array with keys:
		      "stepDividend",
		      "stepSubtrahend",
		      "stepQuotient",
		      "stepRemainder",
		      "stepDivisionRightShift"
		*/

	   $divisor = $this -> divisor;
	   $remainder = $dividend;
	   $quotient = 0;
	   while ($remainder >= $divisor)
	   {
	      $remainder -= $divisor;
	      $quotient++;
	   }
	   	$stepSubtrahend = $dividend - $remainder;

		$stepDivisionRightShift = $thisStepRightShift = $this -> stepDivisionRightShift;
		if(preg_match("/^0/", $dividend))
	    {
	    	$dividend = preg_replace("/^0/", "", $dividend);
	    	$thisStepRightShift++;
	    	$stepDivisionRightShift = $thisStepRightShift+1;
	    }
		else
		{
			$stepDivisionRightShift = $stepDivisionRightShift + strlen($dividend) - strlen($remainder);
		}

		$this -> stepDivisionRightShift = $stepDivisionRightShift;

	   return
	   [
	      "stepDividend" => $dividend,
	      "stepSubtrahend" => $stepSubtrahend,
	      "stepQuotient" => $quotient,
	      "stepRemainder" => $remainder,
	      "stepDivisionRightShift" => $thisStepRightShift
	   ];
	}

	public function сolumnDivisionToHTML(){

		/*
		This Method gets the values of the class fields:
		dividend, divisor, quotient, remainder, divisionAllStepArr

		Returns the result of division as a string in HTML format
		*/

		$dividend = $this -> dividend;
		$divisor = $this -> divisor;
		$quotient = $this -> quotient;
		$remainder = $this -> remainder;
		$divisionAllStepArr = $this -> divisionAllStepArr;

		$divisionStepsToHTML = "
		<div class=divisionSteps align=center>
		<table border=1>";

		$iEnd = count($divisionAllStepArr) - 1;
		$stepDivisionRightShift=0;
		foreach($divisionAllStepArr as $i => $stepArr)
		{
		   extract($stepArr);
		   if($i==0)
		   {
				$R = $remainder > 0?"R $remainder":"";
				$stepDividend = $dividend;
				$stepDividend .=
				"<div>
				<span>$divisor</span><br>
				<span>$quotient $R</span>
				</div>";
		   }

		   $remainderOut = ($i == $iEnd)?$stepRemainder:null;
		   $divisionStepsToHTML .=  $this -> stepDivisionToHTML(
		   	$stepDividend,
		   	$stepSubtrahend,
		   	$stepDivisionRightShift,
		   	$remainderOut
		   );
		}
		$divisionStepsToHTML .=  "</table></div>";
		return 	$divisionStepsToHTML;
	}

	private function stepDivisionToHTML($stepDividend, $stepSubtrahend, $stepDivisionRightShift, $remainder=null){

		/*
			Parameters:
			$stepDividend, $stepSubtrahend, $stepDivisionRightShift - (integer)
			$remainder - null at all steps except the last step,
						 or (integer) at the last step
			This Method gets the values of the class fields:
			divisor

			Returns a string containing a right-shifted HTML table for each step
		*/

		$divisor = $this -> divisor;

		if($stepDivisionRightShift)
		{
			$ml = $stepDivisionRightShift + 0.5;
			$stMl = "style='margin-left: {$ml}em'";
		}
		else
			$stMl= "";

		$remainderToHtml = "";
		if(isset($remainder))
		{
			$remainder = $this -> numPadLeft($stepDividend, $remainder);
			$remainderToHtml = <<<HTML
			<tr>
			<td rowspan="2" align="right"> </td>
			<td align="left">$remainder</td>
			</tr>
			HTML;
		}
		if(is_numeric($stepDividend))
		{
			$wI = strlen($stepDividend) - 1;
			$stI = " style='min-width:{$wI}em;'";
			$stepSubtrahend = $this -> numPadLeft($stepDividend, $stepSubtrahend);
		}
		else
			$stI = "";

		return <<<HTML
		<table border="0" $stMl>
		<tr>
		<td rowspan="2" align="right" width="30"> − </td>
		<td>$stepDividend</td>
		</tr>
		<tr>
		<td width="200" align = left><i $stI>$stepSubtrahend</i></td>
		</tr>
		$remainderToHtml
		</table>
		HTML;
	}

	private function numPadLeft($num1, $num2){

		/*
		$num1, $num2 - (integer)
		If the number of digits in $num1 is greater than in $num2

		Returns $num2 (string) padded with spaces of length $num1,
		otherwise returns $num2 unchanged.
		*/

		$lenNum1 = strlen($num1);
		$lenNum2 = strlen($num2);
		if($lenNum2 < $lenNum1)
			$num2 = str_replace(" "," ", sprintf("%' ".$lenNum1."d",$num2));
		return $num2;
	}
}
?>

index.css
body{
	font-family: arial;
}

.tabHeader
{
	font-size: 20px;
	text-align: center;
}

.tabHeader div
{
	border: 1px solid #000;
	padding: 10 20 10 20;
	margin-bottom: 20px;
	background-color: #ccc;
	border-radius: 5px;
	box-shadow: 5px 5px 10px silver;
}

.tit{
	font-size: 24px;
	text-align: center;
}

ul{
	margin-top: 10px;
	margin-bottom: 10px;
}

ul li{
	text-align: left;
	cursor: pointer;
	padding: 5px;
	text-decoration: none
}

.active{
	text-decoration: underline;
	cursor: none;
}

/*----------------------------------------*/

.tabCalculation div{
	font-family: Courier New, monospace;
   font-size: 20px;
	display: inline-block;
	border: 1px solid #cccccc;
	padding: 1px;
	margin:1px;
	min-width: 1em;
	text-align: center
}

.tabCalculation{
	padding:0 40 0 0;
}

.tabCalculation td[colspan="2"]{
text-align: center;
}

.tabCalculation td:first-of-type{
	text-align: right;
	padding: 0 20 0 0;
	font-size: 20px;
}

.tabCalculation td:first-of-type + td{
	display: inline-block;
	text-align: right;
}

/*----------------------------------------*/

.divisionSteps{
	padding-right: 200px;
}

.divisionSteps table{
   font-family: Courier New, monospace;
   font-size: 20px;
}
.divisionSteps table tr:first-of-type + tr td i{
	display: inline-block;
	border-bottom: 1px solid #000;
	font-style:normal;
}

.divisionSteps table div{
   display: inline-block;
   margin-left: 40px;
   position: absolute;
   border-left: 1px solid #000;
}
.divisionSteps table span{
	padding-left: 10px;
	display: inline-block;
}

.divisionSteps table  br +  span{
	border-top: 1px solid #000;
	padding-top: 2px;
}

Schachfiguren-Animation Demo, Download chessPieceAnimation.zip 227.7Kb

Datei ChessboardConfig.php
<?
$pieceCodeToNameColor =
[
	12 => ["king",1],
	13 => ["queen",1],
	14 => ["rook",1],
	15 => ["bishop",1],
	16 => ["knight",1],
	17 => ["pawn",1],
	18 => ["king",2],
	19 => ["queen",2],
	20 => ["rook",2],
	21 => ["bishop",2],
	22 => ["knight",2],
	23 => ["pawn",2]
];

$pieceToNameColor =
[
	"♔" => ["king",1],
	"♕" => ["queen",1],
	"♖" => ["rook",1],
	"♗" => ["bishop",1],
	"♘" => ["knight",1],
	"♙" => ["pawn",1],
	"♚" => ["king",2],
	"♛" => ["queen",2],
	"♜" => ["rook",2],
	"♝" => ["bishop",2],
	"♞" => ["knight",2],
	"♟" => ["pawn",2]
];

$pieceToCode =
[
	"♔" => 12,
	"♕" => 13,
	"♖" => 14,
	"♗" => 15,
	"♘" => 16,
	"♙" => 17,
	"♚" => 18,
	"♛" => 19,
	"♜" => 20,
	"♝" => 21,
	"♞" => 22,
	"♟" => 23
];

$codeToPiece =
[
	12 => "♔",
	13 => "♕",
	14 => "♖",
	15 => "♗",
	16 => "♘",
	17 => "♙",
	18 => "♚",
	19 => "♛",
	20 => "♜",
	21 => "♝",
	22 => "♞",
	23 => "♟"
];

$indCR = [1,2,3,4,5,6,7,8];
$colsLN = ["A" => 1, "B" => 2, "C" => 3, "D" => 4, "E" => 5, "F" => 6, "G" => 7, "H" => 8];
$colsNL = [1 => "A", 2 => "B", 3 => "C", 4 => "D", 5 => "E", 6 => "F", 7 => "G", 8 => "H"];
?>

Datei index.php
<?
/*
  Demo1:
  https://www.alto-booking.com/developer/chessPieceAnimation/ChessboardMaster.php
  Create chessbord and save in files:
  whitePayerChessBord.html
  blackPayerChessBord.html

  Demo2:
  https://www.alto-booking.com/developer/chessPieceAnimation
*/
$html = <<<HTML
<html>
<head>
<title>Online chess</title>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1,
user-scalable=no, user-scalable=0" >
<link rel="stylesheet" type="text/css" href="css/drawPlayerPieces.css">
<link rel="apple-touch-icon" sizes="180x180" href="icon/apple-touch-icon.png">
<link rel="icon" type="image/png" sizes="32x32" href="icon/favicon-32x32.png">
<link rel="icon" type="image/png" sizes="16x16" href="icon/favicon-16x16.png">
<link rel="manifest" href="icon/site.webmanifest">
HTML;
if(!isset($_POST["playerPieces"]))
{
  echo <<<HTML
  $html
  </head>
  <body>
  <div align="center" class='draw'>
  <h1>Online chess<br>the draw before the game starts</h1>
  <div id="a"><i>♙</i></div>
  <div id="b"><i>♟</i></div>
  <br>
  <div id="selected"></div>
  </div>
  <form action="" method="post">
  <input name = "playerPieces" type="hidden">
  </form>
  </body>
  <script type="text/javascript" src="js/drawPlayerPieces.js"></script>
  </html>
  HTML;
  exit;
}
session_start();
$_SESSION["iMove"] = 0;
$playerPieces = $_SESSION["playerPieces"] = $_POST["playerPieces"];
echo $html
?>
<link rel="stylesheet" type="text/css" href="css/Chessboard.css">
<script>
var playerPieces = <?=$playerPieces=="white"?1:2;?>;
</script>
<script type="text/javascript" src="js/cellsPiecesData.js"></script>
<script type="text/javascript" src="js/rulesPiecesMoves.js"></script>
<script type="text/javascript" src="js/Chessboard.js"></script>
</head>
<body>
<table align=center class="main-table" >
<tr>
<td align="center" class="info" rowspan="2">
<div id="info"></div>
</td>
<td rowspan="2" class="bord">
<?
  echo file_get_contents("ChessBords/".$playerPieces."PayerChessBord.html")
?>
</td>
<td class="info" >
<div id="opponentWonPieces"></div>
</td>
</tr>
<tr>
<td class="info" >
<div id="payerWonPieces"></div>
</td>
</tr>
</table>
</body>
</html>

Datei ChessboardMaster.php
<?
include_once("ChessboardConfig.php");
$cols = range("A", "H");
$cellsClass = ["light","dark","light","dark","light","dark","light", "dark"];
$cellsId = [];
$setCellsId = true;
$pawn[2]= "♙";
$pawn[7] = "♟";
$rkb[1] = ["♖","♘","♗"]; //rook knight bishop
$rkb[8] = ["♜","♞","♝"];
$qk[1] = ["♕","♔"]; //king queen
$qk[8] = ["♛","♚"]; //king queen

function pieceToCode($piece){return mb_ord($piece) - 9800;}

foreach ([1,8] as $row){
	$mainPieces[$row] = array_merge(
		$rkb[$row],
		$qk[$row],
		array_reverse($rkb[$row])
	);
}
#############################################################################
foreach (["white","black"] as $playerPieces) {
	$bordRows = [];
	$bordCells = [];
	$bordRows = [];
	$rowsWithPieces = [1, 2, 7, 8];
	$rows = $playerPieces == "white"?range(8, 1):range(1, 8);
	foreach($rows as $row)
	{
		if(in_array($row, $rowsWithPieces))
		{
			foreach($cols as $key=>$col)
			{
				$id = $col.$row;
				$piece = ($row==2 OR $row==7)?$pawn[$row]:$mainPieces[$row][$key];
				$bordRows[$row][$id] = "<i>$piece</i>";
				$bordCells[$playerPieces][$row][$key+1] = pieceToCode($piece);
			}
		}
		else
		{
			foreach($cols as $key=>$col)
				$bordCells[$playerPieces][$row][$key+1] = 0;
		}
	}
	########################################################
	$indexCol = "<div class='rand col row'></div>\n";
	foreach($cols as $col)
	{
		$indexCol .= "<div class='rand col'>$col</div>\n";
	}
	$indexCol .= "<div class='rand col row'></div>";
	#######################################################
	$chessBordHTML = <<<HTML
	<div class="chess-bord">
	$indexCol
	HTML;
	foreach($rows as $row)
	{
	$chessBordHTML .= <<<HTML
	<div class="dark row">$row</div>\n
	HTML;
		foreach($cols as $j => $col)
		{
			$id = $col.$row;
			if($setCellsId) $cellsId[] = $id;
			$divContent = preg_match("/(1|2|7|8)/",$row)?$bordRows[$row][$id]:"";
			$class = $cellsClass[$j];
			$chessBordHTML .= <<<HTML
			<div id="$id" class="$class">$divContent</div>\n
			HTML;
		}
		$chessBordHTML .= <<<HTML
			<div class="dark row">$row</div>\n
			HTML;
		$cellsClass = array_reverse($cellsClass);
	}
	$chessBordHTML .= <<<HTML
	$indexCol
	</div>
	HTML;
	$file = "ChessBords/".$playerPieces."PayerChessBord.html";
	file_put_contents($file, $chessBordHTML);
	$setCellsId = false;
}
##########################################################
$JS = 'var cellsId=["'.implode('","',$cellsId).'"];';
$JS .= PHP_EOL;
foreach(
	[
		"pieceCodeToNameColor",
		"pieceToNameColor",
		"pieceToCode",
		"codeToPiece",
		"colsNL",
		"colsLN"
	] as $name
)
$JS .= arrToJs($$name, $name);


$name = "indCR";
$JS .= arrToJs($$name, $name, 1);
###################################################################
$buf = [];
foreach($bordCells[$playerPieces] as $iRow => $RowCells){
	$tmp = [];
	foreach($RowCells as $iCol => $cellPiece)
		$tmp[] = "$iCol:$cellPiece";
	$buf[] = "$iRow:{".implode(",",$tmp)."}";
}
$JS .=  "var cellsPieceCode = {".implode(",",$buf)."};";
###################################################################
file_put_contents("js/cellsPiecesData.js", $JS);

//preview chessbords
echo <<<HTML
<html>
<head>
<title>Chessbord Master</title>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1,
user-scalable=no, user-scalable=0" >
<link rel="stylesheet" type="text/css" href="css/Chessboard.css">
<link rel="apple-touch-icon" sizes="180x180" href="icon/apple-touch-icon.png">
<link rel="icon" type="image/png" sizes="32x32" href="icon/favicon-32x32.png">
<link rel="icon" type="image/png" sizes="16x16" href="icon/favicon-16x16.png">
<link rel="manifest" href="icon/site.webmanifest">
</head>
<body>
<div align=center>
HTML;
foreach (["white","black"] as $playerPieces)
echo file_get_contents("ChessBords/".$playerPieces."PayerChessBord.html"),"<br>";
echo <<<HTML
</div>
</body>
</html>
HTML;
#######################################################
function arrToJs($inArray, $name, $toArr=null){
	$buf = [];
	if($toArr)
	{
		$brB="[";
		$brE="]";
	}
	else
	{
		$brB="{";
		$brE="}";
	}

	foreach($inArray as $key => $val){
		$key = (is_string($key))?"\"$key\"":$key;
		if($toArr)
			$buf[] = (is_string($val))?"\"$val\"":$val;
		else
		{
			if(is_array($val))
				$val = "[\"{$val[0]}\",{$val[1]}]";
			else
				$val = (is_string($val))?"\"$val\"":$val;
			$buf[] = "$key:$val";
		}
	}
	return "var $name =  $brB".implode(",",$buf)."$brE;\n";
}
?>

Datei processing.php
<?
// AJAX Request handler
session_start();
$request = file_get_contents('php://input');
$data = json_decode($request);
$iMove = isset($_SESSION["iMove"])?$_SESSION["iMove"]:0;
$playerPieces = isset($_SESSION["playerPieces"])?$_SESSION["playerPieces"]:"white";
$opponentPieces = $playerPieces=="white"?"black":"white";
##########################################
$buf = file("opponentMoves/{$opponentPieces}DemoMoves.txt");
$demoMoves = [];
foreach($buf as $line)
$demoMoves[] = explode(",",trim($line));
##########################################
$move[0] = $demoMoves[$iMove][0];
$move[1] = $demoMoves[$iMove][1];
$iMove++;
$_SESSION['iMove'] =  $iMove;
##########################################
$out =
[
	"move" => $move
];
//send JSON Response
header('Content-Type: application/json; charset=utf-8');
echo json_encode($out);
?>

Datei cellsPiecesData.js
var cellsId=["A8","B8","C8","D8","E8","F8","G8","H8","A7","B7","C7","D7","E7","F7","G7","H7","A6","B6","C6","D6","E6","F6","G6","H6","A5","B5","C5","D5","E5","F5","G5","H5","A4","B4","C4","D4","E4","F4","G4","H4","A3","B3","C3","D3","E3","F3","G3","H3","A2","B2","C2","D2","E2","F2","G2","H2","A1","B1","C1","D1","E1","F1","G1","H1"];
var pieceCodeToNameColor =  {12:["king",1],13:["queen",1],14:["rook",1],15:["bishop",1],16:["knight",1],17:["pawn",1],18:["king",2],19:["queen",2],20:["rook",2],21:["bishop",2],22:["knight",2],23:["pawn",2]};
var pieceToNameColor =  {"♔":["king",1],"♕":["queen",1],"♖":["rook",1],"♗":["bishop",1],"♘":["knight",1],"♙":["pawn",1],"♚":["king",2],"♛":["queen",2],"♜":["rook",2],"♝":["bishop",2],"♞":["knight",2],"♟":["pawn",2]};
var pieceToCode =  {"♔":12,"♕":13,"♖":14,"♗":15,"♘":16,"♙":17,"♚":18,"♛":19,"♜":20,"♝":21,"♞":22,"♟":23};
var codeToPiece =  {12:"♔",13:"♕",14:"♖",15:"♗",16:"♘",17:"♙",18:"♚",19:"♛",20:"♜",21:"♝",22:"♞",23:"♟"};
var colsNL =  {1:"A",2:"B",3:"C",4:"D",5:"E",6:"F",7:"G",8:"H"};
var colsLN =  {"A":1,"B":2,"C":3,"D":4,"E":5,"F":6,"G":7,"H":8};
var indCR =  [1,2,3,4,5,6,7,8];
var cellsPieceCode = {1:{1:14,2:16,3:15,4:13,5:12,6:15,7:16,8:14},2:{1:17,2:17,3:17,4:17,5:17,6:17,7:17,8:17},3:{1:0,2:0,3:0,4:0,5:0,6:0,7:0,8:0},4:{1:0,2:0,3:0,4:0,5:0,6:0,7:0,8:0},5:{1:0,2:0,3:0,4:0,5:0,6:0,7:0,8:0},6:{1:0,2:0,3:0,4:0,5:0,6:0,7:0,8:0},7:{1:23,2:23,3:23,4:23,5:23,6:23,7:23,8:23},8:{1:20,2:22,3:21,4:19,5:18,6:21,7:22,8:20}};

Datei Chessboard.js
var move_from_id = 0;
var move_to_id = 0;
var animByFirstClick = {
  "animationName":"click-piece",
  "animationDuration":"1.0s",
  "animationIterationCount":"infinite"
};
//--------------------------------------------
function movePiece(piece_move_from, piece_move_to, pieceObj, byParent){
  if(!pieceObj)
  {
    pieceObj = document.getElementById(piece_move_from).children[0];
  }
  winningPiece = getPiece(piece_move_to, 2);
  //--------------------------------------------------------------
  cellIdGetSetPiece(piece_move_from, 0);
  cellIdGetSetPiece(piece_move_to, getPiece(piece_move_from, 2));
  //alert(piece_move_from + " > 0\n"+ piece_move_to +" > "+getPiece(piece_move_from, 2));
  //--------------------------------------------------------------
  prop = getPosProp(piece_move_from);
  prop["position"]="absolute";
  setStyle(pieceObj, prop, byParent);
  prop = getPosProp(piece_move_to);
  setStyle(pieceObj, prop, byParent);
  setTimeout(() => {
    pieceObj.removeAttribute("style");
    el = document.getElementById(piece_move_to);
    el.innerHTML = "";
    el.appendChild(pieceObj);
  }, "500");
  return winningPiece;
}
//--------------------------------------------
function getPosProp(cellId){
  el = document.getElementById(cellId);
  pos = el.getBoundingClientRect();
  T = parseInt(pos.y) + shiftPiecePos["T"];
  L = parseInt(pos.x) + shiftPiecePos["L"];
  return {"top":T + "px", "left":L + "px"};
}
//--------------------------------------------
function getPiece(cellObj, $out){
  if(typeof cellObj == "string")
  {
    cellObj  = document.getElementById(cellObj);
  }
  if(typeof cellObj == "object")
  {
    if(cellObj.tagName == "DIV")
      pieceObj = cellObj.children[0];
    else
      pieceObj = cellObj;
  }
  else
    return "";
  if(typeof pieceObj == "object")
  {
    if($out == 1)
      return cellObj.innerHTML;
    else
    {
      piece = pieceObj.innerHTML;
      if($out == 2)
        return piece;
      else if($out == 3)
        return "<i>" + piece + "</i>";
      else if($out == 4)
        return pieceObj;
    }
  }
  else
    return "";
}
//--------------------------------------------
function setStyle(piece, propObj, byParent){
  if(byParent)
    elSt = getEl(piece).children[0].style;
  else
    elSt = piece.style;
  for (prop in propObj) {
    elSt[prop] = propObj[prop];
  }
}
//--------------------------------------------
function getEl(par)
{
  return typeof par === "object"?par:document.getElementById(par);
}
//---------------------------------------------
function getSrcByPiecename(pieceName){
  return pieceImgPatch+pieceName+pieceImgExt;
}

//---------------------------------------------
function addInfo(moveFrom, moveTo){
  if(typeof moveFrom  == "object")
  {
    move_from = moveFrom.move[0];
    move_to = moveFrom.move[1];
  }
  else
  {
    move_from = moveFrom;
    move_to = moveTo;
  }
  move_winning_piece = winningPiece;
  piece = "<i>"+document.getElementById(move_from).children[0].innerHTML+"</i>";
  piece = getPiece(move_from, 3);
  el = document.createElement("div");
  el.innerHTML = `${moveNumber.toString().padStart(2,"0")}.${piece} ${move_from}-${move_to}`;
  info.appendChild(el);
  info.scrollTo(0, info.scrollHeight);
  if(winningPiece)
  {
    el = document.createElement("i");
    el.innerHTML = winningPiece;
    chPiece = pieceToNameColor[winningPiece][1]==2?false:true;
    chPayer = playerPieces == 2?true:false;
    //if((chPayer && chPiece) || (!chPayer && !chPiece))

    if(pieceToNameColor[winningPiece][1] != playerPieces)
      payerWonPieces.appendChild(el);
    else
      opponentWonPieces.appendChild(el);
  }
  moveNumber++;
}
//---------------------------------------------
function ProcessingChessMove(moveFrom, moveTo, Piece, winningPiece, moveRook){
  pieceCode = pieceToCode[Piece];
  winningPieceCode = winningPiece == ""?0:pieceToCode[winningPiece];
  sendDataObj = {
    playerPieces:playerPieces,
    moveNumber:moveNumber,
    move:[moveFrom, moveTo, pieceCode, winningPieceCode],
    moveRook:moveRook,
    cellsPieceCode:cellsPieceCode
  };
 var xhr = new XMLHttpRequest();
 var url = "processing.php";
 xhr.onreadystatechange = function()
 {
   if(this.readyState == 4 && this.status == 200)
   {
     try
     {
        var moveInfoObj = JSON.parse(this.responseText);
        cellIdGetSetPiece(moveInfoObj.move[0]);
        console.log(moveInfoObj.move[0], cellPieceName, cellPieceColor);
        winningPiece = movePiece(moveInfoObj.move[0], moveInfoObj.move[1]);
        addInfo(moveInfoObj);
        makeCastling(moveInfoObj.move[0], moveInfoObj.move[1]); //if cellPieceName == "king"
        lockMove = false;
        return true;
      }
      catch(error)
      {
        return false;
      }
   }
   //lockMove = false; // only tmp for debug piece move
 };
  xhr.open("POST", url, true);
  xhr.setRequestHeader("Content-type", "application/json");
  var data = JSON.stringify(sendDataObj);
  xhr.send(data);
}
//--------------------------------------------
function debug($output){
  $msg = "";
  for(i in $output)
    $msg += $output[i]+"\n";
  alert($msg);
}
//--------------------------------------------
function denyClicks(obj){
  //debug([lockMove, move_from_id, move_to_id]);
  if(lockMove) return lockMove;
  if(move_from_id && move_to_id)
    return true;
  else if(move_from_id == 0)
  {
     piece_from = getPiece(obj, 2);
     allowedTargetCells = allowedMoveToTargetCells(id);
     //console.log(piece_from + "/"+ id + "\n" + allowedTargetCells);
     if(playerPieces == pieceToNameColor[piece_from][1])
      return false;
     else
      return true;
  }
  else
  {
    piece_to = getPiece(obj, 2);
    if(piece_to)
    {
      if(pieceToNameColor[piece_from][1] == pieceToNameColor[piece_to][1])
      {
        obj_from.children[0].removeAttribute("style");
        move_from_id = 0;
        move_to_id = 0;
        clickCell(obj);
      }
    }
    return allowedTargetCells.includes(id)?false:true;
  }
}
//--------------------------------------------
makeCastling = (move_from_id, move_to_id) => {
  if(cellPieceName == "king")
  {
    сastlingMove(move_from_id, move_to_id);
    //alert(сastlingRookMove +"\n"+rook_move_from + " > 0\n"+ rook_move_to +" > "+getPiece(rook_move_from, 2));
    if(сastlingRookMove[cellPieceColor])
    {
      moveRook = [rook_move_from, rook_move_to];
      setTimeout(() => {
      movePiece(rook_move_from, rook_move_to);
      addInfo(rook_move_from, rook_move_to);
      }, "100");
    }
  }
  if(!сastlingRookMove[cellPieceColor]) moveRook = 0;
}
//--------------------------------------------
  function clickCell(obj){
  id = obj.id;
  if(denyClicks(obj)) return;
  if(move_from_id == 0) {
    move_from_id = id;
    obj_from = obj;
    pieceObj = getPiece(obj, 4);
    setStyle(obj, animByFirstClick, 1);
  }
  else
  {
    move_to_id = id;
    //debug([move_from_id, move_to_id])
    if(move_from_id == move_to_id)
    {
        move_from_id = 0;
        move_to_id = 0;
        return;
    }
    pieceObj = getPiece(obj_from, 4);
    Piece = getPiece(obj_from, 2);
    winningPiece = movePiece(move_from_id, move_to_id, pieceObj);
    moveFrom = move_from_id;
    moveTo = move_to_id;
    addInfo(moveFrom, moveTo, winningPiece);
    makeCastling(move_from_id, move_to_id); //if cellPieceName == "king"
    setTimeout(() => {
      ProcessingChessMove(moveFrom, moveTo, Piece, winningPiece, moveRook);
    }, "1000");
    move_from_id = 0;
    move_to_id = 0;
  }
}
//-----------------------------------------------------
function positionEventClick(){
  for (i in cellsId)
  {
    el = document.getElementById(cellsId[i]);
    el.addEventListener("click",function(){clickCell(this)});
  }
}
//-----------------------------------------------------
window.addEventListener("load", (event) => {
  info = document.getElementById("info");
  payerWonPieces = document.getElementById("payerWonPieces");
  opponentWonPieces = document.getElementById("opponentWonPieces");
  moveNumber = 1;
  positionEventClick();
   if(playerPieces == 2)
   {
     ProcessingChessMove(0, 0, 0);
     lockMove = true;
   }
   else
    lockMove = false;
  //---------------------------------
  el = document.getElementById("A1");
  pos = el.getBoundingClientRect();
  T = parseInt(pos.y);
  L = parseInt(pos.x);
  T = pos.y;
  L = pos.x;
  pos = el.children[0].getBoundingClientRect();
  Ti = pos.y;
  Li = pos.x;
  shiftPiecePos = {"T":Math.round(Ti - T),"L":Math.round(Li - L)};
});
//-------move_from----------------------------------------------
window.addEventListener("resize", (event) => {
});

Datei rulesPiecesMoves.js
var castlingAllowed, сastlingRookMove;
castlingAllowed = { 1:true, 2:true };
сastlingRookMove = { 1:false, 2:false };
//------------------------------------------------------------------
cellIdGetSetPiece = (cellId, setPiece) => {
	if(cellId == "") return;
	[cL, cellRow] = cellId.split("");
	cellCol = colsLN[cL];
	cellRow = parseInt(cellRow);
	if(typeof setPiece !== "undefined")
	{
		cellPieceCode = typeof setPiece == "number"?setPiece:pieceToCode[setPiece];
		cellsPieceCode[cellRow][cellCol] = cellPieceCode;
	}
	else
		cellPieceCode =	cellsPieceCode[cellRow][cellCol];
	if(cellPieceCode == 0)
	{
		cellPieceName = cellPieceColor = 0;
		return;
	}
	cellPieceName = pieceCodeToNameColor[cellPieceCode][0];
	cellPieceColor = pieceCodeToNameColor[cellPieceCode][1];
}
//------------------------------------------------------------------
targetCellsFromShiftPiece = () => {
	for(i in shiftPiece)
	{
		if(shiftPiece[i])
		{
			if(cellPieceName == "pawn")
				attack = shiftPiece[i][2]?true:false;
			else
				attack = false;
			targetCell = getTargetCell(shiftPiece[i][0], shiftPiece[i][1], attack);
			//console.log(targetCell);
			if(targetCell)
				targetCells.push(targetCell);
		}
	}
	return targetCells;
}
//------------------------------------------------------------------
allowedMoveToTargetCells = (cellId) => {
	cellIdGetSetPiece(cellId);
	targetCells = [];
	switch (cellPieceName) {
	 case "pawn":
		if(cellPieceColor == 1)
		{
			$shiftPawnRow = 1;
			$shiftPawnRow2 = 2;
		}
		else
		{
			$shiftPawnRow = -1;
			$shiftPawnRow2 = -2;
		}

		shiftPiece =
		[
			[0,$shiftPawnRow],
			[-1,$shiftPawnRow,1],
			[1,$shiftPawnRow,1]
		];
		shiftPieceRow2 =
		[
			[0,$shiftPawnRow2],
			[-1,$shiftPawnRow2,1],
			[1,$shiftPawnRow2,1]
		];
		if(cellRow == 2 || cellRow == 7)
			shiftPiece = shiftPiece.concat(shiftPieceRow2);
		targetCellsFromShiftPiece();
		break;

	  case "bishop":
	  	moveAlongDiagonals();
	  	break;

	  case "knight":
		shiftPiece =
			[
				[-2,-1],
				[-1,-2],
				[-2,1],
				[1,-2],
				[-1, 2],
				[2,-1],
				[1,2],
				[2,1]
			];
			targetCells = [];
			attack = false;
	  		targetCellsFromShiftPiece();
	    break;

	  case "rook":
	  	moveAlongCross();
		break;

	  case "king":
		shiftPiece =
		[
			[-1,1],
			[0,1],
			[1,1],
			[-1,0],
			[1,0],
			[-1,-1],
			[0,-1],
			[1,-1]
		];

		shiftPieceRow2 =
		{
			4:[
				[-2, 0],
				[3,0]
			],
			5:[
				[-3,0],
				[2,0]
			]
		}

		if(castlingAllowed[cellPieceColor] && (cellRow == 1 || cellRow == 8))
			shiftPiece = shiftPiece.concat(shiftPieceRow2[cellCol]);
		targetCellsFromShiftPiece();
	    break;

	  case "queen":
	  	moveAlongDiagonals();
	  	moveAlongCross();
		break;
	}
	return targetCells;
}
//------------------------------------------------------------------
сastlingMove = (piece_move_from, piece_move_to) => {
	//alert("139: " + castlingAllowed[cellPieceColor] + "\n"+piece_move_from + " > 0\n"+ piece_move_to +" > "+getPiece(piece_move_from, 2));
	king_move_from = ["E1","E8"];
	king_move_to = ["G1", "B1","G8", "B8"];
	cellKingToRook = {
		G1:["H1","F1"],
		B1:["A1","C1"],
		G8:["H8","F8"],
		B8:["A8","C8"]
	}
	if(
		king_move_from.includes(piece_move_from)
		&&
		king_move_to.includes(piece_move_to)
		&&
		castlingAllowed[cellPieceColor]
	)
	{
		rook_move_from = cellKingToRook[piece_move_to][0];
		rook_move_to = cellKingToRook[piece_move_to][1];
		сastlingRookMove[cellPieceColor] = true;
	}
	else
	{
		сastlingRookMove[cellPieceColor] = false;
	}
	castlingAllowed[cellPieceColor] = false;
}
//------------------------------------------------------------------
moveAlongDiagonals = () => {
	targetRow = cellRow;
	targetCol = cellCol;
	//------------------------------
	while(targetCol > 1 && targetRow > 1){
		--targetCol;
		--targetRow;
		if(breakCellLoop()) break;
	}
	//------------------------------
	targetRow = cellRow;
	targetCol = cellCol;
	while(targetCol < 8 && targetRow < 8){
		++targetCol;
		++targetRow;
		if(breakCellLoop()) break;
	}
	//------------------------------
	targetRow = cellRow;
	targetCol = cellCol;
	while(targetCol < 8 && targetRow > 1){
		++targetCol;
		--targetRow;
		if(breakCellLoop()) break;
	}
	//------------------------------
	targetRow = cellRow;
	targetCol = cellCol;
	while(targetCol > 1 && targetRow < 8){
		--targetCol;
		++targetRow;
		if(breakCellLoop()) break;
	}
}
//------------------------------------------------------------------
moveAlongCross = () => {
	targetRow = cellRow;
	targetCol = cellCol;
	//------------------------------
	while(targetCol > 1 ){
		--targetCol;
		if(breakCellLoop()) break;
	}
	//------------------------------
	targetRow = cellRow;
	targetCol = cellCol;
	while(targetCol < 8 ){
		++targetCol;
		if(breakCellLoop()) break;
	}
	//------------------------------
	targetRow = cellRow;
	targetCol = cellCol;
	while(targetRow > 1){
		--targetRow;
		if(breakCellLoop()) break;
	}
	//------------------------------
	targetRow = cellRow;
	targetCol = cellCol;
	while(targetRow < 8){
		++targetRow;
		if(breakCellLoop()) break;
	}
}
//------------------------------------------------------------------
//
breakCellLoop = () => {
	targetPieceCode =  cellsPieceCode[targetRow][targetCol];
	if(targetPieceCode > 0 )
		attack = pieceCodeToNameColor[targetPieceCode][1] != cellPieceColor;
	else
		attack = false;

	//console.log(targetCol + "," + targetRow + ","  + targetPieceCode + ","+attack);

	if(targetPieceCode == 0 || attack)
		targetCells.push(colsNL[targetCol] + targetRow);

	if(targetPieceCode == 0)
		return false;
	else
		return true;
}
//------------------------------------------------------------------
getTargetCell = (shiftCol, shiftRow, attack) => {
	targetRow = cellRow + shiftRow;
	targetCol = cellCol + shiftCol;	
	targetOnBord = indCR.includes(targetRow) && indCR.includes(targetCol);
	//console.log(targetCol + "," + targetRow + "," + targetOnBord);
	//------------------------------------
	/*
	if(targetOnBord)
	console.log({
	cell:[cellCol,cellRow],
	shift:[shiftCol,shiftRow],
	target:[targetCol,targetRow],
	targetOnBord:targetOnBord
	});
	*/
	//------------------------------------

	if(targetOnBord)
	{
		targetPieceCode =  cellsPieceCode[targetRow][targetCol];
		targetCellAllow = undefined;
		//console.log(targetCol + "," + targetRow + "," + targetPieceCode + "," + attack + ","+ cellPieceColor);
		if(attack)
		{
			if
			(
				targetPieceCode > 0
				&&
				pieceCodeToNameColor[targetPieceCode][1] != cellPieceColor
			)
				targetCellAllow = true;
			else
				targetCellAllow = false;
		}

		if(typeof targetCellAllow == "undefined")
		{
			if(targetPieceCode == 0)
				targetCellAllow = true;
			else if(pieceCodeToNameColor[targetPieceCode][1] != cellPieceColor)
				targetCellAllow = true;
			else
				targetCellAllow = false;
		}
		//console.log(targetCellAllow);
		if(targetCellAllow)
			return	targetCellId = colsNL[targetCol] + targetRow;
		else
			return null;
	}
	else
		return null;
}
//------------------------------------------------------------------
pieceEnDecode = (inpPiece, pieceEncode ) => {
	if(pieceEncode)
	{
		pieceCode = inpPiece.charCodeAt() - 9800;
		return [pieceCode].concat(pieceCodeToNameColor[pieceCode]);
	}
	else
	{
		return String.fromCharCode(inpPiece + 9800);
	}
}
//------------------------------------------------------------------

Datei drawPlayerPieces.js
var abEl = [];
var iabEl = [];
var posLeft= [];
var drawPieces = {0:["♙","white"],1:["♟","black"]};
var indArr=[0,1];
var sel = document.getElementById("selected");
//---------------------------------------------------------
for(i in arr=["a","b"])
{
	abEl[i] = document.getElementById(arr[i]);
	iabEl[i] = abEl[i].children[0];
	posLeft[i] = iabEl[i].getBoundingClientRect().x;
}
var ml = posLeft[1] - posLeft[0];
//----------------------------------------------------------
function stopChangePieceColor(){
	num = Math.floor(Math.random() * Date.now());
  iDraw = (num % 2 !== 0)?0:1;
	for(i in iabEl)
		abEl[i].style.display="none";
	sel.innerHTML=`${drawPieces[iDraw][0]} <span>Start the game</span>`;
	sel.style.visibility="visible";
}
//--------------------------------------------------------
function sendPost(){
	f = document.forms[0];
	f.playerPieces.value = drawPieces[iDraw][1];
	f.submit();
}
//--------------------------------------------------------
sel.addEventListener("click", sendPost);
setPiece = () => {
	mlArr = [ml,-ml];
	indArr = indArr.reverse();
	for(i in iabEl)
	{
		el = iabEl[i];
		el.style.marginLeft = `${mlArr[i]}px`;
		el.innerHTML= drawPieces[indArr[i]][0];
		el.style.opacity = 0;
	}
}
//----------------------------------------------------
startSelect = () => {
	setPiece();
	indArr = indArr.reverse();
	for(i in iabEl)
		iabEl[i].innerHTML = drawPieces[indArr[i]][0];
	for(i in abEl)
	{
		el = abEl[i];
		el.removeEventListener("mouseover", startSelect);
		el.style.cursor="pointer";
		el.addEventListener("click", stopChangePieceColor);
	}
}
//-----------------------------------------------------
sel.addEventListener("click", sendPost);
//-----------------------------------------------------
for(i in abEl)
	abEl[i].addEventListener("mouseover", startSelect);

Datei Chessboard.css
:root{
  --color-dark: #D9AB91;/*848484*/
  --color-light: #FAF3F0;/*D8D8D8*/
  --cell-size: 50px  /*58px 60px;*/
}

@font-face {
    font-family: 'AbhayaLibre-Regular';
    src: url(fonts/AbhayaLibre-Regular.ttf);
}

.chess-bord{
  display: grid;
  width: calc(var(--cell-size) * 9);
  grid-template-columns: 1fr 1fr 1fr 1fr 1fr 1fr 1fr 1fr 1fr 1fr;
}

.chess-bord div{
  height: var(--cell-size);
  width: var(--cell-size);
  border: 1px solid white;
  display: grid;
  place-items: center;
  font-weight: bold;
}

.chess-bord i{
  font-family: AbhayaLibre-Regular;
  display: block;
  font-size:calc(var(--cell-size) * 0.7);
  font-style: normal;
  font-weight: normal;
  transition: 1s linear;
  z-index: 999;
  cursor: pointer;
}

.chess-bord .dark, .chess-bord .rand{
  background-color: var(--color-dark);
  caret-color:var(--color-dark);
}

.chess-bord .light{  	
  background-color:var(--color-light);
  caret-color:var(--color-light);
}

.chess-bord .light:hover, .chess-bord .dark:hover{
	cursor:pointer		
}
	
.chess-bord .row{
  width:calc(var(--cell-size) * 0.4) !important;
}
.chess-bord .col{
  height:calc(var(--cell-size) * 0.4) !important;
}
.row, .col{
  border: 1px solid var(--color-dark) !important;
  cursor: pointer;
}

@keyframes click-piece{
  /*
  from {margin-left: 2px; margin-top: 2px;}
  to {margin-left: -2px; margin-top: -2px;}
  */
 50%{
    opacity: 0.0;
  }
}
/*-------------------------------------*/
.main-table{border-spacing: 10px}
.main-table .info{
 vertical-align: top;
 text-align: center;
 padding-top: 20px;
 height:200px;
 max-height: 200px;
}

.main-table .info i{
 font-size:calc(var(--cell-size) * 0.4);
 font-style: normal;
}

.main-table #info{
  height: 410px;
  max-height: 410px;
  overflow-y: auto;
}
.main-table td{
  border: 1px solid var(--color-dark);
  background-color: var(--color-light);
  border-radius: 5px;
  padding: 5px;
  width: 180px;
}

.main-table .bord{
  width: 454px;
}

.main-table  #info div{
  padding: 2 0 2 0;
  font-size: 20px;
  font-family: courier;
  font-weight: bold;
}

.main-table  #info i{
  font-family: AbhayaLibre-Regular;
  font-weight: normal;
}

#payerWonPieces, #opponentWonPieces{
  text-align: left; 
  padding-left: 10px
}

#payerWonPieces i, #opponentWonPieces i{
 font-size:calc(var(--cell-size) * 0.6);
 overflow-wrap: anywhere;
 margin: 10 5 0 5;
}

Class extDate-ECMAScript, Demo, Download extDateClassDemo.zip 7.89 KB , GitHab

Datei index.html
<!--https://www.alto-booking.com/developer/extDateClassDemo-->
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>Demo Auto-fill</title>
<script type="module" src="js/extDateClassDemo.js"></script>
<link rel="stylesheet" href="index.css">
</head>
<body>
<button>Next example</button>
<div align=center>
<h2>Demo of extDate class</h2>
<span>Code</span><span>Result</span>
<pre id="code"></pre>
<pre id="result"></pre>
</div>
</body>
</html>

Datei classExtDate.js
/*
  Class extDate
  Version: 1.0, 2025-03-01
  Author: Vladimir Kheifets vladimir.kheifets@online.de
  Copyright © 2025 Vladimir Kheifets All Rights Reserved
*/

class extDate{

  constructor(inDate, options, locale){
    this.creatDateObj(inDate);
    this.defaultLocale = (new Intl.DateTimeFormat()).resolvedOptions().locale;
    this.locale = locale?locale:this.defaultLocale;
    this.defaultOptions = options?options:{year: "numeric", month: "2-digit", day: "2-digit"};
  };

  format(options){
    let locale = this.locale;
    if(!options)
     options = this.defaultOptions;
    return (new Intl.DateTimeFormat(locale, options)).format(this.dateObj);
  };

  setLocale(inLocale){
    this.locale = inLocale?inLocale:this.defaultLocale;
    this.parse();
  };

  creatDateObj(inDate){
    var dateObj;
    if(inDate instanceof Date)
    {
      dateObj = inDate;
      dateObj = this.dateHoursToUTC(dateObj);
    }
    else
    {
      const regDT = /^(\d{4}\-\d{2}\-\d{2}|\d{4}\-\d{2}\-\d{2}(\ |T)\d{2}\:\d{2}\:\d{2})$/;
      const regT = /^\d{2}.\d{2}.\d{2}$/;
      if(regT.test(inDate))
      {
        var dateObj = new Date();
        let reg2 = /\d{1,2}/g
        let hms = inDate.match(reg2);
        dateObj.setUTCHours(...hms);
      }
      else if(regDT.test(inDate))
      {
        dateObj = new Date(inDate);
        dateObj = this.dateHoursToUTC(dateObj);
      }
      else
      {
        dateObj = new Date();
        dateObj = this.dateHoursToUTC(dateObj);
      }
    };

    this.dateObj = dateObj;
  };

  setIntervals(shift){
    const begin = this.dateObj;
    for(let key in shift)
    {
      switch (key)
      {
        case "years":
        begin.setFullYear(begin.getFullYear()+shift.years);
        break;

        case "months":
        begin.setUTCMonth(begin.getUTCMonth()+shift.months);
        break;

        case "days":
        begin.setUTCDate(begin.getDate()+shift.days);
        break;

        case "hours":
        begin.setUTCHours(begin.getUTCHours()+shift.hours);
        break;

        case "minutes":
        begin.setUTCMinutes(begin.getUTCMinutes()+shift.minutes);
        break;
      }
    }
    this.dateObj = begin;
    this.parse();
  };

  getIntervals(inDate){
    const beginTimeMS = this.dateObj.getTime();
    this.creatDateObj(inDate);
    const endDateObj = this.dateObj;
    let endTimeMS = endDateObj.getTime();
    let intervalMs = Math.abs(endTimeMS-beginTimeMS);
    this.daysBetweenDates = Math.floor(intervalMs/86400000);
    this.hoursBetweenDates = Math.floor(intervalMs/3600000);
    this.minutesBetweenDates = Math.floor(intervalMs/60000);
    this.dateObj = endDateObj;
  };

  parse(options)
  {
    const inDateObj = this.dateObj;
    if(!options) options = this.defaultOptions;
    let dateTimeStr = inDateObj.toISOString().slice(0, 19).replace("T"," ");
    let dateTime = dateTimeStr.split(" ");
    this.date = dateTime[0];
    this.dateLocale = this.format(options);
    this.time = dateTime[1];
    this.dateTime= dateTimeStr;
    this.year= inDateObj.getFullYear();
    this.month = inDateObj.getMonth();
    this.monthName = this.format({month: "long"});
    this.day= inDateObj.getDate();
    this.hours= inDateObj.getUTCHours();
    this.minutes= inDateObj.getMinutes();
    this.weekDay = inDateObj.getDay();
    this.weekDayName = this.format({weekday: "long"});
  };

  dateHoursToUTC(dateObj)
  {
    dateObj.setUTCDate(dateObj.getDate());
    dateObj.setUTCHours(dateObj.getHours())
    return dateObj;
  };

  outProp()
  {
    let dt = this;
    let out = "------------------------\n\n"
    out += "<b>Returned properties:\n\n</b>";
    for(var key in dt){
      if(typeof dt[key] !== 'object')
        out +=`\t<b>${key}</b> = ${dt[key]}\n`;
    }
    return out;
  };

};

export {extDate};

Datei extDateClassDemo.js
/*
  Demo of class extDate
  Version: 1.0, 2025-03-01
  Author: Vladimir Kheifets vladimir.kheifets@online.de
  Copyright © 2025 Vladimir Kheifets All Rights Reserved
*/

import {demo1} from './module/extDateClassDemo1.js';
import {demo2} from './module/extDateClassDemo2.js';
import {demo3} from './module/extDateClassDemo3.js';
import {demo4} from './module/extDateClassDemo4.js';
import {viewCode} from './module/viewCode.js';

const buttonEl = document.querySelector("button");
var demoN = 1;
var maxdemoN = 4;
demo1();
viewCode(demoN);
buttonEl.onclick = function(){
  demoN++;
  if(demoN > maxdemoN)
    demoN = 1;
  eval(`demo${demoN}();`);
  viewCode(demoN);
};

PHP-class CaesarCipher, Demo, auf GitHab

Datei index.php
<?
/*
	Demo PHP class CaesarCipher
	Version: 1.2.1
	Author: Vladimir Kheifets (kheifets.vladimir@online.de)
	Copyright ©2021 Vladimir Kheifets All Rights Reserved
*/
session_start();
#######################################################################
include_once("index.inc.php");
echo <<<HTML
<html lang="$la" xml:lang="$la">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no, user-scalable=0" >
<meta name="description" content="$_desc">
<meta name="keywords" content="$_keyw">
<meta name="author" content="Vladimir Kheifets">
<meta name="google" value="notranslate"/>
$header_link
<title>$_2</title>
<link rel="stylesheet" href="css/CaesarCipher.css" >
<link rel="stylesheet" href="css/modal.css" >
<link rel="stylesheet" href="css/button_to_up.css" >
<script src="CompactDOM.min.js"></script>
<script src="CaesarCipher.js"></script>
</head>
HTML;

$demo2=explode("<br>",$_18)[1];
echo <<<HTML
<body>
<div class="content">
<div class="top">
<span class="FL"><span id="cookie">$CookieLink</span>▼</span>
<span class="FR"><span id="la">$la_active</span>▼</span>
</div>
<h1>$_1</h1>
<h2>$_2
<div><span id="demo2">$demo2</span><span>▶</span></div>
</h2>
<p>$_3</p>
<form name="test" method="post">
<h2>
<span class="spsel">$_4: <select name="alphabet" id="alphabet">
$options
</select></span>
<span class="spsel">
 $_5: <select name="key" id="key">
$options_key
</select>
</span>
</h2>
$sel_text
</form>
</p>
HTML;

include_once("ClassCaesarCipher.php");

$CaesarСipher = new CaesarCipher($alphabet_frequency);

//-----------------------------------------
$text = $test_text[$n_text];
$text_encode = $CaesarСipher->encode($text, $key) -> text;
$text_decode = $CaesarСipher->decode($text_encode, $key) -> text;
print_text($_6, $text_encode, $key);
print_text($_7, $text_decode);

//---------------------------------------------------------------------------

echo "<br><hr><b>$_8</b><br>";
$res = $CaesarСipher->BruteForceDecoding($text_encode);
$MaxRatingKey = $res -> MaxRatingKey;
$MaxRating = $res -> MaxRating;
$decoded = $res -> decoded;
$keyRating = $res -> keyRating;
$rating = $res -> rating;
print_text($_9, $decoded[$MaxRatingKey], $res);
block_view_trigger(1, $_10);
foreach ($rating as $item)
{
	$repl["key"] = $key = $item[0];
	$repl["rating"] = $keyRating[$key];
	$text=$decoded[$key];
	print_text($_11,$text, $repl);
}
echo "</div>";
//---------------------------------------------------------------------------

echo "<br><hr><b>$_12</b><br>";
$res = $CaesarСipher->DecodingByCharacterFrequency($text_encode);

if($res->error == 2)
{
	echo $_13;
}
else
{

	$character = $res->MostFrequentlyCharacter;
	$indA = $res->MostFrequentlyCharacterInd;
	$character = ord($character)==32?$_14:$character;
	$s=DicReplaceV($_15, compact("character","indA"))."<br>";
    $keyRating = $res -> keyRating;
	$MaxRatingKey = $res -> MaxRatingKey;
	$MaxRating = $res -> MaxRating;
	$decodedKeys = $res -> decodedKeys;
	$alldecoded = $res -> decoded;
	$rating = $res -> rating;
	$i_decoded = $decodedKeys[$MaxRatingKey];
	$decoded = $alldecoded[$i_decoded];
	print_text($s.$_16, $decoded[3], $res);
	block_view_trigger(2, $_10);
	foreach ($rating as $item)
	{
		if($item[1]>0)
		{
			$repl["indA"]=$indA;
			$repl["key"] = $key=$item[0];
			$i=$decodedKeys[$key];
			$decoded = $alldecoded[$i];
			$repl["decoded"] = $decoded[1];
			$repl["character"] = ord($decoded[0])==32?$_14:$decoded[0];
			$repl["keyRating"] = $keyRating[$key];
			print_text($_17, $decoded[3], $repl);
		}

	}
}
$Y=date("Y");
echo <<<HTML
</div>
<hr>
<div class="copy"><p>©</p>$Y Alto Booking</div>
</div>
</body>
</html>
HTML;
?>

Datei index.inc.php
<?
/*
Demo PHP class CaesarCipher
index.inc.php
Version: 3.1, 2026-05-22
Author: Vladimir Kheifets (kheifets.vladimir@online.de)
Copyright (c) 2026 Vladimir Kheifets All Rights Reserved
*/
$expires=time()+2592000;
$path="CaesarCipher";
if(isset($_POST['alphabet']))
{
	extract($_POST);
}
else if(!empty($_COOKIE["se"]))
{
	$selected = (array)json_decode($_COOKIE["se"]);
	extract($selected);
}
else if(!empty($_SESSION["se"]))
{
	extract($_SESSION);
}
if(empty($alphabet))
{
	$alphabet = "latina";
	$n_text = $key = 1;
}
if(empty($_GET["la"]))
{	if(empty($la))
	{
		$la=!empty($_SESSION["la"])?$_SESSION["la"]:"en";
	}
}
else
	$la = $_GET["la"];
$CookieLink = !empty($_COOKIE["se"])?"✔Cookie":"Cookie";
saveSelected();
if(isset($_COOKIE["se"]))
{
	saveSelected(true,$expires, $path);
}
//---------------------------------------------------------------------------
extract(GetArrayFromFile("dictionary/$la.txt"),EXTR_PREFIX_ALL,"");
//---------------------------------------------------------------------------
$la_liste=file("dictionary/language.txt");
$href_suff="?la=";
$header_link = "";
$defhref="";
foreach($la_liste as $line)
{
	$tmp = explode("~",trim($line));
	$lang=$hreflang=$tmp[0];
	$language[$lang] = $tmp[1];
	if($tmp[2])	$hreflang.="-".$tmp[2];
	$href = $defhref.$href_suff.$lang;
	$header_link .= "<link rel=\"alternate\" hreflang=\"$hreflang\" href=\"$href\" />\n";
}
$la_active=$language[$la];
//---------------------------------------------------------------------------
$alphabet_frequency = GetArrayFromFile("alphabets/alphabet_$alphabet.txt");
$options_key = "";
$max_keys = count($alphabet_frequency);
for ($i=1; $i < $max_keys; $i++) {
	$selected = $i==$key?"selected":"";
	$options_key .= "<option $selected>$i</option>";
}
//---------------------------------------------------------------------------
$options_arr = file("alphabets/alphabets.txt");
$options = "";
foreach($options_arr as $value){
	$value=trim($value);
	$text=${"_".$value};
	$selected = $value==$alphabet?"selected":"";
	$options .= "<option value=\"$value\" $selected>$text</option>";
}
//---------------------------------------------------------------------------
$test_text = GetArrayFromFile("tests/test_$alphabet.txt"," ");
$sel_text = "";
foreach($test_text as $n => $text){
	$sel_text .= select_text($n, $n_text, $text);
}
//---------------------------------------------------------------------------
extract(GetArrayFromFile("dictionary/$la.txt"),EXTR_PREFIX_ALL,"");
//---------------------------------------------------------------------------
$CookieLink = !empty($_COOKIE["se"])?"✔Cookie":"Cookie";
//---------------------------------------------
if(isset($_GET["_la"]))
{
	extract($_POST);
	saveSelected();
	if(isset($_COOKIE["se"]))
	{
		saveSelected(true,$expires, $path);
	}
	echo "<div class=\"la-liste\">";
	foreach($language as $laS =>$name)
	{
		echo <<<HTML
		<span id="la_$laS">$name</span>
		HTML;
	}
	echo "</div>";
	exit;
}
else if(isset($_GET["_cookie"]))
{
	$cookie_b=isset($_COOKIE["se"])?$_cookieN:$_cookieY;
	echo <<<EOF
	<div class="set-cookie">
	$_cookie
	<div align="center"><p id="cookie_p">$cookie_b</p></div>
	</div>
	EOF;
	exit;
}
else if(isset($_GET["_cookie_p"]))
{
	$link="Cookie";
	if(isset($_COOKIE["se"]))
	{
		saveSelected(true, 0, $path);
		echo $link;
	}
	else
	{
		extract($_POST);
		saveSelected(true, $expires, $path);
		echo "✔$link";
	}
	exit;
}
else if(isset($_GET["_demo2"]))
{
	include_once("Demo_GetCharacterFrequency.php");
	exit;
}
//---------------------------------------------------
function GetArrayFromFile($file_name, $add_line=null){
	$buf=file($file_name);
	if(!$add_line) $add_line = "<br>";
	foreach($buf as $line)
	{
		if(preg_match("/^\/{2}/",$line)) continue;
		if(preg_match("/^.+\~/",$line))
		{
			$tmp = explode("~",$line);
			$out[$tmp[0]] = trim($tmp[1]);
		}
		else
		{
			$out[$tmp[0]] .= $add_line.trim($line);
		}
	}
	return $out;
}
//---------------------------------------------------
function DicReplaceV($text, $repl){
	if(is_array($repl) OR is_object($repl))
	{
	    $pattern="{\{(.*?)\}}";
	    preg_match_all($pattern,$text,$m, PREG_PATTERN_ORDER);
		$k=count($m[1]);
		for ($i=0; $i<$k; $i++)
		{
			$a=$m[0][$i];
			$b=$m[1][$i];
			if(is_array($repl))
				$text = str_replace($a, $repl[$b], $text);
			else
				$text = str_replace($a, $repl->$b, $text);
		}
	}
	else
		$text = str_replace("{}",$repl,$text);
	return $text;
}
//---------------------------------------------------
function print_text($label, $text, $repl=null){
if($repl) $label = DicReplaceV($label, $repl);
echo <<<HTML
<p>
$label<br>
<textarea>$text</textarea>
</p>
HTML;
}
//---------------------------------------------------
function select_text($i, $i_check, $text){
$checked = $i==$i_check?"checked":"";
return <<<HTML
<p>
<div><input name="n_text" type="radio" value="$i" $checked>$i</div>
<textarea>$text</textarea>
</p>
HTML;
}
//---------------------------------------------------
function block_view_trigger($i, $text, $repl=null){
if($repl) $text = DicReplaceV($text, $repl);
echo <<<HTML
	<div id='vbl$i'>$text</div>
	<span id='sbl$i'>▼</span>
	<div id='bl$i'>
	HTML;
}
//----------------------------------------------------------------
function saveSelected($inCookie=null,$expires=null, $path=null){
	if($inCookie AND $expires==0)
	{
		setcookie("se", "", time()-7200 ,$path);
		unset ($_COOKIE["se"]);
		return;
	}
	global $la, $alphabet, $latina, $key, $n_text;
	$selected=compact("la","alphabet", "key", "n_text");
	if($inCookie)
	{
		$se=json_encode($selected);
		setcookie("se", $se, $expires,$path);
	}
	else
		$_SESSION["se"]=$selected;
}
//----------------------------------------------------
?>

Datei ClassCaesarCipher.php
<?
/*
	PHP class CaesarCipher
	Version: 3.1, 2026-05-22
	Author: Vladimir Kheifets (kheifets.vladimir@online.de)
	Copyright (c) 2026 Vladimir Kheifets All Rights Reserved
*/

class CaesarCipher {
	
	protected $alphabet;
	protected $alphabet_ind;
	protected $alphabet_count;
	protected $frequency;
	protected $alphabet_count_s;
	protected $most_frequently_used;
	protected $most_frequently_used_ind;
	protected $most_frequently_used_count;
	protected $min_character_frequency;	

	function __construct($alphabet_frequency=null, $min_frequency=3) {
		if($alphabet_frequency)
		{			        
			foreach($alphabet_frequency as $k=>$v) $alphabet_frequency_2c[]=[$k,$v];			
			$frequency = $c1 = array_column($alphabet_frequency_2c, 1);		
			$alphabet = array_column($alphabet_frequency_2c, 0);
			$min_character_frequency = (max($frequency) - min($frequency)) * 0.2;		
			$alphabet_count = count($alphabet);			
			$alphabet_count_s = $alphabet_count - 1;
			array_multisort($c1, SORT_DESC, $alphabet_frequency_2c);
			$most_frequently_used = [];						
			foreach ($alphabet_frequency_2c as $s) 
			{
				if($s[1]>$min_frequency) $most_frequently_used[] = $s[0];					
			}				
			$this->alphabet = $alphabet;
			$this->frequency = $frequency;
			$this->alphabet_ind = array_flip($alphabet);
			$this->alphabet_count = $alphabet_count;
			$this->alphabet_count_s = $alphabet_count_s;
			$this->most_frequently_used = $most_frequently_used;
			$this->most_frequently_used_ind = array_flip($most_frequently_used);
			$this->most_frequently_used_count = count($most_frequently_used);
			$this->min_character_frequency = $min_character_frequency;
		}		
	}	
	//-------------------------------------------------
	public function encode($inp_text, $key){
		
		if($error = self::check_input($inp_text, $key) > 0)
		{
			return (object)
			[
				"error" => $error,					 	   
			];
		}
		$alphabet=$this->alphabet;
		$alphabet_ind=$this->alphabet_ind;		
		$alphabet_count = $this->alphabet_count;
		$alphabet_count_s = $this->alphabet_count_s;
		$out_text="";
		$buf = preg_split('//u', $inp_text, -1, PREG_SPLIT_NO_EMPTY);
		echo "<hr>$inp_text<hr>";
		foreach ($buf as $symbol) {	
			if(isset($alphabet_ind[$symbol]))
			{
				$ind=$alphabet_ind[$symbol];
				$ind_c = $ind + $key;
				if($ind_c < 0)
					$ind_c += $alphabet_count;
				else if($ind_c > $alphabet_count_s)
					$ind_c -= $alphabet_count;
				$out_text.= $alphabet[$ind_c];
			}
		}		
		return (object)
		[
			"error" => 0,
			"text" => $out_text				 	   
		]; 		
	}
//-------------------------------------------------
	public function decode($inp_text, $key){		
		if($error = self::check_input($inp_text, $key) > 0)
		{
			return (object)
			[
				"error" => $error,					 	   
			];
		}
		$key = -$key;
		$alphabet=$this->alphabet;
		$alphabet_ind=$this->alphabet_ind;	
		$frequency=$this->frequency;
		$min_character_frequency= $this->min_character_frequency;
		$most_frequently_used = $this->most_frequently_used;						
		$alphabet_count = $this->alphabet_count;
		$alphabet_count_s = $this->alphabet_count_s;
		$most_frequently_used_ind = $this->most_frequently_used_ind;
		$most_frequently_used_count = $this->most_frequently_used_count;
		$len_inp_text=mb_strlen($inp_text);	
		$out_text="";
		$buf = preg_split('//u', $inp_text, -1, PREG_SPLIT_NO_EMPTY);
		$rating=0;
		foreach($buf as $symbol)
		{
			if(isset($alphabet_ind[$symbol]))
			{
				$ind=$alphabet_ind[$symbol];
				$ind_k = $ind + $key;
				if($ind_k < 0)
					$ind_k +=$alphabet_count;
				else if($ind_k > $alphabet_count_s)
					$ind_k +=-$alphabet_count;
				$character = $alphabet[$ind_k];
				if(isset($most_frequently_used_ind[$character])){
					$i=$alphabet_ind[$character];
					if($frequency[$i] > $min_character_frequency AND $character!=" ")
					{
						$k = 1 + ($most_frequently_used_count - $most_frequently_used_ind[$character])
						/($most_frequently_used_count - 1);
						//echo "$character $k ".$frequency[$i]." ".round($frequency[$i] * $k, 2)."<br>";
					 	$rating += round($frequency[$i] * $k, 3);
					}
				}
				$out_text .= $character;

			}
		}
		//echo "<br>$rating<hr>";				
		return (object)
		[
			"error" => 0,
			"rating" => $rating,
			"text"	=> $out_text		 	   
		];
	}

	//--------------------------------------	
	public function  BruteForceDecoding($inp_text){	
		$alphabet_count = $this->alphabet_count;
		$decoded=[];
		for($key=1; $key< $alphabet_count;$key++)
		{	
			$res = self::decode($inp_text, $key);
			$decoded[$key] = $res->text;
			$rating[] = [$key, $keyRating[$key] = $res->rating];										
		}
		$ratingC1 = array_column($rating, 1);		
		array_multisort($ratingC1, SORT_DESC, $rating);		
		$MaxRatingKey = $rating[0][0];
		$MaxRating = $rating[0][1];		
		return (object)
		[
			"keyRating" => $keyRating,
			"MaxRatingKey" => $MaxRatingKey, 
			"MaxRating" => $MaxRating,
			"rating" => $rating,
			"decoded"	=> $decoded			
		];
			
	}
	//--------------------------------------	
	public function  DecodingByCharacterFrequency($inp_text, $MaxNumberDecoding=null){		
		if(empty($inp_text))
		return (object)
		[
			"error" => 1			 	   
		];
		$buf = preg_split('//u', $inp_text, -1, PREG_SPLIT_NO_EMPTY);
		$CharacterFrequency = self::GetCharacterFrequency($buf);		
		if($CharacterFrequency -> error)
		{
			return (object)
			[
			   "error" => 2			 	   
			];		
		}	

		$alphabet = $this->alphabet;
		$alphabet_ind = $this->alphabet_ind;
		$most_frequently_used = $this -> most_frequently_used;
		$max_number_mfu =  count($most_frequently_used);
		if
		(
			$MaxNumberDecoding > 0 
			AND 
			$MaxNumberDecoding < $max_number_mfu
		) 
		$max_number_mfu = $MaxNumberDecoding;			
		$MostFrequencyCharacter = $CharacterFrequency-> MostFrequentlyCharacter;		
		$iAmfuT = $CharacterFrequency -> MostFrequentlyCharacterInd;				
		//------------------------------------------------		
		$decoded=[];
		$decodedKeys=[];
		for ($i=0; $i < $max_number_mfu; $i++) { 	
			$CharacterMFU = $most_frequently_used[$i];
			$iAmfu = $alphabet_ind[$CharacterMFU];
			$key = $iAmfuT - $iAmfu;
			$decoded[$i][0] = $CharacterMFU;
			$decoded[$i][1] = $iAmfu;
			$decoded[$i][2] = $key;
			$res = self::decode($inp_text, $key);			
			if(isset($res->text))
			{
				$decoded[$i][3] = $res->text;
				$rating[] = [$key, $keyRating[$key] = $res->rating];
				$decodedKeys[$key]=$i;
			}
		}
		$ratingC1 = array_column($rating, 1);		
		array_multisort($ratingC1, SORT_DESC, $rating);		
		$MaxRatingKey = $rating[0][0];
		$MaxRating = $rating[0][1];	
		return (object)
		[
			"error" => 0,		   
			"MostFrequentlyCharacter" => $MostFrequencyCharacter,
			"MostFrequentlyCharacterInd" => $iAmfuT,
			"keyRating" => $keyRating,
			"MaxRatingKey" => $MaxRatingKey, 
			"MaxRating" => $MaxRating,
			"decodedKeys" => $decodedKeys,
			"decoded" => $decoded,
			"rating" => $rating		   
		];	 
	}
	//-------------------------------------------------
	public function  GetCharacterFrequency($buf, $inp_alphabet=null, $decimal=2, $sort_col=null){
		if(empty($buf))
		return (object)
		[
			"error" => 1			 	   
		];
		if(!is_array($buf))
			$buf = preg_split('//u', $buf, -1, PREG_SPLIT_NO_EMPTY);
		if($sort_col)
		{
			$sort_col=$sort_col-1;
			$sort = $sort_col==0?SORT_ASC:SORT_DESC;			
		}
		else
		{
			$sort=SORT_DESC;
			$sort_col=1;
		}			
		$alphabet = $this->alphabet;
		$alphabet_ind = $this->alphabet_ind;	
		$uniq_buf = array_unique($buf);
		$buf_count = count($buf);		
		$Frequency=[];
		if($buf_count==count($uniq_buf))
		{
			return (object)
			[
				"error" => 2				   
			];	
		}					

		foreach ($uniq_buf as $i => $vu)
		{
			$Frequency[$i]=0;
			foreach($buf as $v)
			{
				if($v===$vu) $Frequency[$i]++; 
			}
		}		
		foreach($Frequency as $i=>$vu) 
		{
			$character = $uniq_buf[$i];
			$f = round(($vu/$buf_count)*100, $decimal);					
			$CharacterFrequency[]=[$character, $f]; 
		}
		
		$CharacterFrequencyCol = array_column($CharacterFrequency, $sort_col);			
		array_multisort($CharacterFrequencyCol, $sort, $CharacterFrequency);			
		
		if($inp_alphabet)
		{
			if(is_string($inp_alphabet))
			{	
				$alphabet_arr = preg_split('//u', $inp_alphabet, -1, PREG_SPLIT_NO_EMPTY);
				$CharacterFrequencyC0 = array_flip(array_column($CharacterFrequency, 0));				
				foreach($alphabet_arr as $character)
				{
					if(isset($CharacterFrequencyC0[$character]))
					{
						$i = $CharacterFrequencyC0[$character];
						$alphabet_frequency[$character]=$CharacterFrequency[$i][1];
					}
				} 				
			}
			else
			{
				foreach($CharacterFrequency as $item)
				{
					$character = $item[0];
					$f = $item[1];
					$alphabet_frequency[$character]=$f;		
				}	

				
			}
			return (object)	
			[		   
				"error" => 0,
				"alphabet_frequency" => $alphabet_frequency							   
			];		
		}				
		
		$character = $CharacterFrequency[0][0];		
		return (object)
		[		   
			"error" => 0,
			"CharacterFrequency" => $CharacterFrequency,
			"MostFrequentlyCharacter" => $character,
			"MostFrequentlyCharacterInd" => $alphabet_ind[$character]		   
		];		
	}	
	//-------------------------------------------------
	protected function check_input($inp_text, $key){
		$error = 0;
		if(empty($inp_text)) $error++;			
		$a_key = abs($key);	
		$max_key = $this->alphabet_count_s;		
		if($a_key<1 OR $a_key > $max_key)  $error++;
		return $error;
	}
	//--------------------------------------------------
}
?>

Datei CaesarCipher.js
/*
	Demo PHP class CaesarCipher
	Version: 1.2.3, 2021-04-29
	Author: Vladimir Kheifets (kheifets.vladimir@online.de)
	Copyright (c) 2021 Vladimir Kheifets All Rights Reserved
*/
start = function()
{
	ViewBlock = function (){
		vblId = this.id;
		blId = vblId.substr(1);
		bl = _("#"+blId);
		sbl = _("#s"+blId);
		if(bl.ishide())
		{
			bl.show();
			sbl.content("▲");
			_("textarea").resize(10);
			Ytarget = _(this).position().top;
 			__.scroll(Ytarget);
		}
		else
		{
			bl.hide();
			sbl.content("▼");
		}
	}

	SubmitForm = function() {
		id=this.id;
		if(__.ins("alphabet", id))
		{
			_("input").checked(0);
			_("#key").selected(0);
		}
		__.send();
	};

	SendtoModal = function(){
		id=this.id;
		get = "_"+id+"=1";
		url=__.url(get);
		attr={url:url,to:"#modal",func:viewModal};
		__.send(attr);
	};

	viewModal = function(rsp, to, req){
		_(to).modal(rsp);
		if(__.ins("_la",req))
		{
			setLa = function(){
				la = this.id.substr(3);
				_("form").attr("action", "?la="+la);
				__.send();
			}
			_("span[id^='la_']").click(setLa);
		}
		else if(__.ins("_cookie=",req))
		{
			setCookie = function(e){
				attr={url:"index.php?_cookie_p=1",to:"#cookie"};
				__.send(attr);
				__.modal(0);
			}
			_("#cookie_p").click(setCookie);
		}
		else if(__.ins("_demo2",req))
		{
			ffc=_(".ffc");
			ffc.attr("readonly",true);
			ffc.resize(10);
			inp_text=_("#inp_text");
			inp_text.attr("readonly",true);
			_(to).position(__.pc);
		}
	};

	_("#alphabet").change(SubmitForm);
	_("#key").change(SubmitForm);
	_("input[type='radio']").change(SubmitForm);
	_("div[id^='vbl']").click(ViewBlock);
	_("div[id^='bl']").each(function(el){el.hide();});
	_("#cookie").click(SendtoModal);
	_("#la").click(SendtoModal);
	_("#demo2").click(SendtoModal);
	_("textarea").attribute("readonly",true);
	_(".content textarea").resize(10);

	ch_resize = function(){_(".content textarea").resize(10)};
	ch_orient = function(){__.reload()};
	__.change(ch_resize, ch_orient);
	__.modal();
	__.scroll();
}
__.ready(start);

GCHQ-Weihnachtsrätsel

decodingDigitsIntoLetters, Demo, auf GitHab

Problemstellung
The letters in TWO UV PAIRS have
the values 0,1,2,…,9 in some order,
with each letter representing
a different digit.

UV+UV+V=VAR
RxPxP=AIR
SO+SO=VOW

What is 1234567?

Datei index.php
<!--

Demo PHP scripts decodingDigitsIntoLetters

Version: 1.0, 2025-12-22
Author: Vladimir Kheifets (vladimir.kheifets@online.de)
Copyright (c) 2025 Vladimir Kheifets All Rights Reserved

The scripts provides a solution to one task of the GCHQ 2025 Christmas Challenge.
(GCHQ - UK's intelligence, security and cyber agency)

The letters in TWO UV PAIRS have the values
0,1,2,…,9 in some order, with each letter representing
a different digit.
UV+UV+V=VAR
RxPxP=AIR
SO+SO=VOW
What is 1234567 ?

-->


<html>
<head>
<title>Demo PHP scripts decodingDigitsIntoLetters</title>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1,
user-scalable=no, user-scalable=0" >
<style>
body{
 font-family: arial;
 font-size: 12pt;
 padding: 20pt 0pt 20pt 20pt;
}
@media screen and (max-width: 400px) {
    body{padding: 0pt}
}

</style>
</head>
<body>
<pre>

<?PHP

$taskDescription = file_get_contents("taskDescription.php");
echo <<<HTML
One task from 2025 GCHQ Christmas Challenge
(GCHQ - UK's intelligence, security and
cyber agency)

$taskDescription

HTML;

$fNames =
[
    "decodingDigitsIntoLettersClass",
    "decodingDigitsIntoLettersFunctions",
    "solve_cryptarithmetic"
];

foreach($fNames as $i => $fName)
{
    $fName .= ".php";
    echo "\n\n<h3>$fName</h3>";

    include_once($fName);

    $start = hrtime(true);
    switch ($i)
    {
        case 0:
            $obj = new decodingDigitsIntoLetters($taskDescription);
            break;
        case 1:
            $obj = decoding($taskDescription);
            break;
        case 2:
            $obj = solve_cryptarithmetic($taskDescription);
            break;
    }

    $end=hrtime(true);
    $duration[$i]= ($end-$start)/1e+6;

    echo  "\nreport:\n\n", $obj-> report;
    if($obj-> error)
        echo  "\n\nerrorMsg: ", $obj-> errorMsg;
    else
    {
        echo  "\n\nresult: ", $obj-> result;
        echo  "\n\ndecoded ";
        print_r($obj -> decoded);
    }

}

##############################################
echo "\n<h3>Duration:</h3>";
foreach($fNames as $i => $fName)
{
    $fName .= ".php";
    echo "<b>$fName</b>: {$duration[$i]}s\n";
}
?>
</body>
</html>

Datei decodingDigitsIntoLettersClass.php
<?PHP
/*
PHP-class decodingDigitsIntoLetters

Version: 1.0, 2025-12-22
Author: Vladimir Kheifets (vladimir.kheifets@online.de)
Copyright (c) 2025 Vladimir Kheifets All Rights Reserved

The script provides a solution to one task of the GCHQ 2025 Christmas Challenge.
(GCHQ - UK's intelligence, security and cyber agency)

The letters in TWO UV PAIRS have the values
0,1,2,…,9 in some order, with each letter representing
a different digit.
UV+UV+V=VAR
RxPxP=AIR
SO+SO=VOW
What is 1234567 ?

*/

class decodingDigitsIntoLetters{
    private $taskDescription;
    private $tmpLettersAlpha = [];
    private $formulaLetters = [];
    private $allFormulasLetters = [];
    private $formulas;
    private $foundLettersDigit = [];
    public $report;
    public $result;
    public $decoded;
    public $error = false;
    public $errorMsg = "";

    function __construct($taskDescription){
        $this -> report = $taskDescription;
        preg_match_all("/.+\=\S{3}/m",$taskDescription, $match);
        $this -> formulas = $match[0];
        if(count($match[0]) == 3){
           $this -> formulas = $match[0];
           foreach($match[0] as $nFormula => $formula)
                $this -> allFormulasLetters[$nFormula] = $this -> getFormulaLetters($formula);
           $this -> decoding();
        }
        else
        {
            $this -> error = true;
            $this -> errorMsg = "Three formulas were not found in the task description";
        }
    }

    #########################################################################
    private function getLettersDigit($resDigit, $lettersVal){
        $tmpLettersAlpha = $this -> tmpLettersAlpha;
        $formulaLetters = $this -> formulaLetters;
        foreach($formulaLetters[1] as $j => $letter)
        {
            $tmpLettersAlpha[$letter][]=$resDigit[$j];
        }

        foreach($formulaLetters[6] as $inLetter){
            $tmpLettersAlpha[$inLetter][]=$lettersVal[$inLetter];
        }
        $this -> tmpLettersAlpha = $tmpLettersAlpha;
    }
    #########################################################################
    private function getFoundLettersDigit(){
        $foundLettersDigit = $this -> foundLettersDigit;
        $tmpLettersAlpha = $this -> tmpLettersAlpha;
        foreach($tmpLettersAlpha as $letter => $letterDigit){
            $tmp = array_unique( $letterDigit);
            if(count($tmp) == 1)
                $foundLettersDigit[$letter]=$tmp[0];
        }

        $this -> foundLettersDigit = $foundLettersDigit;
        $this -> tmpLettersAlpha = [];
    }
    #########################################################################
    private function outputDecodedLetters(){
        $foundLettersDigit =  $this -> foundLettersDigit;
        $buf = array_flip($foundLettersDigit);
        ksort($buf);
        $this -> report .=  "\nDecoded:\n";
        foreach($buf as $dig => $letter)
            $this -> report .=  "$letter => $dig\n";

        if(count($buf)>8)
        {
            $this -> result = substr(implode("", array_values($buf)),1,7);
            $this -> report .=  "\n\n1234567 is ";
            $this -> report .=  $this -> result;
            $this -> decoded = array_flip($buf);
        }
    }
    #########################################################################
    private function getFormulaLetters($formula){
        $buf = explode("=", $formula);
        $formulaLetters = [];
        $formulaLetters[] = $buf[1];
        $formulaLetters[] = str_split($buf[1]);
        preg_match_all("/\p{Lu}/", $buf[0], $matches);
        $letters = array_unique($matches[0]);
        $formulaLetters[] = $letters;
        preg_match_all("/(\p{Lu}+|[\+\-x])/", $buf[0], $matches);
        $formulaLetters[] = $matches[0];
        foreach($formulaLetters[1] as $i => $resLetter){
            $key = array_search($resLetter, $formulaLetters[2]);
            if($key !== false)
            {
                $formulaLetters[5][$i] = $key;
                $reverseKey = $key == 1?0:1;
                $formulaLetters[6][] = $formulaLetters[2][$reverseKey];
            }
        }

        return $formulaLetters;
    }

    #####################################################

    private function calcFormula($inValues){
        $foundLettersDigit = $this -> foundLettersDigit;
        $foundDigitsLetter = array_flip($foundLettersDigit);
        $formulaLetters = $this -> formulaLetters;
        foreach($formulaLetters[2] as $i => $Letter)
            $lettersVal[$Letter] = $inValues[$i];

        $рredefResStr = "";
        foreach($formulaLetters[1] as $i => $Letter)
            if(array_key_exists($Letter, $foundLettersDigit))
                $рredefResStr .= $foundLettersDigit[$Letter];
        $рredefRes = strlen($рredefResStr) == 3?intval($рredefResStr):0;

        $buf = "\$res=";
        foreach($formulaLetters[3] as $Letters)
        {
            if(preg_match("/\p{Lu}/", $Letters))
            {
                $tmp = "";
                foreach(str_split($Letters) as $Letter)
                    $tmp .= $lettersVal[$Letter];
                $buf .= intval($tmp);

            }
            else
                $buf .= $Letters=="x"?"*":$Letters;
        }

        eval($buf.";");

        if($res > 0 AND $res == $рredefRes)
        {
            foreach($lettersVal as $Letter => $digit)
                if(!array_key_exists($Letter, $foundLettersDigit))
                     $foundLettersDigit[$Letter] = $digit;
            $this ->  foundLettersDigit =  $foundLettersDigit;
            return true;
        }
        else
        {
            $resDigits = array_unique(str_split((string) $res));

            if(count($resDigits) > 2)
            {

                $resOK = true;

                foreach($resDigits as $i => $resDigit)
                {
                    $letter = $formulaLetters[1][$i];
                    if(array_key_exists($resDigit, $foundDigitsLetter))
                    {
                            if( $foundDigitsLetter[$resDigit] != $letter)
                            {
                                $resOK = false;
                                break;
                            }
                    }
                }

                //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

                if($resOK)
                {
                    foreach($formulaLetters[5] as $i => $inValKey){
                        if($inValKey !== false)
                        {
                            if($resDigits[$i] == $inValues[$inValKey])
                            {
                                $this -> getLettersDigit($resDigits, $lettersVal);
                            }
                        }
                    }
                }
            }
        }
        return false;
    }
    #####################################################

    private function decoding(){
        $hr = "\n--------------------------------\n";
        $allDig = range(0,9);
        $formulas = $this -> formulas;
        $formulas[] = $formulas[0];
        foreach($formulas as $nFormula => $formula)
        {
            $foundLettersDigit =  $this -> foundLettersDigit;
            $this -> report .=  "$hr\nFormula: $formula\n";
            $formulaLetters = $this -> allFormulasLetters[$nFormula>2?0:$nFormula];
            $this -> formulaLetters = $formulaLetters;

            $arrK = array_diff($allDig, array_values($foundLettersDigit));

            foreach($formulaLetters[2] as $i => $letter)
            {
                if(array_key_exists($letter, $foundLettersDigit))
                    $arr[$i] = [$foundLettersDigit[$letter]];
                else
                    $arr[$i] = $arrK;
            }

            foreach($arr[0] as $L1)
            {
                foreach($arr[1] as $L2)
                {
                    $fin = $this -> calcFormula([$L1, $L2]);
                    if($fin) break;
                }
            }

            if(!$fin) $this -> getFoundLettersDigit();
            $this -> outputDecodedLetters();
        }
    }
}

?>

Datei decodingDigitsIntoLettersFunctions.php
<?PHP
/*
PHP-functions decodingDigitsIntoLetters

Version: 1.0, 2025-12-22
Author: Vladimir Kheifets (vladimir.kheifets@online.de)
Copyright (c) 2025 Vladimir Kheifets All Rights Reserved

The script provides a solution to one task of the GCHQ 2025 Christmas Challenge.
(GCHQ - UK's intelligence, security and cyber agency)

The letters in TWO UV PAIRS have the values
0,1,2,…,9 in some order, with each letter representing
a different digit.
UV+UV+V=VAR
RxPxP=AIR
SO+SO=VOW
What is 1234567 ?

*/

#########################################################################
function getLettersDigit($formulaLetters, $resDigit, $lettersVal, &$tmpLettersAlpha){
    foreach($formulaLetters[1] as $j => $letter)
    {
        $tmpLettersAlpha[$letter][]=$resDigit[$j];
    }

    foreach($formulaLetters[6] as $inLetter){
        $tmpLettersAlpha[$inLetter][]=$lettersVal[$inLetter];
    }
}
#########################################################################
function getFoundLettersDigit(&$tmpLettersAlpha, &$foundLettersDigit){

    foreach($tmpLettersAlpha as $letter => $letterDigit){
        $tmp = array_unique( $letterDigit);
        if(count($tmp) == 1)
            $foundLettersDigit[$letter]=$tmp[0];
    }

    $tmpLettersAlpha=[];
}
#########################################################################
function outputDecodedLetters($foundLettersDigit, &$report){
    $buf = array_flip($foundLettersDigit);
    ksort($buf);
    $report .=  "\nDecoded:\n";
    foreach($buf as $dig => $letter)
        $report .=  "$letter => $dig\n";
    if(count($buf)>8)
    {
       $result = substr(implode("", array_values($buf)),1,7);
       $report .= "\n\n1234567 is $result";
       return (object)
       [
           "error" => false,
           "errorMsg" => "",
           "result" => $result,
           "report" => $report,
           "decoded" => array_flip($buf)
       ];
    }
}
#########################################################################
function getFormulaLetters($formula){
    $buf = explode("=", $formula);
    $out = [];
    $out[] = $buf[1];
    $out[] = str_split($buf[1]);
    preg_match_all("/\p{Lu}/", $buf[0], $matches);
    $letters = array_unique($matches[0]);
    $out[] = $letters;
    preg_match_all("/(\p{Lu}+|[\+\-x])/", $buf[0], $matches);
    $out[] = $matches[0];
    foreach($out[1] as $i => $resLetter){
        $key = array_search($resLetter, $out[2]);
        if($key !== false)
        {
            $out[5][$i] = $key;
            $reverseKey = $key == 1?0:1;
            $out[6][] = $out[2][$reverseKey];
        }
    }

    return $out;
}
#########################################################################
function calcFormula($inValues, $formulaLetters, &$tmpLettersAlpha, &$foundLettersDigit){

    $foundDigitsLetter = array_flip($foundLettersDigit);

    foreach($formulaLetters[2] as $i => $Letter)
        $lettersVal[$Letter] = $inValues[$i];

    $рredefResStr = "";
    foreach($formulaLetters[1] as $i => $Letter)
        if(array_key_exists($Letter, $foundLettersDigit))
            $рredefResStr .= $foundLettersDigit[$Letter];
    $рredefRes = strlen($рredefResStr) == 3?intval($рredefResStr):0;

    $buf = "\$res=";
    foreach($formulaLetters[3] as $Letters)
    {
        if(preg_match("/\p{Lu}/", $Letters))
        {
            $tmp = "";
            foreach(str_split($Letters) as $Letter)
                $tmp .= $lettersVal[$Letter];
            $buf .= intval($tmp);

        }
        else
            $buf .= $Letters=="x"?"*":$Letters;
    }

    eval($buf.";");

    if($res > 0 AND $res == $рredefRes)
    {
        foreach($lettersVal as $Letter => $digit)
            if(!array_key_exists($Letter, $foundLettersDigit))
                 $foundLettersDigit[$Letter] = $digit;
        return true;
    }
    else
    {
        $resDigits = array_unique(str_split((string) $res));
        if(count($resDigits) > 2)
        {
            $resOK = true;
            foreach($resDigits as $i => $resDigit)
            {
                $letter = $formulaLetters[1][$i];
                if(array_key_exists($resDigit, $foundDigitsLetter))
                {
                        if( $foundDigitsLetter[$resDigit] != $letter)
                        {
                            $resOK = false;
                            break;
                        }
                }
            }

            #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

            if($resOK)
            {
                foreach($formulaLetters[5] as $i => $inValKey){
                    if($inValKey !== false)
                    {
                        if($resDigits[$i] == $inValues[$inValKey])
                        {
                            getLettersDigit($formulaLetters, $resDigits, $lettersVal, $tmpLettersAlpha);
                        }
                    }
                }
            }
        }
    }
}
#########################################################################
function decoding($taskDescription){
    $hr = "\n--------------------------------\n";
    $report = $taskDescription;
    $allDig = range(0,9);
    $foundLettersDigit = [];
    preg_match_all("/(\p{Lu}{1,2}(\+|x|\-\:)\p{Lu}{1,2}(\+|x|\-\:)\p{Lu}{1,2}|\p{Lu}{1,2}(\+|x|\-\:)\p{Lu}{1,2})\=\p{Lu}{3}/m", $taskDescription, $match);
    if(count($match[0])<3)
    {
       return (object)
       [
           "report" => $report,
           "error" => true,
           "errorMsg" => "Three formulas were not found in the task description"
       ];
    }

    $formulas = $match[0];
    foreach($match[0] as $nFormula => $formula)
        $allFormulasLetters[$nFormula] = getFormulaLetters($formula);
    $formulas[] = $formulas[0];

    foreach($formulas as $nFormula => $formula)
    {
        $report .=  "$hr\nFormula: $formula\n";

        $formulaLetters = $allFormulasLetters[$nFormula>2?0:$nFormula];

        $arrK = array_diff($allDig, array_values($foundLettersDigit));

        foreach($formulaLetters[2] as $i => $letter)
        {
            if(array_key_exists($letter, $foundLettersDigit))
                $arr[$i] = [$foundLettersDigit[$letter]];
            else
                $arr[$i] = $arrK;
        }

        foreach($arr[0] as $L1)
        {
            foreach($arr[1] as $L2)
            {
                $fin = calcFormula([$L1, $L2], $formulaLetters, $tmpLettersAlpha, $foundLettersDigit);
                if($fin) break;
            }
        }

        if(!$fin) getFoundLettersDigit($tmpLettersAlpha, $foundLettersDigit);
        $out = outputDecodedLetters($foundLettersDigit, $report);
    }

    return $out;
}
?>


lettersDecode, Demo, auf GitHab

Problemstellung
One task from 2023 GCHQ Christmas Challenge
(GCHQ - UK's intelligence, security and cyber agency)

Each letter represents a different digit:
MI * MI = MAA
TI + TI = RA
DO - SO + TI - MI = RE
RE * RE = ?
Datei index.php
<!--

Demo PHP scripts decodingDigitsIntoLetters
Version: 1.0, 2023-12-17
Author: Vladimir Kheifets (vladimir.kheifets.@online.de)
Copyright (c) 2023 Vladimir Kheifets All Rights Reserved

One task from 2023 GCHQ Christmas Challenge
(GCHQ - UK's intelligence, security and cyber agency)

Each letter represents a different digit:
MI * MI = MAA
TI + TI = RA
DO - SO + TI - MI = RE
RE * RE = ?

-->


<html>
<head>
<title>Demo PHP scripts decodingDigitsIntoLetters</title>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1,
user-scalable=no, user-scalable=0" >
<style>
body{
 font-family: arial;
 font-size: 12pt;
 padding: 20pt 0pt 20pt 20pt;
}
div{
  width:calk(100%-20px);
  overflow-wrap: break-word;
  padding: 5pt 0pt 5pt;
}
</style>
</head>
<body>
<?
include_once("lettersDecodeClass.php");

$obj = new lettersDecode;
$lettersCode = $obj->lettersCode;
$endResNum =  $obj -> endResNum;
$endResLetter = $obj -> endResLetter;


$taskDescription = file_get_contents("taskDescription.php");
$taskDescription = str_replace(PHP_EOL, "<br>", $taskDescription);
echo <<<HTML

<div>$taskDescription</div>

<div><b>Result from PHP class lettersDecode</b></div>

HTML;
echo "<pre>\$lettersCode ";
print_r($lettersCode);
echo "</pre><br>RE * RE = $endResNum =>  $endResLetter";
/*
Result:
$lettersCode Array
(
    [M] => 1
    [I] => 2
    [A] => 4
    [T] => 3
    [R] => 6
    [D] => 9
    [S] => 5
    [E] => 0
    [O] => 7
)

endResNum RE * RE = 3600 =>  endResLetter = TREE
*/
?>
</body>
</html>
Datei lettersDecodeClass.php
<?
//-------------------------------------------------
/*
	PHP class lettersDecode
	Version: 1.0, 2023-12-17
	Author: Vladimir Kheifets (vladimir.kheifets.@online.de)
	Copyright (c) 2023 Vladimir Kheifets All Rights Reserved
	Demo:
	https://www.alto-booking.com/developer/lettersDecode
*/

class lettersDecode {

	public $lettersCode;
	public $endResNum;
	public $endResLetter;
	private $digits;

	function __construct() {
		$this->digits = range(0, 9);
		$this->lettersCode = $this -> lettersDecodeStep1();
		$this -> lettersDecodeStep2();
		$this -> lettersDecodeStep3();
		extract($this -> lettersCode);
		$RE = intval($R.$E);
		$this -> endResNum = $RE * $RE;
		$this -> endResLetter = "";
		$flippedLettersCode = array_flip($this -> lettersCode);
		foreach(str_split($this -> endResNum) as $digit)
			$this -> endResLetter .= $flippedLettersCode[$digit];
	}

	private function lettersDecodeStep1(){
		//Step1: MI * MI = MAA
		foreach($this->digits as $digit)
		{
			foreach($this -> getNextDigits($digit)  as $digit2)
			{
				$MI = intval($digit.$digit2);
				$MAA = $MI * $MI;
				if($MAA > 100)
				{
					$digitsMAA = str_split($MAA);
					if($digitsMAA[0] == $digit AND $digitsMAA[1] == $digitsMAA[2])
					{
						return
						[
							"M" => $digitsMAA[0],
							"I" => $digit2,
							"A" => $digitsMAA[1]
						];
					}
				}
			}
		}
	}
	//-------------------------------------------------
	private function lettersDecodeStep2(){
			//Step2: TI * TI = RA
			$lettersCode = $this -> lettersCode;
			extract($lettersCode);
			foreach($this->digits as $digit)
			{
				$TI = intval($digit.$I);
				$RA = $TI + $TI;
				if($RA >10 AND $RA<100 AND $digit>$I)
				{
					$digitsRA = str_split($RA);
					$this -> lettersCode["T"] = $digit;
					$this -> lettersCode["R"] = $digitsRA[0];
					return;
				}
			}
		}
	//-------------------------------------------------
	private function lettersDecodeStep3(){
			//Step3: DO - SO + TI - MI = RE
			$lettersCode = $this -> lettersCode;
			extract($lettersCode);
			$Ti = intval($T.$I);
			$Mi = intval($M.$I);
			$diffTiMi = $Ti - $Mi;
			$diffTiMiDigits = str_split($diffTiMi);
			$codes = array_values($lettersCode);

			$digits = array_diff($this -> digits, $codes);
			foreach($digits as $digit)
			{
				foreach($this -> getNextDigits($digit) as $digit2)
				{
					if($digit > $digit2)
					{
						if($digit - $digit2 + $diffTiMiDigits[0] == $R)
						{
							$this -> lettersCode["D"] = $D = $digit;
							$this -> lettersCode["S"] = $S = $digit2;
							break;
						}
					}
				}
			}
			$lettersCode = $this -> lettersCode;
			$codes = array_values($lettersCode);
			$digits = array_diff($this -> digits, $codes);
			foreach($digits as $digit)
			{
				$RE = intval($D.$digit)-intval($S.$digit) + $diffTiMi;
				$DigitsRE = str_split($RE);
				if($digit != $DigitsRE[1])
				{
					$this -> lettersCode["E"] = $DigitsRE[1];
					$this -> lettersCode["O"] = $digit;
					return;
				}
			}
		}

		private function getNextDigits($usetVal){
			return array_diff( $this -> digits, (array)$usetVal);
		}
}
?>
Datei decodingDigitsIntoLettersFunctions.php
<?PHP
/*
PHP-functions decodingDigitsIntoLetters

Version: 1.0, 2025-12-22
Author: Vladimir Kheifets (vladimir.kheifets@online.de)
Copyright (c) 2025 Vladimir Kheifets All Rights Reserved

The script provides a solution to one task of the GCHQ 2025 Christmas Challenge.
(GCHQ - UK's intelligence, security and cyber agency)

The letters in TWO UV PAIRS have the values
0,1,2,…,9 in some order, with each letter representing
a different digit.
UV+UV+V=VAR
RxPxP=AIR
SO+SO=VOW
What is 1234567 ?

*/

#########################################################################
function getLettersDigit($formulaLetters, $resDigit, $lettersVal, &$tmpLettersAlpha){
    foreach($formulaLetters[1] as $j => $letter)
    {
        $tmpLettersAlpha[$letter][]=$resDigit[$j];
    }

    foreach($formulaLetters[6] as $inLetter){
        $tmpLettersAlpha[$inLetter][]=$lettersVal[$inLetter];
    }
}
#########################################################################
function getFoundLettersDigit(&$tmpLettersAlpha, &$foundLettersDigit){

    foreach($tmpLettersAlpha as $letter => $letterDigit){
        $tmp = array_unique( $letterDigit);
        if(count($tmp) == 1)
            $foundLettersDigit[$letter]=$tmp[0];
    }

    $tmpLettersAlpha=[];
}
#########################################################################
function outputDecodedLetters($foundLettersDigit, &$report){
    $buf = array_flip($foundLettersDigit);
    ksort($buf);
    $report .=  "\nDecoded:\n";
    foreach($buf as $dig => $letter)
        $report .=  "$letter => $dig\n";
    if(count($buf)>8)
    {
       $result = substr(implode("", array_values($buf)),1,7);
       $report .= "\n\n1234567 is $result";
       return (object)
       [
           "error" => false,
           "errorMsg" => "",
           "result" => $result,
           "report" => $report,
           "decoded" => array_flip($buf)
       ];
    }
}
#########################################################################
function getFormulaLetters($formula){
    $buf = explode("=", $formula);
    $out = [];
    $out[] = $buf[1];
    $out[] = str_split($buf[1]);
    preg_match_all("/\p{Lu}/", $buf[0], $matches);
    $letters = array_unique($matches[0]);
    $out[] = $letters;
    preg_match_all("/(\p{Lu}+|[\+\-x])/", $buf[0], $matches);
    $out[] = $matches[0];
    foreach($out[1] as $i => $resLetter){
        $key = array_search($resLetter, $out[2]);
        if($key !== false)
        {
            $out[5][$i] = $key;
            $reverseKey = $key == 1?0:1;
            $out[6][] = $out[2][$reverseKey];
        }
    }

    return $out;
}
#########################################################################
function calcFormula($inValues, $formulaLetters, &$tmpLettersAlpha, &$foundLettersDigit){

    $foundDigitsLetter = array_flip($foundLettersDigit);

    foreach($formulaLetters[2] as $i => $Letter)
        $lettersVal[$Letter] = $inValues[$i];

    $рredefResStr = "";
    foreach($formulaLetters[1] as $i => $Letter)
        if(array_key_exists($Letter, $foundLettersDigit))
            $рredefResStr .= $foundLettersDigit[$Letter];
    $рredefRes = strlen($рredefResStr) == 3?intval($рredefResStr):0;

    $buf = "\$res=";
    foreach($formulaLetters[3] as $Letters)
    {
        if(preg_match("/\p{Lu}/", $Letters))
        {
            $tmp = "";
            foreach(str_split($Letters) as $Letter)
                $tmp .= $lettersVal[$Letter];
            $buf .= intval($tmp);

        }
        else
            $buf .= $Letters=="x"?"*":$Letters;
    }

    eval($buf.";");

    if($res > 0 AND $res == $рredefRes)
    {
        foreach($lettersVal as $Letter => $digit)
            if(!array_key_exists($Letter, $foundLettersDigit))
                 $foundLettersDigit[$Letter] = $digit;
        return true;
    }
    else
    {
        $resDigits = array_unique(str_split((string) $res));
        if(count($resDigits) > 2)
        {
            $resOK = true;
            foreach($resDigits as $i => $resDigit)
            {
                $letter = $formulaLetters[1][$i];
                if(array_key_exists($resDigit, $foundDigitsLetter))
                {
                        if( $foundDigitsLetter[$resDigit] != $letter)
                        {
                            $resOK = false;
                            break;
                        }
                }
            }

            #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

            if($resOK)
            {
                foreach($formulaLetters[5] as $i => $inValKey){
                    if($inValKey !== false)
                    {
                        if($resDigits[$i] == $inValues[$inValKey])
                        {
                            getLettersDigit($formulaLetters, $resDigits, $lettersVal, $tmpLettersAlpha);
                        }
                    }
                }
            }
        }
    }
}
#########################################################################
function decoding($taskDescription){
    $hr = "\n--------------------------------\n";
    $report = $taskDescription;
    $allDig = range(0,9);
    $foundLettersDigit = [];
    preg_match_all("/(\p{Lu}{1,2}(\+|x|\-\:)\p{Lu}{1,2}(\+|x|\-\:)\p{Lu}{1,2}|\p{Lu}{1,2}(\+|x|\-\:)\p{Lu}{1,2})\=\p{Lu}{3}/m", $taskDescription, $match);
    if(count($match[0])<3)
    {
       return (object)
       [
           "report" => $report,
           "error" => true,
           "errorMsg" => "Three formulas were not found in the task description"
       ];
    }

    $formulas = $match[0];
    foreach($match[0] as $nFormula => $formula)
        $allFormulasLetters[$nFormula] = getFormulaLetters($formula);
    $formulas[] = $formulas[0];

    foreach($formulas as $nFormula => $formula)
    {
        $report .=  "$hr\nFormula: $formula\n";

        $formulaLetters = $allFormulasLetters[$nFormula>2?0:$nFormula];

        $arrK = array_diff($allDig, array_values($foundLettersDigit));

        foreach($formulaLetters[2] as $i => $letter)
        {
            if(array_key_exists($letter, $foundLettersDigit))
                $arr[$i] = [$foundLettersDigit[$letter]];
            else
                $arr[$i] = $arrK;
        }

        foreach($arr[0] as $L1)
        {
            foreach($arr[1] as $L2)
            {
                $fin = calcFormula([$L1, $L2], $formulaLetters, $tmpLettersAlpha, $foundLettersDigit);
                if($fin) break;
            }
        }

        if(!$fin) getFoundLettersDigit($tmpLettersAlpha, $foundLettersDigit);
        $out = outputDecodedLetters($foundLettersDigit, $report);
    }

    return $out;
}
?>

getPairsLetters, Demo, auf GitHab

Problemstellung
The script provides a solution to one task of the GCHQ 2023
Christmas competition. (GCHQ - UK's intelligence, security
and cyber agency)

Task:
Find the pairs of letters which come next in sequence:
WU, SQ, OM, ??
Datei index.php
<!--
PHP script getPairsLetters
Version: 1.0, 2023-12-22
Author: Vladimir Kheifets (vladimir.kheifets@online.de)
Copyright (c) 2023 Vladimir Kheifets All Rights Reserved

The script provides a solution to one task of the GCHQ 2023 Christmas competition.
(GCHQ - UK's intelligence, security and cyber agency)

Find the pairs of letters which come next in sequence:
WU, SQ, OM, ??
-->

<html>
<head>
<title>PHP script getPairsLetters</title>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1,
user-scalable=no, user-scalable=0" >
<style>
body{
 font-family: Arial;
 font-size: 12pt;
 padding: 20pt 0pt 20pt 20pt;
}
div{
  width:calk(100%-20px);
  overflow-wrap: break-word;
  padding: 5pt 0pt 5pt;
}
</style>
</head>
<body>
<div>
<?
$task = file_get_contents("taskDescription.php");
$taskPr = str_replace(PHP_EOL,"<br>", $task);
preg_match_all("/[A-Z]{2}(?=\,)/", $task, $matches);
$pairLetters = $matches[0];
$str = implode(", ", $pairLetters);
$lettersAlpha = range("A","Z");
$lettersAlphaCode = array_flip($lettersAlpha);

$lettersToCode = [];
$codeKey = [];
foreach($pairLetters as $i => $pairLetter){
    foreach(str_split($pairLetter) as $j => $letter)
    {
        $lettersToCode[$i][$j] = $lettersAlphaCode[$letter];
        if($i>0)
        {
            $codeKey[] = $lettersToCode[$i][$j] - $lettersToCode[$i-1][$j];
        }
    }
}
$codeKey = array_unique($codeKey);
if(count($codeKey) == 1)
{
    echo <<<HTML
    $taskPr

    <div><b>The encryption method has been identified as Caesar Cipher</b></div>
    Encryption key found: {$codeKey[0]}

    Next pair of letters after:<br>
    HTML;
    foreach($pairLetters as $pairLetter)
    {
        $tmp = str_split($pairLetter);
        $nextPairLetter = "";
        $pairLetterCode="";
        foreach(str_split($pairLetter) as $letter)
        {
            $nextCode = $lettersAlphaCode[$letter] + $codeKey[0];
            $nextPairLetter .= $lettersAlpha[$nextCode];
            $pairLetterCode .= $lettersAlphaCode[$letter];
        }
        echo "$pairLetter is $nextPairLetter<br>";
    }
    echo "<hr><b>Next pair of letters after $str is  $nextPairLetter</b>";
}
else
    echo "Key of the Caesar's Cipher not found. Encoding method is not defined";
/*
Find the pairs of letters which come
next in sequence: WU,SQ,OM,??

Сrack of the Caesar's Cipher

Key of the cipher: -4

Next pair of letters after:
WU is SQ
SQ is OM
OM is KI

Next pair of letters after WU,SQ,OM, is KI
*/
?>
</div>
</body>
</html>

Impressum

Angemeldete Tatigkeits - Online-Reisevermittlung Webentwicklung und Wartung von Online-Systemen

Name des Geschäfts:
Alto Booking
Inhaber:
Vladimir Kheifets
Reg.Nr.:
10-2025-tho, Betriebsstätte 09174111, Altomünster, DE
St-Nr.:
107/235/92157

Kontaktdaten
Straße und Hausnummer:
Tulpenstr. 19
PLZ Stadt und Land:
85250 Altomünster, Deutschland
Telefon:
+49 8254 6409593
Mobil:
+49 174 3907391
E-Mail:
info@alto-booking.com

Bankverbindung in EUR
Bankname:
Postbank Leipzig
Bankadresse:
04096 Leipzig
Kontoinhaber:
Vladimir Kheifets
BLZ:
86010090
Kontonummer:
76299908
BIC:
PBNKDEFF
IBAN:
DE20 8601 0090 0076 2999 08

Kontakt

Alto Booking. Webanwendungsentwickler
Tulpenstr. 19, 85250 Altomünster, Deutschland, +49 174 3907391