613 lines
20 KiB
JavaScript
613 lines
20 KiB
JavaScript
// Copyright 2008 The Closure Library Authors. All Rights Reserved.
|
|
//
|
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
// you may not use this file except in compliance with the License.
|
|
// You may obtain a copy of the License at
|
|
//
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
|
//
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
// distributed under the License is distributed on an "AS-IS" BASIS,
|
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
// See the License for the specific language governing permissions and
|
|
// limitations under the License.
|
|
|
|
goog.provide('goog.ui.ContainerTest');
|
|
goog.setTestOnly('goog.ui.ContainerTest');
|
|
|
|
goog.require('goog.a11y.aria');
|
|
goog.require('goog.dom');
|
|
goog.require('goog.dom.classlist');
|
|
goog.require('goog.events');
|
|
goog.require('goog.events.Event');
|
|
goog.require('goog.events.KeyCodes');
|
|
goog.require('goog.events.KeyEvent');
|
|
goog.require('goog.testing.events');
|
|
goog.require('goog.testing.jsunit');
|
|
goog.require('goog.ui.Component');
|
|
goog.require('goog.ui.Container');
|
|
goog.require('goog.ui.Control');
|
|
|
|
var sandbox;
|
|
var containerElement;
|
|
var container;
|
|
var keyContainer;
|
|
var listContainer;
|
|
|
|
function setUpPage() {
|
|
sandbox = goog.dom.getElement('sandbox');
|
|
}
|
|
|
|
function setUp() {
|
|
container = new goog.ui.Container();
|
|
keyContainer = null;
|
|
listContainer = null;
|
|
|
|
sandbox.innerHTML =
|
|
'<div id="containerElement" class="goog-container">\n' +
|
|
' <div class="goog-control" id="hello">Hello</div>\n' +
|
|
' <div class="goog-control" id="world">World</div>\n' +
|
|
'</div>';
|
|
containerElement = goog.dom.getElement('containerElement');
|
|
}
|
|
|
|
function tearDown() {
|
|
goog.dom.removeChildren(sandbox);
|
|
container.dispose();
|
|
goog.dispose(keyContainer);
|
|
goog.dispose(listContainer);
|
|
}
|
|
|
|
function testDecorateHidden() {
|
|
containerElement.style.display = 'none';
|
|
|
|
assertTrue('Container must be visible', container.isVisible());
|
|
container.decorate(containerElement);
|
|
assertFalse('Container must be hidden', container.isVisible());
|
|
container.forEachChild(function(control) {
|
|
assertTrue('Child control ' + control.getId() + ' must report being ' +
|
|
'visible, even if in a hidden container', control.isVisible());
|
|
});
|
|
}
|
|
|
|
function testDecorateDisabled() {
|
|
goog.dom.classlist.add(containerElement, 'goog-container-disabled');
|
|
|
|
assertTrue('Container must be enabled', container.isEnabled());
|
|
container.decorate(containerElement);
|
|
assertFalse('Container must be disabled', container.isEnabled());
|
|
container.forEachChild(function(control) {
|
|
assertFalse('Child control ' + control.getId() + ' must be disabled, ' +
|
|
'because the host container is disabled', control.isEnabled());
|
|
});
|
|
}
|
|
|
|
function testDecorateFocusableContainer() {
|
|
container.decorate(containerElement);
|
|
assertTrue('Container must be focusable', container.isFocusable());
|
|
container.forEachChild(function(control) {
|
|
assertFalse('Child control ' + control.getId() + ' must not be ' +
|
|
'focusable',
|
|
control.isSupportedState(goog.ui.Component.State.FOCUSED));
|
|
});
|
|
}
|
|
|
|
function testDecorateFocusableChildrenContainer() {
|
|
container.setFocusable(false);
|
|
container.setFocusableChildrenAllowed(true);
|
|
container.decorate(containerElement);
|
|
assertFalse('Container must not be focusable', container.isFocusable());
|
|
container.forEachChild(function(control) {
|
|
assertTrue('Child control ' + control.getId() + ' must be ' +
|
|
'focusable',
|
|
control.isSupportedState(goog.ui.Component.State.FOCUSED));
|
|
});
|
|
}
|
|
|
|
function testHighlightOnEnter() {
|
|
// This interaction test ensures that containers enforce that children
|
|
// get highlighted on mouseover, and that one and only one child may
|
|
// be highlighted at a time. Although integration tests aren't the
|
|
// best, it's difficult to test these event-based interactions due to
|
|
// their disposition toward the "misunderstood contract" problem.
|
|
|
|
container.decorate(containerElement);
|
|
assertFalse('Child 0 should initially not be highlighted',
|
|
container.getChildAt(0).isHighlighted());
|
|
|
|
goog.testing.events.fireMouseOverEvent(
|
|
container.getChildAt(0).getElement(), sandbox);
|
|
assertTrue('Child 0 should become highlighted after a mouse over',
|
|
container.getChildAt(0).isHighlighted());
|
|
assertEquals('Child 0 should be the active descendant',
|
|
container.getChildAt(0).getElement(),
|
|
goog.a11y.aria.getActiveDescendant(container.getElement()));
|
|
|
|
goog.testing.events.fireMouseOverEvent(
|
|
container.getChildAt(1).getElement(),
|
|
container.getChildAt(0).getElement());
|
|
assertFalse('Child 0 should lose highlight when child 1 is moused ' +
|
|
'over, even if no mouseout occurs.',
|
|
container.getChildAt(0).isHighlighted());
|
|
assertTrue('Child 1 should now be highlighted.',
|
|
container.getChildAt(1).isHighlighted());
|
|
assertEquals('Child 1 should be the active descendant',
|
|
container.getChildAt(1).getElement(),
|
|
goog.a11y.aria.getActiveDescendant(container.getElement()));
|
|
}
|
|
|
|
function testHighlightOnEnterPreventable() {
|
|
container.decorate(containerElement);
|
|
goog.events.listen(container, goog.ui.Component.EventType.ENTER,
|
|
function(event) {
|
|
event.preventDefault();
|
|
});
|
|
goog.testing.events.fireMouseOverEvent(
|
|
container.getChildAt(0).getElement(), sandbox);
|
|
assertFalse('Child 0 should not be highlighted if preventDefault called',
|
|
container.getChildAt(0).isHighlighted());
|
|
}
|
|
|
|
function testHighlightDisabled() {
|
|
// Another interaction test. Already tested in control_test.
|
|
container.decorate(containerElement);
|
|
container.getChildAt(0).setEnabled(false);
|
|
goog.testing.events.fireMouseOverEvent(
|
|
container.getChildAt(0).getElement(), sandbox);
|
|
assertFalse('Disabled children should not be highlighted',
|
|
container.getChildAt(0).isHighlighted());
|
|
}
|
|
|
|
function testGetOwnerControl() {
|
|
container.decorate(containerElement);
|
|
|
|
assertEquals('Must return appropriate control given an element in the ' +
|
|
'control.',
|
|
container.getChildAt(1),
|
|
container.getOwnerControl(container.getChildAt(1).getElement()));
|
|
|
|
assertNull('Must return null for element not associated with control.',
|
|
container.getOwnerControl(document.body));
|
|
assertNull('Must return null if given null node',
|
|
container.getOwnerControl(null));
|
|
}
|
|
|
|
function testShowEvent() {
|
|
container.decorate(containerElement);
|
|
container.setVisible(false);
|
|
var eventFired = false;
|
|
goog.events.listen(container, goog.ui.Component.EventType.SHOW,
|
|
function() {
|
|
assertFalse('Container must not be visible when SHOW event is ' +
|
|
'fired',
|
|
container.isVisible());
|
|
eventFired = true;
|
|
});
|
|
container.setVisible(true);
|
|
assertTrue('SHOW event expected', eventFired);
|
|
}
|
|
|
|
function testAfterShowEvent() {
|
|
container.decorate(containerElement);
|
|
container.setVisible(false);
|
|
var eventFired = false;
|
|
goog.events.listen(container, goog.ui.Container.EventType.AFTER_SHOW,
|
|
function() {
|
|
assertTrue('Container must be visible when AFTER_SHOW event is ' +
|
|
'fired',
|
|
container.isVisible());
|
|
eventFired = true;
|
|
});
|
|
container.setVisible(true);
|
|
assertTrue('AFTER_SHOW event expected', eventFired);
|
|
}
|
|
|
|
function testHideEvents() {
|
|
var events = [];
|
|
container.decorate(containerElement);
|
|
container.setVisible(true);
|
|
var eventFired = false;
|
|
goog.events.listen(container, goog.ui.Component.EventType.HIDE,
|
|
function(e) {
|
|
assertTrue(
|
|
'Container must be visible when HIDE event is fired',
|
|
container.isVisible());
|
|
events.push(e.type);
|
|
});
|
|
goog.events.listen(container, goog.ui.Container.EventType.AFTER_HIDE,
|
|
function(e) {
|
|
assertFalse(
|
|
'Container must not be visible when AFTER_HIDE event is fired',
|
|
container.isVisible());
|
|
events.push(e.type);
|
|
});
|
|
container.setVisible(false);
|
|
assertArrayEquals('HIDE event followed by AFTER_HIDE expected', [
|
|
goog.ui.Component.EventType.HIDE,
|
|
goog.ui.Container.EventType.AFTER_HIDE
|
|
], events);
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
* Test container to which the elements have to be added with
|
|
* {@code container.addChild(element, false)}
|
|
* @constructor
|
|
* @extends {goog.ui.Container}
|
|
*/
|
|
function ListContainer() {
|
|
goog.ui.Container.call(this);
|
|
}
|
|
goog.inherits(ListContainer, goog.ui.Container);
|
|
|
|
|
|
/** @override */
|
|
ListContainer.prototype.createDom = function() {
|
|
ListContainer.superClass_.createDom.call(this);
|
|
var ul = this.getDomHelper().createDom('ul');
|
|
this.forEachChild(function(child) {
|
|
child.createDom();
|
|
var childEl = child.getElement();
|
|
ul.appendChild(this.getDomHelper().createDom('li', {}, childEl));
|
|
}, this);
|
|
this.getContentElement().appendChild(ul);
|
|
};
|
|
|
|
function testGetOwnerControlWithNoRenderingInAddChild() {
|
|
listContainer = new ListContainer();
|
|
var control = new goog.ui.Control('item');
|
|
listContainer.addChild(control);
|
|
listContainer.render();
|
|
var ownerControl = listContainer.getOwnerControl(control.getElement());
|
|
|
|
assertEquals('Control was added with addChild(control, false)',
|
|
control, ownerControl);
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
* Test container for tracking key events being handled.
|
|
* @constructor
|
|
* @extends {goog.ui.Container}
|
|
*/
|
|
function KeyHandlingContainer() {
|
|
goog.ui.Container.call(this);
|
|
this.keyEventsHandled = 0;
|
|
}
|
|
goog.inherits(KeyHandlingContainer, goog.ui.Container);
|
|
|
|
|
|
/** @override */
|
|
KeyHandlingContainer.prototype.handleKeyEventInternal = function() {
|
|
this.keyEventsHandled++;
|
|
return false;
|
|
};
|
|
|
|
function testHandleKeyEvent_onlyHandlesWhenVisible() {
|
|
keyContainer = new KeyHandlingContainer();
|
|
keyContainer.decorate(containerElement);
|
|
|
|
keyContainer.setVisible(false);
|
|
keyContainer.handleKeyEvent(new goog.events.Event());
|
|
assertEquals('No key events should be handled',
|
|
0, keyContainer.keyEventsHandled);
|
|
|
|
keyContainer.setVisible(true);
|
|
keyContainer.handleKeyEvent(new goog.events.Event());
|
|
assertEquals('One key event should be handled',
|
|
1, keyContainer.keyEventsHandled);
|
|
}
|
|
|
|
function testHandleKeyEvent_onlyHandlesWhenEnabled() {
|
|
keyContainer = new KeyHandlingContainer();
|
|
keyContainer.decorate(containerElement);
|
|
keyContainer.setVisible(true);
|
|
|
|
keyContainer.setEnabled(false);
|
|
keyContainer.handleKeyEvent(new goog.events.Event());
|
|
assertEquals('No key events should be handled',
|
|
0, keyContainer.keyEventsHandled);
|
|
|
|
keyContainer.setEnabled(true);
|
|
keyContainer.handleKeyEvent(new goog.events.Event());
|
|
assertEquals('One key event should be handled',
|
|
1, keyContainer.keyEventsHandled);
|
|
}
|
|
|
|
function testHandleKeyEvent_childlessContainersIgnoreKeyEvents() {
|
|
keyContainer = new KeyHandlingContainer();
|
|
keyContainer.render();
|
|
keyContainer.setVisible(true);
|
|
|
|
keyContainer.handleKeyEvent(new goog.events.Event());
|
|
assertEquals('No key events should be handled',
|
|
0, keyContainer.keyEventsHandled);
|
|
|
|
keyContainer.addChild(new goog.ui.Control());
|
|
keyContainer.handleKeyEvent(new goog.events.Event());
|
|
assertEquals('One key event should be handled',
|
|
1, keyContainer.keyEventsHandled);
|
|
}
|
|
|
|
function testHandleKeyEvent_alwaysHandlesWithKeyEventTarget() {
|
|
keyContainer = new KeyHandlingContainer();
|
|
keyContainer.render();
|
|
keyContainer.setKeyEventTarget(goog.dom.createDom('div'));
|
|
keyContainer.setVisible(true);
|
|
|
|
keyContainer.handleKeyEvent(new goog.events.Event());
|
|
assertEquals('One key events should be handled',
|
|
1, keyContainer.keyEventsHandled);
|
|
}
|
|
|
|
function testHandleKeyEventInternal_onlyHandlesUnmodified() {
|
|
container.setKeyEventTarget(sandbox);
|
|
var event = new goog.events.KeyEvent(
|
|
goog.events.KeyCodes.ESC, 0, false, null);
|
|
|
|
var propertyNames = [
|
|
'shiftKey',
|
|
'altKey',
|
|
'ctrlKey',
|
|
'metaKey'
|
|
];
|
|
|
|
// Verify that the event is not handled whenever a modifier key is true.
|
|
for (var i = 0, propertyName; propertyName = propertyNames[i]; i++) {
|
|
assertTrue('Event should be handled when modifer key is not pressed.',
|
|
container.handleKeyEventInternal(event));
|
|
event[propertyName] = true;
|
|
assertFalse('Event should not be handled when modifer key is pressed.',
|
|
container.handleKeyEventInternal(event));
|
|
event[propertyName] = false;
|
|
}
|
|
}
|
|
|
|
function testOpenFollowsHighlight() {
|
|
container.decorate(containerElement);
|
|
container.setOpenFollowsHighlight(true);
|
|
assertTrue('isOpenFollowsHighlight should return true',
|
|
container.isOpenFollowsHighlight());
|
|
|
|
// Make the children openable.
|
|
container.forEachChild(function(child) {
|
|
child.setSupportedState(goog.ui.Component.State.OPENED, true);
|
|
});
|
|
// Open child 1 initially.
|
|
container.getChildAt(1).setOpen(true);
|
|
|
|
assertFalse('Child 0 should initially not be highlighted',
|
|
container.getChildAt(0).isHighlighted());
|
|
goog.testing.events.fireMouseOverEvent(
|
|
container.getChildAt(0).getElement(), sandbox);
|
|
assertTrue('Child 0 should become highlighted after a mouse over',
|
|
container.getChildAt(0).isHighlighted());
|
|
assertTrue('Child 0 should become open after higlighted',
|
|
container.getChildAt(0).isOpen());
|
|
assertFalse('Child 1 should become closed once 0 is open',
|
|
container.getChildAt(1).isOpen());
|
|
assertEquals('OpenItem should be child 0',
|
|
container.getChildAt(0), container.getOpenItem());
|
|
}
|
|
|
|
function testOpenNotFollowsHighlight() {
|
|
container.decorate(containerElement);
|
|
container.setOpenFollowsHighlight(false);
|
|
assertFalse('isOpenFollowsHighlight should return false',
|
|
container.isOpenFollowsHighlight());
|
|
|
|
// Make the children openable.
|
|
container.forEachChild(function(child) {
|
|
child.setSupportedState(goog.ui.Component.State.OPENED, true);
|
|
});
|
|
// Open child 1 initially.
|
|
container.getChildAt(1).setOpen(true);
|
|
|
|
assertFalse('Child 0 should initially not be highlighted',
|
|
container.getChildAt(0).isHighlighted());
|
|
goog.testing.events.fireMouseOverEvent(
|
|
container.getChildAt(0).getElement(), sandbox);
|
|
assertTrue('Child 0 should become highlighted after a mouse over',
|
|
container.getChildAt(0).isHighlighted());
|
|
assertFalse('Child 0 should remain closed after higlighted',
|
|
container.getChildAt(0).isOpen());
|
|
assertTrue('Child 1 should remain open',
|
|
container.getChildAt(1).isOpen());
|
|
assertEquals('OpenItem should be child 1',
|
|
container.getChildAt(1), container.getOpenItem());
|
|
}
|
|
|
|
function testRemoveChild() {
|
|
goog.dom.removeChildren(containerElement);
|
|
container.decorate(containerElement);
|
|
|
|
var a = new goog.ui.Control('A');
|
|
var b = new goog.ui.Control('B');
|
|
var c = new goog.ui.Control('C');
|
|
|
|
a.setId('a');
|
|
b.setId('b');
|
|
c.setId('c');
|
|
|
|
container.addChild(a, true);
|
|
container.addChild(b, true);
|
|
container.addChild(c, true);
|
|
|
|
container.setHighlightedIndex(2);
|
|
|
|
assertEquals('Parent must remove and return child by ID', b,
|
|
container.removeChild('b'));
|
|
assertNull('Parent must no longer contain this child',
|
|
container.getChild('b'));
|
|
assertEquals('Highlighted index must be decreased', 1,
|
|
container.getHighlightedIndex());
|
|
assertTrue('The removed control must handle its own mouse events',
|
|
b.isHandleMouseEvents());
|
|
|
|
assertEquals('Parent must remove and return child', c,
|
|
container.removeChild(c));
|
|
assertNull('Parent must no longer contain this child',
|
|
container.getChild('c'));
|
|
assertFalse('This child must no longer be highlighted',
|
|
c.isHighlighted());
|
|
assertTrue('The removed control must handle its own mouse events',
|
|
c.isHandleMouseEvents());
|
|
|
|
assertEquals('Parent must remove and return child by index', a,
|
|
container.removeChildAt(0));
|
|
assertNull('Parent must no longer contain this child',
|
|
container.getChild('a'));
|
|
assertTrue('The removed control must handle its own mouse events',
|
|
a.isHandleMouseEvents());
|
|
}
|
|
|
|
function testRemoveHighlightedDisposedChild() {
|
|
goog.dom.removeChildren(containerElement);
|
|
container.decorate(containerElement);
|
|
|
|
var a = new goog.ui.Control('A');
|
|
container.addChild(a, true);
|
|
|
|
container.setHighlightedIndex(0);
|
|
a.dispose();
|
|
container.removeChild(a);
|
|
container.dispose();
|
|
}
|
|
|
|
|
|
/**
|
|
* Checks that getHighlighted() returns the expected value and checks
|
|
* that the child at this index is highlighted and other children are not.
|
|
* @param {string} explanation Message indicating what is expected.
|
|
* @param {number} index Expected return value of getHighlightedIndex().
|
|
*/
|
|
function assertHighlightedIndex(explanation, index) {
|
|
assertEquals(explanation, index, container.getHighlightedIndex());
|
|
for (var i = 0; i < container.getChildCount(); i++) {
|
|
if (i == index) {
|
|
assertTrue('Child at highlighted index should be highlighted',
|
|
container.getChildAt(i).isHighlighted());
|
|
} else {
|
|
assertFalse('Only child at highlighted index should be highlighted',
|
|
container.getChildAt(i).isHighlighted());
|
|
}
|
|
}
|
|
}
|
|
|
|
function testUpdateHighlightedIndex_updatesWhenChildrenAreAdded() {
|
|
goog.dom.removeChildren(containerElement);
|
|
container.decorate(containerElement);
|
|
|
|
var a = new goog.ui.Control('A');
|
|
var b = new goog.ui.Control('B');
|
|
var c = new goog.ui.Control('C');
|
|
|
|
container.addChild(a);
|
|
container.setHighlightedIndex(0);
|
|
assertHighlightedIndex('Highlighted index should match set value', 0);
|
|
|
|
// Add child before the highlighted one.
|
|
container.addChildAt(b, 0);
|
|
assertHighlightedIndex('Highlighted index should be increased', 1);
|
|
|
|
// Add child after the highlighted one.
|
|
container.addChildAt(c, 2);
|
|
assertHighlightedIndex('Highlighted index should not change', 1);
|
|
|
|
container.dispose();
|
|
}
|
|
|
|
function testUpdateHighlightedIndex_updatesWhenChildrenAreMoved() {
|
|
goog.dom.removeChildren(containerElement);
|
|
container.decorate(containerElement);
|
|
|
|
var a = new goog.ui.Control('A');
|
|
var b = new goog.ui.Control('B');
|
|
var c = new goog.ui.Control('C');
|
|
|
|
container.addChild(a);
|
|
container.addChild(b);
|
|
container.addChild(c);
|
|
|
|
// Highlight 'c' and swap 'a' and 'b'
|
|
// [a, b, c] -> [a, b, *c] -> [b, a, *c] (* indicates the highlighted child)
|
|
container.setHighlightedIndex(2);
|
|
container.addChildAt(a, 1, false);
|
|
assertHighlightedIndex('Highlighted index should not change', 2);
|
|
|
|
// Move the highlighted child 'c' from index 2 to index 1.
|
|
// [b, a, *c] -> [b, *c, a]
|
|
container.addChildAt(c, 1, false);
|
|
assertHighlightedIndex('Highlighted index must follow the moved child', 1);
|
|
|
|
// Take the element in front of the highlighted index and move it behind it.
|
|
// [b, *c, a] -> [*c, a, b]
|
|
container.addChildAt(b, 2, false);
|
|
assertHighlightedIndex('Highlighted index must be decreased', 0);
|
|
|
|
// And move the element back to the front.
|
|
// [*c, a, b] -> [b, *c, a]
|
|
container.addChildAt(b, 0, false);
|
|
assertHighlightedIndex('Highlighted index must be increased', 1);
|
|
|
|
container.dispose();
|
|
}
|
|
|
|
function testUpdateHighlightedIndex_notChangedOnNoOp() {
|
|
goog.dom.removeChildren(containerElement);
|
|
container.decorate(containerElement);
|
|
|
|
container.addChild(new goog.ui.Control('A'));
|
|
container.addChild(new goog.ui.Control('B'));
|
|
container.setHighlightedIndex(1);
|
|
|
|
// Re-add a child to its current position.
|
|
container.addChildAt(container.getChildAt(0), 0, false);
|
|
assertHighlightedIndex('Highlighted index must not change', 1);
|
|
|
|
container.dispose();
|
|
}
|
|
|
|
function testUpdateHighlightedIndex_notChangedWhenNoChildSelected() {
|
|
goog.dom.removeChildren(containerElement);
|
|
container.decorate(containerElement);
|
|
|
|
var a = new goog.ui.Control('A');
|
|
var b = new goog.ui.Control('B');
|
|
var c = new goog.ui.Control('C');
|
|
container.addChild(a);
|
|
container.addChild(b);
|
|
container.addChild(c);
|
|
|
|
// Move children around.
|
|
container.addChildAt(a, 2, false);
|
|
container.addChildAt(b, 1, false);
|
|
container.addChildAt(c, 2, false);
|
|
|
|
assertHighlightedIndex('Highlighted index must not change', -1);
|
|
|
|
container.dispose();
|
|
}
|
|
|
|
function testUpdateHighlightedIndex_indexStaysInBoundsWhenMovedToMaxIndex() {
|
|
goog.dom.removeChildren(containerElement);
|
|
container.decorate(containerElement);
|
|
|
|
var a = new goog.ui.Control('A');
|
|
var b = new goog.ui.Control('B');
|
|
container.addChild(a);
|
|
container.addChild(b);
|
|
|
|
// Move higlighted child to an index one behind last child.
|
|
container.setHighlightedIndex(0);
|
|
container.addChildAt(a, 2);
|
|
|
|
assertEquals('Child should be moved to index 1', a, container.getChildAt(1));
|
|
assertEquals('Child count should not change', 2, container.getChildCount());
|
|
assertHighlightedIndex('Highlighted index must point to new index', 1);
|
|
|
|
container.dispose();
|
|
}
|