text: 'use strict';
  
  var DEFAULT_SETTINGS = {
          length: 20,
          repeat: 0,
          iteration: 8,
          lower: null,
          upper: null,
          number: null,
          dash: null,
          space: null,
          symbol: null,
          isVaultCompatible: true,
          useBrowserAction: false,
  
          autosend: false,
          servicename: 'prefix',
          defServicename: '',
  
          pwFieldList: [
              'pass', 'pass1', 'pass2', 'Pass', 'passwd', 'Passwd', 'password', 'Password', 'PASSWORD',
              'pw', 'PW', 'passwort', 'Passwort', 'ap_password', 'login_password', 'user_password',
              'user_pass', 'pwd', 'rpass', 'mypassword', 'myPassword', 'myregpassword'
          ],
          userFieldList: [
              'mail', 'Mail', 'email', 'Email', 'EMail', 'e-mail', 'E-Mail', 'eMail', 'login', 'Login',
              'user', 'User', 'username', 'Username', 'ap_email', 'userid', 'Userid', 'userId', 'UserId',
              'login_email', 'user_login', 'signin-email', 'j_username', 'session[username_or_email]'
          ],
          ccTldList: [
              'com', 'co', 'ar', 'net', 'org', 'jp', 'se', 'ae', 'me', 'plc', 'ac', 'ltd', 'gov', 'kids', 'us'
          ],
  
          serviceExceptions: {
              'alternate.de': {
                  length: 15
              },
              'o2online.de': {
                  space: 0,
                  dash: 0,
                  symbol: 0
              },
              'o2.co.uk': {
                  space: 0,
                  dash: 0,
                  symbol: 0
              },
              'o2online.ie': {
                  space: 0,
                  dash: 0,
                  symbol: 0
              },
              'o2.cz': {
                  space: 0,
                  dash: 0,
                  symbol: 0
              },
              'o2.sk': {
                  space: 0,
                  dash: 0,
                  symbol: 0
              },
              'npmjs.org': {
                  symbol: 0
              },
              'hosteurope.de': {
                  length: 16,
                  symbols: '!%&/()=*+#.,:;'
              },
              'wordpress.com': {
                  symbols: '!%&()=*+#.,:;'
              },
              'zooplus.de': {
                  length: 20,
                  symbols: '!#$%&\'()*+,./=?[\\]^{}~'
              },
              'nodejitsu.com': {
                  space: 0,
                  dash: 0,
                  symbol: 0
              },
              'check24.de': {
                  length: 15
              },
              'kabeldeutschland.de': {
                  space: 0
              },
              'unitymedia.de': {
                  space: 0,
                  symbols: '/+=]',
                  dash: 0
              },
              'paket.de': {
                  length: 13,
                  space: 0,
                  symbols: '!&/()=?*+'
              },
              'postpay.de': {
                  length: 13,
                  space: 0,
                  symbols: '!&/()=?*+'
              },
              'adac.de': {
                  space: 0,
                  symbols: '#/*-+_'
              },
              'seedmatch.de': {
                  space: 0
              }
          },
  
          imgUrl: 'images/close.png'
      },
      TYPES = 'lower upper number dash space symbol'.split(' '),
      UUID = '1e9632fa-e9b3-4a49-ab3b-e17256b83ff9';
  
  var $ = function (selector) {
      var element = document.getElementById(selector);
  
      if (!element) {
          element = document.getElementsByName(selector)[0];
      }
  
      return element;
  };
  
  var on = function (element, event, listener) {
      if (!element) {
          return;
      }
  
      if ('string' === typeof event) {
          event = [event];
      }
  
      for (var i = 0, n = event.length; i < n; i++) {
          if (element.addEventListener) {
              element.addEventListener(event[i], listener, true);
          } else if (element.attachEvent) {
              element.attachEvent('on' + event[i], listener);
          } else {
              throw new Error('element not of type EventTarget');
          }
      }
  };
  
  /**
   * handles specific domain extensions
   */
  var DomainService = {
      _domainName: '',
      _rules: null,
      _setPasswordRules: null,
      _setDomainname: null,
      getPasswordRules: null,
      getDomainname: null
  
  };
  
  DomainService._setDomainname = function _setDomainname(domain) {
      var domainname = domain || document.domain,
          domainparts = domainname.split('.');
  
      // if it's an IP we just skip the domain handling
      if (this._isIp4(domainparts)) {
          this._domainName = domainname;
          return;
      }
  
      if (2 < domainparts.length) {
          domainname = domainparts[domainparts.length - 2] + '.' + domainparts[domainparts.length - 1];
  
          if (Helper.isCcTld(domainparts[domainparts.length - 2])) {
              domainname = domainparts[domainparts.length - 3] + '.' + domainname;
          }
      }
  
      this._domainName = domainname;
  };
  
  DomainService._setPasswordRules = function _setPasswordRules(settings) {
      if (settings && settings[this._domainName]) {
          this._rules = settings[this._domainName];
      }
  };
  
  DomainService._isIp4 = function _isIp4(domainparts) {
      if (domainparts.length !== 4) {
          return false;
      }
  
      for (var i = 1, l = domainparts.length; i < l; i++) {
          if (parseInt(domainparts[i]) < 0 || parseInt(domainparts[i]) > 255) {
              return false;
          }
      }
      return true;
  };
  
  DomainService.getDomainname = function getDomainname() {
      return this._domainName;
  };
  
  DomainService.getPasswordRules = function getPasswordRules() {
      return this._rules;
  };
  
  DomainService.init = function init(ruleSettings, domain) {
      this._setDomainname(domain);
      this._setPasswordRules(ruleSettings);
  
      return this;
  };
  
  /**
   * commonly used logic
   */
  var Helper = {};
  
  /**
   *
   * @param {Array} list
   *
   * @returns {*}
   */
  Helper.getElementFromList = function (list) {
      var i, n, element;
  
      for (i = 0, n = list.length; i < n; i++) {
          element = $(list[i]);
  
          if (element) {
              return element;
          }
      }
  
      return null;
  };
  
  /**
   * checks if the given name is in the cctld list
   *
   * @param {String} name
   *
   * @returns {boolean}
   */
  Helper.isCcTld = function (name) {
      var i, n;
  
      for (i = 0, n = DEFAULT_SETTINGS.ccTldList.length; i < n; i++) {
          if (name === DEFAULT_SETTINGS.ccTldList[i]) {
              return true;
          }
      }
  
      return false;
  };
  
  /**
   * fix problem if a password field id does exist more than once on a page
   *
   * @param {NodeList} nodeList
   */
  Helper.fixDuplicateIds = function (nodeList) {
      var i, j, n = nodeList.length;
  
      for (i = 0; i < n; i++) {
          for (j = i + 1; j < n; j++) {
              if (nodeList[i].id === nodeList[j].id) {
                  nodeList[j].id += '-' + Math.random().toString(16).substring(7);
              }
          }
      }
  };
  
  /**
   * check if given pwField is part of an overlay
   *
   * @param {Object} pwField
   *
   * @returns {Boolean}
   */
  Helper.isOverlay = function (pwField) {
      // @todo this is easy password handler (overlay) only
      return pwField && pwField.id && pwField.id.match(/^easy-password-passphrase/);
  };
  
  /**
   * check if given pwField already has an overlay
   *
   * @param {Object} pwField
   *
   * @returns {Boolean}
   */
  Helper.hasOverlay = function (pwField) {
      // @todo this is easy password handler (overlay) only
      return pwField && pwField.id && $(BASE_NAME + 'generator-overlay-' + pwField.id);
  };
  
  /**
   * cancel event bubbling and capturing
   *
   * @param {Object} event
   */
  Helper.cancelEventPropagation = function (event) {
      if (event.stopImmediatePropagation) {
          // as long it is the first registered event: even captured events are stopped
          event.stopImmediatePropagation();
      }
  
      if (event.cancelBubble != null) {
          event.cancelBubble = true;
      }
  };
  
  /**
   * merges second object into first
   *
   * @param {Object} first
   * @param {Object} second
   *
   * @returns {Object}
   */
  Helper.mergeObject = function (first, second) {
      var attrName;
  
      if (first && second) {
          for (attrName in second) {
              if (!second.hasOwnProperty(attrName)) {
                  continue;
              }
              if ('object' === typeof first[attrName] && typeof second[attrName] === 'object') {
                  first[attrName] = this.mergeObject(first[attrName], second[attrName]);
              } else {
                  first[attrName] = second[attrName];
              }
          }
      }
  
      return first;
  };
  
  /**
   * gets the current password field's login form
   *
   * @param {Object} pwField
   *
   * @returns {Number|Boolean}
   */
  Helper.getLoginForm = function (pwField) {
      for (var i = 0, n = document.forms.length; i < n; i++) {
          for (var o = 0, m = document.forms[i].length; o < m; o++) {
              if (pwField.id === document.forms[i][o].id) {
                  return i;
              }
              if (pwField.name === document.forms[i][o].name) {
                  return i;
              }
          }
      }
  
      return false;
  };
  
  /**
   * generates a (more or less) random servicename
   *
   * @returns {string}
   */
  Helper.getRandomServicename = function () {
      return Math.random().toString(36).substring(7) + '@';
  };
  
  /**
   * get input type radio values
   *
   * @param {string} name
   * @returns {*}
   */
  Helper.getRadio = function (name) {
      var inputs = document.getElementsByTagName('input'), input, i, n;
  
      for (i = 0, n = inputs.length; i < n; i++) {
          input = inputs[i];
          if (input.type === 'radio' && input.name === name && input.checked) {
              return input.value;
          }
      }
  
      return null;
  };
  
  /**
   * Set radio by name
   *
   * @param {String} name
   * @param {int} value
   */
  Helper.setRadio = function (name, value) {
      var inputs = document.getElementsByTagName('input'), input, i, n;
  
      for (i = 0, n = inputs.length; i < n; i++) {
          input = inputs[i];
          if (input.type === 'radio' && input.name === name) {
              switch (input.value) {
                  case 'required':
                      input.checked = (value && value > 0);
                      break;
                  case 'allowed':
                      input.checked = (value === null);
                      break;
                  case 'forbidden':
                      input.checked = (value === 0);
                      break;
              }
          }
      }
  };
  
  /**
   * Get current vault type settings
   *
   * @param {Object} settings
   * @param {int} requiredLength
   * @returns {Object}
   */
  Helper.getTypeSettings = function (settings, requiredLength) {
      var i, n, value;
  
      for (i = 0, n = TYPES.length; i < n; i++) {
          value = this.getRadio(TYPES[i]);
  
          if (value === 'forbidden') {
              settings[TYPES[i]] = 0;
          } else if (value === 'required') {
              settings[TYPES[i]] = requiredLength;
          } else {
              settings[TYPES[i]] = null;
          }
      }
  
      return settings;
  };
  
  /**
   * set all vault radio types
   *
   * @param {Object} settings
   */
  Helper.setTypeSettings = function (settings) {
      var i, n;
  
      for (i = 0, n = TYPES.length; i < n; i++) {
          Helper.setRadio(TYPES[i], settings[TYPES[i]]);
      }
  };
  