'use strict';

var _regenerator = require('babel-runtime/regenerator');

var _regenerator2 = _interopRequireDefault(_regenerator);

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; }; }();

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

function _asyncToGenerator(fn) { return function () { var gen = fn.apply(this, arguments); return new Promise(function (resolve, reject) { function step(key, arg) { try { var info = gen[key](arg); var value = info.value; } catch (error) { reject(error); return; } if (info.done) { resolve(value); } else { return Promise.resolve(value).then(function (value) { step("next", value); }, function (err) { step("throw", err); }); } } return step("next"); }); }; }

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

var StaleElementReferenceError = require('selenium-webdriver').error.StaleElementReferenceError;

var AxeInjector = function () {
  function AxeInjector(_ref) {
    var driver = _ref.driver,
        config = _ref.config,
        _ref$axeSource = _ref.axeSource,
        axeSource = _ref$axeSource === undefined ? null : _ref$axeSource,
        _ref$options = _ref.options,
        options = _ref$options === undefined ? {} : _ref$options;

    _classCallCheck(this, AxeInjector);

    this.driver = driver;
    this.axeSource = axeSource || require('axe-core').source;
    this.config = config ? JSON.stringify(config) : '';
    this.options = options;

    // default logIframeErrors to true so it retains the original behavior
    this.options.logIframeErrors = typeof this.options.logIframeErrors === 'boolean' ? this.options.logIframeErrors : true;

    this.didLogError = false;
    this.errorHandler = this.errorHandler.bind(this);
  }

  // Single-shot error handler. Ensures we don't log more than once.


  _createClass(AxeInjector, [{
    key: 'errorHandler',
    value: function errorHandler(err) {
      // We've already "warned" the user. No need to do it again (mostly for backwards compatiability)
      if (this.didLogError) {
        return;
      }

      this.didLogError = true;
      var msg = void 0;
      if (err instanceof StaleElementReferenceError) {
        msg = 'Tried to inject into a removed iframe. This will not affect the analysis of the rest of the page but you might want to ensure the page has finished updating before starting the analysis.';
      } else {
        msg = 'Failed to inject axe-core into one of the iframes!';
      }

      if (this.options.logIframeErrors) {
        // eslint-disable-next-line no-console
        console.error(msg);
        return;
      }

      throw new Error(msg);
    }

    // Get axe-core source (and configuration)

  }, {
    key: 'handleFrame',


    // Inject into the provided `frame` and its child `frames`
    value: function () {
      var _ref2 = _asyncToGenerator( /*#__PURE__*/_regenerator2.default.mark(function _callee(frame) {
        var _this = this;

        var frames;
        return _regenerator2.default.wrap(function _callee$(_context) {
          while (1) {
            switch (_context.prev = _context.next) {
              case 0:
                _context.next = 2;
                return this.driver.switchTo().frame(frame);

              case 2:
                _context.next = 4;
                return this.driver.executeScript(this.script);

              case 4:
                _context.next = 6;
                return this.driver.findElements({ tagName: 'iframe' });

              case 6:
                frames = _context.sent;
                return _context.abrupt('return', Promise.all(frames.map(function (childFrame) {
                  return _this.handleFrame(childFrame).catch(_this.errorHandler);
                })));

              case 8:
              case 'end':
                return _context.stop();
            }
          }
        }, _callee, this);
      }));

      function handleFrame(_x) {
        return _ref2.apply(this, arguments);
      }

      return handleFrame;
    }()

    // Inject into all frames.

  }, {
    key: 'injectIntoAllFrames',
    value: function () {
      var _ref3 = _asyncToGenerator( /*#__PURE__*/_regenerator2.default.mark(function _callee2() {
        var _this2 = this;

        var frames;
        return _regenerator2.default.wrap(function _callee2$(_context2) {
          while (1) {
            switch (_context2.prev = _context2.next) {
              case 0:
                _context2.next = 2;
                return this.driver.switchTo().defaultContent();

              case 2:
                _context2.next = 4;
                return this.driver.executeScript(this.script);

              case 4:
                _context2.next = 6;
                return this.driver.findElements({ tagName: 'iframe' });

              case 6:
                frames = _context2.sent;
                _context2.next = 9;
                return Promise.all(frames.map(function (childFrame) {
                  return _this2.handleFrame(childFrame).catch(_this2.errorHandler);
                }));

              case 9:
                return _context2.abrupt('return', this.driver.switchTo().defaultContent());

              case 10:
              case 'end':
                return _context2.stop();
            }
          }
        }, _callee2, this);
      }));

      function injectIntoAllFrames() {
        return _ref3.apply(this, arguments);
      }

      return injectIntoAllFrames;
    }()

    // Inject axe, invoking the provided callback when done

  }, {
    key: 'inject',
    value: function inject(callback) {
      var _this3 = this;

      this.injectIntoAllFrames().then(function () {
        return callback();
      }).catch(function (e) {
        if (_this3.options.logIframeErrors) {
          return callback();
        }

        callback(e);
      });
    }
  }, {
    key: 'script',
    get: function get() {
      return `
      ${this.axeSource}
      ${this.config ? `axe.configure(${this.config})` : ''}
      axe.configure({ branding: { application: 'webdriverjs' } })
    `;
    }
  }]);

  return AxeInjector;
}();

module.exports = AxeInjector;