//
//  Copyright 2008, trivago GmbH
//  All rights reserved.
//
//  Redistribution and use in source and binary forms, with or without modification,
//  are permitted provided that the following conditions are met:
//
//  -> Redistributions of source code must retain the above copyright notice, this
//  list of conditions and the following disclaimer.
//  -> Redistributions in binary form must reproduce the above copyright notice, this
//  list of conditions and the following disclaimer in the documentation and/or
//  other materials provided with the distribution.
//  -> Neither the name of the trivago GmbH nor the names of its contributors may
//  be used to endorse or promote products derived from this software without
//  specific prior written permission.
//
//  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
//  ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
//  WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
//  IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
//  INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES(INCLUDING, BUT
//  NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
//  PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
//  WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT(INCLUDING NEGLIGENCE OR OTHERWISE)
//  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
//  POSSIBILITY OF SUCH DAMAGE.
//
//
//  This file represents the common functionality, respectively "the lib" of trivagos
//  JavaScript sources. It's named COM.class.js, but there's many source that is just
//  manipulating prototypes.
//  We have tried to write this as fast and save as possible. For example the logging
//  feature is often just overkill, so we decided not to take over a programmers main
//  task: thinking.
//
//  Actual this is just a beta. You can get the most actual version here:
//  http://www.trivago.com/javascript/base/COM.class.js
//
//  Note that it is NOT recommended to use for(key in array) constructs after binding
//  this source. You'll get strange results: every browser will iterate the prototype
//  methods defined here.
//
//
//  First of all we declare COM and add first basic function to extend prototypes
//  it's only temporary used until COM is redefined. We need that function only for
//  initializing some functions
//
var COM=new function()
{
this.extendPrototype=function(t, s)
{
for(var property in s)
{
try
{
if(typeof t[property] =='undefined')
{
t[property]=s[property];
}
}
catch(e)
{
return null;
}
}
}
};
//
//  you can't avoid such an object...
//
var Browser ={
IE:   window.attachEvent && !window.opera,
IE6:   /msie 6\.0/i.test(navigator.userAgent),
Opera:   !!window.opera,
WebKit:   navigator.userAgent.indexOf('AppleWebKit')!=-1,
Gecko:   navigator.userAgent.indexOf('Gecko')!=-1 && navigator.userAgent.indexOf('KHTML') ==-1,
KHTML:   navigator.userAgent.indexOf('KHTML')!=-1,
MacSafari:((navigator.userAgent.indexOf('Safari')!=-1) &&(navigator.platform? navigator.platform: navigator.userAgent).toLowerCase().indexOf('mac')!=-1),
Safari:   navigator.userAgent.indexOf('Safari')!=-1
};
//
//  First of all we declare our own object. We attach some functions to that object
//  with which we'll later attach Object.prototype. Note: it is recommended to use
//  all following extensions after adopting via COM.GET.Elem(), COM.GET.All(),
//  COM.GET.First(), COM.Element()...
//
var COMObject=function(){};
//
//  We start with some overhead: member(s) that are able to tell us the type of their
//  object. Because it's really uncomfortable to overload Elements(IEs <=v7 are not
//  able to fit with) every element is getting handled like all other objects - until
//  it has passed a COM routine.
//
COMObject.prototype.isElement=function(){ return false };
//
//  Often it is necessary to distinguish objects more accurate than typeof(); does...
//  Example: typeof(new Array) and typeof(new Object) will result in the same string:
//  "object". Note that these and all following prototype definitions will not effect
//  elements in IE before they've been adopted...
//
COMObject.prototype.isBoolean=function(){ return this.constructor ==Boolean };
COMObject.prototype.isString =function(){ return this.constructor == String };
COMObject.prototype.isNumber =function(){ return this.constructor == Number };
COMObject.prototype.isArray=function(){ return this.constructor ==  Array };
COMObject.prototype.isObject =function(){ return this.constructor == Object };
//
//  Here is the alternative for "typeof". The con is that it can't get called on some
//  undefined value. Even if "null" has object character in JavaScript you should use
//  COM.GET.Type(); which is wrapping this by using try/catch.
//
COMObject.prototype.getType=function()
{
return this.isArray()   ?   'array':
this.isElement() ? 'element':
this.isString()  ?  'string': typeof this;
};
//
//  This will return the first index of a value appearing in an array-like object. so
//  expect a number or string. in case of failure it'll return "null". an "arguments"
//  object will get handled like arrays do.
//
COMObject.prototype.getKeyOf=function(value)
{
if(this.isArray() ||(this.callee &&(this.length ||(this.length ===0))))
{
for(var i=0, n=this.length; i!=n; ++i)
{
if(this[i] ==value)
{
return i;
}
}
}
for(var key in this)
{
if(this[key] ==value)
{
return key;
}
}
return null;
}
//
//  Can get used in for/in loops to iterate just the "new" properties.
//
COMObject.prototype.hasNative=function(property)
{
return typeof this.constructor.prototype[property]!='undefined';
}
//
//  A wrapper for .getKeyOf(); that allows simple statements in boolean context with-
//  out necessity to filter undefined, null or whatever. You should note that there's
//  a bit more to do like using .getKeyOf(); and so it's not faster.
//
COMObject.prototype.hasValue=function(value)
{
return this.getKeyOf(value)!==null;
}
//
//  The easiest way to make copies of objects. Will also work with elements. The only
//  argument tells if the cloning has to be recursive. That means: childs of elements
//  will get cloned too.
//  You can define the clone depth by passing a negative value. For example: - 2 will
//  cause a result, where the 1st and the 2nd level are containing copies, but any of
//  the following will just hold references.
//  The value of this argument doesn't effect the cloning depth of elements - in this
//  case boolean context matters.
//
COMObject.prototype.clone=function(recursive)
{
if(typeof this.cloneNode =='function')
{
return this.cloneNode(recursive);
}
if(typeof this!='object')
{
return this.valueOf();
}
var clone=new this.constructor(), property;
for(property in this)
{
if(clone[property]!==this[property])
{
try
{
clone[property]=recursive && this[property] &&
this[property].clone?
this[property].clone(recursive +1):
this[property]
} catch(e){}
}
}
return clone;
}
//
//  You can pass a routine, a routine-reference or a string of Script. Every value of
//  the array(or the array-like object) will become passed as first argument for the
//  function - which will get called for each entry.
//  This function will log errors appearing during iteration, because it wouldn't be
//  very useful if the value that has caused it will also cause a full break. If you
//  want to suppress this behavior, pass a 2nd argument with a "true" value.
//
COMObject.prototype.forEach=function(routine, stop)
{
var result =[];
routine=COM.GET.Function(routine);
for(var i=0, n=this.length; i<n; ++i)
{
try
{
result.push(routine(this[i]));
}
catch(e)
{
if(stop)
{
throw e;
}
result.push(COM.Log(this+'.forEach(['+typeof routine+'])['+i+']: '+e));
}
}
return result;
}
//
//  Nearly the same like .forEach(), calls the routine as member of the entry itself,
//  but: Here is possible to define arguments. The 3rd of .withEach() will become the
//  1st of the member routine...
//
COMObject.prototype.withEach=function(routine, stop)
{
var result=[], args=[];
for(var i=2,n=arguments.length; i<n; ++i)
{
args.push(arguments[i]);
}
for(var i=0, n=this.length; i<n; ++i)
{
try
{
result.push(this[i][routine].apply(this[i], args));
}
catch(e)
{
result.push(COM.Log(this+'.withEach('+routine+')['+i+']: '+e+'('+this[i]+')'));
if(stop)
{
throw e;
}
}
}
return result;
}
//
//  Creates search-parts of URLs by iterating over hashes/ objects members. booleans,
//  numbers and strings get included. other stuff will be ignored.
//
COMObject.prototype.toQuery=function()
{
var result='';
for(var property in this)
{
({'number':1,'string':1,'boolean':1})[typeof this[property]] &&
(result +=(result? '&': '')+ encodeURI(property) +'='+(this[property].isBoolean()? new Number(this[property]): encodeURI(this[property])));
}
return result;
}
//
//  We extend Array.prototype with new functions.
//
COM.extendPrototype(Array.prototype, COMObject.prototype.clone());
//
//  We extend String.prototype with new functions.
//
COM.extendPrototype(String.prototype, COMObject.prototype.clone());
//
//  Standard string functionality. Removes all whitespace characters on the [r]ight,
//  the [l]eft or the [i]side of a string.
//
String.prototype.ltrim=function(){ return this.replace(/^\s+/,  '') }
String.prototype.itrim=function(){ return this.replace(/\s+/g, ' ') }
String.prototype.rtrim=function(){ return this.replace(/\s+$/,  '') }
//
//  Removing leading and following whitespace(s) - with or without the inside ones.
//
String.prototype.gtrim=function(){ return this.trim().itrim()  }
String.prototype.trim =function(){ return this.rtrim().ltrim() }
//
//  Standard string functionality. Extends a string to given [l]ength adding [s]tring
//  on the [l]eft or the [r]ight.
//
String.prototype.lpad=function(l, s)
{
o=this; while(o.length < l){ o=s.concat(o); }
return o.substr(o.length-l);
}
String.prototype.rpad=function(l, s)
{
o=this; while(o.length < l){ o=o.concat(s); }
return o.substr(0, l);
}
//
//  Creating a valid URL query by escaping a string, but not the ampersands and equal
//  signs in it. If there's more than one equal sign between two ampersands, just the
//  first will stay unescaped. Entities will confuse this function, /&+/g won't.
//
String.prototype.toQuery=function()
{
var split=this.replace(/&+/g, '&').split('&');
if(split.length < 2)
{
return this.valueOf();
}
for(var i=0, n=split.length; i!=n; ++i)
{
split[i]=escape((split[i]=split[i].split('=')).shift()) +'='+ escape(split[i].join('='));
}
return split.join('&');
}
//
//We extend Date.prototype with new functions.
//
COM.extendPrototype(Date.prototype, COMObject.prototype.clone());
//
//  Stolen from the nasty DHTML calendar, translated to real source and bound here...
//
Date.prototype.oldFullYear=Date.prototype.setFullYear;
Date.prototype.setFullYear=function(year)
{
var date=new Date(this);
date.oldFullYear(year);
if(date.getMonth()!=this.getMonth())
{
this.setDate(28);
}
this.oldFullYear(year);
return true;
}
//
//  Stolen from the nasty DHTML calendar, translated to real source and bound here...
//
Date.prototype.setYearMonthDay=function()
{
var date=new Date(arguments.length ==1? arguments[0] :
arguments[0] +'-'+
arguments[1] +'-'+
arguments[2] );
this.setDate(1);
this.setFullYear( date.getFullYear());
this.setMonth( date.getMonth());
this.setDate(  date.getDate());
return true;
}
//
//  Stolen from the nasty DHTML calendar, translated to real source and bound here...
//
Date.prototype.getString=function(format)
{
var month=this.getMonth(), day=this.getDate(), year=this.getFullYear(), hash ={};
format ||(format='%y-%m%-%d');
hash['%y']=year;
hash['%m']=++month <10? '0'+ month: month;
hash['%d']=   day <10? '0'+   day:   day;
for(var i=0, a=format.match(/%./g), n=a.length; i<n; ++i)
{
if(hash[a[i]])
{
format=format.replace(new RegExp(a[i], 'g'), hash[a[i]]);
}
}
return format;
}
//
//  Stolen from the nasty DHTML calendar, translated to real source and bound here...
//
Date.prototype.compareDate=function(date)
{
return(this.getFullYear() ==date.getFullYear() ) &&
(this.getMonth()==date.getMonth() ) &&
(this.getDate() ==date.getDate()  );
}
//
//  NOT stolen from the nasty DHTML calendar, but real source by nature...
//
Date.prototype.compareTime=function(date)
{
return(this.getHours() ==date.getHours()   ) &&
(this.getMinutes() ==date.getMinutes() ) &&
(this.getSeconds() ==date.getSeconds() );
}
//
//  Stolen from the nasty DHTML calendar, translated to real source and bound here...
//
Date.prototype.getMonthDayCount=function(month, year)
{
year  ||(year =this.getFullYear());
month ||(month=this.getMonth());
return((0 ==(year %4  )) &&
((0!=(year %100)) ||
(0 ==(year %400)) )) && month ==1? 29:([31,28,31,30,31,30,31,31,30,31,30,31])[month];
}
//
//  Defining COMElement to extend Element.prototype later. Note again: it is recommended
//  to use all following extensions after adopting via COM.GET.Elem(), COM.GET.All(),
//  COM.GET.First(), COM.Element()...
//
var COMElement=function(){};
COMElement.prototype=COMObject.prototype.clone();
//
//  Redefining type.
//
COMElement.prototype.isElement=function(){ return true  }
COMElement.prototype.isObject =function(){ return false }
//
//  Easy getters that are returning dimension and position values or undefined; they
//  don't really do anything. but they should get used - you'll see why...
//
COMElement.prototype.getWidth =function(){ return this.offsetWidth  }
COMElement.prototype.getHeight=function(){ return this.offsetHeight }
COMElement.prototype.getLeft=function(){ return this.offsetLeft   }
COMElement.prototype.getTop =function(){ return this.offsetTop }
//
//  Some comfort...
//
document.getCoords =
self.getCoords =
COMElement.prototype.getCoords=function()
{
return{x:this.getLeft(), y:this.getTop()};
}
//
//  More overhead - simple setter expecting a pixel-value. It's able to become a very
//  useful shortener for your source...
//
COMElement.prototype.setWidth =function(w){ return this.setStyle('width',  parseInt(w) +'px') }
COMElement.prototype.setHeight=function(h){ return this.setStyle('height', parseInt(h) +'px') }
COMElement.prototype.setLeft=function(x){ return this.setStyle('left',   parseInt(x) +'px') }
COMElement.prototype.setTop =function(y){ return this.setStyle('top', parseInt(y) +'px') }
//
//  Maybe you've heard from this, and yes, they work equivalent to members of window.
//  See COM.GET.Elem() if you're interested in what to do with the <body> tag.
//
COMElement.prototype.resizeBy=function(w, h){ return(this.setWidth( this.getWidth() +w) ===null) || this.setHeight( this.getHeight() +h) }
COMElement.prototype.resizeTo=function(w, h){ return(this.setWidth(   w) ===null) || this.setHeight(    h) }
COMElement.prototype.moveBy=function(x, y){ return(this.setLeft(  this.getLeft()  +x) ===null) || this.setTop( this.getTop() +y) }
COMElement.prototype.moveTo=function(x, y){ return(this.setLeft(    x) ===null) || this.setTop( y) }
//
//  Get the next higher bodied element that's positioned absolute, relative or fixed.
//
COMElement.prototype.getContainer=function(){ return this.offsetParent }
//
//  "Abs" abbreviates "Absolute"...
//
COMElement.prototype.getAbsLeft=function(){ var node=this, value=0; do value +=node.offsetLeft; while((node=node.offsetParent) &&(node!=document.body)); return value }
COMElement.prototype.getAbsTop =function(){ var node=this, value=0; do value +=node.offsetTop;  while((node=node.offsetParent) &&(node!=document.body)); return value }
//
//  Detect scroll positions. NOT the window scroll positions!
//
COMElement.prototype.getXPos=function(){ return this.scrollLeft }
COMElement.prototype.getYPos=function(){ return this.scrollTop  }
//
//  Trying to get "computed style". In optimal case it's possible to get also not ex-
//  plicit defined properties - But I don't care. "float"/"opacity" will get returned
//  browser-independent.
//
COMElement.prototype.getStyle=function(property)
{
switch(property)
{
case 'float': return typeof this.style.cssFloat!='undefined'? this.style.cssFloat: this.style.styleFloat;
case 'opacity':  return this.getOpacity();
}
if(typeof self.getComputedStyle =='function')
{
return self.getComputedStyle(this, null).getPropertyValue(property.replace(/([A-Z])/g, "-$1").toLowerCase());
}
return this.style[property];
}
//
//  Maybe this is just to ensure that "float" and "opacity" will get set in the right
//  way. But maybe there are properties we forget?
//
COMElement.prototype.setStyle=function(property, value)
{
switch(property)
{
case 'float': return this.style.cssFloat=this.style.styleFloat=value;
case 'opacity':  return this.setOpacity(value);
}
return this.style[property]=value;
}
//
//  Use percentage values.
//
COMElement.prototype.getOpacity=function()
{
var style=this.style;
if(typeof(style.MozOpacity) !='undefined'){ return style.MozOpacity ===''? 100: parseFloat(style.MozOpacity)   *100; }
if(typeof(style.opacity) !='undefined'){ return style.opacity===''? 100: parseFloat(style.opacity)   *100; }
if(typeof(style.KhtmlOpacity)!='undefined'){ return style.KhtmlOpacity ===''? 100: parseFloat(style.KhtmlOpacity) *100; }
if(typeof(style.filter)  !='undefined')
{
if(!(style=style.filter))
{
return 100;
}
var pos=style.indexOf('alpha');
if((pos ==-1) &&((pos=style.indexOf('Alpha')) ==-1))
{
return 100;
}
if((pos=style.indexOf("opacity='")) ==-1){
if((pos=style.indexOf('opacity="')) ==-1){
if((pos=style.indexOf('opacity=' )) ==-1){ return 100; } --pos; }}
return parseInt(style.substr(pos +9));
}
return null;
}
//
//  Returns percentage values.
//
COMElement.prototype.setOpacity=function(value)
{
var style=this.style;
value=parseInt(value);
value=value > 99? 100: value < 1? 0: value;
style.opacity=style.KhtmlOpacity=style.MozOpacity=(value / 100).toString();
style.filter ='alpha(opacity='+ value +')';
return value;
}
//
//  To toggle style.visibility.
//
COMElement.prototype.show=function(){ return this.style.visibility='visible' }
COMElement.prototype.hide=function(){ return this.style.visibility='hidden' }
COMElement.prototype.swap=function()
{
return this.style.visibility=this.getStyle('visibility') =='hidden'? 'visible': 'hidden';
}
//
//  Will overload the element with the current value of(computed-) style.display and
//  set this value to 'none'. If it's still none, the defaultDisplayType will not get
//  set - so we ensure not to overwrite.
//
COMElement.prototype.disappear=function()
{
if(this.getStyle('display')!='none')
{
this.defaultDisplayType=this.getStyle('display');
}
return this.style.display='none';
}
//
//  If the defaultDisplayType is defined, we set it back. Otherwise the value is set
//  to 'block'. Nasty, but other methods are not fast enough.
//
COMElement.prototype.appear=function()
{
return this.style.display=this.defaultDisplayType? this.defaultDisplayType: 'block';
}
//
//  Toggle(current-) style.display.
//
COMElement.prototype.toggle=function()
{
return this.getStyle('display') =='none'? this.appear(): this.disappear();
}
//
//
//
COMElement.prototype.setClass=function(name){ return this.className=name }
COMElement.prototype.getClass=function() { return this.className }
COMElement.prototype.delClass=function(name){ return this.className=this.className.replace(new RegExp('\\b'+name+'\\b', 'g'), '').gtrim() }
COMElement.prototype.hasClass=function(name){ return this.className.match(new RegExp('\\b'+name+'\\b')) }
COMElement.prototype.addClass=function(name)
{
if(this.className)
{
if(!this.hasClass(name))
{
this.className +=' '+ name;
}
}
else
{
this.className=name;
}
return true;
}
//
//  Check if an element has any tasks for a specific event. Not really nesseccary but
//  the other event-related members will use this. Note that all handlers defined via
//  attributes will get translated, unshifted to the queue and removed from dom.
//  Furthermore: it doesn't matter if you set events to body-tag, document or window,
//  they'll all become entries in the same queue.
//
//  Important: The this. keyword, referring the element, will not work any more after
//    translating javascript-strings to functions! use arguments[1] instead.
//    the event object will become the first argument.
//    Maybe it would be useful to append something like the following pregex
//    to this method, but in this case "this" referring inline-defined elems
//    won't work any more:
//
//  .replace(/([^a-zA-Z0-9_$]*)\this\./, "$1arguments[1].")
//
//    See COM.GET.Function() for further discussion.
//    Any proposals how we should decide?
//
document.hasEvent =
self.hasEvent =
COMElement.prototype.hasEvent=function(type)
{
var holder=this, handle=this;
switch(type.toLowerCase())
{
case 'domcontentloaded':
case 'onreadystatechange':
case 'readystatechange':
holder=handle=(this.attachEvent) ? document :self;
type=(this.attachEvent) ? 'readystatechange':'DOMContentLoaded';
break;
default:
if((this.tagName &&(this.tagName =='BODY')) ||(this ==self))
{
holder=COM.GET.Body();
handle=self;
}
type=type.toLowerCase().replace(/^on/, '');
}
holder=COM.GET.Elem(holder);
handle=COM.GET.Elem(handle);
handle.events ||(handle.events=COM.GET.Elem({}));
if(!handle.events[type])
{
var routine=function(event){ return handle.runEvent(type, event); };
handle.events[type]=COM.GET.Elem([]);
try
{
handle.addEventListener(type, routine, false);
}
catch(e)
{
handle['do'+ type +'Event']=routine;
if(type =='readystatechange')
{
handle['on'+ type +'Event']=function(){ if(handle.readyState ==="complete"){handle['do'+ type +'Event'](self.event);} };
}
else
{
handle['on'+ type +'Event']=function(){ handle['do'+ type +'Event'](self.event); };
}
handle.attachEvent('on'+ type, handle['on'+ type +'Event']);
}
}
if(holder&&holder.getAttribute&&holder.getAttribute('on'+ type))
{
handle.events[type].push(COM.GET.Function(holder.getAttribute('on'+ type)));
holder.setAttribute('on'+ type, null);
holder.removeAttribute('on'+ type);
}
return handle.events[type].length;
}
//
//  Here the tasks get added to the event- related queue. Specify the events type you
//  want to tune('onclick', 'mouseover'), then pass a function or function reference
// (or string, see discussion of .hasEvent() and COM.GET.Function()).
//  With the 3rd arg it's possible to avoid validation of your function - but that is
//  deprecated and not recommended.
//
document.addEvent =
self.addEvent =
COMElement.prototype.addEvent=function(type, routine, force)
{
var node;
switch(type.toLowerCase())
{
case 'domcontentloaded':
case 'onreadystatechange':
case 'readystatechange':
node=(this.attachEvent) ? document :self;
type=(this.attachEvent) ? 'readystatechange':'DOMContentLoaded';
break;
default:
node=(this.tagName &&(this.tagName =='BODY'))? self: this;
type=type.toLowerCase().replace(/^on/, '');
}
node=COM.GET.Elem(node);
node.hasEvent(type);
node.events[type].push(force? routine: COM.GET.Function(routine));
return true;
}
//
//  Removing elements from the queue. Leaving out the 2nd arg will cause that it gets
//  flushed. Note that there's no way to get them back if you don't hold references -
//  or catch the spliced ones.
//
document.delEvent =
self.delEvent =
COMElement.prototype.delEvent=function(type, routine, force)
{
var node, i;
switch(type.toLowerCase())
{
case 'domcontentloaded':
case 'onreadystatechange':
case 'readystatechange':
node=(this.attachEvent) ? document :self;
type=(this.attachEvent) ? 'readystatechange':'DOMContentLoaded';
break;
default:
node =(this.tagName &&(this.tagName =='BODY'))? self: this;
type=type.toLowerCase().replace(/^on/, '');
}
node=COM.GET.Elem(node);
if(node.hasEvent(type))
{
if(!routine)
{
node.events[type]=COM.GET.Elem([]);
return !0;
}
routine=String(force? routine: COM.GET.Function(routine));
for(i=0; node.events[type] && i<node.events[type].length; ++i)
{
if(routine ==String(node.events[type][i]))
{
return node.events[type].splice(i,1);
}
}
}
return i? null: false;
}
//
//  This is called instead of the functions you have passed. Of course it is possible
//  to simulate this call, but you should also provide the 2nd arg, the event-object.
//
document.runEvent =
self.runEvent =
COMElement.prototype.runEvent=function(type, task)
{
var node, i=false;
switch(type.toLowerCase())
{
case 'domcontentloaded':
case 'onreadystatechange':
case 'readystatechange':
node=(this.attachEvent) ? document :self;
type=(this.attachEvent) ? 'readystatechange':'DOMContentLoaded';
break;
default:
node=(this.tagName &&(this.tagName =='BODY'))? self: this;
type=type.toLowerCase().replace(/^on/, '');
}
node=COM.GET.Elem(node);
if(node.hasEvent(type))
{
var q=node.events[type].clone();
for(i=0; i<q.length; ++i)
{
try
{
q[i](task, COM.GET.EventTarget(task));
}
catch(e)
{
COM.Log(node+'.runEvent('+type+'); '+i+': '+e.message);
}
}
}
return i;
}
//
//  Some wrapped stuff...
//
document.resizeTo=self.resizeTo;
document.resizeBy=self.resizeBy;
document.moveTo=self.moveTo;
document.moveBy=self.moveBy;
document.scrollTo=self.scrollTo;
//
//  Our window and document seems to be the same...
//
self.getWidth =document.getWidth =function(){ var width =document.body.clientWidth;  return typeof width=='undefined'? self.innerWidth:  width  }
self.getHeight=document.getHeight=function(){ var height=document.body.clientHeight; return typeof height =='undefined'? self.innerHeight: height }
self.getLeft=document.getLeft=function(){ return self.screenX }
self.getTop =document.getTop =function(){ return self.screenY }
self.getXPos=document.getXPos=function(){ var x=(typeof document.documentElement =='undefined')? self.pageXOffset:document.documentElement.scrollLeft; return typeof x =='undefined'? self.scrollLeft: x }
self.getYPos=document.getYPos=function(){ var y=(typeof document.documentElement =='undefined')? self.pageYOffset:document.documentElement.scrollTop;  return typeof y =='undefined'? self.scrollTop:  y }
self.setWidth =document.setWidth =function(w){ return self.resizeBy(w, 0) }
self.setHeight=document.setHeight=function(h){ return self.resizeBy(0, h) }
self.setLeft=document.setLeft=function(x){ return self.moveTo(x, 0) }
self.setTop =document.setTop =function(y){ return self.moveTo(0, y) }
//
//  Very long prelude, but now the flesh follows. We've decided to use the old object
//  notation because JSON doesn't allows us to define private variables.
//  Furthermore, "new function()" has the touch of a static class, not a singleton...
//  At this point COM.extendPrototype is deleted and all prototypes are extended.
//
COM=new function()
{
//
//  It is not usual to define privates at the top, but if you really want to under-
//  stand what we're going to do, you should have a look at.
//
//  HEAD and BODY become references, LOAD is set after the windows load- event, LOG
//  containes dynamic information for developers, SRC the urls of sources that have
//  still been required, LYR is a stack of LAYER instances and DEBUG you will learn
//  to love...
//
var HEAD=null, BODY=null, LOAD=false, LOG=[], SRC ={}, LYR=[];
try
{
var DEBUG=self.opener &&
self.opener.COM &&
self.opener.COM.DEBUG &&
self.opener.COM.DEBUG.Receiver? self.opener.COM.DEBUG.Receiver: null;
}
catch(e)
{
var DEBUG=null;
}
//
//  Now it's time for some static getter.
//
this.GET ={
//
//  You should use this instead of document.getElementById(). It is an "adopting"
//  routine, we've discussed before. You can pass an element reference or its id.
//
Elem: function(anything)
{
var name='COM.GET.Elem('+anything+'); ';
if(!anything)
{
return COM.Log(name+'is null!');
}
var prototype, result;
try
{
if(typeof anything =='object' ||(typeof anything =='function' && anything.constructor ==Object))
{
prototype=typeof anything.nodeName =='string'? COMElement.prototype: COMObject.prototype;
result =anything;
}
else
{
prototype=COMElement.prototype;
result =document.getElementById(anything);
}
}
catch(e)
{
COM.Log('COM.GET.Elem:'+e);
}
if(result)
{
var body=document.body ==result;
//if(window.attachEvent && !Browser.opera)
//{
for(var property in prototype)
{
try
{
if(typeof result[property] =='undefined')
{
if( body &&
( property =='resizeTo' ||
property =='moveTo' ||
property =='resizeBy' ||
property =='moveBy' ) )
{
continue;
}
result[property]=prototype[property];
}
}
catch(e)
{
COM.Log(name+property+': '+e);
}
}
//}
try
{
if((result ==document.body) && !BODY)
{
for(var a=[
'moveTo', 'resizeTo', 'getWidth', 'getHeight', 'getLeft', 'getTop', 'getXPos', 'getYPos', 'getCoords',
'moveBy', 'resizeBy', 'setWidth', 'setHeight', 'setLeft', 'setTop', /*'setXPos', 'setYPos', 'setCoords', */
'scrollTo'
], i=0; i!=a.length; ++i){ result[a[i]]=self[a[i]] }
}
}
catch(e)
{
COM.Log(name+'BODY: '+e);
}
return result;
}
COM.Log(name+'!exist')
return new COMObject();
},
//
//  Equivalent to document.getElementsByTagName, "adopting". Beside the tagName -
//  which can be represented by a tag reference - you can pass a parent for focus
//  and the max. number of entries in the result that are going to get adopted.
//  The default's zero.
//
All: function(anything, parent, max)
{
parent=parent? COM.GET.Elem(parent): document;
var result=parent.getElementsByTagName(anything.nodeName? anything.nodeName: anything);
for(var i=0;(i!=max)&&(i!=result.length); ++i)
{
COM.GET.Elem(result[i]);
}
return result;
},
//
//  Like .All(), but you'll get just the first reference. Adopting.
//
First: function(anything, parent)
{
try
{
return this.All(anything, parent, 1)[0];
}
catch(e)
{
return null;
}
},
//
//  Like .First(), when you pass 'head' or 'body'.
//
Head: function(){ return HEAD? HEAD: HEAD=this.First('head'); },
Body: function(){ return BODY? BODY: BODY=this.First('body'); },
//
//  To catch the element which caused an event. Browser-independent. Note that it
//  is not nesseccary to call it inside of event- handlers that have been defined
//  by using this sources, try arguments[1].
//
EventTarget: function(task)
{
var target;
if(task)
{
if(task ==self)
{
return self;
}
if((typeof task.nodeName=='string')&&
(typeof task.className =='string'))
{
return task;
}
return typeof task.target =='undefined'? task.srcElement?
task.srcElement: event.srcElement: task.target
}
},
//
//  Translates array-like objects(e.g. "arguments") to arrays.
//  Arrays will stay arrays.
//
Array: function(object)
{
try
{
var result=[], i, n;
for(i=0, n=object.length; i<n; ++i)
{
result.push(object[i]);
}
return result;
}
catch(e)
{
return COM.Log('COM.GET.Array(['+typeof object+']); '+e);
}
},
//
//  Translates strings into functions. Functions will stay functions. If you pass
//  a string, the "this" keyword won't work anymore. That means, usually it won't
//  refer what it has referred before, but maybe the object which calls it.
//
Function: function(routine)
{
try
{
return typeof routine =='function' ? routine:
typeof routine =='string'   ? new Function('', routine): COM.Log('.Function(['+typeof routine+']): !allowed');
}
catch(e)
{
return COM.Log('COM.GET.Function(['+typeof routine+']); '+e);
}
},
//
//  Get whatever LOG contains. If you pass an arg that's true in boolean context,
//  you'll get a reference to this array. Just a subject to developers.
//
Log: function(reference)
{
return reference? LOG: LOG.clone();
}
//
//  Whenever there is anything not detecable failure-save or browser-independent enough, it
//  should become a part of this subJect. But just, if more prototype tuning isn't a better
//  solution.
//
}
//
//  Onscreens.
//
this.WIZARD=new function()
{
//
//  Open an onscreen form. This may not really define any further layouts, but you can pass
//  a COM.Element() compatible as 1st arg, as content. the second arg specifies the opacity
//  of the layer in the middleground(zero is possible). if the third's boolean, a click on
//  the layer will cause .WIZARD.Close(). Otherwise, if it's compatible to .GET.Function(),
//  a click on the layer will cause it's execution.
//  Call this function again to change the content if the WIZARD's still open.
//
this.Open=function(content, opac, click)
{
if(!Form)
{
BODY || COM.GET.Body();
Form=COM.Element(['div',{'class':'COMElement COMWizardForm'}]);
Cont=COM.Element(['div',{'class':'COMElement COMWizardCont'}]);
Layer=new COM.Layer();
Form.appendChild(Cont);
Cont.addEvent('click', COM.Void);
}
if(click)
{
Form.addEvent('click',(typeof click =='function')
||(typeof click =='string')? COM.GET.Function(click): COM.WIZARD.Close);
}
else
{
Form.delEvent('click');
}
self.delEvent('resize', COM.WIZARD.Size);
Layer.open(true, opac? opac: 50);
COM.GET.Body().insertBefore(Form, COM.GET.Body().firstChild);
if(content!=Cont.firstChild)
{
while(Cont.firstChild)
{
Cont.removeChild(Cont.firstChild);
}
Cont.appendChild(COM.Element(content));
}
return COM.WIZARD.Size();
}
//
//  Close the WIZARD. Note that the 3rd argument of .WIZARD.Open() won't take effect if you
//  call this manually.
//
this.Close=function()
{
self.delEvent('resize', COM.WIZARD.Size);
BODY.removeChild(Form);
return Layer.close();
}
//
//  Usually, there's no need to call this routine. It's in window.onresize queue as soon as
//  you've called .WIZARD.Open().
//
this.Size=function()
{
if(!(Form && Cont))
{
return false;
}
self.delEvent('resize', COM.WIZARD.Size);
var width =self.getWidth(),
height=self.getHeight(),
xpos=self.getXPos();
Form.style.width =width  +'px';
Form.style.height=height +'px';
Cont.style.width=Cont.firstChild.getWidth() +'px';
var   y=Cont.firstChild.getHeight(), d;
Cont.style.paddingTop=Cont.style.paddingBottom=((d=height+xpos-y)>10? d/3: height/5) +'px';
Layer && Layer.size();
return self.addEvent('resize', COM.WIZARD.Size);
}
//
//  Usually, there's no need to call this routine, but sometimes you probably need hided elements.
//
this.Hidden=function()
{
if(Layer)
{
return Layer.hidden();
}
return [];
}
//
//  Privates holding references.
//
var Form,
Layer,
Cont;
//
//  Especially if there are many elements in the root of the DOM, there's many stuff to do.
//  So it's recommended to use a <div> or whatelse around the documents main nodes...
//
}
//
//  Used quite often to separate the pages content from the current focus. Not be visible all
//  the times: can also get used just to simluate "onmouseout" for more complex structs, like
//  dropdowns, or clickouts etc...
//
this.Layer=function()
{
//
//  To show the element, you have to define if you still want the user to be able to scroll
//  the main page. Optional you can specify an opacity value and z-index. default is 50,90.
//  Last but not least: here you can also define special event handling, just like .WIZARD.
//
this.open=function(scroll, opacity, zIndex, event)
{
if(proc)
{
return COM.Log('.Layer['+ numb +'].open: double process: '+proc);
}
proc='open';
if(!opac)
{
opac=COM.Element(['div',{'class':'COMElement COMLayerOpac'}]);
temp=COM.Element(['div',{'class':'COMElement COMLayerTemp'}]);
}
else
{
var mySelf=this;
self.delEvent('resize', function(){mySelf.size()}, 1);
}
if(!Browser.IE6)
{
temp.style.position='relative';
temp.style.left='0px';
temp.style.top ='0px';
}
opac.setStyle('opacity', opacity?  opacity:  0);
opac.setStyle('zIndex',   zIndex?   zIndex: 90);
if(!(temp.parentNode && temp.parentNode.nodeType ==1))
{
BODY || COM.GET.Body();
BODY.insertBefore(temp, BODY.firstChild);
while(elem=BODY.firstChild.nextSibling)
{
BODY.removeChild(elem);
temp.appendChild(elem);
}
BODY.appendChild(opac);
if(!hide.length)
{
for(var i=0; i!=3; ++i)
{
hide.push(COM.GET.Array(COM.GET.All((['embed', 'object', 'select'])[i], temp, -1)));
for(var j=0, n=hide[i].length; j<n; ++j)
{
try
{
hide[i][j]=[hide[i][j], hide[i][j].getStyle('visibility')];
hide[i][j][0].setStyle('visibility', 'hidden');
}
catch(e)
{}
}
}
}
}
scfx=scroll;
if(event)
{
exfx=(typeof event =='function') ||(typeof event =='string')? COM.GET.Function(event): null;
opac.addEvent('click', exfx? exfx: this.close, 1);
}
numb=LYR.push(this);
return(proc=false) || this.size();
}
//
//  Hide the Layer. Note that the 4th argument of .Layer.Open() will not take effect if you
//  call this routine manually.
//
this.close=function()
{
if(proc)
{
return COM.Log('.Layer['+ numb +'].close: double process: '+proc);
}
proc='close';
if(scfx)
{
var x=temp.getXPos(),
y=temp.getYPos();
}
while(LYR.length > numb)
{
COM.Log('LYR.pop!!');
LYR.pop().close();
}
LYR.pop();
var mySelf=this;
self.delEvent('resize', function(){mySelf.size()}, 1);
for(var i=0, n=hide.length; i!=n; ++i)
{
for(var j=0, m=hide[i].length; j!=m; ++j)
{
try
{
hide[i][j][0].setStyle('visibility', hide[i][j][1]);
}
catch(e)
{}
}
}
hide=[];
while(elem=temp.firstChild)
{
try
{
temp.removeChild(elem);
BODY.appendChild(elem);
}
catch(e)
{
COM.Log('COM.Layer.close(); '+e);
break;
}
}
try
{
BODY.removeChild(opac);
BODY.removeChild(temp);
}
catch(e)
{
;
}
temp.style.width =opac.style.width ='';
temp.style.height=opac.style.height='';
temp.style.overflow='';
opac.delEvent('click', this.close, 1);
if(scfx)
{
self.scrollTo(x,y);
}
return !(proc=false);
}
//
//  Just like WIZARD: will get done automatically.
//
this.size=function()
{
if(proc)
{
return COM.Log('.Layer['+ numb +'].size: double process'+proc);
}
proc='size';
var mySelf=this;
self.delEvent('resize', function(){mySelf.size()}, 1);
var w=BODY.getWidth(),
h=BODY.getHeight();
if(scfx)
{
var x=BODY.getXPos(),
y=BODY.getYPos();
temp.style.width =opac.style.width =w.toString() +'px';
temp.style.height=opac.style.height=h.toString() +'px';
if(temp.style.overflow!='hidden')
{
temp.style.position='absolute';
temp.style.overflow='hidden';
temp.style.left='0px';
opac.style.top ='0px';
temp.scrollLeft=x;
temp.scrollTop =y;
}
}
else
{
temp.style.position =
opac.style.position='absolute';
opac.style.left=temp.style.left='0px';
opac.style.top =temp.style.top ='0px';
(temp.getWidth()  >=w) || temp.setStyle('width',  w=w? w +'px': '100%'); opac.setStyle('width',  temp.getWidth()  +'px');
(temp.getHeight() >=h) || temp.setStyle('height', h=h? h +'px': '100%'); opac.setStyle('height', temp.getHeight() +'px');
}
self.addEvent('resize', function(){mySelf.size()}, 1);
return !(proc=false);
}
//
//  Just like WIZARD: return hidden elements.
//
this.hidden=function()
{
return hide;
}
//
//  Internals.
//
var opac=null,
temp=null,
numb=0,
proc=false,
elem=null,
hide=[],
exfx=null,
scfx=null;
//
//  IE isn't nice, but maybe there's a more simple way to do all this resizing stuff...
//  Actually we decided to make it instantiateable, but: stack handling for more than 2
//  simultaneous open ones is very bad.
//
}
//
//  Constructor for elements. The most useful part here. Most behavior - what to do
//  which kinds and count of arguments to pass etc... is not defined yet - so it is
//  still able to become more usefull...
//
this.Element=function(a)
{
try
{
if((a.nodeName && a.cloneNode) || a.isString())
{
return COM.GET.Elem(a);
}
var name='.Element('+typeof a+'); ';
if(!a.isArray())
{
return COM.Log(name+'arg?')
}
var l, e, k, h, s, p, x, y, z, v=[], c=document;
if(a.length ==1)
{
return c.createTextNode(a[0]);
}
else
{
e=c.createElement(a[0]);
if(document.body)
{
document.body.appendChild(e);  COM.GET.Elem(e);
document.body.removeChild(e);
}
else
{
COM.GET.Elem(e);
}
if(a[1])
{
if(typeof(a[1]) =='object')
{
for(h in a[1])
{
if(typeof h!='string')
{
continue;
}
switch(h.toLowerCase())
{
case 'disabled':
e.disabled=a[1][h] &&(a[1][h]!='false');
break;
case 'class':
e.setClass(a[1][h]);
break;
case 'id':
e.setAttribute('id', e.id=a[1][h].toString());
break;
case 'style':
s=a[1][h].split(';');
for(var i=0, m=s.length; i!=m; ++i)
{
try
{
s[i] &&(p=s[i].split(':'));
if(p[0].match('-'))
{
x=p[0].split('-');
z=x[0];
for(y=1, n=x.length; y!=n; ++y)
{
z +=x[y].substr(0,1).toUpperCase()+x[y].substring(1);
}
p[0]=z;
}
e.setStyle(p[0], p[1]);
}
catch(e)
{
COM.Log(name+'invalid style: '+e);
}
}
break;
default:
if(h.match(/^on/))
{
a[1][h] && v.push([h.replace(/^on/, ''), a[1][h]]);
}
else
{
try
{
e.setAttribute(h, a[1][h]);
}
catch(e)
{
COM.Log(name+'.setAttribute: '+e)
}
}
}
}
}
else
{
COM.Log(name+'invalid attibutes/argument2 @'+i);
}
}
if(a[2])
{
if(a[2].isString())
{
e.appendChild(c.createTextNode(a[2]));
}
else
{
if(a[2].isArray())
{
for(var i=0, n=a[2].length; i!=n; ++i)
{
try
{
e.appendChild(COM.Element(a[2][i]));
}
catch(err)
{
COM.Log(name+'.appendChild: '+err);
}
}
}
else
{
COM.Log(name+'invalid childs/argument3');
}
}
}
for(var i=0, n=v.length; i!=n; ++i)
{
e.addEvent(v[i][0], v[i][1]);
}
}
return COM.GET.Elem(e);;
}
catch(err)
{
return COM.Log(name, err);
}
}
//
//  Binds JavaScript sources you need NOW.
//
this.Require=function(jsUrl)
{
var name='COM.Require('+jsUrl+'); ';
if(SRC[jsUrl])
{
return COM.Log(name+SRC[jsUrl]);
}
COM.Execute((new COM.HTTP.Request('GET', jsUrl,{}, true)).start());
return COM.Log(name+(SRC[jsUrl]='bound by require'));
}
//
//  Bind JavaScript which has a callback, use it as preloader or simply bind css...
//
this.Include=function(url, isCss)
{
var name='COM.Include('+url+'); ';
if(SRC[url])
{
return COM.Log(name+SRC[url]);
}
HEAD || COM.GET.Head();
// temporary Bugfix for IE: Problems with COM.Element at this point
// HEAD.appendChild(COM.Element(isCss? ['link',  {'rel':'stylesheet','type':'text/css','href':url}]:
//    ['script',{'type':'text/javascript','src':url}]));
if(isCss)
{
var node=document.createElement('link');
node.href=url;
node.rel='stylesheet';
node.type='text/css';
}
else
{
var node=document.createElement('script');
node.src=url;
node.type='text/javascript';
}
HEAD.appendChild(node);
return COM.Log(name+(SRC[url]='bound by include'));
}
//
//
//
this.Execute=function(source)
{
try
{
self.execScript(source);
}
catch(e)
{
try
{
var node=COM.Element(['script',{'type':'text/javascript'}]);
node.appendChild(document.createTextNode(source));
COM.GET.Head().appendChild(node);
}
catch(e)
{
if(typeof node.text!='string')
{
return COM.Log('COM.Require([source]); '+e);
}
node.text=source;
}
}
return true;
}
//
//  Write yourself a note.
//
this.Log=function()
{
arguments[0].message ||(arguments[0]=arguments[0].toString().replace(/^\./, 'COM.'));
var text=[];
for(var i=0, n=arguments.length; i!=n; ++i)
{
text.push(arguments[i] && arguments[i].message? arguments[i].message: arguments[i]);
}
LOG.push(text.join(': '));
DEBUG && DEBUG(text);
return 0;
}
//
//
//
}
//
//
//
COM.HTTP=new function()
{
//
//
//
this.Post=function(url, params, synchron){ var request=new COM.HTTP.Request('POST', url, params); return self.setTimeout(' COM.HTTP.Execute('+ request.getId() +'); ', 1); }
this.Get =function(url, params, synchron){ var request=new COM.HTTP.Request('GET',  url, params); return self.setTimeout(' COM.HTTP.Execute('+ request.getId() +'); ', 1); }
//
//
//
this.Request=function(method, url, params, synchron)
{
//
//
//
params &&(params=COM.GET.Elem(params));
if(!(params && params.isObject()))
{
COM.Log('params will nicht', typeof params);
params=COM.GET.Elem({});
}
for(var i=0, n=EVENTS.length; i!=n; ++i)
{
if(params['on'+ EVENTS[i]])
{
params['on'+ EVENTS[i]]=COM.GET.Function(params['on'+ EVENTS[i]]);
}
}
method=(typeof method =='string') &&(method.toUpperCase() =='POST')? 'POST': 'GET';
//
//
//
this.start=function()
{
xmlreqfx ||(xmlreqfx=new COM.HTTP.Object());
xmlreqfx.open(method, url, !synchron);
if(method =='POST')
{
xmlreqfx.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
if(params.vars)
{
xmlreqfx.setRequestHeader('Content-length', params.vars.length);
}
else
{
params.vars=null;
xmlreqfx.setRequestHeader('Content-length', '0');
xmlreqfx.setRequestHeader('Connection', 'close');
}
}
if(!synchron)
{
var my=this;
xmlreqfx.onreadystatechange=function(){ my.handle(); }
xmlreqfx.send(params.vars)
return true;
}
else
{
xmlreqfx.send(params.vars);
return this.setResponse();
}
}
//
//
//
this.handle=function()
{
if(xmlreqfx.readyState ==4)
{
this.setResponse();
}
else
{
if(params['on'+ EVENTS[xmlreqfx.readyState]])
{
params['on'+ EVENTS[xmlreqfx.readyState]]();
}
}
}
//
//
//
this.setResponse=function()
{
response=params.xml? xmlreqfx.responseXML? xmlreqfx.responseXML.documentElement: null: xmlreqfx.responseText;
if(params.onfinish)
{
params.onfinish(response);
}
return response;
}
//
//
//
this.abort=function()
{
for(var i=0, n=EVENTS.length; i!=n; ++i)
{
(params['on'+ EVENTS[i]]) &&(params['on'+ EVENTS[i]]=null);
}
return xmlreqfx &&(xmlreqfx.abort() || 1);
}
//
//
//
this.getId=function(){ return identity; }
//
//
//
var identity=INDENT.push(this) -1,
response=null,
xmlreqfx=null;
//
//
//
}
//
//
//
var EVENTS=COM.GET.Elem(['begin','load','loading','interactive','finish','abort']),
INDENT=COM.GET.Elem([]);
//
//
//
this.Execute=function(index){ INDENT[index].start(); }
//
//
//
this.Object=function()
{
try
{
return new XMLHttpRequest();
}
catch(e)
{
try{ return new ActiveXObject("Msxml2.XMLHTTP"); } catch(e){
try{ return new ActiveXObject("Microsoft.XMLHTTP"); } catch(e){ return COM.Log('.HTTP.Object', e); }}
}
return true
}
//
//
//
}
//
//
//
COM.HASH=new function()
{
//
//
//
this.Init=function()
{
if(!Browser.Safari && Browser.IE && !H)
{
H=COM.Element(['iframe',{'style':'display:none;','id':'COMHASHFrame'}]);
COM.GET.Body().appendChild(H);
var W=H.contentWindow.document;
W.open();
W.location.hash='#'+document.location.hash.replace(/#/, '');
W.close();
}
return true;
}
//
//
//
this.Get=function(store)
{
if(!Browser.Safari)
{
H || this.Init();
var hash=H ? H.contentWindow.document.location.hash.replace(/#/, ''):document.location.hash.replace(/#/, '');
C=(store || typeof store =='undefined') ? hash:C;
return hash;
}
return null;
}
//
//
//
this.Set=function(hash)
{
if(!Browser.Safari)
{
H || this.Init();
hash &&(hash='#'+hash);
C=document.location.hash=hash;
if(H)
{
var W=H.contentWindow.document;
W.open();
W.close();
W.location.hash=hash;
}
}
return true;
}
//
//
//
this.Reg=function(f, r)
{
if(!Browser.Safari)
{
f &&(F=COM.GET.Function(f));
R=r;
I ||(F &&(I=window.setInterval('COM.HASH.Check()', 200)));
}
return true;
}
//
//
//
this.Check=function()
{
var hash;
if(!Browser.Safari && I && C!=(hash=this.Get(false)))
{
if(hash!='')
{
C=document.location.hash='#'+hash;
(typeof F =='function') && F();
return true;
}
else if(R)
{
window.clearInterval(I);
document.location.href=document.location.href.substr(0, document.location.href.indexOf('#'));
if(!Browser.IE)
{
document.location.reload();
}
}
}
return false;
}
//
// private vars
//
var I=null,
C='',
F=null,
R=true,
H=null;
}
//
//
//
COM.Menu=function(left, top, width, parent)
{
var x=left?(typeof  left =='function'?  left: function(){ return  left }): function(){ if(m=$(ul.parentNode.parentNode)) return ul.setLeft(m.getWidth() - 15); },
y= top?(typeof   top =='function'?   top: function(){ return   top }): function(){ if(m=$(ul.parentNode)) return ul.setTop(m.getTop()+5); },
w=width?(typeof width =='function'? width: function(){ return width }): null,
m, ul=COM.Element(['ul',{'class':'COMElement COMMenuContainer'}]), j='var t=';  parent ||(parent=ul);
//
//
//
function Entry(text, exec)
{
exec=exec.match(/^javascript\:/)?{'href':'javascript:COM.Void();', 'onclick':(m=exec.substring(11))}:
{'href':exec,   'onclick':function(){ ul.disappear(1); }};
return COM.Element(['li',{'style':'display:inline;', 'class':('COMElement COMMenu'+(m&&m.match(/^[\/]/)? 'ListEnd': 'SubList'))}, [['a', exec, text]]]);
}
//
//
//
ul.parade=function(start)
{
for(var i=0, n=ul.childNodes.length; i<n; ++i)
{
ul.childNodes[i].firstChild.setClass('COMElement COMMenu'+(i%2? 'Even': 'UnEven'));
}
return true;
}
//
//
//
ul.getCount=function()
{
return ul.childNodes.length;
}
//
//
//
ul.insert=function(text, exec)
{
var li=new Entry(text, exec);
ul.firstChild? ul.insertBefore(li, ul.firstChild):
ul.appendChild(li);
return ul.parade();
}
//
//
//
ul.append=function(text, exec)
{
var i=ul.childNodes.length, li=new Entry(text, exec);
li.firstChild.setClass('COMElement COMMenu'+(i%2? 'Even': 'UnEven'));
return ul.appendChild(li);
}
//
//
//
ul.remove=function(index)
{
return ul.removeChild(ul.childNodes[index]);
}
//
//
//
ul.replace=function(index, text, exec)
{
m=new Entry(text, exec);
m.firstChild.setClass(ul.childNodes[index].getClass());
m=ul.replaceChild(m, ul.childNodes[index]);
return m;
}
//
//
//
ul.resize=function()
{
ul.moveTo(x(), y());
w && ul.setWidth(w());
return true;
}
//
//
//
ul.disappear=function(recursive)
{
if($(ul.parentNode.firstChild).nodeName =='A')
{
ul.parentNode.firstChild.delClass('COMMenuActive');
}
ul.setStyle('display', 'none');
if(recursive && ul.parentNode.parentNode && $(ul.parentNode.parentNode).hasClass('COMMenuContainer'))
{
return ul.parentNode.parentNode.disappear();
}
return true;
}
//
//
//
ul.appear=function(layer, handle)
{
ul.parentNode && ul.parentNode.parentNode &&
ul.parentNode.parentNode.appear &&
ul.parentNode.parentNode.appear();
for(var i=0, n=ul.childNodes.length; i!=n; ++i)
{
(ul.childNodes[i].childNodes.length > 1) &&
ul.childNodes[i].childNodes[1].disappear();
}
if($(ul.previousSibling) && ul.previousSibling.nodeName =='A')
{
ul.previousSibling.addClass('COMMenuActive');
}
if(layer)
{
ul.layer ||(ul.layer=new COM.Layer());
ul.layer.open(false, 0, layer > 50? layer: 85, handle? handle: function(){ ul.disappear(); ul.layer.close(); });
try{ COM.GET.Body().removeChild(ul); } catch(e){}
COM.GET.Body().appendChild(ul);
}
ul.setStyle('display', 'block');
return ul.resize();
}
//
//
//
ul.alternate=function(link, menu)
{
$(link.parentNode).appendChild(menu);
link.delEvent('onclick');
link.addEvent('onclick', function(e,n){ n.nextSibling.toggle(); });
return true;
}
//
//
//
return ul;
//
//
//
}
//
//  We end like we've opened: with neccessary overhead.
//
COM.Void=function(){}
//
//  License: The BSD License
//  Authors: Mathias J. Hennig, Christina Rankers, Sascha T. Claus
//  Contact: mathias.hennig@trivago.com
//
//
//  Reporting that we're taking over control now,
//  then defining a shortener for COM.
//
COM.Log('--- app ---');
function $()
{
if(arguments.length ==1)
{
return COM.GET.Elem(arguments[0]);
}
for(var i=0, n=arguments.length; i<n; ++i)
{
arguments[i]=COM.GET.Elem(arguments[i]);
}
return COM.GET.Elem(arguments);
}
//
//  Our applications main class.
//  Requires COM and is also a wrapper for it - providing individual features/addons.
//  Contains also special effects and some global handling where a single class isn't
//  really neccessary.
//
var APP=new function()
{
//
//
//
this.InnerExec=function(data)
{
var elems=COM.GET.All('script', APP.Element('<br />'+ data));
for(var i=0, n=elems.length;i<n;i++)
{
try
{
eval(elems[i].innerHTML);
}
catch(e)
{
COM.Log('APP.InnerExec failed while executing \''+elems[i].innerHTML+'\' '+e.message);
}
}
return true;
}
//
//  Nearly equiv to the former jsapi.
//  Can dynamical reload and handle sources.
//
this.RUN=new function()
{
//
//  Initializing, no need to call manually.
//
this.Start=function(f)
{
if(!P)
{
P=true;
window.delEvent('DOMContentLoaded', function(){APP.RUN.Start();});
//window.delEvent('load', function(){APP.RUN.Start();});
this.Call("_self");
}
}
//
//  Main access: define an [o]bject/package that's required(e.g. 'trivago.form') and - maybe -
//  some other [p]arameters as hash, e.g 'onfinish':function(){eval(anything);}.
//
this.Exe=function(o, p)
{
p ||(p ={});
if(!P)
{
return this.Reg("_self", function(){APP.RUN.Exe(o, p);});
}
p["onfinish"] && this.Reg(o, p["onfinish"]);
if(!((F[o] ===true) || J[o]))
{
if(o.substr(0, o.indexOf(".")) =='google')
{
if(o =="google.jsapi")
{
COM.Include("http://www.google.com/jsapi?key="+p["key"]+"&callback=google_api");
}
else
{
if(F["google.jsapi"] && typeof google!="undefined")
{
try
{
google.load(o.substr(o.indexOf(".")+1, o.length), p["version"],{"language":"pt", "callback":function(){APP.RUN.Call(o)}});
}
catch(e)
{
COM.Log('google: '+e);
}
}
else
{
this.Exe("google.jsapi",{"key":p["key"], "onfinish":function(){APP.RUN.Exe(o, p);}});
}
}
}
else
{
COM.Include("http://jsp.trivago.com/br/js_"+o.substr(o.indexOf(".")+1, o.length)+"_v4_07_4h_bt0.js");
}
return true;
}
return this.Call(o);
}
//
//  To register [f]unctions that are getting called as soon as an [o]bject's loaded successfully.
//  They're getting called in the same order as they have been registered. "onfinish" of .Exe is
//  simply the same...
//
this.Reg=function(o, f)
{
COM.Log(f);
(typeof F[o] =='object') ||(F[o]=[]);
f && F[o].push(COM.GET.Function(f));
return true;
}
//
//  Callback for included sources and packages.
//  Should not get called manually.
//
this.Call=function(o)
{
J[o]=1;
if(F[o] &&(F[o]!==true))
{
for(var i=0, n=F[o].length; i!=n; ++i)
{
try
{
F[o][i]();
}
catch(e)
{
COM.Log('APP.RUN.Call['+i+']: '+e);
}
}
}
return F[o]=true;
}
//
//  private vars
//
//  J: already done js
//  F: stored functions to execute
//  P: page loaded
//
var J ={}, P=false,
F ={};
//
// set Events
//
window.addEvent('DOMContentLoaded', function(){APP.RUN.Start();});
window.addEvent('load', function(){APP.RUN.Start();});
//
//  TODO:
//  -> the package handling itself could get optimized & faster
//  -> it isn't nesseccary to declare self.servername -
//  the script should be able to detect the uri by it's own tag.
//
}
//
//  New version of our old speech bubble, much more comfortable: [url] of the data,
//  reference to the [parent] element - done. the 2nd argument, [out], specifies if
//  the bubble will stay after onmouseout event and get
//
this.Bubble=function(parent, out, url)
{
if($(parent).bubble)
{
if(!(parent.bubble.active || parent.bubble.timeOut))
{
parent.bubble.active=true;
window.setTimeout(function(){parent.bubble.appear()}, 300);
}
return false;
}
var div=COM.Element(['div',{'class':'SpeechBubble','style':'opacity:95;display:none'}, [['div',{'class':'module'}]]]);
COM.GET.Body().appendChild(div);
parent.bubble ={
'element': div,
'request': new COM.HTTP.Request('GET', url,{ 'onfinish': function(){ parent.bubble.appear(arguments[0]); APP.InnerExec(arguments[0]); }} ),
'appear': function(content)
{
if(parent.bubble.timeOut)
{
return false;
}
if(content)
{
parent.bubble.request &&(parent.bubble.request=null);
div.lastChild.previousSibling.innerHTML=content;
}
if(parent.bubble.request)
{
if(parent.bubble.active)
{
parent.bubble.timeOut=window.setTimeout(function()
{
window.clearTimeout(parent.bubble.timeOut);
parent.bubble.timeOut=null;
parent.bubble.appear();
}, 300);
}
return false;
}
if(Browser.IE6)
{
div.resizeTo(300, 1);
}
if(parent.bubble.active)
{
var absolute_top;
if((absolute_top=parent.getAbsTop() -div.getHeight() -10) < 0)
{
absolute_top=0;
}
div.appear();
div.moveTo(parent.getAbsLeft() -div.getWidth()  -10,
absolute_top);
}
return true;
},
'disappear': function()
{
window.clearTimeout(parent.bubble.timeOut);
div.disappear();
parent.bubble.active=null;
if(out ==2)
{
parent.bubble.explode();
OpenBubble=null;
}
return true;
},
'explode': function()
{
parent.bubble.timeOut && window.clearTimeout(parent.bubble.timeOut);
parent.bubble.request && parent.bubble.request.abort();
COM.GET.Body().removeChild(parent.bubble.element);
return !(parent.bubble=null);
},
'active': true
}
if(!out)
{
parent.addEvent('mouseout', parent.bubble.disappear);
}
if(out ==2)
{
OpenBubble && OpenBubble.explode();
}
OpenBubble=parent.bubble;
parent.bubble.request.start();
div.insertBefore(COM.Element(['div',{'class':'clear_both'}, ' ']), div.firstChild);
div.insertBefore(COM.Element(['img',{'class':'closelink','onclick': parent.bubble.disappear,
'src': 'http://ip1.trivago.com/images/layoutimages/dialog/x.gif',
'alt': 'Fechar'
}]), div.firstChild);
div.appendChild(COM.Element(['div',{'class':'clear_both'}, ' ']));
return parent.bubble;
}
//
//  Holding last bubbles opened with "2" as 2nd param...
//
var OpenBubble=null;
//
//
//
this.Unbubble=function()
{
return(OpenBubble && OpenBubble.disappear());
}
//
//  Equiv to COM.WIZARD, that's able to load each step and to prepare the trivago layout.
//  Merged with the old ScreenModule.
//
this.WIZ=new function()
{
//
//  Initialize an onscreen form.
//  If there's still an open one, it's [c]ontent will get replaced.
//  NO [c]ontent means: let the big, nasty load-animation appear...
//
this.Open=function(c)
{
try
{
COM.WIZARD.Open(c? c: COM.Element(['div',{'style':'text-align:center;margin:50px;'},[['img',{'src':'http://ip2.trivago.com/images/layoutimages/indicator_big.gif'}]]]));
return 1&&(E={});
}
catch(e)
{
return COM.Log('APP.WIZ.Open(); '+e);
}
}
//
//  WTH could this routine do?
//
this.Close=function()
{
try
{
this.Call('onclose');
COM.WIZARD.Close();
}
catch(e)
{
return COM.Log('APP.WIZ.Close(); '+e);
}
}
//
//  Like Reg in .RUN - register [e]vent-handler,
//  which means [f]unction routines or js-strings...
//
this.Reg=function(e, f)
{
try
{
E[e] ||(E[e]=[]);
return E[e].push(COM.GET.Function(f));
}
catch(e)
{
return COM.Log('APP.WIZ.Reg(); '+e);
}
}
//
//  Run the queue for an [e]vent build with .Reg
//
this.Call=function(e)
{
if(typeof E[e]!='undefined')
{
for(var i=0;i!=E[e].length;++i)
{
try
{
if(typeof E[e][i] =="function")
{
E[e][i]();
}
}
catch(e)
{
return COM.Log('APP.WIZ.Call(); '+e);
}
}
}
}
//
//  Std onscreen module layout - [s]tring or html-node content(.Element),
//  optional [l]ayout-type which has to be defined extern in css.
//
this.Module=function(s,l)
{
try
{
if(typeof s =='string')
{
var n1=COM.Element(['div',{'class':'message'}]);
n1.innerHTML=s;
}
else if(s.isElement())
{
n1=s;
}
else
{
n1=COM.Element(s);
}
var n2=COM.Element(['div',{'class':'mod_body'}, [['img',{'src':'http://ip1.trivago.com/images/layoutimages/dialog/x.gif','class':'closelink','alt':'Fechar','title':'Fechar','onclick':'APP.WIZ.Close();'}]]]);
n2.appendChild(n1);
var n1=COM.Element(['div',{'class':'module '+l}]);
n1.appendChild(n2);
return this.Open(n1);
}
catch(e)
{
return COM.Log('APP.WIZ.Module(); '+e);
}
}
//
//  Let the user make a coice.
//  [p]arams: headline, message
//
this.Question=function(p)
{
try
{
if(p.url)   code="url_replace('"+self.servername+p.url+"');";
else if(p.submit_code) code="$('"+p.submit_code+"').submit();";
else return false;
this.Module(COM.Element([
'div',
{'class':'message'},
[
['h2',{},p.headline],
['p',{},p.msg],
['div',{'class':'buttons'},[
['a',{'class':'button button_blue','href':'javascript:'+code},
[['span',{'class':'content'},"ok"]]
],
['a',{'class':'button button_blue','href':'javascript:APP.WIZ.Close();'},
[['span',{'class':'content'},"Cancelar"]]
]
]]
]
]), 'question');
}
catch(e)
{
return COM.Log('APP.WIZ.Question(); '+e);
}
}
//
//  Let the user take notice of sth.
//  [p]arams: headline, msg
//
this.Alert=function(p)
{
try
{
return this.Module(COM.Element([
'div',
{'class':'message'},
[
['h2',{},p.headline],
['p',{},p.msg],
['div',{'class':'buttons'},[['a',{'class':'button button_blue','href':'javascript:APP.WIZ.Close();'},"Fechar"]]]
]
]), 'alert');
}
catch(e)
{
return COM.Log(4, 'APP.WIZ.Alert(); '+e);
}
}
//
//  private vars
//
//  E: Events
//
var E ={};
//
//  TODO:
//  -> simplify this whole handling - maybe export to an explicit class,
//  but have a look on unifying the layout modules - and the native html.
//  -> decide using [p]aram hashes or simple arguments.
//
}
//
//  Post-loading css. Simple.
//
this.Css=function(s)
{
if(!C[s])
{
if(C[s] ===null)
{
return null;
}
//COM.GET.Head().appendChild(COM.Element(['link',{'rel':'stylesheet','type':'text/css','media':'screen, print','id':'css.trivago.'+s,'href':self.servername+"/css_"+s+"_v4_07_4h_bt0_b0.css"}]));
//IE doen't like our prototype extensions, so I create element without COM.Element
var node=document.createElement('link');
node.rel='stylesheet';
node.type='text/css';
node.media='screen, print';
node.id='css.trivago.'+s;
node.href='http://jsp.trivago.com/br/css_'+s+'_v4_07_4h_bt0_b0.css';
COM.GET.Head().appendChild(node);
}
return C[s]=1;
}
//
//  Simple registry functionallity.
//  Inspired by some freaks from Redmond.
//
this.Register=function(k,v){ return R[k]=v; }
this.Retrieve=function(k)  { return R[k];  }
//
//  private vars
//
//  C: already done css
//  R: apps register
//
var C ={},
R ={};
//
//
//
}
//
//  COM officially doesn't support innerHTML, so we're going to wrap...
//
APP.Element=function(anything)
{
var name; try
{
name='APP.Element(['+ typeof anything +']); ';
if((typeof anything =='string') && !document.getElementById(anything))
{
var div=COM.Element(['div',{'class':'APPElement'}]);
div.innerHTML=anything;
return COM.Element(div);
}
return COM.Element(anything);
}
catch(e)
{
return COM.Log(name+e);
}
}
//
//
//
//
//
//
APP.BCrumb=new function()
{
//
//  initializing breadcrumb menu
//
this.Init=function(path)
{
if(Menu[0])
{
return COM.Log('APP.BCrumb.Init: still done');
}
var d=$('destinationstring');
COM.GET.Body().appendChild(Menu[0]=new COM.Menu(
function(){ return d.getAbsLeft(); },
function(){ return d.getAbsTop()+d.getHeight()+5; },
function(){ return d.getWidth(); }
));
if(path[0] && path[1])
{
Menu[1].push([path[1], 'javascript:APP.BCrumb.Load(arguments[0], "hotels&path='+path[0]+'")&&0;']);
APP.Retrieve('CURRENTPATH') || APP.Register('CURRENTPATH', path[0]);
}
for(index=0; index!=Menu[1].length; ++index)
{
Menu[0].append(Menu[1][index][0], Menu[1][index][1]);
}
return index;
}
//
//
//
this.Show=function()
{
if(Menu[0])
{
// for SSGs only
APP.PRICE.DEALS.SSG.DisplayHideLists('');
APP.PRICE.DEALS.SSG.ClearResultList();
//
Menu[0].appear(98, APP.BCrumb.Hide);
$('destinationstring').focus();
return true;
}
return false;
}
//
//
//
this.Hide=function()
{
if(Menu[0])
{
request && request.abort();
Menu[0].layer && Menu[0].layer.close();
//$('destinationstring').focus();
Menu[0].disappear();
return true;
}
return false;
}
//
//
//
this.Load=function(t,f)
{
try
{
$("breadcrumb").setClass("animation");
var cont=eval('('+(new COM.HTTP.Request('GET', '/trivago_rpc.php?v=v4_07_4h_bt0&path='+APP.Retrieve('CURRENTPATH')+'&action=breadcrumb&extend='+f,{}, 1)).start() +')'),
menu=new COM.Menu(0,0,0, Menu[0]);
for(var i=0; i!=cont.length; ++i)
{
menu.append(cont[i][0], cont[i][1]);
}
Menu[0].alternate(COM.GET.EventTarget(t), menu);
menu.appear();
$('breadcrumb').setClass('arrow');
return true;
}
catch(e)
{
return COM.Log('APP.BCrumb.Load: '+e);
}
}
//
//
//
this.Func=function(n,i)
{
APP.PRICE.DEALS.Clear(0);
$('destinationstring').value=n;
$('destination_path' ).value=i;
return this.Hide();
}
//
//
//
var Menu=[0, [
["50 cidades top", 'javascript:APP.BCrumb.Load(arguments[0], "cities")&&0;'],
["50 regiões top", 'javascript:APP.BCrumb.Load(arguments[0], "regions")&&0;'],
["Todos os países", 'javascript:APP.BCrumb.Load(arguments[0], "countries")&&0;']
]], index, request;
}
COM.Log('--- base ---');
//
//  wrapper for map sources.
//
function google_api()
{
APP.RUN.Call("google.jsapi");
}
//
//  setting up our own quirky.
//  todo: look if we can do the first part via css
//
function manipulateLinks()
{
var x=COM.GET.All('a', null, 0);
for(var i=0; i!=x.length; ++i)
{
if(x[i])
{
x[i].onfocus=function()
{
this.blur();
}
}
if(x[i].getAttribute('type') =='popup')
{
x[i].onclick=function()
{
var newin=window.open(url,'trivagopop','left=50,height=500,width=800,resizable=yes,scrollbars=yes');
if(newin.focus)
{
newin.focus();
}
return false;
}
}
}
}
function showInfo()
{
$("wait").style.visibility="visible";
return true;
}
function disable_one_button(form_id){
window.setTimeout("if($('button')) $('button').disabled=true;",20);
$( form_id ).submit();
return true;
}
function disable_button(){
return disable_one_button("myform");
}APP.CALENDER=function(O, D, N, P)
{
//
//
//
this.AdaptDate=function()
{
if(N > 1)
{
for(var i=0;i!=N;++i)
{
S[i+1]=new Date(S[i].getFullYear(), S[i].getMonth()+1, S[i].getDate());
}
}
return true;
}
//
//
//
this.Create=function(x, y)
{
if(O.isElement())
{
if(!C)
{
C=COM.Element(['div',{'class':'APPCalender','style':'display:none;position:absolute;top:0px;left:0px;z-index:200;'}]);
COM.GET.Elem(O).appendChild(C);
var table='';
for(var k=0;k!=N;++k)
{
table +='<table>\n';
table +='<thead>\n';
table +='<tr class="monthyear">\n';
if(N ==1)
{
table +='<th class="ctrl" style="cursor:pointer;"><<</th>\n';
table +='<th class="ctrl" style="cursor:pointer;"><</th>\n';
table +='<th class="title" colspan="3"></th>\n';
table +='<th class="ctrl" style="cursor:pointer;">></th>\n';
table +='<th class="ctrl" style="cursor:pointer;">>></th>\n';
}
else
{
switch(k)
{
case 0:
table +='<th class="ctrl" style="cursor:pointer;"><<</th>\n';
table +='<th class="ctrl" style="cursor:pointer;"><</th>\n';
table +='<th class="title" colspan="5"></th>\n';
break;
case(N-1):
table +='<th class="title" colspan="5"></th>\n';
table +='<th class="ctrl" style="cursor:pointer;">></th>\n';
table +='<th class="ctrl" style="cursor:pointer;">>></th>\n';
break;
default:
table +='<th class="title" colspan="7"></th>\n';
break;
}
}
table +='</tr>\n';
table +='<tr class="day">\n';
table +='<th class="weekday">'+_D[0]+'</th>\n';
table +='<th class="weekday">'+_D[1]+'</th>\n';
table +='<th class="weekday">'+_D[2]+'</th>\n';
table +='<th class="weekday">'+_D[3]+'</th>\n';
table +='<th class="weekday">'+_D[4]+'</th>\n';
table +='<th class="weekend">'+_D[5]+'</th>\n';
table +='<th class="weekend">'+_D[6]+'</th>\n';
table +='</tr>\n';
table +='</thead>\n';
table +='<tbody>\n';
for(var i=0;i!=6;++i)
{
table +='<tr>\n';
for(var j=0;j!=7;++j){ table +='<td></td>\n'; }
table +='</tr>\n';
}
table +='</tbody>\n';
table +='</table>\n';
}
C.innerHTML=table;
var Me=this;
var div=COM.Element(['div',{'class':'close_link'}, 'Fechar']);
C.appendChild(div);
div.setStyle('cursor', 'pointer');
div.addEvent('click', function(){Me.Close('close');});
var heads=COM.GET.All('thead', C);
for(var k=0;k!=N;++k)
{
var cells=COM.GET.All('th', heads[k]);
if(N ==1)
{
cells[0].addEvent('click', function(){Me.Move('y', -1);});
cells[1].addEvent('click', function(){Me.Move('m', -1);});
cells[3].addEvent('click', function(){Me.Move('m', 1);});
cells[4].addEvent('click', function(){Me.Move('y', 1);});
}
else
{
switch(k)
{
case 0:
cells[0].addEvent('click', function(){Me.Move('y', -1);});
cells[1].addEvent('click', function(){Me.Move('m', -1);});
break;
case(N-1):
cells[1].addEvent('click', function(){Me.Move('m', 1);});
cells[2].addEvent('click', function(){Me.Move('y', 1);});
break;
}
}
}
(O.getStyle('position') =='relative') ||(O.getStyle('position') =='absolute') || O.setStyle('position', 'relative');
C.addEvent('mouseover', function(){Me.Focus(true);});
C.addEvent('mouseout', function(){Me.Focus(false);});
}
(typeof x!='undefined') && C.setLeft(x);
(typeof y!='undefined') && C.setTop(y);
C.appear();
return true;
}
return false;
}
//
//
//
this.Show=function(d)
{
if(this.Create())
{
d &&(S[0]=d.clone());
S[0].setDate(1) && this.AdaptDate();
var heads=COM.GET.All('thead', C);
var bodys=COM.GET.All('tbody', C);
for(var k=0;k!=N;++k)
{
// set title in thead
var cells=COM.GET.All('th', heads[k]);
cells[(k ==0)?2:0 ].innerHTML=_M[S[k].getMonth()]+', '+S[k].getFullYear();
// set cells in tbody
var I=new Date(S[k].getFullYear(), S[k].getMonth(), 1);
I.setDate(I.getDate()-(I.getDay()+6)%7-1);
var cells=COM.GET.All('td', bodys[k]);
for(var i=0,n=cells.length;i!=n;++i)
{
I.setDate(I.getDate()+1);
var c='outside'; cells[i].innerHTML='&nbsp;';
cells[i].delEvent('click');cells[i].setStyle('cursor', 'auto');
if(S[k].getFullYear() ==I.getFullYear() &&
S[k].getMonth() ==I.getMonth())
{
cells[i].innerHTML=String(I.getDate());
c =((I.getDay()+6)%7 < 5)?'inside weekday':'inside weekend';
c +=(D.getFullYear() ==I.getFullYear() && D.getMonth() ==I.getMonth() && D.getDate() ==I.getDate())?' selected':'';
if((!P['max_date'] ||
I <=P['max_date']) &&
(!P['min_date'] ||
I >=P['min_date']))
{
c +=' active';
this.SetEvent(cells[i], new Date(I.getFullYear(), I.getMonth(), I.getDate()));
}
else
{
c +=' inactive';
}
}
cells[i].setClass(c);
}
}
return true;
}
return false;
}
//
//
//
this.SetEvent=function(c, d)
{
var Me=this;
c.setStyle('cursor', 'pointer');
c.addEvent('click', function(){Me.Date(d);Me.Close('selectclose');});
}
//
//
//
this.ShowAt=function(o, d)
{
(typeof P['onopen'] =='function') && P['onopen']();
F=false;
var Me=this;
document.delEvent('mousedown');
this.Create();
document.addEvent('mousedown', function(){Me.CloseEvent('close');});
this.Show(d);
if(o.isElement())
{
switch(P['position'])
{
case 'bottom':
this.Create(o.getLeft()+o.getWidth()-C.getWidth(), o.getTop()+o.getHeight());
break;
case 'left':
default:
this.Create(o.getLeft()+o.getWidth(), o.getTop());
break;
}
}
}
//
//
//
this.Hide=function()
{
(F=true) && document.delEvent('mousedown');
C && C.disappear();
}
//
//
//
this.Close=function(t)
{
this.Hide();
(t =='none') ||((typeof P['on'+t] =='function') && P['on'+t](D));
}
//
//
//
this.Focus=function(b)
{
return(F=b);
}
//
//
//
this.CloseEvent=function(t)
{
F || this.Close(t);
}
//
//
//
this.Move=function(u, i)
{
S[0].setDate(1);
switch(u)
{
case 'y':
var y=S[0].getFullYear();
S[0].setFullYear(S[0].getFullYear()+i);
break;
case 'm':
var m=S[0].getMonth();
S[0].setMonth(S[0].getMonth()+i);
break;
}
if((P['max_date'] &&
(S[0].getFullYear() > P['max_date'].getFullYear()||
(S[0].getFullYear() ==P['max_date'].getFullYear()&&
S[0].getMonth() > P['max_date'].getMonth()))) ||
(P['min_date'] &&
(S[0].getFullYear() < P['min_date'].getFullYear()||
(S[0].getFullYear() ==P['min_date'].getFullYear()&&
S[0].getMonth() < P['min_date'].getMonth()))))
{
switch(u)
{
case 'y':
S[0].setFullYear(y);
break;
case 'm':
S[0].setMonth(m);
break;
}
}
this.AdaptDate();
this.Show();
}
//
//
//
this.MoveDate=function(d)
{
P['min_date'] &&(d < P['min_date']) &&(d=P['min_date']) && this.Date(d);
P['max_date'] &&(d > P['max_date']) &&(d=P['max_date']) && this.Date(d);
var f;(typeof P['onselect'] =='function') &&(f=P['onselect']) &&(P['onselect']=null);
(this.Date(d)) &&(S[0]=new Date(d.getFullYear(), d.getMonth(), 1)) && this.AdaptDate();
(typeof f =='function') &&(P['onselect']=f);
}
//
//
//
this.Date=function(d)
{
try
{
if((!P['min_date'] || d >=P['min_date']) &&(!P['max_date'] || d <=P['max_date']))
{
(typeof P['onselect'] =='function') && P['onselect'](d);
return(D=d);
}
return false;
}
catch(e)
{
return false;
}
}
//
// private vars
//
var C, // calender element
S=[], // shown dates
F=false; // aktiver Focus
_M=['Janeiro', 'Fevereiro', 'Março', 'Abril', 'Maio', 'Junho',
'Julho', 'Agosto', 'Setembro', 'Outubro', 'Novembro', 'Dezembro'],
_D=['Seg', 'Ter', 'Qua', 'Qui',
'Sex', 'Sáb', 'Dom'];
//
// init vars
//
D ||(D=new Date());
P ||(P ={});
N ||(N=1);
S[0]=D.clone();
this.AdaptDate();
};APP.DEALFORM=function(format, id, pos, min, max, steps)
{
this.Handler=function(m, t, d)
{
try
{
switch(m)
{
case 'copen':
(typeof APP_DEALS_SLIDER =='undefined') || APP_DEALS_SLIDER.Hide();
(pos =='bottom') && Browser.IE6 && $('room_type').isElement() && $('room_type').hide();
(d=APP.PRICE.DEALS.CreateDate(format, $(id+'_'+t+'_date').value)) ||(d=APP.PRICE.DEALS.CreateDate($(id+'_'+t+'_date').defaultValue));
d && C[t].MoveDate(d);
break;
case 'csclose':
(typeof APP_DEALS_SLIDER =='undefined') || APP_DEALS_SLIDER.Display();
(pos =='bottom') && $('room_type').isElement() && $('room_type').show();
this.SelectCalender(t, d);
break;
case 'cselect':
this.SelectCalender(t, d);
break;
case 'cclose':
(typeof APP_DEALS_SLIDER =='undefined') || APP_DEALS_SLIDER.Display();
(pos =='bottom') && $('room_type').isElement() && $('room_type').show();
this.CheckDateRange(t);
break;
case 'eclick':
C[t].ShowAt($(id+'_'+t+'_calendar_position'));
break;
case 'eblur':
C[t].CloseEvent('none');
(typeof APP_DEALS_SLIDER =='undefined') || APP_DEALS_SLIDER.Display();
(pos =='bottom') && $('room_type').isElement() && $('room_type').show();
this.CheckDateRange(t);
break;
}
}
catch(e){}
}
this.SetDateField=function(t, d)
{
$(id+'_'+t+'_year').value =d.getFullYear();
$(id+'_'+t+'_month').value=d.getMonth()+1;
$(id+'_'+t+'_day').value=d.getDate();
switch(format)
{
case 'us':
$(id+'_'+t+'_date').value =String(d.getMonth()+1).lpad(2, '0')+'/'+String(d.getDate()).lpad(2, '0')+'/'+d.getFullYear();
break;
case 'uk':
$(id+'_'+t+'_date').value =String(d.getDate()).lpad(2, '0')+'/'+String(d.getMonth()+1).lpad(2, '0')+'/'+d.getFullYear();
break;
case 'eu':
default:
$(id+'_'+t+'_date').value =String(d.getDate()).lpad(2, '0')+'.'+String(d.getMonth()+1).lpad(2, '0')+'.'+d.getFullYear();
break;
}
}
this.SelectCalender=function(t, d)
{
this.SetDateField(t, d);
this.CheckDateRange(t);
}
this.CheckDateRange=function(t)
{
var from_date, to_date;
(from_date=APP.PRICE.DEALS.CreateDate(format, $(id+'_from_date').value)) ||(from_date=APP.PRICE.DEALS.CreateDate(format, $(id+'_from_date').defaultValue));
(to_date=APP.PRICE.DEALS.CreateDate(format, $(id+'_to_date').value)) ||(to_date=APP.PRICE.DEALS.CreateDate(format, $(id+'_to_date').defaultValue));
if(to_date && from_date)
{
if(to_date <=from_date)
{
switch(t)
{
case 'to':
from_date.setDate(1);
from_date.setFullYear(to_date.getFullYear());
from_date.setMonth(to_date.getMonth());
from_date.setDate(to_date.getDate()-1);
break;
case 'from':
to_date.setDate(1);
to_date.setFullYear(from_date.getFullYear());
to_date.setMonth(from_date.getMonth());
to_date.setDate(from_date.getDate()+1);
break;
}
}
this.SetDateField('to', to_date);
this.SetDateField('from', from_date);
C['from'].MoveDate(from_date);
C['to'].MoveDate(to_date);
}
}
var Me=this;
if(pos!==null)
{
var C ={
'from':new APP.CALENDER($(id+'_from_calendar'), null, steps,{
'min_date':min,
'max_date':max,
'position':pos,
'onopen':function(){Me.Handler('copen', 'from', null);},
'onselectclose':function(d){Me.Handler('csclose', 'from', d);},
'onclose':function(d){Me.Handler('cclose', 'from', d);},
'onselect':function(d){Me.Handler('cselect', 'from', d);}
}),
'to':new APP.CALENDER($(id+'_to_calendar'), null, steps,{
'min_date':min,
'max_date':max,
'position':pos,
'onopen':function(){Me.Handler('copen', 'to', null);},
'onselectclose':function(d){Me.Handler('csclose', 'to', d);},
'onclose':function(d){Me.Handler('cclose', 'to', d);},
'onselect':function(d){Me.Handler('cselect', 'to', d);}
})
};
$(id+'_from_calendar_position').show();
$(id+'_from_calendar_position').setStyle('cursor', 'pointer');
$(id+'_from_date').addEvent('click', function(){Me.Handler('eclick', 'from', null);});
$(id+'_from_date').addEvent('focus', function(e){Me.Handler('eclick', 'from', null);});
$(id+'_from_date').addEvent('blur', function(e){Me.Handler('eblur', 'from', null);});
$(id+'_from_calendar_position').addEvent('click', function(){Me.Handler('eclick', 'from', null);});
$(id+'_to_calendar_position').show();
$(id+'_to_calendar_position').setStyle('cursor', 'pointer');
$(id+'_to_date').addEvent('click', function(){Me.Handler('eclick', 'to', null);});
$(id+'_to_date').addEvent('focus', function(){Me.Handler('eclick', 'to', null);});
$(id+'_to_date').addEvent('blur', function(){Me.Handler('eblur', 'to', null);});
$(id+'_to_calendar_position').addEvent('click', function(){Me.Handler('eclick', 'to', null);});
}
this.CheckDateRange('from');
};APP.SNAPSHOT=new function()
{
//
//
//
this.Get=function(k)
{
if(typeof k =='undefined')
{
var S ={},
i;
for(i in P)
{
S[i]=P[i];
}
for(i in J)
{
S[i]=J[i];
}
return S;
}
else
{
if(typeof J[k]!='undefined')
{
return J[k];
}
else if(typeof P[k]!='undefined')
{
return P[k];
}
else
{
return null;
}
}
}
//
//
//
this.Response=function(params, is_protocol)
{
var o=[],
i;
if(is_protocol)
{
for(i in params)
{
o[ key_map[i] ]=params[i];
}
}
else
{
o =params;
}
return((J ={}) &&(P=o));
}
//
//
//
this.Request=function(k, v)
{
return(J[k]=v);
}
//
//
//
this.GetQuery=function(complete, without_request_ident, encode_uri)
{
var retval=!complete ? "&path="+encodeURI(this.Get('path')):'',
hash=complete  ? this.Get():J,
field;
if(typeof encode_uri =='undefined')
{
encode_uri=true;
}
for(param in hash)
{
if(param=='fields')
{
for(field in hash[param])
{
if(!{'function':1,'undefined':1,'object':1}[typeof hash[param][field]])
{
retval +=((retval=='') ? '':'&')+"f_"+field+"=";
retval +=encode_uri ? encodeURI(hash[param][field]):hash[param][field];
}
}
}
else if(
!{'function':1,'undefined':1,'object':1}[typeof hash[param]] &&
(hash[param]!=null)
)
{
retval +=((retval=='') ? '':'&')+param+"=";
if((typeof without_request_ident=='undefined') ||(without_request_ident===true && param!='request_ident'))
{
retval +=encode_uri ? encodeURI(hash[param]):hash[param];
}
}
}
return retval;
}
//
//
//
this.SetMap=function(map)
{
key_map=map;
}
//
// private vars
//
var P ={},
J ={},
key_map=null;
};APP.HASH=new function()
{
//
//
//
this.Get=function(id)
{
if(!Browser.Safari)
{
this.Objects();
if(typeof O[id]!='undefined')
{
return this.Unconvert(O[id]);
}
}
return '';
}
//
//
//
this.Set=function(id, hash, straight)
{
if(!Browser.Safari)
{
this.Objects();
(typeof straight =='undefined') &&(straight=false);
(typeof id!='undefined' && typeof hash!='undefined') &&(O[id]=this.Convert(hash));
var hash='',
h;
for(h in O)
{
if(typeof O[h] =='string' && O[h]!='')
{
hash &&(hash +=',');
hash +=h+':"'+encodeURI(O[h])+'"';
}
}
(hash || straight) && COM.HASH.Set(hash);
}
return true;
}
//
//
//
this.Unset=function(id)
{
return this.Set(id, '', true);
}
//
//
//
this.Reg=function(id, f, r)
{
if(!Browser.Safari)
{
f &&(F[id]=COM.GET.Function(f));
((typeof r =='undefined' || r) &&(r=true)) ||(r=false);
COM.HASH.Reg(function(){APP.HASH.Check();}, r);
return true;
}
}
//
//
//
this.Convert=function(any)
{
var s='';
if(typeof any =='string')
{
s=any;
}
else
{
for(h in any)
{
if(typeof any[h]!='function' && typeof any[h]!='object')
{
s &&(s +='&');
s +=h+'='+encodeURI(any[h]);
}
}
}
return s;
}
//
//
//
this.Unconvert=function(any)
{
var o ={};
if(typeof any =='string')
{
var a=any.split('&');
var p;
for(var i=0;i!=a.length;++i)
{
(p=a[i].split('=', 2)) &&(o[p[0]]=decodeURI(p[1]));
}
}
else
{
o=any;
}
return o;
}
//
//
//
this.Objects=function()
{
try
{
eval('O=COM.GET.Elem({'+decodeURI(COM.HASH.Get().replace(/#/, ''))+'})');
return true;
}
catch(e)
{
O=COM.GET.Elem({});
return false;
}
}
//
//
//
this.Check=function()
{
if(!Browser.Safari)
{
var Old=O.clone();
this.Objects();
for(h in Old)
{
(typeof Old[h]!='function' && Old[h]!=O[h]) &&(typeof F[h] =='function') && F[h]();
}
}
return true;
}
//
// private vars
//
var F ={},
O ={};
};
function AjaxForm_Engine()
{
this.current_url=false;
};
AjaxForm_Engine.prototype ={
get_form_id:function(obj)
{
var node=obj;
while(node.nodeName!="FORM")
node=node.parentNode;
return node.getAttribute((typeof node.getAttribute("name") =="string") ? "name":"id");
},
get_formvars:function(obj, button)
{
var elems;
var postvars="";
if(elems=document.forms[this.get_form_id(obj)].elements)
{
for(var i=0;i<elems.length;i++)
{
var do_add=false;
var add_value=null;
switch(elems[i].type)
{
case "checkbox":
case "radio":
var do_add=elems[i].checked;
var add_value=elems[i].value;
break;
case "submit":
case "button":
if(elems[i].name ==button || button =="")
{
var do_add=true;
var add_value=(elems[i].value =='')?'on':elems[i].value;
}
break;
default:
var do_add=true;
var add_value=elems[i].value;
}
if(do_add && add_value && elems[i].name)
{
if(postvars!="")
{
postvars +="&";
}
postvars +=escape(elems[i].name)+"="+encodeURIComponent(add_value);
}
}
}
return postvars;
},
reset_formvars:function(obj)
{
document.forms[this.get_form_id(obj)].reset();
},
run:function(obj, params)
{
var form_id=this.get_form_id(obj);
if(document.forms[form_id])
{
if(document.forms[form_id].getAttribute("enctype") =="multipart/form-data")
{
if(!$("submit_"+form_id).isElement())
{
if(typeof this.int_iframe_counter =="undefined")
{
this.int_iframe_counter=0;
}
else
{
this.int_iframe_counter++;
}
var emode=document.createElement("input");
var iframe =document.createElement("iframe");
var iframe_id="submit_"+form_id+"_"+this.int_iframe_counter;
emode.setAttribute("type", "hidden");
emode.setAttribute("name", "iframe_mode");
emode.setAttribute("value", "1");
iframe.setAttribute("id",   iframe_id);
iframe.setAttribute("name", iframe_id);
iframe.style.display="none";
var parentNode=$( form_id ).parentNode;
$( form_id ).setAttribute("target", iframe_id);
$( form_id ).appendChild(emode);
parentNode.appendChild(iframe);
/**
* VERY IMPORTANT:
* The following Code is a BUGFIX for the INTERNET EXPLORER !!
* This browser has problems with dynamic generated iframes and changing
* the target - attrib.
* !! NORMAL IT NOT WORKS !!
*/
if(self.frames[ iframe_id ].name!=iframe_id)
{
self.frames[ iframe_id ].name=iframe_id;
}
window.setTimeout("AjaxForm.iframe_handler('"+iframe_id+"', "+params["onfinish"]+");", 75);
}
if(this.current_url!=$(form_id).getAttribute("action"))
{
this.current_url=$(form_id).getAttribute("action");
return true;
}
}
else
{
if(this.current_url!=document.forms[form_id].getAttribute("action"))
{
this.current_url=document.forms[form_id].getAttribute("action");
var form_method=document.forms[form_id].getAttribute("method");
var onfinish=function(r){AjaxForm.current_url=false;params["onfinish"](r);};
switch(form_method)
{
case "get":
COM.HTTP.Get(document.forms[form_id].getAttribute("action")+"?"+this.get_formvars(obj, params["button"]),{ "onfinish":onfinish } );
break;
case "post":
default:
COM.HTTP.Post(document.forms[form_id].getAttribute("action"),{ "vars":this.get_formvars(obj, params["button"]), "onfinish":onfinish } );
}
}
}
}
if(params["reset"])
{
this.reset_formvars(obj);
}
return false;
},
iframe_handler:function(iframe_id, onfinish)
{
var iframe=eval(""+iframe_id+"");
var ibody =iframe.document.body;
if( ibody!=null &&
ibody.innerHTML.length > 0)
{
this.current_url=false;
if(onfinish)
{
onfinish(ibody.innerHTML);
}
}
else
{
window.setTimeout("AjaxForm.iframe_handler('"+iframe_id+"', "+onfinish+");", 75);
}
}
}
var AjaxForm=new AjaxForm_Engine();
APP.SCREEN=new function()
{
//
//
//
this.Button=function(b)
{
return(B=b);
}
//
//
//
this.Get=function(url)
{
APP.RUN.Exe('trivago.form');
APP.Css('form');
if(!this.IsActive(url))
{
APP.Unbubble();
APP.WIZ.Open();
this.InitialHash(url);
COM.HTTP.Get(url,{ 'onfinish':function(r){
APP.HASH.Reg('screenmodule', function(){} );
APP.SCREEN.Response('load', r);
APP.HASH.Reg('screenmodule', function(){APP.SCREEN.InitHash();} );
}});
}
}
//
//
//
this.Reload=function(url, hash)
{
if(!this.IsActive(url))
{
(typeof hash =='undefined') &&(hash ==false);
COM.HTTP.Get(url,{ 'onfinish':function(r){
hash && APP.HASH.Reg('screenmodule', function(){} );
APP.SCREEN.Response('reload', r);
hash && APP.HASH.Reg('screenmodule', function(){APP.SCREEN.InitHash();} );
}});
}
}
//
//
//
this.Post=function(o)
{
APP.RUN.Exe('trivago.form');
APP.Css('form');
APP.Unbubble();
APP.WIZ.Open();
AjaxForm.run(o,{
'button':B,
'reset':true,
'onfinish':function(r){
APP.SCREEN.Response('reload', r);
}
});
return false;
}
//
//
//
this.Submit=function(o)
{
if(!this.IsScreen(o))
{
return true;
}
return AjaxForm.run(o,{
'button':B,
'onfinish':function(r){
APP.SCREEN.Response('reload', r);
}
});
}
//
//
//
this.Response=function(t, r)
{
U=false;
switch(t)
{
case 'reload':
case 'load':
default:
APP.WIZ.Module(r, 'screenmodule');
}
APP.WIZ.Reg('onclose', function(){
try
{
APP.HASH.Get('screenmodule') && APP.HASH.Unset('screenmodule');
}
catch(e)
{
}
});
APP.InnerExec(r);
return true;
}
//
//
//
this.IsActive=function(url)
{ if(U ==url)
{
return true;
}
U=url;
return false;
}
//
//
//
this.IsScreen=function(n)
{
while((n=n.parentNode))
{
if(n.getAttribute)
{
if(n.className.match(/COMWizardCont$/))
{
return true;
}
}
}
return false;
}
//
//
//
this.InitHash=function()
{
U=null;
var h=APP.HASH.Get('screenmodule');
if(h && h['base'])
{
var u=(h['base'].substr(0, 1) =='/'?'':'/')+h['base']+'?';
for(var k in h)
{
(typeof h[k] =='string') &&(k!='base') &&(u +='&'+k+'='+h[k]);
}
APP.HASH.Reg('screenmodule', function(){});
this.Reload(u, true);
}
}
//
//
//
this.InitialHash=function(url)
{
var b=url.split('?', 2);
var h ={ 'base':(b[0].substr(0, 1) =='/')?b[0]:'/'+b[0] };
var q=b[1].split('&');
for(var i=0,n=q.length;i!=n;++i)
{
var qe=q[i].split('=', 2);
qe[0] &&(h[qe[0]]=qe[1]);
}
APP.HASH.Reg('screenmodule', function(){} );
APP.HASH.Set('screenmodule', h);
}
//
//  private vars
//
//  U: current url
//  B: pressed button
//
var U=false,
B;
};
function DynNavigation_Engine()
{
this.bounds ={};
this.listen_map=null;
this.map_try=0;
this.properties ={};
this.elems ={};
this.property('rpc', '/trivago_rpc.php?action=map_navigation');
this.property('path_id', 0);
this.property('view', 'map');
this.property('tab', 'regions');
this.property('type', 'popular_childs');
this.property('attractions', '0');
this.property('use_bounds', false);
this.property('center', 'absolute');
this.property('hash', null);
this.property('is_short', '0');
this.property('reset_map', false);
};
DynNavigation_Engine.prototype =
{
//
// properties setzen
//
property:function(property, value)
{
this.properties[property]=value;
},
//
// Karte initialisieren
//
init:function(id)
{
$('js_loader').setOpacity(50);
$('js_loader').appear();
this.property('path_id', id);
this.set_hash();
COM.HTTP.Get(this.properties['rpc']+'&status=html&tab='+this.properties['tab']+'&type='+this.properties['type']+'&view='+this.properties['view']+'&path='+this.properties['path_id'],{'onfinish':function(r){dynNav.response_handler('init', r);}});
},
//
// neue Teile laden
//
load:function(type, query)
{
this.property('type', type);
var show_markers=0;
if(typeof ggMaps!="undefined" && typeof ggMaps.maps["navigationmap"]!="undefined")
{
ggMaps.maps["navigationmap"].show_loader();
show_markers=1;
}
this.set_hash();
COM.HTTP.Get(this.properties['rpc']+'&status=fragment&tab='+this.properties['tab']+'&type='+this.properties['type']+'&view='+this.properties['view']+'&path='+this.properties['path_id']+"&markers="+show_markers+query,{'onfinish':function(r){dynNav.response_handler(type, r);}});
},
//
// Antwort verarbeiten
//
response_handler:function(type, data)
{
var node=COM.Element(['div',{}]);
node.innerHTML=data;
switch(type){
case 'popular_childs':
case 'geo_childs':
case 'topitemsattraction':
case 'topitemshotel':
this.parse_response(node);
break;
case 'init':
this.replace_content(node);
break;
}
APP.InnerExec(data);
},
//
// View umsetzen - map|list
//
switch_view:function(type)
{
this.property('view', type);
switch(type)
{
case 'list':
$('js_navigationmap_box').disappear();
$('js_icon_list').className='icon_active';
$('js_icon_map').className ='icon_inactive';
break;
case 'map':
default:
$('js_navigationmap_box').appear();
$('js_icon_list').className='icon_inactive';
$('js_icon_map').className ='icon_active';
if(typeof ggMaps =='undefined' || typeof ggMaps.maps['navigationmap'] =='undefined')
{
APP.RUN.Exe('trivago.map', this.map_object(function(){dynNav.init_map();}));
return false;
}
break;
}
this.property('is_short', '0');
this.set_hash();
return true;
},
//
// Umsetzen des Tabs
//
switch_tab:function(type, do_load)
{
if(typeof do_load =='undefined')
{
do_load=true;
}
this.property('tab', type);
switch(type)
{
case 'attractions':
$('topitemattraction','js_map_region','js_map_hotel').withEach('disappear');
if($('topitemhotel').isElement())
{
$('topitemhotel','js_map_attraction').withEach('appear');
}
else
{
$('js_map_attraction').appear();
}
this.property('type', 'topitemsattraction');
$('js_view_regions').className=$('js_view_hotels').className='tab_inactive';
$('js_view_attractions').className ='tab_active';
if(do_load)
{
if(this.properties['use_bounds'])
{
this.map_event(ggMaps.maps['navigationmap'].map.getBounds(), true);
}
else if(this.properties['attractions'] > 0)
{
this.load('topitemsattraction', '&f_'+this.properties['attractions']+'=1');
}
else
{
this.load('topitemsattraction', '');
}
}
break;
case 'hotels':
if($('topitemhotel').isElement())
{
$('topitemhotel','js_map_region','js_map_attraction').withEach('disappear');
}
else
{
$('js_map_region','js_map_attraction').withEach('disappear');
}
$('topitemattraction','js_map_hotel').withEach('appear');
this.property('type', 'topitemshotel');
$('js_view_regions').className=$('js_view_attractions').className='tab_inactive';
$('js_view_hotels').className ='tab_active';
if(do_load)
{
if(this.properties['use_bounds'])
{
this.map_event(ggMaps.maps['navigationmap'].map.getBounds(), true);
}
else
{
this.load('topitemshotel', '');
}
}
break;
case 'regions':
default:
$('js_map_hotel','js_map_attraction').withEach('disappear');
if($('topitemhotel').isElement())
{
$('topitemattraction','topitemhotel','js_map_region').withEach('appear');
}
else
{
$('topitemattraction','js_map_region').withEach('appear');
}
this.property('type', 'popular_childs');
$('js_view_hotels').className=$('js_view_attractions').className='tab_inactive';
$('js_view_regions').className ='tab_active';
if(do_load)
{
if(this.properties['use_bounds'])
{
this.map_event(ggMaps.maps['navigationmap'].map.getBounds(), true);
}
else
{
this.load('popular_childs', '');
}
}
break;
}
},
//
// Attraktionstypen setzen
//
switch_attractions:function(id)
{
this.property('attractions', id);
this.switch_tab('attractions', true);
},
//
// komplettes Modul mit der Response ersetzen
//
replace_content:function(node)
{
var elems=node.childNodes;
for(var i=0;i<elems.length;i++)
{
if(elems[i].nodeType ==1)
{
if(elems[i].getAttribute('id') =='js_dyn_navigation')
{
$('js_dyn_navigation').innerHTML=elems[i].innerHTML;
return true;
}
}
if(elems[i].hasChildNodes())
{
if(this.parse_response(elems[i]))
{
return true;
}
}
}
},
//
// response parsen Teile aktualisieren
//
parse_response:function(node)
{
var elems=node.childNodes;
for(var i=0;i<elems.length;i++)
{
if(elems[i].hasChildNodes())
{
this.parse_response(elems[i]);
}
if(elems[i].nodeType ==1)
{
if($(elems[i]).hasClass('dyn_nav') &&
elems[i].getAttribute('id'))
{
$(elems[i].getAttribute('id')).innerHTML=elems[i].innerHTML;
}
}
}
},
//
// alter dealform to selected map selection
//
deal:function(id, name)
{
if(document.forms['dealform_hotel'] &&
document.forms['dealform_hotel']['query_path_name'] &&
document.forms['dealform_hotel']['path'])
{
if(id > 0)
{
APP.PRICE.DEALS.Clear(0);
document.forms['dealform_hotel']['query_path_name'].value=name;
document.forms['dealform_hotel']['path'].value=id;
}
else if(!APP.PRICE.DEALS.ROTATION.GetRotationStatus())
{
APP.PRICE.DEALS.ROTATION.Start('destinationstring');
}
}
},
//
// for highlighting specific marker
//
marker:function(mode, id)
{
if(this.listen_map)
{
this.listen_map.marker(mode, id);
}
},
//
// initilizing of our movable map
//
init_map:function()
{
if(typeof ggMaps =='undefined' || typeof ggMaps.maps['navigationmap'] =='undefined')
{
APP.RUN.Exe('trivago.map', this.map_object(function(){dynNav.init_map();}));
return false;
}
this.property('center', 'absolute');
this.bounds=ggMaps.maps['navigationmap'].map.getBounds();
ggMaps.maps['navigationmap'].settings['rpc_markers']='none';
this.listen_map=new ListenMap_Engine({
'id':'navigationmap',
'move':function(){dynNav.map_event(ggMaps.maps['navigationmap'].map.getBounds(), false);},
'zoom':function(){dynNav.map_event(ggMaps.maps['navigationmap'].map.getBounds(), false);}
});
this.listen_map.fix();
this.listen_map.listen();
},
//
// is called when html is reloaded for adding marker data
//
map:function(elems)
{
if(typeof ggMaps =='undefined' || typeof ggMaps.maps['navigationmap'] =='undefined')
{
APP.RUN.Exe('trivago.map', this.map_object(function(){dynNav.init_map();dynNav.map(elems);}));
return false;
}
if(typeof this.listen_map =='undefined' || !this.listen_map)
{
this.init_map();
}
if(elems)
{
this.elems[this.properties['type']]=elems;
}
with(ggMaps.maps['navigationmap'])
{
this.listen_map.unlisten();
if(this.properties['center'] =='absolute')
{
this.listen_map.position();
}
if(this.properties['reset_map'] || this.properties['type'] =='geo_childs')
{
this.property('center', 'relative_all');
}
else
{
this.property('center', 'absolute');
}
this.property('reset_map', false);
settings['origin_center']['class']=this.properties['center'];
reset_map();
if(this.elems[this.properties['type']].length > 0)
{
add_markers(this.elems[this.properties['type']]);
all_markers();
}
this.listen_map.listen();
}
},
//
// execute when user is zooming or alters map clipping
//
map_event:function(bounds, do_load)
{
if((this.bounds.getSouthWest().lat()!=bounds.getSouthWest().lat() &&
this.bounds.getNorthEast().lat()!=bounds.getNorthEast().lat() &&
this.bounds.getSouthWest().lng()!=bounds.getSouthWest().lng() &&
this.bounds.getNorthEast().lng()!=bounds.getNorthEast().lng() ) ||
do_load)
{
this.property('use_bounds', true);
this.bounds=bounds;
if(this.properties['type'] =='geo_childs')
{
this.load('popular_childs', '&latw='+this.bounds.getSouthWest().lat()+'&late='+this.bounds.getNorthEast().lat()+'&lngn='+this.bounds.getSouthWest().lng()+'&lngs='+this.bounds.getNorthEast().lng());
}
else if(this.properties['type'] =='topitemsattraction' &&
this.properties['attractions'] > 0)
{
this.load(this.properties['type'], '&f_'+this.properties['attractions']+'=1&latw='+this.bounds.getSouthWest().lat()+'&late='+this.bounds.getNorthEast().lat()+'&lngn='+this.bounds.getSouthWest().lng()+'&lngs='+this.bounds.getNorthEast().lng());
}
else
{
this.load(this.properties['type'], '&latw='+this.bounds.getSouthWest().lat()+'&late='+this.bounds.getNorthEast().lat()+'&lngn='+this.bounds.getSouthWest().lng()+'&lngs='+this.bounds.getNorthEast().lng());
}
}
},
//
// creates map object to given settings for initializing map
//
map_object:function(onfinish)
{
var query='';
if(this.properties['use_bounds'] && this.properties['hash'])
{
if(this.properties['view'] =='map' &&
this.properties['hash']['latw'] &&
this.properties['hash']['late'] &&
this.properties['hash']['lngn'] &&
this.properties['hash']['lngs'])
{
query +='&latw='+this.properties['hash']['latw']+'&late='+this.properties['hash']['late']+'&lngn='+this.properties['hash']['lngn']+'&lngs='+this.properties['hash']['lngs'];
}
}
if(this.properties['attractions'] > 0)
{
query +='&f_'+this.properties['attractions']+'=1';
}
return{'onfinish':function(){ggMaps.add({'id':'navigationmap','rpc_markers':'/trivago_rpc.php?action=map_navigation&status=xml&tab='+dynNav.properties['tab']+'&type='+dynNav.properties['type']+'&path='+dynNav.properties['path_id']+'&is_short='+dynNav.properties['is_short']+query,'onfinish':onfinish}); }};
},
//
// set hash for history back
//
set_hash:function()
{
this.del_hash_event();
var hash={
'path_id':this.properties['path_id'],
'tab':this.properties['tab'],
'type':this.properties['type'],
'attractions':this.properties['attractions'],
'view':this.properties['view']
};
if(this.properties['use_bounds'])
{
hash['latw']=this.bounds.getSouthWest().lat();
hash['late']=this.bounds.getNorthEast().lat();
hash['lngn']=this.bounds.getSouthWest().lng();
hash['lngs']=this.bounds.getNorthEast().lng();
}
APP.HASH.Set('mapnav', hash);
this.property('hash', hash);
this.add_hash_event();
},
//
// init module by hash
//
init_hash:function()
{
var hash;
this.del_hash_event();
// only execute once, so therefore we store used hash
// and we can only alter something when module is in page and we have hash
if($('js_dyn_navigation').isElement() &&
!this.properties['hash'] &&
(hash=APP.HASH.Get('mapnav')))
{
this.property('hash', hash);
this.property('reset_map', false);
// what state do we have to restore?
var do_reload=do_tab=do_view=false;
var query='';
// state was a moved map view, we have to restore
if(this.properties['hash']['view'] =='map')
{
if(this.properties['hash']['latw'] &&
this.properties['hash']['late'] &&
this.properties['hash']['lngn'] &&
this.properties['hash']['lngs'])
{
query='&latw='+this.properties['hash']['latw']+'&late='+this.properties['hash']['late']+'&lngn='+this.properties['hash']['lngn']+'&lngs='+this.properties['hash']['lngs'];
this.property('use_bounds', true);
do_reload=true;
}
else if(this.properties['use_bounds'])
{
this.property('use_bounds', false);
this.property('reset_map', true);
this.property('center', 'relative_all');
do_reload=true;
}
}
// add all has values to properties and proove if something is to do
for(h in this.properties['hash'])
{
if(typeof this.properties['hash'][h]!='function' &&
Array('path_id', 'tab', 'type', 'attractions', 'view').hasValue(h))
{
if(h =='tab')
{
do_tab=(this.properties[h]!=this.properties['hash'][h]) ? true:do_tab;
}
else if(h =='view')
{
do_view=(this.properties[h]!=this.properties['hash'][h]) ? true:do_view;
}
do_reload=(this.properties[h]!=this.properties['hash'][h]) ? true:do_reload;
this.property(h, this.properties['hash'][h]);
}
}
//we have to restore something
if(do_reload && this.properties['path_id'])
{
// short list is only used on city level in direct include
this.property('is_short', '0');
var status='fragment';
var handler=this.properties['type'];
// if this element exists we have to load module completely, it's index page
if($('js_loader').isElement())
{
$('js_loader').setOpacity(50);
$('js_loader').appear();
status='html';
handler='init';
do_view=do_tab=false;
}
// we have to switch view, map|list
(do_view) && this.switch_view(this.properties['view']);
// we have to switch tab, without loading, we load later
(do_tab) && this.switch_tab(this.properties['tab'], false);
// do we have to load marker with html?
// only the first time marker data will be loaded with xml
var show_markers=0;
if(typeof ggMaps!="undefined" &&
typeof ggMaps.maps["navigationmap"]!="undefined")
{
ggMaps.maps["navigationmap"].show_loader();
show_markers=1;
// map is now fixed so we have to move it, we have then a map event
if(this.properties['use_bounds'])
{
this.bounds=new GLatLngBounds(new GLatLng(parseFloat(this.properties['hash']['latw']), parseFloat(this.properties['hash']['lngn'])), new GLatLng(parseFloat(this.properties['hash']['late']), parseFloat(this.properties['hash']['lngs'])));
ggMaps.maps["navigationmap"].map.setCenter( this.bounds.getCenter(), ggMaps.maps["navigationmap"].map.getBoundsZoomLevel(this.bounds));
}
}
// we have to do the request for loading module
if(this.properties['attractions'] > 0)
{
COM.HTTP.Get(this.properties['rpc']+'&status='+status+'&tab='+this.properties['tab']+'&type='+this.properties['type']+'&f_'+this.properties['attractions']+'=1&view='+this.properties['view']+'&path='+this.properties['path_id']+"&markers="+show_markers+query,{'onfinish':function(r){dynNav.response_handler(handler, r);}});
}
else
{
COM.HTTP.Get(this.properties['rpc']+'&status='+status+'&tab='+this.properties['tab']+'&type='+this.properties['type']+'&view='+this.properties['view']+'&path='+this.properties['path_id']+"&markers="+show_markers+query,{'onfinish':function(r){dynNav.response_handler(handler, r);}});
}
}
}
this.add_hash_event();
},
//
//
//
add_hash_event:function()
{
APP.HASH.Reg('mapnav', function(){dynNav.property('hash', null);dynNav.init_hash();});
return true;
},
//
//
//
del_hash_event:function()
{
APP.HASH.Reg('mapnav', function(){});
return true;
}
}
var dynNav=new DynNavigation_Engine();
/*
* This function retrieves the search query from the URL.
*/
function GetParam(name)
{
var match=new RegExp(name+"=(.+)[&]","i").exec(location.search);
if(match==null){
match=new RegExp(name+"=(.+)","i").exec(location.search);
}
if(match==null)
{
return null;
}
match=match+"";
result=match.split(",");
return result[1];
}
/*
* This function is required. It processes the google_ads JavaScript object,
* which contains AFS ads relevant to the user's search query. The name of
* this function <i>must</i> be <b>google_afs_request_done</b>. If this
* function is not named correctly, your page will not display AFS ads.
*/
function google_afs_request_done(google_ads){
// Verify that there are actually ads to display.
if(google_ads.length <=0)
{
return;
}
var wideAds="",   // wide ad unit html text
narrowAds="",   // narrow ad unit html text
node_wide=$("wide_ad_unit2"),
node_narrow=$("narrow_ad_unit"),
headline ="Anúncios Google";
if(node_wide.isElement())
{
node_wide.style.display='block';
}
if(node_narrow.isElement())
{
node_narrow.style.display='block';
}
for(i=0; i<google_ads.length; i++)
{
if(google_ads[i].type=="text/wide")
{
// render a wide ad
wideAds+='<a style="text-decoration:none" onmouseover="javascript:window.status=\'' +
google_ads[i].url+'\';return true;" ' +
'onmouseout="javascript:window.status=\'\';return true;" ' +
'href="'+google_ads[i].url+'">' +
'<span class="ad_line1">'+google_ads[i].line1+'</span></a>'+'&nbsp&nbsp&nbsp' +
'<a style="text-decoration:none" onmouseover="javascript:window.status=\'' +
google_ads[i].url+'\';return true;" ' +
'onmouseout="javascript:window.status=\'\';return true;" ' +
'href="'+google_ads[i].url+'">' +
'<span style="line-height: 1.5em"> <span class="ad_url">'+google_ads[i].visible_url+'</span></a><br />'+
'<span class="ad_text">'+google_ads[i].line2+'</span></span><br />';
}
else
{
// render a narrow ad
narrowAds+='<a style="text-decoration:none" onmouseover="javascript:window.status=\'' +
google_ads[i].url+'\';return true;" ' +
'onmouseout="javascript:window.status=\'\';return true;" ' +
'href="'+google_ads[i].url+'">' +
'<span class="ad_line1">'+google_ads[i].line1+'</span></a><br />' +
'<span class="ad_text">'+google_ads[i].line2+'</span>'+'&nbsp' +
'<span class="ad_text">'+google_ads[i].line3+'</span><br />' +
'<a style="text-decoration:none" onmouseover="javascript:window.status=\'' +
google_ads[i].url+'\';return true;" ' +
'onmouseout="javascript:window.status=\'\';return true;" ' +
'href="'+google_ads[i].url+'">' +
'<span class="ad_url">'+google_ads[i].visible_url+'</span><br /><br /></a>';
}
}
if(narrowAds!="")
{
narrowAds='<a style="text-decoration:none" ' +
'href="http://services.google.com/feedback/online_hws_feedback">' +
'<span class="ad_header" style="text-align:left">'+headline+'</span><br /><br /></a>'+narrowAds;
}
if(wideAds!="")
{
wideAds='<div><a style="text-decoration:none" ' +
'href="http://services.google.com/feedback/online_hws_feedback">' +
'<span class="ad_header" style="text-align:left">'+headline+'</span><br /></a></div>'+ wideAds;
}
// Write HTML for wide and narrow ads to the proper <div> elements
if(wideAds)
{
node_wide.innerHTML=wideAds;
}
if(narrowAds)
{
node_narrow.innerHTML=narrowAds;
}
}APP.FLASH=new function()
{
//
//
//
this.GetVersion=function()
{
// ie
try
{
try
{
var axo=new ActiveXObject('ShockwaveFlash.ShockwaveFlash.6');
try
{
axo.AllowScriptAccess='always';
}
catch(e)
{
return '6,0,0';
}
}
catch(e)
{
}
return new ActiveXObject('ShockwaveFlash.ShockwaveFlash').GetVariable('$version').replace(/\D+/g, ',').match(/^,?(.+),?$/)[1];
// other browsers
}
catch(e)
{
try
{
if(navigator.mimeTypes["application/x-shockwave-flash"].enabledPlugin)
{
return(navigator.plugins["Shockwave Flash 2.0"] || navigator.plugins["Shockwave Flash"]).description.replace(/\D+/g, ",").match(/^,?(.+),?$/)[1];
}
}
catch(e)
{
}
}
return '0,0,0';
}
//
//
//
this.Display=function(node, url)
{
var version=this.GetVersion().split(',').shift();
if(version > 0)
{
var retval ="<object classid=\"clsid:d27cdb6e-ae6d-11cf-96b8-444553540000\" codebase=\"http://fpdownload.macromedia.com/pub/shockwave/cabs/flash/swflash.cab#version=7,0,0,0\">\n";
retval +="\t<param name=\"movie\" value=\""+url+"\" />\n";
retval +="\t<param name=\"quality\" value=\"high\" />\n";
retval +="\t<param name=\"allowScriptAccess\" value=\"always\" />\n";
retval +="\t<embed src=\""+url+"\" name=\"URLvariables\" align=\"middle\" quality=\"high\" pluginspage=\"http://www.macromedia.com/go/getflashplayer\" type=\"application/x-shockwave-flash\" allowScriptAccess=\"always\" />\n";
retval +="</object>";
$(node).innerHTML=retval;
}
}
};
APP.PRICE=new function()
{
//
// this function is quickier than eval(multiplicator: 3.5)
//
this.EvalCode=function(code)
{
if(code)
{
var doc =document;
var node=doc.createElement('script');
doc.getElementsByTagName('head')[0].appendChild(node);
node.text=code;
}
}
};
COM.Log('--- deals ---');
APP.PRICE.DEALS=new function()
{
//
//
//
this.CreateDate=function(f, s)
{
try
{
var i=[];
(s.indexOf('.') ==-1) ||(i=s.split('.'));
(i.length) ||(s.indexOf('/') ==-1) ||(i=s.split('/'));
(i.length) ||(s.indexOf('-') ==-1) ||(i=s.split('-'));
(i.length) ||(s.indexOf(' ') ==-1) ||(i=s.split(' '));
if(i.length ==3 &&
(i[0]=parseInt(i[0], 10)) &&
(i[1]=parseInt(i[1], 10)) &&
(i[2]=parseInt(i[2], 10)))
{
var d, m, y;
(String(i[2]).length ==4) &&(i[2] > 0) &&(y=i[2]) &&(i=[i[0], i[1]]);
(y ==null) &&(String(i[0]).length ==4) &&(i[0] > 0) &&(y=i[0]) &&(i=[i[1], i[2]]);
(y ==null) &&(String(i[2]).length <=2) &&(i[2] > 31) &&(y=i[2]) &&(i=[i[0], i[1]]);
(y ==null) &&(String(i[0]).length <=2) &&(i[0] > 31) &&(y=i[0]) &&(i=[i[1], i[2]]);
(y ==null) &&(i[2] > 0) &&(i[2] < 100) &&(y=i[2]) &&(i=[i[0], i[1]]);
(y ==null) ||((String(y).length <=2) &&(y < 50) &&(y=2000+y));
(y ==null) ||((String(y).length <=2) &&(y > 50) &&(y=1900+y));
if(y!=null)
{
(String(i[0]).length <=2) &&(i[0] > 12) &&(d=i[0]);
(d!=null) &&(i[1] > 0) &&(i[1] <=12)&&(m=i[1]-1);
(d ==null) &&(String(i[1]).length <=2) &&(i[1] > 12) &&(d=i[1]);
(m ==null) && d &&(i[0] > 0) &&(i[0] <=12)&&(m=i[0]-1);
(d ==null) &&(f =='us') &&(i[1] > 0) &&(d=i[1]);
(m ==null) &&(f =='us') &&(i[0] > 0) &&(m=i[0]-1);
(d ==null) &&(i[0] > 0) &&(d=i[0]);
(m ==null) &&(i[1] > 0) &&(m=i[1]-1);
if(d!=null && m!=null)
{
return new Date(y, m, d);
}
}
}
return false;
}
catch(e)
{
return false;
}
}
//
//
//
this.SetViewType=function(value)
{
$('dealform_view_type').value=value;
}
//
//
//
this.SetDealformReferer=function(value)
{
$('dealform_referer').value=value;
}
//
//
//
this.SetGeocode=function(geo_longitude, geo_latitude)
{
var form=document.forms['dealform_hotel'];
form['geo_longitude'].value=geo_longitude;
form['geo_latitude'].value =geo_latitude;
form['order_by'].value   ='distance';
form['geo_distance_item'].value='';
}
//
//
//
this.SwitchItemPage=function(path_id, item_id)
{
var pagetype=item_id ? 'deals':'hotels';
if(!node_deals)
{
this.Init();
}
// set form-action
switch(pagetype)
{
case 'cobrand' :
node_deals.action='/cobrand.php';
break;
case 'hotels' :
node_deals.action='/region.php';
break;
default :
node_deals.action='/item.php';
break;
}
node_deals['pagetype'].value=pagetype;
//set hidden item_id
node_deals['item'].value=item_id;
node_deals['path'].value=path_id;
}
//
//
//
this.GetResponse=function(xml)
{
var error,
item_id,
redirect,
unique_result=xml.getElementsByTagName("result");
if(unique_result[0])
{
//path to big?
if(error=getElD(unique_result[0], "error_path_big"))
{
this.DisplayError(error);
}
else
{
this.SwitchItemPage(getElD(unique_result[0], "path_id"), getElD(unique_result[0], "item_id"));
this.OnSubmit();
}
}
else if(error=getElD(xml, "error"))
{
this.DisplayError(error);
}
else
{
node_dealform_error.appear();
node_destinationstring_select.appear();
node_destinationstring_select.innerHTML=getElD(xml, "result_list");
$('js_dealform_submit_button').setClass("button button_red");
this.SetPreviewLinks($("select_unknown_path").value);
}
}
//
//
//
this.HideError=function()
{
if(typeof node_dealform_error!='undefined')
{
node_dealform_error.disappear();
}
}
//
// display error
//
this.DisplayError=function(str)
{
node_dealform_error.appear();
node_unknown_path.appear();
node_unknown_path.innerHTML="<p>"+str+"</p>";
$('js_dealform_submit_button').setClass("button button_red");
}
//
//
//
this.OnSubmit=function()
{
this.Init();
node_unknown_path.disappear();
node_dealform_error.disappear();
//check from- and to-date
var actual_date=new Date(); //reset actual time. we need only the date
actual_date.setSeconds(0);
actual_date.setHours(0);
actual_date.setMilliseconds(0);
actual_date.setMinutes(0);
var to_date, from_date;
to_date=this.CreateDate('uk', $('dealform_hotel_to_date').value);
from_date=this.CreateDate('uk', $('dealform_hotel_from_date').value);
if(!to_date || !from_date || to_date <=from_date)
{
this.DisplayError("A data da volta é antes da saída");
return false;
}
else if((actual_date > from_date) ||(actual_date > to_date))
{
this.DisplayError("dealform_wrong_date2");
return false;
}
else if(node_deals['query_path_name'].value =='')
{
this.DisplayError("Inserir uma cidade ou região");
return false;
}
APP.PRICE.DEALS.SSG.ClearResultList();
// deactivitate example rotation
APP.PRICE.DEALS.ROTATION.Stop();
if((node_deals['path'].value=="") &&(node_destinationstring_select.innerHTML!=""))
{
this.SetPreviewLinks($('select_unknown_path').value);
}
if((node_deals['path'].value=="") ||(node_deals['path'].value=="0") ||(!APP.PRICE.DEALS.SSG.GetChangeFocus()))
{
$('js_dealform_submit_button').addClass('waiting');
}
if((node_deals['path'].value=="") ||(node_deals['path'].value=="0"))
{
COM.HTTP.Get("/trivago_rpc.php?type=dealform_hotel&action=get_dealform_unkown_path&q="+encodeURIComponent(node_deals['query_path_name'].value),{
"xml":true,
"onfinish":function(xml){
APP.PRICE.DEALS.GetResponse(xml);
}});
return false;
}
if(!APP.PRICE.DEALS.SSG.GetChangeFocus())
{
window.location.href=$("dealform_hotel").action+'?'+AjaxForm.get_formvars(APP.PRICE.DEALS.ONSCREEN.IsOpen() ? APP.PRICE.DEALS.ONSCREEN.getOnscreenForm():$("dealform_hotel"), false);
}
else
{
APP.PRICE.DEALS.SSG.ClearChangeFocus();
$('js_dealform_submit_button').delClass('waiting');
}
return false;
}
//
//
//
this.Init=function()
{
node_deals=$((APP.PRICE.DEALS.ONSCREEN.IsOpen() ? 'onscreen_':'')+'dealform_hotel');
node_unknown_path   =$('unknown_path');
node_destinationstring_select=$('destinationstring_select');
node_dealform_error =$('dealform_error');
}
//
//
//
this.SetPreviewLinks=function(path_and_item)
{
var tmp=path_and_item.split('|');
if(typeof tmp[1]=='undefined')
{
tmp[1]='';
}
this.SwitchItemPage(tmp[0], tmp[1]);
}
//
//
//
this.SetSelectField=function(node_selectfield)
{
this.SetPreviewLinks(node_selectfield.value);
for(var i=0, n=node_selectfield.length; i<n; i++)
{
if(node_selectfield.options[i].selected)
{
$('destinationstring').value=node_selectfield.options[i].text;
break;
}
}
node_selectfield.disappear();
}
//
//
//
this.Clear=function(attraction)
{
APP.PRICE.DEALS.Init();
node_deals.action='/region.php';
if((!APP.PRICE.DEALS.ROTATION.IsInit()) ||(APP.PRICE.DEALS.ROTATION.GetRotationStatus()))
{
node_deals['query_path_name'].value='';
APP.PRICE.DEALS.ROTATION.Stop();
}
else
{
APP.PRICE.DEALS.ROTATION.SetRotationStatus(false);
}
node_destinationstring_select.innerHTML='';
node_unknown_path.innerHTML='';
node_dealform_error.disappear();
this.SwitchItemPage('', '');
if(attraction)
{
node_deals['longitude'].value   ='';
node_deals['latitude'].value ='';
node_deals['geo_distance_item'].value ='';
node_deals['order_by'].value ='';
node_deals['geo_use_distance'].value='0';
node_deals['geo_distance_limit'].value='';
}
return true;
}
//
//
//
var node_unknown_path,
node_destinationstring_select,
node_dealform_error,
node_deals;
}
////////////////////
APP.PRICE.DEALS.ONSCREEN=new function()
{
this.IsOpen=function()
{
return IsOpen;
}
this.getOnscreenForm=function()
{
return COM.GET.First('form', Form);
}
//
// params is an object with the following vars
// - path  :path-id
// - item  :item-id
// - query   :displayed querystring
// - price   :price
// - order  :order
// - referer :dealform-referer
// - view_type  :view_type from the hotellist
// - include_all:include all hotels
// - onClose :onClose-event from the onscreen-window
//
this.Open=function(params)
{
ssg=$('destinationstring').value;
IsOpen=true;
APP.BCrumb.Hide();
//APP.PRICE.DEALS.ROTATION.Stop();
if(!Mod)
{
Mod=COM.Element(['div',{}, [
['br',{}],
['h2',{}, "Comparar preços de hotéis"],
['p',{}, "trivago procura pra você em segundos entre mais de 100 sites de reservas pelo melhor preço em mais de 400.000 hotéis. Digite o estado ou a região, a data e pronto! trivago lhe mostrará o melhor preço!"],
['br',{}],
['div',{'style':'width:300px;margin:0px auto;'}]
]]);
Form=$('check_hotel_prices');
Node=Form.cloneNode(true);
}
var elem=COM.GET.First('form', Form);
FormName=elem.getAttribute('id');
elem.setAttribute('name', 'onscreen_'+FormName);
elem.setAttribute('id',   'onscreen_'+FormName);
Form.parentNode.replaceChild(Node, Form);
Mod.lastChild.appendChild(Form);
APP.WIZ.Module(Mod, "alert module_dealform1");
APP.WIZ.Reg('onclose', function(){APP.PRICE.DEALS.ONSCREEN.Close();});
if(params.onClose)
{
onClose=params.onClose;
}
var hidden=COM.WIZARD.Hidden();
if(hidden.length)
{
for(var i=0; i!=3; ++i)
{
elems=(COM.GET.Array(COM.GET.All((['embed', 'object', 'select'])[i], Form, -1)));
for(var j=0, n=hidden[i].length; j!=n; ++j)
{
(elems.hasValue(hidden[i][j][0])) && hidden[i][j][0].setStyle('visibility', hidden[i][j][1]);
}
}
}
APP.PRICE.DEALS.SwitchItemPage((params.path ? params.path:''),(params.item ? params.item:''));
if(params.query)
{
$('destinationstring').value=params.query;
}
$('order_by').value   =params.order   ? params.order  :'';
$('dealform_price_max').value=params.price   ? params.price  :'';
$('dealform_referer').value=params.referer ? params.referer   :'';
$('dealform_view_type').value=params.view_type  ? params.view_type :'';
$('dealform_include_all').value=params.include_all   ? params.include_all  :'';
//fix ie6 render-bug with the calendar
if(Browser.IE6)
{
$('dealform_hotel_from_calendar_position').disappear();
$('dealform_hotel_to_calendar_position').disappear();
window.setTimeout(function(){
$('dealform_hotel_from_calendar_position').appear();
$('dealform_hotel_to_calendar_position').appear();
}, 50);
}
APP.PRICE.DEALS.SSG.Start();
return true;
}
//
//
//
this.Close=function()
{
APP.BCrumb.Hide();
Node.parentNode.replaceChild(Form, Node);
$('destinationstring').value=ssg;
var elem=COM.GET.First('form', Form);
elem.setAttribute('name', FormName);
elem.setAttribute('id',   FormName);
ssg='';
IsOpen=false;
if(onClose)
{
onClose();
}
//reset
$('order_by').value   ='';
$('dealform_price_max').value='';
$('dealform_referer').value='';
$('dealform_view_type').value='';
$('dealform_include_all').value='';
APP.PRICE.DEALS.SSG.Start();
return true;
}
//
// private vars
//
var Form=null,
Node=null,
Mod =null,
IsOpen=false,
ssg ='',
FormName=null,
onClose=null;
}
COM.Log('--- rotation ---');
//
// example searchstring - rotation
//
APP.ROTATION=function()
{
//
//
//
this.SetExampleStrings=function(arr)
{
rotation_strings=arr;
}
//
//
//
this.Start=function(node_id)
{
this.SetRotationStatus(true);
node=$(node_id);
if((node) &&(node.isElement()))
{
node.setClass('text destination lighttext');
this.setRandSearchstring(node.type =='text');
}
display_string='';
}
//
//
//
this.Stop=function()
{
this.SetRotationStatus(false);
if((!status) &&(node))
{
node.setClass('text destination');
}
}
//
//
//
this.getRandSearchstring=function()
{
if(rotation_strings.length > 0)
{
var index=rand(0, rotation_strings.length-1);
//is it the same string?
if(display_string ==rotation_strings[index])
{
index=((index>0) ? --index:++index);
}
return rotation_strings[index];
}
return '';
}
//
//
//
this.setRandSearchstring=function(text_node)
{
try
{
if((this.GetRotationStatus()) &&(rotation_strings))
{
if(text_node)
{
display_string=node.value=this.getRandSearchstring();
}
else
{
display_string=node.innerHTML=this.getRandSearchstring();
}
var me=this;
window.setTimeout(function(){me.setRandSearchstring(text_node);},(ttl*1000));
}
}
catch(e)
{
COM.Log('APP.ROTATION.setRandSearchstring() - exception with node.type');
}
}
//
//
//
this.GetRotationStatus=function()
{
return status;
}
//
//
//
this.SetRotationStatus=function(val)
{
status=val;
}
//
//
//
this.IsInit=function()
{
return(typeof status =='undefined') ? false:true;
}
//
//
//
this.SetDelay=function(delay)
{
ttl=delay;
}
//
// private vars
//
var rotation_strings,
status,
node=false,
ttl=3,
display_string; //delay in seconds
}
COM.Log('--- ssg ---');
//
//
//
APP.PRICE.DEALS.SSG=new function()
{
//
//
//
this.Start=function()
{
//init vars
_node_query   =$( 'destinationstring' );
_node_resultlist=$( 'destinationstring_sug' );
_node_path =$( 'destination_path' );
_highlight_value='';
_highlight_string ='';
_tp  =0;
_countRows =0;
_focus_changes=false;
this.SetSelectedIndex(0);
_node_query.addEvent('keydown', function(eve, tar){APP.PRICE.DEALS.SSG.KeyPressDown(eve);});
this.ClearResultList();
}
//
// return the key-code related by the browser
//
this.Getkeycode=function(e)
{
var doc=document;
if(doc.layers)
{
return e.which;
}
else if(doc.all)
{
return event.keyCode;
}
else if(doc.getElementById)
{
return e.keyCode;
}
else
{
return 0;
}
}
//
//
//
this.GetChangeFocus=function()
{
return _focus_changes;
}
//
//
//
this.ClearChangeFocus=function()
{
_focus_changes=false;
}
//
// related by the key, we must execute different code(handling for: escape, up/down array, return and different)
//
this.KeyPressDown=function(e)
{
var key_code=this.Getkeycode(e);
switch(key_code)
{
case 27: // escape
_highlight_string='';
_node_resultlist.style.display='none';
this.DisplayHideLists('');
this.AbortConnection();
break;
case 38: // up arrow
case 40: // down arrow
this.HandleMove((key_code ==38 ? 'up':'down'));
break;
case  9: // tab
case 13: // return
if(_highlight_string)
{
_node_query.value=_highlight_string;
_node_path.value =_highlight_value;
}
this.AbortConnection();
this.ClearResultList();
this.DisplayHideLists('');
break;
default:
_highlight_string='';
_highlight_value ='';
this.ClearChangeFocus();
this.StartTimer();
break;
}
}
//
//
//
this.AbortConnection=function()
{
if(_request)
{
_request.abort();
}
}
//
//
//
this.HandleMove=function(direction)
{
if((_countRows > 0) &&(!_node_resultlist.style.display=='inline'))
{
_node_resultlist.style.display='inline';
return;
}
if(_countRows > 1){
_focus_changes=true;
}
var selected_index=this.GetSelectedIndex();
if((direction =='down') &&(this.GetSelectedIndex()!=_countRows - 1))
{
this.SetSelectedIndex( ++selected_index );
}
else if((direction =='up') &&(this.GetSelectedIndex()!=0))
{
this.SetSelectedIndex( --selected_index );
}
this.HighlightRow();
}
//
//
//
this.HighlightRow=function()
{
var li=COM.GET.All('li', _node_resultlist),
node;
for(var i=0, n=li.length; i<n; i++)
{
if(i ==this.GetSelectedIndex())
{
node=li[i];
node.className ='highlight';
_highlight_value =node.getAttribute('value');
_highlight_string=node.innerHTML;
}
else
{
li[i].className=(i%2==0) ? 'blue':'';
}
}
}
//
//
//
this.GetQuery=function()
{
var basicmatch=/[a-z0-9]/i;
if((_node_query.value.length ==0) ||(!basicmatch.test(_node_query.value)))
{
this.ClearResultList();
return '';
}
return(_node_resultlist.currentQuery=_node_query.value);
}
//
//
//
this.MouseClick=function(query, id)
{
_node_query.value=query.replace(/\'/, "'");
_node_path.value =id;
this.ClearResultList();
this.DisplayHideLists('');
}
//
//
//
this.MouseOut=function()
{
this.SetSelectedIndex(-1);
this.HighlightRow();
}
//
//
//
this.MouseOver=function(i)
{
this.SetSelectedIndex(i);
this.HighlightRow();
}
//
//
//
this.ClearResultList=function()
{
_node_resultlist.style.display='none';
_node_resultlist.innerHTML='';
_countRows=0;
this.SetSelectedIndex(0);
}
//
//
//
this.AppendRow=function(query, c, sel)
{
if(!_node_resultlist)
{
return;
}
this.SetSelectedIndex(0);
var css_class=(sel==0 ? 'highlight':((sel%2==0) ? "blue":""))
_node_resultlist.innerHTML +='<li class="'+css_class+'" value="'+c+'" '
+ ' onmouseover="APP.PRICE.DEALS.SSG.MouseOver('+_countRows+');"'
+ ' onmouseout="APP.PRICE.DEALS.SSG.MouseOut();"'
+ ' onclick="APP.PRICE.DEALS.SSG.MouseClick(\''+query.replace(/'/, "\\\'")+'\', '+c +');">'+query+'</li>';
++_countRows;
}
//
//
//
this.StartRequest=function()
{
var q;
if((q=this.GetQuery())!='')
{
this.AbortConnection();
_request=new COM.HTTP.Request('GET', '/trivago_rpc.php?v=v4_07_4h_bt0&action=ssg&type=5&order=weight_hotel&path='+APP.Retrieve('CURRENTPATH')+'&q='+encodeURIComponent( q ),{
'onfinish':function(str){
APP.PRICE.EvalCode("APP.PRICE.DEALS.SSG.GetResponse("+str+");");
}});
_request.start();
}
else
{
this.ClearResultList();
this.DisplayHideLists('');
}
}
//
//
//
this.GetResponse=function(_data)
{
if(this.GetQuery() =='')
{
this.DisplayHideLists('');
return;
}
this.DisplayHideLists('none');
this.ClearResultList();
if((_data) &&(_data.length > 0))
{
// display dropdown-list
_node_resultlist.style.display='block';
// fill the dropdown-list
if((_data.length > 1) ||(_node_query.value.length!=_data[0][0].length))
{
for(var i=0; i < _data.length; i++)
{
this.AppendRow(_data[i][0], _data[i][1], i);
}
_highlight_string=_data[0][0];
_highlight_value =_data[0][1];
}
else
{
// automatically selection
_node_resultlist.value=_data[0][1];
//if((_node_query.value.length ==_data[0][0].length) && (_node_query.value.toLowerCase() ==_data[0][0].toLowerCase()))
if(_node_query.value.length ==_data[0][0].length)
{
_node_query.value =_data[0][0];
_node_path.value  =_data[0][1];
_node_resultlist.style.display='none';
}
else
{
this.HighlightRow();
}
_highlight_value=_node_resultlist.value;
this.ClearResultList();
this.DisplayHideLists('');
}
}
else
{
this.DisplayHideLists(''); //empty list
}
}
//
//
//
this.StartTimer=function()
{
if(_tp > 0)
{
this.ResetTimer();
}
_tp=window.setTimeout(function(){APP.PRICE.DEALS.SSG.StartRequest();}, 100);
}
//
//
//
this.ResetTimer=function()
{
if(_tp > 0)
{
window.clearTimeout(_tp);
}
_tp=0;
}
//
//
//
this.SetSelectedIndex=function(val)
{
_selectedIndex=val;
}
//
//
//
this.GetSelectedIndex=function()
{
return _selectedIndex;
}
//
//
//
this.DisplayHideLists=function(val)
{
if(val =='none')
{
(typeof APP_DEALS_SLIDER!='undefined') && APP_DEALS_SLIDER.Hide();
$('dealform_hotel_from_calendar').disappear();
$('dealform_hotel_to_calendar').disappear();
}
else
{
(typeof APP_DEALS_SLIDER!='undefined') && APP_DEALS_SLIDER.Display();
$('dealform_hotel_from_calendar').appear();
$('dealform_hotel_to_calendar').appear();
}
APP.PRICE.DEALS.HideError();
DisplayHideLists(val);
}
//
// private vars
//
var _highlight_string,
_tp,
_node_query,
_node_resultlist,
_node_path,
_countRows,
_selectedIndex,
_focus_changes,
_highlight_value,
_request;
}
COM.Log('--- toplistfeature ---');
//
//
//
APP.TopListFeature=new function()
{
//
//
//
this.Init=function()
{
var nodes=$('home_toplistfeature').getElementsByTagName('a'),
id;
for(var i=0, n=nodes.length; i<n; i++)
{
id=$(nodes[i]).getAttribute('id');
if(id && id.substring(0, 5) =='path_')
{
nodes[i].addEvent('click', "APP.TopListFeature.Set('"+id+"');return false;");
}
}
}
//
//
//
this.EventMoreClick=function(node1, node_hidden_list)
{
$(node1).disappear();
$(node_hidden_list).appear();
}
//
//
//
this.Set=function(node)
{
var path=$(node).getAttribute('id').split('_');
APP.PRICE.DEALS.SwitchItemPage(path[1], 0);
$('destinationstring').value=$(node).getAttribute('name');
$(node).href="#";
APP.PRICE.DEALS.OnSubmit();
return false;
}
}
APP.LB=new function()
{
//
//
//
this.Set=function(atag, url)
{
var uri;
if(!(uri=this.base64_decode(url)))
{
//error situation go to the homepage
uri='/';
}
if(atag.getAttribute('target')=='_blank')
{
window.open(uri);
}
else
{
window.location.href=uri;
}
},
//
//
//
this.base64_decode=function(string)
{
var retval='';
if(string)
{
var b64='ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=';
var code1, code2, code3;
var h1, h2, h3, h4;
var bits;
var i=0;
var arr_i=0,
arr=[];
string +='';
do
{
h1=b64.indexOf(string.charAt(i++));
h2=b64.indexOf(string.charAt(i++));
h3=b64.indexOf(string.charAt(i++));
h4=b64.indexOf(string.charAt(i++));
bits=h1<<18 | h2<<12 | h3<<6 | h4;
code1=bits>>16 & 0xff;
code2=bits>>8  & 0xff;
code3=bits  & 0xff;
if(h3 ==64)
{
arr[arr_i++]=String.fromCharCode(code1);
}
else if(h4 ==64)
{
arr[arr_i++]=String.fromCharCode(code1, code2);
}
else
{
arr[arr_i++]=String.fromCharCode(code1, code2, code3);
}
}
while(i < string.length);
retval=arr.join('');
}
return retval;
}
}
COM.Log('--- SimilarItems ---');
//
//
//
APP.SIMILAR_ITEMS=new function()
{
//
//
//
this.Left=function(node_id, count_items)
{
var pos=$(node_id).style.left.split('px').join('');
var width=$($(node_id).getElementsByTagName('div')[0]).getWidth()+10;
if((count_items-3)*width > pos*-1)
{
$('js_similar_item_right').className='button button_right_active';
$('js_similar_item_left').className='button button_left_active';
$(node_id).style.left=(pos - width)+'px';
pos=(pos - width);
}
if((count_items-3)*width <=pos*-1)
{
$('js_similar_item_right').setClass('button button_right_inactive');
}
}
//
//
//
this.Right=function(node_id)
{
var pos=$(node_id).style.left.split('px').join('');
var width=$($(node_id).getElementsByTagName('div')[0]).getWidth()+10;
if(pos < 0)
{
$('js_similar_item_left').className='button button_left_active';
$('js_similar_item_right').className='button button_right_active';
$(node_id).style.left=(pos -(-1*width))+'px';
pos=(pos -(-1*width));
}
if(pos >=0)
{
$('js_similar_item_left').className='button button_left_inactive';
}
}
}
//
//  show or hide all critical elements.
//  todo: create more flexible functionallity
//
function DisplayHideLists(StyleType, id){
if(!id)
id=document;
else
id=$( id );
setHideLists(id, "select", StyleType);
if(Browser.IE){
setHideLists(id, "embed",  StyleType);
setHideLists(id, "object", StyleType);
}
}
function setHideLists(id, tagName, StyleType){
var a=id.getElementsByTagName(tagName);
for(var i=0; i<a.length; i++){
a[i].style.display=StyleType;
}
}
function CheckLen(Target,Tlength){
Tlength=Tlength || 250;
var StrLen=Target.value.length;
if(StrLen ==1 && Target.value.substring(0,1) ==" "){
Target.value="";
StrLen=0;
}
if(StrLen > Tlength){
Target.value=Target.value.substring(0,Tlength);
CharsLeft=0;
}
else CharsLeft=Tlength - StrLen;
$('counter_'+Target.getAttribute('name')).value=CharsLeft;
}
function radioValue(rObj){
for(var i=0; i<rObj.length; i++){
if(rObj[i].checked){
return rObj[i].value;
}
}
return false;
}
function showHelpBox(code, r){
r=(r)? '&r='+r:'';
COM.HTTP.Get("/trivago_rpc.php?help=true&action=dialog_box&code="+code+r,{"onfinish":function(r){APP.WIZ.Module(r, "question");}});
}
function CheckLoginForm()
{
var node_js_login_usrname=$("js_login_usrname");
var node_js_login_pass =$("js_login_pass");
if((node_js_login_usrname) &&(node_js_login_usrname.value!=''))
node_js_login_usrname.className='text usrname2';
if((node_js_login_pass) &&(node_js_login_pass.value!=''))
node_js_login_pass.className='text pwd2';
}
function showQuickPollResult(quickpoll_id, selection)
{
// load the data-string from the server
COM.HTTP.Get(self.servername+'/trivago_rpc.php?&action=getquickpollresult&quickpoll='+quickpoll_id+'&selection='+selection,{"onfinish":function(str){$('quickpoll_form').innerHTML=str;}});
}
window.addEvent("load", "if(self.PageType=='static') manipulateLinks();");
function changeDestination(id)
{
var objekt=$('destination');
if(objekt.options[ objekt.selectedIndex ].getAttribute('mylink')){
var sub_url=$("searchstring") ? "&q="+encodeURIComponent($("searchstring").value):"";
window.location.href=(objekt.options[ objekt.selectedIndex ].getAttribute('mylink')+sub_url);
}
}
function displayInputButton(object_id)
{
var node_object_id=$(object_id).parentNode;
node_object_id.className +=" button_waiting";
}
function displayLinkButton(object_id)
{
var node_object_id=$(object_id).parentNode;
node_object_id.className +=" button_waiting";
}
function url_replace(url)
{
window.location.href=url;
return false;
}
function displayAlternativePartners(partners)
{
var popupStatus=true;
for(var i=0; i<partners.length; i++){
if(!displayAlternativePartner(i, partners.length, partners[i])){
popupStatus=false;
}
}
window.focus();
if(!popupStatus){
new APP.Bubble('hotellistitems', 1, '/trivago_rpc.php?action=get_popup_blocker');
}
}
function displayAlternativePartner(no, arr_length, partner)
{
var screenWidth, screenHeight;
if(screen.availWidth){
screenWidth =screen.availWidth;
screenHeight=screen.availHeight;
}else{
screenWidth =1024;
screenHeight=768;
}
var popupWidth=Math.floor(screenWidth/arr_length);
try{
var posLeft=(popupWidth *(no))+10;
var popupArgs="width="+popupWidth+",height="+screenHeight+",left="+posLeft+",top=0,menubar=1,location=1,resizable=1,scrollbars=1,toolbar=1";
popup=window.open(partner.url, "popup"+partner.id, popupArgs);
if(popup)
popup.blur();
return popup;
}catch(status_ignore){
return false;
}
}
function getElD(i,o)
{
var node;
try
{
if(i && o &&(node=i.getElementsByTagName(o)) &&(node[0]) &&(node[0].lastChild))
{
return node[0].lastChild.data;
}
}
catch(er){ }
return "";
}
function LatestActions(){
this.run(0);
}
LatestActions.prototype ={
run:function(request_no){
COM.HTTP.Get("/trivago_rpc.php?action=get_last_actions&request_no="+(request_no? request_no: '0'),
{
"xml":1,
"onfinish":function(xml){
$('last_action_ani').className="hide_img";
if(getElD(xml, "details")){
$("module_body_last_actions").innerHTML=getElD(xml, "details");
}
var request_no=getElD(xml, 'request_no');
window.setTimeout("$('last_action_ani').className=''; LatestActions.run( "+request_no+");", getElD(xml, "delay"));
}
});
}
}
function rand(min, max)
{
return Math.round( Math.random()*max+min);
}
APP.RUN.Call("trivago.base");
