text: 'use strict';
  
  exports.type = 'full';
  
  exports.active = true;
  
  exports.description = 'removes unused IDs and minifies used';
  
  exports.params = {
      remove: true,
      minify: true,
      prefix: ''
  };
  
  var referencesProps = require('./_collections').referencesProps,
      regReferencesUrl = /^url\(("|')?#(.+?)\1\)$/,
      regReferencesHref = /^#(.+?)$/,
      regReferencesBegin = /^(\w+?)\./,
      styleOrScript = ['style', 'script'],
      generateIDchars = [
          'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z',
          'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z'
      ],
      maxIDindex = generateIDchars.length - 1;
  
  /**
   * Remove unused and minify used IDs
   * (only if there are no any <style> or <script>).
   *
   * @param {Object} item current iteration item
   * @param {Object} params plugin params
   *
   * @author Kir Belevich
   */
  exports.fn = function(data, params) {
  
      var currentID,
          currentIDstring,
          IDs = {},
          referencesIDs = {},
          idPrefix = 'id-', // prefix IDs so that values like '__proto__' don't break the work
          hasStyleOrScript = false;
  
      /**
       * Bananas!
       *
       * @param {Array} items input items
       * @return {Array} output items
       */
      function monkeys(items) {
  
          for (var i = 0; i < items.content.length; i++) {
  
              var item = items.content[i],
                  match;
  
              // check if <style> of <script> presents
              if (item.isElem(styleOrScript)) {
                  hasStyleOrScript = true;
              }
  
              // …and don't remove any ID if yes
              if (!hasStyleOrScript) {
  
                  if (item.isElem()) {
  
                      item.eachAttr(function(attr) {
                          // save IDs
                          if (attr.name === 'id') {
                              if (idPrefix + attr.value in IDs) {
                                  item.removeAttr('id');
                              } else {
                                  IDs[idPrefix + attr.value] = item;
                              }
                          }
  
                          // save IDs url() references
                          else if (referencesProps.indexOf(attr.name) > -1) {
                              match = attr.value.match(regReferencesUrl);
  
                              if (match) {
                                  if (referencesIDs[idPrefix + match[2]]) {
                                      referencesIDs[idPrefix + match[2]].push(attr);
                                  } else {
                                      referencesIDs[idPrefix + match[2]] = [attr];
                                  }
                              }
                          }
  
                          // save IDs href references
                          else if (
                              attr.name === 'xlink:href' && (match = attr.value.match(regReferencesHref)) ||
                              attr.name === 'begin' && (match = attr.value.match(regReferencesBegin))
                          ) {
                              if (referencesIDs[idPrefix + match[1]]) {
                                  referencesIDs[idPrefix + match[1]].push(attr);
                              } else {
                                  referencesIDs[idPrefix + match[1]] = [attr];
                              }
                          }
                      });
  
                  }
  
                  // go deeper
                  if (item.content) {
                      monkeys(item);
                  }
  
              }
  
          }
  
          return items;
  
      }
  
      data = monkeys(data);
  
      if (!hasStyleOrScript) {
  
  
          for (var k in referencesIDs) {
              if (IDs[k]) {
  
                  // replace referenced IDs with the minified ones
                  if (params.minify) {
  
                      currentIDstring = getIDstring(currentID = generateID(currentID), params);
                      IDs[k].attr('id').value = currentIDstring;
  
                      referencesIDs[k].forEach(function(attr) {
                          k = k.replace(idPrefix, '');
                          attr.value = attr.value
                              .replace('#' + k, '#' + currentIDstring)
                              .replace(k + '.', currentIDstring + '.');
                      });
  
                  }
  
                  // don't remove referenced IDs
                  delete IDs[idPrefix + k];
  
              }
          }
  
          // remove non-referenced IDs attributes from elements
          if (params.remove) {
  
              for(var ID in IDs) {
                  IDs[ID].removeAttr('id');
              }
  
          }
  
      }
  
      return data;
  
  };
  
  /**
   * Generate unique minimal ID.
   *
   * @param {Array} [currentID] current ID
   * @return {Array} generated ID array
   */
  function generateID(currentID) {
  
      if (!currentID) return [0];
  
      currentID[currentID.length - 1]++;
  
      for(var i = currentID.length - 1; i > 0; i--) {
          if (currentID[i] > maxIDindex) {
              currentID[i] = 0;
  
              if (currentID[i - 1] !== undefined) {
                  currentID[i - 1]++;
              }
          }
      }
  
      if (currentID[0] > maxIDindex) {
          currentID[0] = 0;
          currentID.unshift(0);
      }
  
      return currentID;
  
  }
  
  /**
   * Get string from generated ID array.
   *
   * @param {Array} arr input ID array
   * @return {String} output ID string
   */
  function getIDstring(arr, params) {
  
      var str = params.prefix;
  
      arr.forEach(function(i) {
          str += generateIDchars[i];
      });
  
      return str;
  
  }
  