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.
__.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();
}
});
//##############################################################################
});
/*
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();
});
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
/*
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>
"""
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
"""
/*
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;
}
/*
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
*/
}
}
/*
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
*/
<?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>
--
-- 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;
<?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;
}
//----------------------------------------------------------
}
<?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;
}
#####################################################################
/*
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);
//-----------------------------------------------------------------
});
/*
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;
}
<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>
//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
}
);
});
{
"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"
}
}
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
}
/* 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;
}
--
-- 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;
<?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;
/*
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);
//--------------------------------------------------
});
<?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;
/*
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);
//------------------------------------------------
});
<?
/*
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;
}
?>
/*
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();
});
<?
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;
<?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;
}
}
?>
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;
}
<?
$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"];
?>
<?
/*
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>
<?
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";
}
?>
<?
// 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);
?>
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}};
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) => {
});
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);
}
}
//------------------------------------------------------------------
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);
: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;
}
<!--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>
/*
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};
/*
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);
};
<?
/*
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;
?>
<?
/*
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;
}
//----------------------------------------------------
?>
<?
/*
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;
}
//--------------------------------------------------
}
?>
/*
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);
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?
<!--
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>
<?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();
}
}
}
?>
<?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;
}
?>
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;
}
?>
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>