editor.js 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443
  1. /**
  2. * @fileoverview
  3. * DaumEitor의 Entrypoint역할을 하는 source로 Trex.Editor, Editor 를 포함
  4. */
  5. /**
  6. * 실제 Editor Implementation, 하지만 Editor 생성 시에는 Class Editor를 사용한다
  7. *
  8. * {@link Editor}
  9. * @class
  10. * @param {Object} config
  11. */
  12. Trex.Editor = Trex.Class.create( /** @lends Trex.Editor.prototype */{
  13. /** @ignore */
  14. $mixins: [Trex.I.JobObservable, Trex.I.KeyObservable],
  15. toolbar: _NULL,
  16. sidebar: _NULL,
  17. canvas: _NULL,
  18. config: _NULL,
  19. initialConfig: _NULL,
  20. initialize: function(config) {
  21. this.initialConfig = config;
  22. var _editor = this, _config = this.config = TrexConfig.setup(config);
  23. var _canvas = this.canvas = new Trex.Canvas(_editor, _config);
  24. var _toolbar = this.toolbar = new Trex.Toolbar(_editor, _config);
  25. var _sidebar = this.sidebar = new Trex.Sidebar(_editor, _config);
  26. Trex.invokeInstallation(_editor, _toolbar, _sidebar, _canvas, _config);
  27. /* common key event */
  28. var _evConfig = _config.events;
  29. var _keyDownHandler = function(ev) {
  30. if (_evConfig.useHotKey) {
  31. _editor.fireKeys(ev);
  32. }
  33. };
  34. $tx.observe(_DOC, "keydown", _keyDownHandler.bindAsEventListener(this), _FALSE);
  35. _canvas.observeJob(Trex.Ev.__IFRAME_LOAD_COMPLETE, function() {
  36. //for hanmail iframe load log.
  37. var iframeLoadCompleteTime = new Date().getTime();
  38. var secTime = Math.round((iframeLoadCompleteTime - Editor.initStartTime) / 100) / 10;
  39. _editor.fireJobs(Trex.Ev.__IFRAME_LOADING_TIME, secTime);//TODO unresolved
  40. var _initializedId = _editor.getInitializedId();
  41. var _elLoading = $tx("tx_loading" + _initializedId);
  42. if (!_elLoading) {
  43. return;
  44. }
  45. if (_canvas.mode != Trex.Canvas.__WYSIWYG_MODE) {
  46. _canvas.fireJobs(Trex.Ev.__CANVAS_MODE_INITIALIZE, Trex.Canvas.__WYSIWYG_MODE, _canvas.mode);
  47. }
  48. $tx.hide(_elLoading);
  49. });
  50. Trex.invokeRegisters(_editor, _toolbar, _sidebar, _canvas, _config);
  51. Trex.invokeModules(_editor, _toolbar, _sidebar, _canvas, _config);
  52. },
  53. /**
  54. * Get toolbar instance
  55. * @see Trex.Toolbar
  56. */
  57. getToolbar: function() {
  58. return this.toolbar;
  59. },
  60. /**
  61. * Get sidebar instance
  62. * @see Trex.Sidebar
  63. */
  64. getSidebar: function() {
  65. return this.sidebar;
  66. },
  67. /**
  68. * Get canvas instance
  69. * @see Trex.Canvas
  70. */
  71. getCanvas: function() {
  72. return this.canvas;
  73. },
  74. getUsedWebfont: function() {
  75. return this.canvas.getUsedWebfont();
  76. },
  77. /**
  78. * Get config instance
  79. */
  80. getConfig: function() {
  81. return this.config;
  82. },
  83. getInitialConfig: function () {
  84. return this.initialConfig;
  85. },
  86. getParam: function(name) {
  87. var _params = {}, _config = this.config;
  88. _config.params.each(function(name) {
  89. if (_config[name]) {
  90. _params[name] = _config[name];
  91. }
  92. });
  93. return _params[name];
  94. },
  95. getWrapper: function() {
  96. if (!this.initialConfig.wrapper) {
  97. throw new Error('`wrapper` config variable should be provided');
  98. }
  99. return $must(this.initialConfig.wrapper);
  100. },
  101. getInitializedId: function() {
  102. return this.initialConfig.initializedId || "";
  103. },
  104. saveEditor: function() {
  105. this.setDisableUnloadHandler();
  106. this.getSaver().submit();
  107. },
  108. loadEditor: function(data) {
  109. this.getSaver().load(data);
  110. },
  111. /**
  112. * Editor에서 작성된 저장하기 위해 parsing된 글의 내용을 가져온다.
  113. * @see Trex.Canvas#getContent
  114. */
  115. getContent: function() {
  116. return this.getSaver().getContent();
  117. },
  118. /**
  119. * Editor에 첨부된 첨부데이터 리스트를 가져온다.
  120. * * @see Trex.Sidebar#getAttachments
  121. */
  122. getAttachments: function(type, all) {
  123. return this.getSaver().getAttachments(type, all);
  124. },
  125. /**
  126. * Editor에 삽입된 Embed데이터 리스트를 가져온다.
  127. * * @see Trex.Sidebar#getEmbeddedData
  128. */
  129. getEmbeddedData: function(type) {
  130. return this.getSaver().getEmbeddedData(type);
  131. },
  132. /**
  133. * Editor에 첨부된 정보첨부 리스트를 가져온다.
  134. * * @see Trex.Sidebar#getResults
  135. */
  136. getResults: function(type) {
  137. return this.getSaver().getResults(type);
  138. },
  139. /**
  140. * autosaver의 현재 사용중인 key를 가져온다.
  141. * * @see Trex.Autosaver#getCurSeq
  142. */
  143. getAutosaveSeq: function(){
  144. return (this.getAutoSaver && this.getAutoSaver()) ? this.getAutoSaver().getCurSeq() : "0";
  145. }
  146. });
  147. // Binds helper functions for Editor
  148. (function() {
  149. /**
  150. * Editor
  151. *
  152. * @example
  153. * new Editor({
  154. * txService: 'sampleService',
  155. * txHost: 'sample.daum.net',
  156. * txPath: 'sampleService',
  157. * initializedId: 'stringValue',
  158. * form: 'tx_editor_form'+"$!initializedId"
  159. * });
  160. *
  161. * @extends Trex.Editor
  162. * @class
  163. * @param {Object} config
  164. */
  165. _WIN.Editor = Trex.Class.create({
  166. /** @ignore */
  167. $const: {
  168. __ACTIVE: _FALSE,
  169. __PANEL_LOADED: _FALSE,
  170. __EDITOR_LOADED: _FALSE,
  171. __MULTI_LIST: [],
  172. __SELECTED_INDEX: 0
  173. },
  174. _initEditor: function (_editor, config) {
  175. Editor.__EDITOR_LOADED = _FALSE;
  176. Editor.__PANEL_LOADED = _FALSE;
  177. _editor = new Trex.Editor(config);
  178. var _initializedId = _editor.getInitializedId();
  179. if (_initializedId != _NULL) {
  180. var idx = _initializedId == "" ? 0 : _initializedId;
  181. Editor.__MULTI_LIST[idx] = _editor;
  182. Editor.__SELECTED_INDEX = idx;
  183. }
  184. Object.extend(Editor, _editor);
  185. Editor.__EDITOR_LOADED = _TRUE;
  186. Editor.__ACTIVE = _TRUE;
  187. },
  188. initialize: function(config) {
  189. //for hanmail iframe load log.
  190. if (Trex.hmailLogging) {
  191. Trex.hmailLogging(config);
  192. }
  193. Editor.initStartTime = new Date().getTime();
  194. var _editor = null;
  195. if (_WIN['DEBUG']) {
  196. this._initEditor(_editor, config);
  197. } else {
  198. try {
  199. this._initEditor(_editor, config);
  200. } catch (e) {
  201. if (_editor) {
  202. _editor.fireJobs(Trex.Ev.__RUNTIME_EXCEPTION, e);
  203. } else {
  204. throw 'failed to initialize editor. caused by ' + e;
  205. }
  206. throw e;
  207. }
  208. }
  209. }
  210. });
  211. /**
  212. * 글을 수정할 때 저장된 글을 불러온다.
  213. * @param {Object} data - 에디트에 로드할 내용/첨부파일 값
  214. * @example
  215. * Editor.modify({
  216. * content:'<p>content example</p>' or $tx('tx_content')
  217. * attachments: [
  218. * {attacher: 'image',
  219. * data: {
  220. * thumburl: "http://cfile163.uf.daum.net/P150x100/0126A20248BFAFF72D2229",
  221. * imageurl: "http://cfile163.uf.daum.net/image/0126A20248BFAFF72D2229",
  222. * originalurl: "http://cfile163.uf.daum.net/original/0126A20248BFAFF72D2229",
  223. * exifurl: "http://cfile163.uf.daum.net/info/0126A20248BFAFF72D2229",
  224. * attachurl: "http://cfile163.uf.daum.net/attach/0126A20248BFAFF72D2229",
  225. * filename: "Tree.jpg",
  226. * filesize: "155833"
  227. * }
  228. * }]
  229. * });
  230. */
  231. Editor.modify = function(data) {
  232. if (Editor.__PANEL_LOADED && Editor.__EDITOR_LOADED) {
  233. if (this.loadEditor) {
  234. this.loadEditor(data);
  235. }
  236. } else {
  237. setTimeout(this.modify.bind(this, data), 10);
  238. }
  239. };
  240. /**
  241. * Editor 생성 후 자동저장된 Content를 불러올 경우 사용한다.
  242. * @param {Object} data
  243. * @example
  244. * Editor.restore(
  245. * {content: 'string',
  246. * attachments: [{Object}]});
  247. */
  248. Editor.restore = function(data) {
  249. if (Editor.__PANEL_LOADED && Editor.__EDITOR_LOADED) {
  250. if(this.getAutoSaver && this.getAutoSaver()) {
  251. this.getAutoSaver().load(data);
  252. }
  253. } else {
  254. setTimeout(this.restore.bind(this, data), 10);
  255. }
  256. };
  257. /**
  258. * 글 저장시 사용한다.
  259. * @example
  260. * <a onclick="Editor.save();return _FALSE;" href="#">save</a>
  261. */
  262. Editor.save = function() {
  263. if (Editor.__PANEL_LOADED && Editor.__EDITOR_LOADED) {
  264. if (this.saveEditor) {
  265. this.saveEditor();
  266. }
  267. } else {
  268. setTimeout(this.saveEditor.bind(this), 10);
  269. }
  270. return _FALSE;
  271. };
  272. /**
  273. * Canvas의 최근 focus가 있던 영역에 focus를 준다.
  274. * 예를들어, 이미지를 첨부하는 팝업창에서 작업을 완료 후 팝업창을 닫고 에디터에 최근의 focus를 준다.
  275. */
  276. Editor.focus = function() {
  277. if (Editor.__PANEL_LOADED && Editor.__EDITOR_LOADED) {
  278. var _canvas = this.getCanvas();
  279. if (_canvas) {
  280. _canvas.focus();
  281. }
  282. } else {
  283. setTimeout(this.focus.bind(this), 10);
  284. }
  285. return _FALSE;
  286. };
  287. /**
  288. * Canvas의 맨 위에 focus를 준다.
  289. * @see Canvas#focusOnTop
  290. */
  291. Editor.focusOnTop = function() {
  292. if (Editor.__PANEL_LOADED && Editor.__EDITOR_LOADED) {
  293. var _canvas = this.getCanvas();
  294. if (_canvas) {
  295. _canvas.focusOnTop();
  296. }
  297. } else {
  298. setTimeout(this.focusOnTop.bind(this), 10);
  299. }
  300. return _FALSE;
  301. };
  302. /**
  303. * Canvas의 맨 아래에 focus를 준다.
  304. * @see Canvas#focusOnBottom
  305. */
  306. Editor.focusOnBottom = function() {
  307. if (Editor.__PANEL_LOADED && Editor.__EDITOR_LOADED) {
  308. var _canvas = this.getCanvas();
  309. if (_canvas) {
  310. _canvas.focusOnBottom();
  311. }
  312. } else {
  313. setTimeout(this.focusOnBottom.bind(this), 10);
  314. }
  315. return _FALSE;
  316. };
  317. /**
  318. * Editor가 있는 page를 나갈 경우 beforeunload eventlistener를 실행 시키지 도록 설정한다.
  319. * 예를들면, Editor에서 글을 작성 중에 새로고침했을 경우 경고창을 안뜨게 한다.
  320. */
  321. Editor.permitUnload = function() {
  322. if (Editor.__PANEL_LOADED && Editor.__EDITOR_LOADED) {
  323. this.setDisableUnloadHandler();
  324. } else {
  325. setTimeout(this.permitUnload.bind(this), 500);
  326. }
  327. };
  328. /**
  329. * Editor와 Iframe이 정상적으로 생성 된후 argument로 지정된 function을 실행 시킨다.
  330. * @param {Function} fn
  331. * @example
  332. * Editor.onPanelLoadComplete(function(){
  333. * Editor.focus();
  334. * });
  335. */
  336. Editor.onPanelLoadComplete = function(fn) {
  337. if (Editor.__PANEL_LOADED == _TRUE && Editor.__EDITOR_LOADED == _TRUE) {
  338. if (fn) {
  339. fn();
  340. }
  341. } else {
  342. setTimeout(Editor.onPanelLoadComplete.bind(Editor, fn), 500);
  343. }
  344. };
  345. /**
  346. * 동일한 Page에 Editor가 여러개 생성됬을 경우, 다른 Editor를 지정한다.
  347. * @param {Object} toIndex
  348. */
  349. Editor.switchEditor = function (toIndex) {
  350. Editor.__SELECTED_INDEX = toIndex;
  351. Object.extend(Editor, Editor.__MULTI_LIST[toIndex]);
  352. };
  353. /* 에디터가 여러개 있을 때 async로 불러오는 모듈에서 호출하는 에디터를 찾기 위함. */
  354. Editor.editorForAsyncLoad = Editor;
  355. /* 에디터가 여러개 있을 때 모든 에디터에 적용하기 위함 */
  356. Editor.forEachEditor = function (fn) {
  357. var indexName, list= Editor.__MULTI_LIST;
  358. for (indexName in list) {
  359. if (list.hasOwnProperty(indexName)) {
  360. fn(list[indexName]);
  361. }
  362. }
  363. };
  364. /**
  365. * focus on form
  366. * @param {String} name - focus를 줄 form의 name 속성 값
  367. * @example
  368. * Editor.focusOnForm("tx_article_title");
  369. */
  370. Editor.focusOnForm = function(name) {
  371. if (Editor.__PANEL_LOADED && Editor.__EDITOR_LOADED) {
  372. _WIN.focus();
  373. var _form = Editor.getForm();
  374. if (_form.getElementByName(name)) {
  375. _form.getElementByName(name).focus();
  376. }
  377. } else {
  378. setTimeout(Editor.focusOnForm.bind(Editor, name), 500);
  379. }
  380. return _FALSE;
  381. };
  382. /**
  383. * 파일함에서 export된 데이터를 에디터에 삽입한다. attachment만 삽입된다.
  384. * @param {Object} data - 에디트에 로드할 내용/첨부파일 값
  385. * @example
  386. * Editor.fromHdrive(
  387. [{attacher: 'image',
  388. * data: {
  389. * thumburl: "http://cfile163.uf.daum.net/P150x100/0126A20248BFAFF72D2229",
  390. * imageurl: "http://cfile163.uf.daum.net/image/0126A20248BFAFF72D2229",
  391. * originalurl: "http://cfile163.uf.daum.net/original/0126A20248BFAFF72D2229",
  392. * exifurl: "http://cfile163.uf.daum.net/info/0126A20248BFAFF72D2229",
  393. * attachurl: "http://cfile163.uf.daum.net/attach/0126A20248BFAFF72D2229",
  394. * filename: "Tree.jpg",
  395. * filesize: "155833"
  396. * }
  397. * }]
  398. */
  399. Editor.fromHdrive = function(data) {
  400. var attachments = [];
  401. for (var i = 0; i < data.length; i++) {
  402. attachments.push(data[i]);
  403. }
  404. var modifyData = {
  405. content: "",
  406. attachments: attachments
  407. };
  408. if (Editor.__PANEL_LOADED && Editor.__EDITOR_LOADED) {
  409. if (this.loadEditor) {
  410. this.loadEditor(modifyData);
  411. var _entries = Editor.getAttachBox().datalist;
  412. for (var j = 0; j < _entries.length; j++) {
  413. _entries[j].execAppend();
  414. }
  415. }
  416. } else {
  417. setTimeout(this.fromHdrive.bind(this, data), 10);
  418. }
  419. };
  420. Editor.refreshSize = function () {
  421. this.canvas.fireJobs(Trex.Ev.__CANVAS_WRAP_WIDTH_CHANGE);
  422. //TODO.azki height..???
  423. };
  424. /**
  425. * <b>deprecated</b> - use Editor.switchEditor, 동일한 Page에 Editor가 여러개 생성됬을 경우, 다른 Editor를 지정한다.
  426. * @function
  427. * @deprecated since ver 1.2, use Editor.switchEditor
  428. */
  429. Editor.prototype.switchEditor = Editor.switchEditor;
  430. /**
  431. * <b>deprecated</b> - use Editor.focusOnForm, focus on form
  432. * @function
  433. * @deprecated since ver 1.2, Use Editor.focusOnForm
  434. */
  435. Editor.prototype.focusOnForm = Editor.focusOnForm;
  436. })();