/** * @fileoverview * - Trex.Toolbar */ /** * Trex.Toolbar Class * @class * @param {Object} editor * @param {Object} config */ Trex.Toolbar = Trex.Class.create(/** @lends Trex.Toolbar.prototype */{ /** @ignore */ $mixins: [ Trex.I.JobObservable ], /** * Toolbar Dom Element */ el: _NULL, /** * Tools List */ tools: _NULL, initialize: function(editor, rootConfig) { this.canvas = editor.getCanvas(); var _initializedId = rootConfig.initializedId || ""; this.el = $must("tx_toolbar_basic" + _initializedId, "Trex.Toolbar"); }, /** * Toolbar의 tool을 비활성화 시킨다. * @function * @example * Editor.getToolbar().disableToolbar(); */ disableToolbar: function(){ var _tools = this.tools; for (var _name in _tools) { if (_tools[_name].button) { _tools[_name].button.disable(); } } }, /** * 현재 toolbar의 상태를 serializing한다. * @function * @returns {object} */ serializeToolValues : function(){ var _tools = this.tools; var result = {}; for(var name in _tools){ var _tool = _tools[name]; result[name] = _tool.button.lastValue; } return result; }, widgetSeq:0, makeWidget: function(button, menu, handler) { var _toolbar = this; var _canvas = this.canvas; var _dummyToolClass = new (function() { this.identity = 'widget' + (++_toolbar.widgetSeq); this.wysiwygonly = _TRUE; this.menuFoldAuto = _TRUE; this.canvas = _canvas; this.toolbar = _toolbar; })(); Trex.Tool.prototype.weave.bind(_dummyToolClass)( button, menu, handler ); this.tools[_dummyToolClass.identity] = _dummyToolClass; return _dummyToolClass; } }); Trex.install("editor.getTool", function(editor, toolbar) { var _tools = toolbar.tools = {}; /** * memberOf Editor.prototype * @param {Object} name */ editor.getTool = function(name) { if(_tools[name] != _NULL) { return _tools[name]; } else if(arguments.length == 0){ return _tools; }else{ return _NULL; } }; } ); Trex.register("new tools", function(editor, toolbar, sidebar, canvas, config) { var _tools = toolbar.tools; var _initializedId = config.initializedId || ""; for(var item in Trex.Tool) { var _name = Trex.Tool[item]['__Identity']; if(_name){ var cfg = TrexConfig.getTool(_name, config); cfg.initializedId = _initializedId; if (Trex.available(cfg, _name + _initializedId)) { _tools[_name] = new Trex.Tool[item](editor, toolbar, cfg); } } } if(!!canvas.config.readonly) { toolbar.disableToolbar(); } } ); Trex.module("bind events with tools", function(editor, toolbar, sidebar, canvas) { var _tools = toolbar.tools; var disableToolOnMobile = function () { var isMobile, name, tool, btn; isMobile = $tx.ios || $tx.android; if (!isMobile) { return; } for (name in _tools) { tool = _tools[name]; if (tool.disabledonmobile) { btn = tool.button; btn.disable(); } } }; disableToolOnMobile(); var _changeMode = function(from, to){ if (from == to) { return; } for (var _name in _tools) { var _tool = _tools[_name]; var _btn = _tool.button; if (Trex.Canvas.__WYSIWYG_MODE == to) { _btn.enable(); } else if (Trex.Canvas.__WYSIWYG_MODE == from) { if (_tool.wysiwygonly) { _btn.disable(); } else { _btn.enable(); } } } disableToolOnMobile(); }; canvas.observeJob(Trex.Ev.__CANVAS_MODE_CHANGE, _changeMode); canvas.observeJob(Trex.Ev.__CANVAS_MODE_INITIALIZE, _changeMode); var _releaseTools = function(identity) { for(var _name in _tools) { var _tool = _tools[_name]; if(identity != _tool.identity) { if (_tool.button) { _tool.button.release(); _tool.button.decreaseZindex(); } if(_tool.menu && _tool.menuFoldAuto) { _tool.menu.release(); } } } }; canvas.observeJob(Trex.Ev.__CANVAS_PANEL_CLICK, _releaseTools); canvas.observeJob(Trex.Ev.__CANVAS_SOURCE_PANEL_CLICK, _releaseTools); canvas.observeJob(Trex.Ev.__CANVAS_TEXT_PANEL_CLICK, _releaseTools); toolbar.observeJob(Trex.Ev.__TOOL_CLICK, _releaseTools); canvas.observeKey({ // Esc ctrlKey: _FALSE, altKey: _FALSE, shiftKey: _FALSE, keyCode: 27 }, _releaseTools); editor.observeKey({ // Esc ctrlKey: _FALSE, altKey: _FALSE, shiftKey: _FALSE, keyCode: 27 }, _releaseTools); $tx.observe(_DOC, 'click', function(e){ var _el = $tx.element(e); var _class = [ 'tx-sidebar', 'tx-toolbar-basic' ,'tx-toolbar-advanced', 'tx-sidebar-boundary', 'tx-toolbar-boundary', 'tx-toolbar-boundary']; if (Trex.Util.getMatchedClassName(_el, _class)) { _releaseTools("-"); } } , _FALSE); var _shouldCloseMenus = function () { editor.fireJobs(Trex.Ev.__SHOULD_CLOSE_MENUS); }; toolbar.observeJob(Trex.Ev.__TOOL_CLICK, _shouldCloseMenus); } ); /** * Tool 클래스의 추상 부모클래스로 각각의 tool들은 이 클래스를 상속받아야 하고, * 'oninitialized' 함수를 구현해야한다. * * @abstract * @class * @param {Object} editor * @param {Object} toolbar * @param {Object} config * * @example * Trex.Tool.Example = Trex.Class.create({ * $const: { * __Identity: 'example' * }, * $extend: Trex.Tool, * oninitialized: function(config) { * var _tool = this; * * this.weave.bind(this)( * new Trex.Button(this.buttonCfg), * new Trex.Menu(this.menuCfg), * function(data) { * //TODO * } * ); * } * }); */ Trex.Tool = Trex.Class.draft(/** @lends Trex.Tool.prototype */{ /** * tool identifier. 유일해야한다. * @private */ identity: _NULL, /** * button 객체 */ button: _NULL, /** * menu 객체 */ menu: _NULL, initialize: function(editor, toolbar, config) { if(!this.constructor.__Identity) { throw new Error("[Exception]Trex.Tool : not implement const(__Identity)"); } this.identity = this.constructor.__Identity; if(!editor) { throw new Error("[Exception]Trex.Tool : not exist argument(editor)"); } /** * editor 객체 * @private */ this.editor = editor; /** * toolbar 객체 * @private */ this.toolbar = toolbar; /** * canvas 객체 * @private */ this.canvas = editor.getCanvas(); /** * 해당 tool 설정값 * @private */ this.config = config; this.wysiwygonly = ((config.wysiwygonly != _NULL)? config.wysiwygonly: _TRUE); this.menuFoldAuto = ((config.menuFoldAuto != _NULL)? config.menuFoldAuto: _TRUE); if (config.disabledonmobile != _NULL) { this.disabledonmobile = config.disabledonmobile; } /** * 버튼을 생성할 때 필요한 설정값 * @private */ this.buttonCfg = TrexConfig.merge({ id: "tx_" + this.identity }, config); /** * 메뉴를 생성할 때 필요한 설정값 * @private */ this.menuCfg = TrexConfig.merge({ id: "tx_" + this.identity + "_menu" }, config); this.oninitialized.bind(this)(config); }, /** * tool 객체를 초기화하는 마지막 단계에서 호출되는 함수로, * tool 클래스를 상속받는 tool에서 반드시 구현해야 한다. * * @abstract * @private * @function */ oninitialized: function() { throw new Error("[Exception]Trex.Tool : not implements function(oninitialized)"); }, /** * 보통 tool은 버튼과 메뉴로 구성되는데, 이 함수에서 그 둘 사이의 연결을 해준다.
* menu가 없으면 버튼을 클릭할 때 execHandler가 실행되고, * menu가 있으면 버튼을 클릭할 때 menu가 보이며, * menu에서 특정 값을 선택하면 그 값을 가지고 execHandler가 실행된다. * * @function * @private * @param {Object} button - 버튼 객체 * @param {Object} menu - 메뉴 객체 optional * @param {Function} execHandler * @param {Function} initHandler - optional * * @example * this.weave.bind(this)( * new Trex.Button(this.buttonCfg), * new Trex.Menu(this.menuCfg), * function(data) { * //TODO * }); * } */ weave: function(button, menu, execHandler, initHandler) { var _tool = this; var _identity = this.identity; var _toolbar = this.toolbar; var _canvas = this.canvas; this.button = button; button.tool = this; var cmd = _NULL; if(!menu){ button.setCommand( cmd = function(){ _toolbar.fireJobs(Trex.Ev.__TOOL_CLICK, _identity); return execHandler.apply(_tool, arguments); } ); }else{ this.menu = menu; menu.tool = this; menu.initHandler = initHandler || function(){}; menu.cancelHandler = function(){ button.setState(_FALSE); }; menu.setCommand( cmd = function() { var args = arguments; var success = execHandler.apply(_tool, args); //handler에서 $stop 을 반환하면 버튼 값을 메뉴에서 선택한 값으로 안바꿈.. if (success === $stop) { button.normalState.apply(button, args); } else { button.updateAfterCommand.apply(button, args); } return success; } ); button.setCommand( function(ev) { _toolbar.fireJobs(Trex.Ev.__TOOL_CLICK, _identity, ev); if(!button.isPushed()) { var _lastvalue = button.getValue(); button.increaseZindex(); menu.show(_lastvalue); } else { menu.hide(); if ($tx.msie) { var _processor = _canvas.getProcessor(); if (_processor.restoreRange) { setTimeout(function () { _processor.restoreRange(); }, 0); } } } return _TRUE; } ); menu.observeJob(Trex.Ev.__MENU_LAYER_SHOW, function(ev){ _toolbar.fireJobs(Trex.Ev.__MENU_LAYER_SHOW, ev); }); menu.observeJob(Trex.Ev.__MENU_LAYER_HIDE, function(ev){ _toolbar.fireJobs(Trex.Ev.__MENU_LAYER_HIDE, ev); }); menu.observeJob(Trex.Ev.__MENU_LAYER_CHANGE_SIZE, function(ev){ _toolbar.fireJobs(Trex.Ev.__MENU_LAYER_CHANGE_SIZE, ev); }); } this.execute = cmd; }, /** * 연결된 버튼과 메뉴 레이어와의 관계를 모두 해제한다. * 일반적은 경우에는 필요하지 않고 async를 위한 tool에만 weave 구문 상위에 추가한다. * @function */ resetWeave: function(){ this.button.removeHandler(); this.button.normalState(); this.button = _NULL; this.menu = _NULL; this.execute = _NULL; }, /** * 활성화 상태를 강제한다. * async tool 에서 클릭후 자동 활성화를 위함. * @function */ forceActivate: function(){ if( this.button && this.menu ){ this.button.pushedState(); this.button.increaseZindex(); this.menu.show(); } }, bindKeyboard: function(keys, execHandler) { var toolbar = this.toolbar; var identity = this.identity; this.canvas.observeKey(keys, function(ev) { execHandler(ev); toolbar.fireJobs(Trex.Ev.__TOOL_SHORTCUT_KEY, identity); }); } }); Trex.AsyncTool = Trex.Class.draft(/** @lends Trex.Tool.prototype */{ $extend: Trex.Tool, oninitialized: function() { this.loaded = false; throw new Error("[Exception]Trex.AsyncTool : not implements function(oninitialized)"); }, onLoadModule: function() { var self = this; var url = this.config.asyncUrl; if (/^(?:\/\/)|(?:\w+:\/\/)/.test(url) === false) { url = this.getJSBasePath() + url; } if (EditorJSLoader.getOption('environment') == 'development') { var d = (new Date()).getTime(); if (url.indexOf('?') === -1) { url += '?dummy=' + d; } else { url += '&dummy=' + d; } } Editor.editorForAsyncLoad = this.editor; EditorJSLoader.asyncLoadModule({ url: TrexConfig.getUrl(url), callback: function(){ self.loaded = true; } }); }, getJSBasePath: function() { var basePath; try { basePath = EditorJSLoader.getJSBasePath("editor.js"); } catch (e) { basePath = EditorJSLoader.getJSBasePath(); } return basePath; } }); Trex.I.Tool = {}; Trex.I.Tool.QueryStyle = {}; Trex.I.Tool.QueryStyle.Standard = Trex.Mixin.create({ queryNodeStyle: function(currentNode, cssPropertyName, queryCommandName, matchTagName) { return $tx.getStyle(currentNode, cssPropertyName).include(queryCommandName); } }); Trex.I.Tool.QueryStyle.Gecko = Trex.Mixin.create({ queryNodeStyle: function(currentNode, cssPropertyName, queryCommandName, matchTagName) { var tempNode = currentNode; var isInclude = _FALSE; while(tempNode && !$tom.isBody(tempNode) && !isInclude) { if ($tom.isTagName(tempNode, matchTagName)) { isInclude = _TRUE; } else { isInclude = $tx.getStyle(currentNode, cssPropertyName).include(queryCommandName); } // move to parent tempNode = tempNode.parentNode; } return isInclude; } });