/**
* @fileoverview
* Implements an editor with essential UI.
* @author Partridge Jiang
*/
/*
* requires /lan/classes.js
* requires /utils/kekule.utils.js
* requires /render/kekule.render.utils.js
* requires /widgets/kekule.widget.base.js
* requires /widgets/kekule.widget.helpers.js
* requires /widgets/chem/kekule.chemWidget.base.js
* requires /widgets/operation/kekule.actions.js
* requires /widgets/commonCtrls/kekule.widget.buttons.js
* requires /widgets/commonCtrls/kekule.widget.containers.js
* requires /widgets/advCtrls/objInspector/kekule.widget.objInspectors.js
* requires /widgets/chem/structureTreeView/kekule.chemWidget.structureTreeViews.js
* requires /widgets/chem/editor/kekule.chemEditor.baseEditors.js
* requires /widgets/chem/editor/kekule.chemEditor.nexus.js
* requires /widgets/chem/editor/kekule.chemEditor.objModifiers.js
* requires /localization
*/
(function(){
var PS = Class.PropertyScope;
var AU = Kekule.ArrayUtils;
var CW = Kekule.ChemWidget;
var CE = Kekule.Editor;
var CNS = Kekule.Widget.HtmlClassNames;
var CCNS = Kekule.ChemWidget.HtmlClassNames;
var BNS = Kekule.ChemWidget.ComponentWidgetNames;
//var CWT = Kekule.ChemWidgetTexts;
/** @ignore */
Kekule.ChemWidget.HtmlClassNames = Object.extend(Kekule.ChemWidget.HtmlClassNames, {
COMPOSER: 'K-Chem-Composer',
COMPOSER_EDITOR_STAGE: 'K-Chem-Composer-Editor-Stage',
COMPOSER_ADV_PANEL: 'K-Chem-Composer-Adv-Panel',
COMPOSER_TOP_REGION: 'K-Chem-Composer-Top-Region',
COMPOSER_LEFT_REGION: 'K-Chem-Composer-Left-Region',
COMPOSER_BOTTOM_REGION: 'K-Chem-Composer-Bottom-Region',
COMPOSER_TOOLBAR: 'K-Chem-Composer-Toolbar',
COMPOSER_COMMON_TOOLBAR: 'K-Chem-Composer-Common-Toolbar',
COMPOSER_ZOOM_TOOLBAR: 'K-Chem-Composer-Zoom-Toolbar',
COMPOSER_CHEM_TOOLBAR: 'K-Chem-Composer-Chem-Toolbar',
COMPOSER_ASSOC_TOOLBAR: 'K-Chem-Composer-Assoc-Toolbar',
COMPOSER_STYLE_TOOLBAR: 'K-Chem-Composer-Style-Toolbar',
COMPOSER_OBJMODIFIER_TOOLBAR: 'K-Chem-Composer-ObjModifier-Toolbar',
COMPOSER_FONTNAME_BOX: 'K-Chem-Composer-FontName-Box',
COMPOSER_FONTSIZE_BOX: 'K-Chem-Composer-FontSize-Box',
COMPOSER_COLOR_BOX: 'K-Chem-Composer-Color-Box',
COMPOSER_TEXTDIRECTION_BUTTON: 'K-Chem-Composer-TextDirection-Button',
COMPOSER_TEXTDIRECTION_BUTTON_DEFAULT: 'K-Chem-Composer-TextDirection-Button-Default',
COMPOSER_TEXTDIRECTION_BUTTON_LTR: 'K-Chem-Composer-TextDirection-Button-LTR',
COMPOSER_TEXTDIRECTION_BUTTON_RTL: 'K-Chem-Composer-TextDirection-Button-RTL',
COMPOSER_TEXTDIRECTION_BUTTON_TTB: 'K-Chem-Composer-TextDirection-Button-TTB',
COMPOSER_TEXTDIRECTION_BUTTON_BTT: 'K-Chem-Composer-TextDirection-Button-BTT',
COMPOSER_TEXTALIGN_BUTTON: 'K-Chem-Composer-TextAlign-Button',
COMPOSER_TEXTALIGN_BUTTON_HORIZONTAL: 'K-Chem-Composer-TextAlign-Button-Horizontal',
COMPOSER_TEXTALIGN_BUTTON_VERTICAL: 'K-Chem-Composer-TextAlign-Button-Vertical',
COMPOSER_TEXTALIGN_BUTTON_DEFAULT: 'K-Chem-Composer-TextAlign-Button',
COMPOSER_TEXTALIGN_BUTTON_LEADING: 'K-Chem-Composer-TextAlign-Button-Leading',
COMPOSER_TEXTALIGN_BUTTON_TRAILING: 'K-Chem-Composer-TextAlign-Button-Trailing',
COMPOSER_TEXTALIGN_BUTTON_CENTER: 'K-Chem-Composer-TextAlign-Button-Center',
COMPOSER_TEXTALIGN_BUTTON_LEFT: 'K-Chem-Composer-TextAlign-Button-Left',
COMPOSER_TEXTALIGN_BUTTON_RIGHT: 'K-Chem-Composer-TextAlign-Right',
COMPOSER_TEXTALIGN_BUTTON_TOP: 'K-Chem-Composer-TextAlign-Top',
COMPOSER_TEXTALIGN_BUTTON_BOTTOM: 'K-Chem-Composer-TextAlign-Bottom',
COMPOSER_DIALOG: 'K-Chem-ComposerDialog', //'K-Chem-Viewer-Assoc-Editor'
COMPOSER_FRAME: 'K-Chem-ComposerFrame',
COMPOSER_FRAME_CONTENT_DOC: 'K-Chem-ComposerFrame-ContentDoc',
COMPOSER_FRAME_CONTENT_BODY: 'K-Chem-ComposerFrame-ContentBody'
});
Kekule.globalOptions.add('chemWidget.composer', {
commonToolButtons: [
BNS.newDoc,
//BNS.loadFile,
BNS.loadData,
BNS.saveData,
BNS.undo,
BNS.redo,
BNS.copy,
BNS.cut,
BNS.paste,
//BNS.cloneSelection,
BNS.zoomIn,
//BNS.reset,
//BNS.resetZoom,
BNS.zoomOut,
BNS.config,
BNS.objInspector
],
chemToolButtons: [
BNS.manipulate,
BNS.erase,
BNS.molBond,
//BNS.molAtom,
//BNS.molFormula,
BNS.molAtomAndFormula,
BNS.molRing,
BNS.molCharge,
BNS.glyph,
BNS.textImage
],
styleToolComponentNames: [
BNS.fontName,
BNS.fontSize,
BNS.color,
BNS.textDirection,
BNS.textAlign
]
});
/**
* The style toolbar for composer.
* @class
* @augments Kekule.Widget.Toolbar
* @param {Kekule.Editor.Composer} composer Parent composer.
*
* @property {Kekule.Editor.Composer} composer Parent composer.
* @property {String} fontName Font name setted in toolbar.
* @property {Number} fontSize Font size setted in toolbar, in px.
* @property {Int} textAlign Text align setted in toolbar.
*
* @property {Array} components Array of component names that shows in tool bar.
*/
Kekule.Editor.ComposerStyleToolbar = Class.create(Kekule.Widget.Toolbar,
/** @lends Kekule.Editor.ComposerStyleToolbar# */
{
/** @private */
CLASS_NAME: 'Kekule.Editor.ComposerStyleToolbar',
/**
* @private
* @ignore
*/
ATOM_COLOR_SPECIAL_VALUE_INFO: {
text: Kekule.$L('ChemWidgetTexts.HINT_USE_ATOM_CUSTOM_COLOR'),
value: 'Atom',
className: CNS.COLORPICKER_SPEC_COLOR_MIXED
},
/** @private */
LINKED_VALUE_FIELD: '__$value__',
/** @constructs */
initialize: function($super, composer)
{
$super(composer);
this.setPropStoreFieldValue('composer', composer);
this.createChildWidgets();
this.appendToWidget(composer);
this._relatedPropNames = (composer.getCoordMode() === Kekule.CoordMode.COORD3D)?
['renderOptions', 'render3DOptions']: ['renderOptions'];
this.getEditor().addEventListener('selectionChange', function(e){
if (!this._isApplying)
this.updateStyleValues();
}, this);
this.getEditor().addEventListener('editObjChanged', function(e){
var propNames = e.propNames;
if (!propNames || !propNames.length || !Kekule.ArrayUtils.intersect(this._relatedPropNames, propNames).length) // not changing render options
{
return;
}
if (!this._isApplying)
this.updateStyleValues();
}, this);
this._isApplying = false; // private
},
/** @private */
doFinalize: function($super)
{
this.clearWidgets(); // already clear in $super()
$super();
},
/** @private */
initProperties: function()
{
this.defineProp('composer', {'dataType': 'Kekule.Editor.Composer', 'serializable': false, 'setter': null});
this.defineProp('fontNameBox', {'dataType': 'Kekule.Widget.ComboBox', 'serializable': false, 'setter': null});
this.defineProp('fontSizeBox', {'dataType': 'Kekule.Widget.ComboBox', 'serializable': false, 'setter': null});
this.defineProp('colorBox', {'dataType': 'Kekule.Widget.ColorDropButton', 'serializable': false, 'setter': null});
this.defineProp('textDirectionButtonSet', {'dataType': 'Kekule.Widget.CompactButtonSet', 'serializable': false, 'setter': null});
this.defineProp('textHorizontalAlignButtonSet', {'dataType': 'Kekule.Widget.CompactButtonSet', 'serializable': false, 'setter': null});
this.defineProp('textVerticalAlignButtonSet', {'dataType': 'Kekule.Widget.CompactButtonSet', 'serializable': false, 'setter': null});
this.defineProp('fontName', {'dataType': DataType.STRING, 'serializable': false,
'getter': function() { return this.getFontNameBox()? this.getFontNameBox().getValue(): null; },
'setter': function(value) { if (this.getFontNameBox()) { this.getFontNameBox().setValue(value); } }
});
this.defineProp('fontSize', {'dataType': DataType.NUMBER, 'serializable': false,
'getter': function() { return this.getFontSizeBox()? parseFloat(this.getFontSizeBox().getValue()): null; },
'setter': function(value) { if (this.getFontSizeBox()) { this.getFontSizeBox().setValue(value); } }
});
this.defineProp('color', {'dataType': DataType.STRING, 'serializable': false,
'getter': function() { return this.getColorBox()? this.getColorBox().getValue(): undefined; },
'setter': function(value) { if (this.getColorBox()) { this.getColorBox().setValue(value); } }
});
this.defineProp('textDirection', {'dataType': DataType.INT, 'serializable': false,
'getter': function()
{
return this._getButtonSetLinkedValue(this.getTextDirectionButtonSet());
},
'setter': function(value)
{
this._setButtonSetLinkedValue(this.getTextDirectionButtonSet(), value);
}
});
this.defineProp('textHorizontalAlign', {'dataType': DataType.INT, 'serializable': false,
'getter': function()
{
return this._getButtonSetLinkedValue(this.getTextHorizontalAlignButtonSet());
},
'setter': function(value)
{
this._setButtonSetLinkedValue(this.getTextHorizontalAlignButtonSet(), value);
}
});
this.defineProp('textVerticalAlign', {'dataType': DataType.INT, 'serializable': false,
'getter': function()
{
return this._getButtonSetLinkedValue(this.getTextVerticalAlignButtonSet());
},
'setter': function(value)
{
this._setButtonSetLinkedValue(this.getTextVerticalAlignButtonSet(), value);
}
});
this.defineProp('componentNames', {'dataType': DataType.ARRAY, 'serializable': false,
'getter': function()
{
var result = this.getPropStoreFieldValue('componentNames');
if (!result) // create default one
{
result = this.getDefaultComponentNames();
this.setPropStoreFieldValue('componentNames', result);
}
return result;
},
'setter': function(value)
{
this.setPropStoreFieldValue('componentNames', value);
this.recreateComponents();
}
});
},
/** @ignore */
initPropValues: function($super)
{
$super();
this.setShowGlyph(true);
},
/** @ignore */
removeWidget: function($super, widget, doNotFinalize)
{
if (widget === this.getFontNameBox())
this.setPropStoreFieldValue('fontNameBox', null);
if (widget === this.getFontSizeBox())
this.setPropStoreFieldValue('fontSizeBox', null);
if (widget === this.getColorBox())
this.setPropStoreFieldValue('colorBox', null);
if (widget === this.getTextDirectionButtonSet())
this.setPropStoreFieldValue('textDirectionButtonSet', null);
if (widget === this.getTextHorizontalAlignButtonSet())
this.setPropStoreFieldValue('textHorizontalAlignButtonSet', null);
if (widget === this.getTextVerticalAlignButtonSet())
this.setPropStoreFieldValue('textVerticalAlignButtonSet', null);
$super(widget, doNotFinalize);
},
/** @ignore */
doGetWidgetClassName: function($super)
{
var result = $super() + ' ' + CCNS.COMPOSER_TOOLBAR + ' ' + CCNS.COMPOSER_STYLE_TOOLBAR;
return result;
},
/** @private */
getEditor: function()
{
return this.getComposer().getEditor();
},
/** @private */
getEditorConfigs: function()
{
return this.getEditor().getEditorConfigs();
},
/** @private */
createChildWidgets: function(componentNames)
{
if (!componentNames)
componentNames = this.getComponentNames();
for (var i = 0, l = componentNames.length; i < l; ++i)
{
var name = componentNames[i];
if (name === BNS.fontName)
{
// font name
var comboBox = new Kekule.Widget.ComboBox(this);
this.fillFontNameBox(comboBox);
comboBox.addClassName(CCNS.COMPOSER_FONTNAME_BOX);
comboBox.setHint(/*CWT.HINT_FONTNAME*/Kekule.$L('ChemWidgetTexts.HINT_FONTNAME'));
comboBox.addEventListener('valueChange', function(e)
{
this.applyFontName();
}, this);
this.setPropStoreFieldValue('fontNameBox', comboBox);
}
else if (name === BNS.fontSize)
{
// font size
comboBox = new Kekule.Widget.ComboBox(this);
this.fillFontSizeBox(comboBox);
comboBox.addClassName(CCNS.COMPOSER_FONTSIZE_BOX);
comboBox.setHint(/*CWT.HINT_FONTSIZE*/Kekule.$L('ChemWidgetTexts.HINT_FONTSIZE'));
comboBox.addEventListener('valueChange', function(e)
{
this.applyFontSize();
}, this);
this.setPropStoreFieldValue('fontSizeBox', comboBox);
}
else if (name === BNS.color)
{
// color box
var colorBox = new Kekule.Widget.ColorDropButton(this);
colorBox.setHint(/*CWT.HINT_PICK_COLOR*/Kekule.$L('ChemWidgetTexts.HINT_PICK_COLOR'));
colorBox.setShowText(false);
colorBox.setSpecialColors([Kekule.Widget.ColorPicker.SpecialColors.UNSET, this.ATOM_COLOR_SPECIAL_VALUE_INFO]);
colorBox.addClassName(CCNS.COMPOSER_COLOR_BOX);
colorBox.addEventListener('valueChange', function(e)
{
this.applyColor();
}, this);
this.setPropStoreFieldValue('colorBox', colorBox);
}
else if (name === BNS.textDirection)
{
// text direction button
// its drop downs
var TD = Kekule.Render.TextDirection;
var childInfos = [
{'value': TD.DEFAULT, 'text': /*CWT.CAPTION_TEXT_DIRECTION_DEFAULT*/Kekule.$L('ChemWidgetTexts.CAPTION_TEXT_DIRECTION_DEFAULT'), className: CCNS.COMPOSER_TEXTDIRECTION_BUTTON_DEFAULT},
{'value': TD.LTR, 'text': /*CWT.CAPTION_TEXT_DIRECTION_LTR*/Kekule.$L('ChemWidgetTexts.CAPTION_TEXT_DIRECTION_LTR'), className: CCNS.COMPOSER_TEXTDIRECTION_BUTTON_LTR},
{'value': TD.RTL, 'text': /*CWT.CAPTION_TEXT_DIRECTION_RTL*/Kekule.$L('ChemWidgetTexts.CAPTION_TEXT_DIRECTION_RTL'), className: CCNS.COMPOSER_TEXTDIRECTION_BUTTON_RTL},
{'value': TD.TTB, 'text': /*CWT.CAPTION_TEXT_DIRECTION_TTB*/Kekule.$L('ChemWidgetTexts.CAPTION_TEXT_DIRECTION_TTB'), className: CCNS.COMPOSER_TEXTDIRECTION_BUTTON_TTB},
{'value': TD.BTT, 'text': /*CWT.CAPTION_TEXT_DIRECTION_BTT*/Kekule.$L('ChemWidgetTexts.CAPTION_TEXT_DIRECTION_BTT'), className: CCNS.COMPOSER_TEXTDIRECTION_BUTTON_BTT}
];
var btnSet = this._createStyleButtonSet(
/*CWT.CAPTION_TEXT_DIRECTION, CWT.HINT_TEXT_DIRECTION,*/
Kekule.$L('ChemWidgetTexts.CAPTION_TEXT_DIRECTION'), Kekule.$L('ChemWidgetTexts.HINT_TEXT_DIRECTION'),
CCNS.COMPOSER_TEXTDIRECTION_BUTTON, childInfos);
btnSet.addEventListener('select', function(e)
{
this.applyTextDirection();
}, this);
this.setPropStoreFieldValue('textDirectionButtonSet', btnSet);
}
else if (name === BNS.textAlign)
{
// horizontal text align button
var TA = Kekule.Render.TextAlign;
var childInfos = [
{'value': TA.DEFAULT, 'text': /*CWT.CAPTION_TEXT_ALIGN_DEFAULT*/Kekule.$L('ChemWidgetTexts.CAPTION_TEXT_ALIGN_DEFAULT'), className: CCNS.COMPOSER_TEXTALIGN_BUTTON_DEFAULT},
{'value': TA.LEADING, 'text': /*CWT.CAPTION_TEXT_ALIGN_LEADING*/Kekule.$L('ChemWidgetTexts.CAPTION_TEXT_ALIGN_LEADING'), className: CCNS.COMPOSER_TEXTALIGN_BUTTON_LEADING},
{'value': TA.TRAILING, 'text': /*CWT.CAPTION_TEXT_ALIGN_TRAILING*/Kekule.$L('ChemWidgetTexts.CAPTION_TEXT_ALIGN_TRAILING'), className: CCNS.COMPOSER_TEXTALIGN_BUTTON_TRAILING},
{'value': TA.CENTER, 'text': /*CWT.CAPTION_TEXT_ALIGN_CENTER*/Kekule.$L('ChemWidgetTexts.CAPTION_TEXT_ALIGN_CENTER'), className: CCNS.COMPOSER_TEXTALIGN_BUTTON_CENTER},
{'value': TA.LEFT, 'text': /*CWT.CAPTION_TEXT_ALIGN_LEFT*/Kekule.$L('ChemWidgetTexts.CAPTION_TEXT_ALIGN_LEFT'), className: CCNS.COMPOSER_TEXTALIGN_BUTTON_LEFT},
{'value': TA.RIGHT, 'text': /*CWT.CAPTION_TEXT_ALIGN_RIGHT*/Kekule.$L('ChemWidgetTexts.CAPTION_TEXT_ALIGN_RIGHT'), className: CCNS.COMPOSER_TEXTALIGN_BUTTON_RIGHT}
];
var btnSet = this._createStyleButtonSet(
/*CWT.CAPTION_TEXT_HORIZONTAL_ALIGN, CWT.HINT_TEXT_HORIZONTAL_ALIGN,*/
Kekule.$L('ChemWidgetTexts.CAPTION_TEXT_HORIZONTAL_ALIGN'), Kekule.$L('ChemWidgetTexts.HINT_TEXT_HORIZONTAL_ALIGN'),
CCNS.COMPOSER_TEXTALIGN_BUTTON_HORIZONTAL, childInfos);
btnSet.addEventListener('select', function(e)
{
this.applyTextHorizontalAlign();
}, this);
this.setPropStoreFieldValue('textHorizontalAlignButtonSet', btnSet);
// vertical text align button
var TA = Kekule.Render.TextAlign;
var childInfos = [
{'value': TA.DEFAULT, 'text': /*CWT.CAPTION_TEXT_ALIGN_DEFAULT*/Kekule.$L('ChemWidgetTexts.CAPTION_TEXT_ALIGN_DEFAULT'), className: CCNS.COMPOSER_TEXTALIGN_BUTTON_DEFAULT},
{'value': TA.LEADING, 'text': /*CWT.CAPTION_TEXT_ALIGN_LEADING*/Kekule.$L('ChemWidgetTexts.CAPTION_TEXT_ALIGN_LEADING'), className: CCNS.COMPOSER_TEXTALIGN_BUTTON_LEADING},
{'value': TA.TRAILING, 'text': /*CWT.CAPTION_TEXT_ALIGN_TRAILING*/Kekule.$L('ChemWidgetTexts.CAPTION_TEXT_ALIGN_TRAILING'), className: CCNS.COMPOSER_TEXTALIGN_BUTTON_TRAILING},
{'value': TA.CENTER, 'text': /*CWT.CAPTION_TEXT_ALIGN_CENTER*/Kekule.$L('ChemWidgetTexts.CAPTION_TEXT_ALIGN_CENTER'), className: CCNS.COMPOSER_TEXTALIGN_BUTTON_CENTER},
{'value': TA.TOP, 'text': /*CWT.CAPTION_TEXT_ALIGN_TOP*/Kekule.$L('ChemWidgetTexts.CAPTION_TEXT_ALIGN_TOP'), className: CCNS.COMPOSER_TEXTALIGN_BUTTON_TOP},
{'value': TA.BOTTOM, 'text': /*CWT.CAPTION_TEXT_ALIGN_BOTTOM*/Kekule.$L('ChemWidgetTexts.CAPTION_TEXT_ALIGN_BOTTOM'), className: CCNS.COMPOSER_TEXTALIGN_BUTTON_BOTTOM}
];
var btnSet = this._createStyleButtonSet(
/*CWT.CAPTION_TEXT_VERTICAL_ALIGN, CWT.HINT_TEXT_VERTICAL_ALIGN,*/
Kekule.$L('ChemWidgetTexts.CAPTION_TEXT_VERTICAL_ALIGN'), Kekule.$L('ChemWidgetTexts.HINT_TEXT_VERTICAL_ALIGN'),
CCNS.COMPOSER_TEXTALIGN_BUTTON_VERTICAL, childInfos);
btnSet.addEventListener('select', function(e)
{
this.applyTextVerticalAlign();
}, this);
this.setPropStoreFieldValue('textVerticalAlignButtonSet', btnSet);
}
}
this.updateStyleValues();
},
/** @private */
_createStyleButtonSet: function(text, hint, className, childItemInfos)
{
var btnSet = new Kekule.Widget.CompactButtonSet(this);
btnSet.setText(text);
btnSet.setHint(hint);
btnSet.addClassName(className);
btnSet.setShowText(false);
// show drop down mark rather than compact mark, keep a similar outlook to other widgets
btnSet.setShowCompactMark(false);
btnSet.setButtonKind(Kekule.Widget.Button.Kinds.DROPDOWN);
btnSet.getButtonSet().addClassName(CCNS.COMPOSER_STYLE_TOOLBAR);
btnSet.getButtonSet().setShowText(true);
// drop down children
if (childItemInfos)
{
for (var i = 0, l = childItemInfos.length; i < l; ++i)
{
var info = childItemInfos[i];
var btn = new Kekule.Widget.RadioButton(btnSet, info.text || '');
btn.setHint(info.hint || '');
btn.addClassName(info.className);
btn[this.LINKED_VALUE_FIELD] = info.value;
btnSet.append(btn, info.selected);
}
}
return btnSet;
},
/** @private */
_getButtonSetLinkedValue: function(btnSet)
{
var selected = btnSet? btnSet.getSelected(): null;
return selected? selected[this.LINKED_VALUE_FIELD]: undefined;
},
/** @private */
_setButtonSetLinkedValue: function(btnSet, value)
{
if (btnSet)
{
var group = btnSet.getButtonSet();
var children = group.getChildWidgets();
btnSet.setSelected(null); // uncheck all first
for (var i = 0 , l = children.length; i < l; ++i)
{
var child = children[i];
if (child.hasOwnProperty(this.LINKED_VALUE_FIELD))
{
if (child[this.LINKED_VALUE_FIELD] == value)
{
btnSet.setSelected(child);
break;
}
}
}
}
},
/** @private */
getDefaultComponentNames: function()
{
return Kekule.globalOptions.chemWidget.composer.styleToolComponentNames;
/*
return [
BNS.fontName,
BNS.fontSize,
BNS.color,
BNS.textDirection,
BNS.textAlign
];
*/
},
/** @private */
recreateComponents: function()
{
var cnames = this.getComponentNames();
this.clearWidgets();
this.createChildWidgets(cnames);
},
/** @private */
fillFontSizeBox: function(sizeComboBox)
{
var listedSizes = this.getEditorConfigs().getStyleSetterConfigs().getListedFontSizes();
var boxItems = [{'text': /*Kekule.ChemWidgetTexts.S_VALUE_DEFAULT*/Kekule.$L('ChemWidgetTexts.S_VALUE_DEFAULT'), 'value': undefined}];
for (var i = 0, l = listedSizes.length; i < l; ++i)
{
boxItems.push({'text': listedSizes[i] + ' px', 'value': listedSizes[i]});
}
sizeComboBox.setItems(boxItems);
},
/** @private */
fillFontNameBox: function(fontComboBox)
{
var listedNames = this.getEditorConfigs().getStyleSetterConfigs().getListedFontNames();
var boxItems = [{'text': /*Kekule.ChemWidgetTexts.S_VALUE_DEFAULT*/Kekule.$L('ChemWidgetTexts.S_VALUE_DEFAULT'), 'value': ''}];
for (var i = 0, l = listedNames.length; i < l; ++i)
{
boxItems.push({'text': listedNames[i], 'value': listedNames[i]});
}
fontComboBox.setItems(boxItems);
},
/**
* Apply style settings to objects in editor.
* @param {Array} chemObjs
* @param {Hash} styles Hash of style. {renderOptionName: value}.
* @param {Bool} is3DOption If true, styles will be put to Render3DOptions, otherwise RenderOptions will be set.
* @private
*/
applyStyle: function(chemObjs, styles, is3DOption)
{
this._isApplying = true;
try
{
//Kekule.Render.RenderOptionUtils.setRenderOptionValueOfObjs(chemObjs, styles, is3DOption);
var editor = this.getEditor();
editor.modifyObjectsRenderOptions(chemObjs, styles, is3DOption, true);
}
finally
{
this._isApplying = false;
}
},
/**
* Returns common render option or render 3D option value of chemObjs.
* If values in objects are not same, null will be returned.
* @param {Array} chemObjs
* @param {String} stylePropName
* @param {Bool} is3DOption
* @returns {Variant}
* @private
*/
getStyleValue: function(chemObjs, stylePropName, is3DOption)
{
return Kekule.Render.RenderOptionUtils.getCascadeRenderOptionValueOfObjs(chemObjs, stylePropName, is3DOption);
},
/**
* Apply styles to selected objects in editor.
* @private
*/
applyStyleToEditorSelection: function(styles, is3DOption)
{
var objs = this.getEditor().getSelection();
if (objs && objs.length)
return this.applyStyle(objs, styles, is3DOption);
},
/** @private */
applyFontName: function(objs)
{
if (!objs)
objs = this.getEditor().getSelection();
if (objs && objs.length)
{
var fontName = this.getFontName();
this.applyStyle(objs, {'fontFamily': fontName/*, 'atomFontFamily': fontName*/});
}
},
/** @private */
applyFontSize: function(objs)
{
if (!objs)
objs = this.getEditor().getSelection();
if (objs && objs.length)
{
var fontSize = this.getFontSize();
this.applyStyle(objs, {'fontSize': fontSize/*, 'atomFontSize': fontSize*/});
}
},
/** @private */
applyColor: function(objs)
{
if (!objs)
objs = this.getEditor().getSelection();
if (objs && objs.length)
{
var color = this.getColor();
if (color === this.ATOM_COLOR_SPECIAL_VALUE_INFO.value)
{
this.applyStyle(objs, {'useAtomSpecifiedColor': true, 'color': undefined});
}
else
{
if (color == Kekule.Widget.ColorPicker.SpecialColors.UNSET)
color = undefined;
this.applyStyle(objs, {'useAtomSpecifiedColor': false, 'color': color});
}
}
},
/** @private */
applyTextDirection: function(objs)
{
if (!objs)
objs = this.getEditor().getSelection();
if (objs && objs.length)
{
var direction = this.getTextDirection();
this.applyStyle(objs, {'charDirection': direction});
}
},
/** @private */
applyTextHorizontalAlign: function(objs)
{
if (!objs)
objs = this.getEditor().getSelection();
if (objs && objs.length)
{
var value = this.getTextHorizontalAlign();
this.applyStyle(objs, {'horizontalAlign': value});
}
},
/** @private */
applyTextVerticalAlign: function(objs)
{
if (!objs)
objs = this.getEditor().getSelection();
if (objs && objs.length)
{
var value = this.getTextVerticalAlign();
this.applyStyle(objs, {'verticalAlign': value});
}
},
/** @private */
updateStyleValues: function(objs)
{
if (!objs)
objs = this.getEditor().getSelection();
if (objs && objs.length)
{
this.updateFontName(objs);
this.updateFontSize(objs);
this.updateColor(objs);
this.updateTextDirection(objs);
this.updateTextHorizontalAlign(objs);
this.updateTextVerticalAlign(objs);
}
},
/** @private */
updateFontName: function(objs)
{
if (objs && objs.length && this.getFontNameBox())
{
var fontName = this.getStyleValue(objs, 'fontFamily'); // TODO: atom font family?
this.setFontName(fontName || '');
}
},
/** @private */
updateFontSize: function(objs)
{
if (objs && objs.length && this.getFontSizeBox())
{
var fontSize = this.getStyleValue(objs, 'fontSize'); // TODO: atom font size?
this.setFontSize(fontSize || undefined);
}
},
/** @private */
updateColor: function(objs)
{
if (objs && objs.length && this.getColorBox())
{
var color;
var useAtomSpecified = this.getStyleValue(objs, 'useAtomSpecifiedColor');
var color = this.getStyleValue(objs, 'color');
this.getColorBox().setColorClassName(null);
if (color)
{
this.getColorBox().setColorClassName(null);
}
else if (useAtomSpecified)
{
color = this.ATOM_COLOR_SPECIAL_VALUE_INFO.value;
this.getColorBox().setColorClassName(this.ATOM_COLOR_SPECIAL_VALUE_INFO.className);
}
this.setColor(color || undefined);
}
},
/** @private */
updateTextDirection: function(objs)
{
if (objs && objs.length && this.getTextDirectionButtonSet())
{
var value = this.getStyleValue(objs, 'charDirection');
this.setTextDirection(value);
/*
if (Kekule.ObjUtils.notUnset(value))
{
}
*/
}
},
/** @private */
updateTextHorizontalAlign: function(objs)
{
if (objs && objs.length && this.getTextHorizontalAlignButtonSet())
{
var value = this.getStyleValue(objs, 'horizontalAlign');
this.setTextHorizontalAlign(value);
}
},
/** @private */
updateTextVerticalAlign: function(objs)
{
if (objs && objs.length && this.getTextVerticalAlignButtonSet())
{
var value = this.getStyleValue(objs, 'verticalAlign');
this.setTextVerticalAlign(value);
}
},
/**
* Update toolbar and child widget outlook and other settings according to editor's state.
*/
updateState: function()
{
var editor = this.getEditor();
var hasSelection = (editor && editor.hasSelection());
//this.setEnabled(hasSelection);
/*
var children = [
this.getFontNameBox(),
this.getFontSizeBox(),
this.getColorBox(),
this.getTextDirectionButtonSet(),
this.getTextAlignButtonSet()
];
*/
if (this.getFontNameBox())
this.getFontNameBox().setEnabled(hasSelection);
if (this.getFontSizeBox())
this.getFontSizeBox().setEnabled(hasSelection);
if (this.getColorBox())
this.getColorBox().setEnabled(hasSelection);
if (this.getTextDirectionButtonSet())
this.getTextDirectionButtonSet().setEnabled(hasSelection);
if (this.getTextHorizontalAlignButtonSet())
this.getTextHorizontalAlignButtonSet().setEnabled(hasSelection);
if (this.getTextVerticalAlignButtonSet())
this.getTextVerticalAlignButtonSet().setEnabled(hasSelection);
}
});
/**
* The style toolbar for composer.
* @class
* @augments Kekule.Widget.Toolbar
* @param {Kekule.Editor.Composer} composer Parent composer.
*
* @property {Kekule.Editor.Composer} composer Parent composer.
* @property {Array} components Array of component names that shows in tool bar.
*/
Kekule.Editor.ComposerObjModifierToolbar = Class.create(Kekule.Widget.Toolbar,
/** @lends Kekule.Editor.ComposerObjModifierToolbar# */
{
/** @private */
CLASS_NAME: 'Kekule.Editor.ComposerObjModifierToolbar',
/** @constructs */
initialize: function($super, composer)
{
$super(composer);
this.setPropStoreFieldValue('modifiers', []);
this.setPropStoreFieldValue('modifierMap', new Kekule.MapEx());
this.setPropStoreFieldValue('composer', composer);
this.appendToWidget(composer);
composer.addEventListener('selectionChange', function(e){
if (!this._isApplying)
this.updateModifierValues();
}, this);
composer.addEventListener('selectedObjsUpdated', function(e){
/*
var propNames = e.propNames;
if (!propNames || !propNames.length || !Kekule.ArrayUtils.intersect(this._relatedPropNames, propNames).length) // not changing render options
{
return;
}
*/
if (!this._isApplying)
{
if (composer.getEditor().isManipulatingObject())
this._suppressUpdateModifierValuesInManipulation = true;
else
{
//console.log('selectedObjsUpdated', composer.getEditor()._objectManipulateFlag, e);
this.updateModifierValues();
}
}
}, this);
composer.addEventListener('endManipulateObject', function(e){
if (this._suppressUpdateModifierValuesInManipulation)
{
this.updateModifierValues();
this._suppressUpdateModifierValuesInManipulation = false;
}
}, this);
this._isApplying = false; // private
},
/** @private */
initProperties: function()
{
this.defineProp('composer', {'dataType': 'Kekule.Editor.Composer', 'serializable': false, 'setter': null});
this.defineProp('modifierMap', {'dataType': DataType.OBJECT, 'serializable': false, 'setter': null});
this.defineProp('modifiers', {'dataType': DataType.ARRAY, 'serializable': false, 'setter': null});
},
/** @ignore */
initPropValues: function($super)
{
$super();
this.setShowGlyph(true);
},
/** @private */
doFinalize: function($super)
{
this.clearWidgets();
this.getModifierMap().finalize();
var modifiers = this.getModifiers();
for (var i = 0, l = modifiers.length; i < l; ++i)
modifiers[i].finalize();
$super();
},
/** @ignore */
doGetWidgetClassName: function($super)
{
var result = $super() + ' ' + CCNS.COMPOSER_TOOLBAR + ' ' + CCNS.COMPOSER_OBJMODIFIER_TOOLBAR;
return result;
},
/** @private */
getEditor: function()
{
return this.getComposer().getEditor();
},
/** @private */
getEditorConfigs: function()
{
return this.getEditor().getEditorConfigs();
},
/** @private */
getAllowedModifierCategories: function()
{
var c = this.getComposer();
return c && c.getAllowedObjModifierCategories();
},
/**
* Returns selection of editor.
* @returns {Array}
* @private
*/
getTargetObjs: function()
{
return this.getEditor().getSelection();
},
/**
* Returns modifier classes that can be displayed on toolbar.
* @returns {Array}
* @private
*/
getAvailableModifierClasses: function()
{
var targetClasses = [];
var targets = this.getTargetObjs();
for (var i = 0, l = targets.length; i < l; ++i)
{
if (targets[i].getClass)
AU.pushUnique(targetClasses, targets[i].getClass());
}
var result = [];
var allowedCategories = this.getAllowedModifierCategories();
for (var i = 0, l = targetClasses.length; i < l; ++i)
{
var modifierClasses = Kekule.Editor.ObjModifierManager.getModifierClasses(targetClasses[i], allowedCategories);
if (modifierClasses)
AU.pushUnique(result, modifierClasses);
}
if (result.length)
result.sort(function(a, b){
var protoA = a && ClassEx.getPrototype(a);
var protoB = b && ClassEx.getPrototype(b);
var nameA = protoA && protoA.getModifierName && protoA.getModifierName();
var nameB = protoB && protoB.getModifierName && protoB.getModifierName();
return (nameA === nameB)? 0:
(nameA < nameB)? -1: 1;
});
return result;
},
/**
* Return a cached or newly created modifier instance of class.
* @param {Class} modifierClass
* @param {Bool} autoCreate
* @return {Kekule.Editor.ObjModifier.Base}
*/
getModifierInstanceOfClass: function(modifierClass, autoCreate)
{
var map = this.getModifierMap();
var result = map.get(modifierClass);
if (!result && autoCreate)
{
result = new modifierClass(this.getEditor());
map.set(modifierClass, result);
}
return result;
},
/**
* Refresh displayed modifier widgets on toolbar.
*/
updateModifierWidgets: function()
{
var modifiers = [];
this.clearWidgets(true); // clear old widgets but do not finalize
var modifierClasses = this.getAvailableModifierClasses();
for (var i = 0, l = modifierClasses.length; i < l; ++i)
{
var mClass = modifierClasses[i];
var modifier = this.getModifierInstanceOfClass(mClass, true); // auto create
var widget = modifier.getWidget();
if (widget)
{
this.appendWidget(widget);
modifiers.push(modifier);
}
}
this.setPropStoreFieldValue('modifiers', modifiers);
this.updateModifierValues();
if (this._isAllModifierWidgetButtons(modifiers)) // if all modifiers are buttons, use btnGroup style rather than toolbar
{
this.removeClassName(CNS.TOOLBAR).addClassName(CNS.BUTTON_GROUP);
}
else
{
this.removeClassName(CNS.BUTTON_GROUP).addClassName(CNS.TOOLBAR);
}
},
/**
* Update toolbar and child widget outlook and other settings according to editor's state.
*/
updateState: function()
{
this.updateModifierWidgets();
},
/**
* Refresh modifier widget according to current state of selection in editor.
*/
updateModifierValues: function()
{
var modifiers = this.getModifiers();
for (var i = 0, l = modifiers.length; i < l; ++i)
{
modifiers[i].loadFromTargets();
}
},
/** @private */
_isAllModifierWidgetButtons: function(modifiers)
{
for (var i = 0, l = modifiers.length; i < l; ++i)
{
var m = modifiers[i];
var w = m && m.getWidget();
if (!w || !w.getDisplayed() || w instanceof Kekule.Widget.Button)
continue;
else
return false
}
return true;
}
});
/**
* A editor with essential UI for end users.
* @class
* @augments Kekule.ChemWidget.AbstractWidget
* @param {Variant} parentOrElementOrDocument
* @param {Kekule.Editor.BaseEditor} editor An editor instance embedded in UI.
*
* @property {Kekule.Editor.BaseEditor} editor The editor instance embedded in UI.
* @property {Array} commonToolButtons buttons in common tool bar. This is a array of predefined strings, e.g.: ['zoomIn', 'zoomOut', 'resetZoom', 'molDisplayType', ...].
* If not set, default buttons will be used.
* In the array, complex hash can also be used to add custom buttons, e.g.: <br />
* [ <br />
* 'zoomIn', 'zoomOut',<br />
* {'name': 'myCustomButton1', 'widgetClass': 'Kekule.Widget.Button', 'action': actionClass},<br />
* {'name': 'myCustomButton2', 'htmlClass': 'MyClass' 'caption': 'My Button', 'hint': 'My Hint', '#execute': function(){ ... }},<br />
* ]<br />
* @property {Array} chemToolButtons buttons in chem tool bar. This is a array of predefined strings, e.g.: ['zoomIn', 'zoomOut', 'resetZoom', 'molDisplayType', ...].
* If not set, default buttons will be used.
* Chem tool often has a series of child tool buttons, you can also control to display which child buttons, e.g.:
* [
* {'name': 'bond', 'attached': ['bondSingle', 'bondDouble']}, <br />
* 'atom', 'formula',<br />
* ] <br />
* Note: currently same child button can not be existed in different chem tool buttons.
* In the array, complex hash can also be used to add custom buttons, e.g.: <br />
* [ <br />
* 'atom', 'formula',<br />
* {'name': 'myCustomButton1', 'widgetClass': 'Kekule.Widget.Button', 'action': actionClass},<br />
* {'name': 'myCustomButton2', 'htmlClass': 'MyClass' 'caption': 'My Button', 'hint': 'My Hint', '#execute': function(){ ... }},<br />
* ]<br />
* @property {Array} styleToolComponentNames Array of component names that shows in style tool bar.
* @property {Bool} enableStyleToolbar
* @property {Bool} enableObjModifierToolbar
* @property {Array} allowedObjModifierCategories
* @property {Bool} showInspector Whether show advanced object inspector and structure view.
* @property {Bool} autoSetMinDimension
*
* @property {Kekule.Editor.BaseEditorConfigs} editorConfigs Configuration of this editor.
* @property {Bool} enableOperHistory Whether undo/redo is enabled.
* @property {Kekule.OperationHistory} operHistory History of operations. Used to enable undo/redo function.
* @property {Int} renderType Display in 2D or 3D. Value from {@link Kekule.Render.RendererType}.
* @property {Kekule.ChemObject} chemObj The root object in editor.
* @property {Bool} enableOperContext If this property is set to true, object being modified will be drawn in a
* separate context to accelerate the interface refreshing.
* @property {Object} objContext Context to draw basic chem objects. Can be 2D or 3D context. Alias of property drawContext
* @property {Object} operContext Context to draw objects being operated. Can be 2D or 3D context.
* @property {Object} uiContext Context to draw UI marks. Usually this is a 2D context.
* @property {Object} objDrawBridge Bridge to draw chem objects. Alias of property drawBridge.
* @property {Object} uiDrawBridge Bridge to draw UI markers.
* @property {Array} selection An array of selected basic object.
*
* @property {Bool} enableLoadNewFile Whether open a external file to displayer is allowed.
* @property {Bool} enableCreateNewDoc Whether create new object in editor is allowed.
* @property {Bool} allowCreateNewChild Whether new direct child of space can be created.
* Note: if the space is empty, one new child will always be allowed to create.
*/
Kekule.Editor.Composer = Class.create(Kekule.ChemWidget.AbstractWidget,
/** @lends Kekule.Editor.Composer# */
{
/** @private */
CLASS_NAME: 'Kekule.Editor.Composer',
/** @private */
BINDABLE_TAG_NAMES: ['div'],
/** @private */
CHEM_TOOL_CHILD_FIELDS: '__$children__',
/** @constructs */
initialize: function($super, parentOrElementOrDocument, editor)
{
/*
this.updateStyleToolbarStateBind = this.updateStyleToolbarState.bind(this);
this.updateObjModifierToolbarStateBind = this.updateObjModifierToolbarState.bind(this);
*/
this.updateSelectionAssocToolbarStateBind = this.updateSelectionAssocToolbarState.bind(this);
this.setPropStoreFieldValue('enableStyleToolbar', true);
this.setPropStoreFieldValue('enableObjModifierToolbar', true);
this.setPropStoreFieldValue('editor', editor);
this.setPropStoreFieldValue('editorNexus', new Kekule.Editor.EditorNexus());
$super(parentOrElementOrDocument);
/*
if (!editor)
editor = this.createDefaultEditor();
*/
this.bindEditor(editor);
var ed = this.getEditor();
if (ed) // editor is newly created
ed.setChemObj(ed.getChemObj()); // a force readjust size, otherwise there will be size problem in IE8
// tool bars may already be created by setting buttons property
if (!this.getCommonBtnGroup())
this.createCommonToolbar();
if (!this.getChemBtnGroup())
this.createChemToolbar();
if (!this.getZoomBtnGroup())
this.createZoomToolbar();
// debug
//this.setShowInspector(true);
this.uiLayoutChanged();
},
/** @private */
doFinalize: function($super)
{
//this.getPainter().finalize();
var toolBar = this.getCommonBtnGroup();
if (toolBar)
toolBar.finalize();
var toolBar = this.getChemBtnGroup();
if (toolBar)
toolBar.finalize();
var editor = this.getPropStoreFieldValue('editor');
if (editor)
editor.finalize();
this.getEditorNexus().finalize();
$super();
},
/** @private */
initProperties: function()
{
this.defineProp('editor', {'dataType': 'Kekule.Editor.BaseEditor', 'serializable': false, 'setter': null,
'getter': function()
{
var result = this.getPropStoreFieldValue('editor');
if (!result)
{
result = this.createDefaultEditor();
this.setPropStoreFieldValue('editor', result);
}
return result;
}
});
this.defineProp('showInspector', {'dataType': DataType.BOOL,
'setter': function(value)
{
if (this.getShowInspector() !== value)
{
this.setPropStoreFieldValue('showInspector', value);
this.showInspectorChanged();
}
}});
this.defineProp('autoSetMinDimension', {'dataType': DataType.BOOL});
// private property
this.defineProp('editorStageElem', {'dataType': DataType.OBJECT, 'serializable': false, 'setter': null});
this.defineProp('advPanelElem', {'dataType': DataType.OBJECT, 'serializable': false, 'setter': null});
this.defineProp('topRegionElem', {'dataType': DataType.OBJECT, 'serializable': false, 'setter': null});
this.defineProp('leftRegionElem', {'dataType': DataType.OBJECT, 'serializable': false, 'setter': null});
this.defineProp('bottomRegionElem', {'dataType': DataType.OBJECT, 'serializable': false, 'setter': null});
this.defineProp('editorNexus', {'dataType': 'Kekule.Editor.EditorNexus', 'serializable': false, 'setter': null});
this.defineProp('objInspector', {'dataType': 'Kekule.Widget.ObjectInspector', 'serializable': false, 'setter': null});
this.defineProp('structureTreeView', {'dataType': 'Kekule.ChemWidget.StructureTreeView', 'serializable': false, 'setter': null});
// a private property, toolbar of common tasks (such as save/load)
this.defineProp('commonBtnGroup', {'dataType': 'Kekule.Widget.ButtonGroup', 'serializable': false});
// a private property, toolbar of zoom tasks (such as zoomin/out)
this.defineProp('zoomBtnGroup', {'dataType': 'Kekule.Widget.ButtonGroup', 'serializable': false});
// a private property, toolbar of chem tools (such as atom/bond)
this.defineProp('chemBtnGroup', {'dataType': 'Kekule.Widget.ButtonGroup', 'serializable': false});
// a private property, toolbar of association chem tools (such as single, double bound form for bond tool)
this.defineProp('assocBtnGroup', {'dataType': 'Kekule.Widget.ButtonGroup', 'serializable': false});
// a private property, toolbar of style settings (such as font name, font size, color)
this.defineProp('styleToolbar', {'dataType': 'Kekule.Widget.Toolbar', 'serializable': false,
'getter': function()
{
var result = this.getPropStoreFieldValue('styleToolbar');
if (!result)
{
if (this.getEnableStyleToolbar())
{
result = this.createStyleToolbar();
this.setPropStoreFieldValue('styleToolbar', result);
}
}
return result;
}
});
this.defineProp('styleToolComponentNames', {'dataType': DataType.ARRAY, 'serializable': false,
'getter': function()
{
var toolbar = this.getStyleToolbar();
return toolbar? toolbar.getComponentNames(): null;
},
'setter': function(value)
{
var toolbar = this.getStyleToolbar();
if (toolbar)
toolbar.setComponentNames(value);
return this;
}
});
this.defineProp('enableStyleToolbar', {'dataType': DataType.BOOL,
'getter': function()
{
// if obj modifier toolbar is enabled, old fashioned style toolbar will not be displayed
return this.getPropStoreFieldValue('enableStyleToolbar') && !this.getEnableObjModifierToolbar();
},
'setter': function(value)
{
this.setPropStoreFieldValue('enableStyleToolbar', value);
this.updateSelectionAssocToolbarState();
}
});
// a private property, toolbar of style settings (such as font name, font size, color)
this.defineProp('objModifierToolbar', {'dataType': 'Kekule.Widget.Toolbar', 'serializable': false,
'getter': function()
{
var result = this.getPropStoreFieldValue('objModifierToolbar');
if (!result)
{
if (this.getEnableObjModifierToolbar())
{
result = this.createObjModifierToolbar();
this.setPropStoreFieldValue('objModifierToolbar', result);
}
}
return result;
}
});
this.defineProp('enableObjModifierToolbar', {'dataType': DataType.BOOL,
'setter': function(value)
{
this.setPropStoreFieldValue('enableObjModifierToolbar', value);
this.updateSelectionAssocToolbarState();
}
});
this.defineProp('allowedObjModifierCategories', {'dataType': DataType.ARRAY});
this.defineProp('commonToolButtons', {'dataType': DataType.HASH, 'serializable': false,
'getter': function()
{
var result = this.getPropStoreFieldValue('commonToolButtons');
if (!result) // create default one
{
result = this.getDefaultCommonToolBarButtons();
this.setPropStoreFieldValue('commonToolButtons', result);
}
return result;
},
'setter': function(value)
{
this.setPropStoreFieldValue('commonToolButtons', value);
this.updateCommonToolbar();
this.updateZoomToolbar();
}
});
this.defineProp('chemToolButtons', {'dataType': DataType.HASH, 'serializable': false,
'getter': function()
{
var result = this.getPropStoreFieldValue('chemToolButtons');
if (!result) // create default one
{
result = this.getDefaultChemToolBarButtons();
this.setPropStoreFieldValue('chemToolButtons', result);
}
return result;
},
'setter': function(value)
{
this.setPropStoreFieldValue('chemToolButtons', value);
this.updateChemToolbar();
}
});
this.defineProp('styleBarComponents', {'dataType': DataType.ARRAY, 'serializable': false,
'getter': function()
{
var result = this.getPropStoreFieldValue('styleBarComponents');
if (!result)
{
result = null; // default one
this.setPropStoreFieldValue('styleBarComponents', result);
}
return result;
},
'setter': function(value)
{
this.setPropStoreFieldValue('styleBarComponents', value);
if (this.getStyleToolbar())
this.getStyleToolbar().setComponentNames(value);
}
});
/*
// private
this.defineProp('toolButtonNameMapping', {'dataType': DataType.HASH, 'serializable': false, 'setter': null,
'getter': function()
{
var result = this.getPropStoreFieldValue('toolButtonNameMapping');
if (!result) // create default one
{
result = this._createDefaultToolButtonNameMapping();
this.setPropStoreFieldValue('toolButtonNameMapping', result);
}
return result;
}
});
*/
// private
this.defineProp('commonActions', {'dataType': 'Kekule.ActionList', 'serializable': false, 'setter': null,
'getter': function()
{
var result = this.getPropStoreFieldValue('commonActions');
if (!result)
{
result = new Kekule.ActionList();
this.setPropStoreFieldValue('commonActions', result);
}
return result;
}
});
this.defineProp('zoomActions', {'dataType': 'Kekule.ActionList', 'serializable': false, 'setter': null,
'getter': function()
{
var result = this.getPropStoreFieldValue('zoomActions');
if (!result)
{
result = new Kekule.ActionList();
this.setPropStoreFieldValue('zoomActions', result);
}
return result;
}
});
this.defineProp('chemActions', {'dataType': 'Kekule.ActionList', 'serializable': false, 'setter': null,
'getter': function()
{
var result = this.getPropStoreFieldValue('chemActions');
if (!result)
{
result = new Kekule.ActionList();
this.setPropStoreFieldValue('chemActions', result);
}
return result;
}
});
this.defineProp('actionMap', {'dataType': 'Kekule.MapEx', 'serializable': false, 'setter': null,
'getter': function()
{
var result = this.getPropStoreFieldValue('actionMap');
if (!result)
{
result = new Kekule.MapEx();
this.setPropStoreFieldValue('actionMap', result);
}
return result;
}
});
this.defineProp('allowCreateNewChild', {'dataType': DataType.BOOL,
'getter': function()
{
var ed = this.getEditor();
return (ed && ed.getAllowCreateNewChild)? ed.getAllowCreateNewChild(): null;
},
'setter': function(value)
{
var ed = this.getEditor();
if (ed.setAllowCreateNewChild)
ed.setAllowCreateNewChild(value);
return this;
}
});
// editor delegated property
// from ChemObjDisplayer
this._defineEditorDelegatedProp('chemObj');
this._defineEditorDelegatedProp('chemObjLoaded');
this._defineEditorDelegatedProp('renderType');
this._defineEditorDelegatedProp('renderConfigs');
this._defineEditorDelegatedProp('drawOptions');
this._defineEditorDelegatedProp('allowCoordBorrow');
// from BaseEditor
this._defineEditorDelegatedProp('editorConfigs');
this._defineEditorDelegatedProp('operHistory');
this._defineEditorDelegatedProp('enableOperHistory');
this._defineEditorDelegatedProp('selection');
this._defineEditorDelegatedProp('hotTrackedObjs');
this._defineEditorDelegatedProp('enableOperContext');
this._defineEditorDelegatedProp('enableCreateNewDoc');
this._defineEditorDelegatedProp('enableLoadNewFile');
this._defineEditorDelegatedProp('initOnNewDoc');
},
/**
* Define property that directly mapped to editor's property.
* @param {String} propName
* @param {String} editorPropName Name of corresponding property in editor.
* @return {Object} Property info object added to property list.
* @private
*/
_defineEditorDelegatedProp: function(propName, editorPropName)
{
if (!editorPropName)
editorPropName = propName;
var editorPropInfo = ClassEx.getPropInfo(Kekule.Editor.BaseEditor, editorPropName);
/*
var propOptions = {
'serializable': editorPropInfo.serializable,
'dataType': editorPropInfo.dataType,
'title': editorPropInfo.title,
'description': editorPropInfo.description,
'getter': null,
'setter': null
};
*/
var propOptions = Object.create(editorPropInfo);
propOptions.getter = null;
propOptions.setter = null;
if (editorPropInfo.getter)
{
propOptions.getter = function()
{
return this.getEditor().getPropValue(editorPropName);
};
}
if (editorPropInfo.setter)
{
propOptions.setter = function(value)
{
this.getEditor().setPropValue(editorPropName, value);
}
}
//console.log('define delegate prop', propOptions);
return this.defineProp(propName, propOptions);
},
/** @private */
loadPredefinedResDataToProp: function(propName, resData, success)
{
if (propName === 'chemObj') // only this property can be set by predefined resource
{
if (success)
{
try
{
var chemObj = Kekule.IO.loadTypedData(resData.data, resData.resType, resData.resUri);
//console.log('set predefined chemObj', chemObj);
this.setChemObj(chemObj);
}
catch(e)
{
Kekule.raise(e, Kekule.ExceptionLevel.ERROR);
}
}
else // else, failed
{
Kekule.throwException(/*Kekule.ErrorMsg.CANNOT_LOAD_RES_OF_URI*/Kekule.$L('ErrorMsg.CANNOT_LOAD_RES_OF_URI') + resData.resUri || '');
}
}
},
/** @ignore */
initPropValues: function($super)
{
$super();
},
/** @ignore */
doCreateRootElement: function(doc)
{
var result = doc.createElement('div');
return result;
},
/** @ignore */
doCreateSubElements: function(doc, rootElem)
{
var result = [];
var elem = this._doCreateSubElement(doc, rootElem, 'div', CCNS.COMPOSER_EDITOR_STAGE, 'editorStageElem');
result.push(elem);
elem = this._doCreateSubElement(doc, rootElem, 'div', CCNS.COMPOSER_ADV_PANEL, 'advPanelElem');
result.push(elem);
result.push(this._doCreateSubElement(doc, rootElem, 'div', CCNS.COMPOSER_TOP_REGION, 'topRegionElem'));
result.push(this._doCreateSubElement(doc, rootElem, 'div', CCNS.COMPOSER_LEFT_REGION, 'leftRegionElem'));
result.push(this._doCreateSubElement(doc, rootElem, 'div', CCNS.COMPOSER_BOTTOM_REGION, 'bottomRegionElem'));
return result;
},
/** @private */
_doCreateSubElement: function(doc, parentElem, tagName, htmlClass, propStoreFieldName)
{
var result = doc.createElement(tagName);
result.className = htmlClass;
if (parentElem)
parentElem.appendChild(result);
if (propStoreFieldName)
this.setPropStoreFieldValue(propStoreFieldName, result);
return result;
},
/** @ignore */
doGetWidgetClassName: function($super)
{
var result = $super() + ' ' + CCNS.COMPOSER;
return result;
},
/** @ignore */
doWidgetShowStateChanged: function($super, isShown)
{
$super(isShown);
if (isShown)
this.adjustComponentPositions();
},
/*
doInsertedToDom: function()
{
console.log('composer inserted to DOM');
},
*/
/** @ignore */
getResizerElement: function()
{
return this.getEditorStageElem();
},
/** @ignore */
getChildActionClass: function($super, actionName, checkSupClasses)
{
var result = $super(actionName, checkSupClasses);
if (!result)
result = this.getEditor().getChildActionClass(actionName, checkSupClasses);
return result;
},
/**
* Returns coord mode of editor.
* @returns {Int}
*/
getCoordMode: function()
{
return this.getEditor().getCoordMode();
},
/**
* Returns whether the chem object inside editor has been modified since load.
* @returns {Bool}
*/
isDirty: function()
{
return this.getEditor().isDirty();
},
/**
* Create a new object and load it in editor.
*/
newDoc: function()
{
return this.getEditor().newDoc();
},
/**
* Load chem object in composer.
* @param {Kekule.ChemObject} chemObj
*/
load: function(chemObj)
{
return this.getEditor().load(chemObj);
},
/**
* Returns object in dialog that to be saved.
* @returns {Kekule.ChemObject}
* @private
*/
getSavingTargetObj: function()
{
var c = this.getEditor();
if (c)
return c.getSavingTargetObj();
else
return null;
},
/**
* Returns array of classes that can be exported (saved) from composer.
* @returns {Array}
*/
getExportableClasses: function()
{
return this.getEditor().getExportableClasses();
},
/**
* Returns exportable object for specified class.
* @param {Class} objClass Set null to export default object.
* @returns {Object}
*/
exportObj: function(objClass)
{
return this.getEditor().exportObj(objClass);
},
/**
* Returns all exportable objects for specified class.
* Descendants can override this method.
* @param {Class} objClass Set null to export default object.
* @returns {Array}
*/
exportObjs: function(objClass)
{
return this.getEditor().exportObjs(objClass);
},
/**
* Undo last operation.
*/
undo: function()
{
return this.getEditor().undo();
},
/**
* Redo last operation.
*/
redo: function()
{
return this.getEditor().redo();
},
/**
* Undo all operations.
*/
undoAll: function()
{
return this.getEditor().undoAll();
},
/**
* Check if an undo action can be taken.
* @returns {Bool}
*/
canUndo: function()
{
return this.getEditor().canUndo();
},
/**
* Check if an undo action can be taken.
* @returns {Bool}
*/
canRedo: function()
{
return this.getEditor().canRedo();
},
/**
* Repaint the objects in editor.
*/
repaint: function(overrideOptions)
{
this.getEditor().repaint(overrideOptions);
return this;
},
/**
* Called after UI changing (e.g., show/hide inspector/assoc tool bar).
* @private
*/
uiLayoutChanged: function()
{
this.adjustComponentPositions();
},
/** @ignore */
doResize: function($super)
{
this.adjustComponentPositions();
},
/**
* Change child components' position and dimension to fit current UI widget status.
* @private
*/
adjustComponentPositions: function()
{
// toolbar
var commonToolbarElem = this.getCommonBtnGroup()? this.getCommonBtnGroup().getElement(): null;
var zoomToolbarElem = this.getZoomBtnGroup()? this.getZoomBtnGroup().getElement(): null;
var chemToolbarElem = this.getChemBtnGroup()? this.getChemBtnGroup().getElement(): null;
if (!commonToolbarElem || !chemToolbarElem) // not all toolbars created, widget may in its initial stage, no need to update
return;
if (commonToolbarElem)
var commonRect = Kekule.HtmlElementUtils.getElemPageRect(commonToolbarElem);
if (zoomToolbarElem)
var zoomRect = Kekule.HtmlElementUtils.getElemPageRect(zoomToolbarElem);
if (chemToolbarElem)
var chemRect = Kekule.HtmlElementUtils.getElemPageRect(chemToolbarElem);
if (!commonRect.width || !chemRect.width) // rect is zero, the widget may not be displayed
return;
/*
var style = commonToolbarElem.style;
style.top = '0px';
style.left = chemRect.width + 'px';
style = chemToolbarElem.style;
style.top = commonRect.height + 'px';
style.left = '0px';
*/
var topRegionHeight = commonRect.height;
var leftRegionWidth = chemRect.width;
var bottomRegionHeight = (zoomRect && zoomRect.height) || topRegionHeight; // zoom toolbar may be invisible
// top region
var elem = this.getTopRegionElem();
var style = elem.style;
style.top = '0px';
style.left = leftRegionWidth + 'px';
style.right = '0px';
style.height = topRegionHeight + 'px';
// bottom region
elem = this.getBottomRegionElem();
style = elem.style;
style.bottom = '0px';
style.height = bottomRegionHeight + 'px';
style.left = leftRegionWidth + 'px';
style.right = '0px';
var bottomRect = Kekule.HtmlElementUtils.getElemPageRect(elem);
var bottomFreeWidth = bottomRect.width - (zoomRect? zoomRect.width: 0);
// left region
elem = this.getLeftRegionElem();
style = elem.style;
style.left = '0px';
style.top = topRegionHeight + 'px';
style.bottom = '0px';
style.width = leftRegionWidth + 'px';
var leftRect = Kekule.HtmlElementUtils.getElemPageRect(elem);
var leftFreeHeight = leftRect.height - chemRect.height;
// now we can decide whether shown assoc toolbar on bottom or left side
if (leftFreeHeight / bottomFreeWidth > 0.9) // TODO: now fixed
{
// assoc bar shown in left
this.changeAssocToolbarRegion(true);
}
else
{
// assoc bar shown in bottom
this.changeAssocToolbarRegion(false);
}
// editor stage
var top = topRegionHeight, left = leftRegionWidth, bottom = bottomRegionHeight, right;
// calc right
if (!this.getShowInspector())
right = 0;
else
{
var elem = this.getAdvPanelElem();
var rect = Kekule.HtmlElementUtils.getElemPageRect(elem);
right = rect.width;
}
var stageElem = this.getEditorStageElem();
var style = stageElem.style;
style.position = 'absolute';
style.top = top + 'px';
style.left = left + 'px';
style.bottom = bottom + 'px';
style.right = right + 'px';
// advPanel
elem = this.getAdvPanelElem();
style = elem.style;
style.position = 'absolute';
style.top = top + 'px';
style.bottom = bottom + 'px';
if (this.getAutoSetMinDimension())
{
var minDim = {
'width': leftRegionWidth + commonRect.width,
'height': topRegionHeight + chemRect.height
};
//console.log('get minDim', minDim);
if (minDim.width && minDim.height)
this.setMinDimension(minDim);
/*
var currDim = this.getDimension();
this.setDimension(currDim.width, currDim.height, true); // update size, but do not need to adjust component position again
*/
}
},
/** @private */
adjustAssocToolbarPositions: function()
{
/*
//var commonToolbarElem = this.getCommonBtnGroup().getElement();
var chemToolbarElem = this.getChemBtnGroup().getElement();
//var commonRect= Kekule.HtmlElementUtils.getElemBoundingClientRect(commonToolbarElem)
var chemRect = Kekule.HtmlElementUtils.getElemBoundingClientRect(chemToolbarElem);
// assoc toolbar
if (this.isAssocToolbarShown())
{
var elem = this.getAssocBtnGroup().getElement();
elem.style.left = chemRect.width + 'px';
//elem.style.top = commonRect.height + 'px';
}
*/
this.adjustStyleAndObjModifierToolbarPosition();
},
/** @private */
adjustStyleAndObjModifierToolbarPosition: function()
{
/*
var chemToolbarElem = this.getChemBtnGroup().getElement();
var chemRect = Kekule.HtmlElementUtils.getElemBoundingClientRect(chemToolbarElem);
var assocRect;
if (this.isAssocToolbarShown())
{
assocRect = Kekule.HtmlElementUtils.getElemBoundingClientRect(this.getAssocBtnGroup().getElement());
}
//console.log('adjust pos', assocRect);
//if (this.isStyleToolbarShown())
if (this.getPropStoreFieldValue('styleToolbar')) // stylebar created
{
var elem = this.getStyleToolbar().getElement();
elem.style.left = (assocRect? (assocRect.left + assocRect.width): chemRect.width) + 'px';
}
//if (this.isObjModifierToolbarShown())
if (this.getPropStoreFieldValue('objModifierToolbar')) // modifier toolbar created
{
var elem = this.getObjModifierToolbar().getElement();
elem.style.left = (assocRect? (assocRect.left + assocRect.width): chemRect.width) + 'px';
}
*/
},
////////////////// methods about inner editor ///////////////////////
/** @private */
bindEditor: function(editor)
{
if (!editor)
editor = this.getEditor();
else
editor.setParent(this);
editor.setTouchAction('none'); // disable default touch scroll on editor
var self = this;
var commonActions = this.getCommonActions();
var chemActions = this.getChemActions();
this.createIaControllers(editor);
editor.addEventListener('load', function(e)
{
this.updateAllActions();
this.updateUiWidgets();
// turn select as default checked chem tool
var actions = this.getChemActions();
var action = actions.getActionAt(0);
if (action)
action.execute();
},
this
);
editor.addEventListener('operChange', function(e)
{
commonActions.updateAll();
this.updateUiWidgets();
},
this
);
editor.addEventListener('selectionChange', function(e)
{
commonActions.updateAll();
this.updateUiWidgets();
},
this
);
/*
editor.addEventListener('editObjsChanged', function(e)
{
this.updateAllActions();
},
this
);
*/
editor.appendToElem(this.getEditorStageElem());
this.getEditorNexus().setEditor(editor);
//this.newDoc();
},
/** @private */
createDefaultEditor: function()
{
var result = new Kekule.Editor.ChemSpaceEditor(this, null, Kekule.Render.RendererType.R2D);
result.addClassName(CNS.DYN_CREATED);
return result;
},
/**
* Create available iaController for editor.
* @private
*/
createIaControllers: function(editor)
{
var controllerClasses = Kekule.Editor.IaControllerManager.getAvailableControllerClasses(editor.getClass());
for (var i = 0, l = controllerClasses.length; i < l; ++i)
{
var c = controllerClasses[i];
var controller = new c(editor);
//console.log('add ia controller', controller.getDefId(), controller);
editor.addIaController(controller.getDefId(), controller);
}
},
///////////////// methods about adv panel (objInspector and structureTreeView) /////
/** @private */
showInspectorChanged: function()
{
var display = this.getShowInspector();
if (display)
{
if (!this.getObjInspector()) // not created yet
this.createAdvControls();
this.showAdvPanel();
}
else
this.hideAdvPanel();
this.uiLayoutChanged();
},
/**
* Create object inspector and structure tree view.
* @private
*/
createAdvControls: function(parentElem)
{
if (!parentElem)
parentElem = this.getAdvPanelElem();
var doc = this.getDocument();
var treeView = new Kekule.ChemWidget.StructureTreeView(doc);
treeView.setItemInitialExpanded(true);
treeView.appendToElem(parentElem);
//treeView.setRootObj(chemEditor.getChemObj());
this.setPropStoreFieldValue('structureTreeView', treeView);
var objInspector = new Kekule.Widget.ObjectInspector(doc);
objInspector.setShowPropInfoPanel(false);
objInspector.appendToElem(parentElem);
this.setPropStoreFieldValue('objInspector', objInspector);
var nexus = this.getEditorNexus();
nexus.setObjectInspector(objInspector);
nexus.setStructureTreeView(treeView);
},
/** @private */
showAdvPanel: function()
{
this.getAdvPanelElem().style.display = 'block';
},
/** @private */
hideAdvPanel: function()
{
this.getAdvPanelElem().style.display = 'none';
},
////////////////// methods about tool buttons and actions ///////////////////////
/* @private */
/*
_createDefaultToolButtonNameMapping: function()
{
var result = {};
result[BNS.newDoc] = CE.ActionEditorNewDoc;
result[BNS.loadFile] = CW.ActionDisplayerLoadFile;
result[BNS.loadData] = CW.ActionDisplayerLoadData;
result[BNS.saveData] = CW.ActionDisplayerSaveFile;
result[BNS.zoomIn] = CW.ActionDisplayerZoomIn;
result[BNS.zoomOut] = CW.ActionDisplayerZoomOut;
result[BNS.reset] = CW.ActionDisplayerReset;
result[BNS.config] = Kekule.Widget.ActionOpenConfigWidget;
result[BNS.undo] = CE.ActionEditorUndo;
result[BNS.redo] = CE.ActionEditorRedo;
result[BNS.cloneSelection] = CE.ActionCloneSelection;
result[BNS.copy] = CE.ActionCopySelection;
result[BNS.cut] = CE.ActionCutSelection;
result[BNS.paste] = CE.ActionPaste;
result[BNS.manipulate] = CE.ActionComposerSetManipulateController;
result[BNS.erase] = CE.ActionComposerSetEraserController;
result[BNS.molAtom] = CE.ActionComposerSetAtomController;
result[BNS.molFormula] = CE.ActionComposerSetFormulaController;
result[BNS.molBond] = CE.ActionComposerSetBondController;
result[BNS.molCharge] = CE.ActionComposerSetNodeChargeController;
result[BNS.textBlock] = CE.ActionComposerSetTextBlockController;
result[BNS.imageBlock] = CE.ActionComposerSetImageBlockController;
result[BNS.textImage] = CE.ActionComposerSetTextImageController;
result[BNS.molRing] = CE.ActionComposerSetRepositoryRingController;
result[BNS.glyph] = CE.ActionComposerSetRepositoryGlyphController;
result[BNS.objInspector] = CE.ActionComposerToggleInspector;
return result;
},
*/
/** @private */
getDefaultCommonToolBarButtons: function()
{
return Kekule.globalOptions.chemWidget.composer.commonToolButtons;
/*
var buttons = [
BNS.newDoc,
//BNS.loadFile,
BNS.loadData,
BNS.saveData,
BNS.undo,
BNS.redo,
BNS.copy,
BNS.cut,
BNS.paste,
//BNS.cloneSelection,
BNS.zoomIn,
BNS.reset,
BNS.zoomOut,
BNS.config,
BNS.objInspector
];
return buttons;
*/
},
/** @private */
getZoomButtonNames: function()
{
return [
BNS.zoomIn,
BNS.zoomOut,
BNS.reset,
BNS.resetZoom
];
},
/** @private */
getDefaultChemToolBarButtons: function()
{
return Kekule.globalOptions.chemWidget.composer.chemToolButtons;
/*
var buttons = [
BNS.manipulate,
BNS.erase,
BNS.molBond,
BNS.molAtom,
BNS.molFormula,
BNS.molRing,
BNS.molCharge,
BNS.glyph,
BNS.textImage
];
return buttons;
*/
},
/** @private */
getCompActionClass: function(btnName)
{
//return this.getToolButtonNameMapping()[btnName];
return this.getChildActionClass(btnName, true);
},
/** @private */
_getActionTargetWidget: function(actionClass)
{
if (ClassEx.isOrIsDescendantOf(actionClass, Kekule.Editor.ActionOnComposer) || ClassEx.isOrIsDescendantOf(actionClass, Kekule.Widget.ActionOpenConfigWidget))
return this;
else
return this.getEditor();
},
/**
* Create a tool button inside parent button group.
* //@param {Kekule.Widget} targetWidget
* @param {String} btnName
* @param {Kekule.Widget.ButtonGroup} parentGroup
* @param {Kekule.Action} actions
* @returns {Kekule.Widget.Button}
* @private
*/
createToolButton: function(btnName, parentGroup, actions, checkGroup)
{
/*
var result = null;
var name = DataType.isObjectValue(btnName)? btnName.name: btnName;
var children = DataType.isObjectValue(btnName)? btnName.attached: null;
var actionClass = this.getCompActionClass(name);
if (DataType.isObjectValue(btnName) && !actionClass) // custom button
{
if (!actionClass) // no binded action, custom button
var objDefHash = Object.extend({'widget': Kekule.Widget.Button}, btnName);
result = Kekule.Widget.Utils.createFromHash(parentGroup, objDefHash);
var actionClass = objDefHash.actionClass;
if (actionClass) // create action
{
if (typeof(actionClass) === 'string')
actionClass = ClassEx.findClass(objDefHash.actionClass);
if (actionClass)
{
var action = new actionClass(this);
this.getActions().add(action);
result.setAction(action);
}
}
}
else
{
//var actionClass = this.getCompActionClass(btnName);
//if (actionClass)
{
var btnClass = (btnName === BNS.objInspector) ? Kekule.Widget.CheckButton :
(!!checkGroup) ? Kekule.Widget.RadioButton :
Kekule.Widget.Button;
result = new btnClass(parentGroup);
var targetWidget = this._getActionTargetWidget(actionClass);
var action = new actionClass(targetWidget);
if (checkGroup)
action.setCheckGroup(checkGroup);
//this.getActions().add(action);
actions.add(action);
result.setAction(action);
}
}
return result;
*/
var result = null;
var name = DataType.isObjectValue(btnName)? btnName.name: btnName;
var actionClass = this.getCompActionClass(name);
var action = this._createToolButtonAction(btnName, actions, checkGroup);
if (DataType.isObjectValue(btnName) && !actionClass) // custom button
{
if (!actionClass) // no binded action, custom button
var objDefHash = Object.extend({'widget': Kekule.Widget.Button}, btnName);
result = Kekule.Widget.Utils.createFromHash(parentGroup, objDefHash);
}
else // predefined names
{
var preferredWidgetClass = action.getPreferredWidgetClass && action.getPreferredWidgetClass();
//console.log(action.getClassName(), preferredWidgetClass);
var btnClass =
preferredWidgetClass? preferredWidgetClass:
(btnName === BNS.objInspector) ? Kekule.Widget.CheckButton :
(!!checkGroup) ? Kekule.Widget.RadioButton :
Kekule.Widget.Button;
result = new btnClass(parentGroup);
}
if (action)
result.setAction(action);
return result;
},
/** @private */
_createActionButton: function(action, parentWidget)
{
var checkGroup = action.getCheckGroup();
var preferredClass = action.getPreferredWidgetClass && action.getPreferredWidgetClass();
var btnClass =
preferredClass? preferredClass:
(!!checkGroup) ? Kekule.Widget.RadioButton:
Kekule.Widget.Button;
var btn = new btnClass(parentWidget);
btn.setAction(action);
return btn;
},
/** @private */
_createToolButtonAction: function(actionNameOrHash, defActions, checkGroup)
{
var result = null;
var name = DataType.isObjectValue(actionNameOrHash)? actionNameOrHash.name: actionNameOrHash;
var children = DataType.isObjectValue(actionNameOrHash)? actionNameOrHash.attached: null;
var actionClass = this.getCompActionClass(name);
var result;
if (DataType.isObjectValue(actionNameOrHash) && !actionClass) // custom button
{
var objDefHash = actionNameOrHash;
var actionClass = objDefHash.actionClass;
if (actionClass) // create action
{
if (typeof(actionClass) === 'string')
actionClass = ClassEx.findClass(objDefHash.actionClass);
}
}
if (actionClass)
{
var actionMap = this.getActionMap();
// check if this action already exists
var result = actionMap.get(actionClass);
if (!result)
{
var result = new actionClass(this._getActionTargetWidget(actionClass));
//this.getActions().add(action);
actionMap.set(actionClass, result);
}
var actualGroup = checkGroup || '';
var actionExplicitGroup = result.getExplicitGroup && result.getExplicitGroup();
if (Kekule.ObjUtils.notUnset(actionExplicitGroup))
{
actualGroup = actionExplicitGroup;
}
if (actualGroup)
result.setCheckGroup(actualGroup);
if (result && defActions)
defActions.add(result);
if (result && result.addAttachedAction)
{
//result.setChecked(false);
var subGroupName = result.getClassName();
// result.clearAttachedActions();
var oldAttachedActions = Kekule.ArrayUtils.clone(result.getAttachedActions().getActions() || []);
var attachChildAction = function(action, childAction, oldAttachedActions, asDefault, childIndex)
{
if (!childAction)
return null;
var oldIndex = oldAttachedActions.indexOf(childAction);
if (oldIndex >= 0) // action already attached, change position
{
oldAttachedActions[oldIndex] = null; // indicating this action has been used
//console.log('use old action', oldIndex, childAction.getClassName());
}
else
action.addAttachedAction(childAction, asDefault);
if (Kekule.ObjUtils.notUnset(childIndex)) // change position
{
action.setAttachedActionIndex(childAction, childIndex);
}
};
if (children) // has custom defined chem tool children buttons
{
for (var i = 0, l = children.length; i < l; ++i)
{
var child = children[i];
var childAction = this._createToolButtonAction(child, null, subGroupName); // do not add to default action list
attachChildAction(result, childAction, oldAttachedActions, i === 0, i);
}
}
else // use default attached classes
{
var attachedActionClasses = result.getAttachedActionClasses();
if (attachedActionClasses)
{
for (var i = 0, l = attachedActionClasses.length; i < l; ++i)
{
var aClass = attachedActionClasses[i];
var childAction = actionMap.get(aClass);
if (!childAction)
{
childAction = new aClass(this._getActionTargetWidget(aClass));
var childExplicitGroup = (childAction.getExplicitGroup && childAction.getExplicitGroup());
if (Kekule.ObjUtils.notUnset(childExplicitGroup))
{
childAction.setCheckGroup(childExplicitGroup);
// console.log('set check group', childAction.getClassName(), childExplicitGroup);
}
else
childAction.setCheckGroup(subGroupName);
actionMap.set(aClass, childAction);
}
/*
else
{
console.log('use old action', childAction.getClassName());
if (childAction.getAttachedActions)
console.log(childAction.getAttachedActions().getActions());
}
*/
//result.addAttachedAction(childAction, i === 0);
attachChildAction(result, childAction, oldAttachedActions, i === 0, i);
}
}
}
// at last remove unused old actions
if (oldAttachedActions.length)
{
//var actions = result.getAttachedActions();
for (var i = 0, l = oldAttachedActions.length; i < l; ++i)
{
var unusedAction = oldAttachedActions[i];
if (unusedAction)
{
result.removeAttachedAction(unusedAction);
//actions.remove(unusedAction);
actionMap.remove(unusedAction.getClass());
//unusedAction.finalize();
}
}
}
}
}
return result;
},
/**
* Create a toolbar inside editor UI.
* @returns {Kekule.Widget.ButtonGroup}
* @private
*/
createInnerToolbar: function(parentElem)
{
var toolBar = new Kekule.Widget.ButtonGroup(this);
toolBar.addClassName(CCNS.COMPOSER_TOOLBAR);
toolBar.addClassName(CCNS.INNER_TOOLBAR);
toolBar.addClassName(CNS.DYN_CREATED);
toolBar.setShowText(false);
toolBar.doSetShowGlyph(true);
toolBar.appendToElem(parentElem || this.getElement());
return toolBar;
},
/**
* Create common tool bar.
* @returns {Kekule.Widget.ButtonGroup}
* @private
*/
createCommonToolbar: function()
{
var parentElem = this.getTopRegionElem();
var toolbar = this.createInnerToolbar(parentElem);
toolbar.addClassName(CCNS.COMPOSER_COMMON_TOOLBAR);
// add buttons
var btns = this.getCommonToolButtons();
btns = Kekule.ArrayUtils.exclude(btns, this.getZoomButtonNames());
var actions = this.getCommonActions();
var editor = this.getEditor();
actions.clear();
for (var i = 0, l = btns.length; i < l; ++i)
{
var name = btns[i];
var btn = this.createToolButton(name, toolbar, actions);
}
this.setCommonBtnGroup(toolbar);
toolbar.addClassName(CNS.DYN_CREATED);
this.updateZoomToolbar();
this.adjustComponentPositions();
return toolbar;
},
/**
* Create zoom tool bar.
* @returns {Kekule.Widget.ButtonGroup}
* @private
*/
createZoomToolbar: function()
{
// add buttons
var btns = Kekule.ArrayUtils.intersect(this.getCommonToolButtons(), this.getZoomButtonNames());
if (btns.length)
{
var parentElem = this.getBottomRegionElem();
var toolbar = this.createInnerToolbar(parentElem);
toolbar.addClassName(CCNS.COMPOSER_ZOOM_TOOLBAR);
var actions = this.getZoomActions();
var editor = this.getEditor();
actions.clear();
for (var i = 0, l = btns.length; i < l; ++i)
{
var name = btns[i];
var btn = this.createToolButton(name, toolbar, actions);
}
this.setZoomBtnGroup(toolbar);
toolbar.addClassName(CNS.DYN_CREATED);
this.adjustComponentPositions();
return toolbar;
}
else
{
this.setZoomBtnGroup(null);
return null;
}
},
/**
* Update common toolbar, actually free the old one and create a new one.
* @private
*/
updateCommonToolbar: function()
{
var old = this.getCommonBtnGroup();
if (old)
old.finalize();
this.createCommonToolbar();
},
/**
* Update zoom toolbar, actually free the old one and create a new one.
* @private
*/
updateZoomToolbar: function()
{
var old = this.getZoomBtnGroup();
if (old)
old.finalize();
this.createZoomToolbar();
},
/**
* Create chem tool bar.
* @returns {Kekule.Widget.ButtonGroup}
* @private
*/
createChemToolbar: function()
{
var parentElem = this.getLeftRegionElem();
var toolbar = this.createInnerToolbar(parentElem);
toolbar.addClassName(CCNS.COMPOSER_CHEM_TOOLBAR);
toolbar.setLayout(Kekule.Widget.Layout.VERTICAL);
// add buttons
var btns = this.getChemToolButtons();
var actions = this.getChemActions();
actions.clear();
var checkGroup = 'chemTools';
var firstBtn;
for (var i = 0, l = btns.length; i < l; ++i)
{
var name = btns[i];
var btn = this.createToolButton(name, toolbar, actions, checkGroup);
if (i === 0)
firstBtn = btn;
}
this.setChemBtnGroup(toolbar);
toolbar.addClassName(CNS.DYN_CREATED);
this.adjustComponentPositions();
if (firstBtn)
{
var action = firstBtn.getAction();
if (action)
{
action.setChecked(true);
var attachedActions = action.getAttachedActions && action.getAttachedActions();
this.bindAssocActions(attachedActions || null);
/*
// force update the assoc actions
if (!action.getChecked())
action.setChecked(true);
else
action.setChecked(false).setChecked(true);
*/
}
}
return toolbar;
},
/**
* Update chem toolbar, actually free the old one and create a new one.
* @private
*/
updateChemToolbar: function()
{
var old = this.getChemBtnGroup();
if (old)
old.finalize();
this.createChemToolbar();
},
/**
* Create assoc chem tool bar.
* @returns {Kekule.Widget.ButtonGroup}
* @private
*/
createAssocToolbar: function()
{
var parentElem = this.getBottomRegionElem();
var toolbar = this.createInnerToolbar(parentElem);
//toolbar.setLayout(Kekule.Widget.Layout.VERTICAL);
toolbar.addClassName(CCNS.COMPOSER_ASSOC_TOOLBAR);
this.setAssocBtnGroup(toolbar);
this.adjustAssocToolbarPositions();
toolbar.addClassName(CNS.DYN_CREATED);
return toolbar;
},
/** @private */
changeAssocToolbarRegion: function(toLeftRegion)
{
var toolbar = this.getAssocBtnGroup();
if (!toolbar)
toolbar = this.createAssocToolbar();
var parent;
if (toLeftRegion)
{
parent = this.getLeftRegionElem();
if (toolbar.getElement().parentNode !== parent)
{
toolbar.setLayout(Kekule.Widget.Layout.VERTICAL);
toolbar.appendToElem(parent);
}
}
else // to bottom region
{
parent = this.getBottomRegionElem();
if (toolbar.getElement().parentNode !== parent)
{
toolbar.setLayout(Kekule.Widget.Layout.HORIZONTAL);
var refElem = Kekule.DomUtils.getFirstChildElem(parent);
// insert as the first elem
parent.insertBefore(toolbar.getElement(), refElem);
}
}
},
/**
* Show assoc chem tool bar.
*/
showAssocToolbar: function()
{
var toolbar = this.getAssocBtnGroup();
if (!toolbar)
toolbar = this.createAssocToolbar();
var self = this;
/*
if (this.isStyleToolbarShown()) // need to hide style toolbar first
{
console.log('here hide style');
this.hideStyleToolbar();
}
*/
toolbar.show(null, function()
{
self.uiLayoutChanged();
//self.updateStyleToolbarState();
//setTimeout(self.updateObjModifierToolbarStateBind, 0);
//setTimeout(self.updateStyleToolbarStateBind, 0); // IMPORTANT, defer call to update style toolbar, avoid show/hide it too quickly
setTimeout(self.updateSelectionAssocToolbarStateBind, 0);
});
return this;
},
/**
* Hide assoc chem tool bar.
*/
hideAssocToolbar: function()
{
var toolbar = this.getAssocBtnGroup();
if (toolbar)
{
var self = this;
toolbar.hide(null, function()
{
self.uiLayoutChanged();
//self.updateStyleToolbarState(); // may need to reopen style toolbar
//setTimeout(self.updateObjModifierToolbarStateBind, 0);
//setTimeout(self.updateStyleToolbarStateBind, 0); // IMPORTANT, defer call to update style toolbar, avoid show/hide it too quickly
setTimeout(self.updateSelectionAssocToolbarStateBind, 0);
}
);
}
return this;
},
/**
* Check if assoc chem tool bar is visible.
* @returns {Bool}
*/
isAssocToolbarShown: function()
{
var toolbar = this.getAssocBtnGroup();
return toolbar && toolbar.isShown();
},
/**
* Create buttons in assoc tool bar to display actions.
* @param {Kekule.ActionList} actions
* @private
*/
bindAssocActions: function(actions)
{
var toolbar = this.getAssocBtnGroup();
if (!toolbar)
toolbar = this.createAssocToolbar();
toolbar.clearWidgets();
if (actions)
{
for (var i = 0, l = actions.getActionCount(); i < l; ++i)
{
var action = actions.getActionAt(i);
/*
var checkGroup = action.getCheckGroup();
var btnClass = (!!checkGroup) ? Kekule.Widget.RadioButton : Kekule.Widget.Button;
var btn = new btnClass(toolbar);
btn.setAction(action);
*/
var btn = this._createActionButton(action, toolbar);
}
}
},
/**
* Create obj modifier tool bar.
* @returns {Kekule.Widget.Toolbar}
* @private
*/
createObjModifierToolbar: function()
{
if (this.getEnableObjModifierToolbar())
{
var toolbar = new Kekule.Editor.ComposerObjModifierToolbar(this);
toolbar.appendToElem(this.getBottomRegionElem());
this.setObjModifierToolbar(toolbar);
this.adjustAssocToolbarPositions();
this.updateObjModifierToolbarState();
return toolbar;
}
else
return null;
},
/**
* Show obj modifier tool bar.
*/
showObjModifierToolbar: function()
{
//console.log('show');
if (this.getEnableObjModifierToolbar())
{
var toolbar = this.getObjModifierToolbar();
if (!toolbar.isShown())
{
var self = this;
toolbar.show(null, function()
{
self.uiLayoutChanged();
});
}
}
return this;
},
/**
* Hide obj modifier tool bar.
*/
hideObjModifierToolbar: function()
{
var toolbar = this.getPropStoreFieldValue('objModifierToolbar');
if (toolbar)
{
if (toolbar.isShown())
{
var self = this;
toolbar.hide(null, function()
{
self.uiLayoutChanged();
}, null, {instantly: true}
);
}
}
return this;
},
/**
* Check if obj modifier tool bar is visible.
* @returns {Bool}
*/
isObjModifierToolbarShown: function()
{
var toolbar = this.getPropStoreFieldValue('objModifierToolbar');
return (toolbar && toolbar.isShown());
},
/**
* Update obj modifier toolbar show/hide state according to editor's state.
* @private
*/
updateObjModifierToolbarState: function()
{
var showToolbar = false;
if (this.getEnableObjModifierToolbar() && this.needShowSelectionAssocToolbar())
{
/*
// further check if currently is select series IA controllers
var iaController = this.getEditor().getActiveIaController();
showToolbar = ((iaController instanceof Kekule.Editor.BasicManipulationIaController) && (iaController.getEnableSelect()))
|| (iaController instanceof Kekule.Editor.ClientDragScrollIaController);
*/
showToolbar = true;
/*
// check if currently the space can fullfill a modifier toolbar
var toolbar = this.getObjModifierToolbar();
var modifierRect = Kekule.HtmlElementUtils.getElemBoundingClientRect(toolbar.getElement());
var zoomBar = this.getZoomBtnGroup();
var zoomBarRect = Kekule.HtmlElementUtils.getElemBoundingClientRect(zoomBar.getElement());
var gap = 50; // TODO: current fixed, gap between modifier bar and zoom bar
showToolbar = (modifierRect.left + modifierRect.width + gap < zoomBarRect.left); // if can fullfill, just show it
console.log(showToolbar, modifierRect, zoomBarRect);
*/
}
if (showToolbar)
{
this.showObjModifierToolbar();
this.getObjModifierToolbar().updateState();
}
else
{
this.hideObjModifierToolbar();
}
},
/**
* Create style setting tool bar.
* @returns {Kekule.Widget.Toolbar}
* @private
*/
createStyleToolbar: function()
{
if (this.getEnableStyleToolbar())
{
var toolbar = new Kekule.Editor.ComposerStyleToolbar(this);
toolbar.setComponentNames(this.getStyleBarComponents());
toolbar.appendToElem(this.getBottomRegionElem());
this.setStyleToolbar(toolbar);
this.adjustAssocToolbarPositions();
this.updateStyleToolbarState();
return toolbar;
}
else
return null;
},
/**
* Show style tool bar.
*/
showStyleToolbar: function()
{
//console.log('show style');
if (this.getEnableStyleToolbar())
{
var toolbar = this.getStyleToolbar();
if (!toolbar.isShown())
{
var self = this;
toolbar.show(null, function()
{
self.uiLayoutChanged();
});
}
}
return this;
},
/**
* Hide style tool bar.
*/
hideStyleToolbar: function()
{
//console.log('hide style');
var toolbar = this.getPropStoreFieldValue('styleToolbar');
if (toolbar)
{
if (toolbar.isShown())
{
var self = this;
toolbar.hide(null, function()
{
self.uiLayoutChanged();
}
);
}
}
return this;
},
/**
* Check if style tool bar is visible.
* @returns {Bool}
*/
isStyleToolbarShown: function()
{
var toolbar = this.getPropStoreFieldValue('styleToolbar'); //this.getStyleToolbar();
return (toolbar && toolbar.isShown());
},
/**
* Update style toolbar show/hide state according to editor's state.
* @private
*/
updateStyleToolbarState: function()
{
if (this.getEnableStyleToolbar() && this.needShowSelectionAssocToolbar())
{
this.showStyleToolbar();
this.getStyleToolbar().updateState();
}
else
{
this.hideStyleToolbar();
}
},
/**
* Check if the assoc selection to set properties of selected objects (style or obj modifier toolbar) should be shown.
* @returns {Bool}
* @private
*/
needShowSelectionAssocToolbar: function()
{
var editor = this.getEditor();
return editor && editor.hasSelection();
},
/**
* Update style & obj modifier toolbar show/hide state according to editor's state.
* @private
*/
updateSelectionAssocToolbarState: function()
{
this.adjustStyleAndObjModifierToolbarPosition();
this.updateStyleToolbarState();
this.updateObjModifierToolbarState();
},
/**
* Update both common and chem actions.
* @private
*/
updateAllActions: function()
{
this.getCommonActions().updateAll();
this.getZoomActions().updateAll();
this.getChemActions().updateAll();
},
/**
* Update states of child UI widgets.
* @private
*/
updateUiWidgets: function()
{
this.updateStyleToolbarState();
this.updateObjModifierToolbarState();
},
////// about configurator
/** @ignore */
createConfigurator: function($super)
{
var result = $super();
result.addEventListener('configChange', function(e){
// render config change need to repaint context
this.getEditor().repaint();
}, this);
return result;
}
});
/**
* A class method to returns the preferred min dimension of composer widget.
*/
Kekule.Editor.Composer.getMinPreferredDimension = function()
{
return {width: 630, height: 350}; // current fixed
};
/**
* A special class to give a setting facade for Chem Composer.
* Do not use this class alone.
* @class
* @augments Kekule.Widget.BaseWidget.Settings
*/
Kekule.Editor.Composer.Settings = Class.create(Kekule.Widget.BaseWidget.Settings,
/** @lends Kekule.Editor.Composer.Settings# */
{
/** @private */
CLASS_NAME: 'Kekule.Editor.Composer.Settings',
/** @construct */
initialize: function($super, composer)
{
$super(composer);
},
/** @private */
initProperties: function()
{
//this.defineProp('composer', {'dataType': 'Kekule.Editor.Composer', 'serializable': false, 'scope': PS.PUBLIC});
this.defineDelegatedProps(['enableCreateNewDoc', 'enableLoadNewFile', 'initOnNewDoc', 'enableOperHistory', 'allowCreateNewChild', 'enableStyleToolbar']);
}
});
/**
* A special widget class to open a config widget for ChemObjDisplayer.
* Do not use this widget alone.
* @class
* @augments Kekule.Widget.Configurator
*
* @param {Kekule.Editor.Composer} composer
*/
Kekule.Editor.Composer.Configurator = Class.create(Kekule.Widget.Configurator,
/** @lends Kekule.Editor.Composer.Configurator# */
{
/** @private */
CLASS_NAME: 'Kekule.Editor.Composer.Configurator',
/** @private */
TAB_BTN_DATA_FIELD: '__$data__',
/** @construct */
initialize: function($super, composer)
{
$super(composer);
},
/** @ignore */
initPropValues: function($super)
{
$super();
this.setLayout(Kekule.Widget.Layout.HORIZONTAL);
},
/** @private */
getCategoryInfos: function($super)
{
var result = $super();
var composer = this.getComposer();
var editor = composer.getEditor();
// renderConfigs and displayerConfigs
var configObjs = [editor.getDisplayerConfigs(), editor.getRenderConfigs()];
for (var j = 0, k = configObjs.length; j < k; ++j)
{
var config = configObjs[j];
var props = config.getPropListOfScopes([Class.PropertyScope.PUBLISHED]);
for (var i = 0, l = props.getLength(); i < l; ++i)
{
var propInfo = props.getPropInfoAt(i);
var obj = config.getPropValue(propInfo.name);
if (obj)
{
result.push({
'obj': obj,
'name': propInfo.name,
'title': propInfo.title,
'description': propInfo.description
});
}
}
}
return result;
},
/** @private */
getComposer: function()
{
return this.getWidget();
},
/** @private */
getEditor: function()
{
return this.getComposer().getEditor();
}
});
// register predefined settings of viewer
Kekule._registerAfterLoadSysProc(function(){
var EMC = Kekule.Editor.ObjModifier.Category;
var SM = Kekule.ObjPropSettingManager;
SM.register('Kekule.Editor.Composer.fullFunc', { // composer with all functions
enableStyleToolbar: true,
enableOperHistory: true,
enableLoadNewFile: true,
enableCreateNewDoc: true,
allowCreateNewChild: true,
commonToolButtons: null, // create all default common tool buttons
chemToolButtons: null, // create all default chem tool buttons
styleToolComponentNames: null, // create all default style components
allowedObjModifierCategories: null // allow modifiers of all categories
});
SM.register('Kekule.Editor.Composer.molOnly', { // composer that can only edit molecule
enableStyleToolbar: true,
enableOperHistor: true,
enableLoadNewFile: true,
enableCreateNewDoc: true,
allowCreateNewChild: true,
commonToolButtons: null, // create all default common tool buttons
chemToolButtons: [
BNS.manipulate,
BNS.erase,
BNS.molBond,
BNS.molAtom,
// BNS.molFormula,
BNS.molRing,
BNS.molCharge
], // create only chem tool buttons related with molecule
styleToolComponentNames: null, // create all default style components
allowedObjModifierCategories: [EMC.GENERAL, EMC.CHEM_STRUCTURE] // only all chem structure modifiers
});
SM.register('Kekule.Editor.Composer.compact', { // composer with less tool buttons
enableStyleToolbar: false,
commonToolButtons: [
BNS.newDoc,
BNS.loadData,
BNS.saveData,
BNS.undo,
BNS.redo,
BNS.zoomIn,
BNS.zoomOut
],
chemToolButtons: null, // create all default chem tool buttons
styleToolComponentNames: null, // create all default style components
allowedObjModifierCategories: null // allow modifiers of all categories
});
SM.register('Kekule.Editor.Composer.singleObj', { // only allows create one object in composer
allowCreateNewChild: false
});
});
/**
* A dialog with a composer, executed to edit or create new chem object.
* @class
* @augments Kekule.Widget.Dialog
*
* @property {Kekule.Editor.Composer} composer Composer widget in dialog.
* @property {Kekule.ChemObject} chemObj Get or set chem object in composer.
*/
Kekule.Editor.ComposerDialog = Class.create(Kekule.Widget.Dialog,
/** @lends Kekule.Editor.ComposerDialog# */
{
/** @private */
CLASS_NAME: 'Kekule.Editor.ComposerDialog',
initialize: function($super, parentOrElementOrDocument, caption, buttons)
{
$super(parentOrElementOrDocument, caption,
buttons || [Kekule.Widget.DialogButtons.OK, Kekule.Widget.DialogButtons.CANCEL]);
},
/** @private */
initProperties: function()
{
this.defineProp('composer', {'dataType': 'Kekule.Editor.Composer', 'serializable': false, 'setter': null});
this.defineProp('chemObj', {'dataType': 'Kekule.ChemObject', 'serializable': false,
'getter': function()
{
var c = this.getComposer();
return c && c.getChemObj();
},
'setter': function(value)
{
var c = this.getComposer();
if (c)
c.setChemObj(value);
}
});
},
/** @ignore */
initPropValues: function($super)
{
$super();
//this.setButtons([Kekule.Widget.DialogButtons.OK, Kekule.Widget.DialogButtons.CANCEL]);
},
/** @ignore */
doGetWidgetClassName: function($super)
{
return $super() + ' ' + CCNS.COMPOSER_DIALOG;
},
/** @ignore */
doCreateClientContents: function($super, clientElem)
{
$super();
var composer = this.doCreateComposerWidget();
this.setPropStoreFieldValue('composer', composer);
composer.appendToElem(clientElem);
},
/**
* Returns object in dialog that to be saved.
* @returns {Kekule.ChemObject}
* @private
*/
getSavingTargetObj: function()
{
var c = this.getComposer();
if (c)
return c.getSavingTargetObj();
else
return null;
},
/**
* @private
*/
doCreateComposerWidget: function()
{
// TODO: currently fixed to composer
var result = new Kekule.Editor.Composer(this);
result.addClassName(CNS.DYN_CREATED);
if (result.setResizable)
result.setResizable(true);
var editor = result.getEditor();
/*
editor.setEnableCreateNewDoc(false);
editor.setEnableLoadNewFile(false);
editor.setAllowCreateNewChild(false);
*/
editor.addClassName(CNS.DYN_CREATED);
return result;
}
});
/**
* An iframe containing a composer widget.
* @class
* @augments Kekule.ChemWidget.AbstractWidget
*
* @property {Kekule.Editor.Composer} composer Composer widget in frame.
*
*/
Kekule.Editor.ComposerFrame = Class.create(Kekule.ChemWidget.AbstractWidget,
/** @lends Kekule.Editor.ComposerFrame# */
{
/** @private */
CLASS_NAME: 'Kekule.Editor.ComposerFrame',
/** @private */
BINDABLE_TAG_NAMES: ['iframe'],
initialize: function($super, parentOrElementOrDocument)
{
$super(parentOrElementOrDocument);
},
/** @private */
initProperties: function()
{
this.defineProp('composer', {'dataType': 'Kekule.Editor.Composer',
'scope': Class.PropertyScope.PUBLIC, 'serializable': false,
'setter': null
});
// private
this.defineProp('frameDocument', {'dataType': DataType.OBJECT,
'scope': Class.PropertyScope.PUBLIC, 'serializable': false,
'setter': null,
'getter': function(){
return this.getElement().contentDocument;
}
});
this.defineProp('frameWindow', {'dataType': DataType.OBJECT,
'scope': Class.PropertyScope.PUBLIC, 'serializable': false,
'setter': null,
'getter': function(){
return Kekule.DocumentUtils.getDefaultView(this.getFrameDocument());
}
});
},
/** @ignore */
initPropValues: function($super)
{
$super();
this.setMinDimension({'width': 550, height: 350});
this.setEnableDimensionTransform(true);
},
/** @ignore */
doGetWidgetClassName: function($super)
{
return $super() + ' ' + CCNS.COMPOSER_FRAME;
},
/** @ignore */
doCreateRootElement: function(doc)
{
var result = doc.createElement('iframe');
return result;
},
/** @ignore */
doBindElement: function($super, element)
{
$super(element);
var notInDom = !element.parentNode;
if (notInDom) // add to DOM first, otherwise the frame document will be null
{
this.setDisplayed(false);
var doc = element.ownerDocument;
doc.body.appendChild(element);
}
//console.log(this.getElement(), this.getElement().contentDocument);
this._createComposerWidgetInFrame();
},
/** @private */
_initFrame: function()
{
var content = '<!DOCTYPE html><html><head></head><body></body></html>';
var doc = this.getFrameDocument();
doc.open('text/htmlreplace');
doc.write(content);
doc.close();
},
/** @private */
_createComposerWidgetInFrame: function(callback)
{
this._initFrame(); // replace the quirk mode empty document with a standard one
var self = this;
var doc = this.getFrameDocument();
doc.documentElement.className = CCNS.COMPOSER_FRAME_CONTENT_DOC;
doc.body.className = CCNS.COMPOSER_FRAME_CONTENT_BODY;
// doc type
/*
if (doc.implementation && doc.implementation.createDocumentType)
{
var nodeDoctype = doc.implementation.createDocumentType('html', '', '');
if (doc.doctype)
{
doc.replaceChild(nodeDoctype, doc.doctype);
}
else
{
doc.insertBefore(nodeDoctype, doc.childNodes[0]);
}
}
// <meta charset="UTF-8">
var metaElem = doc.createElement('meta');
metaElem.setAttribute('charset', 'UTF-8');
doc.head.insertBefore(metaElem, Kekule.DomUtils.getFirstChildElem(doc.head));
*/
Kekule.X.Event.addListener(doc.body, 'kekuleload', function(e){
var composer = new Kekule.Editor.Composer(doc);
/*
var style = composer.getElement().style;
style.width = '100%';
style.height = '100%';
*/
composer.appendToElem(doc.body);
self.setPropStoreFieldValue('composer', composer);
if (callback)
callback(composer);
});
// assume element is an iframe, and insert Kekule.js script/css files into it
this._insertKekuleScriptAndStyleSheetFiles(doc);
},
/** @private */
_insertKekuleScriptAndStyleSheetFiles: function(frameDoc, callback)
{
var srcInfo = Kekule.scriptSrcInfo;
var headElem = frameDoc.head;
var cssLinkElem = frameDoc.createElement('link');
cssLinkElem.setAttribute('rel', 'stylesheet');
cssLinkElem.setAttribute('type', 'text/css');
cssLinkElem.setAttribute('href', Kekule.getStyleSheetUrl());
headElem.appendChild(cssLinkElem);
Kekule.ScriptFileUtils.appendScriptFile(frameDoc, Kekule.getScriptSrc(), callback);
}
});
})();