| /* |
| * Copyright (c) 2014-2018 BSI Business Systems Integration AG. |
| * All rights reserved. This program and the accompanying materials |
| * are made available under the terms of the Eclipse Public License v1.0 |
| * which accompanies this distribution, and is available at |
| * http://www.eclipse.org/legal/epl-v10.html |
| * |
| * Contributors: |
| * BSI Business Systems Integration AG - initial API and implementation |
| */ |
| import { |
| arrays, |
| Button, |
| ButtonAdapterMenu, |
| CompositeField, |
| Form, |
| FormField, |
| GroupBoxGridConfig, |
| GroupBoxLayout, |
| GroupBoxMenuItemsOrder, |
| HAlign, |
| HtmlComponent, |
| LogicalGridData, |
| LogicalGridLayout, |
| LogicalGridLayoutConfig, |
| MenuBar, |
| ResponsiveManager, |
| scout, |
| SplitBox, |
| strings, |
| TabBox, |
| TabItemKeyStroke, |
| tooltips, |
| WrappedFormField |
| } from '../../../index'; |
| import * as $ from 'jquery'; |
| |
| export default class GroupBox extends CompositeField { |
| |
| constructor() { |
| super(); |
| this._addWidgetProperties(['fields', 'notification', 'staticMenus']); |
| this._addCloneProperties(['menuBarVisible', 'bodyLayoutConfig', 'borderDecoration', 'borderVisible', 'expandable', 'expanded', 'gridColumnCount', 'scrollable', 'subLabel']); |
| |
| this.fields = []; |
| this.menus = []; |
| this.menuBarVisible = true; |
| this.menuBarPosition = GroupBox.MenuBarPosition.AUTO; |
| this.menuBarEllipsisPosition = MenuBar.EllipsisPosition.RIGHT; |
| this.notification = null; |
| this.bodyLayoutConfig = null; |
| this.borderDecoration = GroupBox.BorderDecoration.AUTO; |
| this.borderVisible = true; |
| this.mainBox = false; |
| // set to null to enable conditional default |
| // -> it will be set to true if it is a mainbox unless it was explicitly set to false |
| this.scrollable = null; |
| this.expandable = false; |
| this.expanded = true; |
| this.logicalGrid = scout.create('scout.VerticalSmartGrid'); |
| this.gridColumnCount = 2; |
| this.gridDataHints.useUiHeight = true; |
| this.gridDataHints.w = FormField.FULL_WIDTH; |
| this.controls = []; |
| this.systemButtons = []; |
| this.customButtons = []; |
| this.processButtons = []; |
| this.processMenus = []; |
| this.staticMenus = []; |
| this.responsive = null; |
| |
| this.$body = null; |
| this.$title = null; |
| this.$subLabel = null; |
| } |
| |
| static BorderDecoration = { |
| AUTO: 'auto', |
| EMPTY: 'empty', |
| LINE: 'line' |
| }; |
| |
| static MenuBarPosition = { |
| AUTO: 'auto', |
| TOP: 'top', |
| BOTTOM: 'bottom', |
| TITLE: 'title' |
| }; |
| |
| _init(model) { |
| super._init(model); |
| this.resolveConsts([{ |
| property: 'menuBarPosition', |
| constType: GroupBox.MenuBarPosition |
| }]); |
| this._setBodyLayoutConfig(this.bodyLayoutConfig); |
| this.menuBar = scout.create('MenuBar', { |
| parent: this, |
| menuOrder: new GroupBoxMenuItemsOrder() |
| }); |
| this._setFields(this.fields); |
| this._setMainBox(this.mainBox); |
| this._updateMenuBar(); |
| |
| ResponsiveManager.get().registerHandler(this, scout.create('GroupBoxResponsiveHandler', { |
| widget: this |
| })); |
| |
| this._setResponsive(this.responsive); |
| } |
| |
| _destroy() { |
| ResponsiveManager.get().unregisterHandler(this); |
| super._destroy(); |
| } |
| |
| /** |
| * @override |
| */ |
| getFields() { |
| return this.fields; |
| } |
| |
| insertField(field, index) { |
| var newFields = this.fields.slice(); |
| index = scout.nvl(index, this.fields.length); |
| newFields.splice(index, 0, field); |
| this.setFields(newFields); |
| } |
| |
| insertFieldBefore(field, sibling) { |
| scout.assertParameter('sibling', sibling); |
| var index = this.fields.indexOf(sibling); |
| this.insertField(field, index); |
| } |
| |
| insertFieldAfter(field, sibling) { |
| scout.assertParameter('sibling', sibling); |
| var index = this.fields.indexOf(sibling) + 1; |
| this.insertField(field, index); |
| } |
| |
| deleteField(field) { |
| var newFields = this.fields.slice(), |
| index = this.fields.indexOf(field); |
| if (index < 0) { |
| return; |
| } |
| newFields.splice(index, 1); |
| this.setFields(newFields); |
| } |
| |
| setFields(fields) { |
| this.setProperty('fields', fields); |
| } |
| |
| _setFields(fields) { |
| this._setProperty('fields', fields); |
| this._prepareFields(); |
| } |
| |
| _renderFields(fields) { |
| this._renderExpanded(); |
| this.invalidateLogicalGrid(true); |
| } |
| |
| /** |
| * @override |
| */ |
| _initKeyStrokeContext() { |
| super._initKeyStrokeContext(); |
| this.keyStrokeContext.invokeAcceptInputOnActiveValueField = true; |
| this.keyStrokeContext.$bindTarget = this._keyStrokeBindTarget.bind(this); |
| } |
| |
| /** |
| * @override FormField.js |
| */ |
| _setKeyStrokes(keyStrokes) { |
| keyStrokes = arrays.ensure(keyStrokes); |
| |
| var groupBoxRenderingHints = { |
| render: function() { |
| return true; |
| }, |
| offset: 0, |
| hAlign: HAlign.RIGHT, |
| $drawingArea: function($drawingArea, event) { |
| if (this.labelVisible) { |
| return this.$title; |
| } |
| return this.$body; |
| }.bind(this) |
| }; |
| |
| keyStrokes |
| .forEach(function(keyStroke) { |
| keyStroke.actionKeyStroke.renderingHints = $.extend({}, keyStroke.actionKeyStroke.renderingHints, groupBoxRenderingHints); |
| }, this); |
| |
| super._setKeyStrokes(keyStrokes); |
| } |
| |
| /** |
| * Returns a $container used as a bind target for the key-stroke context of the group-box. |
| * By default this function returns the container of the form, or when group-box is has no |
| * form as a parent the container of the group-box. |
| */ |
| _keyStrokeBindTarget() { |
| var form = this.getForm(); |
| if (form) { |
| // keystrokes on a group-box have form scope |
| return form.$container; |
| } |
| return this.$container; |
| } |
| |
| _render() { |
| this.addContainer(this.$parent, this.mainBox ? 'root-group-box' : 'group-box'); |
| |
| this.$title = this.$container.appendDiv('group-box-title'); |
| this.addLabel(); |
| this.addSubLabel(); |
| this.addStatus(); |
| this.$body = this.$container.appendDiv('group-box-body'); |
| this.htmlBody = HtmlComponent.install(this.$body, this.session); |
| this.htmlBody.setLayout(this._createBodyLayout()); |
| } |
| |
| _remove() { |
| this._removeSubLabel(); |
| super._remove(); |
| } |
| |
| _renderProperties() { |
| this._renderScrollable(); // Need to be before renderExpanded in order to have the scrollbars when the fields are rendered. The status tooltips require a scrollable parent to move when scrolling. |
| this._renderExpanded(); // Need to be before renderVisible is executed, otherwise controls might be rendered if group box is invisible which breaks some widgets (e.g. Tree and Table) |
| super._renderProperties(); |
| |
| this._renderBodyLayoutConfig(); |
| this._renderNotification(); |
| this._renderBorderVisible(); |
| this._renderExpandable(); |
| this._renderMenuBarPosition(); |
| this._renderMenuBarEllipsisPosition(); |
| this._renderMenuBarVisible(); |
| this._renderSubLabel(); |
| } |
| |
| _createLayout() { |
| return new GroupBoxLayout(this); |
| } |
| |
| _createBodyLayout() { |
| return new LogicalGridLayout(this, this.bodyLayoutConfig); |
| } |
| |
| setBodyLayoutConfig(bodyLayoutConfig) { |
| this.setProperty('bodyLayoutConfig', bodyLayoutConfig); |
| } |
| |
| _setBodyLayoutConfig(bodyLayoutConfig) { |
| if (!bodyLayoutConfig) { |
| bodyLayoutConfig = new LogicalGridLayoutConfig(); |
| } |
| this._setProperty('bodyLayoutConfig', LogicalGridLayoutConfig.ensure(bodyLayoutConfig)); |
| } |
| |
| _renderBodyLayoutConfig() { |
| var oldMinWidth = this.htmlBody.layout.minWidth; |
| this.bodyLayoutConfig.applyToLayout(this.htmlBody.layout); |
| if (oldMinWidth !== this.bodyLayoutConfig.minWidth) { |
| this._renderScrollable(); |
| } |
| if (this.rendered) { |
| this.htmlBody.invalidateLayoutTree(); |
| } |
| } |
| |
| /** |
| * Redraws the group box body by removing and rerendering every control. |
| * This may be necessary if a field does not support a dynamic property change and therefore needs to be redrawn completely to reflect the change. |
| */ |
| rerenderControls() { |
| this._removeControls(); |
| this._renderControls(); |
| this.htmlBody.invalidateLayoutTree(); |
| } |
| |
| _removeControls() { |
| this.controls.forEach(function(control) { |
| control.remove(); |
| }, this); |
| } |
| |
| _renderControls() { |
| this.controls.forEach(function(control) { |
| if (!control.rendered) { |
| control.render(this.$body); |
| // set each children layout data to logical grid data |
| control.setLayoutData(new LogicalGridData(control)); |
| } |
| }, this); |
| } |
| |
| addSubLabel() { |
| if (this.$subLabel) { |
| return; |
| } |
| this.$subLabel = this.$title.appendDiv('sub-label'); |
| tooltips.installForEllipsis(this.$subLabel, { |
| parent: this |
| }); |
| } |
| |
| _removeSubLabel() { |
| if (!this.$subLabel) { |
| return; |
| } |
| tooltips.uninstall(this.$subLabel); |
| this.$subLabel.remove(); |
| this.$subLabel = null; |
| } |
| |
| setSubLabel(subLabel) { |
| this.setProperty('subLabel', subLabel); |
| } |
| |
| _renderSubLabel() { |
| this.$subLabel.setVisible(strings.hasText(this.subLabel)); |
| this.$subLabel.textOrNbsp(this.subLabel); |
| this.$container.toggleClass('has-sub-label', this.$subLabel.isVisible()); |
| this.invalidateLayoutTree(); |
| } |
| |
| setScrollable(scrollable) { |
| this.setProperty('scrollable', scrollable); |
| } |
| |
| _renderScrollable() { |
| this._uninstallScrollbars(); |
| |
| // horizontal (x-axis) scrollbar is only installed when minWidth is > 0 |
| if (this.scrollable) { |
| this._installScrollbars({ |
| axis: ((this.bodyLayoutConfig.minWidth > 0) ? 'both' : 'y') |
| }); |
| } else if (this.bodyLayoutConfig.minWidth > 0) { |
| this._installScrollbars({ |
| axis: 'x' |
| }); |
| } |
| } |
| |
| /** |
| * @override |
| */ |
| get$Scrollable() { |
| return this.$body; |
| } |
| |
| setMainBox(mainBox) { |
| this.setProperty('mainBox', mainBox); |
| } |
| |
| _setMainBox(mainBox) { |
| this._setProperty('mainBox', mainBox); |
| if (this.mainBox) { |
| this.menuBar.setCssClass('main-menubar'); |
| if (this.scrollable === null) { |
| this.setScrollable(true); |
| } |
| if (this.responsive === null) { |
| this.setResponsive(true); |
| } |
| } |
| } |
| |
| addLabel() { |
| if (this.$label) { |
| return; |
| } |
| this.$label = this.$title.appendDiv('label'); |
| tooltips.installForEllipsis(this.$label, { |
| parent: this |
| }); |
| } |
| |
| _renderLabel() { |
| this.$label.textOrNbsp(this.label); |
| if (this.rendered) { |
| this._renderLabelVisible(); |
| } |
| } |
| |
| addStatus() { |
| super.addStatus(); |
| this._updateStatusPosition(); |
| } |
| |
| _renderStatusPosition() { |
| this._updateStatusPosition(); |
| } |
| |
| _updateStatusPosition() { |
| if (!this.fieldStatus) { |
| return; |
| } |
| if (this.statusPosition === FormField.StatusPosition.TOP) { |
| // move into title |
| this.$status.appendTo(this.$title); |
| } else { |
| this.$status.appendTo(this.$container); |
| } |
| this.invalidateLayoutTree(); |
| } |
| |
| setNotification(notification) { |
| this.setProperty('notification', notification); |
| } |
| |
| _renderNotification() { |
| if (!this.notification) { |
| this.invalidateLayoutTree(); |
| return; |
| } |
| this.notification.render(); |
| this.notification.$container.insertBefore(this.$body); |
| this.invalidateLayoutTree(); |
| } |
| |
| _prepareFields() { |
| this.processButtons.forEach(this._unregisterButtonKeyStrokes.bind(this)); |
| |
| this.controls = []; |
| this.systemButtons = []; |
| this.customButtons = []; |
| this.processButtons = []; |
| this.processMenus = []; |
| |
| var i, field; |
| for (i = 0; i < this.fields.length; i++) { |
| field = this.fields[i]; |
| if (field instanceof Button) { |
| if (field.processButton) { |
| this.processButtons.push(field); |
| if (field.systemType !== Button.SystemType.NONE) { |
| this.systemButtons.push(field); |
| } else { |
| this.customButtons.push(field); |
| } |
| } else { |
| this.controls.push(field); |
| this._registerButtonKeyStrokes(field); |
| } |
| } else if (field instanceof TabBox) { |
| this.controls.push(field); |
| for (var k = 0; k < field.tabItems.length; k++) { |
| if (field.tabItems[k].selectionKeystroke) { |
| this.keyStrokeContext.registerKeyStroke(new TabItemKeyStroke(field.tabItems[k].selectionKeystroke, field.tabItems[k])); |
| } |
| } |
| } else { |
| this.controls.push(field); |
| } |
| } |
| |
| // Create menu for each process button |
| this.processMenus = this.processButtons.map(function(button) { |
| return scout.create('ButtonAdapterMenu', |
| ButtonAdapterMenu.adaptButtonProperties(button, { |
| parent: this, |
| menubar: this.menuBar, |
| button: button, |
| // initially defaultMenu should only be set if defaultButton is set to true, false should not be mapped as the default defaultMenu = null setting |
| // would be overridden if this default null setting is overridden MenuBar.prototype.updateDefaultMenu would not consider these entries anymore |
| defaultMenu: button.defaultButton ? true : null |
| })); |
| }, this); |
| this.registerKeyStrokes(this.processMenus); |
| } |
| |
| _unregisterButtonKeyStrokes(button) { |
| if (button.keyStrokes) { |
| button.keyStrokes.forEach(function(keyStroke) { |
| this.keyStrokeContext.unregisterKeyStroke(keyStroke); |
| }, this); |
| } |
| } |
| |
| _registerButtonKeyStrokes(button) { |
| if (button.keyStrokes) { |
| button.keyStrokes.forEach(function(keyStroke) { |
| this.keyStrokeContext.registerKeyStroke(keyStroke); |
| }, this); |
| } |
| } |
| |
| setBorderVisible(borderVisible) { |
| this.setProperty('borderVisible', borderVisible); |
| } |
| |
| _renderBorderVisible() { |
| var borderVisible = this.borderVisible; |
| if (this.borderDecoration === GroupBox.BorderDecoration.AUTO) { |
| borderVisible = this._computeBorderVisible(borderVisible); |
| } |
| |
| this.$body.toggleClass('y-padding-invisible', !borderVisible); |
| this.invalidateLayoutTree(); |
| } |
| |
| setBorderDecoration(borderDecoration) { |
| this.setProperty('borderDecoration', borderDecoration); |
| } |
| |
| // Don't include in renderProperties, it is not necessary to execute it initially because renderBorderVisible is executed already |
| _renderBorderDecoration() { |
| this._renderBorderVisible(); |
| } |
| |
| _getCurrentMenus() { |
| if (this.menuBarVisible) { |
| return []; |
| } |
| return super._getCurrentMenus(); |
| } |
| |
| setMenuBarVisible(visible) { |
| this.setProperty('menuBarVisible', visible); |
| } |
| |
| _setMenuBarVisible(visible) { |
| this._setProperty('menuBarVisible', visible); |
| this._updateMenuBar(); |
| } |
| |
| _renderMenuBarVisible() { |
| if (this.menuBarVisible) { |
| this._renderMenuBar(); |
| } else { |
| this.menuBar.remove(); |
| } |
| this._updateMenus(); |
| this.invalidateLayoutTree(); |
| } |
| |
| _renderMenuBar() { |
| this.menuBar.render(); |
| if (this.menuBarPosition === GroupBox.MenuBarPosition.TITLE) { |
| // move right of title |
| this.menuBar.$container.insertAfter(this.$subLabel); |
| } else if (this.menuBar.position === MenuBar.Position.TOP) { |
| // move below title |
| this.menuBar.$container.insertAfter(this.$title); |
| } |
| } |
| |
| setMenuBarPosition(menuBarPosition) { |
| this.setProperty('menuBarPosition', menuBarPosition); |
| } |
| |
| _renderMenuBarPosition() { |
| var position = this.menuBarPosition; |
| if (position === GroupBox.MenuBarPosition.AUTO) { |
| position = GroupBox.MenuBarPosition.TOP; |
| } |
| |
| var hasMenubar = position === GroupBox.MenuBarPosition.TITLE; |
| this.$title.toggleClass('has-menubar', hasMenubar); |
| |
| if (position === GroupBox.MenuBarPosition.BOTTOM) { |
| this.menuBar.setPosition(MenuBar.Position.BOTTOM); |
| } else { // top + title |
| this.menuBar.setPosition(MenuBar.Position.TOP); |
| } |
| |
| if (this.rendered) { |
| this.menuBar.remove(); |
| this._renderMenuBarVisible(); |
| } |
| } |
| |
| setMenuBarEllipsisPosition(menuBarEllipsisPosition) { |
| this.setProperty('menuBarEllipsisPosition', menuBarEllipsisPosition); |
| this.menuBar.setEllipsisPosition(menuBarEllipsisPosition); |
| } |
| |
| _renderMenuBarEllipsisPosition() { |
| this.menuBar.reorderMenus(); |
| if (this.rendered) { |
| this.menuBar.remove(); |
| this._renderMenuBarVisible(); |
| } |
| } |
| |
| /** |
| * |
| * @returns false if it is the mainbox. Or if the groupbox contains exactly one tablefield which has an invisible label |
| */ |
| _computeBorderVisible(borderVisible) { |
| if (this.mainBox) { |
| borderVisible = false; |
| } else if (this.parent instanceof GroupBox && |
| this.parent.parent instanceof Form && |
| this.parent.parent.parent instanceof WrappedFormField && |
| this.parent.parent.parent.parent instanceof SplitBox && |
| this.parent.getFields().length === 1) { |
| // Special case for wizard: wrapped form in split box with a single group box |
| borderVisible = false; |
| } |
| return borderVisible; |
| } |
| |
| setExpandable(expandable) { |
| this.setProperty('expandable', expandable); |
| } |
| |
| _renderExpandable() { |
| var expandable = this.expandable; |
| var $control = this.$title.children('.group-box-control'); |
| |
| if (expandable) { |
| if ($control.length === 0) { |
| // Create control if necessary |
| this.$container.makeDiv('group-box-control') |
| .on('click', this._onControlClick.bind(this)) |
| .insertAfter(this.$label); |
| } |
| this.$title |
| .addClass('expandable') |
| .on('click.group-box-control', this._onControlClick.bind(this)); |
| } else { |
| $control.remove(); |
| this.$title |
| .removeClass('expandable') |
| .off('.group-box-control'); |
| } |
| } |
| |
| setExpanded(expanded) { |
| this.setProperty('expanded', expanded); |
| } |
| |
| _renderExpanded() { |
| this.$container.toggleClass('collapsed', !this.expanded); |
| |
| // Group boxes have set "useUiHeight=true" by default. When a group box is collapsed, it should not |
| // stretched vertically (no "weight Y"). However, because "weightY" is -1 by default, a calculated value |
| // is assigned (LogicalGridData._inheritWeightY()) that is based on the group boxes height. In collapsed |
| // state, this height would be wrong. Therefore, we manually assign "weightY=0" to collapsed group boxes |
| // to prevent them from beeing stretched. |
| if (this.expanded) { |
| // If group box was previously collapsed, restore original "weightY" griaData value |
| if (this._collapsedWeightY !== undefined) { |
| this.gridData.weightY = this._collapsedWeightY; |
| delete this._collapsedWeightY; |
| } |
| // Update inner layout (e.g. menubar) |
| this.invalidateLayout(); |
| this._renderControls(); |
| } else { |
| // If group box has a weight different than 0, we set it to zero and back up the old value |
| if (this.gridData.weightY !== 0) { |
| this._collapsedWeightY = this.gridData.weightY; |
| this.gridData.weightY = 0; |
| } |
| } |
| |
| this.invalidateLayoutTree(); |
| } |
| |
| setGridColumnCount(gridColumnCount) { |
| this.setProperty('gridColumnCount', gridColumnCount); |
| this.invalidateLogicalGrid(); |
| } |
| |
| /** |
| * @override |
| */ |
| invalidateLogicalGrid(invalidateLayout) { |
| super.invalidateLogicalGrid(false); |
| if (scout.nvl(invalidateLayout, true) && this.rendered) { |
| this.htmlBody.invalidateLayoutTree(); |
| } |
| } |
| |
| /** |
| * @override |
| */ |
| _setLogicalGrid(logicalGrid) { |
| super._setLogicalGrid(logicalGrid); |
| if (this.logicalGrid) { |
| this.logicalGrid.setGridConfig(new GroupBoxGridConfig()); |
| } |
| } |
| |
| /** |
| * @override FormField.js |
| */ |
| _renderLabelVisible(labelVisible) { |
| this.$title.setVisible(this._computeTitleVisible(labelVisible)); |
| this._updateFieldStatus(); |
| if (this.menuBarPosition === GroupBox.MenuBarPosition.TITLE) { |
| this.invalidateLayoutTree(); |
| } |
| } |
| |
| _computeTitleVisible(labelVisible) { |
| labelVisible = scout.nvl(labelVisible, this.labelVisible); |
| return !!(labelVisible && this.label && !this.mainBox); |
| } |
| |
| /** |
| * @override FormField.js |
| * |
| * Only show the group box status if title is visible. |
| */ |
| _computeStatusVisible() { |
| return super._computeStatusVisible() && this._computeTitleVisible(); |
| } |
| |
| _setMenus(menus) { |
| super._setMenus(menus); |
| |
| if (this.menuBar) { |
| // updateMenuBar is required because menuBar is not created yet when synMenus is called initially |
| this._updateMenuBar(); |
| } |
| } |
| |
| _updateMenuBar() { |
| if (!this.menuBarVisible) { |
| // Do not update menuBar while it is invisible, the menus may now be managed by another widget. |
| // -> this makes sure the parent is not accidentally set to the group box, the other widget should remain responsible |
| return; |
| } |
| var menus = this.staticMenus |
| .concat(this.processMenus) |
| .concat(this.menus); |
| |
| this.menuBar.setMenuItems(menus); |
| } |
| |
| _removeMenus() { |
| // menubar takes care about removal |
| } |
| |
| setStaticMenus(staticMenus) { |
| this.setProperty('staticMenus', staticMenus); |
| this._updateMenuBar(); |
| } |
| |
| _onControlClick(event) { |
| if (this.expandable) { |
| this.setExpanded(!this.expanded); |
| // Prevent flickering when expanding the group box |
| this.validateLayoutTree(); |
| } |
| $.suppressEvent(event); // otherwise, the event would be triggered twice sometimes (by group-box-control and group-box-title) |
| } |
| |
| setResponsive(responsive) { |
| this.setProperty('responsive', responsive); |
| } |
| |
| _setResponsive(responsive) { |
| this._setProperty('responsive', responsive); |
| |
| if (!this.initialized) { |
| return; |
| } |
| if (this.responsive) { |
| ResponsiveManager.get().reset(this, true); |
| } else { |
| ResponsiveManager.get().reset(this, true); |
| if (this.responsive === null) { |
| var parent = this.findParent(function(parent) { |
| return parent instanceof GroupBox && parent.responsive; |
| }); |
| ResponsiveManager.get().reset(parent, true); |
| } |
| } |
| this.invalidateLayoutTree(); |
| } |
| |
| clone(model, options) { |
| var clone = super.clone(model); |
| this._deepCloneProperties(clone, ['fields'], options); |
| clone._prepareFields(); |
| return clone; |
| } |
| } |