function BookessBase()
{
	this.requirement = ['jquery', 'jquery.hashchange']; 

	
	var self = this;
	this.lastUrl = null;
	
    this.saveTemplates = function()
    {
        var $p, $c;

        for(var parent in this.DOMTemplates)
        {
            $p = $(parent);

            for(var child in this.DOMTemplates[parent])
            {                         
                $c = $(child, $p);

                this.DOMTemplates[parent][child].dom = $c.filter('.js-template:first').clone(true).removeClass('js-template');

                if($c.filter('.js-template').length > 0)
                {
                    $c.filter('.js-template').remove();
                }
            }
        }
    };

    this.isString = function(input)
    {
        return typeof(input) == 'string';
    };

    this.isObject = function(input)
    {
        return typeof(input) == 'object';
    };

    this.updateObject = function(parent, child, elm)
    {
        return this.getNewObject.apply(this, arguments);
    };

    this.getObjectData = function(parent, child, elm)
    {
        var template = this.DOMTemplates[parent][child];
        var data = {fields: {}, attributes: {}};

        var attr, $c, field;
        for(var f in template.fields)
        {
            if(this.isString(template.fields[f]))
            {
                field = template.fields[f];
                $c = $(child + '-' + field, elm);

                data.fields[field] = $c.html();

                if(template.attributes[field])
                {
                    for(attr in  template.attributes[field])
                    {
                        data.attributes[field] = {};
                        data.attributes[field][attr] = $c.attr(attr);
                    }
                }
            }
        }

        return data;
    };
    
    this.getNewObject = function(parent, child, elm)
    {
        var template = this.DOMTemplates[parent][child];
        var obj = this.isObject(elm) ?  $(elm) : $(template.dom).clone(true);
        var numArgs = this.isObject(elm) ? 2 : 1;

        if(arguments.length > numArgs)
        {
            var p, index, attr, i = 0, j=0, $c, field, k;
            for(var f in template.fields)
            {
                if(this.isString(template.fields[f]))
                {
                    field = template.fields[f];
                    $c = $(child + '-' + field, obj);
                    i++;

                    if($c.length < 1) continue;

                    if($c[0].tagName.toLowerCase() != 'img')
                    {
                        if(arguments[numArgs + i])
                        {
                            $c.html(arguments[numArgs + i]);
                        }
                        else if(template.defaults && template.defaults[field])
                        {
                            var def = template.defaults[field];

                            if( /^[.#]/.test(def))
                            {
                                $c.replaceWith($(def));
                            }
                            else
                            {
                                 $c.replaceWith(def);
                            }

                            continue;
                        }
                    }
                   
                    if(template.attributes && template.attributes[field])
                    {
                        for(p in  template.attributes[field])
                        {
                            attr = template.attributes[field][p];
                            index = template.fields[f].indexOf(attr);

                            if(index >= 0)
                            {
                                $c.attr(p, arguments[numArgs + index]);
                            }
                            else
                            {
                                j = 0;
                                for(k in template.fields)
                                {
                                    if(this.isString(template.fields[k]))
                                    {
                                        attr = attr.replace(new RegExp('{'+template.fields[k]+'}', 'gi'), arguments[numArgs + ++j]);
                                    }
                                }
                                
                                $c.attr(p, attr);
                            }
                            
                        }
                    }


                }
            }
        }

        return obj;
    };

    this.httpRequest = function(url, data, callback, type, dataType)
    {
        return $.ajax
        ({
            type: type || 'POST',
            data: data || {},
            dataType: dataType || 'json',
            url: url,
            success: callback /*function(response){ alert(response); }*/,
            error: function(XMLHttpRequest, textStatus, errorThrown)
            {
            	//alert(XMLHttpRequest.response);
            }
        });
    };

    this.cloneObject = function(o)
    {
        return eval(uneval(o));
    };

    this.cancelEvents = function(elm)
    {
        var $elm = $(elm);
        var events = $elm.clone(true).data('events');

        if(!events) return;

        $elm.data('storedEvents', events);
        $elm.unbind();
    };

    this.restoreEvents = function(elm)
    {
        var $elm = $(elm);        
        var events = $elm.data('storedEvents');
        
        if(!events) return;

        for (var type in events)
        {
            for (var handler in events[type])
            {
                $elm.bind(type, events[type][handler], events[type][handler].data);
            }
        }

        $elm.data('storedEvents', null);
    };

    this.formatArray = function(arr, template, data)
    {
        var from = arr.slice(0);

        for(var i=0; i<template.length; i++)
        {
            from.push(data[template[i]]);
        }

        return from;
    };
    
    this.checkUrlCall = function()
    {
    	var url = location.hash;

    	var params = [];
        var param = null;

        for(var i in self.urlCalls)
        {
            var regex = new RegExp(i, 'i');

            if(regex.test(url))
            {            	
                param = url.match(new RegExp('(' + i + '.+)', 'gi'));
                params = [];
                
                if(param)
                {
                	param = param[0];
	                param = param.replace(new RegExp('(' + i + ').', 'gi'), '$1-');
	                params = param.split('-');
	                params = params.slice(1, params.length);
                }
                
                self.urlCalls[i].apply(self, [url, params]);
                
                return;
            }
        }
    };

    this.initUrlCalls = function()
    {
    	$(window).bind('hashchange', self.checkUrlCall);
    	$(window).trigger('hashchange');
    };

    this.util = {};
    
    this.util.size = function(obj)
    {
        var len = 0;
        for (var k in obj) len++;

        return len;
    };
    
    this.util.arrayKeys = function(obj)
    {
    	var arr = [];
    	for(var k in obj) arr.push(k);
    	
    	return arr;
    };
    
    this.util.inArray = function(arr, needle)
    {
    	for(var k in arr) if(arr[k] == needle) return true;
    	
    	return false;
    };
    
    this.util.utf8 =
    {    		 
		// public method for url encoding
		encode : function (string) {
			string = string.replace(/\r\n/g,"\n");
			var utftext = "";
	 
			for (var n = 0; n < string.length; n++) {
	 
				var c = string.charCodeAt(n);
	 
				if (c < 128) {
					utftext += String.fromCharCode(c);
				}
				else if((c > 127) && (c < 2048)) {
					utftext += String.fromCharCode((c >> 6) | 192);
					utftext += String.fromCharCode((c & 63) | 128);
				}
				else {
					utftext += String.fromCharCode((c >> 12) | 224);
					utftext += String.fromCharCode(((c >> 6) & 63) | 128);
					utftext += String.fromCharCode((c & 63) | 128);
				}
	 
			}
	 
			return utftext;
		},
	 
		// public method for url decoding
		decode : function (utftext) {
			var string = "";
			var i = 0;
			var c = c1 = c2 = 0;
	 
			while ( i < utftext.length ) {
	 
				c = utftext.charCodeAt(i);
	 
				if (c < 128) {
					string += String.fromCharCode(c);
					i++;
				}
				else if((c > 191) && (c < 224)) {
					c2 = utftext.charCodeAt(i+1);
					string += String.fromCharCode(((c & 31) << 6) | (c2 & 63));
					i += 2;
				}
				else {
					c2 = utftext.charCodeAt(i+1);
					c3 = utftext.charCodeAt(i+2);
					string += String.fromCharCode(((c & 15) << 12) | ((c2 & 63) << 6) | (c3 & 63));
					i += 3;
				}
	 
			}
	 
			return string;
		}    	 
	};
};
