////////////////////////////////////////////////////////////////////////////////
// js.js part 1
////////////////////////////////////////////////////////////////////////////////

//////////////////////////////////////////////
//                ATENÇÃO!!!!               //
//                                          //
// Após alterar esse arquivo, você DEVE     //
// rodar o compactador em:                  //
//                                          //
// shell# /home/PerlLib/bin/zipsrc.sh       //
//////////////////////////////////////////////

var layers_visible = [],
    top_interface = '0',
    copy_from_formid = "",
    DEBUG_MODE = false && confirm('Ativar modo de debug?'), // Exclusivo para debug.
    DEFAULT_TIMEOUT = 60 * 1000,
    //
    // Armazena uma lista com o formado "<id do elemento>@<URL>" informando quais URL foram lidas em quais elementos
    // Essa lista eh utilizada pela funcao requestURL() para evitar requisicoes desnecessarias sejam feitas quando a parametro
    // 'once' eh especificado verdadeiro.
    //
    requestURLdone = [],
    requestURLold  = [],
    LEFT,RIGHT,moz,
    POST,GET,XML,TEXT,tip;

function Do(code) { return code() }

if ( window.Ultra == null ) window.Ultra = new Object;

Ultra.Error = function (message) {
 this.message = message;
};

Ultra.Error.prototype.toString = function () {
 return ( this.message != null ? this.message : "Plune gerou excessão desconhecida.");
}

// Classe que armazena z-index de elementos flutuantes.
window.Ultra.Floating = new Object;

window.Ultra.Floating.maxZIndex = 0;

// Script.path = "/Html/javascript";
//
// Script.add(
//  "prototype",
//  "calendar",
//  "myCalendar",
//  "calendar-br"
// //  "ContextMenu"
// );
//
// Script.path = "/Html/JS";
//
// Script.add(
//  "Prototype",
//  "CrossBrowser",
//  "ContextMenu",
//  "Cast",
//  "QueryString",
//  "AJAX",
//  "form",
//  "Menu",
//  "UltraClass",
//  "ChoiceDialog"
// );

window.status  = '';

// NÃO USADO
/*String.prototype.isJSON = function () {
 var str = this.replace(/\\./g, '@').replace(/"[^"\\\n\r]*"/g, '');
 return (/^[,:{}\[\]0-9.\-+Eaeflnr-u \n\r\t]*$/).test(str);
};*/

//
// Função chamada no inicio do processo de uma classe.
//
function init() {
 // Se for pop-up, seta background.
 if (opener) $(document.body).addClassName('AllBody');
}

////////////////////////////////////////////////////////////////////////////////
// Prototype.js
////////////////////////////////////////////////////////////////////////////////

/*

1. String's Prototypes

   1.1 A Função 'any'

       Está função serve para comparar uma string com elementos de um array. Há ainda a opção de se fazer esta comparação, ignorando maiuscular/minusculas.

       1.1.1 Parametros

             - ary  : array com elementos.
             - flag : flag indicando se deve ignorar maiusculas e minusculas.

       1.1.2 Retorno

             Esta função retorna valor booleano, indicando se o valor da string se compara com um ou mais elementos contidos no array passado.

*/
String.prototype.any = function (ary, flag) {
 return ary.include(this);
 /*for (var i = 0; i < ary.length; i ++)
  if ((flag && String(ary[i]).toUpperCase() == this.toUpperCase()) ||
      (ary[i] == this))
   return true;

 return false*/
}

/*

1.2 A Função 'splice'

    Está função tem o mesmo objetivo da função splice para arrays, porém esta é para manipular caracteres de uma string.

    1.2.1 Parametros

          - i      : índice a ser removido
          - offset : comprimento a ser removido a ir do índice
          - str    : string a ser colocada no lugar

    1.2.2 Retorno

          A função retorna uma string com o resultado a partir dos parametros passados a ela.

*/
String.prototype.splice = function (i, offset, str) {
 if (i < 0)
  i = this.length + i;

 return this.substr(0, i) + (str || "") + this.substr(i + offset, this.length)
}

/*

1.3 A Função 'pad'

    Esta função serve para completar a string com um caractere ou mais. Por padrão isso é feito no lado esquerdo.

    1.3.1 Parametros

          - str  : caractere ou conjunto que completará a string.
          - n    : comprimento no qual a string será completada.
          - flag : flag para informar que deve-se completar pela direita ao invés da esquerda.

    1.3.2 Retorno

          A função retorna uma string com o resultado a partir dos parametros passados a ela.

*/
LEFT = 1, RIGHT = 2; // Constantes que definem os lados.

String.prototype.pad = function (str, n, side) {
 // considerando o exemplo 'foo'.pad('xy', 10);
 var pad = "",
     len = n - this.length,// 10 - 3 = 7
     _int,mod,i;

 if (typeof str != "string")
  str = String(str);

 if (len < 1)
  return this.substr(0, n);

 _int = Math.floor(len / str.length), // parseInt(7 / 2) = 3
     mod = len % str.length;             // 7 % 2 = 1

 for (i = 0; i < _int; i ++)
  pad += str;

 pad += str.substr(0, mod);

 // receberá 'xyxyxyxfoo'
 switch (side) {
  case LEFT  : return pad  + this;
  case RIGHT : return this + pad;
  default    : throw "invalid side: "+side
 }
};

// Aliases para completar a esquerda ou direita.
String.prototype.lpad = function (str, n) { return this.pad(str, n, LEFT) }
String.prototype.rpad = function (str, n) { return this.pad(str, n, RIGHT) }

String.prototype.asId = function () { return document.getElementById(this) };

/*

2. Array's Prototypes

   2.1 A Função 'grep'

       A função grep possui a mesma funcionalidade da função grep no perl.

       [1, 2, 3, 4].grep('return e % 2'); // retornará somente os elementos impares.

       2.1.1 Parametros

             - func : string ou função que será executada nos elementos do array.
                      A está função são passados o elemento, seu índice e o próprio array.

       2.1.2 Retorno

             Retorna um novo array com os elementos que passaram pela função.

*/
// DESUSO
/*Array.prototype.grep = function (func) {
 var ary = [],i;

 if (typeof func == "string")
  func = new Function("e", "i", "a", func)

 for (i = 0; i < this.length; i ++)
  if (func(this[i], i, this))
   ary.push(this[i]);

 return ary
};*/

/*

2.2 A Função 'map'

    Esta função também funciona da mesma forma que a função map do perl.

    [1, 2, 3, 4].map('return e * 2'); // retornará 1, 4, 6, 8.

    2.1.1 Parametros

          - func : string ou função que será executada nos elementos do array.
                   A está função são passados o elemento, seu índice e o próprio array.

    2.1.2 Retorno

          Retorna um novo array com os resultados de cada elemento.

*/
// DESUSO
/*Array.prototype.map = function (func) {
 var ary  = [],i;

 if (typeof func == "string")
  func = new Function("e", "i", "a", func);

 for (i = 0; i < this.length; i ++)
  ary.push(func(this[i], i, this));

 return ary
};*/

////////////////////////////////////////////////////////////////////////////////
// CrossBrowser.js
////////////////////////////////////////////////////////////////////////////////

moz = !document.all; // Flag que identifica se é mozilla ou internet explorer.

function keyCode   (e) { return moz ? e.which : e.keyCode }       // Retorna o código da tecla digitada.
function keyChar   (e) { return String.fromCharCode(keyCode(e)) } // Retorna o caractere da tecla digitada.
function getTarget (e) { return moz ? e.target : e.srcElement }   // Retorna o elemento corrente.

function fireEvent (element, event) {
 return $(element).callEvent(event);
}

//
// Deprecated functions.
//
// function scrollLeft () { return document.documentElement.scrollLeft } // Distancia da barra de rolagem horizontal da esquerda.
// function scrollTop  () { return document.documentElement.scrollTop }  // Distancia da barra de rolagem vertical do topo.
//
// function windowWidth ()  { return document.body.clientWidth }
// function windowHeight () { return document.body.clientHeight }
/*Do(function () {
 var deprecated = $H({
  scrollLeft   : 'document.documentElement.scrollLeft',
  scrollTop    : 'document.documentElement.scrollTop',
  windowWidth  : 'document.body.clientWidth',
  windowHeight : 'document.body.clientHeight'
 });

 deprecated.each(function (i) {
  window[i.key] = function () { throw i.key + '() is deprecated. use ' + i.value + ' property instead.' }
 });
});*/

/*

1. Manipulação de Eventos

   1.1 A Função 'addEvent'

       Como o nome já diz, esta função adiciona eventos a elementos javascript de forma transparente com relação ao browser usado.

       1.1.1 Parametros

             - el    : elemento DOM.
             - event : nome do evento, sem o prefixo 'on' na frente. Isto porque a função dos browsers da familia mozilla recebem seu nome sem este prefixo.
             - func  : função a ser adicionada ao evento.

*/
function addEvent ( el, event, func ) {
 return Event.observe(el,event,func);
 /*if ( moz ) {
  if ( event.indexOf( "on" ) == 0 ) {
   event = event.substr( 2 );
  }

  el.addEventListener( event, func, false );
 } else {
  if ( event.indexOf( "on" ) != 0 ) {
   event = "on" + event;
  }

  el.attachEvent( event, func );
 }*/
}

/*
1.2 A Função 'removeEvent'

    Faz o reverso da função 'addEvent', removendo referencias de funções, de eventos.

    1.2.1 Parametros

          - el    : elemento DOM.
          - event : nome do evento, também sem o prefixo 'on'.
          - func  : referencia a uma função que exista no evento.

          var el = document.forms[0].elements[-1];

          function foo () {
           alert(1);

           el.removeEvent('click', foo);
          }

          el.addEvent('click', foo);

*/
function removeEvent ( el, event, func ) {
 return Event.stopObserving(el,event,func);
 /*if ( moz ) {
  if ( event.indexOf( "on" ) == 0 ) {
   event = event.substr( 2 );
  }

  el.removeEventListener( event, func, false );
 } else {
  if ( event.indexOf( "on" ) != 0 ) {
   event = "on" + event;
  }

  el.detachEvent( event, func );
 }*/
}

////////////////////////////////////////////////////////////////////////////////
// ContextMenu.js
////////////////////////////////////////////////////////////////////////////////

//
// Context menu.
//
// @author Matheus Lorenzoni Cruz
// @date   2007-05-07
//
Ultra.ContextMenu = Class.create();

Object.extend( Ultra.ContextMenu, {
 //
 // Sincroniza posição vertical do elemento
 //
 synchPosition: function (menu) {
  menu = $(menu);

  var s      = this.getScreen(),
      z      = this.getZeroPosition( menu ),
      top    = parseInt(menu.getStyle('top') || 0) + z.y,
      bottom = top + menu.offsetHeight,
      offset = bottom - s.bottom,
      realTop;

  // Se está excedendo, reposiciona
  if ( offset > 0 ) {
   realTop  = Math.max( parseInt(menu.getStyle('top') || 0) - offset, 0 );
   menu.setStyle({top: realTop + 'px'});
  }

 },

 event: 'contextmenu',

 isFromSI: false, // Excessão para Html::SuperInterface

 idPreffix: 'b-',

 delay: 100,

 callWith: function ( menu, callee, evt, x, y ) {
  evt = evt || this.event;

  var code = this.showEvent.bindAsEventListener( menu, this, x, y );

  Event.observe( callee, evt, code );

  return code;
 },

 showEvent: function ( e, ContextMenu, x, y ) {
  x = x != null ? x : e.clientX;
  y = y != null ? y : e.clientY;

  if ( !ContextMenu.fake ) {
   this.show( x, y );
  } else {
   delete ContextMenu.fake;
  }

  Event.stop( e );
 },

 // Inicializa item.
 initItem: function ( menu, item, itemContent ) {

  var mouseOverEvent,hideEvent,ContextMenu; // Evento.

  // Item possui conteúdo ( submenu )
  if ( itemContent != null ) {
   hideEvent = this.hideItemContent.bind( itemContent );

   mouseOverEvent = this.showItemContent.bind( menu, this, item, itemContent, hideEvent );

   itemContent.link = this.link.bind( itemContent );

  // Não possui; apenas esconde o item ativo anteriormente.
  } else {
   ContextMenu = this;

   mouseOverEvent = function ( e ) {
    item.t = setTimeout(function () { if ( menu.fireEvent( 'onChange' ) === false ) Event.stop( e ) }, ContextMenu.delay);
   }
  }

  Event.observe(item, 'mouseover', mouseOverEvent);
  Event.observe(item, 'mouseout', function () { clearTimeout(item.t) });
 },

 //
 // Associa um menu ao outro em tempo de execução.
 // Util para carregar multiplos niveis em AJAX.
 //
 // @param menu menu a ser linkado
 //
 link: function ( menu ) {
  this.listen( 'onHide', function () { return menu.fireEvent( 'onHide' ) !== false } );
 },

 //
 // Retorna as condições atuais da tela.
 //
 getScreen: function () {
  var left   = window.pageXOffset || document.documentElement.scrollLeft   || document.body.scrollLeft,   // Canto esquerdo.
      top    = window.pageYOffset || document.documentElement.scrollTop    || document.body.scrollTop,    // Topo.
      width  = self.innerWidth    || document.documentElement.clientWidth  || document.body.clientWidth,  // Largura da janela.
      height = self.innerHeight   || document.documentElement.clientHeight || document.body.clientHeight, // Altura da janela.
      right  = left + width,                          // Canto direito.
      bottom = top  + height;                         // Fundo; parte de baixo.

  return { 'top'   : top,
           'left'  : left,
           'width' : width,
           'height': height,
           'right' : right,
           'bottom': bottom };
 },

 getZeroPosition: function ( element ) {
  var parent = element.parentNode,
      x      = 0,
      y      = 0;

  // Sincroniza posição 0.
  while ( parent != null && parent.nodeType == 1 ) {
   if ( $( parent ).getStyle( 'position' ) == 'absolute' && ( !moz || parent.nodeName != 'TABLE' ) ) {
    x += parseInt( $( parent ).getStyle( 'left' ) || 0 );
    y += parseInt( $( parent ).getStyle( 'top' )  || 0 );
   }

   parent = parent.parentNode;
  }

  return { 'x': x, 'y': y };
 },

 //
 // Exibe menu na posição especifica.
 //
 // @param this   menu element
 //        x      left position
 //        y      top  position
 //        ignore flag para ignorar tratamento de tela (menu aparecer fora da tela)
 //
 show: function ( ContextMenu, x, y, ignore ) {
  if ( !( ContextMenu.fireEvent( 'onShow' ) && this.fireEvent( 'onShow' ) ) ) return;

  $( this ).setStyle( { display: '' } );

  var s = ContextMenu.getScreen(),width,height,z,$this;

  x += s.left; y += s.top;

  if (!ignore) {
   width  = x + this.offsetWidth;  // Largura do conteúdo do item.
   height = y + this.offsetHeight; // Altura do conteúdo do item.

   // Conteúdo do item passou do canto direito.
   if ( width > s.right ) x -= width - s.right;

   // Conteúdo do item passou o fundo.
   if ( height > s.bottom ) y -= height - s.bottom;

   // Posiciona sempre dentro da tela.
   x = Math.max( s.left, x );
   y = Math.max( s.top, y );
  }

  z = ContextMenu.getZeroPosition( this );

  x -= z.x; y -= z.y;

  // Posiciona e exibe.
  $( this ).setStyle( { top       : y + 'px',
                        left      : x + 'px',
                        zIndex    : ++ window.Ultra.Floating.maxZIndex,
                        visibility: 'visible' } );

  // Prepara menu para ser escondido.
  $this = this;

  // Seta eventos para ocultar menu.
  ContextMenu.hideSettings.events.each( function ( evt ) {
   Event.observe( ContextMenu.hideSettings.element, evt, $this.hide );
  } );

  ContextMenu.listen( 'onShow', function () {
   $this.hide();

   this.unlisten( 'onShow', arguments.callee );
  } );

  ContextMenu.listen( 'onClose', function () {
   $this.hide();

   this.unlisten( 'onClose', arguments.callee );
  } );

  this.visible = true;

  return true;
 },

 hideActive: function () {
  return this.fireEvent( 'onClose' );
 },

 showAtEvent: function ( e ) {
  return this.show( e.clientX, e.clientY );
 },

 //
 // Exibe ContextMenu alinhado abaixo do elemento
 //
 // @param element
 //        elementType -- tipo do elemento (se especificado dá up() até achar um elemento desse tipo para alinhar)
 //
 showAligned: function ( e, elType ) {
  var el = Event.element( e );
  if ( elType && ( String( el.nodeName ).toUpperCase() != String( el.nodeName ).toUpperCase() ) ) { el = $(el).up(elType) };
  var coords = Position.page(el);
  return this.show( coords[0], coords[1] + $(el).getHeight(), true );
 },

 // Oculta menu.
 hide: function ( ContextMenu ) {
  if ( ContextMenu.fake ) {
   delete ContextMenu.fake;

   return;
  } else if ( ! ( ContextMenu.fireEvent( 'onHide' ) && this.fireEvent( 'onHide' ) ) ) {
   return;
  }

  $( this ).setStyle( { display: 'none', visibility: 'hidden', height: '' } );

  // Não esconde mais o menu ao clicar na página. Ele já está oculto.
  var $this = this;

  ContextMenu.hideSettings.events.each( function ( evt ) {
   Event.stopObserving( ContextMenu.hideSettings.element, evt, $this.hide )
  } );

  this.visible = false;

  return true;
 },

 // Configurações padrão de exibição do menu.
 hideSettings: {
  events: [ 'mousedown', 'contextmenu' ], // Eventos que ocultam. IE não encara contextmenu como mousedown.

  element: document // Elemento que chama o menu
 },

 hideItemContent: function () {
  this.fireEvent( 'onHide' );

  $( this ).setStyle( { display: 'none', visibility: 'hidden', height: '' } );
 },

 showItemContent: function ( ContextMenu, item, itemContent, hideEvent ) {
  var _this = this;


  item.t = setTimeout(function () {
   // Evita de fazer o menu ficar piscando e pulando.
   if ( $( itemContent ).getStyle( 'visibility' ) == 'visible' ||
        ! itemContent.fireEvent( 'onShow' ) ||
        ! _this.fireEvent( 'onChange' ) ) return;

   _this.listen( 'onHide', hideEvent );

   _this.listen( 'onChange', function () {
    hideEvent();

    _this.unlisten( 'onHide', hideEvent );
    _this.unlisten( 'onChange', arguments.callee );
   } );

   $( itemContent ).setStyle( { display: '' } );

   var s = ContextMenu.getScreen(),
       z = ContextMenu.getZeroPosition( _this ),
       menuXPos = parseInt( $( _this ).getStyle( 'left' ) || 0 ) + z.x,                 // Posição x do menu.
       x        = menuXPos + item.offsetLeft + _this.offsetWidth,                       // Posição x.
       y        = parseInt( $( _this ).getStyle( 'top' ) || 0 ) + z.y + item.offsetTop, // Posição y.
       width  = x + itemContent.offsetWidth,  // Largura do conteúdo do item.
       height = y + itemContent.offsetHeight; // Altura do conteúdo do item.

   // Conteúdo do item passou do canto direito.
   if ( width > s.right ) x -= _this.offsetWidth + itemContent.offsetWidth;
   else x -= 10;

   // Conteúdo do item passou o fundo.
   if ( height > s.bottom ) y -= height - s.bottom;

   // Posiciona sempre dentro da tela.
   x = Math.max( s.left, x );
   y = Math.max( s.top, y );

   // Sincroniza posição real.
   x -= z.x;
   y -= z.y;

   // Posiciona e exibe.
   $( itemContent ).setStyle( { top       : y + 'px',
                                left      : x + 'px',
                                zIndex    : ++ window.Ultra.Floating.maxZIndex,
                                visibility: 'visible' } );
  }, ContextMenu.delay);

 },

 listeners: { onShow: [], onHide: [], onClose: [] },

 listen: function ( event, handler ) {
  var listeners = this.listeners[event];

  if ( ! listeners ) throw 'Unknow event '+event+'!';

  if ( ! listeners.include( handler ) ) {
   listeners.push( handler );

   return true;
  } else {
   return false;
  }
 },

 unlisten: function ( event, handler ) {
  var listeners = this.listeners[event];

  if ( ! listeners ) throw 'Unknow event '+event+'!';

  if ( listeners.include( handler ) ) {
   this.listeners[event] = listeners.without( handler );

   return true;
  } else {
   return false;
  }
 },

 fireEvent: function ( event ) {
  var listeners = this.listeners[event],done,$this;

  if ( ! listeners ) throw 'Unknow event '+event+'!';

  done  = true;
  $this = this;

  listeners.each( function ( listener ) {
   if ( listener.apply( $this ) === false ) {
    done = false;

    throw $break;
   }
  } );

  return done;
 }
} );

Ultra.ContextMenu.prototype.initialize = function ( menu, items, attr ) {
 var ContextMenu = window.Ultra.ContextMenu;

 menu = $( menu ); // Menu ( elemento ).

 // Impede usuário de clicar no menu e esconde-lo.
 ContextMenu.hideSettings.events.each(
  function ( evt ) {
   Event.observe( menu, evt, function () {
    ContextMenu.fake = true;
   }
  )
 } );

 // Função para esconder item ativo.
 menu.show        = ContextMenu.show.bind( menu, ContextMenu ); // Handler de exibição do menu.
 menu.showAtEvent = ContextMenu.showAtEvent.bind( menu );       // Handler de exibição do menu.
 menu.showAligned = ContextMenu.showAligned.bind( menu );       // Handler de exibição do menu.
 menu.hide        = ContextMenu.hide.bind( menu, ContextMenu ); // Handler para ocultar menu.
 menu.synchPosition= ContextMenu.synchPosition.bind( menu );

 // Listeners.
 menu.listen    = ContextMenu.listen.bind( menu );
 menu.unlisten  = ContextMenu.unlisten.bind( menu );
 menu.listeners = { onShow: [], onHide: [], onChange: [] };
 menu.fireEvent = ContextMenu.fireEvent.bind( menu );

 // Percorre array de itens para inicializa-los.
 items.each( function ( itemName ) {
  var item = $( ContextMenu.idPreffix + itemName );

  if ( item == null ) return;

  ContextMenu.initItem( menu, item, $( itemName ) );
 } );

 // Vincula a outro menu, se este for passado.
 if ( attr.linker != null ) {
  $( attr.linker ).link( menu );
 // Seta caller; quem chama o menu.
 } else if ( attr.caller != null ) {
  ContextMenu.callWith( menu, attr.caller, attr.event );
 }

 Try.these(function () {
  var init = String(menu.firstChild.getAttribute("oninit"));

  if (init) new Function(init).apply(menu);
 });
};

////////////////////////////////////////////////////////////////////////////////
// Cast.js
////////////////////////////////////////////////////////////////////////////////

// Script.add("Prototype");

//
// Converte um formulário Plune em uma HASH.
//
// @param FORM       Formulário (elemento)
//        Field_Only Boolean. Define se apenas os campos não iniciados em "_" deverão ser considerados.
//
function FormToHash ( p, field_only ) {
 var myhash = new Object(),i,j;

 // percorre os elementos do formulário
 for (i = 0; i < p.elements.length; i ++) {
  // com base no elemento atual...
  with (p.elements[i]) {
   // se não possuir nome ou estiver desativado ou for uma propriedade especial e usuário só quer os campos válidos, passa para o proximo elemento.
   if ( name == undefined || disabled == true ||
        ( field_only && name.charAt(0) == '_' ) )
    continue;

   // se for um radio ou um checkbox e estiver checado, ou for um hidden, ou texto, ou textarea, ou select-one ou password, adiciona o valor e o nome a estrutura da instancia.
   if ((["radio", "checkbox"].include(type) && checked) ||
       ["hidden", "text", "textarea", "select-one", "password"].include(type))
    // Se for uma lista
    if ( p.elements[name].length ) {
     if ( ! myhash[name] ) myhash[name] = new Array(); // Cria a array de necessário
     myhash[name].push( value );
    // Se for um parâmetro simples
    } else {
     myhash[name] = value;
    }
   // se for um select-multiple, adiciona o nome e os valores selecionados a estrutura.
   else if (type == "select-multiple")
    for (j = 0; j < childNodes.length; j ++)
     if (childNodes[j].selected)
      myhash[name] = childNodes[j].value;
  }
 }

 return myhash;
}

/*

1 Conversão de Array Objeto Para Array

  Esta função recebe um array objeto e retorna um array com base no parametro.

  aryobjToAry(document.forms[0].elements); // retornará um array com os elementos do formulário

  Dessa forma é possivel aplicar funções de array em collections.

*/
function aryobjToAry (aryobj) {
 return $A(aryobj);
 /*var ary = [],i;

 for (i = 0; i < aryobj.length; i ++)
  ary.push(aryobj[i]);

 return ary;*/
}

/*

2 Conversão de Elementos do Tipo 'form' Para Objetos

  Esta função é especifica para converter elementos do tipo 'form' em uma estrutura.

  2.1 Parametros

      - elform : elemento DOM com o atributo 'type' igual a 'form'.

  2.2 Retorno

      Retorna um objeto.

*/
function elformToObj (elform) {
  return Form.Methods.serialize(elform,true);
//  return $(elform).serialize(true);
/* var type = String(elform.getAttribute("type")),
    select,textarea,elements,hidden,i,j,
    oldel,parent,options,newel,
    input,obj,el,k,v;

 // Se elemento não possuir atributo 'type' igual a 'form', gera excessão.
 if (type.toLowerCase() != "form")
  throw "Este elemento não é do tipo form.";

 // Monta arrays com combos e textareas que não estejam dentro de um outro formulário.
 select   = aryobjToAry(elform.getElementsByTagName("select")).grep("return e.name && !e.form"),
 textarea = aryobjToAry(elform.getElementsByTagName("textarea")).grep("return e.name && !e.form"),
 elements = select.concat(textarea),
 hidden   = [];

 // Percorre esses elementos.
 // Este loop faz um processo de criação de inputs invisiveis (hidden), para que depois
 // seja construido um objeto com base somente nos elementos input, assim seguindo a ordem certa.
 for (i = 0; i < elements.length; i ++) {
  oldel  = elements[i],                     // Elemento
  parent = oldel.parentNode;                // Parent

  // No caso de um select-multiple, deve percorrer todas as opções selecionadas.
  if (oldel.type == "select-multiple") {
   options = aryobjToAry(oldel.options).grep("return e.selected");

   for (j = 0; j < options.length; j ++) {
    newel = document.createElement("input");

    newel.type  = "hidden";
    newel.name  = oldel.name;
    newel.value = options[j].value;

    hidden.push(newel);

    parent.insertBefore(newel, oldel);
   }
  } else {
   newel  = document.createElement("input"); // Novo elemento

   // Define o novo elemento.
   newel.type  = "hidden";
   newel.name  = oldel.name;
   newel.value = oldel.value;

   // Coloca o novo elemento em um array, para que possa ser excluido no fim da operação.
   hidden.push(newel);

   // Insere o elemento ao lado do elemento antigo.
   parent.insertBefore(newel, oldel);
  }
 }

 // Pega todos os elementos do tipo input.
 input = aryobjToAry(elform.getElementsByTagName("input")).grep("return e.name && !e.form"),
 obj   = {  };

 // Percorre os inputs, montando a estrutura do objeto.
 for (i = 0; i < input.length; i ++) {
  el = input[i], // elemento
  k  = el.name,  // chave
  v  = el.value; // valor

  // Todas as chaves do objeto devem possuir valores de arrays.
  // Se esta chave ainda não existe, seta um array vázio para ela.
  if (!obj[k])
   obj[k] = [];

  // Coloca o valor na chave.
  obj[k].push(v);
 }

 // Exclui todos os elementos criados.
 hidden.map(function (e) { e.parentNode.removeChild(e) });

 return obj*/
}

////////////////////////////////////////////////////////////////////////////////
// QueryString.js
////////////////////////////////////////////////////////////////////////////////

// Script.add("Prototype");

/*

1 QueryString

   Trata-se de uma classe instanciável, com o propósito de manipular querystrings orientado a objeto. Sua maneira de funcionar é muito parecida com o módulo CGI do perl.

   var q1 = new QueryString,                      // receberá a querystring atual da página.
       q2 = new QueryString({ foo: "bar" }),      // usará a estrutura passada como query string.
       q3 = new QueryString("key=value&foo=bar"), // dividirá a string em uma query string estruturada.
       q4 = new QueryString(document.forms[0]),   // montará uma estrutura de acordo com os elementos do formulário e seus valores atuais.

   document.write(q.toString()) // monta uma querystring baseada na estrutura da instancia.

*/
function QueryString () {
 this.reset();
 // se não for passado parametros, pega a querystring atual da página.
 var p = 0 in arguments ? arguments[0] : document.location.search,
    i,j,k,pairs,pair;

 if (!p)
  return;

 // se for um objeto...
 if (typeof p == "object") {
  // Converte elementos do tipo form para objetos (em geral DIVs).
  if (typeof p.childNodes == "object" && p.nodeName != "FORM") {
   p = elformToObj(p);
  }

  // se for um formulário...
  if (typeof p.elements == "object" && p.nodeName == "FORM")
   // percorre os elementos do formulário
   for (i = 0; i < p.elements.length; i ++)
    // com base no elemento atual...
    with (p.elements[i]) {
     // se não possuir nome ou estiver desativado, passa para o proximo elemento.
     if (name == undefined || disabled == true)
      continue;

     // se for um radio ou um checkbox e estiver checado, ou for um hidden, ou texto, ou textarea, ou select-one ou password, adiciona o valor e o nome a estrutura da instancia.
     if ((["radio", "checkbox"].include(type) && checked) ||
         ["hidden", "text", "textarea", "select-one", "password"].include(type))
      this.append(name, value)
      // se for um select-multiple, adiciona o nome e os valores selecionados a estrutura.
     else if (type == "select-multiple")
      for (j = 0; j < childNodes.length; j ++)
       if (childNodes[j].selected)
        this.append(name, childNodes[j].value)
    }
  // se não for um formulário
  else
   // percorre as chaves do objeto, adicionando chave/valor a estrutura da instancia.
   for (var k in p) {
    if ( typeof p[k] == 'object' ) {
     var _this = this;
     $A(p[k]).map(function(e){ _this.append( k, e ) });
    } else {
     this.append(k, p[k]);
    }
   }

 // se for uma string...
 } else if (typeof p == "string") {
  // retira possivel URL e ponto de interrogação do inicio da querystring, dividindo-a por '&' ou ';'.
  pairs = p.replace(/^.*\?/, "").split(/[&;\n]/);

  // percorre a string dividida.
  for (i = 0; i < pairs.length; i ++) {
   // subdivide por '='.
   pair = pairs[i].split("=");

   // a primeira posição é a chave e a segunda é o valor.
   this.append(pair[0], unescape(pair[1]))
  }
 }
}

/*

2 Manipulação de parametros

  2.1 A Função 'param'

      Está função seta valores aos parametros da query string.

      2.1.1 Parametros

            - k : trata-se do nome do parametro
            - v : trata-se do valor do parametro

      2.1.2 Retorno

            A função retorna o parametro setado

*/
QueryString.prototype.param = function (k, v) {
 if (k == undefined) {
  return this.parameters
 } else if (v != undefined) {
  this.reset(k);
  this.append(k, v)
 }

 return this.parameters[k]
}

/*

2.2 A Função 'append'

    Está função adiciona valores aos parametros da querystring. Quando o parametro não existe, é criado.

    2.2.1 Parametros

          Assim como a função 'param', esta também recebe chave e valor.

    2.2.2 Retorno

          Assim como a função 'param', esta também retorna o parametro que recebeu valores.

*/
QueryString.prototype.append = function (k, v) {
 if (!this.param(k))
  this.parameters[k] = [];
 else if (!(this.parameters[k] instanceof Array))
  this.parameters[k] = [this.parameters[k]];

 if (v instanceof String) {
  this.parameters[k].push(v);
 } else if (v != null && typeof v == "object") {
  for (var i = 0; i < v.length; i ++) {
   this.parameters[k].push(v[i]);
  }
 } else {
  this.parameters[k].push(v);
 }

 return this.parameters[k]
}

/*

2.3 A Função 'reset'

    Está função realiza operações de acordo com os parametros passados.

    2.3.1 Nenhum Parametro

          Quando a função não recebe parametros, ela limpa toda a estrutura da instancia.

    2.3.2 Apenas a Chave

          Quando é passado o primeiro parametro, referente ao nome do parametro query string, este possui seu valor resetado.

    2.3.3 Sâo passados chave e índice do valor.

          Quando os dois parametros são passados, o valor contido no índice informado, é retirado do parametro.

*/
QueryString.prototype.reset = function (k, i) {
 if (k == undefined)
  this.parameters = {};
 else if (i == undefined)
  delete this.parameters[k];
 else
  this.param(k).splice(i < 0 ? this.param(k).length + i : i, 1)
}

/*

3 Saída

  3.1 A Função 'toString'

        Esta função monta uma string no formato query string com os parametros contidos na estrutura da instancia.

        var q = new QueryString({ id: 5, name: 'foo' });

        document.write(q.toString()); // irá retornar 'id=5&name=foo'

        Ainda é possível passar um endereço URL para completar o resultado:

        document.write(q.toString('http://mysite.com')); // irá retornar 'http://mysite.com?id=5&name=foo'

*/
QueryString.prototype.toString = function (URL) {
 var QueryString = [],k,i;

 for (k in this.param())
  for (i = 0; i < this.param(k).length; i ++)
   //QueryString.push(escape(k)+"="+escape(this.param(k)[i]));
   QueryString.push(escape(k)+"="+escape(this.param(k)[i]).replace(/\+/g, encodeURIComponent('+')));

 QueryString = QueryString.join("&");

 if (!URL)
  return QueryString
 else if (!/[?&]$/.test(URL))
  URL += /\?/.test(URL) ? "&" : "?";

 return URL + QueryString
}

/*

3.2 A Função 'location'

    Está função serve de redirecionamento de página, utilizando o mecanismo da função 'toString' para montar a URL.

    3.2.1 O parametro URL

          Assim como a função 'toString', esta também pode receber uma URL para completar a querystring.

*/
QueryString.prototype.location = function (URL) {
 document.location.href = this.toString(URL)
}

////////////////////////////////////////////////////////////////////////////////
// AJAX.js
////////////////////////////////////////////////////////////////////////////////

// Script.add("QueryString");

// Constantes representantes dos métodos disponiveis.
POST = "POST", GET  = "GET",
// Constantes representantes dos tipos de resposta.
XML  = "responseXML", TEXT = "responseText";

// Método construtor/inicializador da classe.
function AJAX (method, URL, vars, handler, type) {
 // Seta o método passado.
 this.method = method || GET;
 // Seta a URL passada.
 this.URL = URL || "Class.cgi";
 // Se for uma instancia da classe QueryString, seta o valor. Se não, cria uma instancia da classe QueryString com base no valor.
 if (vars != null && typeof vars == "object" && vars.tagName == "FORM") vars = $(vars).serialize(true);
 this.vars = vars instanceof QueryString ? vars : new QueryString(vars);
 // Seta eventos. O Evento success pode ser o valor passado ou o handler padrão. O evento fail é o handler padrão.
 this.events = { success     : handler || AJAX.events.success,
                 fail        : AJAX.events.fail,
                 loading     : AJAX.events.loading,
                 loaded      : AJAX.events.loaded,
                 interactive : AJAX.events.interactive,
                 complete    : AJAX.events.complete,
                 error       : AJAX.events.error };
 // Tipo de retorno. Default é texto.
 this.type = type || TEXT;
 var i = 1;
 while (i in AJAX.requests) {
  i ++;
 }
 AJAX.lastRequest = i;
 this.id = AJAX.requests[i] = this;
 this.actions = [];
}

//
// Executa JS depois que determidado AJAX completar (ou que o último AJAX completar)
//
AJAX.wait = function (code, id) {
 id = id || this.lastRequest;
 var request = this.requests[id];
//  if (!id || !request) alert(new Ultra.Error("não há uma requisição em espera"));

 // Se não há nenhum AJAX pendente, executa agora
 if (!id) {
  code();
 // Se tem ajax pendente, coloca na fila
 } else {
  request.actions.push(code);
 }
};

AJAX.prototype = { method : null,
                   URL    : null,
                   vars   : null,
                   events : null,
                   type   : null };

AJAX.requests = {};

// Tradução de alguns status da requisição.
AJAX.status = { 1   : "Não foi possível instanciar objeto XMLHTTP.",
                2   : "URL não especificada.",
                3   : "Método de submissão desconhecido.",
                4   : "Tipo de resposta inválido.",
                403 : "Você não tem permissão para esta requisição.",
                404 : "Página não encontrada.",
                500 : "Erro interno do servidor.",
                503 : "Página requisitada não pode ser carregada." };


// Contador de requisições incompletas.
AJAX.counter = 0;

// Eventos padrões da requisição.
AJAX.events = new Object;

AJAX.events.loading = function (reqNum, URL) {
 window.status = "["+ reqNum +"] Carregando URL "+ URL +"..."
};

AJAX.events.complete = function (reqNum, status) {
 if (status == 200)
  window.status = window.AJAX && window.AJAX.counter ? "Requisição n.º "+ reqNum +" concluída. Carregando (resta(m) "+ window.AJAX.counter +")..." : "Concluído.";
 else
  window.status = "Request #"+ reqNum +" falhou. Mensagem de erro"+ (status in AJAX.status ? ": "+ AJAX.status[status] : " desconhecida.")
};

AJAX.events.success = eval;

AJAX.events.fail = function (status) {
 var message =
  "não foi possível fazer requisição.\n\nErro "+status+":\n"+(status in AJAX.status ? AJAX.status[status] : "<Desconhecido>");
 alert(new Ultra.Error(message));
};

AJAX.events.error = function (message) {
 AJAX.counter --;
 if(message.constructor!=Event)
    alert(window.status = message || "A requisição AJAX foi interrompida!");
}


// Método que realiza a requisição.
AJAX.prototype.request = function () {
 var onSuccess,onFail,onLoading,onLoaded,onInteractive,onComplete,onError,
    XMLHTTP,type,vars,URL,method,reqNum,
    currentState,_this,__fail__;
 // Define variáveis com base nos eventos da instancia.
 with (this.events) {
  onSuccess     = success,
  onFail        = fail,
  onLoading     = loading,
  onLoaded      = loaded,
  onInteractive = interactive,
  onComplete    = complete,
  onError       = error
 }

 XMLHTTP; // Instancia do objeto XMLHTTP.
 type = new String(this.type);

 // Tenta instanciar objeto XMLHTTP com base no browser.
 if (window.XMLHttpRequest) {
  XMLHTTP = new XMLHttpRequest;

  if (type == XML && XMLHTTP.overrideMimeType)
   XMLHTTP.overrideMimeType("text/xml")
 } else if (window.ActiveXObject) {
  try {
   XMLHTTP = new ActiveXObject("Msxml2.XMLHTTP")
  } catch (e) {
   try {
    XMLHTTP = new ActiveXObject("Microsoft.XMLHTTP")
   } catch (e) {
    return onFail(1)
   }
  }
 }

 vars = this.vars,
 URL  = this.URL;

 if (!URL)
  return onFail(2);

 // Método em uppercase.
 method = this.method.toUpperCase();

 // Se método for GET, seta a URL com as variáveis concatenadas como querystring.
 if (method == GET && vars) {
  URL = vars.toString(URL);

  // Faz conversão do método se URL passar dos 1024 caracteres.
  if (URL.length > 1024) {
   URL    = this.URL;
   method = POST
  }
 }

 reqNum;

 if (window.XMLHttpRequest)
  XMLHTTP.onerror = onError;

 currentState = 1;
 _this = this, __fail__ = this.events.fail;
 // Método executado durante a requisição
 XMLHTTP.onreadystatechange = function () {
  switch (XMLHTTP.readyState) {
   // Level 1. Executa se tiver handler.
   case 1:
    if (currentState != 1)
     break;
    reqNum = ++ AJAX.counter;

    if (onLoading)
     onLoading(reqNum, URL);

    currentState = 2
   break;
   // Level 2. Executa se tiver handler.
   case 2:
    if (currentState != 2)
     break;

    if (onLoaded)
     onLoaded();

    currentState = 3
   break;
   // Level 3. Executa se tiver handler.
   case 3:
    if (currentState != 3)
     break;

    if (onInteractive)
     onInteractive();

    currentState = 4
   break;
   // Level 4. Trata a resposta.
   case 4:
    if (currentState != 4)
     break;

    try { AJAX.counter -- } catch (e) {}

    var status = XMLHTTP.status,res,header=XMLHTTP.getResponseHeader("PluneException"); // Status da requisição.
    if(header)header=unescape(header);

    if (onComplete) onComplete(reqNum, status);

    // Se teve sucesso, manipula a resposta.
    if (status == 200 && !header) {
     if (![window.XML, window.TEXT].include(type))
      return __fail__(4);

     res = XMLHTTP[type];

     onSuccess(res);
    // Se não, falha.
    } else if(header && onError) {
     return onError(header);
    } else {
     /*var message =
      "não foi possível fazer requisição.\n\nErro "+status+":\n"+(status in AJAX.status ? AJAX.status[status] : "<Desconhecido>");
     alert(new Ultra.Error(message));*/
     return __fail__(status);
    }

    currentState = null;
    _this.actions.invoke("call", _this, XMLHTTP);
    delete AJAX.requests[_this.id];
  }
 };

 // Abre a requisição.
 XMLHTTP.open(method, URL, true);

 // Se método for POST, seta o cabeçalho necessário.
 if (method == POST)
  XMLHTTP.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");

 XMLHTTP.setRequestHeader("Referer", document.location);

 // Testa valor do método
 switch (method) {
  // POST. Envia as variáveis.
  case POST:
   XMLHTTP.send(vars.toString());
  break;
  // GET. Envia nulo, pois as variáveis já estão concatenadas à URL.
  case GET:
   XMLHTTP.send(null);
  break;
  // Outro. Falha.
  default:
   return onFail(3)
 }
 return this.id;
};

////////////////////////////////////////////////////////////////////////////////
// form.js
////////////////////////////////////////////////////////////////////////////////

// Script.add("Prototype", "Node", "Cast");

/*

1 Função de Checagem Dinâmica

    Trata-se de uma função que recebe o formulário e percorre seus elementos, considerando determinados atributos.

    1.1 Atributos do Formulário

          - oncheckstart  : trata-se do evento executado antes de tudo.
          - oncheckbefore : evento executado após a função percorrer e checar todos os elementos do formulário.
          - check         : checagem geral do formulário.
          - oncheckafter  : última ação a ser feita. é executada somente após o formulário estar correto e pronto para submissão.

    1.2 Atributos dos Elementos

          - oncheckbefore : função executada antes das checagens.
          - notnull       : flag que indica se o elemento é not null ou não.
          - pattern       : padrão de expressão regular cujo elemento deve respeitar se possuir valor.
          - onnotnullfail : função executada quando elemento é not null e não possui valor.
          - onpatternfail : função executada quando elemento possui padrão e seu valor não respeita.
          - check         : função de checagem especifica do elemento.
          - oncheckafter  : está função é executada antes da função oncheckafter do formulário, somente no momento em que ele estiver pronto para a submissão.

    1.3 Retorno

          Retorna uma flag indicando se o formulário está pronto ou não para ser submetido.

*/
function checkForm (frm) {
 frm = $(frm);

 var checkstart  = frm.getAttribute("oncheckstart"),  // primeira função a ser executada.
     checkbefore = frm.getAttribute("oncheckbefore"), // primeira função a ser executada após percorrer todos os elementos.
     checkfrm    = frm.getAttribute("check"),         // função de checagem geral do formulário.
     checkafter  = frm.getAttribute("oncheckafter"),  // função executada depois de tudo.
     func,ignoredFields,i,el,checkafterel;

 // executa função de inicio da checagem se existir.
 if (checkstart) {
  func = new Function("form", "frm", checkstart);

  try { func.call(frm, frm, frm.elements) } catch (e) { throw new Error("Error on 'checkstart' form event: "+e); }
 }

 ignoredFields = new Object;

 // percorre os elementos do formulário
 if ($A(frm.elements).any(function (fld) { return checkField($(fld), ignoredFields) === false })) return false;

 // se formulário possui função pré-checagem, executa.
 if (checkbefore) {
  func = new Function("form", "frm", checkbefore);

  try { func.call(frm, frm, frm.elements) } catch (e) { throw new Error("Error on 'checkbefore' form event:"+e); }
 }

 // se formulário possui função de checagem, checa.
 if (checkfrm) {
  func = new Function("form", "frm", checkfrm);

  try { if (func.call(frm, frm, frm.elements) == false)
   return false } catch (e) { throw new Error("Error on 'check' form event: "+e.description); }
 }

 // faz novamente um loop nos elementos do formulário, a fim de executar funções de pós-checagem.
 for (i = 0; i < frm.elements.length; i ++) {
  el           = frm.elements[i],
  checkafterel = el.getAttribute("oncheckafter");

  // se elemento possui função pós-checagem, executa.
  if (checkafterel) {
   func = new Function("el", "form", "frm", checkafterel);

   try { func.call(el, el, frm, frm.elements) } catch (e) { throw new Error("Error on 'checkafter' form element event: "+e); }
  }
 }

 // se formulário possui função pós-checagem, executa.
 if (checkafter) {
  func = new Function("form", "frm", checkafter);

  try { func.call(frm, frm, frm.elements) } catch (e) { throw new Error("Error on 'checkafter' form event: "+e); }
 }

 return true;
}

function checkField(el, ignoredFields) {
 var frm = el.form,
    checkbefore,notnull,pattern,notnullfail,patternfail,check,func,
    notNullAndEmpty,regexp;
 if(!ignoredFields)ignoredFields={};
 if ( el.disabled && el.isChanging ) {
  el.isChanging = el.disabled = false;

  return true;
 }

                                  // Se
 if ( !el.name                    // não possui nome
    || el.disabled                // ou está desativado
    || el.style.display == "none" // ou está oculto
    || el.name in ignoredFields   // ou deve ser ignorado
    /*|| el.type == 'hidden'*/ )  // ou é um hidden
  return true;                    // então verifica o proximo elemento.

 if ( [ "checkbox", "radio" ].include(el.type) )    // Se for do tipo radio ou checkbox
  el = findElementAndIgnore( el, ignoredFields ); // então procura o elemento que estiver checado.

 checkbefore = el.getAttribute("oncheckbefore"),                                      // função executada antes de qualquer coisa.
 notnull     = el.getAttribute("notnull"),                                            // flag indicando se o elemento é not null.
 pattern     = el.getAttribute("pattern"),                                            // padrão de valor do elemento.
 notnullfail = el.getAttribute("onnotnullfail") || frm.getAttribute("onnotnullfail"), // função executada quando elemento é not null e não possui valor.
 patternfail = el.getAttribute("onpatternfail") || frm.getAttribute("onpatternfail"), // função executada quando elemento possui padrão e seu valor não o respeita.
 check       = el.getAttribute("check");                                              // função de checagem especifica do elemento.

 // se o elemento possuir função pré-checagem, executa.
 if (checkbefore) {
  func = new Function("el", "form", "frm", checkbefore);

  try { func.call(el, el, frm, frm.elements) } catch (e) { throw new Error("Error on 'checkbefore' form element event: "+e); }
 }


 notNullAndEmpty =
    notnull                                // É not null?
  ? [ "checkbox", "radio" ].include(el.type) // é um radio um checkbox?
  ? el.checked                             // está checado?
  ? el.value != null && el.value.length    // possui valor?
  ? false                                  // está liberado.
  : true                                   // se não possui valor é not null e está vázio.
  : true                                   // se não está checado é not null e está vázio.
  : el.value != null && el.value.length    // se não é radio ou checkbox e possui valor
  ? false                                  // está liberado.
  : true                                   // se não é radio ou checkbox e não possui valor é not null e está vázio.
  : false;                                 // se não é not null está liberado.

 // se valor do elemento não pode ser nulo, checa.
 if ( notNullAndEmpty ) {
  // se houver função a ser executada, executa.
  if (notnullfail) {
   func = new Function("el", "form", "frm", notnullfail);

   try { func.call(el, el, frm, frm.elements)  } catch (e) { throw new Error("Error on 'notnullfail' form/element event: "+e); }
  }

  return false
 }

 // se campo possui valor, realiza checagens.
 if (el.value != null && el.value.length) {
  // se valor deve respeitar um padrão de expressão regular, checa.
  if (pattern) {
   regexp = new RegExp(pattern);

   // se valor não respeita o padrão, retorna false e tenta executar função.
   if (!regexp.test(el.value)) {
    if (patternfail) {
     func = new Function("el", "form", "frm", patternfail);

     try { func.call(el, el, frm, frm.elements) } catch (e) { throw new Error("Error on 'pattern' form element event: "+e); }
    }

    return false;
   }
  }

  // realiza checagem especifica do campo.
  if (check) {
   func = new Function("el", "form", "frm", check);

   try { if (func.call(el, el, frm, frm.elements) == false)
    return false  } catch (e) { throw new Error("Error on 'check' form event: "+e); }
  }
 }

 return true;
}

//
// Procura o elemento checado entre os radios/checkboxes e o retorna,
// colocando o grupo na lista de campos ignorados.
//
function findElementAndIgnore ( el                // elemento.
                              , ignoredFields ) { // array associativo com campos a serem ignorados.
 var
   form     = el.form                // formulário
 , name     = el.name                // nome do elemento
 , nodeList = form.elements[ name ], // lista de elementos
 i;

 ignoredFields[ name ] = true;

 if (nodeList != null) {
  for ( i = 0           // i é 0 ( zero ).
      ; i < nodeList.length // Enquanto for menor que o tamanho da lista de elementos
      ; i ++ ) {            // então incrementa seu valor.
   el = nodeList[ i ];  // elemento i da lista.

   if ( el.checked ) // se estiver checado
    return el;       // então retorna o elemento i.
  }

  return nodeList[0] || el; // Retorna o primeiro elemento da lista.
 } else {
  return el;
 }
}

////////////////////////////////////////////////////////////////////////////////
// Menu.js
////////////////////////////////////////////////////////////////////////////////

//
// Classe construtora do menu principal.
//
// @see    Ultra::Handler::INDEX::NoFrames::V
// @author Matheus Lorenzoni Cruz <matheus@sistemica.info>
// @date   2007-06-14
//
Ultra.Menu = Class.create();

//
// Método inicializador.
//
Ultra.Menu.prototype.initialize = function (menu_only) {
 var topFrm     = parent.$( "Top" ).contentWindow,     // Frame topo.
     desktopWin = parent.$( "Desktop" ),
     desktopFrm = desktopWin.contentWindow, // Frame desktop.
     icon       = topFrm.$( "Icon" ),                  // Icone topo menu.
     desktopCM  = desktopFrm.Ultra.ContextMenu,        // ContextMenu desktop class.
     mainFrm    = desktopFrm.$( "Main" ),              // Frame principal ( iframe ).
     mainWin    = mainFrm.contentWindow,               // Frame principal ( window ).
     explorerC  = desktopFrm.$('ExplorerContainer'),
     hideDesktopCMHandler,hideMainWinCMHandler,mainWinLoadEvent,
     mainWinUnloadEvent;

 if ( ! menu_only ) {
 // Esconde menus do desktop quando um menu do frame principal for exibido.
 hideDesktopCMHandler = function () {
  desktopCM.hideActive();
 };

 // Esconde menus do frame principal quando um menu do desktop for exibido.
 hideMainWinCMHandler = function () {
  try {
   mainWin.Ultra.ContextMenu.hideActive();

   [mainWin, desktopWin].each(function (win) { try { win.Ultra.ChoiceDialog.dialogs.invoke('close') } catch (e) {} });
  } catch (e) {
  }
 };

 // Ao carregar frame principal, esconde menu atual do desktop e prepara eventos acima.
 mainWinLoadEvent = function () {
  desktopCM.hideActive();

  desktopCM.listen( 'onShow', hideMainWinCMHandler );

  try {
   mainWin.Ultra.ContextMenu.listen( 'onShow', hideDesktopCMHandler );
  } catch (e) {
  }

  Event.observe( topFrm.document, "click", hideMainWinCMHandler );
  Event.observe( mainWin.document, "mousedown", hideDesktopCMHandler );
  Event.observe( mainWin.document, "contextmenu", hideDesktopCMHandler );
 };

 // Ao descarregar, ignora evento.
 mainWinUnloadEvent = function () {
  desktopCM.unlisten( 'onShow', hideMainWinCMHandler );

  Event.stopObserving( topFrm.document, "click", hideMainWinCMHandler );
 };
 }

 // Ícone chama menu ao clicar.
 //  desktopCM.callWith( menu, icon, "mouseover", 0, 0 );
 function iconMouseOverEvent (e) {
/*  alert(2);
  if ( desktopFrm.$( "Explorer" ).$('ExplorerContainer').innerHTML == '' ) return;*/
  if ( !desktopCM.fake ) {
   prepareEvent(icon,function(){
       try {
        desktopFrm.$( "Explorer" ).show( 0, 0 );

        [mainWin, desktopWin].each(function (win) { try { win.Ultra.ChoiceDialog.dialogs.invoke('close') } catch (e) {} });
       } catch (e) {
       }
   },250,'mouseout');
  } else {
   delete desktopCM.fake;
  }

  e && Event.stop( e );
 }

 icon.observe('mouseover', iconMouseOverEvent);

 icon.observe('click', function (e) { Event.stop(e); });

 icon.observe('dblclick', function () {
  desktopCM.hideActive();

  desktopFrm.requestURL('GET', 'Class.cgi?_ClassId=Ultra.INDEX&_Ultra.INDEX.Method=Explorer&_Show=1', null, explorerC);

  icon.hide();

  new Insertion.After(icon, "<img src='/t/default/img/loading.gif' title='Recarregando pacotes do menu principal. Por favor, aguarde...' />");
 });

 if ( ! menu_only ) {
 Event.observe( topFrm.document, "click", function () {
  desktopCM.hideActive();
 } );

 explorerC.observe("click", function (e) {
  var element = Event.element(e),
      link    = element.tagName.toLowerCase() == "a" ? element : element.up("a");
  if (link.target != "self") {
   mainFrm.setStyle({display: "block", zIndex: ++ desktopFrm.Ultra.Floating.maxZIndex});
  }
 });

 //
 // Sempre que o frame principal carregar uma página,
 // ele vai ocultar qualquer menu ao exibir um, assim como o desktop.
 //
 Event.observe( mainFrm, "load"  , mainWinLoadEvent   );
 Event.observe( mainFrm, "unload", mainWinUnloadEvent );
 }
};

Ultra.Menu.reload = function (menu) {
 menu.show(0, 0);

 var icon = parent.$('Top').contentWindow.$('Icon'),
     img  = icon.next('img');

 img.parentNode.removeChild(img);

 icon.show();
}

////////////////////////////////////////////////////////////////////////////////
// UltraClass.js
////////////////////////////////////////////////////////////////////////////////

//
// UltraClass - Classe Javascript para interação com banco de dados.
//
// Esta classe permite acessar a grande maioria de classes e métodos do UltraClass,
// tratando o seu retorno sempre a partir de uma estrutura facil e simples do Javascript interpretar: o JSON.
//
// <code>
//  new Ultra.Class('Ultra.Class').Select({
//   SchemaId: 'Ultra',
//   ClassId: 'Class'
//  }, {
//   onSuccess: function () {
//    this.Field.Description.value;
//   }
//  });
// </code>
//
// @author Matheus Lorenzoni Cruz <matheus@sistemica.info>
// @date   2007-08-08
//
Ultra.Class = Class.create();

Object.extend(Ultra.Class.prototype, {
 //
 // Método inicializador.
 //
 // @param ClassId id da classe
 //
 initialize: function (ClassId) {
  this.ClassId = ClassId;
 },

 //
 // Faz uma requisição.
 // Os parametros estão todos concentrados em uma hash passada como 1º e único parametro.
 //
 // @param method     Nome do método.
 //        parameters Hash com parâmetros.
 //        attributes onSuccess       Função manipuladora do retorno. O contexto (this) é a propria instancia UltraClass.
 //                   onError(errstr) Função manipuladora do retorno quando houve erro. A mensagem de erro fica em this.ErrorStatus,
 //                                   além de vir como primeiro argumento da função (errstr), como um alias.
 //                   onFail          Função manipuladora do erro de requisição AJAX. Veja documentação de Ultra.AJAX.
 //                   onAfterAll      Função executada após tudo, independente de dar erro.
 //
 process: function (method, parameters, attr) {

  parameters = parameters || {}; // Hash com parametros (dados) a enviar.
  attr       = attr       || {}; // Hash com atributos (eventos de sucesso/falha, etc).

  // Converte lista de parâmetros para HASH caso ela seja um form.
  if ( typeof parameters == 'form' ) parameters = FormToHash( parameters );

  var ClassId     = this.ClassId, // Id da classe.
      _parameters = {},           // Conversão de parametros.
      k,_k,_this,req;

  //
  // Percorre parametros para realizar conversões do tipo:
  //
  // 'CompanyId' vira <nome da classe> + '.CompanyId'
  // '_Fake'     vira '_' + <nome da classe> + '.Fake'
  // 'Some.Parameter' continua a mesma coisa, pois possui ponto em seu nome.
  //
  $w('_Level _null __debug__').each(function (param) {
   if (param in parameters) {
    _parameters[param] = parameters[param];

    delete parameters[param];
   }
  });

  for (k in parameters) {
   // Parametro não possui ponto no nome: converte.
   if (!/\./.test(k)) {
        _k =                                          // Nome do parametro convertido.
       k.indexOf('_') == 0                            // Se começa com underline
     ? k.replace(/^(_)(.+)$/, '$1' + ClassId + '.$2') // fica '_' + <nome da classe> + '.' + <nome do parametro>.
     : ClassId + '.' + k;                             // Se não fica <nome da classe> + '.' + <nome do parametro>.

    _parameters[_k] = parameters[k]; // Parametro convertido.

    delete parameters[k]; // Exclui parametro antigo.
   }
  }

  // se Target for um objeto HTML, sinaliza e prepara a requisição para tal!
  if ( attr.targetElement ) {
   parameters['_V']  = 'SOA';
   parameters['_VX'] = 'HTML';
   parameters['_'+ClassId+'.OK']  = 1;
   parameters['_'+ClassId+'.NextMethod']  = 'Select';
  }

  // Extende parametros.
  Object.extend(parameters, _parameters);

  parameters['_ClassId'] = ClassId;
  parameters['_' + ClassId + '.Method'] = method;

  _this = this;

  if (attr.onLoading) attr.onLoading.call(_this);

  var script = attr.targetElement ? document.location.pathname : (attr.keepSession ? 'REST.cgi' : '/Ultra/REST.cgi');

  // Requisição AJAX.
  // req = new AJAX(POST, '/Ultra/REST.cgi', parameters);
  req = new AJAX(POST, script, parameters);

  // Manipula retorno.
  req.events.success = function (res) {

   // Se for uma saída html...
   if ( attr.targetElement ) {
    $(attr.targetElement).update( res );
    return;
   }

   var stash;
   // Valida JSON.
   try {
    stash = typeof res == 'string' ? res.evalJSON() : null;
   } catch (e) {
    stash = {ErrorStatus: e.description};
   } finally {
    Object.extend(_this, stash);

    _this.ClassId = ClassId;

    // Não houve erro.
    if (!_this.ErrorStatus) {
     (attr.onSuccess || Prototype.emptyFunction).call(_this);

    // Houve erro e há um handler para isso.
    } else if (attr.onError) {
     attr.onError.call(_this, _this.ErrorStatus);

    // Gera excessão se não houver tratamento de erro.
    } else {
     lbox( _this.ErrorStatus + '<br>' + _this.ErrorStatus2 );
//     alert( new Ultra.Error( _this.ErrorStatus.replace(/\n.*/gm,'') ) );
//      try { help(null,"<pre style='max-height:200px;max-width:800px;overflow:scroll;'>"+_this.ErrorStatus+'</pre>','/t/c/img/MessageBoxWarning.gif'); } catch (e) {};
    }

    (attr.onAfterAll || Prototype.emptyFunction).call(_this);
   }
  };

  // Handler para falha na requisição.
  req.events.fail = attr.onFail;

  // Requisita.
  req.request();
 },

 //
 // Alias para Select
 //
 Select: function (parameters, attr) { this.process('Select', parameters, attr) },

 //
 // Alias para Call
 //
 Call: function (parameters, attr) { this.process('Call', parameters, attr) },

 //
 // Alias para Browse
 //
 // Eis alguns parametros especiais:
 //  - _BrowseSequence : array com id's das colunas a serem retornadas.
 //  - _Order          : coluna de ordenamento.
 //  - _OrderDesc      : flag para ordenar decrescente.
 //  - _BrowseLimit    : número máximo de registros a serem retornados.
 //  - _Page           : número da página a ser retornada; uma espécie de offset. (Ex.: limit é 30 e page é 2: mostra registros de 31 à 61).
 //  - _ClassReportId  : id do relatório a ser exibido.
 //
 // <code>
 //  new Ultra.Class('Ultra.Class').Browse({
 //   TypeOwner      : 'system',
 //   Type           : 'public',
 //   _BrowseLimit   : 5,
 //   _BrowseSequence: ['Description'],
 //   _Page          : 2,
 //   _Order         : 'Description',
 //   _OrderDesc     : 1
 //  }, {
 //   onSuccess: function () {
 //    alert(this.data.row[4].Description.value);
 //   }
 //  });
 // </code>
 //
 Browse: function (parameters, attr) { this.process('Browse', parameters, attr) },

 //
 // Este método é uma mistura entre o método Brows e o método Select.
 //
 // Tecnicamente ele é um método Browse, porém que rotorna um registro só, utilizando a propriedade _MSelect para isso. Permite portanto todas as demais propriedades dos métodos Browse, em especial a _BrowseSequence.
 // Esse método é um facilitador apra as situaçõe sonde você quer apenas alguns campos da tabela, e não todos, economizando banda de acesso e recursos do servidor portando.
 // O retorno será também como o método browse (this.data.row[0]...).
 //
 MSelect: function (parameters, attr) {

  var H = $H({});
  for ( k in parameters ) {
   if ( k.indexOf('_') != 0 ) {
    H[k] = parameters[k];
    delete parameters[k];
   }
  }

  parameters['_MSelectOnly'] = 1;
  parameters['_MSelect']     = H.toQueryString();

  this.process('Browse', parameters, attr);
 },

 //
 // Alias para Insert
 //
 Insert: function (parameters, attr) { this.process('Insert', parameters, attr) },

 //
 // Alias para Delete
 //
 Delete: function (parameters, attr) { this.process('Delete', parameters, attr) },

 //
 // Alias para Update
 //
 Update: function (parameters, attr) { this.process('Update', parameters, attr) },

 //
 // Alias para MultiExecute
 //
 MultiExecute: function (parameters, attr) { this.process('MultiExecute', parameters, attr) },

 //
 // Alias para MultiInsert
 //
 // <code>
 //  new Ultra.Class('Demo.Demo').MultiInsert({
 //   Id: [1,2,3],
 //   Description: ['teste 1', 'teste 2', 'teste 3']
 //  }, {
 //   onSuccess: function () {
 //    this.Field.Description.value;
 //   }
 //  });
 // </code>
 //
 MultiInsert: function (parameters, attr) { parameters['_XMethod'] = 'MultiInsert'; this.process('Insert', parameters, attr) },

 //
 // Alias para valor do campo
 //
 $v: function (field, index) { return index != null ? this.row[index][field].value : this.Field[field].value },

 //
 // Alias para resolução do campo
 //
 $r: function (field, index) { return index != null ? this.row[index][field].resolved : this.Field[field].resolved },

 //
 // Adiciona valor/resolução de um campo como option de uma combo
 //
 setOption: function (combo, field, selected) {
  combo = $(combo);
  if (field == null) field = combo.FieldId();
  if (combo.setOption) {
   combo.setOption(this.$r(field), this.$v(field), selected);
  } else {
   alert(new Ultra.Error("Campo "+combo.getAttribute("description")+" não é uma combo."));
  }
 },

 //
 // Seta campos com base em um mapeamento
 //
 // @param base input ou form, sendo ele id ou elemento
 //        map  hash com as chaves
 //             s = nome do campo fonte(source) ou função que retorna valor
 //             d = nome do campo destino(destination, utiliza nome do campo fonte como default)
 //             h = nome do handler: value(default), resolved, combo ou number.
 //
 map: function (base,mapping) {
  base = $(base);
  var _this = this,f,k,i,h,a;
  mapping.each(function (m) {
   f = m.s, k = m.d || m.s,
    i = base.field(k),
    a = base.field("__Fake."+i.ClassId()+"."+k),
    h = _this.constructor.maphandler[m.h || "value"];
   if (a) {
    h.call(_this, a, f);
    i.value = a.value;
   } else if (i) {
    h.call(_this, i, f);
   }
  });
 }
});

Ultra.Class.maphandler = {
 value: function (i, f) {
  var v = this.$v(f);
  i.value = v != null ? v : "";
 },

 resolved: function (i, f) {
  var v = this.$r(f);
  i.value = v != null ? v : "";
 },

 number: function (i, f) {
  var v = typeof f == "function" ? f.call(this) : this.$v(f);
  i.value = v != null ? v : "";
  Ultra.Number.number2String.call(i);
 },

 combo: function (i, f) {
  this.setOption(i, f);
 }
};

////////////////////////////////////////////////////////////////////////////////
// ChoiceDialog.js
////////////////////////////////////////////////////////////////////////////////

/*

ChoiceDialog - Caixa de dialogo para escolha de opção.

@author Matheus Lorenzoni Cruz
@date   2007-12-17

*/
Ultra.ChoiceDialog = Class.create();

Object.extend(Ultra.ChoiceDialog, {
 // Constants
 MIN_DIALOG_WIDTH     : 480,        // Largura minima da janela
 MAX_DIALOG_WIDTH     : 640,        // Largura máxima da janela
 MAX_BODY_HEIGHT      : 100,        // Altura máxima do corpo da janela
 MIN_BUTTON_WIDTH     : 100,        // Largura minima dos botões
 DEFAULT_TITLE        : 'Plune',    // Titulo default da janela
 DEFAULT_CANCEL_OPTION: true,       // Por padrão, exibe botão de cancelar
 CANCEL_BUTTON_LABEL  : 'Cancelar', // Label do botão cancelar

 dialogs: [],

 // Métodos extendidos para a janela.
 Method: {
  close: function (force) {
   var dialog = this.dialog,
       bg;

   if (!dialog.showing() ||
       (dialog.choice() && (dialog.choice().handler.call(dialog) === false || !dialog.fire('choose'))) ||
       (dialog.cancel() && !dialog.fire('cancel')) ||
       (!dialog.fire('close') && !force)) return false;

   bg = this.previous('.ChoiceDialogBackground');

   // Listeners nos eventos cancel e/ou close podem ter alterado o corpo da página e exluido esses elementos.
   try {
    document.body.removeChild(bg);
    document.body.removeChild(this);
   } catch (e) {}

   dialog.showing(false);

   (function (dialog) { this.dialogs = this.dialogs.without(dialog) }).call(dialog.constructor, this);

   return true;
  },

  choose: function (value) {
   var dialog = this.dialog;

   if (value != undefined) dialog.choice(dialog.getChoiceByValue(value));

   return this.close();
  },

  cancel: function () {
   if (this.dialog.cancel()) this.dialog.choice(null);

   return this.close();
  }
 }
});

/*
Propriedades da classe - getters e setters

@define title   get/set titulo da janela (default 'Plune')
@define body    get/set corpo/mensagem da janela
@define cancel  get/set opção para exibir botão de cancelar (default true)
@define showing get/set flag que indica se janela está sendo exibda ou não
*/
$w('title body choice cancel showing').each(function (p) {
 var slot = '_' + p;

 Ultra.ChoiceDialog.prototype[p] = function (value) {
  if (value === undefined) {
   return this[slot];
  } else {
   this[slot] = value;

   return this;
  }
 };
});

// Alias para body.
Ultra.ChoiceDialog.prototype.message = Ultra.ChoiceDialog.prototype.body;

// Prototype
Object.extend(Ultra.ChoiceDialog.prototype, {
 // Método inicializador. Extende listas de eventos e prepara lista de opções.
 initialize: function (sets) {
  var klass = this.constructor,k,v,key,val,event;

  this.title(klass.DEFAULT_TITLE).cancel(klass.DEFAULT_CANCEL_OPTION);

  this.choiceList = [];
  this.eventMap   = {};

  (function (dialog) {
   $w('create show choose cancel close').each(function (name) {
    dialog.eventMap[name] = [];
   });
  })(this);

  if (sets) {
   for (k in sets) {
    v = sets[k];

    if (k == 'choice') {
     if (typeof v == 'string') v = $w(v);

     if (v instanceof Array) {
      (function (dialog) { v.each(function (choice) { dialog.addChoice(choice, choice) }) })(this);
     } else if (typeof v == 'object') {
      for (key in v) {
       val = v[key];

       if (typeof val == 'string') {
        val = {text: val};
       } else if (val instanceof Array) {
        val = {text: val[0], handler: val[1], focus: val[2]};
       } else if (typeof val != 'object') {
        val = {text: key, handler: val};
       }

       this.addChoice(key, val.text, typeof val.disabled == 'boolean' ? val.disabled : val.handler, val.focus);
      }
     }
    } else if (k.match(/^on[A-Z]/)) {
     event = k.match(/^on(.+)/)[1].toLowerCase();

     this.listen(event, v);
    } else {
     this[k](v);
    }
   }
  }

  return this;
 },

 /*
 Choice setter

 @param value   valor da opção
        text    Descrição da opção. Se não é passada, usa value como descrição.
        handler true para desabilitar opção ou uma função para ser disparada quando a opção for escolhida.
        focus   true para dar focus no botão quando a janela for exibida.
 */
 addChoice: function (value, text, handler, focus) {
  var choice = {};

  choice.value = new String(value);
  choice.text  = text;
  choice.focus = focus;

  if (typeof handler == 'boolean') {
   choice.disabled = true;
  } else {
   choice.handler = handler || Prototype.emptyFunction;
  }

  this.choiceList.push(choice);

  return this;
 },

 dropChoice: function (choice) {
  if (typeof choice == 'string') choice = this.getChoiceByValue(choice);

  this.choiceList = this.choiceList.without(choice);

  return choice;
 },

 /*
 Retorna choice com base em seu valor

 @param  value valor da choice
 @return choice
 */
 getChoiceByValue: function (value) {
  return this.choiceList.find(function (choice) { return choice.value == value });
 },

 /*
 Adiciona um handler de evento

 @param event   nome válido de evento
        handler função manipuladora
 */
 listen: function (event, listener) {
  this.eventMap[event].push(listener);

  return this;
 },

 /*
 Desvincula um listener de um evento.
 Quando o listener não é passado, desvincula todos.
 */
 unlisten: function (event, listener) {
  if (event) {
   this.eventMap[event] = listener ? this.eventMap[event].without(listener) : [];
  } else {
   for (var k in this.eventMap) {
    this.eventMap[k] = [];
   }
  }

  return this;
 },

 /*
 Dispara os listeners de um evento

 @param event     nome do evento
        context   contexto
        arguments demais argumentos
 */
 fire: function () {
  var args = $A(arguments), event = args.shift(), context = this, status = true;

  this.eventMap[event].each(function (event) { if (event.apply(context, args) === false) { status = false; throw $break; } });

  return status;
 },

 // Prepara criação/exibição da janela
 show: function () {
  if (this.showing() || !this.fire('create')) return false;

  var viewport = {
   top   : window.pageYOffset || document.documentElement.scrollTop    || document.body.scrollTop,
   left  : window.pageXOffset || document.documentElement.scrollLeft   || document.body.scrollLeft,
   width : self.innerWidth    || document.documentElement.clientWidth  || document.body.clientWidth,
   height: self.innerHeight   || document.documentElement.clientHeight || document.body.clientHeight
  },
  bg = (function () {
   new Insertion.Bottom(document.body, "<div></div>");

   return $($A(document.body.childNodes).last());
  })().addClassName('ChoiceDialogBackground')
      .setStyle({
       position: 'absolute',
       top     : viewport.top    + 'px',
       left    : viewport.left   + 'px',
       width   : viewport.width  + 'px',
       height  : viewport.height + 'px',
       zIndex  : ++ window.Ultra.Floating.maxZIndex
      }),
  dialog = this.dialog = (function () {
   new Insertion.Bottom(document.body, "<div></div>");

   return $($A(document.body.childNodes).last());
  })().addClassName('ChoiceDialog')
      .setStyle({visibility: 'hidden'}),
  klass,buttons,button,cancel,w,x,y,choice;

  [bg, dialog].each(function (element) { element.observe('contextmenu', function (e) { Event.stop(e); }) });

  if (moz) dialog.setStyle({'-moz-user-select': 'none'});
  else     dialog.observe('selectstart', function (e) { Event.stop(e) });

  klass = this.constructor;

  new Insertion.Bottom(dialog, "<div class='title'>"+this.title()+"</div>"
                             + "<div class='body'>"+this.body()+"</div>"
                             + "<div class='buttons'></div>");

  buttons = dialog.down('.buttons');

  // Constrói lista de botões com as opções
  this.choiceList.each(function (choice, index) {
   button = (function () {
    new Insertion.Bottom(buttons, "<input type='button' name='"+choice.value.escapeHTML()+"' value='"+choice.text.escapeHTML()+"' />");

    return $($A(buttons.childNodes).last());
   })().addClassName('btn Choice')
       .observe('focus', function () { dialog.dialog.choice(choice); })
       .observe('click', function () { dialog.choose(this.name); });

   if (button.disabled) button.addClassName('Current');

   button.disabled = choice.disabled;

   if (button.getWidth() < klass.MIN_BUTTON_WIDTH) button.setStyle({width: klass.MIN_BUTTON_WIDTH + 'px'});
  });

  if (this.cancel()) {
   cancel = (function () {
    new Insertion.Bottom(buttons, "<input type='button' value='"+klass.CANCEL_BUTTON_LABEL+"' />");

    return $($A(buttons.childNodes).last());
   })().addClassName('btn Choice Cancel')
       .observe('focus', function () { dialog.dialog.choice(null); })
       .observe('click', function () { dialog.cancel(); });

   if (cancel.getWidth() < klass.MIN_BUTTON_WIDTH) cancel.setStyle({width: klass.MIN_BUTTON_WIDTH + 'px'});
  }

  dialog.dialog = this;

  Object.extend(dialog, klass.Method);

  w = Math.min(viewport.width, Math.max(dialog.getWidth(), klass.MIN_DIALOG_WIDTH), klass.MAX_DIALOG_WIDTH),
  x = (viewport.left + (viewport.width/2 ) - (w/2)),
  y = (viewport.top  + (viewport.height/2) - dialog.getHeight());

  dialog.setStyle({
   width     : w + 'px',
   top       : Math.max(y, 0) + 'px',
   left      : Math.max(x, 0) + 'px',
   visibility: 'visible',
   zIndex    : ++ window.Ultra.Floating.maxZIndex
  });

  (function (body) {
   dialog.body = body;

   if (body.getHeight() > klass.MAX_BODY_HEIGHT)
    body.setStyle({height: klass.MAX_BODY_HEIGHT + 'px'})
  })(dialog.down('.body'));

  bg = dialog.previous('.ChoiceDialogBackground');

  [{element: bg, event: 'click'}, {element: window, event: 'scroll'}].each(function (i) {
   Event.observe(i.element, i.event, function (e) {
    dialog[dialog.dialog.cancel() ? 'cancel' : 'close']();

    Event.stopObserving(i.element, e.type, arguments.callee);
   });
  });

  Event.observe(document, 'keydown', function (e) {
   if (dialog.dialog.showing()) {
    if (dialog == klass.dialogs.last() && e.keyCode == Event.KEY_ESC) {
     dialog.cancel();
    }
   } else {
    Event.stopObserving(this, e.type, arguments.callee);
   }
  });

  new Draggable(dialog, {
   handle      : dialog.down('.title'),
   starteffect : Prototype.emptyFunction,
   endeffect   : Prototype.emptyFunction,
   reverteffect: Prototype.emptyFunction
  });

  choice = this.choiceList.find(function (choice) { return choice.focus === true; });

  if (choice)      dialog.down("input[name='"+choice.value+"']").focus();
  else if (cancel) cancel.focus();
  else             dialog.down("input[name='"+this.choiceList.first().value+"']").focus();

  this.showing(true);

  klass.dialogs.push(dialog);

  if (!this.fire('show')) {
   dialog.close(true);

   return false;
  } else {
   return true;
  }
 }
});

////////////////////////////////////////////////////////////////////////////////
// Node.js part 2
////////////////////////////////////////////////////////////////////////////////

/*
Método que retorna uma função que compara a tag do elemento passado com a tag passada para este método.

COMPARE_TAG("div") // irá retornar uma função que recebe um elemento e compara o nome de sua tag com 'DIV'.

@param  tag nome da tag
@return function função
*/
function COMPARE_TAG (tag) {
 return function (e) {
  return e.nodeType == 1 && e.tagName == tag.toUpperCase()
 }
}

/*
Pega somente as childs que possuem o atributo passado. Considera o valor do atributo se for passado também.

COMPARE_ATTRIBUTE("readonly");        // irá retornar true comente para os elementos que possuirem o attributo readonly.
COMPARE_ATTRIBUTE("class", "header"); // irá retornar true somente para os elementos que possuirem o atributo class com valor 'header'

@param name  nome  do atributo
@param value valor do atributo
*/
function COMPARE_ATTRIBUTE (name, value) {
 return function (e) {
  if (e.nodeType != 1)
   return false;

  var attribute = e.getAttribute(name);

  return value != undefined ? attribute == value : attribute != undefined
 }
}

/*
Retorna array com todos os nodes filhos (recursivamente) que retornarem true em func.

getChilds(document.body, COMPARE_TAG("div")) // retornará todas as divs existentes na página.

@param  el     elemento pai
        func   função
@return childs array com nodes filhos
*/
function getChilds (el, func) {
 var childs = [],i,child;

 for (i = 0; i < el.childNodes.length; i ++) {
  child = el.childNodes[i];

  if (func(child))
   childs.push(child);

  childs = childs.concat(getChilds(child, func))
 }

 return childs
}

/*
Tenta retornar o primeiro parentNode que retornar true em func.

getParent(document.body.firstChild, COMPARE_TAG("html"))

@param  el   elemento
        func função
@return el   elemento pai encontrado por func
*/
function getParent (el, func) {
 var parent = el.parentNode;

 if (parent)
  return func(parent) ? parent : getParent(parent, func)
}

/*
Retorna o primeiro elemento anterior ao elemento passado que retornar true em func.

getPrevious(document.body.childNodes[4], COMPARE_TAG("b"));

@param  el       elemento
        func     função
@return previous primeiro elemento anterior
*/
function getPrevious (el, func) {
 var previous = el.previousSibling;

 if (previous)
  return func(previous) ? previous : getPrevious(previous, func)
}

/*
Retorna o primeiro elemento proximo ao elemento passado que retornar true em func.

getNext(document.body.childNodes[4], COMPARE_TAG("b"));

@param  el       elemento
        func     função
@return previous primeiro elemento proximo
*/
function getNext (el, func) {
 var next = el.nextSibling;

 if (next)
  return func(next) ? next : getNext(next, func)
}

////////////////////////////////////////////////////////////////////////////////
// js.js part 2
////////////////////////////////////////////////////////////////////////////////

(function () {
  function onFail(t, c, err) {
   alert(new Ultra.Error("não pode executar operação "+t+".\n\n"+err));
  }

  Ultra.Try = function (code, interval, times, failHandler) {
   var t, c = 0, err = [];
   interval = interval || 1000;
   times = times || 10;
   failHandler = failHandler || onFail;
   function trier () {
    try {
     code(t, c, err);
     clearInterval(t);
     return true;
    } catch (e) {
     err[c] = e;
     c++;
     if (c == times) {
      clearInterval(t);
      failHandler(t, c, err);
     }
     return false;
    }
   }
   trier() || (t = setInterval(trier, interval));
   return t;
  };
})();

Ultra.Try(function () {
 Element.addMethods({
  // $(ElementId || ElementNode).callEvent(EventName);
  // $('some-element-id').callEvent('click');
  // $(SomeElementNode).callEvent('blur');
  callEvent: function ($this, name) {
   if (document.dispatchEvent) {
    if (name.startsWith("on")) name = name.substr(2);
    var event;
    if ($w("click mousedown mouseup mouseover mousemove mouseout").include(name)) {
     event = document.createEvent("MouseEvents");
     event.initMouseEvent("click", true, true, window, 0, 0, 0, 0, 0, false, false, false, false, 0, null);
    } else {
     event = document.createEvent("HTMLEvents");
     event.initEvent(name, true, false);
    }
    $this.dispatchEvent(event);
   } else {
    if (!name.startsWith("on")) name = "on" + name;
    $this.fireEvent(name);
   }
  },

  getForm:function($this){
   var el=$($this),form=el.form;
   if(form)return $(form);
   return el.up("form");
  }
 });

 function normalizeName($this, name) {
  if(name==null)name=String(name);
  var ClassId = $this.ClassId();
  if (ClassId) {
   if (name.indexOf(".") == -1) {
    if (name.startsWith("_")) {
     name = "_" + ClassId + "." + name.substr(1);
    } else {
     name = ClassId + "." + name;
    }
   }
  }
  return name;
 }

 function toggleCheckbox($this, name, value, checked) {
  $this.fieldList(name).each(function (element) {
   if (value == null || (element.value.toUpperCase() == value.toUpperCase()))
    element.checked = checked;
  });
 }

 function isFieldList(element) {
  return String(element.tagName).toUpperCase() != "SELECT" && (element.toArray || element.length);
 }

 Element.addMethods("FORM", {
  ClassId: function ($this, value) {
   var element = $this.elements["_ClassId"];
   if (element) {
    if (value != null)
     element.value = value;
    return element.value;
   }
  },

  Method: function ($this, value) {
   var element = $this.field("_Method");
   if (element) {
    if (value != null)
     element.value = value;
    return element.value;
   }
  },

  Level: function ($this, value) {
   var element = $this.elements["_Level"];
   if (element) {
    if (value != null)
     element.value = value;
    if (!isNaN(element.value))
     return parseInt(element.value);
   }
  },

  field: function ($this, name, index) {
   name = normalizeName($this, name);
   if (index == null || index < 0 || isNaN(index)) index = 0;
   var element = $this.elements[name];

   if (element) return $(isFieldList(element) ? element[index] : element);
  },

  fieldList: function ($this, name) {
   name = normalizeName($this, name);
   var elements = $this.elements[name];
   if (elements) {
    if (isFieldList(elements)) {
     elements = $A(elements).map(Element.extend);
    } else {
     elements = [elements];
    }
    return elements;
   }
  },

  checkCheckbox: function ($this, name, value) {
   return toggleCheckbox($this, name, value, true);
  },

  uncheckCheckbox: function ($this, name, value) {
   return toggleCheckbox($this, name, value, false);
  },

  save: function ($this, attr) {
   var ClassId = $this.ClassId(), Method = $this.Method();
   if (ClassId && Method)
    (new Ultra.Class(ClassId)).process(Method, $this.serialize(true), attr);
  }
 });

 Element.addMethods($w("INPUT SELECT BUTTON TEXTAREA"), {
  ClassId: function ($this) {
   var name = $this.name, begin = (name.startsWith("_") ? 1 : 0);
   return name.substr(begin, name.lastIndexOf(".") - begin);
  },

  FieldId: function ($this) {
   var name = $this.name;
   return name.substr(name.lastIndexOf(".") + 1);
  },

  index: function ($this) {
   var form = $this.form, name = $this.name, index = -1;
   if (name && form.elements[name] != $this)
    index = $A(form.elements[name]).indexOf($this);
   return index;
  },

  field: function ($this, _name) {
   var name = normalizeName($this, _name),
       index = $this.index(),
       form = $($this.form);
   return form.field(name, index) || form.field(normalizeName(form, _name));
  },

  allValues: function ($this, _inside) {
   try {
    return _inside.select('input[name='+$this.name+'],select[name='+$this.name+'],button[name='+$this.name+'],textarea[name='+$this.name+']').map(function(e){return e.value});
   } catch (e) {}
  },

  empty: function ($this) {
   return $this.value.length==0;
  },

  parseFloat: function ($this, _value) {
   var _float = parseFloat( typeof(_value) != "undefined" ? _value : $this.value.replace(".","","g").replace(",",".") );
   return isNaN(_float) ? 0 : _float;
  }
 });

 Element.addMethods("SELECT", {
  setOption: function ($this, text, value, selected) {
   if (value == null) value = "";
   var combo = $($this), created = false, option;
   combo.value = value;
   if (combo.value != value) {
    option = document.createElement("option");
    option.text = text;
    option.value = value;
    combo.options.add(option);
    option.selected = selected == null || selected;
    created = true;
   }
   setTimeout(function() {
    combo.callEvent("change");
   }, 0);
   return created;
  },

  currentOption: function ($this) {
   return $($this.options[$this.options.selectedIndex]);
  }
 });

 /*$A(document.forms).each(function (form) {
  form._extended = false;
  $A(form.elements).each(function (element) {
   element._extended = false;
  });
 });*/

 Event.keyCode=function(e){return e.which || e.keyCode};
}, 1000, 10, function () {
 alert(new Ultra.Error("não pode carregar biblioteca extendida"));
});

function HelpSearch ( myform, el, cgi, ClassId, inside ) {
 requestURL('GET',(cgi ? cgi : document.location.pathname)+'?_ClassId='+(ClassId ? ClassId : 'Ultra.INDEX')+'&_'+(ClassId ? ClassId : 'Ultra.INDEX')+'.Method=AjaxSuperContext&_PackageId=*HELP*&_search_inside='+inside+'&_Search='+myform.elements['_Search'].value,null,el,0);
 $(el).style.display='';
}


//
// Prepara uma ação a ser executada em um evento com um delay, podendo ser
// abortada em um outro evento especificado.
//
// @param el     elemento
//        code   expressão ou função a ser executada
//        delay  tempo de espera para executar função (default 500)
//        abort  nome do evento que aborta a execução
//        target elemento destino ( optional )
//        e      evento corrente
// @author Matheus Cruz <matheus@sistemica.info>
//
function prepareEvent(el, code, delay, abort, target, e) {
 // defaults
 delay = delay || 500;
 abort = abort || "mouseout";
 var timeoutId = setTimeout(code, delay), event; // Prepara o código pra ser disparado de acordo com o delay, retornando o id da operação.
 function abortHandler() { // Handler que remove a operação.
  clearTimeout(timeoutId); // Remove a operação com base no id.
  Event.stopObserving(el, abort, abortHandler); // Handler se auto-remove.
 }
 Event.observe(el, abort, abortHandler); // Adiciona o handler ao evento.
 if (target) {
  event = e.type;
  function ignoreDelayHandler() {
   code();
   Event.stopObserving(target, event, ignoreDelayHandler);
  };
  Event.observe(target, event, ignoreDelayHandler);
 }
}

//
// Inicializa formulário de inclusão/alteração
//
// @param form id/elemento
//
function initForm(form) {
 form = $(form);
 if (form) {
  var code = form.getAttribute("init");
  if (code != null) Ultra.Try(function () { (new Function(code)).call(form) });
  return true;
 } else {
  return false;
 }
}

//
// @param checkbox checkbox element
//        FieldId  field id
// @see   Html::input::boolean::CheckBox#end
//
function checkBoolean(checkbox) {
 checkbox = $(checkbox);
/* if (checkbox.checked) {
  var hidden = checkbox.next("input[type='hidden']");
  checkbox.name = hidden.name;
  hidden.parentNode.removeChild(hidden);
 } else {
  new Insertion.After(checkbox, "<input type=hidden name='"+checkbox.name+"' value=0 />");
  checkbox.name = null;
  checkbox.next("input[type='hidden']").checked = false;
 }*/
 var hidden = checkbox.next("input");
 if (checkbox.checked) {
  hidden.value = 1;
 } else {
  hidden.value = 0;
 }
}

//
// Função de tratamento para combos no Internet Explorer.
// Quando a opção de um combo é alterada a partir das setas do teclado,
// um combo que possui requisição AJAX a dispara a cada ver que o usuário aperta uma das setas.
// Esta função retarda este comportamento, fazendo com que o disparo seja feito somente no onblur.
//
// @param combo <select />
//        e     evento
//
function IECombo(combo, e) {
 // Tratamento somente para Internet Explorer.
 if (!moz) {
  // Tenta porque se o usuário estiver na primeira opção e apertar a seta pra cima, dará erro.
  // O mesmo inverso acontecerá na última posição.
  Try.these(function () {
   var setEvent = true;
   // Seta pra cima: seleciona opção anterior.
   if (keyCode(e) == 38) {
    combo.options[combo.options.selectedIndex - 1].selected = true;
   // Seta para baixo: seleciona opção posterior.
   } else if (keyCode(e) == 40) {
    combo.options[combo.options.selectedIndex + 1].selected = true;
   // Nenhuma das setas: ignora.
   } else {
    setEvent = false;
   }
   // Uma das setas foi clicada.
   if (setEvent) {
    // Programa a combo para disparar evento onchange no onblur.
    Event.observe(combo, "blur", function (e) {
     $(combo).callEvent("change");
     Event.stopObserving(combo, e.type, arguments.callee);
    });
   }
  });
  // Se uma das setas foi clicada, retarda o evento para que não dispare a requisição AJAX.
  if ([38, 40].include(keyCode(e))) Event.stop(e);
 }
}

//
// Oculta/exibe todas as TR abaixo dessa
//
function toggleTrBelow (tr) {
 tr = $(tr);
 while ( tr && !tr.hasAttribute('rowtop') && !tr.hasClassName('row_total') ) {
  first = false;
  tr = tr.next();
  if ( tr.hasClassName('sibbSection') || tr.hasAttribute('noSection') ) return;
  tr.toggle();
 }
}

//
// Exibe oculta todas as TDs e TR ao lado e baixo dessa
//
function toggleTdRight (td) {
 td = $(td);
 if ( td.nodeName.toUpperCase() == 'IMG' && td.src ) {
  td.src = td.src.match(/\/action\/OpenUp.png$/)   ? td.src.replace(/\/OpenUp.png$/,  '/OpenDown.png')  : td.src.replace(/\/OpenDown.png$/, '/OpenUp.png');
  td.src = td.src.match(/\/action\/OpenLeft.png$/) ? td.src.replace(/\/OpenLeft.png$/,'/OpenRight.png') : td.src.replace(/\/OpenRight.png$/,'/OpenLeft.png');
 }
 if ( td.nodeName.toUpperCase() != 'TD' ) td = td.up('td');
 // Oculta TDs ao lado
 while ( td ) {
  td = td.next();
   td.toggle();
//  Effect.toggle(td,'blind');
 }
}

//
// Reordena um form de browse
//
function reorderBrowse( myform, FieldId, Desc, e, BrowseForm ) {
 var el = Event.element(e);

 var els = myform.elements;
 var ClassId = els['_'+els['_ClassId'].value+'.Method'].value == 'AjaxBrowseCombo' ? els['_ParentClassId'].value : els['_ClassId'].value;

 var OO = els['_'+ClassId+'.Order'];
 var DD = els['_'+ClassId+'.OrderDesc'];
 var O = $A(OO.value.split(','));
 var D = $A(DD.value.split(','));

 // Começa do zero
 if ( BrowseForm && !e.ctrlKey ) {
  tab = el.up('table');
  for (var i=1;i<tab.rows.length;i++){
   var nobr = tab.rows[i].cells[tab.rows[i].cells.length-4].firstChild;
   if ( nobr && nobr.firstChild ) { // Ignora os campos em branco
    nobr.firstChild.src = '/t/c/img/OrderAny.png';
    nobr.lastChild.innerHTML = '';
   }
  }
 }

 if (Desc == null) Desc = D[O.indexOf(FieldId)] == '0' ? '1' : D[O.indexOf(FieldId)] == '1' ? '' : '0';

 // Se for para eliminar ordenamento por completo
 if ( Desc == '' && !e.ctrlKey ) {
  OO.value = '';
  DD.value = '';

 // Se for para eliminar ordenamento desse campo
 } else if (Desc == '') {
  O.splice(O.indexOf(FieldId),1);
  D.splice(O.indexOf(FieldId),1);

  OO.value = O.join(',');
  DD.value = D.join(',');

 // Deve adicionar ao ordenamento atual
 } else if ( e.ctrlKey && OO.value.length ) {
  // Troca ascendência de campo já ordenado
  if ( O.indexOf(FieldId) > -1 ) {
   D[O.indexOf(FieldId)] = Desc;

  // Adiciona campo novo
  } else {
   O.push(FieldId);
   D.push(Desc);
  }

  OO.value = O.join(',');
  DD.value = D.join(',');

 // Deve recomeçar o ordenamento
 } else {
  OO.value = FieldId;
  DD.value = Desc;
 }

 // Atualiza, por via das dúvidas
 O = $A(OO.value.split(','));

 // Browse normal submete
 if (!BrowseForm) {
  myform.onsubmit();

 // Browse form - refaz as imagens
 } else {
  el.src = '/t/c/img/Order'+ ( Desc == '1' ? 'Desc' : Desc == '0' ? '' : 'Any' ) +'.png';
  el.nextSibling.innerHTML = O.indexOf(FieldId) > -1 ? O.indexOf(FieldId)+1 : '';
 }
}

//
// Usado nas barras de seção do formulário para exibir/ocultar as TR abaixo da TR corrente.
//
function showHideFormSection ( thistr ) {
 var ri  = thistr.rowIndex,
     tr  = thistr.parentNode.rows[ri+1];
 while ( tr && tr.className != 'row_section' && !tr.getAttribute('stopSection') && ( tr.firstChild.className == 'k' || ri == thistr.rowIndex ) ) {
  if ( !tr.getAttribute('noSection') ) { tr.style.display = tr.style.display == 'none' ? '' : 'none' };
  ri += 1;
  tr = thistr.parentNode.rows[ri+1];
 }
}

//
// Usado no Browse para exibir/ocultar opções de adicionar colunas
//
function showHideFormSection2 ( thistr ) {
 var tr = thistr.nextSibling;
 while ( tr && findChildElement(tr,'INPUT') ) {
  tr.style.display = tr.style.display == 'none' ? '' : 'none';
  tr = tr.nextSibling;
 };
 return void(0);
}

//
// Requisita LOCK via AJAX
//
function ClassLock ( FormId ) {
 var myform = document.forms[FormId],old_method;

 // LockCount was disabled!!!
// myform.setAttribute( 'LockCount', myform.getAttribute('LockCount') == undefined ? 0 : parseInt(myform.getAttribute('LockCount'))+1 );
 myform.setAttribute( 'LockCount', 0 );

 old_method   = myform.elements['_' + myform.elements['_ClassId'].value + '.Method'].value;
 myform.elements['_' + myform.elements['_ClassId'].value + '.Method'].value = 'AjaxLock';

 requestURL( 'POST', 'Class.cgi', myform, document.getElementById(FormId +'.lockx'), 0, false, true, true );

 myform.elements['_' + myform.elements['_ClassId'].value + '.Method'].value = old_method;

 // Alerta para LockCount se aproximando do limite e pede re-confirmação para renovar
 if ( parseInt(myform.getAttribute('LockCount')) == 45 ) {
  setTimeout( function () {
   try {
    // Renova LOCK
    if ( confirm('Você já está há muito tempo com esse registro aberto. Clique OK em 1 minuto se não quiser perder seus dados!') ) {
     myform.setAttribute( 'LockCount', 0 );
    // Cancela toda a operação agora!
    } else {
     myform.elements['_' + myform.elements['_ClassId'].value + '.Cancel'].click();
    }
   } catch (e) {}
  }, 100 );
 }

 // Respawn if LockCount is not too high
 if ( parseInt(myform.getAttribute('LockCount')) < 60 ) {
  myform.setAttribute( 'LockEvent', setTimeout( function () { ClassLock( FormId ); }, 20000 ) );

 // Passou da validade sem o usuário requisitar uma renovação, tenta cancelar a operação.
 } else if ( parseInt(myform.getAttribute('LockCount')) >= 60 ) {
  myform.elements['_' + myform.elements['_ClassId'].value + '.Method'].value  = 'Select';
  myform.elements['_' + myform.elements['_ClassId'].value + '.XMethod'].value = 'Unlock';
  myform.elements['_' + myform.elements['_ClassId'].value + '.Submit'].click();
 }
}


//
// Carrega combos para seleção das funções de agregação e seleção.
// Isso é usado no Form de Browse avançado.
//
// @param ClassId
//        FieldId
//        el      Elemento onde exibir (innerHTML)
//        force   Força carregamento, independente de já ter carregado ou não.
//
function showFn ( ClassId, FieldId, el, force ) {
 singleTime = force == 1 ? 0 : 1;
 var myform = findParentElement ( el, 'FORM' );
 requestURL( 'GET','Class.cgi?_ClassId='+ClassId+'&_'+ClassId+'.Method=AjaxShowFn&_'+ClassId+'.FieldId='+FieldId+'&_ParentFormId='+myform.name, '', el, singleTime );
}


//
// Especifica mascara para o campo. Deve ser usada no evento onkeyup.
// NOVA FUNÇÃO DE MÁSCARA PARA O CAMPO: INCORPORADA PARA CARACTERES DO TIPO HEXADECIMAL
//
// Exemplo:
// Mask( this, "(99) 9999-9999", event ); /* Mascara para número de telefone */
// Mask( this, "AAA-9999", event ); /* Mascara para placa de carro */
// Mask( this, "#####-#####-#####-#####-#####", event ); /* Mascara para número serial */
// Mask( this, "%%.%%.%%.%%.%%.%%", event ); /* Mascara para tipo de dado addr */
//
// Onde:
// A = Somente letras [a-zA-Z]
// 9 = Somente números [0-9]
// # = Caracteres alfanumericos em geral
// % = Caracteres hexadecimais \w
//
function Mask(Field, Mask, e) {
 var Value = Field.value.replace( /\W/gi, "" ),i;
 for (i = 0; i < Value.length; i ++) {
  if (!/[#A9%]/.test(Mask.charAt(i))) {
   Value = Value.splice(i, 1, Mask.charAt(i) + Value.charAt(i));
  } else if ((Mask.charAt(i) == "A" && !/[a-z]/i.test(Value.charAt(i))) ||
             (Mask.charAt(i) == "9" && !/[0-9]/.test(Value.charAt(i))) ||
             (Mask.charAt(i) == "%" && !/[0-9a-fA-F]/.test(Value.charAt(i))) ||
             (Mask.charAt(i) == "#" && !/\w/.test(Value.charAt(i)))) {
   Value = Value.splice(i, 1);
  }
 }
 Field.value = Value.substr(0, Mask.length);
}

//
// Gera mensagem de erro nas checagens do formulário
//
// @param el  elemento DOM (input)
//        msg mensagem
//
function raiseError(el, msg) {
 alert("RaiseError:"+msg);
 showMyInterface(el);
 window.status = msg;
 setTimeout(function () { $(el).activate() }, 100);
}

//
// Mensagem de erro para campos not-null
//
// @param el elemento DOM (input)
//
function onNotNullFail(el) { raiseError(el, "Campo '"+(el.getAttribute("description") || "["+el.name+"]")+"' não pode ser nulo!") }

//
// Mensagem de erro para campos com padrão
//
// @param el elemento DOM (input)
//
function onPatternFail(el) { raiseError(el, "Campo '"+(el.getAttribute("description") || "["+el.name+"]")+"' possui valor inválido!") }

//
// Tenta mostrar a layer do campo de acordo com seus attributos (layer_id, layer_i),
// já que o campo pode não possuir uma.
//
// @param el input elemento DOM
//
function showMyInterface(el) {
 Try.these(function () {
  SuperInterfaceShow(el.getAttribute("layer_id"), el.getAttribute("layer_i"), 1);
 });
 Try.these(function () {
  var i = findParentElement(el,'DIV','class','SuperInterfaceLayerOff').id.split(/-/);
  SuperInterfaceShow( i[1], i[2], 1 );
 });
}

//
// ArrayBuilder Javascript constructor class
//
// <code>
//  new Ultra.ArrayBuilder('someArrayBuilderElementId');
//  new Ultra.ArrayBuilder($$('.Browse').first());
//  $$('.Browse').each(function (node) { new Ultra.ArrayBuilder(node) });
// </code>
//
// @param element DOM Element
// @param element id element
// @author Matheus Lorenzoni Cruz
// @date   2007-12-13
//
Ultra.ArrayBuilder = function (element) {
 element = $(element);
 var table = element.up("table");
 table.setAttribute("onAdd", table.getAttribute("onAdd")+";this.createSortable()");
 (element.createSortable = function () {
   Sortable.create(element, {
    tag: "tr",
    only: ["i", "p"],
    starteffect: Prototype.emptyFunction,
    endeffect: Prototype.emptyFunction,
    onUpdate: arguments.callee
   })
  })();
 element.observe("keypress", addToArrayByKey);
}

//
// adiciona itens ao array
//
// @param item elemento DOM a ser clonado
//
function addToArray(item, keep) {
     item   = item.parentNode;
 var parent = item.parentNode,
     next   = item.nextSibling,
     clone  = item.cloneNode(true),
     fstInput,combo,getOriginalValue,code,
     event,curcombo,curoption,funcBeforeAdd, check;

 // Função auxiliar para manter o valor original anterior
 getOriginalValue = function (e, i) {
  curcombo  = combo[i],
  curoption = curcombo.selectedIndex;
  e.value = curcombo.options[curoption].value;
 }

 // Não deve manter os valores (deve zerar)
 if( !keep ) {
  // Pré-processa inputs e textareas para possivelmente alterar/zerar/manter o valor
  $A(clone.getElementsByTagName("input"))
  .concat($A(clone.getElementsByTagName("textarea"))).each(function (e) {
   $(e);
   if ( ( typeof(e.getAttribute('keeponclone')) != 'undefined' &&
          typeof(e.getAttribute('keeponclone')) != 'unknow' &&
          e.getAttribute('keeponclone') != '0' &&
          e.getAttribute('keeponclone') != 'false' ) &&
        ( e.disabled || e.hasClassName("fakedDisabledInput") ) ) return;
   if (!moz) {
    if(typeof $(item).getForm().field(e.name) != 'undefined' &&
       typeof $(item).getForm().field(e.name) != 'unknow'){
      e.defaultValue = $(item).getForm().field(e.name).defaultValue;
    }
   }
   // Caso esteja definido o atributo keepOnClone, busca o valor atual do input, caso contrário seta nulo.
   if (e.getAttribute("keepOnClone")){
//     e.value = e.defaultValue;
      return;
   } else {
     e.value = e.defaultValue;
   }
  });
  // SELECTs são um pouco diferentes no keepOnClone
  combo = item.getElementsByTagName("select");
  aryobjToAry(clone.getElementsByTagName("select")).map(function(e,i) { if (e.getAttribute("keepOnClone")) {getOriginalValue(e,i)} });

 // Deve manter/clonar os valores (usuário provavelmente manteve tecla CTRL pressionada)
 } else {
  combo = item.getElementsByTagName("select");
  aryobjToAry(clone.getElementsByTagName("select")).map(getOriginalValue)
 }

 // Busca todos os eventos que deve executar antes de adicionar nova linha;
 code = parent.parentNode.getAttribute("onBeforeAdd");
 check = true;
 if (code) {
  event = new Function("row", code);
  if((check)||(typeof check == 'undefined')){
   check = event.call(parent, clone);
  }else{
   event.call(parent, clone);
  }
 }
 $A(clone.getElementsByTagName("input"))
 .concat($A(clone.getElementsByTagName("select")))
 .concat($A(clone.getElementsByTagName("textarea")))
 .each(function (element) {
  var code = element.getAttribute("onBeforeAdd"),funcBeforeAdd;
  if (code) {
   funcBeforeAdd = new Function(code);
   if((check)||(typeof check == 'undefined')){
    check = funcBeforeAdd.call(code);
   }else{
    funcBeforeAdd.call(code);
   }
  }
 });
 if((!check)&&(typeof check != 'undefined')){
  return false;
 }
 parent.insertBefore(clone, next);
 code = parent.parentNode.getAttribute("onAdd");
 if (code) {
  event = new Function("row", code);
  event.call(parent, clone);
 }
 $A(clone.getElementsByTagName("input"))
 .concat($A(clone.getElementsByTagName("select")))
 .concat($A(clone.getElementsByTagName("textarea")))
 .each(function (element) {
  var code = element.getAttribute("onAdd"),event;
  if (code) {
   event = new Function(code);
   event.call(element);
  }
 });
 return clone;
}

function addToArrayByKey(e) {
 if (Event.element(e).readAttribute('sbox') == '1') return;
 if (!e.shiftKey && !e.ctrlKey && [Event.KEY_RETURN, Event.KEY_TAB].include(e.keyCode)) {
  var field = Event.element(e), current = field, td, tr, tbody, elements, clone;
  do {
   td = current.up("td");
   tr = td.up("tr");
   current = tbody = tr.up("tbody");
   if (tbody.up("table").hasClassName("Browse")) break;
  } while(!tbody.up("table").hasClassName("Browse"));
  elements = $A(tr.getElementsByTagName("*")).select(function (e) {
   $(e);
   return ["INPUT", "SELECT"].include(e.nodeName.toUpperCase())
    && !e.disabled
    &&  e.type != "hidden"
    &&  e.visible()
  });
  if (tbody.lastChild == tr && elements.last() == field) {
   clone = addToArray(td, e.ctrlKey);
   $($A(clone.getElementsByTagName("*")).find(function (e) {
    $(e);
    return ["INPUT", "SELECT", "TEXTAREA"].include(e.nodeName.toUpperCase())
     && !e.disabled
     &&  e.type != "hidden"
     &&  e.visible()
   })).activate();
   Event.stop(e);
  }
 }
}

function onAddExternalFile(row) {
 var bin = $A(row.getElementsByTagName("div")).first();
 if (bin) bin.parentNode.removeChild(bin);
 $A(row.getElementsByTagName("input")).each(function (input) {
  if (input.type == "file") {
   input.disabled = false;
   $(input).setStyle({display: ''});
  } else if (input.type == "hidden" && input.name && input.name.endsWith(".keep")) {
   input.parentNode.removeChild(input);
  }
 });
}

function dropFromArray(cell, nullable) {
 // Defaults
 if (nullable === undefined) nullable = true;
 var row     = cell.parentNode,
     table   = row.parentNode,
     minRows = table.firstChild.className == "row_top" ? 2 : 1,
     event,inputs,code,check,funcBeforeDrop;

 // Percorre todos os eventos onBeforeDrop, seja da tabela, ou dos campos array, e executa o código.
 code = $(row).up("table").getAttribute("onBeforeDrop");
 check = true;
 if (code) {
  event = new Function(code);
  if((check)||(typeof check == 'undefined')){
   check = event.call(code);
  }else{
   event.call(code);
  }
 }
 $A(row.getElementsByTagName("input"))
 .concat($A(row.getElementsByTagName("select")))
 .concat($A(row.getElementsByTagName("textarea")))
 .each(function (element) {
  var code = element.getAttribute("onBeforeDrop"),funcBeforeDrop;
  if (code) {
   funcBeforeDrop = new Function(code);
   if((check)||(typeof check == 'undefined')){
    check = funcBeforeDrop.call(code);
   }else{
    funcBeforeDrop.call(code);
   }
  }
 });
 if((!check)&&(typeof check != 'undefined')){
  return false;
 }

 // Percorre todos os eventos onDrop, seja da tabela, ou dos campos array, mas não executa o código, pois ele deve ser executado depois de excluir a linha.
 check ='';
 code = $(row).up("table").getAttribute("onDrop");
 if (code) {
  event = new Function(code);
  event.call(code);
 }
 $A(row.getElementsByTagName("input"))
 .concat($A(row.getElementsByTagName("select")))
 .concat($A(row.getElementsByTagName("textarea")))
 .each(function (element) {
  var code = element.getAttribute("onDrop"),event;
  if (code) {
   check += code;
  }
 });

 if (table.rows.length == minRows) {
  if (!nullable) return false;
  inputs = $A(row.getElementsByTagName("input"));
  inputs = inputs.concat($A(row.getElementsByTagName("textarea")));
  inputs.each(function (field) {
   if (field.getAttribute("keepOnClone") && field.value != field.defaultValue) {
    field.value = field.defaultValue;
    return;
   }
   if (["radio", "checkbox"].include(field.type)) {
    field.checked = false;
   } else if (!field.getAttribute("keepOnClone")) {
    field.value = "";
   }
  });
  $A(row.getElementsByTagName("select")).each(function (combo) {
   if (combo.options[0].value == "") combo.value = "";
  });
 } else {
  // Exclui a linha
  table.removeChild(row);
 }
  //executa o código contigo no onDrop da linha que foi apagada.
  event = new Function(check);
  event.call(check);
 return true;
}



//
// Retorna a String de uma nova sessão. Exemplo: /2/3/6/7/8/6/4/
//
function getSession(sep) {
 sep=sep || "/";
 return String(Math.random()).substr(2, 10).split("").join(sep);
}

//
// Abre um pop-up
//
// @param  querystring query string
//         popup       nome da janela popup
//         properties  propriedades da janela
//         session     sessão Ultra::Class
//         asPost      boolean que identifica que submissão deverá ser um post
// @return window.open
//
function popUp(querystring, popup, properties, session, asPost) {
   var url,form;

   popup = popup || getSession("_")+'popup';
   if (session == null) session = getSession();
   if (properties == null) properties = "toolbar=no,titlebar=no,status=yes,scrollbars=yes,menubar=yes,location=no,resizable=yes,width=760,height=480";

   if (!asPost) {
      if (querystring.indexOf("?") == -1) querystring = "Class.cgi?" + querystring;

      url = ( session == -1 ? querystring : "/Ultra/"+session+"/"+querystring );
      return window.open(url, popup, properties);

   } else {
      url = "/Ultra/"+session+"/"+(querystring.substr(0, querystring.indexOf("?")) || "Class.cgi");

      querystring = querystring.substr(querystring.indexOf("?") + 1);

      form = "<form method='post' action='"+url.escapeHTML()+"' target='"+popup.escapeHTML()+"'>";
      $H(querystring.toQueryParams()).each(function (param) {

         if (typeof param.value == "string"){
            param.value = [param.value.replace(/\'/gim, "&#39;")];
         }

         param.value.each(function (value) {
            form += "<input type='hidden' name='"+param.key.escapeHTML()+"' value='"+value.escapeHTML()+"' />";
         });
      });

      form += "</form>";

      new Insertion.Bottom(document.body, form);

      form = $A(document.forms).last();
      window.open('', popup.escapeHTML(), properties);
      form.submit();
      document.body.removeChild(form);
   }
}

//
// Envia e-mail
//
function SendMail( mailto, subject, html, text2html ) {
 if ( text2html ) html = html.replace(/\r?\n/, '\n<br>');
 if ( mailto  == undefined ) mailto  = '';
 if ( subject == undefined ) subject = '';
 if ( html    == undefined ) html    = '';
 var querystring =
  new QueryString( {
   '_ClassId': 'Company.Mail',
   '_Company.Mail.Method': 'Insert',
   '_Company.Mail.OK': 'Fake',
   'Company.Mail.To': mailto,
   'Company.Mail.Subject': subject,
   'Company.Mail.Message': html } ).toString();
 popUp( querystring, null, 'toolbar=no,titlebar=no,status=yes,scrollbars=yes,menubar=yes,location=no,resizable=yes,width=800,height=640', null, ( querystring.length > 1000 ? true : false ) );
}

//
// Abre uma nova janela para inserção de um registro
//
function popCalInsert ( Form, ClassId, FieldId, values )  {
 var url  = "Class.cgi?"+
  "_ClassId="+ClassId+
  "&_Level="+escape( Form.elements["_Level"].value )+
  "&_"+ClassId+".Method=Insert"+
  "&"+ClassId+"."+FieldId+"="+escape( Form.getAttribute('TODAY') )+
  "&_"+ClassId+".OK=Fake"+
  "&_"+ClassId+".NextMethod="+escape( "internal:opener.submit('"+Form.name+"');" ),
  k,name;

 for (k in values) {
  if ( values[k] && k != ClassId+'.'+FieldId ) url += "&" + ( /\./.test(k) ? "" : ClassId + "." ) + k + "="+escape( values[k] );
 }

 name = "InsertForm"+getSession().replace( /\//gi, "_" );
 return window.open( url, name, "toolbar=no,titlebar=no,status=yes,scrollbars=yes,menubar=yes,location=no,resizable=yes,width=640,height=480" );
}

// Exibe ContextMenu do calendário
function cm(e) {
 var el = Event.element(e);
 return calMenu(el,e);
}
function calMenu (el,e) {
 var Form = findParentElement(el,'FORM'),
     TODAY = el.getAttribute('TODAY');
 try { if ( TODAY == null ) TODAY = findParentElement(el,'TD').getAttribute('TODAY'); } catch (e) {};
 try { if ( TODAY == null ) TODAY = findParentElement(el,'TR').getAttribute('TODAY'); } catch (e) {};
 Form.setAttribute('TODAY',TODAY);
 $("CalendarMenu-"+Form.name).showAtEvent(e);
 Event.stop(e);
 return false;
}

//
// Função ajudante de desenvolvimento. Ela abre um popup que permite alterar ou incluir tabelas, schemas, pacotes e campos.
// Se for especificada um '*' para algum valor ela entende como INCLUSÃO
//
function XSchema (Signature, Schema, Package, Class, Field, plus, ev) {
 if ( !Schema ) {
  alert( 'Erro interno: é obrigatório que se especifique a SchemaId como primeiro parâmetro para a função XSchema.' );
  return false;
 }


// bloqueia a cascata de eventos. útil para não reordenar os campos.
if (typeof ev != 'undefined') {

   Event.stop(ev);

}


 var QueryString;

 // É alguma alteração extendida
 if ( plus ) {
  if ( plus.ClassFK ) {
   var classFkName = /^\*\*.+\*\*\d+\*\*$/.test( plus.ClassFK ) ? ( 'Ultra.ClassFK.Name='+plus.ClassFK.split('**').reverse()[1] ) : 'Ultra.ClassFK.Name='+plus.ClassFK;

   QueryString = plus.ClassFK == '*' ? '_ClassId=Ultra.ClassFK&_Ultra.ClassFK.Method=Insert&Ultra.ClassFK.SignatureId='+Signature+'&Ultra.ClassFK.SchemaId='+Schema+'&Ultra.ClassFK.ClassId='+Class+'&_Ultra.ClassFK.lockFields=SignatureId&_Ultra.ClassFK.lockFields=SchemaId&_Ultra.ClassFK.lockFields=ClassId&_Ultra.ClassFK.OK=Fake' :
                                       '_ClassId=Ultra.ClassFK&_Ultra.ClassFK.Method=Update&Ultra.ClassFK.SignatureId='+Signature+'&Ultra.ClassFK.SchemaId='+Schema+'&Ultra.ClassFK.ClassId='+Class+'&'+classFkName;
  }

 // Cria/altera campo
 } else if ( Field ) {
  QueryString = Field == '*' ? '_ClassId=Ultra.ClassField&_Ultra.ClassField.Method=Insert&&_Ultra.ClassField.XMethod=Insert&Ultra.ClassField.SignatureId='+Signature+'&Ultra.ClassField.SchemaId='+Schema+'&Ultra.ClassField.ClassId='+Class+'&_Ultra.ClassField.lockFields=SignatureId&_Ultra.ClassField.lockFields=SchemaId&_Ultra.ClassField.lockFields=ClassId&_Ultra.ClassField.OK=Fake' :
                               '_ClassId=Ultra.ClassField&_Ultra.ClassField.Method=Update&Ultra.ClassField.SchemaId='+Schema+'&Ultra.ClassField.ClassId='+Class+'&Ultra.ClassField.FieldId='+Field;

 // Cria/altera tabela
 } else if ( Class ) {
  QueryString = Class == '*' ? '_ClassId=Ultra.Class&_Ultra.Class.Method=Insert&Ultra.Class.SignatureId='+Signature+'&Ultra.Class.SchemaId='+Schema+'&Ultra.Class.PackageId='+Package+'&_Ultra.Class.lockFields=SignatureId&_Ultra.Class.lockFields=SchemaId&_Ultra.Class.lockFields=TypeOwner&_Ultra.Class.OK=Fake' :
                               '_ClassId=Ultra.Class&_Ultra.Class.Method=Update&Ultra.Class.SchemaId='+Schema+'&Ultra.Class.ClassId='+Class;

 // Cria/altera Pacote (submenu)
 } else if ( Package ) {
  QueryString = Package == '*' ? '_ClassId=Ultra.Package&_Ultra.Package.Method=Insert&Ultra.Package.SignatureId='+Signature+'&Ultra.Package.SchemaId='+Schema+'&_Ultra.Package.lockFields=SignatureId&_Ultra.Package.lockFields=SchemaId&_Ultra.Package.OK=Fake' :
                                 '_ClassId=Ultra.Package&_Ultra.Package.Method=Update&Ultra.Package.SchemaId='+Schema+'&Ultra.Package.Id='+Package;

 // Cria/altera Aplicativo
 } else if ( Schema ) {
  QueryString = Schema == '*' ? '_ClassId=Ultra.Schema&_Ultra.Schema.Method=Insert&Ultra.Schema.SignatureId='+Signature+'&_Ultra.Schema.Fake=1' :
                                '_ClassId=Ultra.Schema&_Ultra.Schema.Method=Update&Ultra.Schema.SchemaId='+Schema;
 }

 popUp(QueryString, "XSchema" + String(Math.random()).substr(2,10) );

 return false;
}

//
// Abre um pop-up para o ClassDebugTask referente aos parametros passados
//
// @param  Package pacote
//         Class   classe
//         Field   propriedade
// @return false   falso para o evento oncontextmenu não ser disparado
//
function DebugTask (Schema, Package, Class, Field) {
 if ( !Schema ) {
  alert( 'Erro interno: é obrigatório que se especifique a SchemaId como primeiro parâmetro para a função DebugTask.' );
  return false;
 }

 var q = {
  SchemaId  : Schema,
  PackageId : Package,
  ClassId   : Class,
  FieldId   : Field
 },
  QueryString = "_ClassId=Ultra.ClassDebugTask&_Ultra.ClassDebugTask.Method=Browse&_Ultra.ClassDebugTask.OK=1&_Ultra.ClassDebugTask.Order=DataFim&_Ultra.ClassDebugTask.OrderDesc=1&_Ultra.ClassDebugTask.XMethod=Docs",
  k;

 for (k in q) {
  if (q[k])
   QueryString += "&Ultra.ClassDebugTask."+k+"="+q[k];
 }

 popUp(QueryString, "ClassDebugTask" + String(Math.random()).substr(2,10) );

 return false;
}

//
// divide uma query string, retornando um hash
//
// @param querystring string
//        hash        querystring estruturada
//
function splitQueryString(QueryString) {
 var Pair = QueryString.split(/&|\?/gi),
     Hash = {},
     i,KeyValue;
 for (i = 0; i < Pair.length; i ++) {
  KeyValue = Pair[i].split("=");
  Hash[KeyValue[0]] = KeyValue[1];
 }
 return Hash;
}

//
// Converte um Browse com Group By para uma busca simples, sem group by.
//
// @param FormId
//        ARRAY  Array de pares 'FieldId', 'Valor'
//
function bg2s(row,vals) {
 row = $(row);
 var form = row.getForm(),
     params = form.fieldList("_GroupBy").inject([], function (acc, field) {
  acc.push(field.value);
  acc.push(vals.first().length ? vals.shift() : "\\n");
  return acc;
 });
 return BrowseG2S(form.name, params);
}
function BrowseG2S ( FormId, Fields ) {
 var myform = document.forms[FormId],ii,i;

 // Alimenta busca
 for ( ii = 0; ii < Fields.length; ii += 2 ) {
  myform.elements[ myform.elements['_ClassId'].value + '.' + Fields[ii] ].value = Fields[ii+1].replace(/ /, '+', 'g');
 }

 // Elimina group by
 for ( i = 0; i < myform.elements.length; i++ ) {
  if ( myform.elements[i].name == '_' + myform.elements['_ClassId'].value + '.GroupBy' ) {
   myform.elements[i].disabled = true;
  }
 }

 myform.onsubmit();
}

//
// Função para pular de um campo para o outro a partir da tecla ENTER.
// Com a tecla shift pressionada, volta para o campo anterior.
// A função agora é capaz de pular entre as layers ou navegar entre o primeiro e último elementos.
//
// @param e event
//
function jumpField(e) {
 var element = Event.element(e),shiftKey,elements,next;
 if (!element.form) return;
 if (element.readAttribute('sbox') == '1') return;
 if (e.ctrlKey || e.keyCode != Event.KEY_RETURN || ["button", "submit", "reset", "textarea"].include(element.type)) return;
 shiftKey = e.shiftKey,
 elements = $A(element.form.elements)
  .select(function (el) { return $(el) &&
                                 el.focus &&
                                 !el.disabled &&
                                 el.getStyle("display") != "none" &&
                                 el.getStyle("visibility") != "hidden" &&
                                 !["hidden", "button", "reset"].include(el.type) });
 elements.each(function (_element, index) {
  next = elements[index + (shiftKey ? 1 : -1)];
  if (next == element) {
   showMyInterface(_element);
   $(_element).activate();
   Event.stop(e);
   return $break;
  }
 });
}

Ultra.Number = {
    keypress:function(e){
//    alert('keypress');
        var key=Event.keyCode(e),scale,precision;
        if ( !e.ctrlKey && ![9,13,35,36,37,38,39,40,46].include(key) ) {
            scale=this.getAttribute("scale"),precision=this.getAttribute("precision"),precisionfree=this.getAttribute("precisionfree");
            var ax = this.value.split(',');
            // Chegou ao limite da precisão em um campo de precisão variável
            if (precisionfree!=null && precision!=null && ax.length > 1 && ax[1].length >= precision && (key>=48 && key<=57)) {
                Event.stop(e);
            // Era zero e agora deve ser nulo
            } else if ([8,48].include(key) && this.value.match(/^0(?:,0+)?$/) &&
                (precision!=null || this.value=="0") && precisionfree==null){
                if(key==8)this.value="";
                Event.stop(e);
            } else if (key==45 && this.getAttribute("signed") && this.value.length>0 && !this.value.match(/^0(?:,0*)?$/)) {
                this.value=this.value.indexOf("-")==0?this.value.substr(1):"-"+this.value;
                Event.stop(e);
            } else if (scale!=null && (precision!=null || (key!=44 && this.value.indexOf(",")==-1)) && this.value.replace(/\D/g,"").length==scale && key!=8) {
                Event.stop(e);
//            }else if(key==44 && (precision!=null || this.value.indexOf(",")>-1)){
            // Não permite duas vírgulas... key 44 é a vírgula
            } else if (key==44 && ((precisionfree==null && precision!=null) || this.value.indexOf(",")>-1)) {
                Event.stop(e);
            } else if (![8,44].include(key) && (key<48 || key>57)) {
                Event.stop(e);
/* Foi removido em 02-12-2009 por Diego de Lima porque apresentava um bug com números menores 1ue 0,1 */
/*            } else if ( this.value.match(/^0(?:,0+)?$/) && precision == null && key != 48 ) {
                this.value = String.fromCharCode(key);
                Event.stop(e);*/
/*            } else {
             help(e,Event.keyCode(e) + ' -- ' + Math.random());*/
            }
        }
    },

    formatInt:function(value){
        var scale=this.getAttribute("scale"),dotted=this.getAttribute("dotted"),
            _int=value.replace(/\D/g,""),
            rv,i,c;
        if(_int!="0")_int=_int.replace(/^0+/,"");
        if(scale!=null && _int.length>scale)_int=_int.substr(_int.length-scale,scale);
        if(dotted){
            for(rv="",i=_int.length-1,c=1;i>=0;i--,c++){
                rv=_int.charAt(i)+rv;
                if(c % 3==0 && i!=0)rv="."+rv;
            }
            _int=rv;
        }
        return _int;
    },

    // Arredonda parte decimal(ex.: 20.019 vira 20.02, 20.099 vira 20.1 e 20.999 vira 21)
    trunc:function(number,precision){
        if(precision==null)precision=0;
        var dotindex=number.toString().indexOf("."),divisor,condition;
        if(dotindex==-1)return number;
        divisor=Math.pow(10,precision+1);
        condition=parseInt((number*divisor).toString().substr(dotindex,precision+1).substr(-1));
        if(condition>=5)number=number+(9/divisor);
        return parseFloat(number.toString().substr(0,dotindex+precision+1));
    },

    number2String:function(){
        var number=parseFloat(this.value),
            signed,scale,precision,
            parts,_int,_float,
            dotted,max,i,
            len,string;
        if(isNaN(number))return;
        signed=number<0 && this.getAttribute("signed"),
        scale=this.getAttribute("scale"),
        precision=this.getAttribute("precision");
        if(scale!=null)scale=parseInt(scale);
        if(precision!=null)precision=parseInt(precision);
        number=Ultra.Number.trunc(number,precision);
        if(number<0)number*=-1;
        if(scale!=null && precision!=null)scale-=precision;
        parts=String(number).split("."),_int=String(parts[0]),_float=parts[1]!=null?String(parts[1]):"";
        if(this.getAttribute("dotted")){
            dotted="",max=_int.length;
            for(i=1;i<=max;i++){
                dotted=_int.substr(max-i,1)+dotted;
                if(i % 3==0 && i<max)dotted="."+dotted;
            }
            _int=dotted;
        }
        if(precision!=null){
            if(_float.length>precision){
                _float=_float.substr(0,precision);
            }else if(precision>0){
                _int=_int||"0";
                len=precision-_float.length;
                for(i=0;i<len;i++){
                    _float+="0";
                }
            }
        }
        if(signed)_int="-"+_int;
        string=_int;
        if(_float.length)string+=","+_float;
        this.value=string;
    },

    keyup:function(e){
//    alert('keyup');
        var scale=this.getAttribute("scale"),
            precision=this.getAttribute("precision"),
            precisionfree=this.getAttribute("precisionfree"),
            signed=this.getAttribute("signed"),
            _int,string,chars,_float,
            parts;
        var ax = this.value.split(',');
        if(precision==0 || ax.length == 1){
            _int=Ultra.Number.formatInt.call(this,this.value);
            if(signed && this.value.indexOf("-")==0 && _int!="0")_int="-"+_int;
            this.value=_int;
        }else if(precision!=null && precisionfree!=null &&ax.length > 1 && ax[1].length==0){

        }else if(precision!=null){
            if ( precisionfree!=null ) {
             precision = ax[1].length > precision ? precision : ax[1].length;
            }
            string=this.value.replace(/\D/g,"");
            if(scale!=null && string.length>scale)string=string.substr(string.length-scale,scale);
            chars=string.split(""),
            _float=chars.splice(chars.length-precision,precision).join(""),
            _int=Ultra.Number.formatInt.call(this,chars.join(""));
            if(_float.length>0)_float=_float.lpad(0,precision);
            if(_float.length>0 && _int.length==0)_int="0";
            if(signed && this.value.indexOf("-")==0 && (_int.match(/[^0]/) || _float.match(/[^0]/)))_int="-"+_int;
            this.value=_int.length>0 && _float.length>0?_int+","+_float:"";
        }else{
            parts=this.value.split(","),_float=parts.length>1?parts.pop():null,
            _int=Ultra.Number.formatInt.call(this,parts.join(""));
            if(signed && this.value.indexOf("-")==0 && (_int.match(/[^0]/) || (_float!=null && _float.match(/[^0]/))))_int="-"+_int;
            if(_float!=null){
                if(_int.length==0)_int="0";
                _int+=","+_float;
            }
            this.value=_int;
        }
    },

    blur:function(e){

        var precision=this.getAttribute("precision"),precisionfree=this.getAttribute("precisionfree"),parts,_int,_float;
        if(precision==null){
            parts=this.value.split(",");
            if(parts.length==2){
                _int=parts[0],_float=parts[1];
                _float=parseFloat("."+_float);
                if(_float > 0)this.value=_int+","+String(_float).substr(2);
                else this.value=_int;
            }
        } else if (precision!=null && precisionfree!=null) {
            parts=this.value.split(",");
            if(parts.length==2 && !parts[1]){
             this.value = parts[0];
            }
        }
    },

    setPrecision:function(precision){
        this.setAttribute("precision",precision);
        this.value=this.value.replace(".",".","g").replace(",",".");
        if(this.value.length>0)Ultra.Number.number2String.call(this);
    },

    setValue:function(value){
        this.value=value;
        Ultra.Number.number2String.call(this);
    }
};

function putPoint(value){
    var c=0,r="",x;
    for(x=value.length-1;x>=0;x--){
        c++;
        r=value.charAt(x)+r;
        if(c%3==0 && x>0)r="."+r;
    }
    return r;
}

function float2Money(value,scale,precision){
    value=String(value==null?"":value);
    if(value=="0" || value.length==0)return value;
    value=value.replace(/^0+/,"");
    scale-=precision;
    if(value.indexOf(".")==-1){
        value=putPoint(value.substr(Math.max(value.length-scale,0),scale))
        if(precision>0)value+=","+String(0).times(precision);
        return value;
    }else{
        var parts=value.split("."),
            _int=parts[0].length==0?"0":putPoint(parts[0].substr(Math.max(parts[0].length-scale,0),scale)),
            _float;
        if(!precision)return _int;
        _float=parts[1].substr(0,precision).rpad(0,precision);
        if (_float.length>0)_int+=","+_float;
        return _int;
    }
}

function MaskMoney(el,scale,precision,is_signed){
    if(el.value.length==0)return;

    var m = is_signed ? el.value.match(/-/g) : null;
    var multiple = !m || m.length % 2 == 0 ? 1 : -1;
    var value = el.value.replace(/\D/g,"");
    var _int,_float;

    if(value.length > scale){
      value = value.substr(value.length-scale);
    }else{
      value = value.lpad(0,scale);
      _int = value.substr(0,scale-precision),
      _float = value.substr(value.length-precision,precision);
      value = Number(_int+"."+_float)*multiple;
      el.value = float2Money(value,scale,precision);
    }
}


//DESUSO
function _MaskMoney(el, len, dec, sig, Null) {
 if (el.value.length == 0 || el.value == '0') return;
 if (!(/-/).test(el.value) && sig) sig = 0;
 var Value = el.value.replace(/\D/g, "").replace(/^0+/, "").substr(0, len),
  c = 0,
  limit = Math.max(Value.length - dec, 0),
  Value1 = putPoint(Value.substr(0, limit)),
  Value2 = Value.substr(limit, Value.length);
 if (Value2 && !Value1) Value1 = 0;
 if (Number(Value2) > 0) Value2 = Value2.lpad(0, dec);
 el.value = [Value1, Value2].grep("return String(e).length");
 if (sig) el.value = "-" + el.value;
}


//
// Modifica valor de campos data. Se for valor inválido, avisa perguntando se deseja corrigir
// ou apagar. Se possuir ano com dois digitos, coloca um '20' na frente ('05/08/07' vira '05/08/2007').
//
// @param self input
//        e    event
//
function modifyDate(self, e) {
 var value = self.value;
 // Data Válida
 if (value.match(/^\d{1,2}\/\d{1,2}\/(?:\d{2}|\d{4})$/)) {
  // Data com ano Composto por dois Digitos
  if (value.match(/\/\d{2}$/)) {
   // Transforma em quatro.
   self.value = value.replace(/\/(\d{2})$/, '/20$1');
  }
 }
}

//
// Checagem de data.
//
// @param date data
//
function checkDate (date) {
 var parse = date.split("/"),
     day   = parseInt(Number(parse[0])),
     month = parseInt(Number(parse[1])),
     year  = parseInt(Number(parse[2])),
     last
 if ([day, month, year].any(isNaN)) return false;
    last =
   month < 8     // se mês é menor que 8 (janeiro - julho)
 ? month == 2    // se mês é 2 (fevereiro)
 ? !(year % 100) // se ano é divisivel por 100
 ? !(year % 400) // se ano é divisivel por 400
 ? 29            // então ano é bissexto
 : 28            // se não, ano não é bissexto
 : !(year % 4)   // se ano é divisivel por 4
 ? 29            // então ano é bissexto
 : 28            // se não, ano não é bissexto
 : month % 2     // se mês é impar
 ? 31            // então último dia é 31
 : 30            // se não, último dia é 30
 : month % 2     // se mês é maior que 7 (agosto - dezembro) e é impar
 ? 30            // então último dia é 30
 : 31;           // se não, último dia é 31
 return day && day <= last && month && month <= 12
}

//
// Função utilizada quando um form de insert eh submetido em outra pagina e tem de
// alterar o valor de um combo em outra janela
//
// @param formName    Nome do FORM onde o valor sera inserido
//        comboName   Nome do input onde o valor sera inserido
//        value       Valor a ser inserido (value do option)
//        description Descricao do valor inserido (conteudo da option visivel)
//
function changeParentSelect (formName, comboName, value, description) {
 var frm   = document.forms[formName],
     combo = frm.elements[comboName],
     vcombo= frm.elements['_v.'+comboName],
     i,option;

 // Se for uma aray, encontra o combo certa!
 if ( (!combo.type) && combo.length ) {
  for (i=0;i<combo.length;i++) {
   if ( combo[i].getAttribute('ActiveCombo') == 1 ) {
    combo  = combo[i];
    try { vcombo = vcombo[i]; } catch (e) {}
   }
  }
 }

 // Seta o valor no combo.
 combo.value = value;
 try { vcombo.value = value } catch (e) {}

 // Se não setou, cria uma opção.
 if (combo.value != value) {
  option = document.createElement("option");

  option.text     = description;
  option.value    = value;
  option.selected = true;

  combo.options.add(option)
 }

 // Dispara o evento.
 //setTimeout(function () { moz ? combo.onchange() : combo.fireEvent('onchange') }, 0);
//  setTimeout(function () { fireEvent(combo, 'change'); }, 0);
 window.setTimeout(function () { $(combo).callEvent("change") }, 100);
// combo.onchange();

 combo.lastValue = value;

 // @change <matheus@sistemica.info> 2006-08-01 FF não suporta. Até fecha, mas não deixa completar a requisição.
//  if (!inside && !moz)
//   window.close()
}

//
// Função para limitar e contar(opcional) os caracteres de um textarea
//
// @param event
//        Field     input
//        MaxLength tamanho mï¿½imo de caracteres
//        Counter   id do ojeto contador ou o prï¿½rio objeto
//
function TextLimit (event, Field, MaxLength, Counter) {
 var vSize = Field.value.replace(/[\r]/g,'').replace(/[\r\n]/g,'..').length + 1;

 if(!Field.onkeyup){
  Field.onkeyup = function () {
   if (this.value.length > MaxLength) this.value = this.value.substr(0, MaxLength);
   if (typeof Counter == "string") {
    document.getElementById(Counter).innerHTML = MaxLength - Field.value.length;
   } else if (typeof Counter == "object") {
    Counter.innerHTML = MaxLength - Field.value.length;
   }
  }
 }

 if (vSize > MaxLength && ![0,8].include(String(keyCode(event))))return false;
}

//
// Função para travar o teclado apenas para digitação de números
// Se usuário não apertar as teclas backspace, enter ou setas,
// retorna true caso tenha apertado tecla numerica.
//
// @param  event
// @return boolean
// @see    Array.prototype.contains (Array.contains.js)
//         KeyCode/KeyChar          (Key.js)
//
function NumberOnly(e) {
 var key = String(keyCode(e)),
     chr = keyChar(e);
 if (![0, 8, 13].include(key)) return key != 32 && !isNaN(chr);
}

//
// Função para travar o teclado apenas para digitação de números
// positivos e negativos
// Libera digitação de teclas especiais, números e sinal negativo
// somente uma vez.
//
// @param  event
// @return boolean
// @see    Array.prototype.contains (Array.contains.js)
//         KeyCode/KeyChar          (Key.js)
//
function PositiveNegativeOnly(e, t) {
 var key = String(keyCode(e)),
     chr = keyChar(e),
     menos = new RegExp(/^[-]{1}/);

 //verifica se tem um sinal de menos no início da string
 menos = menos.exec(t.value);
 t.value = t.value.replace(/[A-Za-z!@#$%*+={}\[\]~^`´,.<>;\-]/gi,'');

 if(menos != null){
  t.value = menos + t.value;
 }

 if (t.value == '-'){
   t.value = '';
 }

 if (![0, 8, 13, 45].include(key)) return key != 32 && !isNaN(chr);
 // é somente 1 sinal de menos
 if ( key == 45 )
  if( t.value.indexOf('-') == -1 ) { //não encontrou ainda nenhum -
   t.value = '-' + t.value;
   return false;
  } else
   return false;
}

//
// Usado no Html::input::Increment
//
function addNumber ( Field, Num, Flag ) {
 var x = Number( Field.value ),
 y = Number( Num );
 Field.value = ( x + y );
 if (!Flag && Number(Field.value) < 0) Field.value = 0;
}

//
// Funcao usada pelo Html::SuperInterface para exibir/ocultar suas layers
//
// @param layClass ID da SuperInterface
//        layName  Nome da layers
//        on_off   Boolean, se verdadeiro, exibe, se falso, oculta
//
function SuperInterfaceShow(layClass,layName,on) {
 if (layName == null) layName = 0;
 if ( on ) SuperInterfaceShow(layClass,layers_visible[layClass],0);
 var aba = getSIb(layClass,layName),
    altClass,lay,layAltClass;
 if(!aba)return;
 altClass = aba.getAttribute('altClass');
 lay = getSI(layClass,layName);
 layAltClass = lay.getAttribute('altClass');

 // Exibe
 if ( on ) {
  layers_visible[layClass] = layName;
  if ( aba.className.match( /Off$/ ) || aba.className.match( /Off / ) || !aba.className ) {
   aba.setAttribute('altClass',aba.className);
   lay.setAttribute("altClass", lay.className);
   lay.className = layAltClass || "SuperInterfaceLayer";
   aba.className = altClass ? altClass : "SuperInterfaceAba";
  }
  lay.setAttribute('seen',1);

 // Oculta
 } else {
  if ( !aba.className.match( /Off$/ ) || aba.className.match( /Off / ) || !aba.className ) {
   aba.setAttribute('altClass',aba.className);
   lay.setAttribute("altClass", lay.className);
   lay.className = layAltClass || "SuperInterfaceLayerOff";
   aba.className = altClass ? altClass : "SuperInterfaceAbaOff";
  }
 }
}

//
// Inicializa funcionalidade de contextmenus de abas de uma superinterface.
//
// @param aba  id referente ao elemento da aba ou ele mesmo
//        menu id referente ao contextmenu da aba ou ele mesmo
//
function SuperInterfaceInitMenu(aba, menu) {
 // Extende elementos com prototypejs
 aba  = $(aba);
 menu = $(menu);
 var t, locked = false,func,code;

 // Handler para exibir contextmenu no onmouseover da aba.
 func = function (e) {
  clearTimeout(t);
  if ($(menu).getStyle("visibility") != "visible" && !locked) {
   var coords = Position.page(aba); // Posição da aba na página.
   // Posiciona/exibe menu abaixo da aba.
   menu.show(coords[0], coords[1] + $(aba).getHeight(), true);
   window.Ultra.ContextMenu.isFromSI = true;
   // Classe responsável pelo estilo da aba quando menu está aberto.
   $(aba).addClassName("SuperInterfaceAbaMenuOpened");
   locked = true;
   // Remove-se após exibir menu.
   Event.stopObserving(aba, "mouseover", arguments.callee);
  }
 };

 Event.observe(aba, "mouseout", function () {
  t = setTimeout(function () { locked = false }, 0);
 });

 Event.observe(aba, "mousedown", function () {
  locked = true;
 });

 // Adiciona o evento acima na aba.
 code = function () {
  Event.observe(aba, "mouseover", func);

  window.Ultra.ContextMenu.isFromSI = false;
 };

 code(); // Executa.

 // Isto serve para quando clicar no menu impedir que a aba exiba seu conteúdo.
 // Event.observe(menu, 'click', function (e) { Event.stop(e) });
 // Sempre que contextmenu for escondido, Readiciona evento onmouseover e retira estilo das classes da aba.
 menu.listen("onHide", function () {
  code();
  $(aba).removeClassName("SuperInterfaceAbaMenuOpened");
 });
}

//
// Excessão para IE7. Inicializador do menu das abas.
//
function SuperInterfaceInitMenuForIE7(menu) {
 if (!moz && window.XMLHttpRequest) {
  Event.observe(menu, "mouseover", function (e) {
   window.Ultra.ContextMenu.isFromSI = false;
  });
  Event.observe(menu, "mouseout", function (e) {
   if (!Position.within(menu, e.clientX, e.clientY))
    window.Ultra.ContextMenu.isFromSI = true;
   else
    Event.stop(e)
  });
 }
}

//
// Exibe a promixa aba da layer e oculta a atual.
//
// @param  layClass ID da SuperInterface
// @return boolean  verdadeiro de teve sucesso, falso se era a ultima layer ou id nï¿½ existe
//
function SuperInterfaceShowNext( layClass, i ) {
 var nextLayer = getSINextLayerUnseenIndex ( layClass, i );
 if ( nextLayer > 0 && getSI( layClass, nextLayer ) != null ) {
  SuperInterfaceShow( layClass, nextLayer, 1 );
  if ( getSI( layClass, nextLayer ).offsetTop < 300 ) {
   window.scrollTo( 0, 0 );
  } else {
   window.scrollTo( 0, getSI( layClass, nextLayer ).offsetTop );
  }
  return true;
 } else {
  return false;
 }
}

//
// Retorna objeto do layer.
//
function getSI ( layClass, i ) {
 return document.getElementById('layers-'+layClass+'-'+i);
}

//
// Retorna objeto do botão do layer.
//
function getSIb ( layClass, i ) {
 return document.getElementById('b-layers-'+layClass+'-'+i);
}

//
// Retorna index (integer) do layer imediatamente após o layer atualmente visível
//
function getSINextLayerIndex ( layClass, i ) {
 var nextLayer;
 if ( i == null || i == 0 ) i = 1;
 if ( layers_visible[layClass] == 0 ||
      layers_visible[layClass] == null ) {
  nextLayer = 1;
 } else {
  nextLayer = layers_visible[layClass] + i;
 }
 if ( getSI( layClass, nextLayer ) ) {
  return nextLayer;
 } else {
  return null;
 }
}
//
// Retorna o INDEX do poximo layer apenas se ele ainda não tiver sido visto!
//
function getSINextLayerUnseenIndex ( layClass, i ) {
 var nextLay = getSINextLayerIndex ( layClass, i );
 if ( nextLay && getSI(layClass, nextLay).getAttribute('seen') != '1' ) {
  return nextLay;
 } else {
  return null;
 }
}

//
// Funcao usada pelo Html::SuperInterface::Vert para exibir/ocultar suas layers
//
// @param layClass ID da SuperInterface
//        layName  Nome da layers
//        on_off   Boolean, se verdadeiro, exibe, se falso, oculta
//
function SuperInterfaceVertShow(layClass,layName,on) {
 if ( layName == null ) layName = 0;
 if (on) {
  if ( layers_visible[layClass] != null && layers_visible[layClass] == layName ) {
   SuperInterfaceShow(layClass,layers_visible[layClass],0);
   layers_visible[layClass] = null;
  } else {
   if (on < 2) SuperInterfaceShow(layClass,layers_visible[layClass],0);
   layers_visible[layClass] = layName;
   document.getElementById('layers-'+layClass+'-'+layName).style.display = "";
   document.getElementById('b-'+'layers-'+layClass+'-'+layName).className="SuperInterfaceVertAba";
  }
 } else {
  document.getElementById('layers-'+layClass+'-'+layName).style.display = "none";
  document.getElementById('b-'+'layers-'+layClass+'-'+layName).className="SuperInterfaceVertAbaOff";
 }
}

//
// Valida CNPJ/CPF
//
// @see CPF
//      CNPJ
//
function checkCNPJ_CPF (Document) {
 var Value = Document.replace( /\D/g, "");

 if (Value.length > 0) {
  if (Value.length == 11) {
   return checkCPF(Value);
  } else if (Value.length == 14) {
   return checkCNPJ(Value);
  } else {
   return false;
  }
 }

 return true;
}

//
// Valida CPF
//
function checkCPF(cValue){

 cValue = cValue.replace(/\D/g, '');

 var i,c,dv,d1;
 c = cValue.substr(0,9);
 dv = cValue.substr(9,2);

 d1 = 0;
 for (i = 0; i < 9; i++)
 {
  d1 += c.charAt(i)*(10-i);
 }


 if (d1 == 0) return false;
 d1 = 11 - (d1 % 11);


 if (d1 > 9) d1 = 0;

 if (dv.charAt(0) != d1)
 {
  return false;
 }

 d1 *= 2;
 for (i = 0; i < 9; i++)
 {
  d1 += c.charAt(i)*(11-i);
 }
 d1 = 11 - (d1 % 11);
 if (d1 > 9) d1 = 0;

 if (dv.charAt(1) != d1)
 {
  return false;
 }
 return true;
}

//
// Valida CNPJ
//
function checkCNPJ (cValue) {
 cValue = cValue.replace(/\D/g, '');
 var i,c,dv,cont,d1;
 c = cValue.substr(0,12);
 dv = cValue.substr(12,2);

 cont = 0;
 d1 = 0;
 for (i = 0; i < 12; i++)
 {
  d1 += c.charAt(11-i)*(2+(i % 8));
 }



        if (d1 == 0) return false;
        d1 = 11 - (d1 % 11);

 if (d1 > 9) d1 = 0;

 if (dv.charAt(0) != d1)
 {
  return false;
 }

 d1 *= 2;
 for (i = 0; i < 12; i++)
 {
  d1 += c.charAt(11-i)*(2+((i+1) % 8));
 }


 d1 = 11 - (d1 % 11);


 if (d1 > 9) d1 = 0;

 if (dv.charAt(1) != d1)
 {
  return false;
 }
 return true;
}

//
// Mascara para CNPJ/CPF
//
// @see Mask
//
function maskCNPJ_CPF(Field, e) {
 var vSize = Field.value.length;

 if (vSize <= 14 || (keyCode(e) == 8 && vSize == 14)) {
  Mask( Field, "999.999.999-99", e );
 }

 if (vSize > 14) {
  Mask( Field, "99.999.999/9999-99", e );
 }
}

//
// Usada pela classe Html::input::ForeignKey::ValueInput
// Ele troca o valor da combo no onkeydown ou onblur, dependendo do caso.
//
function checkValueInput ( Field, Combo, Force ) {
   (function (form, field) {
      var i = inputInArr(form, field);
      if (i != null) Combo = Combo[i];
   })(Field.form, Field);
   if ( Combo.lastValue == null ) Combo.lastValue = '';

   for (var i = 0; i < Combo.childNodes.length; i ++) {
      if ( Combo.childNodes[i].value == null ) Combo.childNodes[i].value = '';
      if ( Field.value.toUpperCase() == Combo.childNodes[i].value.toUpperCase() ) {

         Combo.childNodes[i].selected = true;
         Field.value = Combo.childNodes[i].value;

         if(Field.value != Combo.lastValue) {
            $(Combo).callEvent("change");
         }
         Combo.lastValue = Field.value;
         return true;
      }
   }
   // Se chegou ate aqui, eh porque nao encontrou a opçao desejada (vamos mandar o AJAX se encarregar de procurar então)
   // if ( Force && Field.value != Combo.lastValue ) {
   if ( Force && Field.value != Combo.lastValue ) {
   //  Combo.lastValue = Field.value;
      changeParentSelect( Combo.form.name, Combo.name, Field.value, 'Carregando...' );
   }
}

//
// Metodo utilizado pela onblur do Html::input::ForeignKey::ValueInput
// Ele apenas checa se valor eh valido ou nao.
//
function checkValueInputOnBlur ( Field, Combo ){
 (function (form, field) {
  var i = inputInArr(form, field);
  if (i != null) Combo = Combo[i];
 })(Field.form, Field);
 var Status = false,i;

 for ( i = 0; i < Combo.childNodes.length; i ++ ) {
  if ( Field.value.toUpperCase() == Combo.childNodes[i].value.toUpperCase() ) {
   Status = true;
  }
 }

 if ( Field.value != '' && !Status ) {
  alert( 'Valor digitado não é uma opção válida.' );
//  Field.focus();
 }

 return Status
}

//
// Abre janela de Browse Avanï¿½do (quele quando se clica no "Mais").
//
// O FormId necessario como parametro indica o Form normal de origem. Este form de origem deve ser tambem um FormId
// de Browse, o qual sera submetido para a janela externa que apresentara o Form avanï¿½do importando os valores do Form de Origem.
//
// @param FormId ID do FORM de origem
//
function showBrowseForm ( FormId ) {
 var myform = document.forms[FormId],
     old_method       = myform.elements['_' + myform.elements['_ClassId'].value + '.Method'].value,
     old_parentformid = myform.elements['_ParentFormId'].value;


 // Abre a nova janela
 window.open('',FormId,'width=420,height=550,left=0,top=0,scrollbars=yes,menubar=no,status=no,toolbar=no,titlebar=no,resizable=yes,location=no,directories=no');

 myform.target = FormId;
 myform.elements['_' + myform.elements['_ClassId'].value + '.Method'].value = 'BrowseForm';
 myform.submit();

 myform.elements['_ParentFormId'].value = old_parentformid;
 myform.elements['_' + myform.elements['_ClassId'].value + '.Method'].value = old_method;
 myform.target='';
}

//
// Renomeia um relatório
//
function RenameReport ( FormId,        // Id do formulário.
                        el,            // Interface.
                        ClassReportId, // Código do relatório.
                        CurrName ) {   // Nome atual do relatório.
 // Formulário.
 var myform  = document.forms[FormId],

 // Prompt de entrada do novo nome do formulário.
     NewName = prompt( "Renomear Relatório "+ClassReportId, CurrName );

 // Nome indefinido ou inalterado; cancela operação.
 if ( NewName == null || NewName.length == 0 || NewName == CurrName ) {
  return;
 }

 // Settings.
 myform.elements['_' + myform.elements['_ClassId'].value + '.ClassReportId'].value = ClassReportId;
 myform.elements['_' + myform.elements['_ClassId'].value + '.XMethod'].value       = 'Rename';
 myform.elements['_' + myform.elements['_ClassId'].value + '.Filename'].value      = NewName;

 // Submete.
 submitReport( FormId, el );

 myform.elements['_' + myform.elements['_ClassId'].value + '.ClassReportId'].value = '';
}

//
// Semelhanto ao SaveReport, mas sobreescreve um report ja existente.
//
function SaveReportAs ( FormId, el, ClassReportId ) {
 var myform = document.forms[FormId];

 if ( ! confirm('Deseja mesmo sobreescrever este Modelo com as configurações da consulta atual?') ) {
  return;
 }

 myform.elements['_' + myform.elements['_ClassId'].value + '.ClassReportId'].value = ClassReportId;
 myform.elements['_' + myform.elements['_ClassId'].value + '.XMethod'].value       = 'Save';

 submitReport( FormId, el );

 myform.elements['_' + myform.elements['_ClassId'].value + '.ClassReportId'].value = '';
}

//
// Exlui um ClassReport
//
function DeleteReport ( FormId, el, ClassReportId, isShared ) {
 var myform = document.forms[FormId],
     message = 'Deseja realmente excluir definitivamente este Modelo?';

 if ( isShared ) {
  message += "\n\nATENÇÃO: Este modelo está compartilhado com outras unidades!";
 }

 if ( ! confirm( message ) ) {
  return;
 }

 myform.elements['_' + myform.elements['_ClassId'].value + '.ClassReportId'].value = ClassReportId;
 myform.elements['_' + myform.elements['_ClassId'].value + '.XMethod'].value       = 'Delete';

 submitReport( FormId, el );

 myform.elements['_' + myform.elements['_ClassId'].value + '.ClassReportId'].value = '';
}

//
// Define um ClassReport como padrão para a consulta
//
function BookmarkReport ( FormId, el, ClassReportId ) {
 var els = document.forms[FormId].elements;

 if ( ! confirm('Deseja realmente '+(ClassReportId == '' ? 'desmarcar' : 'definir')+' o Modelo '+ClassReportId+' como padrão incial desta tabela?') ) {
  return;
 }

 els['_' + els['_ClassId'].value + '.ClassReportId'].value = ClassReportId;
 els['_' + els['_ClassId'].value + '.XMethod'].value       = 'Bookmark';

 submitReport( FormId, el );

 els['_' + els['_ClassId'].value + '.ClassReportId'].value = '';
}

//
// Define um ClassReport como compartilhado (ou não)
//
function ShareReport ( FormId, el, ClassReportId, share ) {
 var els = document.forms[FormId].elements;

 if ( ! confirm('Deseja realmente '+(share == 1 ? 'compartilhar' : 'tornar privado')+' o Modelo '+ClassReportId+ (share == 1 ? ' com os demais usuários desta empresa' : '') + '?') ) {
  return;
 }

 els['_' + els['_ClassId'].value + '.ClassReportId'].value = ClassReportId;
 els['_' + els['_ClassId'].value + '.XMethod'].value       = share == 1 ? 'Share' : 'Unshare';

 submitReport( FormId, el );

 els['_' + els['_ClassId'].value + '.ClassReportId'].value = '';
}

//
// Salva Modelo
//
function SaveReport ( FormId, el, Bookmark ) {
 var els               = document.forms[FormId].elements,
     ReportNameInput   = els['_' + els['_ClassId'].value + '.Filename'],
     old_ClassId       = els['_ClassId'].value,
     old_ParentClassId = els['_ParentClassId'].value,
     ReportName;

 // É um Bookmark em BrowseCombo
 if ( Bookmark == 2 ) {
  els['_ClassId'].value       = els['_ParentClassId'].value;
  els['_ParentClassId'].value = old_ClassId;
  els['_SubMethod'].value     = 2;
 }

 if ( ReportNameInput && ReportNameInput.value.length == 0 ) {
  ReportName = prompt( "Nome do Modelo", "Favorito" );

  if ( ReportName == null || ReportName == '' ) {
   return;
  }

  ReportNameInput.value = ReportName;
 }

 if ( els['_' + els['_ClassId'].value + '.Bookmark'] && Bookmark ) {
  els['_' + els['_ClassId'].value + '.Bookmark'].checked = Bookmark ? true : false;
 }

 els['_' + els['_ClassId'].value + '.XMethod'].value  = 'Save';
 submitReport( FormId, el );
 els['_' + els['_ClassId'].value + '.XMethod'].value = '';
 els['_ClassId'].value       = old_ClassId;
 els['_ParentClassId'].value = old_ParentClassId;
 els['_SubMethod'].value     = '';
}

//
// Submete o formulário
//
function submitReport ( FormId, el ) {
 var myform = document.forms[FormId],
     els    = myform.elements,
     old_method = els['_' + els['_ClassId'].value + '.Method'].value;

 els['_' + els['_ClassId'].value + '.Method'].value  = 'AjaxReport';

 requestURL( 'POST', 'Class.cgi', myform, el, 0 );

 els['_' + els['_ClassId'].value + '.Method'].value = old_method;
 myform.target='';
}

function LoadReportMSelect ( FormId, ReportId ) {
 var myform  = document.forms[FormId],
     els     = myform.elements,
     ClassId = els['_ClassId'].value;

 if ( !myform.getAttribute('Selected') && ( $A(els['_'+ClassId+'.MSelect'])[0] && $A(els['_'+ClassId+'.MSelect'])[0].type != 'hidden' ) && !isChecked(els['_'+ClassId+'.MSelect']) ) {
  alert( 'Selecione um ou mais registros para aplicar este Modelo.' );
  return false;
 }

 try { window.Ultra.ContextMenu.currentMenu.hide() } catch (e) {};
 LoadReport( myform.name, ReportId, { '_MSelectOnly': 1 } );
}

//
// Carrega ClassReport
//
function LoadReport ( FormId, ReportId, params ) {
 var myform  = document.forms[FormId],
     els     = myform.elements,
     ClassId = els['_ClassId'].value;

 var qs = {
  '_ClassId'                     : els['_ClassId'].value,
  '_Level'                       : els['_Level'].value };

 qs['_'+ClassId+'.Method']          = 'AjaxBrowse';
 qs['_'+ClassId+'.ParentElementId'] = els['_'+ClassId+'.ParentElementId'] ? els['_'+ClassId+'.ParentElementId'].value : '';

 if ( ReportId ) {
  qs['_'+ClassId+'.ClassReportId'] = ReportId;
 } else {
  qs['_'+ClassId+'.Reset']         = 1;
 }

 if ( params ) {
  for ( k in params ) qs[k] = params[k];
 }

 // Se for para enviar os MSelect
 if ( qs['_'+ClassId+'.MSelectOnly'] || qs['_MSelectOnly'] ) {
  delete qs['_MSelectOnly'];
  qs['_'+ClassId+'.MSelectOnly'] = els['_'+ClassId+'.MSelectOnly'] && els['_'+ClassId+'.MSelectOnly'].value ? els['_'+ClassId+'.MSelectOnly'].value : 1;

  // MSelect foi definido no form
  if ( ( els['_'+ClassId+'.Method'].value == 'Update' || els['_'+ClassId+'.Method'].value == 'Select' ) && myform.getAttribute('Selected') ) {
   qs['_'+ClassId+'.MSelect']     = [ myform.getAttribute('Selected') ];
   qs['_'+ClassId+'.MSelectOnly'] = 2;

  // Só existe um objeto
  } else if ( els['_'+ClassId+'.MSelect'] && els['_'+ClassId+'.MSelect'].value ) {
   qs['_'+ClassId+'.MSelect'] = [ els['_'+ClassId+'.MSelect'].value ];

  // É uma lista de objetos
  } else {
   qs['_'+ClassId+'.MSelect']  = [ $A(els['_'+ClassId+'.MSelect']).map(function(e){ if (e.checked || e.type == 'hidden') { return e.value } else { return null } }) ];
  }

 }

 // AjaxBrowse
 if ( els['_' + els['_ClassId'].value + '.Method'].value == 'AjaxBrowse' || myform.getAttribute('Selected') ) {
  var targetEl = els['_'+ClassId+'.ParentElementId'] && els['_'+ClassId+'.ParentElementId'].value ? document.getElementById( els['_'+ClassId+'.ParentElementId'].value ) : $(myform).up('div');
  requestURL( 'POST', 'Class.cgi', qs, targetEl, 0, 180000 );

 // Submissão Normal
 } else {
  els['_' + els['_ClassId'].value + '.Method'].value        = 'BrowseForm';
  if ( els['_' + els['_ClassId'].value + '.ClassReportId'] ) els['_' + els['_ClassId'].value + '.ClassReportId'].value = ReportId;
  myform.target = '_self';
  myform.submit();
 }
}

//
// Exibe relatórios compartilhados da unit.
//
function showReportsBelow ( Unit ) {
 // Primeiro relatório;
 var Report = Unit.nextSibling,
 // Primeiro relatório está exibido ou oculto.
     Display =
    Report.style.display
  ? ""
  : "none";

 // Há uma linha e esta é um relatório.
 while ( Report && Report.className != "row_top" ) {
  // Relatório é exibido/ocultado.
  Report.style.display = Display;

  // Próximo relatório.
  Report = Report.nextSibling;
 }
}

// Abre um popup com o registro para Select ou Update - utilizado no contextmenu do form de Browse.
function popupInstance ( FormId, method ) {
 var myform       = document.forms[FormId],
     old_action = myform.action,
     old_level  = myform.elements['_Level'].value,
     old_method = myform.elements['_'+myform.elements['_ClassId'].value+'.Method'].value,
     winName;

 myform.action = '/Ultra/'+getSession()+'/Class.cgi';
 myform.elements['_Level'].value = 0;

 myform.elements['_'+myform.elements['_ClassId'].value+'.Method'].value = method ? method : 'Select';

 winName = 'popupSelect_' + getSession().replace(/\//g, '_');
 window.open( '', winName, "toolbar=no,titlebar=no,status=yes,scrollbars=yes,menubar=yes,location=no,resizable=yes,width=640,height=480" );
 setValue(FormId, {}, true, winName );

 myform.action = old_action;
 myform.elements['_Level'].value = old_level;
 myform.elements['_'+myform.elements['_ClassId'].value+'.Method'].value = old_method;

 return false;
}

//
// Cancela a edição de um Form e volta para o Browse
//
function FormCancel(frm, url) {
 frm = $(frm);
 if (confirm("As alterações no registro serão perdidas. Deseja mesmo continuar?")) {
  if (url) {
   window.open(url, "_self");
  } else if (frm.Method() == "Update") {
   frm.field("_XMethod").value = "Unlock";
   frm.field("_NextMethod").value = '';
   frm.Method("Select");
   frm.submit();
  } else {
   window.open(document.location.pathname+"?"+$H({_ClassId: frm.ClassId(), _Level: frm.Level()}).toQueryString(), "_self");
  }
 }
 return false;
}

//
// Utilizado em tabelas de Browse padrão, em que cada row é um registro e possui o atributo 'key' o qual contém as chaves primárias do registro.
// Você deve passar apenas um elemento qualquer dentro da TR (ou a própria TR) e o método a solicitar (Select por padrão).
//
// @param $el     Qualquer elemento DOM dentro da TR ou a prria TR
//        $Method Método a executar, Select ou Update em geral.
//
function ri(el, method) {
 var myform = findParentElement( el, 'FORM' ),myrow,key,hash,k;
     myform = document.forms[ myform.getAttribute('PForm') ];
 myrow = el.nodeName == 'TR' ? el : findParentElement( el, 'TR' );

 key  = (new String(myrow.getAttribute('key'))).toQueryParams();
 hash = {};
 for ( k in key ) {
  hash[ myform.elements['_ClassId'].value + '.' + k ] = key[k];
 }

 hash[ '_' + myform.elements['_ClassId'].value + '.Method' ] = method ? method : 'Select';

 setValue(myform.name, hash, true );
}
window.rowInstance=ri;

//
// Verifica se há algum MSelect selecionado. Se não houver, dá mesnagem de erro e retorna falso.
//
function pmeCheck ( FormId ) {
 var els = document.forms[FormId].elements;
 if ( !els['_'+els['_ClassId'].value+'.MSelect'] || !isChecked(els['_'+els['_ClassId'].value+'.MSelect']) ) {
  alert('Escolha um ou mais registros para poder executar a operação!');
  return false;
 }
 return true;
}

// Alias para popupMultiExecute
function pme ( FormId, FKId, single, doAjax, fn ) {
 return popupMultiExecute ( FormId, FKId, single, doAjax, fn )
}

// Abre um popup para MultiInsert - utilizado no contextmenu do form de Browse.
function popupMultiExecute ( FormId, FKId, single, doAjax, fn ) {
 var myform      = document.forms[FormId],
     brform      = document.forms[FormId.replace(/^P/,'')] || myform,
     els         = myform.elements,
     ClassId     = els['_ClassId'].value,
     old_action  = myform.action,
     old_level   = els['_Level'].value,
     old_method  = els['_' + ClassId + '.Method'].value,
     old_history = els['_History'].value,
     winName;

 // MultiExecute em registro único!!!
 if ( single ) {
  if ( isChecked(els['_'+ClassId+'.MSelect']) ) {
   slaveCheck(els['_'+ClassId+'.MSelect'],'_'+ClassId+'.MSelect',0);
  }

  // Se tiver passado os valores da FK no single...
  if ( single && new String(single).match(/=/) ) {
   els['_'+ClassId+'.XMethod'].value = single;

  // Se tiver passado os valores da FK no Selected do formulário (fixo)...
  } else {
   els['_'+ClassId+'.XMethod'].value = brform.getAttribute('Selected');
  }

 // MultiExecute usando checkbox de multipla seleção
 } else {
  // Se não selecionou nada, tranca!
  if ( !pmeCheck( FormId ) ) return false;
 }

 myform.action = '/Ultra/'+getSession()+'/Class.cgi';
 els['_Level'].value = 0;

 els['_'+ClassId+'.Method'].value        = 'MultiExecute';
 els['_'+ClassId+'.XForeignKeyId'].value = FKId;
 els['_History'].value                   = ClassId + ':MultiExecute';

 // Se for submeter via AJAX (sem exibir form)
 if ( doAjax ) {
  new Ultra.Class( ClassId ).MultiExecute(
   FormToHash( myform ),
   {
//    keepSession: true, // Não tenho certeza do que deve ser feito aqui - sempre funcionou bem assim
    onSuccess: ( fn ? fn : function(){ if ( this.ErrorStatus2 && this.ErrorStatus2.MultiInsert ) { alert(this.ErrorStatus2.MultiInsert) } else { alert( 'OK!' ) } } )
   }
  );

 // Submissão normal, em um popup.
 } else {
  winName = 'popupMultiInsert_' + getSession().replace(/\W/g,'_');
  window.open( '', winName, "toolbar=no,titlebar=no,status=yes,scrollbars=yes,menubar=yes,location=no,resizable=yes,width=640,height=480" );

  myform.target = winName;
  myform.submit();
 }

 myform.action                     = old_action;
 els['_Level'].value               = old_level;
 els['_'+ClassId+'.Method'].value  = old_method;
 els['_History'].value             = old_history;
 els['_'+ClassId+'.XMethod'].value = '';
 myform.target                     = null;

 return false;
}


//
// Abre janela de seleção avançada de apenas um campo para browse - quando eh clicado o botao direito do mouse sobre um input de browse.
//
// @param Field   Elemento do input do campo
//        FieldId Id do campo (SEM o ClassId)
//        type    Tipo: 1 = avançado, 0 - simples (padrão)
//
function showBrowseFormField ( el, FieldId, type, Check, targetel ) {
 var myform       = $(el).up('form'),
     Field        = myform.field(FieldId),
     els          = myform.elements,
     old_level    = els['_Level'].value,
     old_classid  = els['_ClassId'].value,
     old_formid   = els['_ParentFormId'].value,
     old_method   = els['_' + els['_ClassId'].value + '.Method'].value,
     old_fieldid  = els['_ParentFieldId'].value,
     i,old_newmethod;

 // Campos extendidos não suportam essa função
 if ( FieldId.match(/#/) ) {
  alert('Campos extendidos não suportam essa função.');
  return;
 }

 if ( ! targetel ) targetel = $(Field).up('td');

 // Checa dependências se requisitado
 if ( Check != undefined ) {
  for (i = 0; i < Check.length; i++) {
   if ( els[Check[i]] && els[Check[i]].tagName == "INPUT" ) {
    els[Check[i]].oncontextmenu();
    return false;
   }
  }
 }

 if ( old_method == 'BrowseCombo' || old_method == 'AjaxBrowseCombo' ) els['_ClassId'].value = els['_ParentClassId'].value;
 old_newmethod = els['_' + els['_ClassId'].value + '.Method'].value;

 myform.target = myform.name;
//  els['_Level'].value = 0; // Boggus?!
 els['_' + els['_ClassId'].value + '.Method'].value = 'AjaxBrowseFormField';
 els['_ParentFormId'].value = myform.name;
 els['_ParentFieldId'].value = FieldId;
 els['_BrowseFormFieldType'].value = type;

 requestURL( 'POST', 'Class.cgi', myform, targetel, 0 );

 els['_Level'].value = old_level;
 els['_' + els['_ClassId'].value + '.Method'].value = old_newmethod;
 els['_ClassId'].value = old_classid;
 els['_' + els['_ClassId'].value + '.Method'].value = old_method;
 els['_ParentFormId'].value = old_formid;
 els['_ParentFieldId'].value = old_fieldid;
 myform.target='';

 return false;
}

//
// Requisita form de export especifico do formato requisitado
//
// @param Form
//        Element Elemento de destino
//        Once    Parametro a ser passado ao requestURL, permitindo ou não cache inteligente de AJAX.
//
function showBrowseFormOptions ( myform, el, once ) {
 var old_method = myform.elements['_' + myform.elements['_ClassId'].value + '.Method'].value;

 myform.elements['_' + myform.elements['_ClassId'].value + '.Method'].value = 'AjaxBrowseFormOptions';

 requestURL( 'POST', 'Class.cgi', myform, el, once );

 myform.elements['_' + myform.elements['_ClassId'].value + '.Method'].value = old_method;
}

//
// Requisita form de uma tabela Slave
//
function showSlaveChildForm ( FormId, ClassId, SlaveClassId, ParentMethod, el, Method, force ) {
 var myform            = document.forms[FormId],
     old_method        = myform.elements['_' + ClassId + '.Method'].value,
     old_parent_method = myform.elements['_' + ClassId + '.ParentMethod'].value;

 myform.elements['_' + ClassId + '.Method'].value       = 'AjaxSlaveChild';
 myform.elements['_' + ClassId + '.ParentMethod'].value = Method ? Method : ParentMethod;
 myform.elements['_' + ClassId + '.SlaveClassId'].value = SlaveClassId;

 requestURL( 'POST', 'Class.cgi', myform, el, force != 0 ? 1 : 0 );

 myform.elements['_' + ClassId + '.Method'].value       = old_method;
 myform.elements['_' + ClassId + '.ParentMethod'].value = old_parent_method;
}

//
// Requisita lista de colunas para adicionar à consulta (Browse).
//
function ShowBrowseAddFields ( FormId, ClassId ) {
 var myform            = document.forms[FormId],
     old_classid       = myform.elements['_ClassId'].value,
     old_method        = myform.elements['_' + ClassId + '.Method'].value;

 myform.elements['_ClassId'].value                = ClassId;
 myform.elements['_' + ClassId + '.Method'].value = 'AjaxBrowseAddFields';

 requestURL( 'POST', 'Class.cgi', myform, document.getElementById(FormId+'.AddFields'), 1 );

 myform.elements['_ClassId'].value                = old_classid;
 myform.elements['_' + ClassId + '.Method'].value = old_method;
}

//
// Exibe tabelas relacionadas
//
function ShowBrowseAddTables ( FormId, ClassId, FKId, type ) {
 var myform            = document.forms[FormId],
     old_classid       = myform.elements['_ClassId'].value,
     old_method        = myform.elements['_' + ClassId + '.Method'].value,
     old_xmethod       = myform.elements['_' + ClassId + '.XMethod'].value;

 myform.elements['_ClassId'].value                 = ClassId;
 myform.elements['_' + ClassId + '.Method'].value  = 'AjaxBrowseAddTables';
 myform.elements['_' + ClassId + '.XMethod'].value = ( type || 'FK' ) + ':' + ( FKId || '' );

 requestURL( 'POST', 'Class.cgi', myform, document.getElementById(FormId+'.AddTables'+(FKId ? '.'+FKId : ( type == 'BackFK' ? '2' : '' ) ) ), 1 );

 myform.elements['_ClassId'].value                 = old_classid;
 myform.elements['_' + ClassId + '.Method'].value  = old_method;
 myform.elements['_' + ClassId + '.XMethod'].value = old_xmethod;
}


//
// Faz requisição do AjaxInputEvent. Esses eventos sao definidos diretamente nas classes Html::input
// dos campos e sao muito flexiveis. A requisicao eh feita atraves da aï¿½o C::AjaxInputEvent, que submete
// o form inteiro especificando no campo _ParentFieldId o nome do campo.
//
// @param FormId
//        FieldId
//        Input    Input element executando a ação
//        Element  (opcional) Caso voce espera uma resposta em html e nao JavaScript, deve haver um elemento de destino da requisição. Eh recomendavel esperar sempre respostas em HTML, para padronizar.
//        hash     (opcional) Uma hash contendo valores para serem alimentados no form (input devem preexistir).
//        evaluate (opcional) true para requestURLeval().
//
function doAjaxInputEvent ( FormId, ClassId, FieldId, Input, el, hash, evaluate ) {
 var myform = document.forms[FormId],
     old_ClassId  = myform.elements['_ClassId'].value,
     old_method   = myform.elements['_' + myform.elements['_ClassId'].value + '.Method'].value,
     old_pclassid = myform.elements['_ParentClassId'].value,
     old_pfieldid = myform.elements['_ParentFieldId'].value,
     i;

 myform.elements['_ClassId'].value = ClassId;

 myform.elements['_' + myform.elements['_ClassId'].value + '.Method'].value = 'AjaxInputEvent';
 myform.elements['_ParentClassId'].value = ClassId;
 myform.elements['_ParentFieldId'].value = FieldId;

 if ( myform.elements['_ParentFieldIdIndex'] ) {
  if ( myform.elements[Input.name] != undefined && myform.elements[Input.name].length ) {
   for (i=0; i < myform.elements[Input.name].length; i++) {
    if ( Input == myform.elements[Input.name].item(i) ) {
     myform.elements['_ParentFieldIdIndex'].value = i;
    }
   }
  } else {
   myform.elements['_ParentFieldIdIndex'].value = '';
  }
 }

 // Se for uma array de elementos, escolhe quem é o real baseado no FieldIndex
 if ( el.length ) {
  el = el[myform.elements['_ParentFieldIdIndex'].value || 0];
 }

 // Alimenta o from se necessario
 if ( hash != undefined ) setValue( FormId, hash, false );

 // Faz requisição
 if ( !evaluate ) {
  requestURL( 'POST', document.location.pathname, myform, el, 0 );
 } else {
  requestURLeval( 'POST', document.location.pathname, myform, el );
 }

   myform.elements['_ClassId'].value = old_ClassId;
   myform.elements['_' + myform.elements['_ClassId'].value + '.Method'].value = old_method;
   myform.elements['_ParentClassId'].value = old_pclassid;
   myform.elements['_ParentFieldId'].value = old_pfieldid;
}





/*
#========================================#
   Início das funções para a Combo Search
#========================================#
*/
/*
#
# Essa função é invocada para construir as chamadas de opções das combos flutuantes
# (combos que não alimentam automático, mas apenas sob demanda).
#
*/
var controleTimeout, block=1, inputlastvalue='', changeInput;
function AjaxFloatCombo ( el, ev) {
   var position, dimensions, url, tecla, msel;
   var i, indice, setblock =0;
   var form    = $(el).form;
   var index   = el.index();
   var id_obj  = -1;
   var old_method = form.Method();
   var letra, searchstr;
   var old_method, old_class_method, old_ClassId, old_ParentClassId;
   var ClassId;

   CriaSearchBox(form);

   var cc = form.elements[el.name.substr(0,(el.name.length-2))];

   /* Identifico e retorno o objeto da combo, se está em um array ou não */
   if ( cc.length && $(form).Method() == 'Browse' || $(form).Method() == 'AjaxBrowse' ) {
//       msel = $(form).down('select.fkcombo[activecombo=1]');
      msel = cc[cc.length-1];
   } else if(typeof cc[index] != 'undefined' && typeof cc[index] != 'unknown'){
      msel = cc[index];
   }else{
      msel = cc;
   }
   /* Caso tenha sido alterado o valor da Combo, alimento o índice */
   indice = msel.selectedIndex;

//    if(ev && typeof ev.type != 'unknown'){
   if(ev && ev.type != 'unknown'){
      /*Caso o evento seja de saída, devo setar o valor antigo e fechar a div*/
      if (ev.type == 'blur'){
         if(indice >= 0){
            SetInputValue(el, index, msel.options[indice].text);
         }
         window.setTimeout(function(){$('searchbox').style.display = 'none';},100);
         return false;
      }

      /* Pego a tecla que o usuário digitou, dependendo, tenho tratamentos diferentes*/
      if (ev.keyCode) {
         tecla = ev.keyCode;
      } else if (ev.which) {
         tecla = evt.which;
      }

      if (tecla == 9){
         /* é um TAB*/
         if(indice >= 0){
            SetInputValue(el, index, msel.options[indice].text);
         }
         window.setTimeout(function(){$('searchbox').style.display = 'none';},100);
         return false;
      }else{
         letra = String.fromCharCode(tecla);
      }


      if (tecla == 27){
         /*
            tecla pressionada é um esc
            Nesse caso, é q mesma coisa que eu saísse do campo, ou seja, blur.
         */

         if(indice >= 0){
            SetInputValue(el, index, msel.options[indice].text);
         }

         window.setTimeout(function(){$('searchbox').style.display = 'none';},100);
         return false;
      }

      if (tecla == 40 || tecla == 38){
         /*
            tecla pressionada é uma seta para baixo ou cima
            Nesse caso, marco as "trs" (na verdade spans) com uma cor, para mostrar que estão selecionadas
         */

         /* Busco todos os filhos da DIV
            O plune coloca uma div a mais, então, tenho que pegar o down
         */
           var obj  = $('searchbox').down().childElements()

         /* Se a div está oculta, quer dizer que quero ir para a primeira opção */
         if($('searchbox').style.display == 'none'){
            $('searchbox').style.display = 'block';
            setblock = 1;
         }

         /* percorro todos os elementos filhos da div e verifico qual deles está marcado*/
         for(i=0; i<obj.length; i++){
            if(obj[i].hasClassName('sva')){
               id_obj = i;
               obj[i].removeClassName('sva');
            }
         }

         /* Verifico se a tecla é pra cima ou pra baixo*/
         id_obj = id_obj + (tecla == 40 ? 1 : -1);

         /* Caso não exista, ou entre em algumas das considerações, é a primeira opção sempre*/
         if (typeof obj[id_obj] == 'undefined' || obj[id_obj].hasClassName('spanvalueclose') || setblock == 1 || typeof obj[id_obj + 1] == 'undefined'){
            id_obj = 0;
         }
         obj[id_obj].addClassName('sva');

         return false;
      }

      if (tecla == 13){
         Event.stop(ev);
         /*
            tecla pressionada é um enter
            Nesse caso devo descobrir qual span está selecionada e jogar o valor dela pra o input/combo atravéz do evento do span
         */

         /* Busco todos os filhos da DIV
            O plune coloca uma div a mais, então, tenho que pegar o down
         */
         clearTimeout(controleTimeout);
         if($('searchbox').style.display == 'none'){

            AjaxFloatComboDirect(el, ev);

            return false;
         }

         var obj  = $('searchbox').down().childElements();

         if(obj.length > 1){
            for(i=0; i<obj.length; i++){
               if(obj[i].hasClassName('sva')){
                  id_obj = i;
               }
            }

            obj[id_obj].onclick();
         }
         return false;
      }

      if (tecla == 37 || tecla == 39 || tecla == 16 || tecla == 17 || tecla == 18 || tecla == 35 || tecla == 36 || tecla == 61 || tecla == 59){
         // Teclas Seta pra esquerda / direita, ctrl, shift, alt, end, home, =, :
         clearTimeout(controleTimeout);
         return false;
      }

   }

   /*
      Se cheguei aqui, é porque é uma tecla normal que foi pressionada, e com isso devo executar o ajax
      para fazer a consulta com as informações que estão sendo digitadas.
   */

   searchstr = el.value;

   if(block == 1){
      clearTimeout(controleTimeout);
      controleTimeout = setTimeout(function(){
                                       block = 0;
                                       AjaxFloatCombo(el,ev);
                                    },800);
   }

   if (searchstr != '' && block == 0 && searchstr != inputlastvalue){

      // Se for em um Browse ou BrowseCombo
      if ( old_method == 'Browse' || old_method == 'AjaxBrowse' ) {
         AjaxFloatComboDirectBrowse(el, ev);
      // Se for alteração em um form normal (Insert ou Update)
      } else {
         AjaxFloatComboDirect(el, ev);
      }

    // Fiz um refactoring aqui... (troquei todo o código abaixo pela linha acima)

/*      clearTimeout(controleTimeout);
      block = 1;
      inputlastvalue = searchstr;
      //Define o posicionamento da Div na página, assim como suas dimensões, de acordo com o input utilizado.
      position = plune_positionedOffset(el);

      $('searchbox').style.left = position[0]+'px';
      $('searchbox').style.top  = (position[1]+23)+'px';

      ClassId  = el.getAttribute('ClassId');

      var els = form.elements;

//       old_method        = form.Method();
      old_class_method  = els['_'+ClassId+'.Method'].value;
      old_ClassId       = els['_ClassId'].value;
      old_method        = els['_'+old_ClassId+'.Method'].value;
      old_ParentClassId = els['_ParentClassId'].value;


      // Seto uma grande parametrização para poder realizar a consulta, e funcionar tanto em campos normais, como arrays e forms embutidos
      form.Method('AjaxFloatCombo');

      try{form.field('_ComboFieldId').value = el.getAttribute('FieldId')}catch(e){};

      els['_'+ClassId+'.ComboFieldId'].value = el.getAttribute('FieldId');
      els['_'+ClassId+'.Method'].value       = 'AjaxFloatCombo';

      els['_ClassId'].value            = ClassId;
      els['_ParentClassId'].value      = ClassId;
      els['_ParentFieldId'].value      = el.getAttribute('FieldId');
      els['_SubMethod'].value          = searchstr;
      els['_ParentFieldIdIndex'].value = (index == -1 ? '' : index);


      submitTo( form, $('searchbox') );
      $('searchbox').style.display = 'block';

      // Depois da Consulta, retorno aos valores originais.
//       form.Method( old_method );
      els['_'+old_ClassId+'.Method'].value = old_method;

      try{form.field('_ComboFieldId').value = ''} catch(e){};

      els['_'+ClassId+'.ComboFieldId'].value = '';
      els['_'+ClassId+'.Method'].value       = old_class_method;

      els['_ClassId'].value            = old_ClassId;
      els['_ParentClassId'].value      = old_ParentClassId;
      els['_ParentFieldId'].value      = '';
      els['_SubMethod'].value          = '';
      els['_ParentFieldIdIndex'].value = '';
*/

      return true;

   }else if(searchstr != inputlastvalue){
      block = 1;
      window.setTimeout(function(){$('searchbox').style.display = 'none';},100);
      return false;
   }
}


/* Função que será ativada, assim que se clicar na lupa ao lado da searchBox
   Ela dispara diretamente o ajax, ignorando se a consulta está igual ao que já esta digitado*/
function AjaxFloatComboDirect(el, ev){
   var myel, position, dimensions, index, form, ClassId;
   var old_method, old_class_method, old_ClassId, old_ParentClassId;

   block = 1;
   clearTimeout(changeInput);

   myel  = $(el).up('span').down('input');

   if(myel.value != ''){
      index = myel.index();
      form  = $(myel).form;

      // Se for via Browse, redireciona
      if ( $(el).up('nobr[BrowseUpdatable]') && ( form.Method() == 'Browse' || form.Method() == 'AjaxBrowse' ) ) {
       return AjaxFloatComboDirectBrowse(el, ev);
      }

      /*Define o posicionamento da Div na página, assim como suas dimensões, de acordo com o input utilizado.*/
      position = plune_positionedOffset(myel);

      $('searchbox').style.left = position[0]+'px';
      $('searchbox').style.top  = (position[1]+23)+'px';

      ClassId  = myel.getAttribute('ClassId');

      var els = form.elements;

//       old_method        = form.Method();
      old_class_method  = els['_'+ClassId+'.Method'].value;
      old_ClassId       = els['_ClassId'].value;
      old_method        = els['_'+old_ClassId+'.Method'].value;
      old_ParentClassId = els['_ParentClassId'].value;


      // Seto uma grande parametrização para poder realizar a consulta, e funcionar tanto em campos normais, como arrays e forms embutidos
      form.Method('AjaxFloatCombo');

      try{form.field('_ComboFieldId').value = myel.getAttribute('FieldId')}catch(e){};

      els['_'+ClassId+'.ComboFieldId'].value = myel.getAttribute('FieldId');
      els['_'+ClassId+'.Method'].value       = 'AjaxFloatCombo';

      els['_ClassId'].value            = ClassId;
      els['_ParentClassId'].value      = ClassId;
      els['_ParentFieldId'].value      = myel.getAttribute('FieldId');
      els['_SubMethod'].value          = myel.value;
      els['_ParentFieldIdIndex'].value = (index == -1 ? '' : index);


      submitTo( form, $('searchbox') );
      $('searchbox').style.display = 'block';

      // Depois da Consulta, retorno aos valores originais.
//       form.Method( old_method );
      els['_'+old_ClassId+'.Method'].value = old_method;

      try{form.field('_ComboFieldId').value = ''} catch(e){};

      els['_'+ClassId+'.ComboFieldId'].value = '';
      els['_'+ClassId+'.Method'].value       = old_class_method;

      els['_ClassId'].value            = old_ClassId;
      els['_ParentClassId'].value      = old_ParentClassId;
      els['_ParentFieldId'].value      = '';
      els['_SubMethod'].value          = '';
      els['_ParentFieldIdIndex'].value = '';
   }
}



function AjaxFloatComboDirectBrowse(el, ev) {
   var myel, position, dimensions, index, form, ClassId;
   var old_method, old_class_method, old_ClassId, old_ParentClassId;

   block = 1;
   clearTimeout(changeInput);

   myel  = $(el).up('span').down('input');

   if ( myel.value != '' ) {
      index = myel.index();
      form  = $(myel).form;

      /*Define o posicionamento da Div na página, assim como suas dimensões, de acordo com o input utilizado.*/
      position = plune_positionedOffset(myel);

      $('searchbox').style.left = position[0]+'px';
      $('searchbox').style.top  = (position[1]+23)+'px';

      ClassId  = myel.getAttribute('ClassId');

      var els = form.elements;

      try{form.field('_ComboFieldId').value = myel.getAttribute('FieldId')} catch(e){};

      var row    = el.up("tr[key]") || el.up("div[key]"),
          form   = row.getForm(),
          klass  = form.ClassId(),
          params = {
             _ClassId: klass,
             _ParentClassId: klass,
             _ParentFieldId: myel.getAttribute('FieldId'),
             _Level:         form.Level(),
             _SubMethod:     myel.value
            },
          k,
          keys;

      // Coloca as chaves primárias na consulta
      keys = row.readAttribute("key").toQueryParams();

      for (k in keys) {
         params[klass+"."+k] = keys[k];
      }

      params["_"+klass+".Method"]       = "AjaxFloatCombo";
      params["_"+klass+".FormId"]       = [form.name, form.name];
      params["_"+klass+".ComboFieldId"] = myel.getAttribute('FieldId');

      requestURL("POST", document.location.pathname, params, $('searchbox'));

      $('searchbox').style.display = 'block';

   }

}

/* Como não estamos utilizando o prototype 1.6 ainda, tive que trazer para cá esta parte do código
   Função que traz o posicionamento em relação à pagina.
*/
Element.plune_returnOffset = function(l, t) {
  var result = [l, t];
  result.left = l;
  result.top = t;
  return result;
};

/* Como não estamos utilizando o prototype 1.6 ainda, tive que trazer para cá esta parte do código
   Função que traz o posicionamento em relação à pagina.
*/
function plune_positionedOffset(element){
    var valueT = 0, valueL = 0;

    do {
      valueT += element.offsetTop  || 0;
      valueL += element.offsetLeft || 0;
      element = element.offsetParent;
      if (element) {
        if (element.tagName.toUpperCase() == 'BODY') break;
      }
    } while (element);
    return Element.plune_returnOffset(valueL, valueT);
}


/* Função que identifica seta o valor de um determinado input, identificando se ele está em um array ou não.*/
function SetInputValue(el, index, value){
   if(value == '' || value == '* Selecione' || value == '* Indefinido'){
      value = 'Digite...';
      $(el).value  = value;
      $(el).style.color = '#777';
   }else{
      $(el).style.color = '#000';
      if($(el).value != value){
         $(el).focus();
      }
      $(el).value = value;
   }
}

/*
   Da mesma forma que o SetInputValue, esta função atribui um valor à uma combo.
   Identificando se ela está em um array ou não.
*/
function ChangeValueField(mspan, el, ev, valor, index){
   var form = $(el).form;
   var msel, myel;

   SearchsetOption(el, valor, mspan.innerHTML)
   try{el.onchange()}catch(e){};

   myel = form.elements[el.name+'.x'];
   SetInputValue(myel, index, mspan.innerHTML);
   $('searchbox').style.display = 'none';
}

/*
   Função que será chamada pelo onclick dos spans na DIV.
   Esta função, seta o valor para a combo e para o input, conforme o span selecionado.
*/
function SSV(a,b,c,d,e,f){SetSpanValue(a,b,c,d,e,f)}
function SetSpanValue(a, el, ev, valor, index, formid){
   clearTimeout(changeInput);
   window.setTimeout(function(){clearTimeout(changeInput);},100);
   var form = document.forms[formid];
   var myel, msel;

   index = (index == '' ? -1 : index);

   var cc = form.elements[el];

   /* Identifico e retorno o objeto da combo, se está em um array ou não */
   if ( cc.length && $(form).Method() == 'Browse' || $(form).Method() == 'AjaxBrowse' ) {
//       msel = $(form).down('select.fkcombo[activecombo=1]');
      msel = cc[cc.length-1];
   } else if ( typeof cc[index] != 'undefined' && typeof cc[index] != 'unknown'){
      msel = cc[index];
   }else{
      msel = cc;
   }

   SearchsetOption(msel, valor, a.innerHTML);

   msel.callEvent("change");

   if ( $(form).Method() == 'Browse' || $(form).Method() == 'AjaxBrowse' ) {
      myel = form.elements[el+'.x'];
   } else if (typeof form.elements[el+'.x'][index] != 'undefined' && typeof form.elements[el+'.x'][index] != 'unknown'){
      myel = form.elements[el+'.x'][index];
   }else{
      myel = form.elements[el+'.x'];
   }

   SetInputValue(myel, index, a.innerHTML)

   $('searchbox').style.display = 'none';
   inputlastvalue = '';
}

/*
   Por incrível que pareça, sair do input não é tão simples, pois tenho que verificar se o valor
   está de acordo com o da combo, para depois poder sair do input. Isto para impedir que esteja
   escrito no input uma coisa, mas a seleção da combo se outra, já que a combo pode estar oculta do user.
*/
function OcultaDiv(el, ev){
   var form = $(el).form;
   CriaSearchBox(form);
   var index = el.index();
   var msel;

   if ( $(form).Method() == 'Browse' || $(form).Method() == 'AjaxBrowse' ) {
      msel = form.elements[el.name.substr(0,(el.name.length-2))];
   } else if(typeof form.elements[el.name.substr(0,(el.name.length-2))][index] != 'undefined' && typeof form.elements[el.name.substr(0,(el.name.length-2))][index] != 'unknown'){
      msel = form.elements[el.name.substr(0,(el.name.length-2))][index];
   }else{
      msel = form.elements[el.name.substr(0,(el.name.length-2))];
   }
   var indice = msel.selectedIndex;

   if(indice >= 0){
      clearTimeout(changeInput);
      changeInput = setTimeout(function(){
                                 if($(el).value == ''){
                                    SetInputValue($(el), index, '');
                                    SearchsetOption(msel, '', '');
                                 }else{
                                    if(isNaN(msel.options[indice].text)){
                                       SetInputValue($(el), index, msel.options[indice].text);
                                    }
                                 }
                                 $('searchbox').style.display = 'none';
                               },200);

   }else{
      window.setTimeout(function(){$('searchbox').style.display = 'none';},100);
   }
}

/* Função que é executada ao se selecionar uma opção diretamente da combo flutuante (SearchBox).*/
function OnChangeSel(el, ev){

   /* Nas execuções normais, o simples this, que é o que a variavel el recebe, funciona
      Mas no IE, em campos array, ele simplesmente não entende isso e tenho que pegar o elemento
      pelo evento. O que também funciona nos browsers padrões.
   */
   var elem = Event.element(ev);
   var form = $(elem).form;

   if ( elem.up('select') ) { elem = elem.up('select') }

   var index;
   try {
    index = elem.index();
   } catch (e) {
    index = 0;
   }

   var indice = elem.selectedIndex;
   var myel;

   if (typeof form.elements[elem.name+'.x'][index] != 'undefined' && typeof form.elements[elem.name+'.x'][index] != 'unknown'){
      myel = form.elements[elem.name+'.x'][index];
   }else{
      myel = form.elements[elem.name+'.x'];
   }

   if(indice >= 0){
      SetInputValue(myel, index, elem.options[indice].text);
   }

//   SearchsetOption(el, el.value, el.innerHTML);
}

/*
   Função criada para facilitar a inserção de registros na combo.
   Assim como sua seleção
*/
function SearchsetOption(combov, value, text) {
   var combo = $(combov), created = false, option;

   if (value == null) value = "";

   combo.value = value;

   if (combo.value != value) {

      option = document.createElement("option");
      option.text = text;
      option.value = value;
      combo.options.add(option);
      option.selected = true;
      created = true;
   }
   return created;
}

/*Função que tem por único objetivo criar a div */
function CriaSearchBox(form){
   // Caso não exista a div ainda(primeira vez). Tenta criá-la

   if (!$('searchbox')){
      // Define o novo elemento.
      var div = document.createElement('div');
      div.setAttribute('id', 'searchbox');
      div.setAttribute('name', 'searchbox');

      // Insere o elemento no body.
      $(form).appendChild(div);
      div.className = 'searchbox';
   }
}



/*
#========================================#
   Fim das funções para a Combo Search
#========================================#
*/


/*
============================
   Função LightBox
   @Param: Html = Html enviado diretamente pela função
   @Param: op   = Hash com opcionais (* não implementado ainda)
   @Date: 12/11/2009
   @Auth: Felipe H. Müller
============================
*/

var lboxauto;
function lbox(Html, op){
   var HtmlCont, top, left, h;

   if (!op) op = {};

   if (Html == ''){
      alert('Html está nulo');
      return false;
   }

   if (!$("divfloat")){
      // Define o novo elemento.
      var div = document.createElement('div');
      div.setAttribute('id', 'divfloat');
      div.setAttribute('name', 'divfloat');
      div.setAttribute('onclick', "lbox('fecha', '')");

      // Insere o elemento no body.
      document.body.appendChild(div);
      div.className = 'divfloat';
      div.style.display = 'none';
      //div.style.visibility = 'hidden';

      // Define o novo elemento.
      var div2 = document.createElement('div');
      div2.setAttribute('id', 'divfloat2');
      div2.setAttribute('name', 'divfloat2');

      // Insere o elemento no body.
      document.body.appendChild(div2);
      div2.className = 'divfloat2';
      div2.style.display = 'none';
   }

   if(Html != 'fecha'){

      HtmlCont = '<div style="margin:0px 8px 8px 8px;">'+
                     '<div style="position:relative;top:-8px;left:8px;display:block; text-align:right; margin-bottom:0px">'+
		                  '<a onclick="lbox(\'fecha\',\'\')">'+
		                     '<img src="/t/c/img/action/Close.png">'+
		                  '</a>'+
                     '</div>'+
		               Html +
	               '</div>';

      $("divfloat2").innerHTML = HtmlCont;

      var rolY = parseInt(document.body.scrollTop); // armazenando os valores de rolagem vertical da página
      var pgY  = parseInt(document.body.clientHeight); // armazenando a altura da página

      if (document.documentElement && !document.documentElement.scrollTop){
      // IE6 +4.01 but no scrolling going on
         rolY = (document.documentElement.clientHeight / 2) - $("divfloat2").getHeight()/2;

         h = document.documentElement.clientHeight;

      }else if (document.documentElement && document.documentElement.scrollTop){
      // IE6 +4.01 and user has scrolled
         rolY = ((document.documentElement.clientHeight / 2) + document.documentElement.scrollTop) - $("divfloat2").getHeight()/2;
         h = document.documentElement.clientHeight + document.documentElement.scrollTop;

      }else if (document.body && document.body.scrollTop){
      // IE5 or DTD 3.2
         rolY = (document.body.scrollTop + (document.body.clientHeight/2)) - $("divfloat2").getHeight();
         h = document.body.scrollTop + document.body.clientHeight;

      }

      if (rolY<0){
         rolY = document.documentElement.scrollTop ? document.documentElement.scrollTop : document.body.scrollTop;

         if(typeof op['height'] != 'undefined' && typeof op['height'] != 'unknow'){
            $("divfloat").style.height = op['height'] + 'px';
         }else{
            h = h + $("divfloat2").getHeight();
            if(h > (document.height ? document.height : document.body.scrollHeight)){
               $("divfloat").style.height = h + 'px';
            }else{
               $("divfloat").style.height = (document.height ? document.height : document.body.scrollHeight) + 'px';
            }
         }
      }else{
         if(typeof op['height'] != 'undefined' && typeof op['height'] != 'unknow'){
            $("divfloat").style.height = op['height'] + 'px';
         }else{
            if(h > (document.height ? document.height : document.body.scrollHeight)){
               $("divfloat").style.height = h + 'px';
            }else{
               $("divfloat").style.height = (document.height ? document.height : document.body.scrollHeight) + 'px';
            }
         }
      }

      $('divfloat2').style.top = (rolY + 30) + 'px';

      var pgX = parseInt(document.documentElement.clientWidth ? document.documentElement.clientWidth : document.body.clientWidth);
      //var pgX = parseInt(document.body.scrollLeft);

      left = (pgX / 2) - (($('divfloat2').getWidth())/2);

      $('divfloat2').style.left = left + 'px';

      Effect.toggle($('divfloat'),'appear',{to: 0.45, duration: 0.5});
      Effect.toggle($('divfloat2'),'appear',{duration: 0.5});

   // fecha!
   }else{

      if ( op.agora ) {
       $('divfloat').style.display  = 'none';
       $('divfloat2').style.display = 'none';
      } else {
       Effect.toggle($('divfloat'), 'appear',{duration: 0.5});
       Effect.toggle($('divfloat2'),'appear',{duration: 0.5});
       clearTimeout(lboxauto);
      }
      try { op.unset('autoclose'); } catch (e) {};

      return true;
   }

   if ( op.autoclose ) {

      lboxauto = setTimeout( function(){ Effect.toggle($('divfloat'), 'appear',{duration: 1});
                            Effect.toggle($('divfloat2'),'appear',{duration: 1});
      }, op.autoclose > 1 ? op.autoclose : 1500 );
   }

}



//
// Este metodo eh responsavel por a janela com a lista de opcoe do BrowseCombo.
//
// @param newTarget Nome da nova janela
//        Form      Elemento Form (nao apenas o seu id) onde esta a combo de origem
//        ClassId   ClassId de origem
//        FieldId   FieldId de origem
//        ForeignFieldId FieldId da tabela referenciada, de onde o valor sera exportado para o Field deste form
//        Element   Elemento clicado ou que ser autilizado para descobrir a combo ativa (em caso de ARRAY)
//
function popupBrowseCombo ( newTarget, Form, ClassId, FieldId, ForeignFieldId, el ) {
  var els = Form.elements;

  var combo = els[ClassId+'.'+FieldId],
    i,realInput,inputs,ii;
  // Se for uma array de campos
  if ( combo.length ) {
   // Limpa qualquer vestigio anterior
   for (i=0;i<combo.length;i++) {
    combo[i].setAttribute( 'ActiveCombo', null );
   }
   if ( el.type == 'select-one' && el.name == ClassId+'.'+FieldId ) {
    realInput = el;
   } else {
    inputs = findParentElement(el,'TD').getElementsByTagName('select');
    for (i=0;i<inputs.length;i++) {
     if ( inputs[i].name == ClassId+'.'+FieldId ) {
      realInput = inputs[i];
     }
    }
    if ( !realInput ) {
     alert("Impossível descobrir qual o elemento SELECT de destino da consulta! Abortando...");
     return;
    }
   }
   // Cria uma lista ordenada com todos os elementos
   inputs = ( findParentElement(el,'TABLE','isArray','1') || findParentElement(el,'TABLE') ).getElementsByTagName('select');
   if ( inputs.length < els[ClassId+'.'+FieldId].length ) {
    inputs = els[ClassId+'.'+FieldId];
   }

   ii = 0;
   for (i=0;i<inputs.length;i++) {
    if ( inputs[i].name == ClassId+'.'+FieldId ) {
     if ( realInput == inputs[i] ) {
      els['_ParentFieldIdIndex'].value = ii;
      inputs[i].setAttribute( 'ActiveCombo', 1 );
     }
     ii++;
    }
   }
  }

  // Guarda todos os valores antigos
  oldAction         = Form.action;
  oldClassId        = els['_ClassId'           ].value;
  oldLevel          = els['_Level'             ].value;
  oldMethod         = els['_'+ClassId+'.Method'].value;
  oldParentFormId   = els['_ParentFormId'      ].value;
  oldParentClassId  = els['_ParentClassId'     ].value;
  oldParentFieldId  = els['_ParentFieldId'     ].value;
  oldForeignFieldId = els['_ForeignFieldId'    ].value;
  try { oldNextMethod     = els['_'+oldClassId+'.NextMethod'].value; } catch (e) {}

  // Abre a janela
  var sw; var sh;
  try { sw = parseInt( screen.width  * 0.7 ) } catch (e) {};
  try { sh = parseInt( screen.height * 0.4 ) } catch (e) {};
  if ( !sw ) { sw = 680 }
  if ( !sh ) { sh = 340 }
  window.open('',newTarget,'width='+sw+',height='+sh+',scrollbars=yes,menubar=no,status=no,toolbar=no,titlebar=no,resizable=yes,location=no');

  // Seta valores novos
  Form.target = newTarget;
  Form.action = '/Ultra/'+getSession()+'/Class.cgi';
  els['_ClassId'                 ].value = ClassId;
  els['_Level'                   ].value = 0;
  els['_'+ClassId+'.Method'      ].value = 'BrowseCombo';

  try { els['_'+oldClassId+'.NextMethod'  ].value = ''; } catch (e) {}
  els['_'+ClassId+'.ComboFieldId'].value = FieldId;
  els['_ParentFormId'            ].value = Form.name;
  els['_ParentClassId'           ].value = ClassId;
  els['_ParentFieldId'           ].value = FieldId;
  els['_ForeignFieldId'          ].value = ForeignFieldId;

  // Submete o from com os valores novos
  Form.submit();

  // Restaura os antigos (como se nada tivesse acontecido)
  els['_ForeignFieldId'          ].value = oldForeignFieldId;
  els['_ParentFieldId'           ].value = oldParentFieldId;
  els['_ParentClassId'           ].value = oldParentClassId;
  els['_ParentFormId'            ].value = oldParentFormId;
  Form.target = '_self';
  els['_'+ClassId+'.Method'      ].value = oldMethod;
  try { els['_'+oldClassId+'.NextMethod'  ].value = oldNextMethod; } catch (e) {}
  Form.action = oldAction;
  els['_ClassId'                 ].value = oldClassId;
  els['_Level'                   ].value = oldLevel;
  els['_ParentFieldIdIndex'      ].value = '';

}

//
// Função invocada quando é dado um blur no campo de BrowseUpdate e é necessário salvar a informação - via REST.
//
// Essa função apenas encaminha para ubbex, mas antes escolhe se vai dar um delay ou não.
//
// @see Html::input::Plugin::BrowseUpdatable
//
function ubbe (el, delay, data, extra, ClassId, FieldId, brefresh, cancelev ) {
 var _this = $(el);
 if ( delay ) {
  _this.setAttribute('browseUpdateId', setTimeout(function () { ubbex( el, delay, data, extra, ClassId, FieldId, brefresh, cancelev ) }, delay ) );
 } else {
  ubbex( el, delay, data, extra, ClassId, FieldId, brefresh, cancelev );
 }
}

//
// Grava alterações de Update via Browse.
//
// Ver função acima
//
function ubbex (el, delay, data, extra, ClassId, FieldId, brefresh, cancelev ) {
 var _this = $(el);
 var thisform = Element.extend(_this.form),
     td = _this.up('td')   || _this.up('div');
 if ( td.up('table.BrowseArray') ) { td = td.up('table.BrowseArray').up('td') };
 if (!td) return false;
 var tr = td.up('tr[key]') || _this.up("div[key]"),
     editspan = td.down('.bu-e'),
     viewspan = td.down('.bu-v');

 if (editspan.getAttribute('sent')) return false;

 function onAfterAll() {
  editspan.update('').removeAttribute('sent');
  _this.removeAttribute('onblur');
  viewspan.show();
 }

 // Se houve uma alteração mesmo no valor
 if ( _this.value != data && !cancelev ) {
  // Coloca nos params as PKs
  var params = tr.readAttribute('key').toQueryParams();
  // Coloca nos params o valor do campo sendo alterado
//    params[_this.FieldId()] = _this.value;
   params[_this.FieldId()] = _this.allValues(editspan);

  // Extras (via XParam ou handler)
  if ( extra ) eval( extra );

  new Ultra.Class( ClassId ).Update(params, {
   onSuccess: function () {
   var value = this.Field[FieldId].resolved;
   viewspan.fieldValue = this.Field[FieldId].value;
   if (viewspan.fieldValue != null) {
    viewspan.update( value != null && !String(value).empty() ? value : viewspan.fieldValue )
            .removeAttribute('isNull');
   } else {
    if ( value != null ) viewspan.update( value );
    viewspan.setAttribute('isNull', '1');
   }
   if ( ( value == null || String(value).empty() ) && ( viewspan.fieldValue == null || String(viewspan.fieldValue).empty() ) ) viewspan.update('<nobr><i>* Indefinido</i></nobr>');
   if ( brefresh ) { try { thisform.elements['_'+ClassId+'.Submit'].click() } catch (e) {} };
  },
  onAfterAll: onAfterAll
  });
  editspan.setAttribute('sent', '1');
  editspan.update('<img src=/t/default/img/loading.gif />')

 // Se não houve alteração no valor
 } else {
  onAfterAll();
 }

 _this.removeAttribute('browseUpdateId');
}

//
// Requisita alteração do campo via Browse (listagem) e exibe o HTML necessário. É necessário fazer uma requisição ao servidor porque cada campo é uma história completamente diferente do outro.
//
// @see Html::input::Plugin::BrowseUpdatable
//
function ubb(_this,field,data,ev){updateByBrowse(_this, field, data, ev)}
function updateByBrowse(_this, field, data, ev) {
 if (ev) Event.stop(ev);
 $(_this).hide();
 var row   = _this.up("tr[key]") || _this.up("div[key]"),
     form  = row.getForm(),
     klass = form.ClassId(),
     _data = _this.readAttribute("isNull") ? "" : [_this.fieldValue, data, ""].find(function (e) { return e != null }),
     params = {
      _ClassId: klass,
      _ParentClassId: klass,
      _ParentFieldId: field,
      _Level: form.Level()
     },
     k,
     keys;

 // Se 'data' não for nulo, coloca-o na query, senão deixa em branco - o que sinalizará o Plune para instanciar o registro.
 if ( data != null ) params['_data'] = _data;

 // Coloca as chaves primárias na consulta
 keys = row.readAttribute("key").toQueryParams();
 for (k in keys) {
  params[klass+"."+k] = keys[k];
 }
 params["_"+klass+".Method"] = "AjaxInputEvent";
 params["_"+klass+".FormId"] = [form.name, form.name];
 requestURL("POST", document.location.pathname, params, _this.previous("span"));
}

//
// Monta toda a estrutura necessária para alterar o campo em uma listagem através de uma janela modal.
//
function ubm (_this,field,data,ev){ updateByModal(_this,field,data,ev) }
function updateByModal (_this,field,data,ev) {
 if (!_this.down('span.bu-v')) {
  _this.innerHTML = "<span><span class='bu-e div__helper2'></span><span class=bu-v>"+_this.innerHTML+"</span></span>";
 } else {
  _this.innerHTML = "<span><span class='bu-e div__helper2'></span><span class=bu-v>"+_this.down('span.bu-v').innerHTML+"</span></span>";
 }
 ubb($(_this).down('span.bu-v'),field,data,ev);
}

//
// Essa funcao é invocada quando a combo eh alterada e precisa alterar outras combos
// Ela usa a ação AjaxCombo para pegar as opções disponíveis para cada combo que será alterado
//
function FormComboReload ( FormId, ClassId, FieldId, thisCombo ) {
   var myform      = document.forms[FormId];

   if (typeof myform == 'undefined'){
      myform = $(thisCombo).up('form');
   }

   var els         = myform.elements,
       old_ClassId = els['_ClassId'].value,
       old_Level   = els['_Level'].value,
       old_method  = els['_'+ClassId+'.Method'].value,
       // Elemento combo atual
       combo = els[ClassId+'.'+FieldId],
       comboIndex,i,inputs,ii,el,req;

 // Se for uma array de campos
 if ( combo.length ) {
  // BrowseUpdate com FloatCombo!
  if ( myform.Method() == 'Browse' || myform.Method() == 'AjaxBrowse' ) {
   combo = combo[combo.length-1];

  // Form normal
  } else {
   // Limpa qualquer vestigio anterior
   for (i=0;i<combo.length;i++) {
    combo[i].setAttribute( 'ActiveCombo', null );
   }

   // Faz isso para descobrir a ordem de exibicao, e não a ordem no form - que pode diferir
   inputs = ( findParentElement(thisCombo,'TABLE','isArray','1') || findParentElement(thisCombo,'TABLE') ).getElementsByTagName('select');

   ii = 0;
   for (i=0;i<inputs.length;i++) {
    if ( ( inputs[i].name == ClassId+'.'+FieldId ) && ( inputs[i] == thisCombo ) ) {
     try {
      inputs[i].setAttribute( 'ActiveCombo', 1 );
      els['_ParentFieldIdIndex'].value = ii;
      comboIndex = ii;
      combo = inputs[i];
     } catch (e) {}
    }
    if ( inputs[i].name == ClassId+'.'+FieldId ) { ii++ };
   }
  }
 }

 els['_ClassId'].value = ClassId;
 els['_Level'].value   = 0;
 try {els['_'+ClassId+'.OK'].value = 'Fake';} catch (e) {}
 try {els['_'+ClassId+'.ReloadFieldId'].value = FieldId;} catch (e) {}
 els['_'+ClassId+'.Method'].value = 'AjaxCombo';

 // Faz a requisição
 doSubmitDisabled( myform );
 req = new AJAX(POST, '/Ultra/'+getSession()+'/Class.cgi', myform);

 req.events.success = function (res) {
// try {
  res = eval(res);
  var Combo,vCombo,tab,AllCombos,inputs,i,ii,iii,Option,k,tr,td,chk,td2,lastVal;

  // Alimenta as combos dependentes (itera aqui combo a combo)
  for (i = 0; i < res.length; i += 2) {
   // Pega a combo dependente
   Combo  = els[res[i]],
   vCombo = els["_v."+res[i]];

   // Se o combo não existir (seja la qual motivo) ignora
   if ( !Combo ) {
    // Do nothing - ignore

   // Se combo for realmente um combo ou array de combo
   } else if ( Combo.type == "select-one" || Combo.type == "checkbox" || Combo.length ) {

    // Se for ARRAY de combos com herança ARRAY tb, pareada, utiliza apenas o par dele!
    if ( Combo.type != 'select-one' && Combo.length &&
         els[ClassId+'.'+FieldId].type != 'select-one' && els[ClassId+'.'+FieldId].length ) {
     // Interessa aqui a ordem de exibição, e não a ordem no form, que pode ser às vezes errônea!
     inputs = ( findParentElement( ( Combo.length ? Combo[0] : Combo ),'TABLE','isArray','1') || findParentElement( ( Combo.length ? Combo[0] : Combo ),'TABLE') ).getElementsByTagName('select');
     iii=0;
     for (ii=0;ii<inputs.length;ii++) {
      // Se esse select for mesmo o esperado (nome esperado)
      if ( inputs[ii].name == res[i] ) {
       // Se este for mesmo o par dele!
       if ( iii == comboIndex ) { Combo = inputs[ii]; }
       // Alementa a sequencia
       iii++
      }
     }
     AllCombos = [ Combo ];

    // Se for uma ARRAY de checkboxes com herança única.
    } else if ( ( Combo.type == 'checkbox' || Combo[0].type == 'checkbox' ) ) {
     tab = findParentElement( ( Combo.length ? Combo[0] : Combo ) ,'TR').parentNode;
     for (iii=1;tab.childNodes.length>0;iii++) { tab.removeChild(tab.lastChild) }

     AllCombos = [ null ];

    // Se for uma ARRAY de combos com herança única, altera todos.
    } else if ( Combo.type != 'select-one' && Combo.length ) {
/*     TR = findParentElement( ( Combo.length ? Combo[0] : Combo ) ,'TR').parentNode;
     for (iii=1;TR.childNodes.length>1;iii++) { TR.removeChild(TR.lastChild) }*/
     AllCombos = Combo.length && Combo[0].type == 'select-one' ? Combo : [ Combo[ Combo ] ];

    // Qualquer outra coisa (em geral um combo única, o que é a maioria dos casos na verdade)
    } else {
     AllCombos = Combo.length && Combo[0].type == 'select-one' ? Combo : [ Combo ];
    }

    // Iterate all the combos (most of the times just one, unless when in array)
    for ( iii = 0; iii < AllCombos.length; iii++ ) {
     Combo = AllCombos[iii];

     // Se combo estiver desabilitado e não for a combo atual, pula ela
     if ( Combo && Combo.disabled && !Combo.getAttribute('ActiveCombo')) continue;

     // Zera opções antigas
     if ( Combo && Combo.type == 'select-one' ) Combo.options.length = 0;
     // Iterate the new options of the combo
     for ( ii = 2; ii < res[i+1].length; ii += 2 ) {
      // É mesmo uma combo?
      if ( Combo && Combo.type == 'select-one' ) {
       Option = document.createElement("option");
       //Option.value = res[i+1][ii];
       (function (opt) {
        if (typeof opt == 'string') { Option.value = opt;
        } else { for (k in opt) { Option.setAttribute(k, opt[k]);} }
       })(res[i+1][ii]);
       Option.text  = res[i+1][ii+1];
       Combo.options.add( Option );
/*       if ( Option.text == ' -- * Mais... -- ' ) {
        alert(Option.text);
        Option.onclick = function () { alert(1) };
       }*/
//ii == res[i+1].length-1 &&
      // Ou é uma checkbox?
      } else if ( tab && res[i+1][ii] != '' || res[i+1].length == 4 ) {
       tr = tab.insertRow(-1);
       td = tr.insertCell(-1);
       chk = document.createElement("input");
       chk.type  = 'checkbox';
       (function (opt) {
        if (typeof opt == 'string') { chk.value = opt }
        else { for (k in opt) { chk.setAttribute(k, opt[k]) } }
       })(res[i+1][ii]);
       chk.name  = res[i];
       td.appendChild( chk );
       td2 = tr.insertCell(-1);
       td2.innerHTML = "<img src='"+findParentElement(tab,'TABLE').getAttribute('ClassImg')+"' align=absmiddle> "+res[i+1][ii+1];
//      tab.innerHTML += "<tr><td><input name='"+res[i]+"' type=checkbox value='"+res[i+1][ii]+"'></td><td><img src='"+findParentElement(tab,'TABLE').getAttribute('ClassImg')+"' align=absmiddle> "+res[i+1][ii+1]+"</td></tr>";
      } else {
       continue;
      }
     }

     // executa evento "onformcomboreload"
     try {
      if ( Combo && Combo.getAttribute('onformcomboreloadfield') ) {
       var func = new Function( "form", "frm", Combo.getAttribute('onformcomboreloadfield') );

       try { func.call(myform, myform, els) } catch (e) { throw new Error("Error on 'onformcomboreloadfield' form event: "+e); }
      }
     } catch (e) {}
    }

    // Selecionar valor anterior no atual se há alteração e se no último nível...
    lastVal = Combo ? Combo.value : null;
    if ( Combo && Combo.type == 'select-one' &&
         res[i+1][0] != null &&
         Combo.value != res[i+1][0] &&
         true ) {
     Combo.value = res[i+1][0];

    }

    if ( Combo && Combo.type == 'select-one' && Combo.value == '' ) Combo.value = lastVal;

   // Se for um text
   } else if ( Combo && Combo.type == "text" ) {
    Combo.value = res[i+1][0];
   }

   // # Se houver um ValueInput
   if ( Combo && vCombo && vCombo.type == "text" ) {
    vCombo.value = Combo.value;
   }
  }

//  myform.elements[ClassId+'.'+FieldId].disabled = false;
  combo.disabled = false;

  if (el) {
   if ( el.oldSrc != null )
    el.src = el.oldSrc;
   else
    el.style.display = 'none';
  }


  // executa evento "onformcomboreload"
  if ( combo.getAttribute('onformcomboreload') ) {
   var func = new Function( "form", "frm", combo.getAttribute('onformcomboreload') );

   try { func.call(myform, myform, els) } catch (e) { throw new Error("Error on 'onformcomboreload' form event: "+e); }
  }


/*  // Restore Form values
  myform.elements['_ClassId'].value = old_ClassId;
  myform.elements['_'+ClassId+'.OK'].value = '1';
  myform.elements['_'+ClassId+'.ReloadFieldId'].value = '';
//   myform.elements[btn_name].value = old_btn;
 // @change 2007-07-20 <matheus@sistemica.info> Ao reler dois combos ao mesmo tempo, não seta o verdadeiro old_method.
//   myform.elements['_'+ClassId+'.Method'].value = old_method
  myform.elements['_'+ClassId+'.Method'].value = myform.elements['_'+ClassId+'.Method'].defaultValue;
  myform.elements['_ParentFieldIdIndex'].value = '';*/


//  } catch (e) { alert(e.description) }
 };


 // Faz a requisição
 req.request();
 rollbackSubmitDisabled( myform );

  // Restore Form values
  els['_ClassId'].value = old_ClassId;
  els['_Level'].value = old_Level;
  try { els['_'+ClassId+'.OK'].value = '1'; } catch (e) {}
  try { els['_'+ClassId+'.ReloadFieldId'].value = '';} catch (e) {}
//   myform.elements[btn_name].value = old_btn;
 // @change 2007-07-20 <matheus@sistemica.info> Ao reler dois combos ao mesmo tempo, não seta o verdadeiro old_method.
   els['_'+ClassId+'.Method'].value = old_method
//  myform.elements['_'+ClassId+'.Method'].value = myform.elements['_'+ClassId+'.Method'].defaultValue;
  try { els['_ParentFieldIdIndex'].value = ''; } catch (e) {}

// if ( myform.elements[ClassId+'.'+FieldId].nextSibling != null && myform.elements[ClassId+'.'+FieldId].nextSibling.tagName == "IMG" ) {
 if ( combo.nextSibling != null && combo.nextSibling.tagName == "IMG" ) {
  el = combo.nextSibling;
  el.oldSrc = el.src;
 } else {
  el = document.createElement("IMG");
  combo.parentNode.appendChild(el);
 }

 /* Isso aqui na verdade é invocado ANTES do final da requisição AJAX */
 el.border=0;
 el.src = '/t/default/img/loading.gif';
//  combo.disabled = true;
}

//
// Adiciona opcoes ao final de uma combo select.
//
// @param el      Elemento SELECT
//        options Array JS com subarrays [ value, text ]
//
function addSelectOptions ( el, options ) {
 var i,Option;
 for ( i = 0; i < options.length; i++ ) {
  Option = document.createElement('option');
  Option.value = options[i][0];
  Option.text  = options[i][1];
  el.options.add( Option );
 }
}

//
// Esta função é invocada no evento "onsubmit" de todos os FORMS do Plune. Ela faz verificações de campos e outras alterações de formulários.
//
// @return boolean (true se tudo OK | false se algum campo tem erro e portanto não deve submeter)
//
function submitForm (form, loadingImgId) {
 form = $(form);

 rollbackSubmitDisabled ( form );

 if (!loadingImgId) { loadingImgId = form.getAttribute('loadingImgId') }
 var ClassId = form.elements['_ClassId'].value, rv, img, oldLabel;

 var SIID = form.getAttribute('SIID');
 // Exibe próxima aba se requisitado
 if ( SIID &&                              // Existe SI definida
      form.getAttribute('SINext') &&       // Deve levar ao próximo layer
      getSINextLayerUnseenIndex( SIID ) && // Existe layer após o atual
      getSI(SIID,getSINextLayerUnseenIndex( SIID )).getAttribute('readonly') != '1' && // Aba não é somente leitura
      confirm('Gostaria de preencher também os dados de ['+new String(getSIb(SIID,getSINextLayerUnseenIndex( SIID )).innerHTML+' ').stripTags()+']?') == true &&           // Pergunta se usuário gostaria de ver o próximo layer
      SuperInterfaceShowNext( SIID )       // Exibe o próximo layer, se existir
     ) { return false; };

 // Forms de Insert e Update devem chamar o checkForm (verificações extendidas do formulário)
 if (['Insert', 'Update'].include(form.elements['_'+ClassId+'.Method'].value)) rv = checkForm(form);

 // Se não houve excessão em nenhum campo do form, vamos submeter de fato!
 if (rv != false) {
  try { img = $(loadingImgId).show() } catch (e) {};
  submitBtn = form.elements['_'+ClassId+'.Submit'];
  oldLabel  = submitBtn.value;
  try { submitBtn.disabled = true;         } catch (e) {}
  try { submitBtn.value    = 'Aguarde...'; } catch (e) {}

  // Outras imagens de "loading..."
  var submitBtn2 = form.elements['_'+ClassId+'.Submit2'];
  if (submitBtn2 && !submitBtn2.length) submitBtn2 = new Array( submitBtn2 );
  var oldLabel2  = new Array();
  var img2       = new Array();

  for (var i=0; submitBtn2 && i < submitBtn2.length; i++) {
   img2.push( $(submitBtn2[i].nextSibling) );
   oldLabel2.push( submitBtn2[i].value );
   $(submitBtn2[i].nextSibling).show();
   submitBtn2[i].value = 'Aguarde...';
  }


  // Percorre todos os campos para fazer pós alterações
  $A(form.elements).any( function (fld) {
   // Campo é um Disabled que deve ser submetido
   if ( fld.getAttribute('submitDisabled') && fld.disabled == true ) {
    fld.disabled = false;
    fld.setAttribute( 'wasDisabled', 1 );
   }
  });


  // Forms de Update com submissão via AJAX são diferentes
  if (form.Method() == "Update" && ( form.field("_submitByAjax") || ( form.field("_NextMethod").value == 'submitByAjax' ) ) ) {
   form.save({
    keepSession: true,
    onSuccess: function () {
     lbox("<img src='/t/c/img/action/OK.gif' class=ico> Operação realizada com sucesso!", {autoclose:1});
    },

    onAfterAll: function () {
     submitBtn.disabled = false;
     submitBtn.value = oldLabel;
     img.hide();

     for (var i=0; submitBtn2 && i < submitBtn2.length; i++) {
      if (img2[i])       img2[i].hide();
      if (submitBtn2[i]) submitBtn2[i].value = oldLabel2[i];
     }
    }
   });
   rollbackSubmitDisabled( form );
   return false;
  }
 }

 return rv != false;
}

//
// Converte os campos disabled configurados com SubmitDisabled em enabled!
//
function doSubmitDisabled ( form ) {
 // Percorre todos os campos para fazer pós alterações
 $A(form.elements).any( function (fld) {
  // Campo é um Disabled que deve ser submetido
  if ( fld.getAttribute('submitDisabled') && fld.disabled == true ) {
   fld.disabled = false;
   fld.setAttribute( 'wasDisabled', 1 );
  }
 });
}

//
// Volta ao estado normal os campos definidos com SubmitDisabled (campos que mesmo quando disabled devem ser submetidos)
//
function rollbackSubmitDisabled ( form ) {
 // Percorre todos os campos para fazer pós alterações
 $A(form.elements).any( function (fld) {
  // Campo é um Disabled que deve ser submetido
  if ( fld.getAttribute('wasDisabled') && fld.disabled == false ) {
   fld.disabled = true;
   fld.removeAttribute( 'wasDisabled' );
  }
 });
}

//
// Adiciona uma imagem de 'loading' apos o elemento. Isso normalmente em botoes de submit, quando eh dado um submit.
// Se o elemento for um botao de submit, adicionalmente eh alterado o valor dele para 'Aguarde...'.
//
// @param Element DHTML Element a usar como base.
//
function appendLoadingImg (el) {
 var img = document.createElement("IMG");
 img.border=0;
 img.src = '/t/default/img/loading.gif';

 el.parentNode.appendChild(img);

 if ( el.type == 'submit' ) el.value = 'Aguarde...';

 return img;
}

//
// Essa eh uma funï¿½o generica para fazer uma requisiï¿½o HTTP e simplesmente
// executï¿½la como cï¿½igo javascript
//
// @param Metodo GET ou POST
//        myURL  URL a ser requisitada (POST)
//        params Parametros a serem passados (pode ser um form, array ou uma string)
//        el     (Opcional) elemento status
//
function requestURLeval( meth, myURL, params, el ) {
 if (el)
  el.innerHTML = el.clientWidth > 200 ?
   "<p class=loading><img src=/t/default/img/loading.gif border=0> Carregando...</p>" :
   "<b class=loading_small><img src=/t/default/img/loading.gif border=0></b>";

 var req = new AJAX(meth, myURL, params), oldHTML;

 req.events.success = function (res) {
  res = eval(res);

  if (el)
   el.innerHTML = ""
 };

 if (el) {
  oldHTML = el.innerHTML;

  req.events.complete = function () {
   el.innerHTML = oldHTML
  }
 }

 req.request()
}

//
// Inicializa variável de Cache, sempre procurandi atingir a janela parent (raiz de todos os frames) ou mesmo a janela opener.
//
var UltraAJAXCache;

function initUltraCache (force) {

 try {
  // Se há uma janela parent
  if ( parent && document != parent.document ) {
   if ( force || parent.Ultra.AJAXCache == undefined ) parent.Ultra.AJAXCache = new Hash();
   UltraAJAXCache = parent.Ultra.AJAXCache;

  // Se há uma opener euma opener da opener, e a opener da opener tiver um frame parent
  } else if ( opener && opener.opener && opener.opener.parent ) {
   if ( force || opener.opener.parent.Ultra.AJAXCache == undefined ) opener.opener.parent.Ultra.AJAXCache = new Hash();
   UltraAJAXCache = opener.opener.parent.Ultra.AJAXCache;

  // Se há uma opener e a opener tiver um frame parent
  } else if ( opener && opener.parent ) {
   if ( force || opener.parent.Ultra.AJAXCache == undefined ) opener.parent.Ultra.AJAXCache = new Hash();
   UltraAJAXCache = opener.parent.Ultra.AJAXCache;

  // Cache nativo nessa janela
  } else {
   if ( force || window.Ultra.AJAXCache == undefined ) window.Ultra.AJAXCache = new Hash();
   UltraAJAXCache = window.Ultra.AJAXCache;
  }
 } catch (e) {
 }
}

// Inicializa o Cache
initUltraCache();

function getUltraCache (key) {
 return key ? UltraAJAXCache[key] : UltraAJAXCache;
}

function setUltraCache (key,value) {
 UltraAJAXCache[key] = value;
}

function resetUltraCache () {
 initUltraCache(true);
// UltraAJAXCache.each( function (e) { setUltraCache(e.key,null) } );
}

//
// Essa eh uma funï¿½o generica para fazer uma requisiï¿½o HTTP e simplesmente
// imprimi-la para o elemento especificado
//
// @param Metodo  GET ou POST
//        myURL   URL a ser requisitada (POST)
//        params  Parametros a serem passados (pode ser um form, array ou uma string)
//        Elem_Func Elemento DOM a ser utilizado (ï¿½o elemento, objeto, nï¿½ apenas o ID dele) ou Função a ser invocada
//        Once    Se verdadeiro, so faz a requisicao se requisicao igual ja nao tiver sido feita para o mesmo elemento
//        timeout Timeout da operacao
//        quiet   Silencioso quando requisição falha
// @see   ClassLock()
//
function requestURL (meth, myURL, params, el, once, timeout, quiet, onsuccess) {
 if (timeout == undefined) timeout = DEFAULT_TIMEOUT;

 var unique, reqNum, req, oldHTML, timedout, t;

 // Cria um ID único para o elemento e URL
 if ( el && typeof( el ) != 'function' ) unique = el.id + '@' + myURL;

 // Corrige quando o elemento for uma TR com apenas uma TD solteira.
 if ( el && typeof( el ) != 'function' && el.nodeName == "TR" && el.childNodes.length == 1 ) el = el.childNodes[0];


 // Se requisicao ja tiver sido feita
 if ( once == 1 && requestURLdone.include(unique) && el.innerHTML.length != 0 ) {
  return;

 // Requisição usa cache
 } else if ( once == 2 ) {
  // Se encontrou entrada no cache, retorna
  if ( getUltraCache(meth+'$'+myURL+'?'+params) != null ) {
   Element.update( el, getUltraCache(meth+'$'+myURL+'?'+params) );
   return;
  }

 // Vamos precisar fazer a requisição
 } else {
//   requestURLdone.push(unique);
 }

 reqNum = requestURLdone.length - 1;

 if ( typeof( el ) != 'function' ) el.lastRequest = myURL;

 req = new AJAX(meth, myURL, params);

 if ( typeof( el ) != 'function' ) oldHTML = String( el.innerHTML );

 timedout = false;

 if (timeout !== false) {
  t = setTimeout(function () {
   timedout = true;

   alert("Requisição foi cancelada porque atingiu o tempo limite("+(timeout / 1000)+" segundos). Por favor, Tente novamente.");

   if ( typeof( el ) != 'function' ) Element.update(el, oldHTML);

//    requestURLdone.splice( reqNum, 1 );
  }, timeout);
 }

 // Senão, usa o comportamento padrão de colocar a resposta
 if ( true ) {
  req.events.success = function(res) {
   clearTimeout(t);

   if (timedout) return;

   // Se o elemento for uma função, usa ela
   if ( typeof( el ) == 'function' ) {
    el( res );
    return;
   }

   try {
    if ( once == 2 ) setUltraCache( meth+'$'+myURL+'?'+params, res );
    if ( typeof( el ) != 'function' ) Element.update(el, res);
    requestURLdone.push(unique);

   } catch (e) {
    if ( typeof( el ) != 'function' ) alert('Não pode atualizar elemento '+el.nodeName+': '+e.description+ ' - O HTML pode estar desconforme?');

    if (DEBUG_MODE) {
     // Debug for IE
     if (!moz) {
      if (confirm(e.description+"\n\nDeseja ver o conteúdo da resposta?")) {
       var win = open(),
           txt = win.document.createElement('textarea');

       txt.value        = res;
       txt.style.width  = "100%";
       txt.style.height = "100%";

       win.document.body.appendChild(txt);
      }
     }
    }
   }

   // Se elemento for um ContextMenu, reposiciona
   try {
    if ( el.up('ul.ContextMenu') ) {
     Ultra.ContextMenu.synchPosition( el.up('ul.ContextMenu').up('div') );
    } else if ( el.up('ul.SubMenuContent') ) {
     Ultra.ContextMenu.synchPosition( el.up('ul.SubMenuContent').up('div') );
    }
   } catch (e) {
   }

  };
 }

 if ( typeof( el ) != 'function' ) oldHTML = String( el.innerHTML );

 req.events.error = function (message) {
  // Quando a janela é fechada e há uma requisição AJAX, ocorre erro. Por isso o try.
  try {
   clearTimeout(t);

   AJAX.events.error(message);

   if ( typeof( el ) != 'function' ) Element.update( el, oldHTML );

//    requestURLdone.splice( reqNum, 1 );
  } catch (e) {}
 };

 if (quiet) req.events.fail = new Function;

 req.events.loading = function (reqNum, URL) {
  AJAX.events.loading(reqNum, URL);

  if ( typeof( el ) == 'function' ) return;

  var realEl = el,i,b;
  // Se elemento for pequeno, tenta ver se não há alguma child dele que seja maior (position absolute)
  if ( realEl.innerHTML.length > 100 && ( realEl.clientWidth <= 200 || realEl.clientHeight <= 70 ) ) {
   for ( i = 0; i < el.childNodes.length; i++ ) {
    if ( el.childNodes[i] && ( el.childNodes[i].nodeType == 1 && el.childNodes[i].style.position == 'absolute') && ( el.childNodes[i].clientWidth > el.clientWidth || el.childNodes[i].clientHeight > el.clientHeight ) ) {
     realEl = el.childNodes[i];
    }
   }
  }

  // Decide como mostrar que elemento está em releitura
  // Elemento grande com dados dentro (desfoca conteudo)
  if ( realEl.nodeName == 'TR' || ( realEl.innerHTML.length > 100 && realEl.clientWidth > 200 && realEl.clientHeight > 50 ) ) {
   // b é a div que ofusca o conteúdo
   b = document.createElement("div");
   b.style.position = 'absolute';
   if ( realEl.style.position == 'absolute' ) {
    b.style.top  = realEl.style.top;
    b.style.left = realEl.style.left;
   }
   b.style.width  = realEl.clientWidth  + 'px';
   b.style.height = realEl.clientHeight + 'px';
   b.style.background = 'url( /t/default/img/loading_bg.gif )';
   b.style.zIndex = 1000;
   b.innerHTML = "<table width='100%' height='100%' border=0><tr><td valign=middle align=center><p class='loading_inside'><img src=/t/default/img/loading.gif border=0> Carregando...</p></td></tr></table>";

   // Insere a div
   el.insertBefore( b, el.firstChild );

  // Se elemento for grande o suficiente (exibe a rodinha mais o texto "Carregando...")
  } else if (realEl.clientWidth > 200 && realEl.clientHeight > 50) {
   realEl.innerHTML = "<p class=loading><img src=/t/default/img/loading.gif border=0 align=absmiddle> Carregando...</p>";

  // Se elemento for pequeno, apenas a bolinha giratoria
  } else {
//   realEl.innerHTML = "<b class=loading_small><img src=/t/default/img/loading.gif border=0 align=absmiddle></b>";
   realEl.innerHTML = "<img aclass=loading_small src=/t/default/img/loading.gif border=0 align=absmiddle alt=loading>";
  }
 };

 req.request()
}

//
// Funcao utilizada pelo Form de Browse avanï¿½do, para trocar a ascendencia do ordenamento.
//
function BrowseFormOrderChange ( FormId, image ) {
 var myform = document.forms[FormId];

 if ( myform.elements['_' +  myform.elements['_ClassId'].value + '.OrderDesc'].value == 1 ) {
  myform.elements['_' + myform.elements['_ClassId'].value + '.OrderDesc'].value = 0;
  image.src = '/t/default/img/OrderDesc.gif';
 } else {
  myform.elements['_' + myform.elements['_ClassId'].value + '.OrderDesc'].value = 1;
  image.src = '/t/default/img/Order.gif';
 }
}

//
// Checa/uncheca todos os checkboxes com o nome especificado baseado em outro chackbox. Ambos devem estar no mesmo form.
//
// @param SrcChecbocElement Elemento/Objeto de origem (em geral eh um 'this')
//        InputName         Nome do Input escravo
//        Check             Opcional, verdadeiro ou falso
//
function slaveCheck( Element, InputName, ch ){
 if (Element.item) Element = Element[0];

 var vForm = Element.form, x;

 if ( ch == undefined ) ch = Element.checked;

 for( x=0; x < vForm.elements.length; x++ ){
  if (vForm.elements[x].name == InputName ){
   vForm.elements[x].checked = ch;
   var p = $(vForm.elements[x]).previous();
   if ( p && p.src.match(/(shown|hidden)\.png/)) {
    p.src = p.src.replace(/(shown|hidden)\.png/,ch?'shown.png':'hidden.png');
   }
  }
 }
}

//
// Alimenta o input do campo com o número de checkboxes selecionadas
//
function bms ( checkboxElement, FieldId ) {
 var Field = $(checkboxElement).up('form').field(FieldId);
 var count = countChecked( Field.form.elements[ checkboxElement.name ] );

 if ( count == 0 ) {
  Field.disabled = false;
  Field.value = '';
 } else {
  Field.disabled = true;
  if ( count == 1 ) {
   Field.value = '1 Selecionado';
  } else {
   Field.value = count+' Selecionados';
  }
 }
}

//
// Conta quantas checkboxes estão selecionadas
//
function countChecked ( Element ) {
 var count = 0;
 if ( Element != undefined ) {
  if ( Element.nodeName == "INPUT" ) {
   if ( Element.checked ) {
    count = 1;
   } else {
    count = 0;
   }
  } else {
   try {
    for ( var x=0; x < Element.length; x++ ) {
     if ( Element[x].checked ) count++;
    }
   } catch (e) {}
  }
 }

 return count;
}

function uncheckAll ( Element ) {
 if ( Element != undefined ) {
  if ( Element.nodeName == "INPUT" ) {
   Element.checked = false;
  } else {
   for( var x=0; x < Element.length; x++ ){
    Element[x].checked = false;
   }
  }
 }
}

//
// Verifica se a checkbox esta marcada ou nao (suporta multiplas ckeckboxes)
//
// @param  Element Elemento ou array de elementos
// @return BOOLEAN
//
function isChecked ( Element ) {

 if ( Element != undefined ) {
  if ( Element.nodeName == "INPUT" ) {
   return Element.checked;
  } else {
   try {
    for ( var x=0; x < Element.length; x++ ) {
     if ( Element[x].checked ) return true;
    }
   } catch (e) {}
  }
 }
 return false;
}

//
// Funï¿½o ultilizada no form de browse avanï¿½do.
// Desmarca todos os checkbox da coluna "G".
//
function uncheck( vForm ){
//  vForm = document.body.firstChild;

 for( var x=0; x < vForm.elements.length; x++ ){
  if (vForm.elements[x].name == "_" + vForm.elements['_ClassId'].value + ".GroupBy"){
   vForm.elements[x].checked = false;
  }
 }

}

//
// Essa função é chamada no método Browse, no contextmenu de qualquer campo do tipo date.
// Ela gera dois inputs com os labels "De" e "Até para fazer a pesquisa no intervalo determinado nos campos.
// Os dois inputs gerados possuem máscara de data, limite de caracteres e o calendário que pode ser chamado
// clicando na imagem ao lado do input.
//
function createInputs(Field, pattern, mask) {
 Field.style.display = "none";
 pattern = pattern || '%d/%m/%Y';
 mask    = mask    || '99/99/9999';

 var Cell = Field.parentNode,
     DataInicio = document.createElement("input"),
     DataFim = document.createElement("input"),
     NOBRDe = document.createElement("nobr"),
     NOBRAte = document.createElement("nobr"),
     ImgCalendarDe = document.createElement("img"),
     ImgCalendarAte = document.createElement("img");

 Cell.appendChild( NOBRDe );
 NOBRDe.appendChild( document.createTextNode( "De: " ) );
 NOBRDe.appendChild( DataInicio );
 NOBRDe.appendChild( ImgCalendarDe );
 Cell.appendChild( NOBRAte );
 NOBRAte.appendChild( document.createTextNode( " Até: " ) );
 NOBRAte.appendChild( DataFim );
 NOBRAte.appendChild( ImgCalendarAte );

 ImgCalendarDe.src = "/t/default/img/action/Calendar.gif";
 ImgCalendarDe.onclick = function() { return showCalendar(this.previousSibling, pattern) };
 ImgCalendarAte.src = "/t/default/img/action/Calendar.gif";
 ImgCalendarAte.onclick = function() { return showCalendar(this.previousSibling, pattern) };

 with( DataInicio ){
  id = Field.name +".Inicio";
  name = Field.name + ".Inicio";
  style.width = "80px";
 }
  DataInicio.onkeypress = function(e) {
   e = document.all ? window.event : e;
   return NumberOnly(e);
  };
  DataInicio.onkeyup = function(e) { return Mask(this, mask, e); };

 with( DataFim ){
  id = Field.name + ".Fim";
  name = Field.name + ".Fim";
  style.width = "80px";
 }
 DataFim.onkeypress = function(e) {
  e = document.all ? window.event : e;
  return NumberOnly(e);
 };
 DataFim.onkeyup = function(e) { return Mask(this, mask, e); };

 return false
}

//
// Cria uma string com um perï¿½do de datas, caso sejam informadas as 2 datas.
// Utilizada pela funï¿½o createInputs no mï¿½odo browse.
//
// @param dateOne Primeira data (ex.: data inï¿½io).
//        dateTwo Segunda data (ex.: data fim).
// @return dateOne + " - " + dateTwo As datas concatenadas, no caso de ter sido informado as duas datas.
//         dateOne + dateTwo         A data que foi informada, caso somente uma tenha sido.
//
function returnDate( dateOne, dateTwo ){
 if ( dateOne.length >= 1 && dateTwo.length >= 1) {
  return dateOne + " - " + dateTwo;
 } else {
  return dateOne + dateTwo;
 }
}

//
// Move as linhas de uma tabela para cima ou para baixo conforme o valor recebido.
// Funï¿½o usada no form avanï¿½do de Browse que trata se a classe da linha a ser movida
// for algum tipo de cabeï¿½lho, ele nï¿½ move.
// @param  Element  O elemento que estï¿½chamando a funï¿½o (this).
//         pos      A posiï¿½o para qual deve ser movido 1 = "para cima" 0 = "para baixo"
//
function move(Element, pos){
 var Row = Element.parentNode.parentNode,
     cloneRow = Row.cloneNode(true),
     Table = Row.parentNode,
     rowAround = pos == 0 ? Row.nextSibling : ( pos == 1 ? Row.previousSibling : undefined ),
     cloneAround,RowOldChecked,AroundOldChecked;
 if( rowAround != undefined && rowAround.className != "row_mid" && rowAround.className != "row_top" ) {
  cloneAround = rowAround.cloneNode(true);
  RowOldChecked = Row.firstChild.firstChild.checked;
  AroundOldChecked = rowAround.firstChild.firstChild.checked;

  with(Table){
   replaceChild( cloneRow, rowAround );
   replaceChild( cloneAround, Row );
  }

  cloneRow.firstChild.firstChild.checked = RowOldChecked;
  cloneAround.firstChild.firstChild.checked = AroundOldChecked;
 }

}

//
// Move row (ou row parente do elemento) para a primeira posicao da tabela.
//
// @see   findParentElement( Element, Name )
// @param Element Elemento (processado com o findElement(Element,'TR'))
//
function moveTop (Element) {
 var Row = findParentElement( Element, "TR" ),
     cloneRow = Row.cloneNode(true),
     Table = Row.parentNode,
     rowAround,cloneAround,RowOldChecked,AroundOldChecked;

 rowAround = Table.firstChild;

 if( rowAround != undefined && rowAround.className != "row_mid" && rowAround.className != "row_top" ) {
  cloneAround = rowAround.cloneNode(true);

  RowOldChecked = Row.firstChild.firstChild.checked;
  AroundOldChecked = rowAround.firstChild.firstChild.checked;

  with(Table){
   insertBefore( Row, rowAround );
  }

  cloneRow.firstChild.firstChild.checked = RowOldChecked;
  cloneAround.firstChild.firstChild.checked = AroundOldChecked;
 }

}

//
// Processa recursivamente os parentNodes do elemento ate achar um elemento do tipo especificado, opcionalmente com o atributo especificado.
// Este metodo eh extremamente util para encontrar, por exemplo, o TD ou TR onde determinado elementos
// esta contido. Alem disso, eh seguro tambem, devendo ser uma alternativa melhor que usar apenas o previousSibleng/parentNode
//
// @param  Elemento
//         Tipo do elemento: TR, TD, etc
// @param  Elemento
//         Tipo do elemento: TR, TD, etc
//         Atributo (type, por exemplo)
//         ValorDoAtributo ('hidden', por exemplo)
// @return Elemento do tipo especificado ou undefined
//
function findParentElement ( Element, Name, attrName, attrValue ) {
 if ( Element.parentNode != null ) {
  if ( Element.parentNode.nodeName == Name && ( !attrName || ( Element.parentNode.getAttribute(attrName) == attrValue ) ) ) {
   return Element.parentNode;
  } else {
   return findParentElement( Element.parentNode, Name, attrName, attrValue );
  }
 } else {
  return null;
 }
}

//
// Processa recursivamente as childNodes do elemento ate achar um elemento do tipo especificado, opcionalmente com o atributo especificado.
// Este metodo eh extremamente util para encontrar, por exemplo, um elemento específico dentro de uma TD. Por exemplo, encontrar um INPUT TYPE=FILE.
// Alem disso, eh seguro tambem, devendo ser uma alternativa melhor que usar apenas o previousSibleng/parentNode.
//
// @param  Elemento
//         Tipo do elemento: TR, TD, etc
// @param  Elemento
//         Tipo do elemento: TR, TD, etc
//         Atributo (type, por exemplo)
//         ValorDoAtributo ('hidden', por exemplo)
// @return Elemento do tipo especificado ou undefined
//
function findChildElement ( Element, Name, attrName, attrValue ) {
 var i,r;
 if ( Element.childNodes != null && Element.childNodes.length > 0 ) {
  for ( i=0; i < Element.childNodes.length; i++ ) {
   if ( Element.childNodes[i].nodeName == Name && ( !attrName || ( Element.childNodes[i].getAttribute(attrName) == attrValue ) ) ) {
    return Element.childNodes[i];
   } else {
    r = findChildElement( Element.childNodes[i], Name, attrName, attrValue );
    if ( r != null ) return r;
   }
  }
 } else {
  return null;
 }
}

//
// Soma ou concatena ao valor do elemento o valor passado.
//
// @param Element
//        StringNumber
//
function sumAppend ( el, number ) {
 el.value = el.value*1 + number*1;
}

//
// Trata para que o campo sï¿½permita .(ponto), nmeros e teclas 0 do onkeypress (aï¿½es).
// Quando ï¿½digitado .(ponto), verifica se o ltimo caracater digitado nï¿½ foi um ponto ou se a string jï¿½contï¿½ 3 pontos,
// em caso verdadeiro retorna false (impede).
// Deve ser chamado no evento onkeypress...
//
// @param Field  O campo que deve receber o tratamento.
//        e      event.
//
function inetPress(Field, e){

 if( !/[.]/.test(String.fromCharCode(keyCode(e))) &&
     !/[0-9]/.test(String.fromCharCode(keyCode(e))) &&
     keyCode(e) != 8 && keyCode(e) != 0 ) {
  return false;
 } else if( /[.]/.test(String.fromCharCode(keyCode(e))) &&
           ( Field.value.findChar(".") >= 3 || Field.value.charAt(Field.value.length-1) == "." ) ) {
  return false;
 }
}

//
// Faz a mï¿½cara do campo inet.
// Deve ser chamado no evento onkeyup
//
function inetUp(Field, e) {
 var pos = Field.value.split("."),i

 if( keyCode(e) != 8 && keyCode(e) != 37 && keyCode(e) != 38 && keyCode(e) != 40 ) {

  for( i=0; i < pos.length +1; i++ ) {
   if( pos[i] > 255 ) {
    pos[i] = pos[i].substr(0, pos[i].length-1);
   }
  }

  Field.value = pos.join(".");

  // Se a tecla for "->" e o timo caracter nï¿½ for um ponto e a string nï¿½ contï¿½ mais de 3 pontos, concatena um ponto.
  if( keyCode(e) == 39 && Field.value.charAt(Field.value.length-1) != "." && Field.value.findChar(".") < 3 ) {
   Field.value = Field.value + ".";
  }
  // Se a ltima posiï¿½o do array tiver 3 caracteres e a string tiver menos de 3 pontos, concatena um ponto.
  if( pos[pos.length-1].length == 3 && Field.value.findChar(".") < 3 ) {
   Field.value += ".";
  }

 }
}

//
// Mostra no evento oncontextmenu de cada linha do browse uma div com opï¿½es referentes ao registro da linha.
// Ex.: Editar, Visualizar, Excluir...
// @param nameForm Nome do formulï¿½io
//        e        event
//        PKs      Uma hash com as primary keys da classe.
// @return False   retorna false para não mostrar o menu padrão do browser.
//
function smb(e,g,grantsr) {
 if (typeof g == "string") g = $A(g);
 var row = Event.element(e).up("tr[key]") || Event.element(e).up("tr"), FormName = row.getForm().name.substr(1), grants;
 if (g) grants = {Update:g[0],Delete:g[1]};
 ShowMenuBrowse(FormName,e,row,grants,grantsr);
 Event.stop(e);
}
function ShowMenuBrowse(nameForm, e, row, Grants, GrantsRel) {
//  var DivForm = document.getElementById('Div' + nameForm);
 var Form    = document.forms[nameForm],
     PForm   = document.forms['P'+nameForm],
     Class   = Form.elements["_ClassId"].value,
     opts,
     PKs = ($(row).readAttribute('key') || "").toQueryParams(),
     q,k,id,el,Y,X;

 // Marca no form que é o registro selecionado (outras funções às vezes podem precisar disso)
 Form.setAttribute(  'Selected', $(row).readAttribute('key') );
 PForm.setAttribute( 'Selected', $(row).readAttribute('key') );

 // Para operações de Paste
 if ( copy_from_formid &&
      document.forms[copy_from_formid] != undefined &&
      ( parseInt(document.forms[copy_from_formid].elements['_Level'].value) == (parseInt(PForm.elements['_Level'].value)+1) ||
        ( document.forms[copy_from_formid].elements['_Level'].value == PForm.elements['_Level'].value && document.forms[copy_from_formid].elements['_ClassId'].value == PForm.elements['_ClassId'].value ) ) ) {
//   q = new QueryString(PKs);
  document.forms[copy_from_formid].elements['_copyTo'].value =
   '_ClassId=' + Class + '&' +
   '_Level='   + Form.elements['_Level'].value + '&' +
   $H(PKs).toQueryString();

   opts = {
    New       : "none",
    Paste     : "",
    Cancel    : "none",
    SelectAll : "none",
    DeleteSelected : "none",
    Copy      : "none",
    Move      : "none",
    Delete    : "none",
    Update    : "none"
   };

 // Nao ha nenhum form de transferencia ou nao eh possivel fazer a transferencia
 } else {
   opts = {
    New       : "",
    Paste     : "none",
    Cancel    : "none",
    SelectAll : "",
    DeleteSelected : "",
    Move      : "",
    Copy      : "",
    Delete    : "",
    Update    : "",
    MultiInserttrigger : "",
    MultiInsertdata : "",
    Insertdata : "",
    Inserttrigger : "",
    Insertlog : "",
    InsertSelect : ""
   };

   // Se NÃO há seleção
   if (!isChecked( PForm.elements['_'+PForm.elements['_ClassId'].value+'.MSelect'] )) {
    opts.DeleteSelected = opts.Move = opts.MultiInserttrigger = opts.MultiInsertdata = opts.Copy = "none";
   // Se há selecão
   } else {
    opts.Delete = opts.InsertSelect = opts.Update = opts.Inserttrigger = opts.Insertdata = opts.Insertlog = "none";
   }
 }

 // Verifica permissões e marca para ocultar a opção ou não
 if ( Grants ) {
  for (k in Grants) {
   opts[k] = Grants[k] == 0 ? 'none' : opts[k];
  }
 }

 // Verifica permissões e marca para ocultar as ações de insert/execute nas tabelas child
 if ( GrantsRel ) {
  for (k in GrantsRel) {
   opts[k] = GrantsRel[k] == '0' ? 'none' : '';
  }
 }

 // Oculta opções impossíveis de serem realizadas
 for (k in opts) {
  var kk = k.split(/\./);
  id = /\./.test(k) ? window.Ultra.ContextMenu.idPreffix + 'XInsert_' + kk[0]+'.'+kk[1] + '_' + nameForm : window.Ultra.ContextMenu.idPreffix + k + nameForm;
  el = document.getElementById(id);

  if (el) {
   el.style.display = opts[k];
  }
 }

 // Alimenta as chaves primarias que identificam o registro.
 for( k in PKs ){
  Form.elements[Class + "." + k].value = PKs[k];
 }

//  alert( window.parent.frames("Top").document.body.clientHeight);
 Y = document.all ? ( document.documentElement.scrollTop - 45 ) : window.scrollY;
 X = document.all ? document.documentElement.scrollLeft : window.scrollX;


 ("ContextMenu-"+nameForm).asId().showAtEvent( e ); Event.stop( e );

 return false;
}

//
// Exibe context menu com ações do registro
//
function acts(e) {
 var ico = Event.element(e), row = ico.up("tr"), key = row.readAttribute("key").toQueryParams(),
  frm = $(document.forms[row.getForm().name.replace(/^P/, "")]),name;
 for (name in key)
  frm.field(name).value = key[name];
 $("ContextMenu-"+frm.name+"-Actions").showAtEvent(e);
 Event.stop(e);
}


//
// Controla as aï¿½es principais de um menu PopUp.
// Percorre os elementos do menu a partir de onde foi clicado e se encontrar um link ele esconde o menu...
// @param Element O elemento que ï¿½o menu (div).
//
function ControlerMenuPopUp(Element) {
 document.currentPopUp = Element;
 if(!document.onclick)
  document.onclick = function(e) {
   e = document.all ? window.event : e;
   var Element = document.all ? e.srcElement : e.target,
       flag    = true;
   while (Element.parentNode != null) {
    if( Element.tagName == "A" ) {
     this.currentPopUp.style.display = "none";
     break;
    } else {
     Element = Element.parentNode;
     if (Element == this.currentPopUp) {
      flag = false;
      break;
     } else if (Element == this.currentPopUp.parentNode) {
      break;
     }
    }
   }
   if (flag)
    this.currentPopUp.style.display = "none";
  }
}

//
// Seta value para campos de um formulï¿½io.
// @param Form   Nome do formulï¿½io
//        Hash   Uma hash com o nome e o value em cada chave e valor da hash (ex.: { Nome: 'Valor', Nome2: 'Valor2' } )
//        Submit boolean para submeter o formulï¿½io apï¿½ executar a funï¿½o... true submete, false nï¿½ submete.
//        target novo target somente para esta submissï¿½. Apï¿½ isso, o form adquire seu target antigo.
//               Util para enviar requisiï¿½es para outras janelas/pop-ups.
//
function setValue (_Form, Hash, Submit, target) {
 var Form  = document.forms[_Form], oldTarget,
     hbval = {},
     k,field,doSubmit;

 // Guarda valores antigos
 for( k in Hash ){
  if (Form.elements[k]) hbval[k] = Form.elements[k].value;
 }

 // Seta valores novos
 for( k in Hash ) {
  if ( Form.elements[k] ) {
   Form.elements[k].value = Hash[k];
  } else {
   field = document.createElement("input");

   with (field) {
    type  = "hidden";
    name  = k;
    value = Hash[k];
   }
   Form.appendChild(field);
  }
 }

 // Foi requisitada submissão
 if (Submit) {
  if(target) {
   oldTarget   = Form.target;
   Form.target = target;
  }

  try { doSubmit = Form.onsubmit(); } catch (e) {};
  if ( doSubmit !== false ) Form.submit();

  if (target) Form.target = oldTarget;

  // Restaura valores antigos se houve submissão
  for( k in hbval ) {
   if ( Form.elements[k] ) { Form.elements[k].value = hbval[k] };
  }
 }

 return hbval;
}

// submete um form via AJAX para dentro de um elemento
function submitTo ( myform, el ) {
 requestURL('POST', 'Class.cgi', $(myform), $(el), 0, 180000);
}

function reorder ( table, rowIndex, rowIndexTo, lastTrId, emptyTdId ) {
 if (rowIndex == rowIndexTo) return;

 var row1 = table.rows[rowIndex].cloneNode(true),
     row2 = table.rows[rowIndexTo].cloneNode(true),
     td;

 table.replaceChild(row1, table.rows[rowIndexTo]);
 table.replaceChild(row2, table.rows[rowIndex]);

 td = $(emptyTdId);

 if (table.rows[table.rows.length - 1] != $(lastTrId)) {
  td.setStyle({display: 'none'});
 } else {
  td.setStyle({display: ''});
 }
}

/*
 Checa se o interval estÃ¡ em formato vÃ¡lido...
 @param Value Valor do campo interval
*/
function checkInterval(Value){
 return /^\d+:\d+(:\d+)?$/.test(Value);
}

/*
 FormataÃ§Ã£o para campos interval...  Permite somente nÃºmeros, 2 dois pontos (:) na string e teclas de navegaÃ§Ã£o...
 Deve ser chamado no onkeyup...
 @param Value Valor que estÃ¡ no campo.
        e     event
*/
function formatInterval(Value, e) {
 var key = String(keyCode(e)),
     chr = keyChar(e);

 if (!/\d|[:]/.test(chr) && ![ 0, 8, 13 ].include(key)) {
  return false;
 } else if (/[:]/.test(chr)) {
  if (Value.length == 0 || Value.charAt(Value.length - 1) == ":" || Value.findChar(":") > 1) {
   return false;
  }
 }
}

/*
 FormataÃ§Ã£o para campos float... Permite somente nÃºmeros, 1 ponto (.) na string e teclas de navegaÃ§Ã£o...
 Deve ser invocado no onkeyup...
 @param Value Valor que estÃ¡ no campo.
        e     event
*/
function formatFloat( Value, e ){
 var key = String(keyCode(e)),
     chr = keyChar(e);

 if (!/\d|[\.]/.test(chr) && ![ 0, 8, 13 ].include(key)) {
  return false;
 } else if (/[\.]/.test(chr) && Value.indexOf(".") >= 1) {
  return false;
 }
}

/*
 FormataÃ§Ã£o para campos float... Permite somente nÃºmeros, 1 ponto (.) e um sinal negativo na string e teclas de navegaÃ§Ã£o...
 @param Value Valor que estÃ¡ no campo.
        e     event
*/
function formatFloatSigned( Value, e ){
 var key = String(keyCode(e)),
     chr = keyChar(e);
 if (!/\d|[\.]|[\-]/.test(chr) && ![ 0, 8, 13 ].include(key)) {
  return false;
 } else if (/[\-]/.test(chr) && Value.indexOf("-") != -1) {
   return false;
 } else if (/[\.]/.test(chr) && Value.indexOf(".") >= 1) {
  return false;
 }
}

/*
 Formatacao para campos float... Ajusta a posicao do sinal negativo
 Deve ser invocado no onkeyup...
 @param Value Valor que estÃ¡ no campo.
*/
function formatSignalAjust( Value ){
 if (Value.indexOf("-") == -1) {
  return Value ; //retorna o valor se não for negativo
 } else {
  return "-" + Value.replace( /\-/, "" ); //retorna o valor com o sinal de menos na frente
 }
}

function moveTHead (table, e) {
 var header    = table.rows[1],
     search    = table.rows[2],
     scrolltop = document.documentElement.scrollTop;

 if (scrolltop)
  scrolltop -= 81;

 header.style.top = search.style.top = scrolltop + "px"
}

function inspectElement (Element) {
 var lines = [],k,text;

 for (k in Element) {
  lines.push(k + " => " + Element[k])
 }

 text = lines.join("<br />");

 doDebug(text)
}

//
// Inicializa o cabeçalho do browser para deslizar na tela ao rolar a barra.
// Disponivel somente para o IE.
//
// @param browserId id do elemento table referente ao browse
//
function initBrowserScroll(browserId) {
 var Browse = document.getElementById(browserId),
     Header = Browse.rows[1],
     Search = Browse.rows[2],
     $scrollTop,s;
 function handleScrollEvent () {
  $scrollTop = document.documentElement.scrollTop;
  if ($scrollTop < 81)
   $scrollTop = null;
  else if ($scrollTop > 81)
   $scrollTop -= 81;
  s = $scrollTop ? $scrollTop + "px" : "";
  [Header, Search].each(function (e) { if (e && e.style) e.style.top = s });
 }
 Event.observe(window, "scroll", handleScrollEvent)
}

//
// Destaca o registro selecionado.
//
// @param Row elemento referente a linha da tabela
//
function sr(Row) {
 if (Row.getAttribute("selected"))
  return;
 Row.setAttribute("selected", "1");
 var oldClasses = new Array,
     cells      = aryobjToAry(Row.cells);
 cells.map(function (e) { oldClasses.push(e.className == "selectedBrowseRow" ? "" : e.className); e.className = "selectedBrowseRow" });
 function doOnMouseOut () {
  function doOnMouseDown () {
   cells.map(function (e, i) { e.className = oldClasses[i] });
   Event.stopObserving(Row     , "mouseout" , doOnMouseOut );
   Event.stopObserving(document, "mousedown", doOnMouseDown);
   Row.removeAttribute("selected")
  }
  Event.observe(document, "mousedown", doOnMouseDown)
 }
 Event.observe(Row, "mouseout", doOnMouseOut)
}
window.selectRow=sr;

function popDoubleInsert () {
 var el = window.open('','_blank','');
// el.document.write('<body id=teste>TESTE!!!</body>');
 requestURL('GET','/Ultra/'+getSession()+'/Class.cgi','_ClassId=tbMovimentacaoFinanceira&_tbMovimentacaoFinanceira.Method=Insert',el.document,0);
}

//
// Retorna o navegador do usuário...
//
function nav() {
 var Name = navigator.appName;

 if ( Name.indexOf("Microsoft") != -1 ) return "ie";
 if ( Name.indexOf("Netscape") != -1 )  return "ff";
 if ( Name.indexOf("Konqueror") != -1 ) return "kq";
 if ( Name.indexOf("iPhone") != -1 )    return "ip";
 if ( Name.indexOf("Safari") != -1 )    return "sf";
}

//
// Função que faz o drag and drop de um objeto... Deve ser chamado no onmousedown
// @param e  O event
//        id O Id do elemento que vai receber o drag
//
function StartDrag( e, id ) {

 var el = document.getElementById(id),X,Y;
 if( nav() == "ff" || nav() == "kq" ) {
  X = e.layerX;
  Y = e.layerY;
 } else if( nav() == "ie" ) {
  X  = event.offsetX;
  Y  = event.offsetY
  el = el.style;
 }

 document.onmousemove = function(e) {
  if (el) {
   if( nav() == "ff" || nav() == "kq" ) {
    el.style.top = e.pageY - Y + "px";
    el.style.left = e.pageX - X + "px";
   } else if( nav() == "ie" ) {
    el.pixelLeft = event.clientX - X + document.body.scrollLeft;
    el.pixelTop  = event.clientY - Y + document.body.scrollTop;
    return false;
   }
  }
 }

 document.onmouseup = function() {
  el = null;
 }
}

//
// Altera a propriedade display de um objeto...
// Se estiver none, muda para block e posiciona o objeto onde foi clicado.
// Se estiver block, muda para none...
// @param id O id do elemento que deseja alterar o display
//        e  O event
//
function changeDisplay( id, e ) {
 var el = document.getElementById(id);

 if( el.style.display == 'none' || !el.style.display )
  with( el.style ) {
   display = 'block';
   top     = e.clientY + 'px';
   left    = e.clientX + 'px';
  }
 else
  el.style.display = 'none';
}

/*
 Retorna o index do input passado se ele estiver em um array... Se não estiver, retorna false;
*/
function inputInArr( frm, Input ) {

 var InputName = Input.name,i;

 if ( frm.elements[InputName] != undefined && frm.elements[InputName].tagName == undefined ) {
  for ( i = 0; i < frm.elements[InputName].length; i++ ) {
   if ( Input == frm.elements[InputName][i] )
    return i;
  }
 } else {
   return null;
 }

}

/*
 Converte o valor passado para um formato que a função Mask consiga gerar a máscara...
 Ele não chama a função Mask... Vc deve chama-la, passando o retorno desta função...
 param Value Valor que vai receber a máscara
*/
function toMask( Value ) {

 if( String( Value ).indexOf('.') == -1 )
  Value = String( Value ) + '.00';

 var Integer  = String( Value ).split('.')[0],
     Decimals = String( Value ).split('.')[1];

 if( Decimals.length > 2 )
  Decimals = Decimals.substr( 0,2 );
  //Decimals = Decimals.substr( 0,2 ) + '.' + Decimals.substr( 2, Decimals.length );
  //Decimals = Math.round( Decimals );

 return Integer + '.' + String(Decimals).rpad(0,2);
}

function unMaskMoney(value){
 return value.replace(/\./g,"").replace(/,/,".");
}

/*
 Esconde a aba que foi passada para a função e seleciona outra aba do grupo...
*/
function InterfaceHide( Id ) {
 var Name = (Id.split("-")[1]),cont,butt,x;

 document.getElementById(Id).style.display        = 'none';
 document.getElementById('b-' + Id).style.display = 'none';

 for( x  =0; x < 3; x++ ) {
  cont = document.getElementById( 'layers-' + Name + '-' + x ),
  butt = document.getElementById( 'b-layers-' + Name + '-' + x );

  if( butt.style.display == 'block' || butt.style.display == 'table-cell' ) {
   butt.onclick();
   break;
  }
 }

}

/*
 Mostra a aba que foi passada para a função e só mostra seu conteúdo, se não houver outra aba do seu grupo que esteja selecionada.
*/
function InterfaceShow( Id ) {
 var show = true,
     Name = Id.split("-")[1],
     x;

 for( x = 0; x < 3; x++ ) {
  if( document.getElementById( 'layers-' + Name + '-' + x ).style.display != 'none' )
   show = false;
 }

 if( show ) {
  document.getElementById(Id).style.display = moz ? 'table-cell' : 'block';
 }

 document.getElementById('b-' + Id).style.display = moz ? 'table-cell' :'block';

 // Esta linha faz com que a aba que acaba de ser inserida, fique selecionda...
 document.getElementById('b-' + Id).onclick();
}

//
// Trata a adição de uma coluna no ordenamento para posicioná-la após o elemento atualmente aberto.
//
function addColumnAfter(el,FieldId) {
 var fc = el.firstChild;
 fc.checked = !fc.checked;
 if ( window.activeColumn && window.activeColumn['name'] ) {
  fc.value = FieldId + '>' + window.activeColumn['name'];
 } else {
  fc.value = FieldId + '>';
 }
}


//
// Oculta/Exibe as opções de colunas.
//
// @param ClassId id da classe
//        FormId  id do form
//
function handleColumnOptions (ClassId, FormId, cell, show) {
 var ids     = [ "sortColumn", "group", "subTotalizeColumn", "Fx", "Info" ],
     realIds = ids.map(function (el) { return "b-"+FormId +"."+ el }),
     cells,oldClasses,className,menu,options,subtotal,sortColumn,display,columnsNum,hideColumn;

 if (cell) {
//   cells = aryobjToAry(cell.parentNode.parentNode.childNodes).grep("return i >= 3").map(
  cells = aryobjToAry(cell.parentNode.parentNode.childNodes).grep(function(i){return i >= 3}).map(
    function (e) {return e?e.childNodes[cell.cellIndex]:document.createElement("td")});


  if (show) {
   oldClasses = new Array;
   cells.map(function (e) {
    if (e) {
     if (e.className != "selectedBrowseRow")
      className = e.className;
     e.className = "selectedBrowseRow"
    }
    oldClasses.push(className)
   });
   function onMouseDown (e) {
    if (document.body.hideMenu == false)
     return;
    cells.map(function (e, i) { if (e) e.className = oldClasses[i] });
    Event.stopObserving(document, "click", onMouseDown)
   }
   Event.observe(document, "click", onMouseDown)
  }
 }

 function handler (e) { return e.nodeType == 1 && realIds.include(String(e.getAttribute("id"))) }

 menu    = document.getElementById("Top"+FormId);
 options = getChilds(menu, handler);
 options.map(function (e) { if (e) e.style.display = show ? "block" : "none" });

 if (typeof document.forms[FormId].elements["_"+ ClassId + ".SubTotal"] != 'undefined') {
    subtotal   = document.forms[FormId].elements["_"+ ClassId + ".SubTotal"];
 }else{
    subtotal = '';
 }

 sortColumn = document.getElementById(FormId +".sortColumn");
 sortColumn.style.display = !show || subtotal ? "none" : "block";
 hideColumn = document.getElementById("b-" + FormId + ".hideColumn");
 display    = !show || !cell ? "none" : "block"

 if (cell && show) {
  columnsNum = cell.parentNode.childNodes.length;
  if (columnsNum == 2)
   display = "none"
 }

 hideColumn.style.display = display;
}

//
// Oculta as opções de colunas. Usada ao clicar no "MAIS".
//
// @param ClassId id da classe
//        FormId  id do form
//
function hideColumnOptions(ClassId, FormId, e) {
 if (("Top"+ FormId).asId().showAtEvent( e ))
  handleColumnOptions(ClassId, FormId, null, false);
 Event.stop( e ) ;
}

//
// Exibe as opções de colunas. Usada ao clicar nas colunas.
//
// @param ClassId id da classe
//        FormId  id do form
//        FieldId nome do campo
//        cell    elemento da célula
//        tipo    (opcional) "BrowseForm" ou "Browse"/null
//
function showColumnOptions(ClassId, FormId, FieldId, cell, e, ftype) {
 // Exibe ou oculta (returna do bloco se estiver ocultando ou proessegue se estiver exibindo)
 if (!("Top"+ FormId).asId().showAligned( e, 'td' ))
//  if (!("Top"+ FormId).asId().show( 110, 110, true ))
  return;

 Event.stop( e );

 // O formulário.
 var TheForm = document.forms[FormId],order,desc,field,formElements,group,subtotal;

 // Seta variável de ambiente com nome do campo e elemento referente a célula da coluna.
 window.activeColumn = { name : FieldId, element : cell };

 // Um BrowseForm possui menos opções
 if ( ftype != 'BrowseForm') {
  // Exibe as opções para colunas.
  handleColumnOptions(ClassId, FormId, cell, true);

  // Verifica se o campo de ordenação atual.
  order = TheForm.elements["_"+ ClassId + ".Order"].value;

  // Verifica se o campo atual é o mesmo campo cujas opções serão exibidas.
  if (order == FieldId) {
   // Verifica o tipo de ordenamento do campo: crescente ou decrescente.
   desc  = TheForm.elements["_"+ ClassId + ".OrderDesc"].value;
   order = desc ? "Desc" : "Asc";
   field = document.getElementById(FormId +".sort"+ order + "Column");
   field.checked = true

  } else {
   field = TheForm.elements[FormId +".sortColumn"];
   field[0].checked = false;
   field[1].checked = false
  }

  // Verifica os campos de agrupamento.
  field = TheForm.elements[FormId +".groupColumnCheck"];
  if ( field != null ) {
   field.checked = $(TheForm).down('[name='+"_"+ ClassId + ".GroupBy"+'][value='+FieldId+']') ? true : null;
  }

  // Verifica os campos que estão marcados para subtotalizar.
  field = TheForm.elements[FormId +".subTotalizeColumnCheck"];
  if ( field != null ) {
   field.checked = $(TheForm).down('[name='+"_"+ ClassId + ".SubTotal"+'][value='+FieldId+']') ? true : null
  }
 }
}

//
// Oculta a coluna corrente do browse.
//
// @param FormId  nome do formulário
// @param ClassId nome da classe
//
function hideColumn(FormId, ClassId, e) {
 var cell    = window.activeColumn.element,
     FieldId = window.activeColumn.name,
     input   = document.forms[FormId].elements[ClassId +"."+ FieldId],
     cells;
 if (input.value) {
  input = input.parentNode.removeChild(input);
  appendInput(document.forms[FormId], input);
 }
 function handler (e) { return e.nodeType == 1 && e.cellIndex == cell.cellIndex }
 // Pega todas as células da coluna a ser ocultada.
 cells = getChilds(cell.parentNode.parentNode, handler);
 // Percorre todas as células, removendo-as.
 cells.map(function (e) { if (e) e.parentNode.removeChild(e) });
}

//
// Ordena pela coluna corrente.
//
// @param FormId  nome do formulário
// @param ClassId nome da classe
// @param flag    valor nulo/falso para crescente ou verdadeiro/definido para decrescente
//
function sortColumn(FormId, ClassId, flag) {
 var formElements = document.forms[FormId].elements,        // elementos do formulário.
     namePreffix  = "_"+ ClassId +".",                      // prefixo dos nomes dos elementos.
     FieldId      = window.activeColumn.name,               // nome da coluna corrente.
     Order        = formElements[namePreffix +"Order"],     // campo de ordenamento atual.
     OrderDesc    = formElements[namePreffix +"OrderDesc"], // tipo de ordenamento atual.
 // novo campo e tipo de ordenamento.
     OrderValue, OrderDescValue,radioNameSuffix;
 // Se campo de ordenamento for igual ao campo corrente e,
 // o ordenamento é crescente e a flag tbém ou o ordenamento é decrescente e a flag tbém,
 // então remove ordenamento.
 if (Order.value == FieldId && ((OrderDesc.value && flag) || (!OrderDesc.value && !flag))) {
  OrderValue = OrderDescValue = "";
  radioNameSuffix = flag ? "sortDescColumn" : "sortAscColumn";
  String( FormId + "." + radioNameSuffix ).asId().checked = false;
 // Caso contrário, seta ordenamento ao campo corrente de acordo com a flag.
 } else {
  OrderValue     = FieldId;
  OrderDescValue = flag || "";
  radioNameSuffix = flag ? "sortDescColumn" : "sortAscColumn";
  String( FormId + "." + radioNameSuffix ).asId().checked = true;
 }
 Order.value     = OrderValue;
 OrderDesc.value = OrderDescValue;
 // Auto-submissão
 trySubmit(FormId, ClassId)
}

function appendInput (TheForm, Input, TargetElement) {
 TargetElement = TargetElement || TheForm;
 if (!moz)
  TheForm.elements[TheForm.elements.length] = Input;
 TargetElement.appendChild(Input)
}

function removeInput (Input) {
 if (!moz)
  with (Input)
   name = value = "";
 var Parent = Input.parentNode;
 Parent.removeChild(Input)
}

//
// Agrupa pela coluna corrente.
//
// @param FormId  nome do formulário
// @param ClassId nome da classe
//
function groupColumn (FormId, ClassId) {
 // Flag.
 var radio = String( FormId + ".groupColumnCheck" ).asId(),
     is    = radio.checked,
 // Formulário.
     TheForm = document.forms[FormId],
 // Nome da coluna corrente.
     FieldId = window.activeColumn.name,
     formElements = aryobjToAry( TheForm.elements ),
     field;

 // Se coluna já está agrupanda por esta coluna, desagrupa.
 if (is) {
  field = $(TheForm).down('[name=_'+ ClassId +'.GroupBy]'+'[value='+FieldId+']');
  if (field) removeInput(field);
  radio.checked = false;

 // Senão, agrupa
 } else {
  field = document.createElement("input");
  field.type  = "hidden";
  field.name  = "_"+ ClassId +".GroupBy";
  field.value = FieldId;
  appendInput(TheForm, field);
  radio.checked = true;
 }

 // Auto-submissão
 trySubmit(FormId, ClassId)
}

//
// Subtotaliza a partir da coluna corrente
//
// @param FormId  nome do formulário
// @param ClassId nome da classe
//
function subTotalizeColumn (FormId, ClassId) {
 // Flag.
 var radio = String( FormId + ".subTotalizeColumnCheck" ).asId(),
     is    = radio.checked,
 // Formulário.
     TheForm = document.forms[FormId],
 // Nome da coluna corrente.
     FieldId = window.activeColumn.name,
     formElements = aryobjToAry( TheForm.elements ),
     field;
 // Se browse está agrupando por esta coluna, desagrupa.
 if (is) {
  field = $(TheForm).down('[name=_'+ ClassId +'.SubTotal]'+'[value='+FieldId+']');
  if (field)
   removeInput(field);
  radio.checked = false;
 // Se não, agrupa.
 } else {
  field = document.createElement("input");
  field.type  = "hidden";
  field.name  = "_"+ ClassId +".SubTotal";
  field.value = FieldId;
  appendInput(TheForm, field);
  radio.checked = true;
 }
 // Auto-submissão
 trySubmit(FormId, ClassId)
}

//
// Função de auto-submissão.
//
// @param FormId  nome do formulário
// @param ClassId nome da classe
//
function trySubmit (FormId, ClassId) {
 // Formulário
 var TheForm  = document.forms[FormId],
 // Flag para auto-submeter.
     doSubmit = TheForm.elements["_"+ ClassId + ".doAutoSubmit"];
 // Id do elemento
//      ElId = TheForm.elements["_"+ ClassId + ".ParentElementId"];
 // Elemento alvo da submissão.
//      element  = document.getElementById(ElId.value);
 // Se for para auto-submeter
 if (doSubmit && doSubmit.checked) {
  TheForm.elements["_"+ ClassId +".Method"].value = "AjaxBrowse";
  TheForm.onsubmit()
 }
}

/*
 Adiciona o evento onfocus para mudar o nome da classe do input para focus
 e o evento onblur para setar a classe antiga ou undefined nos inputs
 Função usada no behavior do input (definido no css)...

 @author Paula S. Zucco
 @date   2006-10-10
*/
function Input( el ) {
 if( el.type == "checkbox" )
  el.style.border = "none";
 else {
  el.onfocus = function() { el.oldClass = el.className; el.className = el.oldClass + " focus" }
  el.onblur  = function() { el.className = el.oldClass ? el.oldClass : undefined }
 }
 if( el.disabled ) {
  el.style.backgroundColor = "#F8F8F8";
  el.style.color           = "#525252";
 }
}

//
// Exibe menu de escolha da função de select OU agregação do cabeçalho do Browse.
//
function showBrowseFn ( ClassId, thisType, FormId, el, FieldId ) {
 if (el && el.value && el.value.match(/^Html::input::Function::ReverseFK/)) return;

 var myform  = document.forms[FormId] || el.form,
     FieldId = window.activeColumn ? window.activeColumn['name'] : el.getAttribute('FieldId'),
     thisLi,ul,i,li,superEl,old_ParentFieldId,old_Method,
     old_XMethod;

 // Se for uma troca de uma função apenas, e não requisição para ver todas
 if ( el.nodeName == 'SELECT' ) {
  thisLi = findParentElement( el, 'LI' );
  ul     = findParentElement( el, 'UL' );
  // Exclui combos abaixo dessa
  for ( i = 0; ul.lastChild != thisLi; ul.removeChild( ul.lastChild ) ) {}
  // E cria uma nova criança parta receber a nova combo
  li  = document.createElement("li");
  ul.appendChild( li );
  el = li;
  // Se essa for uma função de select, sinaliza à combo da função de agregação que ele deve ser relido incondicionalmente
  if ( thisType == 'Fn' || thisType == '*Fn' ) {
   superEl = document.getElementById(FormId+'.Fg');
   if ( superEl ) superEl.lastActiveColumn = '';
  }
 }

 old_ParentFieldId = myform.elements['_ParentFieldId'].value;
 old_Method  = myform.elements['_'+ClassId+'.Method'].value;
 old_XMethod = myform.elements['_'+ClassId+'.XMethod'].value;
 myform.elements['_ParentFieldId'].value       = FieldId;
 myform.elements['_'+ClassId+'.Method'].value  = 'AjaxShowFn';
 myform.elements['_'+ClassId+'.XMethod'].value = thisType;

 // Só carrega se já não estiver carregado
 if ( thisType.substr(0,1) == '*' || FieldId != el.lastActiveColumn ) {
  requestURL( 'GET', 'Class.cgi', myform, el, 0 );
 }
 myform.elements['_ParentFieldId'].value       = old_ParentFieldId;
 myform.elements['_'+ClassId+'.Method'].value  = old_Method;
 myform.elements['_'+ClassId+'.XMethod'].value = old_XMethod;
 el.lastActiveColumn = FieldId;
}

//
// Muda o valor da função de agregação ou visualização. Método usado pelo form de Browse no onchange da combo de seleção dessas funções.
// Essas combos são especiais porque a mesma combo é utilizada para todos os campos, tendo seus valores relidos por ajax.
//
function changeAggFn ( ClassId, FieldId, thisType, combo, comboIndex ) {
 // Formulário.
 var myform = combo.form,combolist,ff,col,len,i,field;
 if ( comboIndex == null ) comboIndex = 0;

 if ( !myform.elements[combo.name] ) {
  return; // Retona silenciosamente
 } else if ( myform.elements[combo.name].nodeName == 'SELECT' ) {
  combolist = [ myform.elements[combo.name] ];
 } else {
  combolist = myform.elements[combo.name];
 }

 // Remove o elemento antigo da pagina antes de qualquer coisa
 // Se for um input sozinho
 if ( myform.elements['_'+ClassId+'.'+FieldId+'.'+thisType] &&
      myform.elements['_'+ClassId+'.'+FieldId+'.'+thisType].nodeName == 'INPUT' ) {
  ff = myform.elements['_'+ClassId+'.'+FieldId+'.'+thisType];
  ff.parentNode.removeChild( ff );
 // Se for vários inputs (mais de uma função definida)
 } else if ( myform.elements['_'+ClassId+'.'+FieldId+'.'+thisType] ) {
  col = myform.elements['_'+ClassId+'.'+FieldId+'.'+thisType];
  len = col.length;
  for ( i = 0; i < len; i++ ) {
   col[0].parentNode.removeChild( col[0] );
  }
 }
 // Cria um novo input real para cada opção atual preenchida
 for ( i = 0; i <= comboIndex; i++ ) {
  if ( combolist[i].value ) {
   field = document.createElement("input");
   field.type  = "hidden";
   field.name  = "_"+ ClassId + "." + FieldId + '.' + thisType;
   field.value = combolist[i].value;
   myform.appendChild(field);
  }
 }
 // Auto-submissão
 trySubmit(myform.name, ClassId)
}

//
// Carrega (ou desabilita) a interface de exibição do formulário de tabelas escravas (paralelas)
//
// @param FormId
//        ClassId
//        enable  Se verdadeiro, deve carregar. Se falso, deve ocultar.
//
function enableSlaveFields (formId, classId, enable) {
 var TheForm = document.forms[formId],
/*     filter =
    (enable ? function (e) { return e.type != "hidden" && String(e.name).indexOf(classId) == 0 && e.disabled && e.getAttribute("canEnable") }
            : function (e) { return e.type != "hidden" && String(e.name).indexOf(classId) == 0 && !e.disabled } ),*/
//      fields = $A(TheForm.elements).grep(filter),
//      fields = $A(TheForm.elements).grep(filter);
     fields = $A(TheForm.elements);

 fields.map(function (e) {
  if ( !( (  enable && e.type != "hidden" && ( String(e.name).indexOf(classId) == 0 || e.name == '_'+classId+'.Submit2' ) && e.disabled && e.getAttribute("canEnable") ) ||
          ( !enable && e.type != "hidden" && ( String(e.name).indexOf(classId) == 0 || e.name == '_'+classId+'.Submit2' ) && !e.disabled ) ) ) {
   // Nothing to do
  // Habilita
  } else if (enable) {
   e.removeAttribute("canEnable");
   e.disabled = !enable;
  // Desabilita
  } else {
   e.setAttribute("canEnable", "true");
   e.disabled = !enable;
  }
 })
}

//
// Esta função é chamada no onload do iframe Main.
// Seta o display do iframe para block
// Seta o z-index para o mais alto possivel
// Esconde o menu
// Esconde o Desktop
//
function loadMain( Main ) {
 var LoadingClass = parent.document.getElementById("Desktop").contentWindow.document.getElementById("LoadingClass"),
     ExplorerMenu = document.getElementById( "Explorer" );
 if( Main.contentWindow.document.body.innerHTML != "" ) {
  Main.style.zIndex  = ++ parent.document.getElementById("Desktop").contentWindow.Ultra.Floating.maxZIndex;
  Main.style.zIndex  =    parent.document.getElementById("Desktop").contentWindow.document.documentElement.scrollTop = 0;
  Main.style.display = "block";
  parent.document.getElementById("Desktop").contentWindow.document.body.style.overflow = "hidden";
  parent.document.getElementById("Desktop").contentWindow.document.getElementById("AllDesktops").style.visibility = "hidden";
  if( LoadingClass.style.display != "none" )
   LoadingClass.style.display = "none";
 }
 try { Main.contentWindow.document.onclick = ExplorerMenu.hide; } catch (e) {}
}

//
// Muda o método do Browse (usado para abrir a visualização de celendário).
//
function changeBrowseMethod ( FormId, ClassId, CalType, When, BrowseTable ) {
 var myform = document.forms[FormId],
     els    = myform.elements,
     oldCal  = els[ '_' + ClassId + '.Calendar' ].value,
     oldWhen = els[ '_' + ClassId + '.CalendarWhen' ].value,
     oldTab  = els[ '_' + ClassId + '.BrowseTableClass' ].value;
 if (CalType     != null) els[ '_' + ClassId + '.Calendar' ].value     = CalType;
 if (CalType     != null) els[ '_' + ClassId + '.BrowseLimit' ].value  = '';
 if (BrowseTable != null) els[ '_' + ClassId + '.BrowseTableClass' ].value  = BrowseTable;
 if (When        != null) els[ '_' + ClassId + '.CalendarWhen' ].value = When;
 myform.onsubmit();
 try {
  els[ '_' + ClassId + '.Calendar' ].value     = oldCal;
  els[ '_' + ClassId + '.CalendarWhen' ].value = oldWhen;
  els[ '_' + ClassId + '.BrowseTableClass' ].value  = oldTab;
 } catch (e) {
 }
}

//
// Muda a visualização do calendário do Browse.
//
// @param Form Elemento do Form
//        What Tipo do calendario (year,month,etc)
//        When Data (2007-01-01)
//        BrowseTableClass Classe de BrowseTable. null para manter a mesma, ou '' para adivinhar a melhor.
//
function cc(el,what,when) {
 var form = $(el).getForm();
 if(!when)when=el.getAttribute("today");
 return changeCalendar(form,what,when);
}
function changeCalendar ( Form, What, When, BrowseTableClass ) {
 Form.elements['_' + Form.elements['_ClassId'].value + '.Calendar'].value = What;
 Form.elements['_' + Form.elements['_ClassId'].value + '.CalendarWhen'].value = When;
 if ( BrowseTableClass != null ) Form.elements['_' + Form.elements['_ClassId'].value + '.BrowseTableClass'].value = BrowseTableClass;
 Form.onsubmit();
}

tip=new Array;
      tip[0]='Visit Dynamic Drive<br> for DHTML Scripts!';
      tip[1]='Visit JavaScript Kit for Great<br> Scripts, Tutorials and Forums!';
      tip[2]='Visit Request Code for FREE JavaScripts!';
      tip[3]='Click here for some excellent<br>Java applets and tutorials';

//
// Exibe uma tooltip HTML
//
// @param Element Elemento de origem (pode ser qualquer coisa)
//        Event
//        Tip     Tip HTML
//
function showtip(el,e,tip) {
 if (!tip) tip = el.innerHTML;
 if (document.getElementById) { // Netscape 6.0+ and Internet Explorer 5.0+
  var elm=document.getElementById("tooltip");
  elm.innerHTML=tip;
  elm.style.top=parseInt(e.pageY+3)+"px";
  elm.style.left=parseInt(e.pageX+3)+"px";
  elm.style.visibility = "visible";
 }
 // TODO: # $class->{html} .= "<div id='tooltip' style='position:absolute;visibility:hidden;border:1px solid black;font-size:12px;layer-background-color:lightyellow;background-color:lightyellow;padding:1px'></div>";
}

//
// Oculta tooltip.
//
function hidetip () {
 if (document.getElementById) { // Netscape 6.0+ and Internet Explorer 5.0+
  document.getElementById('tooltip').style.visibility="hidden";
 }
}

//
// Não deixa que um elemento array em combo tenha dois valores iguais.
//
function noDuplicates (el) {
 var els = el.form.elements[el.name],i;
 if ( els.length && els.length > 1 ) {
  for ( i = 0; i < els.length; i++ ) {
   if ( els[i] != el && els[i].value == el.value ) {
    alert('Já existe um elemento com esse valor. Por favor escolha outro!');
    el.value = '';
   }
  }
 }
}

//
// Calendar functions.
//
Ultra.Calendar = new Object;

// Exibe div com lista de registros naquele dia. Usado quando a classe está renderizada em um container.
Ultra.Calendar.showDayRowsAsContained = function () {
 var div = this.getElementsByTagName('DIV')[0];div.style.visibility='visible';div.style.display='inline';div.style.position='absolute';
};

// Oculta div com lista de registros naquele dia. Usado quando a classe está renderizada em um container.
Ultra.Calendar.hideDayRowsAsContained = function () {
 var div = this.getElementsByTagName('DIV')[0];div.style.visibility='hidden';
};

//
// @ Andre => Alterado em 2007-11-12
// Modificado o style.position do metodo "showDivDayAsContainer" de "absolute" para "relative"
//

Ultra.Calendar.showDivDayAsContainer = function () {
 this.style.position='relative';this.style.overflow='visible';this.firstChild.style.backgroundColor='white';this.firstChild.style.border='1px solid black';
};

//
// @ Andre => Alterado em 2007-11-12
// Modificado o style.position do metodo "hideDivDayAsContainer" de '' para "relative"
//

Ultra.Calendar.hideDivDayAsContainer = function () {
 this.style.position='';this.style.overflow='hidden';this.firstChild.style.backgroundColor='';this.firstChild.style.border='';
};

Ultra.Calendar.hideDivDay = function () {
 this.style.visibility='hidden';
};

// Constantes simbólicas.
var SPECIAL_ATTRIBUTE = ['Special Attribute'],
    NORMAL_ATTRIBUTE  = ['Normal Attribute'],
    OVERWRITE         = ['Overwrite'],
    WRITEBEFORE       = ['Write Before'],
    WRITEAFTER        = ['Write After'];

//
// Seta atributos em elementos considerando o valor do atributo anteriormente, permitindo concatenação.
//
// @param el            id/elemento
//        attr          nome do atributo
//        value         valor do atributo
//        behavior      OVERWRITE | WRITEBEFORE | WRITEAFTER
//        sep           separador quando for para concatenar (default ';')
//
function setAttribute(el, attr, value, _behavior, sep) {
 //
 // Defaults e valores iniciais.
 //
 el = $(el);
 if (!el) return false;
 var elements = el.nodeType === undefined ? $A(el) : [el],
 behavior = _behavior || OVERWRITE;
 sep      = sep === undefined ? ';' : sep;
 elements.each(function (el) {
  var status,attrval,code,attrcode,supercode,attrnewval,codes;
  //
  // Descobre o tipo de atributo, sendo ele especial ou normal.
  //
  // Atributo está definido.
  if (el.getAttribute(attr)) {
   // É um atributo especial.
   if (el[attr]) {
    // Renderizado como função; Pega a string.
    if (typeof el[attr] == 'function') {
     status  = SPECIAL_ATTRIBUTE;
     attrval = el.getAttribute(attr);
    } else {
     status  = NORMAL_ATTRIBUTE;
     attrval = el[attr];
    }
   // É um atributo normal.
   } else {
    status  = NORMAL_ATTRIBUTE;
    attrval = el.getAttribute(attr);
   }
  // Atributo não está definido.
  } else {
   // Atributo é nulo; É um atributo especial.
   if (el[attr] === null) {
    status = SPECIAL_ATTRIBUTE;
   } else {
    // Seta valor fake.
    el.setAttribute(attr, 'void(0)');

    if (el[attr]) {
     // Valor fake renderizado como função; É um atributo especial.
     if (typeof el[attr] == 'function') {
      status = SPECIAL_ATTRIBUTE;
     } else {
      status = NORMAL_ATTRIBUTE;
     }
    } else {
     status = NORMAL_ATTRIBUTE;
    }
    // Remove atributo fake.
    el.removeAttribute(attr);
   }
  }
  //
  // Define a forma como será setado o valor.
  //
  // Deve sobrescrever atributo.
  if (behavior == OVERWRITE) {
   // É um atributo especial; Seta como função.
   if (status == SPECIAL_ATTRIBUTE) {
    code = new Function('event', value);
    // FF e IE mais uma vez se comportam de maneiras muito diferentes
    // ao se clonar um elemento.
    // O firefox só consegue clonar os eventos setados diretamente no objeto,
    // enquanto o internet explorer ignora isso, sendo necessário adicionar listeners de eventos.
    // FF
    if (moz) {
     el[attr] = code.bindAsEventListener(el);
    // IE
    } else {
     el[attr] = null;
     if (!el.listeners) el.listeners = {};
     attr = attr.replace(/^on/, '');
     if (!el.listeners[attr]) {
      el.listeners[attr] = [];
      // Cria função genérica que percorre os listeners setados em ordem.
      Event.observe(el, attr, function (e) {
       el.listeners[attr].each(function (code) {
        if (code.call(el, e) == false) throw $break;
       });
      });
     }
     el.listeners[attr] = [code];
    }
   }
   // É um atributo normal ou é Firefox; Seta como simples atributo.
   if (status == NORMAL_ATTRIBUTE || moz)
    el.setAttribute(attr, value);
  } else {
   // É um atributo especial.
   if (status == SPECIAL_ATTRIBUTE) {
    // Renderiza como função.
//     var code = new Function('event', value);
    try{
        code=new Function("event",value);
    }catch(e){
        alert(new Ultra.Error("Evento "+attr+" do campo "+el.getAttribute("description")+" contém erros: "+String(e.description || e)));
     }
    // Renderiza valor antigo como função, caso não seja ainda.
    attrcode =
       typeof attrval == 'function'
     ? attrval
     : new Function('event', attrval);
    // FF e IE mais uma vez se comportam de maneiras muito diferentes
    // ao se clonar um elemento.
    // O firefox só consegue clonar os eventos setados diretamente no objeto,
    // enquanto o internet explorer ignora isso, sendo necessário adicionar listeners de eventos.
    // FF
    if (moz) {
     supercode;
     // Deve escrever antes.
     if (behavior == WRITEBEFORE) {
      supercode = function (e) {
       e = e ? e : event;
       return code.call(el, e) !== false && attrcode.call(el, e) !== false;
      };
     // Deve escrever depois.
     } else if (behavior == WRITEAFTER) {
      supercode = function (e) {
       e = e ? e : event;
        return attrcode.call(el, e) !== false && code.call(el, e) !== false;
      };
     }
     // Seta função.
     el[attr] = supercode.bindAsEventListener(el);
    } else {
     el[attr] = null;
     if (!el.listeners) el.listeners = {};
     attr = attr.replace(/^on/, '');
     if (!el.listeners[attr]) {
      el.listeners[attr] = [attrcode];
      // Cria função genérica que percorre os listeners setados em ordem.
      Event.observe(el, attr, function (e) {
       el.listeners[attr].each(function (code) {
        if (code.call(el, e) == false) throw $break;
       });
      });
     }
     if (behavior == WRITEBEFORE) {
      el.listeners[attr].unshift(code);
     } else if (behavior == WRITEAFTER) {
      el.listeners[attr].push(code);
     }
    }
   }
   // É um atributo normal.
   if (status == NORMAL_ATTRIBUTE || moz) {
    attrnewval, codes = [value, attrval];
    // É pra escrever antes.
    if (behavior == WRITEBEFORE) {
     attrnewval = codes.join(sep);
    // É pra escrever depois; Inverte ordem.
    } else if (behavior == WRITEAFTER) {
     attrnewval = codes.reverse().join(sep);
    }
    // Seta atributo normalmente.
    el.setAttribute(attr, attrnewval);
   }
  }
 });
 return true;
};

//
// Troca o valor de campos boolean diretamente do browse.
//
// @param klass   class Object(String ClassId, Object PrimaryKeys)
//        field   field Object(String FieldId, String Description, enum('0', '1', '2') NotNull, String value)
//        opts    hash com opções e suas descrições.
//        element elemento DOM alvo
//
// @author Matheus Lorenzoni Cruz
// @date   2007-11-12
//
function toggleBoolBrowseVal(klass, field, choices, element, refresh) {
 if ((element = $(element)).updating) return;
 new Ultra.ChoiceDialog({
  title: "Alteração",
  message: field.Description,
  choice: {"1":choices["1"],"0":choices["0"],"":choices[""]},
  onCreate: function () { if (field.NotNull != "0") this.dropChoice(""); this.getChoiceByValue(field.value).disabled = true },
  onChoose: function () {
   field.value = this.choice().value;
   element.updating = true;
   var oldHTML = element.innerHTML,
       params  = klass.PrimaryKeys;
   params[field.FieldId] = field.value;
   params['_resolvedAction'] = 1;
   element.update("<img src='/t/default/img/loading.gif' />");
   new Ultra.Class(klass.ClassId).Update(params, {
    onSuccess: function ()       { var myform = element.up("form");
                                   element.up("td").update(this.Field[field.FieldId].resolved);
                                   if ( myform && refresh ) { myform.elements['_'+klass.ClassId+'.Submit'].click() } },
    onError  : function (errstr) { alert('toogglebool: '+errstr); element.updating = false; element.update(oldHTML) }
   });
  }
 }).show();
}
function tbbv(){return toggleBoolBrowseVal.apply(this,arguments)}

//
// Alterna entre o modo desenvolvedor e o modo usuário
//
function xsSwitch (el) {
 var c = el.firstChild;
 if (!c.checked) {
  document.cookie = 'xs=1;path=/';
  c.checked = true;
  document.body.setAttribute('xs',1);
 } else {
  document.cookie = 'xs=0;path=/;expire=Sun, 01 Jan 1900 00:00:00 GMT';
  c.checked = false;
  document.body.removeAttribute('xs');
 }
}

//
// Abre um popup com o Editor de Código fonte HighLight
//
// @param el   Elemento de origem (textarea)
//        mode Mode de edição
//
function HighlightEditor ( el, mode, extra ) {
 var random = new Number(Math.random());
 if ( ! el.id ) {
  var newID = 'textarea_'+ random.toString();
  newID = newID.replace(/\./g,'');
  el.setAttribute('id',newID);
 }
 window.open( '/Html/Editor/CodeMirror/Window.html?textarea='+el.id+'&mode='+mode+'&'+extra, 'parser_'+newID, 'toolbar=no,titlebar=no,status=yes,scrollbars=yes,menubar=yes,location=no,resizable=yes,width='+(screen.availWidth-200)+',height='+(screen.availHeight-200) );
 //window.open('/Html/Editor/TextSaveOnly.cgi?Id='+el.id+'&Name='+el.id+'&FormName='+el.up('form').id+'&eval=onSubmitTEXT','EditorHTML','width=800,height=600,toolbar=no,titlebar=no,status=yes,scrollbars=no,menubar=no,location=no,resizable=yes');
}

function ActionsExec (ssource,sthis) {
 ssource(sthis);
}


//
// Altera configuracao do item de menu
//
function doPackageEdit (e,ClassId,Level,PackageId,Type,allHidden,EntryId) {
 Event.stop(e);
 requestURL('GET','?_ClassId='+ClassId+'&_Level='+Level+'&_'+ClassId+'.Method=AjaxSuperContext&_PackageId='+PackageId+'&_PackageType='+Type+':'+allHidden+'&_PackageEdit=1&_PackageHide='+PackageId+':'+EntryId,null,findParentElement(Event.element(e),'DIV','type','SuperContext'),1);
}

// Exibe contedo do howto como uma help-tooltip
function howto(ev,howto){
 help(ev,'Carregando...','','none');
 requestURL(
  'GET',
  'Class.cgi?_ClassId=Ultra.Class&_Ultra.Class.Method=AjaxSuperContext&_PackageId=*HELP*&_search_inside=1&_HowTo='+howto,
  null,
  $('div__helper').firstChild
 );
}

//
// Usado em campo Ultra.CEP para exibir o mapa do google
//
function CEP2Map (el) {
 var
  rua    = el.field(el.getAttribute('-rua')),
  numero = el.field(el.getAttribute('-numero')),
  city   = el.field(el.getAttribute('-city')),
  cityx  = city ? el.form.elements[ city.name + '.x' ] : null,
  state  = el.field(el.getAttribute('-state'));

 var addr =
  ( rua    ? rua.value    : '' )+', '+
  ( numero ? numero.value : '' )+' - '+
  ( cityx  ? cityx.value  : city   ? city.getAttribute('resolved') ? city.getAttribute('resolved') : ( city.currentOption() ? city.currentOption().text : '' ) : '' )+' - '+
  ( state  ? state.value  : '' );


 HelpMap(
  addr,
  null,
  null,
  null,
  [ addr ] );
}

//
// Exibe uma helpbox com um mapa do google.
//
function HelpMap (center, zoom, size, maptype, markers) {
 if (!zoom)    zoom = '14';
 if (!size)    size = '480x480';
 if (!maptype) maptype = 'roadmap';
 if (!markers) markers = [center];

 var url =
  'http://maps.google.com/maps/api/staticmap?'+
  'center='+center+
  '&size='+size+
  '&maptype='+maptype+
  markers.map(function(e){ return '&markers='+e }).join('&')+
  '&sensor=false';


 help(
  null,
  "<img src='"+url+"&zoom="+zoom+"' xurl='"+url+"' xzoom="+zoom+" "+
  "onclick='this.setAttribute(\"xzoom\", parseInt(this.getAttribute(\"xzoom\"))+1);this.src=this.getAttribute(\"xurl\")+\"&zoom=\"+(this.getAttribute(\"xzoom\"));' "+
  "oncontextmenu='this.setAttribute(\"xzoom\", parseInt(this.getAttribute(\"xzoom\"))-1);this.src=this.getAttribute(\"xurl\")+\"&zoom=\"+(this.getAttribute(\"xzoom\"));return false;' "+
  ">",
  '','none');
}


// Exibe védio do YouTube em uma div_helper
function getVideo (url,w,h) {
 if ( !w ) w = 427;
 if ( !h ) h = 268;
 return '<object width="'+w+'" height="'+h+'"><param name="movie" value="http://www.youtube.com/v/'+url+'&hl=pt-br&fs=1&rel=0&autoplay=1&plune"></param><param name="allowFullScreen" value="true"></param><param name="allowscriptaccess" value="always"></param><embed src="http://www.youtube.com/v/'+url+'&hl=pt-br&fs=1&rel=0&autoplay=1&plune" type="application/x-shockwave-flash" allowscriptaccess="always" allowfullscreen="true" width="'+w+'" height="'+h+'"></embed></object>';
}

// variável global para permitir clicar no X da Div Helper e impedí-la de abrir durante a sessão.
var bloq_div__helper = new Array();

/*
################################################

   Função que aplicará sobre uma div no canto inferior direito, qualquer elemento no interior.
   Tem como objetivo desenvolver maiores facilidades de demonstração de recursos que o sistema tem,
   mas ficam "ocultos". Por exemplo, criar relatórios, clicando com o botão direito. Ninguém que entre
   no plune na primeira vez, sabe que ao clicar com o botão direito, uma gama de informações se abre.

   @auth: Felipe H. Müller
   @date: 26/03/2009
   @elem:
         ev    -> evento de origem - ###obrigatório###
         html  -> texto, html ou não que vai pro conteúdo da div
         img   -> url de uma imagem * para o usuário não ter que sempre montar a tag na hora de chamar a func.
         out   -> evento de saída - por padrão onmouseout, mas o usuário pode expecificar outro.

################################################
*/
function help (ev, html, img, out, classe){
   var valortop;
   var content;

   // verifica se o segundo parâmetro da função é uma hash. Se for, realiza o "split" para atribuir os valores e seguir a função.
   if (Object.isHash(html)){

      img    = html['img'] ? html['img'].value : '';
      out    = html['out'] ? html['out'].value : '';
      classe = html['classe'] ? html['classe'].value : '';
      html   = html['html'] ? html['html'].value : '';

   }

   // Classe não definida, indica que é um tooltip global.
   if (typeof classe == 'undefined' || classe == '') {
      classe = 0;
   }

   if ( (bloq_div__helper[classe] == 1) && (ev && ev.type != 'click') ){
      // o usuário definiu que não quer mais ver a div na sessão
      return;
   }

   if ( nav() == 'ip' ) return; // iphone

   // Caso não exista a div ainda(primeira vez). Tenta criá-la
   if (!$('div__helper')){

         // Define o novo elemento.
         var div = document.createElement('div');
         div.setAttribute('id', 'div__helper');
         if (moz) {
            $(div).setStyle({display: 'none', position:'fixed', bottom:'1em', left:'auto', right:'1em'});
         }else{
            $(div).setStyle({display: 'none', position:'absolute', left:'auto', bottom:'auto', right:'1em', opacity: '0.90', Filter: 'Alpha(Opacity=90)', background:'#FFFFFF'});
         }

         // #### Observação fora da função: estranhamente, o setAttribute não funcionou para definir o style. Tive de usar outro método.

         div.setAttribute('class', 'div__helper');
         $(div).setStyle({display:'block'});

         // Insere o elemento no body.
         document.body.appendChild(div);
   }

   var dh = $('div__helper');

   // Se div__helper ja estiver visivel via onclick, nao carrega qualquer outro evento que nao onclick
   if ( (dh.getStyle('display') == 'block') && (dh.getAttribute('last_event') == 'click') && (ev && ev.type != 'click') ) {
    return;

   } else {

    // Marca qual foi o último evento que disparou o div__helper
      if ((ev != null) || (ev && ev != '' && typeof ev != 'undefined' && ev.type != '')){
         dh.setAttribute('last_event', ev.type);
      }else{
         dh.setAttribute('last_event', 'click');
      }

   }

   // Monta o html básico de conteúdo da div
   content = "<span onmouseover=\"$('div__helper').setStyle({display:'block'});\"><table><tr><td>"
           + html
           + "</td>"
           + ( ((typeof img == 'undefined') || (img=='')) ? '' : ("<td><img border=0 src='" + img + "'></td>") )
           + "</tr></table></span>"
           + "<img src=/t/c/img/action/Close.png style='position:absolute;left:-7px;top:-7px' onmouseover=\"$('div__helper').setStyle({display:'block'});\" onclick=\"$('div__helper').setStyle({display:'none'});bloq_div__helper["+ classe +"]=1;\">";


   // joga o conteúdo para dentro da div
   dh.innerHTML = content;

   // Evita bugs de sempre ficar visivel(ignora o evento de saída)
   if(dh.getStyle('display') != 'block'){
      dh.setStyle({display:'block'});
   }

    // Sendo IE o tratamento tem que ser um pouco diferente, pois o IE não entende a propriedade position:fixed
   if (!moz) {
      var rolY;

      if (document.documentElement && !document.documentElement.scrollTop){
      // IE6 +4.01 but no scrolling going on
         rolY = document.documentElement.clientHeight - dh.getHeight();

      }else if (document.documentElement && document.documentElement.scrollTop){
      // IE6 +4.01 and user has scrolled
         rolY = (document.documentElement.clientHeight + document.documentElement.scrollTop) - dh.getHeight();

      }else if (document.body && document.body.scrollTop){
         rolY = (document.body.scrollTop + document.body.clientHeight) - dh.getHeight();
      // IE5 or DTD 3.2

      }

      valortop = (rolY-11)+'px';
      $(dh).setStyle({bottom:'auto'});
      $(dh).setStyle({top:valortop});
   }

   // Se não definiu nenhum método de saída
   if ((typeof out == 'undefined') || (out == '')) {
      if ( ev && ev.type == 'focus' ) {
       out = 'blur';
      } else {
       out = 'mouseout';
      }
   }

   // Observa o elemento do evento, para saber quando sair
   if (ev) {
    Event.observe($(Event.element(ev)), out, function () {
     if ( (dh.getStyle('display') == 'block') && (dh.getAttribute('last_event') != 'click')) {
      dh.setStyle({display:'none'});
     }
    });
   }
}

function reloadForm(ev){
   if (ev){
      var el      = Event.element(ev);
      var form    = $(el).up('form');

      if(typeof form == 'undefined' || typeof form == 'unknow'){
         form = document.forms[$(el).readAttribute('formname')];
      }

      var classid = form.elements['_ClassId'];

      /*é um estanciamento*/
      if(typeof form.elements['_'+classid.value+'.Reload'] != 'undefined' && typeof form.elements['_'+classid.value+'.Reload'] != 'unknow'){
         form.elements['_'+classid.value+'.Reload'].onclick();
      /*é um browse*/
      } else {
         // Descobre o formulário real de Browse se necessário
         if ( form.name.match(/^Form/) && document.forms['P'+form.name] ) {
          form = document.forms['P'+form.name];
         }
         form.onsubmit();
      }
   }
}

/*
Autor: Jackon de Fraga
Finalidade: Auxilia ao des/habilitar campos do handler com calendar.
*/
function disableCalendar(campo, desabilitar){
   var formaux   = campo.form,
       nomeCampo = campo.name,
       el,
       maskaux;
       

   if(desabilitar){

      if (typeof formaux.elements[nomeCampo].length != 'undefined'){
         /* Quando for um campo array */
         for (var i=0; i < formaux.elements[nomeCampo].length; i++){
            formaux.elements[nomeCampo][i].setAttribute('disabled', 1);
            el = formaux.elements[nomeCampo][i];            
            if(typeof $(el).next('img') != 'undefined'){
               $(el).next('img').setAttribute('src', '/t/c/img/action/Calendar-disabled.gif');
            }
         }
      }else{
         /* Quando não for array */
         if(typeof campo.next('img') != 'undefined'){
            campo.next('img').setAttribute('src', '/t/c/img/action/Calendar-disabled.gif');
         }
         campo.setAttribute('disabled', 1);
      }
   }else{
      if (typeof formaux.elements[nomeCampo].length != 'undefined'){
         for (var i=0; i < formaux.elements[nomeCampo].length; i++){
            formaux.elements[nomeCampo][i].removeAttribute('readonly');
            formaux.elements[nomeCampo][i].removeAttribute('disabled');
            el = formaux.elements[nomeCampo][i];
            maskaux = el.getAttribute('datemask');            
            if(typeof $(el).next('img') != 'undefined'){
               $(el).next('img').setAttribute('src', '/t/c/img/action/Calendar.gif');
            }
         }
      }else{
         campo.removeAttribute('disabled');
         campo.removeAttribute('readonly');
         maskaux = campo.getAttribute('datemask');
         if(typeof campo.next('img') != 'undefined'){
            campo.next('img').setAttribute('src', '/t/c/img/action/Calendar.gif');
         }
      }
   }
}

//
// Usado no onkeydown do elemento body para desabilitar o backspace.
//
function bodyKeydown (e) {
 if ( ( e.keyCode == 8 || e.keyCode == 13 ) &&
      ( ( e.target     && ( e.target.nodeName.toUpperCase() == 'BODY' || e.target.nodeName.toUpperCase() == 'BODY' ) ) ||
        ( e.srcElement && ( e.srcElement.tagName.toUpperCase() != 'INPUT' && e.srcElement.tagName.toUpperCase() != 'TEXTAREA' ) ) ) ) {
  return false;
 }
}
