


/**
 * SWFObject v1.5: Flash Player detection and embed - http://blog.deconcept.com/swfobject/
 *
 * SWFObject is (c) 2007 Geoff Stearns and is released under the MIT License:
 * http://www.opensource.org/licenses/mit-license.php
 *
 */
if(typeof deconcept=="undefined"){var deconcept=new Object();}if(typeof deconcept.util=="undefined"){deconcept.util=new Object();}if(typeof deconcept.SWFObjectUtil=="undefined"){deconcept.SWFObjectUtil=new Object();}deconcept.SWFObject=function(_1,id,w,h,_5,c,_7,_8,_9,_a){if(!document.getElementById){return;}this.DETECT_KEY=_a?_a:"detectflash";this.skipDetect=deconcept.util.getRequestParameter(this.DETECT_KEY);this.params=new Object();this.variables=new Object();this.attributes=new Array();if(_1){this.setAttribute("swf",_1);}if(id){this.setAttribute("id",id);}if(w){this.setAttribute("width",w);}if(h){this.setAttribute("height",h);}if(_5){this.setAttribute("version",new deconcept.PlayerVersion(_5.toString().split(".")));}this.installedVer=deconcept.SWFObjectUtil.getPlayerVersion();if(!window.opera&&document.all&&this.installedVer.major>7){deconcept.SWFObject.doPrepUnload=true;}if(c){this.addParam("bgcolor",c);}var q=_7?_7:"high";this.addParam("quality",q);this.setAttribute("useExpressInstall",false);this.setAttribute("doExpressInstall",false);var _c=(_8)?_8:window.location;this.setAttribute("xiRedirectUrl",_c);this.setAttribute("redirectUrl","");if(_9){this.setAttribute("redirectUrl",_9);}};deconcept.SWFObject.prototype={useExpressInstall:function(_d){this.xiSWFPath=!_d?"expressinstall.swf":_d;this.setAttribute("useExpressInstall",true);},setAttribute:function(_e,_f){this.attributes[_e]=_f;},getAttribute:function(_10){return this.attributes[_10];},addParam:function(_11,_12){this.params[_11]=_12;},getParams:function(){return this.params;},addVariable:function(_13,_14){this.variables[_13]=_14;},getVariable:function(_15){return this.variables[_15];},getVariables:function(){return this.variables;},getVariablePairs:function(){var _16=new Array();var key;var _18=this.getVariables();for(key in _18){_16[_16.length]=key+"="+_18[key];}return _16;},getSWFHTML:function(){var _19="";if(navigator.plugins&&navigator.mimeTypes&&navigator.mimeTypes.length){if(this.getAttribute("doExpressInstall")){this.addVariable("MMplayerType","PlugIn");this.setAttribute("swf",this.xiSWFPath);}_19="<embed type=\"application/x-shockwave-flash\" src=\""+this.getAttribute("swf")+"\" width=\""+this.getAttribute("width")+"\" height=\""+this.getAttribute("height")+"\" style=\""+this.getAttribute("style")+"\"";_19+=" id=\""+this.getAttribute("id")+"\" name=\""+this.getAttribute("id")+"\" ";var _1a=this.getParams();for(var key in _1a){_19+=[key]+"=\""+_1a[key]+"\" ";}var _1c=this.getVariablePairs().join("&");if(_1c.length>0){_19+="flashvars=\""+_1c+"\"";}_19+="/>";}else{if(this.getAttribute("doExpressInstall")){this.addVariable("MMplayerType","ActiveX");this.setAttribute("swf",this.xiSWFPath);}_19="<object id=\""+this.getAttribute("id")+"\" classid=\"clsid:D27CDB6E-AE6D-11cf-96B8-444553540000\" width=\""+this.getAttribute("width")+"\" height=\""+this.getAttribute("height")+"\" style=\""+this.getAttribute("style")+"\">";_19+="<param name=\"movie\" value=\""+this.getAttribute("swf")+"\" />";var _1d=this.getParams();for(var key in _1d){_19+="<param name=\""+key+"\" value=\""+_1d[key]+"\" />";}var _1f=this.getVariablePairs().join("&");if(_1f.length>0){_19+="<param name=\"flashvars\" value=\""+_1f+"\" />";}_19+="</object>";}return _19;},write:function(_20){if(this.getAttribute("useExpressInstall")){var _21=new deconcept.PlayerVersion([6,0,65]);if(this.installedVer.versionIsValid(_21)&&!this.installedVer.versionIsValid(this.getAttribute("version"))){this.setAttribute("doExpressInstall",true);this.addVariable("MMredirectURL",escape(this.getAttribute("xiRedirectUrl")));document.title=document.title.slice(0,47)+" - Flash Player Installation";this.addVariable("MMdoctitle",document.title);}}if(this.skipDetect||this.getAttribute("doExpressInstall")||this.installedVer.versionIsValid(this.getAttribute("version"))){var n=(typeof _20=="string")?document.getElementById(_20):_20;n.innerHTML=this.getSWFHTML();return true;}else{if(this.getAttribute("redirectUrl")!=""){document.location.replace(this.getAttribute("redirectUrl"));}}return false;}};deconcept.SWFObjectUtil.getPlayerVersion=function(){var _23=new deconcept.PlayerVersion([0,0,0]);if(navigator.plugins&&navigator.mimeTypes.length){var x=navigator.plugins["Shockwave Flash"];if(x&&x.description){_23=new deconcept.PlayerVersion(x.description.replace(/([a-zA-Z]|\s)+/,"").replace(/(\s+r|\s+b[0-9]+)/,".").split("."));}}else{if(navigator.userAgent&&navigator.userAgent.indexOf("Windows CE")>=0){var axo=1;var _26=3;while(axo){try{_26++;axo=new ActiveXObject("ShockwaveFlash.ShockwaveFlash."+_26);_23=new deconcept.PlayerVersion([_26,0,0]);}catch(e){axo=null;}}}else{try{var axo=new ActiveXObject("ShockwaveFlash.ShockwaveFlash.7");}catch(e){try{var axo=new ActiveXObject("ShockwaveFlash.ShockwaveFlash.6");_23=new deconcept.PlayerVersion([6,0,21]);axo.AllowScriptAccess="always";}catch(e){if(_23.major==6){return _23;}}try{axo=new ActiveXObject("ShockwaveFlash.ShockwaveFlash");}catch(e){}}if(axo!=null){_23=new deconcept.PlayerVersion(axo.GetVariable("$version").split(" ")[1].split(","));}}}return _23;};deconcept.PlayerVersion=function(_29){this.major=_29[0]!=null?parseInt(_29[0]):0;this.minor=_29[1]!=null?parseInt(_29[1]):0;this.rev=_29[2]!=null?parseInt(_29[2]):0;};deconcept.PlayerVersion.prototype.versionIsValid=function(fv){if(this.major<fv.major){return false;}if(this.major>fv.major){return true;}if(this.minor<fv.minor){return false;}if(this.minor>fv.minor){return true;}if(this.rev<fv.rev){return false;}return true;};deconcept.util={getRequestParameter:function(_2b){var q=document.location.search||document.location.hash;if(_2b==null){return q;}if(q){var _2d=q.substring(1).split("&");for(var i=0;i<_2d.length;i++){if(_2d[i].substring(0,_2d[i].indexOf("="))==_2b){return _2d[i].substring((_2d[i].indexOf("=")+1));}}}return "";}};deconcept.SWFObjectUtil.cleanupSWFs=function(){var _2f=document.getElementsByTagName("OBJECT");for(var i=_2f.length-1;i>=0;i--){_2f[i].style.display="none";for(var x in _2f[i]){if(typeof _2f[i][x]=="function"){_2f[i][x]=function(){};}}}};if(deconcept.SWFObject.doPrepUnload){if(!deconcept.unloadSet){deconcept.SWFObjectUtil.prepUnload=function(){__flash_unloadHandler=function(){};__flash_savedUnloadHandler=function(){};window.attachEvent("onunload",deconcept.SWFObjectUtil.cleanupSWFs);};window.attachEvent("onbeforeunload",deconcept.SWFObjectUtil.prepUnload);deconcept.unloadSet=true;}}if(!document.getElementById&&document.all){document.getElementById=function(id){return document.all[id];};}var getQueryParamValue=deconcept.util.getRequestParameter;var FlashObject=deconcept.SWFObject;var SWFObject=deconcept.SWFObject;
/**
 * Observer - Observe formelements for changes
 *
 * @version		1.0rc1
 *
 * @license		MIT-style license
 * @author		Harald Kirschner <mail [at] digitarald.de>
 * @copyright	Author
 */
var Observer = new Class({

	options: {
		periodical: false,
		delay: 1000
	},

	initialize: function(el, onFired, options){
		this.setOptions(options);
		this.addEvent('onFired', onFired);
		this.element = $(el);
		this.listener = this.fired.bind(this);
		this.value = this.element.getValue();
		if (this.options.periodical) this.timer = this.listener.periodical(this.options.periodical);
		else this.element.addEvent('keyup', this.listener);
	},

	fired: function() {
		var value = this.element.getValue();
		if (this.value == value) return;
		this.clear();
		this.value = value;
		this.timeout = this.fireEvent.delay(this.options.delay, this, ['onFired', [value]]);
	},

	clear: function() {
		$clear(this.timeout);
		return this;
	}
});

Observer.implement(new Options);
Observer.implement(new Events);
/**
 * Autocompleter
 *
 * @version		1.0rc4
 *
 * @license		MIT-style license
 * @author		Harald Kirschner <mail [at] digitarald.de>
 * @copyright	Author
 */
var Autocompleter = {};

Autocompleter.Base = new Class({

  options: {
    minLength: 1,
    useSelection: true,
    markQuery: true,
    inheritWidth: false,
    maxChoices: 10,
    injectChoice: null,
    onSelect: Class.empty,
    onShow: Class.empty,
    onHide: Class.empty,
    customTarget: null,
    className: 'autocompleter-choices',
    zIndex: 42,
    observerOptions: {},
    fxOptions: {},
    overflown: []
  },

  initialize: function(el, options) {
    this.setOptions(options);
    this.element = $(el);
    this.build();
    this.observer = new Observer(this.element, this.prefetch.bind(this), $merge({
      delay: 400
    }, this.options.observerOptions));
    this.value = this.observer.value;
    this.queryValue = null;
  },

  /**
   * build - Initialize DOM
   *
   * Builds the html structure for choices and appends the events to the element.
   * Override this function to modify the html generation.
   */
  build: function() {
    if ($(this.options.customTarget)) this.choices = this.options.customTarget;
    else {
      this.choices = new Element('ul', {
        'class': this.options.className,
        styles: {zIndex: this.options.zIndex}
      }).injectInside(document.body);
      this.fix = new OverlayFix(this.choices);
    }
    this.fx = this.choices.effect('opacity', $merge({
      wait: false,
      duration: 200
    }, this.options.fxOptions))
      .addEvent('onStart', function() {
        if (this.fx.now) return;
        this.choices.setStyle('display', '');
        this.fix.show();
      }.bind(this))
      .addEvent('onComplete', function() {
        if (this.fx.now) return;
        this.choices.setStyle('display', 'none');
        this.fix.hide();
      }.bind(this)).set(0);
    this.element.setProperty('autocomplete', 'off')
      .addEvent(window.ie ? 'keydown' : 'keypress', this.onCommand.bindWithEvent(this))
      .addEvent('mousedown', this.onCommand.bindWithEvent(this, [true]))
      .addEvent('focus', this.toggleFocus.bind(this, [true]))
      .addEvent('blur', this.toggleFocus.bind(this, [false]))
      .addEvent('trash', this.destroy.bind(this));
  },

  destroy: function() {
    this.choices.remove();
  },

  toggleFocus: function(state) {
    this.focussed = state;
    if (!state) this.hideChoices();
  },

  onCommand: function(e, mouse) {
    if (mouse && this.focussed) this.prefetch();
    if (e.key && !e.shift) switch (e.key) {
      case 'enter':
        if (this.selected && this.visible) {
          this.choiceSelect(this.selected);
          e.stop();
        } return;
      case 'up': case 'down':
        if (this.observer.value != (this.value || this.queryValue)) this.prefetch();
        else if (this.queryValue === null) break;
        else if (!this.visible) this.showChoices();
        else {
          this.choiceOver((e.key == 'up')
            ? this.selected.getPrevious() || this.choices.getLast()
            : this.selected.getNext() || this.choices.getFirst() );
          this.setSelection();
        }
        e.stop(); return;
      case 'esc': this.hideChoices(); return;
    }
    this.value = false;
  },

  setSelection: function() {
    if (!this.options.useSelection) return;
    var startLength = this.queryValue.length;
    if (this.element.value.indexOf(this.queryValue) != 0) return;
    var insert = this.selected.inputValue.substr(startLength);
    if (document.getSelection) {
      this.element.value = this.queryValue + insert;
      this.element.selectionStart = startLength;
      this.element.selectionEnd = this.element.value.length;
    } else if (document.selection) {
      var sel = document.selection.createRange();
      sel.text = insert;
      sel.move("character", - insert.length);
      sel.findText(insert);
      sel.select();
    }
    this.value = this.observer.value = this.element.value;
  },

  hideChoices: function() {
    if (!this.visible) return;
    this.visible = this.value = false;
    this.observer.clear();
    this.fx.start(0);
    this.fireEvent('onHide', [this.element, this.choices]);
  },

  showChoices: function() {
    if (this.visible || !this.choices.getFirst()) return;
    this.visible = true;
    var pos = this.element.getCoordinates(this.options.overflown);
    // dschivo-20071203
    if (!pos.left) return;
    this.choices.setStyles({
      left: pos.left,
      top: pos.bottom
    });
    if (this.options.inheritWidth) this.choices.setStyle('width', pos.width);
    this.fx.start(1);
    this.choiceOver(this.choices.getFirst());
    this.fireEvent('onShow', [this.element, this.choices]);
  },

  prefetch: function() {
    if (this.element.value.length < this.options.minLength) this.hideChoices();
    else if (this.element.value == this.queryValue) this.showChoices();
    else this.query();
  },

  updateChoices: function(choices) {
    this.choices.empty();
    this.selected = null;
    if (!choices || !choices.length) return;
    if (this.options.maxChoices < choices.length) choices.length = this.options.maxChoices;
    choices.each(this.options.injectChoice || function(choice, i){
      var el = new Element('li').setHTML(this.markQueryValue(choice));
      el.inputValue = choice;
      this.addChoiceEvents(el).injectInside(this.choices);
    }, this);
    this.showChoices();
  },

  choiceOver: function(el) {
    if (this.selected) this.selected.removeClass('autocompleter-selected');
    this.selected = el.addClass('autocompleter-selected');
  },

  choiceSelect: function(el) {
    this.observer.value = this.element.value = el.inputValue;
    this.hideChoices();
    this.fireEvent('onSelect', [this.element], 20);
  },

  /**
   * markQueryValue
   *
   * Marks the queried word in the given string with <span class="autocompleter-queried">*</span>
   * Call this i.e. from your custom parseChoices, same for addChoiceEvents
   *
   * @param		{String} Text
   * @return		{String} Text
   */
  markQueryValue: function(txt) {
    return (this.options.markQuery && this.queryValue) ? txt.replace(new RegExp('^(' + this.queryValue.escapeRegExp() + ')', 'i'), '<span class="autocompleter-queried">$1</span>') : txt;
  },

  /**
   * addChoiceEvents
   *
   * Appends the needed event handlers for a choice-entry to the given element.
   *
   * @param		{Element} Choice entry
   * @return		{Element} Choice entry
   */
  addChoiceEvents: function(el) {
    return el.addEvents({
      mouseover: this.choiceOver.bind(this, [el]),
      mousedown: this.choiceSelect.bind(this, [el])
    });
  }
});

Autocompleter.Base.implement(new Events);
Autocompleter.Base.implement(new Options);

Autocompleter.Local = Autocompleter.Base.extend({

  options: {
    minLength: 0,
    filterTokens : null
  },

  initialize: function(el, tokens, options) {
    this.parent(el, options);
    this.tokens = tokens;
    if (this.options.filterTokens) this.filterTokens = this.options.filterTokens.bind(this);
  },

  query: function() {
    this.hideChoices();
    this.queryValue = this.element.value;
    this.updateChoices(this.filterTokens());
  },

  filterTokens: function(token) {
    var regex = new RegExp('^' + this.queryValue.escapeRegExp(), 'i');
    return this.tokens.filter(function(token) {
      return regex.test(token);
    });
  }

});

Autocompleter.Ajax = {};

Autocompleter.Ajax.Base = Autocompleter.Base.extend({

  options: {
    postVar: 'value',
    postData: {},
    ajaxOptions: {},
    onRequest: Class.empty,
    onComplete: Class.empty
  },

  initialize: function(el, url, options) {
    this.parent(el, options);
    this.ajax = new Ajax(url, $merge({
      autoCancel: true
    }, this.options.ajaxOptions));
    this.ajax.addEvent('onComplete', this.queryResponse.bind(this));
    this.ajax.addEvent('onFailure', this.queryResponse.bind(this, [false]));
  },

  query: function(){
    var data = $extend({}, this.options.postData);
    data[this.options.postVar] = this.element.value;
    this.fireEvent('onRequest', [this.element, this.ajax]);
    this.ajax.request(data);
  },

  /**
   * queryResponse - abstract
   *
   * Inherated classes have to extend this function and use this.parent(resp)
   *
   * @param		{String} Response
   */
  queryResponse: function(resp) {
    this.value = this.queryValue = this.element.value;
    this.selected = false;
    this.hideChoices();
    this.fireEvent(resp ? 'onComplete' : 'onFailure', [this.element, this.ajax], 20);
  }

});

Autocompleter.Ajax.Json = Autocompleter.Ajax.Base.extend({

  queryResponse: function(resp) {
    this.parent(resp);
    var choices = Json.evaluate(resp || false);
    if (!choices || !choices.length) return;
    this.updateChoices(choices);
  }

});

Autocompleter.Ajax.Xhtml = Autocompleter.Ajax.Base.extend({

  options: {
    parseChoices: null
  },

  queryResponse: function(resp) {
    this.parent(resp);
    if (!resp) return;
    this.choices.setHTML(resp).getChildren().each(this.options.parseChoices || this.parseChoices, this);
    this.showChoices();
  },

  parseChoices: function(el) {
    var value = el.innerHTML;
    el.inputValue = value;
    el.setHTML(this.markQueryValue(value));
  }

});


var OverlayFix = new Class({

  initialize: function(el) {
    this.element = $(el);
    if (window.ie){
      this.element.addEvent('trash', this.destroy.bind(this));
      this.fix = new Element('iframe', {
        properties: {
          frameborder: '0',
          scrolling: 'no',
          src: 'javascript:false;'
        },
        styles: {
          position: 'absolute',
          border: 'none',
          display: 'none',
          filter: 'progid:DXImageTransform.Microsoft.Alpha(opacity=0)'
        }
      }).injectAfter(this.element);
    }
  },

  show: function() {
    if (this.fix) this.fix.setStyles($extend(
      this.element.getCoordinates(), {
        display: '',
        zIndex: (this.element.getStyle('zIndex') || 1) - 1
      }));
    return this;
  },

  hide: function() {
    if (this.fix) this.fix.setStyle('display', 'none');
    return this;
  },

  destroy: function() {
    this.fix.remove();
  }

});
Autocompleter.DWR = Autocompleter.Base.extend({

  initialize: function(el, populator, options) {
    this.parent(el, options);
    if (this.choices) {
      this.choices.setStyle('display', 'none');
    }
    this.addEvent('onHide', function(element, choices) {
      jslog.debug('>onHide< (Autocompleter.DWR.initialize)');
      choices.setStyle('display', 'none');
    });
    this.populator = populator;
  },
  
  query: function() {
    jslog.debug('>>>>Autocompleter.DWR.query()');

    var callback = function() {
      this.queryValue = this.element.value;
      this.fireEvent('onRequest', this.element);
      this.populator(this, this.element.value);
    };
    if (this.visible) {
      this.hideChoices();
      this.fx.chain(callback.bind(this));
    } else callback.apply(this);
  },
  
  updateChoices: function(choices) {
    jslog.debug('Autocompleter.DWR.updateChoices()');
    this.fireEvent('onComplete', this.element);
    this.options.array = choices;
    var valueSelector = this.options.valueSelector;
    this.parent(choices.map(function(choice, index) {
      return valueSelector ? valueSelector(choice) : choice;
    }));
  },
  
  choiceSelect: function(el) {
    this.parent(el);
    var afterUpdateElement = this.options.afterUpdateElement;
    if (afterUpdateElement) {
      var index = 0;
      var prev = el.getPrevious();
      while (prev) {
        index++;
        prev = prev.getPrevious();
      }
      afterUpdateElement(this.options.array[index]);
    }
  }

});
var MouseOverEffects = new Class( {

  options : {
    onShow : function(el) {
      //el.setStyle('visibility', 'visible');
    el.setStyle('display', 'block');
  },
  onHide : function(el) {
    //el.setStyle('visibility', 'hidden');
    el.setStyle('display', 'none');
  },
  showDelay :100,
  hideDelay :100,
  tooltipId :null,
  tooltipClass :''
  },

  initialize : function(elements, options) {
    this.setOptions(options);
    $$(elements).each(this.build, this);
    if (this.options.initialize)
      this.options.initialize.call(this);
  },

  build : function(el) {
    el.addEvent('mouseenter', function(event) {
      this.start(el);
    }.bind(this));
    var end = this.end.bind(this);
    el.addEvent('mouseleave', end);
    el.addEvent('trash', end);
    if (this.options.tooltipId) {
      var tooltipContainer = $(this.options.tooltipId);
      if (tooltipContainer == null) {
        tooltipContainer = new Element('div');
        tooltipContainer.setProperty('id', this.options.tooltipId);
        tooltipContainer.setStyle('display', 'none');
        tooltipContainer.setStyle('z-index', '10000');
        tooltipContainer.setStyle('position', 'absolute');
        document.body.appendChild(tooltipContainer);
        tooltipContainer.setProperty('class', this.options.tooltipClass);
        tooltipContainer.addEvent('mouseenter', function(event) {
          $clear(this.timer);
          this.timer = this.show.delay(this.options.showDelay, this);
        }.bind(this));
        var end = this.end.bind(this);
        tooltipContainer.addEvent('mouseleave', function(event) {
          $clear(this.timer);

          this.timer = this.hide.delay(this.options.hideDelay, this);
        }.bind(this));
        tooltipContainer.addEvent('trash', function(event) {
          $clear(this.timer);
          this.timer = this.hide.delay(this.options.hideDelay, this);
        }.bind(this));
      }
    }
  },

  start : function(el) {
    if (this.element) {
      this.hide.call(this);
    }
    this.element = el;
    $clear(this.timer);
    this.timer = this.show.delay(this.options.showDelay, this);
  },

  end : function(event) {
    $clear(this.timer);
    this.timer = this.hide.delay(this.options.hideDelay, this);
  },

  show : function() {
    if ( [ this.element ]) {
      if (this.options.timeout)
        this.timer = this.hide.delay(this.options.timeout, this);
      this.fireEvent('onShow', [ this.element ]);
    }
  },

  hide : function() {
    if ( [ this.element ]) {
      var tooltipContainer = $(this.options.tooltipId);
      if (tooltipContainer) {
        tooltipContainer.setStyle('display', 'none');
      }
      this.fireEvent('onHide', [ this.element ]);
    }
  }

});

MouseOverEffects.implement(new Events, new Options);

/**
 * Inits the mouse over effect for the catalogue page.
 */
function initMouseOverForNavigation() {
  new MouseOverEffects($$('#catalogo .prodotto'), {
    showDelay :500,
    hideDelay :300,
    tooltipId :'tooltipContainer',
    tooltipClass :'prodotto informazioniProdottoGriglia',
    onShow : function(el) {
      if (el) {
        var tooltipContainer = $('tooltipContainer');
        tooltipContainer.innerHTML = '';
        var informazioniProdottoEl = el.getElement('.informazioniProdotto');
        var clonedChildren = cloneWithEvents(informazioniProdottoEl).getChildren();
        clonedChildren.each( function(childEl) {
          tooltipContainer.appendChild(childEl);
        });
        tooltipContainer.setStyle('top', el.getPosition().y + 'px');
        var x = el.getPosition().x + 150;
        tooltipContainer.setStyle('left', x + 'px');
        tooltipContainer.setStyle('display', 'block');
      }
    },
    onHide : function(el) {
    }
  });
}

/**
 * Init the mouse over effects for user shelf page
 */
function initMouseOverForShelf() {
  new MouseOverEffects($$('.prodotto'), {
    showDelay :500,
    hideDelay :300,
    tooltipId :'tooltipContainer',
    tooltipClass :'prodotto informazioniProdottoGriglia informazioniProdottoScaffaleGriglia',
    onShow : function(el) {
      if (el) {
        var tooltipContainer = $('tooltipContainer');
        tooltipContainer.innerHTML = '';
        var informazioniProdottoEl = el.getElement('.informazioniScaffale');
        var clonedChildren = cloneWithEvents(informazioniProdottoEl).getChildren();
        clonedChildren.each( function(childEl) {
          tooltipContainer.appendChild(childEl);
        });
        tooltipContainer.setStyle('top', el.getPosition().y + 'px');
        var x = el.getPosition().x + 150;
        tooltipContainer.setStyle('left', x + 'px');
        tooltipContainer.setStyle('display', 'block');
      }
    },
    onHide : function(el) {
      if (el && el.getElement) {
        var info = el.getElement('.informazioniScaffale');
        info.setStyle('display', 'none');
      }
    }
  });
}

/**
 * Init the mouse over effects for user wishlist page
 */
function initMouseOverForWishlist() {
  new MouseOverEffects($$('.prodotto'), {
    showDelay :500,
    hideDelay :300,
    tooltipId :'tooltipContainer',
    tooltipClass :'prodotto informazioniProdottoGriglia',
    onShow : function(el) {
      if (el) {
        var tooltipContainer = $('tooltipContainer');
        tooltipContainer.innerHTML = '';
        var informazioniProdottoEl = el.getElement('.informazioniProdotto');
        var clonedChildren = cloneWithEvents(informazioniProdottoEl).getChildren();
        clonedChildren.each( function(childEl) {
          tooltipContainer.appendChild(childEl);
        });
        tooltipContainer.setStyle('top', el.getPosition().y + 'px');
        var x = el.getPosition().x + 150;
        tooltipContainer.setStyle('left', x + 'px');
        tooltipContainer.setStyle('display', 'block');
      }
    },
    onHide : function(el) {
      if (el && el.getElement) {
        var tooltipContainer = $('tooltipContainer');
        if (tooltipContainer != null) {
          tooltipContainer.setStyle('display', 'none');
        }
      }
    }
  });
}

/**
 * Init the mouse over effects for promotion page product display.
 */
function initMouseOverForPromotions() {
  new MouseOverEffects($$('#catalogo .prodotto'), {
    showDelay :500,
    hideDelay :300,
    tooltipId :'tooltipContainer',
    tooltipClass :'prodotto informazioniProdottoGriglia',
    onShow : function(el) {
      if (el) {
        var tooltipContainer = $('tooltipContainer');
        tooltipContainer.innerHTML = '';
        var informazioniProdottoEl = el.getElement('.informazioniProdotto');
        var clonedChildren = cloneWithEvents(informazioniProdottoEl).getChildren();
        clonedChildren.each( function(childEl) {
          tooltipContainer.appendChild(childEl);
        });
        tooltipContainer.setStyle('top', el.getPosition().y + 'px');
        var x = el.getPosition().x + 150;
        tooltipContainer.setStyle('left', x + 'px');
        tooltipContainer.setStyle('display', 'block');
      }
    },
    onHide : function(el) {
    }
  });
}

/**
 * Init the mouse over effects for all sidebar products in hit parades.
 */
function initMouseOverForRelatedProducts() {
  new MouseOverEffects($$('.miniClassifica .prodottoClassificaSidebarRelatedProducts'), {
    showDelay :500,
    hideDelay :300,
    tooltipId :'tooltipContainerSidebarForRelatedProducts',
    tooltipClass :'informazioniProdottoRelatedClassificaTooltip',
    onShow : function(el) {
      jslog.debug('onShow()');
      if (el) {
        var tooltipContainer = $('tooltipContainerSidebarForRelatedProducts');
        tooltipContainer.innerHTML = '';
        var informazioniProdottoEl = el.getElement('.informazioniProdottoRelatedClassifica');
        var clonedChildren = cloneWithEvents(informazioniProdottoEl).getChildren();
        clonedChildren.each( function(childEl) {
          tooltipContainer.appendChild(childEl);
        });
        tooltipContainer.setStyle('top', el.getPosition().y - 60 + 'px');
        var x = el.getPosition().x + 195;
        tooltipContainer.setStyle('left', x + 'px');
        tooltipContainer.setStyle('display', 'block');
        el.addClass('sideBarClassificheSfondoSelected');
      }
    },
    onHide : function(el) {
      if (el) {
        el.removeClass('sideBarClassificheSfondoSelected');
      };
    }
  });
  
}

function initMouseOverForSidebarClassifica() {
	new MouseOverEffects($$('.miniClassifica .prodottoClassificaSidebar'), {
		showDelay :500,
		hideDelay :300,
		tooltipId :'tooltipContainerSidebar',
		tooltipClass :'informazioniProdottoClassificaTooltip',
		onShow : function(el) {
		jslog.debug('onShow()');
		if (el) {
			var tooltipContainer = $('tooltipContainerSidebar');
			tooltipContainer.innerHTML = '';
			var informazioniProdottoEl = el.getElement('.informazioniProdottoClassifica');
			var clonedChildren = cloneWithEvents(informazioniProdottoEl).getChildren();
			clonedChildren.each( function(childEl) {
				tooltipContainer.appendChild(childEl);
			});
			tooltipContainer.setStyle('top', el.getPosition().y - 60 + 'px');
			var x = el.getPosition().x - 158;
			tooltipContainer.setStyle('left', x + 'px');
			tooltipContainer.setStyle('display', 'block');
			el.addClass('sideBarClassificheSfondoSelected');
		}
	},
	onHide : function(el) {
		if (el) {
			el.removeClass('sideBarClassificheSfondoSelected');
		};
	}
	});
}
// Mootools extension (not included in the original library) used to upload files via ajax!
// This was modified to execute the onComplete AFTER the response injection in the parent.
// Reproduce this change or the user personal data form won't work!  

/*
Pork.Iframe 1.1

This submits an iframe invisibly to the server, and expects a JSON object in return
handy, so that you do not have to care about posting forms, urlencoding, file uploads etc.
usage: <form method='post' onsubmit='return new iframe(this).request();'>

changelog:
02-04-07: Moo-ified, code cleanup and restructure. Needs .request(). new syntax!
17-01-06: initial release
18-01-06: added options initiator
          added IE5 support
		  added Opera support
01-03-06: added WORKING safari support! Major thanks to Charles Hinshaw and Phil Barrett!
*/

document.iframeLoaders = {};

var iframe = new Class({

	initialize: function(form, options){
		this.form = $(form);
		this.setOptions(this.defaultOptions,options);
		this.options.uniqueId = new Date().getTime();
		document.iframeLoaders[this.options.uniqueId] = this;
		this.transport = this.getTransport();	
	},
	defaultOptions:
	{
		onComplete: null,
		update: null,
		updateMultiple: null,
		uniqueId : null
	},

	request: function() {
		this.form.target= 'frame_'+this.options.uniqueId;
		this.form.setAttribute("target", 'frame_'+this.options.uniqueId); // in case the other one fails.
		this.form.submit();
		return false;
	},

	getResponse: function()
	{
		var response = 'Failed to get response document';
		try { var response = this.transport.contentDocument.document.body.innerHTML; this.transport.contentDocument.document.close(); }	// For NS6
		catch (e){ 
			try{ var response = this.transport.contentWindow.document.body.innerHTML; this.transport.contentWindow.document.close(); } // For IE5.5 and IE6
			 catch (e){
				 try { var response = this.transport.document.body.innerHTML; this.transport.document.body.close(); } // for IE5
					catch (e) {
						try	{ var response = window.frames['frame_'+this.options.uniqueId].document.body.innerText; } // for really nasty browsers
						catch (e) { } // forget it.
				 }
			}
		}
		return response;
	},

	onStateChange: function(){

		this.transport.responseText = this.getResponse();
		if (this.options.update)  setTimeout(function(){ $(this.options.update).innerHTML = this.transport.responseText;}.bind(this), 10);
		if (this.options.updateMultiple){ setTimeout(function(){ // JSON support!
				try	{ var hasscript = false; eval("var inputObject = "+this.transport.responseText);	// we're expecting a JSON object, eval it to inputObject
					for (var i in inputObject) { if (i == 'script') { hasscript = true; } // check if we passed some javascript along too
						else {if ( elm = $(i)) { elm.innerHTML = inputObject[i]; } else { alert("element "+i+" not found!"); } } // if it's not script, update the corresponding div
					} if (hasscript) eval(inputObject['script']); // some on-the-fly-javascript exchanging support too
				} catch (e) {  } // in case of an error					
			}.bind(this), 10);
		};	
		if (this.options.onComplete) setTimeout( function(){this.options.onComplete(this.transport)}.bind(this), 10);
	},

	getTransport: function() 
	{
		var divElm = new Element('DIV', {'styles':	{ "position" : "absolute",	"top":  0,	"marginLeft" : -10000	}});
		if (navigator.userAgent.indexOf('MSIE') > 0 && navigator.userAgent.indexOf('Opera') == -1) { // switch to the crappy solution for IE
            divElm.innerHTML = '<iframe name=\'frame_'+this.options.uniqueId+'\' id=\'frame_'+this.options.uniqueId+'\' src=\'about:blank\' onload=\'setTimeout(function(){document.iframeLoaders['+this.options.uniqueId+'].onStateChange()},20);\'><!--  --></iframe>';
		} else {
			FrameEl = new Element("iframe", {"name": "frame_"+this.options.uniqueId, "id": "frame_"+this.options.uniqueId, "events": { "load" : function(){ this.onStateChange(); }.bind(this) }}).injectInside(divElm);
		}
		divElm.injectInside(document.head);
		divElm.injectInside(document.body);
		return $('frame_'+this.options.uniqueId);
	}
});

iframe.implement(new Options);

/******************************************************************/
/*                        MOOdalBox 1.2.1                         */
/* A modal box (inline popup), used to display remote content     */
/* loaded using AJAX, written for the mootools framework          */
/*         by Razvan Brates, razvan [at] e-magine.ro              */
/******************************************************************/
/*               http://www.e-magine.ro/moodalbox                 */
/******************************************************************/
/*                                                                */
/* MIT style license:                                             */
/* http://en.wikipedia.org/wiki/MIT_License                       */
/*                                                                */
/* mootools found at:                                             */
/* http://mootools.net/                                           */
/*                                                                */
/* Original code based on "Slimbox", by Christophe Beyls:         */
/* http://www.digitalia.be/software/slimbox                       */
/******************************************************************/

// Constants defined here can be changed for easy config / translation
// (defined as vars, because of MSIE's lack of support for const)

var _ERROR_MESSAGE = "Oops.. there was a problem with your request.<br /><br />" +
					"Please try again.<br /><br />" +
					"<em>Click anywhere to close.</em>"; // the error message displayed when the request has a problem
var _RESIZE_DURATION 		= 400; 		// Duration of height and width resizing (ms)
var _INITIAL_WIDTH			= 250;		// Initial width of the box (px)
var _INITIAL_HEIGHT			= 250;		// Initial height of the box (px)
var _CONTENTS_WIDTH 		= 500;		// Actual width of the box (px)
var _CONTENTS_HEIGHT		= 400;		// Actual height of the box (px)
var _DEF_CONTENTS_WIDTH		= 500;		// Default width of the box (px) - used for resetting when a different setting was used
var _DEF_CONTENTS_HEIGHT	= 400;		// Default height of the box (px) - used for resetting when a different setting was used
var _ANIMATE_CAPTION		= true;		// Enable/Disable caption animation
var _EVAL_SCRIPTS			= false;	// Option to evaluate scripts in the response text
var _EVAL_RESPONSE			= false;	// Option to evaluate the whole response text

// The MOOdalBox object in its beauty
var MOOdalBox = {
	
	// init the MOOdalBox
	init: function (options) {
		
		// init default options
		this.options = Object.extend({
			resizeDuration: 	_RESIZE_DURATION,
			initialWidth: 		_INITIAL_WIDTH,	
			initialHeight: 		_INITIAL_HEIGHT,
			contentsWidth: 		_CONTENTS_WIDTH,
			contentsHeight: 	_CONTENTS_HEIGHT,
			defContentsWidth: 	_DEF_CONTENTS_WIDTH,
			defContentsHeight: 	_DEF_CONTENTS_HEIGHT,
			animateCaption: 	_ANIMATE_CAPTION,
			evalScripts: 		_EVAL_SCRIPTS,
			evalResponse: 		_EVAL_RESPONSE
		}, options || {});
		
		// scan anchors for those opening a MOOdalBox
		this.anchors = [];
		$A($$('a')).each(function(el){
			// we use a regexp to check for links that 
			// have a rel attribute starting with "moodalbox"
			if(el.rel && el.href && el.rel.test('^moodalbox', 'i')) {
				el.onclick = this.click.pass(el, this);
				this.anchors.push(el);
			}
		}, this);
		
		// add event listeners
		this.eventKeyDown = this.keyboardListener.bindWithEvent(this);
		this.eventPosition = this.position.bind(this);
		
		// init the HTML elements
		// the overlay (clickable to close)
		this.overlay = new Element('div').setProperty('id', 'mb_overlay').injectInside(document.body);
		// the center element
		this.center = new Element('div').setProperty('id', 'mb_center').setStyles({width: this.options.initialWidth+'px', height: this.options.initialHeight+'px', marginLeft: '-'+(this.options.initialWidth/2)+'px', display: 'none'}).injectInside(document.body);
		// the actual page contents
		this.contents = new Element('div').setProperty('id', 'mb_contents').injectInside(this.center);

		// the bottom part (caption / close)
		this.bottom = new Element('div').setProperty('id', 'mb_bottom').setStyle('display', 'none').injectInside(document.body);
		this.closelink = new Element('a').setProperties({id: 'mb_close_link', href: '#'}).injectInside(this.bottom);
		this.caption = new Element('div').setProperty('id', 'mb_caption').injectInside(this.bottom);
		new Element('div').setStyle('clear', 'both').injectInside(this.bottom);
		
		this.error = new Element('div').setProperty('id', 'mb_error').setHTML(_ERROR_MESSAGE);
		
		// attach the close event to the close button / the overlay
		this.closelink.onclick = this.overlay.onclick = this.close.bind(this);
		
		// init the effects
		var nextEffect = this.nextEffect.bind(this);
		this.fx = {
			overlay: 	this.overlay.effect('opacity', { duration: 500 }).hide(),
			resize: 	this.center.effects({ duration: this.options.resizeDuration, onComplete: nextEffect }),
			contents: 	this.contents.effect('opacity', { duration: 500, onComplete: nextEffect }),
			bottom: 	this.bottom.effects({ duration: 400, onComplete: nextEffect })
		};
		
		this.ajaxRequest = Class.empty;

	},
	
	click: function(link) {
		return this.open (link.href, link.title, link.rel);
	},

	open: function(sLinkHref, sLinkTitle, sLinkRel) {
		this.href = sLinkHref;
		this.title = sLinkTitle;
		this.rel = sLinkRel;
		this.position();
		this.setup(true);
		this.top = Window.getScrollTop() + (Window.getHeight() / 15);
		this.center.setStyles({top: this.top+'px', display: ''});
		this.fx.overlay.custom(0.8);
		return this.loadContents(sLinkHref);
	},

	position: function() {
		this.overlay.setStyles({top: Window.getScrollTop()+'px', height: Window.getHeight()+'px'});
	},

	setup: function(open) {
		var elements = $A($$('object'));
		elements.extend($$(window.ActiveXObject ? 'select' : 'embed'));
		elements.each(function(el){ el.style.visibility = open ? 'hidden' : ''; });
		var fn = open ? 'addEvent' : 'removeEvent';
		window[fn]('scroll', this.eventPosition)[fn]('resize', this.eventPosition);
		document[fn]('keydown', this.eventKeyDown);
		this.step = 0;
	},
	
	loadContents: function() {		
		if(this.step) return false;
		this.step = 1;
		
		// check to see if there are specified dimensions
		// if not, fall back to default values
		var aDim = this.rel.match(/[0-9]+/g);
		this.options.contentsWidth = (aDim && (aDim[0] > 0)) ? aDim[0] : this.options.defContentsWidth;
		this.options.contentsHeight = (aDim && (aDim[1] > 0)) ? aDim[1] : this.options.defContentsHeight;
						
		this.bottom.setStyles({opacity: '0', height: '0px', display: 'none'});
		this.center.className = 'mb_loading';
		
		this.fx.contents.hide();
		
		// AJAX call here
		var nextEffect = this.nextEffect.bind(this);
		var ajaxFailure = this.ajaxFailure.bind(this);
		var ajaxOptions = {
			method: 		'get',
			update: 		this.contents, 
			evalScripts: 	this.options.evalScripts,
			evalResponse: 	this.options.evalResponse,
			onComplete: 	nextEffect, 
			onFailure: 		ajaxFailure
			};
		this.ajaxRequest = new Ajax(this.href, ajaxOptions).request();
				
		return false;
	},
	
	ajaxFailure: function (){
		this.contents.setHTML('');
		this.error.clone().injectInside(this.contents);
		this.nextEffect();
		this.center.setStyle('cursor', 'pointer');
		this.bottom.setStyle('cursor', 'pointer');
		this.center.onclick = this.bottom.onclick = this.close.bind(this);		
	},
	
	nextEffect: function() {
		switch(this.step++) {
		case 1:
			// remove previous styling from the elements 
			// (e.g. styling applied in case of an error)
			this.center.className = '';
			this.center.setStyle('cursor', 'default');
			this.bottom.setStyle('cursor', 'default');
			this.center.onclick = this.bottom.onclick = '';
			this.caption.setHTML(this.title);
			
			this.contents.setStyles ({width: this.options.contentsWidth + "px", height: this.options.contentsHeight + "px"});

			if(this.center.clientHeight != this.contents.offsetHeight) {
				this.fx.resize.custom({height: [this.center.clientHeight, this.contents.offsetHeight]});
				break;
			}
			this.step++;
					
		case 2:
			if(this.center.clientWidth != this.contents.offsetWidth) {
				this.fx.resize.custom({width: [this.center.clientWidth, this.contents.offsetWidth], marginLeft: [-this.center.clientWidth/2, -this.contents.offsetWidth/2]});
				break;
			}
			this.step++;
		
		case 3:
			this.bottom.setStyles({top: (this.top + this.center.clientHeight)+'px', width: this.contents.style.width, marginLeft: this.center.style.marginLeft, display: ''});
			this.fx.contents.custom(0,1);
			break;
		
		case 4:
			if(this.options.animateCaption) {
				this.fx.bottom.custom({opacity: [0, 1], height: [0, this.bottom.scrollHeight]});
				break;
			}
			this.bottom.setStyles({opacity: '1', height: this.bottom.scrollHeight+'px'});

		case 5:
			this.step = 0;
		}
	},
	
	
	keyboardListener: function(event) {
		// close the MOOdalBox when the user presses CTRL + W, CTRL + X, ESC
		if ((event.control && event.key == 'w') || (event.control && event.key == 'x') || (event.key == 'esc')) {
			this.close();
			event.stop();
		}		
	},
	
	close: function() {
		if(this.step < 0) return;
		this.step = -1;
		for(var f in this.fx) this.fx[f].clearTimer();
		this.center.style.display = this.bottom.style.display = 'none';
		this.center.className = 'mb_loading';
		this.fx.overlay.chain(this.setup.pass(false, this)).custom(0);
		return false;
	}
		
};

// startup
Window.onDomReady(MOOdalBox.init.bind(MOOdalBox));
var MOOdalAlert = {
  
  init: function () {
    this.eventKeyDown = this.keyboardListener.bindWithEvent(this);
    this.eventPosition = this.position.bind(this);

    this.overlay = new Element('div', { id: 'mb_overlay', styles: { display: 'none' } }).injectInside(document.body);
    this.center = new Element('div', { id: 'mb_center', styles: { display: 'none', width: '0px', height: '0px' } }).injectInside(document.body);
    this.contents = new Element('div').setProperty('id', 'mb_contents').setStyle('border', '1px').injectInside(this.center);
    
    this.overlay.onclick = this.close.bind(this);
  },
  
  open: function(alertContent) {
    this.position();
    this.setup(true);
    this.top = Window.getScrollTop() + (Window.getHeight() / 5);
    this.center.setStyles({ top: this.top+'px', display: '' });
    this.overlay.setStyles({ opacity: 0.5, display: '' });
    return this.loadContents(alertContent);
  },

  position: function() {
    this.overlay.setStyles({top: Window.getScrollTop()+'px', height: Window.getHeight()+'px'});
  },

  setup: function(open) {
//    var elements = $A($$('object'));
//    elements.extend($$(window.ActiveXObject ? 'select' : 'embed'));
//    elements.each(function(el){ el.style.visibility = open ? 'hidden' : ''; });
    var fn = open ? 'addEvent' : 'removeEvent';
    window[fn]('scroll', this.eventPosition)[fn]('resize', this.eventPosition);
    document[fn]('keydown', this.eventKeyDown);
  },
  
  loadContents: function(alertContent) {
    if (alertContent) {
      this.contents.appendChild(alertContent);
    }
    this.resize();
    return false;
  },
  
  keyboardListener: function(event) {
    // close the MOOdalBox when the user presses CTRL + W, CTRL + X, ESC
    if ((event.control && event.key == 'w') || (event.control && event.key == 'x') || (event.key == 'esc')) {
      this.close();
    }
  },
  
  close: function() {
    this.overlay.setStyles({ opacity: 0, display: 'none' });
    this.center.setStyles({ display: 'none' });
    this.contents.empty();
    return false;
  },
  
  resize: function() {
    var alertContent = this.contents.getLast();
    this.contents.setStyles({width: alertContent.offsetWidth + 'px', height: alertContent.offsetHeight + 'px', opacity: 1});
    this.center.setStyles({width: this.contents.offsetWidth + 'px', height: this.contents.offsetHeight + 'px', marginLeft: '-' + (this.contents.offsetWidth / 2) + 'px'});
  }
    
};

// startup
Window.onDomReady(MOOdalAlert.init.bind(MOOdalAlert));
var AjaxMany = Ajax.extend({

  initialize: function(url, options){
    this.parent(url, options);
    this.addEvent('onComplete', this.clean.bind(this));
  },

  onComplete: function(){
    this.div = new Element('div', { 'styles': { 'display': 'none' } }).injectInside(document.body);
    this.div.setHTML(this.response.text);
    var elements = this.div.getChildren().filter(function(item) { return item.id && item.id.charAt(0) == '_' });
    if (elements.length == 0) {
        // fallback
        if (this.options.update) $(this.options.update).empty().setHTML(this.response.text);
    } else {    	
	    elements.each(function(item) {
        var oldItem = $(item.id.substr(1));        
        if (oldItem) {
          item.id = oldItem.id;
          oldItem.replaceWith(item);
        }
	    });
    }
    if (this.options.evalScripts || this.options.evalResponse) this.evalScripts();
    this.fireEvent('onComplete', [this.response.text, this.response.xml], 20);
  },

  clean: function() {
    if (this.div) {
      this.div.remove();
    }
  }
  
});

/**
 * HistoryManager
 * 
 * Observes back/forward button usage and saves states
 * for registered modules into the hash. This allows to
 * bookmark specific states for an application.
 * 
 * @version   1.0rc2
 * 
 * @see     Events, Options
 * 
 * @license   MIT License
 * @author    Harald Kirschner <mail [at] digitarald.de>
 * @copyright 2007 Author
 */
var HistoryManager = {

  /**
   * Default options - Can be overridden with setOptions
   * 
   * observeDelay: Duration for checking the state, default 100ms
   * stateSeparator: Seperator for module-state join, default ';'
   * iframeSrc: Scr for IE6/7 iframe, must exist on server!
   * onStart: Fires on start
   * onRegister: Fires on register
   * onUnregister: Fires on unregister
   * onUpdate: Fires when state changes from ...
   * onStateChange: ... module changes
   * onObserverChange: ... history change
   */
  options: {
    observeDelay: 100,
    stateSeparator: ';',
    iframeSrc: 'blank.html',
    onStart: Class.empty,
    onRegister: Class.empty,
    onUnregister: Class.empty,
    onStart: Class.empty,
    onUpdate: Class.empty,
    onStateChange: Class.empty,
    onObserverChange: Class.empty
  },

  /**
   * Default options for register
   * 
   * defaults: Default values array, initially empty.
   * regexpParams: When regexp is a String, this is the second argument for new RegExp.
   * skipDefaultMatch: default true; When true onGenerate is not called when current values are similar to the default values.
   */
  dataOptions: {
    skipDefaultMatch: true,
    defaults: [],
    regexpParams: ''
  },

  /**
   * Constructur - Class.initialize
   * 
   * Options:
   *  - observeDelay: duration in ms, default 100 - BackBuddy observe the hash for changes periodical
   *  - stateSeparator: char, default ';' - Separator for multiple module-states in the hash
   *  - iframeSrc: string, default 'blank.html' - File for the iframe (IE6/7), must exist on the server!
   *  - Events: onStart, onRegister, onStart, onUpdate, onStateChange, onObserverChange
   * 
   * @return  this
   * 
   * @param {Object} options
   */
  initialize: function(options) {
    if (this.modules) return this;
    this.setOptions(options);
    this.modules = $H({});
    this.count = history.length;
    this.states = [];
    this.states[this.count] = this.getHash();
    this.state = null;
    return this;
  },

  /**
   * Start - Check hash and start observer
   * 
   * Call start after registering ALL modules. This start the observer,
   * reads the state from the hash and calls onMatch for effected modules.
   * 
   * @return  this
   * 
   */
  start: function() {
    this.observe.periodical(this.options.observeDelay, this);
    this.started = true;
    this.observe();
    this.update();
    this.fireEvent('onStart', [this.state]);
    return this;
  },

  /**
   * Registers a module
   * 
   * @return  {Object} Object with shortcuts for setValues, setValue, generate and unregister
   * 
   * @param {String} Module key
   * @param {RegExp}/{String} Regular expression that matches the string updated from onGenerate
   * @param {Function} Will be called when the regexp matches, with the new values as argument.
   * @param {Function} Should return the string for the state string, values are first argument
   * @param {Array} default values, the input values given to onMatch and onGenerate will be complemented with these
   * @param {Object} (optional) options
   */
  register: function(key, defaults, onMatch, onGenerate, regexp, options) {
    if (!this.modules) this.initialize();
    var data = $merge(this.dataOptions, options || {}, {
      defaults: defaults,
      onMatch: onMatch,
      onGenerate: onGenerate,
      regexp: regexp
    });
    data.regexp = data.regexp || key + '-([\\w_-]*)';
    if (typeof data.regexp == 'string') data.regexp = new RegExp(data.regexp, data.regexpParams);
    data.onGenerate = data.onGenerate || function(values) { return key + '-' + values[0]; };

    data.values = data.defaults.copy();
    this.modules.set(key, data);
    this.fireEvent('onUnregister', [key, data]);
    return {
      setValues: function(values) {
        return this.setValues(key, values);
      }.bind(this),
      setValue: function(index, value) {
        return this.setValue(key, index, value);
      }.bind(this),
      generate: function(values) {
        return this.generate(key, values);
      }.bind(this),
      unregister: function() {
        return this.unregister(key);
      }.bind(this)
    };
  },

  /**
   * unregister - Removes an module from the
   * 
   * @param {String} Module key
   */
  unregister: function(key) {
    this.fireEvent('onRegister', [key]);
    this.modules.remove(key);
  },

  /**
   * setValues - Set all values new, updates new state
   * 
   * @param {String} Module key
   * @param {Object} Complete values
   */
  setValues: function(key, values) {
    var data = this.modules.get(key);
    // dschivo-20071123
    // if (!data || data.values.isSimilar(values)) return this;
    if (!data || data.values.isSimilar(values.complement(data.defaults))) return this;
    data.values = values;
    this.update();
    return this;
  },

  /**
   * setValue - Set one value, updates new state
   * 
   * @param {String} Module key
   * @param {Number} Value index
   * @param {Object} Value
   */
  setValue: function(key, index, value) {
    var data = this.modules.get(key);
    if (!data || data.values[index] == value) return this;
    data.values[index] = value;
    this.update();
    return this;
  },

  /**
   * generate - Generates a hash from the given
   * 
   * @param {String} Module key
   * @param {Number} Value index
   * @param {Object} Value
   */
  generate: function(key, values) {
    var data = this.modules.get(key);
    var current = data.values.copy();
    data.values = values;
    var state = this.generateState();
    data.values = current;
    return '#' + state;
  },

  observe: function() {
    if (this.timeout) return;
    var state = this.getState();
    if (this.state == state) return;
    if ((window.ie || window.webkit419) && (this.state !== null)) this.setState(state, true);
    else this.state = state;
    this.modules.each(function(data, key) {
      var bits = state.match(data.regexp);
      if (bits) {
        bits.splice(0, 1);
        bits.complement(data.defaults);
        if (!bits.isSimilar(data.defaults)) data.values = bits;
      } else data.values = data.defaults.copy();
      data.onMatch(data.values, data.defaults);
    });
    this.fireEvent('onStateChange', [state]).fireEvent('onObserverChange', [state]);
  },

  generateState: function() {
    var state = [];
    this.modules.each(function(data, key) {
      if (data.skipDefaultMatch && data.values.isSimilar(data.defaults)) return;
      state.push(data.onGenerate(data.values));
    });
    return state.join(this.options.stateSeparator);
  },

  update: function() {
    if (!this.started) return this;
    var state = this.generateState();
    if ((!this.state && !state) || (this.state == state)) return this;
    this.setState(state);
    this.fireEvent('onStateChange', [state]).fireEvent('onUpdate', [state]);
    return this;
  },

  observeTimeout: function() {
    if (this.timeout) this.timeout = $clear(this.timeout);
    else this.timeout = this.observeTimeout.delay(200, this);
  },

  getHash: function() {
    var href = top.location.href;
    var pos = href.indexOf('#') + 1;
    return (pos) ? href.substr(pos) : '';
  },

  getState: function() {
    var state = this.getHash();
    if (this.iframe) {
      var doc = this.iframe.contentWindow.document;
      if (doc && doc.body.id == 'state') {
        var istate = doc.body.innerText;
        if (this.state == state) return istate;
        this.istateOld = true;
      } else return this.istate;
    }
    if (window.webkit419 && history.length != this.count) {
      this.count = history.length;
      return $pick(this.states[this.count - 1], state);
    }
    return state;
  },

  setState: function(state, fix) {
    state = $pick(state, '');
    var empty = (state === '') ? [window.getScrollLeft(), window.getScrollTop()] : false;
    if (window.webkit419) {
      if (!this.form) this.form = new Element('form', {method: 'get'}).injectInside(document.body);
      this.count = history.length;
      this.states[this.count] = state;
      this.observeTimeout();
      this.form.setProperty('action', '#' + state).submit();
    } else top.location.hash = empty ? '#' : state;
    if (empty) window.scrollTo(empty[0], empty[1]);
    if (window.ie && (!fix || this.istateOld)) {
      if (!this.iframe) {
        this.iframe = new Element('iframe', {
          src: this.options.iframeSrc,
          styles: 'visibility: hidden;'
        }).injectInside(document.body);
        this.istate = this.state;
      }
      try {
        var doc = this.iframe.contentWindow.document;
        doc.open();
        doc.write('<html><body id="state">' + state + '</body></html>');
        doc.close();
        this.istateOld = false;
      } catch(e) {};
    }

    this.state = state;
  },

  extend: $extend
};

HistoryManager.extend(Events.prototype);
HistoryManager.extend(Options.prototype);


/**
 * Extends Array with 2 helpers: isSimilar(array) and complement(array)
 * 
 */
Array.extend({

  /**
   * isSimilar - Returns true for similar arrays, type-insensitive
   * 
   * @example
   *  [1].isSimilar(['1']) == true
   *  [1, 2].isSimilar([1, false]) == false
   *  
   * @return  {Boolean}
   * @param {Object} Array
   */
  isSimilar: function(array) {
    return (this.toString() == array.toString());
  },

  /**
   * complement - Fills up empty array values from another array, length is the same
   * 
   * @example
   *  [1, null].complement([3, 4]) == [1, 4]
   *  [undefined, '1'].complement([2, 3, 4]) == [2, '1']

   * @return  {Array} this
   * @param {Object} Array
   */
  complement: function(array) {
    for (var i = 0, j = this.length; i < j; i++) this[i] = $pick(this[i], array[i] || null);
    return this;
  }
});

var FcomAccordion = Fx.Elements.extend({

  options: {
    onActive: Class.empty,
    onBackground: Class.empty,
    display: 0,
    show: false,
    height: true,
    width: false,
    opacity: true,
    fixedHeight: false,
    fixedWidth: false,
    wait: false,
    alwaysHide: false
  },

  initialize: function(){
    var options, togglers, elements, container;
    $each(arguments, function(argument, i){
      switch($type(argument)){
        case 'object': options = argument; break;
        case 'element': container = $(argument); break;
        default:
          var temp = $$(argument);
          if (!togglers) togglers = temp;
          else elements = temp;
      }
    });
    this.togglers = togglers || [];
    this.names = togglers.map(function(t) { return t.getElement('a').name; });
    this.elements = elements || [];
    this.container = $(container);
    this.setOptions(options);
    this.previous = -1;
    if (this.options.alwaysHide) this.options.wait = true;
    if ($chk(this.options.show)){
      this.options.display = false;
      this.previous = this.options.show;
    }
    if (this.options.start){
      this.options.display = false;
      this.options.show = false;
    }
    this.effects = {};
    if (this.options.opacity) this.effects.opacity = 'fullOpacity';
    if (this.options.width) this.effects.width = this.options.fixedWidth ? 'fullWidth' : 'offsetWidth';
    if (this.options.height) this.effects.height = this.options.fixedHeight ? 'fullHeight' : 'scrollHeight';
    for (var i = 0, l = this.togglers.length; i < l; i++) this.addSection(this.togglers[i], this.elements[i]);
    this.elements.each(function(el, i){
      if (this.options.show === i){
        this.fireEvent('onActive', [this.togglers[i], el]);
      } else {
        for (var fx in this.effects) el.setStyle(fx, 0);
      }
    }, this);
    this.parent(this.elements);
    if ($chk(this.options.display)) this.display(this.options.display);
  },

  /*
  Property: addSection
    Dynamically adds a new section into the accordion at the specified position.

  Arguments:
    toggler - (dom element) the element that toggles the accordion section open.
    element - (dom element) the element that stretches open when the toggler is clicked.
    pos - (integer) the index where these objects are to be inserted within the accordion.
  */

  addSection: function(toggler, element, pos){
    toggler = $(toggler);
    element = $(element);
    var index = this.names.indexOf(toggler.getElement('a').name);
    var test = index != -1; // this.togglers.contains(toggler);
    if (test) {
      this.togglers[index] = toggler;
      this.elements[index] = element;
    }
    var len = this.togglers.length;
    this.togglers.include(toggler);
    this.elements.include(element);
    if (len && (!test || pos)){
      pos = $pick(pos, len - 1);
      toggler.injectBefore(this.togglers[pos]);
      element.injectAfter(toggler);
    } else if (this.container && !test){
      toggler.inject(this.container);
      element.inject(this.container);
    }
    var idx = this.togglers.indexOf(toggler);
    toggler.addEvent('click', this.display.bind(this, idx));
    if (this.options.height) element.setStyles({'padding-top': 0, 'border-top': 'none', 'padding-bottom': 0, 'border-bottom': 'none'});
    if (this.options.width) element.setStyles({'padding-left': 0, 'border-left': 'none', 'padding-right': 0, 'border-right': 'none'});
    element.fullOpacity = 1;
    if (this.options.fixedWidth) element.fullWidth = this.options.fixedWidth;
    if (this.options.fixedHeight) element.fullHeight = this.options.fixedHeight;
    element.setStyle('overflow', 'hidden');
    if (!test){
      for (var fx in this.effects) element.setStyle(fx, 0);
    }
    return this;
  },

  /*
  Property: display
    Shows a specific section and hides all others. Useful when triggering an accordion from outside.

  Arguments:
    index - integer, the index of the item to show, or the actual element to show.
  */

  display: function(index){
    jslog.debug('>> display ' + index);
    index = ($type(index) == 'element') ? this.elements.indexOf(index) : index;
    if ((this.timer && this.options.wait) || (index === this.previous && !this.options.alwaysHide)) return this;
    this.previous = index;
    var obj = {};
    this.elements.each(function(el, i){
      obj[i] = {};
      var hide = (i != index) || (this.options.alwaysHide && (el.offsetHeight > 0));
      this.fireEvent(hide ? 'onBackground' : 'onActive', [this.togglers[i], el]);
      for (var fx in this.effects) obj[i][fx] = hide ? 0 : el[this.effects[fx]];
    }, this);
    return this.start(obj);
  },

  showThisHideOpen: function(index){return this.display(index);}

});

Fx.FcomAccordion = FcomAccordion;
/**
 * @file elSelect.js
 * @downloaded from http://www.cult-f.net/2007/12/14/elselect/
 * @author Sergey Korzhov aka elPas0
 * @site http://www.cult-f.net
 * @date December 14, 2007
 */
var elSelect = new Class( {
  options : {
    container :false,
    baseClass :'elSelect'
  },
  source :false,
  selected :false,
  _select :false,
  current :false,
  selectedOption :false,
  dropDown :false,
  optionsContainer :false,
  hiddenInput :false,
  /*
   * pass the options, create html and inject into container
   */
  initialize : function(options) {
    this.setOptions(options);

    if (!$(this.options.container))
      return;

    this.selected = false;
    this.source = $(this.options.container).getElement('select');
    if ($(this.source)) {
      this.buildFrameWork();

      $(this.source).getElements('option').each(this.addOption, this);
      $(this.options.container).setHTML('');
      this._select.injectInside($(this.options.container));

      this.bindEvents();
    }
  },

  buildFrameWork : function() {
    this._select = new Element('div').addClass(this.options.baseClass);
    this.current = new Element('div').addClass('selected').injectInside(
        $(this._select));
    this.selectedOption = new Element('div').addClass('selectedOption')
        .injectInside($(this.current));
    this.dropDown = new Element('div').addClass('dropDown').injectInside(
        $(this.current));
    new Element('div').addClass('clear').injectInside($(this._select));
    this.optionsContainer = new Element('div').addClass('optionsContainer')
        .injectInside($(this._select));
    var t = new Element('div').addClass('optionsContainerTop')
        .injectInside($(this.optionsContainer));
    var o = new Element('div').injectInside($(t));
    var p = new Element('div').injectInside($(o));
    var t = new Element('div').addClass('optionsContainerBottom')
        .injectInside($(this.optionsContainer));
    var o = new Element('div').injectInside($(t));
    var p = new Element('div').injectInside($(o));

    this.hiddenInput = new Element('input').setProperties( {
      type :'hidden',
      name :$(this.source).getProperty('name')
    }).injectInside($(this.optionsContainer));
  },

  bindEvents : function() {
    document.addEvent('click', function() {
      if (this.optionsContainer.getStyle('display') == 'block') {
        this.onDropDown();
      }
    }.bind(this));

    $(this.options.container).addEvent('click', function(e) {
      new Event(e).stop();
    });
    this.current.addEvent('click', this.onDropDown.bindWithEvent(this));

  },

  // add single option to select
  addOption : function(option) {
    var o = new Element('div').addClass('option').setProperty('value',
        option.value);

    o.addEvents( {
      'click' :this.onOptionClick.bindWithEvent(this),
      'mouseout' :this.onOptionMouseout.bindWithEvent(this),
      'mouseover' :this.onOptionMouseover.bindWithEvent(this)
    });

    if ($defined(option.getProperty('class'))
        && $chk(option.getProperty('class'))) {
      o.addClass(option.getProperty('class'));
    }

    if (option.selected) {
      if (this.selected)
        this.selected.removeClass('selected');
      this.selected = o;
      o.addClass('selected');
      this.selectedOption.setText(option.text);
      this.hiddenInput.setProperty('value', option.value);
    }
    o.setText(option.text);
    o.injectBefore($(this.optionsContainer).getLast());
  },

  onDropDown : function(e) {

    if (this.optionsContainer.getStyle('display') == 'block') {
      this.optionsContainer.setStyle('display', 'none');
    } else {
      this.optionsContainer.setStyle('display', 'block');
      this.selected.addClass('selected');
      // needed to fix min-width in ie6
     var width = "145px"
      /*this.optionsContainer.getStyle('width').toInt() > this._select
      .getStyle('width').toInt() ? this.optionsContainer
      .getStyle('width') : this._select.getStyle('width')*/;

  this.optionsContainer.setStyle('width', width);
  this.optionsContainer.getFirst().setStyle('width', width);
  this.optionsContainer.getLast().setStyle('width', width);
}

},
onOptionClick : function(e) {
var event = new Event(e);
if (this.selected != event.target) {
  this.selected.removeClass('selected');
  event.target.addClass('selected');
  this.selected = event.target;
  this.selectedOption.setText(this.selected.getText());
  this.hiddenInput.setProperty('value', this.selected.getProperty('value'));
}
this.onDropDown();
},
onOptionMouseover : function(e) {
var event = new Event(e);
this.selected.removeClass('selected');
event.target.addClass('selected');
},
onOptionMouseout : function(e) {
var event = new Event(e);
event.target.removeClass('selected');
}

});
elSelect.implement(new Events);
elSelect.implement(new Options);


/*!
  reflection.js for mootools v1.3
  (c) 2006-2008 Christophe Beyls <http://www.digitalia.be>
  MIT-style license.
*/

Element.extend( {
  reflect : function(options) {
    var img = this;
    if (img.getTag() != "img")
      return;

    options = $extend( {     
      height :0.33,
      opacity :0.5
    }, options || {});

    function doReflect() {
      img.unreflect();
      var reflection, reflectionHeight = Math.floor(img.height * options.height), wrapper, context, gradient;

      if (window.ie) {
        reflection = new Element("img", {
          src :img.src,
          styles : {
            //width: img.width,
            //height: img.height,
            marginBottom :-img.height + reflectionHeight,
            filter :"flipv progid:DXImageTransform.Microsoft.Alpha(opacity=" + (options.opacity * 100)
                + ", style=1, finishOpacity=0, startx=0, starty=0, finishx=0, finishy=" + (options.height * 100) + ")"
          }
        });
      } else {
        reflection = new Element("canvas");
        if (!reflection.getContext)
          return;
      }
      reflection.setStyles( {
        display :"block",
        border :0
      });

      reflection.className = 'imgReflection'; 
    	  
      wrapper = new Element(($(img.parentNode).getTag() == "a") ? "span" : "div").injectAfter(img).adopt(img,
          reflection);
      wrapper.className = img.className;
      wrapper.style.cssText = img.reflected = img.style.cssText;
      //wrapper.setStyles({width: '100%', height: img.height + reflectionHeight, overflow: "hidden"});
      img.style.cssText = "display: block; border: 0px";
      img.className = "reflected";
      if (!window.ie) {
        context = reflection.setProperties( {
          width :img.width,
          height :reflectionHeight
        }).getContext("2d");
        context.save();
        context.translate(0, img.height - 1);
        context.scale(1, -1);
        context.drawImage(img, 0, 0, img.width, img.height);
        context.restore();
        context.globalCompositeOperation = "destination-out";

        gradient = context.createLinearGradient(0, 0, 0, reflectionHeight);
        gradient.addColorStop(0, "rgba(255, 255, 255, " + (1 - options.opacity) + ")");
        gradient.addColorStop(1, "rgba(255, 255, 255, 1.0)");
        context.fillStyle = gradient;
        context.rect(0, 0, img.width, reflectionHeight);
        context.fill();
      }
    }

    if (img.complete) {
      img.unreflect();
      doReflect();
    } else
      img.onload = doReflect;

    return img;
  },

  unreflect : function() {
    var img = this, wrapper;
    img.onload = Class.empty;

    if (img.reflected !== undefined) {
      wrapper = img.parentNode;
      img.className = wrapper.className;
      img.style.cssText = img.reflected;
      img.reflected = undefined;
      wrapper.parentNode.replaceChild(img, wrapper);
    }

    return img;
  }
});

Elements.extend( {
  reflect : function(options) {
    this.forEach( function(el) {
      el.reflect(options);
    });
  },

  unreflect : function() {
    this.forEach( function(el) {
      el.unreflect();
    });
  }
});

/**
 * Adds reflection effects to all the imgs in page that have class reflect.
 * 
 * @return nothing.
 */
function addImageReflections(/*String|DomElement|Element?*/container) {	
  var list;
  if (container) {
    list = $(container).getElements('img.reflect');
  } else {
    list = $$('img.reflect');
  }
  if (list) {
    list.each( function(img) {
      img.unreflect(); // Useful when reattaching events in javascript.
        img.reflect();
      });
  }  
}

Window.addEvent("domready", function() {
  addImageReflections();
});

function getFormElement(frm, name)
    {

      var elementsLength = frm.elements.length;

      var objElement = null;

      for(j = 0; j < elementsLength; j++)
      {

        if (frm.elements[j].name == name)
        {
          objElement = frm.elements[j];

          break;
        }
      }
      return objElement
    }

function getChecked(form, name)
    {
      var elementsLength = form.elements.length;
      var bChecked = false;

      for(k = 0; k < elementsLength; k++)
      {
        if (form.elements[k].checked)
        {
          bChecked= true;
        }
      }
      return bChecked;
    }

  function checkMandatories(form,alertText)
  {
  var elementsLength = form.elements.length;
  var result=true;
    for(i = 0; i < elementsLength; i++)
    {
         if (form.elements[i].type == "hidden")
        {

         var elementName = form.elements[i].value;
         var obj = getFormElement(form, elementName);

          if (obj!=null)
          {

               switch(obj.type)
            {

              case "select-one":
                if (obj.options.selectedIndex== 0)
                {
                   result = false;

                }
              break;

              case "checkbox":
                var bChecked;
                bChecked=getChecked(frm, elementName);
                if (bChecked==false)
                {
                    result = false;

                }
                break;
              case "radio":

                var bChecked;
                bChecked=getChecked(frm, elementName);

                if (bChecked==false)
                {

                    result = false;

                }
                break;

              case "text":
              if (obj.value == "")
                {
                   result = false;

                }
              break;

              case "file":
              if (obj.value == "")
                {
                   result = false;

                }
              break;


              }
          }
        }
        else
        {
        	if ((form.elements[i].className.indexOf("validemail")>=0) && (form.elements[i].type == "text"))
            {                       	
            	var filter = /^(("[\w-\s]+")|([\w-]+(?:\.[\w-]+)*)|("[\w-\s]+")([\w-]+(?:\.[\w-]+)*))(@((?:[\w-]+\.)*\w[\w-]{0,66})\.([a-z]{2,6}(?:\.[a-z]{2})?)$)|(@\[?((25[0-5]\.|2[0-4][0-9]\.|1[0-9]{2}\.|[0-9]{1,2}\.))((25[0-5]|2[0-4][0-9]|1[0-9]{2}|[0-9]{1,2})\.){2}(25[0-5]|2[0-4][0-9]|1[0-9]{2}|[0-9]{1,2})\]?$)/i;
                if(!(filter.test(form.elements[i].value)))
                {
                  alertText = 'Uno o pi&ugrave; campi email non sono compilati correttamente.'                  
                  openAlertMOOdalBox(alertText);
     			  return false;
                }              
            }  
            if (form.elements[i].className.indexOf("obbligatorio")>=0)
            {

              if(form.elements[i].type == "text")
              {
                if(form.elements[i].value == "")
                {
                  result = false;
                }
              }
              else if(form.elements[i].type == "checkbox")
              {
                var bChecked2;
                var elementName2 = form.elements[i].value;
                bChecked2=getChecked(form, elementName2);
                if (bChecked2==false)
                {

                    result = false;
                }
              }
              else if(form.elements[i].type == "radio")
              {
              	
                if (!(form.elements[i].checked))
                {
                		
	               result = false;
                }
              }
              else{

                if(form.elements[i].value == "")
                {
                  result = false;
                }

              }
            }
        }

    }
    if (result==false)
    {
     openAlertMOOdalBox(alertText);
     return false;
    }
    else{
      return true;
    }
  }
function makeCorpo(form){
      var elementsLength = form.elements.length;
      for(i = 0; i < elementsLength; i++)
    {
      if (form.elements[i].className.indexOf("corpo")>=0)
      {
          if (form.elements[i].className.indexOf("checkbox")>=0)
          {
            if (form.elements[i].checked)
            {
            form.corpo.value = form.corpo.value + "\n " + form.elements[i].name + "=" + form.elements[i].value + ";\n";
            }
          }
          else if (form.elements[i].className.indexOf("radio")>=0)
          {
            if (form.elements[i].checked)
            {
            form.corpo.value = form.corpo.value + "\n " + form.elements[i].name + "=" + form.elements[i].value + ";\n";
            }
          }
          else{form.corpo.value = form.corpo.value + "\n " + form.elements[i].name + "=" + form.elements[i].value + ";\n";}
      }
    }
    return false;
}

/*
 * Copyright 2005 Joe Walker
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

/**
 * Declare an object to which we can add real functions.
 */
if (dwr == null) var dwr = {};
if (dwr.engine == null) dwr.engine = {};
if (DWREngine == null) var DWREngine = dwr.engine;

/**
 * Set an alternative error handler from the default alert box.
 * @see getahead.org/dwr/browser/engine/errors
 */
dwr.engine.setErrorHandler = function(handler) {
  dwr.engine._errorHandler = handler;
};

/**
 * Set an alternative warning handler from the default alert box.
 * @see getahead.org/dwr/browser/engine/errors
 */
dwr.engine.setWarningHandler = function(handler) {
  dwr.engine._warningHandler = handler;
};

/**
 * Setter for the text/html handler - what happens if a DWR request gets an HTML
 * reply rather than the expected Javascript. Often due to login timeout
 */
dwr.engine.setTextHtmlHandler = function(handler) {
  dwr.engine._textHtmlHandler = handler;
}

/**
 * Set a default timeout value for all calls. 0 (the default) turns timeouts off.
 * @see getahead.org/dwr/browser/engine/errors
 */
dwr.engine.setTimeout = function(timeout) {
  dwr.engine._timeout = timeout;
};

/**
 * The Pre-Hook is called before any DWR remoting is done.
 * @see getahead.org/dwr/browser/engine/hooks
 */
dwr.engine.setPreHook = function(handler) {
  dwr.engine._preHook = handler;
};

/**
 * The Post-Hook is called after any DWR remoting is done.
 * @see getahead.org/dwr/browser/engine/hooks
 */
dwr.engine.setPostHook = function(handler) {
  dwr.engine._postHook = handler;
};

/**
 * Custom headers for all DWR calls
 * @see getahead.org/dwr/????
 */
dwr.engine.setHeaders = function(headers) {
  dwr.engine._headers = headers;
};

/**
 * Custom parameters for all DWR calls
 * @see getahead.org/dwr/????
 */
dwr.engine.setParameters = function(parameters) {
  dwr.engine._parameters = parameters;
};

/** XHR remoting type constant. See dwr.engine.set[Rpc|Poll]Type() */
dwr.engine.XMLHttpRequest = 1;

/** XHR remoting type constant. See dwr.engine.set[Rpc|Poll]Type() */
dwr.engine.IFrame = 2;

/** XHR remoting type constant. See dwr.engine.setRpcType() */
dwr.engine.ScriptTag = 3;

/**
 * Set the preferred remoting type.
 * @param newType One of dwr.engine.XMLHttpRequest or dwr.engine.IFrame or dwr.engine.ScriptTag
 * @see getahead.org/dwr/browser/engine/options
 */
dwr.engine.setRpcType = function(newType) {
  if (newType != dwr.engine.XMLHttpRequest && newType != dwr.engine.IFrame && newType != dwr.engine.ScriptTag) {
    dwr.engine._handleError(null, { name:"dwr.engine.invalidRpcType", message:"RpcType must be one of dwr.engine.XMLHttpRequest or dwr.engine.IFrame or dwr.engine.ScriptTag" });
    return;
  }
  dwr.engine._rpcType = newType;
};

/**
 * Which HTTP method do we use to send results? Must be one of "GET" or "POST".
 * @see getahead.org/dwr/browser/engine/options
 */
dwr.engine.setHttpMethod = function(httpMethod) {
  if (httpMethod != "GET" && httpMethod != "POST") {
    dwr.engine._handleError(null, { name:"dwr.engine.invalidHttpMethod", message:"Remoting method must be one of GET or POST" });
    return;
  }
  dwr.engine._httpMethod = httpMethod;
};

/**
 * Ensure that remote calls happen in the order in which they were sent? (Default: false)
 * @see getahead.org/dwr/browser/engine/ordering
 */
dwr.engine.setOrdered = function(ordered) {
  dwr.engine._ordered = ordered;
};

/**
 * Do we ask the XHR object to be asynchronous? (Default: true)
 * @see getahead.org/dwr/browser/engine/options
 */
dwr.engine.setAsync = function(async) {
  dwr.engine._async = async;
};

/**
 * Does DWR poll the server for updates? (Default: false)
 * @see getahead.org/dwr/browser/engine/options
 */
dwr.engine.setActiveReverseAjax = function(activeReverseAjax) {
  if (activeReverseAjax) {
    // Bail if we are already started
    if (dwr.engine._activeReverseAjax) return;
    dwr.engine._activeReverseAjax = true;
    dwr.engine._poll();
  }
  else {
    // Can we cancel an existing request?
    if (dwr.engine._activeReverseAjax && dwr.engine._pollReq) dwr.engine._pollReq.abort();
    dwr.engine._activeReverseAjax = false;
  }
  // TODO: in iframe mode, if we start, stop, start then the second start may
  // well kick off a second iframe while the first is still about to return
  // we should cope with this but we don't
};

/**
 * Set the preferred polling type.
 * @param newPollType One of dwr.engine.XMLHttpRequest or dwr.engine.IFrame
 * @see getahead.org/dwr/browser/engine/options
 */
dwr.engine.setPollType = function(newPollType) {
  if (newPollType != dwr.engine.XMLHttpRequest && newPollType != dwr.engine.IFrame) {
    dwr.engine._handleError(null, { name:"dwr.engine.invalidPollType", message:"PollType must be one of dwr.engine.XMLHttpRequest or dwr.engine.IFrame"  });
    return;
  }
  dwr.engine._pollType = newPollType;
};

/**
 * The default message handler.
 * @see getahead.org/dwr/browser/engine/errors
 */
dwr.engine.defaultErrorHandler = function(message, ex) {
  dwr.engine._debug("Error: " + ex.name + ", " + ex.message, true);

  if (message == null || message == "") alert("A server error has occured. More information may be available in the console.");
  // Ignore NS_ERROR_NOT_AVAILABLE if Mozilla is being narky
  else if (message.indexOf("0x80040111") != -1) dwr.engine._debug(message);
  else alert(message);
};

/**
 * The default warning handler.
 * @see getahead.org/dwr/browser/engine/errors
 */
dwr.engine.defaultWarningHandler = function(message, ex) {
  dwr.engine._debug(message);
};

/**
 * For reduced latency you can group several remote calls together using a batch.
 * @see getahead.org/dwr/browser/engine/batch
 */
dwr.engine.beginBatch = function() {
  if (dwr.engine._batch) {
    dwr.engine._handleError(null, { name:"dwr.engine.batchBegun", message:"Batch already begun" });
    return;
  }
  dwr.engine._batch = dwr.engine._createBatch();
};

/**
 * Finished grouping a set of remote calls together. Go and execute them all.
 * @see getahead.org/dwr/browser/engine/batch
 */
dwr.engine.endBatch = function(options) {
  var batch = dwr.engine._batch;
  if (batch == null) {
    dwr.engine._handleError(null, { name:"dwr.engine.batchNotBegun", message:"No batch in progress" });
    return;
  }
  dwr.engine._batch = null;
  if (batch.map.callCount == 0) return;

  // The hooks need to be merged carefully to preserve ordering
  if (options) dwr.engine._mergeBatch(batch, options);

  // In ordered mode, we don't send unless the list of sent items is empty
  if (dwr.engine._ordered && dwr.engine._batchesLength != 0) {
    dwr.engine._batchQueue[dwr.engine._batchQueue.length] = batch;
  }
  else {
    dwr.engine._sendData(batch);
  }
};

/** @deprecated */
dwr.engine.setPollMethod = function(type) { dwr.engine.setPollType(type); };
dwr.engine.setMethod = function(type) { dwr.engine.setRpcType(type); };
dwr.engine.setVerb = function(verb) { dwr.engine.setHttpMethod(verb); };

//==============================================================================
// Only private stuff below here
//==============================================================================

/** The original page id sent from the server */
dwr.engine._origScriptSessionId = "FAC36FE792A41A8261B68B1CA849E23B";

/** The session cookie name */
dwr.engine._sessionCookieName = "JSESSIONID"; // JSESSIONID

/** Is GET enabled for the benefit of Safari? */
dwr.engine._allowGetForSafariButMakeForgeryEasier = "false";

/** The script prefix to strip in the case of scriptTagProtection. */
dwr.engine._scriptTagProtection = "throw 'allowScriptTagRemoting is false.';";

/** The default path to the DWR servlet */
dwr.engine._defaultPath = "/fcom-product-webapp/dwr";

/** The read page id that we calculate */
dwr.engine._scriptSessionId = null;

/** The function that we use to fetch/calculate a session id */
dwr.engine._getScriptSessionId = function() {
  if (dwr.engine._scriptSessionId == null) {
    dwr.engine._scriptSessionId = dwr.engine._origScriptSessionId + Math.floor(Math.random() * 1000);
  }
  return dwr.engine._scriptSessionId;
};

/** A function to call if something fails. */
dwr.engine._errorHandler = dwr.engine.defaultErrorHandler;

/** For debugging when something unexplained happens. */
dwr.engine._warningHandler = dwr.engine.defaultWarningHandler;

/** A function to be called before requests are marshalled. Can be null. */
dwr.engine._preHook = null;

/** A function to be called after replies are received. Can be null. */
dwr.engine._postHook = null;

/** An map of the batches that we have sent and are awaiting a reply on. */
dwr.engine._batches = {};

/** A count of the number of outstanding batches. Should be == to _batches.length unless prototype has messed things up */
dwr.engine._batchesLength = 0;

/** In ordered mode, the array of batches waiting to be sent */
dwr.engine._batchQueue = [];

/** What is the default rpc type */
dwr.engine._rpcType = dwr.engine.XMLHttpRequest;

/** What is the default remoting method (ie GET or POST) */
dwr.engine._httpMethod = "POST";

/** Do we attempt to ensure that calls happen in the order in which they were sent? */
dwr.engine._ordered = false;

/** Do we make the calls async? */
dwr.engine._async = true;

/** The current batch (if we are in batch mode) */
dwr.engine._batch = null;

/** The global timeout */
dwr.engine._timeout = 0;

/** ActiveX objects to use when we want to convert an xml string into a DOM object. */
dwr.engine._DOMDocument = ["Msxml2.DOMDocument.6.0", "Msxml2.DOMDocument.5.0", "Msxml2.DOMDocument.4.0", "Msxml2.DOMDocument.3.0", "MSXML2.DOMDocument", "MSXML.DOMDocument", "Microsoft.XMLDOM"];

/** The ActiveX objects to use when we want to do an XMLHttpRequest call. */
dwr.engine._XMLHTTP = ["Msxml2.XMLHTTP.6.0", "Msxml2.XMLHTTP.5.0", "Msxml2.XMLHTTP.4.0", "MSXML2.XMLHTTP.3.0", "MSXML2.XMLHTTP", "Microsoft.XMLHTTP"];

/** Are we doing comet or polling? */
dwr.engine._activeReverseAjax = false;

/** What is the default polling type */
dwr.engine._pollType = dwr.engine.XMLHttpRequest;
//dwr.engine._pollType = dwr.engine.IFrame;

/** The iframe that we are using to poll */
dwr.engine._outstandingIFrames = [];

/** The xhr object that we are using to poll */
dwr.engine._pollReq = null;

/** How many milliseconds between internal comet polls */
dwr.engine._pollCometInterval = 200;

/** How many times have we re-tried to poll? */
dwr.engine._pollRetries = 0;
dwr.engine._maxPollRetries = 0;

/** Do we do a document.reload if we get a text/html reply? */
dwr.engine._textHtmlHandler = null;

/** If you wish to send custom headers with every request */
dwr.engine._headers = null;

/** If you wish to send extra custom request parameters with each request */
dwr.engine._parameters = null;

/** Undocumented interceptors - do not use */
dwr.engine._postSeperator = "\n";
dwr.engine._defaultInterceptor = function(data) {return data;}
dwr.engine._urlRewriteHandler = dwr.engine._defaultInterceptor;
dwr.engine._contentRewriteHandler = dwr.engine._defaultInterceptor;
dwr.engine._replyRewriteHandler = dwr.engine._defaultInterceptor;

/** Batch ids allow us to know which batch the server is answering */
dwr.engine._nextBatchId = 0;

/** A list of the properties that need merging from calls to a batch */
dwr.engine._propnames = [ "rpcType", "httpMethod", "async", "timeout", "errorHandler", "warningHandler", "textHtmlHandler" ];

/** Do we stream, or can be hacked to do so? */
dwr.engine._partialResponseNo = 0;
dwr.engine._partialResponseYes = 1;
dwr.engine._partialResponseFlush = 2;

/**
 * @private Send a request. Called by the Javascript interface stub
 * @param path part of URL after the host and before the exec bit without leading or trailing /s
 * @param scriptName The class to execute
 * @param methodName The method on said class to execute
 * @param func The callback function to which any returned data should be passed
 *       if this is null, any returned data will be ignored
 * @param vararg_params The parameters to pass to the above class
 */
dwr.engine._execute = function(path, scriptName, methodName, vararg_params) {
  var singleShot = false;
  if (dwr.engine._batch == null) {
    dwr.engine.beginBatch();
    singleShot = true;
  }
  var batch = dwr.engine._batch;
  // To make them easy to manipulate we copy the arguments into an args array
  var args = [];
  for (var i = 0; i < arguments.length - 3; i++) {
    args[i] = arguments[i + 3];
  }
  // All the paths MUST be to the same servlet
  if (batch.path == null) {
    batch.path = path;
  }
  else {
    if (batch.path != path) {
      dwr.engine._handleError(batch, { name:"dwr.engine.multipleServlets", message:"Can't batch requests to multiple DWR Servlets." });
      return;
    }
  }
  // From the other params, work out which is the function (or object with
  // call meta-data) and which is the call parameters
  var callData;
  var lastArg = args[args.length - 1];
  if (typeof lastArg == "function" || lastArg == null) callData = { callback:args.pop() };
  else callData = args.pop();

  // Merge from the callData into the batch
  dwr.engine._mergeBatch(batch, callData);
  batch.handlers[batch.map.callCount] = {
    exceptionHandler:callData.exceptionHandler,
    callback:callData.callback
  };

  // Copy to the map the things that need serializing
  var prefix = "c" + batch.map.callCount + "-";
  batch.map[prefix + "scriptName"] = scriptName;
  batch.map[prefix + "methodName"] = methodName;
  batch.map[prefix + "id"] = batch.map.callCount;
  for (i = 0; i < args.length; i++) {
    dwr.engine._serializeAll(batch, [], args[i], prefix + "param" + i);
  }

  // Now we have finished remembering the call, we incr the call count
  batch.map.callCount++;
  if (singleShot) dwr.engine.endBatch();
};

/** @private Poll the server to see if there is any data waiting */
dwr.engine._poll = function(overridePath) {
  if (!dwr.engine._activeReverseAjax) return;

  var batch = dwr.engine._createBatch();
  batch.map.id = 0; // TODO: Do we need this??
  batch.map.callCount = 1;
  batch.isPoll = true;
  if (navigator.userAgent.indexOf("Gecko/") != -1) {
    batch.rpcType = dwr.engine._pollType;
    batch.map.partialResponse = dwr.engine._partialResponseYes;
  }
  else if (document.all) {
    batch.rpcType = dwr.engine.IFrame;
    batch.map.partialResponse = dwr.engine._partialResponseFlush;
  }
  else {
    batch.rpcType = dwr.engine._pollType;
    batch.map.partialResponse = dwr.engine._partialResponseNo;
  }
  batch.httpMethod = "POST";
  batch.async = true;
  batch.timeout = 0;
  batch.path = (overridePath) ? overridePath : dwr.engine._defaultPath;
  batch.preHooks = [];
  batch.postHooks = [];
  batch.errorHandler = dwr.engine._pollErrorHandler;
  batch.warningHandler = dwr.engine._pollErrorHandler;
  batch.handlers[0] = {
    callback:function(pause) {
      dwr.engine._pollRetries = 0;
      setTimeout("dwr.engine._poll()", pause);
    }
  };

  // Send the data
  dwr.engine._sendData(batch);
  if (batch.rpcType == dwr.engine.XMLHttpRequest) {
  // if (batch.map.partialResponse != dwr.engine._partialResponseNo) {
    dwr.engine._checkCometPoll();
  }
};

/** Try to recover from polling errors */
dwr.engine._pollErrorHandler = function(msg, ex) {
  // if anything goes wrong then just silently try again (up to 3x) after 10s
  dwr.engine._pollRetries++;
  dwr.engine._debug("Reverse Ajax poll failed (pollRetries=" + dwr.engine._pollRetries + "): " + ex.name + " : " + ex.message);
  if (dwr.engine._pollRetries < dwr.engine._maxPollRetries) {
    setTimeout("dwr.engine._poll()", 10000);
  }
  else {
    dwr.engine._debug("Giving up.");
  }
};

/** @private Generate a new standard batch */
dwr.engine._createBatch = function() {
  var batch = {
    map:{
      callCount:0,
      page:window.location.pathname + window.location.search,
      httpSessionId:dwr.engine._getJSessionId(),
      scriptSessionId:dwr.engine._getScriptSessionId()
    },
    charsProcessed:0, paramCount:0,
    headers:[], parameters:[],
    isPoll:false, headers:{}, handlers:{}, preHooks:[], postHooks:[],
    rpcType:dwr.engine._rpcType,
    httpMethod:dwr.engine._httpMethod,
    async:dwr.engine._async,
    timeout:dwr.engine._timeout,
    errorHandler:dwr.engine._errorHandler,
    warningHandler:dwr.engine._warningHandler,
    textHtmlHandler:dwr.engine._textHtmlHandler
  };
  if (dwr.engine._preHook) batch.preHooks.push(dwr.engine._preHook);
  if (dwr.engine._postHook) batch.postHooks.push(dwr.engine._postHook);
  var propname, data;
  if (dwr.engine._headers) {
    for (propname in dwr.engine._headers) {
      data = dwr.engine._headers[propname];
      if (typeof data != "function") batch.headers[propname] = data;
    }
  }
  if (dwr.engine._parameters) {
    for (propname in dwr.engine._parameters) {
      data = dwr.engine._parameters[propname];
      if (typeof data != "function") batch.parameters[propname] = data;
    }
  }
  return batch;
}

/** @private Take further options and merge them into */
dwr.engine._mergeBatch = function(batch, overrides) {
  var propname, data;
  for (var i = 0; i < dwr.engine._propnames.length; i++) {
    propname = dwr.engine._propnames[i];
    if (overrides[propname] != null) batch[propname] = overrides[propname];
  }
  if (overrides.preHook != null) batch.preHooks.unshift(overrides.preHook);
  if (overrides.postHook != null) batch.postHooks.push(overrides.postHook);
  if (overrides.headers) {
    for (propname in overrides.headers) {
      data = overrides.headers[propname];
      if (typeof data != "function") batch.headers[propname] = data;
    }
  }
  if (overrides.parameters) {
    for (propname in overrides.parameters) {
      data = overrides.parameters[propname];
      if (typeof data != "function") batch.map["p-" + propname] = "" + data;
    }
  }
};

/** @private What is our session id? */
dwr.engine._getJSessionId =  function() {
  var cookies = document.cookie.split(';');
  for (var i = 0; i < cookies.length; i++) {
    var cookie = cookies[i];
    while (cookie.charAt(0) == ' ') cookie = cookie.substring(1, cookie.length);
    if (cookie.indexOf(dwr.engine._sessionCookieName + "=") == 0) {
      return cookie.substring(11, cookie.length);
    }
  }
  return "";
}

/** @private Check for reverse Ajax activity */
dwr.engine._checkCometPoll = function() {
  for (var i = 0; i < dwr.engine._outstandingIFrames.length; i++) {
    var text = "";
    var iframe = dwr.engine._outstandingIFrames[i];
    try {
      text = dwr.engine._getTextFromCometIFrame(iframe);
    }
    catch (ex) {
      dwr.engine._handleWarning(iframe.batch, ex);
    }
    if (text != "") dwr.engine._processCometResponse(text, iframe.batch);
  }
  if (dwr.engine._pollReq) {
    var req = dwr.engine._pollReq;
    var text = req.responseText;
    dwr.engine._processCometResponse(text, req.batch);
  }

  // If the poll resources are still there, come back again
  if (dwr.engine._outstandingIFrames.length > 0 || dwr.engine._pollReq) {
    setTimeout("dwr.engine._checkCometPoll()", dwr.engine._pollCometInterval);
  }
};

/** @private Extract the whole (executed an all) text from the current iframe */
dwr.engine._getTextFromCometIFrame = function(frameEle) {
  var body = frameEle.contentWindow.document.body;
  if (body == null) return "";
  var text = body.innerHTML;
  // We need to prevent IE from stripping line feeds
  if (text.indexOf("<PRE>") == 0 || text.indexOf("<pre>") == 0) {
    text = text.substring(5, text.length - 7);
  }
  return text;
};

/** @private Some more text might have come in, test and execute the new stuff */
dwr.engine._processCometResponse = function(response, batch) {
  if (batch.charsProcessed == response.length) return;
  if (response.length == 0) {
    batch.charsProcessed = 0;
    return;
  }

  var firstStartTag = response.indexOf("//#DWR-START#", batch.charsProcessed);
  if (firstStartTag == -1) {
    // dwr.engine._debug("No start tag (search from " + batch.charsProcessed + "). skipping '" + response.substring(batch.charsProcessed) + "'");
    batch.charsProcessed = response.length;
    return;
  }
  // if (firstStartTag > 0) {
  //   dwr.engine._debug("Start tag not at start (search from " + batch.charsProcessed + "). skipping '" + response.substring(batch.charsProcessed, firstStartTag) + "'");
  // }

  var lastEndTag = response.lastIndexOf("//#DWR-END#");
  if (lastEndTag == -1) {
    // dwr.engine._debug("No end tag. unchanged charsProcessed=" + batch.charsProcessed);
    return;
  }

  // Skip the end tag too for next time, remembering CR and LF
  if (response.charCodeAt(lastEndTag + 11) == 13 && response.charCodeAt(lastEndTag + 12) == 10) {
    batch.charsProcessed = lastEndTag + 13;
  }
  else {
    batch.charsProcessed = lastEndTag + 11;
  }

  var exec = response.substring(firstStartTag + 13, lastEndTag);

  dwr.engine._receivedBatch = batch;
  dwr.engine._eval(exec);
  dwr.engine._receivedBatch = null;
};

/** @private Actually send the block of data in the batch object. */
dwr.engine._sendData = function(batch) {
  batch.map.batchId = dwr.engine._nextBatchId++;
  dwr.engine._batches[batch.map.batchId] = batch;
  dwr.engine._batchesLength++;
  batch.completed = false;

  for (var i = 0; i < batch.preHooks.length; i++) {
    batch.preHooks[i]();
  }
  batch.preHooks = null;
  // Set a timeout
  if (batch.timeout && batch.timeout != 0) {
    batch.interval = setInterval(function() { dwr.engine._abortRequest(batch); }, batch.timeout);
  }
  // Get setup for XMLHttpRequest if possible
  if (batch.rpcType == dwr.engine.XMLHttpRequest) {
    if (window.XMLHttpRequest) {
      batch.req = new XMLHttpRequest();
    }
    // IE5 for the mac claims to support window.ActiveXObject, but throws an error when it's used
    else if (window.ActiveXObject && !(navigator.userAgent.indexOf("Mac") >= 0 && navigator.userAgent.indexOf("MSIE") >= 0)) {
      batch.req = dwr.engine._newActiveXObject(dwr.engine._XMLHTTP);
    }
  }

  var prop, request;
  if (batch.req) {
    // Proceed using XMLHttpRequest
    if (batch.async) {
      batch.req.onreadystatechange = function() { dwr.engine._stateChange(batch); };
    }
    // If we're polling, record this for monitoring
    if (batch.isPoll) {
      dwr.engine._pollReq = batch.req;
      // In IE XHR is an ActiveX control so you can't augment it like this
      // however batch.isPoll uses IFrame on IE so were safe here
      batch.req.batch = batch;
    }
    // Workaround for Safari 1.x POST bug
    var indexSafari = navigator.userAgent.indexOf("Safari/");
    if (indexSafari >= 0) {
      var version = navigator.userAgent.substring(indexSafari + 7);
      if (parseInt(version, 10) < 400) {
        if (dwr.engine._allowGetForSafariButMakeForgeryEasier == "true") batch.httpMethod = "GET";
        else dwr.engine._handleWarning(batch, { name:"dwr.engine.oldSafari", message:"Safari GET support disabled. See getahead.org/dwr/server/servlet and allowGetForSafariButMakeForgeryEasier." });
      }
    }
    batch.mode = batch.isPoll ? dwr.engine._ModePlainPoll : dwr.engine._ModePlainCall;
    request = dwr.engine._constructRequest(batch);
    try {
      batch.req.open(batch.httpMethod, request.url, batch.async);
      try {
        for (prop in batch.headers) {
          var value = batch.headers[prop];
          if (typeof value == "string") batch.req.setRequestHeader(prop, value);
        }
        if (!batch.headers["Content-Type"]) batch.req.setRequestHeader("Content-Type", "text/plain");
      }
      catch (ex) {
        dwr.engine._handleWarning(batch, ex);
      }
      batch.req.send(request.body);
      if (!batch.async) dwr.engine._stateChange(batch);
    }
    catch (ex) {
      dwr.engine._handleError(batch, ex);
    }
  }
  else if (batch.rpcType != dwr.engine.ScriptTag) {
    // Proceed using iframe
    var idname = batch.isPoll ? "dwr-if-poll-" + batch.map.batchId : "dwr-if-" + batch.map["c0-id"];
    batch.div = document.createElement("div");
    batch.div.innerHTML = "<iframe src='javascript:void(0)' frameborder='0' style='width:0px;height:0px;border:0;' id='" + idname + "' name='" + idname + "'></iframe>";
    document.body.appendChild(batch.div);
    batch.iframe = document.getElementById(idname);
    batch.iframe.batch = batch;
    batch.mode = batch.isPoll ? dwr.engine._ModeHtmlPoll : dwr.engine._ModeHtmlCall;
    if (batch.isPoll) dwr.engine._outstandingIFrames.push(batch.iframe);
    request = dwr.engine._constructRequest(batch);
    if (batch.httpMethod == "GET") {
      batch.iframe.setAttribute("src", request.url);
      // document.body.appendChild(batch.iframe);
    }
    else {
      batch.form = document.createElement("form");
      batch.form.setAttribute("id", "dwr-form");
      batch.form.setAttribute("action", request.url);
      batch.form.setAttribute("target", idname);
      batch.form.target = idname;
      batch.form.setAttribute("method", batch.httpMethod);
      for (prop in batch.map) {
        var value = batch.map[prop];
        if (typeof value != "function") {
          var formInput = document.createElement("input");
          formInput.setAttribute("type", "hidden");
          formInput.setAttribute("name", prop);
          formInput.setAttribute("value", value);
          batch.form.appendChild(formInput);
        }
      }
      document.body.appendChild(batch.form);
      batch.form.submit();
    }
  }
  else {
    batch.httpMethod = "GET"; // There's no such thing as ScriptTag using POST
    batch.mode = batch.isPoll ? dwr.engine._ModePlainPoll : dwr.engine._ModePlainCall;
    request = dwr.engine._constructRequest(batch);
    batch.script = document.createElement("script");
    batch.script.id = "dwr-st-" + batch.map["c0-id"];
    batch.script.src = request.url;
    document.body.appendChild(batch.script);
  }
};

dwr.engine._ModePlainCall = "/call/plaincall/";
dwr.engine._ModeHtmlCall = "/call/htmlcall/";
dwr.engine._ModePlainPoll = "/call/plainpoll/";
dwr.engine._ModeHtmlPoll = "/call/htmlpoll/";

/** @private Work out what the URL should look like */
dwr.engine._constructRequest = function(batch) {
  // A quick string to help people that use web log analysers
  var request = { url:batch.path + batch.mode, body:null };
  if (batch.isPoll == true) {
    request.url += "ReverseAjax.dwr";
  }
  else if (batch.map.callCount == 1) {
    request.url += batch.map["c0-scriptName"] + "." + batch.map["c0-methodName"] + ".dwr";
  }
  else {
    request.url += "Multiple." + batch.map.callCount + ".dwr";
  }
  // Play nice with url re-writing
  var sessionMatch = location.href.match(/jsessionid=([^?]+)/);
  if (sessionMatch != null) {
    request.url += ";jsessionid=" + sessionMatch[1];
  }

  var prop;
  if (batch.httpMethod == "GET") {
    // Some browsers (Opera/Safari2) seem to fail to convert the callCount value
    // to a string in the loop below so we do it manually here.
    batch.map.callCount = "" + batch.map.callCount;
    request.url += "?";
    for (prop in batch.map) {
      if (typeof batch.map[prop] != "function") {
        request.url += encodeURIComponent(prop) + "=" + encodeURIComponent(batch.map[prop]) + "&";
      }
    }
    request.url = request.url.substring(0, request.url.length - 1);
  }
  else {
    // PERFORMANCE: for iframe mode this is thrown away.
    request.body = "";
    for (prop in batch.map) {
      if (typeof batch.map[prop] != "function") {
        request.body += prop + "=" + batch.map[prop] + dwr.engine._postSeperator;
      }
    }
    request.body = dwr.engine._contentRewriteHandler(request.body);
  }
  request.url = dwr.engine._urlRewriteHandler(request.url);
  return request;
};

/** @private Called by XMLHttpRequest to indicate that something has happened */
dwr.engine._stateChange = function(batch) {
  var toEval;

  if (batch.completed) {
    dwr.engine._debug("Error: _stateChange() with batch.completed");
    return;
  }

  var req = batch.req;
  try {
    if (req.readyState != 4) return;
  }
  catch (ex) {
    dwr.engine._handleWarning(batch, ex);
    // It's broken - clear up and forget this call
    dwr.engine._clearUp(batch);
    return;
  }

  try {
    var reply = req.responseText;
    reply = dwr.engine._replyRewriteHandler(reply);
    var status = req.status; // causes Mozilla to except on page moves

    if (reply == null || reply == "") {
      dwr.engine._handleWarning(batch, { name:"dwr.engine.missingData", message:"No data received from server" });
    }
    else if (status != 200) {
      dwr.engine._handleError(batch, { name:"dwr.engine.http." + status, message:req.statusText });
    }
    else {
      var contentType = req.getResponseHeader("Content-Type");
      if (!contentType.match(/^text\/plain/) && !contentType.match(/^text\/javascript/)) {
        if (contentType.match(/^text\/html/) && typeof batch.textHtmlHandler == "function") {
          batch.textHtmlHandler();
        }
        else {
          dwr.engine._handleWarning(batch, { name:"dwr.engine.invalidMimeType", message:"Invalid content type: '" + contentType + "'" });
        }
      }
      else {
        // Comet replies might have already partially executed
        if (batch.isPoll && batch.map.partialResponse == dwr.engine._partialResponseYes) {
          dwr.engine._processCometResponse(reply, batch);
        }
        else {
          if (reply.search("//#DWR") == -1) {
            dwr.engine._handleWarning(batch, { name:"dwr.engine.invalidReply", message:"Invalid reply from server" });
          }
          else {
            toEval = reply;
          }
        }
      }
    }
  }
  catch (ex) {
    dwr.engine._handleWarning(batch, ex);
  }

  dwr.engine._callPostHooks(batch);

  // Outside of the try/catch so errors propogate normally:
  dwr.engine._receivedBatch = batch;
  if (toEval != null) toEval = toEval.replace(dwr.engine._scriptTagProtection, "");
  dwr.engine._eval(toEval);
  dwr.engine._receivedBatch = null;

  dwr.engine._clearUp(batch);
};

/** @private Called by the server: Execute a callback */
dwr.engine._remoteHandleCallback = function(batchId, callId, reply) {
  var batch = dwr.engine._batches[batchId];
  if (batch == null) {
    dwr.engine._debug("Warning: batch == null in remoteHandleCallback for batchId=" + batchId, true);
    return;
  }
  // Error handlers inside here indicate an error that is nothing to do
  // with DWR so we handle them differently.
  try {
    var handlers = batch.handlers[callId];
    if (!handlers) {
      dwr.engine._debug("Warning: Missing handlers. callId=" + callId, true);
    }
    else if (typeof handlers.callback == "function") handlers.callback(reply);
  }
  catch (ex) {
    dwr.engine._handleError(batch, ex);
  }
};

/** @private Called by the server: Handle an exception for a call */
dwr.engine._remoteHandleException = function(batchId, callId, ex) {
  var batch = dwr.engine._batches[batchId];
  if (batch == null) { dwr.engine._debug("Warning: null batch in remoteHandleException", true); return; }
  var handlers = batch.handlers[callId];
  if (handlers == null) { dwr.engine._debug("Warning: null handlers in remoteHandleException", true); return; }
  if (ex.message == undefined) ex.message = "";
  if (typeof handlers.exceptionHandler == "function") handlers.exceptionHandler(ex.message, ex);
  else if (typeof batch.errorHandler == "function") batch.errorHandler(ex.message, ex);
};

/** @private Called by the server: The whole batch is broken */
dwr.engine._remoteHandleBatchException = function(ex, batchId) {
  var searchBatch = (dwr.engine._receivedBatch == null && batchId != null);
  if (searchBatch) {
    dwr.engine._receivedBatch = dwr.engine._batches[batchId];
  }
  if (ex.message == undefined) ex.message = "";
  dwr.engine._handleError(dwr.engine._receivedBatch, ex);
  if (searchBatch) {
    dwr.engine._receivedBatch = null;
    dwr.engine._clearUp(dwr.engine._batches[batchId]);
  }
};

/** @private Called by the server: Reverse ajax should not be used */
dwr.engine._remotePollCometDisabled = function(ex, batchId) {
  dwr.engine.setActiveReverseAjax(false);
  var searchBatch = (dwr.engine._receivedBatch == null && batchId != null);
  if (searchBatch) {
    dwr.engine._receivedBatch = dwr.engine._batches[batchId];
  }
  if (ex.message == undefined) ex.message = "";
  dwr.engine._handleError(dwr.engine._receivedBatch, ex);
  if (searchBatch) {
    dwr.engine._receivedBatch = null;
    dwr.engine._clearUp(dwr.engine._batches[batchId]);
  }
};

/** @private Called by the server: An IFrame reply is about to start */
dwr.engine._remoteBeginIFrameResponse = function(iframe, batchId) {
  if (iframe != null) dwr.engine._receivedBatch = iframe.batch;
  dwr.engine._callPostHooks(dwr.engine._receivedBatch);
};

/** @private Called by the server: An IFrame reply is just completing */
dwr.engine._remoteEndIFrameResponse = function(batchId) {
  dwr.engine._clearUp(dwr.engine._receivedBatch);
  dwr.engine._receivedBatch = null;
};

/** @private This is a hack to make the context be this window */
dwr.engine._eval = function(script) {
  if (script == null) return null;
  if (script == "") { dwr.engine._debug("Warning: blank script", true); return null; }
  // dwr.engine._debug("Exec: [" + script + "]", true);
  return eval(script);
};

/** @private Called as a result of a request timeout */
dwr.engine._abortRequest = function(batch) {
  if (batch && !batch.completed) {
    clearInterval(batch.interval);
    dwr.engine._clearUp(batch);
    if (batch.req) batch.req.abort();
    dwr.engine._handleError(batch, { name:"dwr.engine.timeout", message:"Timeout" });
  }
};

/** @private call all the post hooks for a batch */
dwr.engine._callPostHooks = function(batch) {
  if (batch.postHooks) {
    for (var i = 0; i < batch.postHooks.length; i++) {
      batch.postHooks[i]();
    }
    batch.postHooks = null;
  }
}

/** @private A call has finished by whatever means and we need to shut it all down. */
dwr.engine._clearUp = function(batch) {
  if (!batch) { dwr.engine._debug("Warning: null batch in dwr.engine._clearUp()", true); return; }
  if (batch.completed == "true") { dwr.engine._debug("Warning: Double complete", true); return; }

  // IFrame tidyup
  if (batch.div) batch.div.parentNode.removeChild(batch.div);
  if (batch.iframe) {
    // If this is a poll frame then stop comet polling
    for (var i = 0; i < dwr.engine._outstandingIFrames.length; i++) {
      if (dwr.engine._outstandingIFrames[i] == batch.iframe) {
        dwr.engine._outstandingIFrames.splice(i, 1);
      }
    }
    batch.iframe.parentNode.removeChild(batch.iframe);
  }
  if (batch.form) batch.form.parentNode.removeChild(batch.form);

  // XHR tidyup: avoid IE handles increase
  if (batch.req) {
    // If this is a poll frame then stop comet polling
    if (batch.req == dwr.engine._pollReq) dwr.engine._pollReq = null;
    delete batch.req;
  }

  if (batch.map && batch.map.batchId) {
    delete dwr.engine._batches[batch.map.batchId];
    dwr.engine._batchesLength--;
  }

  batch.completed = true;

  // If there is anything on the queue waiting to go out, then send it.
  // We don't need to check for ordered mode, here because when ordered mode
  // gets turned off, we still process *waiting* batches in an ordered way.
  if (dwr.engine._batchQueue.length != 0) {
    var sendbatch = dwr.engine._batchQueue.shift();
    dwr.engine._sendData(sendbatch);
  }
};

/** @private Generic error handling routing to save having null checks everywhere */
dwr.engine._handleError = function(batch, ex) {
  if (typeof ex == "string") ex = { name:"unknown", message:ex };
  if (ex.message == null) ex.message = "";
  if (ex.name == null) ex.name = "unknown";
  if (batch && typeof batch.errorHandler == "function") batch.errorHandler(ex.message, ex);
  else if (dwr.engine._errorHandler) dwr.engine._errorHandler(ex.message, ex);
  dwr.engine._clearUp(batch);
};

/** @private Generic error handling routing to save having null checks everywhere */
dwr.engine._handleWarning = function(batch, ex) {
  if (typeof ex == "string") ex = { name:"unknown", message:ex };
  if (ex.message == null) ex.message = "";
  if (ex.name == null) ex.name = "unknown";
  if (batch && typeof batch.warningHandler == "function") batch.warningHandler(ex.message, ex);
  else if (dwr.engine._warningHandler) dwr.engine._warningHandler(ex.message, ex);
  dwr.engine._clearUp(batch);
};

/**
 * @private Marshall a data item
 * @param batch A map of variables to how they have been marshalled
 * @param referto An array of already marshalled variables to prevent recurrsion
 * @param data The data to be marshalled
 * @param name The name of the data being marshalled
 */
dwr.engine._serializeAll = function(batch, referto, data, name) {
  if (data == null) {
    batch.map[name] = "null:null";
    return;
  }

  switch (typeof data) {
  case "boolean":
    batch.map[name] = "boolean:" + data;
    break;
  case "number":
    batch.map[name] = "number:" + data;
    break;
  case "string":
    batch.map[name] = "string:" + encodeURIComponent(data);
    break;
  case "object":
    if (data instanceof String) batch.map[name] = "String:" + encodeURIComponent(data);
    else if (data instanceof Boolean) batch.map[name] = "Boolean:" + data;
    else if (data instanceof Number) batch.map[name] = "Number:" + data;
    else if (data instanceof Date) batch.map[name] = "Date:" + data.getTime();
    else if (data && data.join) batch.map[name] = dwr.engine._serializeArray(batch, referto, data, name);
    else batch.map[name] = dwr.engine._serializeObject(batch, referto, data, name);
    break;
  case "function":
    // We just ignore functions.
    break;
  default:
    dwr.engine._handleWarning(null, { name:"dwr.engine.unexpectedType", message:"Unexpected type: " + typeof data + ", attempting default converter." });
    batch.map[name] = "default:" + data;
    break;
  }
};

/** @private Have we already converted this object? */
dwr.engine._lookup = function(referto, data, name) {
  var lookup;
  // Can't use a map: getahead.org/ajax/javascript-gotchas
  for (var i = 0; i < referto.length; i++) {
    if (referto[i].data == data) {
      lookup = referto[i];
      break;
    }
  }
  if (lookup) return "reference:" + lookup.name;
  referto.push({ data:data, name:name });
  return null;
};

/** @private Marshall an object */
dwr.engine._serializeObject = function(batch, referto, data, name) {
  var ref = dwr.engine._lookup(referto, data, name);
  if (ref) return ref;

  // This check for an HTML is not complete, but is there a better way?
  // Maybe we should add: data.hasChildNodes typeof "function" == true
  if (data.nodeName && data.nodeType) {
    return dwr.engine._serializeXml(batch, referto, data, name);
  }

  // treat objects as an associative arrays
  var reply = "Object_" + dwr.engine._getObjectClassName(data) + ":{";
  var element;
  for (element in data) {
    if (typeof data[element] != "function") {
      batch.paramCount++;
      var childName = "c" + dwr.engine._batch.map.callCount + "-e" + batch.paramCount;
      dwr.engine._serializeAll(batch, referto, data[element], childName);

      reply += encodeURIComponent(element) + ":reference:" + childName + ", ";
    }
  }

  if (reply.substring(reply.length - 2) == ", ") {
    reply = reply.substring(0, reply.length - 2);
  }
  reply += "}";

  return reply;
};

/** @private Returns the classname of supplied argument obj */
dwr.engine._errorClasses = { "Error":Error, "EvalError":EvalError, "RangeError":RangeError, "ReferenceError":ReferenceError, "SyntaxError":SyntaxError, "TypeError":TypeError, "URIError":URIError };
dwr.engine._getObjectClassName = function(obj) {
  // Try to find the classname by stringifying the object's constructor
  // and extract <class> from "function <class>".
  if (obj && obj.constructor && obj.constructor.toString)
  {
    var str = obj.constructor.toString();
    var regexpmatch = str.match(/function\s+(\w+)/);
    if (regexpmatch && regexpmatch.length == 2) {
      return regexpmatch[1];
    }
  }

  // Now manually test against the core Error classes, as these in some
  // browsers successfully match to the wrong class in the
  // Object.toString() test we will do later
  if (obj && obj.constructor) {
  for (var errorname in dwr.engine._errorClasses) {
      if (obj.constructor == dwr.engine._errorClasses[errorname]) return errorname;
    }
  }

  // Try to find the classname by calling Object.toString() on the object
  // and extracting <class> from "[object <class>]"
  if (obj) {
    var str = Object.prototype.toString.call(obj);
    var regexpmatch = str.match(/\[object\s+(\w+)/);
    if (regexpmatch && regexpmatch.length==2) {
      return regexpmatch[1];
    }
  }

  // Supplied argument was probably not an object, but what is better?
  return "Object";
};

/** @private Marshall an object */
dwr.engine._serializeXml = function(batch, referto, data, name) {
  var ref = dwr.engine._lookup(referto, data, name);
  if (ref) return ref;

  var output;
  if (window.XMLSerializer) output = new XMLSerializer().serializeToString(data);
  else if (data.toXml) output = data.toXml;
  else output = data.innerHTML;

  return "XML:" + encodeURIComponent(output);
};

/** @private Marshall an array */
dwr.engine._serializeArray = function(batch, referto, data, name) {
  var ref = dwr.engine._lookup(referto, data, name);
  if (ref) return ref;

  var reply = "Array:[";
  for (var i = 0; i < data.length; i++) {
    if (i != 0) reply += ",";
    batch.paramCount++;
    var childName = "c" + dwr.engine._batch.map.callCount + "-e" + batch.paramCount;
    dwr.engine._serializeAll(batch, referto, data[i], childName);
    reply += "reference:";
    reply += childName;
  }
  reply += "]";

  return reply;
};

/** @private Convert an XML string into a DOM object. */
dwr.engine._unserializeDocument = function(xml) {
  var dom;
  if (window.DOMParser) {
    var parser = new DOMParser();
    dom = parser.parseFromString(xml, "text/xml");
    if (!dom.documentElement || dom.documentElement.tagName == "parsererror") {
      var message = dom.documentElement.firstChild.data;
      message += "\n" + dom.documentElement.firstChild.nextSibling.firstChild.data;
      throw message;
    }
    return dom;
  }
  else if (window.ActiveXObject) {
    dom = dwr.engine._newActiveXObject(dwr.engine._DOMDocument);
    dom.loadXML(xml); // What happens on parse fail with IE?
    return dom;
  }
  else {
    var div = document.createElement("div");
    div.innerHTML = xml;
    return div;
  }
};

/** @param axarray An array of strings to attempt to create ActiveX objects from */
dwr.engine._newActiveXObject = function(axarray) {
  var returnValue;
  for (var i = 0; i < axarray.length; i++) {
    try {
      returnValue = new ActiveXObject(axarray[i]);
      break;
    }
    catch (ex) { /* ignore */ }
  }
  return returnValue;
};

/** @private Used internally when some message needs to get to the programmer */
dwr.engine._debug = function(message, stacktrace) {
  var written = false;
  try {
    if (window.console) {
      if (stacktrace && window.console.trace) window.console.trace();
      window.console.log(message);
      written = true;
    }
    else if (window.opera && window.opera.postError) {
      window.opera.postError(message);
      written = true;
    }
  }
  catch (ex) { /* ignore */ }

  if (!written) {
    var debug = document.getElementById("dwr-debug");
    if (debug) {
      var contents = message + "<br/>" + debug.innerHTML;
      if (contents.length > 2048) contents = contents.substring(0, 2048);
      debug.innerHTML = contents;
    }
  }
};

/*
 * Copyright 2005 Joe Walker
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

/**
 * Declare an object to which we can add real functions.
 */
if (dwr == null) var dwr = {};
if (dwr.util == null) dwr.util = {};
if (DWRUtil == null) var DWRUtil = dwr.util;

/** @private The flag we use to decide if we should escape html */
dwr.util._escapeHtml = true;

/**
 * Set the global escapeHtml flag
 */
dwr.util.setEscapeHtml = function(escapeHtml) {
  dwr.util._escapeHtml = escapeHtml;
}

/** @private Work out from an options list and global settings if we should be esccaping */
dwr.util._shouldEscapeHtml = function(options) {
  if (options && options.escapeHtml != null) {
    return options.escapeHtml;
  }
  return dwr.util._escapeHtml;
}

/**
 * Return a string with &, <, >, ' and " replaced with their entities
 * @see TODO
 */
dwr.util.escapeHtml = function(original) {
  var div = document.createElement('div');
  var text = document.createTextNode(original);
  div.appendChild(text);
  return div.innerHTML;
}

/**
 * Replace common XML entities with characters (see dwr.util.escapeHtml())
 * @see TODO
 */
dwr.util.unescapeHtml = function(original) {
  var div = document.createElement('div');
  div.innerHTML = original.replace(/<\/?[^>]+>/gi, '');
  return div.childNodes[0] ? div.childNodes[0].nodeValue : '';
}

/**
 * Replace characters dangerous for XSS reasons with visually similar characters
 * @see TODO
 */
dwr.util.replaceXmlCharacters = function(original) {
  original = original.replace("&", "+");
  original = original.replace("<", "\u2039");
  original = original.replace(">", "\u203A");
  original = original.replace("\'", "\u2018");
  original = original.replace("\"", "\u201C");
  return original;
}

/**
 * Return true iff the input string contains any XSS dangerous characters
 * @see TODO
 */
dwr.util.containsXssRiskyCharacters = function(original) {
  return (original.indexOf('&') != -1
    && original.indexOf('<') != -1
    && original.indexOf('>') != -1
    && original.indexOf('\'') != -1
    && original.indexOf('\"') != -1);
}

/**
 * Enables you to react to return being pressed in an input
 * @see http://getahead.org/dwr/browser/util/selectrange
 */
dwr.util.onReturn = function(event, action) {
  if (!event) event = window.event;
  if (event && event.keyCode && event.keyCode == 13) action();
};

/**
 * Select a specific range in a text box. Useful for 'google suggest' type functions.
 * @see http://getahead.org/dwr/browser/util/selectrange
 */
dwr.util.selectRange = function(ele, start, end) {
  ele = dwr.util._getElementById(ele, "selectRange()");
  if (ele == null) return;
  if (ele.setSelectionRange) {
    ele.setSelectionRange(start, end);
  }
  else if (ele.createTextRange) {
    var range = ele.createTextRange();
    range.moveStart("character", start);
    range.moveEnd("character", end - ele.value.length);
    range.select();
  }
  ele.focus();
};

/**
 * Find the element in the current HTML document with the given id or ids
 * @see http://getahead.org/dwr/browser/util/$
 */
if (document.getElementById) {
  dwr.util.byId = function() {
    var elements = new Array();
    for (var i = 0; i < arguments.length; i++) {
      var element = arguments[i];
      if (typeof element == 'string') {
        element = document.getElementById(element);
      }
      if (arguments.length == 1) {
        return element;
      }
      elements.push(element);
    }
    return elements;
  };
}
else if (document.all) {
  dwr.util.byId = function() {
    var elements = new Array();
    for (var i = 0; i < arguments.length; i++) {
      var element = arguments[i];
      if (typeof element == 'string') {
        element = document.all[element];
      }
      if (arguments.length == 1) {
        return element;
      }
      elements.push(element);
    }
    return elements;
  };
}

/**
 * Alias $ to dwr.util.byId
 * @see http://getahead.org/dwr/browser/util/$
 */
var $;
if (!$) {
  $ = dwr.util.byId;
}

/**
 * This function pretty-prints simple data or whole object graphs, f ex as an aid in debugging.
 * @see http://getahead.org/dwr/browser/util/todescriptivestring
 */
 dwr.util.toDescriptiveString = function(data, showLevels, options) {
  if (showLevels === undefined) showLevels = 1;
  var opt = {};
  if (dwr.util._isObject(options)) opt = options;
  var defaultoptions = {
    escapeHtml:false,
    baseIndent: "",
    childIndent: "\u00A0\u00A0",
    lineTerminator: "\n",
    oneLineMaxItems: 5,
    shortStringMaxLength: 13,
    propertyNameMaxLength: 30 
  };
  for (var p in defaultoptions) if (!(p in opt)) opt[p] = defaultoptions[p];

  var skipDomProperties = {
    document:true, ownerDocument:true,
    all:true,
    parentElement:true, parentNode:true, offsetParent:true,
    children:true, firstChild:true, lastChild:true,
    previousSibling:true, nextSibling:true,
    innerHTML:true, outerHTML:true,
    innerText:true, outerText:true, textContent:true,
    attributes:true,
    style:true, currentStyle:true, runtimeStyle:true,
    parentTextEdit:true
  };
  
  function recursive(data, showLevels, indentDepth, options) {
    var reply = "";
    try {
      // string
      if (typeof data == "string") {
        var str = data;
        if (showLevels == 0 && str.length > options.shortStringMaxLength)
          str = str.substring(0, options.shortStringMaxLength-3) + "...";
        if (options.escapeHtml) {
          // Do the escape separately for every line as escapeHtml() on some 
          // browsers (IE) will strip line breaks and we want to preserve them
          var lines = str.split("\n");
          for (var i = 0; i < lines.length; i++) lines[i] = dwr.util.escapeHtml(lines[i]);
          str = lines.join("\n");
        }
        if (showLevels == 0) { // Short format
          str = str.replace(/\n|\r|\t/g, function(ch) {
            switch (ch) {
              case "\n": return "\\n";
              case "\r": return "";
              case "\t": return "\\t";
            }
          });
        }
        else { // Long format
          str = str.replace(/\n|\r|\t/g, function(ch) {
            switch (ch) {
              case "\n": return options.lineTerminator + indent(indentDepth+1, options);
              case "\r": return "";
              case "\t": return "\\t";
            }
          });
        }
        reply = '"' + str + '"';
      }
      
      // function
      else if (typeof data == "function") {
        reply = "function";
      }
    
      // Array
      else if (dwr.util._isArray(data)) {
        if (showLevels == 0) { // Short format (don't show items)
          if (data.length > 0)
            reply = "[...]";
          else
            reply = "[]";
        }
        else { // Long format (show items)
          var strarr = [];
          strarr.push("[");
          var count = 0;
          for (var i = 0; i < data.length; i++) {
            if (! (i in data)) continue;
            var itemvalue = data[i];
            if (count > 0) strarr.push(", ");
            if (showLevels == 1) { // One-line format
              if (count == options.oneLineMaxItems) {
                strarr.push("...");
                break;
              }
            }
            else { // Multi-line format
              strarr.push(options.lineTerminator + indent(indentDepth+1, options));
            }
            if (i != count) {
              strarr.push(i);
              strarr.push(":");
            }
            strarr.push(recursive(itemvalue, showLevels-1, indentDepth+1, options));
            count++;
          }
          if (showLevels > 1) strarr.push(options.lineTerminator + indent(indentDepth, options));
          strarr.push("]");
          reply = strarr.join("");
        }
      }
      
      // Objects except Date
      else if (dwr.util._isObject(data) && !dwr.util._isDate(data)) {
        if (showLevels == 0) { // Short format (don't show properties)
          reply = dwr.util._detailedTypeOf(data);
        }
        else { // Long format (show properties)
          var strarr = [];
          if (dwr.util._detailedTypeOf(data) != "Object") {
            strarr.push(dwr.util._detailedTypeOf(data));
            if (typeof data.valueOf() != "object") {
              strarr.push(":");
              strarr.push(recursive(data.valueOf(), 1, indentDepth, options));
            }
            strarr.push(" ");
          }
          strarr.push("{");
          var isDomObject = dwr.util._isHTMLElement(data); 
          var count = 0;
          for (var prop in data) {
            var propvalue = data[prop];
            if (isDomObject) {
              if (!propvalue) continue;
              if (typeof propvalue == "function") continue;
              if (skipDomProperties[prop]) continue;
              if (prop.toUpperCase() == prop) continue;
            }
            if (count > 0) strarr.push(", ");
            if (showLevels == 1) { // One-line format
              if (count == options.oneLineMaxItems) {
                strarr.push("...");
                break;
              }
            }
            else { // Multi-line format
              strarr.push(options.lineTerminator + indent(indentDepth+1, options));
            }
            strarr.push(prop.length > options.propertyNameMaxLength ? prop.substring(0, options.propertyNameMaxLength-3) + "..." : prop);
            strarr.push(":");
            strarr.push(recursive(propvalue, showLevels-1, indentDepth+1, options));
            count++;
          }
          if (showLevels > 1 && count > 0) strarr.push(options.lineTerminator + indent(indentDepth, options));
          strarr.push("}");
          reply = strarr.join("");
        }
      }
  
      // undefined, null, number, boolean, Date
      else {
        reply = "" + data;
      }
  
      return reply;
    }
    catch(err) {
      return (err.message ? err.message : ""+err);
    }
  }

  function indent(count, options) {
    var strarr = [];
    strarr.push(options.baseIndent);
    for (var i=0; i<count; i++) {
      strarr.push(options.childIndent);
    }
    return strarr.join("");
  };
  
  return recursive(data, showLevels, 0, opt);
}

/**
 * Setup a GMail style loading message.
 * @see http://getahead.org/dwr/browser/util/useloadingmessage
 */
dwr.util.useLoadingMessage = function(message) {
  var loadingMessage;
  if (message) loadingMessage = message;
  else loadingMessage = "Loading";
  dwr.engine.setPreHook(function() {
    var disabledZone = dwr.util.byId('disabledZone');
    if (!disabledZone) {
      disabledZone = document.createElement('div');
      disabledZone.setAttribute('id', 'disabledZone');
      disabledZone.style.position = "absolute";
      disabledZone.style.zIndex = "1000";
      disabledZone.style.left = "0px";
      disabledZone.style.top = "0px";
      disabledZone.style.width = "100%";
      disabledZone.style.height = "100%";
      document.body.appendChild(disabledZone);
      var messageZone = document.createElement('div');
      messageZone.setAttribute('id', 'messageZone');
      messageZone.style.position = "absolute";
      messageZone.style.top = "0px";
      messageZone.style.right = "0px";
      messageZone.style.background = "red";
      messageZone.style.color = "white";
      messageZone.style.fontFamily = "Arial,Helvetica,sans-serif";
      messageZone.style.padding = "4px";
      disabledZone.appendChild(messageZone);
      var text = document.createTextNode(loadingMessage);
      messageZone.appendChild(text);
      dwr.util._disabledZoneUseCount = 1;
    }
    else {
      dwr.util.byId('messageZone').innerHTML = loadingMessage;
      disabledZone.style.visibility = 'visible';
      dwr.util._disabledZoneUseCount++;
    }
  });
  dwr.engine.setPostHook(function() {
    dwr.util._disabledZoneUseCount--;
    if (dwr.util._disabledZoneUseCount == 0) {
      dwr.util.byId('disabledZone').style.visibility = 'hidden';
    }
  });
};

/**
 * Set a global highlight handler
 */
dwr.util.setHighlightHandler = function(handler) {
  dwr.util._highlightHandler = handler;
};

/**
 * An example highlight handler
 */
dwr.util.yellowFadeHighlightHandler = function(ele) {
  dwr.util._yellowFadeProcess(ele, 0);
};
dwr.util._yellowFadeSteps = [ "d0", "b0", "a0", "90", "98", "a0", "a8", "b0", "b8", "c0", "c8", "d0", "d8", "e0", "e8", "f0", "f8" ];
dwr.util._yellowFadeProcess = function(ele, colorIndex) {
  ele = dwr.util.byId(ele);
  if (colorIndex < dwr.util._yellowFadeSteps.length) {
    ele.style.backgroundColor = "#ffff" + dwr.util._yellowFadeSteps[colorIndex];
    setTimeout("dwr.util._yellowFadeProcess('" + ele.id + "'," + (colorIndex + 1) + ")", 200);
  }
  else {
    ele.style.backgroundColor = "transparent";
  }
};

/**
 * An example highlight handler
 */
dwr.util.borderFadeHighlightHandler = function(ele) {
  ele.style.borderWidth = "2px";
  ele.style.borderStyle = "solid";
  dwr.util._borderFadeProcess(ele, 0);
};
dwr.util._borderFadeSteps = [ "d0", "b0", "a0", "90", "98", "a0", "a8", "b0", "b8", "c0", "c8", "d0", "d8", "e0", "e8", "f0", "f8" ];
dwr.util._borderFadeProcess = function(ele, colorIndex) {
  ele = dwr.util.byId(ele);
  if (colorIndex < dwr.util._borderFadeSteps.length) {
    ele.style.borderColor = "#ff" + dwr.util._borderFadeSteps[colorIndex] + dwr.util._borderFadeSteps[colorIndex];
    setTimeout("dwr.util._borderFadeProcess('" + ele.id + "'," + (colorIndex + 1) + ")", 200);
  }
  else {
    ele.style.backgroundColor = "transparent";
  }
};

/**
 * A focus highlight handler
 */
dwr.util.focusHighlightHandler = function(ele) {
  try {
    ele.focus();
  }
  catch (ex) { /* ignore */ }
};

/** @private the current global highlight style */
dwr.util._highlightHandler = null;

/**
 * Highlight that an element has changed
 */
dwr.util.highlight = function(ele, options) {
  if (options && options.highlightHandler) {
    options.highlightHandler(dwr.util.byId(ele));
  }
  else if (dwr.util._highlightHandler != null) {
    dwr.util._highlightHandler(dwr.util.byId(ele));
  }
};

/**
 * Set the value an HTML element to the specified value.
 * @see http://getahead.org/dwr/browser/util/setvalue
 */
dwr.util.setValue = function(ele, val, options) {
  if (val == null) val = "";
  if (options == null) options = {};
  if (dwr.util._shouldEscapeHtml(options) && typeof(val) == "string") {
    val = dwr.util.escapeHtml(val);
  }

  var orig = ele;
  if (typeof ele == "string") {
    ele = dwr.util.byId(ele);
    // We can work with names and need to sometimes for radio buttons, and IE has
    // an annoying bug where getElementById() returns an element based on name if
    // it doesn't find it by id. Here we don't want to do that, so:
    if (ele && ele.id != orig) ele = null;
  }
  var nodes = null;
  if (ele == null) {
    // Now it is time to look by name
    nodes = document.getElementsByName(orig);
    if (nodes.length >= 1) ele = nodes.item(0);
  }

  if (ele == null) {
    dwr.util._debug("setValue() can't find an element with id/name: " + orig + ".");
    return;
  }

  // All paths now lead to some update so we highlight a change
  dwr.util.highlight(ele, options);

  if (dwr.util._isHTMLElement(ele, "select")) {
    if (ele.type == "select-multiple" && dwr.util._isArray(val)) dwr.util._selectListItems(ele, val);
    else dwr.util._selectListItem(ele, val);
    return;
  }

  if (dwr.util._isHTMLElement(ele, "input")) {
    if (ele.type == "radio" || ele.type == "checkbox") {
      if (nodes && nodes.length >= 1) {
        for (var i = 0; i < nodes.length; i++) {
          var node = nodes.item(i);
          if (node.type != ele.type) continue;
          if (dwr.util._isArray(val)) {
            node.checked = false;
            for (var j = 0; j < val.length; j++)
              if (val[i] == node.value) node.checked = true;
          }
          else {
            node.checked = (node.value == val);
          }
        }
      }
      else ele.checked = (val == true);
    }
    else ele.value = val;

    return;
  }

  if (dwr.util._isHTMLElement(ele, "textarea")) {
    ele.value = val;
    return;
  }

  // If the value to be set is a DOM object then we try importing the node
  // rather than serializing it out
  if (val.nodeType) {
    if (val.nodeType == 9 /*Node.DOCUMENT_NODE*/) val = val.documentElement;
    val = dwr.util._importNode(ele.ownerDocument, val, true);
    ele.appendChild(val);
    return;
  }

  // Fall back to innerHTML
  ele.innerHTML = val;
};

/**
 * @private Find multiple items in a select list and select them. Used by setValue()
 * @param ele The select list item
 * @param val The array of values to select
 */
dwr.util._selectListItems = function(ele, val) {
  // We deal with select list elements by selecting the matching option
  // Begin by searching through the values
  var found  = false;
  var i;
  var j;
  for (i = 0; i < ele.options.length; i++) {
    ele.options[i].selected = false;
    for (j = 0; j < val.length; j++) {
      if (ele.options[i].value == val[j]) {
        ele.options[i].selected = true;
      }
    }
  }
  // If that fails then try searching through the visible text
  if (found) return;

  for (i = 0; i < ele.options.length; i++) {
    for (j = 0; j < val.length; j++) {
      if (ele.options[i].text == val[j]) {
        ele.options[i].selected = true;
      }
    }
  }
};

/**
 * @private Find an item in a select list and select it. Used by setValue()
 * @param ele The select list item
 * @param val The value to select
 */
dwr.util._selectListItem = function(ele, val) {
  // We deal with select list elements by selecting the matching option
  // Begin by searching through the values
  var found = false;
  var i;
  for (i = 0; i < ele.options.length; i++) {
    if (ele.options[i].value == val) {
      ele.options[i].selected = true;
      found = true;
    }
    else {
      ele.options[i].selected = false;
    }
  }

  // If that fails then try searching through the visible text
  if (found) return;

  for (i = 0; i < ele.options.length; i++) {
    if (ele.options[i].text == val) {
      ele.options[i].selected = true;
    }
    else {
      ele.options[i].selected = false;
    }
  }
};

/**
 * Read the current value for a given HTML element.
 * @see http://getahead.org/dwr/browser/util/getvalue
 */
dwr.util.getValue = function(ele, options) {
  if (options == null) options = {};
  var orig = ele;
  if (typeof ele == "string") {
    ele = dwr.util.byId(ele);
    // We can work with names and need to sometimes for radio buttons, and IE has
    // an annoying bug where getElementById() returns an element based on name if
    // it doesn't find it by id. Here we don't want to do that, so:
    if (ele && ele.id != orig) ele = null;
  }
  var nodes = null;
  if (ele == null) {
    // Now it is time to look by name
    nodes = document.getElementsByName(orig);
    if (nodes.length >= 1) ele = nodes.item(0);
  }
  if (ele == null) {
    dwr.util._debug("getValue() can't find an element with id/name: " + orig + ".");
    return "";
  }

  if (dwr.util._isHTMLElement(ele, "select")) {
    // Using "type" property instead of "multiple" as "type" is an official 
    // client-side property since JS 1.1
    if (ele.type == "select-multiple") {
      var reply = new Array();
      for (var i = 0; i < ele.options.length; i++) {
        var item = ele.options[i];
        if (item.selected) {
          var valueAttr = item.getAttributeNode("value");
          if (valueAttr && valueAttr.specified) {
            reply.push(item.value);
          }
          else {
            reply.push(item.text);
          }
        }
      }
      return reply;
    }
    else {
      var sel = ele.selectedIndex;
      if (sel != -1) {
        var item = ele.options[sel];
        var valueAttr = item.getAttributeNode("value");
        if (valueAttr && valueAttr.specified) {
          return item.value;
        }
        return item.text;
      }
      else {
        return "";
      }
    }
  }

  if (dwr.util._isHTMLElement(ele, "input")) {
    if (ele.type == "radio") {
      if (nodes && nodes.length >= 1) {
        for (var i = 0; i < nodes.length; i++) {
          var node = nodes.item(i);
          if (node.type == ele.type) {
            if (node.checked) return node.value;
          }
        }
      }
      return ele.checked;
    }
    if (ele.type == "checkbox") {
      if (nodes && nodes.length >= 1) {
        var reply = [];
        for (var i = 0; i < nodes.length; i++) {
          var node = nodes.item(i);
          if (node.type == ele.type) {
            if (node.checked) reply.push(node.value);
          }
        }
        return reply;
      }
      return ele.checked;
    }
    return ele.value;
  }

  if (dwr.util._isHTMLElement(ele, "textarea")) {
    return ele.value;
  }

  if (dwr.util._shouldEscapeHtml(options)) {
    if (ele.textContent) return ele.textContent;
    else if (ele.innerText) return ele.innerText;
  }
  return ele.innerHTML;
};

/**
 * getText() is like getValue() except that it reads the text (and not the value) from select elements
 * @see http://getahead.org/dwr/browser/util/gettext
 */
dwr.util.getText = function(ele) {
  ele = dwr.util._getElementById(ele, "getText()");
  if (ele == null) return null;
  if (!dwr.util._isHTMLElement(ele, "select")) {
    dwr.util._debug("getText() can only be used with select elements. Attempt to use: " + dwr.util._detailedTypeOf(ele) + " from  id: " + orig + ".");
    return "";
  }

  // This is a bit of a scam because it assumes single select
  // but I'm not sure how we should treat multi-select.
  var sel = ele.selectedIndex;
  if (sel != -1) {
    return ele.options[sel].text;
  }
  else {
    return "";
  }
};

/**
 * Given a map, or a recursive structure consisting of arrays and maps, call 
 * setValue() for all leaf entries and use intermediate levels to form nested
 * element ids.
 * @see http://getahead.org/dwr/browser/util/setvalues
 */
dwr.util.setValues = function(data, options) {
  var prefix = "";
  if (options && options.prefix) prefix = options.prefix;
  if (options && options.idPrefix) prefix = options.idPrefix;
  dwr.util._setValuesRecursive(data, prefix);
};

/**
 * @private Recursive helper for setValues()
 */
dwr.util._setValuesRecursive = function(data, idpath) {
  // Array containing objects -> add "[n]" to prefix and make recursive call
  // for each item object
  if (dwr.util._isArray(data) && data.length > 0 && dwr.util._isObject(data[0])) {
    for (var i = 0; i < data.length; i++) {
      dwr.util._setValuesRecursive(data[i], idpath+"["+i+"]");
    }
  }
  // Object (not array) -> handle nested object properties
  else if (dwr.util._isObject(data) && !dwr.util._isArray(data)) {
    for (var prop in data) {
      var subidpath = idpath ? idpath+"."+prop : prop;
      // Object (not array), or array containing objects -> call ourselves recursively
      if (dwr.util._isObject(data[prop]) && !dwr.util._isArray(data[prop]) 
          || dwr.util._isArray(data[prop]) && data[prop].length > 0 && dwr.util._isObject(data[prop][0])) {
        dwr.util._setValuesRecursive(data[prop], subidpath);
      }
      // Functions -> skip
      else if (typeof data[prop] == "function") {
        // NOP
      }
      // Only simple values left (or array of simple values, or empty array)
      // -> call setValue()
      else {
        // Are there any elements with that id or name
        if (dwr.util.byId(subidpath) != null || document.getElementsByName(subidpath).length >= 1) {
          dwr.util.setValue(subidpath, data[prop]);
        }
      }
    }
  }
};

/**
 * Given a map, or a recursive structure consisting of arrays and maps, call 
 * getValue() for all leaf entries and use intermediate levels to form nested
 * element ids.
 * Given a string or element that refers to a form, create an object from the 
 * elements of the form.
 * @see http://getahead.org/dwr/browser/util/getvalues
 */
dwr.util.getValues = function(data, options) {
  if (typeof data == "string" || dwr.util._isHTMLElement(data)) {
    return dwr.util.getFormValues(data);
  }
  else {
    var prefix = "";
    if (options != null && options.prefix) prefix = options.prefix;
    if (options != null && options.idPrefix) prefix = options.idPrefix;
    dwr.util._getValuesRecursive(data, prefix);
    return data;
  }
};

/**
 * Given a string or element that refers to a form, create an object from the 
 * elements of the form.
 * @see http://getahead.org/dwr/browser/util/getvalues
 */
dwr.util.getFormValues = function(eleOrNameOrId) {
  var ele = null;
  if (typeof eleOrNameOrId == "string") {
    ele = document.forms[eleOrNameOrId];
    if (ele == null) ele = dwr.util.byId(eleOrNameOrId);
  }
  else if (dwr.util._isHTMLElement(eleOrNameOrId)) {
    ele = eleOrNameOrId;
  }
  if (ele != null) {
    if (ele.elements == null) {
      alert("getFormValues() requires an object or reference to a form element.");
      return null;
    }
    var reply = {};
    var name;
    var value;
    for (var i = 0; i < ele.elements.length; i++) {
      if (ele[i].type in {button:0,submit:0,reset:0,image:0,file:0}) continue;
      if (ele[i].name) {
        name = ele[i].name;
        value = dwr.util.getValue(name);
      }
      else {
        if (ele[i].id) name = ele[i].id;
        else name = "element" + i;
        value = dwr.util.getValue(ele[i]);
      }
      reply[name] = value;
    }
    return reply;
  }
};

/**
 * @private Recursive helper for getValues().
 */
dwr.util._getValuesRecursive = function(data, idpath) {
  // Array containing objects -> add "[n]" to idpath and make recursive call
  // for each item object
  if (dwr.util._isArray(data) && data.length > 0 && dwr.util._isObject(data[0])) {
    for (var i = 0; i < data.length; i++) {
      dwr.util._getValuesRecursive(data[i], idpath+"["+i+"]");
    }
  }
  // Object (not array) -> handle nested object properties
  else if (dwr.util._isObject(data) && !dwr.util._isArray(data)) {
    for (var prop in data) {
      var subidpath = idpath ? idpath+"."+prop : prop;
      // Object, or array containing objects -> call ourselves recursively
      if (dwr.util._isObject(data[prop]) && !dwr.util._isArray(data[prop])
          || dwr.util._isArray(data[prop]) && data[prop].length > 0 && dwr.util._isObject(data[prop][0])) {
        dwr.util._getValuesRecursive(data[prop], subidpath);
      }
      // Functions -> skip
      else if (typeof data[prop] == "function") {
        // NOP
      }
      // Only simple values left (or array of simple values, or empty array)
      // -> call getValue()
      else {
        // Are there any elements with that id or name
        if (dwr.util.byId(subidpath) != null || document.getElementsByName(subidpath).length >= 1) {
          data[prop] = dwr.util.getValue(subidpath);
        }
      }
    }
  }
};

/**
 * Add options to a list from an array or map.
 * @see http://getahead.org/dwr/browser/lists
 */
dwr.util.addOptions = function(ele, data/*, options*/) {
  ele = dwr.util._getElementById(ele, "addOptions()");
  if (ele == null) return;
  var useOptions = dwr.util._isHTMLElement(ele, "select");
  var useLi = dwr.util._isHTMLElement(ele, ["ul", "ol"]);
  if (!useOptions && !useLi) {
    dwr.util._debug("addOptions() can only be used with select/ul/ol elements. Attempt to use: " + dwr.util._detailedTypeOf(ele));
    return;
  }
  if (data == null) return;
  
  var argcount = arguments.length;
  var options = {};
  var lastarg = arguments[argcount - 1]; 
  if (argcount > 2 && dwr.util._isObject(lastarg)) {
    options = lastarg;
    argcount--;
  }
  var arg3 = null; if (argcount >= 3) arg3 = arguments[2];
  var arg4 = null; if (argcount >= 4) arg4 = arguments[3];
  if (!options.optionCreator && useOptions) options.optionCreator = dwr.util._defaultOptionCreator;
  if (!options.optionCreator && useLi) options.optionCreator = dwr.util._defaultListItemCreator;

  var text, value, li;
  if (dwr.util._isArray(data)) {
    // Loop through the data that we do have
    for (var i = 0; i < data.length; i++) {
      options.data = data[i];
      options.text = null;
      options.value = null;
      if (useOptions) {
        if (arg3 != null) {
          if (arg4 != null) {
            options.text = dwr.util._getValueFrom(data[i], arg4);
            options.value = dwr.util._getValueFrom(data[i], arg3);
          }
          else options.text = options.value = dwr.util._getValueFrom(data[i], arg3);
        }
        else options.text = options.value = dwr.util._getValueFrom(data[i]);

        if (options.text != null || options.value) {
          var opt = options.optionCreator(options);
          opt.text = options.text;
          opt.value = options.value;
          ele.options[ele.options.length] = opt;
        }
      }
      else {
        options.value = dwr.util._getValueFrom(data[i], arg3);
        if (options.value != null) {
          li = options.optionCreator(options);
          if (dwr.util._shouldEscapeHtml(options)) {
            options.value = dwr.util.escapeHtml(options.value);
          }
          li.innerHTML = options.value;
          ele.appendChild(li);
        }
      }
    }
  }
  else if (arg4 != null) {
    if (!useOptions) {
      alert("dwr.util.addOptions can only create select lists from objects.");
      return;
    }
    for (var prop in data) {
      options.data = data[prop];
      options.value = dwr.util._getValueFrom(data[prop], arg3);
      options.text = dwr.util._getValueFrom(data[prop], arg4);

      if (options.text != null || options.value) {
        var opt = options.optionCreator(options);
        opt.text = options.text;
        opt.value = options.value;
        ele.options[ele.options.length] = opt;
      }
    }
  }
  else {
    if (!useOptions) {
      dwr.util._debug("dwr.util.addOptions can only create select lists from objects.");
      return;
    }
    for (var prop in data) {
      options.data = data[prop];
      if (!arg3) {
        options.value = prop;
        options.text = data[prop];
      }
      else {
        options.value = data[prop];
        options.text = prop;
      }
      if (options.text != null || options.value) {
        var opt = options.optionCreator(options);
        opt.text = options.text;
        opt.value = options.value;
        ele.options[ele.options.length] = opt;
      }
    }
  }

  // All error routes through this function result in a return, so highlight now
  dwr.util.highlight(ele, options); 
};

/**
 * @private Get the data from an array function for dwr.util.addOptions
 */
dwr.util._getValueFrom = function(data, method) {
  if (method == null) return data;
  else if (typeof method == 'function') return method(data);
  else return data[method];
};

/**
 * @private Default option creation function
 */
dwr.util._defaultOptionCreator = function(options) {
  return new Option();
};

/**
 * @private Default list item creation function
 */
dwr.util._defaultListItemCreator = function(options) {
  return document.createElement("li");
};

/**
 * Remove all the options from a select list (specified by id)
 * @see http://getahead.org/dwr/browser/lists
 */
dwr.util.removeAllOptions = function(ele) {
  ele = dwr.util._getElementById(ele, "removeAllOptions()");
  if (ele == null) return;
  var useOptions = dwr.util._isHTMLElement(ele, "select");
  var useLi = dwr.util._isHTMLElement(ele, ["ul", "ol"]);
  if (!useOptions && !useLi) {
    dwr.util._debug("removeAllOptions() can only be used with select, ol and ul elements. Attempt to use: " + dwr.util._detailedTypeOf(ele));
    return;
  }
  if (useOptions) {
    ele.options.length = 0;
  }
  else {
    while (ele.childNodes.length > 0) {
      ele.removeChild(ele.firstChild);
    }
  }
};

/**
 * Create rows inside a the table, tbody, thead or tfoot element (given by id).
 * @see http://getahead.org/dwr/browser/tables
 */
dwr.util.addRows = function(ele, data, cellFuncs, options) {
  ele = dwr.util._getElementById(ele, "addRows()");
  if (ele == null) return;
  if (!dwr.util._isHTMLElement(ele, ["table", "tbody", "thead", "tfoot"])) {
    dwr.util._debug("addRows() can only be used with table, tbody, thead and tfoot elements. Attempt to use: " + dwr.util._detailedTypeOf(ele));
    return;
  }
  if (!options) options = {};
  if (!options.rowCreator) options.rowCreator = dwr.util._defaultRowCreator;
  if (!options.cellCreator) options.cellCreator = dwr.util._defaultCellCreator;
  var tr, rowNum;
  if (dwr.util._isArray(data)) {
    for (rowNum = 0; rowNum < data.length; rowNum++) {
      options.rowData = data[rowNum];
      options.rowIndex = rowNum;
      options.rowNum = rowNum;
      options.data = null;
      options.cellNum = -1;
      tr = dwr.util._addRowInner(cellFuncs, options);
      if (tr != null) ele.appendChild(tr);
    }
  }
  else if (typeof data == "object") {
    rowNum = 0;
    for (var rowIndex in data) {
      options.rowData = data[rowIndex];
      options.rowIndex = rowIndex;
      options.rowNum = rowNum;
      options.data = null;
      options.cellNum = -1;
      tr = dwr.util._addRowInner(cellFuncs, options);
      if (tr != null) ele.appendChild(tr);
      rowNum++;
    }
  }

  dwr.util.highlight(ele, options);
};

/**
 * @private Internal function to draw a single row of a table.
 */
dwr.util._addRowInner = function(cellFuncs, options) {
  var tr = options.rowCreator(options);
  if (tr == null) return null;
  for (var cellNum = 0; cellNum < cellFuncs.length; cellNum++) {
    var func = cellFuncs[cellNum];
    if (typeof func == 'function') options.data = func(options.rowData, options);
    else options.data = func || "";
    options.cellNum = cellNum;
    var td = options.cellCreator(options);
    if (td != null) {
      if (options.data != null) {
        if (dwr.util._isHTMLElement(options.data)) td.appendChild(options.data);
        else {
          if (dwr.util._shouldEscapeHtml(options) && typeof(options.data) == "string") {
            td.innerHTML = dwr.util.escapeHtml(options.data);
          }
          else {
            td.innerHTML = options.data;
          }
        }
      }
      tr.appendChild(td);
    }
  }
  return tr;
};

/**
 * @private Default row creation function
 */
dwr.util._defaultRowCreator = function(options) {
  return document.createElement("tr");
};

/**
 * @private Default cell creation function
 */
dwr.util._defaultCellCreator = function(options) {
  return document.createElement("td");
};

/**
 * Remove all the children of a given node.
 * @see http://getahead.org/dwr/browser/tables
 */
dwr.util.removeAllRows = function(ele, options) {
  ele = dwr.util._getElementById(ele, "removeAllRows()");
  if (ele == null) return;
  if (!options) options = {};
  if (!options.filter) options.filter = function() { return true; };
  if (!dwr.util._isHTMLElement(ele, ["table", "tbody", "thead", "tfoot"])) {
    dwr.util._debug("removeAllRows() can only be used with table, tbody, thead and tfoot elements. Attempt to use: " + dwr.util._detailedTypeOf(ele));
    return;
  }
  var child = ele.firstChild;
  var next;
  while (child != null) {
    next = child.nextSibling;
    if (options.filter(child)) {
      ele.removeChild(child);
    }
    child = next;
  }
};

/**
 * dwr.util.byId(ele).className = "X", that we can call from Java easily.
 */
dwr.util.setClassName = function(ele, className) {
  ele = dwr.util._getElementById(ele, "setClassName()");
  if (ele == null) return;
  ele.className = className;
};

/**
 * dwr.util.byId(ele).className += "X", that we can call from Java easily.
 */
dwr.util.addClassName = function(ele, className) {
  ele = dwr.util._getElementById(ele, "addClassName()");
  if (ele == null) return;
  ele.className += " " + className;
};

/**
 * dwr.util.byId(ele).className -= "X", that we can call from Java easily
 * From code originally by Gavin Kistner
 */
dwr.util.removeClassName = function(ele, className) {
  ele = dwr.util._getElementById(ele, "removeClassName()");
  if (ele == null) return;
  var regex = new RegExp("(^|\\s)" + className + "(\\s|$)", 'g');
  ele.className = ele.className.replace(regex, '');
};

/**
 * dwr.util.byId(ele).className |= "X", that we can call from Java easily.
 */
dwr.util.toggleClassName = function(ele, className) {
  ele = dwr.util._getElementById(ele, "toggleClassName()");
  if (ele == null) return;
  var regex = new RegExp("(^|\\s)" + className + "(\\s|$)");
  if (regex.test(ele.className)) {
    ele.className = ele.className.replace(regex, '');
  }
  else {
    ele.className += " " + className;
  }
};

/**
 * Clone a node and insert it into the document just above the 'template' node
 * @see http://getahead.org/dwr/???
 */
dwr.util.cloneNode = function(ele, options) {
  ele = dwr.util._getElementById(ele, "cloneNode()");
  if (ele == null) return null;
  if (options == null) options = {};
  var clone = ele.cloneNode(true);
  if (options.idPrefix || options.idSuffix) {
    dwr.util._updateIds(clone, options);
  }
  else {
    dwr.util._removeIds(clone);
  }
  ele.parentNode.insertBefore(clone, ele);
  return clone;
};

/**
 * @private Update all of the ids in an element tree
 */
dwr.util._updateIds = function(ele, options) {
  if (options == null) options = {};
  if (ele.id) {
    ele.setAttribute("id", (options.idPrefix || "") + ele.id + (options.idSuffix || ""));
  }
  var children = ele.childNodes;
  for (var i = 0; i < children.length; i++) {
    var child = children.item(i);
    if (child.nodeType == 1 /*Node.ELEMENT_NODE*/) {
      dwr.util._updateIds(child, options);
    }
  }
};

/**
 * @private Remove all the Ids from an element
 */
dwr.util._removeIds = function(ele) {
  if (ele.id) ele.removeAttribute("id");
  var children = ele.childNodes;
  for (var i = 0; i < children.length; i++) {
    var child = children.item(i);
    if (child.nodeType == 1 /*Node.ELEMENT_NODE*/) {
      dwr.util._removeIds(child);
    }
  }
};

/**
 * Clone a template node and its embedded template child nodes according to
 * cardinalities (of arrays) in supplied data.  
 */
dwr.util.cloneNodeForValues = function(templateEle, data, options) {
  templateEle = dwr.util._getElementById(templateEle, "cloneNodeForValues()");
  if (templateEle == null) return null;
  if (options == null) options = {};
  var idpath;
  if (options.idPrefix != null)
    idpath = options.idPrefix;
  else
    idpath = templateEle.id || ""; 
  return dwr.util._cloneNodeForValuesRecursive(templateEle, data, idpath, options);
};

/**
 * @private Recursive helper for cloneNodeForValues(). 
 */
dwr.util._cloneNodeForValuesRecursive = function(templateEle, data, idpath, options) {
  // Incoming array -> make an id for each item and call clone of the template 
  // for each of them
  if (dwr.util._isArray(data)) {
    var clones = [];
    for (var i = 0; i < data.length; i++) {
      var item = data[i];
      var clone = dwr.util._cloneNodeForValuesRecursive(templateEle, item, idpath + "[" + i + "]", options);
      clones.push(clone);
    }
    return clones;
  }
  else
  // Incoming object (not array) -> clone the template, add id prefixes, add 
  // clone to DOM, and then recurse into any array properties if they contain 
  // objects and there is a suitable template
  if (dwr.util._isObject(data) && !dwr.util._isArray(data)) {
    var clone = templateEle.cloneNode(true);
    if (options.updateCloneStyle && clone.style) {
      for (var propname in options.updateCloneStyle) {
        clone.style[propname] = options.updateCloneStyle[propname];
      }
    }
    dwr.util._replaceIds(clone, templateEle.id, idpath);
    templateEle.parentNode.insertBefore(clone, templateEle);
    dwr.util._cloneSubArrays(data, idpath, options);
    return clone;
  }

  // It is an error to end up here so we return nothing
  return null;
};

/**
 * @private Substitute a leading idpath fragment with another idpath for all 
 * element ids tree, and remove ids that don't match the idpath. 
 */
dwr.util._replaceIds = function(ele, oldidpath, newidpath) {
  if (ele.id) {
    var newId = null;
    if (ele.id == oldidpath) {
      newId = newidpath;
    }
    else if (ele.id.length > oldidpath.length) {
      if (ele.id.substr(0, oldidpath.length) == oldidpath) {
        var trailingChar = ele.id.charAt(oldidpath.length);
        if (trailingChar == "." || trailingChar == "[") {
          newId = newidpath + ele.id.substr(oldidpath.length);
        }
      }
    }
    if (newId) {
      ele.setAttribute("id", newId);
    }
    else {
      ele.removeAttribute("id");
    }
  }
  var children = ele.childNodes;
  for (var i = 0; i < children.length; i++) {
    var child = children.item(i);
    if (child.nodeType == 1 /*Node.ELEMENT_NODE*/) {
      dwr.util._replaceIds(child, oldidpath, newidpath);
    }
  }
};

/**
 * @private Finds arrays in supplied data and uses any corresponding template 
 * node to make a clone for each item in the array. 
 */
dwr.util._cloneSubArrays = function(data, idpath, options) {
  for (prop in data) {
    var value = data[prop];
    // Look for potential recursive cloning in all array properties
    if (dwr.util._isArray(value)) {
      // Only arrays with objects are interesting for cloning
      if (value.length > 0 && dwr.util._isObject(value[0])) {
        var subTemplateId = idpath + "." + prop;
        var subTemplateEle = dwr.util.byId(subTemplateId);
        if (subTemplateEle != null) {
          dwr.util._cloneNodeForValuesRecursive(subTemplateEle, value, subTemplateId, options);
        }
      }
    }
    // Continue looking for arrays in object properties
    else if (dwr.util._isObject(value)) {
      dwr.util._cloneSubArrays(value, idpath + "." + prop, options);
    }
  }
}

/**
 * @private Helper to turn a string into an element with an error message
 */
dwr.util._getElementById = function(ele, source) {
  var orig = ele;
  ele = dwr.util.byId(ele);
  if (ele == null) {
    dwr.util._debug(source + " can't find an element with id: " + orig + ".");
  }
  return ele;
};

/**
 * @private Is the given node an HTML element (optionally of a given type)?
 * @param ele The element to test
 * @param nodeName eg "input", "textarea" - check for node name (optional)
 *         if nodeName is an array then check all for a match.
 */
dwr.util._isHTMLElement = function(ele, nodeName) {
  if (ele == null || typeof ele != "object" || ele.nodeName == null) {
    return false;
  }
  if (nodeName != null) {
    var test = ele.nodeName.toLowerCase();
    if (typeof nodeName == "string") {
      return test == nodeName.toLowerCase();
    }
    if (dwr.util._isArray(nodeName)) {
      var match = false;
      for (var i = 0; i < nodeName.length && !match; i++) {
        if (test == nodeName[i].toLowerCase()) {
          match =  true;
        }
      }
      return match;
    }
    dwr.util._debug("dwr.util._isHTMLElement was passed test node name that is neither a string or array of strings");
    return false;
  }
  return true;
};

/**
 * @private Like typeOf except that more information for an object is returned other than "object"
 */
dwr.util._detailedTypeOf = function(x) {
  var reply = typeof x;
  if (reply == "object") {
    reply = Object.prototype.toString.apply(x); // Returns "[object class]"
    reply = reply.substring(8, reply.length-1);  // Just get the class bit
  }
  return reply;
};

/**
 * @private Object detector. Excluding null from objects.
 */
dwr.util._isObject = function(data) {
  return (data && typeof data == "object");
};

/**
 * @private Array detector. Note: instanceof doesn't work with multiple frames.
 */
dwr.util._isArray = function(data) {
  return (data && data.join);
};

/**
 * @private Date detector. Note: instanceof doesn't work with multiple frames.
 */
dwr.util._isDate = function(data) {
  return (data && data.toUTCString) ? true : false;
};

/**
 * @private Used by setValue. Gets around the missing functionallity in IE.
 */
dwr.util._importNode = function(doc, importedNode, deep) {
  var newNode;

  if (importedNode.nodeType == 1 /*Node.ELEMENT_NODE*/) {
    newNode = doc.createElement(importedNode.nodeName);

    for (var i = 0; i < importedNode.attributes.length; i++) {
      var attr = importedNode.attributes[i];
      if (attr.nodeValue != null && attr.nodeValue != '') {
        newNode.setAttribute(attr.name, attr.nodeValue);
      }
    }

    if (typeof importedNode.style != "undefined") {
      newNode.style.cssText = importedNode.style.cssText;
    }
  }
  else if (importedNode.nodeType == 3 /*Node.TEXT_NODE*/) {
    newNode = doc.createTextNode(importedNode.nodeValue);
  }

  if (deep && importedNode.hasChildNodes()) {
    for (i = 0; i < importedNode.childNodes.length; i++) {
      newNode.appendChild(dwr.util._importNode(doc, importedNode.childNodes[i], true));
    }
  }

  return newNode;
};

/** @private Used internally when some message needs to get to the programmer */
dwr.util._debug = function(message, stacktrace) {
  var written = false;
  try {
    if (window.console) {
      if (stacktrace && window.console.trace) window.console.trace();
      window.console.log(message);
      written = true;
    }
    else if (window.opera && window.opera.postError) {
      window.opera.postError(message);
      written = true;
    }
  }
  catch (ex) { /* ignore */ }

  if (!written) {
    var debug = document.getElementById("dwr-debug");
    if (debug) {
      var contents = message + "<br/>" + debug.innerHTML;
      if (contents.length > 2048) contents = contents.substring(0, 2048);
      debug.innerHTML = contents;
    }
  }
};

/**
*
*  URL encode / decode
*  http://www.webtoolkit.info/
*
**/

var Url = {

	// public method for url encoding
	encode : function (string) {
		return escape(this._utf8_encode(string));
	},

	// public method for url decoding
	decode : function (string) {
		return this._utf8_decode(unescape(string));
	},

	// private method for UTF-8 encoding
	_utf8_encode : function (string) {
		string = string.replace(/\r\n/g,"\n");
		var utftext = "";

		for (var n = 0; n < string.length; n++) {

			var c = string.charCodeAt(n);

			if (c < 128) {
				utftext += String.fromCharCode(c);
			}
			else if((c > 127) && (c < 2048)) {
				utftext += String.fromCharCode((c >> 6) | 192);
				utftext += String.fromCharCode((c & 63) | 128);
			}
			else {
				utftext += String.fromCharCode((c >> 12) | 224);
				utftext += String.fromCharCode(((c >> 6) & 63) | 128);
				utftext += String.fromCharCode((c & 63) | 128);
			}

		}

		return utftext;
	},

	// private method for UTF-8 decoding
	_utf8_decode : function (utftext) {
		var string = "";
		var i = 0;
		var c = c1 = c2 = 0;

		while ( i < utftext.length ) {

			c = utftext.charCodeAt(i);

			if (c < 128) {
				string += String.fromCharCode(c);
				i++;
			}
			else if((c > 191) && (c < 224)) {
				c2 = utftext.charCodeAt(i+1);
				string += String.fromCharCode(((c & 31) << 6) | (c2 & 63));
				i += 2;
			}
			else {
				c2 = utftext.charCodeAt(i+1);
				c3 = utftext.charCodeAt(i+2);
				string += String.fromCharCode(((c & 15) << 12) | ((c2 & 63) << 6) | (c3 & 63));
				i += 3;
			}

		}

		return string;
	}

}


var config_enabled=false;
if(EC==null||typeof (EC)!="object"){
var EC=new Object();
}
if(EC.F==null||typeof (EC.F)!="object"){
EC.F=new Object();
}
if(EC.F["setCookie"]==null){
EC.F.setCookie=function(_1,_2){
var _3=_1+"="+escape(_2);
document.cookie=_3;
return null;
};
EC.F.getCookie=function(_4){
var _5=_4+"=";
var _6=document.cookie;
var _7=null;
if(_6.length>0){
offset=_6.indexOf(_5);
if(offset!=-1){
offset+=_5.length;
end=_6.indexOf(";",offset);
if(end==-1){
end=_6.length;
}
_7=unescape(_6.substring(offset,end));
}
}
return _7;
};
}
var jslog=new function(){
bPersistState=true;
var _8="ec_debug_logging";
var _9="jslog";
var _a=_8+"_"+_9;
var _b=config_enabled;
if(_b==false&&location.href.match(/enablejslog/)){
_b=true;
}
var _c=false;
var _d=0;
function $(o){
return document.getElementById(o);
}
function debug(_f){
logMsg("DEBUG",_f);
}
function info(msg){
logMsg("INFO",msg);
}
function warning(msg){
logMsg("WARN",msg);
}
function error(msg){
logMsg("ERROR",msg);
}
function toggleDisplay(){
var _13=$(_a+"_body");
if(_13.style.display=="none"){
_13.style.display="block";
}else{
_13.style.display="none";
}
if(bPersistState){
EC.F.setCookie(_9+"_visibility",_13.style.display);
}
}
function clearLog(){
$(_a+"_logDisplay").innerHTML="";
_d=0;
$(_a+"_handle").innerHTML=_d;
}
function enable(){
if(!_c){
initializeDisplay();
}
}
function text(_14){
$(_a+"_textArea").value=_14;
}
function getHTML(){
var _15=$(_a+"_idToInspect").value;
if(_15==""){
warning("Provide a non-blank id");
}else{
try{
var _16=$(_a+"_textArea").value=$(_15).innerHTML;
info(_15+" innerHTML is now in the text box below!");
}
catch(e){
error("Could not get innerHTML of id="+_15+": "+e.message);
}
EC.F.setCookie(_9+"_idToInspect",_15);
}
}
this.debug=debug;
this.info=info;
this.warning=warning;
this.error=error;
this.toggleDisplay=toggleDisplay;
this.clearLog=clearLog;
this.text=text;
this.enable=enable;
this.getHTML=getHTML;
function logMsg(_17,msg){
if(_b){
_d+=1;
$(_a+"_handle").innerHTML=_d;
var _19=$(_a+"_logDisplay");
if(_19.childNodes.length==0){
_19.appendChild(createDisplayRow(_17,msg));
}else{
_19.insertBefore(createDisplayRow(_17,msg),_19.childNodes[0]);
}
}
}
function createDisplayRow(_1a,_1b){
if(document.all){
var _1c="styleFloat";
}else{
var _1c="cssFloat";
}
var _1d=document.createElement("div");
if(_d/2==Math.floor(_d/2)){
_1d.style.backgroundColor="#FFF";
}else{
_1d.style.backgroundColor="#F6F6F6";
}
_1d.style.borderBottom="1px solid #AAA";
_1d.style.verticalAlign="top";
var _1e=document.createElement("div");
_1e.style.width="40px";
_1e.style.paddingLeft="3px";
_1e.style[_1c]="left";
if(_1a=="DEBUG"){
_1e.style.backgroundColor="#1515FF";
}else{
if(_1a=="INFO"){
_1e.style.backgroundColor="#10FF10";
}else{
if(_1a=="WARN"){
_1e.style.backgroundColor="yellow";
}else{
if(_1a=="ERROR"){
_1e.style.backgroundColor="#FF7070";
}
}
}
}
_1e.appendChild(document.createTextNode(_1a));
_1d.appendChild(_1e);
var _1f=document.createElement("span");
_1f.style.paddingLeft="3px";
_1f.style.paddingRight="8px";
_1e.style[_1c]="left";
_1f.appendChild(document.createTextNode(getCurrentTimeFormatted()));
_1d.appendChild(_1f);
_1d.appendChild(document.createTextNode(_1b));
var _20=document.createElement("div");
_20.style.clear="both";
_1d.appendChild(_20);
return _1d;
}
function getCurrentTimeFormatted(){
var now=new Date();
var _22=now.getHours();
var _23=now.getMinutes();
var _24=now.getSeconds();
var _25=""+((_22>12)?_22-12:_22);
if(_25=="0"){
_25=12;
}
_25+=((_23<10)?":0":":")+_23;
_25+=((_24<10)?":0":":")+_24;
_25+=(_22>=12)?" PM":" AM";
return _25;
}
if(_b){
initializeDisplay();
}
function initializeDisplay(){
if(!_c){
try{
var _26=2;
var _27=2;
var _28="none";
if(bPersistState){
try{
var _29=EC.F.getCookie(_9+"_position");
if(_29!=null){
var _2a=_29.split("|");
if(!isNaN(parseInt(_2a[0]))){
_27=_2a[0];
}
if(!isNaN(parseInt(_2a[1]))){
_26=_2a[1];
}
}
if(EC.F.getCookie(_9+"_visibility")=="block"){
_28="block";
}
}
catch(e){
}
}
var _2b=EC.F.getCookie(_9+"_idToInspect");
_2b=_2b==null?"":_2b;
document.write("<div id=\""+_a+"_container\" style=\"font-family:arial; color:black; font-size:9px; line-height:normal; letter-spacing: normal; position:absolute; z-index:10000;top:"+_26+"px; left:"+_27+"px; \">"+"<div id=\""+_a+"_handle\" style=\"cursor:move; position: absolute; background-color:#FFFFCC; border:1px solid #FF0400; color:black; padding:2px;\" ondblclick=\""+_9+".toggleDisplay()\">0</div>"+"<div id=\""+_a+"_body\" style=\"text-align:left; border:1px solid #FF0400; width:300px; position: absolute; top:20px; left:0px; background-color:white; display:"+_28+"\">"+"<div id=\""+_a+"_header\" style=\"height:10px; padding:2px; border-bottom:1px solid black; background-color:#FFFFCC;\">"+"<span id=\""+_a+"_clear\" style=\"color: blue;\" onclick=\""+_9+".clearLog()\">clear</span>"+"</div>"+"<div id=\""+_a+"_logDisplay\" style=\"height:240px; overflow:auto;\"></div>"+"<div id=\""+_a+"_footer\" style=\"padding-left:2px; border-top:1px solid black; background-color:#FFFFCC;\">"+"get html:<input id=\""+_a+"_idToInspect\" style=\"font-size:9px; height:18px;\" value=\""+_2b+"\" size=42/> <span id=\""+_a+"_go\" style=\"color: blue;\" onclick=\""+_9+".getHTML()\">go</span>"+"<textarea id=\""+_a+"_textArea\" style=\"width:99%; font-size:9px;\"></textarea>"+"</div></div></div></div>");
$(_a+"_clear").style.cursor="pointer";
$(_a+"_go").style.cursor="pointer";
if(window["Draggable"]!=null){
new Draggable(_a+"_container",{handle:_a+"_handle",revert:false,starteffect:false,endeffect:false});
if(bPersistState){
var _2c=new function(){
this.onStart=function(){
};
this.onEnd=function(s,o){
if(o.element.id==_a+"_container"){
var pos=Position.cumulativeOffset(o.element);
EC.F.setCookie(_9+"_position",+pos[0]+"|"+pos[1]);
}
};
};
Draggables.addObserver(_2c);
}
}else{
$(_a+"_handle").style.cursor="pointer";
}
_c=true;
}
catch(e){
alert("Code-level error initializing jslog: "+e.description);
}
}
}
};
debug=jslog.debug;


function clearField(id, initial){
  if($(id).value==initial){
    $(id).value="";
  }
}

window.addEvent('domready', function() {
	activateAddToCartButtons(document.body);
});

function activateAddToCartButtons(/* String|DomElement|Element? */container) {
	if (container) {
		$(container).getElements('.aggiungiAlCarrello').each(
				function(item, index) {
					var a = item.getElement('a');
					a.removeEvents('click');
					a.addEvent('click', addToCart);
				});
	}
	;

	/* START [RA] */
	$(container).getElements('.prenota').each( function(item, index) {
		var a = item.getElement('a');
		a.addEvent('click', addToCartPrenota);
	});
	$(container).getElements('.prenotaAnonimous').each( function(item, index) {
		var a = item.getElement('a');
		a.addEvent('click', gotoLogin);

	});
	/* END [RA] */
}

/* START [RA] */
function gotoLogin() {
	location.href = '/fcom/it/home/login.html?fcomRedirectUrl=' + Url
			.encode(location.href);
}
/* END [RA] */

/* START [RA] */
function addToCartPrenota(e) {
	e = new Event(e);
	var pId = this.name;
	jslog.debug('product ID = ' + pId);

	var quantity = 1;
	var feedback;

	var parent = this.getParent();
	if (parent) {
		switch (parent.getTag()) {
		case 'li':
			var buyProd = parent.getParent().getParent();
			if (buyProd && buyProd.id == 'compraProdotto') {
				var qSelect = buyProd.getElement('select[name=Quantita]');
				if (qSelect) {
					quantity = qSelect.options[qSelect.selectedIndex].text;
				}
				var tr = buyProd.getParent().getParent();
				if (tr && tr.getTag() == 'tr') {
					feedback = tr.getElement('.feedback');
				}
			}
			break;
		case 'p':
			var infoProd = parent.getParent().getParent().getParent();
			if (infoProd && infoProd.hasClass('informazioniProdotto')) {
				var prod = infoProd.getParent();
				if (prod && prod.hasClass('prodotto')) {
					feedback = prod.getElement('.feedback');
					// yellow fading
					new Fx.Style(prod, 'background-color', {
						duration :4000
					}).start('#FFFF99', '#FFFFFF');
				}
			}
			break;
		}
	}

	var callback;
	if (feedback) {
		callback = function() {
			feedback.setStyle('display', '');
			var hide = function() {
				feedback.setStyle('display', 'none');
			};
			$clear(feedback.$timer);
			feedback.$timer = hide.delay(2000);
		};
	}
	if (Cart.isVisible()) {
		Cart.update('post', {
			_eventName :'add',
			productId :pId,
			quantity :quantity,
			toBeReserved :1
		}, callback);
	} else {
		Cart.request('SidebarCart', 'post', {
			_eventName :'add',
			ajaxId :'miniCarrello',
			productId :pId,
			quantity :quantity,
			toBeReserved :1
		}, callback);
	}
	e.stop();
}
/* END [RA] */

function addToCart(e) {
	e = new Event(e);
	var pId = this.name;
	jslog.debug('product ID = ' + pId);

	var quantity = 1;
	var feedback;

	var parent = this.getParent();
	if (parent) {
		switch (parent.getTag()) {
		case 'li':
			var buyProd = parent.getParent().getParent();
			if (buyProd && buyProd.id == 'compraProdotto') {
				var qSelect = buyProd.getElement('select[name=Quantita]');
				if (qSelect) {
					quantity = qSelect.options[qSelect.selectedIndex].text;
				}
				var tr = buyProd.getParent().getParent();
				if (tr && tr.getTag() == 'tr') {
					feedback = tr.getElement('.feedback');
				}
			}
			break;
		case 'p':
			var infoProd = parent.getParent().getParent().getParent();
			if (infoProd && infoProd.hasClass('informazioniProdotto')) {
				var prod = infoProd.getParent();
				if (prod && prod.hasClass('prodotto')) {
					feedback = prod.getElement('.feedback');
					// yellow fading
					new Fx.Style(prod, 'background-color', {
						duration :4000
					}).start('#FFFF99', '#FFFFFF');
				}
			}
			break;
		}
	}

	var callback;
	if (feedback) {
		callback = function() {
			feedback.setStyle('display', '');
			var hide = function() {
				feedback.setStyle('display', 'none');
			};
			$clear(feedback.$timer);
			feedback.$timer = hide.delay(2000);
		};
	}

	if (Cart.isVisible()) {
		Cart.update('post', {
			_eventName :'add',
			productId :pId,
			quantity :quantity,
			toBeReserved :0
		}, callback);
	} else {
		Cart.request('SidebarCart', 'post', {
			_eventName :'add',
			ajaxId :'miniCarrello',
			productId :pId,
			quantity :quantity,
			toBeReserved :0
		}, callback);
	}
	e.stop();
}

function initSidebarCart(login) {
	jslog.debug('initSidebarCart()');
	var el = $('miniCarrello');
	CartAccordion.addMini(el);
	var azN = el.getElement('.azioneN');
	if (azN) {
		if (window.ie) {
			azN.setStyle('opacity', '1');
		}
		var a = azN.getElement('a');
		a.addEvent('click', function(e) {
			e = new Event(e);
			Cart.open(this);
			e.stop();
		}.bind(el));
	}
	var checkout = el.getElement('input[name=checkout]');
	if (checkout) {
		checkout
				.addEvent(
						'click',
						function(e) {
							e = new Event(e);
							location.href = '/fcom/it/home/pages/cassa/detail.html?safetyParam=' + (Math
									.random() * 1000);
							e.stop();
						});
	}
}

function initSidebarSavedProducts() {
	var el = $('miniProdottiSalvati');
	var azN = el.getElement('.azioneN');
	if (azN) {
		var a = azN.getElement('a');
		a.addEvent('click', function(e) {
			e = new Event(e);
			Cart.open(this);
			e.stop();
		}.bind(el));
	}
}

function initCartSummary(el) {
	var modifica = el.getElement('table').getElement('thead').getElement(
			'.col2').getElement('.linkOpenCart');
	if (modifica) {
		modifica.addEvent('click', openCartFromSummary.bind(el));
	}

	var cartaPiu = el.getElement('td[id=cartaPiuTd]');
	if (cartaPiu) {
		cartaPiu.getElement('a')
				.addEvent('click', openCartFromSummary.bind(el));
	}

	var voucher = el.getElement('td[id=voucherTd]');
	if (voucher) {
		voucher.getElement('a').addEvent('click', openCartFromSummary.bind(el));
	}
	
	var rimuoviAccorpamento = document.getElement('a[id=rimuoviAccorpamento]');
	if (rimuoviAccorpamento) {
		rimuoviAccorpamento
					.addEvent(
							'click',
							function(e) {
								e = new Event(e);
								location.href = '/fcom/it/home/pages/cassa/detail.html?orderData.orderMerge=false&safetyParam=' + (Math
										.random() * 1000);
								e.stop();
							});
		}
	
	
}

// Adds the events needed for a simpler editing of voucher fields.
function initVoucherInputListener() {
	return initVoucherInputListenerCommon($('voucherCodeFragment1'),
			$('voucherCodeFragment2'), $('voucherCodeFragment3'),
			$('voucherCodeFragment4'), 'cart/EditCart', 'addVoucherLink',
			'removeVoucher');
}

var actionForVoucerAddition;

function initVoucherInputListenerCommon(voucherCodeFragment1,
		voucherCodeFragment2, voucherCodeFragment3, voucherCodeFragment4,
		action, addVoucherButtonId, removeVoucherButtonId) {

	jslog.debug('function initVoucherInputListenerCommon(params): ');

	actionForVoucerAddition = action;

	if (voucherCodeFragment1) {
		voucherCodeFragment1.addEvents( {
			'keyup' : function(e) {
				e = new Event(e);
				voucherCodeFragmentHandler(voucherCodeFragment1,
						voucherCodeFragment1, voucherCodeFragment2, e);
			},
			'change' : function(e) {
				e = new Event(e);
				voucherCodeFragmentHandler(voucherCodeFragment1,
						voucherCodeFragment1, voucherCodeFragment2, e);
			}
		});
	}
	;
	if (voucherCodeFragment2) {
		voucherCodeFragment2.addEvents( {
			'keyup' : function(e) {
				e = new Event(e);
				voucherCodeFragmentHandler(voucherCodeFragment2,
						voucherCodeFragment1, voucherCodeFragment3, e);
			},
			'change' : function(e) {
				e = new Event(e);
				voucherCodeFragmentHandler(voucherCodeFragment2,
						voucherCodeFragment1, voucherCodeFragment3, e);
			}
		});
	}
	;
	if (voucherCodeFragment3) {
		voucherCodeFragment3.addEvents( {
			'keyup' : function(e) {
				e = new Event(e);
				voucherCodeFragmentHandler(voucherCodeFragment3,
						voucherCodeFragment2, voucherCodeFragment4, e);
			},
			'change' : function(e) {
				e = new Event(e);
				voucherCodeFragmentHandler(voucherCodeFragment3,
						voucherCodeFragment2, voucherCodeFragment4, e);
			}
		});
	}
	;
	if (voucherCodeFragment4) {
		voucherCodeFragment4.addEvents( {
			'keyup' : function(e) {
				e = new Event(e);
				voucherCodeFragmentHandler(voucherCodeFragment4,
						voucherCodeFragment3, $(addVoucherButtonId), e);
			},
			'change' : function(e) {
				e = new Event(e);
				voucherCodeFragmentHandler(voucherCodeFragment4,
						voucherCodeFragment3, $(addVoucherButtonId), e);
			}
		});
	}
	;
	var voucherAddButton = $(addVoucherButtonId);
	if (voucherAddButton) {
		voucherAddButton
				.addEvent(
						'click',
						function(e) {
							e = new Event(e);
							var voucherParams = {
								_eventName :'addVoucher'
							};
							voucherParams['voucherCodeFragment[0]'] = voucherCodeFragment1.value;
							voucherParams['voucherCodeFragment[1]'] = voucherCodeFragment2.value;
							voucherParams['voucherCodeFragment[2]'] = voucherCodeFragment3.value;
							voucherParams['voucherCodeFragment[3]'] = voucherCodeFragment4.value;
							if (actionForVoucerAddition != 'checkout/Checkout') {
								voucherParams['ajaxId'] = 'carrelloForm';
								voucherParams['cartCallerId'] = document
										.getElementById('cartCallerId').value;
								updateCartFormWithParams(
										'carrelloForm',
										'/' + actionForVoucerAddition + '.action',
										voucherParams);
							} else {
								voucherParams['ajaxId'] = 'carrelloFormInCheckout';
								voucherParams['orderMerge'] = document
										.getElementById('orderMerge').value;
								voucherParams['orderData.previousOrderNumberToMerge'] = document
										.getElementById('orderNumber').value;
								updateCartFormWithParams(
										'carrelloFormInCheckout',
										'/' + actionForVoucerAddition + '.action',
										voucherParams);
							}
							e.stop();
						});
	}
	;
	var voucherRemoveButton = $(removeVoucherButtonId);
	if (voucherRemoveButton) {
		voucherRemoveButton
				.addEvent(
						'click',
						function(e) {

							if (actionForVoucerAddition != 'checkout/Checkout') {
								var voucherRemoveButton = $(removeVoucherButtonId);
								e = new Event(e);
								Cart.updateWithActionPath(
										actionForVoucerAddition, 'post', {
											_eventName :'removeVoucher'
										});
								e.stop();
							} else {
								var voucherParams = {
									_eventName :'removeVoucher',
									ajaxId :'carrelloFormInCheckout'
								};
								voucherParams['orderMerge'] = document
										.getElementById('orderMerge').value;
								voucherParams['orderData.previousOrderNumberToMerge'] = document
										.getElementById('orderNumber').value;
								updateCartFormWithParams(
										'carrelloFormInCheckout',
										'/checkout/Checkout.action',
										voucherParams);
							}
						});
	}
	;
}

function voucherCodeFragmentHandler(element, previous, next, e) {

	if (element) {
		if (e.code) {
			// is not between '0' and '9' , 'a' and 'z', back and space
			if ((((e.code >= 48) && (e.code <= 57))
					|| ((e.code >= 65) && (e.code <= 90)) || (e.code == 8)) == false) {
				return;
			}
		}
		var currentVal = element.value.toUpperCase().replace('-', '');
		element.value = currentVal;
		if ((element.value.length) >= 5) {
			element.value = currentVal.substr(0, 5);
			if (next) {
				next.focus();
				if ((next.tagName == 'INPUT') && (next.type == 'text')
						&& (currentVal.length > 5)) {
					next.value = currentVal.substr(5);
					next.fireEvent('change', e);
				}
				;

			}
			;
		}
		;
		if ((previous) && ((element.value.length) <= 0)) {
			previous.focus();
			if ((previous.tagName == 'INPUT') && (previous.type == 'text')) {
				setCaretPosition(previous, previous.value.length);
			}
			;
		}
		;
	}
	;
}

function openCartFromSummary(e) {
	e = new Event(e);
	Cart.open(this, checkoutCartParams());
	e.stop();
}

function initEditCart(loginHref) {

	jslog.debug('initEditCart(' + loginHref + ')');

	var cart = $('carrello');
	var form = cart.getElement('form');

	if (form) {
		initCartForm(form);
	}

	cart.getElements('.continuaLoShopping').each( function(item) {
		var a = item.getElement('a');
		a.addEvent('click', function(e) {
			e = new Event(e);
			Cart.close();
			e.stop();
		});
	});

	var tblBuyNow, tblBuyLater;
	cart.getElements('h3').each( function(el) {
		jslog.debug(';' + el.id);
		if (el.id == 'compraOra') {
			tblBuyNow = el.getNext();
		} else if (el.id == 'compraPiuTardi') {
			tblBuyLater = el.getNext();
		}
	});
	jslog.debug(':' + tblBuyNow);
	jslog.debug(':' + tblBuyLater);

	if (tblBuyNow) {
		tblBuyNow
				.getElements('tr')
				.each(

						function(tr, index) {
							if (index == 0)
								return;

							var pId = tr.getElement('input[name=Id]').value;
							jslog.debug('pId=' + pId);

							var azPs = tr.getElement('.col5').getElements(
									'.azioneP');

							var lnkRemove = azPs[0].getElement('a');

							lnkRemove['prodId'] = pId;
							lnkRemove
									.addEvent(
											'click',
											function(e) {
												e = new Event(e);
												e.stop();
												var deleteFunction = function() {
													var params = {
														_eventName :'remove',
														productId :pId
													};
													if ($('miniCarrello'))
														params['incSidebarCart'] = true;
													if ($('miniProdottiSalvati'))
														params['incProdSaved'] = true;
													Cart.update('post', params);
												};

												if (lnkRemove.id == 'addwishList'
														|| lnkRemove.id == 'addwishListAnonymous') {
													var btnStyle = {
														color :"#E20A16",
														borderBottom :"2px solid #E20A16",
														fontWeight :"bold",
														align :"middle",
														marginRight :"10px",
														cursor :"pointer"
													};

													var btn1El = new Element(
															'span');
													btn1El
															.appendText('Si, Sposta nella WishList');
													btn1El.setStyles(btnStyle);

													if (lnkRemove.id == 'addwishList') {
														btn1El
																.addEvent(
																		"click",
																		function(
																				e) {
																			var params = {
																				_eventName :'add',
																				productId :pId
																			};
																			doAjaxRequest(new Ajax(
																					'/SidebarWishlist.action',
																					{
																						method :'post',
																						data :params,
																						update :null,
																						evalScripts :true,
																						onComplete : function() {
																							if ($('catalogoWishlist')) {
																								new Ajax(
																										'/UserWishlistContent.action',
																										{
																											method :'post',
																											data :Object
																													.toQueryString( {
																														_eventName :'show',
																														fromUpdatePage :true
																													}),
																											update :$('catalogoWishlist'),
																											evalScripts :true,
																											onComplete : function() {
																												$('catalogoWishlist').style.display = 'block';
																												AjaxSpinner
																														.hide();
																											}
																										})
																										.request();
																							} else
																								AjaxSpinner
																										.hide();
																						}
																					}));

																			var params = {
																				_eventName :'remove',
																				productId :pId
																			};
																			if ($('miniCarrello'))
																				params['incSidebarCart'] = true;
																			if ($('miniProdottiSalvati'))
																				params['incProdSaved'] = true;
																			Cart
																					.update(
																							'post',
																							params);

																		});
													} else if (lnkRemove.id == 'addwishListAnonymous') {
														btn1El
																.addEvent(
																		"click",
																		function(
																				e) {
																			location.href = '/fcom/it/home/login.html?fcomRedirectUrl=' + Url
																					.encode(location.href)
																		});
													}

													var btn2El = new Element(
															'span');
													btn2El
															.appendText('No, Rimuovi dal carrello');
													btn2El.setStyles(btnStyle);
													btn2El
															.addEvent(
																	"click",
																	function(e) {
																		deleteFunction
																				.apply(e);
																	});

													var btn3El = new Element(
															'span');
													btn3El
															.appendText('Annulla');
													btn3El.setStyles(btnStyle);
													btn3El
															.addEvent(
																	"click",
																	function(e) {
																		MOOdalAlert
																				.close();
																	});

													openAlertMOOdalBox(
															"Vuoi spostare il prodotto nella WishList?",
															new Array(btn1El,
																	btn2El,
																	btn3El),
															350, 400);

												} else {
													openConfirmMOOdalBox(
															"Rimuovere il prodotto dal carrello?",
															deleteFunction,
															null, null, "OK",
															false);
												}
											});

							/*
							 * var lnkSaveForLater = azPs[1].getElement('a');
							 * lnkSaveForLater['prodId'] = pId;
							 * lnkSaveForLater.addEvent('click', function(e) { e =
							 * new Event(e); var params = { _eventName:
							 * 'saveForLater', productId: this.prodId }; if
							 * ($('miniCarrello')) params['incSidebarCart'] =
							 * true; if ($('miniProdottiSalvati'))
							 * params['incProdSaved'] = true;
							 * Cart.update('post', params); e.stop(); });
							 */

							var selQuantity = tr
									.getElement('select[name=Quantita]');
							selQuantity['prodId'] = pId;
							var fnQuantity = function(e) {
								e = new Event(e);
								var q = this.options[this.selectedIndex].text;
								Cart.update('post', {
									_eventName :'changeQuantity',
									productId :this.prodId,
									quantity :q
								});
								e.stop();
							};
							selQuantity.addEvent('change', fnQuantity);
						});

	}

	if (tblBuyLater) {
		tblBuyLater
				.getElements('tr')
				.each(
						function(tr, index) {
							if (index == 0)
								return;

							var pId = tr.getElement('input[name=Id]').value;
							jslog.debug('pId=' + pId);

							var azPs = tr.getElement('.col5').getElements(
									'.azioneP');
							var numLinkazPs = azPs.length;

							var lnkRemove = azPs[0].getElement('a');
							lnkRemove['prodId'] = pId;
							lnkRemove
									.addEvent(
											'click',
											function(e) {
												e = new Event(e);
												e.stop();
												var deleteFunction = function() {
													var params = {
														_eventName :'remove',
														productId :pId
													};
													if ($('miniCarrello'))
														params['incSidebarCart'] = true;
													if ($('miniProdottiSalvati'))
														params['incProdSaved'] = true;
													Cart.update('post', params);
												};
												openConfirmMOOdalBox(
														"Rimuovere il prodotto dal carrello?",
														deleteFunction, null,
														null, "OK", false);
											});

							if (numLinkazPs == 3) {
								var lnkAddToWishlist = azPs[1].getElement('a');
								lnkAddToWishlist['prodId'] = pId;
								lnkAddToWishlist
										.addEvent(
												'click',
												function(e) {
													e = new Event(e);
													AjaxSpinner.show();
													new Ajax(
															'/SidebarWishlist.action',
															{
																method :'post',
																data :Object
																		.toQueryString( {
																			_eventName :'add',
																			ajax :true,
																			productId :this.prodId
																		}),
																update :$('miniWishList'),
																evalScripts :true,
																onComplete : function() {
																	if ($('catalogoWishlist')) {
																		new Ajax(
																				'/UserWishlistContent.action',
																				{
																					method :'post',
																					data :Object
																							.toQueryString( {
																								_eventName :'show',
																								fromUpdatePage :true
																							}),
																					update :$('catalogoWishlist'),
																					evalScripts :true,
																					onComplete : function() {
																						$('catalogoWishlist').style.display = 'block';
																						AjaxSpinner
																								.hide();
																					}
																				})
																				.request();
																	} else
																		AjaxSpinner
																				.hide();
																}
															}).request();
													e.stop();
												});
							}

							var lnkUnsaveForLater = azPs[numLinkazPs - 1]
									.getElement('a');
							lnkUnsaveForLater['prodId'] = pId;
							lnkUnsaveForLater.addEvent('click', function(e) {
								e = new Event(e);
								var params = {
									_eventName :'unsaveForLater',
									productId :this.prodId
								};
								if ($('miniCarrello'))
									params['incSidebarCart'] = true;
								if ($('miniProdottiSalvati'))
									params['incProdSaved'] = true;
								Cart.update('post', params);
								e.stop();
							});
						});
	}

	var msgLogin = $('messaggioLogIn');
	if (msgLogin) {
		new LoginLink(msgLogin.getElement('a'), 'fcomRedirectUrl', true);
	}
}

function initCartForm(form) {

	jslog.debug('initCartForm(' + form + ')');
	var fnShip = function(e) {
		jslog.debug('fnShip()');
		this.removeEvent('click', fnShip);
		var label = this.getNext();
		var radios = this.form.getElements('input[name=italyShipping]');
		var other = radios[0] != this ? radios[0] : radios[1];
		other.addEvent('click', fnShip);
		updateCartForm();
	};

	form.getElements('input[name=italyShipping]').each( function(radio) {
		jslog.debug(radio.id + ' ' + radio.checked);
		if (!radio.checked) {
			radio.addEvent('click', fnShip);
		}
	});

	var selCountry = form.getElement('select[name=countryCode]');
	if (selCountry) {
		selCountry.addEvent('change', function(e) {
			updateCartForm();
		});
	}

	var fnPay = function(e) {
		jslog.debug('fnPay()');
		this.removeEvent('click', fnPay);
		var radios = this.form.getElements('input[name=paymentType]');
		var other = radios[0] != this ? radios[0] : radios[1];
		other.addEvent('click', fnPay);
		updateCartForm();
	};

	form.getElements('input[name=paymentType]').each( function(radio) {
		if (!radio.checked) {
			radio.addEvent('click', fnPay);
		}
	});

	form.getElements('select[name=redemptionSelect]').each( function(select) {
		select.addEvent('change', function(e) {
			updateCartForm();
		});
	});

	var msgLogin = $('logInCartaPiu');
	if (msgLogin) {
		new LoginLink(msgLogin.getElement('a'), 'fcomRedirectUrl', true);
	}

	initVoucherInputListener();
}

function updateCartForm() {
	jslog.debug('updateCartForm()');
	updateCartFormWithFormIdAndActionPath('carrelloForm',
			'/cart/EditCart.action', 'update', 'edit-cart');
}

function updateCartFormWithFormIdAndActionPath(carrelloForm, actionPath,
		eventName, ajaxId) {
	jslog.debug('updateCartFormWithFormIdAndActionPath()');

	var temp = new Element('div');
	temp.id = 'ajax-temp';

	temp.setStyle('display', 'none');
	temp.injectInside(document.body);

	if (ajaxId == null)
		ajaxId = carrelloForm;

	AjaxSpinner.show();
	var params = [ '_eventName=' + eventName, 'ajaxId=' + ajaxId,
			$(carrelloForm).toQueryString() ].join('&');

	jslog.debug(params);
	doAjaxRequest(new AjaxMany(
			'' + actionPath, {
				method :'post',
				data :params,
				update :temp,
				evalScripts :true,
				onComplete : function() {
					AjaxSpinner.hide();
				}
			}));
}

function updateCartFormWithParams(carrelloForm, actionPath, params) {
	jslog.debug('updateCartFormWithParams()');

	var temp = new Element('div');
	temp.id = 'ajax-temp';

	temp.setStyle('display', 'none');
	temp.injectInside(document.body);

	AjaxSpinner.show();
	doAjaxRequest(new AjaxMany(
			'' + actionPath, {
				method :'post',
				data :params,
				update :temp,
				evalScripts :true,
				onComplete : function() {
					AjaxSpinner.hide();
				}
			}));
}

var Cart = {
	init : function() {
		jslog.debug('Cart.init()');
		this.wrapper = new Element('div', {
			id :'cartwrapper',
			styles : {
				display :'none'
			}
		}).injectInside($('contenitore'));
		this.cart = $('carrello') || new Element('div', {
			id :'carrello'
		});
		this.cart.injectInside(this.wrapper);
	},

	open : function(caller, params) {
		jslog.debug('Cart.open()');
		params = params || {};
		params['_eventName'] = 'show';
		params['ajaxId'] = 'edit-cart';
		params['cartCallerId'] = caller.id;
		this.update('get', params, this.slideIn.bind(this));
		return false;
	},

	slideIn : function() {
		jslog.debug('Cart.slideIn()');
		this.wrapper.setStyles( {
			left :'965px',
			width :'0',
			display :''
		});
		var myEffects = new Fx.Styles(this.wrapper, {
			duration :300,
			transition :Fx.Transitions.linear
		});
		if (window.ie) {
			setSelectVisibility('contenuti', false);
			setSelectVisibility('colonnaDx', false);
		}
		myEffects.start( {
			'left' : [ '965px', '194px' ],
			'width' : [ '0px', '775px' ]
		}).chain(
				function() {
					if (this.recommProdId) {
						var h2 = $('raccomandazioniCart');
						if (h2) {
							loadAjaxRecommendations(this.recommProdId, h2
									.getNext(), 10, function() {
								var div = h2.getNext();
								if (div.getElementsByTagName('*').length > 0) {
									h2.setStyle('display', '');
									div.setStyle('display', '');
								}
								carouselInit('carrello');
							});
						}
					}
				}.bind(this));
		return false;
	},

	isVisible : function() {
		return this.wrapper.getStyle('display') != 'none';
	},

	slideOut : function(callback) {
		jslog.debug('Cart.slideOut()');
		this.wrapper.setStyles( {
			left :'210px',
			width :'965px',
			display :''
		});
		var myEffects = new Fx.Styles(this.wrapper, {
			duration :300,
			transition :Fx.Transitions.linear
		});
		myEffects.start( {
			'left' : [ '210px', '965px' ],
			'width' : [ '755px', '0px' ]
		}).chain( function() {
			this.wrapper.setStyle('display', 'none');
			if (window.ie) {
				setSelectVisibility('contenuti', true);
				setSelectVisibility('colonnaDx', true);
			}

			// YouEffe bug fix 759 redmine
				if (typeof refreshPage == "function") {
					refreshPage();
				}
				if (callback) {
					callback();
				}
			}.bind(this));
		return false;
	},

	close : function() {
		jslog.debug('Cart.close()');
		var params = {
			_eventName :'hide',
			ajaxId :'edit-cart'
		};
		if ($('miniCarrello'))
			params['incSidebarCart'] = true;
		if ($('riepilogo'))
			params['incCartSummary'] = true;
		this.slideOut(this.update.bind(this, [ 'get', params ]));
		return false;
	},

	update : function(method, params, callback) {
		return this.updateWithActionPath('cart/EditCart', method, params,
				callback);
	},

	updateWithActionPath : function(actionPath, method, params, callback) {
		jslog.debug('Cart.updateWithActionPath()');
		var qParams = 'ajaxId=edit-cart';
		if (this.cart) {
			var fn = function(hidden) {
				if (hidden && hidden.value) {
					params[hidden.name] = hidden.value;
				}
			};
			fn(this.cart.getElement('input[name=cartCallerId]'));
			this.cart.getElements('input[name^=redemptionPointsUsages]').each(
					fn);
			var form = this.cart.getElement('form');
			if (form) {
				qParams = [ qParams, form.toQueryString() ].join('&');
			}
		}
		qParams = [ qParams, Object.toQueryString(params) ].join('&');
		jslog.debug(qParams);
		this.requestWithActionPath(actionPath, method, qParams, callback);
	},

	request : function(action, method, params, callback) {
		return this.requestWithActionPath('cart/' + action, method, params,
				callback)
	},

	requestWithActionPath : function(actionPath, method, params, callback) {

		jslog.debug('Cart.request(' + actionPath + ', ' + method + ')');
		this.recomm = this.cart ? this.cart.getElement('.boxContenuti') : null;
		AjaxSpinner.show();
		doAjaxRequest(new AjaxMany(
				'/' + actionPath + '.action',
				{
					method :method,
					data :params,
					evalScripts :true,
					onComplete : function() {
						this.ajaxComplete(callback);
					}.bind(this)
				}));
	},

	ajaxComplete : function(callback) {

		jslog.debug('Cart.ajaxComplete()');
		AjaxSpinner.hide();
		this.cart = $('carrello');
		if (this.cart) {
			if (this.cart.getStyle('display') != 'none') {
				if (this.recomm) {
					this.cart.getElement('.boxContenuti').replaceWith(
							this.recomm);
					var h2 = this.recomm.getPrevious();
					h2
							.setStyle('display', this.recomm
									.getElementsByTagName('*').length > 0 ? ''
									: 'none');
				}
				var feedback = $('messaggioFeedbackCarrello');
				if (feedback) {
					var hide = function() {
						feedback.setStyle('display', 'none');
					};
					feedback.$timer = hide.delay(2000);
				}
				// } else {
				// this.slideOut();
			}
		}
		var newTotalDiv = $('_total');
		if (newTotalDiv) {
			var checkoutPayment = $('pagamento');
			if (checkoutPayment && checkoutPayment.hasClass('attivo')) {
				jslog.debug('>' + checkoutPayment.getElement('dl'));
				jslog.debug('>' + newTotalDiv.getElement('dl'));
				checkoutPayment.getElement('dl').replaceWith(
						newTotalDiv.getElement('dl'));
			}
			newTotalDiv.remove();
		}
		var alertContainer = $('alert-container');
		if (alertContainer) {
			MOOdalAlert.open($(alertContainer.firstChild));
			alertContainer.remove();
		}
		if (callback)
			callback();
	}

};

// startup
Window.onDomReady(Cart.init.bind(Cart));

var CartAccordion = {

	init : function() {
		var minis = [ $('miniCarrello'), $('miniWishList'), $('miniScaffale') ]
				.filter( function(mini) {
					return mini != null;
				});
		if (minis.length > 0) {
			var div = new Element('div', {
				id :'accordion'
			});
			div.injectBefore(minis[0]);
			div.adopt(minis);
			minis.each( function(mini) {
				this.cook(mini);
			}, this);
			this.accordion = new FcomAccordion('h2.atStart', 'div.atStart',
					div, {
						display :false,
						onComplete : function() {
							if (this.previous >= 0) {
								var el = this.elements[this.previous];
								el.setStyle('height', el.offsetHeight + 'px');
							}
						}
					});
			minis.each( function(mini) {
				mini.setStyle('display', '');
			});
			this.accordion.display(0);
		}
	},

	addMini : function(mini) {
		if (this.cook(mini)) {
			var els = mini.getChildren();
			this.accordion.addSection(els[0], els[1]);
			// stop the FcomAccordion.initialize display effect, if still in
			// progress
			this.accordion.stop(true);
			this.accordion.display(els[1]);
		}
	},

	cook : function(mini) {
		var children = mini.getChildren();
		var h2 = children[0];
		if (h2.hasClass('atStart'))
			return false;
		h2.addClass('atStart');
		var container = new Element('div');
		container.addClass('atStart');
		container.injectAfter(h2);
		container.adopt(children.filter( function(child, index) {
			return index > 0;
		}));
		return true;
	},

	LazySectionLoader :new Class(
			{

				initialize : function(section, actionUrl) {
					this.section = section;
					this.actionUrl = actionUrl;
					this.anchor = this.section.getElement('h2.atStart')
							.getElement('a');
					this.boundAnchorClick = this.anchorClick
							.bindWithEvent(this);
					this.anchor.addEvent('click', this.boundAnchorClick);
				},

				anchorClick : function(e) {
					this.anchor.removeEvent('click', this.boundAnchorClick);
					new Event(e).stop();
					AjaxSpinner.show();
					var temp = new Element('div').setStyles( {
						display :'none'
					}).injectInside(document.body);
					doAjaxRequest(new Ajax(this.actionUrl, {
						method :'get',
						data : {
							_eventName :'load',
							ajax :true
						},
						update :temp,
						evalScripts :true,
						onComplete : function() {
							this.section.getElement('div.atStart').adopt(
									temp.getChildren().filter(
											function(item, index) {
												return index > 0;
											}));
							temp.remove();
							AjaxSpinner.hide();
							this.section.getElement('h2.atStart').fireEvent(
									'click');
						}.bind(this)
					}));
				}

			})

};

Window.onDomReady(CartAccordion.init.bind(CartAccordion));

function asyncSidebarCart(openCart) {
	doAjaxRequest(new AjaxMany(
			'/cart/SidebarCart.action', {
				method :'get',
				data : {
					ajaxId :'miniCarrello',
					safetyParam :(Math.random() * 1000)
				},
				evalScripts :true,
				onComplete : function() {
					if (openCart) {
						var mc = $('miniCarrello');
						if (mc) {
							var azN = mc.getElement('.azioneN');
							if (azN) {
								var link = azN.getElement('a');
								Cart.open(link);
							}
						}
					}
				}
			}));
}

function addItem_trackset(cartId,prodId,prodTitle,prodCategory,prodPrice,prodQuantity,pageName){
		if(typeof sendevent  == 'function') {
			sendevent('_addItem', cartId,prodId,prodTitle, prodCategory,prodPrice, prodQuantity, pageName);
		}
}
		
function addTransaction_trackset(transaction_id,transaction_cartID,transaction_total,transaction_tax,transaction_shipping,transaction_status,transaction_state,transaction_city){
		if(typeof sendevent  == 'function') {
			sendevent('_addTransaction', transaction_id,transaction_cartID,transaction_total, transaction_tax,transaction_shipping, transaction_status, transaction_state,transaction_city);
		}
}		
function empyCart(cartId) {
	if(typeof sendevent  == 'function') {
		sendevent('_emptyCart',cartId);
	}	
}        


// All the checkout related scripts.

var CheckoutManager = {

	init : function() {
		this.historyKey = 'checkout';

		this.history = HistoryManager.register(this.historyKey, [ null, null,
				null ], function(values) {
			jslog.debug('onMatch(' + values.join(',') + ')');
			if (values[0]) {
				updateWizardForm('',
						'show-shipment');
			} else if (values[1]) {
				updateWizardForm('',
						'show-personal-data');
			} else if (values[2]) {
				updateWizardForm('',
						'show-payment');
			}
		}.bind(this), function(values) {
			jslog.debug('onGenerate(' + values.join(',') + ')');
			if (values[0]) {
				return values[0];
			} else if (values[1]) {
				return values[1];
			} else if (values[2]) {
				return values[2];
			}
		}.bind(this), /(riepilogo)|(formDatiPersonali)|(formPagamento)/);
	}

};

function updateWizardForm(contextPath,eventName){
	updateWizardFormWithForm(contextPath, eventName,'checkoutWizardForm');
}

function updateWizardFormWithForm(contextPath, eventName,form) {
	// Cancel is ignored, there is always a forced post.
	jslog.debug('updateWizardForm()');
	// $('wizardForm').getElement('input[name=_eventName]').value = eventName;
	AjaxSpinner.show();
	doAjaxRequest(new Ajax(contextPath + '/checkout/Checkout.action', {
		method :'post',
		data : [ '_eventName=' + eventName,
				$(form).toQueryString() ].join('&'),
		update :$('checkoutWizardFormContainer'),
		evalScripts :true,
		onComplete : function() {
			AjaxSpinner.hide();
		}
	}));
}

function initCheckoutAnonymous(contextPath) {
	jslog.debug('checkout-anonymous: domready');
	var start = document.getElement('input[name=enter-anonymous-order]');
	start.addEvent('click', function(e) {
		jslog.debug('button: click');
		e = new Event(e);
		updateWizardForm(contextPath, 'show-shipment');
		e.stop();
	});
}

function registerBackToCartButton()
{
	var BackToCart = document.getElement('a[id=BackToCart]');
	if (BackToCart) {
		jslog.debug('registerBackToCartButton');
		BackToCart
				.addEvent(
						'click',
						function(e) {
							e = new Event(e);
							location.href = '/fcom/it/home/pages/cassa/detail.html?safetyParam=' + (Math
									.random() * 1000);
							e.stop();
						});
	}
}

// All functions for first step.
function initCheckoutShippingAddress(contextPath, hasValidationErrors,
		hasInvalidStoredAddress) {
	jslog.debug('checkout-shipping: domready');

	registerBackToCartButton();
	
	// CheckoutManager.history.setValues([ 'formSpedizione', null, null ]);
	CheckoutManager.history.setValues( [ 'riepilogo', null, null ]);

	// Forcing a reset of radiobutton in case of stored address error.
	if (hasInvalidStoredAddress) {
		document.getElements('input[name=orderData.shippingAddressId]').each(
				function(radio) {
					radio.checked = false;
				});
		if ($('aggiungiIndirizzo')) {
			$('aggiungiIndirizzo').checked = true;
		}
		;
	}
	;

	if(document.getElement('input[id=accorpano]'))
	{
		var radio = document.getElement('input[id=accorpano]');
		    if (!radio.checked)
		    {
			radio.addEvent('click', function(e) {
				document.getElementById('orderNumber').value = '';		
				updateCheckout();
					});
		    }
	}
	
			
			
	if(document.getElement('input[id=accorpasi]'))
	{		
			var radio = document.getElement('input[id=accorpasi]');
		    if (!radio.checked)
		    {
			radio.addEvent('click', function(e) {
				updateCheckout();
					});
		    }
	}
	
	//fine modifiche

	// [EL] regalo ordine
	// Adding enable/disable modify augurymessage
	document.getElements('input[name=orderData.present]').each(
			function(radio) {
				if(!radio.checked)
				{
				jslog.debug('register event Present Message radio');
				radio.addEvent('click', function(e) {
					updateCheckout();
				});
				}
			});

	// Adding enable/disable new address to radiobuttons.
	document.getElements('input[name=orderData.shippingAddressId]').each(
			function(radio) {
				if(!radio.checked)
				{
				radio.addEvent('click', function(e) {
					updateCheckout();
				});
				}
			});

	// Reloads the page to show/hide modify of invoice data.
	if (document.getElement('input[name=orderData.invoice]')) {
		document.getElement('input[name=orderData.invoice]').addEvent('click',
				function(e) {
					updateCheckout();
				});
	}
	;

	// Gift message has no cost, so no reload is needed.
	if ($('no')) {
		$('no').addEvent('click', function(e) {
			$('bigliettoAuguri').setStyle('display', 'none');
		});
	}
	;
	if ($('si')) {
		$('si').addEvent('click', function(e) {
			$('bigliettoAuguri').setStyle('display', 'block');
		});
	}
	;
	

	document.getElements('input[name=shippingType]').each(
			function(radio) {
				jslog.debug('register event ShippingType radio');
				if (! radio.checked)
				{
				radio.addEvent('click', function(e) {
					if (radio.value != 'ABROAD')
					{
						var abroadAddressCountry = document.getElement('select[name=abroadAddressCountry]');
						if (abroadAddressCountry)
						{
							document.getElement('select[name=abroadAddressCountry]').value='';
						}
					}
					updateCheckout();
				});
				}
			});

	
		
	// Province updater event, at page loading the provincia is already
	// populated, but here may not exists.
	//var countrySelect = document.getElement('select[name=orderData.shippingAddress.country]');
	  var countrySelect = document.getElement('select[name=abroadAddressCountry]');
		if (countrySelect != null) {
			countrySelect.addEvent('change', function(e) {
				jslog.debug('checkoutForm: change country');
				// provinceUpdater(countrySelect,
				// document.getElement('select[name=orderData.shippingAddress.prov]'));
					// Changing provincia is not the only thing to do: changing
					// country means different shipping costs and a recomputation is
					// needed.
					updateCheckout();
				});
		}
		;
		
		var citySelect = document.getElement('select[name=pdvCity]');
		if (citySelect != null) {
			citySelect.addEvent('change', function(e) {
				jslog.debug('checkoutForm: change pdv city');
					updateCheckout();
				});
		}
		;
		
	var btn = document.getElement('input[name=submit-shipping]');
	if(btn)
	{
	btn.addEvent('click', function(e) {
		jslog.debug('submit-shipment: click');
		e = new Event(e);
		updateWizardForm(contextPath, 'submit-shipment');
		e.stop();
	});
	}
	
	var aPersonalData = $('datiPersonali').getElement('.modifica');
	if (aPersonalData) {
		aPersonalData.addEvent('click', function(e) {
			jslog.debug('personal-data: click');
			e = new Event(e);
			updateWizardForm(contextPath, 'show-personal-data');
			e.stop();
		});
	}
	;
	
	// Adding event to privacy link.
	document.getElements('a[id=informativaPrivacyLink]').each(
		function(modalLink){	
		modalLink.addEvent(
				'click',
				function(e) {
					e = new Event(e);
					MOOdalBox.open(contextPath
							+ "/fcom/it/home/pages/infoutili/privacy.content", // the
																				// link
																				// URL
							'Informativa privacy', // the caption (link's
													// title) - can be blank
							"500 500" // width and height of the box - can be
										// left blank
					);
					e.stop();
				});
	})
	;
}

function initButtonsAtCheckoutPersonalData(contextPath, hasValidationErrors){
	CheckoutManager.history.setValues( [ null, 'formDatiPersonali', null ]);

	var btn = document.getElement('input[name=submit-personal-data]');
	btn.addEvent('click', function(e) {
		jslog.debug('submit-personal-data button registered');
		jslog.debug('button: click');
		e = new Event(e);
		updateWizardForm(contextPath, 'submit-personal-data');
		e.stop();
	});

	var aShipment = $('spedizione').getElement('.modifica');
	if (aShipment) {
		aShipment.addEvent('click', function(e) {
			jslog.debug('show-shipment: button registered');
			e = new Event(e);
			updateWizardForm(contextPath, 'show-shipment');
			e.stop();
		});
	}
}


// All functions for second step.
function initCheckoutBillingAddress(contextPath, hasValidationErrors) {


	// Province updater event, at page loading the provincia is already
	// populated.
	var countrySelect = document.getElement('select[name=orderData.billingAddress.country]');
	
	if (countrySelect)
	{
		countrySelect.addEvent(
					'change',
					function(e) {
						jslog.debug('checkoutForm: change country');
						provinceUpdater(
								document
										.getElement('select[name=orderData.billingAddress.country]'),
								document
										.getElement('select[name=orderData.billingAddress.prov]'));
					});
	// This is done in javascript because instantiating a new shippingAddress in
	// java (just for the country) has too many unwanted side effects.
	if ((document.getElement('select[name=orderData.billingAddress.country]').value == null)
			|| (document
					.getElement('select[name=orderData.billingAddress.country]').value == '')) {
		document.getElement('select[name=orderData.billingAddress.country]').value = 'IT';
		provinceUpdater(
				document
						.getElement('select[name=orderData.billingAddress.country]'),
				document
						.getElement('select[name=orderData.billingAddress.prov]'));
	}
	;
	}

	// Enable/disable company fields.
	if (document
			.getElement('input[name=orderData.billingAddress.invoiceToCompany]').checked) {
		document.getElement('input[name=orderData.billingAddress.company]').disabled = false;
		document.getElement('input[name=orderData.billingAddress.company]').style.backgroundColor="white";
		document.getElement('input[name=orderData.billingAddress.vatNumber]').disabled = false;
		document.getElement('input[name=orderData.billingAddress.vatNumber]').style.backgroundColor="white";
		document.getElement('input[name=orderData.billingAddress.name]').disabled = true;
		document.getElement('input[name=orderData.billingAddress.name]').style.backgroundColor="lightGrey";
		document.getElement('input[name=orderData.billingAddress.lastname]').disabled = true;
		document.getElement('input[name=orderData.billingAddress.lastname]').style.backgroundColor="lightGrey";
	} else {
		document.getElement('input[name=orderData.billingAddress.company]').disabled = true;
		document.getElement('input[name=orderData.billingAddress.company]').style.backgroundColor="lightGrey";
		document.getElement('input[name=orderData.billingAddress.vatNumber]').disabled = true;
		document.getElement('input[name=orderData.billingAddress.vatNumber]').style.backgroundColor="lightGrey";
		document.getElement('input[name=orderData.billingAddress.name]').disabled = false;
		document.getElement('input[name=orderData.billingAddress.name]').style.backgroundColor="white";
		document.getElement('input[name=orderData.billingAddress.lastname]').disabled = false;
		document.getElement('input[name=orderData.billingAddress.lastname]').style.backgroundColor="white";
	}
	;
	document
			.getElement('input[name=orderData.billingAddress.invoiceToCompany]')
			.addEvent(
					'click',
					function(e) {
						jslog.debug('showHideCompanyDetail: click');
						if (document.getElement('input[name=orderData.billingAddress.invoiceToCompany]').checked) {
							
							document.getElement('input[name=orderData.billingAddress.company]').disabled = false;
							document.getElement('input[name=orderData.billingAddress.company]').style.backgroundColor="white";
							document.getElement('input[name=orderData.billingAddress.vatNumber]').disabled = false;
							document.getElement('input[name=orderData.billingAddress.vatNumber]').style.backgroundColor="white";
							document.getElement('input[name=orderData.billingAddress.name]').disabled = true;
							document.getElement('input[name=orderData.billingAddress.name]').style.backgroundColor="lightGrey";
							document.getElement('input[name=orderData.billingAddress.lastname]').disabled = true;
							document.getElement('input[name=orderData.billingAddress.lastname]').style.backgroundColor="lightGrey";
						} else {
							document.getElement('input[name=orderData.billingAddress.company]').disabled = true;
							document.getElement('input[name=orderData.billingAddress.company]').style.backgroundColor="lightGrey";
							document.getElement('input[name=orderData.billingAddress.vatNumber]').disabled = true;
							document.getElement('input[name=orderData.billingAddress.vatNumber]').style.backgroundColor="lightGrey";
							document.getElement('input[name=orderData.billingAddress.name]').disabled = false;
							document.getElement('input[name=orderData.billingAddress.name]').style.backgroundColor="white";
							document.getElement('input[name=orderData.billingAddress.lastname]').disabled = false;
							document.getElement('input[name=orderData.billingAddress.lastname]').style.backgroundColor="white";
						}
						;
					});

	// Adding event to privacy link.
	if (document.getElement('a[id=informativaPrivacyLink]')) {
		document.getElement('a[id=informativaPrivacyLink]').addEvent(
				'click',
				function(e) {
					e = new Event(e);
					MOOdalBox.open(contextPath
							+ "/fcom/it/home/pages/infoutili/privacy.content", // the
																				// link
																				// URL
							'Informativa privacy', // the caption (link's
													// title) - can be blank
							"500 500" // width and height of the box - can be
										// left blank
					);
					e.stop();
				});
	}
	;
}

// All functions for second step.
function initCheckoutPayment(contextPath, hasValidationErrors,
		procurementMessage) {
	jslog.debug('checkout-payment: domready');
	registerBackToCartButton();
	CheckoutManager.history.setValues( [ null, null, 'formPagamento' ]);

	// Removes manually any spinner left
	if (hasValidationErrors == true) {
		AjaxSpinner.hide();
	}
	;

	// Shows alert and cart when there are procurement errors.
	if ((hasValidationErrors == true) && (procurementMessage != null)
			&& (procurementMessage.length > 0)) {
		openConfirmMOOdalBox(procurementMessage, function() {
			Cart.open($('riepilogo'), checkoutCartParams());
		}, 400, null, "Modifica", true);
	}
	;

	var fnPay = function(e) {
		jslog.debug('fnPay()');
		this.removeEvent('click', fnPay);
		switch (this.id) {
		case 'cc':
			jslog.debug('cartaDiCredito');
			$('con').addEvent('click', fnPay);
			// Resetting field on form change.
			document.getElement('input[name=orderData.orderServiceData.phone]').value = '';
			break;
		case 'con':
			jslog.debug('contrassegno');
			$('cc').addEvent('click', fnPay);
			// Resetting field on form change.
			document
					.getElement('select[name=orderData.orderServiceData.paymentType]').value = '';
			document
					.getElement('input[name=orderData.orderServiceData.cardNumber]').value = '';
			document.getElement('select[name=orderData.expirationMonth]').value = '';
			document.getElement('select[name=orderData.expirationYear]').value = '';
			document
					.getElement('input[name=orderData.orderServiceData.cardTitleHolder]').value = '';
			break;
		}
		updateWizardForm(contextPath, 'show-payment');
	};

	if (($('cc')) && (!$('cc').checked)) {
		$('cc').addEvent('click', fnPay);
	}
	if (($('con')) && (!$('con').checked)) {
		$('con').addEvent('click', fnPay);
	}

	var btn = document.getElement('input[name=submit-payment]');
	btn.removeEvents('click');
	btn.addEvent('click', function(e) {
		jslog.debug('submit-button: click');
		this.removeEvents('click'); // Avoids double clicks, and the event will
									// be readded at page reloading.
			this.addEvent('click', function(e) {
				e = new Event(e);
				e.stop();
			});
			e = new Event(e);
			AjaxSpinner.show();
			// This does NOT use the updateform because it will not hide
			// ajaxspinner at the end. Spinner will stay till redirect to the
			// feedback page.
			doAjaxRequest(new Ajax(contextPath + '/checkout/Checkout.action', {
				method :'post',
				data : [ '_eventName=submit-payment',
						$('checkoutWizardForm').toQueryString() ].join('&'),
				update :$('checkoutWizardFormContainer'),
				evalScripts :true,
				onComplete : function() {
					var alertContainer = $('alert-container');
					if (alertContainer) {
						MOOdalAlert.open($(alertContainer.firstChild));
						alertContainer.remove();
					}
				}
			}));
			e.stop();
		});

	var aShipment = $('spedizione').getElement('.modifica');
	if (aShipment) {
		aShipment.addEvent('click', function(e) {
			jslog.debug('shipment: click');
			e = new Event(e);
			updateWizardForm(contextPath, 'show-shipment');
			e.stop();
		});
	}

	var aPersonalData = $('datiPersonali').getElement('.modifica');
	if (aPersonalData) {
		aPersonalData.addEvent('click', function(e) {
			jslog.debug('personal-data: click');
			e = new Event(e);
			updateWizardForm(contextPath, 'show-personal-data');
			e.stop();
		});
	}

	// Adding enable disable to order merge, if any.
	document.getElements('input[name=orderData.present]').each(
			function(radio) {
				radio.addEvent('click', function(e) {
					updateCheckout();
				});
			});

	// Gift message has no cost, so no reload is needed.
	if ($('no')) {
		$('no').addEvent('click', function(e) {
			$('bigliettoAuguri').setStyle('display', 'none');
		});
	}
	;
	if ($('si')) {
		$('si').addEvent('click', function(e) {
			$('bigliettoAuguri').setStyle('display', 'block');
		});
	}
	;

	// Adding event to privacy link.
	if (document.getElement('a[id=accettazioneCondizioniDiVenditaLink]')) {
		document
				.getElement('a[id=accettazioneCondizioniDiVenditaLink]')
				.addEvent(
						'click',
						function(e) {
							e = new Event(e);
							e.stop();
							openPrintMOOdalBox(
									contextPath + '/fcom/it/home/pages/infoutili/condizionivendita.content',
									null, "600px");
						});
	}
	;
}

/**
 * Count greeting message characters and initialize field.
 * 
 * @author elaneri (Assioma.net)
 * @author dmartinotti (Assioma.net)
 * @param field
 *            textarea that contains the message
 * @param cntFieldName
 *            field that shows number of characters left
 * @param maxCharLimit
 *            max numbers of characters for the message (new line characters
 *            included)
 * @param newLineChar
 *            escape char for new lines (should be ~ but may also change...)
 */
function initializeCharactersCountCheckout(fieldName, cntFieldName,
		maxCharLimit, newLineChar) {
	field = document.getElementById(fieldName);
	if (field != null) {
		// create a tmp string for calculation (replace \n, \r and \r\n with ~)
		var tmpText = nl2brCheckout(field.value, newLineChar);

		// set chars left (WARNING: it can be negative)
		document.getElementById(cntFieldName).innerHTML = maxCharLimit
				- tmpText.length;
	}
}

/**
 * Replace new line with an escape char
 * 
 * @author dmartinotti (Assioma.net)
 * @param text
 *            the text to escape
 * @param newLineChar
 *            escape character
 * @return the escaped string
 */
function nl2brCheckout(text, newLineChar) {
	text = escape(text);
	if (text.indexOf('%0D%0A') > -1) {
		var re_nlchar = /%0D%0A/g;
	} else if (text.indexOf('%0A') > -1) {
		var re_nlchar = /%0A/g;
	} else if (text.indexOf('%0D') > -1) {
		var re_nlchar = /%0D/g;
	}
	return unescape(text.replace(re_nlchar, newLineChar));
}
function resetContainer() {
       // Just to be sure to have the right container id even in the list.
       // This is a real porkaround, I know... Pretend you didn't see, 99% of this javascript is based on this assumption, 
       // and it's not easy to change it because all of those javascript must work even in the public order page with the same container.
       if ($('orderlist-container')) {
          jslog.debug('Container id changed.');
          $('orderlist-container').setProperty('id', 'orderdetail-container');
       };
}

var OrderManager = {

  init: function() {
    this.historyKey = 'order';
    
    this.history = HistoryManager.register(
      this.historyKey,
      [ 'elenco' ],
      function(values) {
        if (values[1]) {
          this.showDetail(values[2]);
        } else {
          this.showList();
        }
      }.bind(this),
      function(values) {
        if (values[1]) {
          return ['dettaglio', '(', values[2], ')'].join('');
        } else {
          return 'elenco';
        }
      }.bind(this),
      /(elenco)|(dettaglio)\((\d+)\)/);
  },
  
  showList: function() {
	  AjaxSpinner.show();
	  doAjaxRequest(new Ajax('/user/OrderList.action',
	    {
	      method: 'get',
	      data: { safetyParam: (Math.random() * 1000) },
	      update: $('orderdetail-container'),
	      evalScripts: true,
	      onComplete: function() {
	        AjaxSpinner.hide();
	      }
	    }));
	  if (this.history) { // Workaround to avoid error when coming from summary (NOT order list) and doing back to the order list page. This way a "back" is lost, for now I don't care. 
        this.history.setValues([ 'elenco' ]);
	  };
  },

  showDetail: function(number) {
      saveOrRefreshOrderDetail('', {orderNumber: number });
      this.history.setValues([ null, 'dettaglio', number ]);
  }
};

// For registered user order list.
function initOrderList(contextPath, hasValidationErrors) {
       jslog.debug('orders.jsp: domready');
       resetContainer();
       alertText = 'Per modificare questo ordine devi contattare il servizio clienti al numero 199.151.173 o scrivere a clienti@lafeltrinelli.it';
       
       var filterSelect = $('orderListFilterForm').getElement('select[name=filterType]');
       jslog.debug('select = ' + filterSelect);
       filterSelect.addEvent('change', function(e) {
            jslog.debug('Filter change');
            AjaxSpinner.show();
            doAjaxRequest(new Ajax(contextPath + '/user/OrderList.action',
              {
                method: 'get',
                data: $('orderListFilterForm').toQueryString(),
                update: $('orderdetail-container'),
                evalScripts: true,
                onComplete: function() {
                  AjaxSpinner.hide();
                }
              }));
       });

       $('ordini').getElements('input[name=orderNumber]').each(function(hidden) {
           var tr = hidden.getParent().getParent();
           var modifica = tr.getElement('.col4').getElement('a[id=orderListModify]');
           if (modifica) { // Directly goes to the modify page
              modifica.addEvent('click', function(e) {
                 e = new Event(e);
                 saveOrRefreshOrderDetail(contextPath, {_eventName: 'show-edit-order', orderNumber: hidden.value });
                 e.stop();
              });
           };
        
           var number = tr.getElement('.col2').getElement('a[id=oderNumberModify]');
           if (number) { // Goes to the order detail page.
             number.addEvent('click', function(e) {
                 e = new Event(e);
                 //saveOrRefreshOrderDetail(contextPath, {orderNumber: hidden.value });
                 OrderManager.showDetail(hidden.value);
                 e.stop();
             });
           };
           var notModify = tr.getElement('.col4').getElement('a[id=orderListNotModify]');
           if (notModify) { // Directly goes to the modify page
        	   notModify.addEvent('click', function(e) {
                  e = new Event(e);
                  openConfirmMOOdalBox(alertText,null,null,null,"Chiudi",true);
                  e.stop();
               });
            };
            
      });
}

//Common ajax load/save for all orderDetail operations. The formData passed must already contain any eventData.
function saveOrRefreshOrderDetail(contextPath, formData) {
      doAjaxRequest(new Ajax(contextPath + '/user/OrderDetail.action',
      {
         method: 'post', //All operations must be posted for security reson
         data: formData,
         update: $('orderdetail-container'),
         evalScripts: true,
         onComplete: function() {
             AjaxSpinner.hide();
             var alertContainer = $('alert-container');
             if (alertContainer) {
               MOOdalAlert.open($(alertContainer.firstChild));
               alertContainer.remove();
             }
         }
      }));
}

function initOrderDetailShow(contextPath, hasValidationErrors) {
    jslog.debug('order-detail.jsp: domready');
    resetContainer();
    alertText = 'Per modificare questo ordine devi contattare il servizio clienti al numero 199.151.173 o scrivere a clienti@lafeltrinelli.it';
    // Back buttons
    var indietro = $('orderdetail-container').getElement('#indietro');
    if ((indietro) && (indietro.getElement('a'))) {
	  indietro.getElement('a').addEvent('click', function(e) {
	    e = new Event(e);
	    OrderManager.showList();
	    e.stop();
	  });
    }
    
    //Modify form
    var modify = $('orderdetail-container').getElement('input[name=show-edit-order]');
    if (modify) {
	    modify.addEvent('click', function(e) {
	      e = new Event(e);
	      AjaxSpinner.show();
        saveOrRefreshOrderDetail(contextPath, [ '_eventName=show-edit-order', $('orderForm').toQueryString() ].join('&'));
	      e.stop();
	    });
    }
    
    //Modify form ( Copy in order to duplicate bottom )
    var modifyCopy = $('orderdetail-container').getElement('input[name=show-edit-order-copy]');
    if (modifyCopy) {
	    modifyCopy.addEvent('click', function(e) {
	      e = new Event(e);
	      AjaxSpinner.show();
        saveOrRefreshOrderDetail(contextPath, [ '_eventName=show-edit-order', $('orderForm').toQueryString() ].join('&'));
	      e.stop();
	    });
    }
    
    //Order cancelling
    var deleteOrder = $('orderdetail-container').getElement('input[name=delete-order]');
   if (deleteOrder) {
      deleteOrder.addEvent('click', function(e) {
        var e = new Event(e);
        e.stop();
        openConfirmMOOdalBox('Procedere con la cancellazione di questo ordine?', 
        	function() {        	
	        	AjaxSpinner.show();
	          	saveOrRefreshOrderDetail(contextPath, 
	          		[ '_eventName=delete-order', $('orderForm').toQueryString() ].join('&'));          	
       		}, null, null, "Procedi", false);
      });
    }
   
   //Order cancelling ( Copy in order to duplicate bottom )
   var deleteOrderCopy = $('orderdetail-container').getElement('input[name=delete-order-copy]');
  if (deleteOrderCopy) {
     deleteOrderCopy.addEvent('click', function(e) {
       var e = new Event(e);
       e.stop();
       openConfirmMOOdalBox('Procedere con la cancellazione di questo ordine?', 
       	function() {        	
	        	AjaxSpinner.show();
	          	saveOrRefreshOrderDetail(contextPath, 
	          		[ '_eventName=delete-order', $('orderForm').toQueryString() ].join('&'));          	
      		}, null, null, "Procedi", false);
     });
   }

   //show modal box when try to modify freePay order
   var notModify = $('orderdetail-container').getElement('input[name=not-show-edit-order]');
   if (notModify) {
   	notModify.addEvent('click', function(e) {
   		e = new Event(e);
   		openConfirmMOOdalBox(alertText,null,null,null,"Chiudi",true);
   		e.stop();
   	});
   }

   //show modal box when try to modify freePay order ( Copy in order to duplicate bottom )
   var notModifyCopy = $('orderdetail-container').getElement('input[name=not-show-edit-order-copy]');
   if (notModifyCopy) {
   	notModifyCopy.addEvent('click', function(e) {
   		e = new Event(e);
   		openConfirmMOOdalBox(alertText,null,null,null,"Chiudi",true);
   		e.stop();
   	});
   }


    //Reso form
    var reso = $('orderdetail-container').getElement('a[id=singoloResoLink]');
    if (reso) {
  	  reso.addEvent('click', function(e) {
	      e = new Event(e);
	      AjaxSpinner.show();
        saveOrRefreshOrderDetail(contextPath, [ '_eventName=show-return-order', $('orderForm').toQueryString() ].join('&'));
	      e.stop();
	    });
    }
    
    //Reso form ( Copy in order to duplicate bottom )
    var resoCopy = $('orderdetail-container').getElement('a[id=singoloResoLinkCopy]');
    if (resoCopy) {
  	  resoCopy.addEvent('click', function(e) {
	      e = new Event(e);
	      AjaxSpinner.show();
        saveOrRefreshOrderDetail(contextPath, [ '_eventName=show-return-order', $('orderForm').toQueryString() ].join('&'));
	      e.stop();
	    });
    }
    
    //Reso popup when the reso is unavailable
    var notReso = $('orderdetail-container').getElement('a[id=singoloResoUnavailableLink]');
    if ((notReso) && ($('orderdetail-container').getElementById('singoloResoUnavailableContent'))) {
      notReso.addEvent('click', function(e) {
        e = new Event(e);        
        //Il modal alert elimina il contenuto una volta chiso, quindi il clone ï¿½ necessario
        openAlertMOOdalBox($('orderdetail-container').getElement('div[id=singoloResoUnavailableContent]').clone()); 
        e.stop();
      });
    }

    //Reso popup when the reso is unavailable ( Copy in order to duplicate bottom )
    var notResoCopy = $('orderdetail-container').getElement('a[id=singoloResoUnavailableLinkCopy]');
    if ((notResoCopy) && ($('orderdetail-container').getElementById('singoloResoUnavailableContent'))) {
      notResoCopy.addEvent('click', function(e) {
        e = new Event(e);        
        //Il modal alert elimina il contenuto una volta chiso, quindi il clone ï¿½ necessario
        openAlertMOOdalBox($('orderdetail-container').getElement('div[id=singoloResoUnavailableContent]').clone()); 
        e.stop();
      });
    }
   
}

function initOrderDetailModify(contextPath, hasValidationErrors, procurementMessage) {
	
        jslog.debug('order-detail.jsp: domready');
        resetContainer();
 
        if (document.getElement('input[name=orderModificationData.billingAddress.invoiceToCompany]')) {    
          //Enable/disable company fields. 
          if (document.getElement('input[name=orderModificationData.billingAddress.invoiceToCompany]').checked) {
              document.getElement('input[name=orderModificationData.billingAddress.company]').disabled = false;
              document.getElement('input[name=orderModificationData.billingAddress.vatNumber]').disabled = false;
              document.getElement('input[name=orderModificationData.billingAddress.name]').disabled = true;
              document.getElement('input[name=orderModificationData.billingAddress.lastname]').disabled = true;
          } else {
              document.getElement('input[name=orderModificationData.billingAddress.company]').disabled = true;
              document.getElement('input[name=orderModificationData.billingAddress.vatNumber]').disabled = true;
              document.getElement('input[name=orderModificationData.billingAddress.name]').disabled = false;
              document.getElement('input[name=orderModificationData.billingAddress.lastname]').disabled = false;
          };
          document.getElement('input[name=orderModificationData.billingAddress.invoiceToCompany]').addEvent('click', function(e) {
            jslog.debug('showHideCompanyDetail: click');
            if (document.getElement('input[name=orderModificationData.billingAddress.invoiceToCompany]').checked) {
              document.getElement('input[name=orderModificationData.billingAddress.company]').disabled = false;
              document.getElement('input[name=orderModificationData.billingAddress.vatNumber]').disabled = false;
              document.getElement('input[name=orderModificationData.billingAddress.name]').disabled = true;
              document.getElement('input[name=orderModificationData.billingAddress.lastname]').disabled = true;
            } else {
              document.getElement('input[name=orderModificationData.billingAddress.company]').disabled = true;
              document.getElement('input[name=orderModificationData.billingAddress.vatNumber]').disabled = true;
              document.getElement('input[name=orderModificationData.billingAddress.name]').disabled = false;
              document.getElement('input[name=orderModificationData.billingAddress.lastname]').disabled = false;
            };
          });
        };
    
    // Back buttons
    var indietro = $('orderdetail-container').getElement('#indietro');
    if ((indietro) && (indietro.getElement('a'))) {
	  indietro.getElement('a').addEvent('click', function(e) {
	    e = new Event(e);
	    AjaxSpinner.show();
	    doAjaxRequest(new Ajax(contextPath + '/user/OrderList.action',
	      {
	        method: 'get',
	        data: { safetyParam: (Math.random() * 1000) },
	        update: $('orderdetail-container'),
	        evalScripts: true,
	        onComplete: function() {
	          AjaxSpinner.hide();
	        }
	      }));
	    e.stop();
	  });
    }
    
    //Submit modify form
    var modify = $('orderdetail-container').getElement('input[name=update-order]');
    if (modify) {
      modify.removeEvents('click');
      modify.addEvent('click', function(e) {
       jslog.debug('submit-button: click');
       this.removeEvents('click'); //Avoids double clicks, and the event will be readded at page reloading.
       this.addEvent('click', function(e) {
            e = new Event(e);
            e.stop();
       });
	      e = new Event(e);
	      AjaxSpinner.show();
	      saveOrRefreshOrderDetail(contextPath, [ '_eventName=update-order', $('orderForm').toQueryString() ].join('&'));
	      e.stop();
	    });
    }

      //Adding automatic recompute for each new quantity form.    
	  $('orderForm').getElement('tbody').getElements('.col2').each(function(item, index) {
		  var select = item.getElement('select');
		  if (select) {
		    select.addEvent('change', function(e) {
			    e = new Event(e);
			    AjaxSpinner.show();
         	    saveOrRefreshOrderDetail(contextPath, [ '_eventName=show-edit-order', 'ajaxId=update-details', $('orderForm').toQueryString() ].join('&'));
			    e.stop();
		    });
		  }
	  });	  	 

      //Adding automatic recompute for redemption selection
	  $('orderForm').getElements('select[name=redemptionSelect]').each(function(item, index) {
		    item.addEvent('change', function(e) {
			    e = new Event(e);
			    AjaxSpinner.show();
         	    saveOrRefreshOrderDetail(contextPath, [ '_eventName=show-edit-order', 'ajaxId=update-redemption', $('orderForm').toQueryString() ].join('&'));
			    e.stop();
		    });
	  });
	  
      // Adding automatic refresh for invoice request. No real need to recompute costs, but condition to show/hide invoice form are too complex to be tested in javascript.
	  var invoiceCheck = $('orderForm').getElement('input[name=orderModificationData.invoice]');
	  if (invoiceCheck) {
	    invoiceCheck.addEvent('click', function(e) {
			    e = new Event(e);
			    AjaxSpinner.show();
         	    saveOrRefreshOrderDetail(contextPath, [ '_eventName=show-edit-order', 'ajaxId=modify-invoice', $('orderForm').toQueryString() ].join('&'));
			    e.stop();
	    });
	  };
	  
	  // Adding automatic refresh for modify pdv. No real need to recompute costs, but condition to show/hide invoice form are too complex to be tested in javascript.
	  var pdvCheck = $('orderForm').getElement('input[name=modifyPdv]');
	  if (pdvCheck) {
		  
		  pdvCheck.addEvent('click', function(e) {
			  e = new Event(e);
			  AjaxSpinner.show();
			  saveOrRefreshOrderDetail(contextPath, [ '_eventName=show-edit-order', 'ajaxId=modifyPdv', $('orderForm').toQueryString() ].join('&'));
			  e.stop();
		  });
	  };
	  
		var citySelect = document.getElement('select[name=pdvCity]');
		if (citySelect != null) {
			citySelect.addEvent('change', function(e) {
				  AjaxSpinner.show();
				  saveOrRefreshOrderDetail(contextPath, [ '_eventName=show-edit-order', 'ajaxId=modifyPdv', $('orderForm').toQueryString() ].join('&'));
				  AjaxSpinner.hide();
				});
		};
		
	  
	  //Adding automatic recompute for changing shipping country expenses
	  var countrySelect = $('orderForm').getElement('select[name=orderModificationData.shippingAddress.country]');
	  var provSelect = $('orderForm').getElement('select[name=orderModificationData.shippingAddress.prov]');
	  if (countrySelect) {
	    countrySelect.addEvent('change', function(e) {
	    //Province will be automatically updated during order recompute anyway.
			    e = new Event(e);
			    AjaxSpinner.show();
          saveOrRefreshOrderDetail(contextPath, [ '_eventName=show-edit-order', $('orderForm').toQueryString() ].join('&'));
			    e.stop();
	    });
	  };
	
	  // Billing address province doesn't need recomputation.
	  var countrySelect = $('orderForm').getElement('select[name=orderModificationData.billingAddress.country]');
	  var provSelect = $('orderForm').getElement('select[name=orderModificationData.billingAddress.prov]');
	  if (countrySelect) {
	    countrySelect.addEvent('change', function(e) {
	      provinceUpdater(this, provSelect);
	    });
	  };
	  
	  //[EL] regalo ordine
      //Adding enable/disable modify augurymessage
	  $('orderForm').getElements('input[name=orderModificationData.present]').each(function(radio) {
        radio.addEvent('click', function(e) {
		    e = new Event(e);
		    AjaxSpinner.show();
     	    saveOrRefreshOrderDetail(contextPath, [ '_eventName=show-edit-order', 'ajaxId=modify-invoice', $('orderForm').toQueryString() ].join('&'));
		    e.stop();
        });
      });
      
	  
      //Gift message has no cost, so no reload is needed.
      if ($('no')) {
          $('no').addEvent('click', function(e) {
            $('bigliettoAuguri').setStyle('display', 'none');
          });
      };
      if ($('si')) {
          $('si').addEvent('click', function(e) {
            $('bigliettoAuguri').setStyle('display', 'block');
          });
      };
      
     
}

function initResoDetail(contextPath, hasValidationErrors) {
    jslog.debug('order-detail-reso.jsp: domready');
    resetContainer();
  
    //Back button
    var indietro = $('orderdetail-container').getElement('#indietro');
    if ((indietro) && (indietro.getElement('a'))) {
	  indietro.getElement('a').addEvent('click', function(e) {
	    e = new Event(e);
	    AjaxSpinner.show();
	    doAjaxRequest(new Ajax(contextPath + '/user/OrderList.action',
	      {
	        method: 'get',
	        data:  { safetyParam: (Math.random() * 1000) },
	        update: $('orderdetail-container'),
	        evalScripts: true,
	        onComplete: function() {
	          AjaxSpinner.hide();
	        }
	      }));
	    e.stop();
	  });
    }
    var form = $('orderForm');
    //This javascript is used also for the confirm to keep the back button. This if is needed!
    if (form) {
      form.getElement('tbody').getElements('.col4').each(function(item, index) {
      var modifica = item.getChildren()[0];
      if (modifica) {
        modifica.addEvent('click', function(e) {
          if (modifica.checked) {
            form.getElement('tbody').getElements('.col2')[index].getChildren()[0].setStyle('display', 'block');
	          form.getElement('tbody').getElements('.col2')[index].getChildren()[1].setStyle('display', 'none');
	          form.getElement('tbody').getElements('.col2')[index].getChildren()[2].setStyle('display', 'block');
          } else { 
            form.getElement('tbody').getElements('.col2')[index].getChildren()[0].setStyle('display', 'none');
	          form.getElement('tbody').getElements('.col2')[index].getChildren()[1].setStyle('display', 'block');
	          form.getElement('tbody').getElements('.col2')[index].getChildren()[2].setStyle('display', 'none');
	          form.getElement('tbody').getElements('.col2')[index].getChildren()[2].getElement('select').value = "";
          }
	      }.bind(item.getChildren()[0]));
	    }
    });

      if (form.getElement('input[name=return-order]')) {
        form.getElement('input[name=return-order]').removeEvents('click');
        form.getElement('input[name=return-order]').addEvent('click', function(e) {
          jslog.debug('submit-button: click');
          this.removeEvents('click'); //Avoids double clicks, and the event will be readded at page reloading.
          this.addEvent('click', function(e) {
             e = new Event(e);
             e.stop();
          });
          e = new Event(e);
          AjaxSpinner.show();
          saveOrRefreshOrderDetail(contextPath, [ '_eventName=return-order', $('orderForm').toQueryString() ].join('&'));
          e.stop();
        });
      };  
    };
}

//Used to init links from summary to order detail page.
function initSummaryOrderList(contextPath, hasValidationErrors) {
       jslog.debug('order summary: domready');
}


/**
 * Count the inserted characters and check that the text respect all the limits imposed by CEVA (logistics).
 * WARNING: this algorithm was written thinkig to an user that digit the text one character at time in the 
 *          textarea: if one limit was exceeded, the last char inserted war deleted... and all worked fine. 
 *          But if the text is pasted in the textarea from an external text editor, there are big problems,
 *          as many checks can fail at the same time and there can be many chars to delete. So the behaviour
 *          of the algorithm became unpredictable. To solve these problems I removed all chars deletion and 
 *          I left only check alerts. [DM]
 * TODO: if needed restore (and correct) characters deletion: good luck!!
 * 
 * @author elaneri (Assioma.net)
 * @author dmartinotti (Assioma.net)
 * @param  e             event
 * @param  field         textarea that contains the message
 * @param  cntFieldName  field that shows number of characters left 
 * @param  maxCharLimit  max numbers of characters for the message (new line characters included)
 * @param  maxLineLimit  max numbers of characters for each line of the message (new line characters excluded)
 * @param  maxLines      max numbers of liner for the message
 * @param  newLineChar   escape char for new lines (should be ~ but may also change...)
 */
/*
 [RA]: ristrutturazione della funzionalitï¿½ di controllo limiti testuali 
 */
function twdCountOrders(e, field, cntFieldName, maxCharLimit, maxLineLimit, maxLines, newLineChar) 
{
	NEW_LINE_CODE = 13;
	
	//[RA]: Elimina il carattere pipe in qualsiasi caso
	delCaracter(field);
	
	// get the event
	if(!e) e = window.event;

	// get the pressed key
	if (window.event) {
		key = window.event.keyCode;
	} else if (e) {
		key = e.which;
	}
	while(!adattaMessaggio(field, cntFieldName, maxCharLimit, maxLineLimit, maxLines, newLineChar)){
		
	}
	var tmpText = nl2brOrders(field.value, newLineChar);
	document.getElementById(cntFieldName).innerHTML = maxCharLimit - tmpText.length;
}
function adattaMessaggio(field, cntFieldName, maxCharLimit, maxLineLimit, maxLines, newLineChar){
	// create a tmp string for calculation (replace \n, \r and \r\n with ~)
	var tmpText = nl2brOrders(field.value, newLineChar);
	// check text length
	
	var arrText = tmpText.split(newLineChar);
	
	if(tmpText.length>maxCharLimit||arrText.length>maxLines){
		field.value = field.value.substring(0, field.value.length-1);
		return false;
	}
	for(var i=0;i<arrText.length;i++){
		if(arrText[i].length>maxLineLimit){
			field.value = field.value.substring(0, field.value.length-1);
			return false;
		}
	}
	return true;
}
/**
 * Count greeting message characters and initialize field.
 * 
 * @author elaneri (Assioma.net)
 * @author dmartinotti (Assioma.net) 
 * @param  field         textarea that contains the message
 * @param  cntFieldName  field that shows number of characters left 
 * @param  maxCharLimit  max numbers of characters for the message (new line characters included)
 * @param  newLineChar   escape char for new lines (should be ~ but may also change...)
 */
function initializeCharactersCountOrders(fieldName, cntFieldName, maxCharLimit, newLineChar)
{
	field = document.getElementById(fieldName);
	if ( field != null)
	{
		// create a tmp string for calculation (replace \n, \r and \r\n with ~)
		var tmpText = nl2brOrders(field.value, newLineChar);
	
		// set chars left (WARNING: it can be negative)
		document.getElementById(cntFieldName).innerHTML = maxCharLimit - tmpText.length;
	}
}


/**
 * Replace new line with an escape char
 *
 * @author dmartinotti (Assioma.net)
 * @param  text          the text to escape
 * @param  newLineChar   escape character
 * @return the escaped string
 */
function nl2brOrders(text, newLineChar) 
{
	text = escape(text);
	if ( text.indexOf('%0D%0A') > -1 ) {
		var re_nlchar = /%0D%0A/g ;
	} else if ( text.indexOf('%0A') > -1 ) {
		var re_nlchar = /%0A/g ;
	} else if ( text.indexOf('%0D') > -1 ) {
		var re_nlchar = /%0D/g ;
	}
	return unescape( text.replace(re_nlchar, newLineChar) );
}

/**
 * If isgift is not checked textarea will empty for escape validation.
 * Used in checkout-wizard-shipment.jsp
 * 
 * @author elaneri (Assioma.net)
 */
function findIsGiftOrders() 
{
	var regalaNo = document.getElementById("no");
	if ( regalaNo != null && regalaNo.checked ) {
		document.getElementById("biglietto").value = "" ;
	}
}

/**
 * Replace character pipe with null string
 *
 * @author raceto (Assioma.net)
 * @param  txtField      object textarea
 * @return the escaped string
 */
function delCaracter(txtField)
{
	var ind = txtField.value.length;
	while(txtField.value.indexOf('|')>=0){
		txtField.value = txtField.value.replace('|','');
		ind = txtField.value.length;
		i=0;
	}
}

function sendRequest(data,contextPath) {
	alert(contextPath);
	var numberList = '';
	var isFirst = true;
	for (i = 0; i < data.length; i++) {
		var record = data[i];
		if (!isFirst)
			numberList += '|';
		numberList += record;
		isFirst = false;
	}
	doAjaxRequest(new AjaxMany(
			contextPath + 'admin2/ReturnOrdersPdvAmin.action',
            {
            	method: 'post',
                update: $('returnOrdersContainer'),
                data: '_eventName=' + 'confirm'
                   +'&numberList='+numberList
//        		   +'&'+document.getElementById('returnOrdersForm').toQueryString()
        		,			
				evalScripts :true
	}));
};


function initProductDetail(bodyId, prodId) {
  jslog.debug('initProductDetail()');
  document.body.id = bodyId;
  var scheda = $('schedaProdotto');
  activateAddToCartButtons(scheda);

  scheda.getElements('.menuInterno').each( function(ul) {
    new ProductTabBar(ul);
  });

  // Redmine #1249 Eliminazione scritta da scheda prodotto
  if (scheda.getElementById('contLaFeltrinelli').style.display != 'none')
  {
	  scheda.getElementById('headLaFeltrinelli').style.display = scheda.getElementById('contLaFeltrinelli').style.display;
  }
  
  loadAjaxRecommendations(prodId, $('prodottiCorrelati'), 10, function() {
    var container = $('prodottiCorrelati');
    if (container) {
      container.$content.fireEvent('update');
      if (container.$content.link.isVisible()) {
        container.$content.link.onSelected();
      };      
    };
    carouselInit(container);
    selectBraniIfExist();
  });
}

function selectBraniIfExist() {
  var cont1 = $('prodottiCorrelati');
  var cont2 = $('tracklist');
  var cont3 = $('laNostraOpinione');
  if (cont2) {
  	  if(!cont2.$content.isEmpty()){
  		  if (cont1) {
  			cont1.$content.link.setActive(false);
  			cont3.$content.refresh;
  		  }
  		  if (cont3) {
  			cont3.$content.link.setActive(false);
  			cont3.$content.refresh;
  		  }
  		  cont2.$content.link.setActive(true);
  		  cont2.$content.refresh;
  	  }
  }
}

/*
Class: GenericTabBar
  A bar containing tab links.

Arguments:
  ul - list of anchors.
*/
var GenericTabBar = new Class( {

  initialize : function(ul) {
    this.ul = ul;
    this.links = [];
    this.ul.getChildren().each( function(li) {
      this.initializeChildLink(li);
    }, this);
    this.contents = [];
    var div = this.ul.getNext();
    var index = 0;
    while (this.links[index] && div && div.getTag() == 'div') {
      this.contents.push(new ProductTabContent(div, this.links[index]));
      div = div.getNext();
      index++;
    }

    this.refresh();
    this.links.each( function(link) {
      link.addEvent('show', this.refresh.bind(this));
      link.addEvent('hide', this.refresh.bind(this));
      link.addEvent('activate', this.fireEvent.pass( [ 'change', link ], this));
    }, this);
  },

  initializeChildLink : function(li) {
    this.links.push(new GenericTabLink(li, this));
  },

  isVisible : function() {
    return this.ul.getStyle('display') != 'none';
  },

  setVisible : function(visible) {
    this.ul.setStyle('display', visible ? '' : 'none');
  },

  refresh : function() {
    var visibleLinks = this.links.filter( function(link) {
      return link.isVisible();
    });
    var activeLinks = visibleLinks.filter( function(link) {
      return link.isActive();
    });
    this.setVisible(visibleLinks.length > 0);
    if (activeLinks.length == 0 && visibleLinks.length > 0) {
      visibleLinks[0].onSelected();
    }
  }

});

GenericTabBar.implement(new Events);

/*
Class: ProductTabBar
  A bar containing tab links.

Arguments:
  ul - list of anchors.
*/
var ProductTabBar = GenericTabBar.extend({
initializeChildLink : function(li){
this.links.push(new ProductTabLink(li, this));}}
);

/*
Class: GenericTabLink
  A tab link.

Arguments:
  li - list item containing the anchor.
  bar - tab bar owning this link.
*/
var GenericTabLink = new Class( {

  initialize : function(li, bar) {
    this.li = li;
    this.bar = bar;
    this.a = this.li.getElement('a');
    //Any link with class 'disableTabBarLink' is NOT considered as a part of the show/hide tab, it's just a link on the same line.
  if (!this.a.hasClass('disableTabBarLink')) {
    this.a.addEvent('click', function(e) {
      e.stop();
      this.onSelected();
    }.bindWithEvent(this));
  }
  ;
},

onSelected : function() {
  this.bar.links.each( function(link) {
    if (link != this && link.isActive()) {
      link.setActive(false);
    }
  }, this);
  this.setActive(true);
},

isVisible : function() {
  // A link with class 'disableTabBarLink' is always visible.
  return (this.li.getStyle('display') != 'none') || ((this.a) && (this.a.hasClass('disableTabBarLink')));
},

setVisible : function(visible) {
  this.li.setStyle('display', visible ? '' : 'none');
  if (visible) {
    this.fireEvent('show');
  } else {
    this.fireEvent('hide');
  }
},

isActive : function() {
  return this.a == null;
},

setActive : function(active) {
  if (active) {
    this.fireEvent('activate');
  } else {
    this.fireEvent('deactivate');
  }
}

});

GenericTabLink.implement(new Events);




/*
Class: ProductTabLink
  A tab link.

Arguments:
  li - list item containing the anchor.
  bar - tab bar owning this link.
*/
var ProductTabLink = GenericTabLink.extend( {

  isActive : function() {
    return (this.em != null);
  },

  setActive : function(active) {
    if (active) {
      // this.a.addClass('active')
	  if ((this.a) && (!this.a.hasClass('disableTabBarLink'))) { // A link 'disableTabBarLink' is never decorated with active class.
	    this.em = new Element('em');
	    this.em.setText(this.a.getText());
	    this.a.replaceWith(this.em);
	    this.a = null;
	    this.fireEvent('activate');
	    this.li.addClass('selectedProductTab');
	  }
	} else {
	  //this.a.removeClass('active');
	  if (this.em) {
	    this.a = new Element('a', {
	      href :'#'
	    });
	    this.a.setText(this.em.getText());
	    this.a.addEvent('click', function(e) {
	      e.stop();
	      this.onSelected();
	    }.bindWithEvent(this));
	    this.em.replaceWith(this.a);
	    this.em = null;
	    this.li.removeClass('selectedProductTab');
	    this.fireEvent('deactivate');
	  }
	}
}
});

/*
Class: ProductTabContent
  The content of a tab.

Arguments:
  div - the content to display when the tab is active.
  link - the tab link of this content.

Events:
  change - .
*/
var ProductTabContent = new Class( {

  initialize : function(div, link) {
    this.div = div;
    this.link = link;
    this.div.$content = this;
    this.link.content = this;
    this.addEvent('update', this.onUpdate.bind(this));
    this.link.addEvent('activate', this.show.bind(this));
    this.link.addEvent('deactivate', this.hide.bind(this));
    this.onUpdate();
  },

  isEmpty : function() {
    return this.div.getElementsByTagName('*').length == 0
  },

  show : function() {
    jslog.debug('ProductTabContent.show()');
    this.div.setStyle('display', '');
  },

  hide : function() {
    jslog.debug('ProductTabContent.hide()');
    this.div.setStyle('display', 'none');
  },

  onUpdate : function() {
    this.link.setVisible(!this.isEmpty());
  }

});

ProductTabContent.implement(new Events);

/**
 * Recommendations are loaded asyncronously to avoid excessive wait during product page loading.
 * 
 * @param prodId
 *          Id of the product
 * @param container
 *          Recommendation container div
 * @param count
 *          Max products to show
 * @return Nothing
 */
function loadAjaxRecommendations(prodId, container, count, callback) {
  var url = '/Recommendations.action';
  var params = {
    productId :prodId,
    maxCount :count,
    // safetyParam avoids caching of http request and force new recommendations for each different user.
    safetyParam :(Math.random() * 1000)
  };
  if (container) {
    doAjaxRequest(new Ajax(url, {
      method :'get',
      data :Object.toQueryString(params),
      update :container,
      evalScripts :true,
      onComplete :callback
    }));
    
  }
}

/**
 * Extracted from product-insertReview.tag
 */
function salvaReviewFromProductDet() {
  jslog.debug('salvaReviewFromProductDet');
  AjaxSpinner.show();
  $('eventName').value = 'insertReview';
  new Ajax('/UserPopupReview.action', {
    method :'post',
    data :$('reviewInsertForm'),
    update :$('insertNewReviewProductDetailContainer'),
    evalScripts :true,
    onComplete : function() {
      AjaxSpinner.hide();
    }
  }).request();
}

function doAjaxRequest(myAjax) {
	if (window.ie && myAjax.options.method == 'get') {
		myAjax.setHeader('If-Modified-Since', 'Sat, 1 Jan 2000 00:00:00 GMT');
	}
	myAjax.request();
}

function removeParam(query, param) {
	var re = new RegExp('&' + param + '=[^&]*');
	return ('&' + query).replace(re, '').substr(1);
}

function showAjaxSpinner() {
	var loading = new Element('div');
	loading.id = 'loading-area';
	var top = Window.getScrollTop() + (Window.getHeight() / 10) + 110;
	loading.setStyle('top', top + 'px');
	// var pLoading = new Element('p');
	// pLoading.setText('Loading...');
	// loading.appendChild(pLoading);
	document.body.appendChild(loading);
}

function hideAjaxSpinner() {
	var lArea = $('loading-area');
	if (lArea) {
		lArea.remove();
	}
}

/**
 * Scrolls directly to an element in the page, using mootools but without
 * effects. Element should be wrapped in Mootools Element class.
 */
function scrollToElement(element) {
	if (element != null) {
		(new Fx.Scroll(window, {
			transition :Fx.Transitions.linear,
			duration :0,
			wait :false
		})).toElement(element);
	}
	;
}

function setSelectVisibility(container, visible) {
	container = $(container);
	if (container) {
		container.getElements('select').setStyle('visibility',
				visible ? '' : 'hidden');
	}
}

function showErrorPage() {
	var errContainers = $$('.error_page');
	if (errContainers.length > 0) {
		var containers = $(document.body).getChildren().filter( function(item) {
			return item.id == 'contenitore';
		});
		if (containers.length > 0) {
			containers[0].replaceWith(errContainers[0]);
		}
	}
}
function checkCookies(linkAiuto) {
	if (!Cookie.get("testCookie")) {
		Cookie.set("testCookie", 'test');
	}
	if (Cookie.get("testCookie") != 'test') {
		div = document.getElementById("contenitore");
		var par = document.createElement('p');
		par.id = 'noCookie';

		var spContainer = document.createElement('span');
		var sp_text = document
				.createTextNode('Per utilizzare il sito devi abilitare i cookie.');
		spContainer.appendChild(sp_text);

		var spLink = document.createElement('a');

		spLink.href = linkAiuto;
		var link_text = document.createTextNode('Scopri come fare.');
		spLink.appendChild(link_text);
		spContainer.appendChild(spLink);

		par.appendChild(spContainer);

		document.getElementsByTagName("body").item(0).insertBefore(par, div);

	}
}

var CountdownLink = new Class( {

	initialize : function(link, timeout) {
		this.link = $(link);
		this.timeout = timeout;
	},

	start : function() {
		this.label = this.link.getText();
		this.counter = this.timeout;
		this.tick();
	},

	tick : function() {
		this.link.setText(this.label + ' (' + this.counter + ')');
		if (this.counter > 0) {
			this.counter--;
			this.tick.delay(1000, this);
		} else {
			location.href = this.link.href;
		}
	}

});

function getScrollXY() {
	var scrOfX = 0, scrOfY = 0;
	if (typeof (window.pageYOffset) == 'number') {
		// Netscape compliant
		scrOfY = window.pageYOffset;
		scrOfX = window.pageXOffset;
	} else if (document.body
			&& (document.body.scrollLeft || document.body.scrollTop)) {
		// DOM compliant
		scrOfY = document.body.scrollTop;
		scrOfX = document.body.scrollLeft;
	} else if (document.documentElement
			&& (document.documentElement.scrollLeft || document.documentElement.scrollTop)) {
		// IE6 standards compliant mode
		scrOfY = document.documentElement.scrollTop;
		scrOfX = document.documentElement.scrollLeft;
	}
	return [ scrOfX, scrOfY ];
}

var LoginLink = new Class( {

	initialize : function(link, param, encode) {
		this.link = link;
		this.param = param;
		this.encode = encode;
		this.link.addEvent('click', this.click.bindWithEvent(this));
	},

	click : function(e) {
		// event must be propagated
		var url = location.pathname + location.hash + location.search;
		this.link.href += [ '?', this.param, '=',
				(this.encode ? encodeURIComponent(url) : url) ].join('');
	}

});

var AjaxSpinner = {

	init : function() {
		jslog.debug('AjaxSpinner.init()');
		this.stack = 0;
	},

	show : function() {
		this.stack++;
		if (this.stack == 1) {
			showAjaxSpinner();
		}
	},

	hide : function() {
		if (this.stack <= 0)
			return;
		this.stack--;
		if (this.stack == 0) {
			hideAjaxSpinner();
		}
	}

};

Window.onDomReady(AjaxSpinner.init.bind(AjaxSpinner));

function openPopup(url, name, width, height, resizable, scrollbars, menubar,
		toolbar, location, directories, status) {
	popup = window.open(url, name, 'width=' + width + ',height=' + height
			+ ',resizable=' + resizable + ',scrollbars=' + scrollbars
			+ ',menubar=' + menubar + ',toolbar=' + toolbar + ',location='
			+ location + ',directories=' + directories + ',status=' + status);
	// popup.moveTo(((screen.availWidth-340)/2),((screen.availHeight-360)/2));
	popup.focus();
	return popup;
}

function openAlertMOOdalBox(sMessage, buttons, width, height) {

	var contents = new Element('div');
	var contStyles = {
		// border: '8px solid #E20A16',
		padding :15
	}
	contents.setStyles(contStyles);
	contents.setProperty("class", "mb_internal");
	if (height) {
		contents.setStyle("width", height);
	}
	if (width) {
		contents.setStyle("width", height);
	}

	contents.setStyle("font-size", "8pt");
	var closeEl = new Element('div');
	closeEl.setProperty("class", "mb_close");
	closeEl.appendText("X");
	closeEl.injectInside(contents);
	closeEl.addEvent("click", function() {
		MOOdalAlert.close();
	});

	var textEl = new Element('div');
	textEl.setProperty("class", "mb_text");
	if (sMessage.trim) {
		textEl.innerHTML = sMessage;
	} else {
		sMessage.injectInside(textEl);
	}
	textEl.injectInside(contents);
	if (buttons) {
		var buttonsContainerEl = new Element('div');
		buttonsContainerEl.injectInside(contents);
		buttonsContainerEl.setStyles( {
			align :"center",
			marginTop :15
		});
		var i;

		for (i = 0; i < buttons.length; i++) {
			buttons[i].injectInside(buttonsContainerEl);
			buttons[i].addEvent("click", function() {
				MOOdalAlert.close();
			});
			buttons[i].setStyle("cursor", "pointer");
		}
	}

	MOOdalAlert.open(contents);
}

function openMOOdalBox(sMessage, buttons, width, height) {

	var contents = new Element('div');
	var contStyles = {
		// border: '8px solid #E20A16',
		padding :15
	}
	contents.setStyles(contStyles);
	contents.setProperty("class", "mb_internal");
	if (height) {
		contents.setStyle("width", height);
	}
	if (width) {
		contents.setStyle("width", height);
	}

	contents.setStyle("font-size", "8pt");
//	var closeEl = new Element('div');
//	closeEl.setProperty("class", "mb_close");
//	closeEl.appendText("X");
//	closeEl.injectInside(contents);
//	closeEl.addEvent("click", function() {
//		MOOdalAlert.close();
//	});

	var textEl = new Element('div');
	textEl.setProperty("class", "mb_text");
	if (sMessage.trim) {
		textEl.innerHTML = sMessage;
	} else {
		sMessage.injectInside(textEl);
	}
	textEl.injectInside(contents);
	if (buttons) {
		var buttonsContainerEl = new Element('div');
		buttonsContainerEl.injectInside(contents);
		buttonsContainerEl.setStyles( {
			align :"center",
			marginTop :15
		});
		var i;

		for (i = 0; i < buttons.length; i++) {
			buttons[i].injectInside(buttonsContainerEl);
			buttons[i].addEvent("click", function() {
				MOOdalAlert.close();
			});
			buttons[i].setStyle("cursor", "pointer");
		}
	}

	MOOdalAlert.open(contents);
}
function openConfirmMOOdalBox(sMessage, confirmFunction, width, height,
		confirmText, hideCancel) {
	var btnStyle = {
		color :"#E20A16",
		borderBottom :"2px solid #E20A16",
		fontWeight :"bold",
		align :"middle",
		cursor :"pointer"
	};

	var okEl = new Element('span');
	okEl.setStyles(btnStyle);
	if (confirmText) {
		okEl.appendText(confirmText);
	} else {
		okEl.appendText("OK");
	}
	;
	if(confirmFunction != null) {
	okEl.addEvent("click", function(e) {
		confirmFunction.apply(e);
	});
	}
	okEl.setStyle("padding", "0 6px 0 6px");

	var cancelEl = new Element('span');
	cancelEl.setStyles(btnStyle);
	cancelEl.appendText("Annulla");
	cancelEl.setStyle("margin", "0 8px 0 8px");

	openAlertMOOdalBox(sMessage, (hideCancel) ? new Array(okEl) : new Array(
			okEl, cancelEl), width, height);
}

function openYesNoMOOdalBox(sMessage, width, height, yesFunction, noFunction,
		yesText, noText) {
	var btnStyle = {
		color :"#E20A16",
		borderBottom :"2px solid #E20A16",
		fontWeight :"bold",
		align :"middle",
		cursor :"pointer"
	};

	var yesEl = new Element('span');
	yesEl.setStyles(btnStyle);
	if (yesText) {
		yesEl.appendText(yesText);
	} else {
		yesEl.appendText("S&iacute;");
	}
	;
	okEl.addEvent("click", function(e) {
		if (yesFunction)
			yesFunction.apply(e);
	});
	okEl.setStyle("padding", "0 6px 0 6px");

	var noEl = new Element('span');
	noEl.setStyles(btnStyle);
	if (noText) {
		noEl.appendText(noText);
	} else {
		noEl.appendText("No");
	}
	;
	noEl.addEvent("click", function(e) {
		if (noFunction)
			noFunction.apply(e);
	});
	noEl.setStyle("margin", "0 8px 0 8px");

	openAlertMOOdalBox(sMessage, new Array(yesEl, noEl), width, height);
}

function openPrintMOOdalBox(sLinkHref, width, height) {

	var btnStyle = {
		color :"#E20A16",
		borderBottom :"2px solid #E20A16",
		fontWeight :"bold",
		align :"middle",
		cursor :"pointer"
	};

	var printEl = new Element('span');
	printEl.setStyles(btnStyle);
	printEl.appendText("Stampa");
	var printFunction = function(e) {
		var popup = openPopup(sLinkHref, 'printFriendlyView', 710, 650, 'no',
				1, 1, 0, 0, 0, 0);
		var printPopup = function() {
			popup.print();
		};
		printPopup.delay(1000);
	};

	printEl.addEvent("click", function(e) {
		printFunction.apply(e);
	});
	printEl.setStyle("padding", "0 6px 0 6px");

	var contents = new Element('div');
	AjaxSpinner.show();

	var ajaxOptions = {
		method :'get',
		update :contents,
		onComplete : function() {
			var mainContent = new Element('div');
			mainContent.setStyles( {
				height :400
			});
			mainContent.setStyle("overflow", "auto");
			contents.injectInside(mainContent);
			var iframeEl = new Element('iframe', {
				id :"iframePrint",
				src :sLinkHref,
				display :"none"
			});
			iframeEl.setStyle("display", "none");
			iframeEl.setStyle("display", "none");
			iframeEl.injectInside(mainContent);

			openAlertMOOdalBox(mainContent, new Array(printEl), width, height);
			AjaxSpinner.hide();
		},
		onFailure : function() {
			AjaxSpinner.hide();
		}
	};
	this.ajaxRequest = new Ajax(sLinkHref, ajaxOptions).request();
}

function openAgreementMOOdalBox(sLinkHref, width, height) {

	var btnStyle = {
		color :"#E20A16",
		borderBottom :"2px solid #E20A16",
		fontWeight :"bold",
		align :"middle",
		cursor :"pointer"
	};

	var printEl = new Element('span');
	printEl.setStyles(btnStyle);
	printEl.appendText("Accetta Regolamento");
	// var printFunction = function(e) {
	// var popup = openPopup(sLinkHref, 'printFriendlyView', 710, 650, 'no', 1,
	// 1, 0, 0, 0, 0);
	// var printPopup = function() {
	// popup.print();
	// };
	// printPopup.delay(1000);
	// };

	var setParamiter = function() {
		if (document.getElementById("autorizzo2") != null) {
			document.getElementById("autorizzo2").value = 'true';
			// document.getElementsByName("customer.cartaPiuRulesAgreement").value
			// = 'true';
		}
		else if (document.getElementById("cartaPiuAgreement") != null)
		{
			document.getElementById("cartaPiuAgreement").value = 'true';
		}
	}

	printEl.addEvent("click", function() {
		setParamiter.apply();
	});
	printEl.setStyle("padding", "0 6px 0 6px");

	var contents = new Element('div');
	AjaxSpinner.show();

	var ajaxOptions = {
		method :'get',
		update :contents,
		onComplete : function() {
			var mainContent = new Element('div');
			mainContent.setStyles( {
				height :400
			});
			mainContent.setStyle("overflow", "auto");
			contents.injectInside(mainContent);
			var iframeEl = new Element('iframe', {
				id :"iframePrint",
				src :sLinkHref,
				display :"none"
			});
			iframeEl.setStyle("display", "none");
			iframeEl.setStyle("display", "none");
			iframeEl.injectInside(mainContent);

			openMOOdalBox(mainContent, new Array(printEl), width, height);
			AjaxSpinner.hide();
		},
		onFailure : function() {
			AjaxSpinner.hide();
		}
	};
	this.ajaxRequest = new Ajax(sLinkHref, ajaxOptions).request();
}
// Gets the current carte position in the given object, browser independent
function doGetCaretPosition(ctrl) {

	var CaretPos = 0;
	if (ctrl) {
		// IE Support
		if (document.selection) {
			ctrl.focus();
			var Sel = document.selection.createRange();
			Sel.moveStart('character', -ctrl.value.length);
			CaretPos = Sel.text.length;
		}
		// Firefox support
		else if (ctrl.selectionStart || ctrl.selectionStart == '0') {
			CaretPos = ctrl.selectionStart;
		}
		;
	}
	return (CaretPos);

}

// Set caret position in a text field, browser independent.
function setCaretPosition(ctrl, pos) {
	if (ctrl) {
		if (ctrl.setSelectionRange) {
			ctrl.focus();
			ctrl.setSelectionRange(pos, pos);
		} else if (ctrl.createTextRange) {
			var range = ctrl.createTextRange();
			range.collapse(true);
			range.moveEnd('character', pos);
			range.moveStart('character', pos);
			range.select();
		}
	}
}

function cloneWithEvents(srcElement) {
	var clone = srcElement.clone().cloneEvents(srcElement);
	var inEl = srcElement.getElements('*');
	var outEl = clone.getElements('*');
	inEl.each( function(el, i) {
		if (el.$events)
			$(outEl[i]).cloneEvents(el);
	});
	return clone;
}

/**
 * This function groups everything that should be initialized regardless the
 * page in which is called. This is called at DomReady event.
 * 
 * @return nothing.
 */
function initGenericDomReadyPageEvents() {
	$$('a[class=external]').each( function(link) {
		link.target = "_blank";
	});

	// MouseOver for sidebar products.
	initMouseOverForSidebarClassifica();
	initMouseOverForRelatedProducts();
}

/**
 * This function groups everything that should be initialized regardless the
 * page in which is called. This is called at OnLoad event (used when there must
 * be all the images load in the page).
 * 
 * @return nothing.
 */
function initGenericLoadPageEvents() {
	// Classifiche accordion. This is initialized when ALL the images are
	// loaded, included the reflected ones,
	// otherwise the accordion element can't know the right heigth of its div.
	var accordionElements = $$('.sidebarClassificaToggler');
	if ((accordionElements) && (accordionElements.length > 0)) {
		var accordion = new Accordion(accordionElements, $$('.elementChart'), {
			onActive : function(toggler, el) {
				toggler.addClass('accordionSelected');

				var link = $$('.tuttaLaClassifica a').getProperty('href');

				if (accordionElements.length == 1) {
					toggler.addEvent('click', function() {
						location.href = $$('.tuttaLaClassifica a').getProperty(
								'href');
					});
				}
			},
			onBackground : function(toggler, el) {
				toggler.removeClass('accordionSelected');
				if (accordionElements.length > 1) {
					toggler.removeEvent('click');
				}
			},
			display :0,
			show :false,
			height :true,
			width :false,
			opacity :true,
			fixedHeight :false,
			fixedWidth :false,
			wait :false,
			alwaysHide :false
		});
	}
	;
  
//  var accordionElementsYouEffe = $$('.sidebarClassificaTogglerYouEffe');
//  if ((accordionElementsYouEffe) && (accordionElementsYouEffe.length > 0)) {
//    var accordionYouEffe = new Accordion(accordionElementsYouEffe, $$('.elementChartYouEffe'), {
//      onActive: function(togglerYouEffe, el) {
//        togglerYouEffe.addClass('accordionSelected');
//
//        var linkYouEffe = $$('.tuttaLaClassificaYouEffe a').getProperty('href');
//
//        if (accordionElementsYouEffe.length == 1) {
//          togglerYouEffe.addEvent('click', function() {
//            location.href = $$('.tuttaLaClassificaYouEffe a').getProperty('href');
//          });
//        }
//      },
//      onBackground: function(togglerYouEffe, el) {
//        togglerYouEffe.removeClass('accordionSelected');
//        if (accordionElementsYouEffe.length > 1) {
//          togglerYouEffe.removeEvent('click');
//        }
//      },
//      display: 0,
//      show: false,
//      height: true,
//      width: false,
//      opacity: true,
//      fixedHeight: false,
//      fixedWidth: false,
//      wait: false,
//      alwaysHide: false
//    });
//  };
//	var accordionElementsEvents = $$('.sidebarClassificaTogglerEvents');
//	if ((accordionElementsEvents) && (accordionElementsEvents.length > 0)) {
//	  var accordionEvents = new Accordion(accordionElementsEvents, $$('.elementChartEvents'), {
//	    onActive: function(togglerEvents, el) {
//	      togglerEvents.addClass('accordionSelected');
//	
//	      var linkEvents = $$('.tuttaLaClassificaEvents a').getProperty('href');
//	
//	      if (accordionElementsEvents.length == 1) {
//	        togglerEvents.addEvent('click', function() {
//	          location.href = $$('.tuttaLaClassificaEvents a').getProperty('href');
//	        });
//	      }
//	    },
//	    onBackground: function(togglerEvents, el) {
//	      togglerEvents.removeClass('accordionSelected');
//	      if (accordionElementsEvents.length > 1) {
//	        togglerEvents.removeEvent('click');
//	      }
//	    },
//	    display: 0,
//	    show: false,
//	    height: true,
//	    width: false,
//	    opacity: true,
//	    fixedHeight: false,
//	    fixedWidth: false,
//	    wait: false,
//	    alwaysHide: false
//	  });
//	};
//	
//	var accordionElementsTopQuality = $$('.sidebarClassificaTogglerTopQuality');
//	if ((accordionElementsTopQuality) && (accordionElementsTopQuality.length > 0)) {
//	  var accordionTopQuality = new Accordion(accordionElementsTopQuality, $$('.elementChartTopQuality'), {
//	    onActive: function(togglerTopQuality, el) {
//	      togglerTopQuality.addClass('accordionSelected');
//	
//	      var linkTopQuality = $$('.tuttaLaClassificaTopQuality a').getProperty('href');
//	
//	      if (accordionElementsTopQuality.length == 1) {
//	        togglerTopQuality.addEvent('click', function() {
//	          location.href = $$('.tuttaLaClassificaTopQuality a').getProperty('href');
//	        });
//	      }
//	    },
//	    onBackground: function(togglerTopQuality, el) {
//	      togglerTopQuality.removeClass('accordionSelected');
//	      if (accordionElementsTopQuality.length > 1) {
//	        togglerTopQuality.removeEvent('click');
//	      }
//	    },
//	    display: 0,
//	    show: false,
//	    height: true,
//	    width: false,
//	    opacity: true,
//	    fixedHeight: false,
//	    fixedWidth: false,
//	    wait: false,
//	    alwaysHide: false
//	  });
//	};
	
}
var Navigation = {

  replaceparams : [
  //-------------------------------
      [ 'prkw', 'k' ], //productKeyword
      [ 'cat1', 'c1' ], //categoryId1
      [ 'cat2', 'c2' ], //categoryId2
      [ 'cat3', 'c3' ], //categoryId3
      [ 'pub', 'pu' ], //publisherId
      [ 'art', 'ac' ], //artistId
      [ 'att', 'ar' ], //actorId
      [ 'aut', 'au' ], //authorId
      [ 'kas', 'ca' ], //castElementId
      [ 'cor', 'ch' ], //choirElementId
      [ 'cur', 'cu' ], //curatorId
      [ 'reg', 'di' ], //directorId
      [ 'ill', 'il' ], //illustratorId
      [ 'int', 'in' ], //interpreterId
      [ 'orc', 'or' ], //orchestraId
      [ 'dor', 'ol' ], //orchestraLeaderId
      [ 'for', 'ph' ], //photographerId
      [ 'ese', 'pl' ], //playerId
      [ 'trs', 'sw' ], //scorewriterId
      [ 'can', 'si' ], //singerId
      [ 'tra', 'tr' ], //translatorId
      [ 'tagId', 't' ], //tag
      [ 'page', 'p' ], //pageNumber
      [ 'pbkw', 'pk' ], //publisherKeyword
      [ 'srch', 's' ], //searchType
      [ 'layout', 'l' ], //presentation layout
      [ 'sort', 'o' ], //result ordering
      [ 'prcSlt', 'ps' ], //priceSlot
      [ 'dscSlt', 'ds' ], //discountPercentageSlot
      [ 'dlvSlt', 'dls' ], //deliverySlot
      [ 'pblSlt', 'pb' ], //publicationSlot
      [ 'prmt', 'pt' ], //promotion type
      [ 'prm', 'pr' ], //promotionOnly
      [ 'prmc', 'pc' ], //promotionCode
      [ 'pegiId', 'pd'], // pegi code
      [ 'platformId', 'plt'], // platform code
      [ 'seriesId', 'ser'] // series desc
      
  ],

  replacepar : function(param, reverse) {
    var j = reverse ? 1 : 0;
    var k = reverse ? 0 : 1;
    for ( var i = 0, l = this.replaceparams.length; i < l; i++) {
      if (this.replaceparams[i][j] == param) {
        return this.replaceparams[i][k];
      }
    }
    return null;
  },

  filter : [],

  // Parameters ordered list, space separated.
  // @todo This should be an array, but almost everything should be refactored in this navigation...
  fsrt : " ",

  setFilter : function(paramName, value) {
    if (paramName) {
      delete this.filter[paramName];
      this.filter[paramName] = value;
      if (this.fsrt && (this.fsrt.indexOf(paramName) > -1)) {
        this.fsrt = this.fsrt.replace(paramName, "");
        this.fsrt = this.fsrt.replace(/  /g, " "); // Removes leftovers double spaces.
      }
      this.fsrt = this.fsrt + paramName + " "
    }
  },

  removeFilter : function(paramName) {
    if (paramName) {
      delete this.filter[paramName];
      if (this.fsrt && (this.fsrt.indexOf(paramName) > -1)) {
        this.fsrt = this.fsrt.replace(paramName, "");
        this.fsrt = this.fsrt.replace(/  /g, " "); // Removes leftovers double spaces.
      }
    }
  },
  
  setHref : function(href) {
	  this.href = href;
  },

  init : function() {
    jslog.debug('Navigation.init()' + this.historyKey);

    HistoryManager.initialize( {
      iframeSrc :'/blank.htm'
    });

    this.historyKey = 'nav';
    this.history = HistoryManager.register(this.historyKey, [], function(values) {
      jslog.debug('onMatch(' + values.join(' | ') + ')');
      switch (values.length) {
        case 0:
          var filter0 = navFilter0();
          var fsrt0 = navFsrt0();

          var length = 0;
          for ( var p in this.filter)
            length++;
          var length0 = 0;
          for ( var p0 in filter0)
            length0++;

          var eq = length == length0;
          if (eq) {
            for (k in this.filter) {
              if (this.filter[k] != filter0[k]) {
                eq = false;
                break;
              }
            }
          }

          if (!eq) {
            this.filter = filter0;
            this.fsrt = fsrt0;
            this.request();
          }
          break;
        case 1:
          var array = decodeURIComponent(values[0]).split('..');
          this.filter = {};
          this.fsrt = " ";
          array.each( function(item) {
            var p = item.indexOf('_');
            var key = this.replacepar(item.substr(0, p), true);
            this.setFilter(key, item.substr(p + 1));
          }, this);
          this.request();
          break;
      }
      if (values && values.length == 1) {
      }
    }.bind(this), function(values) {
      jslog.debug('onGenerate(' + values.join(' | ') + ')');
      return [ this.historyKey, '(', values[0], ')' ].join('');
    }.bind(this), this.historyKey + '\\(([^\\)]*)\\)');
  },

  search : function(callback) {

	/*if (this.href) {
		var href = this.href;
		var indexOfSharp = href.indexOf('#');
		if(indexOfSharp > -1) {
			href = href.substring(0, indexOfSharp);
		}
		delete this.href;
	    location.href = href + '#nav(' + this.hash() + ')';
	} else*/
    if (this.$cataloguePath) {
      location.href = '' + this.$cataloguePath + '#nav(' + this.hash() + ')';
    } else {
      this.saveState();
      this.request(callback);
    }
  },

  request : function(callback) {
    var params = {};

    var validParams = {};
    for ( var i = 0, l = this.replaceparams.length; i < l; i++) {
      validParams[this.replaceparams[i][0]] = true;
    }

    for ( var key in this.filter) {
      if (validParams[key]) {
        params[key] = this.filter[key];
      }
    }

    params['fsrt'] = this.fsrt;
    AjaxSpinner.show();
    doAjaxRequest(new AjaxMany('/AjaxNavigation.action', {
      method :'post',
      data :params,
      update :$('contenuti'),
      evalScripts :true,
      onComplete : function() {
        AjaxSpinner.hide();
        if (callback)
          callback();
      }.bind(this),
      onFailure : function() {
        AjaxSpinner.hide();
        jslog.debug(this.queryResponse);
      }.bind(this)
    }));
  },

  saveState : function() {
    jslog.debug('saveState()');
    this.history.setValues( [ this.hash() ]);
  },

  hash : function() {
    var array = [];
    for ( var key in this.filter) {
      var par = this.replacepar(key);
      if (par != null) {
          array.push(par + '_' + escape(this.filter[key]));
      }
    }
    return array.join('..');
  }

};

function initAutocompleters(cataloguePath) {
  jslog.debug('initAutocompleters()');
  if (cataloguePath)
    Navigation.$cataloguePath = cataloguePath;

  [ $('cercaEditoreButton'), $('cercaAutoreButton'), $('cercaArtistaButton'), $('cercaRegistaButton'),
      $('cercaAttoreButton') ].each( function(button) {
   // Blocks any click on the button "Cerca" of autocompleters before something is selected from autocomplete result.
    if (button) {
      $(button).addEvent('click', function(e) {
        e = new Event(e);
        e.preventDefault();
        e.stop();
      });
    }
  });

  // hides any previously built html structure for choices (will be removed on trash event)
  $$('.autocompleter-choices').setStyle('display', 'none');

  jslog.debug('cercaEditore');
  var pubSearch = $('cercaEditore');
  if (pubSearch) {
    var pubIndicator = new Element('div', {
      'class' :'ajax-spinner',
      'styles' : {
        'display' :'none'
      }
    }).setHTML('').injectAfter(pubSearch);
    var autoDwr = new Autocompleter.DWR(pubSearch, updatePubChoices, {
      valueSelector :getPubLabel,
      inheritWidth :false,
      afterUpdateElement :onPubSelected,
      'onRequest' : function(el) {
        pubIndicator.setStyle('display', 'block');
      },
      'onComplete' : function(el) {
        pubIndicator.setStyle('display', 'none');
      },
      minLength :2,
      useSelection :false
    });
    // text search when no choices are suggested
    autoDwr.element.addEvent(window.ie ? 'keydown' : 'keypress', function(e) {
      if (!this.selected && !e.shift && e.key == 'enter' && this.element.value.length >= this.options.minLength) {
        onPubTextSearch(this.element.value);
        e.stop();
      }
    }.bindWithEvent(autoDwr));
  }

  initContrAutocompleter($('cercaAutore'), updateAutChoices, onAutSelected, onAutTextSearch);
  initContrAutocompleter($('cercaArtista'), updateArtChoices, onArtSelected, onArtTextSearch);
  initContrAutocompleter($('cercaRegista'), updateDirChoices, onDirSelected, onDirTextSearch);
  initContrAutocompleter($('cercaAttore'), updateActChoices, onActSelected, onActTextSearch);
}

function initContrAutocompleter(search, updateChoices, onSelected, onTextSearch) {
  if (search) {
    var indicator = new Element('div', {
      'class' :'ajax-spinner',
      'styles' : {
        'display' :'none'
      }
    }).setHTML('').injectAfter(search);
    var autoDwr = new Autocompleter.DWR(search, updateChoices, {
      valueSelector :getContrLabel,
      afterUpdateElement :onSelected,
      'onRequest' : function(el) {
        indicator.setStyle('display', 'block');
      },
      'onComplete' : function(el) {
        indicator.setStyle('display', 'none');
      },
      minLength :2,
      useSelection :false
    });
    // text search when no choices are suggested
    autoDwr.element.addEvent(window.ie ? 'keydown' : 'keypress', function(e) {
      if (!this.selected && !e.shift && e.key == 'enter' && this.element.value.length >= this.options.minLength) {
        onTextSearch(this.element.value);
        e.stop();
      }
    }.bindWithEvent(autoDwr));
  }
}

function updatePubChoices(autocompleter, token) {
  PublisherAutocompletion.autocompletePublisherItems(token, Navigation.filter, function(data) {
    autocompleter.updateChoices(data)
  });
}

function updateAutChoices(autocompleter, token) {
  ContributorAutocompletion.autocompleteAuthorItems(token, Navigation.filter, function(data) {
    autocompleter.updateChoices(data)
  });
}

function updateArtChoices(autocompleter, token) {
  ContributorAutocompletion.autocompleteArtistItems(token, Navigation.filter, function(data) {
    autocompleter.updateChoices(data)
  });
}

function updateDirChoices(autocompleter, token) {
  ContributorAutocompletion.autocompleteDirectorItems(token, Navigation.filter, function(data) {
    autocompleter.updateChoices(data)
  });
}

function updateActChoices(autocompleter, token) {
  ContributorAutocompletion.autocompleteActorItems(token, Navigation.filter, function(data) {
    autocompleter.updateChoices(data)
  });
}

function getPubLabel(navItem) {
  var label = navItem.item.shortDescription;
  if (navItem.productCount) {
    label = label + ' (' + navItem.productCount + ')';
  }
  return label;
}

function getContrLabel(navItem) {
  var label = '';
  if (navItem.item.name)
    label = navItem.item.name;
  if (navItem.item.lastName) {
    label = (label != '') ? (label + ' ' + navItem.item.lastName) : navItem.item.lastName;
  }
  if (navItem.productCount) {
    label = label + ' (' + navItem.productCount + ')';
  }
  return label;
}

//@todo this was modified to force use the search button. To restore the original function of "auto-search on select",
//remove the addEvent and keep just the central lines from delete to search().
function onPubSelected(navItem) {
  if ($('cercaEditoreButton')) {
    $('cercaEditoreButton').addEvent('click', function(e) {
      e = new Event(e);
      e.stop();
      Navigation.setFilter('pub', navItem.item.id);
      Navigation.search();
    });
  }
}

//@todo this was modified to force use the search button. To restore the original function of "auto-search on select",
//remove the addEvent and keep just the central lines from delete to search().
function onAutSelected(navItem) {
  if ($('cercaAutoreButton')) {
    $('cercaAutoreButton').addEvent('click', function(e) {
      e = new Event(e);
      e.stop();
      Navigation.setFilter('aut', navItem.item.id);
      Navigation.search();
    });
  }
}

//@todo this was modified to force use the search button. To restore the original function of "auto-search on select",
//remove the addEvent and keep just the central lines from delete to search().
function onArtSelected(navItem) {
  if ($('cercaArtistaButton')) {
    $('cercaArtistaButton').addEvent('click', function(e) {
      e = new Event(e);
      e.stop();
      Navigation.setFilter('art', navItem.item.id);
      Navigation.search();
    });
  }
}

//@todo this was modified to force use the search button. To restore the original function of "auto-search on select",
//remove the addEvent and keep just the central lines from delete to search().
function onDirSelected(navItem) {
  if ($('cercaRegistaButton')) {
    $('cercaRegistaButton').addEvent('click', function(e) {
      e = new Event(e);
      e.stop();
      Navigation.setFilter('reg', navItem.item.id);
      Navigation.search();
    });
  }
}

//@todo this was modified to force use the search button. To restore the original function of "auto-search on select",
//remove the addEvent and keep just the central lines from delete to search().
function onActSelected(navItem) {
  if ($('cercaAttoreButton')) {
    $('cercaAttoreButton').addEvent('click', function(e) {
      e = new Event(e);
      e.stop();
      Navigation.setFilter('att', navItem.item.id);
      Navigation.search();
    });
  }
}

function onAutTextSearch(value) {
  Navigation.setFilter('srch', '3');
  Navigation.setFilter('prkw', value);
  Navigation.search();
}

function onArtTextSearch(value) {
  Navigation.setFilter('srch', '4');
  Navigation.setFilter('prkw', value);
  Navigation.search();
}

function onDirTextSearch(value) {
  Navigation.setFilter('srch', '6');
  Navigation.setFilter('prkw', value);
  Navigation.search();
}

function onActTextSearch(value) {
  Navigation.setFilter('srch', '7');
  Navigation.setFilter('prkw', value);
  Navigation.search();
}

function onPubTextSearch(value) {
  Navigation.setFilter('srch', '8');
  Navigation.setFilter('prkw', value);
  Navigation.search();
}

/*
 * Wishlist
 */
function changeWishlistPage(idName, pageNumberLoc, customerId, layout, printlinkBaseUri) {
  jslog.debug('changeWishlistPage()');
  if ((idName == 'pageLinkIndietro') || (idName == 'pageLinkIndietro2')) {
    pageNumberLoc = pageNumberLoc - 1;
  } else if ((idName == 'pageLinkAvanti') || (idName == 'pageLinkAvanti2')) {
    pageNumberLoc = pageNumberLoc + 1;
  } else {
    pageNumberLoc = idName.substring(idName.indexOf('_') + 1);
  }
  jslog.debug('pageNumberLoc = ' + pageNumberLoc);
  AjaxSpinner.show();
  var data = {
    _eventName: 'show',
    pageNumber: pageNumberLoc,
    layout: layout,
    fromUpdatePage: true
  };
  if (customerId) {
    data['customerId'] = customerId;
  }
  new Ajax('/UserWishlistContent.action', {
    method: 'get',
    data: data,
    update: $('catalogoWishlist'),
    evalScripts: true,
    onComplete: function() {
      $('catalogoWishlist').setStyle('display', 'block');
      var printLink = $('printLink');
      if (printLink) {
        printLink.setProperty('href', printlinkBaseUri + '?pageNumber=' + pageNumberLoc);
      }
      AjaxSpinner.hide();
    }
  }).request();
}

function changeWishlistViewMode(idName, customerId) {
  jslog.debug('changeWishlistViewMode()');
  AjaxSpinner.show();
  var data = {
    _eventName: 'show',
    layout: idName.substring(idName.indexOf('_') + 1),
    fromUpdatePage: true
  };
  if (customerId) {
    data['customerId'] = customerId;
  }
  new Ajax('/UserWishlistContent.action', {
    method: 'get',
    data: data,
    update: $('catalogoWishlist'),
    evalScripts: true,
    onComplete: function() {
      $('catalogoWishlist').setStyle('display', 'block');
      AjaxSpinner.hide();
    }
  }).request();
}

/*
 * Scaffale
 */
function changeShelfPage(idName, pageNumberLoc, customerId, layout, printlinkBaseUri) {
  jslog.debug('changeShelfPage()');
  if ((idName == 'pageLinkIndietroShelf') || (idName == 'pageLinkIndietroShelf2')) {
    pageNumberLoc = pageNumberLoc - 1;
  } else if ((idName == 'pageLinkAvantiShelf') || (idName == 'pageLinkAvantiShelf2')) {
    pageNumberLoc = pageNumberLoc + 1;
  } else {
    pageNumberLoc = idName.substring(idName.indexOf('_') + 1);
  }
  jslog.debug('pageNumberLoc = ' + pageNumberLoc);
  AjaxSpinner.show();
  var data = {
    _eventName: 'show',
    pageNumber: pageNumberLoc,
    layout: layout,
    fromUpdatePage: true
  };
  if (customerId) {
    data['customerId'] = customerId;
  }
  new Ajax('/UserShelfContent.action', {
    method: 'get',
    data: data,
    update: $('catalogoScaffale'),
    evalScripts: true,
    onComplete: function() {
      $('catalogoScaffale').setStyle('display', 'block');
      var printLink = $('printLink');
      if (printLink) {
        printLink.setProperty('href', printlinkBaseUri + '?pageNumber=' + pageNumberLoc);
      }
      AjaxSpinner.hide();
    }
  }).request();
}

function changeShelfViewMode(idName, pageNumber, customerId) {
  jslog.debug('changeShelfViewMode()');
  AjaxSpinner.show();
  var data = {
    _eventName: 'show',
    layout: idName.substring(idName.indexOf('_') + 1),
    fromUpdatePage: true,
    pageNumber: pageNumber
  };
  if (customerId) {
    data['customerId'] = customerId;
  }
  new Ajax('/UserShelfContent.action', {
    method: 'get',
    data: data,
    update: $('catalogoScaffale'),
    evalScripts: true,
    onComplete: function() {
      $('catalogoScaffale').setStyle('display', 'block');
      AjaxSpinner.hide();
    }
  }).request();
}

function removeFromShelf(idName, layout, pageNumber) {
  jslog.debug('removeFromShelf()');
  AjaxSpinner.show();
  new Ajax('/UserShelfContent.action', {
    method: 'get',
    data: {
      _eventName: 'deleteFromShelf',
      productId: idName.substring(idName.indexOf('_') + 1),
      layout: layout,
      fromUpdatePage: true,
      pageNumber: pageNumber
    },
    update: $('catalogoScaffale'),
    evalScripts: true,
    onComplete: function() {
      $('catalogoScaffale').setStyle('display', 'block');
      new Ajax('/SidebarShelf.action', {
        method: 'get',
        data: {
          _eventName: 'show',
          ajax: true
        },
        update: $('miniScaffale'),
        onComplete: function() {
          AjaxSpinner.hide();
          CartAccordion.addMini($('miniScaffale'));
        }
      }).request();
    }
  }).request();
}

function changeShelfRating(idName, layout, pageNumber) {
  jslog.debug('changeShelfRating()');
  AjaxSpinner.show();
  new Ajax('/UserShelfContent.action', {
    method: 'get',
    data: {
      _eventName: 'changeRating',
      productId: idName.substring(idName.indexOf('_') + 1),
      layout: layout,
      pageNumber: pageNumber,
      fromUpdatePage: true
    },
    update: $('catalogoScaffale'),
    evalScripts: true,
    onComplete: function() {
      $('catalogoScaffale').setStyle('display', 'block');
      AjaxSpinner.hide();
    }
  }).request();
}

function caroselloTabInit() {
  $each($$('.caroselli'), function(caroselliEl) {
    var i = 0;
    var caroselloList = caroselliEl.getElements('.carosello');
    $each(caroselliEl.getElements('.tabCarosello'), function(tabCaroselloEl) {
      tabCaroselloEl.refCarosello = caroselloList[i];
      i++;
    });
  });
}

function caroselloSlide(el, tabEl, container) {

  var ix = 0;
  var carouselTabs = $$('.tabCarosello');
  var contenitoreList = container.getElements('.contenitoreCarosello');

  if (!contenitoreList || (contenitoreList.length == 0))
    return;

  var currentCarousel = null;
  for ( var i = 0; i < carouselTabs.length; i++) {
    if (carouselTabs[i] == tabEl) {
      carouselTabs[i].addClass('currentCarousel');
      currentCarousel = carouselTabs[i];
      ix = i;
    } else {
      carouselTabs[i].removeClass('currentCarousel');
    }
  }

  if (currentCarousel
      && (currentCarousel.refCarosello != contenitoreList[0]
          .getElements('.carosello')[0])) {
    var carosello = currentCarousel.refCarosello;
    var caroselloParent = carosello.parentNode;
    caroselloParent.removeChild(carosello);
    caroselloParent.insertBefore(carosello, contenitoreList[0]
        .getElements('.carosello')[0]);
  }

}

function carouselMove(containerEl, step) {
  var viewPort = containerEl.getElements('.viewPort')[0];
  var items = viewPort.getElements('.caroselloItem');
  var offset = 45 + 12;
  var elements = new Array();
  var itemsLength = items.length;
  var diveffects = {};
  if (itemsLength <=  3) {
    // Three elements only in carousel: effects are reduced to adapt the displayed elements
    if (step < 0) {
      offset = offset * -1;

      diveffects = {
        '0' : {
          'width' :75
        },
        '1' : {
          'width' :75
        },
        '2' : {
          'width' :100
        },
        '3' : {
          'bottom' :50
        },
        '4' : {
          'bottom' :50
        },
        '5' : {
          'bottom' :35
        },
        '6' : {
          'top' :160
        },
        '7' : {
          'top' :160
        },
        '8' : {
          'top' :175
        }
      };
    } else {
      var el = items[itemsLength - 1];
      viewPort.removeChild(el);
      viewPort.insertBefore(el, items[0]);
      offset = 0;
      items = viewPort.getElements('.caroselloItem');
      diveffects = {
        '0' : {
          'width' :75
        },
        '1' : {
          'width' :100
        },
        '2' : {
          'width' :75
        },
        '3' : {
          'bottom' :50
        },
        '4' : {
          'bottom' :35
        },
        '5' : {
          'bottom' :50
        },
        '6' : {
          'top' :160
        },
        '7' : {
          'top' :175
        },
        '8' : {
          'top' :160
        }
      };
    }
  } else {
    // Five or more items in carousel: five elements have effects.

    if (step < 0) {
      offset = offset * -1;

      diveffects = {
        '0' : {
          'margin-left' :offset
        },
        '1' : {
          'width' :45
        },
        '2' : {
          'width' :75
        },
        '3' : {
          'width' :100
        },
        '4' : {
          'width' :75
        },
        '5' : {
          'bottom' :60
        },
        '6' : {
          'bottom' :60
        },
        '7' : {
          'bottom' :50
        },
        '8' : {
          'bottom' :35
        },
        '9' : {
          'bottom' :50
        },
        '10' : {
          'top' :150
        },
        '11' : {
          'top' :150
        },
        '12' : {
          'top' :160
        },
        '13' : {
          'top' :175
        },
        '14' : {
          'top' :160
        }
      };
    } else {
      var el = items[itemsLength - 1];
      viewPort.removeChild(el);
      el.setStyle('margin-left', '-' + offset + 'px');
      viewPort.insertBefore(el, items[0]);
      offset = 0;
      items = viewPort.getElements('.caroselloItem');
      diveffects = {
        '0' : {
          'margin-left' :offset
        },
        '1' : {
          'width' :75
        },
        '2' : {
          'width' :100
        },
        '3' : {
          'width' :75
        },
        '4' : {
          'width' :45
        },
        '5' : {
          'bottom' :60
        },
        '6' : {
          'bottom' :50
        },
        '7' : {
          'bottom' :35
        },
        '8' : {
          'bottom' :50
        },
        '9' : {
          'bottom' :60
        },
        '10' : {
          'top' :150
        },
        '11' : {
          'top' :160
        },
        '12' : {
          'top' :175
        },
        '13' : {
          'top' :160
        },
        '14' : {
          'top' :150
        }
      };
    }
  }
  if (itemsLength >= 3) {
    var elementsLength = (itemsLength > 4) ? 5 : 3;
    
    for ( var i = 0; i < elementsLength; i++) {
      elements.push(items[i]);
    }

    var newElements = elements.slice(0, elementsLength);
    for ( var i = elementsLength; i < (elementsLength * 2); i++) {
      newElements[i] = newElements[i - elementsLength].getElements('.reflected')[0];
    }
    for ( var i = (elementsLength * 2); i < (elementsLength * 3); i++) {
      newElements[i] = newElements[i - (elementsLength * 2)].getElements('.imgReflection')[0];
    }
    var slideFx = new Fx.Elements(newElements, {
      duration :300,
      transition :Fx.Transitions.Cubic.easeInOut,
      wheelStops :false,
      onComplete : function() {
        if (step < 0) {
          var el = items[0];
          viewPort.removeChild(el);
          el.setStyle('margin-left', '0');
          viewPort.appendChild(el);
        }
        if (step < -1) {
          carouselMove(containerEl, step + 1);
        } else if (step > 1) {
          carouselMove(containerEl, step - 1);
        } else {
          updateBaloon(containerEl.getParent());
        }
      }
    });
    slideFx.start(diveffects);

  }
}

function adjustBaloon(img, baloon) {
  var carosello = baloon.getParent();
  updateBaloon(carosello);
}

function updateBaloon(carosello) {
  if ((carosello == undefined) || (carosello == null)) {
    return;
  }

  var baloon = carosello.getElements('.caroselloPopup')[0];
  var items = carosello.getElements('.viewPort')[0].getElements('.caroselloItem');
  var currDetail = cloneWithEvents(carosello
      .getElements('.caroselloItemoDetail')[(items.length <= 3) ? 1 : 2]);
  var baloonConetnetNl = baloon.getElements('.caroselloItemoDetail');

  if (baloonConetnetNl.length > 0) {
    baloon.removeChild(baloonConetnetNl[0]);
  }

  currDetail.setStyle('display', 'block');
  currDetail.setStyle('visibility', 'visible');
  if (currDetail.hasClass('caroselloItemDetailPromotion')) {
    baloon.addClass('caroselloPopupPromotion');
  } else {
    baloon.removeClass('caroselloPopupPromotion');
  }
  baloon.appendChild(currDetail);
}

// /-------------------------------- DA MODIFICARE
// -----------------------------------------------------

var CarouselSlider = new Class( {
  container :null,
  delay :5000,
  timer :null,
  active :true,
  initialize : function(container, delay) {
    this.container = container;
    if (delay) {
      this.delay = delay;
    }

    $each(container.getElements('.caroselli'), function(el) {
      el.addEvent('mouseenter', this.onMouseEnter.bind(this));
      el.addEvent('mouseleave', this.onMouseLeave.bind(this));
    }, this);
    this.setTimerActive();
  },
  onMouseEnter : function(e) {
    this.pause = true;
    this.active = false;
    this.setTimerActive();
  },
  onMouseLeave : function(e) {
    this.pause = false;
    this.active = true;
    this.setTimerActive();
  },
  carouselTimerFunction : function() {
    var ix = 0;
    var carouselTabs = this.container.getElements('.tabCarosello');

    for ( var i = 0; i < carouselTabs.length; i++) {
      ix++;
      if (carouselTabs[i].hasClass('currentCarousel')) {
        break;
      }
    }

    if (ix == carouselTabs.length) {
      ix = 0;
    }
    var contenitoreList = this.container.getElements('.contenitoreCarosello');
    if (contenitoreList && (contenitoreList.length > 0)) {
      caroselloSlide(this.container.getElements('.contenitoreCarosello')[0],
          carouselTabs[ix], this.container);
      this.timer = this.carouselTimerFunction.delay(this.delay, this);
    }
  },
  setTimerActive : function() {
    if (this.active) {
      if (this.timer || this.pause) {
        return;
      }
      this.timer = this.carouselTimerFunction.delay(this.delay, this);
    } else {
      if (!this.timer)
        return;
      $clear(this.timer);
      this.timer = null;
    }
  }
});

// -----------------------------------inizializzazione
// ----------------------------------
function carouselInit(/* String|DomElement|Element? */container) {
  if (container) {
    container = $(container);
  } else {
    container = $(document);
  }
  if ((container == undefined) || (container == null)) {
    return;
  }
  caroselloTabInit();
  activateAddToCartButtons(container);
  addImageReflections(container);

  $each(container.getElements('.caroselloPopup'), function(el) {
    var carosello = el.getParent();

    var middleTipsLst = carosello.getElements('.tips');
    if (middleTipsLst && (middleTipsLst.length > 2)) {
      var middleTips = middleTipsLst[((middleTipsLst.length == 3) ? 1 : 2)];
      var imageList = middleTips.getElements('img')
      if (imageList && (imageList.length > 0)) {
        var imageEl = imageList[0];
        adjustBaloon(imageEl, el);

      }
    }
  });

  $each(container.getElements('.caroselloPopup'), function(el) {
    var carosello = el.getParent();
    var middleTipsLst = carosello.getElements('.tips');
    if (middleTipsLst && (middleTipsLst.length > 2)) {
      var middleTips = middleTipsLst[2];
      var imageList = middleTips.getElements('img')
      if (imageList && (imageList.length > 0)) {
        var imageEl = imageList[0];
        imageEl.removeEvents('load');
        imageEl.addEvent('load', function() {
          adjustBaloon(imageEl, el);
        });
      }
    }
  });

  $each(container.getElements('.caroselloArrowLeftButton'), function(el) {
    var carosello = el.getParent().getParent();
    try {
      var caroselloItemContainer = carosello
          .getElements('.caroselloItemContainer')[0];
      el.removeEvents('click');
      el.addEvent('click', function(event) {
        var event = new Event(event);
        carouselMove(caroselloItemContainer, -1);
        event.preventDefault();
      });
    } catch (e) {
    }
  });

  $each(container.getElements('.caroselloArrowRightButton'), function(el) {
    var carosello = el.getParent().getParent();
    try {
      var caroselloItemContainer = carosello
          .getElements('.caroselloItemContainer')[0];
      el.addEvent('click', function(event) {
        var event = new Event(event);
        carouselMove(caroselloItemContainer, 1);
        event.preventDefault();
      });
    } catch (e) {
    }
  });

  $each(container.getElements('.tips'), function(el) {
    el.removeEvents('click');
    el.addEvent('click', function(event) {
      var event = new Event(event);
      var caroselloItem = this.getParent().getParent();
      var viewPort = caroselloItem.getParent();
      var items = viewPort.getElements('.caroselloItem');
      if (items) {
        if ((caroselloItem == items[0]) && (items.length > 3)) {
          event.stop();
          event.preventDefault();
          carouselMove(viewPort.getParent(), 2);
        } else if (((caroselloItem == items[1]) && (items.length > 3)) || ((caroselloItem == items[0]) && (items.length <= 3))) {
          event.stop();
          event.preventDefault();
          carouselMove(viewPort.getParent(), 1);
        } else if ((caroselloItem == items[2]) && (items.length <= 3)) {
          event.stop();
          event.preventDefault();
          carouselMove(viewPort.getParent(), -1);
        } else if ((caroselloItem == items[3]) && (items.length > 3)) {
          event.stop();
          event.preventDefault();
          carouselMove(viewPort.getParent(), -1);
        } else if ((caroselloItem == items[4]) && (items.length > 4)) {
          event.stop();
          event.preventDefault();
          carouselMove(viewPort.getParent(), -2);
        }
      }
    });
    el.removeEvents('focus');
    el.addEvent('focus', function(event) {
      var event = new Event(event);
      el.blur();
      event.preventDefault();
    });
  });

  $each(container.getElements('.tabCarosello'), function(el) {
    el.removeEvents('click');
    el.addEvent('click', function(event) {
      var event = new Event(event);
      var caroselli = el.getParent().getParent().getParent();
      caroselloSlide(caroselli.getElement('.contenitoreCarosello'), el,
          container);
      event.preventDefault();
    });
  });

  if (container) {

    new CarouselSlider(container, carouselDelay);

  }

}

var carouselDelay = 5000;

window.addEvent('domready', function() {

  carouselInit('contenuti');

});

