(function (global, factory) {
  typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) :
  typeof define === 'function' && define.amd ? define(['exports'], factory) :
  (factory((global.jss = {})));
}(this, (function (exports) { 'use strict';

  var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) { return typeof obj; } : function (obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; };

  var isBrowser = (typeof window === "undefined" ? "undefined" : _typeof(window)) === "object" && (typeof document === "undefined" ? "undefined" : _typeof(document)) === 'object' && document.nodeType === 9;

  /**
   * Link rule with CSSStyleRule and nested rules with corresponding nested cssRules if both exists.
   */
  function linkRule(rule, cssRule) {
    rule.renderable = cssRule;
    if (rule.rules && cssRule.cssRules) rule.rules.link(cssRule.cssRules);
  }

  var global$1 = (typeof global !== "undefined" ? global :
              typeof self !== "undefined" ? self :
              typeof window !== "undefined" ? window : {});

  if (typeof global$1.setTimeout === 'function') ;
  if (typeof global$1.clearTimeout === 'function') ;

  // from https://github.com/kumavis/browser-process-hrtime/blob/master/index.js
  var performance = global$1.performance || {};
  var performanceNow =
    performance.now        ||
    performance.mozNow     ||
    performance.msNow      ||
    performance.oNow       ||
    performance.webkitNow  ||
    function(){ return (new Date()).getTime() };

  /**
   * Similar to invariant but only logs a warning if the condition is not met.
   * This can be used to log issues in development environments in critical
   * paths. Removing the logging code for production environments will keep the
   * same logic and follow the same code paths.
   */

  var __DEV__ = "development" !== 'production';

  var warning = function() {};

  if (__DEV__) {
    warning = function(condition, format, args) {
      var len = arguments.length;
      args = new Array(len > 2 ? len - 2 : 0);
      for (var key = 2; key < len; key++) {
        args[key - 2] = arguments[key];
      }
      if (format === undefined) {
        throw new Error(
          '`warning(condition, format, ...args)` requires a warning ' +
          'message argument'
        );
      }

      if (format.length < 10 || (/^[s\W]*$/).test(format)) {
        throw new Error(
          'The warning format should be able to uniquely identify this ' +
          'warning. Please, use a more descriptive format than: ' + format
        );
      }

      if (!condition) {
        var argIndex = 0;
        var message = 'Warning: ' +
          format.replace(/%s/g, function() {
            return args[argIndex++];
          });
        if (typeof console !== 'undefined') {
          console.error(message);
        }
        try {
          // This error was thrown as a convenience so that you can use this stack
          // to find the callsite that caused this warning to fire.
          throw new Error(message);
        } catch(x) {}
      }
    };
  }

  var warning_1 = warning;

  var join = function join(value, by) {
    var result = '';
    for (var i = 0; i < value.length; i++) {
      // Remove !important from the value, it will be readded later.
      if (value[i] === '!important') break;
      if (result) result += by;
      result += value[i];
    }
    return result;
  };

  /**
   * Converts array values to string.
   *
   * `margin: [['5px', '10px']]` > `margin: 5px 10px;`
   * `border: ['1px', '2px']` > `border: 1px, 2px;`
   * `margin: [['5px', '10px'], '!important']` > `margin: 5px 10px !important;`
   * `color: ['red', !important]` > `color: red !important;`
   */
  function toCssValue(value) {
    var ignoreImportant = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : false;

    if (!Array.isArray(value)) return value;

    var cssValue = '';

    // Support space separated values via `[['5px', '10px']]`.
    if (Array.isArray(value[0])) {
      for (var i = 0; i < value.length; i++) {
        if (value[i] === '!important') break;
        if (cssValue) cssValue += ', ';
        cssValue += join(value[i], ' ');
      }
    } else cssValue = join(value, ', ');

    // Add !important, because it was ignored.
    if (!ignoreImportant && value[value.length - 1] === '!important') {
      cssValue += ' !important';
    }

    return cssValue;
  }

  /**
   * Indent a string.
   * http://jsperf.com/array-join-vs-for
   */
  function indentStr(str, indent) {
    var result = '';
    for (var index = 0; index < indent; index++) {
      result += '  ';
    }return result + str;
  }

  /**
   * Converts a Rule to CSS string.
   */
  function toCss(selector, style) {
    var options = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {};

    var result = '';

    if (!style) return result;

    var _options$indent = options.indent,
        indent = _options$indent === undefined ? 0 : _options$indent;
    var fallbacks = style.fallbacks;


    indent++;

    // Apply fallbacks first.
    if (fallbacks) {
      // Array syntax {fallbacks: [{prop: value}]}
      if (Array.isArray(fallbacks)) {
        for (var index = 0; index < fallbacks.length; index++) {
          var fallback = fallbacks[index];
          for (var prop in fallback) {
            var value = fallback[prop];
            if (value != null) {
              result += '\n' + indentStr(prop + ': ' + toCssValue(value) + ';', indent);
            }
          }
        }
      } else {
        // Object syntax {fallbacks: {prop: value}}
        for (var _prop in fallbacks) {
          var _value = fallbacks[_prop];
          if (_value != null) {
            result += '\n' + indentStr(_prop + ': ' + toCssValue(_value) + ';', indent);
          }
        }
      }
    }

    for (var _prop2 in style) {
      var _value2 = style[_prop2];
      if (_value2 != null && _prop2 !== 'fallbacks') {
        result += '\n' + indentStr(_prop2 + ': ' + toCssValue(_value2) + ';', indent);
      }
    }

    // Allow empty style in this case, because properties will be added dynamically.
    if (!result && !options.allowEmpty) return result;

    indent--;
    result = indentStr(selector + ' {' + result + '\n', indent) + indentStr('}', indent);

    return result;
  }

  var _typeof$1 = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) {
    return typeof obj;
  } : function (obj) {
    return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj;
  };

  var classCallCheck = function (instance, Constructor) {
    if (!(instance instanceof Constructor)) {
      throw new TypeError("Cannot call a class as a function");
    }
  };

  var createClass = function () {
    function defineProperties(target, props) {
      for (var i = 0; i < props.length; i++) {
        var descriptor = props[i];
        descriptor.enumerable = descriptor.enumerable || false;
        descriptor.configurable = true;
        if ("value" in descriptor) descriptor.writable = true;
        Object.defineProperty(target, descriptor.key, descriptor);
      }
    }

    return function (Constructor, protoProps, staticProps) {
      if (protoProps) defineProperties(Constructor.prototype, protoProps);
      if (staticProps) defineProperties(Constructor, staticProps);
      return Constructor;
    };
  }();

  var _extends = Object.assign || function (target) {
    for (var i = 1; i < arguments.length; i++) {
      var source = arguments[i];

      for (var key in source) {
        if (Object.prototype.hasOwnProperty.call(source, key)) {
          target[key] = source[key];
        }
      }
    }

    return target;
  };

  var StyleRule = function () {
    function StyleRule(key, style, options) {
      classCallCheck(this, StyleRule);
      this.type = 'style';
      this.isProcessed = false;
      var sheet = options.sheet,
          Renderer = options.Renderer,
          selector = options.selector;

      this.key = key;
      this.options = options;
      this.style = style;
      if (selector) this.selectorText = selector;
      this.renderer = sheet ? sheet.renderer : new Renderer();
    }

    /**
     * Set selector string.
     * Attention: use this with caution. Most browsers didn't implement
     * selectorText setter, so this may result in rerendering of entire Style Sheet.
     */


    createClass(StyleRule, [{
      key: 'prop',


      /**
       * Get or set a style property.
       */
      value: function prop(name, value) {
        // It's a getter.
        if (value === undefined) return this.style[name];

        // Don't do anything if the value has not changed.
        if (this.style[name] === value) return this;

        value = this.options.jss.plugins.onChangeValue(value, name, this);

        var isEmpty = value == null || value === false;
        var isDefined = name in this.style;

        // Value is empty and wasn't defined before.
        if (isEmpty && !isDefined) return this;

        // We are going to remove this value.
        var remove = isEmpty && isDefined;

        if (remove) delete this.style[name];else this.style[name] = value;

        // Renderable is defined if StyleSheet option `link` is true.
        if (this.renderable) {
          if (remove) this.renderer.removeProperty(this.renderable, name);else this.renderer.setProperty(this.renderable, name, value);
          return this;
        }

        var sheet = this.options.sheet;

        if (sheet && sheet.attached) {
          warning_1(false, 'Rule is not linked. Missing sheet option "link: true".');
        }
        return this;
      }

      /**
       * Apply rule to an element inline.
       */

    }, {
      key: 'applyTo',
      value: function applyTo(renderable) {
        var json = this.toJSON();
        for (var prop in json) {
          this.renderer.setProperty(renderable, prop, json[prop]);
        }return this;
      }

      /**
       * Returns JSON representation of the rule.
       * Fallbacks are not supported.
       * Useful for inline styles.
       */

    }, {
      key: 'toJSON',
      value: function toJSON() {
        var json = {};
        for (var prop in this.style) {
          var value = this.style[prop];
          if ((typeof value === 'undefined' ? 'undefined' : _typeof$1(value)) !== 'object') json[prop] = value;else if (Array.isArray(value)) json[prop] = toCssValue(value);
        }
        return json;
      }

      /**
       * Generates a CSS string.
       */

    }, {
      key: 'toString',
      value: function toString(options) {
        var sheet = this.options.sheet;

        var link = sheet ? sheet.options.link : false;
        var opts = link ? _extends({}, options, { allowEmpty: true }) : options;
        return toCss(this.selector, this.style, opts);
      }
    }, {
      key: 'selector',
      set: function set$$1(selector) {
        if (selector === this.selectorText) return;

        this.selectorText = selector;

        if (!this.renderable) return;

        var hasChanged = this.renderer.setSelector(this.renderable, selector);

        // If selector setter is not implemented, rerender the rule.
        if (!hasChanged && this.renderable) {
          var renderable = this.renderer.replaceRule(this.renderable, this);
          if (renderable) this.renderable = renderable;
        }
      }

      /**
       * Get selector string.
       */
      ,
      get: function get$$1() {
        return this.selectorText;
      }
    }]);
    return StyleRule;
  }();

  function unwrapExports (x) {
  	return x && x.__esModule && Object.prototype.hasOwnProperty.call(x, 'default') ? x['default'] : x;
  }

  function createCommonjsModule(fn, module) {
  	return module = { exports: {} }, fn(module, module.exports), module.exports;
  }

  var ponyfill = createCommonjsModule(function (module, exports) {

  Object.defineProperty(exports, "__esModule", {
  	value: true
  });
  exports['default'] = symbolObservablePonyfill;
  function symbolObservablePonyfill(root) {
  	var result;
  	var _Symbol = root.Symbol;

  	if (typeof _Symbol === 'function') {
  		if (_Symbol.observable) {
  			result = _Symbol.observable;
  		} else {
  			result = _Symbol('observable');
  			_Symbol.observable = result;
  		}
  	} else {
  		result = '@@observable';
  	}

  	return result;
  }});

  unwrapExports(ponyfill);

  var lib = createCommonjsModule(function (module, exports) {

  Object.defineProperty(exports, "__esModule", {
    value: true
  });



  var _ponyfill2 = _interopRequireDefault(ponyfill);

  function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }

  var root; /* global window */


  if (typeof self !== 'undefined') {
    root = self;
  } else if (typeof window !== 'undefined') {
    root = window;
  } else if (typeof global$1 !== 'undefined') {
    root = global$1;
  } else {
    root = module;
  }

  var result = (0, _ponyfill2['default'])(root);
  exports['default'] = result;
  });

  unwrapExports(lib);

  var symbolObservable = lib;

  var isObservable = (function (value) {
    return value && value[symbolObservable] && value === value[symbolObservable]();
  });

  var isArray = Array.isArray;


  function cloneStyle(style) {
    // Support empty values in case user ends up with them by accident.
    if (style == null) return style;

    // Support string value for SimpleRule.
    var typeOfStyle = typeof style === 'undefined' ? 'undefined' : _typeof$1(style);

    if (typeOfStyle === 'string' || typeOfStyle === 'number' || typeOfStyle === 'function') {
      return style;
    }

    // Support array for FontFaceRule.
    if (isArray(style)) return style.map(cloneStyle);

    // Support Observable styles.  Observables are immutable, so we don't need to
    // copy them.
    if (isObservable(style)) return style;

    var newStyle = {};
    for (var name in style) {
      var value = style[name];
      if ((typeof value === 'undefined' ? 'undefined' : _typeof$1(value)) === 'object') {
        newStyle[name] = cloneStyle(value);
        continue;
      }
      newStyle[name] = value;
    }

    return newStyle;
  }

  /**
   * Create a rule instance.
   */
  function createRule() {
    var name = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : 'unnamed';
    var decl = arguments[1];
    var options = arguments[2];
    var jss = options.jss;

    var declCopy = cloneStyle(decl);

    var rule = jss.plugins.onCreateRule(name, declCopy, options);
    if (rule) return rule;

    // It is an at-rule and it has no instance.
    if (name[0] === '@') {
      warning_1(false, '[JSS] Unknown at-rule %s', name);
    }

    return new StyleRule(name, declCopy, options);
  }

  var CSS = global$1.CSS;

  var env$1 = "development";

  var escapeRegex = /([[\].#*$><+~=|^:(),"'`])/g;

  var escape = (function (str) {
    // We don't need to escape it in production, because we are not using user's
    // input for selectors, we are generating a valid selector.
    if (env$1 === 'production') return str;

    if (!CSS || !CSS.escape) {
      return str.replace(escapeRegex, '\\$1');
    }

    return CSS.escape(str);
  });

  /**
   * Contains rules objects and allows adding/removing etc.
   * Is used for e.g. by `StyleSheet` or `ConditionalRule`.
   */
  var RuleList = function () {

    // Original styles object.
    function RuleList(options) {
      var _this = this;

      classCallCheck(this, RuleList);
      this.map = {};
      this.raw = {};
      this.index = [];

      this.update = function (name, data) {
        var _options = _this.options,
            plugins = _options.jss.plugins,
            sheet = _options.sheet;

        if (typeof name === 'string') {
          plugins.onUpdate(data, _this.get(name), sheet);
        } else {
          for (var index = 0; index < _this.index.length; index++) {
            plugins.onUpdate(name, _this.index[index], sheet);
          }
        }
      };

      this.options = options;
      this.classes = options.classes;
    }

    /**
     * Create and register rule.
     *
     * Will not render after Style Sheet was rendered the first time.
     */


    // Used to ensure correct rules order.

    // Rules registry for access by .get() method.
    // It contains the same rule registered by name and by selector.


    createClass(RuleList, [{
      key: 'add',
      value: function add(name, decl, options) {
        var _options2 = this.options,
            parent = _options2.parent,
            sheet = _options2.sheet,
            jss = _options2.jss,
            Renderer = _options2.Renderer,
            generateClassName = _options2.generateClassName;


        options = _extends({
          classes: this.classes,
          parent: parent,
          sheet: sheet,
          jss: jss,
          Renderer: Renderer,
          generateClassName: generateClassName
        }, options);

        if (!options.selector && this.classes[name]) {
          options.selector = '.' + escape(this.classes[name]);
        }

        this.raw[name] = decl;

        var rule = createRule(name, decl, options);

        var className = void 0;

        if (!options.selector && rule instanceof StyleRule) {
          className = generateClassName(rule, sheet);
          rule.selector = '.' + escape(className);
        }

        this.register(rule, className);

        var index = options.index === undefined ? this.index.length : options.index;
        this.index.splice(index, 0, rule);

        return rule;
      }

      /**
       * Get a rule.
       */

    }, {
      key: 'get',
      value: function get$$1(name) {
        return this.map[name];
      }

      /**
       * Delete a rule.
       */

    }, {
      key: 'remove',
      value: function remove(rule) {
        this.unregister(rule);
        this.index.splice(this.indexOf(rule), 1);
      }

      /**
       * Get index of a rule.
       */

    }, {
      key: 'indexOf',
      value: function indexOf(rule) {
        return this.index.indexOf(rule);
      }

      /**
       * Run `onProcessRule()` plugins on every rule.
       */

    }, {
      key: 'process',
      value: function process() {
        var plugins = this.options.jss.plugins;
        // We need to clone array because if we modify the index somewhere else during a loop
        // we end up with very hard-to-track-down side effects.

        this.index.slice(0).forEach(plugins.onProcessRule, plugins);
      }

      /**
       * Register a rule in `.map` and `.classes` maps.
       */

    }, {
      key: 'register',
      value: function register(rule, className) {
        this.map[rule.key] = rule;
        if (rule instanceof StyleRule) {
          this.map[rule.selector] = rule;
          if (className) this.classes[rule.key] = className;
        }
      }

      /**
       * Unregister a rule.
       */

    }, {
      key: 'unregister',
      value: function unregister(rule) {
        delete this.map[rule.key];
        if (rule instanceof StyleRule) {
          delete this.map[rule.selector];
          delete this.classes[rule.key];
        }
      }

      /**
       * Update the function values with a new data.
       */

    }, {
      key: 'link',


      /**
       * Link renderable rules with CSSRuleList.
       */
      value: function link(cssRules) {
        var map = this.options.sheet.renderer.getUnescapedKeysMap(this.index);

        for (var i = 0; i < cssRules.length; i++) {
          var cssRule = cssRules[i];
          var _key = this.options.sheet.renderer.getKey(cssRule);
          if (map[_key]) _key = map[_key];
          var rule = this.map[_key];
          if (rule) linkRule(rule, cssRule);
        }
      }

      /**
       * Convert rules to a CSS string.
       */

    }, {
      key: 'toString',
      value: function toString(options) {
        var str = '';
        var sheet = this.options.sheet;

        var link = sheet ? sheet.options.link : false;

        for (var index = 0; index < this.index.length; index++) {
          var rule = this.index[index];
          var css = rule.toString(options);

          // No need to render an empty rule.
          if (!css && !link) continue;

          if (str) str += '\n';
          str += css;
        }

        return str;
      }
    }]);
    return RuleList;
  }();

  /* eslint-disable-next-line no-use-before-define */

  var StyleSheet = function () {
    function StyleSheet(styles, options) {
      var _this = this;

      classCallCheck(this, StyleSheet);

      this.update = function (name, data) {
        if (typeof name === 'string') {
          _this.rules.update(name, data);
        } else {
          _this.rules.update(name);
        }
        return _this;
      };

      this.attached = false;
      this.deployed = false;
      this.linked = false;
      this.classes = {};
      this.options = _extends({}, options, {
        sheet: this,
        parent: this,
        classes: this.classes
      });
      this.renderer = new options.Renderer(this);
      this.rules = new RuleList(this.options);

      for (var _name in styles) {
        this.rules.add(_name, styles[_name]);
      }

      this.rules.process();
    }

    /**
     * Attach renderable to the render tree.
     */


    createClass(StyleSheet, [{
      key: 'attach',
      value: function attach() {
        if (this.attached) return this;
        if (!this.deployed) this.deploy();
        this.renderer.attach();
        if (!this.linked && this.options.link) this.link();
        this.attached = true;
        return this;
      }

      /**
       * Remove renderable from render tree.
       */

    }, {
      key: 'detach',
      value: function detach() {
        if (!this.attached) return this;
        this.renderer.detach();
        this.attached = false;
        return this;
      }

      /**
       * Add a rule to the current stylesheet.
       * Will insert a rule also after the stylesheet has been rendered first time.
       */

    }, {
      key: 'addRule',
      value: function addRule(name, decl, options) {
        var queue = this.queue;

        // Plugins can create rules.
        // In order to preserve the right order, we need to queue all `.addRule` calls,
        // which happen after the first `rules.add()` call.

        if (this.attached && !queue) this.queue = [];

        var rule = this.rules.add(name, decl, options);
        this.options.jss.plugins.onProcessRule(rule);

        if (this.attached) {
          if (!this.deployed) return rule;
          // Don't insert rule directly if there is no stringified version yet.
          // It will be inserted all together when .attach is called.
          if (queue) queue.push(rule);else {
            this.insertRule(rule);
            if (this.queue) {
              this.queue.forEach(this.insertRule, this);
              this.queue = undefined;
            }
          }
          return rule;
        }

        // We can't add rules to a detached style node.
        // We will redeploy the sheet once user will attach it.
        this.deployed = false;

        return rule;
      }

      /**
       * Insert rule into the StyleSheet
       */

    }, {
      key: 'insertRule',
      value: function insertRule(rule) {
        var renderable = this.renderer.insertRule(rule);
        if (renderable && this.options.link) linkRule(rule, renderable);
      }

      /**
       * Create and add rules.
       * Will render also after Style Sheet was rendered the first time.
       */

    }, {
      key: 'addRules',
      value: function addRules(styles, options) {
        var added = [];
        for (var _name2 in styles) {
          added.push(this.addRule(_name2, styles[_name2], options));
        }
        return added;
      }

      /**
       * Get a rule by name.
       */

    }, {
      key: 'getRule',
      value: function getRule(name) {
        return this.rules.get(name);
      }

      /**
       * Delete a rule by name.
       * Returns `true`: if rule has been deleted from the DOM.
       */

    }, {
      key: 'deleteRule',
      value: function deleteRule(name) {
        var rule = this.rules.get(name);

        if (!rule) return false;

        this.rules.remove(rule);

        if (this.attached && rule.renderable) {
          return this.renderer.deleteRule(rule.renderable);
        }

        return true;
      }

      /**
       * Get index of a rule.
       */

    }, {
      key: 'indexOf',
      value: function indexOf(rule) {
        return this.rules.indexOf(rule);
      }

      /**
       * Deploy pure CSS string to a renderable.
       */

    }, {
      key: 'deploy',
      value: function deploy() {
        this.renderer.deploy();
        this.deployed = true;
        return this;
      }

      /**
       * Link renderable CSS rules from sheet with their corresponding models.
       */

    }, {
      key: 'link',
      value: function link() {
        var cssRules = this.renderer.getRules();

        // Is undefined when VirtualRenderer is used.
        if (cssRules) this.rules.link(cssRules);
        this.linked = true;
        return this;
      }

      /**
       * Update the function values with a new data.
       */

    }, {
      key: 'toString',


      /**
       * Convert rules to a CSS string.
       */
      value: function toString(options) {
        return this.rules.toString(options);
      }
    }]);
    return StyleSheet;
  }();

  var PluginsRegistry = function () {
    function PluginsRegistry() {
      classCallCheck(this, PluginsRegistry);
      this.hooks = {
        onCreateRule: [],
        onProcessRule: [],
        onProcessStyle: [],
        onProcessSheet: [],
        onChangeValue: [],
        onUpdate: []

        /**
         * Call `onCreateRule` hooks and return an object if returned by a hook.
         */
      };
    }

    createClass(PluginsRegistry, [{
      key: 'onCreateRule',
      value: function onCreateRule(name, decl, options) {
        for (var i = 0; i < this.hooks.onCreateRule.length; i++) {
          var rule = this.hooks.onCreateRule[i](name, decl, options);
          if (rule) return rule;
        }
        return null;
      }

      /**
       * Call `onProcessRule` hooks.
       */

    }, {
      key: 'onProcessRule',
      value: function onProcessRule(rule) {
        if (rule.isProcessed) return;
        var sheet = rule.options.sheet;

        for (var i = 0; i < this.hooks.onProcessRule.length; i++) {
          this.hooks.onProcessRule[i](rule, sheet);
        }

        // $FlowFixMe
        if (rule.style) this.onProcessStyle(rule.style, rule, sheet);

        rule.isProcessed = true;
      }

      /**
       * Call `onProcessStyle` hooks.
       */

    }, {
      key: 'onProcessStyle',
      value: function onProcessStyle(style, rule, sheet) {
        var nextStyle = style;

        for (var i = 0; i < this.hooks.onProcessStyle.length; i++) {
          nextStyle = this.hooks.onProcessStyle[i](nextStyle, rule, sheet);
          // $FlowFixMe
          rule.style = nextStyle;
        }
      }

      /**
       * Call `onProcessSheet` hooks.
       */

    }, {
      key: 'onProcessSheet',
      value: function onProcessSheet(sheet) {
        for (var i = 0; i < this.hooks.onProcessSheet.length; i++) {
          this.hooks.onProcessSheet[i](sheet);
        }
      }

      /**
       * Call `onUpdate` hooks.
       */

    }, {
      key: 'onUpdate',
      value: function onUpdate(data, rule, sheet) {
        for (var i = 0; i < this.hooks.onUpdate.length; i++) {
          this.hooks.onUpdate[i](data, rule, sheet);
        }
      }

      /**
       * Call `onChangeValue` hooks.
       */

    }, {
      key: 'onChangeValue',
      value: function onChangeValue(value, prop, rule) {
        var processedValue = value;
        for (var i = 0; i < this.hooks.onChangeValue.length; i++) {
          processedValue = this.hooks.onChangeValue[i](processedValue, prop, rule);
        }
        return processedValue;
      }

      /**
       * Register a plugin.
       * If function is passed, it is a shortcut for `{onProcessRule}`.
       */

    }, {
      key: 'use',
      value: function use(plugin) {
        for (var name in plugin) {
          if (this.hooks[name]) this.hooks[name].push(plugin[name]);else warning_1(false, '[JSS] Unknown hook "%s".', name);
        }
      }
    }]);
    return PluginsRegistry;
  }();

  var SimpleRule = function () {
    function SimpleRule(key, value, options) {
      classCallCheck(this, SimpleRule);
      this.type = 'simple';
      this.isProcessed = false;

      this.key = key;
      this.value = value;
      this.options = options;
    }

    /**
     * Generates a CSS string.
     */
    // eslint-disable-next-line no-unused-vars


    createClass(SimpleRule, [{
      key: 'toString',
      value: function toString(options) {
        if (Array.isArray(this.value)) {
          var str = '';
          for (var index = 0; index < this.value.length; index++) {
            str += this.key + ' ' + this.value[index] + ';';
            if (this.value[index + 1]) str += '\n';
          }
          return str;
        }

        return this.key + ' ' + this.value + ';';
      }
    }]);
    return SimpleRule;
  }();

  /**
   * Rule for @keyframes
   */
  var KeyframesRule = function () {
    function KeyframesRule(key, frames, options) {
      classCallCheck(this, KeyframesRule);
      this.type = 'keyframes';
      this.isProcessed = false;

      this.key = key;
      this.options = options;
      this.rules = new RuleList(_extends({}, options, { parent: this }));

      for (var name in frames) {
        this.rules.add(name, frames[name], _extends({}, this.options, {
          parent: this,
          selector: name
        }));
      }

      this.rules.process();
    }

    /**
     * Generates a CSS string.
     */


    createClass(KeyframesRule, [{
      key: 'toString',
      value: function toString() {
        var options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : { indent: 1 };

        var inner = this.rules.toString(options);
        if (inner) inner += '\n';
        return this.key + ' {\n' + inner + '}';
      }
    }]);
    return KeyframesRule;
  }();

  /**
   * Conditional rule for @media, @supports
   */
  var ConditionalRule = function () {
    function ConditionalRule(key, styles, options) {
      classCallCheck(this, ConditionalRule);
      this.type = 'conditional';
      this.isProcessed = false;

      this.key = key;
      this.options = options;
      this.rules = new RuleList(_extends({}, options, { parent: this }));

      for (var name in styles) {
        this.rules.add(name, styles[name]);
      }

      this.rules.process();
    }

    /**
     * Get a rule.
     */


    createClass(ConditionalRule, [{
      key: 'getRule',
      value: function getRule(name) {
        return this.rules.get(name);
      }

      /**
       * Get index of a rule.
       */

    }, {
      key: 'indexOf',
      value: function indexOf(rule) {
        return this.rules.indexOf(rule);
      }

      /**
       * Create and register rule, run plugins.
       */

    }, {
      key: 'addRule',
      value: function addRule(name, style, options) {
        var rule = this.rules.add(name, style, options);
        this.options.jss.plugins.onProcessRule(rule);
        return rule;
      }

      /**
       * Generates a CSS string.
       */

    }, {
      key: 'toString',
      value: function toString() {
        var options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : { indent: 1 };

        var inner = this.rules.toString(options);
        return inner ? this.key + ' {\n' + inner + '\n}' : '';
      }
    }]);
    return ConditionalRule;
  }();

  var FontFaceRule = function () {
    function FontFaceRule(key, style, options) {
      classCallCheck(this, FontFaceRule);
      this.type = 'font-face';
      this.isProcessed = false;

      this.key = key;
      this.style = style;
      this.options = options;
    }

    /**
     * Generates a CSS string.
     */


    createClass(FontFaceRule, [{
      key: 'toString',
      value: function toString(options) {
        if (Array.isArray(this.style)) {
          var str = '';
          for (var index = 0; index < this.style.length; index++) {
            str += toCss(this.key, this.style[index]);
            if (this.style[index + 1]) str += '\n';
          }
          return str;
        }

        return toCss(this.key, this.style, options);
      }
    }]);
    return FontFaceRule;
  }();

  var ViewportRule = function () {
    function ViewportRule(key, style, options) {
      classCallCheck(this, ViewportRule);
      this.type = 'viewport';
      this.isProcessed = false;

      this.key = key;
      this.style = style;
      this.options = options;
    }

    /**
     * Generates a CSS string.
     */


    createClass(ViewportRule, [{
      key: 'toString',
      value: function toString(options) {
        return toCss(this.key, this.style, options);
      }
    }]);
    return ViewportRule;
  }();

  var classes = {
    '@charset': SimpleRule,
    '@import': SimpleRule,
    '@namespace': SimpleRule,
    '@keyframes': KeyframesRule,
    '@media': ConditionalRule,
    '@supports': ConditionalRule,
    '@font-face': FontFaceRule,
    '@viewport': ViewportRule,
    '@-ms-viewport': ViewportRule

    /**
     * Generate plugins which will register all rules.
     */
  };var plugins = Object.keys(classes).map(function (key) {
    // https://jsperf.com/indexof-vs-substr-vs-regex-at-the-beginning-3
    var re = new RegExp('^' + key);
    var RuleClass = classes[key];
    var onCreateRule = function onCreateRule(name, decl, options) {
      return re.test(name) ? new RuleClass(name, decl, options) : null;
    };
    return { onCreateRule: onCreateRule };
  });

  var observablesPlugin = {
    onCreateRule: function onCreateRule(name, decl, options) {
      if (!isObservable(decl)) return null;

      // Cast `decl` to `Observable`, since it passed the type guard.
      var style$ = decl;

      var rule = createRule(name, {}, options);

      // TODO
      // Call `stream.subscribe()` returns a subscription, which should be explicitly
      // unsubscribed from when we know this sheet is no longer needed.
      style$.subscribe(function (style) {
        for (var prop in style) {
          rule.prop(prop, style[prop]);
        }
      });

      return rule;
    },
    onProcessRule: function onProcessRule(rule) {
      if (!(rule instanceof StyleRule)) return;
      var styleRule = rule;
      var style = styleRule.style;

      var _loop = function _loop(prop) {
        var value = style[prop];
        if (!isObservable(value)) return 'continue';
        delete style[prop];
        value.subscribe({
          next: function next(nextValue) {
            styleRule.prop(prop, nextValue);
          }
        });
      };

      for (var prop in style) {
        var _ret = _loop(prop);

        if (_ret === 'continue') continue;
      }
    }
  };

  // A symbol replacement.
  var now = Date.now();
  var fnValuesNs = 'fnValues' + now;
  var fnStyleNs = 'fnStyle' + ++now;

  var functionsPlugin = {
    onCreateRule: function onCreateRule(name, decl, options) {
      if (typeof decl !== 'function') return null;
      var rule = createRule(name, {}, options);
      rule[fnStyleNs] = decl;
      return rule;
    },
    onProcessStyle: function onProcessStyle(style, rule) {
      var fn = {};
      for (var prop in style) {
        var value = style[prop];
        if (typeof value !== 'function') continue;
        delete style[prop];
        fn[prop] = value;
      }
      rule = rule;
      rule[fnValuesNs] = fn;
      return style;
    },
    onUpdate: function onUpdate(data, rule) {
      // It is a rules container like for e.g. ConditionalRule.
      if (rule.rules instanceof RuleList) {
        rule.rules.update(data);
        return;
      }
      if (!(rule instanceof StyleRule)) return;

      rule = rule;

      // If we have a fn values map, it is a rule with function values.
      if (rule[fnValuesNs]) {
        for (var prop in rule[fnValuesNs]) {
          rule.prop(prop, rule[fnValuesNs][prop](data));
        }
      }

      rule = rule;

      var fnStyle = rule[fnStyleNs];

      // If we have a style function, the entire rule is dynamic and style object
      // will be returned from that function.
      if (fnStyle) {
        var style = fnStyle(data);
        for (var _prop in style) {
          rule.prop(_prop, style[_prop]);
        }
      }
    }
  };

  /**
   * Sheets registry to access them all at one place.
   */
  var SheetsRegistry = function () {
    function SheetsRegistry() {
      classCallCheck(this, SheetsRegistry);
      this.registry = [];
    }

    createClass(SheetsRegistry, [{
      key: 'add',


      /**
       * Register a Style Sheet.
       */
      value: function add(sheet) {
        var registry = this.registry;
        var index = sheet.options.index;


        if (registry.indexOf(sheet) !== -1) return;

        if (registry.length === 0 || index >= this.index) {
          registry.push(sheet);
          return;
        }

        // Find a position.
        for (var i = 0; i < registry.length; i++) {
          if (registry[i].options.index > index) {
            registry.splice(i, 0, sheet);
            return;
          }
        }
      }

      /**
       * Reset the registry.
       */

    }, {
      key: 'reset',
      value: function reset() {
        this.registry = [];
      }

      /**
       * Remove a Style Sheet.
       */

    }, {
      key: 'remove',
      value: function remove(sheet) {
        var index = this.registry.indexOf(sheet);
        this.registry.splice(index, 1);
      }

      /**
       * Convert all attached sheets to a CSS string.
       */

    }, {
      key: 'toString',
      value: function toString(options) {
        return this.registry.filter(function (sheet) {
          return sheet.attached;
        }).map(function (sheet) {
          return sheet.toString(options);
        }).join('\n');
      }
    }, {
      key: 'index',


      /**
       * Current highest index number.
       */
      get: function get$$1() {
        return this.registry.length === 0 ? 0 : this.registry[this.registry.length - 1].options.index;
      }
    }]);
    return SheetsRegistry;
  }();

  /**
   * This is a global sheets registry. Only DomRenderer will add sheets to it.
   * On the server one should use an own SheetsRegistry instance and add the
   * sheets to it, because you need to make sure to create a new registry for
   * each request in order to not leak sheets across requests.
   */
  var sheets = new SheetsRegistry();

  var ns = '2f1acc6c3a606b082e5eef5e54414ffb';
  if (global$1[ns] == null) global$1[ns] = 0;

  // Bundle may contain multiple JSS versions at the same time. In order to identify
  // the current version with just one short number and use it for classes generation
  // we use a counter. Also it is more accurate, because user can manually reevaluate
  // the module.
  var moduleId = global$1[ns]++;

  var maxRules = 1e10;

  var env$2 = "development";

  /**
   * Returns a function which generates unique class names based on counters.
   * When new generator function is created, rule counter is reseted.
   * We need to reset the rule counter for SSR for each request.
   */
  var createGenerateClassNameDefault = (function () {
    var ruleCounter = 0;
    var defaultPrefix = env$2 === 'production' ? 'c' : '';

    return function (rule, sheet) {
      ruleCounter += 1;

      if (ruleCounter > maxRules) {
        warning_1(false, '[JSS] You might have a memory leak. Rule counter is at %s.', ruleCounter);
      }

      var prefix = defaultPrefix;
      var jssId = '';

      if (sheet) {
        prefix = sheet.options.classNamePrefix || defaultPrefix;
        if (sheet.options.jss.id != null) jssId += sheet.options.jss.id;
      }

      if (env$2 === 'production') {
        return '' + prefix + moduleId + jssId + ruleCounter;
      }

      return prefix + rule.key + '-' + moduleId + (jssId && '-' + jssId) + '-' + ruleCounter;
    };
  });

  /**
   * Cache the value from the first time a function is called.
   */
  var memoize = function memoize(fn) {
    var value = void 0;
    return function () {
      if (!value) value = fn();
      return value;
    };
  };

  /**
   * Get a style property value.
   */
  function getPropertyValue(cssRule, prop) {
    try {
      return cssRule.style.getPropertyValue(prop);
    } catch (err) {
      // IE may throw if property is unknown.
      return '';
    }
  }

  /**
   * Set a style property.
   */
  function setProperty(cssRule, prop, value) {
    try {
      var cssValue = value;

      if (Array.isArray(value)) {
        cssValue = toCssValue(value, true);

        if (value[value.length - 1] === '!important') {
          cssRule.style.setProperty(prop, cssValue, 'important');
          return true;
        }
      }

      cssRule.style.setProperty(prop, cssValue);
    } catch (err) {
      // IE may throw if property is unknown.
      return false;
    }
    return true;
  }

  /**
   * Remove a style property.
   */
  function removeProperty(cssRule, prop) {
    try {
      cssRule.style.removeProperty(prop);
    } catch (err) {
      warning_1(false, '[JSS] DOMException "%s" was thrown. Tried to remove property "%s".', err.message, prop);
    }
  }

  var CSSRuleTypes = {
    STYLE_RULE: 1,
    KEYFRAMES_RULE: 7

    /**
     * Get the CSS Rule key.
     */

  };var getKey = function () {
    var extractKey = function extractKey(cssText) {
      var from = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 0;
      return cssText.substr(from, cssText.indexOf('{') - 1);
    };

    return function (cssRule) {
      if (cssRule.type === CSSRuleTypes.STYLE_RULE) return cssRule.selectorText;
      if (cssRule.type === CSSRuleTypes.KEYFRAMES_RULE) {
        var name = cssRule.name;

        if (name) return '@keyframes ' + name;

        // There is no rule.name in the following browsers:
        // - IE 9
        // - Safari 7.1.8
        // - Mobile Safari 9.0.0
        var cssText = cssRule.cssText;

        return '@' + extractKey(cssText, cssText.indexOf('keyframes'));
      }

      // Conditionals.
      return extractKey(cssRule.cssText);
    };
  }();

  /**
   * Set the selector.
   */
  function setSelector(cssRule, selectorText) {
    cssRule.selectorText = selectorText;

    // Return false if setter was not successful.
    // Currently works in chrome only.
    return cssRule.selectorText === selectorText;
  }

  /**
   * Gets the `head` element upon the first call and caches it.
   */
  var getHead = memoize(function () {
    return document.head || document.getElementsByTagName('head')[0];
  });

  /**
   * Gets a map of rule keys, where the property is an unescaped key and value
   * is a potentially escaped one.
   * It is used to identify CSS rules and the corresponding JSS rules. As an identifier
   * for CSSStyleRule we normally use `selectorText`. Though if original selector text
   * contains escaped code points e.g. `:not(#\\20)`, CSSOM will compile it to `:not(# )`
   * and so CSS rule's `selectorText` won't match JSS rule selector.
   *
   * https://www.w3.org/International/questions/qa-escapes#cssescapes
   */
  var getUnescapedKeysMap = function () {
    var style = void 0;
    var isAttached = false;

    return function (rules) {
      var map = {};
      // https://github.com/facebook/flow/issues/2696
      if (!style) style = document.createElement('style');
      for (var i = 0; i < rules.length; i++) {
        var rule = rules[i];
        if (!(rule instanceof StyleRule)) continue;
        var selector = rule.selector;
        // Only unescape selector over CSSOM if it contains a back slash.

        if (selector && selector.indexOf('\\') !== -1) {
          // Lazilly attach when needed.
          if (!isAttached) {
            getHead().appendChild(style);
            isAttached = true;
          }
          style.textContent = selector + ' {}';
          var _style = style,
              sheet = _style.sheet;

          if (sheet) {
            var cssRules = sheet.cssRules;

            if (cssRules) map[cssRules[0].selectorText] = rule.key;
          }
        }
      }
      if (isAttached) {
        getHead().removeChild(style);
        isAttached = false;
      }
      return map;
    };
  }();

  /**
   * Find attached sheet with an index higher than the passed one.
   */
  function findHigherSheet(registry, options) {
    for (var i = 0; i < registry.length; i++) {
      var sheet = registry[i];
      if (sheet.attached && sheet.options.index > options.index && sheet.options.insertionPoint === options.insertionPoint) {
        return sheet;
      }
    }
    return null;
  }

  /**
   * Find attached sheet with the highest index.
   */
  function findHighestSheet(registry, options) {
    for (var i = registry.length - 1; i >= 0; i--) {
      var sheet = registry[i];
      if (sheet.attached && sheet.options.insertionPoint === options.insertionPoint) {
        return sheet;
      }
    }
    return null;
  }

  /**
   * Find a comment with "jss" inside.
   */
  function findCommentNode(text) {
    var head = getHead();
    for (var i = 0; i < head.childNodes.length; i++) {
      var node = head.childNodes[i];
      if (node.nodeType === 8 && node.nodeValue.trim() === text) {
        return node;
      }
    }
    return null;
  }

  /**
   * Find a node before which we can insert the sheet.
   */
  function findPrevNode(options) {
    var registry = sheets.registry;


    if (registry.length > 0) {
      // Try to insert before the next higher sheet.
      var sheet = findHigherSheet(registry, options);
      if (sheet) return sheet.renderer.element;

      // Otherwise insert after the last attached.
      sheet = findHighestSheet(registry, options);
      if (sheet) return sheet.renderer.element.nextElementSibling;
    }

    // Try to find a comment placeholder if registry is empty.
    var insertionPoint = options.insertionPoint;

    if (insertionPoint && typeof insertionPoint === 'string') {
      var comment = findCommentNode(insertionPoint);
      if (comment) return comment.nextSibling;
      // If user specifies an insertion point and it can't be found in the document -
      // bad specificity issues may appear.
      warning_1(insertionPoint === 'jss', '[JSS] Insertion point "%s" not found.', insertionPoint);
    }

    return null;
  }

  /**
   * Insert style element into the DOM.
   */
  function insertStyle(style, options) {
    var insertionPoint = options.insertionPoint;

    var prevNode = findPrevNode(options);

    if (prevNode) {
      var parentNode = prevNode.parentNode;

      if (parentNode) parentNode.insertBefore(style, prevNode);
      return;
    }

    // Works with iframes and any node types.
    if (insertionPoint && typeof insertionPoint.nodeType === 'number') {
      // https://stackoverflow.com/questions/41328728/force-casting-in-flow
      var insertionPointElement = insertionPoint;
      var _parentNode = insertionPointElement.parentNode;

      if (_parentNode) _parentNode.insertBefore(style, insertionPointElement.nextSibling);else warning_1(false, '[JSS] Insertion point is not in the DOM.');
      return;
    }

    getHead().insertBefore(style, prevNode);
  }

  /**
   * Read jss nonce setting from the page if the user has set it.
   */
  var getNonce = memoize(function () {
    var node = document.querySelector('meta[property="csp-nonce"]');
    return node ? node.getAttribute('content') : null;
  });

  var DomRenderer = function () {
    function DomRenderer(sheet) {
      classCallCheck(this, DomRenderer);
      this.getPropertyValue = getPropertyValue;
      this.setProperty = setProperty;
      this.removeProperty = removeProperty;
      this.setSelector = setSelector;
      this.getKey = getKey;
      this.getUnescapedKeysMap = getUnescapedKeysMap;
      this.hasInsertedRules = false;

      // There is no sheet when the renderer is used from a standalone StyleRule.
      if (sheet) sheets.add(sheet);

      this.sheet = sheet;

      var _ref = this.sheet ? this.sheet.options : {},
          media = _ref.media,
          meta = _ref.meta,
          element = _ref.element;

      this.element = element || document.createElement('style');
      this.element.setAttribute('data-jss', '');
      if (media) this.element.setAttribute('media', media);
      if (meta) this.element.setAttribute('data-meta', meta);
      var nonce = getNonce();
      if (nonce) this.element.setAttribute('nonce', nonce);
    }

    /**
     * Insert style element into render tree.
     */


    // HTMLStyleElement needs fixing https://github.com/facebook/flow/issues/2696


    createClass(DomRenderer, [{
      key: 'attach',
      value: function attach() {
        // In the case the element node is external and it is already in the DOM.
        if (this.element.parentNode || !this.sheet) return;

        // When rules are inserted using `insertRule` API, after `sheet.detach().attach()`
        // browsers remove those rules.
        // TODO figure out if its a bug and if it is known.
        // Workaround is to redeploy the sheet before attaching as a string.
        if (this.hasInsertedRules) {
          this.deploy();
          this.hasInsertedRules = false;
        }

        insertStyle(this.element, this.sheet.options);
      }

      /**
       * Remove style element from render tree.
       */

    }, {
      key: 'detach',
      value: function detach() {
        this.element.parentNode.removeChild(this.element);
      }

      /**
       * Inject CSS string into element.
       */

    }, {
      key: 'deploy',
      value: function deploy() {
        if (!this.sheet) return;
        this.element.textContent = '\n' + this.sheet.toString() + '\n';
      }

      /**
       * Insert a rule into element.
       */

    }, {
      key: 'insertRule',
      value: function insertRule(rule, index) {
        var sheet = this.element.sheet;
        var cssRules = sheet.cssRules;

        var str = rule.toString();
        if (!index) index = cssRules.length;

        if (!str) return false;

        try {
          sheet.insertRule(str, index);
        } catch (err) {
          warning_1(false, '[JSS] Can not insert an unsupported rule \n\r%s', rule);
          return false;
        }
        this.hasInsertedRules = true;

        return cssRules[index];
      }

      /**
       * Delete a rule.
       */

    }, {
      key: 'deleteRule',
      value: function deleteRule(cssRule) {
        var sheet = this.element.sheet;

        var index = this.indexOf(cssRule);
        if (index === -1) return false;
        sheet.deleteRule(index);
        return true;
      }

      /**
       * Get index of a CSS Rule.
       */

    }, {
      key: 'indexOf',
      value: function indexOf(cssRule) {
        var cssRules = this.element.sheet.cssRules;

        for (var _index = 0; _index < cssRules.length; _index++) {
          if (cssRule === cssRules[_index]) return _index;
        }
        return -1;
      }

      /**
       * Generate a new CSS rule and replace the existing one.
       */

    }, {
      key: 'replaceRule',
      value: function replaceRule(cssRule, rule) {
        var index = this.indexOf(cssRule);
        var newCssRule = this.insertRule(rule, index);
        this.element.sheet.deleteRule(index);
        return newCssRule;
      }

      /**
       * Get all rules elements.
       */

    }, {
      key: 'getRules',
      value: function getRules() {
        return this.element.sheet.cssRules;
      }
    }]);
    return DomRenderer;
  }();

  /* eslint-disable class-methods-use-this */

  /**
   * Rendering backend to do nothing in nodejs.
   */
  var VirtualRenderer = function () {
    function VirtualRenderer() {
      classCallCheck(this, VirtualRenderer);
    }

    createClass(VirtualRenderer, [{
      key: 'setProperty',
      value: function setProperty() {
        return true;
      }
    }, {
      key: 'getPropertyValue',
      value: function getPropertyValue() {
        return '';
      }
    }, {
      key: 'removeProperty',
      value: function removeProperty() {}
    }, {
      key: 'setSelector',
      value: function setSelector() {
        return true;
      }
    }, {
      key: 'getKey',
      value: function getKey() {
        return '';
      }
    }, {
      key: 'attach',
      value: function attach() {}
    }, {
      key: 'detach',
      value: function detach() {}
    }, {
      key: 'deploy',
      value: function deploy() {}
    }, {
      key: 'insertRule',
      value: function insertRule() {
        return false;
      }
    }, {
      key: 'deleteRule',
      value: function deleteRule() {
        return true;
      }
    }, {
      key: 'replaceRule',
      value: function replaceRule() {
        return false;
      }
    }, {
      key: 'getRules',
      value: function getRules() {}
    }, {
      key: 'indexOf',
      value: function indexOf() {
        return -1;
      }
    }]);
    return VirtualRenderer;
  }();

  var defaultPlugins = plugins.concat([observablesPlugin, functionsPlugin]);

  var instanceCounter = 0;

  var Jss = function () {
    function Jss(options) {
      classCallCheck(this, Jss);
      this.id = instanceCounter++;
      this.version = "9.8.7";
      this.plugins = new PluginsRegistry();
      this.options = {
        createGenerateClassName: createGenerateClassNameDefault,
        Renderer: isBrowser ? DomRenderer : VirtualRenderer,
        plugins: []
      };
      this.generateClassName = createGenerateClassNameDefault();

      // eslint-disable-next-line prefer-spread
      this.use.apply(this, defaultPlugins);
      this.setup(options);
    }

    createClass(Jss, [{
      key: 'setup',
      value: function setup() {
        var options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};

        if (options.createGenerateClassName) {
          this.options.createGenerateClassName = options.createGenerateClassName;
          // $FlowFixMe
          this.generateClassName = options.createGenerateClassName();
        }

        if (options.insertionPoint != null) this.options.insertionPoint = options.insertionPoint;
        if (options.virtual || options.Renderer) {
          this.options.Renderer = options.Renderer || (options.virtual ? VirtualRenderer : DomRenderer);
        }

        // eslint-disable-next-line prefer-spread
        if (options.plugins) this.use.apply(this, options.plugins);

        return this;
      }

      /**
       * Create a Style Sheet.
       */

    }, {
      key: 'createStyleSheet',
      value: function createStyleSheet(styles) {
        var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};

        var index = options.index;
        if (typeof index !== 'number') {
          index = sheets.index === 0 ? 0 : sheets.index + 1;
        }
        var sheet = new StyleSheet(styles, _extends({}, options, {
          jss: this,
          generateClassName: options.generateClassName || this.generateClassName,
          insertionPoint: this.options.insertionPoint,
          Renderer: this.options.Renderer,
          index: index
        }));
        this.plugins.onProcessSheet(sheet);

        return sheet;
      }

      /**
       * Detach the Style Sheet and remove it from the registry.
       */

    }, {
      key: 'removeStyleSheet',
      value: function removeStyleSheet(sheet) {
        sheet.detach();
        sheets.remove(sheet);
        return this;
      }

      /**
       * Create a rule without a Style Sheet.
       */

    }, {
      key: 'createRule',
      value: function createRule$$1(name) {
        var style = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
        var options = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {};

        // Enable rule without name for inline styles.
        if ((typeof name === 'undefined' ? 'undefined' : _typeof$1(name)) === 'object') {
          options = style;
          style = name;
          name = undefined;
        }

        // Cast from RuleFactoryOptions to RuleOptions
        // https://stackoverflow.com/questions/41328728/force-casting-in-flow
        var ruleOptions = options;

        ruleOptions.jss = this;
        ruleOptions.Renderer = this.options.Renderer;
        if (!ruleOptions.generateClassName) ruleOptions.generateClassName = this.generateClassName;
        if (!ruleOptions.classes) ruleOptions.classes = {};
        var rule = createRule(name, style, ruleOptions);

        if (!ruleOptions.selector && rule instanceof StyleRule) {
          rule.selector = '.' + ruleOptions.generateClassName(rule);
        }

        this.plugins.onProcessRule(rule);

        return rule;
      }

      /**
       * Register plugin. Passed function will be invoked with a rule instance.
       */

    }, {
      key: 'use',
      value: function use() {
        var _this = this;

        for (var _len = arguments.length, plugins$$1 = Array(_len), _key = 0; _key < _len; _key++) {
          plugins$$1[_key] = arguments[_key];
        }

        plugins$$1.forEach(function (plugin) {
          // Avoids applying same plugin twice, at least based on ref.
          if (_this.options.plugins.indexOf(plugin) === -1) {
            _this.options.plugins.push(plugin);
            _this.plugins.use(plugin);
          }
        });

        return this;
      }
    }]);
    return Jss;
  }();

  /**
   * Extracts a styles object with only props that contain function values.
   */
  function getDynamicStyles(styles) {
    var to = null;

    for (var key in styles) {
      var value = styles[key];
      var type = typeof value === 'undefined' ? 'undefined' : _typeof$1(value);

      if (type === 'function') {
        if (!to) to = {};
        to[key] = value;
      } else if (type === 'object' && value !== null && !Array.isArray(value)) {
        var extracted = getDynamicStyles(value);
        if (extracted) {
          if (!to) to = {};
          to[key] = extracted;
        }
      }
    }

    return to;
  }

  /**
   * SheetsManager is like a WeakMap which is designed to count StyleSheet
   * instances and attach/detach automatically.
   */
  var SheetsManager = function () {
    function SheetsManager() {
      classCallCheck(this, SheetsManager);
      this.sheets = [];
      this.refs = [];
      this.keys = [];
    }

    createClass(SheetsManager, [{
      key: 'get',
      value: function get$$1(key) {
        var index = this.keys.indexOf(key);
        return this.sheets[index];
      }
    }, {
      key: 'add',
      value: function add(key, sheet) {
        var sheets = this.sheets,
            refs = this.refs,
            keys = this.keys;

        var index = sheets.indexOf(sheet);

        if (index !== -1) return index;

        sheets.push(sheet);
        refs.push(0);
        keys.push(key);

        return sheets.length - 1;
      }
    }, {
      key: 'manage',
      value: function manage(key) {
        var index = this.keys.indexOf(key);
        var sheet = this.sheets[index];
        if (this.refs[index] === 0) sheet.attach();
        this.refs[index]++;
        if (!this.keys[index]) this.keys.splice(index, 0, key);
        return sheet;
      }
    }, {
      key: 'unmanage',
      value: function unmanage(key) {
        var index = this.keys.indexOf(key);
        if (index === -1) {
          // eslint-ignore-next-line no-console
          warning_1(false, "SheetsManager: can't find sheet to unmanage");
          return;
        }
        if (this.refs[index] > 0) {
          this.refs[index]--;
          if (this.refs[index] === 0) this.sheets[index].detach();
        }
      }
    }, {
      key: 'size',
      get: function get$$1() {
        return this.keys.length;
      }
    }]);
    return SheetsManager;
  }();

  /**
   * Creates a new instance of Jss.
   */
  var create = function create(options) {
    return new Jss(options);
  };

  /**
   * A global Jss instance.
   */
  var index$1 = create();

  exports.create = create;
  exports.default = index$1;
  exports.getDynamicStyles = getDynamicStyles;
  exports.toCssValue = toCssValue;
  exports.SheetsRegistry = SheetsRegistry;
  exports.SheetsManager = SheetsManager;
  exports.RuleList = RuleList;
  exports.sheets = sheets;
  exports.createGenerateClassName = createGenerateClassNameDefault;

  Object.defineProperty(exports, '__esModule', { value: true });

})));
//# sourceMappingURL=jss.js.map
