toolbar.js 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514
  1. /**
  2. * @fileoverview
  3. * - Trex.Toolbar
  4. */
  5. /**
  6. * Trex.Toolbar Class
  7. * @class
  8. * @param {Object} editor
  9. * @param {Object} config
  10. */
  11. Trex.Toolbar = Trex.Class.create(/** @lends Trex.Toolbar.prototype */{
  12. /** @ignore */
  13. $mixins: [
  14. Trex.I.JobObservable
  15. ],
  16. /**
  17. * Toolbar Dom Element
  18. */
  19. el: _NULL,
  20. /**
  21. * Tools List
  22. */
  23. tools: _NULL,
  24. initialize: function(editor, rootConfig) {
  25. this.canvas = editor.getCanvas();
  26. var _initializedId = rootConfig.initializedId || "";
  27. this.el = $must("tx_toolbar_basic" + _initializedId, "Trex.Toolbar");
  28. },
  29. /**
  30. * Toolbar의 tool을 비활성화 시킨다.
  31. * @function
  32. * @example
  33. * Editor.getToolbar().disableToolbar();
  34. */
  35. disableToolbar: function(){
  36. var _tools = this.tools;
  37. for (var _name in _tools) {
  38. if (_tools[_name].button) {
  39. _tools[_name].button.disable();
  40. }
  41. }
  42. },
  43. /**
  44. * 현재 toolbar의 상태를 serializing한다.
  45. * @function
  46. * @returns {object}
  47. */
  48. serializeToolValues : function(){
  49. var _tools = this.tools;
  50. var result = {};
  51. for(var name in _tools){
  52. var _tool = _tools[name];
  53. result[name] = _tool.button.lastValue;
  54. }
  55. return result;
  56. },
  57. widgetSeq:0,
  58. makeWidget: function(button, menu, handler) {
  59. var _toolbar = this;
  60. var _canvas = this.canvas;
  61. var _dummyToolClass = new (function() {
  62. this.identity = 'widget' + (++_toolbar.widgetSeq);
  63. this.wysiwygonly = _TRUE;
  64. this.menuFoldAuto = _TRUE;
  65. this.canvas = _canvas;
  66. this.toolbar = _toolbar;
  67. })();
  68. Trex.Tool.prototype.weave.bind(_dummyToolClass)(
  69. button,
  70. menu,
  71. handler
  72. );
  73. this.tools[_dummyToolClass.identity] = _dummyToolClass;
  74. return _dummyToolClass;
  75. }
  76. });
  77. Trex.install("editor.getTool",
  78. function(editor, toolbar) {
  79. var _tools = toolbar.tools = {};
  80. /**
  81. * memberOf Editor.prototype
  82. * @param {Object} name
  83. */
  84. editor.getTool = function(name) {
  85. if(_tools[name] != _NULL) {
  86. return _tools[name];
  87. } else if(arguments.length == 0){
  88. return _tools;
  89. }else{
  90. return _NULL;
  91. }
  92. };
  93. }
  94. );
  95. Trex.register("new tools",
  96. function(editor, toolbar, sidebar, canvas, config) {
  97. var _tools = toolbar.tools;
  98. var _initializedId = config.initializedId || "";
  99. for(var item in Trex.Tool) {
  100. var _name = Trex.Tool[item]['__Identity'];
  101. if(_name){
  102. var cfg = TrexConfig.getTool(_name, config);
  103. cfg.initializedId = _initializedId;
  104. if (Trex.available(cfg, _name + _initializedId)) {
  105. _tools[_name] = new Trex.Tool[item](editor, toolbar, cfg);
  106. }
  107. }
  108. }
  109. if(!!canvas.config.readonly) {
  110. toolbar.disableToolbar();
  111. }
  112. }
  113. );
  114. Trex.module("bind events with tools",
  115. function(editor, toolbar, sidebar, canvas) {
  116. var _tools = toolbar.tools;
  117. var disableToolOnMobile = function () {
  118. var isMobile, name, tool, btn;
  119. isMobile = $tx.ios || $tx.android;
  120. if (!isMobile) {
  121. return;
  122. }
  123. for (name in _tools) {
  124. tool = _tools[name];
  125. if (tool.disabledonmobile) {
  126. btn = tool.button;
  127. btn.disable();
  128. }
  129. }
  130. };
  131. disableToolOnMobile();
  132. var _changeMode = function(from, to){
  133. if (from == to) {
  134. return;
  135. }
  136. for (var _name in _tools) {
  137. var _tool = _tools[_name];
  138. var _btn = _tool.button;
  139. if (Trex.Canvas.__WYSIWYG_MODE == to) {
  140. _btn.enable();
  141. } else if (Trex.Canvas.__WYSIWYG_MODE == from) {
  142. if (_tool.wysiwygonly) {
  143. _btn.disable();
  144. } else {
  145. _btn.enable();
  146. }
  147. }
  148. }
  149. disableToolOnMobile();
  150. };
  151. canvas.observeJob(Trex.Ev.__CANVAS_MODE_CHANGE, _changeMode);
  152. canvas.observeJob(Trex.Ev.__CANVAS_MODE_INITIALIZE, _changeMode);
  153. var _releaseTools = function(identity) {
  154. for(var _name in _tools) {
  155. var _tool = _tools[_name];
  156. if(identity != _tool.identity) {
  157. if (_tool.button) {
  158. _tool.button.release();
  159. _tool.button.decreaseZindex();
  160. }
  161. if(_tool.menu && _tool.menuFoldAuto) {
  162. _tool.menu.release();
  163. }
  164. }
  165. }
  166. };
  167. canvas.observeJob(Trex.Ev.__CANVAS_PANEL_CLICK, _releaseTools);
  168. canvas.observeJob(Trex.Ev.__CANVAS_SOURCE_PANEL_CLICK, _releaseTools);
  169. canvas.observeJob(Trex.Ev.__CANVAS_TEXT_PANEL_CLICK, _releaseTools);
  170. toolbar.observeJob(Trex.Ev.__TOOL_CLICK, _releaseTools);
  171. canvas.observeKey({ // Esc
  172. ctrlKey: _FALSE,
  173. altKey: _FALSE,
  174. shiftKey: _FALSE,
  175. keyCode: 27
  176. }, _releaseTools);
  177. editor.observeKey({ // Esc
  178. ctrlKey: _FALSE,
  179. altKey: _FALSE,
  180. shiftKey: _FALSE,
  181. keyCode: 27
  182. }, _releaseTools);
  183. $tx.observe(_DOC, 'click',
  184. function(e){
  185. var _el = $tx.element(e);
  186. var _class = [ 'tx-sidebar', 'tx-toolbar-basic' ,'tx-toolbar-advanced',
  187. 'tx-sidebar-boundary', 'tx-toolbar-boundary', 'tx-toolbar-boundary'];
  188. if (Trex.Util.getMatchedClassName(_el, _class)) {
  189. _releaseTools("-");
  190. }
  191. }
  192. , _FALSE);
  193. var _shouldCloseMenus = function () {
  194. editor.fireJobs(Trex.Ev.__SHOULD_CLOSE_MENUS);
  195. };
  196. toolbar.observeJob(Trex.Ev.__TOOL_CLICK, _shouldCloseMenus);
  197. }
  198. );
  199. /**
  200. * Tool 클래스의 추상 부모클래스로 각각의 tool들은 이 클래스를 상속받아야 하고,
  201. * 'oninitialized' 함수를 구현해야한다.
  202. *
  203. * @abstract
  204. * @class
  205. * @param {Object} editor
  206. * @param {Object} toolbar
  207. * @param {Object} config
  208. *
  209. * @example
  210. * Trex.Tool.Example = Trex.Class.create({
  211. * $const: {
  212. * __Identity: 'example'
  213. * },
  214. * $extend: Trex.Tool,
  215. * oninitialized: function(config) {
  216. * var _tool = this;
  217. *
  218. * this.weave.bind(this)(
  219. * new Trex.Button(this.buttonCfg),
  220. * new Trex.Menu(this.menuCfg),
  221. * function(data) {
  222. * //TODO
  223. * }
  224. * );
  225. * }
  226. * });
  227. */
  228. Trex.Tool = Trex.Class.draft(/** @lends Trex.Tool.prototype */{
  229. /**
  230. * tool identifier. 유일해야한다.
  231. * @private
  232. */
  233. identity: _NULL,
  234. /**
  235. * button 객체
  236. */
  237. button: _NULL,
  238. /**
  239. * menu 객체
  240. */
  241. menu: _NULL,
  242. initialize: function(editor, toolbar, config) {
  243. if(!this.constructor.__Identity) {
  244. throw new Error("[Exception]Trex.Tool : not implement const(__Identity)");
  245. }
  246. this.identity = this.constructor.__Identity;
  247. if(!editor) {
  248. throw new Error("[Exception]Trex.Tool : not exist argument(editor)");
  249. }
  250. /**
  251. * editor 객체
  252. * @private
  253. */
  254. this.editor = editor;
  255. /**
  256. * toolbar 객체
  257. * @private
  258. */
  259. this.toolbar = toolbar;
  260. /**
  261. * canvas 객체
  262. * @private
  263. */
  264. this.canvas = editor.getCanvas();
  265. /**
  266. * 해당 tool 설정값
  267. * @private
  268. */
  269. this.config = config;
  270. this.wysiwygonly = ((config.wysiwygonly != _NULL)? config.wysiwygonly: _TRUE);
  271. this.menuFoldAuto = ((config.menuFoldAuto != _NULL)? config.menuFoldAuto: _TRUE);
  272. if (config.disabledonmobile != _NULL) {
  273. this.disabledonmobile = config.disabledonmobile;
  274. }
  275. /**
  276. * 버튼을 생성할 때 필요한 설정값
  277. * @private
  278. */
  279. this.buttonCfg = TrexConfig.merge({
  280. id: "tx_" + this.identity
  281. }, config);
  282. /**
  283. * 메뉴를 생성할 때 필요한 설정값
  284. * @private
  285. */
  286. this.menuCfg = TrexConfig.merge({
  287. id: "tx_" + this.identity + "_menu"
  288. }, config);
  289. this.oninitialized.bind(this)(config);
  290. },
  291. /**
  292. * tool 객체를 초기화하는 마지막 단계에서 호출되는 함수로,
  293. * tool 클래스를 상속받는 tool에서 반드시 구현해야 한다.
  294. *
  295. * @abstract
  296. * @private
  297. * @function
  298. */
  299. oninitialized: function() {
  300. throw new Error("[Exception]Trex.Tool : not implements function(oninitialized)");
  301. },
  302. /**
  303. * 보통 tool은 버튼과 메뉴로 구성되는데, 이 함수에서 그 둘 사이의 연결을 해준다.<br/>
  304. * menu가 없으면 버튼을 클릭할 때 execHandler가 실행되고,
  305. * menu가 있으면 버튼을 클릭할 때 menu가 보이며,
  306. * menu에서 특정 값을 선택하면 그 값을 가지고 execHandler가 실행된다.
  307. *
  308. * @function
  309. * @private
  310. * @param {Object} button - 버튼 객체
  311. * @param {Object} menu - 메뉴 객체 optional
  312. * @param {Function} execHandler
  313. * @param {Function} initHandler - optional
  314. *
  315. * @example
  316. * this.weave.bind(this)(
  317. * new Trex.Button(this.buttonCfg),
  318. * new Trex.Menu(this.menuCfg),
  319. * function(data) {
  320. * //TODO
  321. * });
  322. * }
  323. */
  324. weave: function(button, menu, execHandler, initHandler) {
  325. var _tool = this;
  326. var _identity = this.identity;
  327. var _toolbar = this.toolbar;
  328. var _canvas = this.canvas;
  329. this.button = button;
  330. button.tool = this;
  331. var cmd = _NULL;
  332. if(!menu){
  333. button.setCommand(
  334. cmd = function(){
  335. _toolbar.fireJobs(Trex.Ev.__TOOL_CLICK, _identity);
  336. return execHandler.apply(_tool, arguments);
  337. }
  338. );
  339. }else{
  340. this.menu = menu;
  341. menu.tool = this;
  342. menu.initHandler = initHandler || function(){};
  343. menu.cancelHandler = function(){ button.setState(_FALSE); };
  344. menu.setCommand(
  345. cmd = function() {
  346. var args = arguments;
  347. var success = execHandler.apply(_tool, args);
  348. //handler에서 $stop 을 반환하면 버튼 값을 메뉴에서 선택한 값으로 안바꿈..
  349. if (success === $stop) {
  350. button.normalState.apply(button, args);
  351. } else {
  352. button.updateAfterCommand.apply(button, args);
  353. }
  354. return success;
  355. }
  356. );
  357. button.setCommand(
  358. function(ev) {
  359. _toolbar.fireJobs(Trex.Ev.__TOOL_CLICK, _identity, ev);
  360. if(!button.isPushed()) {
  361. var _lastvalue = button.getValue();
  362. button.increaseZindex();
  363. menu.show(_lastvalue);
  364. } else {
  365. menu.hide();
  366. if ($tx.msie) {
  367. var _processor = _canvas.getProcessor();
  368. if (_processor.restoreRange) {
  369. setTimeout(function () {
  370. _processor.restoreRange();
  371. }, 0);
  372. }
  373. }
  374. }
  375. return _TRUE;
  376. }
  377. );
  378. menu.observeJob(Trex.Ev.__MENU_LAYER_SHOW, function(ev){
  379. _toolbar.fireJobs(Trex.Ev.__MENU_LAYER_SHOW, ev);
  380. });
  381. menu.observeJob(Trex.Ev.__MENU_LAYER_HIDE, function(ev){
  382. _toolbar.fireJobs(Trex.Ev.__MENU_LAYER_HIDE, ev);
  383. });
  384. menu.observeJob(Trex.Ev.__MENU_LAYER_CHANGE_SIZE, function(ev){
  385. _toolbar.fireJobs(Trex.Ev.__MENU_LAYER_CHANGE_SIZE, ev);
  386. });
  387. }
  388. this.execute = cmd;
  389. },
  390. /**
  391. * 연결된 버튼과 메뉴 레이어와의 관계를 모두 해제한다.
  392. * 일반적은 경우에는 필요하지 않고 async를 위한 tool에만 weave 구문 상위에 추가한다.
  393. * @function
  394. */
  395. resetWeave: function(){
  396. this.button.removeHandler();
  397. this.button.normalState();
  398. this.button = _NULL;
  399. this.menu = _NULL;
  400. this.execute = _NULL;
  401. },
  402. /**
  403. * 활성화 상태를 강제한다.
  404. * async tool 에서 클릭후 자동 활성화를 위함.
  405. * @function
  406. */
  407. forceActivate: function(){
  408. if( this.button && this.menu ){
  409. this.button.pushedState();
  410. this.button.increaseZindex();
  411. this.menu.show();
  412. }
  413. },
  414. bindKeyboard: function(keys, execHandler) {
  415. var toolbar = this.toolbar;
  416. var identity = this.identity;
  417. this.canvas.observeKey(keys, function(ev) {
  418. execHandler(ev);
  419. toolbar.fireJobs(Trex.Ev.__TOOL_SHORTCUT_KEY, identity);
  420. });
  421. }
  422. });
  423. Trex.AsyncTool = Trex.Class.draft(/** @lends Trex.Tool.prototype */{
  424. $extend: Trex.Tool,
  425. oninitialized: function() {
  426. this.loaded = false;
  427. throw new Error("[Exception]Trex.AsyncTool : not implements function(oninitialized)");
  428. },
  429. onLoadModule: function() {
  430. var self = this;
  431. var url = this.config.asyncUrl;
  432. if (/^(?:\/\/)|(?:\w+:\/\/)/.test(url) === false) {
  433. url = this.getJSBasePath() + url;
  434. }
  435. if (EditorJSLoader.getOption('environment') == 'development') {
  436. var d = (new Date()).getTime();
  437. if (url.indexOf('?') === -1) {
  438. url += '?dummy=' + d;
  439. } else {
  440. url += '&dummy=' + d;
  441. }
  442. }
  443. Editor.editorForAsyncLoad = this.editor;
  444. EditorJSLoader.asyncLoadModule({
  445. url: TrexConfig.getUrl(url),
  446. callback: function(){
  447. self.loaded = true;
  448. }
  449. });
  450. },
  451. getJSBasePath: function() {
  452. var basePath;
  453. try {
  454. basePath = EditorJSLoader.getJSBasePath("editor.js");
  455. } catch (e) {
  456. basePath = EditorJSLoader.getJSBasePath();
  457. }
  458. return basePath;
  459. }
  460. });
  461. Trex.I.Tool = {};
  462. Trex.I.Tool.QueryStyle = {};
  463. Trex.I.Tool.QueryStyle.Standard = Trex.Mixin.create({
  464. queryNodeStyle: function(currentNode, cssPropertyName, queryCommandName, matchTagName) {
  465. return $tx.getStyle(currentNode, cssPropertyName).include(queryCommandName);
  466. }
  467. });
  468. Trex.I.Tool.QueryStyle.Gecko = Trex.Mixin.create({
  469. queryNodeStyle: function(currentNode, cssPropertyName, queryCommandName, matchTagName) {
  470. var tempNode = currentNode;
  471. var isInclude = _FALSE;
  472. while(tempNode && !$tom.isBody(tempNode) && !isInclude) {
  473. if ($tom.isTagName(tempNode, matchTagName)) {
  474. isInclude = _TRUE;
  475. } else {
  476. isInclude = $tx.getStyle(currentNode, cssPropertyName).include(queryCommandName);
  477. }
  478. // move to parent
  479. tempNode = tempNode.parentNode;
  480. }
  481. return isInclude;
  482. }
  483. });