Документация

Конвертор величин измерения расстояния

В данном примере рассматривается процесс создания виджета, который динамически обрабатывает информацию при помощи JavaScript. Будут рассмотрены два подхода к размещению тела виджета.

Подготовка тела виджета

Описываемый конвертор величин измерения расстояния должен переводить друг в друга следующие величины:

  • миллиметры;
  • сантиметры;
  • метры;
  • дюймы;
  • футы.

Создайте HTML-файл, реализующий интерфейс будущего виджета:

<html>
  <head>
    <style type="text/css">
      body {
        background-color:#ffcc00;
        text-align:center;
      }
      #toVal{
        color:blue;
      }
    </style>
  </head>  
  <body style="margin:0">
    <table>
      <tr>
        <td><input type="text" id="fromVal" style="width:80px"/></td>
        <td>
          <select id="fromSystem">
            <option value="mm">миллиметров</option>
            <option selected="selected" value="cm">сантиметров</option>
            <option value="m">метров</option>
            <option value="foot">футов</option>
            <option value="inch">дюймов</option>
          </select>
        </td>
      </tr>
      <tr>
        <td colspan="2" align="center">=</td>
      </tr>
      <tr>
        <td><input type="text" disabled="true" id="toVal" style="width:80px"/></td>
        <td>
          <select id ="toSystem">
            <option value="mm">миллиметры</option>
            <option value="cm">сантиметры</option>
            <option value="m">метры</option>
            <option selected="selected" value="foot">футы</option>
            <option value="inch">дюймы</option>
          </select>
        </td>
      </tr>
    </table>
  </body>
</html>

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

Для перевода данных из одной единицы измерения в другую примем миллиметр за единицу, а все остальные величины выразим через него. Получится следующее соотношение:

Единица измеренияСоответствие в миллиметрах
миллиметр1
сантиметр10
метр1000
дюйм25,4
фут304,8

Выразим эти соотношения с помощью объекта JavaScript:

var metrics = {
  "mm" : 1,
  "cm" : 10,
  "m" : 1000,
  "inch" : 25.4,
  "foot" : 304.8
};

Тогда можно написать следующую функцию конвертирования:

function convert(){
  var val = document.getElementById("fromVal").value;
  <!-- проверка правильности введенного значения -->
  if(isNaN(val)){
    return;
  }  
  <!-- если проверка прошла успешно, выполняем преобразование -->
  document.getElementById("toVal").value = val * metrics[document.getElementById("fromSystem").value] /
     metrics[document.getElementById("toSystem").value];
}

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

<input type="text" id="fromVal" onfocus="watchChanges()" onblur="watchChanges()" />
  ...
<select id="fromSystem" onchange="convert()" />
  ...
<select id="toSystem" onchange="convert()" />

К полю ввода fromVal добавлены события onfocus и onblur, которые вызывают функцию watchChanges. Данная функция запускает таймер, который следит за изменениями в поле ввода. Таким образом будут обработаны любые изменения, включая вставку значения из контекстного меню. Код функции следующий:

var interval = null;
function watchChanges(){
  interval == null ? setInterval("convert()", 500) : clearInterval(interval);
}

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

<html>
  <head>
    <style type="text/css">
      body {
        background-color:#ffcc00;
        text-align:center;
      }
      #toVal{
        color:blue;
      }
    </style>
    <script type="text/javascript">
      <!--
      var metrics = {
        "mm" : 1,
        "cm" : 10,
        "m" : 1000,
        "inch" : 25.4,
        "foot" : 304.8
      };
      function convert(){
        var val = document.getElementById("fromVal").value;
        if(isNaN(val)){
          return;
        }  
        document.getElementById("toVal").value = val *    
          metrics[document.getElementById("fromSystem").value] / 
          metrics[document.getElementById("toSystem").value];
      }
      var interval = null;
      function watchChanges(){
        interval == null ? setInterval("convert()", 500) : clearInterval(interval);
      }  
      //-->
    </script>
  </head>  
  <body style="margin:0">
    <table>
      <tr>
        <td>
         <input type="text" id="fromVal" style="width:80px" onfocus="watchChanges()" onblur="watchChanges()"/>
        </td>
        <td>
          <select id="fromSystem" onchange="convert()">
            <option value="mm">миллиметров</option>
            <option selected="selected" value="cm">сантиметров</option>
            <option value="m">метров</option>
            <option value="foot">футов</option>
            <option value="inch">дюймов</option>
          </select>
        </td>
      </tr>
      <tr>
        <td colspan="2" align="center">=</td>
      </tr>
      <tr>
        <td><input type="text" disabled="true" style="width:80px" id="toVal"/></td>
        <td>
          <select id ="toSystem" onchange="convert()">
            <option value="mm">миллиметры</option>
            <option value="cm">сантиметры</option>
            <option value="m">метры</option>
            <option selected="selected" value="foot">футы</option>
            <option value="inch">дюймы</option>
          </select>
        </td>
      </tr>
    </table>
  </body>
</html>

Подготовка описания виджета

Файл с описанием виджета должен содержать валидный XHTML. Поэтому описание виджета нужно создавать в файле вида:

<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" 
  "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:widget="http://wdgt.yandex.ru/ns/">
  ...
</html>
Внимание! Нужно обязательно указать кодировку UTF-8.

Чем подробнее заполнена секция свойств, тем легче будет найти виджет в общем каталоге. Добавьте в блок <head> документа следующий фрагмент:

<meta name="title" content="Конвертор величин измерения расстояний" />
<meta name="description" 
content="Конвертор величин измерения расстояний позволяет переводить различные меры длины друг в друга."/>

Здесь передается информация о названии и описание функциональности виджета.

Пользовательские настройки можно сравнить с переменными, в которых можно сохранять информацию и использовать ее, например, для формирования внешнего вида виджета.

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

Добавьте в блок <head> следующий фрагмент:

<widget:preferences>
  <preference name="mm" type="boolean" defaultValue="true" label="Миллиметры" />
  <preference name="cm" type="boolean" defaultValue="true" label="Сантиметры" />
  <preference name="m" type="boolean" defaultValue="true" label="Метры" />
  <preference name="foot" type="boolean" defaultValue="true" label="Футы" />
  <preference name="inch" type="boolean" defaultValue="true" label="Дюймы" />
</widget:preferences>
Примечание. Для использования настроек должно быть объявлено пространство имен xmlns:widget="http://wdgt.yandex.ru/ns/".

Каждая настройка задается в собственном теге <preference>; настройки помещаются в тег-контейнер <widget:preferences>.

Примечание. Общий размер настроек не должен превышать 1Kb. Это связано с тем, что настройки передаются в адресной строке (GET).

На основе приведенного выше кода будет сгенерирована следующая форма настроек:

Пользователи смогут вызвать данную форму, нажав на знак шестеренки в правом верхнем углу виджета в режиме настроек главной страницы.

Добавьте в тело виджета следующую логику обработки этих настроек:

<script type="text/javascript" 
        src="http://img.yandex.net/webwidgets/1/WidgetApi.js"></script>

widget.onload = function(){
  widget.adjustIFrameHeight();
  var metricNames = ["m", "cm", "mm", "foot", "inch"];
  var metrics = "";
  for(var i = 0; i < metricNames.length; ++i){
    if(widget.getValue(metricNames[i]) == "true"){
      metrics += ("," + metricNames[i] + ",");
    }
  }
}

function delOtherValuesFromList(list, values){
  for(var i = 0; i < list.options.length;){
    values.indexOf("," + list.options[i].value + ",") == -1 ? list.remove(i) : ++i;
  }  
}
Внимание! В данном примере используется виджетное JS API. Для его использования нужно подключить файл WidgetApi.js. Подробнее про API см. в справочнике по JS API.

Событие виджета onload, выполняющееся при загрузке виджета, получает при помощи метода JS API getValue значения настройек, которые были ранее добавлены в теге <widget:preferences>.

Примечание. Подробнее см. раздел Пользовательские настройки в руководстве разработчика.

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

В onload также вызывается метод JS API adjustIFrameHeight, который устанавливает высоту элемента <iframe> так, чтобы он вместил все содержимое виджета без вертикальной полосы прокрутки.

Способы хранения виджетов

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

"Конвертор величин" как автономный виджет

Чтобы получить автономный виджет "Конвертор величин измерения расстояния", нужно поместить тело виджета в файл с описанием:

<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" 
  "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:widget="http://wdgt.yandex.ru/ns/"  >
  <head>
    <meta name="title" content="Конвертор величин измерения расстояний" />
    <meta name="description" 
content="Конвертор величин измерения расстояний позволяет переводить различные меры длины друг в друга."/>
    <widget:preferences>
      <preference name="mm" type="boolean" defaultValue="true" label="Миллиметры" />
      <preference name="cm" type="boolean" defaultValue="true" label="Сантиметры" />
      <preference name="m" type="boolean" defaultValue="true" label="Метры" />
      <preference name="foot" type="boolean" defaultValue="true" label="Футы" />
      <preference name="inch" type="boolean" defaultValue="true" label="Дюймы" />
    </widget:preferences>
    <style type="text/css">
      body {
        background-color:#ffcc00;
        text-align:center;
      }
      #toVal{
        color:blue;
      }
    </style>
    <script type="text/javascript" src="http://img.yandex.net/webwidgets/1/WidgetApi.js">
    </script>
    <script type="text/javascript">
      <!--
      var metrics = {
        "mm" : 1,
        "cm" : 10,
        "m" : 1000,
        "inch" : 25.4,
        "foot" : 304.8
      };
      function convert(){
        var val = document.getElementById("fromVal").value;
        if(isNaN(val)){
          return;
        } 
        document.getElementById("toVal").value =
          document.getElementById("fromVal").value *    
          metrics[document.getElementById("fromSystem").value] / 
          metrics[document.getElementById("toSystem").value];
      }
      widget.onload = function(){
        widget.adjustIFrameHeight();
        var metricNames = ["m", "cm", "mm", "foot", "inch"];
        var metrics = "";
        for(var i = 0; i < metricNames.length; ++i){
          if(widget.getValue(metricNames[i]) == "true"){
            metrics += ("," + metricNames[i] + ",");
          }
        }
        delOtherValuesFromList(document.getElementById("fromSystem"), metrics);
        delOtherValuesFromList(document.getElementById("toSystem"), metrics);
      }
      function delOtherValuesFromList(list, values){
        for(var i = 0; i < list.options.length;){
          values.indexOf("," + list.options[i].value + ",") == -1 ? list.remove(i) : ++i;
        }  
      }
      var interval = null;
      function watchChanges(){
        interval == null ? setInterval("convert()", 500) : clearInterval(interval);
      }
      //-->
    </script>
  </head>  
  <body style="margin:0">
    <table>
      <tr>
        <td>
         <input type="text" id="fromVal" style="width:80px" 
              onfocus="watchChanges()" onblur="watchChanges()"/>
        </td>
        <td>
          <select id="fromSystem" onchange="convert()">
            <option value="mm">миллиметров</option>
            <option selected="selected" value="cm">сантиметров</option>
            <option value="m">метров</option>
            <option value="foot">футов</option>
            <option value="inch">дюймов</option>
          </select>
        </td>
      </tr>
      <tr>
        <td colspan="2" align="center">=</td>
      </tr>
      <tr>
        <td><input type="text" style="width:80px" disabled="true" id="toVal"/></td>
        <td>
          <select id ="toSystem" onchange="convert()">
            <option value="mm">миллиметров</option>
            <option value="cm">сантиметров</option>
            <option value="m">метров</option>
            <option selected="selected" value="foot">футов</option>
            <option value="inch">дюймов</option>
          </select>
        </td>
      </tr>
    </table>
  </body>
</html>

"Конвертор величин" как серверный виджет

При серверной реализации тело виджета нужно разместить на каком-либо хостинге (в данном примере тело виджета размещается по адресу http://example.com/widgets/converter.html) как обычную интернет-страницу.

В описание виджета нужно добавить свойство src:

<meta name="src" content="http://example.com/widgets/converter.html" />

Тогда файл виджета примет вид:

<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:widget="http://wdgt.yandex.ru/ns/"  >
  <head>
    <!-- Свойства виджета -->
    <meta name="title" content="Конвертор величин измерения расстояний" />
    <meta name="description" 
      content="Конвертор величин измерения расстояний позволяет переводить различные меры длины друг в друга."/>
      <meta name="src" content="http://example.com/widgets/converter.html" />
    <!-- Настройки виджета -->
    <widget:preferences>
      <preference name="mm" type="boolean" defaultValue="true" label="Миллиметры" />
      <preference name="cm" type="boolean" defaultValue="true" label="Сантиметры" />
      <preference name="m" type="boolean" defaultValue="true" label="Метры" />
      <preference name="foot" type="boolean" defaultValue="true" label="Футы" />
      <preference name="inch" type="boolean" defaultValue="true" label="Дюймы" />
    </widget:preferences>
  </head>
  <body style="margin:0">
  </body>
</html>

Файл http://example.com/widgets/converter.html должен выглядеть следующим образом:

<html>
  <head>
    <!-- Каскадные таблицы стилей -->
    <style type="text/css">
      body {
        background-color:#ffcc00;
        text-align:center;
      }
      #toVal{
        color:blue;
      }
    </style>
    <script type="text/javascript" src="http://img.yandex.net/webwidgets/1/WidgetApi.js">
    </script>
    <script type="text/javascript">
      <!--
      var metrics = {
        "mm" : 1,
        "cm" : 10,
        "m" : 1000,
        "inch" : 25.4,
        "foot" : 304.8
      };
      function convert(){
        var val = document.getElementById("fromVal").value;
        if(isNaN(val)){
          return;
        } 
        document.getElementById("toVal").value =
          document.getElementById("fromVal").value *    
          metrics[document.getElementById("fromSystem").value] / 
          metrics[document.getElementById("toSystem").value];
      }
      widget.onload = function(){
        widget.adjustIFrameHeight();
        var metricNames = ["m", "cm", "mm", "foot", "inch"];
        var metrics = "";
        for(var i = 0; i < metricNames.length; ++i){
          if(widget.getValue(metricNames[i]) == "true"){
            metrics += ("," + metricNames[i] + ",");
          }
        }
        delOtherValuesFromList(document.getElementById("fromSystem"), metrics);
        delOtherValuesFromList(document.getElementById("toSystem"), metrics);
      }
      function delOtherValuesFromList(list, values){
        for(var i = 0; i < list.options.length;){
          values.indexOf("," + list.options[i].value + ",") == -1 ? list.remove(i) : ++i;
        }  
      }
      var interval = null;
      function watchChanges(){
        interval == null ? setInterval("convert()", 500) : clearInterval(interval);
      }
      //-->
    </script>
  </head>  
  <body style="margin:0">
    <table>
      <tr>
        <td>
         <input type="text" id="fromVal" style="width:80px" 
            onfocus="watchChanges()" onblur="watchChanges()"/>
        </td>
        <td>
          <select id="fromSystem" onchange="convert()">
            <option value="mm">миллиметров</option>
            <option selected="selected" value="cm">сантиметров</option>
            <option value="m">метров</option>
            <option value="foot">футов</option>
            <option value="inch">дюймов</option>
          </select>
        </td>
      </tr>
      <tr>
        <td colspan="2" align="center">=</td>
      </tr>
      <tr>
        <td><input type="text" style="width:80px" disabled="true" id="toVal"/></td>
        <td>
          <select id ="toSystem" onchange="convert()">
            <option value="mm">миллиметров</option>
            <option value="cm">сантиметров</option>
            <option value="m">метров</option>
            <option selected="selected" value="foot">футов</option>
            <option value="inch">дюймов</option>
          </select>
        </td>
      </tr>
    </table>
  </body>
</html>

Что получилось?

Созданный виджет имеет вид:

пример