com.smartclient.debug.public.sc.client.widgets.Portal.js Maven / Gradle / Ivy
Show all versions of smartgwt Show documentation
/*
* Isomorphic SmartClient
* Version SC_SNAPSHOT-2011-08-08 (2011-08-08)
* Copyright(c) 1998 and beyond Isomorphic Software, Inc. All rights reserved.
* "SmartClient" is a trademark of Isomorphic Software, Inc.
*
* [email protected]
*
* http://smartclient.com/license
*/
// Portlet
// ---------------------------------------------------------------------------------------
//> @class Portlet
// Custom subclass of Window configured to be embedded within a PortalLayout.
// @visibility external
// @treeLocation Client Reference/Layout/PortalLayout
//<
isc.defineClass("Portlet", "Window").addProperties({
showShadow:false,
// enable predefined component animation
animateMinimize:true,
// Window is draggable with "outline" appearance by default.
// "target" is the solid appearance.
dragAppearance:"outline",
//>@attr portlet.canDrop (boolean : true : IRW)
// Portlets have canDrop set to true to enable drag/drop reposition within the portalLayout
// @visibility external
//<
canDrop:true,
//>@attr portlet.minHeight (Number : 60 : IRW)
// Specifies a minimum height for the Portlet. The height of rows within a +link{PortaLayout}
// will be adjusted to take into account the minHeight of all the Portlets in that row.
// @see Canvas.minHeight
// @visibility external
//<
minHeight: 60,
setMinHeight : function (height) {
this.minHeight = height;
if (this.portalRow) this.portalRow._applyMinHeight();
},
//>@attr portlet.rowHeight (Number or String : null : IRW)
// If you set the rowHeight of a Portlet before adding it to a +link{PortalLayout}, then
// the height will be used when creating the new row. If adding the Portlet
// to an existing row (or dragging it there), the Portlet's rowHeight will be used if
// the row's height has not already been specified. However, if you
// set the rowHeight of a Portlet after adding it to the PortalLayout, then the height
// of the entire row will always be adjusted to match.
//
// You can also just specify +link{Canvas.height,height} when initializing a Portlet, and it
// will be applied to the rowHeight when added to a PortalLayout. However, changing the Portlet's
// height after initialization will not affect the row.
//
// Note that getting the rowHeight will indicate the rowHeight specified for this Portlet,
// not the actual height of the row it is in.
// @visibility external
//<
// Also see the code in portalRow.addPortlets to see how the rowHeight and _userHeight are applied there.
// Note that we do not keep track of changes to the real row's height -- we are only responding
// to explicitly setting rowHeight on the portlet. Tracking the real row's height may sometimes make some
// sense, but the user's intentions aren't necessarily easy to model -- for instance, the user may have
// shrunk a row but want the portlet to automatically expand if dragged to an empty column.
setRowHeight : function (height) {
this.rowHeight = height;
if (this.portalRow) this.portalRow.setHeight(height);
},
// resize from any edge
resizeFrom: null,
// customize the appearance and order of the controls in the window header
// (could do this in load_skin.js instead)
showMaximizeButton: true,
headerControls:["headerLabel", "minimizeButton", "maximizeButton", "closeButton"],
// show either a shadow, or translucency, when dragging a portlet
// (could do both at the same time, but these are not visually compatible effects)
//showDragShadow:true,
dragOpacity:30,
//>@attr portlet.showCloseConfirmationMessage (boolean : true : IRW)
// If true, +link{closeConfirmationMessage} will be displayed before portlets are closed
// @visibility external
//<
showCloseConfirmationMessage:true,
//>@attr portlet.closeConfirmationMessage (string : "Close portlet?" : IRW)
// Confirmation message to show the user when closing portlets if
// +link{showCloseConfirmationMessage} is true.
// @visibility external
// @group i18nMessages
//<
closeConfirmationMessage:"Close portlet?",
//>@attr portlet.destroyOnClose (boolean : false : IRW)
// Whether to call +link{Canvas.destroy,destroy()} when closing the Portlet.
// @visibility external
//<
//>@method portlet.closeClick()
// closeClick overridden to show +link{portlet.closeConfirmationMessage} to the user before
// removing the portlet from the PortalLayout via +link{portalLayout.removePortlet()}
// @visibility external
//<
closeClick : function () {
if (this.showCloseConfirmationMessage) {
isc.confirm(this.closeConfirmationMessage,
{target:this, methodName:"confirmedClosePortlet"});
} else {
this.confirmedClosePortlet(true);
}
},
confirmedClosePortlet : function (value) {
if (!value) return;
// If we have an editContext, we'll do the removal from there whether or not
// we are in editMode
if (this.editContext && this.editNode) {
this.editContext.removeNode(this.editNode);
} else {
if (this.portalRow) {
this.portalRow.removePortlets(this);
} else {
this.clear();
}
}
if (this.destroyOnClose) this.markForDestroy();
},
maximize : function () {
var width = this.getVisibleWidth(),
height = this.getVisibleHeight(),
pageLeft = this.getPageLeft(),
pageTop = this.getPageTop()
;
this._portletPlaceholder = isc.Canvas.create({
width: this.getVisibleWidth(),
height: this.getVisibleHeight(),
minHeight: this.getMinHeight(),
_portlet: this
});
this.masterLayout = this.parentElement;
this.masterLayout.portletMaximizing = true;
this.masterLayout.replaceMember(this, this._portletPlaceholder, false);
this.masterLayout.portletMaximizing = false;
// maximize to the dashboard container, not whole window
this.setWidth(width);
this.setHeight(height);
this.moveTo(pageLeft, pageTop);
this.bringToFront();
this.draw();
this.delayCall("doMaximize");
},
completeRestore : function () {
this.Super("completeRestore", arguments);
this.masterLayout.portletMaximizing = true;
this.masterLayout.replaceMember(this._portletPlaceholder, this);
this.masterLayout.portletMaximizing = false;
this._portletPlaceholder._portlet = null;
this._portletPlaceholder.destroy();
delete this._portletPlaceholder;
delete this.masterLayout;
},
doMaximize : function () {
this.Super("maximize", arguments);
}
});
// provides a menu for adding a remove columns
isc.defineClass("PortalColumnHeader", "HLayout").addProperties({
height: 20,
noResizer: true,
border:"1px solid #CCCCCC",
// allow dragging by the header
canDragReposition: true,
initWidget : function () {
this.Super("initWidget", arguments);
// header drags the portalColumn
this.dragTarget = this.creator;
this.addMember(isc.LayoutSpacer.create());
this.menu = this.getMenuConstructor().create({
width: 150,
portalColumn: this.creator,
data: [{
title: "Remove Column",
click: "menu.portalColumn.removeSelf()",
// Don't offer to remove the last column.
enableIf: function (target, menu, item) {
return menu.portalColumn.portalLayout.getMembers().length > 1;
}
},{
title: "Add Column",
click: "menu.portalColumn.addNewColumn()"
}]
});
this.addMember(isc.MenuButton.create({
title: "Column Properties",
width: 150,
menu: this.menu
}));
this.addMember(isc.LayoutSpacer.create());
}
});
// Manages horizontal vs vertical drag and drop such that a drop to the sides is a drop within
// this PortalRow and a drop above or below is a drop within the parent, before or after this
// PortalRow.
// Created whenever a drop occurs in a PortalColumn (even if it's the first drop).
// Note that you can drop just about anything on a PortalRow -- it will be wrapped in a Portlet
// if necessary.
isc.defineClass("PortalRow", "Layout").addProperties({
defaultResizeBars : "marked",
vertical : false,
overflow: "hidden",
// leave some space between portlets
layoutMargin: 3,
// enable drop handling
canAcceptDrop:true,
// change appearance of drag placeholder and drop indicator
dropLineThickness:2,
dropLineProperties:{backgroundColor:"blue"},
// Will accept a portlets attribute and add it (or them, if an array) to the portalRow
initWidget : function () {
this.Super("initWidget", arguments);
if (this.portlets) this.addPortlets(this.portlets);
this.portlets = null;
},
// number of pixels you have to be within the left or right border of a portlet for us to
// show a drop to the left or right of this portlet. If not within this margin, drop is
// indicated above or below instead.
hDropOffset: 15,
isHDrop : function () {
var dropPosition = this.getDropPosition();
var dropOverTarget = this.getMember(dropPosition == 0 ? 0 : dropPosition - 1);
if (!dropOverTarget.containsEvent() && dropPosition < this.members.length) {
dropOverTarget = this.getMember(dropPosition);
}
var targetOffsetX = dropOverTarget.getOffsetX();
if (targetOffsetX < this.hDropOffset || targetOffsetX > dropOverTarget.getVisibleWidth() - this.hDropOffset) {
return true;
} else {
return false;
}
},
// We pass through the drop if it is a PortalColumn, since it doesn't make sense to drop
// a PortalColumn on a PortalRow -- the PortalLayout will handle it.
isPortalColumnDrop : function () {
var dragTarget = this.ns.EH.dragTarget;
var type = dragTarget.getDragType();
if (type == "PortalColumn") return true;
//>EditMode
if (dragTarget.isA("Palette")) {
var data = dragTarget.getDragData(),
component = (isc.isAn.Array(data) ? data[0] : data);
if (component.className == "PortalColumn" || component.type == "PortalColumn") return true;
}
// EditMode
if (this.handleDroppedEditNode) dropComponent = this.handleDroppedEditNode(dropComponent, dropPosition);
// EditMode
if (this.editContext && this.editNode) this.editContext.removeNode(this.editNode);
// EditMode
// If we have an editContext and we aren't coming from addPortlets, then we check
// whether the portlets have an editNode ... if so, we should add it
if (this.editContext && !this._addingPortlets && !this.portletMaximizing) {
for (var i = 0; i < portlets.length; i++) {
var portlet = portlets[i];
if (portlet.editNode) {
this.editContext.addNode(portlet.editNode, this.editNode, index + i, null, true);
}
}
}
// EditMode
// We keep track of whether we're calling addMembers from here in order to know whether
// to check for editNodes. The assumption is that if we're calling from here, we have
// already dealt with editNodes appropriately.
// EditMode
// If we have an editContext, then we check whether we got here via
// removePortlets. If we did, then we assume that the code has done
// the right thing re: editContext. If not, then we're probably doing
// a drag & drop from Layout.js, so we should remove the component
if (self.editContext && portlet.editNode && !self._removingPortlets) {
// Note that we skip live removal, since we'll have just done that
self.editContext.removeNode(portlet.editNode, true);
}
// EditMode
// We keep track of whether we are calling removeMembers from here in order
// to know whether to check for editNodes. The assumption is that if we are
// calling from here, we have already done the appropriate thing with any
// editNodes.
// EditMode
if (dragTarget.isA("Palette")) {
var data = dragTarget.getDragData(),
component = (isc.isAn.Array(data) ? data[0] : data);
if (component.className == "PortalColumn" || component.type == "PortalColumn") return true;
}
// EditMode
if (this.handleDroppedEditNode) dropComponent = this.handleDroppedEditNode(dropComponent, dropPosition);
// 0) {
var allNumeric = sizes.map(function (size) {
return isc.isA.Number(size);
}).and();
if (allNumeric) {
var totalNumericSizes = sizes.sum();
if (totalNumericSizes < totalSize) {
sizes[sizes.length - 1] = "*";
}
}
}
return this.Super("applyStretchResizePolicy", arguments);
}
});
// Vertical layout based container rendered within a PortalLayout.
// PortalColumns are automatically constructed by the PortalLayout class and will not typically
// be directly instantiated.
//
// The only reason to expose this would be to allow customization of appearance - and it makes
// more sense to do that via attributes on the PortalLayout itself.
// ---------------------------------------------------------------------------------------
// Offers Drag and drop creation of Portlets, where a new PortalRow is created to manage the
// portlet.
isc.defineClass("PortalColumn", "Layout").addProperties({
vertical:true,
layoutMargin: 3,
// Can drag the PortalColumn, but it does not handle drops ... the PortalColumnBody
// manages that.
dragAppearance: "outline",
canAcceptDrop: false,
canDrop: true,
dragType: "PortalColumn",
// This one should not change heights ...
overflow: "hidden",
// The columnHeader is handled as an AutoChild
showColumnHeader: true,
columnHeaderConstructor: "PortalColumnHeader",
columnHeaderDefaults: {
title: "Column"
},
// Make showColumnHeader updatable
setShowColumnHeader : function (show) {
if (show) {
if (this.showColumnHeader) return;
this.showColumnHeader = show;
this.addAutoChild("columnHeader", {autoParent: "none"});
this.addMember(this.columnHeader, 0);
} else {
if (!this.showColumnHeader) return;
this.showColumnHeader = show;
this.removeMember(this.columnHeader);
}
},
// The scrollContainer helps to manage scrolling the rowLayoutContainer. Without it,
// the problem is that overflow: auto on the PortalColumnBody triggers a re-layout
// of the parent, which we don't want ... this cuts it out.
scrollContainerConstructor: "Canvas",
scrollContainerDefaults: {
overflow: "auto"
},
// The rowLayout is where the actual rows go ... this avoids having to
// continually adjust the code for whether the columnHeader is
// showing or not.
//
// These Autochild settings are referenced in PortalLayout as well, since that is where they
// are exposed as an API. Changing rowLayoutDefaults there will also change it
// here, but that is fine, since people should be using changeDefaults() anyway. The
// settings are then copied dynamically from PortalLayout when it creates columns.
rowLayoutConstructor: "PortalColumnBody",
rowLayoutDefaults: {
autoParent: "scrollContainer"
},
// Will accept a portalRows attribute, containing portalRows to insert,
// or properties to be used to construct them.
initWidget : function () {
this.Super("initWidget", arguments);
this.addAutoChild("columnHeader");
this.addAutoChild("scrollContainer");
this.addAutoChild("rowLayout");
this.setCanResizeRows(this.canResizeRows);
if (this.portalRows) this.addPortalRows(this.portalRows);
this.portalRows = null;
},
addNewColumn : function () {
this.portalLayout.addColumnAfter(this);
},
removeSelf : function () {
this.portalLayout.removeColumn(this.portalLayout.getMemberNumber(this));
},
// See comment on rowLayoutConstructor re: the reference in PortalLayout
rowConstructor: "PortalRow",
// Creates rows via AutoChild logic, or modifies existing rows
// to prepare them to be added to the column. Note that rowConstructor,
// rowDefaults and rowProperties will have been copied from the portalLayout.
makePortalRow : function (props) {
if (props == null) props = {};
var dynamicProperties = {
portalLayout: this.portalLayout,
portalColumn: this
};
var portalRow;
if (isc.isA.PortalRow(props)) {
// If we're given an already created PortalRow, then use setProperties
// to add the dynamicProperties.
props.setProperties(dynamicProperties);
portalRow = props;
} else {
// Otherwise, construct it as an autoChild
isc.addProperties(props, dynamicProperties);
portalRow = this.createAutoChild("row", props);
}
return portalRow;
},
setCanResizeRows : function (canResize) {
this.canResizeRows = canResize;
// Using "all" instead of "middle" so that we can adjust the overall size ...
// see comment on PortalColumnBody.applyStretchResizePolicy.
this.rowLayout.setDefaultResizeBars(canResize ? "all" : "none");
},
addPortalRows : function (rows, position) {
if (!isc.isAn.Array(rows)) rows = [rows];
var self = this;
rows = rows.map(function (row) {
return self.makePortalRow(row);
});
this.rowLayout.addMembers(rows, position);
},
addPortalRow : function (row, position) {
this.addPortalRows(row, position);
},
removePortalRows : function (rows) {
this.rowLayout.removeMembers(rows);
},
removePortalRow : function (row) {
this.removePortalRows(row);
},
getPortalRows : function () {
return this.rowLayout.getMembers();
},
getPortalRowNumber : function (id) {
return this.rowLayout.getMemberNumber(id);
},
getPortalRow : function (rowID) {
return this.rowLayout.getMember(rowID);
},
// Returns flat list of portlets
getPortlets : function () {
var portlets = [];
this.getPortalRows().map(function (row) {
portlets.addList(row.getPortlets());
});
return portlets;
},
// Returns portlets in array of arrays, corresponding to rows
getPortletArray : function () {
return this.getPortalRows().map(function (row) {
return row.getPortlets();
});
},
getPortlet : function (id) {
var rows = this.getPortalRows();
for (var x = 0; x < rows.length; x++) {
var portlet = rows[x].getPortlet(id);
if (portlet) return portlet;
}
return null;
},
// Adds portlets, auto-wrapping them in rows
addPortlets : function (portlets, position) {
if (!isc.isAn.Array(portlets)) portlets = [portlets];
var self = this;
var rows = portlets.map(function(portlet) {
return self.makePortalRow({
portlets: portlet
});
});
this.addPortalRows(rows, position);
},
addPortlet : function (portlet, position) {
this.addPortlets(portlet, position);
},
addPortletToExistingRow : function (portlet, rowNum, rowOffset) {
var rows = this.rowLayout.getMembers();
if (rows == null || rows.length <= rowNum) {
if (this.editContext && this.editNode && portlet.editNode) {
this.addNode(portlet.editNode, this.editNode, rows.length);
} else {
this.addPortlet(portlet, rows.length);
}
} else {
var portalRow = this.rowLayout.getMember(rowNum);
if (portalRow.editContext && portalRow.editNode && portlet.editNode) {
portalRow.editContext.addNode(portlet.editNode, portalRow.editNode, rowOffset);
} else {
portalRow.addPortlets(portlet, rowOffset);
}
}
}
});
//> @class PortalLayout
// A PortalLayout is a special subclass of Layout designed to display +link{Portlet} windows.
// A PortalLayout displays Portlets in columns and supports drag-drop interaction for moving
// Portlets around within the PortalLayout. Portlets may be drag-reordered within columns, dragged
// into other columns, or dragged next to other Portlets to sit next to them horizontally
// within a column.
//
// @visibility external
// @treeLocation Client Reference/Layout
//<
isc.defineClass("PortalLayout", "Layout").addProperties({
vertical:false,
// Allow the column widths to scroll if necessary
overflow: "auto",
//> @attr portalLayout.numColumns (integer : 2 : IR)
// Initial number of columns to show in this PortalLayout. Note that after initialization
// columns should be added / removed via +link{addColumn()} and +link{removeColumn}.
// @visibility external
//<
// Note that numColumns is ignored on initialization if the portalColumns attribute is supplied ...
// in that case, the columns are initialized from that attribute instead. Also, see
// addedToEditContext for manipulation of the numColumns in defaults/initData -- we set it to
// zero there because the columns will get their own editNodes (since they need to contain things)
numColumns:2,
//> @method portalLayout.getNumColumns()
// Returns the current number of columns displayed in this PortalLayout.
// @return numColumns (Integer)
// @visibility external
//<
// Overridden to return this.getMembers.length. Will have been set up at initialization time.
getNumColumns : function () {
return this.getMembers().length;
},
//> @attr portalLayout.showColumnMenus (boolean : true : IRW)
// Should a menu be shown within each column with options to add / remove columns?
// @visibility external
//<
showColumnMenus:true,
//> @method portalLayout.setShowColumnMenus()
// Sets +link{showColumnMenus} and updates existing columns to reflect the new setting.
// @param showMenus (boolean) Whether to show column menus
// @visibility external
//<
setShowColumnMenus : function (show) {
if (this.showColumnMenus == show) return;
this.showColumnMenus = show;
this.getPortalColumns().map(function (column) {
column.setShowColumnHeader(show);
});
},
//> @attr portalLayout.columnBorder (string : "1px solid gray" : IRW)
// Border to show around columns in this PortalLayout
// @visibility external
//<
columnBorder:"1px solid gray",
//> @method portalLayout.setColumnBorder()
// Sets the columnBorder for to the specified value and updates any drawn columns to reflect
// this.
// @param columnBorder (string) New border to show around columns
// @visibility external
//<
setColumnBorder : function (columnBorder) {
this.columnBorder = columnBorder;
var members = this.members || [];
for (var i = 0; i < members.length; i++) {
members[i].setBorder(columnBorder);
}
},
//> @attr portalLayout.canResizeColumns (boolean : false : IRW)
// Are columns in this portalLayout drag-resizeable?
// @visibility external
//<
canResizeColumns:false,
//> @method portalLayout.setCanResizeColumns()
// Set whether columns in this portalLayout are drag-resizable, and update any
// drawn columns to reflect this.
// @param canResize (Boolean) Whether columns are drag-resizable
// @visibility external
//<
setCanResizeColumns : function (resizeColumns) {
this.canResizeColumns = resizeColumns;
// Using "all" instead of "middle" so that we can change the overal size ...
// see comment on PortalLayout.applyStretchResizePolicy
this.setDefaultResizeBars(resizeColumns ? "all" : "none");
},
//> @attr portalLayout.canResizeRows (boolean : false : IRW)
// Should vertical drag-resize of portlets within columns be allowed?
// @visibility external
//<
// Can resize rows since we want to resize the entire row of portlets probably
canResizeRows:false,
//> @method portalLayout.setCanResizeRows()
// Set whether vertical drag-resize of portlets within columns is allowed, and
// update any drawn columns to reflect this.
// @param canResize (Boolean) Whether drag-resize of portlets within columns is allowed
// @visibility external
//<
setCanResizeRows : function (resizeRows) {
this.canResizeRows = resizeRows;
this.getPortalColumns().map(function (column) {
column.setCanResizeRows(resizeRows);
});
},
// This allows drag/drop reordering within the portal layout
canAcceptDrop: true,
dropTypes: ["PortalColumn"],
// change appearance of drag placeholder and drop indicator
dropLineThickness:2,
dropLineProperties:{backgroundColor:"blue"},
initWidget : function () {
this.Super("initWidget", arguments);
this.setCanResizeColumns(this.canResizeColumns);
if (this.portalColumns) {
this.addPortalColumns(this.portalColumns);
this.portalColumns = null;
} else {
if (this.numColumns) {
for (var x = 0; x < this.numColumns; x++) {
this.addColumn();
}
}
}
},
//> @method portalLayout.getDropPortlet()
// This method is called when the user drops components into the rows or columns of this
// PortalLayout.
//
// Overriding this method allows you to modify drop behaviour when creating or reordering
// portlets via drag & drop. You can return the dragTarget for the standard behavior,
// or null to cancel the drop.
//
// Otherwise, return the component you want to be dropped (as for +link{layout.getDropComponent}).
// You will generally want to return a +link{Portlet} or subclass. However, you can return
// any +link{Canvas}, and it will automatically be wrapped in a Portlet if necessary.
// @param dragTarget (Canvas) drag target
// @param colNum (int) indicates which column the portlet is being dropped on.
// @param rowNum (int) indicates the row number being dropped on.
// @param [dropPosition] (int) Drop position within an existing row. If the dropPosition
// is null, then that means that a new row will be created.
// @return (Canvas) drop-component or custom Portlet to embed in the portalLayout. Returning
// null will cancel the drop.
// @visibility external
//<
// This is called from portalColumnBody.getDropComponent and portalRow.getDropComponent.
// Note that return type is documented as Canvas but overrides would probably
// always return a Portlet.
getDropPortlet : function (dragTarget, colNum, rowNum, dropPosition) {
return dragTarget;
},
//> @attr portalLayout.row (AutoChild : null : A)
// Automatically generated +link{HLayout} used to create rows of +link{Portlet,Portlets} via
// +link{Class.createAutoChild,createAutoChild()}. Since this is an +link{AutoChild}, you can use
// rowDefaults and rowProperties to customize the rows.
//
// Rows are created inside +link{rowLayout,rowLayouts}, which in turn are inside +link{column,columns}.
// @see portalLayout.column
// @see portalLayout.rowLayout
// @visibility external
//<
// Note that the actual call to createAutoChild is in PortalColumn. We expose the property
// here instead because we're trying to avoid exposing PortalColumn and PortalRow.
rowConstructor: isc.PortalColumn.getInstanceProperty("rowConstructor"),
//> @attr portalLayout.rowLayout (AutoChild : null : A)
// Automatically generated +link{VLayout} used to create columns of +link{Portlet,Portlets} via
// +link{Class.createAutoChild,createAutoChild()}. Since this is an +link{AutoChild}, you can use
// rowLayoutDefaults and rowLayoutProperties to customize the layout used to contain the rows.
//
// The rowLayout is the actual container for +link{row,rows} of +link{Portlet,Portlets}. See +link{column,column} for
// the column as a whole, which may include a menu as well (depending on +link{showColumnMenus,showColumnMenus}).
// If you want to style the columns as a whole,
// use columnDefaults or columnProperties, but if you want to style the layout that actually contains the
// rows, use rowLayoutDefaults or rowLayoutProperties.
// @see portalLayout.rowLayout
// @see portalLayout.row
// @visibility external
//<
// Note that the actual call to addAutoChild is in PortalColumn. We expose teh property
// here instead because we're trying to avoid exposing PortalColumn and PortalRow.
rowLayoutConstructor: isc.PortalColumn.getInstanceProperty("rowLayoutConstructor"),
rowLayoutDefaults: isc.PortalColumn.getInstanceProperty("rowLayoutDefaults"),
//> @attr portalLayout.column (AutoChild : null : A)
// Automatically generated +link{VLayout} used to create columns of +link{Portlet,Portlets} via
// +link{Class.createAutoChild,createAutoChild()}. Since this is an +link{AutoChild}, you can use
// columnDefaults and columnProperties to customize the columns.
//
// The column includes a menu, if +link{showColumnMenus,showColumnMenus} is true, and a +link{rowLayout,rowLayout} which
// actually contains the +link{row,rows}. Therefore, if you want to style the columns as a whole,
// use columnDefaults or columnProperties, but if you want to style the layout that contains the
// rows, use rowLayoutDefaults or rowLayoutProperties.
// @see portalLayout.rowLayout
// @see portalLayout.row
// @visibility external
//<
columnConstructor: "PortalColumn",
// Make columns using autoChild logic, or apply this PortalLayout's
// settings to an existing PortalColumn. Note that we copy rowConstructor etc. in order
// to pass the AutoChild logic down for rows.
makePortalColumn : function (props) {
if (props == null) props = {};
var dynamicProperties = {
portalLayout: this,
showColumnHeader: this.showColumnMenus,
border: this.columnBorder,
canResizeRows: this.canResizeRows,
rowConstructor: this.rowConstructor,
rowDefaults: this.rowDefaults,
rowProperties: this.rowProperties,
rowLayoutConstructor: this.rowLayoutConstructor,
rowLayoutDefaults: this.rowLayoutDefaults,
rowLayoutProperties: this.rowLayoutProperties
}
var portalColumn;
if (isc.isA.PortalColumn(props)) {
// If we're given an already created PortalColumn, then use setProperties
// to make it conform to the PortalLayout settings here.
props.setProperties(dynamicProperties);
portalColumn = props;
} else {
// Otherwise, construct it as an autoChild
isc.addProperties(props, dynamicProperties);
portalColumn = this.createAutoChild("column", props);
}
return portalColumn;
},
// Apply portalColumn logic when adding PoralColumns as members. This allows smoother interaction
// with existing drag and drop code in Layout.js, since that code ultimately calls
// addMembers and removeMembers
addMembers : function (columns, index) {
if (!isc.isAn.Array(columns)) columns = [columns];
var self = this;
columns = columns.map(function (column) {
return self.makePortalColumn(column);
});
this.Super("addMembers", arguments);
//>EditMode
// If we have an editContext and we aren't coming from addPortalColumns, then we check
// whether the columns have an editNode ... if so, we should add it
if (this.editContext && !this._addingPortalColumns) {
for (var i = 0; i < columns.length; i++) {
var column = columns[i];
if (column.editNode) {
this.editContext.addNode(column.editNode, this.editNode, index + i, null, true);
}
}
}
// EditMode
// If we have an editContext, then we check whether we got here via
// removePortalColumns. If we did, then we assume that the code has done
// the right thing re: editContext. If not, then we're probably doing
// a drag & drop from Layout.js, so we should remove the component
if (this.editContext && !this._removingPortalColumns) {
if (!isc.isAn.Array(portalColumns)) portalColumns = [portalColumns];
var self = this;
portalColumns.map(function (column) {
if (column.editNode) {
// Note that we skip live removal, since we'll have just done that
self.editContext.removeNode(column.editNode, true);
}
});
}
// @method portalLayout.addColumn()
// Adds a new portal column to this layout at the specified position
// @param index (integer) target position for the new column
// @visibility external
//<
addColumn : function (index) {
//>EditMode
// PortalLayout is a little special with respect to EditMode, since the
// whole purpose of PortalLayout is to be editable ... thus, it makes
// more sense to integrate the editing code here, rather than relying
// on separate code in EditMode.js. For instance, it makes more sense
// to rely on the standard PortalLayout interface for adding columns,
// rather than forcing the user to drag a column from a palette.
//
// Thus, when adding a Column, we note whether there is an edit context
// around and, if so, ask it to do it. That will also eventually run
// through addPortalColumn, given the standard sequence of events.
// EditMode
// This is a bit hackish to generate nice ID's in cases where we
// will soon be put into an editContext
// @method portalLayout.removeColumn()
// Removes the specified column from this layout.
// All portlets displayed within this column will be destroyed when the column is removed.
// @param index (integer) column number to remove
// @visibility external
//<
removeColumn : function (index) {
var column = this.members[index];
if (column != null) {
if (this.editContext && column.editNode) {
this.editContext.removeNode(column.editNode);
} else {
column.destroy();
}
}
},
// addColumnAfter is used by the header menus shown within columns if appropriate
addColumnAfter : function (portalColumn) {
var targetIndex = this.getMemberNumber(portalColumn) + 1;
this.addColumn(targetIndex);
},
//>@method portalLayout.getPortlets()
// Returns a flat array of all the +link{Portlet,Portlets} in this PortalLayout.
// @return portlets (Array of Portlet)
// @visibility external
// @see getPortletArray()
//<
getPortlets : function () {
var portlets = [];
this.getPortalColumns().map(function (column) {
portlets.addList(column.getPortlets());
});
return portlets;
},
//>@method portalLayout.getPortletArray()
// Returns a multi-level array of the +link{Portlet,Portlets} in this PortalLayout,
// where the first level corresponds to columns, the second to rows, and the third
// to Portlets within rows.
// @return portlets (Array of Array of Array of Portlet)
// @visibility external
// @see getPortlets()
//<
getPortletArray : function () {
return this.getPortalColumns().map(function (column) {
return column.getPortletArray();
});
},
//>@method portalLayout.addPortlet()
// Adds a +link{Portlet} instance to this portalLayout in the specified position.
// @param portlet (Portlet) Portlet to add to this layout.
// @param [colNum] (integer) Column in which the Portlet should be added. If unspecified,
// defaults to zero.
// @param [rowNum] (integer) Row within the column for the Portlet.
// @param [rowOffset] (integer) Offset within the row. If you specify a
// rowOffset, then the Portlet will be added to the existing row. If not, then a new row
// will be created at rowNum.
// @visibility external
//<
//>EditMode in EditMode users can drag/drop from paletteNodes to add portlets to columns.
// This will never run through this method so this is not a valid override point to catch every
// newly added portlet // @method portalLayout.setColumnWidth()
// Sets the width of a column in the PortalLayout.
// @param colNumber (Integer) Which column's width to set.
// @param width (Number or String) How wide to make the column
// @see Canvas.setWidth()
// @visibility external
//<
setColumnWidth : function (columnNumber, width) {
var column = this.getPortalColumn(columnNumber);
if (!column) return;
// This automatically adjusts the editNode if present
if (column.editContext && column.editNode) {
column.editContext.setNodeProperties(column.editNode, {
width: width
});
} else {
column.setWidth(width);
}
},
//>@method portalLayout.getColumnWidth()
// Gets the width of a column in the PortalLayout.
// @param colNumber (Integer) Which column's width to get
// @return width (Number)
// @see Canvas.getWidth()
// @visibility external
//<
getColumnWidth : function (columnNumber) {
var column = this.getPortalColumn(columnNumber);
if (column) {
return column.getWidth();
} else {
return null;
}
},
getPortalColumns : function () {
return this.getMembers();
},
getPortalColumn : function (columnID) {
return this.getMember(columnID);
},
getPortalColumnNumber : function (columnID) {
return this.getMemberNumber(columnID);
},
getColumn : function (colNum) {
return this.getPortalColumn(colNum);
},
//>@method portalLayout.removePortlet()
// Removes a +link{Portlet} which is currently rendered in this PortalLayout.
// Portlet will not be destroyed by default - if this is desired the calling code should
// do this explicitly.
// @param portlet (Portlet) portlet to remove
// @visibility external
//<
//>EditMode We *DO* auto-destroy portlets on closeclick in editMode if they were dragged in
// from a paletteNode // 0) {
var allNumeric = sizes.map(function (size) {
return isc.isA.Number(size);
}).and();
if (allNumeric) {
var totalNumericSizes = sizes.sum();
if (totalNumericSizes < totalSize) {
sizes[sizes.length - 1] = "*";
}
}
}
return this.Super("applyStretchResizePolicy", arguments);
}
});