UNPKG

kekule

Version:

Open source JavaScript toolkit for chemoinformatics

510 lines (495 loc) 15.5 kB
/** * @fileoverview * Implementation of an advanced plain text editor. * @author Partridge Jiang */ /* * requires /lan/classes.js * requires /utils/kekule.utils.js * requires /utils/kekule.domUtils.js * requires /xbrowsers/kekule.x.js * requires /widgets/kekule.widget.base.js * requires /widgets/kekule.widget.sys.js * requires /widgets/kekule.widget.styleResources.js * requires /widgets/commonCtrls/kekule.widget.formControls.js */ (function(){ "use strict"; var DU = Kekule.DomUtils; var EU = Kekule.HtmlElementUtils; var CNS = Kekule.Widget.HtmlClassNames; //var CWT = Kekule.WidgetTexts; /** @ignore */ Kekule.Widget.HtmlClassNames = Object.extend(Kekule.Widget.HtmlClassNames, { TEXTEDITOR: 'K-TextEditor', TEXTEDITOR_TOOLBAR: 'K-TextEditor-Toolbar', TEXTEDITOR_TEXTAREA: 'K-TextEditor-TextArea', TEXTEDITOR_FONTBOX: 'K-TextEditor-FontBox', TEXTEDITOR_BTN_FONTSIZEINC: 'K-TextEditor-Btn-FontSizeInc', TEXTEDITOR_BTN_FONTSIZEDEC: 'K-TextEditor-Btn-FontSizeDec', TEXTEDITOR_BTN_TEXTWRAP: 'K-TextEditor-Btn-TextWrap' }); /** * An widget to edit plain text. * @class * @augments Kekule.Widget.FormWidget * * @property {String} text Text in editor. * @property {Bool} readOnly * @property {String} wrap Wrap mode of textarea in editor, value between "soft", "hard" and "off". * @property {Int} autoWrapThreshold If this value is set, when the (total text length) / (text lines) > threshold, the wrap property will be automatically changed to 'soft' and 'off' vice versa. * Value true can also be set to this property to turn on this feature with the default threshold value. * @property {Bool} showToolbar Whether show toolbar in editor. * @property {Array} toolbarComponents Shown widgets in toolbar. * @property {Array} candidateFontFamilies Array of font names that may be shown in font family combo box in toolbar. * @property {Float} fontSizeLevel Size of text in editor. E.g., set this property value to 1.2 will add a 1.2em rule to textarea. */ /** * Invoked when the color value is selected. Event param of it has field: {value, colorClassName}. * @name Kekule.Widget.TextEditor#valueSet * @event */ Kekule.Widget.TextEditor = Class.create(Kekule.Widget.FormWidget, /** @lends Kekule.Widget.TextEditor# */ { /** @private */ CLASS_NAME: 'Kekule.Widget.TextEditor', /** @private */ BINDABLE_TAG_NAMES: ['div', 'span'], /** @private */ DEF_TOOLBAR_COMPONENTS: ['fontFamily', 'fontSizeInc', 'fontSizeDec', 'textWrap'], /** @ignore */ DEF_AUTOWRAP_THRESHOLD: 80, /** @constructs */ initialize: function(/*$super, */parentOrElementOrDocument) { this._toolCompWidgets = []; this.setPropStoreFieldValue('showToolbar', true); this.tryApplySuper('initialize', [parentOrElementOrDocument]) /* $super(parentOrElementOrDocument) */; }, /** @private */ initProperties: function() { this.defineProp('text', {'dataType': DataType.STRING, 'serializable': false, 'getter': function() { var textArea = this.getTextArea(); return textArea? textArea.getText(): null; }, 'setter': function(value) { var textArea = this.getTextArea(); if (textArea) { textArea.setText(value); this._checkAutoWrap(); } } }); this.defineProp('readOnly', {'dataType': DataType.BOOL, 'serializable': false, 'getter': function() { return this.getTextArea().getReadOnly(); }, 'setter': function(value) { return this.getTextArea().setReadOnly(value); } }); this.defineProp('wrap', {'dataType': DataType.STRING, 'serializable': false, 'getter': function() { return this.getTextArea().getWrap(); }, 'setter': function(value) { var result = this.getTextArea().setWrap(value); this.updateToolButtonStates(); return result; } }); this.defineProp('autoWrapThreshold', {'dataType': DataType.INT, 'setter': function(value) { var v = (value === true)? this.DEF_AUTOWRAP_THRESHOLD: value; this.setPropStoreFieldValue('autoWrapThreshold', v); this._checkAutoWrap(); } }); // private this.defineProp('textArea', {'dataType': 'Kekule.Widget.TextArea', 'serializable': false, 'setter': null, 'scope': Class.PropertyScope.PRIVATE, 'getter': function() { var result = this.getPropStoreFieldValue('textArea'); if (!result) { result = this.createTextArea(); this.setPropStoreFieldValue('textArea', result); } return result; } }); // private this.defineProp('toolbar', {'dataType': 'Kekule.Widget.Toolbar', 'serializable': false, 'setter': null, 'scope': Class.PropertyScope.PRIVATE, 'getter': function() { var result = this.getPropStoreFieldValue('toolbar'); if (!result) { if (this.getShowToolbar()) { result = this.createToolbar(); this.setPropStoreFieldValue('styleToolbar', result); } } return result; } }); this.defineProp('showToolbar', {'dataType': DataType.BOOL, 'setter': function(value) { this.setPropStoreFieldValue('showToolbar', value); var toolbar = this.getToolbar(); toolbar.setDisplayed(value); } }); this.defineProp('toolbarPos', {'dataType': DataType.INT, 'setter': function(value) { this.setPropStoreFieldValue('toolbarPos', value); this.updateChildWidgetPos(); } }); this.defineProp('toolbarComponents', {'dataType': DataType.ARRAY, 'serializable': false, 'getter': function() { var result = this.getPropStoreFieldValue('toolbarComponents'); if (!result) { result = null; // default one this.setPropStoreFieldValue('toolbarComponents', result); } return result; }, 'setter': function(value) { this.setPropStoreFieldValue('toolbarComponents', value); this.recreateToolbarComponents(value); } }); this.defineProp('candidateFontFamilies', {'dataType': DataType.ARRAY}); this.defineProp('fontSizeLevel', {'dataType': DataType.FLOAT, 'setter': function(value) { this.setPropStoreFieldValue('fontSizeLevel', value); this.setTextStyle({'fontSize': '' + value + 'em'}); } }); }, /** @ignore */ finalize: function(/*$super*/) { this._finalizeSubElements(); this.tryApplySuper('finalize') /* $super() */; }, /** @private */ _finalizeSubElements: function() { var textArea = this.getTextArea(); if (textArea) { textArea.finalize(); } }, /** @ignore */ getCoreElement: function(/*$super*/) { var textArea = this.getTextArea(); if (textArea) return textArea.getElement(); else return this.tryApplySuper('getCoreElement') /* $super() */; }, /** @ignore */ getChildrenHolderElement: function(/*$super*/) { return this.getElement(); }, /** @ignore */ doSetValue: function(/*$super, */value) { this.tryApplySuper('doSetValue', [value]) /* $super(value) */; this._checkAutoWrap(); }, /** @ignore */ doGetWidgetClassName: function(/*$super*/) { return this.tryApplySuper('doGetWidgetClassName') /* $super() */ + ' ' + CNS.TEXTEDITOR; }, /** @ignore */ doCreateRootElement: function(doc) { var result = doc.createElement('span'); return result; }, /** @ignore */ doCreateSubElements: function(doc, rootElem) { // toolbar element var toolbar = this.createToolbar(); if (toolbar) toolbar.appendToElem(rootElem); // text area element var textArea = this.createTextArea(); textArea.appendToElem(rootElem); this.updateChildWidgetPos(); return [toolbar.getElement(), textArea.getElement()]; }, /** @ignore */ doWidgetShowStateChanged: function(/*$super, */isShown) { this.tryApplySuper('doWidgetShowStateChanged', [isShown]) /* $super(isShown) */; if (isShown) { this.updateChildWidgetPos(); this.updateToolButtonStates(); } }, /** @ignore */ doFileDragDrop: function(/*$super, */files) { if (!files /* || files.length > 1 */) return this.tryApplySuper('doFileDragDrop') /* $super() */; else // if only one file is dropped in, output the file content { if (Kekule.BrowserFeature.fileapi) { var self = this; // try open it the file by FileReader var reader = new FileReader(); reader.onload = function(e) { var content = reader.result; self.setText(content); }; reader.readAsText(files[0]); } return true; } }, /** @private */ createToolbar: function() { if (this.getShowToolbar()) { var toolbar = new Kekule.Widget.Toolbar(this); this.setPropStoreFieldValue('toolbar', toolbar); toolbar.addClassName(CNS.TEXTEDITOR_TOOLBAR); this.recreateToolbarComponents(); //this.adjustAssocToolbarPositions(); return toolbar; } else { return null; } }, /** @private */ recreateToolbarComponents: function(comps) { if (!comps) comps = this.getToolbarComponents(); var components = comps || this.DEF_TOOLBAR_COMPONENTS; var toolbar = this.getToolbar(); if (toolbar) { this._toolCompWidgets.length = 0; toolbar.clearWidgets(); toolbar.setShowText(false); toolbar.setShowGlyph(true); var widget; for (var i = 0, l = components.length; i < l; ++i) { var comp = components[i]; if (comp === 'fontFamily') // font family list box widget = this.createFontFamilyComboBox(toolbar); else widget = this.createToolButton(toolbar, comp); this._toolCompWidgets[comp] = widget; } this.updateToolButtonStates(); } }, /** @private */ createFontFamilyComboBox: function(toolbar) { var result = new Kekule.Widget.ComboBox(toolbar); // fill fonts var fontFamilies = Kekule.Widget.FontEnumerator.getAvailableFontFamilies(this.getCandidateFontFamilies()); var boxItems = []; //[{'text': Kekule.ChemWidgetTexts.S_VALUE_DEFAULT, 'value': ''}]; for (var i = 0, l = fontFamilies.length; i < l; ++i) { boxItems.push({'text': fontFamilies[i], 'value': fontFamilies[i]}); } result.setHint(/*CWT.HINT_CHOOSE_FONT_FAMILY*/Kekule.$L('WidgetTexts.HINT_CHOOSE_FONT_FAMILY')); result.addClassName(CNS.TEXTEDITOR_FONTBOX); result.setItems(boxItems); result.appendToWidget(toolbar); result.addEventListener('valueChange', function(e){ this.setTextStyle({'fontFamily': result.getValue()}); }, this); return result; }, /** @private */ createToolButton: function(toolbar,btnName) { var caption, hint; var cssClassName; var btnClass = (btnName === 'textWrap')? Kekule.Widget.CheckButton: Kekule.Widget.Button; var result = new btnClass(toolbar); result.appendToWidget(toolbar); if (btnName === 'textWrap') { caption = Kekule.$L('WidgetTexts.CAPTION_TOGGLE_TEXTWRAP'); // CWT.CAPTION_TOGGLE_TEXTWRAP; hint = Kekule.$L('WidgetTexts.HINT_TOGGLE_TEXTWRAP'); // CWT.HINT_TOGGLE_TEXTWRAP; cssClassName = CNS.TEXTEDITOR_BTN_TEXTWRAP; result.setChecked(this.getWrap() !== 'off'); result.addEventListener('checkChange', function(e){ var wrap = result.getChecked(); this.setWrap(wrap? 'soft': 'off'); }, this); } else if (btnName === 'fontSizeInc') { caption = Kekule.$L('WidgetTexts.CAPTION_INC_TEXT_SIZE'); // CWT.CAPTION_INC_TEXT_SIZE; hint = Kekule.$L('WidgetTexts.HINT_INC_TEXT_SIZE'); // CWT.HINT_INC_TEXT_SIZE; cssClassName = CNS.TEXTEDITOR_BTN_FONTSIZEINC; result.addEventListener('execute', this.increaseTextSize, this); } else if (btnName === 'fontSizeDec') { caption = Kekule.$L('WidgetTexts.CAPTION_DEC_TEXT_SIZE'); //CWT.CAPTION_DEC_TEXT_SIZE; hint = Kekule.$L('WidgetTexts.HINT_DEC_TEXT_SIZE'); //CWT.HINT_DEC_TEXT_SIZE; cssClassName = CNS.TEXTEDITOR_BTN_FONTSIZEDEC; result.addEventListener('execute', this.decreaseTextSize, this); } result.setText(caption); result.setHint(hint); result.addClassName(cssClassName); return result; }, /** @private */ createToolCheckButton: function(toolbar,btnName) { var result = new Kekule.Widget.CheckButton(toolbar); result.appendToWidget(toolbar); return result; }, /** * Returns child widget in toolbar by component name. * @param {String} compName * @returns {Kekule.Widget.BaseWidget} */ getToolbarComponentWidget: function(compName) { return this._toolCompWidgets[compName]; }, /** @private */ createTextArea: function() { var result = new Kekule.Widget.TextArea(this); this.setPropStoreFieldValue('textArea', result); result.addClassName(CNS.TEXTEDITOR_TEXTAREA); result.addEventListener('valueChange', this._checkAutoWrap, this); result.addEventListener('valueInput', this._checkAutoWrap, this); return result; }, /** @private */ updateChildWidgetPos: function() { /* var toolbarHeight; var toolbar = this.getToolbar(); if (toolbar) { var toolbarBound = Kekule.HtmlElementUtils.getElemBoundingClientRect(toolbar.getElement()) || 0; toolbarHeight = toolbarBound.height || 0; } var textArea = this.getTextArea(); textArea.setStyleProperty('marginTop', toolbarHeight + 'px'); */ var toolbarPos = this.getToolbarPos(); var toolbarElem = this.getToolbar().getElement(); var textAreaElem = this.getTextArea().getElement(); var parentElem = toolbarElem.parentNode; if (toolbarPos === Kekule.Widget.Position.BOTTOM) { parentElem.insertBefore(textAreaElem, toolbarElem); } else // defaultly, toolbar on top { parentElem.insertBefore(toolbarElem, textAreaElem); } }, /** @private */ updateToolButtonStates: function() { //console.log('update state', this.getWrap()); var btn = this._toolCompWidgets['textWrap']; if (btn) btn.setChecked(this.getWrap() !== 'off'); }, /** * Set display style of text area in editor. * @param {Hash} cssStyles A hash of css styles, e.g. {'fontSize': '19px', 'fontFamily': 'arial'}. */ setTextStyle: function(cssStyles) { var textArea = this.getTextArea(); if (textArea) { var props = Kekule.ObjUtils.getOwnedFieldNames(cssStyles); for (var i = 0, l = props.length; i < l; ++i) { var p = props[i]; textArea.setStyleProperty(p, cssStyles[p]); } } return this; }, /** * Increase text size in editor. */ increaseTextSize: function() { var level = this.getFontSizeLevel() || 1; var newLevel = Kekule.ZoomUtils.getNextZoomInRatio(level); this.setFontSizeLevel(newLevel); }, /** * Decrease text size in editor. */ decreaseTextSize: function() { var level = this.getFontSizeLevel() || 1; var newLevel = Kekule.ZoomUtils.getNextZoomOutRatio(level); this.setFontSizeLevel(newLevel); }, /** @private */ _checkAutoWrap: function() { var autoWrapThreshold = this.getAutoWrapThreshold(); if (autoWrapThreshold) { var newWrap; var btnWrap = this.getToolbarComponentWidget('textWrap'); var currWrap = (this.getWrap() || '').toLowerCase(); var text = this.getValue() || ''; var lineCount = text.split('\n').length; var ratio = text.length / lineCount; btnWrap.setChecked(ratio > autoWrapThreshold); /* if (ratio > autoWrapThreshold) newWrap = (currWrap === 'off')? 'soft': null; else newWrap = (currWrap === 'soft')? 'off': null; if (newWrap) this.setWrap(newWrap); */ } } }); })();