tableutil.js 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535
  1. /*jslint nomen: false*/
  2. /*global Trex, $tom, $tx, _FALSE, _NULL, _TRUE */
  3. Trex.TableUtil = {
  4. /**
  5. * isDaumTable
  6. * @param {Element} table
  7. * @return {boolean}
  8. */
  9. isDaumTable: function (table) {
  10. return $tx.hasClassName(table, "txc-table");
  11. },
  12. /**
  13. * cloneNodeForEmptyTd
  14. * @param {Element} node
  15. */
  16. cloneNodeForEmptyTd: function (node) {
  17. var newNode;
  18. newNode = node.cloneNode(_FALSE);
  19. Trex.TableUtil.emptyTd(newNode);
  20. return newNode;
  21. },
  22. emptyTd: function (node) {
  23. node.innerHTML = " ";
  24. },
  25. /**
  26. * splitWidthByColSpan
  27. * @param {Element} td
  28. */
  29. splitWidthByColSpan: function (td) {
  30. var styleWidth;
  31. if (1 < td.colSpan && td.style.width) {
  32. styleWidth = parseInt(td.style.width, 10);
  33. $tom.setStyles(td, {
  34. width: Math.floor(styleWidth / td.colSpan) + "px"
  35. }, _TRUE);
  36. }
  37. },
  38. /**
  39. * splitHeightByRowSpan
  40. * @param {Element} td
  41. */
  42. splitHeightByRowSpan: function (td) {
  43. var styleHeight;
  44. if (1 < td.rowSpan && td.style.height) {
  45. styleHeight = parseInt(td.style.height, 10);
  46. $tom.setStyles(td, {
  47. height: Math.floor(styleHeight / td.rowSpan) + "px"
  48. }, _TRUE);
  49. }
  50. },
  51. /**
  52. * collapseCaret
  53. * @param {Trex.Canvas.WysiwygPanel} wysiwygPanel
  54. * @param {Element} node
  55. */
  56. collapseCaret: function (wysiwygPanel, node) {
  57. var range;
  58. try {
  59. range = wysiwygPanel.getProcessor().createGoogRangeFromNodes(node, 0, node, 0);
  60. range.select();
  61. } catch (ignore) {}
  62. //td space bug from create textnode.
  63. //wysiwygPanel.getProcessor().moveCaretTo(node);
  64. },
  65. /**
  66. * getClosestByTagNames
  67. * @param {Array} tagNames
  68. * @param {Element} el
  69. * @return {Element}
  70. */
  71. getClosestByTagNames: function (tagNames, el) {
  72. var tagName;
  73. if (el && typeof el.tagName === "string") {
  74. tagName = el.tagName.toLowerCase();
  75. if (tagName !== "body") {
  76. if (tagNames.contains(tagName)) {
  77. return el;
  78. } else {
  79. return arguments.callee(tagNames, el.parentNode);
  80. }
  81. }
  82. }
  83. return _NULL;
  84. },
  85. /**
  86. * getTableIndexerFromTd
  87. * @param {Element} td
  88. * @return {Trex.TableUtil.Indexer}
  89. */
  90. getTableIndexerFromTd: function (td) {
  91. var currentTable;
  92. currentTable = Trex.TableUtil.getClosestByTagNames(["table"], td);
  93. return new Trex.TableUtil.Indexer(currentTable);
  94. }
  95. };
  96. //////////////////////////////////////////////////////////
  97. /**
  98. * Trex.TableUtil.Boundary class
  99. * @param {Object} indexs (optional)
  100. */
  101. Trex.TableUtil.Boundary = Trex.Class.create({
  102. initialize: function (indexs) {
  103. this.top = -1;
  104. this.left = -1;
  105. this.bottom = -1;
  106. this.right = -1;
  107. if (indexs) {
  108. this.set(indexs);
  109. }
  110. },
  111. /**
  112. * getTop
  113. * @return {number} start row index
  114. */
  115. getTop: function () {
  116. return this.top;
  117. },
  118. /**
  119. * getLeft
  120. * @return {number} start col index
  121. */
  122. getLeft: function () {
  123. return this.left;
  124. },
  125. /**
  126. * getBottom
  127. * @return {number} end row index
  128. */
  129. getBottom: function () {
  130. return this.bottom;
  131. },
  132. /**
  133. * getRight
  134. * @return {number} end col index
  135. */
  136. getRight: function () {
  137. return this.right;
  138. },
  139. /**
  140. * setTop
  141. * @param {number} index
  142. */
  143. setTop: function (index) {
  144. this.top = index;
  145. },
  146. /**
  147. * setLeft
  148. * @param {number} index
  149. */
  150. setLeft: function (index) {
  151. this.left = index;
  152. },
  153. /**
  154. * setBottom
  155. * @param {number} index
  156. */
  157. setBottom: function (index) {
  158. this.bottom = index;
  159. },
  160. /**
  161. * setRight
  162. * @param {number} index
  163. */
  164. setRight: function (index) {
  165. this.right = index;
  166. },
  167. /**
  168. * set
  169. * @param {Object} indexs
  170. */
  171. set: function (indexs) {
  172. if ("top" in indexs) {
  173. this.setTop(indexs.top);
  174. }
  175. if ("left" in indexs) {
  176. this.setLeft(indexs.left);
  177. }
  178. if ("bottom" in indexs) {
  179. this.setBottom(indexs.bottom);
  180. }
  181. if ("right" in indexs) {
  182. this.setRight(indexs.right);
  183. }
  184. },
  185. /**
  186. * isValid
  187. * @return {boolean}
  188. */
  189. isValid: function () {
  190. if (this.top === -1) {
  191. return _FALSE;
  192. }
  193. if (this.left === -1) {
  194. return _FALSE;
  195. }
  196. if (this.bottom === -1) {
  197. return _FALSE;
  198. }
  199. if (this.right === -1) {
  200. return _FALSE;
  201. }
  202. return _TRUE;
  203. },
  204. /**
  205. * addBoundary
  206. * @param {number} rowIndex
  207. * @param {number} colIndex
  208. * @return {boolean} changed
  209. */
  210. addBoundary: function (rowIndex, colIndex) {
  211. var changedStart, changedEnd;
  212. changedStart = this.addStartBoundary(rowIndex, colIndex);
  213. changedEnd = this.addEndBoundary(rowIndex, colIndex);
  214. return changedStart || changedEnd;
  215. },
  216. /**
  217. * merge
  218. * @param {Trex.TableUtil.Boundary} boundary
  219. * @return {boolean} changed
  220. */
  221. merge: function (boundary) {
  222. var changedStart, changedEnd;
  223. changedStart = this.addStartBoundary(boundary.top, boundary.left);
  224. changedEnd = this.addEndBoundary(boundary.bottom, boundary.right);
  225. return changedStart || changedEnd;
  226. },
  227. /**
  228. * addStartBoundary
  229. * @private
  230. * @param {number} rowIndex
  231. * @param {number} colIndex
  232. * @return {boolean} changed
  233. */
  234. addStartBoundary: function (rowIndex, colIndex) {
  235. var changed;
  236. changed = _FALSE;
  237. if (this.top === -1 || rowIndex < this.top) {
  238. this.top = rowIndex;
  239. changed = _TRUE;
  240. }
  241. if (this.left === -1 || colIndex < this.left) {
  242. this.left = colIndex;
  243. changed = _TRUE;
  244. }
  245. return changed;
  246. },
  247. /**
  248. * addEndBoundary
  249. * @private
  250. * @param {number} rowIndex
  251. * @param {number} colIndex
  252. * @return {boolean} changed
  253. */
  254. addEndBoundary: function (rowIndex, colIndex) {
  255. var changed;
  256. changed = _FALSE;
  257. if (this.bottom === -1 || this.bottom < rowIndex) {
  258. this.bottom = rowIndex;
  259. changed = _TRUE;
  260. }
  261. if (this.right === -1 || this.right < colIndex) {
  262. this.right = colIndex;
  263. changed = _TRUE;
  264. }
  265. return changed;
  266. }
  267. });
  268. //////////////////////////////////////////////////////////
  269. // Indexer 는 사람에게 보이는대로의 index 로 table 을
  270. // 조작하는데 도움을 준다. DOM 에서는 rowSpan 과
  271. // colSpan 때문에 보이는 index 와 일치하지 않기 때문임.
  272. /**
  273. * Trex.TableUtil.Indexer class
  274. * @param {Element} table
  275. */
  276. Trex.TableUtil.Indexer = Trex.Class.create({
  277. initialize: function (table) {
  278. this.indexData = _NULL;
  279. this.table = _NULL;
  280. this.resetIndex();
  281. this.setTable(table);
  282. this.makeIndex();
  283. },
  284. /**
  285. * getRowSize
  286. * @return {number}
  287. */
  288. getRowSize: function () {
  289. return this.indexData.length;
  290. },
  291. /**
  292. * getColSize
  293. * @return {number}
  294. */
  295. getColSize: function () {
  296. if (0 < this.indexData.length) {
  297. return this.indexData[0].length;
  298. }
  299. return 0;
  300. },
  301. /**
  302. * getTd
  303. * rowIndex 와 colIndex 에 매칭되는 td 를 가져온다.
  304. * @param {number} rowIndex
  305. * @param {number} colIndex
  306. * @return {Elememt} td
  307. */
  308. getTd: function (rowIndex, colIndex) {
  309. if (this.indexData[rowIndex]) {
  310. if (this.indexData[rowIndex][colIndex]) {
  311. return this.indexData[rowIndex][colIndex];
  312. }
  313. }
  314. return _NULL;
  315. },
  316. /**
  317. * getTdArr
  318. * Boundary 에 포함되는 td 들을 가져온다.
  319. * @param {Trex.TableUtil.Boundary} boundary
  320. * @return {Array} tdArr [td, td, ...] (order by top-left)
  321. */
  322. getTdArr: function (boundary) {
  323. var result, rowIndex, cells, colIndex;
  324. result = [];
  325. rowIndex = boundary.top;
  326. while (rowIndex <= boundary.bottom) {
  327. cells = this.indexData[rowIndex];
  328. colIndex = boundary.left;
  329. while (colIndex <= boundary.right) {
  330. if (result.contains(cells[colIndex]) === _FALSE) {
  331. result.push(cells[colIndex]);
  332. }
  333. colIndex += 1;
  334. }
  335. rowIndex += 1;
  336. }
  337. return result;
  338. },
  339. /**
  340. * getTdArrHasTop
  341. * 해당하는 row index 를 top 으로 가지는 cell 들을 가져온다.
  342. * @param {number} index
  343. * @return {Array} tdArr [td, td, ...] (order by left-top)
  344. */
  345. getTdArrHasTop: function (index) {
  346. var result, currentCell, adjoiningCell, len, i;
  347. result = [];
  348. len = this.getColSize();
  349. for (i = 0; i < len; i += 1) {
  350. currentCell = this.getTd(index, i);
  351. adjoiningCell = this.getTd(index - 1, i);
  352. this.uniquePushWhenDifferent(result, currentCell, adjoiningCell);
  353. }
  354. return result;
  355. },
  356. /**
  357. * getTdArrHasBottom
  358. * 해당하는 row index 를 bottom 으로 가지는 cell 들을 가져온다.
  359. * @param {number} index
  360. * @return {Array} tdArr [td, td, ...] (order by left-top)
  361. */
  362. getTdArrHasBottom: function (index) {
  363. var result, currentCell, adjoiningCell, len, i;
  364. result = [];
  365. len = this.getColSize();
  366. for (i = 0; i < len; i += 1) {
  367. currentCell = this.getTd(index, i);
  368. adjoiningCell = this.getTd(index + 1, i);
  369. this.uniquePushWhenDifferent(result, currentCell, adjoiningCell);
  370. }
  371. return result;
  372. },
  373. /**
  374. * getTdArrHasLeft
  375. * 해당하는 row index 를 left 로 가지는 cell 들을 가져온다.
  376. * @param {number} index
  377. * @return {Array} tdArr [td, td, ...] (order by left-top)
  378. */
  379. getTdArrHasLeft: function (index) {
  380. var result, currentCell, adjoiningCell, len, i;
  381. result = [];
  382. len = this.getRowSize();
  383. for (i = 0; i < len; i += 1) {
  384. currentCell = this.getTd(i, index);
  385. adjoiningCell = this.getTd(i, index - 1);
  386. this.uniquePushWhenDifferent(result, currentCell, adjoiningCell);
  387. }
  388. return result;
  389. },
  390. /**
  391. * getTdArrHasRight
  392. * 해당하는 row index 를 right 로 가지는 cell 들을 가져온다.
  393. * @param {number} index
  394. * @return {Array} tdArr [td, td, ...] (order by left-top)
  395. */
  396. getTdArrHasRight: function (index) {
  397. var result, currentCell, adjoiningCell, len, i;
  398. result = [];
  399. len = this.getRowSize();
  400. for (i = 0; i < len; i += 1) {
  401. currentCell = this.getTd(i, index);
  402. adjoiningCell = this.getTd(i, index + 1);
  403. this.uniquePushWhenDifferent(result, currentCell, adjoiningCell);
  404. }
  405. return result;
  406. },
  407. /**
  408. * getBoundary
  409. * td 에 해당하는 boundary 를 구한다.
  410. * @param {Elememt} td
  411. * @return {Trex.TableUtil.Boundary} boundary
  412. */
  413. getBoundary: function (td) {
  414. var result, rows, rowLen, rowIndex, cells, cellLen, colIndex;
  415. result = new Trex.TableUtil.Boundary();
  416. rows = this.indexData;
  417. rowLen = rows.length;
  418. for (rowIndex = 0; rowIndex < rowLen; rowIndex += 1) {
  419. cells = rows[rowIndex];
  420. if (cells) {
  421. cellLen = cells.length;
  422. for (colIndex = 0; colIndex < cellLen; colIndex += 1) {
  423. if (cells[colIndex] === td) {
  424. result.addBoundary(rowIndex, colIndex);
  425. }
  426. }
  427. }
  428. }
  429. return result;
  430. },
  431. /**
  432. * reload
  433. * 인덱스 갱신(테이블이 변경되었을 때).
  434. */
  435. reload: function () {
  436. this.resetIndex();
  437. this.makeIndex();
  438. },
  439. /**
  440. * uniquePushWhenDifferent
  441. * currentCell 과 adjoiningCell 이 다르면 currentCell 를 tdArr 에 중복없이 push 한다.
  442. * @private
  443. * @param {Array} tdArr
  444. * @param {Element} currentCell
  445. * @param {Element} adjoiningCell
  446. */
  447. uniquePushWhenDifferent: function (tdArr, currentCell, adjoiningCell) {
  448. if (currentCell !== adjoiningCell) {
  449. if (tdArr.contains(currentCell) === _FALSE) {
  450. tdArr.push(currentCell);
  451. }
  452. }
  453. },
  454. /**
  455. * resetIndex
  456. * @private
  457. */
  458. resetIndex: function () {
  459. this.indexData = [];
  460. },
  461. /**
  462. * setTable
  463. * @private
  464. * @param {Element} table
  465. */
  466. setTable: function (table) {
  467. this.table = table;
  468. },
  469. /**
  470. * makeIndex
  471. * rowSpan 과 colSpan 을 펼친 형태의 array 에 table cell 들을 매칭시킨다.
  472. * @private
  473. */
  474. makeIndex: function () {
  475. var rows, rowLen, rowIndex, row, cells, cellLen, colIndex, cell;
  476. rows = this.table.rows;
  477. rowLen = rows.length;
  478. for (rowIndex = 0; rowIndex < rowLen; rowIndex += 1) {
  479. row = rows[rowIndex];
  480. cells = row.cells;
  481. cellLen = cells.length;
  482. for (colIndex = 0; colIndex < cellLen; colIndex += 1) {
  483. cell = cells[colIndex];
  484. this.addCellIndex(rowIndex, cell);
  485. }
  486. }
  487. },
  488. /**
  489. * addCellIndex
  490. * 만들고 있는 indexData 에 해당 cell 에 대한 index 를 추가한다.
  491. * @private
  492. * @param {number} rowIndex
  493. * @param {Element} cell
  494. */
  495. addCellIndex: function (rowIndex, cell) {
  496. var viewIndexOfCell, row, rowSpan, calculatedRowIndex, col, colSpan;
  497. viewIndexOfCell = this.getNextCellIndex(this.indexData[rowIndex]);
  498. rowSpan = cell.rowSpan;
  499. for (row = 0; row < rowSpan; row += 1) {
  500. calculatedRowIndex = rowIndex + row;
  501. if (! this.indexData[calculatedRowIndex]) {
  502. this.indexData[calculatedRowIndex] = [];
  503. }
  504. colSpan = cell.colSpan;
  505. for (col = 0; col < colSpan; col += 1) {
  506. this.indexData[calculatedRowIndex][viewIndexOfCell + col] = cell;
  507. }
  508. }
  509. },
  510. /**
  511. * getNextCellIndex
  512. * arr 를 순환하면서 처음으로 만난 빈 요소의 index 를 반환한다.
  513. * arr 가 없으면 0 을 반환, 빈 요소가 없으면 length 를 반환한다.
  514. * @private
  515. * @param {Array} arr
  516. * @return {number} cell index
  517. */
  518. getNextCellIndex: function (arr) {
  519. var i, len;
  520. if (! arr) {
  521. return 0;
  522. }
  523. len = arr.length;
  524. for (i = 0; i < len; i += 1) {
  525. if (! arr[i]) {
  526. break;
  527. }
  528. }
  529. return i;
  530. }
  531. });