groc icon indicating copy to clipboard operation
groc copied to clipboard

dom.js can't be documented due to errors.

Open crocket opened this issue 12 years ago • 1 comments

The errors were ! Failed to parse doc tags /blah/blah/dom.js: Cannot read property '1' of null TypeError: Cannot read property '1' of null at Object.module.exports.DOC_TAGS.return.parseValue (/home/elisa/repos/others/winternote/node_modules/groc/lib/doc_tags.coffee:180:23) at Object.module.exports.Utils.parseDocTags (/home/elisa/repos/others/winternote/node_modules/groc/lib/utils.coffee:472:41) at Default.module.exports.Base.renderFile (/home/elisa/repos/others/winternote/node_modules/groc/lib/styles/base.coffee:31:20) at /home/elisa/repos/others/winternote/node_modules/groc/lib/project.coffee:74:24 at fs.js:266:14 at Object.oncomplete (fs.js:107:15)

The content of dom.js is shown below.

"use strict";

// > # [Document Object Model](https://developer.mozilla.org/en-US/docs/DOM/About_the_Document_Object_Model)
define('dom', ['underscore', 'iter', 'assert'], function(_, iter, assert) {
  /* ------------------------------------------------------------------------------- */
  // dom predicate

  // ### isBlock
  // > 크기를 갖는 컨테이너 여부 리턴 ex) P, DIV, IMG(float)
  // > [HTML Display property](http://www.w3schools.com/cssref/pr_class_display.asp)
  var isBlock = function(el) {
    if (el && (isBodyContainer(el) || isPara(el) || isList(el) || isHR(el) || isDiv(el) || isTable(el)) ) {
      return true;
    }

    if (el && el.style && el.style["float"] && el.style["float"] !== '' && el.style["float"] !== 'none'
        || (el && el.style && el.style.cssText && el.style.cssText.toLowerCase().indexOf('float') > -1)) {
      return true;
    }

    return false;
  };

  // ### isTable
  // > 테이블 여부 판단
  var isTable = function(el) {
    return el && el.nodeName === "TABLE";
  };

  // ### isTBody
  // > TBody 여부 판단
  var isTBody = function(el) {
    return el && el.nodeName === "TBODY";
  };

  // ### isTHead
  // > THead 여부 판단
  var isTHead = function(el) {
    return el && el.nodeName === "THEAD";
  };

  // ### isTR
  // > TR 여부 판단
  // * `el` HTMLElement -> bool
  var isTR = function(el) {
    return el && el.nodeName === 'TR';
  };

  // ### isTD
  // > TD 여부 판단
  // * `el` HTMLElement -> bool
  var isTD = function(el) {
    return el && el.nodeName === 'TD';
  };

  // ### isCell
  // > 테이블셀 여부 판단
  // * `el` HTMLElement -> bool
  var isCell = function(el) {
    return el && (el.nodeName === 'TD' || el.nodeName === 'TH');
  };

  // ### isContainer
  // > 컨테이너 객체 여부 판단: 다른 객체(UL, DIV 등)을 담을 수 있는 객체
  var isContainer = function(el) {
    return !isData(el);
  };

  // ### isFirstContainerChild
  // > 부모의 첫번째 컨테이너 객체인지 여부 판단
  var isFirstContainerChild = function(el) {
    var aChild = _.filter(_.map(el.parentNode.childNodes), isContainer);
    return aChild.length > 0 && _.first(aChild) === el;
  };

  // ### isLastContainerChild
  // > 부모의 마지막 컨테이너 객체인지 여부 판단
  var isLastContainerChild = function(el) {
    var aChild = _.filter(_.map(el.parentNode.childNodes), isContainer);
    return aChild.length > 0 && _.last(aChild) === el;
  };

  // ###isDataBlock
  // > hr이나 float된 image등 커서가 위치할 수 있는 객체
  var isDataBlock = function(el) {
    return isHR(el) || isIMG(el) || isBR(el);
  };

  // ###isDocumentFragment
  var isDocumentFragment = function(el) {
    return el && el.nodeName === "#document-fragment";
  };

  // ### isInlineContainer
  // > 인라인을 가질 수 있는 노드 여부 판단
  var isInlineContainer = function(el) {
    return isPara(el) || isBodyContainer(el) || isDocumentFragment(el) || isNoteEditable(el);
  };

  // ### isIMG
  // > 이미지 노드 여부 판단
  var isIMG = function(el) {
    return el && el.nodeName === "IMG";
  };

  // ### isData
  // > 인라인 노드 여부 판단
  var isData = function(el) {
    return el && (el.nodeName === "#text" ||
                  el.nodeName === "IMG" ||
                  el.nodeName === "HR" ||
                  el.nodeName === "INPUT" ||
                  el.nodeName === "BR") ;
  };

  // ### isText
  // > 텍스트 노드 여부 판단
  var isText = function(el) {
    return el && (el.nodeName === "#text");
  };

  var isFont = function(el) {
    return el && (el.nodeName === "FONT");
  };

  var isStrike = function(el) {
    return el && (el.nodeName === "STRIKE" || el.nodeName === "S" || el.nodeName === "DEL");
  };

  var isBold = function(el) {
    return el && (el.nodeName === "BOLD" || el.nodeName === "B" || el.nodeName === "STRONG");
  };

  var isItalic = function(el) {
    return el && (el.nodeName === "ITALIC" || el.nodeName === "I");
  };

  var isUnderline = function(el) {
    return el && (el.nodeName === "UNDERLINE" || el.nodeName === "U");
  };

  var isDiv = function(el) {
    return el && el.nodeName === "DIV";
  };

  var isBlockquote = function(el) {
    return el && el.nodeName === "BLOCKQUOTE";
  };

  var isNoteEditor = function(el){
    return el && $(el).hasClass('note-editor');
  };

  var isNoteEditable = function(el){
    return el && $(el).hasClass('note-editable');
  };

  var isFirstParaTextnode = function(el) {
    var elPara = ancestor(el, function(el){ return isPara(el) || isCell(el); });
    if (!elPara) { return false; }

    var aAncestor =  _.reject(listAncestor(elPara), isCell);
    return _.all(aAncestor, isFirstContainerChild);
  };

  var isLastParaTextnode = function(el) {
    var elPara = ancestor(el, function(el){ return isPara(el) || isCell(el); });
    if (!elPara) { return false; }

    var aAncestor = _.reject(listAncestor(elPara), isCell);
    return _.all(aAncestor, isLastContainerChild);
  };

  var isBodyContainer = function(el) {
    return isNoteEditable(el) || isCell(el) || isTR(el) || isTD(el) || isBlockquote(el) || isTHead(el) || isTBody(el) || isDiv(el);
  };

  // ### isEdgeOf
  // > 입력 받은 위치의 구석(왼쪽 혹은 오른쪽 위치) 여부 리턴
  var isEdgeOf = function(el, offset) {
    if (isEmpty(el)) { return true; }
    return offset === 0 || length(el) === offset;
  };

  // ### isLeftEdge
  // > 입력 받은 위치의 왼쪽구석 여부 리턴
  var isLeftEdge = function(el, offset) {
    return offset === 0;
  };

  // ### isRightEdge
  // > 입력 받은 위치의 오른쪽구석 여부 리턴
  var isRightEdge = function(el, offset) {
    return offset === length(el);
  };

  // ### isLeftEdgeOf
  // > 입력 받은 위치의 root로 부터의 왼쪽구석 여부 리턴
  var isLeftEdgeOf = function(elRoot, el) {
    while (el && el !== elRoot) {
      if (position(el) !== 0) { return false; }
      el = el.parentNode;
    }

    assert.ok(el !== null); // el이 root에 포함되지 않는 경우...
    return true;
  };

  // ### isRightEdgeOf
  // > 입력 받은 위치의 root로 부터의 오른쪽구석 여부 리턴
  var isRightEdgeOf = function(elRoot, el) {
    while (el && el !== elRoot) {
      if (position(el) !== length(el.parentNode)-1) { return false; }
      el = el.parentNode;
    }

    assert.ok(el !== null); // el이 root에 포함되지 않는 경우...
    return true;
  };

  // ### isEmpty
  // > 노드가 비어 있는 노드인지 판단
  var isEmpty = function(el) {
    if (isData(el)) {
      return false;
    }

    return el.childNodes.length === 0;
  };

  // ### isSiblingOf
  // > elSrc 가 elTarget에 형재 노드인지 검사
  var isSiblingOf = function(elSrc, elTarget) {
    var aChild = elSrc.parentNode.childNodes;
    for (var i=0, len=aChild.length; i  elSrc가 elParent에 자식 여부인지 판단
  var contains = function(elSrc, elParent) {
    return !!ancestor(elSrc, function(el) {
      return el === elParent;
    });
  };

  // ### isAdjacent
  // > 인접노드 여부 리턴
  var isAdjacent = function(elA, elB) {
    return elA.nextSibling === elB || elB.nextSibling === elA;
  };

  // ### isNBSP
  // > non breakable space 판단
  var isNBSP = function(el) {
    return isText(el) && NBSP === el.nodeValue;
  };

  // ### isFEFF
  // > no width non breakable space 판단
  var isFEFF = function(el) {
    return isText(el) && FEFF === el.nodeValue;
  };

  // ### isHeading
  // > 머리글여부 리턴
  // * `el` HTMLElement -> bool
  var isHeading = function(el) {
    return el && /^H[1-7]/.test(el.nodeName);
  };

  // ### isPara
  // > 문단 여부 판단
  // * `el` HTMLElement -> bool
  var isPara = function(el) {
    return el && (/^P|^LI/.test(el.nodeName) || /^H[1-7]/.test(el.nodeName));
  };

  // ### isPurePara
  // > 리스트가 아닌 순수한 문단
  var isPurePara = function(el) {
    return isPara(el) && !isLI(el);
  };

  // ### isList
  // > 리스트 여부 판단
  // * `el` HTMLElement -> bool
  var isList = function(el) {
    return el && (el.nodeName === 'UL' || el.nodeName === 'OL');
  };

  // ### isOrderedList
  // > 순서가 있는 리스트 여부 판단
  // * `el` HTMLElement -> bool
  var isOrderedList = function(el) {
    return el && el.nodeName === 'OL';
  };

  // ### isUnorderedList
  // > 순서가 없는 리스트 여부 판단
  // * `el` HTMLElement -> bool
  var isUnorderedList = function(el) {
    return el && el.nodeName === 'UL';
  };

  // ### isBullet
  // > ul/ol여부 판단
  var isBullet = function(el) {
    return isOrderedList(el) || isUnorderedList(el);
  };

  // ### isLI
  // > LI 여부 리턴
  var isLI = function(el) {
    return el && el.nodeName === 'LI';
  };

  // ### isP
  // > P 여부 리턴
  var isP = function(el) {
    return el && el.nodeName === 'P';
  };

  // ### isA
  // > A 여부 리턴
  var isA = function(el) {
    return el && el.nodeName === 'A';
  };

  // ### isHR
  // > HorizontalRule 여부 판단
  // * `el` HTMLElement -> bool
  var isHR = function(el) {
    return el && el.nodeName === 'HR';
  };

  // ### isBR
  // * `el` HTMLElement -> bool
  var isBR = function(el) {
    return el && el.nodeName === 'BR';
  };

  // ### isEmptyText
  // > 빈 텍스트 엘리먼트 여부 리턴
  var isEmptyText = function(el) {
    return isText(el) && (el.nodeValue === '');
  };

  // ### isEmptyPara
  // > 빈 문단 여부 리턴
  var isEmptyPara = function(el) {
    if (!isPara(el)) { return false; }

    var aElInline = descendants(el, isData);
    return aElInline.length === 0 ||
           (aElInline.length === 1 && isNBSP(aElInline[0])) ||
           (aElInline.length === 1 && isFEFF(aElInline[0]));
  };

  // ### isEmptyInlineContainer
  // > 빈 컨테이너 여부 리턴
  var isEmptyInlineContainer = function(el) {
    if (isEmptyPara(el)) { return true; }
    if (!isInlineContainer(el)) { return false; }

    var aElInline = descendants(el, isData);

    return aElInline.length === 0 ||
           (aElInline.length === 1 && isNBSP(aElInline[0])) ||
           (aElInline.length === 1 && isFEFF(aElInline[0]));
  };

  // ### isP
  // > SPAN 여부 리턴
  var isSpan = function(el) {
    return el && el.nodeName === 'SPAN';
  };

  // ### isInvisible
  // > 보이지 않는 노드 여부 판단
  var isInvisible = function(el) {
    var aElInline = descendants(el, isData);
    return aElInline.length === 0;
  };

  // ### isOnContentEditable
  // > 편집영역 위에 있는 노드 여부 판단
  var isOnContentEditable = function(el) {
    while (el) {
      if (isNoteEditable(el)) { return true; }
      el = el.parentNode;
    }
    return false;
  };

  /* ------------------------------------------------------------------------------- */
  // dom modifier

  // ### slice
  // > dom 트레버싱
  // > `fApply` 각 노드에 적용할 함수
  // > `fPred` 노드 탐색을 어느 시점에 멈출지 판단 | optional
  var _traverse = function(el, fBefore, fAfter, fStop, bRtoL) {
    try {
      var trav = function(el) {
        if (fStop && fStop(el)) { throw "break"; }
        if (fBefore) { fBefore(el); }
        var aChild = bRtoL ? _.toArray(el.childNodes).reverse() : _.toArray(el.childNodes);
        for (var i=0, len=aChild.length; i  상향 탐색
  var traverse = function(el, fApply, fStop) {
    _traverse(el, null, fApply, fStop);
  };

  // ### traverseInt
  // 좌측 구석으로 하향탐색
  var traverseIn = function(el, fApply, fStop) {
    _traverse(el, fApply, null, fStop);
  };

  // ### traverseRight
  // 우측 구석 -> 상향탐색
  var traverseRight = function(el, fApply, fPred) {
    _traverse(el, null, fApply, fPred, true);
  };

  // ### traverseRightIn
  // 우측 구석으로 하향 탐색
  var traverseRightIn = function(el, fApply, fPred) {
    _traverse(el, fApply, null, fPred, true);
  };

  // ### descendants
  // > el 밑에 자식 노드 중에 fPred를 만족하는 모든 노드를 반환
  var descendants = function(el, fPred) {
    fPred = fPred || iter.success;

    var aElDescendant = [];
    traverse(el, function(elDescendant) {
      if (fPred(elDescendant)) {
        aElDescendant.push(elDescendant);
      }
    });

    return aElDescendant;
  };

  // ### hasDescendants
  // > 자식 노드중 pred를 만족하는지 여부 판단
  var hasDescendants = function(el, pred) {
    return descendants(el, pred).length > 0;
  };

  // ### hasParent
  // > 부모 노드가 있는지 여부 판단
  var hasParent = function(el) {
    return el.parentNode;
  };

  // ### hasNext
  // > 다음 형제 노드가 있는지 검사
  var hasNext = function(el) {
    return el && el.nextSibling;
  };

  // ### hasPrev
  // > 이전 형제 노드가 있는지 검사
  var hasPrev = function(el) {
    return el && el.previousSibling;
  };

  // ### hasChild
  // > 자식 노드 존재하는지 여부 검사
  var hasChild = function(el) {
    return el && el.childNodes && el.childNodes.length > 0;
  };

  // ### ancestor
  // > pred에 일치하는 가장 가까운 부모 노드 찾기
  // > 자신을 포함해서 찾는다.
  // TODO: note-editable을 사용하지 않는 버전 필요
  var ancestor = function(el, pred) {
    var elCurrent = el;
    while (elCurrent) {
      if (!elCurrent || elCurrent.className === 'note-editable') { break; }
      if (pred(elCurrent)) { return elCurrent; }
      elCurrent = elCurrent.parentNode;
    }
    return null;
  };

  // ### lastAncestor
  // > pred에 일치하는 최상위 부모노드 찾기
  var lastAncestor = function(el, pred) {
    var aAncestor = listAncestor(el);
    return _.last(_.filter(aAncestor, pred));
  };

  // ### listAncestor
  // > ancestor 목록 리턴(가까운 Ancestor부터)
  var listAncestor = function(el, pred) {
    pred = pred || iter.fail;

    var aElVisited = [];
    ancestor(el, function(e) {
      aElVisited.push(e);
      return pred(e);
    });

    return aElVisited;
  };


  // ### commonAncestor
  // > commonAncestor 반환
  var commonAncestor = function(node1, node2) {
    var ancestors = [], n;
    for (n = node1; n; n = n.parentNode) {
      ancestors.push(n);
    }

    for (n = node2; n; n = n.parentNode) {
      if (_.contains(ancestors, n)) {
        return n;
      }
    }
    return null;
  };

  var getClosestAncestorIn = function(node, ancestor, selfIsAncestor) {
    var p, n = selfIsAncestor ? node : node.parentNode;
    while (n) {
      p = n.parentNode;
      if (p === ancestor) {
        return n;
      }
      n = p;
    }
    return null;
  };

  var comparePoints = function(nodeA, offsetA, nodeB, offsetB) {
    // See http://www.w3.org/TR/DOM-Level-2-Traversal-Range/ranges.html#Level-2-Range-Comparing
    var nodeC, root, childA, childB, n;
    if (nodeA == nodeB) {
      // Case 1: nodes are the same
      return offsetA === offsetB ? 0 : (offsetA  입력받은 posA, posB을 비교
  // > @return number  0: equal to,
  // >                -1: posA less than posB,
  // >                 1: posA greater than posB
  var comparePositions = function(posA, posB) {
    return comparePoints(posA.cont, posA.offset, posB.cont, posB.offset);
  };

  // ### leftBottom
  // > 가장 왼쪽 노드 찾아가서 반환
  var leftBottom = function(el) {
    return el.firstChild ? leftBottom(el.firstChild) : el;
  };

  // ### inlineTop
  // Block에 가장 가까운 Inline 노드를 반환
  var inlineTop = function(el) {
    return _.last(listAncestor(el, function(e) { return isBlock(e.parentNode); }));
  };

  // ### styleInlineTop
  // Block에 가장 가까운 style 가능한 Inline 노드를 반환
  var styleInlineTop = function(el) {
    return _.last(listAncestor(el, function(e) { return isA(e.parentNode) ||  isBlock(e.parentNode); }));
  };

  // ### closetBody
  // > 가장 가까운 bodyContainer를 반환
  var closetBody = function(el) {
    var elCurrent = el;

    while (elCurrent) {
      if (isBodyContainer(elCurrent)) {
        return elCurrent;
      }
      elCurrent = elCurrent.parentNode;
    }
  };

  // ### length
  // > 입력받은 엘리먼트의 자식노드의 수 반환
  // > 텍스트 노드의 경우 글자 수 반환
  var length = function(el) {
    assert.ok(el);
    if (isData(el)) {
      return isText(el) ? el.nodeValue.length : 1;
    }

    switch (el.nodeType) {
    case 7:
    case 10:
      return 0;
    case 3:
    case 8:
      return el.length;
    default:
      return el.childNodes.length;
    }
  };

  // ### position
  // > 현재 노드 위치를 반환
    var position = function (el) {
    var cnt = 0;
        for (var i=0; el = el.previousSibling; i++) {}
    return i;
    };

  // ### depth
  // > 현재 노드에 depth를 반환
  var depth = function(el, pred) {
    pred = pred || iter.fail;
    return listAncestor(el, pred).length;
  };

  // ### remove
  // > node el을 dom 에서 삭제
  var remove = function(el) {
    removeNode(el, true);
  };

  // ### removeNode
  // > elNode를 dom 상에서 제거 [removeNode](http://help.dottoro.com/ljxjnsic.php)
  // > `bRemoveChild` child node 제거 여부, true인 경우 자식 노드를 모두 제거한다
  var removeNode = function(elNode, bRemoveChild) {
    if (!elNode || !elNode.parentNode) { return; }
    if (elNode.removeNode) { elNode.removeNode(bRemoveChild); return; }

    var elParent = elNode.parentNode;
    if (!bRemoveChild) {
      var aNode = [], i, len;
      for (i=0; i  el 이 빈 노드인 경우 dom 상에서 제거한다.
  // > 재귀적으로 상위 노드를 계속 탐색한다
  var removeWhile = function(el, pred) {
    assert.ok(el);

    var walk = function(el) {
      var elParent = el.parentNode;
      if (elParent && pred(el)) {
        elParent.removeChild(el);
        walk(elParent, pred);
      }
    };

    var elParent = el.parentNode;
    remove(el);
    walk(elParent);
  };

  // ### removeAllAttributes
  // > element의 모든 속성 제거
  var removeAllAttributes = function(el, excludePred) {
    _.each(_.toArray(el.attributes), function(attr) {
      if (!attr) { return; }
      if (excludePred && excludePred(attr.name)) return;
      el.removeAttribute(attr.name);
    });
  };

  var NBSP = String.fromCharCode(160);
  var FEFF = "\ufeff";
  var BASE_PARA_HTML = '

'+FEFF+'

'; // appends: append children var appends = function(node, aChild) { _.each(aChild, function(child) { node.appendChild(child); }); }; // ### split // > `elRoot` : 잘라낼 트리 루트 // > `elPivot` : 잘라낼 기준 노드 // > `offset` : 텍스트 노드인 경우 잘라낼 offset var splitTree = function(root, pivot, offset) { assert.ok(root); assert.ok(pivot); // split pivot var splitPivot = function(current, offset) { if (offset >= length(current)) { return current.nextSibling; } if (offset === 0) { return current; } // splitText if (isText(current)) { return current.splitText(offset); } // splitNode var child = current.childNodes[offset]; current = insertAfter(current.cloneNode(false), current); appends(current, listNext(child, iter.fail)); return current; }; var aAncestor = listAncestor(pivot, iter.eq(root)); if (aAncestor.length === 1) { return splitPivot(pivot, offset); } return _.reduce(_.tail(aAncestor), function(current, parent) { var parentClone = insertAfter(parent.cloneNode(false), parent); if (current === pivot) { current = splitPivot(current, offset); } appends(parentClone, listNext(current, iter.fail)); return parentClone; }, pivot); }; // Note that we cannot use splitText() because it is bugridden in IE 9. var splitDataNode = function(node, index, positionsToPreserve) { var newNode = node.cloneNode(false); newNode.deleteData(0, index); node.deleteData(index, node.length - index); insertAfter(newNode, node); // Preserve positions if (positionsToPreserve) { for (var i = 0, position; position = positionsToPreserve[i++]; ) { // Handle case where position was inside the portion of node after the split point if (position.node == node && position.offset > index) { position.node = newNode; position.offset -= index; } // Handle the case where the position is a node offset within node's parent else if (position.node == node.parentNode && position.offset > position(node)) { ++position.offset; } } } return newNode; }; // ### splitText // > 텍스트 노드를 반환 var splitText = function(el, offset) { if (offset === 0) { return el; } else if (length(el) === offset) { return null; } else { return splitDataNode(el, offset); } }; // ### insertBefore // > elNew를 elTarget 이전에 삽입 var insertBefore = function(elNew, elTarget) { return elTarget.parentNode.insertBefore(elNew, elTarget); }; // ### insertAfter // > elNew를 elTarget 이후에 삽입 var insertAfter = function(node, precedingNode) { var nextNode = precedingNode.nextSibling, parent = precedingNode.parentNode; if (nextNode) { parent.insertBefore(node, nextNode); } else { parent.appendChild(node); } return node; }; // ### replace // > 입력받은 노드의 nodeName을 변경 // > ! 원본은 삭제 var replace = function(el, sNodeName) { assert.ok(el && sNodeName); var elNew = document.createElement(sNodeName); var sStyle = el.style.cssText; if (sStyle) { elNew.style.cssText = sStyle; } if (hasChild(el)) { _.each(_.toArray(el.childNodes), function(elChild) { elNew.appendChild(elChild); }); } insertAfter(elNew, el); removeNode(el); return elNew; }; // ### clone // 노드를 복제 // > !ie8 에서 잘라진 텍스트 노드를 normalize 해버리는 문제가 있다. var clone = function(el) { if (!el) { return; } if (el.childNodes) { // IE8에서 textNode가 병합되는 문제 회피 var elClone = el.cloneNode(false); _.each(el.childNodes, function(elChild){ elClone.appendChild(elChild.cloneNode(true)); }); return elClone; } else { return el.cloneNode(true); } }; // ### parent // > 노드에 부모 노드 반환 var parent = function(el) { var elParent = el.parentNode; if (elParent.className === 'note-editable') { return null; } return elParent; }; // ### next // > 다음 형제 노드 찾기 var next = function(el, pred) { var elNext = el; while (elNext) { if (pred(elNext)) { return elNext; } elNext = elNext.nextSibling; } return null; }; // ### listNext // > 다음 형제 노드를 pred가 만족할 때 까지 acc, 자신을 포함 var listNext = function(el, pred) { var aNode = [], elNext = el; while (elNext) { if (pred(elNext)) { break; } aNode.push(elNext); elNext = elNext.nextSibling; } return aNode; }; // ### listPrev // > 이전 형제 노드를 pred가 만족할 때 까지 acc, 자신을 포함 var listPrev = function(el, pred) { var aNode = [], elPrev = el; while (elPrev) { if (pred(elPrev)) { break; } aNode.push(elPrev); elPrev = elPrev.previousSibling; } return aNode; }; // ### nodesBetween // > start, end 사이에 노드를 모두 가지고 온다 var nodesBetween = function(elStart, elEnd) { if (elStart === elEnd) { return [elStart]; } var elCommon = commonAncestor(elStart, elEnd); var elEndAncestor = ancestor(elEnd, iter.peq('parentNode', elCommon)); var aNode = [], bFlag = false; traverse(elCommon, function(el) { if (el === elStart) { bFlag = true; } if (bFlag) { aNode.push(el); } }, iter.eq(elEndAncestor)); var aEnd = []; traverseIn(elEndAncestor, iter.push(aEnd), iter.eq(elEnd)); aEnd.push(elEnd); return aNode.concat(aEnd); }; // ### nextBP var nextBP = function(bpInit) { var elCont = bpInit.node, offset = bpInit.offset; if (length(elCont) === offset) { if (elCont.className === 'note-editable') { return null; } return {node: elCont.parentNode, offset: position(elCont) + 1}; } else { if (hasChild(elCont)) { var elChild = elCont.childNodes[offset]; return {node: elChild, offset: 0}; } else { return {node: elCont, offset: offset + 1}; } } }; // ### prevBP var prevBP = function(bpInit) { var elCont = bpInit.node, offset = bpInit.offset; if (offset === 0) { if (elCont.className === 'note-editable') { return null; } return {node: elCont.parentNode, offset: position(elCont)}; } else { if (hasChild(elCont)) { var elChild = elCont.childNodes[offset - 1]; return {node: elChild, offset: length(elChild)}; } else { return {node: elCont, offset: offset - 1}; } } }; // ### makeUntil var makeUntil = function(fnUnit) { return function(pred) { var stack = [], item = fnUnit.call(this); while (item) { if (pred(item, stack)) { return item; } stack.push(item); item = fnUnit.call(item); } }; }; // ### makeOffsetPath // > BPPath를 리턴 var makeOffsetPath = function(elRoot, elPivot) { var aOffset = [], el = elPivot; while (el && el !== elRoot) { aOffset.push(position(el)); el = el.parentNode; } assert.ok(el !== null); // el이 root에 포함되지 않는 경우... aOffset.push(position(elRoot)); return aOffset.reverse(); }; // ### fromOffsetPath // > elRoot와 aOffset을 받아 특정 노드를 리턴 var fromOffsetPath = function(elRoot, aOffset) { assert.ok(elRoot && aOffset); aOffset = _.rest(aOffset); var el = elRoot; _.each(aOffset, function(offset) { el = el.childNodes[offset]; }); return el; }; // ### create // > dom 노드 생성 var create = function(sTagName) { assert.ok(sTagName); return document.createElement(sTagName); }; // ### fragment // > html 을 받아서 documentFrgment를 반환 var fragment = function(sHTML) { var frag = document.createDocumentFragment(); if (!sHTML) { return frag; } var elTemp = document.createElement("DIV"); elTemp.innerHTML = sHTML; _.each(_.toArray(elTemp.childNodes), function(el) { frag.appendChild(el); }); return frag; }; // ### element // > shtml을 받아서 element를 반환 // ! shtml은 루트에 요소가 하나여야 한다 // ex) true ==
 
, // ex) false ==

var element = function(sHTML) { var elFrag = fragment(sHTML); assert.ok(elFrag.childNodes.length === 1); return elFrag.firstChild; }; // ### text var text = function(el) { var elDiv = create("div"); elDiv.appendChild(clone(el)); return elDiv.textContent || elDiv.innerText ; }; // ### wrap var wrap = function(sTag, el) { var elWrap = create(sTag); insertBefore(elWrap, el); elWrap.appendChild(el); return elWrap; }; // ### stripTagSpace // > HTML 태그 사이에 공백을 제거한다 var stripTagSpace = function(sText) { // 중복되는 정규식이 있어보임 return sText.replace(/\r?\n?/g, "") // 뉴라인 제거 .replace(/>([ \t]+) 모든 FEFF를 제거 공백을 제거한다 var stripFEFF = function(sText) { // 중복되는 정규식이 있어보임 return sText.replace(/\ufeff/g, ""); // FEFF 제거 }; // ### repairHTML // > HTML 태그 보정 var repairHTML = function(sHTML) { // '>'짝이 맞지 않는 html 보정 sHTML = common.stringFilter.tagCorrector(sHTML); return sHTML; // custom attribute를 처리하지 못해 주석 처리 ex) data-user-style // // '

abc

abc

'와 같이 짝기 맞지 않는 태그 보정 // var results = ""; // HTMLParser(sHTML, { // start: function( tag, attrs, unary ) { // results += ""; // }, // end: function( tag ) { // results += "" + tag + ">"; // }, // chars: function( text ) { // results += text; // }, // comment: function( text ) { // results += ""; // } // }); // return results; }; // ### cloneTagStruct // > aEl로 받은 태그 구조를 복사해서 생성 // > ex) [P, SPAN, TEXT] ->

TEXT

var cloneStruct = function(aEl) { var removeChild = function(el) { if (el.childNodes) { _.each(_.toArray(el.childNodes), remove); } return el; }; var elTop = removeChild(clone(aEl.shift())); while (aEl.length > 0) { var el = removeChild(clone(aEl.shift())); var elParent = elTop.firstChild ? elTop.firstChild : elTop; elParent.appendChild(el); } return elTop; }; // ### normalize // http://stackoverflow.com/questions/2023255/node-normalize-crashes-in-ie6 var normalize = function(node) { if (node.normalize) { node.normalize(); return; } for (var i=0, children = node.childNodes, nodeCount = children.length; i "1px 1px 1px 1px"형의 값을 split 함 var splitPostionString = function(sPos) { var aPos = sPos.split(' '); var nTop, nRight, nBottom, nLeft; if (aPos.length === 1) { nTop = parseInt(aPos[0]), nRight = nTop, nBottom = nTop, nLeft = nTop; } else if( aPos.length === 2 ) { nTop = parseInt(aPos[0]), nRight = parseInt(aPos[1]), nBottom = nTop, nLeft = nRight; } else if( aPos.length === 3 ) { nTop = parseInt(aPos[0]), nRight = parseInt(aPos[1]), nBottom = parseInt(aPos[2]), nLeft = nRight; } else{ nTop = parseInt(aPos[0]), nRight = parseInt(aPos[1]), nBottom = parseInt(aPos[2]), nLeft = parseInt(aPos[3]); } return {'top':nTop, 'right' : nRight, 'bottom' : nBottom, 'left': nLeft}; }; // ### parseColSpan // > td colSpan 값을 리턴(number) var parseColSpan = function(elTD) { assert.ok(isCell(elTD)); return parseInt(elTD.getAttribute('colSpan'), 10) || 1; }; // ### parseRowSpan // > td rowSpan 값을 리턴(number) var parseRowSpan = function(elTD) { assert.ok(isCell(elTD)); return parseInt(elTD.getAttribute('rowSpan'), 10) || 1; }; // ### isMergeHead var isMergeHead = function(elTD) { return isRowMergeHead(elTD) || isColMergeHead(elTD); }; // ### isRowMergeHead var isRowMergeHead = function(elTD) { if (!isCell(elTD)) { return false; } return parseRowSpan(elTD) > 1; }; // ### isColMergeHead var isColMergeHead = function(elTD) { if (!isCell(elTD)) { return false; } return parseColSpan(elTD) > 1; }; // ### padding para // > element가 비어있는 경우 FEFF 를 하나 넣어준다. // >> UL/OL이 들어오는 경우도 처리 var paddingInlineContainer = function(el) { if (isList(el) && _.all(el.childNodes, isEmpty)) { _.each(el.childNodes, paddingInlineContainer); return leftBottom(_.last(el.childNodes)); } else if (isEmptyInlineContainer(el) ) { var elInline = leftBottom(el); if (isText(elInline)) { elInline.nodeValue = FEFF; return elInline; } else { elInline.innerHTML = FEFF; return elInline.firstChild; } } return null; }; var MIN_PT = 7; // ### px2pt // > 글꼴크기의 px를 pt로 변환 var px2pt = function (px) { return _.max([px - parseInt((px + 1) / 4), MIN_PT]); }; // ### getUnit // > 정규식에 매치되는 값 리턴(없으면 빈 문자열) // > 정규식 : UNIT_REGEXP = /pt$|in$|em$|cm$|px$/ var getUnit = function (sValue) { if (!sValue) { return ""; } sValue = sValue.replace(/^\s+|\s+$/g, ''); var matched = sValue.match(/pt$|in$|em$|cm$|px$/); return matched ? matched[0] : ""; }; return { isBlock: isBlock, isInline: iter.not(isBlock), isTable: isTable, isTBody: isTBody, isTR: isTR, isTD: isTD, isCell: isCell, isContainer: isContainer, isInlineContainer: isInlineContainer, isIMG: isIMG, isData: isData, isText: isText, isEmptyText: isEmptyText, isFont: isFont, isStrike: isStrike, isBold: isBold, isItalic: isItalic, isUnderline: isUnderline, isDiv: isDiv, isBlockquote: isBlockquote, isNoteEditor: isNoteEditor, isNoteEditable: isNoteEditable, isFirstContainerChild : isFirstContainerChild, isLastContainerChild : isLastContainerChild, isFirstParaTextnode: isFirstParaTextnode, isLastParaTextnode: isLastParaTextnode, isBodyContainer: isBodyContainer, isEdgeOf: isEdgeOf, isLeftEdge: isLeftEdge, isRightEdge: isRightEdge, isLeftEdgeOf: isLeftEdgeOf, isRightEdgeOf: isRightEdgeOf, isEmpty: isEmpty, isSiblingOf: isSiblingOf, contains: contains, isAdjacent: isAdjacent, isNBSP: isNBSP, isFEFF: isFEFF, isPara: isPara, isHeading: isHeading, isPurePara: isPurePara, isList: isList, isOrderedList: isOrderedList, isUnorderedList: isUnorderedList, isBullet: isBullet, isLI: isLI, isP: isP, isA: isA, isHR: isHR, isBR: isBR, isEmptyPara: isEmptyPara, isEmptyInlineContainer: isEmptyInlineContainer, isSpan: isSpan, isInvisible: isInvisible, isOnContentEditable: isOnContentEditable, isDocumentFragment: isDocumentFragment, isDataBlock: isDataBlock, traverse: traverse, traverseIn: traverseIn, descendants: descendants, hasDescendants: hasDescendants, hasParent: hasParent, hasNext: hasNext, hasPrev: hasPrev, hasChild: hasChild, ancestor: ancestor, lastAncestor: lastAncestor, listAncestor: listAncestor, comparePositions: comparePositions, commonAncestor: commonAncestor, leftBottom: leftBottom, inlineTop: inlineTop, styleInlineTop: styleInlineTop, closetBody: closetBody, length: length, position: position, depth: depth, remove: remove, removeNode: removeNode, removeWhile: removeWhile, removeAllAttributes : removeAllAttributes, insertAfter: insertAfter, insertBefore: insertBefore, NBSP: NBSP, FEFF: FEFF, BASE_PARA_HTML: BASE_PARA_HTML, splitTree: splitTree, splitText: splitText, replace: replace, clone: clone, parent: parent, next: next, listNext: listNext, listPrev: listPrev, nodesBetween: nodesBetween, nextBP: nextBP, prevBP: prevBP, makeUntil: makeUntil, makeOffsetPath: makeOffsetPath, fromOffsetPath: fromOffsetPath, create: create, fragment: fragment, element: element, text: text, wrap: wrap, stripTagSpace: stripTagSpace, stripFEFF: stripFEFF, repairHTML: repairHTML, cloneStruct: cloneStruct, normalize: normalize, splitPostionString: splitPostionString, parseRowSpan: parseRowSpan, parseColSpan: parseColSpan, isMergeHead: isMergeHead, isRowMergeHead: isRowMergeHead, isColMergeHead: isColMergeHead, paddingInlineContainer: paddingInlineContainer, px2pt: px2pt, getUnit: getUnit }; });

crocket avatar Oct 04 '13 18:10 crocket

I'd bet, this line makes groc fail:

// > @return number  0: equal to

… but I didn't debug it further … not enough time :smirk:

sjorek avatar Oct 09 '13 23:10 sjorek