Сценарии JavaScript в активных страницах Web

         

Шестнадцатеричный калькулятор


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

Рис. 3.19. Калькулятор, выполненный на языке сценариев JavaScript

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

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

Кнопки “+”, “-“, “*” и “/” предназначены, соответственно, для выполнения операции сложения, вычитания, умножения и деления. При помощи кнопки “Enter” можно получить результат вычислений. Кнопка CE позволяет стереть текущее введенное число, а кнопка C - сбросить промежуточный результат вычислений и текущее введенное число.

Исходный текст документа HTML с калькулятором представлен в листинге 3.9.

Листинг 3.9. Файл chapter3/hexcalc/hexcalc.html

<HTML>

<HEAD>

  <TITLE>Hexadecimal calculator</TITLE>

  <SCRIPT>

  <!--



  var total = 0;

  var lastOperation = "+";

  var newnumber = true;

 

  function dec2hex(nDec)

  {

    var szHexTable="0123456789ABCDEF";

    var szResult = "";

    var szBuf="";

    var nRem = 0;

    var bNegative=false;

    if(nDec < 0)

    {

      bNegative=true;

      nDec = -nDec;

    }

    nTmp=nDec;

     

    while(true)

    {

      nRem = nTmp % 16;

      nTmp = nTmp / 16;

      if(Math.floor(nTmp) < 16)

        break;

      szBuf=szHexTable.charAt(nRem);

      szResult = szBuf.concat(szResult);

    }

   

    szBuf=szHexTable.charAt(nRem);

    szResult = szBuf.concat(szResult);

    if(Math.floor(nTmp) != 0)

    {

      szBuf=szHexTable.charAt(Math.floor(nTmp));

      szResult = szBuf.concat(szResult);

    }


    if(bNegative == true)

      return ("-" + szResult);

    else

      return szResult;

  }

  function putNumber(btn,form)

  {

    var szOld = "";

    var szNew = "";

    if(newnumber)

    {

      form.displayHex.value = "";

      form.displayDec.value = "";

      newnumber = false; 

    }

   

    szOld = form.displayHex.value;

  

    szNew = szOld.concat(btn.name);

    nCurrent = eval("0x" + szNew);

  

    form.displayHex.value = szNew;

    form.displayDec.value = nCurrent;

  }

  function clearNumber(form)

  { 

    form.displayHex.value = "0";

    form.displayDec.value = "0";

    newnumber = true; 

  }

  function clearAll(form)

  {

    total = 0; 

    lastOperation = "+"; 

    clearNumber(form);

  }

  function plusOp(form)

  {

    var result;

    result = total + " " + lastOperation + " " + form.displayDec.value;

    total = eval(result);

    lastOperation = "+"; 

    form.displayHex.value = dec2hex(total); 

    form.displayDec.value = total; 

    newnumber = true;

  }

  function minusOp(form)

  {

    var result;

 

    result = total + " " + lastOperation + " " + form.displayDec.value;

    total = eval(result);

    lastOperation = "-"; 

    form.displayHex.value = dec2hex(total); 

    form.displayDec.value = total; 

    newnumber = true;

  }

  function mulOp(form)

  {

    var result;

 

    result = total + " " + lastOperation + " " + form.displayDec.value;

    total = eval(result);

    lastOperation = "*"; 

    form.displayHex.value = dec2hex(total); 

    form.displayDec.value = total; 

    newnumber = true;

  }

  function divOp(form)

  {

    var result;

 

    result = total + " " + lastOperation + " " + form.displayDec.value;



    total = eval(result);

    lastOperation = "/"; 

    form.displayHex.value = dec2hex(total); 

    form.displayDec.value = total; 

    newnumber = true;

  }

  function getResult(form)

  {

    var result;

    result = total + lastOperation + eval("0x" + form.displayHex.value);

    total = eval(result);

    form.displayHex.value = dec2hex(total); 

    form.displayDec.value = total; 

    newnumber = true;

  }

  // -->

  </SCRIPT>

  </HEAD>

  <BODY>

    <FORM>

      <TABLE BORDER=2 BORDERCOLOR="Black" BGCOLOR="Yellow">

        <TR>

          <TD>Hex:</TD>

          <TD COLSPAN=3><INPUT TYPE=text NAME="displayHex" VALUE="0" onFocus="this.blur();"></TD>

        </TR>

        <TR>

          <TD>Dec:</TD>

          <TD COLSPAN=3><INPUT TYPE=text NAME="displayDec" VALUE="0" onFocus="this.blur();"></TD>

        </TR>

        <TR>

          <TD ALIGN=CENTER><INPUT TYPE="button" NAME="C" VALUE=" C " onClick="putNumber(this,this.form);"></TD>

          <TD ALIGN=CENTER><INPUT TYPE="button" NAME="D" VALUE=" D " onClick="putNumber(this,this.form);"></TD>

          <TD ALIGN=CENTER><INPUT TYPE="button" NAME="E" VALUE=" E " onClick="putNumber(this,this.form);"></TD>

          <TD ALIGN=CENTER><INPUT TYPE="button" NAME="F" VALUE=" F " onClick="putNumber(this,this.form);"></TD>

        </TR>

        <TR>

          <TD ALIGN=CENTER><INPUT TYPE="button" NAME="8" VALUE=" 8 " onClick="putNumber(this,this.form);"></TD>



          <TD ALIGN=CENTER><INPUT TYPE="button" NAME="9" VALUE=" 9 " onClick="putNumber(this,this.form);"></TD>

          <TD ALIGN=CENTER><INPUT TYPE="button" NAME="A" VALUE=" A " onClick="putNumber(this,this.form);"></TD>

          <TD ALIGN=CENTER><INPUT TYPE="button" NAME="B" VALUE=" B " onClick="putNumber(this,this.form);"></TD>

        </TR>

        <TR>

          <TD ALIGN=CENTER><INPUT TYPE="button" NAME="4" VALUE=" 4 " onClick="putNumber(this,this.form);"></TD>

          <TD ALIGN=CENTER><INPUT TYPE="button" NAME="5" VALUE=" 5 " onClick="putNumber(this,this.form);"></TD>

          <TD ALIGN=CENTER><INPUT TYPE="button" NAME="6" VALUE=" 6 " onClick="putNumber(this,this.form);"></TD>

          <TD ALIGN=CENTER><INPUT TYPE="button" NAME="7" VALUE=" 7 " onClick="putNumber(this,this.form);"></TD>

        </TR>

        <TR>

          <TD ALIGN=CENTER><INPUT TYPE="button" NAME="0" VALUE=" 0 " onClick="putNumber(this,this.form);"></TD>

          <TD ALIGN=CENTER><INPUT TYPE="button" NAME="1" VALUE=" 1 " onClick="putNumber(this,this.form);"></TD>

          <TD ALIGN=CENTER><INPUT TYPE="button" NAME="2" VALUE=" 2 " onClick="putNumber(this,this.form);"></TD>

          <TD ALIGN=CENTER><INPUT TYPE="button" NAME="3" VALUE=" 3 " onClick="putNumber(this,this.form);"></TD>

        </TR>

        <TR>



          <TD ALIGN=CENTER><INPUT TYPE="button" NAME="+" VALUE=" + " onClick="plusOp(this.form);"></TD>

          <TD ALIGN=CENTER><INPUT TYPE="button" NAME="-" VALUE="  -  " onClick="minusOp(this.form);"></TD>

          <TD ALIGN=CENTER><INPUT TYPE="button" NAME="*" VALUE=" * " onClick="mulOp(this.form);"></TD>

          <TD ALIGN=CENTER><INPUT TYPE="button" NAME="/" VALUE="  /  " onClick="divOp(this.form);"></TD>

        </TR>

        <TR>

          <TD ALIGN=CENTER><INPUT TYPE="button" NAME="C" VALUE=" C " onClick="clearAll(this.form);"></TD>

          <TD ALIGN=CENTER><INPUT TYPE="button" NAME="CE" VALUE="CE" onClick="clearNumber(this.form);"></TD>

          <TD COLSPAN=2 ALIGN=CENTER><INPUT TYPE="button" NAME="Enter" VALUE="Enter" onClick="getResult(this.form);"></TD>

        </TR>

      </TABLE>

    </FORM>

  </BODY>

</HTML>

Рассмотрим наиболее интересные моменты.

Когда калькулятор отображается в первый раз, в текстовых полях displayHex и displayDec, предназначенных, соответственно, для отображения чисел в шестнадцатеричном и десятичном виде, находятся нулевые значения:

  . . .

<INPUT TYPE=text NAME="displayHex" VALUE="0" onFocus="this.blur();">

  . . .

<INPUT TYPE=text NAME="displayDec" VALUE="0" onFocus="this.blur();">

  . . .

Это значение устанавливается параметром VALUE.

Обратите внимание, что для текстовых полей displayHex и displayDec мы предусмотрели обработчики событий onFocus. Этот обработчик получает управление, когда пользователь передает полю фокус ввода. Задача обработчика заключается в том, чтобы снова отобрать фокус ввода, предотвратив непосредственное редактирование числа пользователем.



С каждой из кнопок, связанной с вводом шестнадцатеричного числа, связан обработчик события onClick, вызывающий функцию putNumber, например:

INPUT TYPE="button" NAME="F" VALUE=" F " onClick="putNumber(this,this.form);">

Этой функции передаются два параметра - нажатая кнопка (как объект класса button) и форма, в которой эта кнопка находится.

Задача функции putNumber - ввод числа и его отображение в двух текстовых полях, расположенных в верхней части калькулятора:

function putNumber(btn,form)

{

  var szOld = "";

  var szNew = "";

  if(newnumber)

  {

    form.displayHex.value = "";

    form.displayDec.value = "";

    newnumber = false; 

  }

   

  szOld = form.displayHex.value;

  

  szNew = szOld.concat(btn.name);

  nCurrent = eval("0x" + szNew);

  

  form.displayHex.value = szNew;

  form.displayDec.value = nCurrent;

}

В самом начале функция putNumber проверяет двоичную переменную newnumber. Если значение этой переменной равно true, пользователь вводит первую цифру нового числа. В этом случае функция putNumber сбрасывает содержимое текстовых полей displayHex и displayDec, а также устанавливает значение newnumber, равное false.

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

На следующем этапе к введенному шестнадцатеричному числу добавляется префикс “0x”, после чего происходит вычисление текущего значения функцией eval. Эта функция пытается интерпретировать текстовую строку, переданную ей в качестве параметра, как арифметическое выражение, возвращая результат интерпретации в виде численного значения. Этот результат сохраняется в переменной nCurrent и отображается в текстовом поле displayDec (исходное шестнадцатеричное число отображается в поле displayHex).

Если после ввода числа пользователь нажимает одну из четырех кнопок, предназначенных для выполнения операций сложения, вычитания, умножения и деления, вызываются функции, назначенные для этих кнопок в обработчике события onClick. Это функции plusOp (сложение), minusOp (вычитание), mulOp (умножение) и divOp (деление).



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

function plusOp(form)

{

  var result;

  result = total + " " + lastOperation + " " +

    form.displayDec.value;

  total = eval(result);

  lastOperation = "+"; 

  form.displayHex.value = dec2hex(total); 

  form.displayDec.value = total; 

  newnumber = true;

}

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

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

С помощью функции eval функция plusOp вычисляет результат операции и записывает его в переменную total. Затем в переменную lastOperation записывается код операции сложения - строка “+”.

На следующем этапе функция plusOp преобразует значение total с помощью функции dec2hex и отображает результат в шестнадцатеричном виде в поле displayHex, а также в двоичном - в поле displayDec.

Перед тем как возвратить управление, функция plusOp записывает в переменную newnumber значение true. Это приводит к тому, что при дальнейшем вводе цифр они будут рассматриваться как цифры второго слагаемого, участвующего в операции сложения.

Функция getResult вызывается, когда пользователь нажимает на клавишу нашего калькулятора с надписью “Enter”:

function getResult(form)

{

  var result;

  result = total + lastOperation + eval("0x" +

    form.displayHex.value);

  total = eval(result);

  form.displayHex.value = dec2hex(total); 

  form.displayDec.value = total; 

  newnumber = true;

}

От только что описанной функции plusOp эта функция отличается лишь тем, что она не изменяет значение переменной lastOperation (так как данная кнопка служит для получения итогового результата, а не для выполнения арифметической операции).



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

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

Алгоритм преобразования десятичного числа в шестнадцатеричное основан на делении исходного числа на 16 в цикле. Если целая часть результата деления, вычисляемая с помощью метода Math.floor, оказывается меньше 16, цикл завершается. В противном случае остаток от деления рассматривается как значение текущего шестнадцатеричного разряда.

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

szBuf=szHexTable.charAt(nRem);

szResult = szBuf.concat(szResult);

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


Содержание раздела