text: define([
    'summernote/core/func',
    'summernote/core/list',
    'summernote/core/agent'
  ], function (func, list, agent) {
  
    var NBSP_CHAR = String.fromCharCode(160);
    var ZERO_WIDTH_NBSP_CHAR = '\ufeff';
  
    /**
     * @class core.dom
     *
     * Dom functions
     *
     * @singleton
     * @alternateClassName dom
     */
    var dom = (function () {
      /**
       * @method isEditable
       *
       * returns whether node is `note-editable` or not.
       *
       * @param {Node} node
       * @return {Boolean}
       */
      var isEditable = function (node) {
        return node && $(node).hasClass('note-editable');
      };
  
      /**
       * @method isControlSizing
       *
       * returns whether node is `note-control-sizing` or not.
       *
       * @param {Node} node
       * @return {Boolean}
       */
      var isControlSizing = function (node) {
        return node && $(node).hasClass('note-control-sizing');
      };
  
      /**
       * @method  buildLayoutInfo
       *
       * build layoutInfo from $editor(.note-editor)
       *
       * @param {jQuery} $editor
       * @return {Object}
       * @return {Function} return.editor
       * @return {Node} return.dropzone
       * @return {Node} return.toolbar
       * @return {Node} return.editable
       * @return {Node} return.codable
       * @return {Node} return.popover
       * @return {Node} return.handle
       * @return {Node} return.dialog
       */
      var buildLayoutInfo = function ($editor) {
        var makeFinder;
  
        // air mode
        if ($editor.hasClass('note-air-editor')) {
          var id = list.last($editor.attr('id').split('-'));
          makeFinder = function (sIdPrefix) {
            return function () { return $(sIdPrefix + id); };
          };
  
          return {
            editor: function () { return $editor; },
            editable: function () { return $editor; },
            popover: makeFinder('#note-popover-'),
            handle: makeFinder('#note-handle-'),
            dialog: makeFinder('#note-dialog-')
          };
  
          // frame mode
        } else {
          makeFinder = function (sClassName) {
            return function () { return $editor.find(sClassName); };
          };
          return {
            editor: function () { return $editor; },
            dropzone: makeFinder('.note-dropzone'),
            toolbar: makeFinder('.note-toolbar'),
            editable: makeFinder('.note-editable'),
            codable: makeFinder('.note-codable'),
            statusbar: makeFinder('.note-statusbar'),
            popover: makeFinder('.note-popover'),
            handle: makeFinder('.note-handle'),
            dialog: makeFinder('.note-dialog')
          };
        }
      };
  
      /**
       * @method makePredByNodeName
       *
       * returns predicate which judge whether nodeName is same
       *
       * @param {String} nodeName
       * @return {Function}
       */
      var makePredByNodeName = function (nodeName) {
        nodeName = nodeName.toUpperCase();
        return function (node) {
          return node && node.nodeName.toUpperCase() === nodeName;
        };
      };
  
      /**
       * @method isText
       *
       *
       *
       * @param {Node} node
       * @return {Boolean} true if node's type is text(3)
       */
      var isText = function (node) {
        return node && node.nodeType === 3;
      };
  
      /**
       * ex) br, col, embed, hr, img, input, ...
       * @see http://www.w3.org/html/wg/drafts/html/master/syntax.html#void-elements
       */
      var isVoid = function (node) {
        return node && /^BR|^IMG|^HR/.test(node.nodeName.toUpperCase());
      };
  
      var isPara = function (node) {
        if (isEditable(node)) {
          return false;
        }
  
        // Chrome(v31.0), FF(v25.0.1) use DIV for paragraph
        return node && /^DIV|^P|^LI|^H[1-7]/.test(node.nodeName.toUpperCase());
      };
  
      var isLi = makePredByNodeName('LI');
  
      var isPurePara = function (node) {
        return isPara(node) && !isLi(node);
      };
  
      var isTable = makePredByNodeName('TABLE');
  
      var isInline = function (node) {
        return !isBodyContainer(node) &&
               !isList(node) &&
               !isPara(node) &&
               !isTable(node) &&
               !isBlockquote(node);
      };
  
      var isList = function (node) {
        return node && /^UL|^OL/.test(node.nodeName.toUpperCase());
      };
  
      var isCell = function (node) {
        return node && /^TD|^TH/.test(node.nodeName.toUpperCase());
      };
  
      var isBlockquote = makePredByNodeName('BLOCKQUOTE');
  
      var isBodyContainer = function (node) {
        return isCell(node) || isBlockquote(node) || isEditable(node);
      };
  
      var isAnchor = makePredByNodeName('A');
  
      var isParaInline = function (node) {
        return isInline(node) && !!ancestor(node, isPara);
      };
  
      var isBodyInline = function (node) {
        return isInline(node) && !ancestor(node, isPara);
      };
  
      var isBody = makePredByNodeName('BODY');
  
      /**
       * returns whether nodeB is closest sibling of nodeA
       *
       * @param {Node} nodeA
       * @param {Node} nodeB
       * @return {Boolean}
       */
      var isClosestSibling = function (nodeA, nodeB) {
        return nodeA.nextSibling === nodeB ||
               nodeA.previousSibling === nodeB;
      };
  
      /**
       * returns array of closest siblings with node
       *
       * @param {Node} node
       * @param {function} [pred] - predicate function
       * @return {Node[]}
       */
      var withClosestSiblings = function (node, pred) {
        pred = pred || func.ok;
  
        var siblings = [];
        if (node.previousSibling && pred(node.previousSibling)) {
          siblings.push(node.previousSibling);
        }
        siblings.push(node);
        if (node.nextSibling && pred(node.nextSibling)) {
          siblings.push(node.nextSibling);
        }
        return siblings;
      };
  
      /**
       * blank HTML for cursor position
       */
      var blankHTML = agent.isMSIE ? '&nbsp;' : '<br>';
  
      /**
       * @method nodeLength
       *
       * returns #text's text size or element's childNodes size
       *
       * @param {Node} node
       */
      var nodeLength = function (node) {
        if (isText(node)) {
          return node.nodeValue.length;
        }
  
        return node.childNodes.length;
      };
  
      /**
       * returns whether node is empty or not.
       *
       * @param {Node} node
       * @return {Boolean}
       */
      var isEmpty = function (node) {
        var len = nodeLength(node);
  
        if (len === 0) {
          return true;
        } else if (!dom.isText(node) && len === 1 && node.innerHTML === blankHTML) {
          // ex) <p><br></p>, <span><br></span>
          return true;
        }
  
        return false;
      };
  
      /**
       * padding blankHTML if node is empty (for cursor position)
       */
      var paddingBlankHTML = function (node) {
        if (!isVoid(node) && !nodeLength(node)) {
          node.innerHTML = blankHTML;
        }
      };
  
      /**
       * find nearest ancestor predicate hit
       *
       * @param {Node} node
       * @param {Function} pred - predicate function
       */
      var ancestor = function (node, pred) {
        while (node) {
          if (pred(node)) { return node; }
          if (isEditable(node)) { break; }
  
          node = node.parentNode;
        }
        return null;
      };
  
      /**
       * find nearest ancestor only single child blood line and predicate hit
       *
       * @param {Node} node
       * @param {Function} pred - predicate function
       */
      var singleChildAncestor = function (node, pred) {
        node = node.parentNode;
  
        while (node) {
          if (nodeLength(node) !== 1) { break; }
          if (pred(node)) { return node; }
          if (isEditable(node)) { break; }
  
          node = node.parentNode;
        }
        return null;
      };
  
      /**
       * returns new array of ancestor nodes (until predicate hit).
       *
       * @param {Node} node
       * @param {Function} [optional] pred - predicate function
       */
      var listAncestor = function (node, pred) {
        pred = pred || func.fail;
  
        var ancestors = [];
        ancestor(node, function (el) {
          if (!isEditable(el)) {
            ancestors.push(el);
          }
  
          return pred(el);
        });
        return ancestors;
      };
  
      /**
       * find farthest ancestor predicate hit
       */
      var lastAncestor = function (node, pred) {
        var ancestors = listAncestor(node);
        return list.last(ancestors.filter(pred));
      };
  
      /**
       * returns common ancestor node between two nodes.
       *
       * @param {Node} nodeA
       * @param {Node} nodeB
       */
      var commonAncestor = function (nodeA, nodeB) {
        var ancestors = listAncestor(nodeA);
        for (var n = nodeB; n; n = n.parentNode) {
          if ($.inArray(n, ancestors) > -1) { return n; }
        }
        return null; // difference document area
      };
  
      /**
       * listing all previous siblings (until predicate hit).
       *
       * @param {Node} node
       * @param {Function} [optional] pred - predicate function
       */
      var listPrev = function (node, pred) {
        pred = pred || func.fail;
  
        var nodes = [];
        while (node) {
          if (pred(node)) { break; }
          nodes.push(node);
          node = node.previousSibling;
        }
        return nodes;
      };
  
      /**
       * listing next siblings (until predicate hit).
       *
       * @param {Node} node
       * @param {Function} [pred] - predicate function
       */
      var listNext = function (node, pred) {
        pred = pred || func.fail;
  
        var nodes = [];
        while (node) {
          if (pred(node)) { break; }
          nodes.push(node);
          node = node.nextSibling;
        }
        return nodes;
      };
  
      /**
       * listing descendant nodes
       *
       * @param {Node} node
       * @param {Function} [pred] - predicate function
       */
      var listDescendant = function (node, pred) {
        var descendents = [];
        pred = pred || func.ok;
  
        // start DFS(depth first search) with node
        (function fnWalk(current) {
          if (node !== current && pred(current)) {
            descendents.push(current);
          }
          for (var idx = 0, len = current.childNodes.length; idx < len; idx++) {
            fnWalk(current.childNodes[idx]);
          }
        })(node);
  
        return descendents;
      };
  
      /**
       * wrap node with new tag.
       *
       * @param {Node} node
       * @param {Node} tagName of wrapper
       * @return {Node} - wrapper
       */
      var wrap = function (node, wrapperName) {
        var parent = node.parentNode;
        var wrapper = $('<' + wrapperName + '>')[0];
  
        parent.insertBefore(wrapper, node);
        wrapper.appendChild(node);
  
        return wrapper;
      };
  
      /**
       * insert node after preceding
       *
       * @param {Node} node
       * @param {Node} preceding - predicate function
       */
      var insertAfter = function (node, preceding) {
        var next = preceding.nextSibling, parent = preceding.parentNode;
        if (next) {
          parent.insertBefore(node, next);
        } else {
          parent.appendChild(node);
        }
        return node;
      };
  
      /**
       * append elements.
       *
       * @param {Node} node
       * @param {Collection} aChild
       */
      var appendChildNodes = function (node, aChild) {
        $.each(aChild, function (idx, child) {
          node.appendChild(child);
        });
        return node;
      };
  
      /**
       * returns whether boundaryPoint is left edge or not.
       *
       * @param {BoundaryPoint} point
       * @return {Boolean}
       */
      var isLeftEdgePoint = function (point) {
        return point.offset === 0;
      };
  
      /**
       * returns whether boundaryPoint is right edge or not.
       *
       * @param {BoundaryPoint} point
       * @return {Boolean}
       */
      var isRightEdgePoint = function (point) {
        return point.offset === nodeLength(point.node);
      };
  
      /**
       * returns whether boundaryPoint is edge or not.
       *
       * @param {BoundaryPoint} point
       * @return {Boolean}
       */
      var isEdgePoint = function (point) {
        return isLeftEdgePoint(point) || isRightEdgePoint(point);
      };
  
      /**
       * returns wheter node is left edge of ancestor or not.
       *
       * @param {Node} node
       * @param {Node} ancestor
       * @return {Boolean}
       */
      var isLeftEdgeOf = function (node, ancestor) {
        while (node && node !== ancestor) {
          if (position(node) !== 0) {
            return false;
          }
          node = node.parentNode;
        }
  
        return true;
      };
  
      /**
       * returns whether node is right edge of ancestor or not.
       *
       * @param {Node} node
       * @param {Node} ancestor
       * @return {Boolean}
       */
      var isRightEdgeOf = function (node, ancestor) {
        while (node && node !== ancestor) {
          if (position(node) !== nodeLength(node.parentNode) - 1) {
            return false;
          }
          node = node.parentNode;
        }
  
        return true;
      };
  
      /**
       * returns offset from parent.
       *
       * @param {Node} node
       */
      var position = function (node) {
        var offset = 0;
        while ((node = node.previousSibling)) {
          offset += 1;
        }
        return offset;
      };
  
      var hasChildren = function (node) {
        return !!(node && node.childNodes && node.childNodes.length);
      };
  
      /**
       * returns previous boundaryPoint
       *
       * @param {BoundaryPoint} point
       * @param {Boolean} isSkipInnerOffset
       * @return {BoundaryPoint}
       */
      var prevPoint = function (point, isSkipInnerOffset) {
        var node, offset;
  
        if (point.offset === 0) {
          if (isEditable(point.node)) {
            return null;
          }
  
          node = point.node.parentNode;
          offset = position(point.node);
        } else if (hasChildren(point.node)) {
          node = point.node.childNodes[point.offset - 1];
          offset = nodeLength(node);
        } else {
          node = point.node;
          offset = isSkipInnerOffset ? 0 : point.offset - 1;
        }
  
        return {
          node: node,
          offset: offset
        };
      };
  
      /**
       * returns next boundaryPoint
       *
       * @param {BoundaryPoint} point
       * @param {Boolean} isSkipInnerOffset
       * @return {BoundaryPoint}
       */
      var nextPoint = function (point, isSkipInnerOffset) {
        var node, offset;
  
        if (nodeLength(point.node) === point.offset) {
          if (isEditable(point.node)) {
            return null;
          }
  
          node = point.node.parentNode;
          offset = position(point.node) + 1;
        } else if (hasChildren(point.node)) {
          node = point.node.childNodes[point.offset];
          offset = 0;
        } else {
          node = point.node;
          offset = isSkipInnerOffset ? nodeLength(point.node) : point.offset + 1;
        }
  
        return {
          node: node,
          offset: offset
        };
      };
  
      /**
       * returns whether pointA and pointB is same or not.
       *
       * @param {BoundaryPoint} pointA
       * @param {BoundaryPoint} pointB
       * @return {Boolean}
       */
      var isSamePoint = function (pointA, pointB) {
        return pointA.node === pointB.node && pointA.offset === pointB.offset;
      };
  
      /**
       * returns whether point is visible (can set cursor) or not.
       * 
       * @param {BoundaryPoint} point
       * @return {Boolean}
       */
      var isVisiblePoint = function (point) {
        if (isText(point.node) || !hasChildren(point.node) || isEmpty(point.node)) {
          return true;
        }
  
        var leftNode = point.node.childNodes[point.offset - 1];
        var rightNode = point.node.childNodes[point.offset];
        if ((!leftNode || isVoid(leftNode)) && (!rightNode || isVoid(rightNode))) {
          return true;
        }
  
        return false;
      };
  
      /**
       * @method prevPointUtil
       *
       * @param {BoundaryPoint} point
       * @param {Function} pred
       * @return {BoundaryPoint}
       */
      var prevPointUntil = function (point, pred) {
        while (point) {
          if (pred(point)) {
            return point;
          }
  
          point = prevPoint(point);
        }
  
        return null;
      };
  
      /**
       * @method nextPointUntil
       *
       * @param {BoundaryPoint} point
       * @param {Function} pred
       * @return {BoundaryPoint}
       */
      var nextPointUntil = function (point, pred) {
        while (point) {
          if (pred(point)) {
            return point;
          }
  
          point = nextPoint(point);
        }
  
        return null;
      };
  
      /**
       * @method walkPoint
       *
       * @param {BoundaryPoint} startPoint
       * @param {BoundaryPoint} endPoint
       * @param {Function} handler
       * @param {Boolean} isSkipInnerOffset
       */
      var walkPoint = function (startPoint, endPoint, handler, isSkipInnerOffset) {
        var point = startPoint;
  
        while (point) {
          handler(point);
  
          if (isSamePoint(point, endPoint)) {
            break;
          }
  
          var isSkipOffset = isSkipInnerOffset &&
                             startPoint.node !== point.node &&
                             endPoint.node !== point.node;
          point = nextPoint(point, isSkipOffset);
        }
      };
  
      /**
       * @method makeOffsetPath
       *
       * return offsetPath(array of offset) from ancestor
       *
       * @param {Node} ancestor - ancestor node
       * @param {Node} node
       */
      var makeOffsetPath = function (ancestor, node) {
        var ancestors = listAncestor(node, func.eq(ancestor));
        return $.map(ancestors, position).reverse();
      };
  
      /**
       * @method fromOffsetPath
       *
       * return element from offsetPath(array of offset)
       *
       * @param {Node} ancestor - ancestor node
       * @param {array} offsets - offsetPath
       */
      var fromOffsetPath = function (ancestor, offsets) {
        var current = ancestor;
        for (var i = 0, len = offsets.length; i < len; i++) {
          if (current.childNodes.length <= offsets[i]) {
            current = current.childNodes[current.childNodes.length - 1];
          } else {
            current = current.childNodes[offsets[i]];
          }
        }
        return current;
      };
  
      /**
       * @method splitNode
       *
       * split element or #text
       *
       * @param {BoundaryPoint} point
       * @param {Boolean} [isSkipPaddingBlankHTML]
       * @return {Node} right node of boundaryPoint
       */
      var splitNode = function (point, isSkipPaddingBlankHTML) {
        // split #text
        if (isText(point.node)) {
          // edge case
          if (isLeftEdgePoint(point)) {
            return point.node;
          } else if (isRightEdgePoint(point)) {
            return point.node.nextSibling;
          }
  
          return point.node.splitText(point.offset);
        }
  
        // split element
        var childNode = point.node.childNodes[point.offset];
        var clone = insertAfter(point.node.cloneNode(false), point.node);
        appendChildNodes(clone, listNext(childNode));
  
        if (!isSkipPaddingBlankHTML) {
          paddingBlankHTML(point.node);
          paddingBlankHTML(clone);
        }
  
        return clone;
      };
  
      /**
       * @method splitTree
       *
       * split tree by point
       *
       * @param {Node} root - split root
       * @param {BoundaryPoint} point
       * @param {Boolean} [isSkipPaddingBlankHTML]
       * @return {Node} right node of boundaryPoint
       */
      var splitTree = function (root, point, isSkipPaddingBlankHTML) {
        // ex) [#text, <span>, <p>]
        var ancestors = listAncestor(point.node, func.eq(root));
  
        if (!ancestors.length) {
          return null;
        } else if (ancestors.length === 1) {
          return splitNode(point, isSkipPaddingBlankHTML);
        }
  
        return ancestors.reduce(function (node, parent) {
          var clone = insertAfter(parent.cloneNode(false), parent);
  
          if (node === point.node) {
            node = splitNode(point, isSkipPaddingBlankHTML);
          }
  
          appendChildNodes(clone, listNext(node));
  
          if (!isSkipPaddingBlankHTML) {
            paddingBlankHTML(parent);
            paddingBlankHTML(clone);
          }
          return clone;
        });
      };
  
      /**
       * split point
       *
       * @param {Point} point
       * @param {Boolean} isInline
       * @return {Object}
       */
      var splitPoint = function (point, isInline) {
        // find splitRoot, container
        //  - inline: splitRoot is a child of paragraph
        //  - block: splitRoot is a child of bodyContainer
        var pred = isInline ? isPara : isBodyContainer;
        var ancestors = listAncestor(point.node, pred);
        var topAncestor = list.last(ancestors) || point.node;
  
        var splitRoot, container;
        if (pred(topAncestor)) {
          splitRoot = ancestors[ancestors.length - 2];
          container = topAncestor;
        } else {
          splitRoot = topAncestor;
          container = splitRoot.parentNode;
        }
  
        // split with splitTree
        var pivot = splitRoot && splitTree(splitRoot, point, isInline);
  
        return {
          rightNode: pivot,
          container: container
        };
      };
  
      var create = function (nodeName) {
        return document.createElement(nodeName);
      };
  
      var createText = function (text) {
        return document.createTextNode(text);
      };
  
      /**
       * @method remove
       *
       * remove node, (isRemoveChild: remove child or not)
       *
       * @param {Node} node
       * @param {Boolean} isRemoveChild
       */
      var remove = function (node, isRemoveChild) {
        if (!node || !node.parentNode) { return; }
        if (node.removeNode) { return node.removeNode(isRemoveChild); }
  
        var parent = node.parentNode;
        if (!isRemoveChild) {
          var nodes = [];
          var i, len;
          for (i = 0, len = node.childNodes.length; i < len; i++) {
            nodes.push(node.childNodes[i]);
          }
  
          for (i = 0, len = nodes.length; i < len; i++) {
            parent.insertBefore(nodes[i], node);
          }
        }
  
        parent.removeChild(node);
      };
  
      /**
       * @method removeWhile
       *
       * @param {Node} node
       * @param {Function} pred
       */
      var removeWhile = function (node, pred) {
        while (node) {
          if (isEditable(node) || !pred(node)) {
            break;
          }
  
          var parent = node.parentNode;
          remove(node);
          node = parent;
        }
      };
  
      /**
       * @method replace
       *
       * replace node with provided nodeName
       *
       * @param {Node} node
       * @param {String} nodeName
       * @return {Node} - new node
       */
      var replace = function (node, nodeName) {
        if (node.nodeName.toUpperCase() === nodeName.toUpperCase()) {
          return node;
        }
  
        var newNode = create(nodeName);
  
        if (node.style.cssText) {
          newNode.style.cssText = node.style.cssText;
        }
  
        appendChildNodes(newNode, list.from(node.childNodes));
        insertAfter(newNode, node);
        remove(node);
  
        return newNode;
      };
  
      var isTextarea = makePredByNodeName('TEXTAREA');
  
      /**
       * @method html
       *
       * get the HTML contents of node
       *
       * @param {jQuery} $node
       * @param {Boolean} [isNewlineOnBlock]
       */
      var html = function ($node, isNewlineOnBlock) {
        var markup = isTextarea($node[0]) ? $node.val() : $node.html();
  
        if (isNewlineOnBlock) {
          var regexTag = /<(\/?)(\b(?!!)[^>\s]*)(.*?)(\s*\/?>)/g;
          markup = markup.replace(regexTag, function (match, endSlash, name) {
            name = name.toUpperCase();
            var isEndOfInlineContainer = /^DIV|^TD|^TH|^P|^LI|^H[1-7]/.test(name) &&
                                         !!endSlash;
            var isBlockNode = /^BLOCKQUOTE|^TABLE|^TBODY|^TR|^HR|^UL|^OL/.test(name);
  
            return match + ((isEndOfInlineContainer || isBlockNode) ? '\n' : '');
          });
          markup = $.trim(markup);
        }
  
        return markup;
      };
  
      var value = function ($textarea, stripLinebreaks) {
        var val = $textarea.val();
        if (stripLinebreaks) {
          return val.replace(/[\n\r]/g, '');
        }
        return val;
      };
  
      return {
        /** @property {String} NBSP_CHAR */
        NBSP_CHAR: NBSP_CHAR,
        /** @property {String} ZERO_WIDTH_NBSP_CHAR */
        ZERO_WIDTH_NBSP_CHAR: ZERO_WIDTH_NBSP_CHAR,
        /** @property {String} blank */
        blank: blankHTML,
        /** @property {String} emptyPara */
        emptyPara: '<p>' + blankHTML + '</p>',
        makePredByNodeName: makePredByNodeName,
        isEditable: isEditable,
        isControlSizing: isControlSizing,
        buildLayoutInfo: buildLayoutInfo,
        isText: isText,
        isVoid: isVoid,
        isPara: isPara,
        isPurePara: isPurePara,
        isInline: isInline,
        isBodyInline: isBodyInline,
        isBody: isBody,
        isParaInline: isParaInline,
        isList: isList,
        isTable: isTable,
        isCell: isCell,
        isBlockquote: isBlockquote,
        isBodyContainer: isBodyContainer,
        isAnchor: isAnchor,
        isDiv: makePredByNodeName('DIV'),
        isLi: isLi,
        isBR: makePredByNodeName('BR'),
        isSpan: makePredByNodeName('SPAN'),
        isB: makePredByNodeName('B'),
        isU: makePredByNodeName('U'),
        isS: makePredByNodeName('S'),
        isI: makePredByNodeName('I'),
        isImg: makePredByNodeName('IMG'),
        isTextarea: isTextarea,
        isEmpty: isEmpty,
        isEmptyAnchor: func.and(isAnchor, isEmpty),
        isClosestSibling: isClosestSibling,
        withClosestSiblings: withClosestSiblings,
        nodeLength: nodeLength,
        isLeftEdgePoint: isLeftEdgePoint,
        isRightEdgePoint: isRightEdgePoint,
        isEdgePoint: isEdgePoint,
        isLeftEdgeOf: isLeftEdgeOf,
        isRightEdgeOf: isRightEdgeOf,
        prevPoint: prevPoint,
        nextPoint: nextPoint,
        isSamePoint: isSamePoint,
        isVisiblePoint: isVisiblePoint,
        prevPointUntil: prevPointUntil,
        nextPointUntil: nextPointUntil,
        walkPoint: walkPoint,
        ancestor: ancestor,
        singleChildAncestor: singleChildAncestor,
        listAncestor: listAncestor,
        lastAncestor: lastAncestor,
        listNext: listNext,
        listPrev: listPrev,
        listDescendant: listDescendant,
        commonAncestor: commonAncestor,
        wrap: wrap,
        insertAfter: insertAfter,
        appendChildNodes: appendChildNodes,
        position: position,
        hasChildren: hasChildren,
        makeOffsetPath: makeOffsetPath,
        fromOffsetPath: fromOffsetPath,
        splitTree: splitTree,
        splitPoint: splitPoint,
        create: create,
        createText: createText,
        remove: remove,
        removeWhile: removeWhile,
        replace: replace,
        html: html,
        value: value
      };
    })();
  
    return dom;
  });
  