domutil.js 45 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726
  1. /** @namespace */
  2. var $tom = {};
  3. (function() {
  4. var __TRANSLATIONS = {
  5. '%body': ['body'],
  6. '%text': ['#text', 'br'],
  7. '%element': ['#element'],
  8. '%control': ['img','object','hr','table','button','iframe'], //['input','select','textarea','label','br'],
  9. '%inline': ['span','font','u','i','b','em','strong','big','small','a','sub','sup','span'],//['tt','dfn','code','samp','kbd','var','cite','abbr','acronym','img','object','br','script','map','q','bdo','input','select','textarea','label','button'],
  10. '%block': ['p','div','ul','ol','h1','h2','h3','h4','h5','h6','pre','dl','hr','table','button'], //['noscript','blockquote','form','fieldset','address'], !button
  11. '%paragraph': ['p','li','dd','dt','h1','h2','h3','h4','h5','h6','td','th','div','caption'], //!button
  12. '%wrapper': ['div','ul','ol','dl','pre','xmp','table','button','blockquote'],// FTDUEDTR-1412
  13. '%innergroup': ['li','dd','dt','td', 'th'],
  14. '%outergroup': ['ul','ol','dl','tr','tbody','thead','tfoot','table'],
  15. '%tablegroup': ['td', 'th','tr','tbody','thead','tfoot','table'],
  16. '%listgroup': ['li','ul','ol'],
  17. '%datagroup': ['dd','dt','dl'],
  18. '%listhead': ['ul','ol']
  19. };
  20. var __TRANSLATIONS_MAP = {}; //for caching
  21. for(var _ptrn in __TRANSLATIONS) {
  22. __TRANSLATIONS_MAP[_ptrn] = {};
  23. if (__TRANSLATIONS[_ptrn]) {
  24. $A(__TRANSLATIONS[_ptrn]).each(function(tag){
  25. __TRANSLATIONS_MAP[_ptrn][tag] = _TRUE;
  26. });
  27. }
  28. }
  29. function createMap(patterns) {
  30. var _map = {};
  31. var _patterns = patterns.split(",");
  32. _patterns.each(function(pattern) {
  33. if(__TRANSLATIONS_MAP[pattern]) {
  34. for(var _part in __TRANSLATIONS_MAP[pattern]) {
  35. _map[_part] = _TRUE;
  36. }
  37. } else {
  38. _map[pattern] = _TRUE;
  39. }
  40. });
  41. return _map;
  42. }
  43. var Translator = Trex.Class.create({
  44. initialize: function(patterns) {
  45. this.patterns = patterns;
  46. this.map = createMap(patterns);
  47. },
  48. hasParts: function() {
  49. return (this.patterns.length > 0);
  50. },
  51. include: function(partPtrn) {
  52. var _partMap = createMap(partPtrn);
  53. for(var _part in _partMap) {
  54. if(this.map[_part]) {
  55. return _TRUE;
  56. }
  57. }
  58. return _FALSE;
  59. },
  60. memberOf: function(wholePtrn) {
  61. var _wholeMap = createMap(wholePtrn);
  62. for(var _part in this.map) {
  63. if(_wholeMap[_part]) {
  64. return _TRUE;
  65. }
  66. }
  67. return _FALSE;
  68. },
  69. extract: function(wholePtrn) {
  70. var _wholeMap = createMap(wholePtrn);
  71. var _matches = [];
  72. for(var _part in this.map) {
  73. if(_wholeMap[_part]) {
  74. _matches.push(_part);
  75. }
  76. }
  77. return $tom.translate(_matches.join(","));
  78. },
  79. getExpression: function() {
  80. if(!this.exprs) {
  81. var _exprs = [];
  82. for(var _part in this.map) {
  83. _exprs.push(_part);
  84. }
  85. this.exprs = _exprs.join(",");
  86. }
  87. return this.exprs;
  88. }
  89. });
  90. var __TRANSLATOR_CACHES = {}; //for caching
  91. Object.extend($tom, {
  92. translate: function(pattern) {
  93. if(!__TRANSLATOR_CACHES[pattern]) {
  94. __TRANSLATOR_CACHES[pattern] = new Translator(pattern);
  95. }
  96. return __TRANSLATOR_CACHES[pattern];
  97. }
  98. });
  99. })();
  100. Object.extend($tom, {
  101. __POSITION: {
  102. __START_OF_TEXT: -1,
  103. __MIDDLE_OF_TEXT: 0,
  104. __END_OF_TEXT: 1,
  105. __EMPTY_TEXT: -2
  106. }
  107. });
  108. Object.extend($tom, /** @lends $tom */{
  109. /**
  110. * node가 HTMLElement이면 true를 아니면 false를 반환한다.
  111. * @function
  112. */
  113. isElement: function(node) {
  114. return node && node.nodeType == 1;
  115. },
  116. /**
  117. * node가 <body> 요소이면 true를 반환한다.
  118. * @function
  119. */
  120. isBody: function(node) {
  121. return $tom.isElement(node) && node.tagName == "BODY";
  122. },
  123. /**
  124. * node가 아래에 나열된 block 요소이면 true 를 반환한다.
  125. * 'p','div','ul','ol','h1','h2','h3','h4','h5','h6','pre','dl','hr','table','button'
  126. * @function
  127. */
  128. isBlock: function(node) {
  129. return $tom.kindOf(node, '%block');
  130. },
  131. /**
  132. * node가 아래에 나열된 요소이면 true 를 반환한다.
  133. * 'p','li','dd','dt','h1','h2','h3','h4','h5','h6','td','th','div','caption'
  134. * @function
  135. */
  136. isParagraph: function(node) {
  137. return $tom.kindOf(node, '%paragraph');
  138. },
  139. /**
  140. * node가 텍스트이거나 <br> 요소이면 true 를 반환한다.
  141. * @function
  142. */
  143. isText: function(node) {
  144. return $tom.kindOf(node, '%text');
  145. },
  146. /**
  147. * node가 아래에 나열된 요소이면 true를 반환한다.
  148. * 'img','object','hr','table','button'
  149. * @function
  150. */
  151. isControl: function(node) {
  152. return $tom.kindOf(node, '%control');
  153. },
  154. /**
  155. * element가 tagName면 true를 반환한다.
  156. * @function
  157. */
  158. isTagName: function(element, tagName){
  159. tagName = tagName.toUpperCase();
  160. return element && element.tagName === tagName;
  161. },
  162. getOwnerDocument: function(node) {
  163. return node.ownerDocument || node.document;
  164. },
  165. /**
  166. * node의 이름을 반환한다.
  167. * @function
  168. */
  169. getName: function(node) {
  170. return ((node && node.nodeType == 1)? node.nodeName.toLowerCase(): "");
  171. },
  172. /**
  173. * node의 text content 를 반환한다.
  174. * @function
  175. */
  176. getText: function(node) {
  177. return node.textContent || node.text || node.innerText || "";
  178. },
  179. /**
  180. * 요소의 nodeType 1이면 child 노드의 길이를, nodeType 3이면 nodeValue의 길이를 반환한다.
  181. * @function
  182. */
  183. getLength: function(node) {
  184. if(!node) {
  185. return 0;
  186. }
  187. if(node.nodeType == 1) {
  188. return node.childNodes.length;
  189. } else if(node.nodeType == 3) {
  190. return node.nodeValue.length;
  191. }
  192. return 0;
  193. },
  194. /**
  195. * node가 같은 레벨의 요소 중 몇 번째인지 인덱스값을 반환한다.
  196. * @function
  197. */
  198. indexOf: function(node){
  199. if(!node) {
  200. return -1;
  201. }
  202. var _pNode = node.parentNode;
  203. for (var i = 0, len = _pNode.childNodes.length, childNodes = _pNode.childNodes; i < len; i++) {
  204. if (childNodes[i] == node) {
  205. return i;
  206. }
  207. }
  208. return -1;
  209. },
  210. /**
  211. * node가 textNode이면 공백을 제거한 nodeValue의 내용이 존재하면 true를 반환한다.
  212. * @function
  213. */
  214. hasContent: function(node, ignoreZWNBS) {
  215. if(!node || node.nodeType != 3) {
  216. return _TRUE;
  217. }
  218. var _text = $tom.removeMeaninglessSpace( node.nodeValue );
  219. if(ignoreZWNBS) {
  220. _text = _text.replace(Trex.__WORD_JOINER_REGEXP, "");
  221. }
  222. return (_text != "");
  223. },
  224. removeEmptyTextNode: function(textNode) {
  225. if (textNode && textNode.nodeType == 3 && !textNode.nodeValue) {
  226. $tom.remove(textNode);
  227. }
  228. },
  229. hasUsefulChildren: function(node, ignoreZWNBS) {
  230. if(!node) {
  231. return _FALSE;
  232. }
  233. var _inner = $tom.removeMeaninglessSpace( node.innerHTML );
  234. if(ignoreZWNBS) {
  235. _inner = _inner.replace(Trex.__WORD_JOINER_REGEXP, "");
  236. }
  237. if(!_inner) {
  238. return _FALSE;
  239. }
  240. if(_inner.stripTags()) {
  241. return _TRUE;
  242. }
  243. if(_inner.search(/<(img|br|hr)\s?[^>]*>/i) > -1) {
  244. return _TRUE;
  245. }
  246. if(_inner.search(/<span\sid="?tx_(start|end)_marker"?><\/span>/i) > -1) {
  247. return _TRUE;
  248. }
  249. return _FALSE;
  250. },
  251. /**
  252. * node에 의미있는 데이터가 있는지 확인한다.
  253. * @function
  254. */
  255. hasData: function(node, ignoreStuff) {
  256. if(!node) {
  257. return _FALSE;
  258. }
  259. var _inner = '';
  260. if(node.nodeType == 1) {
  261. _inner = node.innerHTML;
  262. } else {
  263. _inner = node.nodeValue;
  264. }
  265. _inner = $tom.removeMeaninglessSpace( _inner );
  266. if(_inner.trim() == '') {// #PCCAFEQA-11
  267. return _FALSE;
  268. }
  269. if(_inner.stripTags() != '') {
  270. return _TRUE;
  271. }
  272. if(ignoreStuff) {
  273. return _FALSE;
  274. }
  275. if(_inner.search(/<br\s?\/?>/i) > -1) {
  276. return _TRUE;
  277. }
  278. return _FALSE;
  279. },
  280. /**
  281. * 주어진 스트링에서 의미없는 스페이스를 제거하는 함수.
  282. * @function
  283. */
  284. removeMeaninglessSpace: function(str){
  285. /* /\s/ == /[\f\n\r\t\v\u2028\u2029\u00a0]/ */
  286. return str.replace(/(^[\f\n\r\t\v\u2028\u2029]*)|([\f\n\r\t\v\u2028\u2029]*$)/g, "");
  287. }
  288. });
  289. Object.extend($tom, /** @lends $tom */{
  290. /**
  291. * $tom.find, $tom.collect, $tom.collectAll 에서 공통적으로 호출되는 함수.
  292. * @function
  293. * @example
  294. * var result1 = $tom.search(["td,th"], dFindy, _NULL);
  295. * var result2 = $tom.search([context, "td,th"], dFindy, _NULL);
  296. * var results = $tom.search([context, "td,th"], dGetties, []);
  297. */
  298. search: function(args, searchFunction, defaultValue) {
  299. var context = (args.length == 1) ? _DOC : args[0];
  300. var pattern = args[args.length - 1];
  301. var invalidArgument = (!pattern ||
  302. !context ||
  303. !context.nodeType ||
  304. typeof pattern != "string");
  305. if (invalidArgument) {
  306. return defaultValue;
  307. }
  308. var translator = $tom.translate(pattern);
  309. return searchFunction(context, translator.getExpression());
  310. },
  311. /**
  312. * css selector 로 요소를 찾아서 반환하는데 인자 node의 상위에 있는 요소를 찾는다.
  313. * @function
  314. * @example
  315. * var _elNode = $tom.find(node, "table.txc-layout-wz");
  316. */
  317. find: function() {
  318. return this.search(arguments, dFindy, _NULL);
  319. },
  320. /**
  321. * css selector 로 요소를 찾아서 반환하는데 인자 node의 하위에 있는 요소를 찾는다.
  322. * @function
  323. * @example
  324. * var _elInput = $tom.collect(this.elMenu, 'textarea');
  325. */
  326. collect: function() {
  327. return this.search(arguments, dGetty, _NULL);
  328. },
  329. /**
  330. * css selector로 요소를 찾아서 반환하는데 인자 node의 하위에 있는 요소를 찾고 모든 요소를 배열에 담아서 반환한다.
  331. * @function
  332. * @example
  333. * var _elItemList = $tom.collectAll(this.elMenu, "li a");
  334. */
  335. collectAll: function() {
  336. return this.search(arguments, dGetties, []);
  337. }
  338. });
  339. (function() {
  340. function makeFilter(pattern) {
  341. if(pattern) {
  342. if(typeof(pattern) === 'function') {
  343. return pattern;
  344. } else {
  345. var _translator = $tom.translate(pattern);
  346. return function(node) {
  347. if(node.nodeType == 1) {
  348. if (_translator.include('#element')) {
  349. return _TRUE;
  350. } else {
  351. return dChecky(node, _translator.getExpression());
  352. }
  353. } else {
  354. return _translator.include('#text');
  355. }
  356. };
  357. }
  358. } else {
  359. return _NULL;
  360. }
  361. }
  362. var nodePatternCache = {};
  363. function findNodePattern(pattern) {
  364. pattern = pattern || "#element,#text";
  365. if (nodePatternCache[pattern]) {
  366. return nodePatternCache[pattern];
  367. }
  368. var filter = new NodePattern(pattern);
  369. nodePatternCache[pattern] = filter;
  370. return filter;
  371. }
  372. var NodePattern = Trex.Class.create({
  373. initialize: function(pattern) {
  374. this.pattern = pattern;
  375. this.translator = $tom.translate(pattern);
  376. // for better performance
  377. this.hasClassPattern = pattern.indexOf(".") >= 0;
  378. this.hasIdPattern = pattern.indexOf("#") >= 0;
  379. this.matchesText = this.translator.include("#text");
  380. this.matchesElement = this.translator.include("#element");
  381. },
  382. test: function(node) {
  383. var nodeType = node.nodeType;
  384. var translatorMap = this.translator.map;
  385. if (nodeType == 1) {
  386. if (this.matchesElement) {
  387. return _TRUE;
  388. }
  389. var tagName = node.tagName.toLowerCase();
  390. // early matching for performance
  391. if (translatorMap[tagName]) {
  392. return _TRUE;
  393. }
  394. var checkPattern = [];
  395. if (this.hasClassPattern && node.className) {
  396. node.className.split(/\s/).each(function(className) {
  397. checkPattern.push("." + className);
  398. checkPattern.push(tagName + "." + className);
  399. });
  400. }
  401. if (this.hasIdPattern && node.id) {
  402. var id = node.id;
  403. checkPattern.push("#" + id);
  404. checkPattern.push(tagName + "#" + id);
  405. }
  406. for (var i = 0; i < checkPattern.length; i++) {
  407. if (translatorMap[checkPattern[i]]) {
  408. return _TRUE;
  409. }
  410. }
  411. return _FALSE;
  412. } else if (nodeType == 3) {
  413. return this.matchesText;
  414. }
  415. }
  416. });
  417. Object.extend($tom, /** @lends $tom */{
  418. tagName: function(node, tagName) {
  419. if (!node) {
  420. return _NULL;
  421. }
  422. return node.tagName;
  423. },
  424. /**
  425. * node가 pattern에 맞는 요소이면 true를 반환한다.
  426. * @function
  427. * @param node
  428. * @param pattern css selector rule
  429. * @example
  430. * $tom.kindOf(node, "img.txc-image") // node가 txc-image라는 이름의 class속성을 가진 img 요소이면 true
  431. */
  432. // 더 이상 사용하지 않는 dChecky를 없애자.
  433. kindOf: function(node, pattern) {
  434. if (!node || !pattern) {
  435. return _FALSE;
  436. }
  437. var filter = findNodePattern(pattern);
  438. return filter.test(node);
  439. },
  440. kindOf_old: function(node, pattern) {
  441. if(!node || !pattern) {
  442. return _FALSE;
  443. }
  444. return makeFilter(pattern)(node);
  445. },
  446. /* has filter */
  447. /**
  448. * pattern에 맞는 descendant의 상위요소를 찾아서 반환한다.
  449. * @function
  450. */
  451. ancestor: function(descendant, pattern) {
  452. if(!descendant || !descendant.parentNode) {
  453. return _NULL;
  454. }
  455. var filter = findNodePattern(pattern);
  456. var _node = descendant.parentNode;
  457. while(_node) {
  458. if($tom.isBody(_node)) {
  459. return _NULL;
  460. }
  461. if (filter.test(_node)) {
  462. break;
  463. }
  464. _node = _node.parentNode;
  465. }
  466. return _node;
  467. },
  468. findAncestor: function(node, matched, mustStop) {
  469. while (!mustStop(node)) {
  470. if (matched(node)) {
  471. return node;
  472. }
  473. node = node.parentNode;
  474. }
  475. return _NULL;
  476. },
  477. /**
  478. * pattern에 맞는 descendant의 하위요소를 찾아서 반환한다.
  479. * @function
  480. */
  481. descendant: function(ancestor, pattern) {
  482. var _nodes = $tom.descendants(ancestor, pattern, _TRUE);
  483. if(_nodes.length == 0) {
  484. return _NULL;
  485. }
  486. return _nodes[0];
  487. },
  488. /**
  489. * pattern에 맞는 descendant의 모든 하위요소를 찾아서 반환한다.
  490. * @function
  491. */
  492. descendants: function(ancestor, pattern, single) {
  493. single = single || _FALSE;
  494. if(!ancestor || !ancestor.firstChild) {
  495. return [];
  496. }
  497. var _found = _FALSE;
  498. var filter = findNodePattern(pattern);
  499. var _nodes = [];
  500. var _gets = function(parent) {
  501. if(single && _found) {
  502. return;
  503. }
  504. if(!$tom.first(parent)) {
  505. return;
  506. }
  507. var _chilren = $tom.children(parent);
  508. for(var i=0,len=_chilren.length;i<len;i++) {
  509. if (filter.test(_chilren[i])) {
  510. _nodes.push(_chilren[i]);
  511. _found = _TRUE;
  512. } else {
  513. _gets(_chilren[i]);
  514. }
  515. }
  516. };
  517. _gets(ancestor);
  518. return _nodes;
  519. },
  520. /**
  521. * node의 자식요소 중 pattern에 맞는 모든 요소를 찾아서 반환한다.
  522. * @function
  523. */
  524. children: function(node, pattern) {
  525. var _nodes = [];
  526. if(!node || !node.firstChild) {
  527. return _nodes;
  528. }
  529. var filter = findNodePattern(pattern);
  530. var _node = $tom.first(node);
  531. while(_node) {
  532. if (filter.test(_node)) {
  533. _nodes.push(_node);
  534. }
  535. _node = _node.nextSibling;
  536. }
  537. return _nodes;
  538. },
  539. /**
  540. * node의 nextSibling 요소 중 pattern에 맞는 요소를 찾아서 반환한다.
  541. * @function
  542. */
  543. next: function(node, pattern) {
  544. if(!node || !node.nextSibling) {
  545. return _NULL;
  546. }
  547. var filter = findNodePattern(pattern);
  548. var _node = node.nextSibling;
  549. while(_node) {
  550. if($tom.hasContent(_node)) {
  551. if (filter.test(_node)) {
  552. break;
  553. }
  554. }
  555. _node = _node.nextSibling;
  556. }
  557. return _node;
  558. },
  559. /**
  560. * node의 previousSibling 요소 중 pattern에 맞는 요소를 찾아서 반환한다.
  561. * @function
  562. */
  563. previous: function(node, pattern) {
  564. if(!node || !node.previousSibling) {
  565. return _NULL;
  566. }
  567. var filter = findNodePattern(pattern);
  568. var _node = node.previousSibling;
  569. while(_node) {
  570. if($tom.hasContent(_node)) {
  571. if (filter.test(_node)) {
  572. break;
  573. }
  574. }
  575. _node = _node.previousSibling;
  576. }
  577. return _node;
  578. },
  579. /**
  580. * pattern에 맞는 node의 첫번째 자식요소를 찾아서 반환한다.
  581. * @function
  582. */
  583. first: function(node, pattern) {
  584. if(!node || !node.firstChild) {
  585. return _NULL;
  586. }
  587. var filter = findNodePattern(pattern);
  588. var _node = node.firstChild;
  589. while(_node) {
  590. if($tom.hasContent(_node)) {
  591. if (filter.test(_node)) {
  592. break;
  593. }
  594. }
  595. _node = _node.nextSibling;
  596. }
  597. return _node;
  598. },
  599. /**
  600. * pattern에 맞는 node의 마지막 자식요소를 찾아서 반환한다.
  601. * @function
  602. */
  603. last: function(node, pattern) {
  604. if(!node || !node.lastChild) {
  605. return _NULL;
  606. }
  607. var filter = findNodePattern(pattern);
  608. var _node = node.lastChild;
  609. while(_node) {
  610. if($tom.hasContent(_node)) {
  611. if (filter.test(_node)) {
  612. break;
  613. }
  614. }
  615. _node = _node.previousSibling;
  616. }
  617. return _node;
  618. },
  619. /**
  620. *
  621. * @function
  622. */
  623. extract: function(parent, child, pattern) {
  624. var _nodes = [];
  625. if(!parent || !child ||!pattern) {
  626. return _nodes;
  627. }
  628. var filter = findNodePattern(pattern);
  629. var _found = _FALSE;
  630. var _node = parent.firstChild;
  631. while(_node) {
  632. if($tom.include(_node, child)) {
  633. _found = _TRUE;
  634. }
  635. if (filter.test(_node)) {
  636. _nodes.push(_node);
  637. } else {
  638. if(_found) {
  639. break;
  640. } else {
  641. _nodes = [];
  642. }
  643. }
  644. _node = _node.nextSibling;
  645. }
  646. return _found ? _nodes : [];
  647. // return _nodes;
  648. },
  649. /* has no filter */
  650. /**
  651. * node의 parent node를 반환한다.
  652. * @function
  653. */
  654. parent: function(node) {
  655. if(!node || !node.parentNode) {
  656. return _NULL;
  657. }
  658. return node.parentNode;
  659. },
  660. /**
  661. * node를 포함하고 있는 body 요소를 반환한다.
  662. * @function
  663. */
  664. body: function(node) {
  665. if(!node || !node.parentNode) {
  666. return _NULL;
  667. }
  668. var _node = node.parentNode;
  669. while(_node) {
  670. if($tom.isBody(_node)) {
  671. return _node;
  672. }
  673. _node = _node.parentNode;
  674. }
  675. return _NULL;
  676. },
  677. /**
  678. * ancestor의 하위에서 처음 나오는 텍스트 노드를 찾아서 반환한다.
  679. * @function
  680. */
  681. top: function(ancestor, all) {
  682. all = all || _FALSE;
  683. var _node = ancestor;
  684. while($tom.first(_node)) {
  685. _node = $tom.first(_node);
  686. }
  687. if(all) {
  688. return _node;
  689. } else {
  690. if ($tom.kindOf(_node, "#tx_start_marker,#tx_end_marker")) {
  691. _node = _node.nextSibling || _node.parentNode;
  692. } else if($tom.kindOf(_node, '%control')) {
  693. _node = _node.parentNode;
  694. }
  695. return _node;
  696. }
  697. },
  698. /**
  699. * ancestor의 하위에서 마지막에 나오는 텍스트 노드를 찾아서 반환한다.
  700. * @function
  701. */
  702. bottom: function(ancestor, all) {
  703. all = all || _FALSE;
  704. var _node = ancestor;
  705. while($tom.last(_node)) {
  706. _node = $tom.last(_node);
  707. }
  708. if (all) {
  709. return _node;
  710. } else {
  711. if ($tom.kindOf(_node, "#tx_start_marker,#tx_end_marker")) {
  712. _node = _node.previousSibling || _node.parentNode;
  713. } else if ($tom.kindOf(_node, '%control')) {
  714. _node = _node.parentNode;
  715. }
  716. return _node;
  717. }
  718. },
  719. /**
  720. * child가 parent에 포함되어 있는 요소이면 true를 반환한다.
  721. * @function
  722. */
  723. include: function(parent, child) {
  724. if(!parent || !child) {
  725. return _FALSE;
  726. }
  727. if(parent == child) {
  728. return _TRUE;
  729. }
  730. var _node = child;
  731. while (_node) {
  732. if ($tom.isBody(_node)) {
  733. return _FALSE;
  734. } else if (_node == parent) {
  735. return _TRUE;
  736. }
  737. _node = _node.parentNode;
  738. }
  739. return _FALSE;
  740. },
  741. /**
  742. * node, offset 이전 커서의 위치를 반환한다.
  743. * @function
  744. */
  745. prevNodeUntilTagName: function(node, offset, tagName){
  746. tagName = tagName.toUpperCase();
  747. if(offset === 0)
  748. node = node.previousSibling;
  749. else {
  750. node = node.childNodes[offset-1];
  751. }
  752. while(node&&node.lastChild){
  753. if(node.tagName === tagName)
  754. break;
  755. node = node.lastChild;
  756. }
  757. return node;
  758. },
  759. /**
  760. * node 다음 content를 반환한다.
  761. * @function
  762. */
  763. nextContent : function (node, filter){
  764. do{
  765. var _node = $tom.next(node, filter);
  766. if(_node)
  767. return _node;
  768. node = $tom.parent(node);
  769. }while(node && !$tom.isBody(node));
  770. return null;
  771. }
  772. });
  773. })();
  774. Object.extend($tom, /** @lends $tom */{
  775. /**
  776. * parent요소의 첫번째 자식노드로 child를 삽입한다.
  777. * @function
  778. */
  779. insertFirst: function(parent, child) {
  780. if(!parent || !child) {
  781. return;
  782. }
  783. if (parent.firstChild) {
  784. parent.insertBefore(child, parent.firstChild);
  785. } else {
  786. parent.appendChild(child);
  787. }
  788. return child;
  789. },
  790. /**
  791. * target 요소 전 위치에 source 요소를 삽입한고 source 요소를 반환한다.
  792. * @function
  793. */
  794. insertAt: function(source, target) {
  795. if(!source || !target) {
  796. return;
  797. }
  798. target.parentNode.insertBefore(source, target);
  799. return source;
  800. },
  801. /**
  802. * target 요소 다음 위치에 source 요소를 삽입한고 source 요소를 반환한다.
  803. * @function
  804. */
  805. insertNext: function(source, target) {
  806. if(!source || !target) {
  807. return;
  808. }
  809. var nextSibling = target.nextSibling;
  810. if (nextSibling) {
  811. nextSibling.parentNode.insertBefore(source, nextSibling);
  812. } else {
  813. target.parentNode.appendChild(source);
  814. }
  815. return source;
  816. },
  817. /**
  818. * parent 요소에 child 요소를 붙인 후 child 요소를 반환한다.
  819. * @function
  820. */
  821. append: function(parent, child) {
  822. if(!parent || !child) {
  823. return;
  824. }
  825. parent.appendChild(child);
  826. return child;
  827. },
  828. /**
  829. * node 를 제거한다.
  830. * @function
  831. */
  832. remove: function(node) {
  833. if(!node) {
  834. return;
  835. }
  836. if(node.parentNode) {
  837. node.parentNode.removeChild(node);
  838. }
  839. node = _NULL;
  840. },
  841. /**
  842. * node의 innerHTML로 html를 넣고 node를 반환한다.
  843. * @function
  844. */
  845. html: function(node, html) {
  846. if(!node) {
  847. return;
  848. }
  849. node.innerHTML = html || "";
  850. return node;
  851. },
  852. /**
  853. * node의 내용을 지운다.
  854. * @function
  855. */
  856. clean: function(node) {
  857. return $tom.html(node);
  858. },
  859. /**
  860. * node안에 해당 html를 채워넣고 node를 반환한다.
  861. * @function
  862. */
  863. stuff: function(node, html) {
  864. if(!node) {
  865. return node;
  866. }
  867. if($tom.hasUsefulChildren(node, _TRUE)) {
  868. return node;
  869. }
  870. if(node.lastChild) {
  871. var _node = node;
  872. while (_node.lastChild) {
  873. _node = _node.lastChild;
  874. }
  875. $tom.insertNext(html, _node);
  876. } else {
  877. $tom.append(node, html);
  878. }
  879. return node;
  880. }
  881. });
  882. Object.extend($tom, /** @lends $tom */{
  883. /**
  884. * child가 없는 listhead라면 삭제한다
  885. * @param node
  886. */
  887. removeListIfEmpty: function(node) {
  888. while ($tom.kindOf(node, "%listhead") && node.childNodes.length == 1 && $tom.kindOf(node.firstChild, "%listhead")) {
  889. node = node.firstChild;
  890. }
  891. while ($tom.kindOf(node, "%listhead") && node.childNodes.length == 0) {
  892. var tempNode = node.parentNode;
  893. $tom.remove(node);
  894. node = tempNode;
  895. }
  896. }
  897. });
  898. Object.extend($tom, /** @lends $tom */{
  899. /**
  900. * sNode의 자식노드들을 dNode의 child로 삽입 하는데 sInx, eInx는 자식노드의 시작, 끝 인덱스번호다.
  901. * @function
  902. * @param sNode
  903. * @param dNode
  904. * @param sInx
  905. * @param eInx
  906. */
  907. moveChild: function(sNode, dNode, sInx, eInx) {
  908. if(!sNode || !dNode) {
  909. return;
  910. }
  911. sInx = Math.min(Math.max(sInx || 0), sNode.childNodes.length);
  912. eInx = Math.min(Math.max(eInx || sNode.childNodes.length), sNode.childNodes.length);
  913. if(sInx >= eInx) {
  914. return;
  915. }
  916. var _inx = sInx;
  917. while (_inx++ < eInx && sInx < sNode.childNodes.length) {
  918. dNode.appendChild(sNode.childNodes[sInx]);
  919. }
  920. },
  921. /**
  922. * node의 자식노드를 node의 부모노드에 붙인다.
  923. * @function
  924. */
  925. moveChildToParent: function(node) {
  926. if(!node) {
  927. return;
  928. }
  929. while (node.firstChild) {
  930. node.parentNode.insertBefore(node.firstChild, node);
  931. }
  932. }
  933. });
  934. /*
  935. * Create, Destroy, Change
  936. */
  937. Object.extend($tom, /** @lends $tom */{
  938. /**
  939. * source를 target로 교체하고 target를 반환한다.
  940. * @function
  941. */
  942. replace: function(source, target) {
  943. if (!source || !target) {
  944. return _NULL;
  945. }
  946. if ($tom.getName(source) == $tom.getName(target)) {
  947. $tom.remove(target);
  948. return source;
  949. } else {
  950. // FTDUEDTR-1248
  951. var children = [],
  952. childNodes = source.childNodes,
  953. len = childNodes.length;
  954. for (var i = 0; i < len; i++) {
  955. children.push(childNodes[i]);
  956. }
  957. for (i = 0; i < len; i++) {
  958. var child = children[i];
  959. if (child.lastChild === source) {
  960. var cloneChild = $tom.clone(child);
  961. $tom.moveChild(child, cloneChild);
  962. child.innerHTML = "";
  963. target.appendChild(cloneChild);
  964. } else {
  965. target.appendChild(child);
  966. }
  967. }
  968. $tom.insertAt(target, source);
  969. $tom.remove(source);
  970. return target;
  971. }
  972. },
  973. /**
  974. * node를 복사 후 반환한다.
  975. * @function
  976. */
  977. clone: function(node, deep) {
  978. var cloneNode = node.cloneNode(!!deep);
  979. if (node.nodeType == 1) {
  980. cloneNode.removeAttribute("id");
  981. }
  982. return cloneNode;
  983. }
  984. });
  985. /*
  986. * Wrap, Unwrap
  987. */
  988. Object.extend($tom, /** @lends $tom */{
  989. /**
  990. * wNode 아래에 pNodes를 붙여서 pNodes를 wNode로 감싼다.
  991. * @function
  992. * @return wNode
  993. */
  994. wrap: function(wNode, pNodes) { //NOTE: quote, quotenodesign, textbox 등에서 사용됨, actually using 'div', 'blockquote'
  995. if (!wNode || !pNodes) {
  996. return _NULL;
  997. }
  998. if (pNodes instanceof Array == _FALSE) {
  999. pNodes = [].concat(pNodes);
  1000. }
  1001. $tom.insertAt(wNode, pNodes[0]);
  1002. pNodes.each((function(pNode){
  1003. $tom.append(wNode, pNode);
  1004. }));
  1005. return wNode;
  1006. },
  1007. /**
  1008. * node를 제거하고 node의 자식노드는 node의 상위에 붙인다.
  1009. * @function
  1010. */
  1011. unwrap: function(node) {
  1012. if (!node) {
  1013. return _NULL;
  1014. }
  1015. var _nNode = $tom.first(node);
  1016. if ($tx.msie_nonstd) {
  1017. node.removeNode(); // IE에서는 이게 더 빠름
  1018. } else {
  1019. $tom.moveChildToParent(node);
  1020. $tom.remove(node);
  1021. }
  1022. return _nNode;
  1023. }
  1024. });
  1025. Object.extend($tom, /** @lends $tom */{
  1026. /**
  1027. * @private
  1028. * @function
  1029. */
  1030. divideText: function(node, offset) {
  1031. if(!$tom.isText(node)) {
  1032. return node;
  1033. }
  1034. if(offset <= 0 || offset >= node.length) { //나눌필요가 있을까?
  1035. return node;
  1036. }
  1037. var _newNode = node.cloneNode(_FALSE);
  1038. node.deleteData(offset, node.length - offset);
  1039. _newNode.deleteData(0, offset);
  1040. $tom.insertNext(_newNode, node);
  1041. return _newNode;
  1042. },
  1043. /**
  1044. * node의 offset번째 child를 기준으로 두 개로 분리한다.
  1045. */
  1046. divideNode: function(node, offset) {
  1047. if(!$tom.isElement(node)) {
  1048. return _NULL;
  1049. }
  1050. /*if(offset <= 0 || offset >= node.childNodes.length) { //나눌필요가 있을까?
  1051. return node;
  1052. }*/
  1053. var _lastOffset = node.childNodes.length - offset;
  1054. var _newNode = node.cloneNode(_FALSE);
  1055. for(var i=0;i<_lastOffset;i++) {
  1056. $tom.insertFirst(_newNode, node.lastChild);
  1057. }
  1058. $tom.insertNext(_newNode, node);
  1059. return _newNode;
  1060. },
  1061. /**
  1062. * divideNode와 비슷한데, node를 clone할 때에 style, attribute를 모두 복사하게 된다.
  1063. * divideNode 사용에 대한 legacy 때문에 따로 만들었으며, 사용법이 확인된 이후에는 두 개가 합쳐질 필요가 있다.
  1064. * 예를 들어 style을 복사하는 책임을 caller에게 넘기는 방식이나, 파라미터로 선택할 수 있도록 해서...
  1065. */
  1066. splitAt: function(node, index) {
  1067. if (!$tom.isElement(node)) {
  1068. return;
  1069. }
  1070. var clonedNode = $tom.clone(node);
  1071. $tom.moveChild(node, clonedNode, index + 1, node.childNodes.length);
  1072. $tom.insertNext(clonedNode, node);
  1073. return clonedNode;
  1074. },
  1075. /**
  1076. * stopAncestor은 dividedPoint의 ancestor 이어야 함
  1077. * stopAncestor와 dividedPoint 사이에 table이 없어야 함
  1078. */
  1079. divideTree: function(stopAncestor, dividedPoint) {
  1080. var currentNode = dividedPoint, offset, parent;
  1081. do {
  1082. parent = currentNode.parentNode;
  1083. offset = $tom.indexOf(currentNode);
  1084. currentNode = $tom.divideNode(parent, offset);
  1085. } while (currentNode.previousSibling != stopAncestor);
  1086. return currentNode;
  1087. },
  1088. /**
  1089. * @private
  1090. * @function
  1091. */
  1092. divideParagraph: function(node) {
  1093. var _node = node;
  1094. var _offset = $tom.indexOf(node);
  1095. var _divided = _node;
  1096. while (_node) {
  1097. if ($tom.isBody(_node)) {
  1098. break;
  1099. } else if ($tom.kindOf(_node, 'td,th,%wrapper,%outergroup')) {
  1100. break;
  1101. } else if ($tom.kindOf(_node, "#tx_start_marker,#tx_end_marker")) {
  1102. _offset = $tom.indexOf(_node);
  1103. } else if($tom.isControl(_node)) {
  1104. _offset = $tom.indexOf(_node);
  1105. } else if ($tom.isText(_node)) { //text
  1106. _node = $tom.divideText(_node, _offset);
  1107. _offset = $tom.indexOf(_node);
  1108. } else { //%inline, %paragraph
  1109. _node = $tom.divideNode(_node, _offset);
  1110. _offset = $tom.indexOf(_node);
  1111. _divided = _node;
  1112. if ($tom.kindOf(_node, 'p,li,dd,dt,h1,h2,h3,h4,h5,h6')) {
  1113. break;
  1114. }
  1115. }
  1116. _node = _node.parentNode;
  1117. }
  1118. return _divided;
  1119. },
  1120. wrapInlinesWithP: function(inline, ancestorBlock) {
  1121. var ownerDocument = $tom.getOwnerDocument(inline);
  1122. var inlineNodes = $tom.extract(ancestorBlock || ownerDocument.body, inline, '%text,%inline,%control');
  1123. // caret은 곧 사라지기 때문에P로 감쌀 필요가 없다
  1124. if (this.hasOnlySavedCaret(inlineNodes, inline)) {
  1125. return _NULL;
  1126. }
  1127. var newParagraph = ownerDocument.createElement("p");
  1128. $tom.wrap(newParagraph, inlineNodes);
  1129. return newParagraph;
  1130. },
  1131. hasOnlySavedCaret: function(inlines, inline) {
  1132. var validInlines = inlines.findAll(function(node) {
  1133. return node.nodeType != 3 || node.nodeValue.trim() != "";
  1134. });
  1135. return this.isGoogRangeCaret(inline) && validInlines.length == 1 && validInlines[0] == inline;
  1136. },
  1137. isGoogRangeCaret: function(node) {
  1138. return node && /goog_[0-9]+/.test(node.id);
  1139. }
  1140. });
  1141. Object.extend($tom, /** @lends $tom */{
  1142. /**
  1143. * name의 하위요소로 들어올 요소이름 반환
  1144. * @function
  1145. * @example
  1146. * $tom.paragraphOf("table") // 'td'를 반환한다.
  1147. */
  1148. paragraphOf: function(name) {
  1149. if(!name) {
  1150. return 'p';
  1151. }
  1152. var _translator = $tom.translate(name);
  1153. if (_translator.memberOf('ul,ol')) {
  1154. return 'li';
  1155. } else if (_translator.memberOf('dl')) {
  1156. return 'dd';
  1157. } else if (_translator.memberOf('tr,tbody,thead,tfoot,table')) {
  1158. return 'td';
  1159. } else {
  1160. return 'p';
  1161. }
  1162. },
  1163. /**
  1164. * 'span' 을 반환한다.
  1165. * @function
  1166. */
  1167. inlineOf: function() {
  1168. return 'span';
  1169. },
  1170. /**
  1171. * 요소의 name을 받아서 상위요소가 되는 요소이름을 반환한다.
  1172. * @function
  1173. * @example
  1174. * $tom.outerOf("td") // "table"을 반환한다.
  1175. */
  1176. outerOf: function(name) {
  1177. if(!name) {
  1178. return 'span';
  1179. }
  1180. var _translator = $tom.translate(name);
  1181. if (_translator.memberOf('li')) {
  1182. return 'ol';
  1183. } else if (_translator.memberOf('dd,dt')) {
  1184. return 'dl';
  1185. } else if (_translator.memberOf('td,th,tr')) {
  1186. return 'table';
  1187. } else {
  1188. return 'p';
  1189. }
  1190. }
  1191. });
  1192. (function() {
  1193. var __IGNORE_NAME_FLAG = 0;
  1194. var UnitCalculate = Trex.Class.create({
  1195. $const: {
  1196. __FONT_SIZE_BASIS: 9,
  1197. __REG_EXT_NUMBER: new RegExp("[0-9\.]+"),
  1198. __REG_EXT_UNIT: new RegExp("px|pt|em")
  1199. },
  1200. initialize: function() {
  1201. this.unitConverter = { //1em = 9pt
  1202. "px2em": 1 / 12,
  1203. "px2pt": 9 / 12,
  1204. "em2px": 12, // 12 : 1
  1205. "em2pt": 9, // 9 : 1
  1206. "pt2px": 12 / 9,
  1207. "pt2em": 1 / 9
  1208. };
  1209. },
  1210. calculate: function(strA, strB) {
  1211. if (strA == _NULL || strA.length == 0) {
  1212. strA = "0em";
  1213. }
  1214. if (strB == _NULL || strB.length == 0) {
  1215. strB = "0em";
  1216. }
  1217. var _sign = this.extractSign(strB);
  1218. var _unitA = this.extractUnit(strA);
  1219. var _unitB = this.extractUnit(strB); //basis unit
  1220. var _numA = this.extractNumber(strA).toNumber();
  1221. var _numB = this.extractNumber(strB).toNumber();
  1222. if(_unitA != _unitB) { //different unit
  1223. if(this.unitConverter[_unitA+"2"+_unitB]) {
  1224. _numA *= this.unitConverter[_unitA+"2"+_unitB];
  1225. }
  1226. }
  1227. var _result = 0;
  1228. if(_sign == "-") {
  1229. _result = Math.max(_numA - _numB, 0);
  1230. } else {
  1231. _result = (_numA + _numB);
  1232. }
  1233. _result = (Math.round(_result*10)/10);
  1234. if (_result == 0) {
  1235. return _NULL;
  1236. } else {
  1237. return _result + _unitB;
  1238. }
  1239. },
  1240. needCalculation: function(str) {
  1241. if(str == _NULL || typeof str != "string") {
  1242. return _FALSE;
  1243. } else {
  1244. return (str.charAt(0) == '+' || str.charAt(0) == '-');
  1245. }
  1246. },
  1247. extractSign: function(str) {
  1248. var _sign = "+";
  1249. if(str.charAt(0) == '+' || str.charAt(0) == '-') {
  1250. _sign = str.charAt(0);
  1251. }
  1252. return _sign;
  1253. },
  1254. extractNumber: function(str) {
  1255. var _num = 0;
  1256. var _match;
  1257. if((_match = str.match(UnitCalculate.__REG_EXT_NUMBER)) != _NULL) {
  1258. _num = _match[0];
  1259. }
  1260. if(str.indexOf("%") > -1) { //%
  1261. _num = _num / 100;
  1262. }
  1263. return _num;
  1264. },
  1265. extractUnit: function(str) {
  1266. var _unit = "em";
  1267. var _match;
  1268. if((_match = str.match(UnitCalculate.__REG_EXT_UNIT)) != _NULL) {
  1269. _unit = _match[0];
  1270. }
  1271. return _unit;
  1272. }
  1273. });
  1274. var _unitCalculator = new UnitCalculate();
  1275. var __ATTRIBUTE_TRANSLATIONS = {
  1276. colspan: "colSpan",
  1277. rowspan: "rowSpan",
  1278. valign: "vAlign",
  1279. datetime: "dateTime",
  1280. accesskey: "accessKey",
  1281. tabindex: "tabIndex",
  1282. enctype: "encType",
  1283. maxlength: "maxLength",
  1284. readonly: "readOnly",
  1285. longdesc: "longDesc",
  1286. cellPadding: "cellPadding",
  1287. cellSpacing: "cellSpacing",
  1288. more: "more",
  1289. less: "less",
  1290. style: "style"
  1291. };
  1292. Object.extend($tom, /** @lends $tom */{
  1293. /**
  1294. * node에 인자로 받은 attributes 속성을 세팅한다.
  1295. * @function
  1296. * @param {Element} node
  1297. * @param {JSON} attributes
  1298. * @example
  1299. * $tom.applyAttributes(inNode, {
  1300. * 'style': { 'fontSize': null },
  1301. * 'size': null
  1302. * });
  1303. */
  1304. applyAttributes: function(node, attributes) {
  1305. if(!$tom.isElement(node)) {
  1306. return;
  1307. }
  1308. for(var _name in attributes) {
  1309. if(_name == "style") {
  1310. $tom.applyStyles(node, attributes[_name]);
  1311. } else {
  1312. $tom.setAttribute(node, _name, attributes[_name]);
  1313. }
  1314. }
  1315. },
  1316. /**
  1317. * node에 인자로 받은 attributes 속성을 제거한다.
  1318. * @function
  1319. */
  1320. removeAttributes: function(node, attributes) {
  1321. if(!$tom.isElement(node)) {
  1322. return;
  1323. }
  1324. for(var _name in attributes) {
  1325. if(_name == "style") {
  1326. $tom.removeStyles(attributes[_name])
  1327. } else {
  1328. node.removeAttribute(_name, __IGNORE_NAME_FLAG);
  1329. }
  1330. }
  1331. },
  1332. /**
  1333. * node에서 attrName을 이름으로 갖는 속성의 값을 반환
  1334. * @function
  1335. * @example
  1336. * $tx("tx_image").getAttribute("class") // class속성의 값 반환
  1337. */
  1338. getAttribute: function(node, attrName) {
  1339. if(!$tom.isElement(node)) {
  1340. return _NULL;
  1341. }
  1342. if(node && node.getAttribute) {
  1343. return node.getAttribute(__ATTRIBUTE_TRANSLATIONS[attrName] || attrName);
  1344. } else {
  1345. return _NULL;
  1346. }
  1347. },
  1348. /**
  1349. * node에 attrName를 이름으로, attrValue를 값으로 갖는 속성을 세팅한다.
  1350. * @function
  1351. */
  1352. setAttribute: function(node, attrName, attrValue) {
  1353. if(!$tom.isElement(node)) {
  1354. return;
  1355. }
  1356. if(attrValue == _NULL || attrValue.length == 0 || attrValue == 0) {
  1357. node.removeAttribute(attrName, __IGNORE_NAME_FLAG);
  1358. } else {
  1359. if(__ATTRIBUTE_TRANSLATIONS[attrName]) {
  1360. node.setAttribute(__ATTRIBUTE_TRANSLATIONS[attrName], attrValue);
  1361. } else {
  1362. try {
  1363. node[attrName] = attrValue;
  1364. } catch(e) {
  1365. console.log(e);
  1366. node.setAttribute(__ATTRIBUTE_TRANSLATIONS[attrName] || attrName, attrValue);
  1367. }
  1368. }
  1369. }
  1370. },
  1371. // TODO : refactoring 뭔가 복잡하다.
  1372. setStyles: function(node, styles, overwrite) {
  1373. var nodeCssText = node.style.cssText;
  1374. var canSetStyle;
  1375. var styleToSet = Object.extend({}, styles);
  1376. if (styleToSet.font) {
  1377. if (overwrite) {
  1378. node.style.font = styleToSet.font; // 이 부분에서 chrome, opera는 font의 css 속성이 분해된 형태로 적용된다.
  1379. } else if (node.style.cssText.indexOf("font:") == -1) {
  1380. node.style.cssText = 'font: ' + styleToSet.font + '; ' + node.style.cssText;
  1381. }
  1382. delete styleToSet.font;
  1383. }
  1384. for (var styleName in styleToSet) {
  1385. var styleValue;
  1386. if (_unitCalculator.needCalculation(styleToSet[styleName])) {
  1387. styleValue = _unitCalculator.calculate(node.style[styleName], styleToSet[styleName]);
  1388. } else {
  1389. styleValue = styleToSet[styleName];
  1390. }
  1391. if (styleValue == _NULL) {
  1392. styleValue = "";
  1393. }
  1394. if (styleName == 'float') {
  1395. styleName = $tx.msie ? 'styleFloat' : 'cssFloat';
  1396. }
  1397. canSetStyle = (!node.style[styleName] && (styleName.indexOf("font") != 0 || nodeCssText.indexOf("font:") == -1)) || overwrite;
  1398. var newTextDecoration = (styleName == "textDecoration") && !node.style[styleName].include(styleValue);
  1399. if (canSetStyle) {
  1400. node.style[styleName] = styleValue;
  1401. } else if (newTextDecoration) {
  1402. node.style[styleName] += " " + styleValue;
  1403. }
  1404. }
  1405. $tom._clearUselessStyle(node);
  1406. },
  1407. /**
  1408. * node에 styles에서 지정한 스타일을 적용한다.
  1409. * @function
  1410. * @example
  1411. * $tom.applyStyles(node, {
  1412. * 'width': width
  1413. * });
  1414. */
  1415. applyStyles: function(node, styles) {
  1416. this.setStyles(node, styles, _TRUE);
  1417. },
  1418. /**
  1419. * node의 style 속성값을 적용하되, 이미 존재하는 속성은 유지된다.
  1420. * @param node
  1421. * @param styles
  1422. */
  1423. addStyles: function(node, styles) {
  1424. this.setStyles(node, styles, _FALSE);
  1425. },
  1426. /**
  1427. * node에서 styles인자에서 지정한 스타일 속성값을 제거한다.
  1428. * @function
  1429. */
  1430. removeStyles: function(node, styles) {
  1431. // FTDUEDTR-1166
  1432. var cssText = node.style.cssText;
  1433. var orignalCssText = cssText;
  1434. for(var _name in styles) {
  1435. _name = _name.replace(/([A-Z])/g, "-$1");
  1436. cssText = cssText.replace(new RegExp("(^| )" + _name + "\\s*:[^;]+;? ?", "ig"), "");
  1437. }
  1438. if (orignalCssText != cssText) {
  1439. node.style.cssText = cssText;
  1440. $tom._clearUselessStyle(node);
  1441. }
  1442. },
  1443. _clearUselessStyle: function(node) {
  1444. var _attrValue = $tom.getAttribute(node, "style");
  1445. if (!_attrValue) { //remove needless style
  1446. node.removeAttribute("style", __IGNORE_NAME_FLAG);
  1447. }
  1448. },
  1449. /**
  1450. * node에서 style 속성값 텍스트를 모두 반환한다.
  1451. * @function
  1452. */
  1453. getStyleText: function(node) {
  1454. return node.style.cssText;
  1455. },
  1456. /**
  1457. * node의 style 속성값을 value로 넣는다. 기존에 있는 값은 덮어쓰여진다.
  1458. * @function
  1459. * @param {Element} node
  1460. * @param {String} value style 속성에 바로 세팅할 텍스트 값을 넣어야 함
  1461. * @example
  1462. * $tom.setStyleText($tx("tx_article_category"), "width:50px;height:10px")
  1463. */
  1464. setStyleText: function(node, value) {
  1465. node.style.cssText = value;
  1466. !value && $tom._clearUselessStyle(node);
  1467. }
  1468. });
  1469. })();
  1470. Object.extend($tom, /** @lends $tom */{
  1471. /**
  1472. * @private
  1473. * @function
  1474. */
  1475. goInto: function(node, toTop) {
  1476. if(!node || !node.scrollIntoView) {
  1477. return;
  1478. }
  1479. node.scrollIntoView(toTop);
  1480. },
  1481. /**
  1482. * 수직 스크롤 위치값을 반환한다.
  1483. * @function
  1484. * @example
  1485. * $tom.getScrollTop(document)
  1486. */
  1487. getScrollTop: function(doc) {
  1488. if(!doc) {
  1489. return 0;
  1490. }
  1491. return doc.documentElement.scrollTop >= 0 ? doc.documentElement.scrollTop : doc.body.scrollTop;
  1492. },
  1493. /**
  1494. * 수직 스크롤 값을 셋팅한다.
  1495. * @function
  1496. * @param {Element} doc
  1497. * @param {Number} scrollTop 수직 스크롤 값
  1498. */
  1499. setScrollTop: function(doc, scrollTop) {
  1500. if(!doc) {
  1501. return;
  1502. }
  1503. if(doc.documentElement.scrollTop) {
  1504. doc.documentElement.scrollTop = scrollTop;
  1505. } else {
  1506. doc.body.scrollTop = scrollTop;
  1507. }
  1508. },
  1509. /**
  1510. * 수평 스크롤 위치값을 반환한다.
  1511. * @function
  1512. */
  1513. getScrollLeft: function(doc) {
  1514. if(!doc) {
  1515. return 0;
  1516. }
  1517. return (doc.documentElement.scrollLeft || doc.body.scrollLeft);
  1518. },
  1519. /**
  1520. * 수평 스크롤 값을 셋팅한다.
  1521. * @function
  1522. * @param {Element} doc
  1523. * @param {Number} scrollLeft 수평 스크롤 값
  1524. */
  1525. setScrollLeft: function(doc, scrollLeft) {
  1526. if(!doc) {
  1527. return;
  1528. }
  1529. if(doc.documentElement.scrollLeft) {
  1530. doc.documentElement.scrollLeft = scrollLeft;
  1531. } else {
  1532. doc.body.scrollLeft = scrollLeft;
  1533. }
  1534. },
  1535. /**
  1536. * element요소의 left, top, width, height 값을 계산하여 반환한다.
  1537. * @function
  1538. * @return {
  1539. * x: 0,
  1540. * y: 0,
  1541. * width: 0,
  1542. * height: 0
  1543. * }
  1544. */
  1545. getPosition: function(element, cumulative) {
  1546. if(!element) {
  1547. return {
  1548. x: 0,
  1549. y: 0,
  1550. width: 0,
  1551. height: 0
  1552. };
  1553. }
  1554. cumulative = !!cumulative;
  1555. element = $tx(element);
  1556. var pos = (cumulative)? $tx.cumulativeOffset(element): $tx.positionedOffset(element);
  1557. var dim;
  1558. var display = element.style.display;
  1559. if (display != 'none' && display != _NULL) { //Safari bug
  1560. dim = {
  1561. width: element.offsetWidth,
  1562. height: element.offsetHeight
  1563. };
  1564. } else {
  1565. var els = element.style;
  1566. var originalVisibility = els.visibility;
  1567. var originalPosition = els.position;
  1568. var originalDisplay = els.display;
  1569. els.visibility = 'hidden';
  1570. els.position = 'absolute';
  1571. els.display = 'block';
  1572. var originalWidth = element.clientWidth;
  1573. var originalHeight = element.clientHeight;
  1574. els.display = originalDisplay;
  1575. els.position = originalPosition;
  1576. els.visibility = originalVisibility;
  1577. dim = {
  1578. width: originalWidth,
  1579. height: originalHeight
  1580. };
  1581. }
  1582. return {
  1583. x: pos[0],
  1584. y: pos[1],
  1585. width: dim.width,
  1586. height: dim.height
  1587. };
  1588. },
  1589. /**
  1590. * node 요소의 width값을 반환한다.
  1591. * inline style이 px값으로 유효하지 않으면 offset으로 대체한다.
  1592. * @function
  1593. */
  1594. getWidth: function(node) {
  1595. var width = node.style["width"];
  1596. if( width.isPx() ){
  1597. return width.parsePx();
  1598. }
  1599. return node.offsetWidth;
  1600. },
  1601. /**
  1602. * node 요소 스타일속성의 width 값을 세팅한다.
  1603. * @function
  1604. */
  1605. setWidth: function(node, width) {
  1606. $tom.applyStyles(node, {
  1607. 'width': width
  1608. });
  1609. },
  1610. /**
  1611. * node 요소의 height값을 반환한다.
  1612. * inline style이 px값으로 유효하지 않으면 offset으로 대체한다.
  1613. * @function
  1614. */
  1615. getHeight: function(node) {
  1616. var height = node.style["height"];
  1617. if( height.isPx() ){
  1618. return height.parsePx();
  1619. }
  1620. return node.offsetHeight;
  1621. },
  1622. /**
  1623. * node 요소 스타일속성의 height 값을 세팅한다.
  1624. * @function
  1625. */
  1626. setHeight: function(node, height) {
  1627. $tom.applyStyles(node, {
  1628. 'height': height
  1629. });
  1630. },
  1631. /**
  1632. * @private
  1633. * @function
  1634. */
  1635. replacePngPath: function(node) {
  1636. if ($tx.msie6) {
  1637. if(_DOC.location.href.indexOf("http://") > -1) {
  1638. return;
  1639. }
  1640. try {
  1641. var _orgFilter = $tx.getStyle(node, 'filter');
  1642. var _orgSrc = /src='([^']+)'/.exec(_orgFilter)[1];
  1643. if(!_orgSrc || _orgSrc == 'none') {
  1644. return;
  1645. } else if(_orgSrc.indexOf("http://") > -1) {
  1646. return;
  1647. }
  1648. var _docPathSlices = _DOC.location.href.split("/");
  1649. _docPathSlices.push("css");
  1650. _docPathSlices.pop();
  1651. _orgSrc = _orgSrc.replace(/\.\.\//g, function() {
  1652. _docPathSlices.pop();
  1653. return "";
  1654. });
  1655. var _newSrc = _docPathSlices.join("/") + "/" + _orgSrc;
  1656. node.style.filter = _orgFilter.replace(/src='([^']+)'/, "src='" + _newSrc + "'");
  1657. } catch(e) {alert(e)}
  1658. }
  1659. }
  1660. });
  1661. Object.extend($tom, /** @lends $tom */{
  1662. /**
  1663. * 편집영역에서 기본 빈 문단에 해당하는 content
  1664. * @constant
  1665. */
  1666. EMPTY_BOGUS: ($tx.msie_quirks || $tx.msie && $tx.msie_ver < 11 ? "&nbsp;" : "<br>")
  1667. });
  1668. Object.extend($tom, /** @lends $tom */{
  1669. /**
  1670. * 편집영역에서 기본 빈 문단에 해당하는 HTML
  1671. * @constant
  1672. */
  1673. EMPTY_PARAGRAPH_HTML: "<p>" + $tom.EMPTY_BOGUS + "</p>"
  1674. });
  1675. _WIN.$tom = $tom;