observable.js 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384
  1. _WIN.$stop = {};
  2. _WIN.$propagate = {};
  3. /**
  4. * 에디터에 정의된 custom 이벤트들을 발생시키고 등록된 이벤트 핸들러들을 실행시킨다.
  5. * custom 이벤트를 발생시키거나 혹은 custom 이벤트 발생시 핸들러를 실행시키기 위해서는 이 클래스를 minxin 받아야 한다.
  6. * @class
  7. */
  8. Trex.I.JobObservable = Trex.Faculty.create(/** @lends Trex.I.JobObservable */{
  9. /**
  10. * @private
  11. */
  12. jobObservers: {},
  13. /**
  14. * custom 이벤트가 발생하는지를 관찰하는 observer를 등록한다.
  15. * @param {String} name - custom 이벤트의 이름
  16. * @param {Function} observer - custom 이벤트 발생시 실행될 handler
  17. * @example
  18. * canvas.observeJob(Trex.Ev.__CANVAS_HEIGHT_CHANGE, function(){alert('canvas의 크기가 변했네요')})
  19. * cinema.observeJob("cinema_on_paste", function(){alert('영화가 첨부되었네요')})
  20. */
  21. observeJob: function(name, observer) {
  22. if(!this.jobObservers[name]) {
  23. this.jobObservers[name] = [];
  24. }
  25. this.jobObservers[name].push(observer);
  26. },
  27. reserveJob: function(name, observer, delay) {
  28. delay = delay || 500;
  29. if(!this.jobObservers[name]) {
  30. this.jobObservers[name] = [];
  31. }
  32. var _self = this;
  33. this.jobObservers[name].push(function() {
  34. var args = $A(arguments);
  35. setTimeout(function() {
  36. observer.apply(_self, args);
  37. }, delay);
  38. });
  39. },
  40. removeJob: function(name, observe){
  41. if(!this.jobObservers[name])
  42. return;
  43. if(!observe){
  44. this.jobObservers[name].length = 0;
  45. }else {
  46. for(var i = 0 ; i < this.jobObservers[name].length; i++){
  47. if(this.jobObservers[name][i]===observe){
  48. this.jobObservers[name].splice(i,1);
  49. }
  50. }
  51. }
  52. },
  53. /**
  54. * custom 이벤트를 발생시킨다. 이때 발생시킨 이벤트는 observerJob를 통해 등록된 observer들에게 전파된다.
  55. * @param {String} name - custom 이벤트의 이름
  56. * @example
  57. * canvas.observeJob(Trex.Ev.__CANVAS_HEIGHT_CHANGE, function(){alert('canvas의 크기가 변했네요')})
  58. * cinema.observeJob("cinema_on_paste", function(){alert('영화가 첨부되었네요')})
  59. */
  60. fireJobs: function(name) {
  61. var _self = this;
  62. var args = $A(arguments).slice(1);
  63. if(!this.jobObservers[name]) {
  64. return;
  65. }
  66. if (_WIN['DEBUG']) {
  67. this.jobObservers[name].each(function(observer) {
  68. observer.apply(_self, args);
  69. });
  70. } else {
  71. try {
  72. this.jobObservers[name].each(function(observer) {
  73. observer.apply(_self, args);
  74. });
  75. } catch (e) {
  76. if(e != $stop) { throw e; }
  77. }
  78. }
  79. }
  80. });
  81. /**
  82. * 에디터에서 custom key이벤트들을 발생시키고 등록된 이벤트 핸들러들을 실행시킨다.
  83. * custom key 이벤트를 발생시키거나 혹은 custom key 이벤트 발생시 핸들러를 실행시키기 위해서는 이 클래스를 minxin 받아야 한다.
  84. * @class
  85. */
  86. Trex.I.KeyObservable = Trex.Faculty.create(/** @lends Trex.I.KeyObservable */{
  87. /**
  88. * @private
  89. */
  90. keyObservers: {},
  91. /**
  92. * custom 이벤트가 발생하는지를 관찰하는 observer를 등록한다.
  93. * @param {Object} keys - 이벤트가 발생하길 원하는 키의 조합 {ctrlKey:T, altKey:F, shiftKey:T, keyCode:17}
  94. * @param {Function} observer - 해당 이벤트 발생시 실행될 handler
  95. * @example
  96. * canvas.observeKey({ctrlKey:'T', altKey:'F', keyCode:32}, function(){alert('ctrl + 32키가 눌렸네요.')})
  97. */
  98. observeKey: function(keys, observer) {
  99. var _name = function(keys) {
  100. return (keys.ctrlKey? 'T': 'F') + (keys.altKey? 'T': 'F') + (keys.shiftKey? 'T': 'F') + "_" + keys.keyCode;
  101. }(keys);
  102. if(!this.keyObservers[_name]) {
  103. this.keyObservers[_name] = [];
  104. }
  105. this.keyObservers[_name].push(observer);
  106. },
  107. /**
  108. * 사용자가 정의한 custom key event를 발생시킨다. 이때 발생시킨 이벤트는 observerKey를 통해 등록된 observer들에게 전파된다.
  109. * @param {Object} ev - 사용자가 정의한 key의 pushed 상태 객체
  110. * @example
  111. * canvas.fireKyes({ctrlKey:'T', altKey:'F', keyCode:32}), function(){alert('영화가 첨부되었네요')})
  112. */
  113. fireKeys: function(ev) {
  114. var _name = function(ev) {
  115. return (ev.ctrlKey? 'T': 'F') + (ev.altKey? 'T': 'F') + (ev.shiftKey? 'T': 'F') + "_" + ev.keyCode;
  116. }(ev);
  117. if(!this.keyObservers[_name]) {
  118. return;
  119. }
  120. var _self = this;
  121. var eventStopped = _FALSE;
  122. var stopEventOnce = function() {
  123. if (!eventStopped) {
  124. $tx.stop(ev);
  125. eventStopped = _TRUE;
  126. }
  127. };
  128. this.keyObservers[_name].each(function(observer) {
  129. try {
  130. observer.apply(_self, [ev]);
  131. stopEventOnce();
  132. } catch (e1) {
  133. if(e1 === $stop) {
  134. stopEventOnce();
  135. } else if (e1 !== $propagate) {
  136. console.log(e1, e1.stack);
  137. }
  138. }
  139. });
  140. },
  141. registerKeyEvent: function(el) {
  142. try {
  143. $tx.observe(el, 'keydown', this.fireKeys.bind(this), _TRUE);
  144. } catch(e) {}
  145. }
  146. });
  147. /**
  148. * 마우스클릭이나 방향키를 이용해 특정 엘리먼트에 포커스가 갔을 경우 등록된 handler를 실행시킨다.
  149. * @class
  150. */
  151. Trex.I.ElementObservable = Trex.Faculty.create(/** @lends Trex.I.ElementObservable */{
  152. elementObservers: {},
  153. /**
  154. * 선택되길 원하는 element를 등록한다 .
  155. * @param {Object} layer - 관찰하기를 원하는 element의 tag name과 class name {tag: 'div', klass: 'txc-textbox'}
  156. * @param {Function} observer - 원하는 엘리먼트가 선택되었을때 실행되길 원하는 handler
  157. * @example
  158. * canvas.observeElement({tag:'div', klass: 'txc-textbox'}), function(){alert("div.txc-textbox가 선택되었네요.")})
  159. */
  160. observeElement: function(layer, observer) {
  161. if(layer == _NULL) { //all
  162. this.observeElement({ tag: "*tx-final-body*"}, observer);
  163. } else if (layer.length) {
  164. for (var i = 0; i < layer.length; i++) {
  165. var item = layer[i];
  166. this.observeElement(item, observer);
  167. }
  168. } else {
  169. if (!this.elementObservers[layer.tag]) {
  170. this.elementObservers[layer.tag] = {};
  171. }
  172. if (!layer.klass) {
  173. layer.klass = "*tx-all-class*";
  174. }
  175. if (!this.elementObservers[layer.tag][layer.klass]) {
  176. this.elementObservers[layer.tag][layer.klass] = [];
  177. }
  178. this.elementObservers[layer.tag][layer.klass].push(observer);
  179. }
  180. },
  181. /**
  182. * 특정 element가 선택되었을때 그 element가 선택되길 기다린 observer들에게 알려준다.
  183. * 해당하는 observer들은 handler를 실행시킨다.
  184. * @param {Element} node - 선택된 node
  185. * @example
  186. * canvas.fireElements(document.body)
  187. */
  188. fireElements: function(node) {
  189. if(!node) {
  190. return;
  191. }
  192. var _node = node;
  193. var args = $A(arguments).slice(1);
  194. var _self = this;
  195. try {
  196. var _observers;
  197. if($tom.kindOf(_node, 'img,hr,table,button,iframe')) {
  198. _observers = this.collectObserverByElement(_node.nodeName.toLowerCase(), _node.className);
  199. if(_observers) {
  200. _observers.each(function(observer) {
  201. observer.apply(_self, [_node].concat(args));
  202. });
  203. }
  204. } else {
  205. while (_node) {
  206. _observers = this.collectObserverByElement(_node.nodeName.toLowerCase(), _node.className);
  207. if(_observers) {
  208. _observers.each(function(observer) {
  209. observer.apply(_self, [_node].concat(args));
  210. });
  211. }
  212. if($tom.isBody(_node)) {
  213. break;
  214. }
  215. _node = $tom.parent(_node);
  216. }
  217. }
  218. } catch (e) {
  219. if(e != $stop) { throw e; }
  220. }
  221. this.fireFinally();
  222. },
  223. fireFinally: function() {
  224. var _self = this;
  225. var args = $A(arguments).slice(1);
  226. var _observers = this.collectObserverByElement("*tx-final-body*");
  227. if(_observers) {
  228. _observers.each(function(observer) {
  229. observer.apply(_self, [_NULL].concat(args));
  230. });
  231. }
  232. },
  233. collectObserverByElement: function(tag, klass) {
  234. if(!this.elementObservers[tag]) {
  235. return _NULL;
  236. }
  237. var _observers = [];
  238. klass = klass || "";
  239. if(klass != "") {
  240. var _classes = klass.split(" ");
  241. for(var _klass in this.elementObservers[tag]) {
  242. if(_classes.contains(_klass)) {
  243. _observers.push(this.elementObservers[tag][_klass]);
  244. }
  245. }
  246. }
  247. if (this.elementObservers[tag]["*tx-all-class*"]) {
  248. _observers.push(this.elementObservers[tag]["*tx-all-class*"]);
  249. }
  250. return _observers.flatten();
  251. }
  252. });
  253. Trex.I.MouseoverObservable = Trex.Faculty.create(/** @lends Trex.I.MouseoverObservable */{
  254. mouseoverObservers: {},
  255. /**
  256. * 선택되길 원하는 element를 등록한다 .
  257. * @param {Object} selector - 관찰하기를 원하는 element의 tag name과 class name {tag: 'div', klass: 'txc-textbox'}
  258. * @param {Function} successObserver - 원하는 엘리먼트가 선택되었을때 실행되길 원하는 handler
  259. * @param {Function} failObserver
  260. * @example
  261. * canvas.observeElement({tag:'div', klass: 'txc-textbox'}), function(){alert("div.txc-textbox가 선택되었네요.")})
  262. */
  263. observeMouseover: function(selector, successObserver, failObserver) {
  264. if (!this.mouseoverObservers[selector]) {
  265. this.mouseoverObservers[selector] = {
  266. 'success': [],
  267. 'fail': [],
  268. 'flag': _FALSE
  269. }
  270. }
  271. this.mouseoverObservers[selector]['success'].push(successObserver);
  272. if ( failObserver ){
  273. this.mouseoverObservers[selector]['fail'].push(failObserver);
  274. }
  275. },
  276. fireMouseover: function(node) {
  277. if(!node) { return; }
  278. var _node = node;
  279. var _self = this;
  280. try {
  281. for (var i in this.mouseoverObservers){
  282. this.mouseoverObservers[i].flag = _FALSE;
  283. }
  284. while (_node) {
  285. var _observers = this.collectMouseoverObserver(_node);
  286. if(_observers.length > 0) {
  287. var _nodePos = this.getPositionByNode(_node);
  288. _observers.each(function(observer) {
  289. observer.apply(_self, [_node, _nodePos]);
  290. });
  291. }
  292. if($tom.isBody(_node)) {
  293. break;
  294. }
  295. _node = $tom.parent(_node);
  296. }
  297. } catch (e) {
  298. if(e != $stop) { throw e; }
  299. }
  300. this.runMouseoverFailHandler();
  301. },
  302. runMouseoverFailHandler: function(){
  303. var _failHandlers = [];
  304. for (var i in this.mouseoverObservers){
  305. if ( !this.mouseoverObservers[i].flag ){
  306. _failHandlers.push( this.mouseoverObservers[i]['fail'] );
  307. }
  308. }
  309. _failHandlers.flatten().each( function(handler){
  310. handler();
  311. });
  312. },
  313. collectMouseoverObserver: function(node) {
  314. var _observers = [];
  315. var klass = node.className || "";
  316. var tag = node.tagName;
  317. if ( tag ){
  318. tag = tag.toLowerCase();
  319. if ( this.mouseoverObservers[tag] ){
  320. _observers.push( this.mouseoverObservers[tag]['success'] );
  321. this.mouseoverObservers[tag]['flag'] = _TRUE;
  322. }
  323. }
  324. if(klass != "") {
  325. var _classes = klass.split(" ");
  326. for(var i = 0, len = _classes.length; i < len; i++ ){
  327. var key = tag + "." + _classes[i];
  328. if ( this.mouseoverObservers[key] ) {
  329. _observers.push(this.mouseoverObservers[key]['success']);
  330. this.mouseoverObservers[key]['flag'] = _TRUE;
  331. }
  332. }
  333. }
  334. return _observers.flatten();
  335. }
  336. });
  337. /*---- Trex.I.Runnable ------------------------------------------------------*/
  338. Trex.I.Runnable = Trex.Faculty.create({
  339. isRunning: _FALSE,
  340. repeater: _NULL,
  341. threads: [],
  342. startThread: function(term) {
  343. if (this.repeater) {
  344. this.repeater.clear();
  345. } else {
  346. this.repeater = new Trex.Repeater(this.runThread.bind(this));
  347. }
  348. this.repeater.start(term);
  349. },
  350. stopThread: function() {
  351. this.repeater.clear();
  352. },
  353. runThread: function() {
  354. if(this.isRunning) {
  355. return;
  356. }
  357. if(this.threads.length > 0) {
  358. this.isRunning = _TRUE;
  359. (this.threads.shift())();
  360. this.isRunning = _FALSE;
  361. }
  362. },
  363. putThread: function(thread, important) {
  364. if(important) {
  365. this.threads.unshift(thread);
  366. } else {
  367. this.threads.push(thread);
  368. }
  369. }
  370. });