/* -*- Mode: MooTools; tab-width: 2; indent-tabs-mode: nil; -*- */

/**
 * Ninja Form Validator
 * ---
 * The Definitive Form Validation Script
 *
 * @version $Id: main.js 781 2009-09-24 16:04:51Z vincenzo.acinapura $
 * @author Vincenzo Acinapura, <vincenzo.acinapura@cayenne.it>
 */
var NinjaFormValidator = new Class({

  Implements: [Options, Events],

  options: {
    labels: {},
    onCheck: $empty,
    onError: $empty,
    onInit: $empty,
    SID: ''
  },

  initialize: function(formElement, validations, options) {
    if (!$(formElement)) {
      throw new Error('Il form con id ' + formElement + ' non è stato trovato in questa pagina.');
    }

    // console.time('form-initialization');
    this.setOptions(options);

    this.formElement = $(formElement);
    this.formElement.set('autocomplete', 'off');
    this.validations = validations;

    this.fields = $H(validations).getKeys().map(function(el) { return $(el); }).clean();
    this.fields.combine($$('.required, .at-least-one input, .at-least-one select, .at-least-one textarea'));

    this.fields.each(function(field) {
      if (!field.hasClass('ignore') || (field.get('type') == 'hidden')) {
        field.validate(validations[field.get('id')]);
      }
    });

    this.submitElements = this.formElement.getElements('input[type=submit], input[type=image], button, .submit');

    this.partialSubmitRequest = new Request({
      'url': '/services.php?' + this.options.SID + '&type=partialsubmit'
    });

    this.submitElements.addEvent('click', function(e) {
      e.stop();
      this.check();
    }.bind(this));

    this.fireEvent('onInit', this);
    // console.timeEnd('form-inizialization');
  },

  check: function() {
    // console.time('form-check');
    this.errors = new Hash();
    this.waiting_fields = new Hash();

    this.fireEvent('check');

    this.fields = this.fields.filter(function(field) {
      return !field.hasClass('ignore-during-submission');
    });

    this.fields.each(function(field) {
      if (!field.retrieve('validator').check(field)) {
        this.errors.include(field.get('id'), field.retrieve('errors'));
      }
      if (field.get('wait')) {
        this.waiting_fields.include(field);
      }
    }, this);

    this.errors = this.errors.filter(function(value) {
      return value !== null;
    });

    if (this.errors.getLength() > 0 || this.waiting_fields.getLength() > 0) {
      this.fireEvent('error');
      this.createErrorLog(this.errors);
    } else {
      if (!this.submitting) {
        $('sottoscrivi-btn').set('disabled', 'disabled');
        this.submitting = true;
        this.formElement.submit();
      };
    }
    // console.timeEnd('form-check');
  },

  createErrorLog: function(errors) {

    new Fx.SmoothScroll().toTop();

    if ($('error_log')) { $('error_log').dispose(); }

    this.error_log = new Element('div', {
      'id': 'error_log'
    });

    this.formElement.grab(this.error_log, 'before');

    this.error_log.grab(new Element('h1', {
      text: 'Attenzione: alcuni campi non risultano corretti.'
    }));

    this.error_log.grab(new Element('p', {
      text: 'Ti preghiamo di controllare i seguenti campi:'
    }));

    var error_list = new Element('ul');
    this.error_log.grab(error_list);

    errors.each(function(error, key) {

      if (this.options.labels[key] !== null) {
        error_list.grab(new Element('li').grab(new Element('a', {
          'class': 'scroll',
          'rel': key,
          'href': '#' + key,
          'text': this.options.labels[key] || key,
          events: {
            'click': function(e) {
              e.stop();
              new SmoothScroll({ offset: { y: -60 } }).toElement(key);
              $(key).focus();
              (function() { $(key).getParent('p').highlight() }).delay(600);
            }
          }
        })));
      }
    }, this);
  },

  disposeErrorLog: function() {
    if ($('error_log')) { $('error_log').dispose(); }
  },

  partialSubmit: function() {
    this.partialSubmitRequest.send(this.formElement.toQueryString());
  }

});

var NinjaFieldValidator = new Class({

  Implements: [Options, Events],

  options: {

    paused: true,

    requiredText: 'Campo obbligatorio.',

    onCheck: function(field) {
      // console.log('Controllo il campo %s', field.get('id'));
    },

    onSuccess: function(field) {
      // console.info('Il campo %s è valido', field.get('id'));

      if ($('error_log')) {

        if (document.getElement('a[rel=' + field.id + ']')) {
          document.getElement('a[rel=' + field.id + ']').getParent().dispose();
        }

        if ($('error_log').getElements('li').length === 0) {
          nfv.disposeErrorLog();
        }
      }
    },

    onError: function(field, errors) {
      // console.warn('Il campo %s non è valido.', field.get('id'));
    }
  },

  initialize: function(el, testers, options) {
    this.setOptions(options);
    this.element = el;
    this.special_fields = ['comune_fornitura_gas','attuale_fornitore_luce', 'attuale_fornitore_gas'];
    this.paused = this.special_fields.contains(el.get('id')) ? false : this.options.paused;
    testers = testers || [];

    el.store('testers', testers.filter(function(el) { return ($type(el) == 'array') || ($type(el) == 'string'); }));
    callbacks = testers.filter(function(el) { return ($type(el) == 'object'); });
    callbacks = $H(callbacks[0]);

    if (['input', 'select', 'textarea'].contains(el.get('tag'))) {
      el.addEvents({
        change: (function(e) {
          this.paused = false;
          if (e.target.get('tag') == 'select') {
            this.check(e.target);
          }
        }).bind(this),

        blur: (function(e) {
          if (!this.paused) {
            if (['text', 'password'].contains(e.target.get('type')) || e.target.get('tag') == 'textarea') {
              this.check(e.target);
            }
          }
        }).bind(this),

        click: (function(e) {
          if (['radio', 'checkbox'].contains(e.target.get('type'))) {
            this.check(e.target);
          }
        }).bind(this)
      });

      callbacks.each(function(fn, key) {
        el.addEvent(key, fn);
      });

    } else {
      throw new Error("Il campo " + el.get('id') + " deve essere un elemento di tipo input, select o textarea.");
    }
  },

  check: function(field) {
    // console.time('field-validation');
    this.fireEvent('check', field);

    if ((field.get('type') == 'hidden') || field.hasClass('ignore')) {
      this.markAsValid(field);
      return true;
    }

    this.testers = field.retrieve('testers') || [];
    this.errors = new Hash();
    
    if (field.hasClass('required') && !field.retrieve('testers').flatten().contains('required')) {
      this.testers.include(['required']);
    }

    this.testers.each((function(tester, key) {
      if ($type(tester) == 'array') {
        if ($H(NinjaFieldValidator.testers).has(tester[0]) && tester[0] !== 'remote') {
          var params = (tester[1]) ? tester[1].params : null;
          var valid = NinjaFieldValidator.testers[tester[0]].run([params], field);
          if ($type(valid) == 'boolean') {
            if (!valid) {
              errorMsg = (tester[1]) ? tester[1].errorMsg : this.options.requiredText;
              this.errors.include(tester[0], errorMsg);
            }
          }
        }
      } else if ($type(tester) == 'string') {
        if ($H(NinjaFieldValidator.testers).has(tester) && tester !== 'remote') {
          var valid = NinjaFieldValidator.testers[tester].apply(field);
          if ($type(valid) == 'boolean') {
            if (!valid) {
              this.errors.include(tester, this.options.requiredText);
            }
          }
        }
      }

      if (tester[0] == 'remote') { field.store('remote-validator', tester[1] || {}); }

    }).bind(this));

    if (this.errors.getLength() === 0) {
      if (field.retrieve('remote-validator')) {
        NinjaFieldValidator.testers.remote.run([field.retrieve('remote-validator')], field);
        // console.timeEnd('field-validation');
        return null;
      } else {
        this.markAsValid(field);
        // console.timeEnd('field-validation');
        return true;
      }
    } else {
      if (this.isFieldHiddenToUser(field)) {
        this.markAsValid(field);
        // console.timeEnd('field-validation');
        return true;
      } else if (field.getParent('.at-least-one')) {
        this.checkSiblings(field);
      } else {
        this.markAsInvalid(field, this.errors);
        // console.timeEnd('field-validation');
        return false;
      }
    }

    // console.timeEnd('field-validation');
  },

  checkSiblings: function(field) {
    if (!field.retrieve('ignore-siblings')) {

      var fieldsInGroup = $$(field.getParent('.at-least-one').getElements('input', 'select', 'textarea'));

      var siblings = fieldsInGroup.filter(function(el) {
        return el.retrieve('validator') && el.get('id') !== field.get('id');
      });

      // console.group('Controllo campi correlati di ' + field.get('id'));
      siblings.each(function(el) {
        el.retrieve('validator').paused = false;
        this.check(el.store('ignore-siblings', true));
        el.eliminate('ignore-siblings');
      }, this);
      // console.groupEnd();

      if (!this.areAllBlank(fieldsInGroup) && (this.siblingsAreValid(siblings) && NinjaFieldValidator.testers.blank.apply(field))) {
        this.markAsValid(field);
        siblings.each(function(el) {
          el.markAsValid();
        });
        return true;
      } else {
        this.markAsInvalid(field, this.errors);
        return false;
      }
    } else {
      this.markAsInvalid(field, this.errors);
      return false;
    }
  },

  markAsValid: function(field) {
    if (field.get('type') == 'hidden') { return true; }
    this.fireEvent('success', field);
    field.fireEvent('onSuccess', field);
    field.getParent('p').removeClass('error');
    field.getParent('p').set('style', '');
    field.getParent('p').getElements('span.advice, .ask').dispose();
    field.eliminate('errors');
    field.store('valid', true);
  },

  markAsInvalid: function(field, errors) {
    
    field.fireEvent('onError', field);
    
    if (!field.retrieve('escape')) {
      this.fireEvent('error', [field, errors]);
      field.getParent('p').addClass('error');
      field.getParent('p').getElements('span.advice').dispose();
    
      if (!field.getParent('p').getElement('.ask')) {
        errors.each(function(value, key, hash) {
          field.getParent('p').adopt(new Element('span', {
            'class': 'advice',
            text: hash[key] || this.options.requiredText
          }));
        }, this);
      }
    
      field.eliminate('errors').store('errors', errors);
      field.removeClass('ignore-during-submission');
      field.store('valid', false);
    }
  },

  overlay: function(params) {
    var askBox = new Element('div', { 'class': 'overlay_ask' });
    askBox.adopt(new Element('h3', { text: "Attenzione:" }));
    askBox.adopt(new Element('div', { 'html': params.content }));
    var actionsBox = new Element('div', { 'class': 'actionsBox' });
    askBox.adopt(actionsBox);
    var actions = params.actions || [['Modifica', $empty], ['Interrompi', $empty]];
    
    actions.each(function(el, i) {
      actionsBox.adopt(new Element('a', {
        'text': el[0],
        'href': ($type(el[1]) == 'string' ? el[1] : 'javascript:void(0)'),
        'class': 'closeSticky',
        'events': {
          'click': ($type(el[1]) == 'function' ? el[1].bind(this.element) : $empty)
        }
      }));
    }, this);
    
    document.body.eliminate('modal').store('modal', new StickyWin.Modal({
      content: askBox,
      relativeTo: document.body,
      modalOptions: {
        hideOnClick: false,
        position: 'center',
        modalStyle: {
          'background-color':'#333',
          'opacity': '.65'
        }
      }
    }));
  },

  ask: function() {
    this.element.set('disabled', 'disabled');
    this.element.set('wait', true);
    this.element.getParent('p').getElements('.ask').dispose();

    askBox = new Element('div', { 'class': 'ask' });
    p = new Element('p', { 'html': arguments[0] || 'Sei sicuro di quello che hai scritto?'});

    c = new Element('a', {
      text: arguments[1] || 'Si, lo sono',
      href: 'javascript: void(0)',
      events: {
        click: (function() {
          this.element.erase('disabled');
          this.element.erase('wait');
          this.element.addClass('ignore-during-submission');
          this.element.markAsValid();
        }).bind(this)
      }
    });

    m = new Element('a', {
      text: arguments[2] || 'Modifica',
      href: 'javascript: void(0)',
      events: {
        click: (function() {
          this.element.erase('disabled');
          this.element.erase('wait');
          this.element.focus();
          this.element.getParent('p').getElements('.ask').dispose();
        }).bind(this)
      }
    });

    askBox.adopt(p);
    p.adopt(new Element('br'));
    p.adopt(c);
    p.adopt(new Element('span', { text: ' · '}));
    p.adopt(m);
    askBox.inject(this.element, 'after');
    this.element.getParent('p').getElements('span.advice').dispose();
  },

  isFieldHiddenToUser: function(field) {
    hidden = field.getParents().some(function(el, i) {
      return (el.getStyle('display') == 'none');
    });

    return hidden;
  },

  areAllBlank: function(fields) {
    return fields.every(function(el) {
      return NinjaFieldValidator.testers.blank.apply(el);
    }, this);
  },

  siblingsAreValid: function(fields) {
    return fields.every(function(el) {
      return !!el.retrieve('valid') || NinjaFieldValidator.testers.blank.apply(el);
    }, this);
  }

});

NinjaFieldValidator.testers = new Hash({

  remote: function(options) {
    var data = {
      type:  this.get('name'),
      value: this.get('value')
    };

    if ($type(options.data) == 'function') {
      data = options.data.apply(this);
    }

    var req = new Request.JSON({
      url: '/services.php',
      data: data,
      onRequest: (function() {
        this.store('wait', true);
      }).bind(this),
      onSuccess: (function(response) {
        this.eliminate('wait');
        if ($type(options.callback) == 'function') {
          return options.callback.run([response], this) ? this.markAsValid() : this.markAsInvalid($H({remote: response.message}));
        } else {
          return (response.result == "success") ? this.markAsValid() : this.markAsInvalid($H({remote: response.message}));
        }
      }).bind(this)
    });

    if (!this.retrieve('wait')) { req.post(); }

    return null;
  },

  regexp: function(regexp) {
    if ($type(regexp) !== 'regexp') {
      throw new Error("Attenzione: al validatore regexp deve essere passata una regexp.");
    }

    return this.value.trim().test(regexp);
  },

  alpha: function() {
    return this.value.trim().test(/^[a-zA-Z_.' ]+$/);
  },

  alphanumeric: function() {
    var re = new RegExp("^[a-zA-Z0-9_àèéìòùÀÈÉÌÒÙ.' ]+$");
    return this.value.trim().test(re);
  },

  price: function() {
    var re = new RegExp("^([0-9]+)|([0-9]+,[0-9]{1,2})$");
    return this.value.trim().test(re);
  },

  numeric: function() {
    var re = new RegExp("^[0-9]+$");
    return this.value.trim().test(re);
  },

  more_than: function(sum) {
    return (this.value.toInt() > sum) ? true : false;
  },

  less_than: function(sum) {
    return (this.value.toInt() < sum) ? true : false;
  },

  in_range: function(range) {
    return (this.value.toInt() >= range[0] && this.value.toInt() <= range[1]) ? true : false;
  },

  phone: function() {
    return this.value.trim().test(/^[\d() .:\-\+#]+$/);
  },

  mail: function() {
    return this.value.trim().test(/^[\w-\.]{1,}\@([\da-zA-Z\-]{1,}\.){1,}[\da-zA-Z\-]{2,4}$/);
  },

  cap: function() {
    return this.value.trim().test(/^[0-9]{5}$/);
  },

  iban: function() {

    var b = this.value.trim().toUpperCase(), c, i, k, r, s;

    if (b.length < 5) { return false; }

    s = b.substring(4) + b.substring(0, 4);

    for (i = 0, r = 0; i < s.length; i++ ) {
      c = s.charCodeAt(i);

      if (48 <= c && c <= 57) {
        if (i == s.length-4 || i == s.length-3) { return false; }
        k = c - 48;
      } else if (65 <= c && c <= 90) {
        if (i == s.length-2 || i == s.length-1) { return false; }
        k = c - 55;
      } else { return false; }

      if (k > 9) {
        r = (100 * r + k) % 97;
      } else {
        r = (10 * r + k) % 97;
      }
    }

    if (r != 1) { return false; }

    return true;
  },

  length: function(params) {
    if (params.length < 2) {
      throw new Error("Attenzione: al validatore length devono essere passati 2 parametri.");
    }

    var regexp = new RegExp("^.{" + params[0] + "," + params[1] + "}$");
    return this.value.trim().test(regexp);
  },

  vat: function() {
    return this.value.trim().test(/^[0123456789]{11}$/);
  },

  tax_number: function() {
    var taxNumber = this.value;
    taxNumber = taxNumber.trim().toUpperCase();
    var pattern = /^[A-Z]{6}\d{2}[A-Z]\d{2}[A-Z]\d{3}[A-Z]$/;
    if (!pattern.test(taxNumber)) { return false; }

    var set1 = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
    var set2 = "ABCDEFGHIJABCDEFGHIJKLMNOPQRSTUVWXYZ";
    var setEven = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
    var setOdd  = "BAKPLCQDREVOSFTGUHMINJWZYX";
    var s = 0;
    var i = 0;

    for(i = 1; i < 14; i += 2) {
      s += setEven.indexOf(set2.charAt(set1.indexOf(taxNumber.charAt(i))));
    }

    for(i = 0; i < 15; i += 2) {
      s += setOdd.indexOf(set2.charAt(set1.indexOf(taxNumber.charAt(i))));
    }

    if (s % 26 != taxNumber.charCodeAt(15) - "A".charCodeAt(0)) {
      return false;
    } else {
      return true;
    }
  },

  checked: function() {
    return this.checked;
  },

  not_edison: function() {
    return !this.value.trim().test(/EDISON ENERGIA/i);
  },

  required: function() {
    return !(this.get('value') === '' || (['checkbox', 'radio'].contains(this.get('type')) && !this.checked));
  },

  yes: function() {
    return this.value.trim().test(/^Y$/);
  },

  blank: function() {
    return this.get('value') === '' || (['checkbox', 'radio'].contains(this.get('type')) && !this.checked);
  },

  codice_pod: function() {
    var code = this.value.trim().toUpperCase();
    if (code.test(/^IT002/)) {
      return code.test(/^IT002E[0-9]{7}A$/i);
    } else {
      return code.test(/^IT[0-9]{3}E[0-9]{8}$/i);
    }
  },

  codice_pdr: function() {
    return this.value.trim().test(/^[0-9]{14}$/);
  }
});

Element.implement({

  validate: function(testers, options) {
    this.store('validator', new NinjaFieldValidator(this, testers, options));
    return this;
  },

  markAsValid: function() {
    this.retrieve('validator').markAsValid(this);
    return this;
  },

  check: function() {
    this.retrieve('validator').check(this);
  },

  markAsInvalid: function(errors) {
    this.retrieve('validator').markAsInvalid(this, errors);
    return this;
  }

});

Element.implement({

  actAsToggler: function() {
    var element = $(this.get('id') + '_details');
    if (this.get('tag') == 'a') {
      this.addEvent('click', function() {
        if (Browser.Engine.trident) {
          element.toggle();
        } else {
          element.get('reveal').toggle();
        }
      }, this);
    } else if ((this.get('tag') == 'input') && ['checkbox', 'radio'].contains(this.get('type'))) {
      (this.checked) ? element.show() : element.hide();
      this.getParent('p').addEvent('click', (function(e) {
        this.checked ? element.show() : element.hide();
      }).bind(this));
    }
  }
});

var AddressHelper = new Class({

  Implements: [Options, Events],

  options: {
    requestEvent: 'blur'
  },

  initialize: function(addressKind, options) {
    this.setOptions(options);
    var that = this;
    this.index = (addressKind == "fornitura") ? 0 : 1;
    this.addressKind = addressKind;
    this.indirizzo = $('indirizzo_' + this.addressKind);
    this.comune    = $('comune_' + this.addressKind)?$('comune_' + this.addressKind):$('comune_' + this.addressKind + '_gas');
    this.provincia = $('provincia_' + this.addressKind);
    this.cap       = $('cap_' + this.addressKind);

    var req = new Request.JSON({

      url: "/services.php",

      onRequest: function() {
        that.isWaiting = true;

        // document.getElements('em.spinner').dispose();
        // that.toggleSpinner('show');
      },

      onSuccess: function(response, text) {
        that.isWaiting = false;

        if (document.getElement("#suggestions_" + that.addressKind)) {
          document.getElement("#suggestions_" + that.addressKind).destroy();
        }

        if ($defined(response) && response.length > 0) {
          that.suggestions = response;
          that.buildTheList();
        } else {
          // console.log("Nessuna ambiguità.");
        }

        // that.toggleSpinner('hide');
      }
    });
    
    this.indirizzo.addEvent(this.options.requestEvent, function(e) {
      if (that.comune.value !== "" && that.provincia.value !== "" && !that.isWaiting) {
        (function() {
          req.send({
            data: {
              'type': 'normalizza',
              'addressKind': that.addressKind,
              'indirizzo': that.indirizzo.value,
              'comune': that.comune.value,
              'provincia': that.provincia.value,
              'cap': that.cap.value,
              'regionKey': window.regionKey[that.index],
              'provinceKey': window.provinceKey[that.index],
              'streetKey': window.streetKey[that.index],
              'townKey': window.townKey[that.index]
            }
          });
        }).delay(100);
      }
    });
  },

  buildTheList: function() {

    var that = this;

    if ($type(this.suggestions) == "array") {
      this.label = document.getElement('label[for=indirizzo_' + this.addressKind + ']');

      this.suggestions_div = new Element('div', {
        'id': "suggestions_" + this.addressKind,
        'class': 'address_helper'
      }).inject(this.indirizzo, 'after');

      if (this.suggestions.length > 1) {
        this.suggestions_div.set('html', "<strong class='enfasi'>Seleziona una delle scelte proposte...</strong>");
      } else {
        this.suggestions_div.set('html', "<strong class='enfasi'>Forse intendevi...</strong>");
      }

      this.suggestions_ul = new Element('ul', {
        'id': "suggestions_list"
      }).inject(this.suggestions_div);

      this.suggestions.each(function(suggestion, i) {
        var li = new Element(that.buildTheVoice(suggestion)).inject(that.suggestions_ul);
      });

      this.dismiss = new Element('a', {
        'html': "chiudi",
        'id': 'dismiss_suggestions',
        'styles': {
          'color': '#333',
          'cursor': 'pointer',
          'float': 'right'
        },
        'events': {
          'click': function() {
            var fx = new Fx.Reveal(that.suggestions_div).dissolve();
          }
        }
      }).inject(this.suggestions_div, 'top');

    } else {
      // console.log('Il servizio di supporto al normalizzatore ha ricevuto una risposta anomala dal server');
    }
  },

  buildTheVoice: function(suggestion) {

    var that = this;

    return new Element('li', {
      'html': "<a href='javascript:void(0)'><strong>" + suggestion.indirizzo + "</strong>, " + suggestion.comune + " (" + suggestion.cap + ")</a>",
      'events': {
        'click': function() {
          that.indirizzo.value = suggestion.indirizzo;
          that.comune.value = suggestion.comune;
          that.provincia.value = suggestion.provincia;
          that.cap.value = suggestion.cap;

          var fx = new Fx.Reveal('suggestions_' + that.addressKind).dissolve();
          that.indirizzo.focus();
        }
      }
    });
  },

  toggleSpinner: function(mode) {

    if (mode == "show") {
      this.spinner = new Element('em', {
        'class': 'spinner',
        'events': {
          'click': function(e) {
            e.stop();
          }
        }
      });

      document.getElement('#indirizzo_' + this.addressKind).grab(this.spinner, 'after');
    } else {
      this.spinner.dispose();
    }
  }
});

var Collapsable = new Class({
  Extends: Fx.Reveal,
  initialize: function(clicker, section, options) {
    this.clicker = $(clicker);
    this.section = $(section);
    this.parent(this.section, options);
    this.addEvents();
  },
  addEvents: function(){
    this.clicker.addEvent('click', this.toggle.bind(this));
  }
});

var Faqs = function(selector) {
  $$(selector).each((function(el, i) {
    this[i] = new Fx.Accordion(el.getElements('dt'), el.getElements('dd'), {
      show: -1,
      alwaysHide: true,
      onActive: function(h, d) {
        h.addClass('active');
      },
      onBackground: function(h, d) {
        h.removeClass('active');
      }
    });
  }).bind(this));
}

if (typeof window.console == 'undefined') {
    window.console = {};
    var firebugNames = ["log", "debug", "info", "warn", "error", "assert", "dir", "dirxml", "group",
                        "groupEnd", "time", "timeEnd", "count", "trace", "profile", "profileEnd"];
    firebugNames.each(function(el, i, names) { window.console[names[i]] = $empty; });
}
