/*

CUSTOM FORM ELEMENTS

Version 1.0 
By Martin Ström, <martinstrom@gmail.com>
<http://my-domain.se>
<http://github.com/haraldmartin/field_custom>

Based on the work by Ryan Fait (www.ryanfait.com)

## Usage

Activate it like this in you code

document.observe('dom:loaded', function() {
  Field.Custom.initialize({
    radio:    { height: 25 },
    checkbox: { height: 25 },
    select:   { width: 190 }
  });
});

Use CSS like this as start

<style type="text/css">
span.custom-checkbox, 
span.custom-radio    { width: 25px; clear; left; float: left; display: block; cursor: pointer; }
span.custom-checkbox { height: 11px; background: url(/images/checkbox-red.png) no-repeat; }
span.custom-radio    { height: 25px; background: url(/images/radio.png) no-repeat; }
span.custom-select   { width: 158px; height: 21px; position: absolute; padding: 0 24px 0 8px; 
                       color: #fff; background: url(/images/select.gif) no-repeat; }
</style>

*/

Field.Custom = Class.create({
  initialize: function(input, custom, options) {}
});

Field.Custom.initialize = function(options) {
  opitons = options || {};
  options.klass  = options.klass || 'styled';
  $$('input[type=checkbox].' + options.klass, 'input[type=radio].' + options.klass, 'select.' + options.klass).
    each(function(elm) {
      var klassName = elm.tagName.capitalize();
      new (Field.Custom[klassName == 'Input' ? elm.type.capitalize() : klassName])(elm, options);
    }
  );
}

// Superclass for togglers (checkboxes and radio buttons since they share much)
Field.Custom.Toggler = Class.create(Field.Custom, {
  UNCHECKED:        0,
  UNCHECKED_PUSHED: 1,
  CHECKED:          2,
  CHECKED_PUSHED:   3,
  
  initialize: function($super, element, options) {
    Field.Custom.Toggler._instances = Field.Custom.Toggler._instances || [];
    Field.Custom.Toggler._instances.push(this);
    this.options = options || {};
    this.input = element;
    this.create();
    this.observe();
    $super();
  },
  
  setState: function(state) {
    this.custom.setStyle('background-position: 0 -' + (this.options.height * state) + 'px');
  },
  
  create: function() {  
    this.custom = new Element('span', { 'class': 'custom-' + this.options.klass });
    if (this.input.checked) this.setState(this.CHECKED);
    this.input.hide().insert({ before: this.custom });
  },
  
  observe: function() {
    this.input.observe('change',     this.clear.bind(this));
    // TODO: Use event delegation instead
    this.custom.observe('mousedown', this.pushed.bind(this));
    this.custom.observe('mouseup',   this.check.bind(this));
  },
  
  pushed: function() {
    this.setState(this.input.checked ? this.CHECKED_PUSHED : this.UNCHECKED_PUSHED);
  },
  
  check: function() {},
  
  clear: function() {
    Field.Custom.Toggler._instances.each(function(elm) {
      elm.setState(elm.input.checked ? elm.CHECKED : elm.UNCHECKED);
    });
  }
});

// Checkboxes
Field.Custom.Checkbox = Class.create(Field.Custom.Toggler, {
  initialize: function($super, element, options) {
    options = options || {};
    $super(element, { klass: 'checkbox', height: options.checkbox.height });
  },
  
  check: function($super) {
    this.setState(this.input.checked ? this.UNCHECKED : this.CHECKED);
    this.input.checked = !this.input.checked
    $super();
  }
});

// Radio buttons
Field.Custom.Radio = Class.create(Field.Custom.Toggler, {
  initialize: function($super, element, options) {
    options = options || {};
    $super(element, { klass: 'radio', height: options.radio.height });
  },
  
  check: function($super) {
    this.setState(this.CHECKED);
    $$('input[type=radio][name="' + this.input.name + '"]').without(this.input).
      each(function(input) { input.previous().style.backgroundPosition = '0 0' });
    this.input.checked = true;
    $super();
  }
});

// Selects 
Field.Custom.Select = Class.create(Field.Custom, {
  initialize: function($super, element, options) {
    if (/MSIE 8\./.test(navigator.userAgent)) return; // no ie8 support right now
    this.options = options || {};
    this.options.width = this.options.select.width;
    this.input = $(element);
    this.create();
    this.observe();
    $super();
  },

  create: function() {
    this.custom = new Element('span', { 'class': 'custom-select', id: "_select_" + this.input.name });
    this.custom.insert((this.input.down('option[selected]') || this.input.down('option')).innerHTML)
    this.input.insert({ before: this.custom }).setStyle({
      position: 'relative',
      width:    this.options.width + 'px', 
      opacity:  0,
      filter:   'alpha(opacity=0)',
      zIndex:   5,
      overflow: 'hidden'
    });
  },

  observe: function() {
    this.input.observe('change', this.choose.bind(this));
  },
  
  choose: function() {
    var selected = this.input.childElements().detect(function(opt) { return opt.selected });
    if (selected) $('_select_' + this.input.name).update(selected.innerHTML);
  }
});

// http://mislav.caboo.se
function when(obj, fn) {
  if (Object.isString(obj)) obj = /^[\w-]+$/.test(obj) ? $(obj) : $(document.documentElement).down(obj)
  if (Object.isArray(obj) && !obj.length) return
  if (obj) fn(obj)
}

function on(string, funktion) {
  var selector = string.split(':'), rule = {
    stop     : selector.last().endsWith('!'),
    selector : selector[0],
    event    : selector.slice(1).join(':').replace(/\!$/, '')
  }, element = /^#[\w-]+$/.test(rule.selector) && rule.selector != 'body' ?
       $(rule.selector.substr(1)) : $(document.documentElement).down(rule.selector);
  
  if (element) element.observe(rule.event, function(event) {
    if (rule.stop) event.stop();
    funktion(event, event.element());
  })
}

Element.addMethods(['input', 'textarea'], {
  defaultValueActsAsHint: function(element, initialValue){
    element = $(element);
    element._default = element.value;
    initialValue = initialValue || '';
    
    return element.observe('focus', function(){
      if(element._default != element.value) return;
      element.removeClassName('hint').value = initialValue;
    }).observe('blur', function(){
      var value = element.value.strip();
      if(value != '' && value != initialValue) return;
      element.addClassName('hint').value = element._default;
    }).addClassName('hint');
  }
});

// --------------------------------------------------------------------

var Validation = {
  EMAIL: /^([A-Za-z0-9_\+\-\.])+\@([A-Za-z0-9_\-\.])+\.([A-Za-z]{2,4})$/,
  
  isValidEmail: function(string) {
    return Validation.EMAIL.test(string);
  }
}

var Subscribe = {
  initialize: function(element) {
    element.select("input[type=text]:not(#e-mail)").invoke('defaultValueActsAsHint');
    on('#new_subscription:submit', Newsletter.handleSubmit);
    Field.Custom.initialize({ radio: { width: 28, height: 27 }, select: { width: 70 } });
  },
  
  nextStep: function() {
    $('step1', 'step2').invoke('toggle');
  },
  
  submitForm: function() {
    var fakeEvent = {
      stopped: false,
      stop: function() { fakeEvent.stopped = true },
      element: function() { return $('new_subscription') }
    };
    
    // TODO: Use a more generic solution
    Newsletter.handleSubmit(fakeEvent);
    if (!fakeEvent.stopped) $('new_subscription').submit();
  }
}

var Newsletter = {
  initialize: function(element) {
    element.select("input[type=text]").invoke('defaultValueActsAsHint');
    on('#details_form:submit', Newsletter.handleSubmit);
  },
  
  // TODO: used on newsletter, schmuck des monat and subscribe. Move to a more generic solution
  handleSubmit: function(event) {
    var valid = true, email = $F('email'), form = event.element();
    
    // checkIfValidEmail
    if (email == 'email@example.com' || !Validation.isValidEmail(email)) {
      valid = false;
      $('email').removeClassName("hint").addClassName('error');
    } else {
      $('email').removeClassName('error');
    }
    
    // isValid
    var required = form.select("input.required, select.required").partition(function(input) {
      var value = $F(input);
      return value.blank() || value == input._default;
    }), invalidRequired = required[0], validRequired = required[1];
    
    if (invalidRequired.length > 0) valid = false;
    
    // toggleErrorAndHintClasses
    invalidRequired.each(function(element) {
      element.removeClassName('hint').addClassName("error");
      if (element.hasClassName("styled")) element.up().addClassName('error');
    });

    validRequired.each(function(element) {
      element.removeClassName('hint').removeClassName("error");
      if (element.hasClassName("styled")) element.up().removeClassName('error');
    });
    
    // if termsAccepted
    if (!$('agreed_to_terms').checked) {
      valid = false;
      $('agreed_to_terms_label', 'agreed_to_terms').invoke('addClassName', 'error');
    } else {
      $('agreed_to_terms_label', 'agreed_to_terms').invoke('removeClassName', 'error');
    }
    
    if (valid) {
      // blankDefaultValues
      $$('input.hint').each(function(element) {
        if (element.value === element._default) element.value = '';
      })
    } else {
      // activateFirstInvalidField
      event.stop();
      form.select('#details_form .hint').invoke('setValue', '');
      var nextInvalid = form.down('.error');
      if (nextInvalid && nextInvalid.activate) nextInvalid.activate();
    }
  }
};

var Designer = {
  initialize: function() {
    $('country_select') && new Field.Custom.Select('country_select', { select : { width: 97 }});
    $('city_select') && new Field.Custom.Select('city_select', { select: { width: 57 }});
    
    on('#country_select:change', Designer.handleCountrySelect);
    on('#city_select:change', Designer.handleCitySelect);
  },
  
  handleCountrySelect: function(event) {
    var country     = $('country_select'),
      citySelect    = $('city_select'),
      selectedValue = country.options[country.selectedIndex].value,
      cities        = COUNTRIES_AND_CITIES[selectedValue];
      
    if (selectedValue.blank()) return;
      
    citySelect.length = 1;

    $('available_stores').hide();
    $('city_label_wrapper', 'city_select_wrapper').invoke('show');
    
    cities.each(function(city, idx) {
      citySelect.options[idx + 1] = new Option(city[0], city[1]);
    });
    
    $('_select_city_select') && $('_select_city_select').update($('store_select_label').innerHTML + ' &darr;');
  },
  
  handleCitySelect: function(event) {
    if ($('city_select').selectedIndex == 0) return;
    $('city_loader').show();
    $('available_stores').hide();
    var form = $('stores_form');
    new Ajax.Request(form.readAttribute('action') + '.js', {
      method: 'get', parameters: form.serialize(true) 
    });
  },
  
  setEachRowHeightFromTallest: function() {
    var height, designers = $$('#designers li');
    designers.eachSlice(3, function(row) {
      height = row.invoke('getHeight').max();
      row.invoke('setStyle', 'height: ' + height + 'px');
    });
  },
  
  // TODO move the image dimensions to db and set the width directly in html
  setJewelDescriptionsWidthFromImage: function() {
    $$("#second_col .image").each(function(image) {
      image.next().style.width = Math.min(480, image.down().getWidth()) + "px";
    });
  }
}

var Order = {
  initialize: function(){
    $$('.order input[type=text]').invoke('defaultValueActsAsHint');
    $$('.order .order-form').each(function(form) {
      form.observe('submit', Order.handleSubmit);
    });
  },
  
  handleSubmit: function(event) {
    event.stop();

    var form = Order.form = event.element();

    if (Order.valid()) {
      Order.send();
      form.disable().down('.spinner').appear({ duration: 0.5 });
    } else {
      form.down('.error').activate()
    }
  },
  
  valid: function() {
    var valid = true, termsCheckBox = Order.form.down('input[type=checkbox]'), termsLabel = Order.form.down('.terms');
    
    Order.form.select('input.text:not(.order-company)').each(function(input) {
      var value = $F(input);
      
      if (value == input._default || value.blank() || (input.match('.order-email') && !Validation.isValidEmail(value))) {
        valid = false;
        input.removeClassName('hint').addClassName('error');
      } else {
        input.removeClassName('error');
      }
    });
    
    if (!termsCheckBox.checked) {
      valid = false;
      $(termsCheckBox, termsLabel).invoke('addClassName', 'error');
    } else {
      $(termsCheckBox, termsLabel).invoke('removeClassName', 'error');
    }
    
    return valid;
  },
  
  send: function() {
    var container = Order.form.up('.order');
    
    Order.form.request({
      onSuccess: function(response) {
        container.blindUp({ duration: 0.5, afterFinish: function() {
          container.insert({ after: '<div class="order-sent">Ihre Bestellung war erfolgreich!</div>'})  
        }});
      }
    });
  }
};

var Document = {
  initialize: function() {
    Event.observe(window, 'load', function() {
      when('body.designers#page_index', Designer.setEachRowHeightFromTallest);
      when('body.designers#page_show', Designer.setJewelDescriptionsWidthFromImage);
    });
    when('body.newsletter', Newsletter.initialize);
    when('body.schmuck-des-monats', Newsletter.initialize);
    when('body.stores', Field.Custom.initialize.curry({ select: { width: 220 } }));
    when('body.designers#page_show', Designer.initialize);
    when('body.designers#page_index', Field.Custom.initialize.curry({ select: { width: 220 } }));
    when('body.publications#page_index', Field.Custom.initialize.curry({ select: { width: 220 } }));
    when('body.publications', Order.initialize);
    when('body.subscriptions', Subscribe.initialize);
  }
}

// --------------------------------------------------------------------

Document.initialize();
Cufon.replace('h2');
Cufon.now()