Разработка

Разработка IT-проектов 程序

Загрузка

 

<!doctype html>

<html lang="ru">

<head>

<title>Титульное название страницы</title>

</head>

<body>

<h1>Заголовок первого уровня</h1>

<!-- Веб-страницы могут содержать скрытые комментарии -->

<div>Блок информации (дивизион), где может содержаться самостоятельный контент или сгруппированные секции с разным контентом, например:

<h2>Заголовки второго уровня</h2>

<img src="picture.jpg" alt="Изображения">

<ol>

<li type="a">Списки, нумерованные буквами</li>

<li type="I">римскими числами</li>

<li type="1">или арабскими числами</li>

</ol>

<ul>

<li type="disc">Списки с маркером-кругом</li>

<li type="circle">с маркером-окружностью</li>

<li type="square">или маркером-квадратом</li>

</ul>

<table border="1">

<caption>Таблицы</caption>

<tbody>

<tr>

<td>с одной</td>

<td>двумя</td>

</tr>

<tr>

<td colspan="2">или большим числом ячеек</td>

</tr>

</tbody>

</table>

<h3>Заголовки третьего уровня</h3>

<form>Формы

с <input placeHolder="полями ввода">

и <input type="button" value="кнопками">

</form>

<p>Абзацы (параграфы) с текстовым контентом, включая <b>жирный</b>, <i>курсивный</i> или <u>подчёркнутый</u> текст</p>

<a href="#">Ссылки</a> и прочие объекты…

</div>

</body>

</html>

Основы программирования

Вместо эпиграфа: «Программист перед сном ставит возле кровати два стакана: один с водой, другой — пустой. Первый на случай, если ночью захочет пить, второй — если не захочет!»

Разработка — создание программного продукта с помощью информационных технологий (IT). В современном мире востребованы разные направления разработки: разработка систем искусственного интеллекта (AI), разработка виртуальной реальности (VR), разработка дополненной реальности (AR), разработка игр (Game Dev), разработка мобильных приложений для Android и iOS (Mobile), разработка веб-приложений для сайтов (Web)… Но начинающим джунам (от англ. Junior), мечтающим связать свою жизнь с высокими технологиями и стать успешными разрабами, прежде чем осваивать полюбившийся многим Python, резонно постичь основы веб-программирования, заложив прочный фундамент технического бэкграунда. Поскольку веб-направление относительно легко освоить, и при этом не нужно искать и устанавливать дополнительное программное обеспечение (ПО). Достаточно наличия простенького компьютера или ноутбука с веб-браузером в операционной системе!

Для успешного взаимодействия с кодом разработчикам полезно прокачивать память, логику и образное мышление. Осуществлять это можно посредством регулярной практики кодинга (написания кода), решения логических задач, участия в развивающих играх, проведения мыслительных экспериментов, знакомства с кейсами опытных разработчиков, чтения тематической литературы, просмотра образовательных видеоматериалов, прохождения курсов повышения квалификации и других способов саморазвития… Как говорят мудрецы: «Проживайте дни так, словно завтра вам умирать, стараясь успеть сделать как можно больше! Учитесь так, словно вы собираетесь жить вечно, стараясь познать себя и окружающий мир как можно лучше!»

Незнакомые языки программирования эффективней всего изучать на конкретных примерах, стараясь сразу реализовывать на практике приобретённые теоритические знания и оттачивать своё мастерство (подобно филигранному преобразованию алмаза в огранённый бриллиант). В психологии существует теория о том, что информация наилучшим образом воспринимается и запоминается мозгом в моменты, когда испытываются сильные эмоции. И игровые примеры, как потенциальный источник ярких впечатлений, можно считать весьма эффективным обучающим инструментом.

Таким образом, чтобы знакомство с особенностями веб-разработки не было утомительно скучным, прокачивать мозг, осваивая новый скилл, резонно именно в игровой форме! К тому же, в разработанную собственными силами игру потом можно будет поиграть! В качестве такого наглядного игрового примера можно использовать Судоку — популярную японскую игру-головоломку, способствующую формированию внимательности, развитию логического мышления и тренировке памяти у детей и взрослых людей!

Программирование, по своей сути, является процессом записи идей с помощью специальных служебных слов выбранных языков программирования. Освоить синтаксис незнакомых языков возможно всего за несколько недель, причём некоторые отличительные особенности построения служебных конструкций могут запомниться после просмотра первого же примера с кодом! В частности, это касается особенностей использования цикла for и условного оператора if, с помощью которых можно реализовывать множество разных задумок. Но при этом важно чётко представлять в голове архитектуру будущего программного продукта и научиться придумывать эффективные алгоритмы (определённые последовательности действий, приводимые к желаемому результату), которые должны быть реализованы средствами языков программирования. Например, для реализации игровой логики Судоку как минимум понадобятся пользовательский интерфейс, обработчики событий для взаимодействия с пользователем и переменные для хранения массива данных.

Мысленные эксперименты

Для лучшего понимания концепции алгоритмизации (составления алгоритма), являющейся первым шагом в разработке программного продукта, резонно проанализировать решение популярной наглядной задачи из разряда мысленных экспериментов. Допустим, есть человек, которому нужно перебраться с одного берега реки на другой, например, чтобы принять участие в конкурсе федерального масштаба с большими призовыми. Для участия в конкурсе человек взял с собой волка, козу и капусту внушительных размеров! Возле берега реки находится маленькая лодка с большой пробоиной в борту, через которую начнёт просачиваться вода, если лодка будет перегружена. Поэтому, чтобы переправиться в этой лодке на другой берег, человек может взять с собой только 1 пассажира или 1 груз (либо волка, либо козу, либо капусту). Сложность заключается в том, что если человек поплывёт с волком, коза останется на берегу наедине с капустой и съест её! Если человек поплывёт с капустой, тогда на берегу останутся волк с козой, и волк съест козу! Таким образом, нужно продумать последовательность действий (алгоритм), способных помочь человеку вместе со всей троицей преодолеть препятствие и попасть на конкурс.

  1. Вначале человек должен перевезти на другой берег козу (тогда на первом берегу останутся капуста и волк, равнодушный к вегетарианству).
  2. После этого человек должен вернуться один к исходному (первому) берегу.
  3. Затем человек должен перевезти на другой берег волка.
  4. Человек должен вынужденно вернуться с козой на первый берег (чтобы волк не съел козу).
  5. Далее человек должен перевезти на другой берег капусту.
  6. Человек должен вернуться на первый берег за козой (оставив на другом берегу волка с капустой).
  7. Человек должен перевезти на другой берег козу.
  8. Успешно преодолев препятствие благодаря пониманию основ алгоритмизации, человек должен продолжить путь на конкурс!

В данном случае очерёдность 3 и 5 пунктов не имеет значения (на третьем шаге человек может перевезти вместо волка капусту, тем не менее, после этого человеку всё равно придётся вернуться с козой, чтобы она не съела капусту, а затем человеку придётся перевезти волка). Но остальные пункты должны выполняться строго в указанном порядке. Подобные задачи можно использовать в качестве тренинга для развития мышления.

Также помимо умения придумывать и реализовывать эффективные алгоритмы программисту важно знать технический английский язык. Поскольку служебные слова, используемые в языках программирования, как правило, имеют английское написание. Убедиться в этом можно на примере синтаксиса гармонично дополняющего друг друга трио: HTML, JavaScript и CSS!

HTML (HyperText Markup Language)

HTML — язык разметки гипертекста, с помощью которого можно создавать специальные инструкции для обработки контента веб-документов. В рамках разработки собственного веб-приложения в первую очередь резонно создать на компьютере отдельную тематическую папку с символическим именем GameDev. Внутри папки следует создать пустой текстовый документ с именем sudoku и расширением .html (для установки расширения файла может потребоваться изменить настройки операционной системы, указав, чтобы в папках отображались расширения для зарегистрированных типов файлов). Затем необходимо открыть файл с помощью встроенного в операционную систему текстового редактора. И скопипастить (от англ. Copy+Paste) код из листинга-1.

После этого следует сохранить файл (например, в кодировке UTF-8 с помощью команды «Сохранить как…») и открыть обновлённый текстовый документ через веб-браузер, чтобы посмотреть, как HTML-код будет отображаться в программе для просмотра интернет-контента. Для удобства, чтобы не приходилось постоянно переключаться между окнами, окно браузера можно свернуть на половину экрана и расположить его в левой части, а окно текстового редактора с кодом — в правой половине. С таким положением окон можно будет смотреть на визуальный пользовательский интерфейс и параллельно редактировать код.

В данном случае в окне браузера можно заметить, что название вкладки содержит текст из тайтла «Титульное название страницы». А в самом окне находятся большой «Заголовок первого уровня» с полужирным начертанием шрифта, после которого сразу следует блок информации. При этом скрытый текст комментария не отображается (также должно отсутствовать изображение picture.jpg, поскольку этого файла нет в текущей папке GameDev).

Этот простейший пример наглядно иллюстрирует типичную структуру HTML-документа. В начале документа принято указывать его тип. Указание подобных инструкций осуществляется с помощью специальных служебных слов, заключённых в угловые скобки (символы «меньше» и «больше»). Такую запись принято называть дескриптором или тегом. Чаще всего HTML-теги представлены в виде парного контейнера: открывающего и закрывающего тегов. Но некоторые теги могут быть одиночными, как специальный тег с объявлением типа документа <!doctype html>, отличием которого также является наличие восклицательного знака после первой скобки.

Следом за доктайпом указываются признак начала документа в виде открывающего тега <html>, внутри которого можно определить используемый в документе язык lang="ru", и признак конца документа в виде закрывающего тега </html> со слэшем (косой чертой). Между ними находятся теги <head>…</head>, предназначенные для служебных заголовков, и тело документа <body>…</body> для указания видимого пользователю контента. В разделе заголовков традиционно указывают название документа между тегами <title>…</title> и специальные мета-теги со служебными спецификациями. В теле документа располагается HTML-код c указанием обрабатываемого контента, представляющего предполагаемую ценность для пользователя.

В случаях, когда в документах оказывается много данных для обработки, допустимо добавлять скрытые комментарии с вспомогательными подсказками, облегчающими код-ревью (просмотр) или редактирование кода спустя несколько месяцев с момента разработки. HTML-комментарии пишутся подобно тегам в угловых скобках с добавлением двойных дефисов (минусов) по краям и восклицательного знака после первой скобки <!--…-->. Но для экономии времени и места длинные комментарии можно заменять говорящими идентификаторами (id) внутри HTML-тегов. Например, в рамках разработки игрового веб-приложения в HTML-коде можно использовать блок <div>…</div> с указанным атрибутом id="game". Такой id (подобно комментарию) способен указать на игровую тематику блока. Кроме того, id позволяет получить доступ к HTML-элементу и считать или изменить его содержимое с помощью JavaScript-сценария!

Листинг-2. HTML-заготовка для игры

<!doctype html>

<html lang="ru">

<head>

<title>IT-проект</title>

<meta charset="UTF-8">

<meta name="viewport" content="width=device-width, initial-scale=1">

<meta name="description" content="Веб-приложение для игры и обучения детей и взрослых людей…">

<script type="text/javascript" src="script.js"></script>

</head>

<!-- Формирование интерфейса -->

<body onLoad="GUI()">

<h1>Судоку</h1>

<div id="game">Загрузка…</div>

</body>

</html>

В листинге-2 представлена сырая (предварительная) заготовка для будущей игры, которую следует скопипастить в HTML-документ вместо примера из листинга-1. В начале HTML-кода в разделе заголовков помимо тайтла добавлены три мета-тега с указанием кодировки документа, параметров отображения документа на мобильных устройствах и краткого описания содержимого документа. А также добавлен признак подключения внешнего JavaScript-сценария. Далее внутри тега <body> с помощью обработчика события onLoad браузеру сообщается о потребности в автозапуске JavaScript-функции с именем GUI() сразу при открытии веб-документа. В самом теле документа указываются заголовок первого уровня с текстом «Судоку» и игровой блок с текстом-заглушкой «Загрузка…», который будет отображаться до момента загрузки внешнего сценария и запуска JS-функции GUI() для внедрения в блок пользовательского интерфейса веб-приложения.

JS (JavaScript)

JavaScript — объектно-ориентированный язык программирования (с адаптированным синтаксисом языка C++), который можно использовать в frontend-разработке интерактивных веб-приложений, выполняющихся в клиентском браузере пользователя. JS-код небольшого объёма допустимо располагать в произвольном месте веб-документа между тегами <script>…</script>. Но для удобочитаемости код сценариев традиционно выносят в отдельные текстовые файлы, которые подключают к нужным веб-документам (лучше в начале документа в разделе заголовков после мета-тегов), как показано в примере из листинга-2. Для этого в папке GameDev необходимо создать новый текстовый документ с именем script и расширением .js, который также следует открыть с помощью текстового редактора (и расположить открывшееся окно в правой части экрана). Затем можно приступить к программированию, начав с создания функции для формирования пользовательского интерфейса.

Судоку внешне представляет собой квадратную таблицу с числами, поэтому визуальное игровое поле в документе можно создать с помощью одноимённого HTML-тега <table>. Учитывая особенности Судоку, таблица должна содержать по 9 строк и 9 столбцов. Таким образом, необходимо прорисовать 81 табличную ячейку. При этом следует предусмотреть способ ввода данных с последующей обработкой информации. В данном случае такое взаимодействие с пользователем проще всего реализовать с помощью текстовых полей, в которые можно будет вводить числа.

Листинг-3. Графический пользовательский интерфейс

function GUI(){

var ui='<table align="center"><tbody>';

for(i=1;i<10;i++){

ui+='<tr>';

for(j=1;j<10;j++){

ui+='<td><input id="i'+i+j+'"></td>'

}

ui+='</tr>'

}

ui+='</tbody></table>';

document.getElementById('game').innerHTML=ui

}

/* Фрагмент внедрённого HTML-кода

<table align="center">

<tbody>

<tr>

<td><input id="i11"></td>

<td><input id="i12"></td>

<td><input id="i13"></td>

<td><input id="i14"></td>

<td><input id="i15"></td>

<td><input id="i16"></td>

<td><input id="i17"></td>

<td><input id="i18"></td>

<td><input id="i19"></td>

</tr>

<tr>

<td><input id="i91"></td>

<td><input id="i92"></td>

<td><input id="i93"></td>

<td><input id="i94"></td>

<td><input id="i95"></td>

<td><input id="i96"></td>

<td><input id="i97"></td>

<td><input id="i98"></td>

<td><input id="i99"></td>

</tr>

</tbody>

</table>

*/

В листинге-3 приведён пример внедрения HTML-кода в блок <div> с id="game" из JS-функции с символическим именем GUI (от англ. Graphical User Interface), объявленной в начале кода с помощью предшествующего служебного ключевого слова function. Как и в случае с id, имена JS-функций и всех используемых переменных резонно подбирать таким образом, чтобы они чётко соответствовали своему назначению и были понятны даже сторонним кодерам при просмотре кода.

В теле функции между начальной и конечной фигурными скобками {…} с помощью служебного слова var (от англ. Variable) создаётся локальная переменная ui, которой изначально присваивается текстовая строка с таблицей, выравненной по центру экрана благодаря атрибуту align="center", указанному внутри открывающего HTML-тега <table>. Затем внутри двух циклов for, выполняющих по 9 итераций (повторных выполнений внутреннего кода с увеличивающимися счётчиками i и j от 1 до 9), к значению переменной ui добавляются 9 строк <tr> с 9 ячейками <td>, в каждой из которых располагается текстовое поле для ввода чисел <input> с указанием id. В качестве id для каждого текстового поля указывается связка из номера строки [i] и номера столбца [j], в которых оно расположено (например, текстовое поле, расположенное во втором столбце третьей строки, будет иметь id="i32"). После завершения циклов for к переменной ui добавляется закрывающий табличный тег </table>. Далее вызывается метод getElementById() с указанием значения id, позволяющий получить доступ к элементу документа и добавить внутрь него содержимое переменной ui с помощью метода innerHTML.

После функции GUI() представлен JS-комментарий с примером того, как браузер будет воспринимать содержимое обновлённого контейнера <div id="game">…</div> в веб-документе после выполнения операторов функции и внедрения фрагмента HTML-кода. Данный пример показывает, что написание всего нескольких строк JS-кода может сэкономить байтовое пространство (объём), позволяя быстрее загружать документ с лёгким весом и, следовательно, быстрее начинать выполнение кода, а также время разработчика, избавляя от рутинного ввода большого объёма информации. Иными словами, JS-сценарии можно использовать не только для чистого программирования в рамках разработки веб-приложений, но и для редактирования исходного HTML-кода.

В приведённом примере текст закомментирован с помощью одиночных косых черт и звёздочек /*…*/. Такую конструкцию принято использовать для многострочных комментариев, но для небольшого однострочного текста допустимо использовать парные слэши // перед комментарием. В отличие от HTML-комментариев, без которых можно обойтись, в тело функций уместно добавлять JS-комментарии, поясняющие отдельные фрагменты выполняемого кода (поскольку в некоторых случаях легко понять, ЧТО делает конкретная строчка кода, но сложно понять, ЗАЧЕМ она это делает). Также в ряде случаев может возникнуть необходимость закомментировать неиспользуемые фрагменты кода (например, при отладке сценария). Но в данном конкретном случае комментарий приводится исключительно в качестве наглядного примера, демонстрирующего, как несколько строчек JS-кода могут заменить большой HTML-фрагмент!

После формирования игровой таблицы резонно предусмотреть ограничения на ввод чисел в текстовые поля. По правилам Судоку в ячейках должны находиться только натуральные числа от 1 до 9. Поэтому в функции GUI() для полей ввода логично указать максимальную длину в 1 символ с помощью атрибута maxLength=1 в теге <input>. Дополнительно можно добавить проверку на соответствие вводимых символов правилам игры, чтобы удалять все некорректные символы. Для реализации такой проверки достаточно прописать всего одну строчку JS-кода!

Листинг-4. Проверка вводимых символов

function InpChk(el){

/* Если значение не число или 0, тогда очистить поле */

if(isNaN(el.value)||el.value==0){

el.value=''

}

}

В листинге-4 приведён пример функции с аргументом — специальным параметром для передачи дополнительных значений. В данном примере для хранения такого параметра используется переменная el, которой будет передаваться ссылка на проверяемый HTML-элемент при вызове этой функции в обработчике события. Несмотря на краткость, название переменной el (от англ. Element) и название самой функции InpChk (от англ. Input+Check) чётко отражают назначение кода!

В отличие от функции GUI(), вызываемой сразу при открытии веб-страницы, для функции InpChk() необходим триггер — наступление особого события, инициируемого пользователем. Учитывая назначение функции, подходящим событием является ввод символа с клавиатуры в любое текстовое поле. Поэтому для вызова функции InpChk() внутрь каждого тега <input> из листинга-3 следует добавить обработчик события onKeyUp="InpChk(this)", через который с помощью служебного слова this функции в качестве параметра будет передаваться ссылка на активный элемент.

В теле функции для считывания содержимого элемента используется свойство value, связанное с аргументом el через точку. Проверка значения активного текстового поля осуществляется в условном операторе if. В данном случае с помощью служебной JS-функции isNaN (от англ. is Not a Number), логического «или» || и оператора сравнения == (читается «равно») выполняется проверка одновременно двух условий: если значение не является числительным или равняется 0, текстовому полю присваивается новое значение в виде пустой строки.

Таким образом, благодаря простой проверке в одну строчку кода можно удостовериться, что после пользовательского ввода в ячейках будут находиться корректные данные, обработка которых в дальнейшем не должна привести к программному сбою! И можно смело заводить переменные для хранения данных!

Учитывая размер игрового поля, для хранения и обработки данных из ячеек 9x9-таблицы потребуется 81 переменная. Такое количество переменных удобно представить в формате массива вида НазваниеМассива[индекс] или в формате двумерного массива вида НазваниеМассива[индекс1][индекс2]. Для этого в коде сценария необходимо объявить глобальную переменную, например, с именем A (от англ. Array), указав, что она будет использоваться для хранения массива данных.

Листинг-5. Создание двумерного массива

/* i — номер строки,

j — номер столбца */

var A=new Array();

for(i=1;i<10;i++){

A[i]=new Array();

//for(j=1;j<10;j++){A[i][j]=0}

}

A[1][1]=1;A[1][2]=2;A[1][3]=4;A[1][4]=7;A[1][5]=6;A[1][6]=5;A[1][7]=8;A[1][8]=9;A[1][9]=3;

A[2][1]=6;A[2][2]=7;A[2][3]=9;A[2][4]=8;A[2][5]=4;A[2][6]=3;A[2][7]=2;A[2][8]=5;A[2][9]=1;

A[3][1]=3;A[3][2]=8;A[3][3]=5;A[3][4]=2;A[3][5]=1;A[3][6]=9;A[3][7]=7;A[3][8]=4;A[3][9]=6;

A[4][1]=4;A[4][2]=5;A[4][3]=2;A[4][4]=9;A[4][5]=8;A[4][6]=1;A[4][7]=6;A[4][8]=3;A[4][9]=7;

A[5][1]=9;A[5][2]=3;A[5][3]=8;A[5][4]=6;A[5][5]=2;A[5][6]=7;A[5][7]=4;A[5][8]=1;A[5][9]=5;

A[6][1]=7;A[6][2]=6;A[6][3]=1;A[6][4]=5;A[6][5]=3;A[6][6]=4;A[6][7]=9;A[6][8]=2;A[6][9]=8;

A[7][1]=8;A[7][2]=9;A[7][3]=3;A[7][4]=4;A[7][5]=5;A[7][6]=6;A[7][7]=1;A[7][8]=7;A[7][9]=2;

A[8][1]=5;A[8][2]=4;A[8][3]=6;A[8][4]=1;A[8][5]=7;A[8][6]=2;A[8][7]=3;A[8][8]=8;A[8][9]=9;

A[9][1]=2;A[9][2]=1;A[9][3]=7;A[9][4]=3;A[9][5]=9;A[9][6]=8;A[9][7]=5;A[9][8]=6;A[9][9]=4;

В листинге-5 представлен фрагмент JS-кода с созданием массива A, содержащего 81 переменную, каждой из которой присваивается числовое значение. В JS глобальные переменные можно объявлять в произвольном месте кода по мере необходимости (например, перед функциями, в которых они должны использоваться). Но для экономии места резонно перечислять все глобальные переменные через запятую в самом начале сценария с использованием служебного слова var всего один раз!

В начале кода приведён пример двухстрочного комментария внутри конструкции /*…*/. Затем с помощью служебных слов var и new Array() объявляется пустой массив с именем A. При помощи цикла for с шагом i++ (краткая запись i++ соответствует записи i=i+1 и увеличивает счётчик на 1) создаётся 9 элементов массива с индексами от 1 до 9, каждый из которых также становится массивом. Таким образом, массив A[i] преобразовывается в двумерный (двойной) массив вида A[i][j], в котором первый индекс [i] соответствует табличной строке, а второй индекс [j] — табличному столбцу. Строчка кода for(j=1;j<10;j++){A[i][j]=0} с присваиванием элементам массива изначальных нулевых значений во втором (внутреннем) цикле for является избыточной, поскольку после первого (внешнего) цикла for всем элементам массива присваиваются новые значения (поэтому она представлена в виде однострочного комментария, демонстрирующего, как можно обращаться к элементам массива по их индексам).

После формирования массива данных можно приступить к выводу его значений на экран. Для этого некоторым изначально пустым текстовым полям нужно присвоить числовые значения из переменных, хранящихся в массиве. При этом резонно внедрить в код генератор случайных чисел, помогающий подбирать разные текстовые поля при каждом запуске игры (чтобы игра не получилась скучной).

Листинг-6. Генератор случайных чисел

function rnd(hi){

var r=Math.floor(Math.random()*hi)+1;

return r

}

В листинге-6 приведён пример функции с возвращаемым значением. В теле функции создаётся локальная переменная r (от англ. Random) для хранения сгенерированного числа. С помощью метода random() объекта Math, применяемого в JS при математических операциях, генерируется случайное число с плавающей точкой в диапазоне от 0 до 1. Затем сгенерированное число умножается на аргумент функции hi (от англ. High), представляющий собой верхнюю границу генерируемого числа. При помощи метода floor() объекта Math от результата умножения отсекается дробная часть после запятой, и к полученному целому числу прибавляется 1, символизирующая нижнюю границу генерируемого числа. Итоговый результат присваивается переменной r и возвращается в качестве значения функции с помощью служебного слова return. Для использования полученного генератора и заполнения полей необходимо усовершенствовать код функции GUI().

Листинг-7. Пользовательский интерфейс 2.0

function GUI(){

var n,ui='<table align="center"><tbody>';

for(i=1;i<10;i++){ui+='<tr>';

for(j=1;j<10;j++){

// Генерация случайных чисел

if(j==1){n=rnd(3)}

if(j==4){n=rnd(3)+3}

if(j==7){n=rnd(3)+6}

ui+='<td><input id="i'+i+j+'" maxLength=1 ';

if(j==n){ui+='value="'+A[i][n]+'" disabled'}

else{ui+='onKeyUp="InpChk(this)"'}

ui+='></td>'

}

ui+='</tr>'

}

ui+='</tbody></table>';

document.getElementById('game').innerHTML=ui

}

В листинге-7 представлен обновлённый вариант функции GUI() с добавленной переменной n для хранения сгенерированных чисел. В начале второго цикла for были добавлены три условных оператора if для генерации чисел с помощью функции rnd() при первой, четвёртой и седьмой итерациях (чтобы выбрать номер открытой ячейки в каждой из трёх малых 3x3-квадратных секций). В конце второго цикла for добавлен ещё один if, проверяющий, совпадает ли номер столбца ячейки со случайным числом. Если условие выполняется, к значению переменной внутрь тега <input> с помощью атрибута value добавляется значение из массива данных A[i][j] и признак недоступности с помощью атрибута disabled. Если условие не выполняется, внутрь тега <input> добавляется обработчик события со ссылкой на функцию для проверки вводимых символов.

После внесённых изменений в код функции GUI() получился полноценный игровой уровень. При каждом запуске полученной игры изначально будут открываться ячейки с разными номерами. Но, учитывая, что значения берутся из одного массива A, числа в каждой из 81 ячейки всегда будут одинаковыми. Чтобы этого избежать, можно создать базу данных из 10 или 100 разных массивов. Тогда при запуске новой игры загружались бы разные массивы. Но и в этом случае количество уровней всё равно было бы ограничено количеством массивов. Поэтому вместо создания альтернативных массивов для новых уровней резонно перемешивать числа в единственном массиве A.

Судоку по сути представляет собой квадратную алгебраическую матрицу, в которой можно менять местами строки и столбы. Главное, чтобы в строках, в столбцах и в 3x3-секциях Судоку не было повторяющихся чисел. С точки зрения разработчика это означает, что сумма чисел от 1 до 9 в каждой строке, в каждом столбце и в каждой секции должна равняться 45 (1+2+3+4+5+6+7+8+9=45). А, как известно из школьного курса элементарной математики, от перестановки мест слагаемых сумма не меняется! Следовательно, в соседних горизонтальных и вертикальных малых 3x3-квадратах можно поменять местами строки или столбцы. Например, в первой строке первой секции первый, второй и третий столбцы можно поставить шестью разными способами: {1,2,3}, {1,3,2}, {2,1,3}, {2,3,1}, {3,1,2} или {3,2,1}. То же касается и расположения первых трёх строк в первом столбце, и расположения самих 3x3-секций. Иными словами, можно создать специальную функцию, которая бы перемешивала значения трёх сгруппированных элементов.

Листинг-8. Триады

function tri(){

var sw=rnd(6),t=new Array();

switch(sw){

case 1:t[1]=1;t[2]=3;t[3]=2;break;

case 2:t[1]=2;t[2]=1;t[3]=3;break;

case 3:t[1]=2;t[2]=3;t[3]=1;break;

case 5:t[1]=3;t[2]=2;t[3]=1;break;

default:t[1]=3;t[2]=1;t[3]=2

}

return t

}

В листинге-8 приведён пример функции tri(), которая возвращает массив данных из трёх чисел от 1 до 3, представленных в произвольном порядке. В теле функции объявляются локальная переменная sw (от англ. Switch), которой с помощью функции rnd() присваивается случайное число в диапазоне от 1 до 6, и пустой массив t (от англ. Triad). Затем вместо привычного условного оператора if используется оператор переключения switch, который сравнивает значение sw с числовыми вариантами, указанными после служебного слова case, и включает выполнение соответствующих действий (расположенных между двоеточием и служебным словом break). При этом, если значение переменной sw будет равно 4 или 6, выполнятся действия, указанные после служебного слова default. Таким образом, после выполнения оператора switch в массиве t окажутся три числа {1,2,3} в произвольном порядке. Для перемешивания сгруппированных троек можно создать отдельную функцию или дополнить код в начале функции GUI().

Листинг-9. Обмен данных массива

function mix(){

var col,m=new Array(),row;

for(i=0;i<7;i+=3){

// Генерация трёх столбцов

col=tri();

for(j=1;j<10;j++){

// Считывание трёх столбцов

m[1]=A[j][1+i];

m[2]=A[j][2+i];

m[3]=A[j][3+i];

// Перемешивание трёх столбцов

A[j][1+i]=m[col[1]];

A[j][2+i]=m[col[2]];

A[j][3+i]=m[col[3]]

}

// Генерация трёх строк

row=tri();

for(j=1;j<10;j++){

// Считывание трёх строк

for(l=1;l<4;l++){m[l]=A[l+i][j]}

// Перемешивание трёх строк

for(l=1;l<4;l++){A[l+i][j]=m[row[l]]}

} } }

В листинге-9 представлен пример использования функции tri() для перемешивания столбцов и строк игровой таблицы. В начале кода объявляются локальные переменные col (от англ. Column) для хранения массива номеров перемешиваемых столбцов, m (от англ. Mixer) для временного хранилища обновляемых данных и row (от англ. Row) для хранения массива номеров перемешиваемых строк. Затем внутри первого цикла for с шагом i+=3 (i=i+3) переменой col с помощью функции tri() присваивается массив из трёх чисел, причём по задумке при каждой итерации цикла порядок чисел должен меняться. Во втором цикле for с привычным шагом j++ (j=j+1) создаются три элемента массива m, которым присваиваются значения из глобального массива A. Далее данные выбранной тройки элементов глобального массива A обновляются на новые значения из временного хранилища m в порядке, определяемом тройкой перемешанных чисел из массива col (например, если при вызове функции tri() будет сформирован порядок {2,3,1}, вместо первого столбца должен оказаться второй, вместо второго — третий, а вместо третьего — первый). Аналогичным образом перемешиваются тройки строк, но в данном примере в третьем цикле for для считывания и обмена данных между массивами m и A создаются дополнительные вложенные циклы for с шагом l++ (l=l+1), позволяющие сделать запись более компактной и сэкономить объём документа. Для активации перемешивания чисел в начало функции GUI() следует добавить вызов созданной функции mix() либо скопипастить туда весь код.

После внесённых изменений при каждом запуске функции GUI() должен формироваться новый уровень со случайными числами-подсказками. Но при этом сами уровни будут весьма сложными (особенно для новичков), поскольку в коде функции GUI() для каждой игры изначально предусмотрено открытие лишь 27 ячеек из 81. Упростить игровой процесс для начинающих игроков можно с помощью внедрения дополнительной опции с подсказками, открывающими закрытые ячейки. Реализовать это можно, придерживаясь разной логики: открыть все угловые ячейки, полностью открыть одну из строк, полностью открыть столбец, открыть одну из 3x3 секций, открыть ячейку, выбранную пользователем. Но для поддержания эффекта неожиданности резонно вновь воспользоваться генератором случайных чисел!

Листинг-10. Игровые подсказки

var HN=0;

function hint(){

/* Если счётчик меньше 15, выполнить код */

if(HN<15){HN++;

var col=new Array(),n=0,row=new Array();

for(i=1;i<10;i++){

for(j=1;j<10;j++){

if(document.getElementById('i'+i+j).value==''){n++;row[n]=i;col[n]=j}

} }

if(n>0){

if(n>1){n=rnd(n)}

document.getElementById('i'+row[n]+col[n]).value=A[row[n]][col[n]];

document.getElementById('i'+row[n]+col[n]).disabled=1

}else{HN=15}

} }

В листинге-10 приведён пример реализации игровых подсказок, которыми можно воспользоваться до 15 раз. Перед использованием функции необходимо объявить глобальную переменную HN (от англ. Hint+Number) для отслеживания количества запрошенных подсказок, значение которой проверяется в начале кода. Если при вызове функции значение переменной будет меньше 15, счётчик увеличивается на 1. Далее внутри if объявляются параллельные массивы col и row для хранения номеров столбцов и строк ячеек и переменная n для хранения количества элементов этих массивов. Внутри двух циклов for с помощью if проверяются значения всех текстовых полей из игровой таблицы. Если проверяемая ячейка оказывается пустой, счётчик n увеличивается на 1, а в массивы row и col записываются номера строки и столбца пустой ячейки.

После выполнения циклов for в if проверяется значение счётчика n. В случае, если была зафиксирована хотя бы одна пустая ячейка, во вложенном if уточняется количество таких ячеек. Если количество пустых ячеек окажется больше 1, при помощи функции rnd() генерируется случайное число в диапазоне от 1 до n, и это число присваивается переменной n, перезаписывая её старое значение. Затем в пустое текстовое поле записывается значение из глобального массива A с индексами row[n] и col[n]. А само поле становится заблокированным для изменений с помощью активации свойства disabled, значение которого меняется на 1 (булеву истину).

Внедрение в код алгоритма с подсказками позволит дополнительно открыть 15 чисел-подсказок в начале каждой игры (и останется заполнить меньше половины ячеек), что должно помочь начинающим игрокам в поисках успешного решения Судоку! А, чтобы игрок мог убедиться в правильности решения Судоку, необходимо добавить финальную проверку всех введённых данных.

Листинг-11. Проверка по массиву

function chk(){

var err=0;

for(i=1;i<10;i++){

for(j=1;j<10;j++){

if(document.getElementById('i'+i+j).value!=A[i][j]){err++}

} }

/* if(err>0){err='Количество ошибок: '+err}else{err='Судоку решена!'} */

err=(err>0)?'Количество ошибок: '+err:'Судоку решена!';

alert(err)

}

В листинге-11 представлена реализация самой простой проверки решения Судоку — сравнение значений ячеек игровой таблицы со значениями данных из глобального массива A. В начале функции с именем chk (от англ. Check) объявляется локальная переменная err (от англ. Error) для мониторинга ошибок. Внутри двух циклов for с помощью if и оператора сравнения != (читается «не равно») проверяется значение каждого текстового поля. В случае, если значение проверяемой ячейки не совпадает со значением элемента массива A с теми же индексами i и j, счётчик err увеличивается на 1. Далее переменной err присваивается строковое значение с помощью сокращённой формы условного оператора if…else (для наглядности привычная форма записи представлена в комментарии). Если в ходе проверки была зафиксирована хотя бы одна ошибка, переменной err присваивается строка «Количество ошибок:» с добавлением старого значения счётчика err. В противном случае, если решение безошибочно, переменной err присваивается строка «Судоку решена!». В конце функции при помощи служебной функции alert() вызывается всплывающее диалоговое окно с текстовым сообщением из обновлённой переменной err, передаваемым в качестве параметра.

Такая проверка проста в реализации, но она может принять правильное решение за ошибочное, если порядок введённых чисел не будет в точности соответствовать значениям массива A. А это совсем не комильфо и даже полный моветон, поскольку, как известно, некоторые задачи могут иметь несколько решений! В ряде случаев в строках/столбцах из соседних секций Судоку встречаются симметричные ячейки, которые можно заполнить, например, числами {1,9} и {9,1} либо {9,1} и {1,9}. И оба таких решения будут верным, но проверка по массиву засчитает лишь тот вариант, который будет совпадать со значениями из массива. Поэтому алгоритм проверки решения Судоку резонно реализовать в соответствии с иной логикой, сравнивая суммы чисел из строк, столбцов и малых 3x3-квадратов с 45 (1+2+3+4+5+6+7+8+9=45). Также, чтобы не раздражать пользователя, вынуждая совершать дополнительные действия, закрывая диалоговые окна после каждой проверки, в качестве альтернативы можно внедрить в пользовательский интерфейс дисплей для отображения сообщений!

Листинг-12. Универсальная проверка

function chk(){

var col=0,err=0,row=0,s9=0,sc,sr;

for(i=1;i<10;i++){sc=0;sr=0;

for(j=1;j<10;j++){

// Сумма в строках

sr+=1*document.getElementById('i'+i+j).value;

// Сумма в столбцах

sc+=1*document.getElementById('i'+j+i).value

}

if(sc!=45||sr!=45){err=1;break}

}

// Сумма в 3x3-секциях

if(!err){

for(i=1;i<4;i++){

for(j=1;j<4;j++){s9+=1*document.getElementById('i'+(i+row)+(j+col)).value}

if(i==3){

if(s9!=45){err=1;break}

s9=0;col+=3;

if(col>6){col=0;row+=3}

if(row<7){i=0}

} } }

// Формирование сообщения

document.getElementById('scr').innerHTML=(err)?'Эпик-фэйл!':'Судоку решена!';

// Цвет текста сообщения

document.getElementById('scr').style.color=(err==1)?'#bb0000':'#009900'

}

В листинге-12 приведён пример реализации универсальной проверки, учитывающей все возможные варианты решения Судоку. В начале усовершенствованной функции chk() помимо переменной err объявляются локальные переменные col для перехода между столбцами 3x3-секций, row для перехода между строками 3x3-секций, s9 (от англ. Sum+9) для вычисления суммы чисел в каждой из девяти 3x3-секций, sc (от англ. Sum+Column) для вычисления суммы чисел в столбцах и sr (от англ. Sum+Row) для вычисления суммы чисел в строках. Внутри двух циклов for одновременно вычисляются суммы чисел в строках и столбцах. Для этого в первом цикле for переменным sc и sr присваиваются 0. Затем во втором цикле for к значениям sc и sr прибавляются значения текстовых полей, умноженные на 1, с разными комбинациями индексов (i и j, j и i).

По умолчанию значения текстовых полей относятся к строковому типу данных String. А умножение значений на 1 переводит строковый тип в числовой Number. И если забыть изменить тип данных, при сложении двух строковых значений вместо суммы чисел получится строка из двух элементов (например, '1'+'2'='12', но '1'*1+'2'*1=1+2=3).

После завершения итераций второго цикла for в if проверяются суммы строк и столбцов. Если одна или обе суммы будут отличны от 45, переменной err присвоится 1, а выполнение циклов for досрочно прервётся служебным словом break. После вычисления сумм всех строк и столбцов в if проверяется истинность переменной err.

В JS числовые значения 0 и 1 соответствуют булевым false и true. Поэтому в случаях, когда переменные в условии if равняются только 0 или 1, допустимо относить их числовые значения к условному типу Boolean. При этом, если в условии такие значения должны быть равны true, можно не указывать оператор сравнения и использовать краткую запись. В примере краткая запись в условии !err соответствует записи !err==true (читается «не err истина»). Но чтобы не запутаться, можно использовать привычное условие err==0.

Таким образом, если проверка строк и столбцов не выявит ошибок и в переменной err останется 0 (булева ложь), будут выполнены вложенные циклы for для проверки суммы чисел в девяти 3x3-секциях. Для этого во внутреннем цикле for с шагом j++ к значению переменной s9 добавляются значения текстовых полей с индексами [i+row] и [j+col], умноженные на 1. Далее в if производится перехват третьей итерации внешнего цикла for (при i==3). С помощью вложенного if проверяется сумма чисел первой 3x3-секции. Если эта сумма будет отлична от 45, переменной err присвоится 1, а выполнение циклов for досрочно прервётся служебным словом break. В противном случае значение переменной s9 обнулится, а к значению переменной col прибавится 3 для горизонтального перехода к соседней группе 3x3-секций. Затем в if проверяется значение переменной col. Если это значение окажется больше 6, переменная col обнулится, а значение переменной row увеличится на 3 для вертикального перехода к соседней группе 3x3-секций. После этого в if проверяется значение переменной row. Если это значение окажется меньше 7, произойдёт обнуление счётчика i внешнего цикла for, и итерации циклов повторятся ещё несколько раз (до тех пор, пока переменная row не окажется больше или равна 7 либо сумма s9 не станет отличной от 45).

В конце функции с помощью краткой записи условия (err) формируется сообщение, которое записывается в элемент с id="scr" (от англ. Screen), выступающий в роли дисплея. Если в переменной err находится булева истина (err==1), на дисплей выводится забавная строка «Эпик-фэйл!» (от англ. Epic+Fail). В противном случае, если ошибок зафиксировано не было (err==0), на дисплей выводится строка «Судоку решена!». Затем для дисплея указывается шестнадцатеричный номер цвета текста (в диапазоне от 0 до 255). Причём во втором случае используется привычная форма записи условия (err==1), но можно опустить указание оператора сравнения == и использовать краткую запись (err). Если при проверке были выявлены ошибки и в переменной err находится 1, с помощью объекта style и свойства color переопределяется настройка стиля оформления элемента с id="scr", меняя цвет шрифта текста на #bb0000 (оттенок красного). В противном случае, если ошибок выявлено не было (err==0), цвет шрифта элемента меняется на #009900 (оттенок зелёного).

Для активации функций chk() и hint() нужно усовершенствовать код функции GUI(), добавив в пользовательский интерфейс дисплей для вывода оповещений и кнопки с обработчиками событий для вызова этих функций. Также для разнообразия в интерфейс можно добавить секундомер или часы!

Листинг-13. Пользовательский интерфейс 3.0

function GUI(){

var n,ui='<table align="center"><tbody>';

// Внедрение дисплея

ui+='<tr><td colspan="9"><div id="scr">Обыграй ИИ</div></td></tr>';

HN=0; // Обнуление счётчика подсказок

mix(); // Перемешивание массива A

// Внедрение кнопок

ui+='<tr><td colspan="3"><div id="bt1" onClick="hint()">!</div></td><td colspan="3"><div id="bt2" onClick="GUI()">&#9658;</div></td><td colspan="3"><div id="bt3" onClick="chk()">&#10003;</div></td></tr></tbody></table>';

// Отображение пользовательского интерфейса

document.getElementById('game').innerHTML=ui;

// Переопределение цвета шрифта на дисплее

document.getElementById('scr').style.color='#ffffff'

}

В листинге-13 представлена обновлённая версия функции GUI() из листинга-7 с добавлением новых опций. В начале функции после объявления локальных переменных к значению ui добавляется новая табличная строка <tr> для формирования дисплея. Атрибут colspan=9 внутри тега <td> сообщает браузеру о том, что при отображении контента в этой строке 9 ячеек нужно объединить в одну общую группу, содержащую блок <div>…</div>, выступающий в роли дисплея, с id="scr" и текстом «Обыграй ИИ» (Искусственный Интеллект). Затем обнуляется значения глобальной переменной HN, выступающей в роли счётчика запрашиваемых подсказок при запуске функции hint(). Следом вызывается функция mix() для перемешивания элементов массива A.

В конце функции к переменной ui добавляется новая табличная строка <tr>, содержащая три сгруппированные ячейки <td> с colspan=3, в которых находятся блоки <div>…</div>, выступающие в роли кнопок. Первая кнопка с id="bt1" (от англ. Button+1) предназначена для запроса подсказок из функции hint(), вызываемой при нажатии на кнопку благодаря обработчику события onClick. Вторая кнопка с id="bt2" предназначена для запуска новой игры, что равнозначно перезапуску функции GUI(). Третья кнопка с id="bt3" предназначена для финальной проверки, вызываемой функцией chk(). Для экономии пространства вместо текстовых надписей кнопки содержат символьные обозначения: восклицательный знак «!», треугольник «►» с кодом &#9658; и галочку «✓» с кодом &#10003; (в данном примере символы надписей второй и третьей кнопок представлены в формате Юникода). Далее содержимое переменной ui внедряется в элемент с id="game", отображая в браузере сформированную игровую таблицу. Затем переопределяется стиль оформления дисплея, меняя цвет текста на #ffffff (белый).

Учитывая, что на кнопках представлены символьные обозначения, после игровой таблицы резонно добавить краткое описание веб-приложения, разнообразив код HTML-документа.

Листинг-14. Обновлённое тело HTML-документа

<body onLoad="GUI()">

<div id="bar">

<h1>Судоку</h1>

<div id="clock"></div>

</div>

<div id="game">Загрузка…</div>

<div id="ds">

<h2>Описание приложения</h2>

<p><span>!</span> — запрос подсказки;</p>

<p><span>&#9658;</span> — новая игра;</p>

<p><span>&#10003;</span> — проверка решения.</p>

<p>Веб-приложение для игры и обучения детей и взрослых людей!</p>

</div>

<p id="cop">© Мазаяки</p>

</body></html>

В листинге-14 приведён пример описания игрового приложения. В теле документа в блоке <div> с id="bar", имитирующим компьютерную панель управления, указываются заголовок первого уровня <h1> с названием игры и вложенный блок <div> с id="clock", служащий заготовкой для будущих часов. Далее представлен знакомый блок <div> с id="game". Следом указывается блок <div> с id="ds" (от англ. Description), содержащий четыре абзаца <p>. В первых трёх абзацах <p> описаны кнопки, в данном случае представленные с помощью тегов <span>, использующиеся для выделения небольших фрагментов контента. В четвёртом абзаце <p> указано краткое описание игрового веб-приложения. В конце тела документа указан пятый абзац <p> с id="cop" (от англ. Copyright), служащий для отображения сведений об авторе веб-приложения!

Для вывода на веб-страницу текущего времени потребуется создание отдельной JS-функции с таймером (благодаря которой также можно модернизировать вывод оповещений на дисплей, представив их в формате телетайпа).

Листинг-15. Телетайп

var TC,TL=1,Txt='Обыграй ИИ';

function txt(){

document.getElementById('scr').innerHTML=Txt.substr(0,TL);

document.getElementById('scr').style.color='#'+TC;

TL=(TL<Txt.length)?TL+1:0

}

В листинге-15 представлен вариант вывода оповещений, имитируя посимвольную печать текста от лица искусственного интеллекта (ИИ)! Перед использованием функции необходимо объявить глобальные переменные Txt (от англ. Text) для хранения текста сообщения, TL (от англ. Text+Length) для мониторинга длины выводимого текста и TC (от англ. Text+Color) для хранения шестнадцатеричного номера цвета текста сообщения. В начале функции txt() с помощью служебной JS-функции substr() из Txt извлекается подстрока в диапазоне от начального (нулевого) символа до значения TL, и эта подстрока записывается в элемент с id="scr", выступающий в роли дисплея. Затем для этого элемента с id="scr" с помощью объекта style и свойства color меняется стиль цвета текста на значение из TC. В конце функции с помощью свойства length проверяется значение глобальной переменной TL. Если значение TL меньше количества символов Txt, счётчик увеличивается на 1. В противном случае счётчик TL обнуляется. Для активации опции телетайпа при создании функции с таймером необходимо указать ссылку на функцию txt().

Листинг-16. Таймер

var Dy;

function timer(){

var dt=new Date(),dh=dt.getHours(),dm=dt.getMinutes(),sp=':';

Dy=dt.getFullYear();

if(dh<10){dh='0'+dh}

if(dm<10){dm='0'+dm}

if(dt.getSeconds()%2==0){sp='<span style="visibility:hidden">'+sp+'</span>'}

document.getElementById('clock').innerHTML=dh+sp+dm;

if(TL>0){txt()}

}

В листинге-16 приведён пример работы с объектом Date(), отвечающего в JS за дату и время. Перед использованием функции необходимо объявить глобальную переменную Dy (от англ. Date+Year) для хранения текущего года, которая может пригодиться для использования в других функциях. В теле функции timer() с помощью объекта Date() в объявленную локальную переменную dt (от англ. Date) записывается информация о текущих дате и времени, которая будет обновляться при каждом вызове функции. Следом объявляются локальные переменные dh (от англ. Date+Hours) и dm (от англ. Date+Minutes), в которые записывается текущее время, извлечённое из переменной dt с помощью методов getHours() и getMinutes(). А также локальная переменная sp (от англ. Span) для хранения строки с двоеточием, разделяющим часы и минуты. Далее обновляется значение глобальной переменной Dy, в которую записывается текущий год, извлекаемый из dt методом getFullYear().

Методы getHours() и getMinutes() возвращают числовые значения в диапазонах 0–23 и 0–59, поэтому для отображения времени в формате 09:06 необходима проверка значений переменных dh и dm. Если в ходе проверки в if часы (dh) или минуты (dm) окажутся меньше 10, к соответствующей переменной добавится префикс-строка '0', преобразовывая значение в двузначный формат. Для создания эффекта мигающего двоеточия, разделяющего часы от минут, в третьем if осуществляется проверка на чётность текущих секунд с помощью метода getSeconds() и оператора модального деления %, используемого для определения наличия остатка от деления двух чисел. В случае, если секунды делятся на 2 без остатка (являются чётным числом), разделитель из переменной sp помещается в контейнер <span>…</span> с добавлением атрибута style и указанием свойства visibility со значением hidden, делая двоеточие невидимым. Далее в элемент с id="clock" записывается текущее время, сформированное из часов (dh), разделителя (sp) и минут (dm). В конце кода в if проверяется значение глобальной переменной TL, чтобы вызывать функцию txt() до тех пор, пока этот счётчик не обнулится.

Для установки параметров таймера можно создать отдельную функцию starter(), которая будет вызываться при открытии веб-страницы вместо функции GUI().

Листинг-17. Стартёр

var Tmr;

function starter(){

GUI();timer();

// Установка интервала таймера

Tmr=setInterval('timer()',1000);

// Дополнение копирайта

document.getElementById('cop').innerHTML='© '+Dy+' <a href="https://mazayaki.ru/" target="_blank" title="Лайфхаки от Мазаяки">Мазаяки</a>'

}

В листинге-17 представлена специальная функция для автозапуска, позволяющая задать временной интервал для повторного выполнения команд из тела функции timer(). Перед использованием функции необходимо объявить глобальную переменную Tmr (от англ. Timer) для хранения метки-идентификатора. В начале кода вызываются функции GUI() для прорисовки пользовательского интерфейса и timer() для вывода текущего времени, а также для активации телетайпа. Далее с помощью метода setInterval() для функции timer() задаётся интервал в 1000 миллисекунд (что соответствует 1 секунде). Затем в глобальную переменную Tmr записывается сгенерированная метка-идентификатор, с помощью которой при необходимости можно убрать интервал методом clearInterval(Tmr), снизив загруженность памяти. В конце кода происходит перезапись содержимого элемента с id="cop" с добавлением текущего года из глобальной переменной Dy и гиперссылки, ведущей на сайт автора веб-приложения с помощью тега <a>, внутри которого указаны атрибуты href с адресом сайта, target с признаком открытия сайта в новой вкладке и title с подсказкой, всплывающей при наведении курсора.

Для автозапуска функции starter() при открытии веб-страницы следует изменить HTML-документ, указав её имя вместо функции GUI() в обработчике события onLoad внутри тега <body> из листинга-14. Таким образом, после открытия веб-страницы функция timer() должна повторно запускаться каждую секунду. Но для вывода оповещений на дисплей в формате телетайпа необходимо внести изменения в концовки функций chk() и GUI(), указав значения глобальных переменных TC, TL и Txt.

Листинг-18. Активация телетайпа

function chk(){

Txt=(err)?'Эпик-фэйл!':'Судоку решена!';

TC=(err==1)?'bb0000':'009900';TL=1

}

function GUI(){

Txt='Обыграй ИИ';

TC='ffffff';TL=1

}

В листинге-18 приведены фрагменты кода изменённых функций chk() и GUI(). В обоих случаях в глобальную переменную Txt перезаписывается текст сообщения. Затем переопределяется цвет текста в глобальной переменной TC, а счётчику TL присваивается 1, выполняя условие для запуска функции txt().

После внесённых изменений все выводимые на дисплей оповещения должны печататься со скоростью 1 символ в секунду, оживляя пользовательский интерфейс имитацией взаимодействия с ИИ. Также для разнообразия можно добавить всплывающее меню с опциями отключения дисплея и управления цветовой схемы оформления. Но для реализации этой задумки вначале резонно объявить правила оформления контента HTML-документа…

CSS (Cascading Style Sheets)

CSS — каскадные таблицы стилей, содержащие правила оформления HTML-элементов веб-документов вида селектор{свойство:значение;}. CSS-селекторы сообщают браузеру, к каким именно элементам документа следует применять объявленные правила, а свойства и значения помогают управлять оформлением этих элементов. Как и в случае с JS-сценариями, CSS-код небольшого объёма может находиться в текущем веб-документе внутри контейнера <style type="text/css">…</style>. Но для удобочитаемости кода стили оформления резонно вынести в отдельный файл, создав в папке GameDev новый текстовый документ с именем style и расширением .css, который также следует открыть с помощью текстового редактора (и расположить открывшееся окно в правой части экрана). Для подключения CSS-правил из внешнего файла в HTML-документе в разделе заголовков после мета-тегов необходимо указать ссылку на стилевой файл внутри одиночного тега <link> с добавлением служебных атрибутов: <link href="style.css" rel="stylesheet" type="text/css">. После этого, вернувшись к CSS-окну, можно приступить к модернизации дизайна интерфейса игрового веб-приложения.

Процесс создания правил оформления контента подобен форматированию текста в текстовом редакторе: для контента можно указать цвет заднего фона (background-color), установить фоновый рисунок (background-image), определить границы элементов (border), оформить шрифт текста (font), задать межстрочный интервал (line-height), добавить внешние и внутренние поля-отступы (margin; padding), выбрать способ выравнивания контента (text-align) и т.д. Учитывая, что Судоку представляет собой таблицу с 81 ячейкой, в первую очередь резонно объявить правила для элементов в игровой таблице, указав параметры дисплея, текстовых полей для ввода чисел, кнопок, ячеек и самой таблицы. При этом важно помнить, что для лучшего восприятия информации следует отделять разные смысловые блоки интерфейса с помощью цвета заднего фона, больших отступов или прорисованных линий.

Листинг-19. Объявление CSS-правила

/* Параметры дисплея */

.scr{

background-color:#aaa;

background-image:linear-gradient(#eee,transparent);

background-image:-webkit-linear-gradient(#eee,transparent);

border-radius:5px;

color:#fff;

cursor:default;

margin:0 5px 5px;

padding:6px 9px 9px;

text-align:left;

text-shadow:-1px -1px 1px #fff, 1px 1px 1px #000;

transition:2s;

-webkit-transition:2s

}

В листинге-19 объявлено правило отображения HTML-элемента с классом class="scr" (выполняющего функцию дисплея), на что указывает селектор класса .scr, записываемый с префиксом в виде точки перед именем. Как видно из примера, при необходимости в CSS-код также можно добавлять пояснительные комментарии, которые записываются подобно длинным JS-комментариям в конструкции из косых черт и звёздочек /*…*/. А правила записываются аналогично функциям или операторам JS между начальной и конечной фигурными скобками {…}. Но в CSS вместо привычного оператора присваивания = значения свойств указываются через двоеточие. При этом, если для одного селектора требуется указать несколько свойств, эти записи разделяются точкой с запятой.

В начале CSS-кода с помощью свойства background-color указывается цвет заднего фона дисплея с оттенком серого #aaa. Цветовая схема в веб-дизайне традиционно задаётся в формате RGB (от англ. Red+Green+Blue) тремя числами, регулирующими соотношение красного, зелёного и синего. Эти числа можно указывать в шестнадцатеричной системе счисления от 00 до ff, в десятичном виде от 0 до 255 и в процентом соотношении от 0% до 100%. Причём, если в группах шестнадцатеричного номера цвета используются одинаковые цифры вида #1199ff, их допустимо записывать в сокращённой форме #19f. Таким образом, номер цвета #aaa может указываться в полном шестнадцатеричном виде #aaaaaa, а также с помощью служебной функции rgb() в десятичном виде rgb(170,170,170) либо в процентах rgb(66%,66%,66%). Но сокращённый формат записи позволяет сэкономить объём документа и время, затраченное на указание таких значений!

Далее с помощью свойства background-image устанавливается фоновый рисунок в виде полупрозрачного линейного градиента linear-gradient() с начальным значением цвета #eee (светло-серый) и служебным словом transparent вместо конечного цвета заливки. Значение transparent сообщаемого браузеру о том, что в качестве конечного цвета градиента следует выбрать цвет заднего фона (то есть ранее объявленное значение #aaa). Такая заливка градиента поможет с внедрением опции для изменения цветовой схемы дисплея. Для разработки кросс-браузерных веб-приложений некоторые свойства и значения правил требуется дублировать, указывая разные служебные слова, поддерживаемые разными браузерами. В данном случае дублируется значение linear-gradient() с добавлением префикса -webkit- для совместимости с разными версиями браузеров.

После этого с помощью свойства border-radius задаются закруглённые края с радиусом в 5 пикселей (px). Затем с помощью свойства color указывается цвет текста дисплея со значением #fff (белый). Следом с помощью свойства cursor переопределяется вид курсора мыши на значение по умолчанию (default), чтобы при наведении на дисплей курсор отображался в виде стрелочки (при запуске веб-приложения на смартфоне данное правило будет проигнорировано из-за отсутствия мышки). Далее задаются внешние и внутренние поля дисплея с помощью свойств margin и padding, которым присваиваются тройные значения, перечисленные через пробел. Для внешних полей указываются нулевой верхний отступ (перед дисплеем) и одинаковые 5-пиксельные боковые (слева и справа от дисплея) и нижний отступы (после дисплея). А для внутренних полей указываются верхний отступ в 6px и одинаковые боковые и нижний отступы в 9px.

Затем с помощью свойства text-align определяется центрированный способ выравнивания контента (center). Следом с помощью свойства text-shadow задаются параметры тени текста дисплея, причём для тени указываются два разных значения, перечисленные через запятую. В первом случае устанавливается тень с 1-пиксельными сдвигами влево по горизонтали и вверх по вертикали, толщиной в 1px и с белым цветом #fff. Во втором случае устанавливается чёрная тень со сдвигами на 1px вправо и вниз. Такой способ с указанием двойных значений создаёт визуальный 3D-эффект объёмного текста!

В конце CSS-правила с помощью свойства transition в интерфейс внедряется визуальный эффект, позволяющий осуществлять плавный переход (в течение 2 секунд) при смене цветовой схемы дисплея. Как и в случае с линейным градиентом, запись со свойством transition дублируется для поддержки разными версиями браузеров, но в данном случае префикс -webkit- добавляется перед свойством, а не перед значением.

В приведённом примере цвет текста элемента указывается с помощью CSS-записи .scr{color:#fff}, которая равнозначна HTML-записи <div class="scr" id="scr" style="color:#ffffff">!</div> и JS-записи document.getElementById('scr').style.color='#ffffff'. Таким образом, управлять оформлением контента веб-страниц можно и в HTML, и в JS, указывая соответствующие параметры для каждого элемента в отдельности. Но в CSS можно не только объявлять правила отображения отдельных элементов, но и создавать правила для родственных элементов одного типа (например, чтобы в браузере отображались одинаково все элементы input), а также формировать групповые классы (например, чтобы одинаково отображались элементы div и p, в которых указан родственный класс class="txt").

Для применения правила объявленного класса необходимо в начале кода JS-функции GUI() внутри тега <div> с id="scr" добавить атрибут class="scr": <div class="scr" id="scr">. Аналогично можно объявлять правила оформления с разными параметрами для разных состояний элементов или для элементов с определёнными атрибутами.

Листинг-20. Параметры игровой таблицы

table{

background-color:#ccc;

border:2px solid #777;

border-left-color:#fff;

border-top-color:#fff;

border-radius:10px;

border-spacing:0;

box-shadow:2px 2px 3px #777

}

td{

padding:0;

vertical-align:middle

}

/* Параметры ячейки с дисплеем */

td[colspan="9"]{padding:5px 0 0}

В листинге-20 представлен пример использования селекторов элементов и селектора атрибутов. В начале кода для элемента table задаётся серебристый цвет заднего фона #ccc. Затем указываются границы (border) с толщиной в 2px, со сплошным стилем прорисовки solid и с тёмно-серым цветом #777. При этом для создания объёмного 3D-эффекта цвет заливки левой и верхней границ переопределяется на белый #fff. Далее устанавливается радиус закругления углов в 10px. А с помощью свойства border-spacing задаётся нулевой интервал между ячейками таблицы (чтобы игровое поле не было слишком большим). В конце первого правила с помощью свойства box-shadow вокруг границ игровой таблицы устанавливается тень со сдвигом вправо на 1px, вниз на 2px, толщиной в 3px и с тёмно-серым цветом #777.

Во втором правиле для всех элементов td объявляются нулевые внутренние поля (padding). А с помощью свойства vertical-align и значения middle в табличных ячейках определяется способ выравнивания контента по вертикали (по центру). В конце кода используется селектор атрибута td[colspan="9"], позволяющий отдельно установить для содержащей дисплей ячейки <td> с атрибутом colspan="9" (указанным в селекторе в квадратных скобках после наименования элемента) внутренний верхний отступ в 5px.

В CSS правила отображения с селекторами элементов применяются ко всем указанным элементам документа автоматически, не требуя дополнительных манипуляций с кодом. А правила с селекторами атрибутов применяются только к элементам документа с указанными атрибутами.

Листинг-21. Параметры текстовых полей

input{

border-radius:5px;

font-family:Tahoma;

font-size:18px;

height:28px;

width:28px

}

input[disabled]{

border-style:outset;

color:#555;

font-weight:bold

}

input[id$="1"],input[id$="4"]{margin-left:5px}

input[id$="6"],input[id$="9"]{margin-right:5px}

input[id^="i4"],input[id^="i7"]{margin-top:5px}

input,td{text-align:center}

/* Параметры <input> для устройств с шириной экрана <= 360px */

@media(max-width:360px){

input{

height:25px;

width:25px

} }

В листинге-21 приведён пример с использованием особых CSS-выражений (условий) в квадратных скобках селекторов атрибутов. В первом правиле задаётся радиус закругления границ текстовых полей. Параметры самих границ при этом не указываются, чтобы использовались браузерные значения по умолчанию. Далее для текстовых полей определяются гарнитура Tahoma и размер шрифта в 18px с помощью свойств font-family и font-size. Затем с помощью свойств height и width задаются одинаковые высота и ширина текстовых полей в 28px.

Во втором правиле с помощью селектора атрибута input[disabled] элементам input с указанным атрибутом disabled задаются стиль границ (border-style) с имитацией объёмного 3D-эффекта outset, светло-чёрный цвет текста и полужирное начертание шрифта bold (font-weight).

В веб-дизайне для одних и тех же свойств разных элементов могут задаваться одинаковые значения. В таких случаях селекторы допустимо объединять в группы, перечисляя их через запятую, и указывать в фигурных скобках общие значения одинаковых свойств.

В приведённом примере по такому принципу объявляются три однотипных правила, устанавливающие внешние отступы избранных элементов input с помощью селекторов атрибутов, отделяя сгруппированные 3x3-секции. Запись внутри квадратных скобок селектора атрибута input[id$="1"] означает, что объявленное правило должно применяться ко всем элементам input, чьи id заканчиваются на 1 (то есть текстовые поля из первого столбца таблицы). Аналогично правило с input[id$="4"] должно применяться ко всем input, чьи id заканчиваются на 4 (текстовые поля из четвёртого столбца). А правило с input[id^="i4"] должно применяться ко всем input, чьи id начинаются с "i4" (текстовые поля из четвёртой строки). Таким образом, для 1 и 4 столбцов задаётся внешний левый, для 6 и 9 столбцов — внешний правый, а для 4 и 7 строк — внешний верхний отступы в 5px. Далее для текстовых полей input и табличных ячеек td объявляется общее правило с центрированным выравниванием контента.

В конце кода с помощью служебного слова @media и свойства max-width объявляется правило, которое будет применяться к элементам input только на небольших пользовательских устройствах с шириной экрана не более 360px. Данное правило уменьшает высоту (height) и ширину (width) текстовых полей до 25px, чтобы игровая таблица не выходила за рамки небольшого экрана мобильного телефона.

В CSS дополнительные ограничения с @media помогают создавать адаптивный дизайн, частично или полностью отображая контент на разных устройствах по-разному. При этом важно помнить, что в нижней части игровой таблицы расположены три кнопки, ширина которых не должна троекратно превышать минимальную ширину ячеек (3*25=75px).

Листинг-22. Оформление кнопок

div[id="bt1"]{color:#b00}

#bt2{color:#090}

div#bt3{color:#00d}

.btn{

background-color:#ddd;

border:1px outset #fff;

border-radius:50%;

cursor:pointer;

display:inline-block;

font-family:Arial;

font-weight:bold;

line-height:1.5;

text-align:center;

text-indent:0

}

/* Цвет фона кнопки при наведении курсора */

.btn:hover{background-color:#eee}

div.btn{

margin:9px auto 6px;

width:75px

}

span.btn{

margin:0;

width:50px

}

В листинге-22 представлены правила отображения кнопок с использованием селекторов идентификаторов и селекторов классов. В начале кода объявляются три однотипных правила, устанавливающих разный цвет текста для трёх кнопок.

Как видно из примера, в CSS при объявлении правил оформления элементов с указанным id можно использовать селекторы атрибутов либо специальные селекторы идентификаторов, записываемые по принципу хэштега с префиксом в виде решётки # перед именем id (при необходимости перед решёткой можно дополнительно указать конкретный элемент). Но для экономии времени и места проще всего использовать краткий вариант записи селекторов идентификаторов с решёткой и именем id (#bt1, #bt2 и #bt3). А селекторы атрибутов резонно использовать лишь в особых случаях, например, когда потребуется объявить правила для большой группы однотипных элементов со схожими значениями атрибутов (как в примере из листинга-21).

Далее для кнопок формируется правило с общим классом .btn (от англ. Button). В начале класса указывается цвет заднего фона. Затем задаются границы с 3D-стилем outset и радиус закругления в 50%. Следом устанавливается вид курсора мыши на pointer (чтобы при наведении на кнопки курсор выглядел так, как при наведении на гиперссылки). После этого с помощью свойства display указывается блочный способ отображения кнопок inline-block. А для шрифта задаются гарнитура Arial с полужирным начертанием bold. В конце класса с помощью свойств line-height, text-align и text-indent для текста кнопок задаются полуторный межстрочный интервал, центрированный способ выравнивания контента и нулевая красная строка.

Помимо объявления правил для элементов в статичных состояниях, в CSS можно управлять оформлением контента с учётом наступления разных событий-триггеров, инициируемых поведением пользователя. В приведённом примере после класса .btn объявляется псевдокласс с добавлением двоеточия и служебного слова hover, отвечающего за движение курсора мыши по кнопке или нажатие пальцем на кнопку на сенсорном экране. При наступлении одного из этих событий у используемой кнопки должен будет измениться цвет фона на более светлый.

В случаях, когда схожим элементам необходимо задать как общие, так и различные параметры, одинаковые значения можно объявлять в общем классе, а для различающихся параметров резонно формировать отдельные правила с указанием конкретных элементов. В конце кода объявляются два однотипных правила с селекторами div.btn и span.btn для отображения разных кнопок, представленных в документе в тегах <div> и <span>. Для элементов div с классом .btn задаются внешние отступы (значение auto означает, что боковые отступы должны определиться браузером автоматически), а также ширина кнопок в 75px. Для элементов span с классом .btn задаются нулевые внешние поля и укороченная ширина в 50px (поскольку в информационном блоке с кратким описанием основной акцент делается на компактности вида). Таким образом, инструментарий CSS позволяет с помощью небольших фрагментов кода управлять оформлением больших веб-документов!

Листинг-23. Дополнительные параметры веб-документа

/* Цвет текста документа */

*{color:#333}

a{

border-bottom:1px dashed #00d;

color:#00d;

text-decoration:none

}

a:hover{

border-color:#77f;

color:#77f

}

#bar{

background-color:#ddd;

background-image:linear-gradient(#eee,#ccc);

background-image:-webkit-linear-gradient(#eee,#ccc);

border-bottom:double #ddd;

cursor:default;

padding:6px 0 9px

}

#bar,#clock,#cop{text-align:right}

body{

background-color:#eee;

font-family:Verdana;

font-size:18px;

text-align:center

}

body,#cop,h1,h2{margin:0}

#clock{

color:#fff;

padding-right:15px;

text-shadow:1px 1px 1px #aaa

}

#clock,#cop{font-size:17px}

#clock,h1{display:inline-block}

#cop,#ds{padding:10px 15px}

#ds{background:#fafafa}

#game{margin:15px auto 20px}

h1{padding-left:8px}

h1,h2{

color:#777;

font-size:20px;

text-align:left

}

p{

line-height:1.5;

margin:5px 0 0;

text-align:justify

}

В листинге-23 приведён пример правил оформления для различных элементов веб-документа. В начале кода объявляется правило с универсальным селектором * для установки цвета текста документа с чёрным оттенком #333. Звёздочка * в данном случае обозначает любые элементы документа, для которых не определена другая цветовая схема.

Во втором правиле с селектором элемента a для гиперссылки задаётся цвет текста и меняется сплошное подчёркивание на штриховое. Для этого устанавливается нижняя граница в виде штрихов (dashed). А свойству text-decoration присваивается обнуляющее значение none.

В третьем правиле с селектором псевдокласса :hover переопределяется цветовая схема гиперссылки, которая будет применяться при движении курсора или при нажатии пальцем. Для этого меняются цвет нижней границы и цвет текста.

Затем объявляется правило с селектором идентификатора #bar, задающее параметры верхнего блока (содержащего заголовок первого уровня и часы). В начале правила устанавливаются цвет заднего фона и фоновый рисунок в виде линейного градиента. Далее задаётся двойная нижняя граница и переопределяется вид курсора на стрелочку по умолчанию. В конце правила указываются внутренние отступы. После этого формируется общее правило с селекторами идентификаторов #bar, #clock и #cop, устанавливающее выравнивание контента по правому краю для верхней панели и блоков с часами и копирайтом.

Далее с помощью селектора элемента body для тела документа устанавливаются цвет заднего фона, гарнитура Verdana и размер шрифта в 18px, а также центрированный способ выравнивания контента. Параметры, указанные для элемента body, должны наследоваться вложенными элементами, для которых не определены другие значения. Но при необходимости указать параметры, которые должны быть обязательно применимы ко всем элементам документа, резонно использовать универсальный селектор в виде звёздочки!

Следом формируется общее правило с группой селекторов, задающее телу документа, блоку с копирайтом и заголовкам первого и второго уровня нулевые внешние отступы. Затем объявляется правило с селектором идентификатора #clock, устанавливающее для блока с часами белый цвет текста, правый внутренний отступ и тень текста. Далее формируется общее правило для блоков с часами и копирайтом, задающее размер шрифта в 17px. Следом формируется общее правило для блока с часами и заголовка первого уровня, устанавливающее свойству display значение inline-block, чтобы эти элементы могли отображаться в браузере небольшими блоками в одной общей строке. После этого формируется общее правило с селекторами идентификаторов #cop и #ds, задающее внутренние отступы блокам с копирайтом и кратким описанием.

Затем объявляется правило с селектором идентификатора #ds, устанавливающее фон белого оттенка #fafafa. Следом для игрового блока с идентификатором #game указываются внешние отступы. Далее объявляются параметры заголовков первого и второго уровня. В правиле с селектором элемента h1 задаётся внутренний левый отступ. А в общем правиле с селекторами элементов h1 и h2 для текста заголовков устанавливаются тёмно-серый цвет, размер шрифта в 20px и выравнивание по левому краю. В конце кода объявляется правило с селектором элемента p, задающее абзацам документа межстрочный полуторный интервал, внешние отступы, а также способ выравнивания по ширине контента (justify).

После объявления правил оформления элементов документа можно добавить новые правила, которые помогут внедрить в веб-страницу всплывающее меню для персонализации настроек! Для реализации этой задачи как минимум понадобятся значок меню, который можно представить в теге <span> в виде символа в формате Юникода, например, триграммы «☰» с кодом &#9776;, используемой в эзотерических текстах с уклоном в Фэншуй! Для значка-кнопки следует указать id #men (от англ. Menu) и два однотипных класса .me0 (от англ. Menu+0) и .me1 (от англ. Menu+1), управляющие оформлением значка в статичном и активном состояниях. При этом значок меню должен быть хорошо заметен, поэтому его резонно расположить в начале верхнего блока слева от заголовка, объединив их в общий блок <div> с id #adj (от англ. Adjacent), чтобы было проще позиционировать эти смежные элементы в левой части верхней панели, а блок с часами — в правой. Блок со всплывающим меню можно представить в теге <div> с id="menu" сразу после верхней панели. Поскольку у блока с меню должно быть два состояния (скрытый и видимый режимы), для этого элемента также необходимо предусмотреть два однотипных класса .hid (от англ. Hidden) и .vis (от англ. Visible).

В самом блоке меню понадобятся три дополнительных текстовых поля <input> с id #r, #g, #b, с помощью которых пользователь сможет устанавливать желаемое соотношение красного, зелёного и синего цветов для заднего фона дисплея. А чтобы пользователю было проще ориентироваться в RGB-цветах, резонно добавить шаблоны с цветами радуги, которые можно представить в тегах <span> с id вида #bg7 (от англ. Background+7). Также в меню можно добавить переключатель, позволяющий пользователю отключать и включать дисплей. В качестве переключателя можно использовать тег <input> с указанием специального типа type="checkbox" (квадрат с галочкой) либо внедрить самостоятельно созданный триггер. Для внедрения триггера понадобятся два тега <span>, один из которых будет вложен в другой. Внутренний <span> будет выполнять функцию флажка оповещения, поэтому для него можно ввести id или класс с именем flg (от англ. Flag). Внешний <span> будет использоваться в качестве поля для флажка-переключателя, поэтому для него потребуются id #swi (от англ. Switch) и два однотипных класса .on и .off, управляющие разными положениями флажка. Рядом с переключателем можно добавить пояснительный текст вида «Дисплей вкл.», объединённый с переключателем в общий блок <div> с id #box. Для наглядности этот текст можно перечёркивать при отключении дисплея. Для управления оформлением текста следует добавить два однотипных класса .decThr (от англ. Decoration+Through) и .decNon (от англ. Decoration+None).

Листинг-24. Параметры элементов меню

/* Позиционирование блоков */

#adj,#menu{position:absolute}

#adj,#menu,.off{text-align:left}

/* RGB-поля */

#b,#g,#r{width:48px}

#b{color:#00d}

#g{color:#090}

#r{color:#b00}

/* Шаблоны */

.bg0{

border:1px outset #fff;

border-radius:5px;

opacity:.5

}

.bg0:hover{opacity:1}

.bg0,.flg{width:20px}

.bg0,.flg,#swi{height:20px}

.bg0,.flg,#men,#swi{display:inline-block}

#bg1{background:#b00}

#bg2{background:#ea0}

#bg3{background:#dd0}

#bg4,.on{background:#090}

#bg5{background:#6dd}

#bg6{background:#00d}

#bg7{background:#b0b}

/* Блок с переключателем */

#box{

border-bottom:1px solid #aaa;

color:#777;

font-family:Tahoma;

padding:0 0 5px

}

/* Блок с настройками цветовой схемы */

.clr{

border-top:1px solid #eee;

padding:5px 0 0

}

/* Текст без оформления */

.decNon{text-decoration:none}

/* Перечёркивание текста */

.decThr{text-decoration:line-through}

/* Флажок */

.flg{

background-image:linear-gradient(#fff,#aaa);

background-image:-webkit-linear-gradient(#fff,#aaa);

box-shadow:0px 0px 2px #555

}

.flg,.off{background-color:#ddd}

.flg,#swi{border-radius:10px}

/* Выкл. */

.hid{display:none}

/* Значок меню */

.me0,.me1:hover{color:#aaa}

.me0:hover,.me1{color:#eee}

#men{

border-right:1px solid #aaa;

padding-left:12px;

text-shadow:0 1px 1px #777;

width:30px

}

#men,#menu{cursor:pointer}

/* Вкл. */

.on{text-align:right}

/* Блок с шаблонами */

.pat{

padding-top:5px;

text-align:center

}

/* Переключатель */

#swi{

border:1px solid #aaa;

transition:1s;

-webkit-transition:1s;

vertical-align:middle;

width:39px

}

/* Всплывающее меню */

.vis{

background:#ccc;

border-bottom:double #ddd;

border-right:double #ddd;

border-radius:5px;

color:#777;

line-height:1;

padding:5px

}

В листинге-24 представлены правила оформления, позволяющие внедрить в документ всплывающее меню. В начале кода формируется общее правило с селекторами идентификаторов #adj и #menu, с помощью свойства position задающее абсолютный способ позиционирования блоков, позволяя следующим за ними элементам располагаться в браузере на тех же позициях, где и эти блоки. В первом случае абсолютное позиционирование позволяет блоку с часами отображаться в одной плоскости вместе с верхним левым блоком (со значком меню и заголовком), во втором случае — накладывать контент всплывающего меню поверх секции игровой таблицы. Далее формируется общее правило с id #adj и #menu и классом .off, устанавливающее выравнивание контента по левому краю. Затем объявляются правила с селекторами #r, #g и #b, задающие текстовым полям меню ширину в 48px и цвет текста.

После этого объявляется общий класс .bg0, задающий для шаблонов цветовой схемы в статичном состоянии границы и радиус закругления, а также эффект полупрозрачности (opacity) со значением .5, что соответствует 50%. Следом объявляется правило с псевдоклассом .bg0:hover для переопределения полупрозрачности шаблонов на 1 (100%). Затем формируются общее правило с классами .bg0 и .flg, задающее ширину в 20px, и общее правило с этими классами и id #swi, задающее высоту в 20px. Далее формируется общее правило с классами .bg0 и .flg и id #men и #swi, устанавливающее свойству display значение inline-block, чтобы эти элементы могли отображаться в браузере небольшими блоками с заданными размерами. После этого объявляются однотипные правила, устанавливающие цвет заднего фона 7 шаблонов с id #bg1–7 и поля переключателя с классом .on (во включённом режиме).

Затем объявляется правило с id #box, задающее нижнюю границу, цвет текста, гарнитуру шрифта и внутренний нижний отступ. Следом объявляется класс .clr (от англ. Color), задающий верхнюю границу и внутренний верхний отступ для блока с настройками цветовой схемы. Указанные в этих правилах параметры границ и внутренних отступов позволяют визуально отделить два разных блока меню, облегчая восприятие интерфейса!

Далее объявляются два однотипных правила с классами .decNon и .decThr, управляющие оформлением пояснительного текста. В активном режиме с включённым переключателем к блоку будет применяться класс .decNon без дополнительного оформления. В выключенном режиме — класс .decThr с оформлением текста перечёркнутой горизонтальной линией.

После этого объявляется класс .flg, устанавливающий флажку-переключателю фоновый рисунок в виде линейного градиента и тень вокруг границ (box-shadow). Затем формируются общее правило с классами .flg и .off, устанавливающее цвет заднего фона, и общее правило с классом .flg и id #swi, задающее радиус закругления. Далее объявляется класс .hid, используемый для скрытия дисплея и всплывающего меню с помощью значения none для свойства display.

Следом формируются два однотипных правила для значка меню, регулирующие цвет значка в обычном режиме с классом .me0 и активированном режиме с классом .me1. В статичном состоянии для значка меню будет применяться цвет с оттенком серого #aaa, а при наведении курсора мыши цвет будет меняться на светло-серый оттенок #eee. В нажатом состоянии — наоборот.

Затем объявляется правило с id #men, содержащее параметры значка меню, не зависящие от его состояния. В начале правила задаётся правая граница, помогающая визуально отделить значок от смежного заголовка. Далее указывается левый внутренний отступ и параметры тени значка. В конце правила задаются ширина значка в 30px. После этого формируется общее правило с id #men и #menu, устанавливающее курсор-пойнтер.

Далее объявляется класс .on, задающий выравнивание контента по правому краю. Следом объявляется класс .pat (от англ. Pattern), устанавливающий сгруппированному блоку с шаблонами верхний внутренний отступ и центрированный способ выравнивания контента.

После этого объявляется правило с id #swi, управляющее оформлением поля с переключателем. В начале правила задаются границы и эффект плавного перехода (transition) между стилями .on и .off при каждом переключении флажка. Затем задаётся центрированный способ выравнивания контента по вертикали и ширина в 39px.

В конце кода объявляется класс .vis, используемый для активации видимого режима всплывающего меню. В начале правила устанавливается серебристый цвет заднего фона. Далее задаются двойные нижняя и правая границы и радиус закругления. Затем для текста указываются тёмно-серый цвет и единичный межстрочный интервал. В конце правила задаются внутренние отступы в 5px.

Таким образом, с помощью коллаборации CSS с HTML и JS можно успешно управлять внешним видом контента веб-документа!

Листинг-25. Содержимое файла style.css

*{color:#333}

a{border-bottom:1px dashed #00d}

a,#b,#bt3{color:#00d}

a,.decNon{text-decoration:none}

a:hover{

border-color:#77f;

color:#77f

}

#adj,#menu{position:absolute}

#adj,h1,h2,#menu,.off,.scr{text-align:left}

#b,#g,#r{width:48px}

#bar{

background-image:linear-gradient(#eee,#ccc);

background-image:-webkit-linear-gradient(#eee,#ccc);

border-bottom:double #ddd;

padding:6px 0 9px

}

#bar,.btn,.flg,.off{background-color:#ddd}

#bar,#clock,#cop,.on{text-align:right}

#bar,.scr{cursor:default}

.bg0{opacity:.5}

.bg0:hover{opacity:1}

.bg0,.btn{border:1px outset #fff}

.bg0,.btn,#clock,.flg,h1,#men,#swi{display:inline-block}

.bg0,.flg{width:20px}

.bg0,.flg,#swi{height:20px}

.bg0,input,.scr,.vis{border-radius:5px}

#bg1{background:#b00}

#bg2{background:#ea0}

#bg3{background:#dd0}

#bg4,.on{background:#090}

#bg5{background:#6dd}

#bg6{background:#00d}

#bg7{background:#b0b}

body{font-family:Verdana}

body,.btn:hover{background-color:#eee}

body,.btn,input,.pat{text-align:center}

body,#cop,h1,h2{margin:0}

body,input{font-size:18px}

#box{

border-bottom:1px solid #aaa;

padding:0 0 5px

}

#box,h1,h2,.vis{color:#777}

#box,input{font-family:Tahoma}

#bt1,#r{color:#b00}

#bt2,#g{color:#090}

.btn{

border-radius:50%;

font-family:Arial;

text-indent:0

}

.btn,input[disabled]{font-weight:bold}

.btn,#men,#menu{cursor:pointer}

.btn,p{line-height:1.5}

#clock{

padding-right:15px;

text-shadow:1px 1px 1px #aaa

}

#clock,#cop{font-size:17px}

#clock,.scr{color:#fff}

.clr{border-top:1px solid #eee}

.clr,td[colspan="9"]{padding:5px 0 0}

#cop,#ds{padding:10px 15px}

.decThr{text-decoration:line-through}

div.btn{

margin:9px auto 6px;

width:75px

}

#ds{background:#fafafa}

.flg{

background-image:linear-gradient(#fff,#aaa);

background-image:-webkit-linear-gradient(#fff,#aaa);

box-shadow:0px 0px 2px #555

}

.flg,#swi,table{border-radius:10px}

#game{margin:15px auto 20px}

h1{padding-left:8px}

h1,h2{font-size:20px}

.hid{display:none}

input{

height:28px;

width:28px

}

input[disabled]{

border-style:outset;

color:#555

}

input[id$="1"],input[id$="4"]{margin-left:5px}

input[id$="6"],input[id$="9"]{margin-right:5px}

input[id^="i4"],input[id^="i7"]{margin-top:5px}

.me0,.me1:hover{color:#aaa}

.me0:hover,.me1{color:#eee}

#men{

border-right:1px solid #aaa;

padding-left:12px;

text-shadow:0 1px 1px #777;

width:30px

}

p{

margin:5px 0 0;

text-align:justify

}

.pat{padding-top:5px}

.scr{

background-color:#aaa;

background-image:linear-gradient(#eee,transparent);

background-image:-webkit-linear-gradient(#eee,transparent);

margin:0 5px 5px;

padding:6px 9px 9px;

text-shadow:-1px -1px 1px #fff, 1px 1px 1px #000;

transition:2s;

-webkit-transition:2s

}

span.btn{width:50px}

#swi{

border:1px solid #aaa;

transition:1s;

-webkit-transition:1s;

width:39px

}

#swi,td{vertical-align:middle}

table{

background-color:#ccc;

border:2px solid #777;

border-left-color:#fff;

border-top-color:#fff;

border-spacing:0;

box-shadow:1px 2px 3px #777

}

td{padding:0}

.vis{

background:#ccc;

border-bottom:double #ddd;

border-right:double #ddd;

line-height:1;

padding:5px

}

@media(max-width:360px){

input{

height:25px;

width:25px

} }

В листинге-25 приведена финальная версия CSS-кода файла style.css с упорядоченными правилами оформления элементов документа. Для применения новых CSS-правил необходимо внести изменения в скрипт и HTML-документ, добавив описанные элементы, а также JS-функции для взаимодействия с ними.

Листинг-26. Содержимое файла sudoku.html

<!doctype html>

<html lang="ru">

<head>

<title>Судоку с ИИ</title>

<meta charset="UTF-8">

<meta name="viewport" content="width=device-width, initial-scale=1">

<meta name="description" content="Веб-приложение для игры и обучения детей и взрослых людей…">

<link href="style.css" rel="stylesheet" type="text/css">

<script type="text/javascript" src="script.js"></script>

</head>

<body onLoad="starter()">

<div id="bar">

<div id="adj">

<span class="me0" id="men" onClick="menu()">&#9776;</span>

<h1>Судоку</h1>

</div>

<div id="clock"></div>

</div>

<div class="hid" id="menu"></div>

<div id="game" onClick="mof()">Загрузка…</div>

<div id="ds">

<h2>Описание приложения</h2>

<p><span class="btn">!</span> — запрос подсказки;</p>

<p><span class="btn">&#9658;</span> — новая игра;</p>

<p><span class="btn">&#10003;</span> — проверка решения.</p>

<p>Веб-приложение для игры и обучения детей и взрослых людей!</p>

</div>

<p id="cop">© Мазаяки</p>

</body>

</html>

В листинге-26 приведена финальная версия HTML-кода файла sudoku.html с новыми элементами, необходимыми для внедрения всплывающего меню.

Листинг-27. Содержимое файла script.js

var A=new Array(),C=new Array(),Cr=0,Dy,HN,Me=2,Swi=1,TC,TL=1,Tmr=0,Txt;

for(i=1;i<10;i++){A[i]=new Array()}

A[1][1]=1;A[1][2]=2;A[1][3]=4;A[1][4]=7;A[1][5]=6;A[1][6]=5;A[1][7]=8;A[1][8]=9;A[1][9]=3;

A[2][1]=6;A[2][2]=7;A[2][3]=9;A[2][4]=8;A[2][5]=4;A[2][6]=3;A[2][7]=2;A[2][8]=5;A[2][9]=1;

A[3][1]=3;A[3][2]=8;A[3][3]=5;A[3][4]=2;A[3][5]=1;A[3][6]=9;A[3][7]=7;A[3][8]=4;A[3][9]=6;

A[4][1]=4;A[4][2]=5;A[4][3]=2;A[4][4]=9;A[4][5]=8;A[4][6]=1;A[4][7]=6;A[4][8]=3;A[4][9]=7;

A[5][1]=9;A[5][2]=3;A[5][3]=8;A[5][4]=6;A[5][5]=2;A[5][6]=7;A[5][7]=4;A[5][8]=1;A[5][9]=5;

A[6][1]=7;A[6][2]=6;A[6][3]=1;A[6][4]=5;A[6][5]=3;A[6][6]=4;A[6][7]=9;A[6][8]=2;A[6][9]=8;

A[7][1]=8;A[7][2]=9;A[7][3]=3;A[7][4]=4;A[7][5]=5;A[7][6]=6;A[7][7]=1;A[7][8]=7;A[7][9]=2;

A[8][1]=5;A[8][2]=4;A[8][3]=6;A[8][4]=1;A[8][5]=7;A[8][6]=2;A[8][7]=3;A[8][8]=8;A[8][9]=9;

A[9][1]=2;A[9][2]=1;A[9][3]=7;A[9][4]=3;A[9][5]=9;A[9][6]=8;A[9][7]=5;A[9][8]=6;A[9][9]=4;

C['r']=170;C['g']=170;C['b']=170;

function cbx(){

Swi=(Swi==1)?0:1;

document.getElementById('scr').className=(Swi==0)?'hid':'scr';

document.getElementById('swi').className=(Swi==0)?'off':'on';

document.getElementById('box').className=(Swi==0)?'decThr':'decNon'

}

function chk(){

var err=0,row=0,col=0,s9=0,sc,sr;

for(i=1;i<10;i++){

sc=0;sr=0;

for(j=1;j<10;j++){

sr+=1*document.getElementById('i'+i+j).value;

sc+=1*document.getElementById('i'+j+i).value

}

if(sc!=45||sr!=45){err=1;break}

}

if(err==0){

for(i=1;i<4;i++){

for(j=1;j<4;j++){

s9+=1*document.getElementById('i'+(i+row)+(j+col)).value

}

if(i==3){

if(s9!=45){err=1;break}

s9=0;col+=3;

if(col>6){col=0;row+=3}

if(row<7){i=0}

} } }

Txt=(err==1)?'Эпик-фэйл!':'Судоку решена!';

TC=(err==1)?'b00':'090';TL=1;

if(Swi==0){alert(Txt)}

}

function clr(pt){

switch(pt){

case 1:C['r']=187;C['g']=0;C['b']=0;break;

case 2:C['r']=238;C['g']=170;C['b']=0;break;

case 3:C['r']=221;C['g']=221;C['b']=0;break;

case 4:C['r']=0;C['g']=99;C['b']=0;break;

case 5:C['r']=66;C['g']=221;C['b']=221;break;

case 6:C['r']=0;C['g']=0;C['b']=221;break;

case 7:C['r']=187;C['g']=0;C['b']=187;break;

default:C['r']=170;C['g']=170;C['b']=170

}

document.getElementById('r').value=C['r'];

document.getElementById('g').value=C['g'];

document.getElementById('b').value=C['b'];

custom()

}

function custom(){

C['r']=document.getElementById('r').value;

C['g']=document.getElementById('g').value;

C['b']=document.getElementById('b').value;

document.getElementById('scr').style.backgroundColor='rgb('+C['r']+','+C['g']+','+C['b']+')'

}

function GUI(){

var col,m=new Array(),n,row,ui='<table align="center"><tbody>';

ui+='<tr><td colspan=9><div class="';

ui+=(Swi==1)?'scr':'hid';

ui+='" id="scr">О</div></td></tr>';

HN=0;

for(i=0;i<7;i+=3){

col=tri();

for(j=1;j<10;j++){

for(l=1;l<4;l++){m[l]=A[j][l+i]}

for(l=1;l<4;l++){A[j][l+i]=m[col[l]]}

}

row=tri();

for(j=1;j<10;j++){

for(l=1;l<4;l++){m[l]=A[l+i][j]}

for(l=1;l<4;l++){A[l+i][j]=m[row[l]]}

} }

for(i=1;i<10;i++){

ui+='<tr>';

for(j=1;j<10;j++){

if(j==1){n=rnd(3)}

if(j==4){n=rnd(3)+3}

if(j==7){n=rnd(3)+6}

ui+='<td><input id="i'+i+j+'" maxLength=1 ';

if(j==n){ui+='value="'+A[i][n]+'" disabled'}

else{ui+='onKeyUp="InpChk(this)"'}

ui+='></td>'

}

ui+='</tr>'

}

ui+='<tr><td colspan=3><div class="btn" id="bt1" onClick="hint()">!</div></td><td colspan=3><div class="btn" id="bt2" onClick="GUI()">&#9658;</div></td><td colspan=3><div class="btn" id="bt3" onClick="chk()">&#10003;</div></td></tr></tbody></table>';

document.getElementById('game').innerHTML=ui;

TC='ffffff';Txt='Обыграй ИИ';TL=1;

document.getElementById('scr').style.backgroundColor='rgb('+C['r']+','+C['g']+','+C['b']+')'

}

function hint(){

if(HN<15){

HN++;

var col=new Array(),n=0,row=new Array();

for(i=1;i<10;i++){

for(j=1;j<10;j++){

if(document.getElementById('i'+i+j).value==''){

n++;row[n]=i;col[n]=j

} } }

if(n>0){

if(n>1){n=rnd(n)}

document.getElementById('i'+row[n]+col[n]).value=A[row[n]][col[n]];

document.getElementById('i'+row[n]+col[n]).disabled=1

}else{HN=15}

} }

function inp(el){

var elv=(el.value).trim();

elv=elv.replace(/[^0-9]/g,'');

elv=(elv=='')?0:elv*1;

if(elv>255){elv=255}

el.value=elv;

custom()

}

function InpChk(el){if(isNaN(el.value)||el.value==0){el.value=''}}

function menu(){

if(Me==2){mim()}

Me=(Me==1)?0:1;

document.getElementById('menu').className=(Me==1)?'vis':'hid';

document.getElementById('men').className='me'+Me

}

function mim(){

var bk='<div class="decNon" id="box" onClick="cbx()">';

bk+='<span class="on" id="swi">';

bk+='<span class="flg"></span>';

bk+='</span> Дисплей вкл.</div>';

bk+='<div class="clr">';

bk+='<input id="r" maxLength=3 value='+C['r']+' onKeyUp="inp(this)">';

bk+='<input id="g" maxLength=3 value='+C['g']+' onKeyUp="inp(this)">';

bk+='<input id="b" maxLength=3 value='+C['b']+' onKeyUp="inp(this)">';

bk+='<div class="pat">';

for(i=1;i<8;i++){

bk+='<span class="bg0" id="bg'+i+'" onClick="clr('+i+')"></span>'

}

document.getElementById('menu').innerHTML=bk+'</div></div>'

}

function mof(){if(Me==1){menu()}}

function rnd(hi){return Math.floor(Math.random()*hi)+1}

function starter(){

GUI();timer();

Tmr=setInterval('timer()',1000);

document.getElementById('cop').innerHTML='© '+Dy+' <a href="https://mazayaki.ru/" target="_blank" title="Лайфхаки от Мазаяки">Мазаяки</a>'

}

function timer(){

var dt=new Date(),dh=dt.getHours(),dm=dt.getMinutes(),sp=':';

Dy=dt.getFullYear();

if(dh<10){dh='0'+dh}

if(dm<10){dm='0'+dm}

if(dt.getSeconds()%2==0){sp='<span style="visibility:hidden">'+sp+'</span>'}

document.getElementById('clock').innerHTML=dh+sp+dm;

if(TL>0){txt()}

}

function tri(){

var sw=rnd(6),t=new Array();

switch(sw){

case 1:t[1]=1;t[2]=3;t[3]=2;break;

case 2:t[1]=2;t[2]=1;t[3]=3;break;

case 3:t[1]=2;t[2]=3;t[3]=1;break;

case 5:t[1]=3;t[2]=2;t[3]=1;break;

default:t[1]=3;t[2]=1;t[3]=2

}

return t

}

function txt(){

document.getElementById('scr').innerHTML=Txt.substr(0,TL);

document.getElementById('scr').style.color='#'+TC;

TL=(TL<Txt.length)?TL+1:0

}

В листинге-27 приведена финальная версия JS-кода файла script.js с упорядоченными переменными и функциями, включая новые cbx(), clr(), custom(), inp(), menu(), mim(), mof(), добавленные для внедрения всплывающего меню. Перед использованием новых функций необходимо объявить глобальные переменные C (от англ. Color) для хранения массива со значениями RGB-полей, Me (от англ. Menu) для отслеживания режимов блока со всплывающим меню, Swi (от англ. Switch) для отслеживания положений переключателя.

Функция с именем cbx (от англ. Checkbox) управляет видимым и скрытым режимами дисплея и видом чекбокса. В начале функции cbx() обновляется значение глобальной переменной Swi (0 или 1). Если до вызова функции переключатель находился во включённом положении (Swi==1), в переменную Swi записывается 0, иначе — 1. Затем с помощью методов getElementById() и className переопределяются классы элементов с id #scr, #swi и #box, зависящие от нового значения Swi. Если после выполнения функции переключатель должен быть выключен (Swi==0), дисплею указывается класс .hid для активации скрытого режима, чекбоксу (полю с переключателем) — .off, а блоку с чекбоксом и пояснительным текстом — .decThr (с перечёркнутым текстом). В противном случае дисплею задаётся класс .scr (для применения видимых параметров дисплея), чекбоксу — .on, а блоку с пояснительным текстом — .decNon (без оформления текста).

Функция с именем clr (от англ. Color) отвечает за применение шаблонов цветовой схемы. В качестве аргумента pt (от англ. Pattern) функции передаётся порядковый номер шаблона цветовой схемы, нажатие по которому спровоцировало вызов функции. В теле функции с помощью switch выбираются подходящие значения глобального массива C, зависящие от аргумента pt. Далее с помощью метода getElementById() и свойства value выбранные значения записываются в RGB-поля. Затем вызывается функция custom() для обновления цветовой схемы дисплея.

Функция custom() управляет цветовой схемой дисплея. В начале функции переопределяются значения элементов массива C, которым присваиваются значения RGB-полей с помощью метода getElementById() и свойства value. В конце функции с помощью свойства backgroundColor объекта style устанавливается цвет заднего фона дисплея, заданный в RGB-формате с указанием значений из массива C.

Функция с именем inp (от англ. Input) помогает взаимодействовать с RGB-полями меню, удаляя недопустимые символы после случайного или умышленного ввода. В теле функции объявляется локальная переменная elv (от англ. Element+Value) для хранения и редактирования значения текстового поля, передаваемого через аргумент el. При объявлении в переменную elv записывается значение текстового поля, очищенное от ненужных пробелов с помощью служебной JS-функции trim(), применяемой для обработки строк (если вместо числа в поле окажутся только пробелы, в переменную elv будет записана пустая строка). Затем с помощью служебной JS-функции replace(), также применяемой для обработки строк, перезаписывается значение переменной elv с заменой недопустимых символов на пустую строку. В качестве первого аргумента функции replace() передаётся регулярное выражение с указанием того, что нужно найти и заменить, а в качестве второго аргумента — чем нужно заменить. Как видно из примера, использование регулярок помогает осуществлять (сложный) поиск с заменой при помощи всего одной строчки кода! Причём в данном случае указанное регулярное выражение способно удалить в том числе и пробелы, поэтому предварительное использование функции trim() является избыточным (и приводится исключительно в ознакомительных целях).

В JS регулярные выражения составляются подобно регуляркам языка Perl. Два слэша (косые черты) открывают и закрывают регулярное выражение. Квадратные скобки указывают на то, что выражение помогает найти заданный набор символов. Запись 0-9 задаёт диапазон числовых символов от 0 до 9. Символ «^» перед набором означает отрицание, указывая, что требуется искать не числа. Модификатор g после закрывающего слэша задаёт глобальный формат поиска (без него в RGB-полях будет заменяться только первый найденный символ).

После выполнения поиска и замены по шаблону, указанному в регулярном выражении, переопределяется значение переменной elv. Если переменная elv содержит пустую строку, её значение заменяется на 0. В противном случае в переменную elv записывается её старое значение, умноженное на 1. Такой приём помогает убирать из RGB-полей лишние нули в виде префикса перед числом (если строку '007' умножить на 1, результатом будет число 7). Далее в if проверяется обновлённое значение переменной elv. Если полученное число больше 255, в переменную записывается 255. Затем с помощью свойства value в текстовое поле записывается отредактированное значение elv. Таким образом, в текстовом поле, спровоцировавшем вызов функции, должно оказаться целое число в диапазоне от 0 до 255. В конце функции inp() после проведённой масштабной проверки вызывается функция custom() для обновления цветовой схемы дисплея.

Функция menu() управляет режимами всплывающего меню и видом значка. В начале функции в if проверяется значение глобальной переменной Me, которой при объявлении в начале сценария было присвоено число 2. Если функция запускается впервые (Me==2), вызывается вспомогательная функция mim() для прорисовки элементов меню. Далее обновляется значение Me на 0 или 1. Если до вызова функции всплывающее меню находилось в видимом режиме (Me==1), в переменную Me записывается 0, иначе — 1. Затем с помощью методов getElementById() и className переопределяются классы элементов с id #menu и #men, зависящие от нового значения Me. Если к моменту выполнения группы операторов Me содержит 1, к всплывающему меню будет применён класс .vis, а к значку меню — .me1 (всплывающее меню будет отображено на экране). В противном случае, если в Me содержится 0, к всплывающему меню будет применён класс .hid, а к значку — .me0 (всплывающее меню будет скрыто).

Функция с именем mim (от анг. Menu+Item) отвечает за формирование элементов меню. В теле функции mim() объявляется локальная переменная bk (от англ. Block) для формирования строки с HTML-кодом и обновления содержимого блока с id="menu". Изначально в bk записывается открывающий тег <div> для чекбокса с указанием трёх атрибутов: класса .decNon, id #box и обработчика события onClick для вызова функции cbx(). Следом к bk добавляется вложенный открывающий тег <span> (поле переключателя) с указанием класса .on и id #swi. Далее к переменной bk добавляется контейнер <span></span> с классом .flg (флажок-переключатель). Затем к bk добавляются тег </span>, закрывающий поле переключателя, пояснительный текст «Дисплей вкл.» и тег </div>, закрывающий блок чекбокса.

После этого к переменной bk добавляется открывающий тег <div> (блок управления цветовой схемой дисплея) с классом .clr. Далее к значению bk добавляются три тега <input> (RGB-поля) с указанием id (#r, #g, #b), максимальной длины в 3 символа (maxLength), значений массива C (value) и обработчиков события onKeyUp, вызывающих функцию inp() после каждого ввода новых значений. Следом к bk добавляется открывающий тег <div> (блок с шаблонами цветовой схемы) с классом .pat. Затем в цикле for к переменной bk добавляются 7 контейнеров <span></span> (шаблонов) с общим классом .bg0, id #bg1–7 и обработчиками события onClick, вызывающими функцию clr() при каждом нажатии по шаблону. После этого с помощью методов getElementById() и innerHTML меняется содержимое элемента с id="menu", вместо которого записывается содержимое переменной bk с добавлением двух тегов </div>, закрывающих блок с шаблонами и общий блок управления цветовой схемой.

Функция с именем mof (от анг. Menu+Off) состоит всего из одной строчки кода, вызывающей функцию menu() для сокрытия меню в случае, если в глобальной переменной Me содержится 1. Таким образом, в документе предусматривается два способа сокрытия всплывающего меню: через повторное нажатие на значок меню либо через нажатие на игровое поле. При желании, чтобы функция mof() вызывалась ещё и после нажатия по блоку с описанием, внутри тега <div id="ds"> также следует добавить обработчик события onClick="mof()"!

Заключение

Веб-программирование представляет собой запись инструкций, сохраняемых в текстовых файлах, с помощью которых браузерам сообщаются правила обработки контента. Традиционно в HTML-документах на сайтах присутствуют главный заголовок, текстовое описание и мультимедиа-контент, подключаемые с помощью специальных тегов. Управлять оформлением элементов веб-документа можно с помощью внешнего файла с CSS-правилами. При этом важно помнить, что на вкус и на цвет товарищей нет! Поэтому крайне сложно создать удобный интерфейс, который понравится абсолютно всем. Но можно предусмотреть наличие личного кабинета или меню для кастомизации настроек стилей оформления, позволяющих в дальнейшем анализировать предпочтения пользователей. Таким образом, можно накопить пользовательский опыт (UX), способный помочь экспериментальным путём определить оптимальные параметры для интерфейса веб-документа. Для внедрения в документ веб-приложения можно использовать внешний JS-сценарий с операторами, которые должны выполняться при наступлении определённых событий (триггеров). Мудрые люди говорят, что слово — серебро, а молчание — золото! И если не планируется, что слова могут изменить тишину к лучшему, резонно промолчать! Программирование строится по схожим принципам: если конкретная строка не делает код лучше, возможно, без этого фрагмента можно обойтись! В ряде случаев простое решение может оказаться самым успешным…

Навигационная карта

Ознакомиться с другими опциями IT-проекта вам поможет навигационная карта с подробным списком веб-сервисов и приложений! Если вы активно пользуетесь социальными медиа, не забудьте рассказать об этом проекте друзьям и подписаться на группы Мазаяки Проджект в ВКонтакте и Одноклассниках, чтобы быть в курсе важных обновлений! Сайонара-сайонара!

© 2021 Mazayaki Project