/*
  Class: Mogul
  
  Global container for everything.
*/
/*jslint browser: true, newcap: false, white: false, onevar: false, plusplus: false, eqeqeq: false, nomen: false */
/*globals document, Event */
var Mogul, $, YAHOO, $$, $A, Ajax, Effect;

if (undefined === Mogul) {
	var Mogul = {};
}


/*
  Function: namespace
  
  Creates a namespace if it doesn't exist (ripped from YUI ;)
  
  Example:
  
  | Mogul.namespace('util.tools') 
  
    Results in namespace Mogul.util.tools.
    Don't use reserved words in namespaces.
*/
Mogul.namespace = function() {
    var a=arguments, o=null, i, j, d;
    for (i=0; i<a.length; i=i+1) {
        d=(""+a[i]).split(".");
        o=Mogul;
        // Moutique is implied, so it is ignored if it is included
        for (j=(d[0] == "Mogul") ? 1 : 0; j<d.length; j=j+1) {
            o[d[j]]=o[d[j]] || {};
            o=o[d[j]];
        }
    }
    return o;
};

Mogul.namespace('core');

/*
  Section: Mogul.core
*/
/*
  Class: Mogul.core.Userswitch

  The userswitch object handles the user switching operations in the header.
*/
(function() {

	var originalValue = "", 
		myData,
		myDataSource,
		myAutoComp;

	Mogul.core.Userswitch = {

    /*
      Function: init
      
      Initializes the userswitch module.
    */
		init: function(userList) {
			// auto-complete for the quick user change
			myData = {"result": userList};
			myDataSource = new YAHOO.util.LocalDataSource(myData); 
			myDataSource.responseType = YAHOO.util.LocalDataSource.TYPE_JSON;
			myDataSource.responseSchema = {
				resultsList: "result",
				fields: ["value", "name"] // the first element must be the key to which search queries are compared
			};
			myDataSource.queryMatchContains = true; 

			myAutoComp = new YAHOO.widget.AutoComplete("user_quickset", "quickset_result", myDataSource); 
			myAutoComp.allowBrowserAutocomplete = false;
			myAutoComp.autoHighlight = false;
			myAutoComp.itemSelectEvent.subscribe(this.onItemSelect);
			myAutoComp.minQueryLength = 2; 
			myAutoComp.textboxBlurEvent.subscribe(this.closeQuickChange);
			myAutoComp.textboxFocusEvent.subscribe(this.onFocusEvent, myAutoComp);
			myAutoComp.useShadow = false; 
      // myAutoComp.alwaysShowContainer = true;
      myAutoComp.maxResultsDisplayed = 200;
			$('cancelQuickChange').observe('click', this.toggleQuickChange);
			$('switchQuickChange').observe('click', this.toggleQuickChangeType);
		},

		/**
		* Event handler for when the user selects a row. We check if the user selected the 
		* lookup service and act accordingly. 
		* http://developer.yahoo.com/yui/docs/YAHOO.widget.AutoComplete.html#event_itemSelectEvent
		*/
		onItemSelect: function (type, args) {
			Mogul.setTargetUser(args[2][1]);
		},

		onFocusEvent: function(e){
			originalValue = this.getInputEl().value;
			this.getInputEl().value = "";
		},

		toggleQuickChange: function(useSelectElement) {
			if (undefined === useSelectElement) {useSelectElement = false;}
			$('quick_change_trigger').toggle();
			$('quickchange_selectors').toggle();
			if(!$('quickchange_selectors').visible()) {
				myAutoComp.getInputEl().value = originalValue;
			}
			if(useSelectElement) {
				$('quickchange_autocomplete').hide();
				$('quickchange_select').show();
			}
		},

		closeQuickChange: function() {
			$('quick_change_trigger').show();
			$('quickchange_selectors').hide();
			myAutoComp.getInputEl().value = originalValue;
		},

		toggleQuickChangeType: function() {
			$('quickchange_autocomplete').toggle();
			$('quickchange_select').toggle();
		}

	};

}());

(function() {
	var element, openCategory;
	
	Mogul.core.ApplicationMenu = {
		init: function(footerElement) {
			element = $(footerElement);
			element.select('img.appMenuButton').each(function(btn) {
				btn.observe('click', this.toggleMenu.bindAsEventListener(this, btn));
				btn.observe('mouseover', this.onMouseOver.bindAsEventListener(this, btn));
				btn.observe('mouseout', this.onMouseOut.bindAsEventListener(this, btn));
			}.bind(this));
			Mogul.util.Lightbox.closedByBodyEvent.subscribe(this.onClosedByBodyEvent, this);
		}, 
		
		toggleMenu: function(e) {
			var btn = arguments[1],
				category = btn.id.sub(/appMenuButton_/, '');
				
			this.dimButtons();
			if(Mogul.util.Lightbox.getActiveLightbox('context') && openCategory == category) {
				Mogul.log('closing application menu');
				openCategory = null;
				Mogul.util.Lightbox.hideActiveLightbox('context');
			} else {
				Mogul.log('opening application menu ' + category);
				openCategory = category;
				Mogul.util.Lightbox.context(btn, '/index/lightbox_footermenu/'+category, {
					overlayCorner: 'bl', 
					contextCorner: 'tl', 
					center: true
				}, {
					width: '',
					className: 'appMenu',
					closeOnOutsideClick: true
				});
				this.highlightButton(btn);
			}
		},
		
		onClosedByBodyEvent: function(type, args, me) {
			me.dimButtons();
		},
		
		dimButtons: function() {
			element.select('img.appMenuButton.highlighted').each(this.dimButton);
		},
		
		dimButton: function(btn) {
			btn.removeClassName('highlighted');
			btn.src = btn.src.sub(/01/, "02");
		},
		
		highlightButton: function(btn) {
			btn.addClassName('highlighted');
			btn.src = btn.src.sub(/02/, "01");
		},
		
		onMouseOver: function() {
			var btn = arguments[1];
			btn.src = btn.src.sub(/02/, "01");
		},
		
		onMouseOut: function () {
			var btn = arguments[1];
			if(!btn.hasClassName('highlighted')) {
				btn.src = btn.src.sub(/01/, "02");
			}
		}
	};
}());

/**
* Notes object
* Contains functions for showing, hiding the notes field
*/
(function() {

	var cancel,
		element,
		height,
		trigger,
		user;

	Mogul.core.Notes = {
		init: function(el, target_user, embedded) {
			if (undefined === el || undefined === target_user) { return; }
			if (undefined === embedded) { embedded = false; }
			
			if (element) {
				trigger.stopObserving();
				cancel.stopObserving();
				element.update('');
			}
			
			element = $(el);
			user = parseInt(target_user);
			
			new Ajax.Updater(element, '/tools/notes/'+user+'?type=invoice&fixed='+embedded, {
				onComplete: function(o) {
					cancel = element.select('.cancel')[0];
					trigger = element.select('.tab')[0];

					trigger.observe('click', this.toggleDrawer.bindAsEventListener(this));
					cancel.observe('click',	this.toggleDrawer.bindAsEventListener(this));

					this.position(embedded);

				}.bind(this)
			});

		},

		position: function(embedded) {
			if(embedded) {return;}
			height = element.getHeight() - trigger.getHeight();
			element.setStyle({top: document.viewport.getHeight() - trigger.getHeight() - 42 + "px"});
		},

		toggleDrawer: function() {
			if(element.hasClassName('visible')) {
				new Effect.Move(element, {y: height+18, mode: 'relative', duration: 0.5});
				element.removeClassName('visible');
			} else {
				new Effect.Move(element, {y: -height-18, mode: 'relative', duration: 0.5});
				element.addClassName('visible');
			}
		}
	};
}());

/**
* Clock object for displaying and running the clock in the template header
*/
Mogul.core.clock = function(elementId, first) {
	if(typeof elementId == 'undefined' || !$(elementId)) {
		return;
	}
	if(typeof first == 'undefined') {
		first = true;
	}

	// if we want server time, make ruby print the current time (Time.now)
	// in the clock element's title attribute before calling this function
	var lastTime = $(elementId).readAttribute('title'),

	// start off with a fresh local date
		now = new Date(),
		nowSecs = now.getSeconds();

	if(lastTime !== null && lastTime !== "") {
		// try to parse the date in the title
		var parsedDate = Date.parse(lastTime);
		if (!isNaN(parsedDate)) {
			now.setTime(parsedDate);
		}
		// since the read date was last minute, we increment by one
		now.setMinutes(now.getMinutes() + 1);
		// make seconds match the local clock (makes minutes change simultaneously ;)
		now.setSeconds(nowSecs);
	} 

	// since js doesn't have a strftime function, we have to format it ourselves :((
	var year = now.getFullYear(),
		month = now.getMonth() + 1,
		day = now.getDate(),
		hour = now.getHours().toPaddedString(2),
		minute = now.getMinutes().toPaddedString(2),
		seconds = now.getSeconds(),
		// time the next clockupdate to when the minute changes
		timeout = 60000 - seconds * 1000,
		t = setTimeout("Mogul.core.clock('" + elementId + "', false)", timeout); 

	$(elementId).update([day,month,year].join(".") + " " + [hour,minute].join(":"));
	$(elementId).writeAttribute('title', now.toString());

	if (!first) {
		new Ajax.Request('/index/check_session_expiration', {
			onSuccess: function(o) {
				if (o.responseText.startsWith('false')) {
					window.location.href = '/welcome/logout?reason=expire';
				} /* else {
					var timeleft = parseInt(o.responseText.match(/\d+/)[0], 10);
					if(timeleft < 5) {
						// $('session_warning').update('Sisäänkirjautumisesi vanhentuu ' + timeleft + ' minuutin kuluttua. ').show();
					} else {
						// $('session_warning').hide();
					}
				} */
			}
		});
	}
};	

/**
* Unified error/info/warning nofifier
* The error element is included in the template header and thus visible
* on every page. All errors should 
*/
(function(){
	var visibletime = 3, 					// setting
		element, 										// errorelement (the one dropping down)
		contentElement,	  					// where the content should be put
		t, 													// timer
		timeleft = visibletime, 		// countdown counter
		closebutton, 								// button for manual closing
		countdownSpan,							// span for countdown and closebutton
		autoCloseText = "Suljetaan #{timeleft} sekunnin kuluttua.",
		visible = false;
	
	Mogul.core.Notify = {
		init: function(el, contentEl) {
			element = $(el);
			if(contentEl === undefined) {
				contentElement = element.childElements()[0];
			} else {
				contentElement = $(contentEl);
			}
			element.observe('mouseover', function(event){
				clearTimeout(t);
				countdownSpan.update("Sulje: ");
				countdownSpan.insert({'bottom': closebutton});
			});

			closebutton = new Element('img', {'src': '/moutique/skins/fonecta/images/icons/close.png'});
			closebutton.setStyle({verticalAlign: 'middle', width:'10px', height: '10px', cursor: 'pointer'});
			closebutton.observe('click', this.hide);

			countdownSpan = new Element('div',{'id':'errorCountDown'}).update(autoCloseText.interpolate({timeleft: timeleft}));
			countdownSpan.setStyle({marginRight: '10px', height: '12px', marginTop: '5px', fontWeight: 'normal', color:'#666666', fontSize:'9px', textAlign: 'right'});
		},
		
		// function for counting down to auto-close
		countDown: function() {
			if(timeleft == 1) {
				clearTimeout(t);
				this.hide();
			} else {
				timeleft -= 1;
				countdownSpan.update(autoCloseText.interpolate({timeleft: timeleft}));
				t = setTimeout(this.countDown.bind(this),1000);
			}
		},
		
		show: function(message) {
			if(!element) {
				return;
			}
			// check if the error element is visible (i.e. a previous error has just been shown)
			if(!visible) {
				visible = true;
				contentElement.update(message);
				contentElement.insert({'bottom': countdownSpan});
				timeleft = visibletime;
				countdownSpan.update(autoCloseText.interpolate({timeleft: timeleft}));

				// scroll window to top so we can see the popup
				//window.scrollTo(0,0);

				// show the container
				Effect.SlideDown(element, {duration: 0.3});
				t = setTimeout(this.countDown.bind(this),1000);
			} else {
				// append new error message
				countdownSpan.insert({'before': "<br/>" + message});
				// reset countdown
				timeleft = visibletime;
				countdownSpan.update(autoCloseText.interpolate({timeleft: timeleft}));
			}
		},

		error: function(message) {
			// set a classname
			element.removeClassName('warning');
			element.removeClassName('info');
			element.addClassName('error');
			// show message
			this.show(message);
		},

		warning: function(message) {
			// set a classname
			element.removeClassName('error');
			element.removeClassName('info');
			element.addClassName('warning');
			// show message
			this.show(message);
		},

		info: function(message) {
			// set a classname
			element.removeClassName('warning');
			element.removeClassName('error');
			element.addClassName('info');
			// show message
			this.show(message);
		},
		
		hide: function() {
			Effect.SlideUp(element, {duration: 0.3});
			visible = false;
		},
		
		inline: function(msg, el) {
			var warningMessage = new Element('span', { 'class': 'error_in_modal'}).update(msg);
			warningMessage.insert({ top: new Element('img', { 'src': '/moutique/skins/fonecta/images/icons/warning.png' }) });				
			Element.insert(el, {top: warningMessage });
			warningMessage.fade({duration:3});
		}
	};
	
})();
// transition/convenience method for backwards compatibility
Mogul.error = function(message) {
	Mogul.core.Notify.error(message);
};
Mogul.warning = function(message) {
	Mogul.core.Notify.warning(message);
}
Mogul.info = function(message) {
	Mogul.core.Notify.info(message);
}

Mogul.namespace('util');
(function() {
	
	var activeContextLightbox;
	var activeLightbox;
	var progressLightbox;
	var boundBodyHandler;
	var contextConfig;
	var defaultConfig = { 
		width : "750px",
		fixedcenter : true,
		visible : true,
		draggable: false,
		// we configure the lightbox to not be modal, but in the end of the init function, we
		// still show the semi transparent mask. This allows us to have a nested context lightbox on top of 
		// a "modal" lightbox. I.e. technically not modal, but with apparent modality. 
		modal: false,
		close: false,
		constraintoviewport : true,
		zIndex: 45,
		underlay: "none",
		postmethod: "async",
		hideaftersubmit: true,
		effect: {effect: YAHOO.widget.ContainerEffect.FADE, duration: 0.30},
		buttons : [ { text:'OK', handler: function() {this.submit();}, isDefault:true }, 
		                  { text:'Peruuta', handler: function() {this.cancel();}} ]
	};
	var node;
	var node2;
	var CBBEvent = new YAHOO.util.CustomEvent('closedByBody', Mogul.util.Lightbox);
	var rzEvent = new YAHOO.util.CustomEvent('resize', Mogul.util.Lightbox);
	
	Mogul.util.Lightbox = {
		closedByBodyEvent: CBBEvent,
		resizeEvent: rzEvent,
		
		// transitional hook for the active lightbox for backwards compatibility
		currentLightbox: activeLightbox,
		currentContextLightbox: activeContextLightbox,
		
		getActiveLightbox: function(type) {
			switch(type) {
				case 'modal':
				return activeLightbox;
				break;
				case 'context':
				return activeContextLightbox;
				break;
				case 'progress':
				return progressLightbox;
				break;
				default: 
				return null;
				break;
			}
		},
		
		progress: function(url, config) {
			new Ajax.Request(url, {
				onSuccess: function(response) {
					this.init('progress', response.responseText, config);
				}.bind(this)
			});
		},
		
		modal: function(url, config, callback, validation) {
			new Ajax.Request(url, { 
				onFailure: function() { alert('Yhteysvirhe. Lataa sivu uudestaan jatkaaksesi.'); },
				onSuccess: function(response) {
					this.init('modal', response.responseText, config, callback, validation);
				}.bind(this),
				onCreate: function() {
					Action.start();
				},
				onComplete: function() {
					Action.complete();
				}
			});
		},
		
		context: function(el, url, ctxConfig, config, callback, validation) {
			contextConfig = [$(el)];
			if (undefined === ctxConfig) {ctxConfig = {};}
			if (undefined === config) {config = {};}
			contextConfig.push(ctxConfig.overlayCorner || 'tl');
			contextConfig.push(ctxConfig.contextCorner || 'tr');
			contextConfig.push(['beforeShow', 'windowResize'].concat(ctxConfig.triggerEvents).compact().uniq());
			contextConfig.push(ctxConfig.xyOffset || [0,-20]);
			
			// put the center config into the class available centerToContextElement variable
			config.centerToContextElement = ctxConfig.center || false;
			
			Object.extend(config, {
				modal: false,
				fixedcenter: false,
				context: contextConfig,
				zIndex: 100,
				buttons: config.buttons || []
			});

			new Ajax.Request(url, { 
				onFailure: function() { 
					alert('Yhteysvirhe. Lataa sivu uudestaan jatkaaksesi.'); 
				},
				onSuccess: function(response) {
					this.init('context', response.responseText, config, callback, validation);
				}.bind(this)
			});
		},

		init: function(type, innerHTML, config, callback, validation) {
			Mogul.log('init lightbox ' + type);

			// destroy visible lightbox if such exists
			this.destroyLightbox('', type);

			// set element name 
			var elId;
			if(type == 'modal') {
				elId = 'lightbox';
			} else if(type == 'context') {
				elId = 'lightbox_context';
			} else if(type == 'progress') {
				elId = 'lightbox_progress';
			}

			node = $$('body')[0];
			node2 = $$('html')[0];
		
			var newConfig = Object.clone(defaultConfig);
			if(config) {
				Object.extend(newConfig, config);
			}

			// create an element for the lightbox
			var el = new Element('div', {id: elId}).update(innerHTML);
			if(config.className) {
				el.addClassName(config.className);
			}

			// create the dialog object
			var dialog = new YAHOO.widget.Dialog(el, newConfig);
			dialog.renderEvent.subscribe(this.fixFooter.bindAsEventListener(dialog));
			dialog.renderEvent.subscribe(this.showMask.bindAsEventListener(dialog));
			if(type == 'modal') {
        // dialog.renderEvent.subscribe(this.disableScrolling.bindAsEventListener(this));
        // dialog.hideEvent.subscribe(this.enableScrolling.bindAsEventListener(this));
  			dialog.hideEvent.subscribe(this.destroyLightbox.bindAsEventListener(this, type));
			}
			if(type == 'context') {
  			dialog.hideEvent.subscribe(this.destroyLightbox.bindAsEventListener(this, type));
				if(config.centerToContextElement) {
					dialog.renderEvent.subscribe(this.centerContextLightbox, this);
					YAHOO.widget.Overlay.windowResizeEvent.subscribe(this.centerContextLightbox, this);
				} else {
					dialog.renderEvent.subscribe(this.positionContextArrow, this);
					YAHOO.widget.Overlay.windowResizeEvent.subscribe(this.positionContextArrow, this);
				}
				if(config.closeOnOutsideClick) {
					boundBodyHandler = this.bodyClickHandler.bindAsEventListener(this, 'context');
					$$('body')[0].observe('click', boundBodyHandler);
				}
				
			}

			// we need this for safari and ie to evaluate all script blocks inside the html
			

			if(callback) {
				dialog.callback = callback;
			}

			if(typeof validation != 'function') {
				validation = function() { return true; };
			}
			dialog.validate = validation;

			// assign the lightbox to publicly available variable
			if(type == 'modal') {
				activeLightbox = dialog;
			} else if(type == 'context') {
				activeContextLightbox = dialog;
			} else if(type == 'progress') {
				progressLightbox = dialog;
			}

			// last, render the lightbox
			dialog.render(node);
		},
		
		// standard callback for evaluating javascript code from the controller (rjs)
		// pass the resultobject for the success callback directly to this method
		// example:
		// success: Mogul.util.Lightbox.evalCallback,
		// failure: function(o) { alert('Noo!'); }
		evalCallback: function(res) {
			// when we do a manual eval of javascript returned from the controller, 
			// IE7 doesn't like there to be newlines in wrong places in 
			// the evaluated string, so we remove them, then eval.
			eval(res.responseText.gsub("\n",""));
			Action.complete();
		},

		/** 
		* add corner divs to the footer
		* this can't be done in the innerHTML if we've got buttons in the footer
		* since the Dialog's button setup function overwrites the footer with it's own markup
		*/
		fixFooter: function(e) {
			if(this.buttonSpan) {
				Mogul.log('creating footer elements');
				var stretchRow = new Element('tr').insert(new Element('td').addClassName('stretch'));
				var smallRow = new Element('tr').insert(new Element('td').addClassName('small'));
		    var corner = new Element('table').addClassName('corner').insert(stretchRow).insert({bottom: smallRow});
				var secondCorner = Element.clone(corner, true);

				var bl = new Element('div').addClassName('bl');
				bl.insert(corner.addClassName('left'));

				var br = new Element('div').addClassName('br');
				br.insert(secondCorner.addClassName('right'));

				this.buttonSpan.insert({before: bl});
				this.buttonSpan.insert({after: br});
			}
		},
		
		showMask: function(e) {
			// hack to show the mask although the modal lightbox isn't configured to be modal
			this.buildMask();
			$('lightbox_mask').setStyle({'display': 'block'});
		},
		
		/**
		* disables scrolling of the browser window
		*/
		disableScrolling: function(e) {
			node.setStyle({overflow: 'hidden'});
			node2.setStyle({overflow: 'hidden'}); // ie
		},

		enableScrolling: function(e) {
			node.setStyle({overflow: 'auto'}); // re-enable scrolling
			node2.setStyle({overflow: 'auto'}); // re-enable scrolling in ie
		},
		
		/**
		* Kills the active lightbox (i.e. bypasses graceful fade and hide-events)
		*/
		destroyLightbox: function(e) {
			type = arguments[1];
		  if(type == 'progress') {
  		  return;
		  }
		  
			Mogul.log("destroying lightbox: " + type);
			if (type == 'context' && activeContextLightbox) {
				activeContextLightbox.destroy();
				activeContextLightbox = null;
			} else if (type == 'modal' && activeLightbox) {
				activeLightbox.destroy();
				activeLightbox = null;
			}
			// and unregister the bodyclick handler
			try {
				$$('body')[0].stopObserving('click', boundBodyHandler);
			} catch (e) {
				Mogul.log("Lightbox stop observing failed: " + e);
			}
			// and remove all listeners from our custom events
			rzEvent.unsubscribeAll();
			CBBEvent.unsubscribeAll();
		},

		/**
		* hides the current lightbox. 
		* this will also trigger the destroylightbox function
		*/
		hideActiveLightbox: function(type) {
			Mogul.log("gracefully hiding active lightbox " + type);
			if (type == 'context' && activeContextLightbox) {
				activeContextLightbox.cancel();
			} else if (type == 'modal' && activeLightbox) {
				activeLightbox.cancel();
			}
		},
		
		/**
		* runs when someone clicks the body element (i.e. the screen),
		* but only if the event was subscribed to in the init-phase
		*/
		bodyClickHandler: function(e) {
			// if we clicked inside the lightbox, don't close
			if(!e.findElement('div#lightbox_context') && !e.findElement('.contextLightboxTrigger')) {
				Mogul.log('body clicked, closing active lightbox ' + arguments[1]);
				// otherwise, close the context-lightbox
				this.hideActiveLightbox(arguments[1]);
				// fire our custom event that we closed a lightbox "automatically"
				CBBEvent.fire();
			}
		},
		
		positionContextArrow: function(event, obj, my){
			Mogul.log('positioning arrow');
			$$('.contextarrow').invoke('hide');
			var el = contextConfig[0];
			var oc = contextConfig[1];
			var cc = contextConfig[2];
			// four sides, four cases
			// first we calculate the "local offset", to ensure that the arrow
			// is at the center of the trigger element.
			// then we position the arrow according to the lightbox' position 
			// in relation to the element's position
			if(oc.include('l') && cc.include('r')) {
				var localOffset = parseInt((el.getHeight() - $$('.contextarrow.left')[0].getHeight()) / 2);
				var offset = el.viewportOffset().top - $('lightbox_context').viewportOffset().top + localOffset;
				$$('.contextarrow.left')[0].setStyle({top: offset+'px'}).show();
			} else if (oc.include('r') && cc.include('l')) {
				var localOffset = parseInt((el.getHeight() - $$('.contextarrow.right')[0].getHeight()) / 2);
				var offset = el.viewportOffset().top - $('lightbox_context').viewportOffset().top + localOffset;
				$$('.contextarrow.right')[0].setStyle({top: offset+'px'}).show();
			} else if (oc.include('b') && cc.include('t')) {
				var localOffset = parseInt((el.getHeight() - $$('.contextarrow.down')[0].getWidth()) / 2);
				var offset = el.viewportOffset().left - $('lightbox_context').viewportOffset().left + localOffset;
				$$('.contextarrow.down')[0].setStyle({left: offset+'px'}).show();
			} else if (oc.include('t') && cc.include('b')) {
				var localOffset = parseInt((el.getHeight() - $$('.contextarrow.up')[0].getWidth()) / 2);
				var offset = el.viewportOffset().left - $('lightbox_context').viewportOffset().left + localOffset;
				$$('.contextarrow.up')[0].setStyle({left: offset+'px'}).show();
			}
		}, 
		
		centerContextLightbox: function(event, obj, my) {
			Mogul.log('centering');
			var el = contextConfig[0];
			var oc = contextConfig[1];
			var cc = contextConfig[2];
			// two cases, either left/right or top/bottom
			if(oc.include('l') && cc.include('r') || oc.include('r') && cc.include('l')) {
				var offset = (el.getHeight() - $('lightbox_context').getHeight()) / 2;
				activeContextLightbox.align(oc, cc, [0,offset]);
			} else if (oc.include('b') && cc.include('t') || oc.include('t') && cc.include('b')) {
				var offset = (el.getWidth() - $('lightbox_context').getWidth()) / 2;
				my.getActiveLightbox('context').align(oc, cc, [offset,0]);
			}
			my.positionContextArrow().bind(my);
		},
		
		position: function() {
			if(type == 'context') {
				this.getActiveLightbox('context').align();
				if(config.centerToContextElement) {
					this.centerContextLightbox(null, this, this);
				} else {
					this.positionContextArrow(null, this, this);
				}
			} else if(type == 'modal') {
				this.getActiveLightbox('modal').align();
			}
		},
		
		resize: function(oConfig) {
			var config = $H({
				width: null, // for absolute width
				height: null, // for absolute height
				maximizeWidth: false, // rest is for relative size to viewport
				maximizeHeight: false,
				minimizeWidth: false,
				minimizeHeight: false,
				horizMargin: 0,
				vertMargin: 0,
				scrollEl: null
			}).merge(oConfig).toObject();

			// grab some viewport values
      var docHeight = document.viewport.getHeight();
			var docWidth = document.viewport.getWidth();

			var newWidth;
			var newHeight;

			// reset the height first, if we're about to change it
			if (config.width || config.maximizeWidth || config.minimizeWidth) {
  			this.getActiveLightbox('modal').cfg.setProperty('width', null);
			}
			// then set widths
			if(config.width) {
				newWidth = config.width + "px";
			} else if(config.maximizeWidth || docWidth < $('lightbox').getWidth()) {
				// we want to maximize the width, or the lightbox is bigger than the screen
				// clear any possible width style from the title 
				$$('#lightbox .hd .title')[0].setStyle({width: null});
				if(config.horizMargin < 20) {
					config.horizMargin = 20;
				}
				newWidth = (docWidth - config.horizMargin*2) + "px";
			} else if(config.minimizeWidth) {
				newWidth = "";
			}

			// reset the height first, if we're about to change it
			if (config.height || config.maximizeHeight || config.minimizeHeight) {
  			this.getActiveLightbox('modal').cfg.setProperty('height', null);
  			$('lightboxbody').setStyle({height: null});
			}
			// then set heights
			if(config.height) {
				newHeight = config.height + "px";
			} else if(config.maximizeHeight || docHeight < $('lightbox').getHeight()) {
				// we want to maximize or the lightbox is bigger than the screen
				if(config.vertMargin < 20) {
					config.vertMargin = 20;
				}
				newHeight = (docHeight - config.vertMargin*2) + "px";
			} else if(config.minimizeHeight) {
				// we want to minimize
				newHeight = "";
			}

			// set new widths
			if (typeof(newWidth) != 'undefined')
				this.getActiveLightbox('modal').cfg.setProperty('width', newWidth);
			if (typeof(newHeight) != 'undefined')
				this.getActiveLightbox('modal').cfg.setProperty('height', newHeight);

      $$('.lightboxScroller').invoke('removeClassName', 'lightboxScroller').invoke('setStyle', '{height: auto, overflowY: auto}');
			if(config.scrollEl) {
				var scrollEl = $(config.scrollEl);
				var offsetDiff = $('lightboxbody').viewportOffset()[1] - scrollEl.viewportOffset()[1];
				var h = $('lightboxbody').getHeight();
				var mb = 0;
				var mt = 0;
				if(scrollEl.getStyle('padding-bottom')) {
					mb = parseInt(scrollEl.getStyle('padding-bottom').match(/\d+/)[0]);
				}
				if(scrollEl.getStyle('padding-top')) {
					mt = parseInt(scrollEl.getStyle('padding-top').match(/\d+/)[0]);
				}
				var scrollHeight = h-mb-mt-1+offsetDiff;
				
				scrollEl.setStyle({
					height: scrollHeight + "px",
					overflowY: "auto"
				}).addClassName('lightboxScroller');
			}
			// finally re-align the lightbox 
			this.getActiveLightbox('modal').center();
			
			// and fire event 
			var eventObj = {};
			if(newWidth){
				eventObj.width = parseInt(newWidth.match(/\d+/)[0]);
			}
			if(newHeight){
				eventObj.height = parseInt(newHeight.match(/\d+/)[0]);
			}
			if(config.scrollEl) {
				eventObj.scrollHeight = $(config.scrollEl).getHeight();
			}
			rzEvent.fire(eventObj);
			
		}
	}
})();
	
Mogul.namespace('core');
(function() {
	
	Mogul.core.disableSubmit = function(element, text) {
	  element = $(element);
		element.addClassName('disabled');
		element.disabled = true;
		if(text !== undefined) {
			element.value = text;
		}
	}

	Mogul.core.enableSubmit = function(element, text) {
	  element = $(element);
		element.removeClassName('disabled');
		element.writeAttribute('disabled', null);
		if(text !== undefined) {
			element.value = text;
		}
	}
	
})();
// ========================================================
// =============================== PENDINGACTIONS COMPONENT
// ========================================================

if (typeof Action == 'undefined') {
	var Action = {};

	Action.pendingActions = 0;

  document.observe('dom:loaded', function() {
  	if (!Mogul.util.Lightbox.getActiveLightbox('progress')) {
  		// create lightbox, but leave it hidden
      // Mogul.util.Lightbox.progress('/index/lightbox_progress', {
      //   width: "220px", 
      //   zindex: 50,
      //   modal: true,
      //   underlay: "none",
      //   visible: false
      // }); 
  	}
  });
	
	Action.start = function() {
		Action.pendingActions ++;
		if (Action.pendingActions > 0 && Mogul.util.Lightbox.getActiveLightbox('progress')) {
			Mogul.util.Lightbox.getActiveLightbox('progress').show();
		}
	};
	Action.complete = function() {
		Action.pendingActions --;
		if (Action.pendingActions < 1 && Mogul.util.Lightbox.getActiveLightbox('progress')) {
			Mogul.util.Lightbox.getActiveLightbox('progress').hide();
		}
	};
}
	

// ========================================================
// ============================= MOUTIQUE COMMONS COMPONENT
// ========================================================

// colors used in charts
Mogul.chartColors = ["#053061","#2166ac","#4393c3","#92c5de","#d1e5f0","#fddbc7","#f4a582","#b2182b","#b2182b","#67001f"];

Mogul.loggingEnabled = false;

Mogul.log = function(object) {
	if(Mogul.loggingEnabled) {
	 	if(typeof console != "undefined") {
			if(typeof object == "string") {
				var d = new Date();
				object = d.getHours() + ":" + d.getMinutes() + ":" + d.getSeconds() + "." + d.getMilliseconds() + ": " + object;
			} 
			console.log(object);
		}

		if (typeof DP_Debug != 'undefined') {
			if(typeof object == "string") {
				DP_Debug.logger(object,'info');
			} else {
				DP_Debug.dump(object);
			}
			
		}
	}
};

Mogul.trim = function(string) {
	return string.replace(/^\s+|\s+$/g, '');
};

Mogul.isEmail = function(string, strict) {
	if (typeof strict == 'undefined') { strict = false; }
	if (strict) {
		return string.strip().match(/^[A-Za-z0-9._%+\-]+@[A-Za-z0-9.\-]+\.[A-Za-z]{2,4}$/);
	} else {
		return string.strip().match(/[A-Za-z0-9._%+\-]+@[A-Za-z0-9.\-]+\.[A-Za-z]{2,4}/);
	}
};

/**
* Makes a valid HTML id of a string by replacing chars
*/
Mogul.htmlId = function(string) {
	// this is the regex for a valid HTML id
	var htmlId = /^[a-zA-Z]+[a-zA-Z0-9_]*$/;
	
	// try to match the string
	if (string.match(htmlId) === null) {
		// if it didn't match, fix the string
		if (string.match(/^[a-zA-Z]/) ===  null) {
			// if the string didn't start with a-zA-Z, make it do so
			string = "js_" + string;
		}
		// then replace all illegal characters in the string
		string = string.replace(/[^a-zA-Z0-9_]/g, '_');
	}
	return string;
};

Mogul.updateStatistics = function(newWindow) {	
	var params = Form.serialize($('statistics_form'));
	params += "&timestamp=" + new Date().getTime();
	if (newWindow) {
		$('statistics_form').target = '_blank';
		$('statistics_form').submit();
		$('statistics_form').target = '';
	} else {
	 	Mogul.ajaxLoad('messaging_result', $('statistics_form').action, params);
	}
};

Mogul.updateTransactions = function() {	
	var params = Form.serialize($('transactions_form')) + "&" + Form.serialize($('transactions_sidebarform'));
	
	params += "&timestamp=" + new Date().getTime();
	Mogul.ajaxLoad('transactions_result', $('transactions_form').action, params);
};
	
Mogul.showStatistics = function(statsType, viewType) {
	$('fSort').value = '';
	$('fSortOrder').value = '';
	 
	$('fStatisticsType').value = statsType;
	if (viewType && $('viewType') !== undefined) {
		$('fViewType').value = viewType;
		if (viewType == 'chart') {
			$('viewType').selectedIndex = 1; 
		} else {
			$('viewType').selectedIndex = 0;
		}
	}
	Mogul.updateStatistics();
};

Mogul.sort = function(col) {
	if ($('fSort').value == col) {
		if ($('fSortOrder').value === '' || $('fSortOrder').value == 'DESC') {
			$('fSortOrder').value = 'ASC';
		} else {
			$('fSortOrder').value = 'DESC'; 	
		}
	} else {
		$('fSort').value = col;
		$('fSortOrder').value = '';
	} 
	Mogul.updateStatistics();
};

Mogul.setTargetUser = function(id) {
	new Ajax.Request('/index/target_user/' + id, { onComplete: function(req) { 
		if ($('fUserId')) {
			$('fUserId').value = req.responseText;
			Mogul.updateStatistics();
		} else {
			window.location.reload(); 
		}
	} } );
};
	
Mogul.getUserId = function() {
	return -1;
};

Mogul.getTargetUser = function() {
	return null;
}

Mogul.setLoading = function(container, verbose) {
	if ($('spinner') && $(container)) {
		var contentWidth = $(container).getWidth();
		var contentHeight = $(container).getHeight();
		var contentOffset = $(container).viewportOffset();
			
		// calculate spinner position. (this could maybe be done more intelligent...)
    var spinnerTopMarg = [contentHeight / 2 - 15, 0].max();

    // $(container).innerHTML = '';
    $('spinner').setStyle({
      left: contentOffset[0] + 'px',
      top: contentOffset[1] + 'px',
      width: contentWidth + 'px',
      height: contentHeight + 'px',
      backgroundColor: "#ffffff",
      textAlign: 'center'
    });
    $$('#spinner img')[0].setStyle({
      marginTop: spinnerTopMarg + 'px'
    });
    // $('spinner').style.top = spinnerTop + "px";
    // $('spinner').style.left = spinnerLeft + "px";
		$('spinner').show();
	}
};

Mogul.stopLoading = function() {
  $$('.spinnerContainer').invoke
	if ( $('spinner') ) {	
		$('spinner').hide();	
	}
};

Mogul.setLoadingText = function(container, message) {
	if($(container) && $(container).value) {
		// we can set some value of this element
		$(container).value = message;
	} else if ($(container) && $(container).innerHTML) {
		$(container).innerHTML = message;
	}
};
	    	
// copy items from source select box to destination select box
Mogul.copyOptions = function(src, dest) {
	for (i = src.length - 1; i>=0; i--) {
    	if (src.options[i].selected) {
		      try {
    			dest.add(src.options[i], null); // standards compliant; doesn't work in IE
  			  } catch(ex) {
			    dest.add(src.options[i]); // IE only
			  }
	    }
 	}
};
	
Mogul.toggleAll = function(elem, selected) {
	if (selected === undefined) { selected = true; }
	for (i = elem.length - 1; i>=0; i--) {
		elem.options[i].selected = selected;	
	}
};

Mogul.toggle = function(elem) {
    if (elem === undefined) { return; }
    if (elem.checked === undefined) { return; }
    if (elem.checked) {
		elem.checked = false; 
	} else {
    	elem.checked = true;	
	}
};	

Mogul.ajaxLoad = function(element, url, parameters) {
	if (!$(element)) {alert(element + " not found");}
	Mogul.setLoading(element);
	new Ajax.Updater(element, url, { 
		postBody: parameters, 
		evalScripts: true, 
		onCreate: function() { Mogul.setLoading(element); },
		onLoading: function() { Mogul.setLoading(element); },
		onInteractive: function() { Mogul.setLoading(element); },
		onLoaded: function() { Mogul.setLoading(element); },
		onFailure: function() { alert('Yhteysvirhe. Lataa sivu uudestaan jatkaaksesi.'); },
		onComplete: function() { Mogul.stopLoading(); }
	}); 
};

Mogul.ajaxRequest = function(url, parameters) {
	new Ajax.Request(url, { 
		postBody: parameters, 
		evalScripts: true, 
		onFailure: function() { alert('Yhteysvirhe. Lataa sivu uudestaan jatkaaksesi.'); }
	});
};
	
Mogul.createDatePicker = function(element, type) {
	var mPicker = new Ext.DatePicker();
	mPicker.type = type;
	
	var field = 'fStartTime';
	if (type == 'endTime') {
		field = 'fEndTime';
		mPicker.minDate = Date.parseDate($('fStartTime').value, 'Y-m-d H:i:s');
	} else {
		mPicker.maxDate = Date.parseDate($('fEndTime').value, 'Y-m-d H:i:s');
	}
	
	value = $(field).value;
	var d = Date.parseDate(value, 'Y-m-d H:i:s');
	
	var mPickDate = function(picker, date) {
		picker.hide();
		
		if (picker.type == 'endTime') {
			$('fEndTime').value = date.format('Y-m-d 23:59:59'); 
			$('endTime').innerHTML = date.format('Y-m-d');
		} else {
			$('fStartTime').value = date.format('Y-m-d 00:00:00');
			$('startTime').innerHTML = date.format('Y-m-d');
		}
		Mogul.updateStatistics();			
	};
	mPicker.setValue(d);
	mPicker.addListener("select", mPickDate);
	// $(element).style.left = '400px';	
	mPicker.render(element);		
};

Mogul.hideShow = function(toBeToggled) {
	if ($(toBeToggled)) {
		$(toBeToggled).toggle();	
	}
};

Mogul.revealElement = function(element, container) {
	if ($(element)) {
		if ($(container)) {
			var links = $(container).getElementsByTagName('select');
			for (var i = 0; i < links.length; i++) {
				links[i].style.display = 'none';
			}
		}
		$(element).toggle();
	}
};
	
Mogul.updateCount = function(textarea, element, limit, text) {
	var cnt = Mogul.smsLength($(textarea).value);	

	if (limit !== undefined && limit > 0) {
		while (cnt > limit) {
			$(textarea).value = $(textarea).value.substring(0, $(textarea).value.length-1);
			cnt = Mogul.smsLength($(textarea).value);
		}
	} 
	var smsCnt = (cnt > 160) ? Math.ceil(cnt / 153) : 1
	var currLimit = 160 + 153 * (smsCnt - 1)
	var txt = cnt + "/" + currLimit + " " + text;
	if (typeof limit == 'undefined' || !limit || limit > 160) {
		txt += " ("+smsCnt+" SMS)"
	}
	$(element).innerHTML = txt;
};

Mogul.smsLength = function(text) {
	var cnt = text.length;
	
	for (var i = 0; i < text.length; i++) {
		var c = text.charAt(i);
		if (c == '€' || c == '|' || c == '^' || c == '{' || c == '}' || c == '[' || c == ']'  || c == '~') {
			cnt++;
		}
	}
	if (text.match(/@ETUNIMI@/)) {
		cnt += 11; // just to be safe ("@ETUNIMI@".length == 9)
	}
	return cnt;
};

Mogul.formatPrice = function(price) {
	var delimiter = " ";
	var separator = ",";
	var currency = "&euro;";

	// if we got a number, make it into a string
	if (typeof price == "number") {
		price += "";
	}

	// round the number to two decimals
	price = (Math.round((price - 0) * 100) / 100) + "";
	
	var p = price.split('.',2);
	var d = isFinite(p[1]) ? p[1] : "00"; // if we don't have a decimal part, create one
	var i = parseInt(p[0].split(" ").join(""), 10); // remove spaces from integer part
	if (isNaN(i)) { 
		return price; 
	}
	var minus = '';
	if (i < 0) { 
		minus = '-'; 
	}
	i = Math.abs(i);
	var n = i.toString();
	var a = [];

	while(n.length > 3)	{
		var nn = n.substr(n.length-3);
		a.unshift(nn);
		n = n.substr(0,n.length-3);
	}

	if (n.length > 0) { 
		a.unshift(n); 
	}

	n = a.join(delimiter);

	while (d.length < 2) { 
		d = d + "0";
	}
	var amount = minus + n + separator + d + " " + currency; 

	return amount;
};

Mogul.formatAmount = function(amount) {
	var delimiter = " ";

	var n = amount.toString();
	var a = [];

	while(n.length > 3)	{
		var nn = n.substr(n.length-3);
		a.unshift(nn);
		n = n.substr(0,n.length-3);
	}

	if (n.length > 0) { 
		a.unshift(n); 
	}

	n = a.join(delimiter);

	return n;
};
	
Mogul.rgb2html = function(red, green, blue) {
    var decColor = red + 256 * green + 65536 * blue;
    return decColor.toString(16);
};
	
Mogul.removeClassFromLinks = function(element) {
	var e = $(element);
	var links = e.getElementsByTagName('a');
	for (var i = 0; i < links.length; i++) {
		links[i].className = '';
	}
};

Mogul.popup = function(options) {
    this.options = {
      url: '#',
      width: 600,
      height: 500,
      name:"_blank",
      location:"no",
      menubar:"no",
      toolbar:"no",
      status:"yes",
      scrollbars:"yes",
      resizable:"yes",
      left:"",
      top:"",
      normal:false
    };
    Object.extend(this.options, options || {});

    if (this.options.normal){
        this.options.menubar = "yes";
        this.options.status = "yes";
        this.options.toolbar = "yes";
        this.options.location = "yes";
    }

    this.options.width = this.options.width < screen.availWidth?this.options.width:screen.availWidth;
    this.options.height=this.options.height < screen.availHeight?this.options.height:screen.availHeight;
    var openoptions = 'width='+this.options.width+',height='+this.options.height+',location='+this.options.location+',menubar='+this.options.menubar+',toolbar='+this.options.toolbar+',scrollbars='+this.options.scrollbars+',resizable='+this.options.resizable+',status='+this.options.status;
    if (this.options.top!=="") {openoptions+=",top="+this.options.top;}
    if (this.options.left!==""){openoptions+=",left="+this.options.left;}
    return window.open(this.options.url, this.options.name,openoptions );
};

// sidebar stuff

Mogul.selectSidebarItem = function(elem, url) {
	var groupList = $('report_nav');
	if (groupList) {
		var links = groupList.getElementsByTagName('a');
		for (var i = 0; i < links.length; i++) {
			links[i].className = '';
		}
		if ($(elem)) {
			$(elem).className = 'current';
		}
	}
	if (url) {
		Mogul.ajaxLoad('result', url);
	}
};

Mogul.moveBetweenLists = function(source, dest) {
	if (!$(source)) {return;}
	if (!$(dest)) {return;}
	
	
	for (var i = 0; i < source.options.length; i++) {
		var opt = source.options[i];
		if (!opt.selected) {continue;}
		try {
    		dest.add(opt,null); // standards compliant
    	} catch(ex) {
    		dest.add(opt); // IE only
    	}
    	source.remove(i);
    }
 	source.blur();
 	dest.selectedIndex = 0; 	
};

Mogul.getTreeValue = function(tree, nodes) {
	checkedNodes = [];
	if (tree) {
		nodes = nodes || tree.getRoot().children;       
        for(var i=0, l=nodes.length; i<l; i=i+1) {
            var n = nodes[i];
            if (n.checkState == 2) {
                if (n.getValue() !== null) {
                	checkedNodes.push(n.getValue());
				}
			}
            if (n.hasChildren()) {
              checkedNodes = checkedNodes.concat(Mogul.getTreeValue(tree, n.children));            
			}
        }
	}
    return checkedNodes;    
};

Mogul.generatePassword = function() {
	var chars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890";
	var pass = "";
	for(x=0;x<8;x++) {
	    var i = Math.floor(Math.random() * 62);
	    pass += chars.charAt(i);
	}
	return pass;
};

setFlashDimensions = function(height, width) {	
	if ($('flash_content')) {
		$('flash_content').style.height = (height + 100) + 'px';
		$('flash_content').style.width = (width + 5) + 'px';				
	}
};

Mogul.onSidebarHide = new YAHOO.util.CustomEvent("onSidebarHide");
Mogul.onSidebarShow = new YAHOO.util.CustomEvent("onSidebarShow");

Mogul.toggleSidebar = function(action) {
	var col3 = $$('.col3')[0];
	var col1 = $$('.col1')[0];
	var sidebar = $('sidebar');
	var colmid = $$('.colmid')[0];
	var my = this;

	function hideSidebar() {
		new Effect.Parallel([
			new Effect.Morph(col1,   		{sync: true, style: 'margin-right: 15px'}),
			new Effect.Move(sidebar, 		{sync: true, x: 215})
			], {
				afterFinish: function() {
					col3.hide();
					$$('#hideshowlink span').invoke('toggle');
					my.onSidebarHide.fire({});
				},
				beforeStart: function() {
					colmid.setStyle({borderRight: '0 none'});
				}
		});
		sidebar.addClassName('hidden');
	}

	function showSidebar() {
		new Effect.Parallel([
			new Effect.Morph(col1,   {sync: true, style: 'margin-right: 215px'}),
			new Effect.Move(sidebar, {sync: true, x: -215})
			], {
				beforeStart: function() {
					col3.show();
				},
				afterFinish: function() {
					$$('#hideshowlink span').invoke('toggle');
					colmid.setStyle({borderRight: '1px dashed #898989'});
					my.onSidebarShow.fire({});
					sidebar.setStyle({position: '', top: '', left: ''});
				}
		});
		sidebar.removeClassName('hidden');
	}

	switch (action) {
		case "show":
			if(sidebar.hasClassName('hidden')) {
				showSidebar();
			}
			break;
		case "hide":
			if(!sidebar.hasClassName('hidden')) {
				hideSidebar();
			}
			break;
		default:
			if (!sidebar.hasClassName('hidden')) {
				hideSidebar();
			} else {
				showSidebar();
			}
			break;
	}
	
};

Mogul.highlight = function(id) {
  	var realId = "menu_"+id;
	if(!$(realId)) {
		return;
	}
  	if (!$(realId).ancestors()[0].visible()) {
    	$(realId).ancestors()[0].show();
  	}
  	$(realId).addClassName("active");
};

Mogul.toggleSubmenu = function(parentId, childCount) {
  var dur = 0.1*childCount + 0.3/childCount; // makes the menu appear to open equally fast
  if ($("submenu_"+parentId).visible()) {
    Effect.BlindUp("submenu_"+parentId, {duration: dur});
  } else {
    Effect.BlindDown("submenu_"+parentId, {duration: dur});
  }
};


Mogul.validateKeyword = function(params) {
	// var allowedChars = [
	// '\u0040', // NUL @
	//        '\u00A3', // STX Â£
	//        '\u0024', // SOT $
	//        '\u00A5', // ETX Â¥
	//        '\u00E8', // EOT Ã¨
	//        '\u00E9', // ENQ Ã©
	//        '\u00F9', // ACK Ã¹
	//        '\u00EC', // 7 07 BEL Ã¬
	//        '\u00F2', // 8 08 BS Ã²
	//        '\u00C7', // 9 09 HT Ã‡
	//        0x0A, // 10 0A LF LF
	//        '\u00D8', // 11 0B VT Ã˜
	//        '\u00F8', // 12 0C FF Ã¸
	//        0x0D, // 13 0D CR CR
	//        '\u00C5', // 14 0E SO Ã…
	//        '\u00E5', // 15 0F SI Ã¥
	//        '\u0394', // 16 10 DLE ? (U+0394)
	//        '_', // 17 11 DC1 DC1 (tai _)
	//        '\u03A6', // 18 12 DC2 ? (U+03A6)
	//        '\u0393', // 19 13 DC3 ? (U+0393)
	//        '\u039B', //20 14 DC4 ? (U+039B)
	//        '\u03A9', // 21 15 NAK ? (U+03A9)
	//        '\u03A0', // 22 16 SYN ? (U+03A0)
	//        '\u03A8', // 23 17 ETB ? (U+03A8)
	//        '\u03A3', // 24 18 CAN ? (U+03A3)
	//        '\u0398', // 25 19 EM ? (U+0398)
	//        '\u039E', // 26 1A SUB ? (U+039E)
	//        0x1B, // 27 1B ESC ESC
	//        '\u00C6', // 28 1C FS Ã†
	//        '\u00E6', // 29 1D GS Ã¦
	//        '\u00DF', // 30 1E RS ÃŸ
	//        '\u00C9', // 31 1F US Ã‰
	//        ' ', // 32 20     (space)
	//        '!', // 33 21 ! !
	//        '"', // 34 22 " "
	//        '#', // 35 23 # #
	//        '\u00A4', // 36 24 $ â‚¬
	//        '%', // 37 25 % %
	//        '&', // 38 26 & &
	//        '\'', // 39 27 ' '
	//        '(', // 40 28 ( (
	//        ')', // 41 29 ) )
	//        '*', // 42 2A * *
	//        '+', // 43 2B + +
	//        ',', // 44 2C , ,
	//        '-', // 45 2D - -
	//        '.', // 46 2E . .
	//        '/', // 47 2F / /
	//        '0', // 48 30 0 0
	//        '1', // 49 31 1 1
	//        '2', // 50 32 2 2
	//        '3', // 51 33 3 3
	//        '4', // 52 34 4 4
	//        '5', // 53 35 5 5
	//        '6', // 54 36 6 6
	//        '7', // 55 37 7 7
	//        '8', // 56 38 8 8
	//        '9', // 57 39 9 9
	//        ':', // 58 3A : :
	//        ';', // 59 3B ; ;
	//        '<', // 60 3C < <
	//        '=', // 61 3D = =
	//        '>', // 62 3E > >
	//        '?', // 63 3F ? ?
	//        '\u00A1', // 64 40 @ Â¡
	//        'A', // 65 41 A A
	//        'B', // 66 42 B B
	//        'C', // 67 43 C C
	//        'D', // 68 44 D D
	//        'E', // 69 45 E E
	//        'F', // 70 46 F F
	//        'G', // 71 47 G G
	//        'H', // 72 48 H H
	//        'I', // 73 49 I I
	//        'J', // 74 4A J J
	//        'K', // 75 4B K K
	//        'L', // 76 4C L L
	//        'M', // 77 4D M M
	//        'N', // 78 4E N N
	//        'O', // 79 4F O O
	//        'P', // 80 50 P P
	//        'Q', // 81 51 Q Q
	//        'R', // 82 52 R R
	//        'S', // 83 53 S S
	//        'T', // 84 54 T T
	//        'U', // 85 55 U U
	//        'V', // 86 56 V V
	//        'W', // 87 57 W W
	//        'X', // 88 58 X X
	//        'Y', // 89 59 Y Y
	//        'Z', // 90 5A Z Z
	//        '\u00C4', // 91 5B [ Ã„
	//        '\u00D6', // 92 5C \ Ã–
	//        '\u00D1', // 93 5D ] Ã‘
	//        '\u00DC', // 94 5E ^ 
	//        '\u00A7', // 95 5F _ Â§
	//        '\u00BF', // 96 60 ` Â¿
	//        'a', // 97 61 a a
	//        'b', // 98 62 b b
	//        'c', // 99 63 c c
	//        'd', // 100 64 d d
	//        'e', // 101 65 e e
	//        'f', // 102 66 f f
	//        'g', // 103 67 g g
	//        'h', // 104 68 h h
	//        'i', // 105 69 i i
	//        'j', // 106 6A j j
	//        'k', // 107 6B k k
	//        'l', // 108 6C l l
	//        'm', // 109 6D m m
	//        'n', // 110 6E n n
	//        'o', // 111 6F o o
	//        'p', // 112 70 p p
	//        'q', // 113 71 q q
	//        'r', // 114 72 r r
	//        's', // 115 73 s s
	//        't', // 116 74 t t
	//        'u', // 117 75 u u
	//        'v', // 118 76 v v
	//        'w', // 119 77 w w
	//        'x', // 120 78 x x
	//        'y', // 121 79 y y
	//        'z', // 122 7A z z
	//        '\u00E4', // 123 7B { Ã¤
	//        '\u00F6', // 124 7C | Ã¶
	//        '\u00F1', // 125 7D } Ã±
	//        '\u00FC', // 126 7E ~ Ã¼
	//        '\u00E0' // 127 7F DEL Ã
	// ]

	var okChars = /^[a-zA-Z0-9åäöÅÄÖ\-_ ]+$/
	var replaceChars = /[^a-zA-Z0-9åäöÅÄÖ\-_ ]/g
	// var okChars = /^[\u0040\u00A3\u0024\u00A5\u00E8\u00E9\u00F9\u00EC\u00F2\u00C70x0A\u00D8\u00F80x0D\u00C5\u00E5\u0394_\u03A6\u0393\u039B\u03A9\u03A0\u03A8\u03A3\u0398\u039E0x1B\u00C6\u00E6\u00DF\u00C9 !"#\u00A4%&\'()*+,-.\/0123456789:;<=>?\u00A1ABCDEFGHIJKLMNOPQRSTUVWXYZ\u00C4\u00D6\u00D1\u00DC\u00A7\u00BFabcdefghijklmnopqrstuvwxyz\u00E4\u00F6\u00F1\u00FC\u00E0]+$/
	// var replaceChars = /[^\u0040\u00A3\u0024\u00A5\u00E8\u00E9\u00F9\u00EC\u00F2\u00C70x0A\u00D8\u00F80x0D\u00C5\u00E5\u0394_\u03A6\u0393\u039B\u03A9\u03A0\u03A8\u03A3\u0398\u039E0x1B\u00C6\u00E6\u00DF\u00C9 !"#\u00A4%&\'()*+,-.\/0123456789:;<=>?\u00A1ABCDEFGHIJKLMNOPQRSTUVWXYZ\u00C4\u00D6\u00D1\u00DC\u00A7\u00BFabcdefghijklmnopqrstuvwxyz\u00E4\u00F6\u00F1\u00FC\u00E0]/g
	
	if(!params.keywordEl.value.match(okChars)) {
		params.keywordEl.value = params.keywordEl.value.replace(replaceChars, "");
		params.resultEl.update('Hakusana ei saa sisältää välilyöntejä eikä erikoismerkkejä mukaanlukien äkkösiä.');
	} else {
		params.resultEl.update('');
	}
}
// =======================================
// ================= Javascript extensions
// =======================================

Object.extend(Date.prototype, {
  strftime: function(format) {
    // var day = this.getUTCDay(), month = this.getUTCMonth();
    // var hours = this.getUTCHours(), minutes = this.getUTCMinutes();
    var day = this.getDay(), month = this.getMonth();
    var hours = this.getHours(), minutes = this.getMinutes();
    function pad(num) { return num.toPaddedString(2); };

    return format.gsub(/\%([aAbBcdDHiImMpSwyY])/, function(part) {
      switch(part[1]) {
        case 'a': return $w("Sun Mon Tue Wed Thu Fri Sat")[day]; break;
        case 'A': return $w("Sunday Monday Tuesday Wednesday Thursday Friday Saturday")[day]; break;
        case 'b': return $w("Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec")[month]; break;
        case 'B': return $w("January February March April May June July August September October November December")[month]; break;
        case 'c': return this.toString(); break;
        case 'd': return this.getDate(); break;
        // case 'd': return this.getUTCDate(); break;
        case 'D': return pad(this.getDate()); break;
        // case 'D': return pad(this.getUTCDate()); break;
        case 'H': return pad(hours); break;
        case 'i': return (hours === 12 || hours === 0) ? 12 : (hours + 12) % 12; break;
        case 'I': return pad((hours === 12 || hours === 0) ? 12 : (hours + 12) % 12); break;
        case 'm': return pad(month + 1); break;
        case 'M': return pad(minutes); break;
        case 'p': return hours > 11 ? 'PM' : 'AM'; break;
        case 'S': return pad(this.getUTCSeconds()); break;
        case 'w': return day; break;
        case 'y': return pad(this.getUTCFullYear() % 100); break;
        case 'Y': return this.getUTCFullYear().toString(); break;
      }
    }.bind(this));
  }
});

Date.prototype.getDOY = function() {
var onejan = new Date(this.getFullYear(),0,1);
return Math.ceil((this - onejan) / 86400000);
}
