All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.smartclient.debug.public.sc.client.widgets.SectionStack.js Maven / Gradle / Ivy

The newest version!
/*
 * 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
 */

 


//> @class SectionStack
// A container that manages a list of sections of widgets, each with a header.  Sometimes called
// an "Accordion".
// 

// SectionStack can be configured so that only one section is visible at a time (similar to MS Outlook's // left-hand Nav), or to allow multiple sections to be visible and share the available space. // // @treeLocation Client Reference/Layout // @visibility external // @example sectionsExpandCollapse //< isc.defineClass("SectionStack", "VLayout"); //>!BackCompat 2005.6.15 old name: "ListBar" isc.addGlobal("ListBar", isc.SectionStack); // @attr sectionStack.overflow (Overflow : "hidden" : IR) // Normal +link{type:Overflow} settings can be used on layouts, for example, an // overflow:auto Layout will scroll if sections are resized to exceed the specified size, // whereas an overflow:visible Layout will grow to accommodate the resized sections. // @visibility external //< overflow:"hidden", //> @attr sectionStack.styleName (CSSStyleName : "sectionStack" : IR) // Default CSS style for the SectionStack as a whole. // @visibility external //< styleName:"sectionStack", // Section Header Creation // --------------------------------------------------------------------------------------- //> @attr sectionStack.sectionHeaderClass (Classname : "SectionHeader" : IRA) // Name of the Canvas subclass to use as a header that labels the section and allows // showing and hiding. The default class can be skinned, or trivial subclasses created to // allow different appearances for headers in different SectionStacks. //

// Very advanced developers can use the following information to create custom header // classes. //

// The SectionStack will instantiate this class, giving the following properties on init: //

    //
  • layout: the SectionStack //
  • expanded: true or false //
  • hidden: true or false //
  • title: section title //
// From then on, when the sectionHeader is clicked on, it should call // +link{method:SectionStack.sectionHeaderClick()}. //

// Whenever the section is hidden or shown, sectionHeader.setExpanded(true|false) will be // called if implemented. // // @visibility external //< sectionHeaderClass:"SectionHeader", //> @attr sectionStack.headerHeight (Number : 20 : IR) // Height of headers for sections. // @visibility external //< headerHeight:20, // sectionStack header styles for printing printHeaderStyleName:"printHeader", // All Sections // --------------------------------------------------------------------------------------- //> @attr SectionStack.sections (Array of SectionStackSection Properties : null : [IR]) // List of sections of components managed by this SectionStack. // // @see sectionStack.getSections() // @visibility external // @example sectionsExpandCollapse //< //> @attr SectionStack.canResizeSections (boolean : true : [IRA]) // Whether sections can be drag resized by the user dragging the section header. //

// Note that, with canResizeSections:true, not all sections can be resized: // sections that contain only +link{Button.autoFit,autofitting} components or that are // marked with +link{SectionStackSection.resizeable,section.resizeable:false} will not be // resizeable. // // @visibility external //< canResizeSections:true, // whether to allow the user to change the overall size of the SectionStack by resizing // sections canResizeStack:true, //> @attr SectionStack.canReorderSections (boolean : false : [IRA]) // Whether sections can be drag reordered by the user dragging the section header. //

// Note that, with canReorderSections:true, sections with // +link{SectionStackSection.canReorder,section.canReorder:false} will not be // able to be drag-reordered (though their index may still be changed by dropping other // sections above or below them in the section stack). // // @visibility external //< canReorderSections:false, //> @attr SectionStack.canTabToHeaders (boolean : null : [IRA]) // If true, the headers for the sections (if shown) will be included in the page's tab // order for accessibility. // May be overridden at the Section level via +link{Section.canTabToHeader} //

// If unset, section headers will be focusable if +link{isc.setScreenReaderMode} has been called. // See +link{group:accessibility}. // @visibility external //< //> @attr SectionStack.scrollSectionIntoView (boolean : true : [IR]) // If an expanded or shown section expands past the current viewport and this property is // true, then the viewport will auto-scroll to fit as much of the section content into the // viewport without scrolling the top of the section out of the viewport. // // @visibility external //< scrollSectionIntoView: true, // NOTE: vertical:false (horizontal stacks) does work, however the default SectionHeader // class has a height of 20 which needs to be wiped to allow vertical stretching. And, you // have to have a strategy for vertical text labels. // isc.defineClass("MyHeader", "SectionHeader").addProperties({height:null}); // isc.SectionStack.create({ // vertical:false, // sectionHeaderClass:"MyHeader", //vertical:true, // Section Header Properties // --------------------------------------------------------------------------------------- //> @object SectionStackSection // Section descriptor used by a SectionStack to describe a section of items which are shown // or hidden together, and their associated header. // // @treeLocation Client Reference/Layout/SectionStack // @visibility external //< //> @attr SectionStackSection.name (String : null : [IR]) // Identifier for the section. This can be used later in calls to +link{SectionStack} APIs such as // +link{sectionStack.expandSection} and +link{sectionStack.collapseSection}. Note that if no name // is specified for the section, one will be auto-generated when the section is created. // This property should be a string which may be used as a valid JavaScript identifier // (should start with a letter and not contain space or special characters such as "*"). // @visibility external //< //> @attr SectionStackSection.ID (String : null : [IR]) // Optional ID for the section. If +link{useGlobalSectionIDs} is true, this property will // be applied to the generated SectionStackHeader widget as a standard widget ID, meaning // it should be unique within a page. //

// Backcompat Note: Section stack sections may be uniquely identified within a stack // via the +link{SectionStackSection.name} attribute (introduced in Jan 2010). Prior to this, // the section ID attribute was used in this way (and would not be applied to the section header // as a widget ID). For backwards compatibility this is still supported: If // section.name is unspecified for a section but section.ID is set, // the ID will be used as a default name attribute for the section. For backwards compatibility // we also disable the standard behavior of having the section.ID being applied to the generated // section header (thereby avoiding the page-level uniqueness requirement) by defaulting // +link{SectionStackSection.useGlobalSectionIDs} to false. // // @visibility external //< //> @attr SectionStack.useGlobalSectionIDs (boolean : false : [IR]) // Should any specified +link{SectionStackSection.ID} be applied to the generated SectionHeader widget // for the section as a widget ID? If set to false, SectionStackSection.ID will behave as a synonym for // SectionStackSection.name. // // @visibility external //< // Default to false for back-compat useGlobalSectionIDs:false, //> @attr SectionStackSection.title (String : null : [IR]) // Title to show for the section // @visibility external //< //> @attr SectionStackSection.items (Array of Canvas : null : [I]) // List of Canvases that constitute the section. These Canvases will be shown and hidden // together. // @visibility external //< //> @attr SectionStackSection.showHeader (boolean : true : [I]) // If true, a header will be shown for this section. If false, no header will be shown. // @visibility external //< //> @attr sectionStackSection.canTabToHeader (boolean : null : IR) // If true, the header for this Section will be included in the page's tab // order for accessibility. May also be set at the +link{SectionStack} level via // +link{SectionStack.canTabToHeaders}. //

// See +link{group:accessibility}. // // @visibility external //< //> @attr SectionStackSection.resizeable (boolean : null : [I]) // If set to false, then the items in this section will not be resized by sectionHeader // repositioning. You may also set this flag directly on any of the items in any section to // cause that item to not be resizeable. // @visibility external // @example resizeSections //< //> @attr SectionStackSection.canReorder (boolean : null : [I]) // If set to false, then this sectionHeader will not be able to be dragged to perform a drag // reorder, if +link{SectionStack.canReorderSections} is true. // You can also disable dropping other sections before this one by setting // +link{canvas.canDropBefore,canDropBefore} to false. // @visibility external //< //> @attr SectionStackSection.canDropBefore (boolean : null : [I]) // @include Canvas.canDropBefore // @visibility external //< //> @attr SectionStackSection.expanded (boolean : false : [I]) // Sections default to the collapsed state unless +link{SectionStackSection.showHeader} is // set to false in which case they default to the expanded state. This // attribute allows you to explicitly control the expand/collapse state of the // section by overriding the above default behavior. // @visibility external //< //> @attr SectionStackSection.hidden (boolean : false : [I]) // Sections default to the visible state. This // attribute allows you to explicitly control the visible/hidden state of the // section by overriding the above default behavior. // @visibility external //< //> @attr SectionStackSection.canCollapse (boolean : true : [I]) // This attribute controls whether or not the expand/collapse UI control is shown on the // header of this section. Any section can still be expanded/collapsed programmatically, // regardless of this setting. // @visibility external // @example sectionsExpandCollapse //< //>Animation // --------------------------------------------------------------------------------------- //> @attr sectionStack.animateSections (boolean : null : IRW) // If true, sections are animated during expand/collapse and addition/removal of // SectionItems is likewise animated. // @group animation // @visibility animation // @example animateSections //< // change layout default effect for showing/hiding members; "slide" is appropriate for // eg Window minimize, but "wipe" is usually the best effect for SectionStacks animateMemberEffect:"wipe", // @type VisibilityMode // Settings for whether multiple sections can be in the expanded state simultaneously. // // @value "mutex" // Only one section can be expanded at a time. // // @value "multiple" // Multiple sections can be expanded at the same time, and will share space. // // @visibility external //< //> @attr SectionStack.visibilityMode (VisibilityMode : "mutex" : [IRW]) // Whether multiple sections can be visible at once // // @see type:VisibilityMode // @visibility external // @example sectionsExpandCollapse //< visibilityMode:"mutex", // internal flag: if true, section stack will null out _userHeight on an eligible item when // hiding/collapsing sections to maintain the overall height of the SectionStack. If // false, the SectionStack will grow/shrink instead. This needs to be rolled up to Layout // as a policy instead. forceFill: true, //> @attr sectionStack.itemIndent (Number : 0 : [IRW]) // Size, in pixels, of indentation of all member items. Items will be offset // and reduced in width by this amount. Overridden by // +link{itemStartIndent} or +link{itemEndIndent}. // Setting itemIndent is equivalent to setting itemStartIndent to the same amount // and itemEndIndent to 0. // @visibility external // @group layoutMember //< itemIndent: 0, //> @attr sectionStack.itemStartIndent (Number : undefined : [IRW]) // Size, in pixels, of indentation of all member items relative to the start of // the alignment axis. For instance, for left-aligned members, // itemStartIndent specifies indentation for every item from the left side of the // section stack. Overrides +link{itemIndent}. // @visibility external // @group layoutMember //< //> @attr sectionStack.itemEndIndent (Number : undefined : [IRW]) // Size, in pixels, of indentation of all member items relative to the end of // the alignment axis. For instance, for left-aligned members, // itemStartIndent specifies indentation for every item from the right side of the // section stack. // @visibility external // @group layoutMember //< //> @attr sectionStack.showExpandControls (Boolean : true : [IRW]) // Whether to show the Expand/Collapse controls in the headers of sections. If false, hides // the expand/collapse controls and, instead, treats a click anywhere on the header as if // it were a click on the expand control. // @visibility external //< showExpandControls: true }); isc.SectionStack.addMethods({ initWidget : function () { this.Super(this._$initWidget); if (this.canReorderSections) this.canAcceptDrop = true; //>Animation if (this.visibilityMode == "mutex") this.animateSections = false; if (this.animateSections != null) this.animateMembers = this.animateSections //!BackCompat 2005.6.15 old name: "ListBar" with "groups" if (this.groups != null && this.sections == null) this.sections = this.groups; //EditMode if (!this._dragIsSectionReorder()) { return this.getEditModeDropPosition(dropType); } //

// Note that where APIs exist to explicitly manipulate section properties these should be // used in preference to this method. For example, to add or remove items in a section use // +link{sectionStack.addItem()} or +link{sectionStack.removeItem()}. To change the title of // a section, use +link{sectionStack.setSectionTitle}. //

// Also note that to modify properties of items within a section, call // the appropriate setter methods directly on the item you want to modify. // // @param section (String or Number) ID or index of the section to modify // @param properties (section properties) properties to apply to the section. // @visibility external //< setSectionProperties : function (section, properties) { var section = this.getSection(section); if (section != null) { if (isc.isA.Canvas(section)) { section.setProperties(properties); } else { isc.addProperties(section, properties); } } }, // override removeChild to properly remove items / sections removeChild : function (child, name) { isc.Layout._instancePrototype.removeChild.call(this, child, name); //this.Super("removeChild", arguments); var sections = this.sections; if (sections) { for (var i = 0; i < sections.length; i++) { var section = sections[i]; if (child == section) { this.removeSection(child); break; } else if (section.items && section.items.contains(child)) { this.removeItem(section, child); break; } } } }, // sectionNameIndex, used for auto-generated section names per stack. sectionNameIndex:0, addSections : function (sections, position, expandOne) { if (sections == null) return; if (!isc.isAn.Array(sections)) sections = [sections]; if (position == null || position > this.sections.length) { position = this.sections.length; } for (var i = 0; i < sections.length; i++) { var section = sections[i]; // support sparse arrays if (!section) continue; if (section.showHeader == null) section.showHeader = true; if (section.canHide == null) section.canHide = true; // use section.expanded if explicitly set. Otherwise default to collapsed // unless showHeader is false or autoShow is true (backcompat). var expanded = section.expanded != null ? section.expanded : // previous logic was // isOpen = section.autoShow || section.showHeader == false; section.autoShow || section.showHeader == false; if (section.hidden == null) section.hidden = false; // normalize items to Arrays if (section.items == null) section.items = []; else if (!isc.isA.Array(section.items)) section.items = [section.items]; // create a header for each section, which will also serve as the section itself. // NOTE: if showHeader is false, we still create a header object to represent the // section and track it's position in the members array, but it will never be // show()n, hence never drawn var headerClass = isc.ClassFactory.getClass(this.sectionHeaderClass), sectionHeader = headerClass.createRaw(); sectionHeader.autoDraw = false; sectionHeader._generated = true; sectionHeader.expanded = expanded; sectionHeader.isSectionHeader = true; // Suppress double clicks on section headers //>EditMode // We do allow double click in edit mode if (!this.editingOn) // @method sectionStack.addSection() // // Add a section to the SectionStack. // // @param sections (SectionStackSection Properties | List of SectionStackSection Properties) Initialization block // for the section or a list of initialization blocks to add. // @param [position] (number) index for the new section(s) (if not specified, the section // will be added at the end of the SectionStack). // // @visibility external // @example sectionsAddAndRemove //< addSection : function (sections, position) { this.addSections(sections, position); }, //> @method sectionStack.removeSection() // // Remove a section or set of sections from the SectionStack. The removed sections' header // and items (if any) are automatically destroyed. // // @param sections (Integer | String | Array of Integers | Array of Strings) Section(s) to remove. For this // parameter, you can pass the position of the section in the // SectionStack, the name of the section, or a List of // section names or indices. // // @visibility external // @example sectionsAddAndRemove //< removeSection : function (sections) { if (!isc.isAn.Array(sections)) sections = [sections]; for (var i = 0; i < sections.length; i++) { var section = sections[i]; section = this.getSectionHeader(section); for (var ii = 0; ii < section.items.length; ii++) { var item = section.items[ii]; if (this.members.contains(item)) this.removeMember(item); } this.sections.remove(section); if (!section.destroying && !section.destroyed) section.destroy(); } }, //> @method sectionStack.getSections() // // Returns a list of all +link{SectionStackSection.name,section names} in the order in which // they appear in the SectionStack. // // @return (List) list of all section names in the order in which they appear in the SectionStack. // @visibility external //< getSections : function () { return this.sections.getProperty("name"); }, //> @method sectionStack.reorderSection() // // Reorder the sections by shifting the specified section to a new position // // @param section (Integer or String) Section to move. You can pass the position // of the section in the SectionStack or the name of the section. // @param position (number) new position index for the section. // // @deprecated As of SmartClient version 5.5, use +link{sectionStack.moveSection}. // // @visibility external //< reorderSection : function (section, newPosition) { this.moveSection(section, newPosition); }, //> @method sectionStack.moveSection() // // Moves the specified section(s) to a new position in the SectionStack order. If you pass // in multiple sections, then each section will be moved to newPosition in the // order specified by the sections argument. // // @param sections (Integer | String | Array of Integer | Array of String) Section(s) to move. // For this parameter, you can pass the position of the section in the // SectionStack, the name of the section, or a List of section names/positions. // // @param position (number) new position index for the section(s). // // @visibility external //< moveSection : function (sections, newPosition) { if (newPosition == null) return; if (!isc.isAn.Array(sections)) sections = [sections]; // normalize initial positions to sections - indices will become // invalid as we go through the loop manipulating // this.sections. for (var i = 0; i < sections.length; i++) { var section = this.getSectionHeader(sections[i]); if (section == null) { this.logInfo("moveSection(): Unable to find header for specified section:" + sections[i] + ", skipping"); i--; sections.removeAt(i); } else { sections[i] = section; // and pull it out from this.sections this.sections[this.sections.indexOf(section)] = null; } } this.sections.removeEmpty(); this.sections.addListAt(sections, newPosition); // Now update the members array. var currentMemberIndex = 0; for (var i = 0; i < this.sections.length; i++) { var header = this.getSectionHeader(i), currentStart = this.members.indexOf(header), currentEnd = currentStart+1; var items = header.items; if (items != null && items.length != 0 && this.members.contains(items[0])) { if (currentStart == -1) { currentStart = this.members.indexOf(items[0]); currentEnd = currentStart; } currentEnd += items.length; } if (currentStart == -1) continue; this.members.slideRange(currentStart,currentEnd, currentMemberIndex); // next slot will be after this header and any items. currentMemberIndex += (currentEnd-currentStart); } this._membersReordered("moveSection() called"); }, //> @method sectionStack.showSection() // // Shows a section or sections. This includes the section header and its items. If the // section is collapsed, only the header is shown. If the section is expanded, the section // header and all items are shown. // // @param sections (Integer | String | Array of Integers | Array of Strings) // Section(s) to show. For this parameter, you can pass the position // of the section in the SectionStack, the name of the section, or a // List of section names / positions. // @param [callback] callback to fire when the sections have been expanded. // // @see sectionStack.expandSection // @see sectionStack.scrollSectionIntoView // @visibility external // @example sectionsShowAndHide //< showSection : function (sections, callback) { this._showSection(sections, true, false, callback); }, //> @method sectionStack.expandSection() // // Expands a section or sections. This action shows all the items assigned to the section. // If the section is currently hidden, it is shown first and then expanded. Calling this // method is equivalent to the user clicking on the SectionHeader of a collapsed section. // This method is called when the user clicks on SectionHeaders // to expand / collapse sections and so may be overridden to act as a notification method // for the user expanding or collapsing sections. // // @param sections (Integer | String | Array of Integers | Array of Strings) // Section(s) to expand. For this parameter, you can pass the position // of the section in the SectionStack, the name of the section, or a // List of section names/positions. // @param [callback] callback to fire when the section has been expanded. // // @see sectionStack.showSection // @see sectionStack.scrollSectionIntoView // @visibility external // @example sectionsExpandCollapse //< expandSection : function (sections, callback) { if (!isc.isAn.Array(sections)) sections = [sections]; if (this.visibilityMode == "mutex") { // keep only one section visible: hide the currently visible section this.collapseSection(this._lastExpandedSection); } this._showSection(sections, false, true, callback); }, _showSection : function (sections, showSection, expandSection, callback) { if (sections == null) return; if (!isc.isAn.Array(sections)) sections = [sections]; var itemsToShow = []; for (var i = 0; i < sections.length; i++) { var section = this.getSectionHeader(sections[i]); // bad section specification if (section == null) { this.logWarn("showSection(): no such section [" + i + "]: " + this.echo(sections[i])); continue; } // If section.showHeader is true and the section isn't visible, show it // (whether we're expanding or showing) if (section.showHeader && section.hidden && (showSection || expandSection)) { itemsToShow.add(section); section.hidden = false; } if (expandSection || section.expanded) { // Backcompat: setOpen is deprecated, but we still want to call it if // there's a backcompat definition. Otoh it's possible that we just have // setExpanded, so try that first and then call setOpen if (section.setExpanded && !section.setOpen) section.setExpanded(true); else if (section.setOpen) section.setOpen(true); // store the last expanded section this._lastExpandedSection = section; // NOTE: a section with no items doesn't make much sense, but it occurs in tools if (section.items) { // normalize items specified as strings / uninstantiated objects etc // to canvii for (var ii = section.items.length-1; ii >= 0; ii--) { var itemCanvas = this.createCanvas(section.items[ii]); if (!isc.isA.Canvas(itemCanvas)) { this.logWarn("Section with title:"+ section.title + " contains invalid item:" + section.items[ii] + " - ignoring this item."); section.items.removeAt(ii); continue; } section.items[ii] = itemCanvas; } // ensure the section's members are added, after the section header var sectionPosition = this._getSectionPosition(section) + 1; // NOTE: don't animate on add because we do the animation on showMembers // instead this.addMembers(section.items, sectionPosition, true); itemsToShow.addList(section.items); } } } var theStack = this; this.showMembers(itemsToShow, function () { theStack._completeShowOrExpandSection(sections, callback); } ); }, // fired as a callback to showMembers() from showSection() and expandSection() _completeShowOrExpandSection : function (sections, callback) { // sections is always an array here because this is an internal method and sections is // normalized by the caller if (sections.length == 0) return; // this method jsut scrolls things into view, but if we haven't been drawn yet, then // there's no need to do anything. if (this.isDrawn()) { // scroll the first passed section into view var section = this.getSectionHeader(sections[0]); // bring the section that was just shown into the viewport if (this.vscrollOn && this.scrollSectionIntoView) { var firstMember = (section.showHeader ? section : section.items.first()), lastMember = section.items.last(); // NOTE: visible height wouldn't be correct until component is drawn this.delayCall("scrollIntoView", [firstMember.getLeft(), firstMember.getTop(), firstMember.getVisibleWidth(), lastMember.getVisibleHeight(), "left", "top"], 0); } } if (callback != null) this.fireCallback(callback); }, //> @method sectionStack.hideSection() // // Hides a section or sections. This includes the section header and its items. The space // vacated by this action is reassigned to the nearest visible section item above this // section. If there are no visible section items above this section, the space is // reassigned to the nearest visible section item below this section. // // @param sections (Integer | String | Array of Integer | Array of String) // Section(s) to hide. For this parameter, you can pass the position // of the section in the SectionStack, the name of the section, or a // List of section names / positions. // @param [callback] callback to fire when the section has been hidden // // @see sectionStack.collapseSection // @visibility external // @example sectionsShowAndHide //< hideSection : function (sections, callback) { this._hideSection(sections, true, false, callback); }, //> @method sectionStack.collapseSection() // // Collapse a section or sections. This action hides all the items assigned to the // section. Calling this method is equivalent to the user clicking on the SectionHeader of // an expanded section. // This method is called when the user clicks on SectionHeaders // to expand / collapse sections and so may be overridden to act as a notification method // for the user expanding or collapsing sections. // // @param sections (Integer | String | Array of Integers | Array of Strings) // Section(s) to collapse. For this parameter, you can pass the position // of the section in the SectionStack, the name of the section, or a // List of section positions / names // // @param [callback] callback to fire when the section has been collapsed // @see sectionStack.hideSection // @visibility external // @example sectionsExpandCollapse //< collapseSection : function (sections, callback) { this._hideSection(sections, false, true, callback); }, _hideSection : function (sections, hideSection, collapseSection, callback) { if (sections == null) return; if (!isc.isAn.Array(sections)) sections = [sections]; var itemsToHide = []; for (var i = 0; i < sections.length; i++) { var section = this.getSectionHeader(sections[i]); // bad section specification if (section == null) { this.logWarn("hideSection(): no such section [" + i + "]: " + this.echo(sections[i])); continue; } if (hideSection && !section.hidden) { section.hidden = true; itemsToHide.add(section); } if (collapseSection || section.expanded) { // Backcompat: setOpen is deprecated, but we still want to call it if there's a // backcompat definition. Otoh it's possible that we just have setExpanded, so try // that first and then call setOpen if (collapseSection) { if (section.setExpanded && !section.setOpen) section.setExpanded(false); else if (section.setOpen) section.setOpen(false); } // some items may not have yet been added as members, so don't try to hide() // those or we'll crash in Layout if (section.items) { for (var j = 0; j < section.items.length; j++) { if (this.members.contains(section.items[j])) itemsToHide.add(section.items[j]); } } } } // forceFill: override recent user resizes to fill available space. NOTE: don't // forceFill if we're overflowed, as this would shrink us further after a collapse, // which is unexpected (this feature should be moved up to Layout as an optional // reaction to a hide) if (this.forceFill && this.getVisibleHeight() <= this.getHeight()) { // we want to make sure that some section(s) expand to fill the space vacated by this // hide/collapse. We scan through the members array to see if one of the items // will be resized by the layout automatically. If not, we pick one to forcibly // resize to fill the vacated space. // We're going to scan back from the first sectionHeader and then forward to try to // find an auto-resizable member and at the same time, we'll flag one that we can // forcefully resize if no auto-resizeable members are found. var startIndex = this.getMemberNumber(this.getSectionHeader(sections[0])); var forceResizeTarget; var layoutWillReflow = false; // scan back for (var i = startIndex-1; i >= 0; i--) { var member = this.members[i]; if (itemsToHide.contains(member)) continue; if (this.memberIsDragResizeable(member)) { if (this.memberHasAutoResizeableHeight(member)) { layoutWillReflow = true; break; } else if (forceResizeTarget == null) { forceResizeTarget = member; } } } if (!layoutWillReflow) { // scan forward for (var i = startIndex+1; i < this.members.length; i++) { var member = this.members[i]; if (itemsToHide.contains(member)) continue; if (this.memberIsDragResizeable(member)) { if (this.memberHasAutoResizeableHeight(member)) { layoutWillReflow = true; break; } else if (forceResizeTarget == null) { forceResizeTarget = member; } } } } if (!layoutWillReflow && forceResizeTarget != null) { // this.logWarn("layout will not reflow, forceResizing: " + forceResizeTarget.ID); forceResizeTarget._userHeight = null; // } else { // if (layoutWillReflow) this.logWarn("layout will reflow"); // else this.logWarn("layout will not reflow and no forceResizeTarget"); } } this.hideMembers(itemsToHide, callback); }, //> @method sectionStack.sectionIsVisible() // // Returns true if the specified section is visible, false if it is not. A section is // visible if it shows a header and the header is visible or if it has items and the first // item is visible. // // @param section (Integer|String) // Section for which you want to obtain visibility information. // For this parameter, you can pass the position of the section in the // SectionStack, or the name of the section. // // @return (boolean) true if the section is visible, false if it is not. // // @visibility external //< sectionIsVisible : function (section) { section = this.getSectionHeader(section); if (!section) return false; if (section.showHeader && section.isVisible()) return true; // NOTE: have to consider lazy initialization case var sectionMember = section.items.first(); if (sectionMember == null || !isc.isA.Canvas(sectionMember) || !sectionMember.isDrawn() || sectionMember.visibility == isc.Canvas.HIDDEN) return false; return true; }, //> @method sectionStack.getVisibleSections() // // Returns the list of currently visible sections. The list items are section names. // // @return (List) list of hidden sections // // @visibility external //< getVisibleSections : function() { var visibleSections = []; for (var i = 0; i < this.sections.length; i++) if (this.sectionIsVisible(this.sections[i])) visibleSections.add(this.sections[i].name); return visibleSections; }, //> @method sectionStack.sectionIsExpanded() // // Returns true if the specified section is expanded, false if it is collapsed. // // @param section (String|Integer) // Section for which you want to obtain information. // For this parameter, you can pass the position of the section in the // SectionStack, or the name of the section. // // @return (boolean) true if the section is expanded, false if it is not. // // @visibility external //< sectionIsExpanded : function (section) { return this.getSectionHeader(section).expanded; }, //> @method sectionStack.getExpandedSections() // // Returns the list of currently expanded sections. The list items are section IDs. // // @return (List) list of currently expanded sections // // @visibility external //< getExpandedSections : function () { var expandedSections = this.sections.findAll("expanded", true); return expandedSections == null ? [] : expandedSections.getProperty("name"); }, //> @method sectionStack.setSectionTitle() // Changes the title of a SectionHeader. // // @param section (String or Number) ID or index of the section whose title you want to change // @param newTitle (String) new title for the SectionHeader // @visibility external //< setSectionTitle : function (section, newTitle) { var sectionHeader = this.getSectionHeader(section); if (sectionHeader) sectionHeader.setTitle(newTitle); }, //> @method sectionStack.getSectionHeader() // Return the SectionHeader for a section. //

// This will be an instance of the +link{sectionHeaderClass}. Since different // SectionStacks may use different header classes, be careful about what APIs you rely on // for the section header unless you have explicitly set the // sectionHeaderClass. In particular, use APIs such as // +link{setSectionTitle()} to manipulate headers indirectly wherever possible, as high // performance SectionStacks designed for very large numbers of sections may cache and // re-use headers or use other strategies that would make it invalid to store a pointer to // a section header, assume the header is a layout member, etc. // // @param section (String or Number) ID or index of the section for which you want the header // @return (SectionHeader) the section header indicated // @visibility external //< getSectionHeader : function (section) { return isc.Class.getArrayItem(section, this.sections, "name"); }, getSection : function (section) { return this.getSectionHeader(section) }, // Retrieves the section config object passed in when a section stack section was first // created. getSectionConfig : function (section) { var sectionHeader = this.getSectionHeader(section); if (!isc.isA.Canvas(sectionHeader)) return sectionHeader; return sectionHeader._sectionConfig; }, //> @method sectionStack.getSectionNumber() // // Returns the position of the specified section in the SectionStack. The numbering is // zero-based. // // @param sectionName (string) name of a section for which you want to obtain the position. // // @return (number) Position of the section in the SectionStack or -1 if the specified // section is not a member of this SectionStack. // // @visibility external //< getSectionNumber : function (section) { if (isc.isA.String(section)) { return this.sections.findIndex("name", section); // handle being passed a pointer to a section directly } else { return this.sections.indexOf(section); } }, // returns the position in the members array where the first item (header or first item in the // section.items array if showHeader = false) in this section should be // // for external interfaces we only care about the index of the section in this.sections, // because that's what all external methods take as a section identifier (among others) and // end users shouldn't be directly modifying the underlying Layout. _getSectionPosition : function (section) { // if the section header has already been added as a member, it's position is // straightforward. var headerPosition = this.getMemberNumber(section); if (headerPosition != -1) return headerPosition; // otherwise look for the position of the last item in the previous section var sectionIndex = this.sections.indexOf(section); // if we're the first section we start at zero if (sectionIndex <= 0) return sectionIndex; // otherwise we start after the last item of the preceding section var previousSection = this.sections[sectionIndex-1], lastMember = previousSection.items ? previousSection.items.last() : null; if (this.hasMember(lastMember)) { return this.getMemberNumber(lastMember) + 1; } else { // NOTE: sections without headers always have their items added immediately, since // there's no way to hide them return this.getMemberNumber(previousSection) + 1; } }, //> @method SectionStack.sectionHeaderClick (A) // Method intended to be called by the sectionHeader when it is clicked on. // // @param sectionHeader (Canvas) the sectionHeader clicked on // @visibility external //< sectionHeaderClick : function (section) { // If onSectionHeaderClick exists, allow it to cancel default behavior if (this.onSectionHeaderClick && (this.onSectionHeaderClick(section) == false)) { return false; } // hide the currently visible pane and show the pane for the header that got clicked on if (this.visibilityMode == "mutex") { // if this section is expanded, collapse it and expand the following section if (this.sectionIsExpanded(section)) { var sectionIndex = this.sections.indexOf(section); // if last section, next section will revert back to first if (sectionIndex == this.sections.getLength() - 1) sectionIndex = 0; else sectionIndex += 1; var nextSection = this.sections[sectionIndex]; // collapse this section this.collapseSection(section); // expand next section this.expandSection(nextSection); this._lastExpandedSection = nextSection; return false; } // keep only one section visible: hide the currently visible section this.collapseSection(this._lastExpandedSection); // show the new section this.expandSection(section); } else { // just toggle expanded/collapsed if (!this.sectionIsExpanded(section)) { this.expandSection(section); } else { this.collapseSection(section); } } // remember this as the new last section this._lastExpandedSection = section; return false; // cancel event bubbling }, getSectionCursor : function (sectionHeader) { var cursor; var config = this.getSectionConfig(sectionHeader); if (config == null) cursor = isc.Canvas.DEFAULT; else if (config.cursor) cursor = config.cursor; else { if (config.canCollapse != false) { cursor = isc.Canvas.HAND; } else if (this.canRorderSections && config.canReorder != false) { cursor = "move"; } else { cursor = isc.Canvas.DEFAULT; } } return cursor; }, // For a given member, return the closest resizeable member _before_ us in the members // array. See memberIsResizeable() for what constitutes a resizeable member. getDragResizeTarget : function (member) { var myIndex = this.getMemberNumber(member); // look for a member preceding us that can be resized var resizeTarget; this._resizeIgnore = 0; for (var i = myIndex-1; i >= 0; i--) { var member = this.getMember(i); if (this.memberIsDragResizeable(member)) { resizeTarget = member; break; } // as we pass non-resizeable members, store up their total height, which we will // use as offset when using the coordinate of the dragged section header to resize // whatever member actually gets chosen as the resize target. // NOTE: if we pass a section header, don't resize if the preceding member is // another section header, detected via the isSectionHeader flag rather than // isc.isA.SectionHeader since section header implementation is pluggable if (member.isSectionHeader || (!member.resizeable && member.isVisible())) this._resizeIgnore += member.getVisibleHeight(); } // if there are no preceeding resizeable members, never allow resize (eg, no // resize is possible if you are grabbing the first section header, or a section header // after a series of collapsed or fixed-size sections) if (!resizeTarget) return null; // Read as: // - if canResizeStack is true (default), always allow resize if there is a preceeding, // resizeable member, even though this *may* change the overall stack size if there // isn't also a resizeable member after this section header // - if canResizeStack is false, only allow a resize if there is *also* a member // after us that can resize, because only then will all available space still be // filled. if (this.canResizeStack) return resizeTarget; // look for a member after us that can resize var numMembers = this.getMembers().length; for (var i = myIndex+1; i < numMembers; i++) { var member = this.getMember(i); // some member after the sectionHeader is resizeable, so go ahead and return the // resizeTarget we previously determined if (this.memberIsDragResizeable(member)) return resizeTarget; } return null; }, memberIsDragResizeable : function (member) { if (!member.isSectionHeader && member.resizeable !== false && member.isVisible() && (!this.memberHasInherentLength(member) || member.resizeable) ) return true; }, memberHasAutoResizeableHeight : function (member) { var uh = member._userHeight; return uh == null || (isc.isA.String(uh) && (uh == "*" || isc.endsWith(uh, "%"))); }, getMemberDefaultBreadth : function (member, defaultBreadth) { var breadth = defaultBreadth; if (!member.isSectionHeader) { if (this.itemStartIndent != null || this.itemEndIndent != null) breadth -= this.itemStartIndent + this.itemEndIndent; else breadth -= this.itemIndent; } return breadth; }, getMemberOffset : function (member, defaultOffset, alignment) { var offset = this.itemIndent; if (member.isSectionHeader) return defaultOffset; if (this.itemStartIndent != null) offset = this.itemStartIndent; if (alignment == isc.Canvas.RIGHT || alignment == isc.Canvas.BOTTOM) offset *= -1; return defaultOffset + offset; } }); // SectionHeader classes // --------------------------------------------------------------------------------------- isc._commonMediaProps = { icon:"[SKIN]SectionHeader/opener.gif", overflow:"hidden", baseStyle:"sectionHeader", // Show the disabled style in both image based headers and label-based headers showDisabled:true, // expanded/collapsed styling // --------------------------------------------------------------------------------------- expanded: false, setExpanded : function (expanded) { this.expanded = expanded; this.stateChanged(); }, //>!BackCompat 2005.12.22 setOpen : function (isOpen) { this.setExpanded(isOpen); }, //@method SectionHeader.getSectionStack() // For a SectionHeader embedded in a SectionStack, this method will return // a pointer to the +link{SectionStack} in which this section header is // embedded. // @return (SectionStack) Section Stack containing this section header // @visibility external //< //>@method ImgSectionHeader.getSectionStack() // @include SectionHeader.getSectionStack() // @visibility external //< getSectionStack : function () { // we store the attribute as "layout" when addSection runs var layout = this.layout; if (layout) return isc.isA.String(layout) ? window[layout] : layout; else return null; }, // Collapse behavior // --------------------------------------------------------------------------------------- // Snap open/closed on "space" / "enter" keypress // Allow resizing via ctrl+arrow keys keyPress : function () { var layout = this.getSectionStack(); if (layout == null) return; var keyName = isc.EH.getKey(); if (keyName == "Enter" || keyName == "Space") { if (this.canCollapse) return layout.sectionHeaderClick(this); } else if (keyName == "Arrow_Up" || keyName == "Arrow_Down") { var target = layout.getDragResizeTarget(this); // NOTE: don't resize if the preceding member is another section header, detected // via marker rather than class since section header is pluggable if (target == null) return false; var resizeStep = (keyName == "Arrow_Up" ? -5 : 5); this.bringToFront(); // so we aren't occluded by what we will resize this.resizeTarget(target, true, this.resizeInRealTime, 0, 0, (this.getPageTop() + resizeStep)) // set a flag so we know to kill the when the user releases the ctrl key this._keyResizeTarget = target; } }, keyUp : function () { if (this._keyResizeTarget) { var keyName = isc.EH.getKey(); if (keyName == "Arrow_Up" || keyName == "Arrow_Down") { this.finishTargetResize(this._keyResizeTarget, true, this.resizeInRealTime); this._keyResizeTarget = null; } } }, _canFocus : function () { // Support setting section.canTabToHeader explicitly. if (this.canTabToHeader != null) return this.canTabToHeader; var layout = this.getSectionStack(); // layout will be either a SectionStack or a SectionItem - support canTabToHeader and its plural if (layout) { if (layout.canTabToHeaders != null) return layout.canTabToHeaders; if (layout.canTabToHeader != null) return layout.canTabToHeader; if (isc.SectionItem && isc.isA.SectionItem(layout)) { var form = layout.form; if (form && form.canTabToSectionHeaders != null) return form.canTabToSectionHeaders; } // If canTabToHeader isn't explicitly set, allow tab to header if isc.screenReader is // set. return !!isc.screenReader; } else return true; }, _hasLayout : function () { var layout = this.getSectionStack(); return layout ? true : false; }, // Editing // --------------------------------------------------------------------------------------- //>EditMode schemaName : "SectionStackSection", // schema to use when editing and serializing addItem : function (item, index) { if (!this._hasLayout()) return; var layout = this.getSectionStack(); layout.addItem(this, item, index); // Visual Builder expects addItem to also expand this section layout.expandSection(this); }, removeItem : function (item) { if (!this._hasLayout()) return; this.getSectionStack().removeItem(this, item); }, // @attr sectionHeader.controls (Array of Canvas : null : IR) // Custom controls to be shown on top of this section header. //

// These controls are shown in the +link{controlsLayout}. // @example sectionControls // @visibility external //< //> @attr imgSectionHeader.controls (Array of Canvas : null : IR) // @include sectionHeader.controls //< //> @attr sectionHeader.controlsLayout (AutoChild : null : IR) // A +link{Layout} containing specified +link{controls} if any. Sets // +link{layout.membersMargin}:5, +link{layout.defaultLayoutAlign}:"center", and // RTL-sensitive +link{layout.align} (right by default). // @visibility external //< //> @attr imgSectionHeader.controlsLayout (AutoChild : null : IR) // @include sectionHeader.controlsLayout //< controlsLayoutDefaults : { _constructor:isc.HStack, defaultLayoutAlign:"center", snapTo:"R", membersMargin:5, layoutEndMargin:5, addAsChild:true }, addControls : function () { if (!this.controls) return; this.addAutoChild("controlsLayout", { height:this.getInnerHeight(), align:this.isRTL() ? "left" : "right", members:this.controls }); this.allowContentAndChildren = true; }, refreshControls : function () { if (!this.controls) return; if (!this.controlsLayout) this.addControls(); var layout = this.controlsLayout; layout.addMembers(this.controls); /* this.addAutoChild("controlsLayout", { height:this.getInnerHeight(), align:this.isRTL() ? "left" : "right", members:this.controls }); */ this.allowContentAndChildren = true; }, // Printing // ------------------------------------------------------------------------------------------ // When printing, pick up the printStyleName from our sectionStack if it's set // Note that SectionHeaders are used in sectionItems as well. In this case the parentElement // will be the DynamicForm which may not have printHeaderStyleName set getPrintStyleName : function () { var sectionStack = this.parentElement; if (sectionStack && sectionStack.printHeaderStyleName != null) { this.printStyleName = sectionStack.printHeaderStyleName; } return this.Super("getPrintStyleName", arguments); }, // force section headers to print even though they're controls shouldPrint:true }; //> @class SectionHeader // Simple SectionHeader class based on a Label with an icon, skinnable via CSS. // // @treeLocation Client Reference/Layout/SectionStack // @visibility external //< isc.defineClass("SectionHeader", "Label").addMethods(isc._commonHeaderProps, isc._commonMediaProps, { // We use this.title, not this.contents for the section header title useContents:false, //> @attr sectionHeader.icon (SCImgURL : "[SKIN]SectionHeader/opener.gif" : [IRA]) // Base filename of the icon that represents open and closed states. The default settings // also change the icon for disabled sections, so a total of four images are required // (opened, closed, Disabled_opened, Disabled_closed). //

// Not shown if +link{sectionStackSection.canCollapse} is false. // // @visibility external //< //> @attr sectionHeader.baseStyle (CSSStyleName : "sectionHeader" : [IRA]) // CSS class for the section header. // @visibility external //< // call our layout on click click : function () { // for certain skins (e.g. fleet) a widget inside of the sectionheader, when clicked, // will also cause the sectionheader to fire a click event. In that case we don't // want the sectionheader to register the click. if (this.contains(isc.EH.lastTarget)) return; if (!this.canCollapse || !this._hasLayout()) return; return this.getSectionStack().sectionHeaderClick(this); }, draw : function (a,b,c,d) { if (isc._traceMarkers) arguments.__this = this; if (!this.readyToDraw()) return; this.align = this.isRTL() ? "right" : "left"; // if the section cannot be collapsed, or SectionStack.showExpandControls: false, don't // show the expand/collapse icons and allow clicks anywhere to expand and collapse if (!this.canCollapse || (this._hasLayout() && this.getSectionStack() && this.getSectionStack().showExpandControls == false)) { this.icon = null; this.showIconState = false; } this.setCursor(this.getCurrentCursor()); this.invokeSuper(isc.SectionHeader, "draw", a,b,c,d); this.addControls(); if (this.headerControls != null) { this.headerLayout = isc.HLayout.create({ autoDraw:false, width:this.getInnerWidth(), height:this.getInnerHeight(), members:this.headerControls }); // Has to be a child, not a peer, so it will bubble clicks etc through if appropriate this.addChild(this.headerLayout); this.allowContentAndChildren = true; } }, getCurrentCursor : function () { var cursor = this.cursor; // sections may be rendered outside of true sectionStacks // (for example in SectionItems) if (this.getSectionStack() && this.getSectionStack().getSectionCursor != null) { cursor = this.getSectionStack().getSectionCursor(this); } return cursor; } }); //> @class ImgSectionHeader // SectionHeader class based on an HLayout with +link{StretchImg} background. // @treeLocation Client Reference/Layout/SectionStack // @visibility external //< isc.defineClass("ImgSectionHeader", "HLayout").addMethods({ //> @attr ImgSectionHeader.background (AutoChild : null : R) // Background of the section header, based on a StretchImg. // @visibility external //< backgroundDefaults : isc.addProperties({ titleStyle:"sectionHeaderTitle", // These images now live in SectionHeader/ in the provided skins, but SectionStack/ // is left as the default for backcompat with customer skins. src:"[SKIN]SectionStack/header.gif", backgroundColor:"#a0a0a0", // call our layout on click. Note this function is placed on the background element so // that clicks on headerControls floating above the background do not trigger // expand/collapse click : function () { //>EditMode if (this.parentElement && this.parentElement.editingOn) { return this.Super("click", arguments); } //!BackCompat 2005.12.22 setOpen : function (isOpen) { this.setExpanded(isOpen); }, // @method sectionStack.onSectionHeaderClick() // Notification method fired when the user clicks on a section header. // Returning false will cancel the default behavior (expanding / collapsing the section) // @param section (SectionHeader) SectionHeader clicked by the user // @return (boolean) returning false cancels the default behavior // @visibility sgwt //< onSectionHeaderClick:"sectionHeader" }); isc.SectionStack.registerDupProperties( "sections", // second array is sub-properties! ["items"]);





© 2015 - 2024 Weber Informatics LLC | Privacy Policy