/*
Script: printmaster.js
  
Copyright:
  copyright (c) 2007 wollzelle GmbH, <http://www.wollzelle.com>

Group: printmaster
  collects generic widgets and functionality for the Printmaster application
*/

/*
Class: Sheets
  each time a request for a sheet completes, the Sheets object gets extended
  with all sheet specific properties it needs; extending happens in the response

Note:
  static class
*/

var Sheets = {};

/*
Class: Shelf
  collects functions for product container

Note:
  static class
*/

var Shelf = {
  
  /*
  Property: resize
    window.onresize callback which resizes the product shelf (div#container);
    checks for window dimensions, resizes the toolbox for window width < 970px
  */
  
  resize: function() {
    //  101 = header height, 25 = messages height if available, 16 = padding: 8px 0;
    var dim = window.getInnerDimensions();
    var toolboxHeight = $chk($('toolbox')) ? $('toolbox').getStyle('height').toInt() : 0;
    if ($('search')) $('toolbox').setStyle('height', dim.width < 970 ? 90 : 56);
    var shelfHeight = dim.height - (101 + toolboxHeight + 6) - ($chk($('help')) ? 25 : 0) - 16;
    var marginHeight = window.ie6 ? 0 : 5
    if ($('container')) $('container').setStyles({
      'height': shelfHeight,
      'margin-top': marginHeight
    });
  }   
}

/*
Class: Products
  collects functions for the following products:
    business cards (V1)

Note:
  static class
*/

var Products = {
  
  /*
  Property: initialize  
    sets initial states (livesearch, shelf dimensions)
  */
  
  initialize: function() {
    this.shelf = $('container');
    this.liveSearch = new LiveSearch('input_search', {
      request: '/overview/search',
      update: this.shelf
    });
    Shelf.resize();
  },
  
  /*
  Property: highlighNewProduct
    animates the appearance of a new product;
  
  Arguments:
    product - the DOM id of a div.product
    message - the success message to display, e.g. "Visitenkarte erfolgreich angelegt"
  */
  
  highlightNewProduct: function(product, message) {
    product = $(product).setStyle('width', 0);
    var slideFx = new Fx.Style(product, 'width', { onComplete: function() { appearFx.start(0,1) } });
    var appearFx = new Fx.Style(product, 'opacity', {
      duration: 1000,
      onComplete: function() { Messages.success(message, product); }
    });
    slideFx.start(250);
  },
  
  /*
  Property: deleteProduct
    sends a request to delete a product; animates the disappearance of a product
  
  Arguments:
    product - a HTML element
    message - a success message to be shown  
  */
  
  deleteProduct: function(product, message) {  
    var fadeOutProduct = new Fx.Style(product, 'opacity', {
      duration: 500
    });
    
    var collapseProduct = new Fx.Style(product, 'width', {
      duration: 500
    });
    
    var onRequestComplete = function() {
      ProductsPopup.close();
      (function() { fadeOutProduct.start(0); }).delay(700);
      (function() {
        collapseProduct.start(0);
        Messages.success('Drucksorte erfolgreich gelöscht!', null, { position: 'centered' });
      }).delay(1500);
    }

    new Ajax('/overview/delete_product?card='+ProductsPopup.activeProduct.id, {
      onComplete: onRequestComplete,
      evalScripts: true
    }).request();
  },
  
  /*
  Property: reloadShelf
    requests the shelf content, reloads the shelf
  */  
  
  reloadShelf: function() {
    new Ajax('/overview/reload/business_cards', {
      update: Products.shelf,
      evalScripts: true
    }).request();
  },
  
  /*
  Property: registerProductEvents
    registers events for products, attaches event handlers
  */
  
  registerProductEvents: function() {    
    $$('div.template div.thumbnail').each(function(product) {
      product.addEvent('click', function(event) {
        ProductsPopup.open('/overview/popup_vorlage', product.getParent());
      });
    });
    $$('div.editable div.thumbnail').each(function(product) {
      product.addEvent('click', function(event) {
        ProductsPopup.open('/overview/popup_bearbeiten', product.getParent());
      });
    });
  }
  
}

/*
Class: ApprovalsPopup
  extends <Popup> with specific methods

Note:
  static class
*/

var ApprovalsPopup = $merge(Popup, {
  
  /*
  Property: saveAndCancel
    saves the content of a form;
    closes the popup
  */
  
  saveAndCancel: function() {
    // TODO add code for saving data here
    ApprovalsPopup.close();
  }
});


/*
Class: Articles
  collects functions for the following articles/products:
    stock items (v2)
    may be used later in general for all types of articles

Note:
  static class
*/

var Articles = {
  
  /*
  Property: initialize  
    sets initial states (livesearch, shelf dimensions)
  */
  
  initialize: function() {
    this.shelf = $('container');
    this.liveSearch = new LiveSearch('input_search', {
      request: '/articles/search',
      update: this.shelf
    });
    Shelf.resize();
  },
  
  /*
  Property: highlighNewArticle
    animates the appearance of a new product;
  
  Arguments:
    product - the DOM id of a div.product
    message - the success message to display, e.g. "Visitenkarte erfolgreich angelegt"
  */
  
  highlightNewArticle: function(article, message) {
    article = $(article).setStyle('width', 0);
    var slideFx = new Fx.Style(article, 'width', { onComplete: function() { appearFx.start(0,1) } });
    var appearFx = new Fx.Style(article, 'opacity', {
      duration: 1000,
      onComplete: function() { Messages.success(message, article); }
    });
    slideFx.start(250);
  },
  
  /*
  Property: deleteArticle
    sends a request to delete an article; animates the disappearance of an article
  
  Arguments:
    article - a HTML element
    message - a success message to be shown  
  */
  
  deleteArticle: function(article, message) {  
    var fadeOutArticle = new Fx.Style(article, 'opacity', {
      duration: 500
    });
    
    var collapseArticle = new Fx.Style(article, 'width', {
      duration: 500
    });
    
    var onRequestComplete = function() {
      ArticlesPopup.close();
      (function() { fadeOutArticle.start(0); }).delay(700);
      (function() {
        collapseArticle.start(0);
        Messages.success('Visitenkarte erfolgreich gelöscht!', null, { position: 'centered' });
      }).delay(1500);
    }

    new Ajax('/articles/delete_article?card='+ArticlesPopup.activeArticle.id, {
      onComplete: onRequestComplete,
      evalScripts: true
    }).request();
  },
  
  /*
  Property: reloadShelf
    requests the shelf content, reloads the shelf
  */  
  
  reloadShelf: function() {
    new Ajax('/articles/reload/', {
      update: Articles.shelf,
      evalScripts: true
    }).request();
  },
  
  /*
  Property: registerArticleEvents
    registers events for articles, attaches event handlers
  */
  
  registerArticleEvents: function() {    
    $$('a.order').each(function(article) {
      article.addEvent('click', function(event) {
        ArticlesPopup.open('/articles/popup_order', article.getParent().getParent().getParent());
      });
    });
    
    $$('a.produce').each(function(article) {
      article.addEvent('click', function(event) { 
        ArticlesPopup.open('/articles/popup_produce', article.getParent().getParent().getParent());
      });
    });
    
    $$('a.details').each(function(article) {
      article.addEvent('click', function(event) { 
        ArticlesPopup.open('/articles/popup_details', article.getParent().getParent());
      });
    });
  }  
  
}

/*
Class: HistoryPopup
  extends <Popup> with specific methods
  
Note:
  static class
*/

var HistoryPopup = $merge(Popup, {
  
  /*
  Property: saveAndCancel
    saves the content of a form;
    closes the popup
  */
    
   open: function(path, product) { //bound to event object
    		
    if (product) this.activeProduct = $(product);
    var sheetsRequest = new Ajax(path, { 
      method: 'GET', 
      data: 'itemId=' + this.activeProduct.id,
      onComplete: this.afterOpen.bind(this),
      evalScripts: true
    });
    sheetsRequest.request();
  },
    
  saveAndCancel: function() {
    HistoryPopup.close();
  }
});


/*
Class: BudgetsPopup
  extends <Popup> with specific methods

Note:
  static class
*/

var BudgetsPopup = $merge(Popup, {
  
  initialize: function(sheetBaseURL, validationBaseURL) {
    Popup.initialize.pass([sheetBaseURL], this)();
    this.validationBaseURL = validationBaseURL;
  },
  
  /*
  Property: saveAndCancel
    saves the content of a form;
    closes the popup
  */  
  saveAndCancel: function() {
    BudgetsPopup.forms.processActiveSheet(function() {
      var onRequestComplete = function() {
        BudgetsPopup.close();
        Budgets.reloadShelf();
        Messages.success("Budget wurde erfolgreich gespeichert!", null, { position: 'centered' });
      }
      
      new Ajax('/budgets/update', {
        data: BudgetsPopup.activeSheet.toQueryString(),
        update: Budgets.shelf,
        evalScripts: true,
        onComplete: onRequestComplete
      }).request();
    });
  },
  cancel: function() {
    BudgetsPopup.close();
  },
  
  /*
  Property: forms
    collects functions to work with forms
  */
  
  forms: {
    /*
    Property: processActiveSheet
      validates the active sheet; sends an request to validationBaseURL containing sheet specific
      data as well as the data of the form fields;
      calls the following callbacks:
        <afterSheetValidated> - called when validation is complete
        <callbackOnValid> - called when validation results contain no errors
    
      Arguments:
        callbackOnValid - a callback function to be called if the validation results contain no errors  
    */
  
    processActiveSheet: function(callbackOnValid) {
      //var form = BudgetsPopup.activeSheet.getElementsByTagName('form').length < 1 ? '' : BudgetsPopup.activeSheet.getElementsByTagName('form')[0];
      var form = BudgetsPopup.activeSheet
      if ($chk(form)) {
        new Ajax(BudgetsPopup.validationBaseURL + BudgetsPopup.activeSheet.id, {
          data: form.toQueryString(),
          onComplete: function(response) {
            BudgetsPopup.forms.afterSheetValidated(response, callbackOnValid);
          },
          evalScripts: true
        }).request();
      } else {
        callbackOnValid();
      }
    },  
    /*
    Property: afterSheetValidated
      updated the sheets HTML with the results from the response;
      checks if validation results contain errors;
      highlights form fields which contain errrors;
  
    Arguments:
      response - the response from the server containing the HTML of the validated sheet
      callbackOnValid - the callback function which is called when the validated sheet contains no errrors  
    */

    afterSheetValidated: function(response, callbackOnValid) {
      if(response == 'ok') { // validation succeeded, TODO: check with KB
        callbackOnValid();
      } else {
        BudgetsPopup.activeSheet.empty().setHTML(response);
        BudgetsPopup.fireOnInitialize(BudgetsPopup.activeSheet);
        BudgetsPopup.fireOnActivate(BudgetsPopup.activeSheet);
        var errorInputs = $E('form', BudgetsPopup.activeSheet).getElements('.error');
      
        if(errorInputs.length > 0) {
          var inputsEffect = new Fx.Elements(errorInputs, { duration: 1000 });
          var inputs = {};
          errorInputs.each(function(input, i) {
            inputs[i] = { 'background-color': ['#ffffff', '#f1d6d6'] };
          });
          BudgetsPopup.popup.shake();
          inputsEffect.start(inputs);
        }
      }
    }
  }

});


/*
Class: UsersPopup
  extends <Popup> with specific methods

Note:
  static class
*/

var UsersPopup = $merge(Popup, {
  
  /*
  Property: initialize
    extends <Popup> with specific methods;
    sets initial states for a product specific popup;
    sets sheetBaseURL and validationBaseURL
    
  Arguments:
    sheetBaseURL - this default URL the sheets will request to
    validationBaseURL - the default URL to send validation requests to    
  */
  
  initialize: function(sheetBaseURL, validationBaseURL) {
    Popup.initialize.pass([sheetBaseURL], this)();
    this.validationBaseURL = validationBaseURL;
  },
  
  /*
  Property: saveAndCancel
    saves the content of a form; serializes the new users tags;
    closes the popup
  */
  
  saveAndCancel: function() {
    new Ajax('/users/reload', {
      data: this.tagSelector.activeTagsToQueryString(),
      update: $('container'),
      evalScripts: true
    }).request();
    
    $('user_tags').hide();
    UsersPopup.close();
  },
  
  /*
  Property: tagSelector
    gets set by users_sheet_edit/add specific javascript code;
    this code gets evaluated after the sheet was requested successfully
  */
  
  tagSelector: Class.empty,
  
  
  /*
  Property: forms
    collects functions to work with forms
  */
  
  forms: {
    
    /*
    Property: processActiveSheet
      validates the active sheet; sends an request to validationBaseURL containing sheet specific
      data as well as the data of the form fields;
      calls the following callbacks:
        <afterSheetValidated> - called when validation is complete
        <callbackOnValid> - called when validation results contain no errors
      
      Arguments:
        callbackOnValid - a callback function to be called if the validation results contain no errors  
    */
    
    processActiveSheet: function(callbackOnValid) {
      //var form = UsersPopup.activeSheet.getElementsByTagName('form').length < 1 ? '' : UsersPopup.activeSheet.getElementsByTagName('form')[0];
      var form = UsersPopup.activeSheet;
      if ($chk(form)) {
        new Ajax(UsersPopup.validationBaseURL + UsersPopup.activeSheet.id, {
          data: form.toQueryString(),
          onComplete: function(response) {
            UsersPopup.forms.afterSheetValidated(response, callbackOnValid);
          },
          evalScripts: true
        }).request();
      } else {
        callbackOnValid();
      }
    },

    /*
    Property: afterSheetValidated
      updated the sheets HTML with the results from the response;
      checks if validation results contain errors;
      highlights form fields which contain errrors;
    
    Arguments:
      response - the response from the server containing the HTML of the validated sheet
      callbackOnValid - the callback function which is called when the validated sheet contains no errrors  
    */

    afterSheetValidated: function(response, callbackOnValid) {
      if(response == 'ok') { // validation succeeded, TODO: check with KB
        callbackOnValid();
      } else {
        UsersPopup.activeSheet.empty().setHTML(response);
        UsersPopup.fireOnInitialize(UsersPopup.activeSheet);
        UsersPopup.fireOnActivate(UsersPopup.activeSheet);
        var errorInputs = $E('form', UsersPopup.activeSheet).getElements('.error');
        
        if(errorInputs.length > 0) {
          var inputsEffect = new Fx.Elements(errorInputs, { duration: 1000 });
          var inputs = {};
          errorInputs.each(function(input, i) {
            inputs[i] = { 'background-color': ['#ffffff', '#f1d6d6'] };
          });
          UsersPopup.popup.shake();
          inputsEffect.start(inputs);
        }
      }
    }
  } //forms
  
});

/*
Class: LiveSearch
  instantiates a Live search widget
*/

var LiveSearch = new Class({
  
  /*
  Property: initialize
    sets properties for a new live search widget
    
  Arguments:
    input - DOM id of an HTML input element
    options - optional, can contain the following properties:
      request - the URL to query
      update - the DOM id of an HTML element to update
  */
  
  initialize: function(input, options) {
    this.input = $(input);
    this.options = $extend({
      request: '',
      update: ''
    }, options || {});

    Shelf.resize();
  		
    new Observer(this.input, function() {
      this.triggerLiveSearchQuery();
    }.bind(this));
  },
  
  /*
  Property: triggerLiveSearchQuery
    sends a request containing a search query; updates an element with search results
  */
  
  /*triggerLiveSearchQuery: function() {
	console.log('here i am base');
    new Ajax(this.options.request, {
      data: { query: this.input.value },
      update: this.options.update,
      evalScripts: true
    }).request();
  },*/
  triggerLiveSearchQuery: function() {
        new Ajax(this.options.request, {
            data: "query=" + $('input_search').value + '&' + this.activeTagsToQueryString() + '&p=' + Math.floor(Math.random()*100),
            update: this.options.update,
            evalScripts: true
        }).request();    
    },
  
  /*
  Property: resetLiveSearch
    resets the search;
  */
  
  resetLiveSearch: function() {
    this.input.value = '';
    this.triggerLiveSearchQuery();
  },
  
  
  activeTagsToQueryString: function(){
        return $$('div#tags_list a.active').map(function(tag) {
            return "tags[]=" + escape(tag.title);
        }).join('&');

    }
  
});


/*
Class: Budgets
  collects functions for managing budgets

Note:
  static class
*/

var Budgets = {
  
  /*
  Property: initialize
    sets initial states
  */
  
  initialize: function() {
    this.shelf = $('container');
    Shelf.resize();
    this.registerEvents();
    this.liveSearch = new LiveSearch('input_search', {
      request: '/budgets/livesearch',
      update: 'container'
    })
  },
  
  /*
  Property: registerEvents
    registers events for UI elements; defines event handlers
  */
  
  registerEvents: function() {
    var openEditPopup = function() { BudgetsPopup.open('/budgets/popup_edit'); }
    var openAddPopup = function() { BudgetsPopup.open('/budgets/popup_add'); }
    $$('div.user a.default').each(function(editLink) {
      editLink.addEvent('click', openEditPopup);
    });
    
    $('new_budget_button').addEvent('click', openAddPopup);
  },
  
  /*
  Property: reloadShelf
    requests the shelf content, reloads the shelf
  */  
  
  reloadShelf: function() {
    new Ajax('/budgets/', {
      update: Products.shelf,
      evalScripts: true
    }).request();
  }
}


/*
Class: Users
  collects functions for managing users

Note:
  static class
*/

var Users = {
  
  /*
  Property: initialize
    sets initial states
  */
  
  initialize: function() {
    this.shelf = $('container');
    Shelf.resize();
    this.registerEvents();
    this.liveSearch = new LiveSearch('input_search', {
      request: '/users/search',
      update: 'container'
    })
  },
  
  /*
  Property: registerEvents
    registers events for UI elements; defines event handlers
  */
  
  registerEvents: function() {
    var openEditPopup = function() { UsersPopup.open('/users/popup_edit'); }
    var openAddPopup = function() { UsersPopup.open('/users/popup_add'); }
    $$('div.user a.default').each(function(editLink) {
      editLink.addEvent('click', openEditPopup);
    });
    $('new_user_button').addEvent('click', openAddPopup);
  },
  
  /*
  Property: highlightNewUser
    animates the appearance of a new user card; shows a success message
  
  Arguments:
    user - DOM id of a user card to highlight
    message - text of the success message to show
  */
  
  highlightNewUser: function(user, message) {
    user = $(user).setStyle('width', 0);
    var slideFx = new Fx.Style(user, 'width', { onComplete: function() { appearFx.start(0,1) } });
    var appearFx = new Fx.Style(user, 'opacity', {
      duration: 1000,
      onComplete: function() { Messages.userSaved(message, user); }
    });
    slideFx.start(340);
  }
}

/*
Class: ProductsPopup
  collects functions for product specific popups
  
Note:
  static class
*/

var ProductsPopup = $merge(Popup, {
  
  /*
  Property: initialize
    extends <Popup> with specific methods;
    sets initial states for a product specific popup;
    sets sheetBaseURL and validationBaseURL
    
  Arguments:
    sheetBaseURL - this default URL the sheets will request to
    validationBaseURL - the default URL to send validation requests to    
  */
  
  initialize: function(sheetBaseURL, validationBaseURL) {
    Popup.initialize.pass([sheetBaseURL], this)();
    this.validationBaseURL = validationBaseURL;
  },
  
  /*
  Property: saveAndCancel
    validates a sheet, calls a callback function if the validation successes
  */
  
  saveAndCancel: function() {
    ProductsPopup.forms.processActiveSheet(function() {
      ProductsPopup.close();
      Products.reloadShelf();
    });
  },

  /*
  Property: saveAndOrder
    orders a product by sending a request containing product specific data;
    reloads the shelf; closes the popup
  */

  saveAndOrder: function() {
    new Ajax('/overview/order/business_cards', {
      update: Products.shelf,
      evalScripts: true
    }).request();
    ProductsPopup.close();  
  },
  
  /*
  Property: saveAndOrderFromDashboard
    places an order from the dashboard;
    closes the popup; shows a success message
  */
  
  saveAndOrderFromDashboard: function() {
    // TODO code when saved
    ProductsPopup.close();
    Messages.success("Bestellung abgeschickt!", null, { position: 'centered' });
  },
  
  /*
  Property: photos
    collects functions to work with photos
  */
    
  photos: {
    
    /*
    Property: select
      selects a media (photo, logo, background, etc.);
      toggles 'active' states; sets media for <MediaUploader>;
      performs a local scroll back to the 'edit sheet'
    
    Arguments:
      media - the requested type of media, e.g. 'photo', 'logo', etc.
      photo - the thumbnail of the selected media
    */
    
    select: function(media, photo) { //photo = event.currentTarget
      $$('div.photo').removeClass('active');
      photo.addClass('active');
      this.activePhoto = photo;
      MediaUploader.setMedia(media, photo.getElement('img').src);
      ProductsPopup.scrollToSheetLocally('sheet_edit', {
      data: { 'media': media }
        });
    }
  },
  
  /*
  Property: forms
    collects functions to work with forms
  */
  
  forms: {
    
    /*
    Property: toggleOrderAddressForm
      transitions between a prefilled order address and an order address form
    
    Arguments:
      from - the element to hide (the currently visible element)
      to - the element to show
    */
    
    toggleOrderAddressForm: function(from, to) {
      from = $(from), to = $(to);
      var variator = (from.id == 'order_address' ? 20 : 50);
      var resizeFx = new Fx.Style('order_address_field', 'height');
      
      if (window.ie6) variator = 80;
        
      [from, to].each(function(div) {
        div.fadeFx = new Fx.Style(div.id, 'opacity');
      });
      from.fadeFx.start(0).chain(function() {
        from.hide(); 
      });
      resizeFx.start(to.getStyle('height').toInt() + variator).chain(function() {
        to.setOpacity(0).show();
        to.fadeFx.start(1);
      });
    },
    
    /*
    Property: processActiveSheet
      validates the active sheet; sends an request to validationBaseURL containing sheet specific
      data as well as the data of the form fields;
      calls the following callbacks:
        <afterSheetValidated> - called when validation is complete
        <callbackOnValid> - called when validation results contain no errors
      
      Arguments:
        callbackOnValid - a callback function to be called if the validation results contain no errors  
    */
    
    processActiveSheet: function(callbackOnValid) {
      var form = ProductsPopup.activeSheet.getElementsByTagName('form').length < 1 ? '' : ProductsPopup.activeSheet.getElementsByTagName('form')[0];
      if ($chk(form)) {
        new Ajax(ProductsPopup.validationBaseURL + ProductsPopup.activeSheet.id, {
          data: form,
          onComplete: function(response) {
            ProductsPopup.forms.afterSheetValidated(response, callbackOnValid);
          },
          evalScripts: true
        }).request();
      } else {
        callbackOnValid();
      }
    },

    /*
    Property: afterSheetValidated
      updated the sheets HTML with the results from the response;
      checks if validation results contain errors;
      highlights form fields which contain errrors;
    
    Arguments:
      response - the response from the server containing the HTML of the validated sheet
      callbackOnValid - the callback function which is called when the validated sheet contains no errrors  
    */

    afterSheetValidated: function(response, callbackOnValid) {
      if(response == 'ok') { // validation succeeded, TODO: check with KB
        callbackOnValid();
      } else {
        ProductsPopup.activeSheet.empty().setHTML(response);
        ProductsPopup.fireOnInitialize(ProductsPopup.activeSheet);
        ProductsPopup.fireOnActivate(ProductsPopup.activeSheet);
        var errorInputs = $E('form', ProductsPopup.activeSheet).getElements('.error');
        if(errorInputs.length > 0) {
          var inputsEffect = new Fx.Elements(errorInputs, { duration: 1000 });
          var inputs = {};
          errorInputs.each(function(input, i) {
            inputs[i] = { 'background-color': ['#ffffff', '#f1d6d6'] };
          });
          ProductsPopup.checkForResize();
          ProductsPopup.popup.shake();
          inputsEffect.start(inputs);
        }
      }
    }
  } //forms
  
});


/*
Class: ArticlesPopup
  collects functions for article specific popups (stockitems)
  
Note:
  static class
*/

var ArticlesPopup = $merge(Popup, {
  initialize: function(sheetBaseURL, validationBaseURL) {
    Popup.initialize.pass([sheetBaseURL], this)();
    this.validationBaseURL = validationBaseURL;
  },

  /*
  Property: deleteAttachment
    sends a request to delete an attachment; animates the deletion of an attachment
  
  Arguments:
    attachment - a HTML element
  */
  
  deleteAttachment: function(attachment) {
    var onRequestComplete = function() {
      
      var fadeOutAttachment = new Fx.Style(attachment, 'opacity', {
        duration: 500
      });
      
      fadeOutAttachment.start(0);
      (function() { attachment.hide(); }).delay(500);
      
    }
    
    new Ajax('/articles/delete_attachment?attachment='+attachment.id, {
      onComplete: onRequestComplete,
      evalScripts: true
    }).request();
  },
  
  /*
  Property: deletePDFFile
    sends a request to delete the PDF File; animates the deletion
  */
  
  deletePDFFile: function() {
    $('lbl_upload_pdf_file').setHTML('PDF hinzufügen');
    $('pdf_file_details').setHTML('Derzeit ist keine Datei vorhanden');
    $('delete_pdf_file').hide();
  },
  
  /*
  Property: uploadAttachment
    sends a request to upload an attachment
  
  Arguments:
    attachmentForm - a HTML element
  */
  
  uploadAttachment: function(attachmentForm) {
    $(attachmentForm).submit();
  },
  
  /*
  Property: updateArticle
    sends a request to update an article
  
  Arguments:
    attachment - a HTML element
  */
  
  updateArticle: function(attachment) {
    var onRequestComplete = function() {
      ArticlesPopup.close();
      Articles.reloadShelf();
      Messages.success("Artikel wurde erfolgreich geändert!", null, { position: 'centered' });
    }
    
    new Ajax('/articles/update_article?article='+ArticlesPopup.activeProduct.id, {
      data: ArticlesPopup.activeSheet.toQueryString(),
      onComplete: onRequestComplete,
      evalScripts: true
    }).request();
  },

  /*
  Property: saveAndCancel
    validates a sheet, calls a callback function if the validation successes
  */
  
  saveAndCancel: function() {
    ArticlesPopup.forms.processActiveSheet(function() {
      ArticlesPopup.close();
      Articles.reloadShelf();
    });
  },

  /*
  Property: saveAndOrder
    orders an article by sending a request containing article specific data;
    reloads the shelf; closes the popup
  */

  saveAndOrder: function() {
    
			ArticlesPopup.forms.processActiveSheet(function() {
      
      var onRequestComplete = function() {
        ArticlesPopup.close();
        Messages.success("Artikel wurde erfolgreich bestellt!", null, { position: 'centered' });
      }
      
      new Ajax('/articles/order?article='+ArticlesPopup.activeProduct.id, {
        data: ArticlesPopup.activeSheet.toQueryString(),
        update: Articles.shelf,
        evalScripts: true,
        onComplete: onRequestComplete
      }).request();
      
    });
    
  },
  
  /*
  Property: saveAndProduce
    orders an article by sending a request containing article specific data
    reloads the shelf; closes the popup
  */

  saveAndProduce: function() {
    ArticlesPopup.forms.processActiveSheet(function() {
      var onRequestComplete = function() {
        ArticlesPopup.close();
        Messages.success("Der Produktionsauftrag wurde erfolgreich durchgeführt!", null, { position: 'centered' });
      }
      
      new Ajax('/articles/produce?article='+ArticlesPopup.activeProduct.id, {
        data: ArticlesPopup.activeSheet.toQueryString(),
        update: Articles.shelf,
        evalScripts: true,
        onComplete: onRequestComplete
      }).request();
      
    });
  },
  
  /*
  Property: saveAndOrderFromDashboard
    places an order from the dashboard;
    closes the popup; shows a success message
  */
  
  saveAndOrderFromDashboard: function() {
    // TODO code when saved
    ArticlesPopup.close();
    Messages.success("Bestellung abgeschickt!", null, { position: 'centered' });
  },
  
  /*
  Property: photos
    collects functions to work with photos
  */
    
  photos: {
    
    /*
    Property: select
      selects a media (photo, logo, background, etc.);
      toggles 'active' states; sets media for <MediaUploader>;
      performs a local scroll back to the 'edit sheet'
    
    Arguments:
      media - the requested type of media, e.g. 'photo', 'logo', etc.
      photo - the thumbnail of the selected media
    */
    
    select: function(media, photo) { //photo = event.currentTarget
      $$('div.photo').removeClass('active');
      photo.addClass('active');
      this.activePhoto = photo;
      MediaUploader.setMedia(media, photo.getElement('img').src);
      ArticlesPopup.scrollToSheetLocally('sheet_edit');
    }
  },
  
  /*
  Property: forms
    collects functions to work with forms
  */
  
  forms: {
    
    /*
    Property: toggleOrderAddressForm
      transitions between a prefilled order address and an order address form
    
    Arguments:
      from - the element to hide (the currently visible element)
      to - the element to show
    */
    
    toggleOrderAddressForm: function(from, to) {
      from = $(from), to = $(to);
      var variator = (from.id == 'order_address' ? 20 : 60);
      var resizeFx = new Fx.Style('order_address_field', 'height');
      
      if (window.ie6) variator = 60;
        
      [from, to].each(function(div) {
        div.fadeFx = new Fx.Style(div.id, 'opacity');
      });
      from.fadeFx.start(0).chain(function() {
        from.hide(); 
      });
      resizeFx.start(to.getStyle('height').toInt() + variator).chain(function() {
        to.setOpacity(0).show();
        to.fadeFx.start(1);
      });
    },
    
    /*
    Property: processActiveSheet
      validates the active sheet; sends an request to validationBaseURL containing sheet specific
      data as well as the data of the form fields;
      calls the following callbacks:
        <afterSheetValidated> - called when validation is complete
        <callbackOnValid> - called when valdation results contain no errors
      
      Arguments:
        callbackOnValid - a callback function to be called if the validation results contain no errors  
    */
    
    processActiveSheet: function(callbackOnValid) {
      //var form = ArticlesPopup.activeSheet.getElementsByTagName('form').length < 1 ? '' : ArticlesPopup.activeSheet.getElementsByTagName('form')[0];
      var form = ArticlesPopup.activeSheet;
      
      if ($chk(form)) {
        new Ajax(ArticlesPopup.validationBaseURL + ArticlesPopup.activeSheet.id, {
          data: form.toQueryString(),
          onComplete: function(response) {
            ArticlesPopup.forms.afterSheetValidated(response, callbackOnValid);
          },
          evalScripts: true
        }).request();
      } else {
        callbackOnValid();
      }
    },

    /*
    Property: afterSheetValidated
      updated the sheets HTML with the results from the response;
      checks if validation results contain errors;
      highlights form fields which contain errrors;
    
    Arguments:
      response - the response from the server containing the HTML of the validated sheet
      callbackOnValid - the callback function which is called when the validated sheet contains no errrors  
    */

    afterSheetValidated: function(response, callbackOnValid) {
      if(response == 'ok') { // validation succeeded, TODO: check with KB
        callbackOnValid();
      } else {
        ArticlesPopup.activeSheet.empty().setHTML(response);
        ArticlesPopup.fireOnInitialize(ArticlesPopup.activeSheet);
        ArticlesPopup.fireOnActivate(ArticlesPopup.activeSheet);
        //var errorInputs = $E('form', ArticlesPopup.activeSheet).getElements('.error');
        var errorInputs = ArticlesPopup.activeSheet.getElements('.error');
        if(errorInputs.length > 0) {
          var inputsEffect = new Fx.Elements(errorInputs, { duration: 1000 });
          var inputs = {};
          errorInputs.each(function(input, i) {
            inputs[i] = { 'background-color': ['#ffffff', '#f1d6d6'] };
          });
          ArticlesPopup.checkForResize();
          ArticlesPopup.popup.shake();
          inputsEffect.start(inputs);
        }
      }
    }
  } //forms  
  
});

/*
Class: Login
  collects functions for the login screen

Note:
  static class
*/

var Login = {
  
  /*
  Property: showError
    animates the appearance of an error message;
    shakes the login form
  
  Arguments:
    message - the text for the error message to be shown  
  */
  
  showError: function(message) {
    $('login').shake();
    $('login_error').setHTML(message).setOpacity(0).show();
    var errorFx = new Fx.Style('login_error', 'opacity', {
      onComplete: function() { (function(){errorFx.start(0)}).delay(2000); }
    }).set(0);
    errorFx.start(1);
  },
  
  /*
  Property: showOk
    animates the appearance of a success message;
    animates a nodding of the login form
  
  Arguments:
    message - the text for the success message to be shown  
  */
  
  showOk: function(message) {
    $('login').nod();
    $('login_ok').setHTML(message).setOpacity(0).show();
    var errorFx = new Fx.Style('login_ok', 'opacity', {
      onComplete: function() { (function(){errorFx.start(0)}).delay(2000); }
    }).set(0);
    errorFx.start(1);
  }
}

/*
Class: Messages
  collects functions to show different Notifications/Messages

Note:
  static class  
*/

var Messages = {
  
  /*
  Property: show
    animates the appearance of a message;
    sets text, styles, position of a message
  
  Arguments:
    type - the type of message, e.g. 'success', 'error', etc.
    text - the text of the message to be shown
    element - the element to append the message to
    options - optional, an object which can contain a <position> property
      <position> can be 'centered', which centers the message absolutely to the page
  */
  
  show: function(type, text, element, options) {
    var effectOptions = options ? options : { wait: 0 };
    var message = new Element('div', {
      'class': 'message '+type
    }).setHTML(text);
    
    options = options || {};
    
    element = element ? element : document.body
    if(options.position) {
      if(options.position == 'centered') {
        var windowdimensions = window.getInnerDimensions();
        message.setStyles({
           position: 'absolute',
           top: windowdimensions.height/2-120,
           left: windowdimensions.width/2-125
        });       
      } else {
        message.setStyles({
           position: 'absolute',
           top: options.position.top,
           left: options.position.left
        });
      }
    }
    
    var messageOpacityEffect = new Fx.Style(message, 'opacity', {
      duration: 400,
      onComplete: function() {
        message.remove();
      }
    }).set(0);
    

    message.injectInside(element);
    
    (function() { message.setOpacity(1); }).delay(effectOptions.wait);
    messageOpacityEffect.start.delay(3000, messageOpacityEffect, 0);

  },
  
  /*
  Property: success
    shows a success message
  */

  success: function(text, element, options) {
    Messages.show('success', text, element, options);
  },
  
  /*
  Property: error
    show an error message
  */
  
  error: function(text, element, options) {
    Messages.show('error', text, element, options);
  },
  
  /*
  Property: userSaved
    shows an userSaved message
  */
  
  userSaved: function(text, element, options) {
    Messages.show('user_saved', text, element, options);
  }
}

/*
Class: Scala
  instantiates a new Scala object;
*/

var Scala = new Class({
  
  /*
  Property: initialize
    initializes a Scala object; sticks a scala to the bottom of a slider
  
  Arguments:
    slider - a Slider() object
    div - the HTML DIV element the scala gets appended to
  */
  
  initialize: function(slider, div) {
    slider.options.values.each(function(unit, i) {
      var delta = slider.toPosition(slider.options.values[i]);
      var label = new Element('div').addClass('unit').setStyle('left', delta).setHTML(unit);
      div.adopt(label);
    });
  }
});

/*
Class: CustomSliderValue
  instantiates a new Scala object;
*/

var CustomSliderValue = new Class({
  
  /*
  Property: initialize
    initializes a CustomerSliderValue object; sticks a customslidervalue right beside a slider
  
  Arguments:
    slider - a Slider() object
    inputfield - the HTML DIV element the CustomerSliderValue object gets appended to
  */
  
  initialize: function(slider, inputfield) {
    /*slider.options.values.each(function(unit, i) {
      var delta = slider.toPosition(slider.options.values[i]);
      var label = new Element('div').addClass('unit').setStyle('left', delta).setHTML(unit);
      div.adopt(label);
    });*/
    
    inputfield.addEvent('change', function() { 
      customValue = parseInt(this.getProperty("value"));
      if (isNaN(customValue)) {
        ArticlesPopup.popup.shake();
        inputfield.setProperty("value", "");
        inputfield.focus();
      } else
        slider.setCustom(customValue);
      
    }.bind(inputfield));
  }
});


/*
Class: BudgetSelector
  instantiates a budget selector widget
*/

var BudgetSelector = new Class({
  /*
  Property: initialize
    constructs a budget selector widget; sets default options  	
	
  Arguments:
    budgetlist - the DOM id of the element which contains the budgets
    options - optional, an object which can contain the following properties:
      <onToggle> - callback function to be called when the budgetsBar is expanded or hidden.
      <onSelected> - callback function to be called when a budget gets chosen
      <request> - an URL to request to if a budget gets selected
      <update> - the DOM id of an HTML element to update if a tag gets selected and a request gets fired
  */ 
	
  initialize: function(budgetSelector, budgetsList, options) {
    this.budgetSelector = $(budgetSelector);
    this.budgetList = $(budgetsList);
		var budgetId = null;
		var filterObj = null;
	
    this.budgets = this.budgetList.getElements('div.budget');
    BudgetSelector.activeBudget = undefined;
    
    this.budgetResetter = $('budget_total');
    if ($chk(this.budgetResetter))
      this.budgetResetter.hide();
    
    this.budgetList.hide();
    
    this.options = $extend({
      onToggle: Class.empty,
      onSelected: Class.empty,
      onReset: Class.empty,
      request: '',
      update: ''
    }, options || {});

    this.budgets.each(function(budget) {
      budget.addEvent('click', function() { this.toggleActive(budget); }.bind(this));
    }.bind(this));
  },
  
  /*
  Property: toggleActive
    toggles the state of a budget; can send a request; calls the <onSelected> callback
  
  Arguments:
    tag - the HTML element which fired the event
    
  Note:
    this method is bound to an event object, in this context <this> is the HTML element which fired the event
  */  
  toggleActive: function(budget) {
    var txt1=budget.getElement('span.label_selected_budget').innerHTML;
    var txt2=budget.getElement('div.budget_info').innerHTML;
		var graph=budget.getElement('div.budget_status').innerHTML;
    this.budgetSelector.getElement('span.label_selected_budget').setHTML(txt1);
    this.budgetSelector.getElement('div.budget_info').setHTML(txt2);
    this.budgetSelector.getElement('div.budget_status').setHTML(graph);
    //this.slideFx.slideOut();
		this.budgetId = budget.id;
    if (this.options.request)
      this.respondToSelectedBudget(budget);
    // reshow previous selected budget
    if ($chk(BudgetSelector.activeBudget))
      BudgetSelector.activeBudget.setStyle("display","block");
    // set previous budget to the now selected budget
    BudgetSelector.activeBudget=budget;		
    // hide selected budget from the list
    budget.setStyle("display","none");
    
    if ($chk(this.budgetResetter)) { // in case the budgetresetter is not present
      if (budget.id!="budget_total")
        this.budgetResetter.setStyle("display","block");
      else
        this.budgetResetter.setStyle("display","none");
    }
    
    this.budgetList.hide();
    
    this.options.onSelected();
  },
  
  /*
  Property: toggleBudgetList
    collapses/expands the budgetlist
  */
  
  toggleBudgetList: function() {
      // this.slideFx.toggle();
      this.budgetList.toggle();
      this.options.onToggle();
  },
  
	setFilter: function(filterObj) {
		this.filterObj = filterObj;		
	},	
	
	getBudgetId: function() {
		return this.budgetId;
	}	,	
	
  /*
  Property: respondToSelectedBudget
    sends a request containing all selected tags to a specified request URL;
    can update an HTML element if specified
  */    
  respondToSelectedBudget: function(budget) {
		if(this.filterObj != null)
			 this.filterObj.triggerLiveSearchQuery();
	}	
});


/*
Class: Tags
  instantiates a tag selector widget
*/

var Tags = new Class({
  
  /*
  Property: initialize
    constructs a tag selector widget; sets default options
  
  Arguments:
    taglist - the DOM id of the element which contains the tags
    options - optional, an object which can contain the following properties:
      <onAllSelected> - callback function to be called when all tags get selected
      <onSelected> - callback function to be called when a tag gets selected
      <onReset> - callback function to be called when all tags are resetted
      <request> - an URL to request to if a tag gets selected
      <update> - the DOM id of an HTML element to update if a tag gets selected and a request gets fired
  */
  
  initialize: function(taglist, options) {
    this.tagList = $(taglist);
    this.tags = this.tagList.getElements('a');

    this.expandFx = new Fx.Style(this.tagList, 'height', {
      duration: 300,
      transition: Fx.Transitions.Quart.easeOut
    });
    
    this.options = $extend({
      onAllSelected: Class.empty,
      onSelected: Class.empty,
      onReset: Class.empty,      
      request: '',
      update: ''
    }, options || {});

    this.tags.each(function(tag) {
      tag.addEvent('click', function() { this.toggleActive(tag); }.bind(this));
	  if(tag.title == "") 
		tag.title = tag.innerHTML;
	  else
		tag.title = tag.title;	  	  
      tag.setHTML(tag.innerHTML.truncate(20, '...'));
    }.bind(this));

    if ($('tags_overlay')) $('tags_overlay').addEvent('click', this.collapse.bind(this));
  },  
  
  /*
  Property: toggleActive
    toggles the state of a tag; can send a request; calls the <onSelected> callback
  
  Arguments:
    tag - the HTML element which fired the event
    
  Note:
    this method is bound to an event object, in this context <this> is the HTML element which fired the event
  */
  
  toggleActive: function(tag) {
    tag.toggleClass('active');
    this.updateMoreTagsButton();
    if (this.options.request)
      this.respondToActiveTags();
    this.options.onSelected();
  },
  
  /*
  Property: selectAll
    sets all tags to an 'active' state, calls an <onAllSelected> callback
  */
  
  selectAll: function() {
    this.tags.each(function(tag) {
      tag.addClass(tag.hasClass('active') ? '' : 'active');
    });
    this.options.onAllSelected();
  },
  
  /*
  Property: reset
    resets all tags by removing all 'active' states; calls an <onReset> callback
  */
  
  reset: function() {
    this.tags.removeClass('active');
    this.updateMoreTagsButton(0);
    if (this.options.request)
      this.respondToActiveTags();
    this.options.onReset();
  },
  
  /*
  Property: respondToActiveTags
    sends a request containing all selected tags to a specified request URL;
    can update an HTML element if specified
  */
  
  respondToActiveTags: function() {
    new Ajax(this.options.request, {
      data: this.activeTagsToQueryString() + '&p=' + Math.floor(Math.random()*1000),
      update: this.options.update,
      evalScripts: true
    }).request();
  },
  
  /*
  Property: activeTagsToQueryString
    constructs a query string based on all selected tags
    
  Returns:
    a query string of all selected tags
  */
  
  activeTagsToQueryString: function() {
    return $$('div#'+ this.tagList.id +' a.active').map(function(tag) {
      return "tags[]=" + escape(tag.innerHTML);
    }).join('&');
  },
  
  /*
  Property: countActiveTags
    counts all selected tags
    
  Returns:
    the number of all selected tags
  */
  
  countActiveTags: function() {
    var counter = 0;
    this.tagList.getElements('a.active').each(function(tag, i) {
      if (this.tags.indexOf(tag) >= 5) counter++;
    }.bind(this));
    return counter;
  },
  
  /*
  Property: updateMoreTagsButton
    updates the HTML of a button with the count of all selected tags
  
  Arguments:
    count - the number of all selected tags  
  */
  
  updateMoreTagsButton: function(count) {
    count = count || this.countActiveTags();
    if ($('button_more_tags')) {
      $('button_more_tags').getFirst().setHTML(count + ' weitere');
      $('button_more_tags')[count == 0 ? 'removeClass' : 'addClass']('selected');
    }
  },
  
  /*
  Property: toggleTagList
    collapses/expands the taglist
  */
  
  toggleTagList: function() {
    if (this.tagList.getHeight() > 25)
      this.collapse();
    else
      this.expand();
  },
  
  /*
  Property: expand
    transitions from one specific height to another
  */
  
  expand: function() {
    var height = (this.tags.length/5).round() * 25;
    if ($('tags_overlay')) $('tags_overlay').show();
    this.tagList.setStyle('z-index', 10001);
    this.expandFx.start(height);
  },
  
  /*
  Property: collapse
    transitions from one specific height to another
  */
  
  collapse: function() {  
    if ($('tags_overlay')) $('tags_overlay').hide();
    this.tagList.setStyle('z-index', 0);
    this.expandFx.start(25);
  }
  
});

/*
Class: History
  collects functions to work with history data
  
Note:
  static class
*/

var History = {
  
  /*
  Property: sendDateSelect
    filters history data by a specified date;
    sets begin/end dates for print stylesheet
  */
  
  sendDateSelect: function() {
    var beginDate = $('begin_date').value+' '+$('begin_time').value;
    var endDate = $('end_date').value+' '+$('end_time').value;
    
    Filter.filterByDateRangeFromExactDate("begin_DateTime="+beginDate+"&end_DateTime="+endDate);
    
    ['begin', 'end'].each(function(state) {
      $('print_' + state + '_date').setHTML($(state + '_date').value + ' ' + $(state + '_time').value);
    });
  },
  
  /*
  Property: print
    opens a popup window containing a receipt ready for print;
    shows a print dialog
  */
  
  print: function(receiptUrl) {
    var receipt = window.open(receiptUrl, "print", "width=500,height=600,location=no");
    receipt.print();
  }  
}

/*
Class: Dashboard
  collects all functions to work with the Dashboard
  
Note:
  static class
*/

var Dashboard = {
  
  /*
  Property: initialize
    sets initial states
  */
  
  initialize: function() {
    this.registerArticleEvents();
  },
  
  /*
  Property: registerArticleEvents
    registers events for UI controls, defines event handlers
  */
  
  registerArticleEvents: function() {
    $$('div.article a.reorder').each(function(reorderLink) {
      reorderLink.addEvent('click', function() {			
        ProductsPopup.open('/dashboard/popup_reorder');
      });
    });
  }
}

/*
Class: MediaUploader
  collects functions to work with a MediaUploader widget

Note:
  static class
*/

var MediaUploader = {
  
  /*
  Property: initialize
    sets initial states, registers events and defines event handlers
  */
  
  initialize: function() {
    this.types = ['photo', 'logo', 'background'];
    this.types.each(function(media) {
      $('map_'+media).addEvents({
        'click': function() { MediaUploader.selectMedia(media); },
        'mouseenter': function() { MediaUploader.onMouseEnter(media); },
        'mouseleave': function() { MediaUploader.onMouseLeave(media); }
      });
    });
  },
  
  /*
  Property: selectMedia
    scrolls a sheet locally, sets a local media object
  
  Arguments:
    media - a media type, e.g. 'photo'
  
  Note:
    event handler
  */
  
  selectMedia: function(media) {
    ProductsPopup.scrollToSheet('sheet_media', {
      data: { 'media': media }
    });
  },
  
  /*
  Property: onMouseEnter
    sets the className of an HTML element
    
  Arguments:
    media - a media type, e.g. 'photo'  
  
  Note:
    event handler
  */
  
  onMouseEnter: function(media) {
    $('map_'+media).getElement('div.actuator').addClass('over');
  },
  
  /*
  Property: onMouseLeave
    sets the className of an HTML element

  Arguments:
    media - a media type, e.g. 'photo'  
      
  Note:
    event handler
  */
  
  onMouseLeave: function(media) {
    $('map_'+media).getElement('div.actuator').removeClass('over');
  },
  
  /*
  Property: setMedia
    sets the background of an HTML element which represents a certain media
  
  Arguments:
    media - a media type, e.g. 'photo'
    source - the source to an image  
  */
  
  setMedia: function(media, source) {
    $('map_'+media).setStyle('background', 'url(' + source + ')');
    $('input_'+media).value = source;
  },
  
  /*
  Property: getQueryStringForMedia
    constructs a querystring out of input values for each media type
  */
  
  getQueryStringForMedia: function() {
    return this.types.map(function(media) {
      return media+'=' + escape($('input_'+media).value);
    }).join('&');
  }
}

/*
Class: Filter
  collects functions to interact with data filters
  
Note:
  static class
*/

var Filter = {
  
  /*
  Property: initialize
    sets initial states for filters
  */
  
  initialize: function() {
    this.slider = $('table_slider');
    this.isDetail = false;
    this.slideSlider = new Fx.Slide('table_slider', { 
      duration: 300, 
      onComplete: function() {
        if (!window.ie6)
          $('table_slider').getParent().setStyle('height', 'auto');
      }
    });
    this.slideDetails = new Fx.Slide('table_filter', { 
      duration: 300, 
      onComplete: function() {
        if (!window.ie6)
          $('table_filter').getParent().setStyle('height', 'auto');
      } 
    }).hide();
  },
  
  /*
  Property: showDetails
    transitions from timeslider widget to details widget
  */
  
  showDetails: function() {
    if (window.ie) this.slider.getElements('div.knob').hide();
    this.slideSlider.slideOut().chain(function() {
      Filter.slideDetails.slideIn();
    });
    this.isDetail = true;
  },
  
  /*
  Property: showSlider
    transitions from details widget to timeslider widget
  */
  
  showSlider: function() {
    this.slideDetails.slideOut().chain(function() {
      Filter.slideSlider.slideIn();
      if (window.ie) (function(){Filter.slider.getElements('div.knob').show();}).delay(300);
    });
    this.isDetail = false;        
  },
  
  /*
  Property: filterByDateRange
    requests data from a specific time period; updated the history overview with the results
  
  Arguments:
    rawBeginDate - timestamp of the beginDate, in milliseconds
    rawEndDate - timestamp of the endDate, in milliseconds      
  */
  
  filterByDateRange: function(rawBeginDate, rawEndDate, bId) {    
    //new Ajax('/history/date_range/', {
    
    new Ajax('/history/history_date_range.aspx', {
      data: 'begin_date=' + rawBeginDate + 
						'&end_date=' + rawEndDate +
						'&liveSearch=' + $('input_search').value +
						'&action=' + Filter.aktionName +
						'&auftragPosId=' + Filter.auftragPositionId + 
						'&budget=' + bId,
      update: 'history_overview',
      evalScripts: true
    }).request();
  },
  
  /*
  Property: filterByDateRangeFromExactDate
    requests data from a specific time period; updated the history overview with the results
  
  Arguments:
    params - paramters for the request  
  */
  
  filterByDateRangeFromExactDate: function(params) {
    HistoryTimeFilter.validateDateRange(params);
    
    new Ajax('/history/history_date_range.aspx', {
      data: params+'&liveSearch='+$('input_search').value+'&action='+Filter.aktionName+'&auftragPosId='+Filter.auftragPositionId ,
      update: 'history_overview',
      evalScripts: true
    }).request();
  }  
}

/*
Class: RoleSelector
  instantiates a role selector widget, e.g. to select a gender or a specific role
*/

var RoleSelector = new Class({
  
  /*
  Property: initialize
    sets initial states; sets properties for a new role selector widget
  
  Arguments:
    selector - a CSS selector to catch all controls, e.g. 'a.gender'
    input - DOM id of a hidden input field which contains the selected role
    options - optional, object which can provide the following callbacks:
      <onSelected> - function to be called after a role was selected
  */
  
  initialize: function(selector, input, options) { //CSS selector to catch all controls
    this.controls = $$(selector);
    this.input = $(input);
    
    this.options = $extend({
      onSelected: Class.empty
    }, options || {});
    
    this.controls.each(function(control) {
      control.addEvent('click', this.select.pass(control, this));
    }.bind(this));
  },
  
  /*
  Property: select
    sets the value of a hidden input field to the selected role;
    sets 'selected' states
  */
  
  select: function(control) {
    this.input.value = control.id.replace(/select_/, '');
    this.controls.removeClass('selected');
    control.addClass('selected');
    this.options.onSelected(control);
  }
});

window.addEvent('resize', Shelf.resize);
window.addEvent('load', function() {
  (function() { Shelf.resize(); }).delay(100);
});

/* Tokens */
var tokens = [
  ['Josef Müller', 'Coffi GmbH', 'Hauptstraße 5', 'Wien'],
  ['Markus Simpler', 'Siemens', 'Siemensstraße 1', 'Wien'],
  ['Lars Mayer', 'Coffi GmbH', 'Hauptstraße 5', 'Wien'],
  ['Leopold Mayer', 'Coffi GmbH', 'Hauptstraße 5', 'Wien'],
  ['Marion Schmied', 'Coffi GmbH', 'Hauptstraße 5', 'Wien']
]