Source: widgets/chem/editor/kekule.chemEditor.composers.js

  1. /**
  2. * @fileoverview
  3. * Implements an editor with essential UI.
  4. * @author Partridge Jiang
  5. */
  6. /*
  7. * requires /lan/classes.js
  8. * requires /utils/kekule.utils.js
  9. * requires /render/kekule.render.utils.js
  10. * requires /widgets/kekule.widget.base.js
  11. * requires /widgets/kekule.widget.helpers.js
  12. * requires /widgets/chem/kekule.chemWidget.base.js
  13. * requires /widgets/operation/kekule.actions.js
  14. * requires /widgets/commonCtrls/kekule.widget.buttons.js
  15. * requires /widgets/commonCtrls/kekule.widget.containers.js
  16. * requires /widgets/advCtrls/objInspector/kekule.widget.objInspectors.js
  17. * requires /widgets/chem/structureTreeView/kekule.chemWidget.structureTreeViews.js
  18. * requires /widgets/chem/editor/kekule.chemEditor.baseEditors.js
  19. * requires /widgets/chem/editor/kekule.chemEditor.nexus.js
  20. * requires /widgets/chem/editor/kekule.chemEditor.objModifiers.js
  21. * requires /localization
  22. */
  23. (function(){
  24. var PS = Class.PropertyScope;
  25. var AU = Kekule.ArrayUtils;
  26. var CW = Kekule.ChemWidget;
  27. var CE = Kekule.Editor;
  28. var CNS = Kekule.Widget.HtmlClassNames;
  29. var CCNS = Kekule.ChemWidget.HtmlClassNames;
  30. var BNS = Kekule.ChemWidget.ComponentWidgetNames;
  31. //var CWT = Kekule.ChemWidgetTexts;
  32. /** @ignore */
  33. Kekule.ChemWidget.HtmlClassNames = Object.extend(Kekule.ChemWidget.HtmlClassNames, {
  34. COMPOSER: 'K-Chem-Composer',
  35. COMPOSER_EDITOR_STAGE: 'K-Chem-Composer-Editor-Stage',
  36. COMPOSER_ADV_PANEL: 'K-Chem-Composer-Adv-Panel',
  37. COMPOSER_TOP_REGION: 'K-Chem-Composer-Top-Region',
  38. COMPOSER_LEFT_REGION: 'K-Chem-Composer-Left-Region',
  39. COMPOSER_BOTTOM_REGION: 'K-Chem-Composer-Bottom-Region',
  40. COMPOSER_TOOLBAR: 'K-Chem-Composer-Toolbar',
  41. COMPOSER_COMMON_TOOLBAR: 'K-Chem-Composer-Common-Toolbar',
  42. COMPOSER_ZOOM_TOOLBAR: 'K-Chem-Composer-Zoom-Toolbar',
  43. COMPOSER_CHEM_TOOLBAR: 'K-Chem-Composer-Chem-Toolbar',
  44. COMPOSER_ASSOC_TOOLBAR: 'K-Chem-Composer-Assoc-Toolbar',
  45. COMPOSER_STYLE_TOOLBAR: 'K-Chem-Composer-Style-Toolbar',
  46. COMPOSER_OBJMODIFIER_TOOLBAR: 'K-Chem-Composer-ObjModifier-Toolbar',
  47. COMPOSER_FONTNAME_BOX: 'K-Chem-Composer-FontName-Box',
  48. COMPOSER_FONTSIZE_BOX: 'K-Chem-Composer-FontSize-Box',
  49. COMPOSER_COLOR_BOX: 'K-Chem-Composer-Color-Box',
  50. COMPOSER_TEXTDIRECTION_BUTTON: 'K-Chem-Composer-TextDirection-Button',
  51. COMPOSER_TEXTDIRECTION_BUTTON_DEFAULT: 'K-Chem-Composer-TextDirection-Button-Default',
  52. COMPOSER_TEXTDIRECTION_BUTTON_LTR: 'K-Chem-Composer-TextDirection-Button-LTR',
  53. COMPOSER_TEXTDIRECTION_BUTTON_RTL: 'K-Chem-Composer-TextDirection-Button-RTL',
  54. COMPOSER_TEXTDIRECTION_BUTTON_TTB: 'K-Chem-Composer-TextDirection-Button-TTB',
  55. COMPOSER_TEXTDIRECTION_BUTTON_BTT: 'K-Chem-Composer-TextDirection-Button-BTT',
  56. COMPOSER_TEXTALIGN_BUTTON: 'K-Chem-Composer-TextAlign-Button',
  57. COMPOSER_TEXTALIGN_BUTTON_HORIZONTAL: 'K-Chem-Composer-TextAlign-Button-Horizontal',
  58. COMPOSER_TEXTALIGN_BUTTON_VERTICAL: 'K-Chem-Composer-TextAlign-Button-Vertical',
  59. COMPOSER_TEXTALIGN_BUTTON_DEFAULT: 'K-Chem-Composer-TextAlign-Button',
  60. COMPOSER_TEXTALIGN_BUTTON_LEADING: 'K-Chem-Composer-TextAlign-Button-Leading',
  61. COMPOSER_TEXTALIGN_BUTTON_TRAILING: 'K-Chem-Composer-TextAlign-Button-Trailing',
  62. COMPOSER_TEXTALIGN_BUTTON_CENTER: 'K-Chem-Composer-TextAlign-Button-Center',
  63. COMPOSER_TEXTALIGN_BUTTON_LEFT: 'K-Chem-Composer-TextAlign-Button-Left',
  64. COMPOSER_TEXTALIGN_BUTTON_RIGHT: 'K-Chem-Composer-TextAlign-Right',
  65. COMPOSER_TEXTALIGN_BUTTON_TOP: 'K-Chem-Composer-TextAlign-Top',
  66. COMPOSER_TEXTALIGN_BUTTON_BOTTOM: 'K-Chem-Composer-TextAlign-Bottom',
  67. COMPOSER_DIALOG: 'K-Chem-ComposerDialog', //'K-Chem-Viewer-Assoc-Editor'
  68. COMPOSER_FRAME: 'K-Chem-ComposerFrame',
  69. COMPOSER_FRAME_CONTENT_DOC: 'K-Chem-ComposerFrame-ContentDoc',
  70. COMPOSER_FRAME_CONTENT_BODY: 'K-Chem-ComposerFrame-ContentBody'
  71. });
  72. Kekule.globalOptions.add('chemWidget.composer', {
  73. commonToolButtons: [
  74. BNS.newDoc,
  75. //BNS.loadFile,
  76. BNS.loadData,
  77. BNS.saveData,
  78. BNS.undo,
  79. BNS.redo,
  80. BNS.copy,
  81. BNS.cut,
  82. BNS.paste,
  83. //BNS.cloneSelection,
  84. BNS.zoomIn,
  85. //BNS.reset,
  86. //BNS.resetZoom,
  87. BNS.zoomOut,
  88. BNS.config,
  89. BNS.objInspector
  90. ],
  91. chemToolButtons: [
  92. BNS.manipulate,
  93. BNS.erase,
  94. BNS.molBond,
  95. //BNS.molAtom,
  96. //BNS.molFormula,
  97. BNS.molAtomAndFormula,
  98. BNS.molRing,
  99. BNS.molCharge,
  100. BNS.glyph,
  101. BNS.textImage
  102. ],
  103. styleToolComponentNames: [
  104. BNS.fontName,
  105. BNS.fontSize,
  106. BNS.color,
  107. BNS.textDirection,
  108. BNS.textAlign
  109. ]
  110. });
  111. /**
  112. * The style toolbar for composer.
  113. * @class
  114. * @augments Kekule.Widget.Toolbar
  115. * @param {Kekule.Editor.Composer} composer Parent composer.
  116. *
  117. * @property {Kekule.Editor.Composer} composer Parent composer.
  118. * @property {String} fontName Font name setted in toolbar.
  119. * @property {Number} fontSize Font size setted in toolbar, in px.
  120. * @property {Int} textAlign Text align setted in toolbar.
  121. *
  122. * @property {Array} components Array of component names that shows in tool bar.
  123. */
  124. Kekule.Editor.ComposerStyleToolbar = Class.create(Kekule.Widget.Toolbar,
  125. /** @lends Kekule.Editor.ComposerStyleToolbar# */
  126. {
  127. /** @private */
  128. CLASS_NAME: 'Kekule.Editor.ComposerStyleToolbar',
  129. /**
  130. * @private
  131. * @ignore
  132. */
  133. ATOM_COLOR_SPECIAL_VALUE_INFO: {
  134. text: Kekule.$L('ChemWidgetTexts.HINT_USE_ATOM_CUSTOM_COLOR'),
  135. value: 'Atom',
  136. className: CNS.COLORPICKER_SPEC_COLOR_MIXED
  137. },
  138. /** @private */
  139. LINKED_VALUE_FIELD: '__$value__',
  140. /** @constructs */
  141. initialize: function($super, composer)
  142. {
  143. $super(composer);
  144. this.setPropStoreFieldValue('composer', composer);
  145. this.createChildWidgets();
  146. this.appendToWidget(composer);
  147. this._relatedPropNames = (composer.getCoordMode() === Kekule.CoordMode.COORD3D)?
  148. ['renderOptions', 'render3DOptions']: ['renderOptions'];
  149. this.getEditor().addEventListener('selectionChange', function(e){
  150. if (!this._isApplying)
  151. this.updateStyleValues();
  152. }, this);
  153. this.getEditor().addEventListener('editObjChanged', function(e){
  154. var propNames = e.propNames;
  155. if (!propNames || !propNames.length || !Kekule.ArrayUtils.intersect(this._relatedPropNames, propNames).length) // not changing render options
  156. {
  157. return;
  158. }
  159. if (!this._isApplying)
  160. this.updateStyleValues();
  161. }, this);
  162. this._isApplying = false; // private
  163. },
  164. /** @private */
  165. doFinalize: function($super)
  166. {
  167. this.clearWidgets(); // already clear in $super()
  168. $super();
  169. },
  170. /** @private */
  171. initProperties: function()
  172. {
  173. this.defineProp('composer', {'dataType': 'Kekule.Editor.Composer', 'serializable': false, 'setter': null});
  174. this.defineProp('fontNameBox', {'dataType': 'Kekule.Widget.ComboBox', 'serializable': false, 'setter': null});
  175. this.defineProp('fontSizeBox', {'dataType': 'Kekule.Widget.ComboBox', 'serializable': false, 'setter': null});
  176. this.defineProp('colorBox', {'dataType': 'Kekule.Widget.ColorDropButton', 'serializable': false, 'setter': null});
  177. this.defineProp('textDirectionButtonSet', {'dataType': 'Kekule.Widget.CompactButtonSet', 'serializable': false, 'setter': null});
  178. this.defineProp('textHorizontalAlignButtonSet', {'dataType': 'Kekule.Widget.CompactButtonSet', 'serializable': false, 'setter': null});
  179. this.defineProp('textVerticalAlignButtonSet', {'dataType': 'Kekule.Widget.CompactButtonSet', 'serializable': false, 'setter': null});
  180. this.defineProp('fontName', {'dataType': DataType.STRING, 'serializable': false,
  181. 'getter': function() { return this.getFontNameBox()? this.getFontNameBox().getValue(): null; },
  182. 'setter': function(value) { if (this.getFontNameBox()) { this.getFontNameBox().setValue(value); } }
  183. });
  184. this.defineProp('fontSize', {'dataType': DataType.NUMBER, 'serializable': false,
  185. 'getter': function() { return this.getFontSizeBox()? parseFloat(this.getFontSizeBox().getValue()): null; },
  186. 'setter': function(value) { if (this.getFontSizeBox()) { this.getFontSizeBox().setValue(value); } }
  187. });
  188. this.defineProp('color', {'dataType': DataType.STRING, 'serializable': false,
  189. 'getter': function() { return this.getColorBox()? this.getColorBox().getValue(): undefined; },
  190. 'setter': function(value) { if (this.getColorBox()) { this.getColorBox().setValue(value); } }
  191. });
  192. this.defineProp('textDirection', {'dataType': DataType.INT, 'serializable': false,
  193. 'getter': function()
  194. {
  195. return this._getButtonSetLinkedValue(this.getTextDirectionButtonSet());
  196. },
  197. 'setter': function(value)
  198. {
  199. this._setButtonSetLinkedValue(this.getTextDirectionButtonSet(), value);
  200. }
  201. });
  202. this.defineProp('textHorizontalAlign', {'dataType': DataType.INT, 'serializable': false,
  203. 'getter': function()
  204. {
  205. return this._getButtonSetLinkedValue(this.getTextHorizontalAlignButtonSet());
  206. },
  207. 'setter': function(value)
  208. {
  209. this._setButtonSetLinkedValue(this.getTextHorizontalAlignButtonSet(), value);
  210. }
  211. });
  212. this.defineProp('textVerticalAlign', {'dataType': DataType.INT, 'serializable': false,
  213. 'getter': function()
  214. {
  215. return this._getButtonSetLinkedValue(this.getTextVerticalAlignButtonSet());
  216. },
  217. 'setter': function(value)
  218. {
  219. this._setButtonSetLinkedValue(this.getTextVerticalAlignButtonSet(), value);
  220. }
  221. });
  222. this.defineProp('componentNames', {'dataType': DataType.ARRAY, 'serializable': false,
  223. 'getter': function()
  224. {
  225. var result = this.getPropStoreFieldValue('componentNames');
  226. if (!result) // create default one
  227. {
  228. result = this.getDefaultComponentNames();
  229. this.setPropStoreFieldValue('componentNames', result);
  230. }
  231. return result;
  232. },
  233. 'setter': function(value)
  234. {
  235. this.setPropStoreFieldValue('componentNames', value);
  236. this.recreateComponents();
  237. }
  238. });
  239. },
  240. /** @ignore */
  241. initPropValues: function($super)
  242. {
  243. $super();
  244. this.setShowGlyph(true);
  245. },
  246. /** @ignore */
  247. removeWidget: function($super, widget, doNotFinalize)
  248. {
  249. if (widget === this.getFontNameBox())
  250. this.setPropStoreFieldValue('fontNameBox', null);
  251. if (widget === this.getFontSizeBox())
  252. this.setPropStoreFieldValue('fontSizeBox', null);
  253. if (widget === this.getColorBox())
  254. this.setPropStoreFieldValue('colorBox', null);
  255. if (widget === this.getTextDirectionButtonSet())
  256. this.setPropStoreFieldValue('textDirectionButtonSet', null);
  257. if (widget === this.getTextHorizontalAlignButtonSet())
  258. this.setPropStoreFieldValue('textHorizontalAlignButtonSet', null);
  259. if (widget === this.getTextVerticalAlignButtonSet())
  260. this.setPropStoreFieldValue('textVerticalAlignButtonSet', null);
  261. $super(widget, doNotFinalize);
  262. },
  263. /** @ignore */
  264. doGetWidgetClassName: function($super)
  265. {
  266. var result = $super() + ' ' + CCNS.COMPOSER_TOOLBAR + ' ' + CCNS.COMPOSER_STYLE_TOOLBAR;
  267. return result;
  268. },
  269. /** @private */
  270. getEditor: function()
  271. {
  272. return this.getComposer().getEditor();
  273. },
  274. /** @private */
  275. getEditorConfigs: function()
  276. {
  277. return this.getEditor().getEditorConfigs();
  278. },
  279. /** @private */
  280. createChildWidgets: function(componentNames)
  281. {
  282. if (!componentNames)
  283. componentNames = this.getComponentNames();
  284. for (var i = 0, l = componentNames.length; i < l; ++i)
  285. {
  286. var name = componentNames[i];
  287. if (name === BNS.fontName)
  288. {
  289. // font name
  290. var comboBox = new Kekule.Widget.ComboBox(this);
  291. this.fillFontNameBox(comboBox);
  292. comboBox.addClassName(CCNS.COMPOSER_FONTNAME_BOX);
  293. comboBox.setHint(/*CWT.HINT_FONTNAME*/Kekule.$L('ChemWidgetTexts.HINT_FONTNAME'));
  294. comboBox.addEventListener('valueChange', function(e)
  295. {
  296. this.applyFontName();
  297. }, this);
  298. this.setPropStoreFieldValue('fontNameBox', comboBox);
  299. }
  300. else if (name === BNS.fontSize)
  301. {
  302. // font size
  303. comboBox = new Kekule.Widget.ComboBox(this);
  304. this.fillFontSizeBox(comboBox);
  305. comboBox.addClassName(CCNS.COMPOSER_FONTSIZE_BOX);
  306. comboBox.setHint(/*CWT.HINT_FONTSIZE*/Kekule.$L('ChemWidgetTexts.HINT_FONTSIZE'));
  307. comboBox.addEventListener('valueChange', function(e)
  308. {
  309. this.applyFontSize();
  310. }, this);
  311. this.setPropStoreFieldValue('fontSizeBox', comboBox);
  312. }
  313. else if (name === BNS.color)
  314. {
  315. // color box
  316. var colorBox = new Kekule.Widget.ColorDropButton(this);
  317. colorBox.setHint(/*CWT.HINT_PICK_COLOR*/Kekule.$L('ChemWidgetTexts.HINT_PICK_COLOR'));
  318. colorBox.setShowText(false);
  319. colorBox.setSpecialColors([Kekule.Widget.ColorPicker.SpecialColors.UNSET, this.ATOM_COLOR_SPECIAL_VALUE_INFO]);
  320. colorBox.addClassName(CCNS.COMPOSER_COLOR_BOX);
  321. colorBox.addEventListener('valueChange', function(e)
  322. {
  323. this.applyColor();
  324. }, this);
  325. this.setPropStoreFieldValue('colorBox', colorBox);
  326. }
  327. else if (name === BNS.textDirection)
  328. {
  329. // text direction button
  330. // its drop downs
  331. var TD = Kekule.Render.TextDirection;
  332. var childInfos = [
  333. {'value': TD.DEFAULT, 'text': /*CWT.CAPTION_TEXT_DIRECTION_DEFAULT*/Kekule.$L('ChemWidgetTexts.CAPTION_TEXT_DIRECTION_DEFAULT'), className: CCNS.COMPOSER_TEXTDIRECTION_BUTTON_DEFAULT},
  334. {'value': TD.LTR, 'text': /*CWT.CAPTION_TEXT_DIRECTION_LTR*/Kekule.$L('ChemWidgetTexts.CAPTION_TEXT_DIRECTION_LTR'), className: CCNS.COMPOSER_TEXTDIRECTION_BUTTON_LTR},
  335. {'value': TD.RTL, 'text': /*CWT.CAPTION_TEXT_DIRECTION_RTL*/Kekule.$L('ChemWidgetTexts.CAPTION_TEXT_DIRECTION_RTL'), className: CCNS.COMPOSER_TEXTDIRECTION_BUTTON_RTL},
  336. {'value': TD.TTB, 'text': /*CWT.CAPTION_TEXT_DIRECTION_TTB*/Kekule.$L('ChemWidgetTexts.CAPTION_TEXT_DIRECTION_TTB'), className: CCNS.COMPOSER_TEXTDIRECTION_BUTTON_TTB},
  337. {'value': TD.BTT, 'text': /*CWT.CAPTION_TEXT_DIRECTION_BTT*/Kekule.$L('ChemWidgetTexts.CAPTION_TEXT_DIRECTION_BTT'), className: CCNS.COMPOSER_TEXTDIRECTION_BUTTON_BTT}
  338. ];
  339. var btnSet = this._createStyleButtonSet(
  340. /*CWT.CAPTION_TEXT_DIRECTION, CWT.HINT_TEXT_DIRECTION,*/
  341. Kekule.$L('ChemWidgetTexts.CAPTION_TEXT_DIRECTION'), Kekule.$L('ChemWidgetTexts.HINT_TEXT_DIRECTION'),
  342. CCNS.COMPOSER_TEXTDIRECTION_BUTTON, childInfos);
  343. btnSet.addEventListener('select', function(e)
  344. {
  345. this.applyTextDirection();
  346. }, this);
  347. this.setPropStoreFieldValue('textDirectionButtonSet', btnSet);
  348. }
  349. else if (name === BNS.textAlign)
  350. {
  351. // horizontal text align button
  352. var TA = Kekule.Render.TextAlign;
  353. var childInfos = [
  354. {'value': TA.DEFAULT, 'text': /*CWT.CAPTION_TEXT_ALIGN_DEFAULT*/Kekule.$L('ChemWidgetTexts.CAPTION_TEXT_ALIGN_DEFAULT'), className: CCNS.COMPOSER_TEXTALIGN_BUTTON_DEFAULT},
  355. {'value': TA.LEADING, 'text': /*CWT.CAPTION_TEXT_ALIGN_LEADING*/Kekule.$L('ChemWidgetTexts.CAPTION_TEXT_ALIGN_LEADING'), className: CCNS.COMPOSER_TEXTALIGN_BUTTON_LEADING},
  356. {'value': TA.TRAILING, 'text': /*CWT.CAPTION_TEXT_ALIGN_TRAILING*/Kekule.$L('ChemWidgetTexts.CAPTION_TEXT_ALIGN_TRAILING'), className: CCNS.COMPOSER_TEXTALIGN_BUTTON_TRAILING},
  357. {'value': TA.CENTER, 'text': /*CWT.CAPTION_TEXT_ALIGN_CENTER*/Kekule.$L('ChemWidgetTexts.CAPTION_TEXT_ALIGN_CENTER'), className: CCNS.COMPOSER_TEXTALIGN_BUTTON_CENTER},
  358. {'value': TA.LEFT, 'text': /*CWT.CAPTION_TEXT_ALIGN_LEFT*/Kekule.$L('ChemWidgetTexts.CAPTION_TEXT_ALIGN_LEFT'), className: CCNS.COMPOSER_TEXTALIGN_BUTTON_LEFT},
  359. {'value': TA.RIGHT, 'text': /*CWT.CAPTION_TEXT_ALIGN_RIGHT*/Kekule.$L('ChemWidgetTexts.CAPTION_TEXT_ALIGN_RIGHT'), className: CCNS.COMPOSER_TEXTALIGN_BUTTON_RIGHT}
  360. ];
  361. var btnSet = this._createStyleButtonSet(
  362. /*CWT.CAPTION_TEXT_HORIZONTAL_ALIGN, CWT.HINT_TEXT_HORIZONTAL_ALIGN,*/
  363. Kekule.$L('ChemWidgetTexts.CAPTION_TEXT_HORIZONTAL_ALIGN'), Kekule.$L('ChemWidgetTexts.HINT_TEXT_HORIZONTAL_ALIGN'),
  364. CCNS.COMPOSER_TEXTALIGN_BUTTON_HORIZONTAL, childInfos);
  365. btnSet.addEventListener('select', function(e)
  366. {
  367. this.applyTextHorizontalAlign();
  368. }, this);
  369. this.setPropStoreFieldValue('textHorizontalAlignButtonSet', btnSet);
  370. // vertical text align button
  371. var TA = Kekule.Render.TextAlign;
  372. var childInfos = [
  373. {'value': TA.DEFAULT, 'text': /*CWT.CAPTION_TEXT_ALIGN_DEFAULT*/Kekule.$L('ChemWidgetTexts.CAPTION_TEXT_ALIGN_DEFAULT'), className: CCNS.COMPOSER_TEXTALIGN_BUTTON_DEFAULT},
  374. {'value': TA.LEADING, 'text': /*CWT.CAPTION_TEXT_ALIGN_LEADING*/Kekule.$L('ChemWidgetTexts.CAPTION_TEXT_ALIGN_LEADING'), className: CCNS.COMPOSER_TEXTALIGN_BUTTON_LEADING},
  375. {'value': TA.TRAILING, 'text': /*CWT.CAPTION_TEXT_ALIGN_TRAILING*/Kekule.$L('ChemWidgetTexts.CAPTION_TEXT_ALIGN_TRAILING'), className: CCNS.COMPOSER_TEXTALIGN_BUTTON_TRAILING},
  376. {'value': TA.CENTER, 'text': /*CWT.CAPTION_TEXT_ALIGN_CENTER*/Kekule.$L('ChemWidgetTexts.CAPTION_TEXT_ALIGN_CENTER'), className: CCNS.COMPOSER_TEXTALIGN_BUTTON_CENTER},
  377. {'value': TA.TOP, 'text': /*CWT.CAPTION_TEXT_ALIGN_TOP*/Kekule.$L('ChemWidgetTexts.CAPTION_TEXT_ALIGN_TOP'), className: CCNS.COMPOSER_TEXTALIGN_BUTTON_TOP},
  378. {'value': TA.BOTTOM, 'text': /*CWT.CAPTION_TEXT_ALIGN_BOTTOM*/Kekule.$L('ChemWidgetTexts.CAPTION_TEXT_ALIGN_BOTTOM'), className: CCNS.COMPOSER_TEXTALIGN_BUTTON_BOTTOM}
  379. ];
  380. var btnSet = this._createStyleButtonSet(
  381. /*CWT.CAPTION_TEXT_VERTICAL_ALIGN, CWT.HINT_TEXT_VERTICAL_ALIGN,*/
  382. Kekule.$L('ChemWidgetTexts.CAPTION_TEXT_VERTICAL_ALIGN'), Kekule.$L('ChemWidgetTexts.HINT_TEXT_VERTICAL_ALIGN'),
  383. CCNS.COMPOSER_TEXTALIGN_BUTTON_VERTICAL, childInfos);
  384. btnSet.addEventListener('select', function(e)
  385. {
  386. this.applyTextVerticalAlign();
  387. }, this);
  388. this.setPropStoreFieldValue('textVerticalAlignButtonSet', btnSet);
  389. }
  390. }
  391. this.updateStyleValues();
  392. },
  393. /** @private */
  394. _createStyleButtonSet: function(text, hint, className, childItemInfos)
  395. {
  396. var btnSet = new Kekule.Widget.CompactButtonSet(this);
  397. btnSet.setText(text);
  398. btnSet.setHint(hint);
  399. btnSet.addClassName(className);
  400. btnSet.setShowText(false);
  401. // show drop down mark rather than compact mark, keep a similar outlook to other widgets
  402. btnSet.setShowCompactMark(false);
  403. btnSet.setButtonKind(Kekule.Widget.Button.Kinds.DROPDOWN);
  404. btnSet.getButtonSet().addClassName(CCNS.COMPOSER_STYLE_TOOLBAR);
  405. btnSet.getButtonSet().setShowText(true);
  406. // drop down children
  407. if (childItemInfos)
  408. {
  409. for (var i = 0, l = childItemInfos.length; i < l; ++i)
  410. {
  411. var info = childItemInfos[i];
  412. var btn = new Kekule.Widget.RadioButton(btnSet, info.text || '');
  413. btn.setHint(info.hint || '');
  414. btn.addClassName(info.className);
  415. btn[this.LINKED_VALUE_FIELD] = info.value;
  416. btnSet.append(btn, info.selected);
  417. }
  418. }
  419. return btnSet;
  420. },
  421. /** @private */
  422. _getButtonSetLinkedValue: function(btnSet)
  423. {
  424. var selected = btnSet? btnSet.getSelected(): null;
  425. return selected? selected[this.LINKED_VALUE_FIELD]: undefined;
  426. },
  427. /** @private */
  428. _setButtonSetLinkedValue: function(btnSet, value)
  429. {
  430. if (btnSet)
  431. {
  432. var group = btnSet.getButtonSet();
  433. var children = group.getChildWidgets();
  434. btnSet.setSelected(null); // uncheck all first
  435. for (var i = 0 , l = children.length; i < l; ++i)
  436. {
  437. var child = children[i];
  438. if (child.hasOwnProperty(this.LINKED_VALUE_FIELD))
  439. {
  440. if (child[this.LINKED_VALUE_FIELD] == value)
  441. {
  442. btnSet.setSelected(child);
  443. break;
  444. }
  445. }
  446. }
  447. }
  448. },
  449. /** @private */
  450. getDefaultComponentNames: function()
  451. {
  452. return Kekule.globalOptions.chemWidget.composer.styleToolComponentNames;
  453. /*
  454. return [
  455. BNS.fontName,
  456. BNS.fontSize,
  457. BNS.color,
  458. BNS.textDirection,
  459. BNS.textAlign
  460. ];
  461. */
  462. },
  463. /** @private */
  464. recreateComponents: function()
  465. {
  466. var cnames = this.getComponentNames();
  467. this.clearWidgets();
  468. this.createChildWidgets(cnames);
  469. },
  470. /** @private */
  471. fillFontSizeBox: function(sizeComboBox)
  472. {
  473. var listedSizes = this.getEditorConfigs().getStyleSetterConfigs().getListedFontSizes();
  474. var boxItems = [{'text': /*Kekule.ChemWidgetTexts.S_VALUE_DEFAULT*/Kekule.$L('ChemWidgetTexts.S_VALUE_DEFAULT'), 'value': undefined}];
  475. for (var i = 0, l = listedSizes.length; i < l; ++i)
  476. {
  477. boxItems.push({'text': listedSizes[i] + ' px', 'value': listedSizes[i]});
  478. }
  479. sizeComboBox.setItems(boxItems);
  480. },
  481. /** @private */
  482. fillFontNameBox: function(fontComboBox)
  483. {
  484. var listedNames = this.getEditorConfigs().getStyleSetterConfigs().getListedFontNames();
  485. var boxItems = [{'text': /*Kekule.ChemWidgetTexts.S_VALUE_DEFAULT*/Kekule.$L('ChemWidgetTexts.S_VALUE_DEFAULT'), 'value': ''}];
  486. for (var i = 0, l = listedNames.length; i < l; ++i)
  487. {
  488. boxItems.push({'text': listedNames[i], 'value': listedNames[i]});
  489. }
  490. fontComboBox.setItems(boxItems);
  491. },
  492. /**
  493. * Apply style settings to objects in editor.
  494. * @param {Array} chemObjs
  495. * @param {Hash} styles Hash of style. {renderOptionName: value}.
  496. * @param {Bool} is3DOption If true, styles will be put to Render3DOptions, otherwise RenderOptions will be set.
  497. * @private
  498. */
  499. applyStyle: function(chemObjs, styles, is3DOption)
  500. {
  501. this._isApplying = true;
  502. try
  503. {
  504. //Kekule.Render.RenderOptionUtils.setRenderOptionValueOfObjs(chemObjs, styles, is3DOption);
  505. var editor = this.getEditor();
  506. editor.modifyObjectsRenderOptions(chemObjs, styles, is3DOption, true);
  507. }
  508. finally
  509. {
  510. this._isApplying = false;
  511. }
  512. },
  513. /**
  514. * Returns common render option or render 3D option value of chemObjs.
  515. * If values in objects are not same, null will be returned.
  516. * @param {Array} chemObjs
  517. * @param {String} stylePropName
  518. * @param {Bool} is3DOption
  519. * @returns {Variant}
  520. * @private
  521. */
  522. getStyleValue: function(chemObjs, stylePropName, is3DOption)
  523. {
  524. return Kekule.Render.RenderOptionUtils.getCascadeRenderOptionValueOfObjs(chemObjs, stylePropName, is3DOption);
  525. },
  526. /**
  527. * Apply styles to selected objects in editor.
  528. * @private
  529. */
  530. applyStyleToEditorSelection: function(styles, is3DOption)
  531. {
  532. var objs = this.getEditor().getSelection();
  533. if (objs && objs.length)
  534. return this.applyStyle(objs, styles, is3DOption);
  535. },
  536. /** @private */
  537. applyFontName: function(objs)
  538. {
  539. if (!objs)
  540. objs = this.getEditor().getSelection();
  541. if (objs && objs.length)
  542. {
  543. var fontName = this.getFontName();
  544. this.applyStyle(objs, {'fontFamily': fontName/*, 'atomFontFamily': fontName*/});
  545. }
  546. },
  547. /** @private */
  548. applyFontSize: function(objs)
  549. {
  550. if (!objs)
  551. objs = this.getEditor().getSelection();
  552. if (objs && objs.length)
  553. {
  554. var fontSize = this.getFontSize();
  555. this.applyStyle(objs, {'fontSize': fontSize/*, 'atomFontSize': fontSize*/});
  556. }
  557. },
  558. /** @private */
  559. applyColor: function(objs)
  560. {
  561. if (!objs)
  562. objs = this.getEditor().getSelection();
  563. if (objs && objs.length)
  564. {
  565. var color = this.getColor();
  566. if (color === this.ATOM_COLOR_SPECIAL_VALUE_INFO.value)
  567. {
  568. this.applyStyle(objs, {'useAtomSpecifiedColor': true, 'color': undefined});
  569. }
  570. else
  571. {
  572. if (color == Kekule.Widget.ColorPicker.SpecialColors.UNSET)
  573. color = undefined;
  574. this.applyStyle(objs, {'useAtomSpecifiedColor': false, 'color': color});
  575. }
  576. }
  577. },
  578. /** @private */
  579. applyTextDirection: function(objs)
  580. {
  581. if (!objs)
  582. objs = this.getEditor().getSelection();
  583. if (objs && objs.length)
  584. {
  585. var direction = this.getTextDirection();
  586. this.applyStyle(objs, {'charDirection': direction});
  587. }
  588. },
  589. /** @private */
  590. applyTextHorizontalAlign: function(objs)
  591. {
  592. if (!objs)
  593. objs = this.getEditor().getSelection();
  594. if (objs && objs.length)
  595. {
  596. var value = this.getTextHorizontalAlign();
  597. this.applyStyle(objs, {'horizontalAlign': value});
  598. }
  599. },
  600. /** @private */
  601. applyTextVerticalAlign: function(objs)
  602. {
  603. if (!objs)
  604. objs = this.getEditor().getSelection();
  605. if (objs && objs.length)
  606. {
  607. var value = this.getTextVerticalAlign();
  608. this.applyStyle(objs, {'verticalAlign': value});
  609. }
  610. },
  611. /** @private */
  612. updateStyleValues: function(objs)
  613. {
  614. if (!objs)
  615. objs = this.getEditor().getSelection();
  616. if (objs && objs.length)
  617. {
  618. this.updateFontName(objs);
  619. this.updateFontSize(objs);
  620. this.updateColor(objs);
  621. this.updateTextDirection(objs);
  622. this.updateTextHorizontalAlign(objs);
  623. this.updateTextVerticalAlign(objs);
  624. }
  625. },
  626. /** @private */
  627. updateFontName: function(objs)
  628. {
  629. if (objs && objs.length && this.getFontNameBox())
  630. {
  631. var fontName = this.getStyleValue(objs, 'fontFamily'); // TODO: atom font family?
  632. this.setFontName(fontName || '');
  633. }
  634. },
  635. /** @private */
  636. updateFontSize: function(objs)
  637. {
  638. if (objs && objs.length && this.getFontSizeBox())
  639. {
  640. var fontSize = this.getStyleValue(objs, 'fontSize'); // TODO: atom font size?
  641. this.setFontSize(fontSize || undefined);
  642. }
  643. },
  644. /** @private */
  645. updateColor: function(objs)
  646. {
  647. if (objs && objs.length && this.getColorBox())
  648. {
  649. var color;
  650. var useAtomSpecified = this.getStyleValue(objs, 'useAtomSpecifiedColor');
  651. var color = this.getStyleValue(objs, 'color');
  652. this.getColorBox().setColorClassName(null);
  653. if (color)
  654. {
  655. this.getColorBox().setColorClassName(null);
  656. }
  657. else if (useAtomSpecified)
  658. {
  659. color = this.ATOM_COLOR_SPECIAL_VALUE_INFO.value;
  660. this.getColorBox().setColorClassName(this.ATOM_COLOR_SPECIAL_VALUE_INFO.className);
  661. }
  662. this.setColor(color || undefined);
  663. }
  664. },
  665. /** @private */
  666. updateTextDirection: function(objs)
  667. {
  668. if (objs && objs.length && this.getTextDirectionButtonSet())
  669. {
  670. var value = this.getStyleValue(objs, 'charDirection');
  671. this.setTextDirection(value);
  672. /*
  673. if (Kekule.ObjUtils.notUnset(value))
  674. {
  675. }
  676. */
  677. }
  678. },
  679. /** @private */
  680. updateTextHorizontalAlign: function(objs)
  681. {
  682. if (objs && objs.length && this.getTextHorizontalAlignButtonSet())
  683. {
  684. var value = this.getStyleValue(objs, 'horizontalAlign');
  685. this.setTextHorizontalAlign(value);
  686. }
  687. },
  688. /** @private */
  689. updateTextVerticalAlign: function(objs)
  690. {
  691. if (objs && objs.length && this.getTextVerticalAlignButtonSet())
  692. {
  693. var value = this.getStyleValue(objs, 'verticalAlign');
  694. this.setTextVerticalAlign(value);
  695. }
  696. },
  697. /**
  698. * Update toolbar and child widget outlook and other settings according to editor's state.
  699. */
  700. updateState: function()
  701. {
  702. var editor = this.getEditor();
  703. var hasSelection = (editor && editor.hasSelection());
  704. //this.setEnabled(hasSelection);
  705. /*
  706. var children = [
  707. this.getFontNameBox(),
  708. this.getFontSizeBox(),
  709. this.getColorBox(),
  710. this.getTextDirectionButtonSet(),
  711. this.getTextAlignButtonSet()
  712. ];
  713. */
  714. if (this.getFontNameBox())
  715. this.getFontNameBox().setEnabled(hasSelection);
  716. if (this.getFontSizeBox())
  717. this.getFontSizeBox().setEnabled(hasSelection);
  718. if (this.getColorBox())
  719. this.getColorBox().setEnabled(hasSelection);
  720. if (this.getTextDirectionButtonSet())
  721. this.getTextDirectionButtonSet().setEnabled(hasSelection);
  722. if (this.getTextHorizontalAlignButtonSet())
  723. this.getTextHorizontalAlignButtonSet().setEnabled(hasSelection);
  724. if (this.getTextVerticalAlignButtonSet())
  725. this.getTextVerticalAlignButtonSet().setEnabled(hasSelection);
  726. }
  727. });
  728. /**
  729. * The style toolbar for composer.
  730. * @class
  731. * @augments Kekule.Widget.Toolbar
  732. * @param {Kekule.Editor.Composer} composer Parent composer.
  733. *
  734. * @property {Kekule.Editor.Composer} composer Parent composer.
  735. * @property {Array} components Array of component names that shows in tool bar.
  736. */
  737. Kekule.Editor.ComposerObjModifierToolbar = Class.create(Kekule.Widget.Toolbar,
  738. /** @lends Kekule.Editor.ComposerObjModifierToolbar# */
  739. {
  740. /** @private */
  741. CLASS_NAME: 'Kekule.Editor.ComposerObjModifierToolbar',
  742. /** @constructs */
  743. initialize: function($super, composer)
  744. {
  745. $super(composer);
  746. this.setPropStoreFieldValue('modifiers', []);
  747. this.setPropStoreFieldValue('modifierMap', new Kekule.MapEx());
  748. this.setPropStoreFieldValue('composer', composer);
  749. this.appendToWidget(composer);
  750. composer.addEventListener('selectionChange', function(e){
  751. if (!this._isApplying)
  752. this.updateModifierValues();
  753. }, this);
  754. composer.addEventListener('selectedObjsUpdated', function(e){
  755. /*
  756. var propNames = e.propNames;
  757. if (!propNames || !propNames.length || !Kekule.ArrayUtils.intersect(this._relatedPropNames, propNames).length) // not changing render options
  758. {
  759. return;
  760. }
  761. */
  762. if (!this._isApplying)
  763. {
  764. if (composer.getEditor().isManipulatingObject())
  765. this._suppressUpdateModifierValuesInManipulation = true;
  766. else
  767. {
  768. //console.log('selectedObjsUpdated', composer.getEditor()._objectManipulateFlag, e);
  769. this.updateModifierValues();
  770. }
  771. }
  772. }, this);
  773. composer.addEventListener('endManipulateObject', function(e){
  774. if (this._suppressUpdateModifierValuesInManipulation)
  775. {
  776. this.updateModifierValues();
  777. this._suppressUpdateModifierValuesInManipulation = false;
  778. }
  779. }, this);
  780. this._isApplying = false; // private
  781. },
  782. /** @private */
  783. initProperties: function()
  784. {
  785. this.defineProp('composer', {'dataType': 'Kekule.Editor.Composer', 'serializable': false, 'setter': null});
  786. this.defineProp('modifierMap', {'dataType': DataType.OBJECT, 'serializable': false, 'setter': null});
  787. this.defineProp('modifiers', {'dataType': DataType.ARRAY, 'serializable': false, 'setter': null});
  788. },
  789. /** @ignore */
  790. initPropValues: function($super)
  791. {
  792. $super();
  793. this.setShowGlyph(true);
  794. },
  795. /** @private */
  796. doFinalize: function($super)
  797. {
  798. this.clearWidgets();
  799. this.getModifierMap().finalize();
  800. var modifiers = this.getModifiers();
  801. for (var i = 0, l = modifiers.length; i < l; ++i)
  802. modifiers[i].finalize();
  803. $super();
  804. },
  805. /** @ignore */
  806. doGetWidgetClassName: function($super)
  807. {
  808. var result = $super() + ' ' + CCNS.COMPOSER_TOOLBAR + ' ' + CCNS.COMPOSER_OBJMODIFIER_TOOLBAR;
  809. return result;
  810. },
  811. /** @private */
  812. getEditor: function()
  813. {
  814. return this.getComposer().getEditor();
  815. },
  816. /** @private */
  817. getEditorConfigs: function()
  818. {
  819. return this.getEditor().getEditorConfigs();
  820. },
  821. /** @private */
  822. getAllowedModifierCategories: function()
  823. {
  824. var c = this.getComposer();
  825. return c && c.getAllowedObjModifierCategories();
  826. },
  827. /**
  828. * Returns selection of editor.
  829. * @returns {Array}
  830. * @private
  831. */
  832. getTargetObjs: function()
  833. {
  834. return this.getEditor().getSelection();
  835. },
  836. /**
  837. * Returns modifier classes that can be displayed on toolbar.
  838. * @returns {Array}
  839. * @private
  840. */
  841. getAvailableModifierClasses: function()
  842. {
  843. var targetClasses = [];
  844. var targets = this.getTargetObjs();
  845. for (var i = 0, l = targets.length; i < l; ++i)
  846. {
  847. if (targets[i].getClass)
  848. AU.pushUnique(targetClasses, targets[i].getClass());
  849. }
  850. var result = [];
  851. var allowedCategories = this.getAllowedModifierCategories();
  852. for (var i = 0, l = targetClasses.length; i < l; ++i)
  853. {
  854. var modifierClasses = Kekule.Editor.ObjModifierManager.getModifierClasses(targetClasses[i], allowedCategories);
  855. if (modifierClasses)
  856. AU.pushUnique(result, modifierClasses);
  857. }
  858. if (result.length)
  859. result.sort(function(a, b){
  860. var protoA = a && ClassEx.getPrototype(a);
  861. var protoB = b && ClassEx.getPrototype(b);
  862. var nameA = protoA && protoA.getModifierName && protoA.getModifierName();
  863. var nameB = protoB && protoB.getModifierName && protoB.getModifierName();
  864. return (nameA === nameB)? 0:
  865. (nameA < nameB)? -1: 1;
  866. });
  867. return result;
  868. },
  869. /**
  870. * Return a cached or newly created modifier instance of class.
  871. * @param {Class} modifierClass
  872. * @param {Bool} autoCreate
  873. * @return {Kekule.Editor.ObjModifier.Base}
  874. */
  875. getModifierInstanceOfClass: function(modifierClass, autoCreate)
  876. {
  877. var map = this.getModifierMap();
  878. var result = map.get(modifierClass);
  879. if (!result && autoCreate)
  880. {
  881. result = new modifierClass(this.getEditor());
  882. map.set(modifierClass, result);
  883. }
  884. return result;
  885. },
  886. /**
  887. * Refresh displayed modifier widgets on toolbar.
  888. */
  889. updateModifierWidgets: function()
  890. {
  891. var modifiers = [];
  892. this.clearWidgets(true); // clear old widgets but do not finalize
  893. var modifierClasses = this.getAvailableModifierClasses();
  894. for (var i = 0, l = modifierClasses.length; i < l; ++i)
  895. {
  896. var mClass = modifierClasses[i];
  897. var modifier = this.getModifierInstanceOfClass(mClass, true); // auto create
  898. var widget = modifier.getWidget();
  899. if (widget)
  900. {
  901. this.appendWidget(widget);
  902. modifiers.push(modifier);
  903. }
  904. }
  905. this.setPropStoreFieldValue('modifiers', modifiers);
  906. this.updateModifierValues();
  907. if (this._isAllModifierWidgetButtons(modifiers)) // if all modifiers are buttons, use btnGroup style rather than toolbar
  908. {
  909. this.removeClassName(CNS.TOOLBAR).addClassName(CNS.BUTTON_GROUP);
  910. }
  911. else
  912. {
  913. this.removeClassName(CNS.BUTTON_GROUP).addClassName(CNS.TOOLBAR);
  914. }
  915. },
  916. /**
  917. * Update toolbar and child widget outlook and other settings according to editor's state.
  918. */
  919. updateState: function()
  920. {
  921. this.updateModifierWidgets();
  922. },
  923. /**
  924. * Refresh modifier widget according to current state of selection in editor.
  925. */
  926. updateModifierValues: function()
  927. {
  928. var modifiers = this.getModifiers();
  929. for (var i = 0, l = modifiers.length; i < l; ++i)
  930. {
  931. modifiers[i].loadFromTargets();
  932. }
  933. },
  934. /** @private */
  935. _isAllModifierWidgetButtons: function(modifiers)
  936. {
  937. for (var i = 0, l = modifiers.length; i < l; ++i)
  938. {
  939. var m = modifiers[i];
  940. var w = m && m.getWidget();
  941. if (!w || !w.getDisplayed() || w instanceof Kekule.Widget.Button)
  942. continue;
  943. else
  944. return false
  945. }
  946. return true;
  947. }
  948. });
  949. /**
  950. * A editor with essential UI for end users.
  951. * @class
  952. * @augments Kekule.ChemWidget.AbstractWidget
  953. * @param {Variant} parentOrElementOrDocument
  954. * @param {Kekule.Editor.BaseEditor} editor An editor instance embedded in UI.
  955. *
  956. * @property {Kekule.Editor.BaseEditor} editor The editor instance embedded in UI.
  957. * @property {Array} commonToolButtons buttons in common tool bar. This is a array of predefined strings, e.g.: ['zoomIn', 'zoomOut', 'resetZoom', 'molDisplayType', ...].
  958. * If not set, default buttons will be used.
  959. * In the array, complex hash can also be used to add custom buttons, e.g.: <br />
  960. * [ <br />
  961. * 'zoomIn', 'zoomOut',<br />
  962. * {'name': 'myCustomButton1', 'widgetClass': 'Kekule.Widget.Button', 'action': actionClass},<br />
  963. * {'name': 'myCustomButton2', 'htmlClass': 'MyClass' 'caption': 'My Button', 'hint': 'My Hint', '#execute': function(){ ... }},<br />
  964. * ]<br />
  965. * @property {Array} chemToolButtons buttons in chem tool bar. This is a array of predefined strings, e.g.: ['zoomIn', 'zoomOut', 'resetZoom', 'molDisplayType', ...].
  966. * If not set, default buttons will be used.
  967. * Chem tool often has a series of child tool buttons, you can also control to display which child buttons, e.g.:
  968. * [
  969. * {'name': 'bond', 'attached': ['bondSingle', 'bondDouble']}, <br />
  970. * 'atom', 'formula',<br />
  971. * ] <br />
  972. * Note: currently same child button can not be existed in different chem tool buttons.
  973. * In the array, complex hash can also be used to add custom buttons, e.g.: <br />
  974. * [ <br />
  975. * 'atom', 'formula',<br />
  976. * {'name': 'myCustomButton1', 'widgetClass': 'Kekule.Widget.Button', 'action': actionClass},<br />
  977. * {'name': 'myCustomButton2', 'htmlClass': 'MyClass' 'caption': 'My Button', 'hint': 'My Hint', '#execute': function(){ ... }},<br />
  978. * ]<br />
  979. * @property {Array} styleToolComponentNames Array of component names that shows in style tool bar.
  980. * @property {Bool} enableStyleToolbar
  981. * @property {Bool} enableObjModifierToolbar
  982. * @property {Array} allowedObjModifierCategories
  983. * @property {Bool} showInspector Whether show advanced object inspector and structure view.
  984. * @property {Bool} autoSetMinDimension
  985. *
  986. * @property {Kekule.Editor.BaseEditorConfigs} editorConfigs Configuration of this editor.
  987. * @property {Bool} enableOperHistory Whether undo/redo is enabled.
  988. * @property {Kekule.OperationHistory} operHistory History of operations. Used to enable undo/redo function.
  989. * @property {Int} renderType Display in 2D or 3D. Value from {@link Kekule.Render.RendererType}.
  990. * @property {Kekule.ChemObject} chemObj The root object in editor.
  991. * @property {Bool} enableOperContext If this property is set to true, object being modified will be drawn in a
  992. * separate context to accelerate the interface refreshing.
  993. * @property {Object} objContext Context to draw basic chem objects. Can be 2D or 3D context. Alias of property drawContext
  994. * @property {Object} operContext Context to draw objects being operated. Can be 2D or 3D context.
  995. * @property {Object} uiContext Context to draw UI marks. Usually this is a 2D context.
  996. * @property {Object} objDrawBridge Bridge to draw chem objects. Alias of property drawBridge.
  997. * @property {Object} uiDrawBridge Bridge to draw UI markers.
  998. * @property {Array} selection An array of selected basic object.
  999. *
  1000. * @property {Bool} enableLoadNewFile Whether open a external file to displayer is allowed.
  1001. * @property {Bool} enableCreateNewDoc Whether create new object in editor is allowed.
  1002. * @property {Bool} allowCreateNewChild Whether new direct child of space can be created.
  1003. * Note: if the space is empty, one new child will always be allowed to create.
  1004. */
  1005. Kekule.Editor.Composer = Class.create(Kekule.ChemWidget.AbstractWidget,
  1006. /** @lends Kekule.Editor.Composer# */
  1007. {
  1008. /** @private */
  1009. CLASS_NAME: 'Kekule.Editor.Composer',
  1010. /** @private */
  1011. BINDABLE_TAG_NAMES: ['div'],
  1012. /** @private */
  1013. CHEM_TOOL_CHILD_FIELDS: '__$children__',
  1014. /** @constructs */
  1015. initialize: function($super, parentOrElementOrDocument, editor)
  1016. {
  1017. /*
  1018. this.updateStyleToolbarStateBind = this.updateStyleToolbarState.bind(this);
  1019. this.updateObjModifierToolbarStateBind = this.updateObjModifierToolbarState.bind(this);
  1020. */
  1021. this.updateSelectionAssocToolbarStateBind = this.updateSelectionAssocToolbarState.bind(this);
  1022. this.setPropStoreFieldValue('enableStyleToolbar', true);
  1023. this.setPropStoreFieldValue('enableObjModifierToolbar', true);
  1024. this.setPropStoreFieldValue('editor', editor);
  1025. this.setPropStoreFieldValue('editorNexus', new Kekule.Editor.EditorNexus());
  1026. $super(parentOrElementOrDocument);
  1027. /*
  1028. if (!editor)
  1029. editor = this.createDefaultEditor();
  1030. */
  1031. this.bindEditor(editor);
  1032. var ed = this.getEditor();
  1033. if (ed) // editor is newly created
  1034. ed.setChemObj(ed.getChemObj()); // a force readjust size, otherwise there will be size problem in IE8
  1035. // tool bars may already be created by setting buttons property
  1036. if (!this.getCommonBtnGroup())
  1037. this.createCommonToolbar();
  1038. if (!this.getChemBtnGroup())
  1039. this.createChemToolbar();
  1040. if (!this.getZoomBtnGroup())
  1041. this.createZoomToolbar();
  1042. // debug
  1043. //this.setShowInspector(true);
  1044. this.uiLayoutChanged();
  1045. },
  1046. /** @private */
  1047. doFinalize: function($super)
  1048. {
  1049. //this.getPainter().finalize();
  1050. var toolBar = this.getCommonBtnGroup();
  1051. if (toolBar)
  1052. toolBar.finalize();
  1053. var toolBar = this.getChemBtnGroup();
  1054. if (toolBar)
  1055. toolBar.finalize();
  1056. var editor = this.getPropStoreFieldValue('editor');
  1057. if (editor)
  1058. editor.finalize();
  1059. this.getEditorNexus().finalize();
  1060. $super();
  1061. },
  1062. /** @private */
  1063. initProperties: function()
  1064. {
  1065. this.defineProp('editor', {'dataType': 'Kekule.Editor.BaseEditor', 'serializable': false, 'setter': null,
  1066. 'getter': function()
  1067. {
  1068. var result = this.getPropStoreFieldValue('editor');
  1069. if (!result)
  1070. {
  1071. result = this.createDefaultEditor();
  1072. this.setPropStoreFieldValue('editor', result);
  1073. }
  1074. return result;
  1075. }
  1076. });
  1077. this.defineProp('showInspector', {'dataType': DataType.BOOL,
  1078. 'setter': function(value)
  1079. {
  1080. if (this.getShowInspector() !== value)
  1081. {
  1082. this.setPropStoreFieldValue('showInspector', value);
  1083. this.showInspectorChanged();
  1084. }
  1085. }});
  1086. this.defineProp('autoSetMinDimension', {'dataType': DataType.BOOL});
  1087. // private property
  1088. this.defineProp('editorStageElem', {'dataType': DataType.OBJECT, 'serializable': false, 'setter': null});
  1089. this.defineProp('advPanelElem', {'dataType': DataType.OBJECT, 'serializable': false, 'setter': null});
  1090. this.defineProp('topRegionElem', {'dataType': DataType.OBJECT, 'serializable': false, 'setter': null});
  1091. this.defineProp('leftRegionElem', {'dataType': DataType.OBJECT, 'serializable': false, 'setter': null});
  1092. this.defineProp('bottomRegionElem', {'dataType': DataType.OBJECT, 'serializable': false, 'setter': null});
  1093. this.defineProp('editorNexus', {'dataType': 'Kekule.Editor.EditorNexus', 'serializable': false, 'setter': null});
  1094. this.defineProp('objInspector', {'dataType': 'Kekule.Widget.ObjectInspector', 'serializable': false, 'setter': null});
  1095. this.defineProp('structureTreeView', {'dataType': 'Kekule.ChemWidget.StructureTreeView', 'serializable': false, 'setter': null});
  1096. // a private property, toolbar of common tasks (such as save/load)
  1097. this.defineProp('commonBtnGroup', {'dataType': 'Kekule.Widget.ButtonGroup', 'serializable': false});
  1098. // a private property, toolbar of zoom tasks (such as zoomin/out)
  1099. this.defineProp('zoomBtnGroup', {'dataType': 'Kekule.Widget.ButtonGroup', 'serializable': false});
  1100. // a private property, toolbar of chem tools (such as atom/bond)
  1101. this.defineProp('chemBtnGroup', {'dataType': 'Kekule.Widget.ButtonGroup', 'serializable': false});
  1102. // a private property, toolbar of association chem tools (such as single, double bound form for bond tool)
  1103. this.defineProp('assocBtnGroup', {'dataType': 'Kekule.Widget.ButtonGroup', 'serializable': false});
  1104. // a private property, toolbar of style settings (such as font name, font size, color)
  1105. this.defineProp('styleToolbar', {'dataType': 'Kekule.Widget.Toolbar', 'serializable': false,
  1106. 'getter': function()
  1107. {
  1108. var result = this.getPropStoreFieldValue('styleToolbar');
  1109. if (!result)
  1110. {
  1111. if (this.getEnableStyleToolbar())
  1112. {
  1113. result = this.createStyleToolbar();
  1114. this.setPropStoreFieldValue('styleToolbar', result);
  1115. }
  1116. }
  1117. return result;
  1118. }
  1119. });
  1120. this.defineProp('styleToolComponentNames', {'dataType': DataType.ARRAY, 'serializable': false,
  1121. 'getter': function()
  1122. {
  1123. var toolbar = this.getStyleToolbar();
  1124. return toolbar? toolbar.getComponentNames(): null;
  1125. },
  1126. 'setter': function(value)
  1127. {
  1128. var toolbar = this.getStyleToolbar();
  1129. if (toolbar)
  1130. toolbar.setComponentNames(value);
  1131. return this;
  1132. }
  1133. });
  1134. this.defineProp('enableStyleToolbar', {'dataType': DataType.BOOL,
  1135. 'getter': function()
  1136. {
  1137. // if obj modifier toolbar is enabled, old fashioned style toolbar will not be displayed
  1138. return this.getPropStoreFieldValue('enableStyleToolbar') && !this.getEnableObjModifierToolbar();
  1139. },
  1140. 'setter': function(value)
  1141. {
  1142. this.setPropStoreFieldValue('enableStyleToolbar', value);
  1143. this.updateSelectionAssocToolbarState();
  1144. }
  1145. });
  1146. // a private property, toolbar of style settings (such as font name, font size, color)
  1147. this.defineProp('objModifierToolbar', {'dataType': 'Kekule.Widget.Toolbar', 'serializable': false,
  1148. 'getter': function()
  1149. {
  1150. var result = this.getPropStoreFieldValue('objModifierToolbar');
  1151. if (!result)
  1152. {
  1153. if (this.getEnableObjModifierToolbar())
  1154. {
  1155. result = this.createObjModifierToolbar();
  1156. this.setPropStoreFieldValue('objModifierToolbar', result);
  1157. }
  1158. }
  1159. return result;
  1160. }
  1161. });
  1162. this.defineProp('enableObjModifierToolbar', {'dataType': DataType.BOOL,
  1163. 'setter': function(value)
  1164. {
  1165. this.setPropStoreFieldValue('enableObjModifierToolbar', value);
  1166. this.updateSelectionAssocToolbarState();
  1167. }
  1168. });
  1169. this.defineProp('allowedObjModifierCategories', {'dataType': DataType.ARRAY});
  1170. this.defineProp('commonToolButtons', {'dataType': DataType.HASH, 'serializable': false,
  1171. 'getter': function()
  1172. {
  1173. var result = this.getPropStoreFieldValue('commonToolButtons');
  1174. if (!result) // create default one
  1175. {
  1176. result = this.getDefaultCommonToolBarButtons();
  1177. this.setPropStoreFieldValue('commonToolButtons', result);
  1178. }
  1179. return result;
  1180. },
  1181. 'setter': function(value)
  1182. {
  1183. this.setPropStoreFieldValue('commonToolButtons', value);
  1184. this.updateCommonToolbar();
  1185. this.updateZoomToolbar();
  1186. }
  1187. });
  1188. this.defineProp('chemToolButtons', {'dataType': DataType.HASH, 'serializable': false,
  1189. 'getter': function()
  1190. {
  1191. var result = this.getPropStoreFieldValue('chemToolButtons');
  1192. if (!result) // create default one
  1193. {
  1194. result = this.getDefaultChemToolBarButtons();
  1195. this.setPropStoreFieldValue('chemToolButtons', result);
  1196. }
  1197. return result;
  1198. },
  1199. 'setter': function(value)
  1200. {
  1201. this.setPropStoreFieldValue('chemToolButtons', value);
  1202. this.updateChemToolbar();
  1203. }
  1204. });
  1205. this.defineProp('styleBarComponents', {'dataType': DataType.ARRAY, 'serializable': false,
  1206. 'getter': function()
  1207. {
  1208. var result = this.getPropStoreFieldValue('styleBarComponents');
  1209. if (!result)
  1210. {
  1211. result = null; // default one
  1212. this.setPropStoreFieldValue('styleBarComponents', result);
  1213. }
  1214. return result;
  1215. },
  1216. 'setter': function(value)
  1217. {
  1218. this.setPropStoreFieldValue('styleBarComponents', value);
  1219. if (this.getStyleToolbar())
  1220. this.getStyleToolbar().setComponentNames(value);
  1221. }
  1222. });
  1223. /*
  1224. // private
  1225. this.defineProp('toolButtonNameMapping', {'dataType': DataType.HASH, 'serializable': false, 'setter': null,
  1226. 'getter': function()
  1227. {
  1228. var result = this.getPropStoreFieldValue('toolButtonNameMapping');
  1229. if (!result) // create default one
  1230. {
  1231. result = this._createDefaultToolButtonNameMapping();
  1232. this.setPropStoreFieldValue('toolButtonNameMapping', result);
  1233. }
  1234. return result;
  1235. }
  1236. });
  1237. */
  1238. // private
  1239. this.defineProp('commonActions', {'dataType': 'Kekule.ActionList', 'serializable': false, 'setter': null,
  1240. 'getter': function()
  1241. {
  1242. var result = this.getPropStoreFieldValue('commonActions');
  1243. if (!result)
  1244. {
  1245. result = new Kekule.ActionList();
  1246. this.setPropStoreFieldValue('commonActions', result);
  1247. }
  1248. return result;
  1249. }
  1250. });
  1251. this.defineProp('zoomActions', {'dataType': 'Kekule.ActionList', 'serializable': false, 'setter': null,
  1252. 'getter': function()
  1253. {
  1254. var result = this.getPropStoreFieldValue('zoomActions');
  1255. if (!result)
  1256. {
  1257. result = new Kekule.ActionList();
  1258. this.setPropStoreFieldValue('zoomActions', result);
  1259. }
  1260. return result;
  1261. }
  1262. });
  1263. this.defineProp('chemActions', {'dataType': 'Kekule.ActionList', 'serializable': false, 'setter': null,
  1264. 'getter': function()
  1265. {
  1266. var result = this.getPropStoreFieldValue('chemActions');
  1267. if (!result)
  1268. {
  1269. result = new Kekule.ActionList();
  1270. this.setPropStoreFieldValue('chemActions', result);
  1271. }
  1272. return result;
  1273. }
  1274. });
  1275. this.defineProp('actionMap', {'dataType': 'Kekule.MapEx', 'serializable': false, 'setter': null,
  1276. 'getter': function()
  1277. {
  1278. var result = this.getPropStoreFieldValue('actionMap');
  1279. if (!result)
  1280. {
  1281. result = new Kekule.MapEx();
  1282. this.setPropStoreFieldValue('actionMap', result);
  1283. }
  1284. return result;
  1285. }
  1286. });
  1287. this.defineProp('allowCreateNewChild', {'dataType': DataType.BOOL,
  1288. 'getter': function()
  1289. {
  1290. var ed = this.getEditor();
  1291. return (ed && ed.getAllowCreateNewChild)? ed.getAllowCreateNewChild(): null;
  1292. },
  1293. 'setter': function(value)
  1294. {
  1295. var ed = this.getEditor();
  1296. if (ed.setAllowCreateNewChild)
  1297. ed.setAllowCreateNewChild(value);
  1298. return this;
  1299. }
  1300. });
  1301. // editor delegated property
  1302. // from ChemObjDisplayer
  1303. this._defineEditorDelegatedProp('chemObj');
  1304. this._defineEditorDelegatedProp('chemObjLoaded');
  1305. this._defineEditorDelegatedProp('renderType');
  1306. this._defineEditorDelegatedProp('renderConfigs');
  1307. this._defineEditorDelegatedProp('drawOptions');
  1308. this._defineEditorDelegatedProp('allowCoordBorrow');
  1309. // from BaseEditor
  1310. this._defineEditorDelegatedProp('editorConfigs');
  1311. this._defineEditorDelegatedProp('operHistory');
  1312. this._defineEditorDelegatedProp('enableOperHistory');
  1313. this._defineEditorDelegatedProp('selection');
  1314. this._defineEditorDelegatedProp('hotTrackedObjs');
  1315. this._defineEditorDelegatedProp('enableOperContext');
  1316. this._defineEditorDelegatedProp('enableCreateNewDoc');
  1317. this._defineEditorDelegatedProp('enableLoadNewFile');
  1318. this._defineEditorDelegatedProp('initOnNewDoc');
  1319. },
  1320. /**
  1321. * Define property that directly mapped to editor's property.
  1322. * @param {String} propName
  1323. * @param {String} editorPropName Name of corresponding property in editor.
  1324. * @return {Object} Property info object added to property list.
  1325. * @private
  1326. */
  1327. _defineEditorDelegatedProp: function(propName, editorPropName)
  1328. {
  1329. if (!editorPropName)
  1330. editorPropName = propName;
  1331. var editorPropInfo = ClassEx.getPropInfo(Kekule.Editor.BaseEditor, editorPropName);
  1332. /*
  1333. var propOptions = {
  1334. 'serializable': editorPropInfo.serializable,
  1335. 'dataType': editorPropInfo.dataType,
  1336. 'title': editorPropInfo.title,
  1337. 'description': editorPropInfo.description,
  1338. 'getter': null,
  1339. 'setter': null
  1340. };
  1341. */
  1342. var propOptions = Object.create(editorPropInfo);
  1343. propOptions.getter = null;
  1344. propOptions.setter = null;
  1345. if (editorPropInfo.getter)
  1346. {
  1347. propOptions.getter = function()
  1348. {
  1349. return this.getEditor().getPropValue(editorPropName);
  1350. };
  1351. }
  1352. if (editorPropInfo.setter)
  1353. {
  1354. propOptions.setter = function(value)
  1355. {
  1356. this.getEditor().setPropValue(editorPropName, value);
  1357. }
  1358. }
  1359. //console.log('define delegate prop', propOptions);
  1360. return this.defineProp(propName, propOptions);
  1361. },
  1362. /** @private */
  1363. loadPredefinedResDataToProp: function(propName, resData, success)
  1364. {
  1365. if (propName === 'chemObj') // only this property can be set by predefined resource
  1366. {
  1367. if (success)
  1368. {
  1369. try
  1370. {
  1371. var chemObj = Kekule.IO.loadTypedData(resData.data, resData.resType, resData.resUri);
  1372. //console.log('set predefined chemObj', chemObj);
  1373. this.setChemObj(chemObj);
  1374. }
  1375. catch(e)
  1376. {
  1377. Kekule.raise(e, Kekule.ExceptionLevel.ERROR);
  1378. }
  1379. }
  1380. else // else, failed
  1381. {
  1382. Kekule.throwException(/*Kekule.ErrorMsg.CANNOT_LOAD_RES_OF_URI*/Kekule.$L('ErrorMsg.CANNOT_LOAD_RES_OF_URI') + resData.resUri || '');
  1383. }
  1384. }
  1385. },
  1386. /** @ignore */
  1387. initPropValues: function($super)
  1388. {
  1389. $super();
  1390. },
  1391. /** @ignore */
  1392. doCreateRootElement: function(doc)
  1393. {
  1394. var result = doc.createElement('div');
  1395. return result;
  1396. },
  1397. /** @ignore */
  1398. doCreateSubElements: function(doc, rootElem)
  1399. {
  1400. var result = [];
  1401. var elem = this._doCreateSubElement(doc, rootElem, 'div', CCNS.COMPOSER_EDITOR_STAGE, 'editorStageElem');
  1402. result.push(elem);
  1403. elem = this._doCreateSubElement(doc, rootElem, 'div', CCNS.COMPOSER_ADV_PANEL, 'advPanelElem');
  1404. result.push(elem);
  1405. result.push(this._doCreateSubElement(doc, rootElem, 'div', CCNS.COMPOSER_TOP_REGION, 'topRegionElem'));
  1406. result.push(this._doCreateSubElement(doc, rootElem, 'div', CCNS.COMPOSER_LEFT_REGION, 'leftRegionElem'));
  1407. result.push(this._doCreateSubElement(doc, rootElem, 'div', CCNS.COMPOSER_BOTTOM_REGION, 'bottomRegionElem'));
  1408. return result;
  1409. },
  1410. /** @private */
  1411. _doCreateSubElement: function(doc, parentElem, tagName, htmlClass, propStoreFieldName)
  1412. {
  1413. var result = doc.createElement(tagName);
  1414. result.className = htmlClass;
  1415. if (parentElem)
  1416. parentElem.appendChild(result);
  1417. if (propStoreFieldName)
  1418. this.setPropStoreFieldValue(propStoreFieldName, result);
  1419. return result;
  1420. },
  1421. /** @ignore */
  1422. doGetWidgetClassName: function($super)
  1423. {
  1424. var result = $super() + ' ' + CCNS.COMPOSER;
  1425. return result;
  1426. },
  1427. /** @ignore */
  1428. doWidgetShowStateChanged: function($super, isShown)
  1429. {
  1430. $super(isShown);
  1431. if (isShown)
  1432. this.adjustComponentPositions();
  1433. },
  1434. /*
  1435. doInsertedToDom: function()
  1436. {
  1437. console.log('composer inserted to DOM');
  1438. },
  1439. */
  1440. /** @ignore */
  1441. getResizerElement: function()
  1442. {
  1443. return this.getEditorStageElem();
  1444. },
  1445. /** @ignore */
  1446. getChildActionClass: function($super, actionName, checkSupClasses)
  1447. {
  1448. var result = $super(actionName, checkSupClasses);
  1449. if (!result)
  1450. result = this.getEditor().getChildActionClass(actionName, checkSupClasses);
  1451. return result;
  1452. },
  1453. /**
  1454. * Returns coord mode of editor.
  1455. * @returns {Int}
  1456. */
  1457. getCoordMode: function()
  1458. {
  1459. return this.getEditor().getCoordMode();
  1460. },
  1461. /**
  1462. * Returns whether the chem object inside editor has been modified since load.
  1463. * @returns {Bool}
  1464. */
  1465. isDirty: function()
  1466. {
  1467. return this.getEditor().isDirty();
  1468. },
  1469. /**
  1470. * Create a new object and load it in editor.
  1471. */
  1472. newDoc: function()
  1473. {
  1474. return this.getEditor().newDoc();
  1475. },
  1476. /**
  1477. * Load chem object in composer.
  1478. * @param {Kekule.ChemObject} chemObj
  1479. */
  1480. load: function(chemObj)
  1481. {
  1482. return this.getEditor().load(chemObj);
  1483. },
  1484. /**
  1485. * Returns object in dialog that to be saved.
  1486. * @returns {Kekule.ChemObject}
  1487. * @private
  1488. */
  1489. getSavingTargetObj: function()
  1490. {
  1491. var c = this.getEditor();
  1492. if (c)
  1493. return c.getSavingTargetObj();
  1494. else
  1495. return null;
  1496. },
  1497. /**
  1498. * Returns array of classes that can be exported (saved) from composer.
  1499. * @returns {Array}
  1500. */
  1501. getExportableClasses: function()
  1502. {
  1503. return this.getEditor().getExportableClasses();
  1504. },
  1505. /**
  1506. * Returns exportable object for specified class.
  1507. * @param {Class} objClass Set null to export default object.
  1508. * @returns {Object}
  1509. */
  1510. exportObj: function(objClass)
  1511. {
  1512. return this.getEditor().exportObj(objClass);
  1513. },
  1514. /**
  1515. * Returns all exportable objects for specified class.
  1516. * Descendants can override this method.
  1517. * @param {Class} objClass Set null to export default object.
  1518. * @returns {Array}
  1519. */
  1520. exportObjs: function(objClass)
  1521. {
  1522. return this.getEditor().exportObjs(objClass);
  1523. },
  1524. /**
  1525. * Undo last operation.
  1526. */
  1527. undo: function()
  1528. {
  1529. return this.getEditor().undo();
  1530. },
  1531. /**
  1532. * Redo last operation.
  1533. */
  1534. redo: function()
  1535. {
  1536. return this.getEditor().redo();
  1537. },
  1538. /**
  1539. * Undo all operations.
  1540. */
  1541. undoAll: function()
  1542. {
  1543. return this.getEditor().undoAll();
  1544. },
  1545. /**
  1546. * Check if an undo action can be taken.
  1547. * @returns {Bool}
  1548. */
  1549. canUndo: function()
  1550. {
  1551. return this.getEditor().canUndo();
  1552. },
  1553. /**
  1554. * Check if an undo action can be taken.
  1555. * @returns {Bool}
  1556. */
  1557. canRedo: function()
  1558. {
  1559. return this.getEditor().canRedo();
  1560. },
  1561. /**
  1562. * Repaint the objects in editor.
  1563. */
  1564. repaint: function(overrideOptions)
  1565. {
  1566. this.getEditor().repaint(overrideOptions);
  1567. return this;
  1568. },
  1569. /**
  1570. * Called after UI changing (e.g., show/hide inspector/assoc tool bar).
  1571. * @private
  1572. */
  1573. uiLayoutChanged: function()
  1574. {
  1575. this.adjustComponentPositions();
  1576. },
  1577. /** @ignore */
  1578. doResize: function($super)
  1579. {
  1580. this.adjustComponentPositions();
  1581. },
  1582. /**
  1583. * Change child components' position and dimension to fit current UI widget status.
  1584. * @private
  1585. */
  1586. adjustComponentPositions: function()
  1587. {
  1588. // toolbar
  1589. var commonToolbarElem = this.getCommonBtnGroup()? this.getCommonBtnGroup().getElement(): null;
  1590. var zoomToolbarElem = this.getZoomBtnGroup()? this.getZoomBtnGroup().getElement(): null;
  1591. var chemToolbarElem = this.getChemBtnGroup()? this.getChemBtnGroup().getElement(): null;
  1592. if (!commonToolbarElem || !chemToolbarElem) // not all toolbars created, widget may in its initial stage, no need to update
  1593. return;
  1594. if (commonToolbarElem)
  1595. var commonRect = Kekule.HtmlElementUtils.getElemPageRect(commonToolbarElem);
  1596. if (zoomToolbarElem)
  1597. var zoomRect = Kekule.HtmlElementUtils.getElemPageRect(zoomToolbarElem);
  1598. if (chemToolbarElem)
  1599. var chemRect = Kekule.HtmlElementUtils.getElemPageRect(chemToolbarElem);
  1600. if (!commonRect.width || !chemRect.width) // rect is zero, the widget may not be displayed
  1601. return;
  1602. /*
  1603. var style = commonToolbarElem.style;
  1604. style.top = '0px';
  1605. style.left = chemRect.width + 'px';
  1606. style = chemToolbarElem.style;
  1607. style.top = commonRect.height + 'px';
  1608. style.left = '0px';
  1609. */
  1610. var topRegionHeight = commonRect.height;
  1611. var leftRegionWidth = chemRect.width;
  1612. var bottomRegionHeight = (zoomRect && zoomRect.height) || topRegionHeight; // zoom toolbar may be invisible
  1613. // top region
  1614. var elem = this.getTopRegionElem();
  1615. var style = elem.style;
  1616. style.top = '0px';
  1617. style.left = leftRegionWidth + 'px';
  1618. style.right = '0px';
  1619. style.height = topRegionHeight + 'px';
  1620. // bottom region
  1621. elem = this.getBottomRegionElem();
  1622. style = elem.style;
  1623. style.bottom = '0px';
  1624. style.height = bottomRegionHeight + 'px';
  1625. style.left = leftRegionWidth + 'px';
  1626. style.right = '0px';
  1627. var bottomRect = Kekule.HtmlElementUtils.getElemPageRect(elem);
  1628. var bottomFreeWidth = bottomRect.width - (zoomRect? zoomRect.width: 0);
  1629. // left region
  1630. elem = this.getLeftRegionElem();
  1631. style = elem.style;
  1632. style.left = '0px';
  1633. style.top = topRegionHeight + 'px';
  1634. style.bottom = '0px';
  1635. style.width = leftRegionWidth + 'px';
  1636. var leftRect = Kekule.HtmlElementUtils.getElemPageRect(elem);
  1637. var leftFreeHeight = leftRect.height - chemRect.height;
  1638. // now we can decide whether shown assoc toolbar on bottom or left side
  1639. if (leftFreeHeight / bottomFreeWidth > 0.9) // TODO: now fixed
  1640. {
  1641. // assoc bar shown in left
  1642. this.changeAssocToolbarRegion(true);
  1643. }
  1644. else
  1645. {
  1646. // assoc bar shown in bottom
  1647. this.changeAssocToolbarRegion(false);
  1648. }
  1649. // editor stage
  1650. var top = topRegionHeight, left = leftRegionWidth, bottom = bottomRegionHeight, right;
  1651. // calc right
  1652. if (!this.getShowInspector())
  1653. right = 0;
  1654. else
  1655. {
  1656. var elem = this.getAdvPanelElem();
  1657. var rect = Kekule.HtmlElementUtils.getElemPageRect(elem);
  1658. right = rect.width;
  1659. }
  1660. var stageElem = this.getEditorStageElem();
  1661. var style = stageElem.style;
  1662. style.position = 'absolute';
  1663. style.top = top + 'px';
  1664. style.left = left + 'px';
  1665. style.bottom = bottom + 'px';
  1666. style.right = right + 'px';
  1667. // advPanel
  1668. elem = this.getAdvPanelElem();
  1669. style = elem.style;
  1670. style.position = 'absolute';
  1671. style.top = top + 'px';
  1672. style.bottom = bottom + 'px';
  1673. if (this.getAutoSetMinDimension())
  1674. {
  1675. var minDim = {
  1676. 'width': leftRegionWidth + commonRect.width,
  1677. 'height': topRegionHeight + chemRect.height
  1678. };
  1679. //console.log('get minDim', minDim);
  1680. if (minDim.width && minDim.height)
  1681. this.setMinDimension(minDim);
  1682. /*
  1683. var currDim = this.getDimension();
  1684. this.setDimension(currDim.width, currDim.height, true); // update size, but do not need to adjust component position again
  1685. */
  1686. }
  1687. },
  1688. /** @private */
  1689. adjustAssocToolbarPositions: function()
  1690. {
  1691. /*
  1692. //var commonToolbarElem = this.getCommonBtnGroup().getElement();
  1693. var chemToolbarElem = this.getChemBtnGroup().getElement();
  1694. //var commonRect= Kekule.HtmlElementUtils.getElemBoundingClientRect(commonToolbarElem)
  1695. var chemRect = Kekule.HtmlElementUtils.getElemBoundingClientRect(chemToolbarElem);
  1696. // assoc toolbar
  1697. if (this.isAssocToolbarShown())
  1698. {
  1699. var elem = this.getAssocBtnGroup().getElement();
  1700. elem.style.left = chemRect.width + 'px';
  1701. //elem.style.top = commonRect.height + 'px';
  1702. }
  1703. */
  1704. this.adjustStyleAndObjModifierToolbarPosition();
  1705. },
  1706. /** @private */
  1707. adjustStyleAndObjModifierToolbarPosition: function()
  1708. {
  1709. /*
  1710. var chemToolbarElem = this.getChemBtnGroup().getElement();
  1711. var chemRect = Kekule.HtmlElementUtils.getElemBoundingClientRect(chemToolbarElem);
  1712. var assocRect;
  1713. if (this.isAssocToolbarShown())
  1714. {
  1715. assocRect = Kekule.HtmlElementUtils.getElemBoundingClientRect(this.getAssocBtnGroup().getElement());
  1716. }
  1717. //console.log('adjust pos', assocRect);
  1718. //if (this.isStyleToolbarShown())
  1719. if (this.getPropStoreFieldValue('styleToolbar')) // stylebar created
  1720. {
  1721. var elem = this.getStyleToolbar().getElement();
  1722. elem.style.left = (assocRect? (assocRect.left + assocRect.width): chemRect.width) + 'px';
  1723. }
  1724. //if (this.isObjModifierToolbarShown())
  1725. if (this.getPropStoreFieldValue('objModifierToolbar')) // modifier toolbar created
  1726. {
  1727. var elem = this.getObjModifierToolbar().getElement();
  1728. elem.style.left = (assocRect? (assocRect.left + assocRect.width): chemRect.width) + 'px';
  1729. }
  1730. */
  1731. },
  1732. ////////////////// methods about inner editor ///////////////////////
  1733. /** @private */
  1734. bindEditor: function(editor)
  1735. {
  1736. if (!editor)
  1737. editor = this.getEditor();
  1738. else
  1739. editor.setParent(this);
  1740. editor.setTouchAction('none'); // disable default touch scroll on editor
  1741. var self = this;
  1742. var commonActions = this.getCommonActions();
  1743. var chemActions = this.getChemActions();
  1744. this.createIaControllers(editor);
  1745. editor.addEventListener('load', function(e)
  1746. {
  1747. this.updateAllActions();
  1748. this.updateUiWidgets();
  1749. // turn select as default checked chem tool
  1750. var actions = this.getChemActions();
  1751. var action = actions.getActionAt(0);
  1752. if (action)
  1753. action.execute();
  1754. },
  1755. this
  1756. );
  1757. editor.addEventListener('operChange', function(e)
  1758. {
  1759. commonActions.updateAll();
  1760. this.updateUiWidgets();
  1761. },
  1762. this
  1763. );
  1764. editor.addEventListener('selectionChange', function(e)
  1765. {
  1766. commonActions.updateAll();
  1767. this.updateUiWidgets();
  1768. },
  1769. this
  1770. );
  1771. /*
  1772. editor.addEventListener('editObjsChanged', function(e)
  1773. {
  1774. this.updateAllActions();
  1775. },
  1776. this
  1777. );
  1778. */
  1779. editor.appendToElem(this.getEditorStageElem());
  1780. this.getEditorNexus().setEditor(editor);
  1781. //this.newDoc();
  1782. },
  1783. /** @private */
  1784. createDefaultEditor: function()
  1785. {
  1786. var result = new Kekule.Editor.ChemSpaceEditor(this, null, Kekule.Render.RendererType.R2D);
  1787. result.addClassName(CNS.DYN_CREATED);
  1788. return result;
  1789. },
  1790. /**
  1791. * Create available iaController for editor.
  1792. * @private
  1793. */
  1794. createIaControllers: function(editor)
  1795. {
  1796. var controllerClasses = Kekule.Editor.IaControllerManager.getAvailableControllerClasses(editor.getClass());
  1797. for (var i = 0, l = controllerClasses.length; i < l; ++i)
  1798. {
  1799. var c = controllerClasses[i];
  1800. var controller = new c(editor);
  1801. //console.log('add ia controller', controller.getDefId(), controller);
  1802. editor.addIaController(controller.getDefId(), controller);
  1803. }
  1804. },
  1805. ///////////////// methods about adv panel (objInspector and structureTreeView) /////
  1806. /** @private */
  1807. showInspectorChanged: function()
  1808. {
  1809. var display = this.getShowInspector();
  1810. if (display)
  1811. {
  1812. if (!this.getObjInspector()) // not created yet
  1813. this.createAdvControls();
  1814. this.showAdvPanel();
  1815. }
  1816. else
  1817. this.hideAdvPanel();
  1818. this.uiLayoutChanged();
  1819. },
  1820. /**
  1821. * Create object inspector and structure tree view.
  1822. * @private
  1823. */
  1824. createAdvControls: function(parentElem)
  1825. {
  1826. if (!parentElem)
  1827. parentElem = this.getAdvPanelElem();
  1828. var doc = this.getDocument();
  1829. var treeView = new Kekule.ChemWidget.StructureTreeView(doc);
  1830. treeView.setItemInitialExpanded(true);
  1831. treeView.appendToElem(parentElem);
  1832. //treeView.setRootObj(chemEditor.getChemObj());
  1833. this.setPropStoreFieldValue('structureTreeView', treeView);
  1834. var objInspector = new Kekule.Widget.ObjectInspector(doc);
  1835. objInspector.setShowPropInfoPanel(false);
  1836. objInspector.appendToElem(parentElem);
  1837. this.setPropStoreFieldValue('objInspector', objInspector);
  1838. var nexus = this.getEditorNexus();
  1839. nexus.setObjectInspector(objInspector);
  1840. nexus.setStructureTreeView(treeView);
  1841. },
  1842. /** @private */
  1843. showAdvPanel: function()
  1844. {
  1845. this.getAdvPanelElem().style.display = 'block';
  1846. },
  1847. /** @private */
  1848. hideAdvPanel: function()
  1849. {
  1850. this.getAdvPanelElem().style.display = 'none';
  1851. },
  1852. ////////////////// methods about tool buttons and actions ///////////////////////
  1853. /* @private */
  1854. /*
  1855. _createDefaultToolButtonNameMapping: function()
  1856. {
  1857. var result = {};
  1858. result[BNS.newDoc] = CE.ActionEditorNewDoc;
  1859. result[BNS.loadFile] = CW.ActionDisplayerLoadFile;
  1860. result[BNS.loadData] = CW.ActionDisplayerLoadData;
  1861. result[BNS.saveData] = CW.ActionDisplayerSaveFile;
  1862. result[BNS.zoomIn] = CW.ActionDisplayerZoomIn;
  1863. result[BNS.zoomOut] = CW.ActionDisplayerZoomOut;
  1864. result[BNS.reset] = CW.ActionDisplayerReset;
  1865. result[BNS.config] = Kekule.Widget.ActionOpenConfigWidget;
  1866. result[BNS.undo] = CE.ActionEditorUndo;
  1867. result[BNS.redo] = CE.ActionEditorRedo;
  1868. result[BNS.cloneSelection] = CE.ActionCloneSelection;
  1869. result[BNS.copy] = CE.ActionCopySelection;
  1870. result[BNS.cut] = CE.ActionCutSelection;
  1871. result[BNS.paste] = CE.ActionPaste;
  1872. result[BNS.manipulate] = CE.ActionComposerSetManipulateController;
  1873. result[BNS.erase] = CE.ActionComposerSetEraserController;
  1874. result[BNS.molAtom] = CE.ActionComposerSetAtomController;
  1875. result[BNS.molFormula] = CE.ActionComposerSetFormulaController;
  1876. result[BNS.molBond] = CE.ActionComposerSetBondController;
  1877. result[BNS.molCharge] = CE.ActionComposerSetNodeChargeController;
  1878. result[BNS.textBlock] = CE.ActionComposerSetTextBlockController;
  1879. result[BNS.imageBlock] = CE.ActionComposerSetImageBlockController;
  1880. result[BNS.textImage] = CE.ActionComposerSetTextImageController;
  1881. result[BNS.molRing] = CE.ActionComposerSetRepositoryRingController;
  1882. result[BNS.glyph] = CE.ActionComposerSetRepositoryGlyphController;
  1883. result[BNS.objInspector] = CE.ActionComposerToggleInspector;
  1884. return result;
  1885. },
  1886. */
  1887. /** @private */
  1888. getDefaultCommonToolBarButtons: function()
  1889. {
  1890. return Kekule.globalOptions.chemWidget.composer.commonToolButtons;
  1891. /*
  1892. var buttons = [
  1893. BNS.newDoc,
  1894. //BNS.loadFile,
  1895. BNS.loadData,
  1896. BNS.saveData,
  1897. BNS.undo,
  1898. BNS.redo,
  1899. BNS.copy,
  1900. BNS.cut,
  1901. BNS.paste,
  1902. //BNS.cloneSelection,
  1903. BNS.zoomIn,
  1904. BNS.reset,
  1905. BNS.zoomOut,
  1906. BNS.config,
  1907. BNS.objInspector
  1908. ];
  1909. return buttons;
  1910. */
  1911. },
  1912. /** @private */
  1913. getZoomButtonNames: function()
  1914. {
  1915. return [
  1916. BNS.zoomIn,
  1917. BNS.zoomOut,
  1918. BNS.reset,
  1919. BNS.resetZoom
  1920. ];
  1921. },
  1922. /** @private */
  1923. getDefaultChemToolBarButtons: function()
  1924. {
  1925. return Kekule.globalOptions.chemWidget.composer.chemToolButtons;
  1926. /*
  1927. var buttons = [
  1928. BNS.manipulate,
  1929. BNS.erase,
  1930. BNS.molBond,
  1931. BNS.molAtom,
  1932. BNS.molFormula,
  1933. BNS.molRing,
  1934. BNS.molCharge,
  1935. BNS.glyph,
  1936. BNS.textImage
  1937. ];
  1938. return buttons;
  1939. */
  1940. },
  1941. /** @private */
  1942. getCompActionClass: function(btnName)
  1943. {
  1944. //return this.getToolButtonNameMapping()[btnName];
  1945. return this.getChildActionClass(btnName, true);
  1946. },
  1947. /** @private */
  1948. _getActionTargetWidget: function(actionClass)
  1949. {
  1950. if (ClassEx.isOrIsDescendantOf(actionClass, Kekule.Editor.ActionOnComposer) || ClassEx.isOrIsDescendantOf(actionClass, Kekule.Widget.ActionOpenConfigWidget))
  1951. return this;
  1952. else
  1953. return this.getEditor();
  1954. },
  1955. /**
  1956. * Create a tool button inside parent button group.
  1957. * //@param {Kekule.Widget} targetWidget
  1958. * @param {String} btnName
  1959. * @param {Kekule.Widget.ButtonGroup} parentGroup
  1960. * @param {Kekule.Action} actions
  1961. * @returns {Kekule.Widget.Button}
  1962. * @private
  1963. */
  1964. createToolButton: function(btnName, parentGroup, actions, checkGroup)
  1965. {
  1966. /*
  1967. var result = null;
  1968. var name = DataType.isObjectValue(btnName)? btnName.name: btnName;
  1969. var children = DataType.isObjectValue(btnName)? btnName.attached: null;
  1970. var actionClass = this.getCompActionClass(name);
  1971. if (DataType.isObjectValue(btnName) && !actionClass) // custom button
  1972. {
  1973. if (!actionClass) // no binded action, custom button
  1974. var objDefHash = Object.extend({'widget': Kekule.Widget.Button}, btnName);
  1975. result = Kekule.Widget.Utils.createFromHash(parentGroup, objDefHash);
  1976. var actionClass = objDefHash.actionClass;
  1977. if (actionClass) // create action
  1978. {
  1979. if (typeof(actionClass) === 'string')
  1980. actionClass = ClassEx.findClass(objDefHash.actionClass);
  1981. if (actionClass)
  1982. {
  1983. var action = new actionClass(this);
  1984. this.getActions().add(action);
  1985. result.setAction(action);
  1986. }
  1987. }
  1988. }
  1989. else
  1990. {
  1991. //var actionClass = this.getCompActionClass(btnName);
  1992. //if (actionClass)
  1993. {
  1994. var btnClass = (btnName === BNS.objInspector) ? Kekule.Widget.CheckButton :
  1995. (!!checkGroup) ? Kekule.Widget.RadioButton :
  1996. Kekule.Widget.Button;
  1997. result = new btnClass(parentGroup);
  1998. var targetWidget = this._getActionTargetWidget(actionClass);
  1999. var action = new actionClass(targetWidget);
  2000. if (checkGroup)
  2001. action.setCheckGroup(checkGroup);
  2002. //this.getActions().add(action);
  2003. actions.add(action);
  2004. result.setAction(action);
  2005. }
  2006. }
  2007. return result;
  2008. */
  2009. var result = null;
  2010. var name = DataType.isObjectValue(btnName)? btnName.name: btnName;
  2011. var actionClass = this.getCompActionClass(name);
  2012. var action = this._createToolButtonAction(btnName, actions, checkGroup);
  2013. if (DataType.isObjectValue(btnName) && !actionClass) // custom button
  2014. {
  2015. if (!actionClass) // no binded action, custom button
  2016. var objDefHash = Object.extend({'widget': Kekule.Widget.Button}, btnName);
  2017. result = Kekule.Widget.Utils.createFromHash(parentGroup, objDefHash);
  2018. }
  2019. else // predefined names
  2020. {
  2021. var preferredWidgetClass = action.getPreferredWidgetClass && action.getPreferredWidgetClass();
  2022. //console.log(action.getClassName(), preferredWidgetClass);
  2023. var btnClass =
  2024. preferredWidgetClass? preferredWidgetClass:
  2025. (btnName === BNS.objInspector) ? Kekule.Widget.CheckButton :
  2026. (!!checkGroup) ? Kekule.Widget.RadioButton :
  2027. Kekule.Widget.Button;
  2028. result = new btnClass(parentGroup);
  2029. }
  2030. if (action)
  2031. result.setAction(action);
  2032. return result;
  2033. },
  2034. /** @private */
  2035. _createActionButton: function(action, parentWidget)
  2036. {
  2037. var checkGroup = action.getCheckGroup();
  2038. var preferredClass = action.getPreferredWidgetClass && action.getPreferredWidgetClass();
  2039. var btnClass =
  2040. preferredClass? preferredClass:
  2041. (!!checkGroup) ? Kekule.Widget.RadioButton:
  2042. Kekule.Widget.Button;
  2043. var btn = new btnClass(parentWidget);
  2044. btn.setAction(action);
  2045. return btn;
  2046. },
  2047. /** @private */
  2048. _createToolButtonAction: function(actionNameOrHash, defActions, checkGroup)
  2049. {
  2050. var result = null;
  2051. var name = DataType.isObjectValue(actionNameOrHash)? actionNameOrHash.name: actionNameOrHash;
  2052. var children = DataType.isObjectValue(actionNameOrHash)? actionNameOrHash.attached: null;
  2053. var actionClass = this.getCompActionClass(name);
  2054. var result;
  2055. if (DataType.isObjectValue(actionNameOrHash) && !actionClass) // custom button
  2056. {
  2057. var objDefHash = actionNameOrHash;
  2058. var actionClass = objDefHash.actionClass;
  2059. if (actionClass) // create action
  2060. {
  2061. if (typeof(actionClass) === 'string')
  2062. actionClass = ClassEx.findClass(objDefHash.actionClass);
  2063. }
  2064. }
  2065. if (actionClass)
  2066. {
  2067. var actionMap = this.getActionMap();
  2068. // check if this action already exists
  2069. var result = actionMap.get(actionClass);
  2070. if (!result)
  2071. {
  2072. var result = new actionClass(this._getActionTargetWidget(actionClass));
  2073. //this.getActions().add(action);
  2074. actionMap.set(actionClass, result);
  2075. }
  2076. var actualGroup = checkGroup || '';
  2077. var actionExplicitGroup = result.getExplicitGroup && result.getExplicitGroup();
  2078. if (Kekule.ObjUtils.notUnset(actionExplicitGroup))
  2079. {
  2080. actualGroup = actionExplicitGroup;
  2081. }
  2082. if (actualGroup)
  2083. result.setCheckGroup(actualGroup);
  2084. if (result && defActions)
  2085. defActions.add(result);
  2086. if (result && result.addAttachedAction)
  2087. {
  2088. //result.setChecked(false);
  2089. var subGroupName = result.getClassName();
  2090. // result.clearAttachedActions();
  2091. var oldAttachedActions = Kekule.ArrayUtils.clone(result.getAttachedActions().getActions() || []);
  2092. var attachChildAction = function(action, childAction, oldAttachedActions, asDefault, childIndex)
  2093. {
  2094. if (!childAction)
  2095. return null;
  2096. var oldIndex = oldAttachedActions.indexOf(childAction);
  2097. if (oldIndex >= 0) // action already attached, change position
  2098. {
  2099. oldAttachedActions[oldIndex] = null; // indicating this action has been used
  2100. //console.log('use old action', oldIndex, childAction.getClassName());
  2101. }
  2102. else
  2103. action.addAttachedAction(childAction, asDefault);
  2104. if (Kekule.ObjUtils.notUnset(childIndex)) // change position
  2105. {
  2106. action.setAttachedActionIndex(childAction, childIndex);
  2107. }
  2108. };
  2109. if (children) // has custom defined chem tool children buttons
  2110. {
  2111. for (var i = 0, l = children.length; i < l; ++i)
  2112. {
  2113. var child = children[i];
  2114. var childAction = this._createToolButtonAction(child, null, subGroupName); // do not add to default action list
  2115. attachChildAction(result, childAction, oldAttachedActions, i === 0, i);
  2116. }
  2117. }
  2118. else // use default attached classes
  2119. {
  2120. var attachedActionClasses = result.getAttachedActionClasses();
  2121. if (attachedActionClasses)
  2122. {
  2123. for (var i = 0, l = attachedActionClasses.length; i < l; ++i)
  2124. {
  2125. var aClass = attachedActionClasses[i];
  2126. var childAction = actionMap.get(aClass);
  2127. if (!childAction)
  2128. {
  2129. childAction = new aClass(this._getActionTargetWidget(aClass));
  2130. var childExplicitGroup = (childAction.getExplicitGroup && childAction.getExplicitGroup());
  2131. if (Kekule.ObjUtils.notUnset(childExplicitGroup))
  2132. {
  2133. childAction.setCheckGroup(childExplicitGroup);
  2134. // console.log('set check group', childAction.getClassName(), childExplicitGroup);
  2135. }
  2136. else
  2137. childAction.setCheckGroup(subGroupName);
  2138. actionMap.set(aClass, childAction);
  2139. }
  2140. /*
  2141. else
  2142. {
  2143. console.log('use old action', childAction.getClassName());
  2144. if (childAction.getAttachedActions)
  2145. console.log(childAction.getAttachedActions().getActions());
  2146. }
  2147. */
  2148. //result.addAttachedAction(childAction, i === 0);
  2149. attachChildAction(result, childAction, oldAttachedActions, i === 0, i);
  2150. }
  2151. }
  2152. }
  2153. // at last remove unused old actions
  2154. if (oldAttachedActions.length)
  2155. {
  2156. //var actions = result.getAttachedActions();
  2157. for (var i = 0, l = oldAttachedActions.length; i < l; ++i)
  2158. {
  2159. var unusedAction = oldAttachedActions[i];
  2160. if (unusedAction)
  2161. {
  2162. result.removeAttachedAction(unusedAction);
  2163. //actions.remove(unusedAction);
  2164. actionMap.remove(unusedAction.getClass());
  2165. //unusedAction.finalize();
  2166. }
  2167. }
  2168. }
  2169. }
  2170. }
  2171. return result;
  2172. },
  2173. /**
  2174. * Create a toolbar inside editor UI.
  2175. * @returns {Kekule.Widget.ButtonGroup}
  2176. * @private
  2177. */
  2178. createInnerToolbar: function(parentElem)
  2179. {
  2180. var toolBar = new Kekule.Widget.ButtonGroup(this);
  2181. toolBar.addClassName(CCNS.COMPOSER_TOOLBAR);
  2182. toolBar.addClassName(CCNS.INNER_TOOLBAR);
  2183. toolBar.addClassName(CNS.DYN_CREATED);
  2184. toolBar.setShowText(false);
  2185. toolBar.doSetShowGlyph(true);
  2186. toolBar.appendToElem(parentElem || this.getElement());
  2187. return toolBar;
  2188. },
  2189. /**
  2190. * Create common tool bar.
  2191. * @returns {Kekule.Widget.ButtonGroup}
  2192. * @private
  2193. */
  2194. createCommonToolbar: function()
  2195. {
  2196. var parentElem = this.getTopRegionElem();
  2197. var toolbar = this.createInnerToolbar(parentElem);
  2198. toolbar.addClassName(CCNS.COMPOSER_COMMON_TOOLBAR);
  2199. // add buttons
  2200. var btns = this.getCommonToolButtons();
  2201. btns = Kekule.ArrayUtils.exclude(btns, this.getZoomButtonNames());
  2202. var actions = this.getCommonActions();
  2203. var editor = this.getEditor();
  2204. actions.clear();
  2205. for (var i = 0, l = btns.length; i < l; ++i)
  2206. {
  2207. var name = btns[i];
  2208. var btn = this.createToolButton(name, toolbar, actions);
  2209. }
  2210. this.setCommonBtnGroup(toolbar);
  2211. toolbar.addClassName(CNS.DYN_CREATED);
  2212. this.updateZoomToolbar();
  2213. this.adjustComponentPositions();
  2214. return toolbar;
  2215. },
  2216. /**
  2217. * Create zoom tool bar.
  2218. * @returns {Kekule.Widget.ButtonGroup}
  2219. * @private
  2220. */
  2221. createZoomToolbar: function()
  2222. {
  2223. // add buttons
  2224. var btns = Kekule.ArrayUtils.intersect(this.getCommonToolButtons(), this.getZoomButtonNames());
  2225. if (btns.length)
  2226. {
  2227. var parentElem = this.getBottomRegionElem();
  2228. var toolbar = this.createInnerToolbar(parentElem);
  2229. toolbar.addClassName(CCNS.COMPOSER_ZOOM_TOOLBAR);
  2230. var actions = this.getZoomActions();
  2231. var editor = this.getEditor();
  2232. actions.clear();
  2233. for (var i = 0, l = btns.length; i < l; ++i)
  2234. {
  2235. var name = btns[i];
  2236. var btn = this.createToolButton(name, toolbar, actions);
  2237. }
  2238. this.setZoomBtnGroup(toolbar);
  2239. toolbar.addClassName(CNS.DYN_CREATED);
  2240. this.adjustComponentPositions();
  2241. return toolbar;
  2242. }
  2243. else
  2244. {
  2245. this.setZoomBtnGroup(null);
  2246. return null;
  2247. }
  2248. },
  2249. /**
  2250. * Update common toolbar, actually free the old one and create a new one.
  2251. * @private
  2252. */
  2253. updateCommonToolbar: function()
  2254. {
  2255. var old = this.getCommonBtnGroup();
  2256. if (old)
  2257. old.finalize();
  2258. this.createCommonToolbar();
  2259. },
  2260. /**
  2261. * Update zoom toolbar, actually free the old one and create a new one.
  2262. * @private
  2263. */
  2264. updateZoomToolbar: function()
  2265. {
  2266. var old = this.getZoomBtnGroup();
  2267. if (old)
  2268. old.finalize();
  2269. this.createZoomToolbar();
  2270. },
  2271. /**
  2272. * Create chem tool bar.
  2273. * @returns {Kekule.Widget.ButtonGroup}
  2274. * @private
  2275. */
  2276. createChemToolbar: function()
  2277. {
  2278. var parentElem = this.getLeftRegionElem();
  2279. var toolbar = this.createInnerToolbar(parentElem);
  2280. toolbar.addClassName(CCNS.COMPOSER_CHEM_TOOLBAR);
  2281. toolbar.setLayout(Kekule.Widget.Layout.VERTICAL);
  2282. // add buttons
  2283. var btns = this.getChemToolButtons();
  2284. var actions = this.getChemActions();
  2285. actions.clear();
  2286. var checkGroup = 'chemTools';
  2287. var firstBtn;
  2288. for (var i = 0, l = btns.length; i < l; ++i)
  2289. {
  2290. var name = btns[i];
  2291. var btn = this.createToolButton(name, toolbar, actions, checkGroup);
  2292. if (i === 0)
  2293. firstBtn = btn;
  2294. }
  2295. this.setChemBtnGroup(toolbar);
  2296. toolbar.addClassName(CNS.DYN_CREATED);
  2297. this.adjustComponentPositions();
  2298. if (firstBtn)
  2299. {
  2300. var action = firstBtn.getAction();
  2301. if (action)
  2302. {
  2303. action.setChecked(true);
  2304. var attachedActions = action.getAttachedActions && action.getAttachedActions();
  2305. this.bindAssocActions(attachedActions || null);
  2306. /*
  2307. // force update the assoc actions
  2308. if (!action.getChecked())
  2309. action.setChecked(true);
  2310. else
  2311. action.setChecked(false).setChecked(true);
  2312. */
  2313. }
  2314. }
  2315. return toolbar;
  2316. },
  2317. /**
  2318. * Update chem toolbar, actually free the old one and create a new one.
  2319. * @private
  2320. */
  2321. updateChemToolbar: function()
  2322. {
  2323. var old = this.getChemBtnGroup();
  2324. if (old)
  2325. old.finalize();
  2326. this.createChemToolbar();
  2327. },
  2328. /**
  2329. * Create assoc chem tool bar.
  2330. * @returns {Kekule.Widget.ButtonGroup}
  2331. * @private
  2332. */
  2333. createAssocToolbar: function()
  2334. {
  2335. var parentElem = this.getBottomRegionElem();
  2336. var toolbar = this.createInnerToolbar(parentElem);
  2337. //toolbar.setLayout(Kekule.Widget.Layout.VERTICAL);
  2338. toolbar.addClassName(CCNS.COMPOSER_ASSOC_TOOLBAR);
  2339. this.setAssocBtnGroup(toolbar);
  2340. this.adjustAssocToolbarPositions();
  2341. toolbar.addClassName(CNS.DYN_CREATED);
  2342. return toolbar;
  2343. },
  2344. /** @private */
  2345. changeAssocToolbarRegion: function(toLeftRegion)
  2346. {
  2347. var toolbar = this.getAssocBtnGroup();
  2348. if (!toolbar)
  2349. toolbar = this.createAssocToolbar();
  2350. var parent;
  2351. if (toLeftRegion)
  2352. {
  2353. parent = this.getLeftRegionElem();
  2354. if (toolbar.getElement().parentNode !== parent)
  2355. {
  2356. toolbar.setLayout(Kekule.Widget.Layout.VERTICAL);
  2357. toolbar.appendToElem(parent);
  2358. }
  2359. }
  2360. else // to bottom region
  2361. {
  2362. parent = this.getBottomRegionElem();
  2363. if (toolbar.getElement().parentNode !== parent)
  2364. {
  2365. toolbar.setLayout(Kekule.Widget.Layout.HORIZONTAL);
  2366. var refElem = Kekule.DomUtils.getFirstChildElem(parent);
  2367. // insert as the first elem
  2368. parent.insertBefore(toolbar.getElement(), refElem);
  2369. }
  2370. }
  2371. },
  2372. /**
  2373. * Show assoc chem tool bar.
  2374. */
  2375. showAssocToolbar: function()
  2376. {
  2377. var toolbar = this.getAssocBtnGroup();
  2378. if (!toolbar)
  2379. toolbar = this.createAssocToolbar();
  2380. var self = this;
  2381. /*
  2382. if (this.isStyleToolbarShown()) // need to hide style toolbar first
  2383. {
  2384. console.log('here hide style');
  2385. this.hideStyleToolbar();
  2386. }
  2387. */
  2388. toolbar.show(null, function()
  2389. {
  2390. self.uiLayoutChanged();
  2391. //self.updateStyleToolbarState();
  2392. //setTimeout(self.updateObjModifierToolbarStateBind, 0);
  2393. //setTimeout(self.updateStyleToolbarStateBind, 0); // IMPORTANT, defer call to update style toolbar, avoid show/hide it too quickly
  2394. setTimeout(self.updateSelectionAssocToolbarStateBind, 0);
  2395. });
  2396. return this;
  2397. },
  2398. /**
  2399. * Hide assoc chem tool bar.
  2400. */
  2401. hideAssocToolbar: function()
  2402. {
  2403. var toolbar = this.getAssocBtnGroup();
  2404. if (toolbar)
  2405. {
  2406. var self = this;
  2407. toolbar.hide(null, function()
  2408. {
  2409. self.uiLayoutChanged();
  2410. //self.updateStyleToolbarState(); // may need to reopen style toolbar
  2411. //setTimeout(self.updateObjModifierToolbarStateBind, 0);
  2412. //setTimeout(self.updateStyleToolbarStateBind, 0); // IMPORTANT, defer call to update style toolbar, avoid show/hide it too quickly
  2413. setTimeout(self.updateSelectionAssocToolbarStateBind, 0);
  2414. }
  2415. );
  2416. }
  2417. return this;
  2418. },
  2419. /**
  2420. * Check if assoc chem tool bar is visible.
  2421. * @returns {Bool}
  2422. */
  2423. isAssocToolbarShown: function()
  2424. {
  2425. var toolbar = this.getAssocBtnGroup();
  2426. return toolbar && toolbar.isShown();
  2427. },
  2428. /**
  2429. * Create buttons in assoc tool bar to display actions.
  2430. * @param {Kekule.ActionList} actions
  2431. * @private
  2432. */
  2433. bindAssocActions: function(actions)
  2434. {
  2435. var toolbar = this.getAssocBtnGroup();
  2436. if (!toolbar)
  2437. toolbar = this.createAssocToolbar();
  2438. toolbar.clearWidgets();
  2439. if (actions)
  2440. {
  2441. for (var i = 0, l = actions.getActionCount(); i < l; ++i)
  2442. {
  2443. var action = actions.getActionAt(i);
  2444. /*
  2445. var checkGroup = action.getCheckGroup();
  2446. var btnClass = (!!checkGroup) ? Kekule.Widget.RadioButton : Kekule.Widget.Button;
  2447. var btn = new btnClass(toolbar);
  2448. btn.setAction(action);
  2449. */
  2450. var btn = this._createActionButton(action, toolbar);
  2451. }
  2452. }
  2453. },
  2454. /**
  2455. * Create obj modifier tool bar.
  2456. * @returns {Kekule.Widget.Toolbar}
  2457. * @private
  2458. */
  2459. createObjModifierToolbar: function()
  2460. {
  2461. if (this.getEnableObjModifierToolbar())
  2462. {
  2463. var toolbar = new Kekule.Editor.ComposerObjModifierToolbar(this);
  2464. toolbar.appendToElem(this.getBottomRegionElem());
  2465. this.setObjModifierToolbar(toolbar);
  2466. this.adjustAssocToolbarPositions();
  2467. this.updateObjModifierToolbarState();
  2468. return toolbar;
  2469. }
  2470. else
  2471. return null;
  2472. },
  2473. /**
  2474. * Show obj modifier tool bar.
  2475. */
  2476. showObjModifierToolbar: function()
  2477. {
  2478. //console.log('show');
  2479. if (this.getEnableObjModifierToolbar())
  2480. {
  2481. var toolbar = this.getObjModifierToolbar();
  2482. if (!toolbar.isShown())
  2483. {
  2484. var self = this;
  2485. toolbar.show(null, function()
  2486. {
  2487. self.uiLayoutChanged();
  2488. });
  2489. }
  2490. }
  2491. return this;
  2492. },
  2493. /**
  2494. * Hide obj modifier tool bar.
  2495. */
  2496. hideObjModifierToolbar: function()
  2497. {
  2498. var toolbar = this.getPropStoreFieldValue('objModifierToolbar');
  2499. if (toolbar)
  2500. {
  2501. if (toolbar.isShown())
  2502. {
  2503. var self = this;
  2504. toolbar.hide(null, function()
  2505. {
  2506. self.uiLayoutChanged();
  2507. }, null, {instantly: true}
  2508. );
  2509. }
  2510. }
  2511. return this;
  2512. },
  2513. /**
  2514. * Check if obj modifier tool bar is visible.
  2515. * @returns {Bool}
  2516. */
  2517. isObjModifierToolbarShown: function()
  2518. {
  2519. var toolbar = this.getPropStoreFieldValue('objModifierToolbar');
  2520. return (toolbar && toolbar.isShown());
  2521. },
  2522. /**
  2523. * Update obj modifier toolbar show/hide state according to editor's state.
  2524. * @private
  2525. */
  2526. updateObjModifierToolbarState: function()
  2527. {
  2528. var showToolbar = false;
  2529. if (this.getEnableObjModifierToolbar() && this.needShowSelectionAssocToolbar())
  2530. {
  2531. /*
  2532. // further check if currently is select series IA controllers
  2533. var iaController = this.getEditor().getActiveIaController();
  2534. showToolbar = ((iaController instanceof Kekule.Editor.BasicManipulationIaController) && (iaController.getEnableSelect()))
  2535. || (iaController instanceof Kekule.Editor.ClientDragScrollIaController);
  2536. */
  2537. showToolbar = true;
  2538. /*
  2539. // check if currently the space can fullfill a modifier toolbar
  2540. var toolbar = this.getObjModifierToolbar();
  2541. var modifierRect = Kekule.HtmlElementUtils.getElemBoundingClientRect(toolbar.getElement());
  2542. var zoomBar = this.getZoomBtnGroup();
  2543. var zoomBarRect = Kekule.HtmlElementUtils.getElemBoundingClientRect(zoomBar.getElement());
  2544. var gap = 50; // TODO: current fixed, gap between modifier bar and zoom bar
  2545. showToolbar = (modifierRect.left + modifierRect.width + gap < zoomBarRect.left); // if can fullfill, just show it
  2546. console.log(showToolbar, modifierRect, zoomBarRect);
  2547. */
  2548. }
  2549. if (showToolbar)
  2550. {
  2551. this.showObjModifierToolbar();
  2552. this.getObjModifierToolbar().updateState();
  2553. }
  2554. else
  2555. {
  2556. this.hideObjModifierToolbar();
  2557. }
  2558. },
  2559. /**
  2560. * Create style setting tool bar.
  2561. * @returns {Kekule.Widget.Toolbar}
  2562. * @private
  2563. */
  2564. createStyleToolbar: function()
  2565. {
  2566. if (this.getEnableStyleToolbar())
  2567. {
  2568. var toolbar = new Kekule.Editor.ComposerStyleToolbar(this);
  2569. toolbar.setComponentNames(this.getStyleBarComponents());
  2570. toolbar.appendToElem(this.getBottomRegionElem());
  2571. this.setStyleToolbar(toolbar);
  2572. this.adjustAssocToolbarPositions();
  2573. this.updateStyleToolbarState();
  2574. return toolbar;
  2575. }
  2576. else
  2577. return null;
  2578. },
  2579. /**
  2580. * Show style tool bar.
  2581. */
  2582. showStyleToolbar: function()
  2583. {
  2584. //console.log('show style');
  2585. if (this.getEnableStyleToolbar())
  2586. {
  2587. var toolbar = this.getStyleToolbar();
  2588. if (!toolbar.isShown())
  2589. {
  2590. var self = this;
  2591. toolbar.show(null, function()
  2592. {
  2593. self.uiLayoutChanged();
  2594. });
  2595. }
  2596. }
  2597. return this;
  2598. },
  2599. /**
  2600. * Hide style tool bar.
  2601. */
  2602. hideStyleToolbar: function()
  2603. {
  2604. //console.log('hide style');
  2605. var toolbar = this.getPropStoreFieldValue('styleToolbar');
  2606. if (toolbar)
  2607. {
  2608. if (toolbar.isShown())
  2609. {
  2610. var self = this;
  2611. toolbar.hide(null, function()
  2612. {
  2613. self.uiLayoutChanged();
  2614. }
  2615. );
  2616. }
  2617. }
  2618. return this;
  2619. },
  2620. /**
  2621. * Check if style tool bar is visible.
  2622. * @returns {Bool}
  2623. */
  2624. isStyleToolbarShown: function()
  2625. {
  2626. var toolbar = this.getPropStoreFieldValue('styleToolbar'); //this.getStyleToolbar();
  2627. return (toolbar && toolbar.isShown());
  2628. },
  2629. /**
  2630. * Update style toolbar show/hide state according to editor's state.
  2631. * @private
  2632. */
  2633. updateStyleToolbarState: function()
  2634. {
  2635. if (this.getEnableStyleToolbar() && this.needShowSelectionAssocToolbar())
  2636. {
  2637. this.showStyleToolbar();
  2638. this.getStyleToolbar().updateState();
  2639. }
  2640. else
  2641. {
  2642. this.hideStyleToolbar();
  2643. }
  2644. },
  2645. /**
  2646. * Check if the assoc selection to set properties of selected objects (style or obj modifier toolbar) should be shown.
  2647. * @returns {Bool}
  2648. * @private
  2649. */
  2650. needShowSelectionAssocToolbar: function()
  2651. {
  2652. var editor = this.getEditor();
  2653. return editor && editor.hasSelection();
  2654. },
  2655. /**
  2656. * Update style & obj modifier toolbar show/hide state according to editor's state.
  2657. * @private
  2658. */
  2659. updateSelectionAssocToolbarState: function()
  2660. {
  2661. this.adjustStyleAndObjModifierToolbarPosition();
  2662. this.updateStyleToolbarState();
  2663. this.updateObjModifierToolbarState();
  2664. },
  2665. /**
  2666. * Update both common and chem actions.
  2667. * @private
  2668. */
  2669. updateAllActions: function()
  2670. {
  2671. this.getCommonActions().updateAll();
  2672. this.getZoomActions().updateAll();
  2673. this.getChemActions().updateAll();
  2674. },
  2675. /**
  2676. * Update states of child UI widgets.
  2677. * @private
  2678. */
  2679. updateUiWidgets: function()
  2680. {
  2681. this.updateStyleToolbarState();
  2682. this.updateObjModifierToolbarState();
  2683. },
  2684. ////// about configurator
  2685. /** @ignore */
  2686. createConfigurator: function($super)
  2687. {
  2688. var result = $super();
  2689. result.addEventListener('configChange', function(e){
  2690. // render config change need to repaint context
  2691. this.getEditor().repaint();
  2692. }, this);
  2693. return result;
  2694. }
  2695. });
  2696. /**
  2697. * A class method to returns the preferred min dimension of composer widget.
  2698. */
  2699. Kekule.Editor.Composer.getMinPreferredDimension = function()
  2700. {
  2701. return {width: 630, height: 350}; // current fixed
  2702. };
  2703. /**
  2704. * A special class to give a setting facade for Chem Composer.
  2705. * Do not use this class alone.
  2706. * @class
  2707. * @augments Kekule.Widget.BaseWidget.Settings
  2708. */
  2709. Kekule.Editor.Composer.Settings = Class.create(Kekule.Widget.BaseWidget.Settings,
  2710. /** @lends Kekule.Editor.Composer.Settings# */
  2711. {
  2712. /** @private */
  2713. CLASS_NAME: 'Kekule.Editor.Composer.Settings',
  2714. /** @construct */
  2715. initialize: function($super, composer)
  2716. {
  2717. $super(composer);
  2718. },
  2719. /** @private */
  2720. initProperties: function()
  2721. {
  2722. //this.defineProp('composer', {'dataType': 'Kekule.Editor.Composer', 'serializable': false, 'scope': PS.PUBLIC});
  2723. this.defineDelegatedProps(['enableCreateNewDoc', 'enableLoadNewFile', 'initOnNewDoc', 'enableOperHistory', 'allowCreateNewChild', 'enableStyleToolbar']);
  2724. }
  2725. });
  2726. /**
  2727. * A special widget class to open a config widget for ChemObjDisplayer.
  2728. * Do not use this widget alone.
  2729. * @class
  2730. * @augments Kekule.Widget.Configurator
  2731. *
  2732. * @param {Kekule.Editor.Composer} composer
  2733. */
  2734. Kekule.Editor.Composer.Configurator = Class.create(Kekule.Widget.Configurator,
  2735. /** @lends Kekule.Editor.Composer.Configurator# */
  2736. {
  2737. /** @private */
  2738. CLASS_NAME: 'Kekule.Editor.Composer.Configurator',
  2739. /** @private */
  2740. TAB_BTN_DATA_FIELD: '__$data__',
  2741. /** @construct */
  2742. initialize: function($super, composer)
  2743. {
  2744. $super(composer);
  2745. },
  2746. /** @ignore */
  2747. initPropValues: function($super)
  2748. {
  2749. $super();
  2750. this.setLayout(Kekule.Widget.Layout.HORIZONTAL);
  2751. },
  2752. /** @private */
  2753. getCategoryInfos: function($super)
  2754. {
  2755. var result = $super();
  2756. var composer = this.getComposer();
  2757. var editor = composer.getEditor();
  2758. // renderConfigs and displayerConfigs
  2759. var configObjs = [editor.getDisplayerConfigs(), editor.getRenderConfigs()];
  2760. for (var j = 0, k = configObjs.length; j < k; ++j)
  2761. {
  2762. var config = configObjs[j];
  2763. var props = config.getPropListOfScopes([Class.PropertyScope.PUBLISHED]);
  2764. for (var i = 0, l = props.getLength(); i < l; ++i)
  2765. {
  2766. var propInfo = props.getPropInfoAt(i);
  2767. var obj = config.getPropValue(propInfo.name);
  2768. if (obj)
  2769. {
  2770. result.push({
  2771. 'obj': obj,
  2772. 'name': propInfo.name,
  2773. 'title': propInfo.title,
  2774. 'description': propInfo.description
  2775. });
  2776. }
  2777. }
  2778. }
  2779. return result;
  2780. },
  2781. /** @private */
  2782. getComposer: function()
  2783. {
  2784. return this.getWidget();
  2785. },
  2786. /** @private */
  2787. getEditor: function()
  2788. {
  2789. return this.getComposer().getEditor();
  2790. }
  2791. });
  2792. // register predefined settings of viewer
  2793. Kekule._registerAfterLoadSysProc(function(){
  2794. var EMC = Kekule.Editor.ObjModifier.Category;
  2795. var SM = Kekule.ObjPropSettingManager;
  2796. SM.register('Kekule.Editor.Composer.fullFunc', { // composer with all functions
  2797. enableStyleToolbar: true,
  2798. enableOperHistory: true,
  2799. enableLoadNewFile: true,
  2800. enableCreateNewDoc: true,
  2801. allowCreateNewChild: true,
  2802. commonToolButtons: null, // create all default common tool buttons
  2803. chemToolButtons: null, // create all default chem tool buttons
  2804. styleToolComponentNames: null, // create all default style components
  2805. allowedObjModifierCategories: null // allow modifiers of all categories
  2806. });
  2807. SM.register('Kekule.Editor.Composer.molOnly', { // composer that can only edit molecule
  2808. enableStyleToolbar: true,
  2809. enableOperHistor: true,
  2810. enableLoadNewFile: true,
  2811. enableCreateNewDoc: true,
  2812. allowCreateNewChild: true,
  2813. commonToolButtons: null, // create all default common tool buttons
  2814. chemToolButtons: [
  2815. BNS.manipulate,
  2816. BNS.erase,
  2817. BNS.molBond,
  2818. BNS.molAtom,
  2819. // BNS.molFormula,
  2820. BNS.molRing,
  2821. BNS.molCharge
  2822. ], // create only chem tool buttons related with molecule
  2823. styleToolComponentNames: null, // create all default style components
  2824. allowedObjModifierCategories: [EMC.GENERAL, EMC.CHEM_STRUCTURE] // only all chem structure modifiers
  2825. });
  2826. SM.register('Kekule.Editor.Composer.compact', { // composer with less tool buttons
  2827. enableStyleToolbar: false,
  2828. commonToolButtons: [
  2829. BNS.newDoc,
  2830. BNS.loadData,
  2831. BNS.saveData,
  2832. BNS.undo,
  2833. BNS.redo,
  2834. BNS.zoomIn,
  2835. BNS.zoomOut
  2836. ],
  2837. chemToolButtons: null, // create all default chem tool buttons
  2838. styleToolComponentNames: null, // create all default style components
  2839. allowedObjModifierCategories: null // allow modifiers of all categories
  2840. });
  2841. SM.register('Kekule.Editor.Composer.singleObj', { // only allows create one object in composer
  2842. allowCreateNewChild: false
  2843. });
  2844. });
  2845. /**
  2846. * A dialog with a composer, executed to edit or create new chem object.
  2847. * @class
  2848. * @augments Kekule.Widget.Dialog
  2849. *
  2850. * @property {Kekule.Editor.Composer} composer Composer widget in dialog.
  2851. * @property {Kekule.ChemObject} chemObj Get or set chem object in composer.
  2852. */
  2853. Kekule.Editor.ComposerDialog = Class.create(Kekule.Widget.Dialog,
  2854. /** @lends Kekule.Editor.ComposerDialog# */
  2855. {
  2856. /** @private */
  2857. CLASS_NAME: 'Kekule.Editor.ComposerDialog',
  2858. initialize: function($super, parentOrElementOrDocument, caption, buttons)
  2859. {
  2860. $super(parentOrElementOrDocument, caption,
  2861. buttons || [Kekule.Widget.DialogButtons.OK, Kekule.Widget.DialogButtons.CANCEL]);
  2862. },
  2863. /** @private */
  2864. initProperties: function()
  2865. {
  2866. this.defineProp('composer', {'dataType': 'Kekule.Editor.Composer', 'serializable': false, 'setter': null});
  2867. this.defineProp('chemObj', {'dataType': 'Kekule.ChemObject', 'serializable': false,
  2868. 'getter': function()
  2869. {
  2870. var c = this.getComposer();
  2871. return c && c.getChemObj();
  2872. },
  2873. 'setter': function(value)
  2874. {
  2875. var c = this.getComposer();
  2876. if (c)
  2877. c.setChemObj(value);
  2878. }
  2879. });
  2880. },
  2881. /** @ignore */
  2882. initPropValues: function($super)
  2883. {
  2884. $super();
  2885. //this.setButtons([Kekule.Widget.DialogButtons.OK, Kekule.Widget.DialogButtons.CANCEL]);
  2886. },
  2887. /** @ignore */
  2888. doGetWidgetClassName: function($super)
  2889. {
  2890. return $super() + ' ' + CCNS.COMPOSER_DIALOG;
  2891. },
  2892. /** @ignore */
  2893. doCreateClientContents: function($super, clientElem)
  2894. {
  2895. $super();
  2896. var composer = this.doCreateComposerWidget();
  2897. this.setPropStoreFieldValue('composer', composer);
  2898. composer.appendToElem(clientElem);
  2899. },
  2900. /**
  2901. * Returns object in dialog that to be saved.
  2902. * @returns {Kekule.ChemObject}
  2903. * @private
  2904. */
  2905. getSavingTargetObj: function()
  2906. {
  2907. var c = this.getComposer();
  2908. if (c)
  2909. return c.getSavingTargetObj();
  2910. else
  2911. return null;
  2912. },
  2913. /**
  2914. * @private
  2915. */
  2916. doCreateComposerWidget: function()
  2917. {
  2918. // TODO: currently fixed to composer
  2919. var result = new Kekule.Editor.Composer(this);
  2920. result.addClassName(CNS.DYN_CREATED);
  2921. if (result.setResizable)
  2922. result.setResizable(true);
  2923. var editor = result.getEditor();
  2924. /*
  2925. editor.setEnableCreateNewDoc(false);
  2926. editor.setEnableLoadNewFile(false);
  2927. editor.setAllowCreateNewChild(false);
  2928. */
  2929. editor.addClassName(CNS.DYN_CREATED);
  2930. return result;
  2931. }
  2932. });
  2933. /**
  2934. * An iframe containing a composer widget.
  2935. * @class
  2936. * @augments Kekule.ChemWidget.AbstractWidget
  2937. *
  2938. * @property {Kekule.Editor.Composer} composer Composer widget in frame.
  2939. *
  2940. */
  2941. Kekule.Editor.ComposerFrame = Class.create(Kekule.ChemWidget.AbstractWidget,
  2942. /** @lends Kekule.Editor.ComposerFrame# */
  2943. {
  2944. /** @private */
  2945. CLASS_NAME: 'Kekule.Editor.ComposerFrame',
  2946. /** @private */
  2947. BINDABLE_TAG_NAMES: ['iframe'],
  2948. initialize: function($super, parentOrElementOrDocument)
  2949. {
  2950. $super(parentOrElementOrDocument);
  2951. },
  2952. /** @private */
  2953. initProperties: function()
  2954. {
  2955. this.defineProp('composer', {'dataType': 'Kekule.Editor.Composer',
  2956. 'scope': Class.PropertyScope.PUBLIC, 'serializable': false,
  2957. 'setter': null
  2958. });
  2959. // private
  2960. this.defineProp('frameDocument', {'dataType': DataType.OBJECT,
  2961. 'scope': Class.PropertyScope.PUBLIC, 'serializable': false,
  2962. 'setter': null,
  2963. 'getter': function(){
  2964. return this.getElement().contentDocument;
  2965. }
  2966. });
  2967. this.defineProp('frameWindow', {'dataType': DataType.OBJECT,
  2968. 'scope': Class.PropertyScope.PUBLIC, 'serializable': false,
  2969. 'setter': null,
  2970. 'getter': function(){
  2971. return Kekule.DocumentUtils.getDefaultView(this.getFrameDocument());
  2972. }
  2973. });
  2974. },
  2975. /** @ignore */
  2976. initPropValues: function($super)
  2977. {
  2978. $super();
  2979. this.setMinDimension({'width': 550, height: 350});
  2980. this.setEnableDimensionTransform(true);
  2981. },
  2982. /** @ignore */
  2983. doGetWidgetClassName: function($super)
  2984. {
  2985. return $super() + ' ' + CCNS.COMPOSER_FRAME;
  2986. },
  2987. /** @ignore */
  2988. doCreateRootElement: function(doc)
  2989. {
  2990. var result = doc.createElement('iframe');
  2991. return result;
  2992. },
  2993. /** @ignore */
  2994. doBindElement: function($super, element)
  2995. {
  2996. $super(element);
  2997. var notInDom = !element.parentNode;
  2998. if (notInDom) // add to DOM first, otherwise the frame document will be null
  2999. {
  3000. this.setDisplayed(false);
  3001. var doc = element.ownerDocument;
  3002. doc.body.appendChild(element);
  3003. }
  3004. //console.log(this.getElement(), this.getElement().contentDocument);
  3005. this._createComposerWidgetInFrame();
  3006. },
  3007. /** @private */
  3008. _initFrame: function()
  3009. {
  3010. var content = '<!DOCTYPE html><html><head></head><body></body></html>';
  3011. var doc = this.getFrameDocument();
  3012. doc.open('text/htmlreplace');
  3013. doc.write(content);
  3014. doc.close();
  3015. },
  3016. /** @private */
  3017. _createComposerWidgetInFrame: function(callback)
  3018. {
  3019. this._initFrame(); // replace the quirk mode empty document with a standard one
  3020. var self = this;
  3021. var doc = this.getFrameDocument();
  3022. doc.documentElement.className = CCNS.COMPOSER_FRAME_CONTENT_DOC;
  3023. doc.body.className = CCNS.COMPOSER_FRAME_CONTENT_BODY;
  3024. // doc type
  3025. /*
  3026. if (doc.implementation && doc.implementation.createDocumentType)
  3027. {
  3028. var nodeDoctype = doc.implementation.createDocumentType('html', '', '');
  3029. if (doc.doctype)
  3030. {
  3031. doc.replaceChild(nodeDoctype, doc.doctype);
  3032. }
  3033. else
  3034. {
  3035. doc.insertBefore(nodeDoctype, doc.childNodes[0]);
  3036. }
  3037. }
  3038. // <meta charset="UTF-8">
  3039. var metaElem = doc.createElement('meta');
  3040. metaElem.setAttribute('charset', 'UTF-8');
  3041. doc.head.insertBefore(metaElem, Kekule.DomUtils.getFirstChildElem(doc.head));
  3042. */
  3043. Kekule.X.Event.addListener(doc.body, 'kekuleload', function(e){
  3044. var composer = new Kekule.Editor.Composer(doc);
  3045. /*
  3046. var style = composer.getElement().style;
  3047. style.width = '100%';
  3048. style.height = '100%';
  3049. */
  3050. composer.appendToElem(doc.body);
  3051. self.setPropStoreFieldValue('composer', composer);
  3052. if (callback)
  3053. callback(composer);
  3054. });
  3055. // assume element is an iframe, and insert Kekule.js script/css files into it
  3056. this._insertKekuleScriptAndStyleSheetFiles(doc);
  3057. },
  3058. /** @private */
  3059. _insertKekuleScriptAndStyleSheetFiles: function(frameDoc, callback)
  3060. {
  3061. var srcInfo = Kekule.scriptSrcInfo;
  3062. var headElem = frameDoc.head;
  3063. var cssLinkElem = frameDoc.createElement('link');
  3064. cssLinkElem.setAttribute('rel', 'stylesheet');
  3065. cssLinkElem.setAttribute('type', 'text/css');
  3066. cssLinkElem.setAttribute('href', Kekule.getStyleSheetUrl());
  3067. headElem.appendChild(cssLinkElem);
  3068. Kekule.ScriptFileUtils.appendScriptFile(frameDoc, Kekule.getScriptSrc(), callback);
  3069. }
  3070. });
  3071. })();