Запуск приложения Node.js в фоновом режиме

👁 143 просмотров

Допустим, мы создали приложение Node.js и хотим, чтобы при выходе из консоли после запуска через npm start/node index.js через команду Ctrl + C или из ssh — клиента наше приложение продолжало выполняться, как нормальный сервер. Для этого есть несколько способов, чтобы это решить.

Запуск в фоновом режиме при помощи nohup

Традиционный подход заключается в том, чтобы запустить команду, используя nohup:

$ nohup node simple-server.js> output.log &

или

$ nohup npm start> output.log &

Эта строчка говорит о том, чтобы мы запустили приложение Node.js в фоновом режиме, а все логи записывались в файл output.log.

Использование модуля Forever

Цель Forever — постоянно поддерживать дочерний процесс (например, веб-сервер node.js) и автоматически перезапускать его при неожиданном выходе из него.

Установить данный модуль можно следующей командой:

$ [sudo] npm install forever

Самые простые команды для использования данного модуля — это четыре простые задачи командной строки: start, stop, stopall, list:

usage: forever [start | stop | stopall | list] [options] SCRIPT [script options]

options:
  start          start SCRIPT as a daemon
  stop           stop the daemon SCRIPT
  stopall        stop all running forever scripts
  list           list all running forever scripts

Чтобы использовать Forever, как долговременный процесс, нужно запустить процесс через команду «start». Имея эти задачи, запуск сценария nodejs с Forever прост:

$ forever start simple-server.js 
$ forever list 
  [0] simple-server.js [24597, 24596]

Первая команда запускает скрипт simple-server.js в фоновом режиме с помощью daemon.node и возвращает управление текущему процессу оболочки. Вторая команда перечисляет все процессы, выполняемые с Forever. Идентификаторами после имени сценария являются идентификаторы процесса целевого скрипта и демона, которые будут запущены всегда, пока работает сервер. Мы можем подтвердить это, просмотрев список процессов:

$ ps axl | grep node 
  501 24596 1 0 31 0 Ss ?? 0: 00.03 node / usr / local / bin / forever start simple-server.js 
  501 24597 24412 0 31 0 S ?? 0: 00.07 узел simple-server.js

Чтобы проиллюстрировать, что Forever автоматически перезапустит дочерний процесс, который, каким-либо образом был закрыт в процессе перезагрузки или ошибки. Для этого давайте принудительно убьем запущенный процесс и проверим перезапустит ли Forever его:

$ kill 24597 
$ forever list 
  [0] simple-server.js [24611, 24596]

Как вы видите, идентификатор процесса целевого скрипта «simple-server.js» изменился с 24597 до 24611, что указывает на то, что новый процесс был запущен Forever. Таким образом, наш целевой скрипт будет работать непрерывно, но как мы его остановим? Остановка процесса с Forever проста из командной строки. Нам просто нужно передать индекс этого процесса из списка «вечный список» на остановку:

$ forever stop 0 
Forever остановил процесс: 
  [0] simple-server.js [24611, 24596]

Существуют некоторые параметры по умолчанию и соглашения о конфигурации, о которых вам следует знать при использовании Forever:

  1. Forever отслеживает запущенные процессы в файлах * .fvr, которые помещаются в /tmp/forever/pids;
  2. Каждый процесс Forever будет генерировать уникальный файл журнала, помещенный в /tmp/forever/*.log;
  3. Если не указано иное, вывод дочернего процесса stdout и stderr будет записан в указанный выше файл журнала.

Подробнее об этих параметрах можно узнать из использования Forever и документации по Github .

Использование модуля PM2

Последняя стабильная версия PM2 устанавливается через NPM:

npm install pm2@latest -g

Самый простой способ запустить, демонзировать и контролировать ваше приложение — это использовать эту командную строку:

pm2 start app.js

Можно, также, декларировать управление несколькими приложениями, для этого нужно создать файл конфигурации со следующим содержимым:

apps:
  - script   : app.js
    instances: 4
    exec_mode: cluster
  - script : worker.js
    watch  : true
    env    :
      NODE_ENV: development
    env_production:
      NODE_ENV: production

И далее очень просто их запускаем:

pm2 start process.yml

Подробнее об объявлении приложения читайте здесь .

Перезапуск PM2 с процессами, которые вы выполняете при загрузке / перезагрузке сервера, имеет решающее значение. Чтобы решить эту проблему, просто запустите эту команду для создания активного сценария запуска:

pm2 startup

Дополнительно тут.

Вот некоторые команды, которые стоит знать:

# Fork mode
pm2 start app.js --name my-api # Name process

# Cluster mode
pm2 start app.js -i 0        # Will start maximum processes with LB depending on available CPUs
pm2 start app.js -i max      # Same as above, but deprecated.

# Listing

pm2 list               # Display all processes status
pm2 jlist              # Print process list in raw JSON
pm2 prettylist         # Print process list in beautified JSON

pm2 describe 0         # Display all informations about a specific process

pm2 monit              # Monitor all processes

# Logs

pm2 logs [--raw]       # Display all processes logs in streaming
pm2 flush              # Empty all log files
pm2 reloadLogs         # Reload all logs

# Actions

pm2 stop all           # Stop all processes
pm2 restart all        # Restart all processes

pm2 reload all         # Will 0s downtime reload (for NETWORKED apps)

pm2 stop 0             # Stop specific process id
pm2 restart 0          # Restart specific process id

pm2 delete 0           # Will remove process from pm2 list
pm2 delete all         # Will remove all processes from pm2 list

# Misc

pm2 reset <process>    # Reset meta data (restarted time...)
pm2 updatePM2          # Update in memory pm2
pm2 ping               # Ensure pm2 daemon has been launched
pm2 sendSignal SIGUSR2 my-app # Send system signal to script
pm2 start app.js --no-daemon
pm2 start app.js --no-vizion
pm2 start app.js --no-autorestart

 

Пожалуй, основные из вышеизложенного списка:

pm2 start app.js           # Start app.js

pm2 start app.js -- -a 23  # Pass arguments '-a 23' argument to app.js script

pm2 start app.js --name serverone # Start a process and name it as serverone
                                    # you can now stop the process by doing
                                    # pm2 stop serverone

pm2 start app.js --node-args="--debug=7001" # --node-args to pass options to node V8

pm2 start app.js -i 0             # Start maximum processes depending on available CPUs (cluster mode)

pm2 start app.js --log-date-format "YYYY-MM-DD HH:mm Z"    # Log will be prefixed with custom time format

pm2 start app.json                # Start processes with options declared in app.json
                                    # Go to chapter Multi process JSON declaration for more

pm2 start app.js -e err.log -o out.log  # Start and specify error and out log

 

Если используется фреймворк express, то команда запуска приложения в демон-режиме будет выглядеть так:

 pm2 start bin/www  -i 0 --name "appName"

или просто, без дополнительного мониторинга:

pm2 start bin/www

Базовые команды:

 Basic Examples:

    Start an app using all CPUs available + set a name :
    $ pm2 start app.js -i 0 --name "api"

    Restart the previous app launched, by name :
    $ pm2 restart api

    Stop the app :
    $ pm2 stop api

    Restart the app that is stopped :
    $ pm2 restart api

    Remove the app from the process list :
    $ pm2 delete api

    Kill daemon pm2 :
    $ pm2 kill

    Update pm2 :
    $ npm install pm2@latest -g ; pm2 update

    More examples in https://github.com/Unitech/pm2#usagefeatures

  Deployment help:

    $ pm2 deploy help

 

Создание приложения Node.js при помощи фреймворка express.js

👁 57 просмотров

Рассмотрим пример создания приложения Node.js на основе популярного фреймворка express.js

Установка express.js

Для установки достаточно набрать команду:

$ npm install -g express --save

Установка генератора приложений Express

Для быстрого создания «скелета» приложения используется инструмент для генерации приложений express. Установите express с помощью следующей команды:

$ npm install -g express-generator --save

Для просмотра опций команды воспользуйтесь опцией -h:

$ express -h

  Usage: express [options][dir]

  Options:

    -h, --help          output usage information
        --version       output the version number
    -e, --ejs           add ejs engine support
        --hbs           add handlebars engine support
        --pug           add pug engine support
    -H, --hogan         add hogan.js engine support
    -v, --view <engine> add view <engine> support (ejs|hbs|hjs|jade|pug|twig|vash) (defaults to jade)
    -c, --css <engine>  add stylesheet <engine> support (less|stylus|compass|sass) (defaults to plain css)
        --git           add .gitignore
    -f, --force         force on non-empty directory

 

Создание приложения express.js

Например, следующая команда создает приложение Express с именем myapp в текущем рабочем каталоге:

$ express --view=pug myapp

   create : myapp
   create : myapp/package.json
   create : myapp/app.js
   create : myapp/public
   create : myapp/public/javascripts
   create : myapp/public/images
   create : myapp/routes
   create : myapp/routes/index.js
   create : myapp/routes/users.js
   create : myapp/public/stylesheets
   create : myapp/public/stylesheets/style.css
   create : myapp/views
   create : myapp/views/index.pug
   create : myapp/views/layout.pug
   create : myapp/views/error.pug
   create : myapp/bin
   create : myapp/bin/www

Затем установите зависимости:

$ cd myapp
$ npm install

В MacOS или Linux запустите приложение с помощью следующей команды:

$ DEBUG=myapp:* npm start

В Windows используется следующая команда:

> set DEBUG=myapp:* & npm start

Затем откройте страницу http://localhost:3000/ в браузере для доступа к приложению.

Структура каталогов сгенерированного приложения выглядит следующим образом:

.
├── app.js
├── bin
│   └── www
├── package.json
├── public
│   ├── images
│   ├── javascripts
│   └── stylesheets
│       └── style.css
├── routes
│   ├── index.js
│   └── users.js
└── views
    ├── error.pug
    ├── index.pug
    └── layout.pug

7 directories, 9 files

 

Клиентская часть организации игры «Рулетка»

👁 34 просмотров

Это краткое описание того, как разработать интерфейс игры «Рулетка» или Programming Roulette Game Principle. В посте представлена принципиальная схема и код рабочей программы.

Схема работы

Для лучшего понимания хорошо бы рассмотреть схему того, что мы хотим проектировать

Опишем все параметры, которые участвуют в процессе:

  • ni — исходный порядковый номер выигранной карты;
  • m — число карт в видимой части галереи;
  • gW — длина галереи;
  • W — длина галереи, вычисленная исходя из помещающихся колод в видимой части;
  • xW — длина колоды карт до выигранной карты;
  • dW — разница между длиной до выигранной и длиной галереи;
  • alignLeft — искомый параметр для перемещения, чтобы выигранная карта встала по центру.

Рулетка у нас будет иметь один входной параметр и один выходной параметр, т.е., выходной зависит от входного:

  • ni — номер выигранной карты в списке;
  • alignLeft — это то, насколько надо передвинуть весь список, чтобы выигранная карта оказалась в центре рулетки.

Примечание. ni — обычно генерируется на сервере и передается всем пользователям через сокет-сообщение. Генерацию получают случайными числами из всего списка. К примеру, есть массив всех карт и мы вычисляем общее количество карт в массиве и генерируем случайное число от 0 до количества карт. Серверную часть мы не рассматриваем, поэтому ограничимся этим кратким описанием.

alignLeft — это величина css — параметра align-left первой карты. По сути дела мы определяем с серверной части номер выигранной карты из всего списка и в клиентской части при помощи JS мы визуально показываем, что именно карта и выиграна, хотя, в необходимости такой бы не было, можно было бы просто номер выигранной карты показать, но тогда смысл назвать весь этот процесс игры «Рулеткой» потерялся бы.

Исходный код рулетки

Код HTML

<!DOCTYPE html>
<title>Roulette</title>
<body>
  <div id="container">
    <div class="cardList">
      <div class="card">1</div>
      <div class="card">14</div>
      <div class="card">2</div>
      <div class="card">13</div>
      <div class="card">3</div>
      <div class="card">12</div>
      <div class="card">4</div>
      <div class="card">0</div>
      <div class="card">11</div>
      <div class="card">5</div>
      <div class="card">10</div>
      <div class="card">6</div>
      <div class="card">9</div>
      <div class="card">7</div>
      <div class="card">8</div>
      <div class="card">1</div>
      <div class="card">14</div>
      <div class="card">2</div>
      <div class="card">13</div>
      <div class="card">3</div>
      <div class="card">12</div>
      <div class="card">4</div>
      <div class="card">0</div>
      <div class="card">11</div>
      <div class="card">5</div>
      <div class="card winner">10</div>
      <div class="card">6</div>
      <div class="card">9</div>
      <div class="card">7</div>
      <div class="card">8</div>
    </div>
    <div class="bts">
      <span>Balance:</span>
      <span>*</span>
      <button id="spin" class="cooldown">Spin</button>
    </div>
    <span id="timer"></span>
  </div>
</body>

Код JavaScript

var cardList = $('.cardList');
var cards = $('.card');
var cardWidth = $(cards).width();
var cardListWidth = $(cardList).width();
var winnerCard = ".winner"; //Класс, выигранной карты
var winner = null;
var speed = 3000;
var winnerNumber = null;
/**Вычислим номер с эти классом или можно без класса сразу указать номер*/
$.each(cards, function(i, value) {
  var isWinner = $(this).hasClass("winner");
  if (isWinner) {
    winner = $(this);
    winnerNumber = (i + 1);
  }
});
console.log("winNumber:" + winnerNumber + " winner" + winner.html());
//ni - порядковый номер выигранной карты
//m - число карт в видимой части галереи
//gW - длина галереи
//xW - длина колоды карт до выигранной карты
//dW - разница между длиной до выигранной и длиной галереи
var ni = winnerNumber;
//
var m = cardListWidth / cardWidth; //Math.floor(cardListWidth/cardWidth);
var gW = m * cardWidth; //
var sW = gW / 2;
var sw = cardWidth / 2
var xW = ni * cardWidth;
var dW = xW - gW + sW + sw; //
//
var leftAlign = dW;

$('#spin').click(function() {
  cards.first().css('margin-left', 0);
  cards.first().animate({
    "margin-left": -leftAlign + "px"
  }, speed);
  return false;
});

Код CSS

* {
  box-sizing: border-box;
  padding: 0;
  margin: 0;
}

.card:nth-child(odd) {
  background: red;
}

.card:nth-child(even) {
  background: blue;
}

.cardList {
  height: 100px;
  width: 100%;
  position: relative;
  margin: 10px;
  overflow: hidden;
  white-space: nowrap;
}

.card {
  display: inline-block;
  text-align: center;
  height: 100px;
  width: 100px;
  line-height: 100px;
  font-family: monospace;
  font-size: 2em;
  color: #fff;
  margin: 0 auto;
}

#container {
  text-align: center;
  background: #f7f7f7;
}

.cardcontainer {}

.cardList::before,
.cardList::after {
  content: '';
  display: block;
  z-index: 100;
  width: 0px;
  height: 0px;
  transform: translateX(-50%);
  border-left: 8px solid transparent;
  border-right: 8px solid transparent;
}

.cardList::before {
  position: absolute;
  top: 0px;
  left: 50%;
  border-top: 12px solid #c3c3c3;
}

.cardList::after {
  position: absolute;
  bottom: 0px;
  left: 50%;
  border-bottom: 12px solid #c3c3c3;
}

.cardList {
  margin: 0 auto;
}

button {
  margin: 10px;
  padding: 8px;
  font-family: monospace;
}

В коде мы указываем для выигришной карты класс winner и дальше в списке мы ищем порядковый номер карты с этим классом, но можно было бы указать сразу номер — тут как программу составить. В результате работы данного кода, при нажатии на кнопку рулетка переместится так, что карта, помеченная, как побежденная будет в центре между стрелками, что покажет пользователю, что она выиграла. Результат показан ниже на картинке

 

Полностью код можно скачать из репозитория проекта на GitHub.

Отправка AJAX — запросов к сервлету Java

👁 312 просмотров

Для взаимодействия пользователя серверной частью очень часто нужно использовать запросы, которые позволяют отправлять и принимать данные без обновления текущей страницы иначе говоря асинхронные AJAX — запросы. В сегодняшнем примере покажем, как сделать запрос со страницы пользователя на сервер, где у нас стоит сервер Tomcat 8, а серевер будет асинхронно отвечать пользователю.

Для того, чтобы отправить ассинхронные запросы можно воспользоваться еще и апплетами, которые будут грузиться в HTML — код на стороне клиента, но минусом такого подхода является необходимость дополнительно устанавливать клиенту плагины для браузера, а иногда, этот плагин, из-за несовметимости или блокировки будет не работать и нам эта возня, как и клиенту вообще не нужно…

Что потребуется? Потребуется:

  • Пользовательская HTML — страница, на котором работает AJAX — код через jQuery;
  • Сервер, на котором установлен Tomcat … 7 — 8 … или любой другой, поддерживающий сервлеты;
  • Любимая Java — IDE(NetBeans, Eclipse, IntelliJ IDEA, …).

Постановка задачи

Допустим есть страница index.html, на котором есть формы ввода пароля и логина. Наша задача:

  • асинхронно отправить со стороны клиента index.html данные вводимого пользователя на сторону сервера через AJAX;
  • обработать эти данные(проверить наличие в БД, проверить на допустимость символов, на количество символов и т.д.) через сервлет AuthServlet.java;
  • асинхронно отправить данные о статусе входа со стороны сервера на сторону клиента через тот же AJAX.

Практика

HTML — страница, которая показывается клиенту состоит из пару полей логина и пароля и одной кнопки

auth-forms-of-post-servlet-ajax-login

Ниже представлен код клиентской страницы auth.html

<!DOCTYPE html>
<html>
    <head>
        <title>Авторизация через через AJAX-JAVA</title>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
        <script type="text/javascript" src="jquery-1.11.1.min.js"></script>
        <script type="text/javascript" src="script.js"></script>
        <style>
            body{
                background-color: azure;
                text-align: center;
                font-family: "Lucida Sans Unicode"
            }
            .auth-forms{
                width: 30%;
                margin: auto;
                background-color: background;
                text-align: center;
                margin-top: 200px;
            }
            .auth-forms input, button{
                margin: 10px;
                height: 30px;
                padding: 3px;
                border: none;
                border-radius: 2px;
            }
            #auth-info{
                margin: 5px;
                height: 50px;
                text-align: center;
                padding: 5px;
            }
        </style>
    </head>
    <body>
        <div>
            <form class="auth-forms">
                <div id="auth-info"></div>
                <input type="text" placeholder="Логин" id="login"/>
                <br/>
                <input type="password" placeholder="Пароль" id="password"/>
                <br/>
            </form>
            <br/>
            <button id="button">Вход</button>
        </div>
    </body>
</html>

Код для обработки событий на стороне клиента в виде файла JavaScript — script.js

$(document).ready(function(){
    $("#button").click(function(){
        var data = {};
        data = {"login":$("#login").val(), "password":$("#password").val()};
        //
        $.ajax
        ({
            type: "POST",//Метод передачи
            data: data,//Передаваемые данные в JSON - формате
            url: 'AuthServlet',//Название сервлета
            success:function(serverData)//Если запрос удачен
            {
                $("#auth-info").css({"background-color":serverData.backgroundColor, "height": "50px", "color":"white"});
                $("#auth-info").html(serverData.serverInfo);
            },
            error: function(e)//Если запрос не удачен
            {
                $("#auth-info").css({"background-color":"#CC6666", "height": "50px", "color":"white"});
                $("#auth-info").html("Запрос не удался!");
            }
        });
    });
});

Код для проверки и переотправки информационных сообщений о статусе авторизации представлен сервлетом AuthServlet.java

package ru.websofter.smilerado;

import java.io.IOException;
import java.io.PrintWriter;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.json.JSONException;
import org.json.JSONObject;

/**
 *
 * @author WebSofter
 */
@WebServlet(name = "AuthServlet", urlPatterns = {"/AuthServlet"})
public class AuthServlet extends HttpServlet {
    protected void processRequest(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException, JSONException {
        response.setContentType("application/json");//Отправляем от сервера данные в JSON -формате
        response.setCharacterEncoding("utf-8");//Кодировка отправляемых данных
        try (PrintWriter out = response.getWriter()) {
            JSONObject jsonEnt = new JSONObject();
            if(request.getParameter("login").equals("myLogin")&&request.getParameter("password").equals("myPassword"))
            {
                jsonEnt.put("backgroundColor","#99CC66");
                jsonEnt.put("serverInfo", "Вы вошли!");
            }else
            {
                jsonEnt.put("backgroundColor","#CC6666");
                jsonEnt.put("serverInfo", "Введен неправильный логин или пароль!"); 
            }
            out.print(jsonEnt.toString());
        }
    }
    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        try {
            processRequest(request, response);
        } catch (JSONException ex) {
            Logger.getLogger(AuthenticationServlet.class.getName()).log(Level.SEVERE, null, ex);
        }
    }
    @Override
    protected void doPost(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        try {
            processRequest(request, response);
        } catch (JSONException ex) {
            Logger.getLogger(AuthenticationServlet.class.getName()).log(Level.SEVERE, null, ex);
        }
    }
    @Override
    public String getServletInfo() {
        return "Short description";
    }// 

}

После ввода логина и пароля сервер проверяет на наличие и соответствие и, в соответствии со статусом авторизации, выдает сообщение либо о  правильности ввода или об ошибочности

auth-forms-of-post-servlet-ajax-login-ok-info

 

auth-forms-of-post-servlet-ajax-login-no-ok-info

Вызов кода C# из JavaScript

👁 94 просмотров

При разработке JavaScript — приложений на платформе .NET очень часто, даже не очень, а требуется использовать JavaScript — функции вызова кода C#. Так как JavaScript не умеет пользоваться файловой системой не может работать с БД, то нам необходим код C#, который подгрузит данные из файла, запишет в файл, почитает и перезапишет данные в БД, обработает методами .NET данные и т.д.
Есть 2 похода того, как это сделать:

  • использовать специальный метод JS window.external, который будет содержать метод, определяемый в классе COM — объекта;
  • использовать ASP.NET страницы или любого другого серверного языка и подгрузить их через Ajax — запросы.

В данном посте рассмотрим первый вариант. Ниже представлен класс, который мы определяем , как видимый COM — объект

 

namespace MyApp
{
    [ComVisible(true)] //Устанавливаем как видимый COM
    public class ScriptClass
    {
        public ScriptClass()
        {
        }
        //Данный метод будем вызывать из JS
        public void readFile()
        {
            MessageBox.Show("File Reading!");
        }
    }
}

Далее нужно установить параметры для браузера, который мы используем в проекте

WebBrowser webBrowser = new WebBrowser(); 
webBrowser.ObjectForScripting = new ScriptClass(); 
webBrowser.Navigate("http://websofter.ru/");

Теперь, чтобы запустить данный код из JS, нам надо написать в обработчике такой код

window.external.readFile();

Использование JavaScript в WebBrowser .NET/C#

👁 229 просмотров

При использовании JavaScript в .NET объекте WebBrowser нужно учитывать некоторые ньюансы, связанные с загрузкой страницы. Для того, чтобы выполнить какой-то JavaScript — код необходимо проверить загрузку страницы. Простое решение — это использование в C# специального слушателя события — WebBrowser.DocumentCompleted, который вызывается, когда страница полностью загружена.

Пример вызовава JavaScript — кода из C#

Ниже дан пример его использования. Пусть у нас есть HTML сраница index.html со следующим JavaScript — методом, который определен в head

<script>
	function runScript(){
		alert("Hello World!");
	}
</script>

Тогда вызов данного метода через C# будет выглядеть вот так

public MyForm()
{
//...
//Сначала определяем новое событие для браузера и передаем имя обработчика
            string appDir = Path.GetDirectoryName(Assembly.GetExecutingAssembly().GetName().CodeBase);
            webBrowser.Url = new Uri(Path.Combine(appDir, @"pages\index.html"));
  webBrowser.DocumentCompleted += new WebBrowserDocumentCompletedEventHandler(runWebScripts);
}

//Далее, в классе определяем новый обработчик
void runWebScripts(object sender, WebBrowserDocumentCompletedEventArgs e)
{
   webBrowser.Document.InvokeScript("runScript", new object[] { });
}

 Пример создания JavaScript — кода и его запуска из C#

Теперь посмотрим как создать новый элемент в DOM через C# и создать в нем код JavaScript

public MyForm()
{
//...
//Сначала определяем новое событие для браузера и передаем имя обработчика
            string appDir = Path.GetDirectoryName(Assembly.GetExecutingAssembly().GetName().CodeBase);
            webBrowser.Url = new Uri(Path.Combine(appDir, @"pages\index.html"));
  webBrowser.DocumentCompleted += new WebBrowserDocumentCompletedEventHandler(runWebScripts);
}

//Далее, в классе определяем новый обработчик
void runWebScripts(object sender, WebBrowserDocumentCompletedEventArgs e)
{

            HtmlDocument doc = webBrowser.Document;
            HtmlElement head = doc.GetElementsByTagName("head")[0];
            HtmlElement s = doc.CreateElement("script");
            s.SetAttribute("text", "function sayHello() { alert('hello'); }");
            head.AppendChild(s);
            webBrowser.Document.InvokeScript("sayHello");

}

Мы добаляем в тег head новый элемент script и устанавливаем в качестве его атрибута text JavaScript — код и далее вызываем этот код с помощью метода InvokeScript

Подключение jQuery в WebBrowser через C# в виде гиперссылки

Давайте посмотрим, как использовать библиотеку jQuery в своем проекте на C#. Для этого нам необходимо создать новый элемент script и передать в атрибут src значение ссылки, для примера подключим библиотеку из стандартной ссылки Google

public MyForm()
{
//...
//Сначала определяем новое событие для браузера и передаем имя обработчика
            string appDir = Path.GetDirectoryName(Assembly.GetExecutingAssembly().GetName().CodeBase);
            webBrowser.Url = new Uri(Path.Combine(appDir, @"pages\index.html"));
  webBrowser.DocumentCompleted += new WebBrowserDocumentCompletedEventHandler(runWebScripts);
}
//Далее, в классе определяем новый обработчик
void runWebScripts(object sender, WebBrowserDocumentCompletedEventArgs e)
{
            //Получаем доступ к тегу head
            HtmlDocument doc = webBrowser.Document;
            HtmlElement head = doc.GetElementsByTagName("head")[0];

            //Подключаем библиотеку
            HtmlElement jQuery = doc.CreateElement("script");
            jQuery.SetAttribute("src", "http://code.jquery.com/jquery-latest.min.js");
            head.AppendChild(jQuery);
            
            //Далее запускаем код, содержащий jQuery
            HtmlElement script = doc.CreateElement("script");
            script.SetAttribute("text", "function setRedColor() { $('body').css({ 'background-color': 'red' }); }");
            head.AppendChild(script);

            //Вызываем метод, содержащий код jQuery
            webBrowser.Document.InvokeScript("setRedColor");

}

Подобным образом можно подключить не только jQuery библиотеку, но и любой удаленный JavaScript файл.

Подключение jQuery в WebBrowser через C# из локального хранилища

Если нам необходимо подгрузить jQuery или любой JavaScript — файл локально, то тут необходимо учитывать некоторые особенности, связанные с jQuery, так как при локальном подключении у вас может выскочить ошибка такого рода: Object doesn’t support property or method ‘defineProperty’, который означает: Объект не поддерживает свойство или метод ‘defineProperty’. Что непонятно, так это то, что при подключении через удаленную гиперссылку эта ошибка не выводиться и браузер работает нормально. Данная ошибка актуальная для версий IE 10 — 11 и выше, возможно. В IE он поддерживается, а в IE 8 — ограниченно. Решением данной проблемы является использование тегов для переключения браузера в режимы совместимости с ранними версиями. Подробно про это написано в официальной статье Microsoft «Specifying legacy document modes»

Из данной статьи следует, что для совместимости нужно использовать мета — тег

<meta http-equiv="x-ua-compatible" content="IE=edge" />

Запись атрибута content дает указание на то, с какой версией IE следует иметь совместимость. В нашем случае, данная запись эквивалентна использованию типа документа HTML5 и совместимости с другими браузерами. Это заставит Internet Explorer работать в режиме последней поддерживаемой спецификации документа. Значение «IE=edge» является наиболее полезным для регулярного поддерживания веб-сайтов, которые отрабатываются на совместимость между несколькими браузерами, в том числе Internet Explorer.
Данный тег совместимости необходимо в начале тега head в документе.

Итак, теперь, собственно сам код подключения. HTML документ будет таким

 

...
<head>
    <meta http-equiv="x-ua-compatible" content="IE=edge" />
</head>
...

C# — код будет таким

HtmlDocument doc = wbTestEditor.Document;
HtmlElement head = doc.GetElementsByTagName("head")[0];
HtmlElement s = doc.CreateElement("script");
s.SetAttribute("src", Environment.CurrentDirectory + @"\pages\js\jquery-2.1.3.min.js");
head.AppendChild(s);

Свойство Environment.CurrentDirectory содержит корневую папку запускаемой программы, а @ нужен для обработки слэшей в нормальном виде, чтобы не писать как \\. Environment.CurrentDirectory — это тоже самое, если бы мы писали как Path.GetDirectoryName(Assembly.GetExecutingAssembly().GetName().CodeBase), но в коротком виде.

LangZilla — интерактивный переводчик для сайта

👁 56 просмотров

Каждому разработчику сайта приходилось иметь дело с локализацией сайта, чтобы каждый пользователь, когда заходит на страницу, сайт автоматически перенаправлял его на ту версию языка, на котором привык общаться клиент. Есть много способов это сделать:

  • можно сделать копию сайта в разных папках www.domen.com/ru и www.domen.com/en и т.д.;
  • можно использовать api переводчика от Google Translate;
  • можно использовать код на JavaScript и определять страну и язык самостоятельно при помощи свойства window.navigator.languages на JavaScript;
  • наконец, можно использовать готовое и быстрое решение — LangZilla.

Описание

LangZilla — это бесплатный JavaScript автопереводчик сайтов, с возможностью расширять языковую базу при помощи языковых файлов.

Что может LangZilla? Вот ее функции:

  • автоматически определять страну клиента и дает ему его перевод;
  • возможность определять любой язык при помощи json — файлов;
  • возможность переводить внутри тегов HTML;
  • возможность переводить слова в атрибутах тегов;
  • возможность перевести часть текста внутри до содержимого в теге;
  • возможность перевести текст после содержимого в теге;
  • возможность запустить пользовательский кода, определив функцию обратного вызова;
  • возможность использовать в переводе теги HTML;
  • возможность определять собственные пути к картам перевода и определять для них иконки.

Использование

Чтобы быстро использовать плагин, необходимо скачать архив, в папке src будут все исходные файлы плагина:

  • папка loc содержит все файлы перевода, которые вы захотите добавить. Причем, имя файла должно соответствовать коду языка;
  • папка images содержит ресурсы плагина, такие как иконки языков и т.д.;
  • файл langzilla.js — основной файл плагина, его необходимо подключить к нашей странице, где требуется перевод.

Итак, давайте поподробнее. После разархивирования в корневую папку сайта подключаем  файлы jQuery и langzilla.js к той странице, на которой требуется перевод

 


<script src="js/jquery-2.1.4.min.js"></script>
<script src="src/langzilla.js"></script>

Далее нам необходимо создать карты языка в формате .json и переместить их в папке loc, откуда их будет читать наш плагин перевода. Название карты языка должно соответствовать кодовому определению языка в формате, который определен для свойства window.navigator.languages, данный формат языков для браузеров опеределен по стандарту RFC 4646. Примеры валидных языковых кодов включают «en», «EN-US», «FR«, «es-ES», «ru-RU» и т.д. Некоторые можно посмотреть здесь. После того, как языковую карту создали и дали ему валидное имя, соответствующее кодовому названию языка, нам необходимо написать код запуска с регистрированными языковыми картами. Простейший код будет выглядеть вот так:

jQuery(function(){
    $("body").createLang({//определяем тег, в которую добавятся кнокпки
        locals:{
            "en-US":"src/images/en.png",//подключаем и регистрируем для языка иконку кнопки
            "ru-RU":"src/images/ru.png"//подключаем и регистрируем для языка иконку кнопки
        },
        locPath: "src/loc/"//указываем путь к папке языковых карт
    });
});

Теперь нам необходимо заполнить языковые карты. Допустим мы создали языковые карты ru-RU.json для русского языка и en-US.json — для английского языка. Запись в языковых картах поизводиться в формате JSON, ниже представлены примеры заполнения для русского

{
    "lzTitle": "LangZilla|Русский",
    "lzMenuHome": "Главная",
    "lzMenuBlog": "Блог",
    "lzMenuGallery": "Галерея",
    "lzMenuAbout": "О нас",
    "lzMenuContact": "Контакты",
    "lzMenuItem1": "Проектируем для WEB и дизайн",
    "lzMenuItem2": "Проектируем для гаджетов",
    "lzMenuItem3": "Проектируем для ПК",
    "lzMenuItem4": "Проектируем для микроэлектроники",
    "lzTextAboutAppend": "Вы можете использовать и изменять наш плагин для личного и коммерческого использования.[:]append",
    "lzTextAboutPrepend": "Бесплатный плагин перевода от [:]prepend",
    "lzImageEnRU": "images/langzilla-ru.png[:]attr=src",
    "lzHeaderPopularPost": "Популярные посты",
    "lzHeaderCategories": "Категории",
    "lzHeadDef": "Заголовок текста",
    "lzHeadReadMore": "Читать далее",
    "lzHeadNext": "дальше"
    
}

и для английского

{
    "lzTitle": "LangZilla|English",
    "lzMenuHome": "Home",
    "lzMenuBlog": "Blog",
    "lzMenuGallery": "Gallery",
    "lzMenuAbout": "About",
    "lzMenuContact": "Contact",
    "lzMenuItem1": "We develop for WEB and Design",
    "lzMenuItem2": "We develop for Phones",
    "lzMenuItem3": "We develop for PC",
    "lzMenuItem4": "We develop for Microelectronic",
    "lzTextAboutAppend": "You can use and modify the our plugin for both personal and commercial use.[:]append",
    "lzTextAboutPrepend": "Free translate plugin by [:]prepend",
    "lzImageEnRU": "images/langzilla-en.png[:]attr=src",
    "lzHeaderPopularPost": "Popular Post",
    "lzHeaderCategories": "Categories",
    "lzHeadDef": "Text Heading",
    "lzHeadReadMore": "Read more",
    "lzHeadNext": "next"
}

Названия переменных «lzTitle», «lzMenuHome», «lzMenuBlog», … произвольные и они должны быть одинаковы во всех языковых картах, в данном случае, и для ru-RU.json и en-EN.json иначе вы не получите перевод.

Далее, нам необходимо заставить выполнить перевод по нажатии на кнопок, которые автоматически появятся, по умолчанию, в верхнем правом углу страницы, к которой подключен LangZilla — плагин

langzilla-btn-imgДля этого нужно создавать или добавлять классы к тегам, в которых есть содержимое, требующий перевода. Названия глассов соответствуют названиям переменных в языковых картов, например для переменно «lzMenuHome» класс будет написан как class = «lzMenuHome» и т.д. Пример этого показан ниже

<div class="menu">
	<ul>
		<li><a class="lzMenuHome" href="index.html"><!--Home--></a></li>
		<li><a class="lzMenuBlog" href="#"><!--Blog--></a></li>
		<li><a class="lzMenuGallery" href="#"><!--Gallery--></a></li>
		<li><a class="lzMenuAbout" href="#"><!--About--></a></li>
		<li><a class="lzMenuContact" href="#"><!--Contact--></a></li>
	</ul>
</div>

Готовый результат можно посмотреть на странице плагина.

Фильтры действий

Фильтры действий — это дополнительное указание для плагина как обработать данный текст перевода внутри тега. Все фильтры действий записываются после текста перевода через разделитель [:]. На данный момент существует следующий список фильтров действий:

  • [:]prepend — добавляет текст перевода перед содержимым внутри тега;
  • [:]append — добавляет текст перевода  после содержимого тега;
  • [:]html — добавляет текст перевода внутрь тега, удаляя содержимое. Данный фильтр применяется по умолчанию, если не указаны выше описанные фильтры принудительно;
  • [:]attr=attributeName — добавляет текст перевода в качестве значения атрибута;

Как вы заметили, данные фильтры идентичны с методами jQuery и выполняют похожие действия.

Дополнительные параметры LangZilla

LangZilla имеет дополнительные параметры для гибкой настройки. Ниже представлен весь список этих параметров:

  • position — определяет поведение кнопок на странице и соответствует CSS значениям position. По умолчанию кнопки находятся на верхнем правом углу и этот параметр установлен как «absolute»;
  • top — расстояние до кнопок с верхней части страницы в пикселях. По умолчанию равен 0;
  • right — расстояние до кнопок от правой стороны страницы. По умолчанию равен значению 80;
  • locals — параметр для регистрации языковых карт и определения для них кнопок. При использовании данный параметр должен быть определен;
  • locPath — путь к языковым картам относительно страницы, к которому подключен плагин. По умолчанию содержит значение «langzilla/loc/»;
  • callBack — функция обратного вызова. Если требуется использовать пользовательский код, который бы выполнялся при каждой смене языка, то можно смело ею пользоваться.

Это все, описание окончено. Удачного пользования!

 

Создание пользовательских модулей в Node.js

👁 101 просмотров

Масштабируемость проекта на Node.js отличается легкостью реализации и подразумевает создание пользовательских модулей. Все модули Node.js можно разделить на встроенные, которые скомпилированы вместе с сервером, так и сторонние, пользовательские. Встроенными можно считать часто используемые модули, которые работают с сетью (http), с файловой системой (fs) , с событиями (events) и т.д. Пользовательские модули можно загрузить из менеджера пакетов npm или создавать собственные, которые будут решать те задачи проекта, которые легче отделить от остальной части. Виды модулей, которые можно реализовать можно подразделить на 3 вида:

  • модули js — обычные модули, в которых можно написать серверный код Node.js;
  • модули json — хорошо подходят для хранения часто изменяемых переменных проекта или для его конфигурации. По сути это обычный файл с данными типа json;
  • модули node — это скомпилированный тип модулей, которые отличаются высокой производительностью для высоконагруженных частей проекта.

Любой готовый модуль можно разместить в пользовательской директории, но первую очередь сервер ищет модуль с таким названием в стандартной директории node_modules и если там не находит, то ищет уже в пользовательских директориях, при этом, если по молчанию названия модуля не указано, то ищет модуль с названием index.js, дальше index.json и index.node. Для подключения модуля в серверной части используется функция include(), который возвращает переменную из модуля. Для того, чтобы эта переменная была доступна при подключении используется конструкция exports.varName1 = varName2, где varName1 — это пользовательская переменная, которая доступна при подключении модуля, а varName2 — это данные, которые передаются в переменную модуля varName1 после обработки в модуле. Далее покажем на примере как создать и использовать модули js и json.

1. Структура примера

Давайте, чтобы детально описать пример создадим проект со следующей структурой:node-modules-exmpl-structure

где www — корневая папка проекта, server.js — главный файл проекта для организации точки входа в проект и подключения модулей, configs — директория для модулей json и наш модуль в ней conf.js, modules — директория для модулей js и наш модуль в ней module.js. Давайте напишем код работы для каждого файла в структуре.

2. Организация модуля conf.json

Данный модуль является файл для хранения одной переменной в формате json и давайте запишем в ней вот такой код с переменными:

{
"name":"Mark",
"age":25
}

3. Организация модуля module.js

Данный модуль, пусть, будет содержать код для возврата объекта, который мы в ней создадим:

//Создадим объект пользователей
function Users(name){
    this.name = name;
}
//Добавим еще один метод для вывода имени
Users.prototype.view = function(){
    console.log(this.name);
};
exports.Users =  Users;//Возвращаем переменную из модуля

4. Файл запуска server.js

Ниже представлен код файла server.js, в котором подключаются модули и запускается пример работы проекта:

var user = require('./modules/module.js');//Подключаем наш модуль js
var conf = require('./configs/conf.json');//Подключаем наш модуль json
var name = new user.Users(conf.name);//Создаем объект из переменной модуля
name.view();//Выводим имя юзера на консоль

При указании файла модуля можно опустить расширения *.js и *.json

5. Возврат чистого объекта из модуля

Если мы посмотрим на код выше, то станет понятно, что объект Users — это объект переменной user, который возвращается модулем через конструкцию exports. А как сделать так, чтобы модуль возвращал чистый объект, т.е. можно было бы сразу написать вот так:

var Users = require('./modules/module.js');//Подключаем наш модуль js
var conf = require('./configs/conf.json');//Подключаем наш модуль json
var name = new Users(conf.name);//Создаем объект
name.view();//Выводим имя юзера на консоль

Для того, чтобы данный код сработал необходимо в модуле указать export через его глобальный объект module:

//Создадим объект пользователей
function Users(name){
    this.name = name;
}
//Добавим еще один метод для вывода имени
Users.prototype.view = function(){
    console.log(this.name);
};
module.exports.Users =  Users;//Возвращаем переменную из модуля

6. Запуск примера

Через консоль переходим в директорию нашего проекта www и выполняем команду(в Windows):

node server.js

и мы увидим результат, как на скриншоте:
node-modules-post-resultСкачать рабочий пример можно скачать по ссылке.

 

Статья о том, как использовать IndexedDB в HTML5

👁 220 просмотров

IndexedDB — это API для хранения данных в браузере пользователя. Можно также использовать куки(cookie) и локальное хранилище(Local Storage), но они не предоставляют желаемый юзабилити, который предоставляет IndexedDB. Данные, хранимые в IndexedDB устойчивы. Данный способ хранения предоставляет богатые возможности запросов и доступны как в онлайн так и в оффлайн режимах. Также IndexedDB способен сохранять большие объемы данных. Увидеть состояние доступных хранилищ браузера можно через инструменты WEB — разработчика на панели Resources
IndexedDB-show-storage-img

 

IndexedDB — это текущий и рекомендованный кондидат для хранения локальных данных и доступный только в современных браузерах для Desctop.

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

IndexedDB vs LocalStorage

Хотя оба были спроектированы на предоставление клиентской стороне, как хранилища данных, но оба имеют различные цели и должны быть использованы согласно требованиям. LocalStorage хранит данные в key-value парах, но в отличии от IndexedDB, они не могут храниться в форме объектов. Вместо этого, они хранятся только в виде строковых key-value парах. Простой трюк хранения объектов с использованием LocalStorage — это использование объекта JSON.parse(). Но это не лучшее решение для хранения большого объема данных и сложных объектов. Тем не менее, LocalStorage был спроектирован для хранения мелких данных и предоставляет синхронный API доступа к ним. IndexedDB — это отличное решение для манипуляции больших объемов данных и предоставляет асинхронный API. Он использует индексацию на хранение данных и транзакции на выполнение операций. Он даже поддерживает простые типы данных. IndexedDB может казаться лучше, чем LocalStorage, но его API сложен для использования и на данный момент только последние версии современных браузеров поддерживают его. Для базового простого хранения можно продолжать использовать LocalStorage, но если вы хотите хранить большой объем данных, то IndexedDB — лучшее решение для этого, который, помимо всего позволяет вам выполнять комплексные сложные запросы на поиск данных.

IndexedDB vs Web SQL

WebSQL был также WEB — хранилищем с собственным API для хранения хранения данных на клиентской стороне. В отличии от IndexedDB, который является NoSQL базой данных, WebSQL использует SQL — запросы для операций с данными. W3C больше не поддерживает эту спецификацию, согласно http://www.w3.org/TR/webdatabase/

Предостережение. Эта спецификация больше не активна в поддержке и Web Applications Working Group не включила его в будущее развитие.

С этого момента, оно не поддерживается и его нельзя использовать в своих проектах.

IndexedDB vs Cookies

Cookies(печенки) — это может звучать вкусно, но это не так. Cookies могут отправлять и получать с каждым запросом http запросом данные в результируя в дополнительном трафике. Для примера, если у вас 10 кБ куков каждый и делаете 10 запросов, то сможете передать 100 кБ данных. Также, данные в куках имеют только строковый тип и следовательно их из строки необходимо парсировать. Также, область хранения куков лимитирован и многие пользователи могут блокировать поддержку куков в своих браузерах. Следовательно Cookies(куки) можно использовать для хранения очень мелких и не критичных данных.

Использование IndexedDB

1. Открытие БД IndexedDB.

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

window.indexedDB = window.indexedDB || window.mozIndexedDB || window.webkitIndexedDB || window.msIndexedDB;
 
if(!window.indexedDB)
{
    console.log("Your Browser does not support IndexedDB");
}

Раз мы знаем, что IndexedDb поддерживается, то мы можем открыть базу данных. Вы не можете поросто открыть базу данных, скорее IndexedDb потребует от вас запроса на окрытие базы данных.

var request = window.indexedDB.open("testDB", 2);

Здесь первый параметр — это имя базы данных, а второй — версия базы данных. Версия БД позволит вам представить схему текущей вашей БД в тех или иных режимах,т.е. предатсавит хранилища объектов, сохраненные в них и их структуры. Если вы обновите вашу БД до новой версии, то придется создать/удалить несколько объектов в хранилище, чем создать/удалить все объекты в хранилище.
Когда вы захотите увеличить версиб БД, то происходит событие onupgradeneeded. Вместе с onupgradeneeded имеются также события success, error и blocked, которые информируют об результатах запроса.

var db;
request.onerror = function(event){
    console.log("Error opening DB", event);
}
request.onupgradeneeded   = function(event){
    console.log("Upgrading");
    db = event.target.result;
    var objectStore = db.createObjectStore("students", { keyPath : "rollNo" });
};
request.onsuccess  = function(event){
    console.log("Success opening DB");
    db = event.target.result;
}

Событие onupgradeneeded должно вызываться каждый раз, когда WEB — страница выпадает в первые секунды открытия на пользовательских браузерах или, если, версии их БД были обновлены. Следовательно, вы можете создавать объекты хранения только при событии onupgradeneeded. Если не имеются обновления в версия и страница открыта предварительно, то вы можете получить событие onsuccess. onerror — событие происходит когда в процессе были допущены какие-либо ошибки. onblocked — происходит, когда предыдущее соединение не было закрыто. Выше в коде мы создаем объект хранения под именем «student» с индексным ключом “roll no”.

 

2. Добавление объекта в ObjectStore

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

var transaction = db.transaction(["students"],"readwrite");
transaction.oncomplete = function(event) {
    console.log("Success");
};

transaction.onerror = function(event) {
    console.log("Error");
};  
var objectStore = transaction.objectStore("students");

objectStore.add({rollNo: rollNo, name: name});

3. Удаление данных из ObjectStore

Удаление аналогично добавление. Для этого вам необходимо создать транзакцию и вызвать функцию удаления с ключом, удаляемого объекта

db.transaction(["students"],"readwrite").objectStore("students").delete(rollNo);

4. Доступ к объекту данных через ключ ObjectStore

Необходимо использовать get() функцию для получения данных объекта через ключ

var request = db.transaction(["students"],"readwrite").objectStore("students").get(rollNo);
request.onsuccess = function(event){
    console.log("Name : "+request.result.name);    
};

5. Обновление(редактирование данных) объекта

Для изменения объекта, сначала получаем объект, а затем после обновления содержимого обратно кладем в хранилище объектов.

var transaction = db.transaction(["students"],"readwrite");
var objectStore = transaction.objectStore("students");
var request = objectStore.get(rollNo);
request.onsuccess = function(event){
    console.log("Updating : "+request.result.name + " to " + name);
    request.result.name = name;
    objectStore.put(request.result);
};

 

Графический редактор на JavaScript. Часть 1. Введение

👁 93 просмотров

Данный цикл статей будет относится к проектированию простого графического редактора на основе клиентского языка JavaScript и серверного языка PHP. Графический редактор будет иметь возможность создавать простейшие примитивы: линия, эллипс, прямоугольник, многоугольник. Опишем возможность добавления, редактирования и удаления примитивов из текстового файла. Прорисовка примитивов будет производиться средствами HTML5 Canvas. HTML5 интегрирован с языком JavaScript и позволяет нам напрямую использовать API Canvas.

1. Клиентская часть на JavaScript

Клиентская часть, как известно, выполняется в браузера удаленного пользователя и для того, чтобы пользователь имел возможность редактировать файл на сервере необходимо будет иметь постоянную связь с PHP скриптом, для передачи параметров интерактивного редактирования примитива. Для этого будем использовать технологию ассинхроной связи с сервером AJAX. Для кроссбраузерной совместимости будем сразу использовать какую-нибудь библиотеку JavaScript, чтобы не отвлекаться на ненужные приемы совместимости и лучщим решением будет jQuery.

2. Серверная часть на PHP

Серверная часть будет написана на PHP. Необходимость в использовании PHP обусловлено тем, что JavaScript не имеет возможности работать с файлами: записывать, удалять, редактировать… Поэтому использование серверного языка — это одна из необходимостей для создания и редактирования графического файла, в котором будут храниться наши данные.

3. Расширение файла

Для того, чтобы сохранить наши примитивы для передачи между пользователями будем использовать обычный текстовый файл с расширением *.sgf(Simple Graphic Format). Название расширения не играет роли, можно любое, которое угодно вам и от этого файл не перестает быть текстовым. Другое дело — это , если бы мы использовали бинарный файл с 16-ричными значениями, но нас сейчас интересует первый вариант в виду легкости разработки и быстроты реализации.

4. Стрктура файла

Файл является собственным, структурированным, а это значит, что он не стнадартизирован как xml, хотя, можно было бы и его использовать, но не будем плодить копию SVG, а создадим свою структуру с нуля, чтобы лучше понять процесс разработки