   // Controls for angle input;
   // v1.0.02 commercial 19 Apr 2000;
   // (c) Andrew Golovin (exper@inbox.ru);

   
   // Константы направления вращения
   //   _CCW_ - против часовой стрелки
   //   _CW_ - по часовой стрелке
   var _CCW_ = true;
   var _CW_ = false;
   
   // Константы для пользовательских сообщений
   //   erWrongAngle - алерт при вводе неверного угла
   //   msAngle - подпись к полю ввода текущего угла
   var erWrongAngle = "Неверное значение угла";
   var msAngle = "Угол";
   var tlAquire = "Использовать текущее значение угла";

   //   arcInput - коллекция всех элементов ввода
   //   totalArcs - количество элементов ввода
   var arcInputs = new Array();
   var totalArcs = 0;
   

   //   mmd - число по модулю
   function mmd(v) { 
     return (v < 0) ? -v : v; 
   }

   // >>CONSTRUCTORS
   // arcInput - Элемент ввода, для интерактивного указания
   // угла.
   // =====================================================
   // Свойсвтва:
   // name - имя элемента;
   // width - ширина элемента, должна соответсвовать ширине
   //         изображения;
   // height - высота элемента, должна соответсвовать высоте
   //         изображения;
   // cntx - горизонтальная координата центра вращения движка;
   // cnty - вертикальная координата центра вращения движка;
   // rad - радиус вращения движка;
   // img - изображение по которому проводятся измерения;
   // absstart - физический угол начала шкалы;
   // absend - физический угол конца шкалы;
   // start - логический угол начала шкалы;
   // end - логический угол конца шкалы;
   // rotationDir - положительное направление вращения
   //               true - против часовой стрелки (_CCW_)
   //               false - по часовой стрелке (_CW_);
   // title - заголовок элемента. Используется в атрибуте
   //         ALT изображения и в тексте метки (arcLabel());
   // rotating - состояние элемента. true - вращается с
   //            помощью мыши; false - не вращается;
   // isTextInput - устанавливается true после вывода текстового
   //         поля ввода;
   // isLabel - устанавливается true после вывода метки элемента;
   // element - указвает на элемент ввода в иерархии элементов страницы;
   // style - указывает на свойство style элемента в иерархии элементов
   //         страницы;
   // current - текущий угол;
   
   // =====================================================
   // Методы:
   // toFisicalAngle(angle) - преобразует логическое значение
   //          угла angle в физическое;
   // toLogiclAngle(angle) - преобразует физическое значени
   //          угла angle в логическое;
   // rotateTo(angle) - устанавливает текущее значение в angle,
   //          устанавливает положение движка; в случае указания
   //          недопустимого значения угла, выводит предупреждение
   //          и устанавливае угол в ближайщий конец шкалы; angle
   //          должен содержать логическое значение угла;
   // isValidAngle(angle) - проверяет допустимость значения угла
   //          angle для логической шкалы; возвращает true, если
   //          угол является допустимым, false - в противном случае;
   // rotate() - осуществляет поворот движка в соотвествии с положением
   //          указателя мыши;
   // arcControl() - выводит в текст документа набор тэгов для отображения
   //          элемента (изображение и движок) и устанавливает обработчики
   //          событий;
   // arcText() - выводит в текст документа текстовое поле ввода для
   //          отображения и установки значения угла;
   // arcLabel() - выводит в текст документа метку (<LABEL>) для
   //          текстового поля ввода;
   // startRot() - включает режим вращения;
   // stopRot() - выключает режим вращения;
   // initArc() - устанавливает значение угла и положение движка в
   //          начало шкалы;
   // =====================================================
   // Общие свойства:
   // arcInputs - массив, содержащий все элементы ввода arcInput на
   //          странице; к отдельным элементам можно ссылаться как по
   //          цировому индексу, так и по значению свойства name;
   // totalArcs - количество элементов ввода arcInput на странице;
   
   function arcInput(name) {
      this.name = name;
      this.width = 100;
      this.height = 100;
      this.cntx = 50;
      this.cnty = 50;
      this.rad = 40;
      this.img = "images/pnscpf.gif";
      this.absstart = 0;
      this.start = -180;
      this.end = 180;
      this.rotationDir = true;
      this.title = "Handle";
	  this.rotating = false;
      this.isTextInput = false;
      this.isLabel = false;
      this.form = "none";
      this.initHandleValue = 0;
      
      this.toFisicalAngle = toFisicalAngle;
      this.toLogicalAngle = toLogicalAngle;
	  this.rotateTo = rotateTo;
      this.isValidAngle = isValidAngle;
	  this.rotate = rotate;
	  this.arcControl = arcControl;
	  this.arcText = arcText;
	  this.arcLabel = arcLabel;
	  this.startRot = startRot;
	  this.stopRot = stopRot;
	  this.initArc = initArc;
	  arcInputs[totalArcs++] = this;
	  arcInputs[this.name] = this;
   }
   

   // arcInput >> IMPLEMENTATION
   
   function initArc() {
      this.rotateTo(this.initHandleValue);
      this.current = this.initHandleValue;
   }
   
   function arcControl() {
      if (IE && ( br.version >= 5 )) {
         document.write(
         "<DIV ID=\"" + this.name + "\" CLASS=\"arcInput\" " +
         "TITLE=\"" + this.title + "\" " +
         "STYLE=\"width: " + this.width + "; height: " + this.height + ";\" " +
         "ONMOUSEDOWN=\"" + this.name + ".startRot()\" " + 
         "ONMOUSEUP=\"" + this.name + ".stopRot()\" " +
         "ONMOUSEOUT=\"" + this.name + ".stopRot()\" " +
         "ONMOUSEMOVE=\"" + this.name + ".rotate()\" ONSELECTSTART=\"return false;\" ONDRAGSTART=\"return false;\">" +
         "<IMG SRC=\"" + this.img + "\" WIDTH=" + this.width + " HEIGHT=" + this.height + " BORDER=0>" +
         "<DIV ID=\"dv" + this.name + "\" CLASS=\"dvizhok\" STYLE=\"top: " + this.cntx + "; left: " + this.cnty + ";\">" +
         "<IMG ID=dvi SRC=\"images/bltred.gif\" WIDTH=10 HEIGHT=10 BORDER=0>" +
         "</DIV></DIV>");
         this.element = document.all[this.name];
         this.style = this.element.style;
       } else {
         document.write("<IMG SRC=\"" + this.img + "\" WIDTH=" + this.width + " HEIGHT=" + this.height + " BORDER=0>");
       }
    }
		
   function arcText() {
      document.write(
      "<INPUT TYPE=\"Text\" NAME=\"" + this.name + "Ang\" SIZE=\"5\" MAXLENGTH=\"5\" VALUE=" + this.start);
      if (IE && ( br.version >= 5 )) {      
	     document.write("ONCHANGE=\"" + this.name + ".rotateTo(this.value)\")\">");
      } else {
         document.write(">");
      } 
      this.isTextInput = true;
   }
   
   function arcLabel() {
      document.write("<LABEL FOR=\"" + this.name + "Ang\">" + this.title + "</LABEL>");
      this.isLabel = true;
   }
   
   function startRot() {
      document.all[this.name].style.cursor="hand";
      this.rotating = true;
   }
   
   function stopRot() {
      document.all[this.name].style.cursor="auto";
      this.rotating = false;
   }
   
   function rotateTo(angle) {
      rang = this.toFisicalAngle(angle);
      ny = this.cnty-5+this.rad*Math.sin(rang*Math.PI/180);
      nx = this.cntx-5+this.rad*Math.cos(rang*Math.PI/180);
      if (this.isValidAngle(angle)) {
         moveTo("dv" + this.name,nx,ny);
         this.current = Math.round(angle);
         if (this.isTextInput) {
           eval("document.all." + this.name + "Ang.value=this.current");
         }
      } else {
         if (this.isTextInput) {
           eval("document.all." + this.name + "Ang.value=this.current");
         }
         alert(erWrongAngle + " (" + angle + ")\n \"" + this.title + "\"");
      }
   }
   
   function rotate() {
      if ((this.rotating)) {
        cx = event.x - this.cntx ;
        cy = event.y - this.cnty ;
        r = Math.sqrt(cx*cx + cy*cy);
        ang = Math.asin(cy / r);
        ang = 180*ang / Math.PI;
		oa = ang;
        if ((cx < 0) && (cy > 0)) {
          ang = 90 - ang + 90;
        }
        if ((cx < 0) && (cy <= 0)) {
          ang = mmd(ang) + 180;
        }
		if ((cx >= 0) && (cy < 0)) {
		  ang = 270 + 90 - mmd(ang);
		}
        rang = this.toLogicalAngle(ang);
        if (!this.isValidAngle(rang)) {
		   if (mmd(this.start - rang) > mmd(this.end - rang)) {
		      rang = this.end;
		   } else {
		      rang = this.start;
		   }
           ang = this.toFisicalAngle(rang);
        }
        ny = this.cnty-5+this.rad*Math.sin(ang*Math.PI/180);
        nx = this.cntx-5+this.rad*Math.cos(ang*Math.PI/180);
        moveTo("dv" + this.name,nx,ny);
        this.current = Math.round(rang);
        if (this.isTextInput) {
           eval("document.all." + this.name + "Ang.value=this.current");
        }
      }
	  return false;
   }
   
   function toFisicalAngle(angle) {
      if (this.rotationDir) {
         angle = 360 - angle;
         if (angle > 360) {
            angle = angle - 360;
         }
      }
      if ( this.absstart < 180 ) {
         ant = parseInt(this.absstart) + parseInt(angle);
         return (ant > 0 ) ? ant : 360 - mmd(ant);
      } else {
         ant = parseInt(this.absstart) + parseInt(angle);
         return (ant > 360) ?ant - 360 : ant;
      }
   }
   
   function toLogicalAngle(angle) {
      if (this.rotationDir) {
         angle = 360 - angle;
         tstart = 360 - this.absstart;
      } else {
         tstart = this.absstart
      }
      //  ============  по часовой
      if ( angle < 180 ) {
         if ( tstart < 180 ) {
            at = angle - tstart;
         } else {
            at = 360 - (tstart - angle);
         }
      } else {
         tt = angle - tstart;
         at = (tt > 180) ? tt - 360 : tt;            
      }
      return (at > 180) ? at - 360 : at;
   }
   
   function isValidAngle(angle) {
     return (( angle <= this.end ) && ( angle >= this.start ));
   }

   // arcGhostInput - Элемент ввода, для интерактивного указания
   // угла, с возможностью объединения нескольких элементов в группу,
   // при этом видимым является только активный элемент.
   // =====================================================
   // Свойства:
   // memberOf - Имя группы, к которой принадлежит элемент;
   
   // arcGhostInput >> CONSTRUCTOR
   function arcGhostInput(name) {
      this.constructor = arcInput;
      this.constructor(name);
      this.memberOf = null;
      this.arcText = arcGhostText;
   }
   
   // arcGhostInput >> IMPLEMENTATION
   function arcGhostText() {
      document.write(
      "<INPUT TYPE=\"Text\" NAME=\"" + this.name + "Ang\" SIZE=\"5\" MAXLENGTH=\"5\" VALUE=" + 
	  this.start + " ONCHANGE=\"" + this.name + ".rotateTo(this.value)\" ONFOCUS=\"" + this.memberOf+ ".activate(" + this.name + ")\">");
      this.isTextInput = true;
   }   
   
   

   // arcInputGroup - Группа, содержащая элементы ввода углов. Может содержать
   // только элементы класса arcGhostInput;
   // =====================================================
   // Свойства:
   // arcInputs - Массив, содержащий элементы ввода, принадлежащие группе;
   // arcCount - Количество элементов ввода в группе;
   // activeArc - Активный элемент ввода на данный момент;
   // =====================================================
   // Методы:
   // insertArc() - Помещает элемент ввода в группу, устанавливает
   //     свойство memberOf данного элемента и увеличивает количество
   //     элементов в группе;
   // activate(element) - Делает элемент element активным, а активный
   //     прежде - неактивным; устанавливает activeArc в element;
   // initArcGroup - Инициализирует все элементы в группе, путем вызова
   //     соответсвтующего метода arcInit(); устанавливает атрибуты
   //     style.display для всех элементов ввода; делает активным
   //     первый элемент в группе;
   
   // arcInputGroup >> CONSTRUCTOR
   function arcGroup(name) {
      this.name = name;
      this.arcInputs = new Array();
      this.arcCount = 0;
      this.activeArc = null;
      this.insertArc = insertArc;
      this.activate = activate;
      this.initArcGroup = initArcGroup;
   }
   
   // arcInputGroup >> IMPLEMENTATION
   function insertArc(aI) {
      this.arcInputs[this.arcCount++] = aI;
      aI.memberOf = this.name;
   }
   
   function activate(aI) {
      if (this.activeArc != null) {
         this.activeArc.style.display = "none";
      }
      aI.style.display = "block";
      this.activeArc = eval(aI);
   }
   
   function initArcGroup() {
      for (i = 0; i < this.arcCount; i++) {
          this.arcInputs[i].initArc();
          this.arcInputs[0].style.display = "none";
      }
      this.arcInputs[0].style.display = "block";
      this.activeArc = this.arcInputs[0];
   }
   
   // arcAmpInput - Элемент ввода, для интерактивного указания
   // двух углов на одной оси; Введенные значения отмечаются на оси
   // неподвижными маркерами;
   // =====================================================
   // Свойства:
   //   startTitle - текст метки поля ввода начального значения угла;
   //   endTitle - текст метки поля ввода начального значения угла;
   //   startMark - путь к изображению-маркеру начального значения угла;
   //   endMark - путь к изображению-маркеру конечного значения угла;
   //   isStartFirst - указывает последовательность расположения 
   //       текстовых полей ввода углов;
   //   
   // Методы:
   //   setStart(angle) - устанавливает значение первого измеряемого
   //       угла, предварительно осуществляя проверку;
   //   setEnd(angle) - устанавливает значение второго измеряемого
   //       угла, предварительно осуществляя проверку;
   //   setPlainStart(angle) - устанавливает значение первого измеряемого
   //       угла, предварительно осуществляя проверку; для браузеров,
   //       отличных от IE;
   //   setPlainEnd(angle) - устанавливает значение второго измеряемого
   //       угла, предварительно осуществляя проверку; для браузеров,
   //       отличных от IE;
   //   setCurrentStart() - устанавливает значение первого измеряемого
   //       угла в положение движка; устанавливает маркер;
   //   setCurrentEnd() - устанавливает значение второго измеряемого
   //       угла в положение движка; устанавливает маркер;

   function arcAmpInput(name) {
      this.constructor = arcInput;
      this.constructor(name);
      this.startTitle = "Начало:";
      this.endTitle = "Конец:";
	  this.startMark = "images/startmark.gif";
	  this.endMark = "images/endmark.gif";
      this.isStartFirst = true;
      this.arcText = arcAmpText;
      this.arcControl = arcAmpControl;
      this.setStart = setStart;
      this.setEnd = setEnd;
      this.setPlainStart = setPlainStart;
      this.setPlainEnd = setPlainEnd;
      this.setCurrentStart = setCurrentStart;
      this.setCurrentEnd = setCurrentEnd;
      this.initArc = initAmpArc;
      this.isAmpValidAngle = isAmpValidAngle;
      this.isTextInput = true;
   }
   
   function arcAmpText() {
      // все текстовые элементы ввода выводятся в arcAmpControl();
   }
   
   function arcAmpControl() {
      if (IE && ( br.version >= 5 )) {
        document.write(
        "<DIV ID=\"" + this.name + "\" CLASS=\"arcInput\" " +
        "TITLE=\"" + this.title + "\" " +
        "STYLE=\"width: " + this.width + "; height: " + this.height + ";\" " +
        "ONMOUSEDOWN=\"" + this.name + ".startRot()\" " + 
        "ONMOUSEUP=\"" + this.name + ".stopRot()\" " +
        "ONMOUSEOUT=\"" + this.name + ".stopRot()\" " +
        "ONMOUSEMOVE=\"" + this.name + ".rotate()\" ONSELECTSTART=\"return false;\" ONDRAGSTART=\"return false;\">" +
        "<IMG SRC=\"" + this.img + "\" WIDTH=" + this.width + " HEIGHT=" + this.height + " BORDER=0>" +
        "<DIV ID=\"dv" + this.name + "\" CLASS=\"dvizhok\" STYLE=\"top: " + this.cntx + "; left: " + this.cnty + ";\">" +
        "<IMG ID=dvi SRC=\"images/bltred.gif\" WIDTH=10 HEIGHT=10 BORDER=0>" +
        "</DIV>" +
        "<DIV CLASS=\"marker\" ID=\"" + this.name + "startMarker\"><IMG SRC=\"" + this.startMark + "\" WIDTH=\"11\" HEIGHT=\"11\" BORDER=\"0\" ALT=\"" + this.startTitle + "\"></DIV>" +
        "<DIV CLASS=\"marker\" ID=\"" + this.name + "endMarker\"><IMG SRC=\"" + this.endMark + "\" WIDTH=\"11\" HEIGHT=\"11\" BORDER=\"0\" ALT=\"" + this.endTitle + "\"></DIV>" +
        "</DIV>" +
        "<DIV ID='ampControls'><TABLE BORDER=0><TR><TD>" +
        msAngle + ":</TD><TD><INPUT TYPE='text' NAME=\"" + this.name + "Ang\" SIZE=\"5\" MAXLENGTH=\"5\" VALUE=" + this.start + " ONCHANGE=\"" + this.name + ".rotateTo(this.value)\" DISABLED STYLE=\"border: none; background: transparent;\"></TD></TR>");

        startBody = 
        "<TR><TD>" + this.startTitle + "</TD><TD><INPUT TYPE='text' NAME=\"" + this.name + "Start\" SIZE=\"5\" MAXLENGTH=\"5\" VALUE=" + this.start + " ONCHANGE=\"" + this.name + ".setStart(this.value)\"></TD>" +
        "<TD><INPUT TYPE='IMAGE' SRC='images/bltbtn.gif' WIDTH=22 HEIGHT=22 ALT='" + tlAquire + "' ONCLICK=\"return " + this.name + ".setCurrentStart()\"></TD></TR>";
        endBody =
        "<TR><TD>" + this.endTitle + "</TD><TD><INPUT TYPE='text' NAME=\"" + this.name + "End\" SIZE=\"5\" MAXLENGTH=\"5\" VALUE=" + this.end + " ONCHANGE=\"" + this.name + ".setEnd(this.value)\"></TD>" +
        "<TD><INPUT TYPE='IMAGE' SRC='images/bltbtn.gif' WIDTH=22 HEIGHT=22 ALT='" + tlAquire + "' ONCLICK=\"return " + this.name + ".setCurrentEnd()\"></TD></TR>";
        if (this.isStartFirst) {
           document.write(startBody + endBody);
        } else {
           document.write(endBody + startBody);
        }
        document.write("</TABLE></DIV>");
      } else {
         document.write(
         "<IMG SRC=\"" + this.img + "\" WIDTH=" + this.width + " HEIGHT=" + this.height + " BORDER=0>" +
         "<TABLE BORDER=0><TR><TD>");
         startBody = "<TR><TD>" + this.startTitle + "</TD><TD><INPUT TYPE='text' NAME=\"" + this.name + "Start\" SIZE=\"5\" MAXLENGTH=\"5\" VALUE=" + this.start + " ONCHANGE=\"" + this.name + ".setPlainStart(this.value)\"></TD></TR>";
         endBody = "<TR><TD>" + this.endTitle + "</TD><TD><INPUT TYPE='text' NAME=\"" + this.name + "End\" SIZE=\"5\" MAXLENGTH=\"5\" VALUE=" + this.end + " ONCHANGE=\"" + this.name + ".setPlainEnd(this.value)\"></TD>";
         if (this.isStartFirst) {
             document.write(startBody + endBody);
         } else {
             document.write(endBody + startBody);
         }
         document.write("</TR></TABLE>");
      }
   }

   function setStart(angle) {
      rang = this.toFisicalAngle(angle);
      ny = this.cnty-5+this.rad*Math.sin(rang*Math.PI/180);
      nx = this.cntx-5+this.rad*Math.cos(rang*Math.PI/180);
      eval("oa = document.all." + this.name + "Start.value");
      tEnd = eval("document.all." + this.name + "End.value");
      if (this.isAmpValidAngle(angle, angle, tEnd)) {
         moveTo(this.name + "startMarker",nx,ny);
         eval("document.all." + this.name + "Start.value=Math.round(angle)");
      } else {
         eval("document.all." + this.name + "Start.value=" + this.start);
         alert(erWrongAngle + " (" + angle + ")\n " + this.title + "\n " + this.startTitle);
      }
   }
   
   function setEnd(angle) {
      rang = this.toFisicalAngle(angle);
      ny = this.cnty-5+this.rad*Math.sin(rang*Math.PI/180);
      nx = this.cntx-5+this.rad*Math.cos(rang*Math.PI/180);
      eval("oa = document.all." + this.name + "End.value");
      tStart = eval("document.all." + this.name + "Start.value");
      if (this.isAmpValidAngle(angle, tStart, angle)) {
         moveTo(this.name + "endMarker",nx,ny);
         eval("document.all." + this.name + "End.value=Math.round(angle)");
      } else {
         eval("document.all." + this.name + "End.value=" + this.end);
         alert(erWrongAngle + " (" + angle + ")\n " + this.title + "\n " + this.endTitle);
      }
   }
   
   function setPlainStart(angle) {
      (NN) ? eval("el = 'document.forms." + this.form + ".'") : eval("el = 'document.all.'");
      tEnd = eval(el + this.name + "End.value");
      if (this.isAmpValidAngle(angle, angle, tEnd)) {
         eval(el + this.name + "Start.value=angle");
      } else {
         eval(el + this.name + "Start.value=" + this.start);
         alert(erWrongAngle + " (" + angle + ")\n " + this.title + "\n " + this.startTitle);
      }
   }
   
   function setPlainEnd(angle) {
      (NN) ? eval("el = 'document.forms." + this.form + ".'") : eval("el = 'document.all.'");
      tStart = eval(el + this.name + "Start.value");
      if (this.isAmpValidAngle(angle, tStart, angle)) {
         eval(el + this.name + "End.value=angle");
      } else {
         eval(el + this.name + "End.value=" + this.end);
         alert(erWrongAngle + " (" + angle + ")\n " + this.title + "\n " + this.endTitle);
      }
   }

   function isAmpValidAngle(angle, cs, ce) {
     if (!cs) {
        return true;
     }
     if (!ce) {
        return true;
     }
     return (( angle <= this.end ) && ( angle >= this.start ) && (ce >= cs));
   }

   function setCurrentStart() {
      this.setStart(this.current);
      return false;
   }
   
   function setCurrentEnd() {
      this.setEnd(this.current);
      return false;
   }
   
   function initAmpArc() {
      this.rotateTo(this.initHandleValue);
      this.setStart(this.start);
      this.setEnd(this.end);
      this.current = this.initHandleValue;
   }

document.write(
   "<STYLE>" + 
   ".dvizhok {" +
      "position: absolute;" +
      "width: 10px;" +
      "height: 10px;" +
      "cursor: hand;" +
      "z-index: 2;" +
   "}" +
   ".arcInput {" +
      "border: 1px solid black;" +
      "position: relative;" +
      "text-align: center;" +
   "}" +
   "#ampControls {" +
      "background-color: #dddddd;" +
   "}" +
   ".marker {" +
      "position: absolute;" +
      "width: 11px;" +
      "height: 11px;" +
      "z-index: 1;" +
   "}" +
   "</STYLE>");
