
var RB;
if(!RB) { 
  RB = {
    __name: 'RB',
    __version : '1.0',
    __info: 'Global methods, classes and objects for rezeptebuch',
    
    options: {},
    
    init: function(options) {
      this.options = options;
      // init some functions here
      RB.Modal.init();
    },
    
    /**
      * A Method to attach classes to a namespace
      */
    defineClass : function(data) {
        // Extract the fields we'll use from the argument object.
        // Set up default values.
        var classname = data.name;
        var superclass = data.extend || Object;
        var constructor = data.construct || function() {};
        var methods = data.methods || {};
        var statics = data.statics || {};
        var borrows;
        var provides;
    
        // Borrows may be a single constructor or an array of them.
        if (!data.borrows) borrows = [];
        else if (data.borrows instanceof Array) borrows = data.borrows;
        else borrows = [ data.borrows ];
    
        // Ditto for the provides property.
        if (!data.provides) provides = [];
        else if (data.provides instanceof Array) provides = data.provides;
        else provides = [ data.provides ];
    
        // Create the object that will become the prototype for our class.
        var proto = new superclass();
    
        // Delete any noninherited properties of this new prototype object.
        for(var p in proto) {
            if (proto.hasOwnProperty(p)) delete proto[p];  
        }
    
        // Borrow methods from "mixin" classes by copying to our prototype.
        for(var i = 0; i < borrows.length; i++) {
            var c = data.borrows[i];
            borrows[i] = c;
            // Copy method properties from prototype of c to our prototype
            for(var p in c.prototype) {
                if (typeof c.prototype[p] != "function") continue;
                proto[p] = c.prototype[p];
            }
        }
    
        // Copy instance methods to the prototype object
        // This may overwrite methods of the mixin classes
        for(var p in methods) proto[p] = methods[p];
    
        // Set up the reserved "constructor", "superclass", and "classname"
        // properties of the prototype.
        proto.constructor = constructor;
        proto.superclass = superclass;
        // classname is set only if a name was actually specified.
        if (classname) proto.classname = classname;
    
        // Verify that our prototype provides all of the methods it is supposed to.
        for(var i = 0; i < provides.length; i++) {  // for each class
            var c = provides[i];
            for(var p in c.prototype) {   // for each property
                if (typeof c.prototype[p] != "function") continue;  // methods only
                if (p == "constructor" || p == "superclass") continue;
                // Check that we have a method with the same name and that
                // it has the same number of declared arguments.  If so, move on
                if (p in proto &&
                    typeof proto[p] == "function" &&
                    proto[p].length == c.prototype[p].length) continue;
                // Otherwise, throw an exception
                throw new Error("Class " + classname + " does not provide method "+
                                c.classname + "." + p);
            }
        }
    
        // Associate the prototype object with the constructor function
        constructor.prototype = proto;
    
        // Copy static properties to the constructor
        for(var p in statics) constructor[p] = data.statics[p];
    
        // Finally, return the constructor function
        return constructor;
    }
  };
}

  /** Classes
    * Create new Classes like this:
    * to create an object use "new RB.Class.FooClass()"
   
  FooClass: RB.defineClass({
    extend: BarClass,
    
    construct: function() {
      // construct
    },
    methods: {
      // Object variables
      _foo: null,
      _bar: null,
      _baz: null,
      
      // Methods
      fooBaz: function(str) {
        return 'bar';
      }
    }
  })
  */



RB.Browser = {
  
  isIe: function() {
    return $.browser.msie;
  },
  
  isIe6: function() {
    return $.browser.msie && $.browser.version.substr(0,1) == 6;
  },
  
  isIe7: function() {
  },
  
  isMozilla: function() {
    return $.browser.mozilla;  
  },
  
  isSafari: function() {
    return $.browser.safari;
  },
  
  getScrollX: function() {
    return this.getScrollXY()[0];
  },
  
  getScrollY: function() {
    return this.getScrollXY()[1];
  },
  
  getScrollXY: function() {
    var scrOfX = 0, scrOfY = 0;
    if( typeof( window.pageYOffset ) == 'number' ) {
      //Netscape compliant
      scrOfY = window.pageYOffset;
      scrOfX = window.pageXOffset;
    } else if( document.body && ( document.body.scrollLeft || document.body.scrollTop ) ) {
      //DOM compliant
      scrOfY = document.body.scrollTop;
      scrOfX = document.body.scrollLeft;
    } else if( document.documentElement && ( document.documentElement.scrollLeft || document.documentElement.scrollTop ) ) {
      //IE6 standards compliant mode
      scrOfY = document.documentElement.scrollTop;
      scrOfX = document.documentElement.scrollLeft;
    }
    return [ scrOfX, scrOfY ];
  }
};



RB.Util = {
  merge: function(obj1, obj2) {
    var merged = {};
    for (var i in obj1) {
      merged[i] = obj1[i];
    }
    // Overwrite attributes of obj1 with obj2
    for (var i in obj2) {
      merged[i] = obj2[i];
    }
    return merged;
  },
  
  // get the content of an iframe (cross browser compatible)
  getIframeDocument: function(jqIframe) {
    return jqIframe.attr('contentWindow') != undefined ? jqIframe.attr('contentWindow').document : jqIframe.attr('document');
  }
};





RB.Debug = {
  debugging: true,
  _cmd: function(command, args) {
    if(this.debugging && $.browser.mozilla == true) {
      var strEval = '';
      for(var i = 0; i < args.length; i++) {
        if(i != 0) {
          strEval += ', ';
        }
        strEval += 'args['+i+']';
      }
      try {
        eval("console." + command + "(" +  strEval +");");
      }
      catch(e) {
        // no console found (no firebug installed?)
      }
    }
  },
  log: function() {
    this._cmd('log', arguments);
  },
  info: function() {
    this._cmd('info', arguments);
  },
  warn: function() {
    this._cmd('warn', arguments);
  },
  error: function() {
    this._cmd('error', arguments);
  },
  dir: function(obj) {
    if(this.debugging && $.browser.mozilla == true) {
      console.dir(obj);
    }
  }
};





RB.Ajax = {

  jsonDefaults: {
    dataType: 'json'
  },
  
  json: function(url, callbackSuccess, callbackError, options) {
    options = RB.Util.merge(this.jsonDefaults, options);
    options.url = url;
    options.success = function(data, textStatus) {
      if(data && data.status && data.status == 'success') {
        callbackSuccess(data);
      }
      else {
        callbackError(data);
      }
    };
    options.error = function (XMLHttpRequest, textStatus, errorThrown) {
      RB.Debug.error("Json error: " + textStatus + ", url: "+ url);
      if(callbackError) {
        callbackError({message: "Unerwarteter Fehler"});
      }
    };
    
    return $.ajax(options);
  },
  
  HtmlUpdater: RB.defineClass({
    
    construct: function(jqContainer) {
      this.jqContainer = jqContainer;
    },
    methods: {
      // Object variables
      jqContainer: null,
      isLoading: false,
      
      // Methods
      register: function(jqElement, strUrl, strEvent) {
        strEvent = strEvent || 'click';
        jqElement.bind(strEvent, this, function(e) {
          e.data.update(strUrl);
        });
      },
      
      update: function(strUrl, withoutContainer, callbackFunction) {
        if(this.isLoading) {
          return;
        }
        this.isLoading = true;
        
        var _this = this;
        RB.Ajax.json(strUrl, function(json) {
          _this.isLoading = false;
          _this.updateContent(json, withoutContainer);
          if(callbackFunction) {
            callbackFunction();
          }
        }, function(json) {
          RB.Modal.open(json.message);
        });
      },
      
      updateContent: function(data, withoutContainer) {
        if(withoutContainer) {
          var content = $(data.content).html();
        }
        else {
          var content = data.content;
        }
        this.jqContainer.html(content);
      }
    }
  }),

  
  Form:  RB.defineClass({
    construct: function(jqForm, callbackSuccess, callbackError) {
      this.jqForm = jqForm;
      this.callbackSuccess = callbackSuccess;
      this.callbackError = callbackError;
      
      var _this = this;
      jqForm.bind("submit", function() { 
        _this.submit();
        return false; 
      });
    },
    methods: {
      // Object variables
      jqForm: null,
      
      // Methods
      submit: function() {
        var _this = this;
        RB.Ajax.json(this.jqForm.attr('action'), function(json) {
          _this.callbackSuccess(json);
        }, function(json) {
          if(_this.callbackError) {
            _this.callbackError(json);
          }
        }, {type: "POST", data: this.jqForm.serialize() });
      }
    }
  }),
  
  };


RB.Form  = {
SiteLoader:  RB.defineClass({
	construct:function(a){
		this.strBaseUrl=a;
		this.boxes=new Array()
	},
	methods:{
		strBaseUrl:null,boxes:null,addFilter:function(a){
			this.boxes.push(a);
			a.bind("change",this,function(b){
				b.data.getUrl()
			}
	)},
	getUrl:function(){
	var b="";
	for(var a in this.boxes){
		b+="/"+$(this.boxes[a]).attr("value")
	}
	window.location.href=this.strBaseUrl+b
	}
	}
	})

};


RB.Modal = {
  // html ids for the layer
  _idModal: 'modal-layer',
  _idModalContent: 'modal-layer-content',
  _jqModal: null,
  _jqModalContent: null,
  
  _jqBox: null,
  _jqBoxContent: null,
  _jqBoxClose: null,
  
  _isLoading: false,
  _isOpen: false,
  // last opened url
  _url: null,
  // last loaded jsonObj
  _jsonObj: null,
  
  /**
    * init
    */
  init: function() {
    this._jqModal = $('<div id="' + this._idModal + '"></div>');
    this._jqModal.css({
      'position': 'absolute',
      'z-index': '1000',
      'width': '100%',
      'height': '100%',
      'opacity': '0.5',
      '-moz-opacity': '0.5',
      'background-color': '#000',
      'top': '0',
      'left': '0',
      'display': 'none'
    });
    
    this._jqModalContent = $('<div id="' + this._idModalContent + '"></div>');
    this._jqModalContent.css({
      'position': 'absolute',
      'z-index': '1010',
      'background-color': 'transparent',
      'top': '0',
      'left': '0',
      'display': 'none',
      'padding': '10px'
    });
    
    $('body').append(this._jqModal).append(this._jqModalContent);
    
    $(window).bind('resize', this, function(e) { e.data._centerContent(); });
    
    this._jqBox = jQuery('<div class="cf modal-box"><span onclick="RB.Modal.close()" class="close no-text clickable">Fenster schließen</span><div id="modal-box-content"></div></div>');
    this._jqBoxContent = this._jqBox.find('#modal-box-content');
    this._jqBoxClose = this._jqBox.find('.close');
  },
  
  /**
    * Load content for the modal layer via ajax
    */
  loadUrl: function(strUrl, withBox, forceReload) {
    if(!strUrl) {
      RB.Debug.error('No URL defined for modal layer.');
      return;
    }
    if(withBox == undefined) {
      withBox = true;
    }
    if(forceReload == undefined) {
      forceReload = true;
    }
    
    // if the url has been loaded and no reload forced, display cached jsonObj
    if(forceReload == false && strUrl == this._url && this._jsonObj !== null) {
      this.open(this._jsonObj.html, withBox);
    }
    else {
      var _this = this;
      
      this.setLoading('lade Inhalt...');
      
      RB.Ajax.json(strUrl, function(data){
        _this._loadingComplete(data, withBox);
      }, function(data) { 
        _this.close();
        RB.Debug.error('Failed to load url "' + strUrl + '" for modal layer.');
      });
    }
    
    // set the url
    this._url = strUrl;
    
    return false;
  },
  
  setLoading: function(label) {
      this.open('<div class="loader">'+label+'</div>');
      this._isLoading = true;
  },
  
  _loadingComplete: function(jsonObj, withBox) {
    this._isLoading = false;
    this._jsonObj = jsonObj;
    this.open(jsonObj.content, withBox);
  },
  
  /**
    * Open a modal layer with specific html
    */
  open: function(html, withBox) {
    if(withBox == undefined) {
      withBox = true;
    }
    
    this._isOpen = true;
    
    if(!html) {
      this.close();
      return;
    }
    
    // hide all selects for IE6
    if(RB.Browser.isIe6()) {
      $('select').css('visibility', 'hidden');
    }
    // Hide <objects> like flash
    $('object').css('visibility', 'hidden');
    
    
    
    this._jqModal.fadeIn('normal');
    this._jqModalContent.show();
    
    
    // wrap with box
    if(withBox) {
      this._jqModalContent.html(this._jqBox);
      this._jqBoxContent.html(html);
    }
    else {
      this._jqModalContent.html(html);
    }
    
    // center after content has been set
    this._centerContent();
  },
  
  /**
    * Opens an modal layer as an confirm window
    * callback is called with parameter true if confirmed
    */
  openConfirm: function(strText, callbackFunc) {
    
    var jqContent = $('<div><h3>Bitte bestätigen</h3><p>'+strText+'</p></div>');

    var jqButtons = $('<div class="clearfix "></div>');
    
    var jqOk = $('<input type="submit" class="button small float-right ml10" value="bestätigen" />');
    jqOk.bind('click',this,function(e) { callbackFunc(); e.data.close(); });
    
    var jqCancel = $('<input type="button" class="button small light float-right" value="abbrechen" />');
    jqCancel.bind('click',this,function(e) { e.data.close(); });
    
    jqContent.append(jqButtons.append(jqOk).append(jqCancel));
    
    this.open(jqContent);
  },
  
  /**
    * Closes the modal layer
    */
  close: function() {
    this._isOpen = false;
    this._isLoading = false;
    this._jqModal.fadeOut("small");
    this._jqModalContent.hide();
    
    // show all hidden selects for IE6
    if(RB.Browser.isIe6()) {
      $('select').css('visibility', 'visible');
    }
    $('object').css('visibility', 'visible');
  },
  
  /**
    * Center the content to window
    */
  _centerContent: function() {
    if(!this._isOpen){
      return;
    }
    // reposiotionize the content
    var left = parseInt(($(window).width() - this._jqModalContent.width()) / 2);
    var top = parseInt(($(window).height() - this._jqModalContent.height()) / 2) + RB.Browser.getScrollY();
    
    // dont allow negativ values
    if(left < 0) {left = 0;}
    if(top < 0) {top = 0;}
    
    this._jqModalContent.css({left: left , top: top });
    
    // refresh size for modal layer
    this._jqModal.css({width: $(document).width(), height: $(document).height()});
  }
};


RB.Rating = RB.defineClass({

    construct: function(attributes) {
      this.attributes = attributes;
      
      this._jqList = new Array();
      var _this = this;
      
      this.attributes.jqElement.find('li').each( function(i) {
        _this._jqList.push($(this));
        $(this).bind('mouseover', _this, function(e) {
          // mouse over the #i element
          e.data.setRating(i);
        });
        $(this).bind('click', _this, function(e) {
          // mouse over the #i element
          e.data.clickRating(i);
        });
      });
      
      this.attributes.jqElement.bind('mouseout', _this, function(e) {
        // mouse over the #i element
        e.data.resetRating();
      });
    },
    
    methods: {
      // Object variables
      attributes: null,
      _jqList: null,
      
      // Methods
      
      // set the rating
      setRating: function(index) {
        for(var i in this._jqList) {
          if(i <= index) {
            this._jqList[i].addClass('hover');
          }
          else {
            this._jqList[i].removeClass('hover');
          }
        }
      },
      
      // when a rating has been clicked, make the ajax call
      clickRating: function(index) {
        var _this = this;
        RB.Ajax.json(this.attributes.requestUrl, function(json){
          _this.updateContainer(json);
        }, function(json) { 
          RB.Debug.error('Failed to load url "' + _this.attributes.requestUrl + '" for rating.');
        }, {data: {value: index+1}});
      },
      
      // update the outer html container
      updateContainer: function(jsonObj) {
        this.attributes.jqContainer.html(jsonObj.content);
      },
      
      // sets the rating back to the current rating
      resetRating: function() {
        this.setRating(this.attributes.currentRating);
        for(var i in this._jqList) {
         this._jqList[i].removeClass('hover');
        }
      }
    }
});








RB.ViewHelper = {
  // all view controllers
  _controllers: {},
  
  // register a single view controller
  set: function(strIndex, objController) {
    this._controllers[strIndex] = objController;
    // call the init function if existing
    if(objController.init) {
      objController.init();
    }
    return objController;
  },
  
  // get a registered viewController
  get: function(strIndex) {
    if(!this._controllers[strIndex]) {
      RB.Debug.error('No viewController set with index "' + strIndex + '"');
      return null;
    }
    return this._controllers[strIndex];
  }
};