dom.js can't be documented due to errors.
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
};
});
I'd bet, this line makes groc fail:
// > @return number 0: equal to
… but I didn't debug it further … not enough time :smirk: