text: var _ = require('lodash');
  var inherits = require('util').inherits;
  var Element = require('../element');
  
  var reservedTagList = [
      'annotation-xml', 'color-profile', 'font-face', 'font-face-src',
      'font-face-uri', 'font-face-format', 'font-face-name', 'missing-glyph'
  ];
  
  function isReservedTag(name) {
      for (var i = 0; i < reservedTagList.length; i++) {
          if (name === reservedTagList[i]) {
              return true;
          }
      }
  }
  
  // element registry (maps tag names to definitions)
  var registry = {};
  
  function getRegisteredDefinition(name) {
      if (name) {
          return registry[name.toLowerCase()];
      }
  }
  
  var CustomElements = {};
  
  function generateConstructor(definition) {
      return function() {
          var element = new Element(definition.__name);
          element.__proto__ = definition.ctor.prototype;
          return element;
      }
  }
  
  CustomElements.register = function (name, options) {
      // TODO: probably should clone options instead of mutating it
      var definition = options || {};
      if (!name) {
          throw new Error('CustomElements.register: first argument `name` must not be empty');
      }
  
      if (name.indexOf('-') < 0) {
          throw new Error('CustomElements.register: first argument (\'name\') must contain a dash (\'-\'). Argument provided was \'' + String(name) + '\'.');
      }
      // prevent registering reserved names
      if (isReservedTag(name)) {
          throw new Error('Failed to execute \'register\' on \'CustomElements\': Registration failed for type \'' + String(name) + '\'. The type name is invalid.');
      }
      // elements may only be registered once
      if (getRegisteredDefinition(name)) {
          throw new Error('DuplicateDefinitionError: a type with name \'' + String(name) + '\' is already registered');
      }
  
      // must have a prototype, default to an extension of HTMLElement
      if (!definition.prototype) {
          definition.prototype = Element.prototype;
          // TODO: probably should throw if no prototype, check spec
          // throw new Error('Options missing required prototype property');
      }
  
      // record name
      definition.__name = name.toLowerCase();
  
      // Run custom element constructor generation algorithm with PROTOTYPE
      definition.ctor = generateConstructor(definition);
  
      if(definition.extends){
  
          var parentDefinition = getRegisteredDefinition(definition.extends);
  
          if(!parentDefinition){
              throw new Error('Failed to extends')
          }else{
              definition = _.merge({}, parentDefinition, definition);
              definition.ctor.prototype = _.extend({}, parentDefinition.prototype, definition.prototype);
          }
  
      }else{
          // TODO: when no prototype
          definition.ctor.prototype = _.extend({}, Element.prototype, definition.prototype);
      }
  
      // force our .constructor to be our actual constructor
      definition.prototype.constructor = definition.ctor;
      // if initial parsing is complete
      // upgrade any pre-existing nodes of this type
  
      // Register the DEFINITION
      registry[definition.__name] = definition;
  
      return definition.ctor;
  };
  
  CustomElements.create = function (tag) {
  
      var definition = getRegisteredDefinition(tag);
      if (definition) {
          var element = new definition.ctor();
          if(definition.attributes){
              element.attributes = _.clone(definition.attributes);
          }
          return element;
      }
  };
  
  CustomElements.instanceof = function(obj, base) {
      return obj instanceof base;
  };
  
  CustomElements.getRegistered = function(is){
      if(is){
          return _.map(registry, function(definition, key){
              if(definition.is == is){
                  return definition.__name
              }
          }).filter(function(name){
              return name;
          });
      }else{
          return Object.keys(registry)
      }
  };
  
  CustomElements.getRegisteredDefinition = getRegisteredDefinition;
  
  CustomElements.getPublishedAttributes = function (name) {
      var definition = getRegisteredDefinition(name);
      if (definition && definition.attributes) {
          return Object.keys(definition.attributes)
      } else {
          return []
      }
  };
  
  CustomElements.registry = registry;
  
  module.exports = CustomElements;
  