"use strict";

exports.__esModule = true;
exports.default = vdom;

var _ssrWindow = require("ssr-window");

var _h = _interopRequireDefault(require("./snabbdom/h"));

var _customComponents = _interopRequireDefault(require("./custom-components"));

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

/* eslint no-use-before-define: "off" */

/* eslint import/no-named-as-default: "off" */
var selfClosing = 'area base br col command embed hr img input keygen link menuitem meta param source track wbr'.split(' ');
var propsAttrs = 'hidden checked disabled readonly selected autofocus autoplay required multiple value indeterminate'.split(' ');
var booleanProps = 'hidden checked disabled readonly selected autofocus autoplay required multiple readOnly indeterminate'.split(' ');
var tempDIV;
var tempTBODY;
var tempTROW;

function toCamelCase(name) {
  return name.split('-').map(function (word, index) {
    if (index === 0) return word.toLowerCase();
    return word[0].toUpperCase() + word.substr(1);
  }).join('');
}

function contextFromAttrs() {
  var context = {};

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

  args.forEach(function (obj) {
    if (obj === void 0) {
      obj = {};
    }

    Object.keys(obj).forEach(function (key) {
      context[toCamelCase(key)] = obj[key];
    });
  });
  return context;
}

function createCustomComponent(_ref) {
  var app = _ref.app,
      vnode = _ref.vnode,
      tagName = _ref.tagName,
      data = _ref.data;
  app.component.create(Object.assign({
    el: vnode.elm
  }, _customComponents.default[tagName]), {
    $props: contextFromAttrs(data.attrs || {}, data.props || {})
  }, vnode.children).then(function (c) {
    if (vnode.data && vnode.data.on && c && c.$el) {
      Object.keys(vnode.data.on).forEach(function (eventName) {
        c.$el.on(eventName, vnode.data.on[eventName]);
      });
    } // eslint-disable-next-line


    vnode.elm.__component__ = c;
  });
}

function updateCustomComponent(vnode) {
  // eslint-disable-next-line
  var component = vnode && vnode.elm && vnode.elm.__component__;
  if (!component) return;
  var newProps = contextFromAttrs(vnode.data.attrs || {}, vnode.data.props || {});
  component.$children = vnode.children;
  Object.assign(component.$props, newProps);
  component.$update();
}

function destroyCustomComponent(vnode) {
  // eslint-disable-next-line
  var component = vnode && vnode.elm && vnode.elm.__component__;

  if (component) {
    var el = component.el,
        $el = component.$el;

    if (vnode.data && vnode.data.on && $el) {
      Object.keys(vnode.data.on).forEach(function (eventName) {
        $el.off(eventName, vnode.data.on[eventName]);
      });
    }

    if (component.$destroy) component.$destroy();
    if (el && el.parentNode) el.parentNode.removeChild(el);
    delete vnode.elm.__component__; // eslint-disable-line
  }
}

function getHooks(data, app, initial, isRoot, tagName) {
  var hooks = {};
  var insert = [];
  var destroy = [];
  var update = [];
  var postpatch = [];
  var isFakeElement = false;

  if (data && data.attrs && data.attrs.component) {
    // eslint-disable-next-line
    tagName = data.attrs.component;
    delete data.attrs.component;
    isFakeElement = true;
  }

  var isCustomComponent = tagName && tagName.indexOf('-') > 0 && _customComponents.default[tagName];

  if (isCustomComponent) {
    insert.push(function (vnode) {
      if (vnode.sel !== tagName && !isFakeElement) return;
      createCustomComponent({
        app: app,
        vnode: vnode,
        tagName: tagName,
        data: data
      });
    });
    destroy.push(function (vnode) {
      destroyCustomComponent(vnode);
    });
    update.push(function (oldVnode, vnode) {
      updateCustomComponent(vnode);
    });
  }

  if (!isCustomComponent) {
    if (!data || !data.attrs || !data.attrs.class) return hooks;
    var classNames = data.attrs.class;
    classNames.split(' ').forEach(function (className) {
      if (!initial) {
        insert.push.apply(insert, app.getVnodeHooks('insert', className));
      }

      destroy.push.apply(destroy, app.getVnodeHooks('destroy', className));
      update.push.apply(update, app.getVnodeHooks('update', className));
      postpatch.push.apply(postpatch, app.getVnodeHooks('postpatch', className));
    });
  }

  if (isRoot && !initial) {
    postpatch.push(function (oldVnode, vnode) {
      var vn = vnode || oldVnode;
      if (!vn) return;

      if (vn.data && vn.data.context && vn.data.context.$options.updated) {
        vn.data.context.$hook('updated');
      }
    });
  }

  if (insert.length === 0 && destroy.length === 0 && update.length === 0 && postpatch.length === 0) {
    return hooks;
  }

  if (insert.length) {
    hooks.insert = function (vnode) {
      insert.forEach(function (f) {
        return f(vnode);
      });
    };
  }

  if (destroy.length) {
    hooks.destroy = function (vnode) {
      destroy.forEach(function (f) {
        return f(vnode);
      });
    };
  }

  if (update.length) {
    hooks.update = function (oldVnode, vnode) {
      update.forEach(function (f) {
        return f(oldVnode, vnode);
      });
    };
  }

  if (postpatch.length) {
    hooks.postpatch = function (oldVnode, vnode) {
      postpatch.forEach(function (f) {
        return f(oldVnode, vnode);
      });
    };
  }

  return hooks;
}

function getEventHandler(handlerString, context, _temp) {
  var _ref2 = _temp === void 0 ? {} : _temp,
      stop = _ref2.stop,
      prevent = _ref2.prevent,
      once = _ref2.once;

  var fired = false;
  var methodName;
  var method;
  var needMethodBind = true;

  if (handlerString.indexOf('(') < 0) {
    methodName = handlerString;
  } else {
    methodName = handlerString.split('(')[0];
  }

  if (methodName.indexOf('.') >= 0) {
    methodName.split('.').forEach(function (path, pathIndex) {
      if (pathIndex === 0 && path === 'this') return;

      if (pathIndex === 0 && path === 'window') {
        // eslint-disable-next-line
        method = window;
        needMethodBind = false;
        return;
      }

      if (!method) method = context;
      if (method[path]) method = method[path];else {
        throw new Error("Framework7: Component doesn't have method \"" + methodName.split('.').slice(0, pathIndex + 1).join('.') + "\"");
      }
    });
  } else {
    if (!context[methodName]) {
      throw new Error("Framework7: Component doesn't have method \"" + methodName + "\"");
    }

    method = context[methodName];
  }

  if (needMethodBind) {
    method = method.bind(context);
  }

  function handler() {
    for (var _len2 = arguments.length, args = new Array(_len2), _key2 = 0; _key2 < _len2; _key2++) {
      args[_key2] = arguments[_key2];
    }

    var e = args[0];
    var customArgs = [];
    if (once && fired) return;
    if (stop) e.stopPropagation();
    if (prevent) e.preventDefault();
    fired = true;

    if (handlerString.indexOf('(') < 0) {
      customArgs = args;
    } else {
      var handlerArguments = handlerString.split('(')[1].split(')')[0].replace(/'[^']*'|"[^"]*"/g, function (a) {
        return a.replace(/,/g, '<_comma_>');
      }).split(',').map(function (a) {
        return a.replace(/<_comma_>/g, ',');
      });
      handlerArguments.forEach(function (argument) {
        var arg = argument.trim(); // eslint-disable-next-line

        if (!isNaN(arg)) arg = parseFloat(arg);else if (arg === 'true') arg = true;else if (arg === 'false') arg = false;else if (arg === 'null') arg = null;else if (arg === 'undefined') arg = undefined;else if (arg[0] === '"') arg = arg.replace(/"/g, '');else if (arg[0] === "'") arg = arg.replace(/'/g, '');else if (arg.indexOf('.') > 0) {
          var deepArg;
          arg.split('.').forEach(function (path) {
            if (!deepArg) deepArg = context;
            deepArg = deepArg[path];
          });
          arg = deepArg;
        } else {
          arg = context[arg];
        }
        customArgs.push(arg);
      });
    }

    method.apply(void 0, customArgs);
  }

  return handler;
}

function getData(el, context, app, initial, isRoot, tagName) {
  var data = {
    context: context
  };
  var attributes = el.attributes;
  Array.prototype.forEach.call(attributes, function (attr) {
    var attrName = attr.name;
    var attrValue = attr.value;

    if (propsAttrs.indexOf(attrName) >= 0) {
      // Props
      if (!data.props) data.props = {};

      if (attrName === 'readonly') {
        attrName = 'readOnly';
      }

      if (tagName === 'option' && attrName === 'value') {
        if (!data.attrs) data.attrs = {};
        data.attrs.value = attrValue;
      }

      if (booleanProps.indexOf(attrName) >= 0) {
        // eslint-disable-next-line
        data.props[attrName] = attrValue === false ? false : true;
      } else {
        data.props[attrName] = attrValue;
      }
    } else if (attrName === 'key') {
      // Key
      data.key = attrValue;
    } else if (attrName.indexOf('@') === 0) {
      // Events
      if (!data.on) data.on = {};
      var eventName = attrName.substr(1);
      var stop = false;
      var prevent = false;
      var once = false;

      if (eventName.indexOf('.') >= 0) {
        eventName.split('.').forEach(function (eventNamePart, eventNameIndex) {
          if (eventNameIndex === 0) eventName = eventNamePart;else {
            if (eventNamePart === 'stop') stop = true;
            if (eventNamePart === 'prevent') prevent = true;
            if (eventNamePart === 'once') once = true;
          }
        });
      }

      data.on[eventName] = getEventHandler(attrValue, context, {
        stop: stop,
        prevent: prevent,
        once: once
      });
    } else if (attrName === 'style') {
      // Style
      if (attrValue.indexOf('{') >= 0 && attrValue.indexOf('}') >= 0) {
        try {
          data.style = JSON.parse(attrValue);
        } catch (e) {
          if (!data.attrs) data.attrs = {};
          data.attrs.style = attrValue;
        }
      } else {
        if (!data.attrs) data.attrs = {};
        data.attrs.style = attrValue;
      }
    } else {
      // Rest of attribures
      if (!data.attrs) data.attrs = {};
      data.attrs[attrName] = attrValue; // ID -> Key

      if (attrName === 'id' && !data.key && !isRoot) {
        data.key = attrValue;
      }
    }
  });

  if (isRoot && context && context.$id && context.$style && context.$styleScoped) {
    if (!data.attrs) data.attrs = {};
    data.attrs["data-f7-" + context.$id] = '';
  }

  var hooks = getHooks(data, app, initial, isRoot, tagName);

  hooks.prepatch = function (oldVnode, vnode) {
    if (!oldVnode || !vnode) return;

    if (oldVnode && oldVnode.data && oldVnode.data.props) {
      Object.keys(oldVnode.data.props).forEach(function (key) {
        if (booleanProps.indexOf(key) < 0) return;
        if (!vnode.data) vnode.data = {};
        if (!vnode.data.props) vnode.data.props = {};

        if (oldVnode.data.props[key] === true && !(key in vnode.data.props)) {
          vnode.data.props[key] = false;
        }
      });
    }
  };

  if (hooks) {
    data.hook = hooks;
  }

  return data;
}

function getChildren(el, context, app, initial) {
  var children = [];
  var nodes = el.childNodes;

  for (var i = 0; i < nodes.length; i += 1) {
    var childNode = nodes[i];
    var child = elementToVNode(childNode, context, app, initial);

    if (Array.isArray(child)) {
      children.push.apply(children, child);
    } else if (child) {
      children.push(child);
    }
  }

  return children;
}

function getSlots(slotEl, context, app, initial) {
  var slotName = slotEl.getAttribute('name') || 'default';
  var slots = (context.$children || []).filter(function (childEl) {
    var childSlotName = 'default';

    if (childEl.data) {
      childSlotName = childEl.data.attrs && childEl.data.attrs.slot || 'default';
    }

    return childSlotName === slotName;
  });

  if (slots.length === 0) {
    return getChildren(slotEl, context, app, initial);
  }

  return slots;
}

function elementToVNode(el, context, app, initial, isRoot) {
  var window = (0, _ssrWindow.getWindow)();

  if (el.nodeType === 3) {
    // text
    return el.textContent;
  }

  if (el.nodeType !== 1) return null; // element (statement adds inline SVG compatibility)

  var tagName = el instanceof window.SVGElement ? el.nodeName : el.nodeName.toLowerCase();

  if (tagName === 'slot') {
    return getSlots(el, context, app, initial);
  }

  return (0, _h.default)(tagName, getData(el, context, app, initial, isRoot, tagName), selfClosing.indexOf(tagName) >= 0 ? [] : getChildren(el, context, app, initial));
}

function vdom(html, context, initial) {
  if (html === void 0) {
    html = '';
  }

  var document = (0, _ssrWindow.getDocument)(); // Save to temp dom

  var htmlTrim = html.trim();
  var tempDOM = tempDIV;

  if (!tempDOM) {
    tempDOM = document.createElement('div');
  }

  if (htmlTrim.indexOf('<tr') === 0) {
    if (!tempTBODY) tempTBODY = document.createElement('tbody');
    tempDOM = tempTBODY;
  }

  if (htmlTrim.indexOf('<td') === 0 || htmlTrim.indexOf('<th') === 0) {
    if (!tempTROW) tempTROW = document.createElement('tr');
    tempDOM = tempTROW;
  }

  tempDOM.innerHTML = htmlTrim; // Parse DOM

  var rootEl;

  for (var i = 0; i < tempDOM.childNodes.length; i += 1) {
    if (!rootEl && tempDOM.childNodes[i].nodeType === 1) {
      rootEl = tempDOM.childNodes[i];
    }
  }

  var result = elementToVNode(rootEl, context, context.$app, initial, true); // Clean

  tempDOM.innerHTML = '';
  return result;
}