4 * Copyright(c) 2006-2007, Ext JS, LLC.
6 * Originally Released Under LGPL - original licence link has changed is not relivant.
9 * <script type="text/javascript">
15 * These classes are derivatives of the similarly named classes in the YUI Library.
16 * The original license:
17 * Copyright (c) 2006, Yahoo! Inc. All rights reserved.
18 * Code licensed under the BSD License:
19 * http://developer.yahoo.net/yui/license.txt
24 var Event=Roo.EventManager;
28 * @class Roo.dd.DragDrop
29 * @extends Roo.util.Observable
30 * Defines the interface and base operation of items that that can be
31 * dragged or can be drop targets. It was designed to be extended, overriding
32 * the event handlers for startDrag, onDrag, onDragOver and onDragOut.
33 * Up to three html elements can be associated with a DragDrop instance:
35 * <li>linked element: the element that is passed into the constructor.
36 * This is the element which defines the boundaries for interaction with
37 * other DragDrop objects.</li>
38 * <li>handle element(s): The drag operation only occurs if the element that
39 * was clicked matches a handle element. By default this is the linked
40 * element, but there are times that you will want only a portion of the
41 * linked element to initiate the drag operation, and the setHandleElId()
42 * method provides a way to define this.</li>
43 * <li>drag element: this represents the element that would be moved along
44 * with the cursor during a drag operation. By default, this is the linked
45 * element itself as in {@link Roo.dd.DD}. setDragElId() lets you define
46 * a separate element that would be moved, as in {@link Roo.dd.DDProxy}.
49 * This class should not be instantiated until the onload event to ensure that
50 * the associated elements are available.
51 * The following would define a DragDrop obj that would interact with any
52 * other DragDrop obj in the "group1" group:
54 * dd = new Roo.dd.DragDrop("div1", "group1");
56 * Since none of the event handlers have been implemented, nothing would
57 * actually happen if you were to run the code above. Normally you would
58 * override this class or one of the default implementations, but you can
59 * also override the methods you want on an instance of the class...
61 * dd.onDragDrop = function(e, id) {
62 * alert("dd was dropped on " + id);
66 * @param {String} id of the element that is linked to this instance
67 * @param {String} sGroup the group of related DragDrop objects
68 * @param {object} config an object containing configurable attributes
69 * Valid properties for DragDrop:
70 * padding, isTarget, maintainOffset, primaryButtonOnly
72 Roo.dd.DragDrop = function(id, sGroup, config) {
74 this.init(id, sGroup, config);
79 Roo.extend(Roo.dd.DragDrop, Roo.util.Observable , {
82 * The id of the element associated with this object. This is what we
83 * refer to as the "linked element" because the size and position of
84 * this element is used to determine when the drag and drop objects have
92 * Configuration attributes passed into the constructor
99 * The id of the element that will be dragged. By default this is same
100 * as the linked element , but could be changed to another element. Ex:
109 * the id of the element that initiates the drag operation. By default
110 * this is the linked element, but could be changed to be a child of this
111 * element. This lets us do things like only starting the drag when the
112 * header element within the linked html element is clicked.
113 * @property handleElId
120 * An associative array of HTML tags that will be ignored if clicked.
121 * @property invalidHandleTypes
122 * @type {string: string}
124 invalidHandleTypes: null,
127 * An associative array of ids for elements that will be ignored if clicked
128 * @property invalidHandleIds
129 * @type {string: string}
131 invalidHandleIds: null,
134 * An indexted array of css class names for elements that will be ignored
136 * @property invalidHandleClasses
139 invalidHandleClasses: null,
142 * The linked element's absolute X position at the time the drag was
144 * @property startPageX
151 * The linked element's absolute X position at the time the drag was
153 * @property startPageY
160 * The group defines a logical collection of DragDrop objects that are
161 * related. Instances only get events when interacting with other
162 * DragDrop object in the same group. This lets us define multiple
163 * groups using a single DragDrop subclass if we want.
165 * @type {string: string}
170 * Individual drag/drop instances can be locked. This will prevent
171 * onmousedown start drag.
182 lock: function() { this.locked = true; },
185 * Unlock this instace
188 unlock: function() { this.locked = false; },
191 * By default, all insances can be a drop target. This can be disabled by
192 * setting isTarget to false.
199 * The padding configured for this drag and drop object for calculating
200 * the drop zone intersection with this object.
207 * Cached reference to the linked element
214 * Internal typeof flag
215 * @property __ygDragDrop
221 * Set to true when horizontal contraints are applied
222 * @property constrainX
229 * Set to true when vertical contraints are applied
230 * @property constrainY
237 * The left constraint
245 * The right constraint
262 * The down constraint
270 * Maintain offsets when we resetconstraints. Set to true when you want
271 * the position of the element relative to its parent to stay the same
272 * when the page changes
274 * @property maintainOffset
277 maintainOffset: false,
280 * Array of pixel locations the element will snap to if we specified a
281 * horizontal graduation/interval. This array is generated automatically
282 * when you define a tick interval.
289 * Array of pixel locations the element will snap to if we specified a
290 * vertical graduation/interval. This array is generated automatically
291 * when you define a tick interval.
298 * By default the drag and drop instance will only respond to the primary
299 * button click (left button for a right-handed mouse). Set to true to
300 * allow drag and drop to start with any mouse click that is propogated
302 * @property primaryButtonOnly
305 primaryButtonOnly: true,
308 * The availabe property is false until the linked dom element is accessible.
309 * @property available
315 * By default, drags can only be initiated if the mousedown occurs in the
316 * region the linked element is. This is done in part to work around a
317 * bug in some browsers that mis-report the mousedown if the previous
318 * mouseup happened outside of the window. This property is set to true
319 * if outer handles are defined.
321 * @property hasOuterHandles
325 hasOuterHandles: false,
328 * Code that executes immediately before the startDrag event
329 * @method b4StartDrag
332 b4StartDrag: function(x, y) { },
335 * Abstract method called after a drag/drop object is clicked
336 * and the drag or mousedown time thresholds have beeen met.
338 * @param {int} X click location
339 * @param {int} Y click location
341 startDrag: function(x, y) { /* override this */ },
344 * Code that executes immediately before the onDrag event
348 b4Drag: function(e) { },
351 * Abstract method called during the onMouseMove event while dragging an
354 * @param {Event} e the mousemove event
356 onDrag: function(e) { /* override this */ },
359 * Abstract method called when this element fist begins hovering over
360 * another DragDrop obj
361 * @method onDragEnter
362 * @param {Event} e the mousemove event
363 * @param {String|DragDrop[]} id In POINT mode, the element
364 * id this is hovering over. In INTERSECT mode, an array of one or more
365 * dragdrop items being hovered over.
367 onDragEnter: function(e, id) { /* override this */ },
370 * Code that executes immediately before the onDragOver event
374 b4DragOver: function(e) { },
377 * Abstract method called when this element is hovering over another
380 * @param {Event} e the mousemove event
381 * @param {String|DragDrop[]} id In POINT mode, the element
382 * id this is hovering over. In INTERSECT mode, an array of dd items
383 * being hovered over.
385 onDragOver: function(e, id) { /* override this */ },
388 * Code that executes immediately before the onDragOut event
392 b4DragOut: function(e) { },
395 * Abstract method called when we are no longer hovering over an element
397 * @param {Event} e the mousemove event
398 * @param {String|DragDrop[]} id In POINT mode, the element
399 * id this was hovering over. In INTERSECT mode, an array of dd items
400 * that the mouse is no longer over.
402 onDragOut: function(e, id) { /* override this */ },
405 * Code that executes immediately before the onDragDrop event
409 b4DragDrop: function(e) { },
412 * Abstract method called when this item is dropped on another DragDrop
415 * @param {Event} e the mouseup event
416 * @param {String|DragDrop[]} id In POINT mode, the element
417 * id this was dropped on. In INTERSECT mode, an array of dd items this
420 onDragDrop: function(e, id) { /* override this */ },
423 * Abstract method called when this item is dropped on an area with no
425 * @method onInvalidDrop
426 * @param {Event} e the mouseup event
428 onInvalidDrop: function(e) { /* override this */ },
431 * Code that executes immediately before the endDrag event
435 b4EndDrag: function(e) { },
438 * Fired when we are done dragging the object
440 * @param {Event} e the mouseup event
442 endDrag: function(e) { /* override this */ },
445 * Code executed immediately before the onMouseDown event
446 * @method b4MouseDown
447 * @param {Event} e the mousedown event
450 b4MouseDown: function(e) { },
453 * Event handler that fires when a drag/drop obj gets a mousedown
454 * @method onMouseDown
455 * @param {Event} e the mousedown event
457 onMouseDown: function(e) { /* override this */ },
460 * Event handler that fires when a drag/drop obj gets a mouseup
462 * @param {Event} e the mouseup event
464 onMouseUp: function(e) { /* override this */ },
467 * Override the onAvailable method to do what is needed after the initial
468 * position was determined.
469 * @method onAvailable
471 onAvailable: function () {
475 * Provides default constraint padding to "constrainTo" elements (defaults to {left: 0, right:0, top:0, bottom:0}).
478 defaultPadding : {left:0, right:0, top:0, bottom:0},
481 * Initializes the drag drop object's constraints to restrict movement to a certain element.
485 var dd = new Roo.dd.DDProxy("dragDiv1", "proxytest",
486 { dragElId: "existingProxyDiv" });
487 dd.startDrag = function(){
488 this.constrainTo("parent-id");
491 * Or you can initalize it using the {@link Roo.Element} object:
493 Roo.get("dragDiv1").initDDProxy("proxytest", {dragElId: "existingProxyDiv"}, {
494 startDrag : function(){
495 this.constrainTo("parent-id");
499 * @param {String/HTMLElement/Element} constrainTo The element to constrain to.
500 * @param {Object/Number} pad (optional) Pad provides a way to specify "padding" of the constraints,
501 * and can be either a number for symmetrical padding (4 would be equal to {left:4, right:4, top:4, bottom:4}) or
502 * an object containing the sides to pad. For example: {right:10, bottom:10}
503 * @param {Boolean} inContent (optional) Constrain the draggable in the content box of the element (inside padding and borders)
505 constrainTo : function(constrainTo, pad, inContent){
506 if(typeof pad == "number"){
507 pad = {left: pad, right:pad, top:pad, bottom:pad};
509 pad = pad || this.defaultPadding;
510 var b = Roo.get(this.getEl()).getBox();
511 var ce = Roo.get(constrainTo);
512 var s = ce.getScroll();
514 if(cd == document.body){
515 c = { x: s.left, y: s.top, width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
518 c = {x : xy[0]+s.left, y: xy[1]+s.top, width: cd.clientWidth, height: cd.clientHeight};
522 var topSpace = b.y - c.y;
523 var leftSpace = b.x - c.x;
525 this.resetConstraints();
526 this.setXConstraint(leftSpace - (pad.left||0), // left
527 c.width - leftSpace - b.width - (pad.right||0) //right
529 this.setYConstraint(topSpace - (pad.top||0), //top
530 c.height - topSpace - b.height - (pad.bottom||0) //bottom
535 * Returns a reference to the linked element
537 * @return {HTMLElement} the html element
541 this._domRef = Roo.getDom(this.id);
548 * Returns a reference to the actual element to drag. By default this is
549 * the same as the html element, but it can be assigned to another
550 * element. An example of this can be found in Roo.dd.DDProxy
552 * @return {HTMLElement} the html element
554 getDragEl: function() {
555 return Roo.getDom(this.dragElId);
559 * Sets up the DragDrop object. Must be called in the constructor of any
560 * Roo.dd.DragDrop subclass
562 * @param id the id of the linked element
563 * @param {String} sGroup the group of related items
564 * @param {object} config configuration attributes
566 init: function(id, sGroup, config) {
567 this.initTarget(id, sGroup, config);
568 Event.on(this.id, "mousedown", this.handleMouseDown, this);
569 // Event.on(this.id, "selectstart", Event.preventDefault);
573 * Initializes Targeting functionality only... the object does not
574 * get a mousedown handler.
576 * @param id the id of the linked element
577 * @param {String} sGroup the group of related items
578 * @param {object} config configuration attributes
580 initTarget: function(id, sGroup, config) {
582 // configuration attributes
583 this.config = config || {};
585 // create a local reference to the drag and drop manager
586 this.DDM = Roo.dd.DDM;
587 // initialize the groups array
590 // assume that we have an element reference instead of an id if the
591 // parameter is not a string
592 if (typeof id !== "string") {
599 // add to an interaction group
600 this.addToGroup((sGroup) ? sGroup : "default");
602 // We don't want to register this as the handle with the manager
603 // so we just set the id rather than calling the setter.
604 this.handleElId = id;
606 // the linked element is the element that gets dragged by default
607 this.setDragElId(id);
609 // by default, clicked anchors will not start drag operations.
610 this.invalidHandleTypes = { A: "A" };
611 this.invalidHandleIds = {};
612 this.invalidHandleClasses = [];
616 this.handleOnAvailable();
620 * Applies the configuration parameters that were passed into the constructor.
621 * This is supposed to happen at each level through the inheritance chain. So
622 * a DDProxy implentation will execute apply config on DDProxy, DD, and
623 * DragDrop in order to get all of the parameters that are available in
625 * @method applyConfig
627 applyConfig: function() {
629 // configurable properties:
630 // padding, isTarget, maintainOffset, primaryButtonOnly
631 this.padding = this.config.padding || [0, 0, 0, 0];
632 this.isTarget = (this.config.isTarget !== false);
633 this.maintainOffset = (this.config.maintainOffset);
634 this.primaryButtonOnly = (this.config.primaryButtonOnly !== false);
639 * Executed when the linked element is available
640 * @method handleOnAvailable
643 handleOnAvailable: function() {
644 this.available = true;
645 this.resetConstraints();
650 * Configures the padding for the target zone in px. Effectively expands
651 * (or reduces) the virtual object size for targeting calculations.
652 * Supports css-style shorthand; if only one parameter is passed, all sides
653 * will have that padding, and if only two are passed, the top and bottom
654 * will have the first param, the left and right the second.
656 * @param {int} iTop Top pad
657 * @param {int} iRight Right pad
658 * @param {int} iBot Bot pad
659 * @param {int} iLeft Left pad
661 setPadding: function(iTop, iRight, iBot, iLeft) {
662 // this.padding = [iLeft, iRight, iTop, iBot];
663 if (!iRight && 0 !== iRight) {
664 this.padding = [iTop, iTop, iTop, iTop];
665 } else if (!iBot && 0 !== iBot) {
666 this.padding = [iTop, iRight, iTop, iRight];
668 this.padding = [iTop, iRight, iBot, iLeft];
673 * Stores the initial placement of the linked element.
674 * @method setInitialPosition
675 * @param {int} diffX the X offset, default 0
676 * @param {int} diffY the Y offset, default 0
678 setInitPosition: function(diffX, diffY) {
679 var el = this.getEl();
681 if (!this.DDM.verifyEl(el)) {
688 var p = Dom.getXY( el );
690 this.initPageX = p[0] - dx;
691 this.initPageY = p[1] - dy;
693 this.lastPageX = p[0];
694 this.lastPageY = p[1];
697 this.setStartPosition(p);
701 * Sets the start position of the element. This is set when the obj
702 * is initialized, the reset when a drag is started.
703 * @method setStartPosition
704 * @param pos current position (from previous lookup)
707 setStartPosition: function(pos) {
708 var p = pos || Dom.getXY( this.getEl() );
709 this.deltaSetXY = null;
711 this.startPageX = p[0];
712 this.startPageY = p[1];
716 * Add this instance to a group of related drag/drop objects. All
717 * instances belong to at least one group, and can belong to as many
720 * @param sGroup {string} the name of the group
722 addToGroup: function(sGroup) {
723 this.groups[sGroup] = true;
724 this.DDM.regDragDrop(this, sGroup);
728 * Remove's this instance from the supplied interaction group
729 * @method removeFromGroup
730 * @param {string} sGroup The group to drop
732 removeFromGroup: function(sGroup) {
733 if (this.groups[sGroup]) {
734 delete this.groups[sGroup];
737 this.DDM.removeDDFromGroup(this, sGroup);
741 * Allows you to specify that an element other than the linked element
742 * will be moved with the cursor during a drag
743 * @method setDragElId
744 * @param id {string} the id of the element that will be used to initiate the drag
746 setDragElId: function(id) {
751 * Allows you to specify a child of the linked element that should be
752 * used to initiate the drag operation. An example of this would be if
753 * you have a content div with text and links. Clicking anywhere in the
754 * content area would normally start the drag operation. Use this method
755 * to specify that an element inside of the content div is the element
756 * that starts the drag operation.
757 * @method setHandleElId
758 * @param id {string} the id of the element that will be used to
761 setHandleElId: function(id) {
762 if (typeof id !== "string") {
765 this.handleElId = id;
766 this.DDM.regHandle(this.id, id);
770 * Allows you to set an element outside of the linked element as a drag
772 * @method setOuterHandleElId
773 * @param id the id of the element that will be used to initiate the drag
775 setOuterHandleElId: function(id) {
776 if (typeof id !== "string") {
779 Event.on(id, "mousedown",
780 this.handleMouseDown, this);
781 this.setHandleElId(id);
783 this.hasOuterHandles = true;
787 * Remove all drag and drop hooks for this element
791 Event.un(this.id, "mousedown",
792 this.handleMouseDown);
794 this.DDM._remove(this);
797 destroy : function(){
802 * Returns true if this instance is locked, or the drag drop mgr is locked
803 * (meaning that all drag/drop is disabled on the page.)
805 * @return {boolean} true if this obj or all drag/drop is locked, else
808 isLocked: function() {
809 return (this.DDM.isLocked() || this.locked);
813 * Fired when this object is clicked
814 * @method handleMouseDown
816 * @param {Roo.dd.DragDrop} oDD the clicked dd object (this dd obj)
819 handleMouseDown: function(e, oDD){
820 if (this.primaryButtonOnly && e.button != 0) {
824 if (this.isLocked()) {
828 this.DDM.refreshCache(this.groups);
830 var pt = new Roo.lib.Point(Roo.lib.Event.getPageX(e), Roo.lib.Event.getPageY(e));
831 if (!this.hasOuterHandles && !this.DDM.isOverTarget(pt, this) ) {
833 if (this.clickValidator(e)) {
835 // set the initial element position
836 this.setStartPosition();
842 this.DDM.handleMouseDown(e, this);
844 this.DDM.stopEvent(e);
852 clickValidator: function(e) {
853 var target = e.getTarget();
854 return ( this.isValidHandleChild(target) &&
855 (this.id == this.handleElId ||
856 this.DDM.handleWasClicked(target, this.id)) );
860 * Allows you to specify a tag name that should not start a drag operation
861 * when clicked. This is designed to facilitate embedding links within a
862 * drag handle that do something other than start the drag.
863 * @method addInvalidHandleType
864 * @param {string} tagName the type of element to exclude
866 addInvalidHandleType: function(tagName) {
867 var type = tagName.toUpperCase();
868 this.invalidHandleTypes[type] = type;
872 * Lets you to specify an element id for a child of a drag handle
873 * that should not initiate a drag
874 * @method addInvalidHandleId
875 * @param {string} id the element id of the element you wish to ignore
877 addInvalidHandleId: function(id) {
878 if (typeof id !== "string") {
881 this.invalidHandleIds[id] = id;
885 * Lets you specify a css class of elements that will not initiate a drag
886 * @method addInvalidHandleClass
887 * @param {string} cssClass the class of the elements you wish to ignore
889 addInvalidHandleClass: function(cssClass) {
890 this.invalidHandleClasses.push(cssClass);
894 * Unsets an excluded tag name set by addInvalidHandleType
895 * @method removeInvalidHandleType
896 * @param {string} tagName the type of element to unexclude
898 removeInvalidHandleType: function(tagName) {
899 var type = tagName.toUpperCase();
900 // this.invalidHandleTypes[type] = null;
901 delete this.invalidHandleTypes[type];
905 * Unsets an invalid handle id
906 * @method removeInvalidHandleId
907 * @param {string} id the id of the element to re-enable
909 removeInvalidHandleId: function(id) {
910 if (typeof id !== "string") {
913 delete this.invalidHandleIds[id];
917 * Unsets an invalid css class
918 * @method removeInvalidHandleClass
919 * @param {string} cssClass the class of the element(s) you wish to
922 removeInvalidHandleClass: function(cssClass) {
923 for (var i=0, len=this.invalidHandleClasses.length; i<len; ++i) {
924 if (this.invalidHandleClasses[i] == cssClass) {
925 delete this.invalidHandleClasses[i];
931 * Checks the tag exclusion list to see if this click should be ignored
932 * @method isValidHandleChild
933 * @param {HTMLElement} node the HTMLElement to evaluate
934 * @return {boolean} true if this is a valid tag type, false if not
936 isValidHandleChild: function(node) {
939 // var n = (node.nodeName == "#text") ? node.parentNode : node;
942 nodeName = node.nodeName.toUpperCase();
944 nodeName = node.nodeName;
946 valid = valid && !this.invalidHandleTypes[nodeName];
947 valid = valid && !this.invalidHandleIds[node.id];
949 for (var i=0, len=this.invalidHandleClasses.length; valid && i<len; ++i) {
950 valid = !Dom.hasClass(node, this.invalidHandleClasses[i]);
959 * Create the array of horizontal tick marks if an interval was specified
960 * in setXConstraint().
964 setXTicks: function(iStartX, iTickSize) {
966 this.xTickSize = iTickSize;
970 for (var i = this.initPageX; i >= this.minX; i = i - iTickSize) {
972 this.xTicks[this.xTicks.length] = i;
977 for (i = this.initPageX; i <= this.maxX; i = i + iTickSize) {
979 this.xTicks[this.xTicks.length] = i;
984 this.xTicks.sort(this.DDM.numericSort) ;
988 * Create the array of vertical tick marks if an interval was specified in
993 setYTicks: function(iStartY, iTickSize) {
995 this.yTickSize = iTickSize;
999 for (var i = this.initPageY; i >= this.minY; i = i - iTickSize) {
1001 this.yTicks[this.yTicks.length] = i;
1006 for (i = this.initPageY; i <= this.maxY; i = i + iTickSize) {
1008 this.yTicks[this.yTicks.length] = i;
1013 this.yTicks.sort(this.DDM.numericSort) ;
1017 * By default, the element can be dragged any place on the screen. Use
1018 * this method to limit the horizontal travel of the element. Pass in
1019 * 0,0 for the parameters if you want to lock the drag to the y axis.
1020 * @method setXConstraint
1021 * @param {int} iLeft the number of pixels the element can move to the left
1022 * @param {int} iRight the number of pixels the element can move to the
1024 * @param {int} iTickSize optional parameter for specifying that the
1026 * should move iTickSize pixels at a time.
1028 setXConstraint: function(iLeft, iRight, iTickSize) {
1029 this.leftConstraint = iLeft;
1030 this.rightConstraint = iRight;
1032 this.minX = this.initPageX - iLeft;
1033 this.maxX = this.initPageX + iRight;
1034 if (iTickSize) { this.setXTicks(this.initPageX, iTickSize); }
1036 this.constrainX = true;
1040 * Clears any constraints applied to this instance. Also clears ticks
1041 * since they can't exist independent of a constraint at this time.
1042 * @method clearConstraints
1044 clearConstraints: function() {
1045 this.constrainX = false;
1046 this.constrainY = false;
1051 * Clears any tick interval defined for this instance
1052 * @method clearTicks
1054 clearTicks: function() {
1062 * By default, the element can be dragged any place on the screen. Set
1063 * this to limit the vertical travel of the element. Pass in 0,0 for the
1064 * parameters if you want to lock the drag to the x axis.
1065 * @method setYConstraint
1066 * @param {int} iUp the number of pixels the element can move up
1067 * @param {int} iDown the number of pixels the element can move down
1068 * @param {int} iTickSize optional parameter for specifying that the
1069 * element should move iTickSize pixels at a time.
1071 setYConstraint: function(iUp, iDown, iTickSize) {
1072 this.topConstraint = iUp;
1073 this.bottomConstraint = iDown;
1075 this.minY = this.initPageY - iUp;
1076 this.maxY = this.initPageY + iDown;
1077 if (iTickSize) { this.setYTicks(this.initPageY, iTickSize); }
1079 this.constrainY = true;
1084 * resetConstraints must be called if you manually reposition a dd element.
1085 * @method resetConstraints
1086 * @param {boolean} maintainOffset
1088 resetConstraints: function() {
1091 // Maintain offsets if necessary
1092 if (this.initPageX || this.initPageX === 0) {
1093 // figure out how much this thing has moved
1094 var dx = (this.maintainOffset) ? this.lastPageX - this.initPageX : 0;
1095 var dy = (this.maintainOffset) ? this.lastPageY - this.initPageY : 0;
1097 this.setInitPosition(dx, dy);
1099 // This is the first time we have detected the element's position
1101 this.setInitPosition();
1104 if (this.constrainX) {
1105 this.setXConstraint( this.leftConstraint,
1106 this.rightConstraint,
1110 if (this.constrainY) {
1111 this.setYConstraint( this.topConstraint,
1112 this.bottomConstraint,
1118 * Normally the drag element is moved pixel by pixel, but we can specify
1119 * that it move a number of pixels at a time. This method resolves the
1120 * location when we have it set up like this.
1122 * @param {int} val where we want to place the object
1123 * @param {int[]} tickArray sorted array of valid points
1124 * @return {int} the closest tick
1127 getTick: function(val, tickArray) {
1130 // If tick interval is not defined, it is effectively 1 pixel,
1131 // so we return the value passed to us.
1133 } else if (tickArray[0] >= val) {
1134 // The value is lower than the first tick, so we return the first
1136 return tickArray[0];
1138 for (var i=0, len=tickArray.length; i<len; ++i) {
1140 if (tickArray[next] && tickArray[next] >= val) {
1141 var diff1 = val - tickArray[i];
1142 var diff2 = tickArray[next] - val;
1143 return (diff2 > diff1) ? tickArray[i] : tickArray[next];
1147 // The value is larger than the last tick, so we return the last
1149 return tickArray[tickArray.length - 1];
1156 * @return {string} string representation of the dd obj
1158 toString: function() {
1159 return ("DragDrop " + this.id);
1167 * Ext JS Library 1.1.1
1168 * Copyright(c) 2006-2007, Ext JS, LLC.
1170 * Originally Released Under LGPL - original licence link has changed is not relivant.
1173 * <script type="text/javascript">
1178 * The drag and drop utility provides a framework for building drag and drop
1179 * applications. In addition to enabling drag and drop for specific elements,
1180 * the drag and drop elements are tracked by the manager class, and the
1181 * interactions between the various elements are tracked during the drag and
1182 * the implementing code is notified about these important moments.
1185 // Only load the library once. Rewriting the manager class would orphan
1186 // existing drag and drop instances.
1187 if (!Roo.dd.DragDropMgr) {
1190 * @class Roo.dd.DragDropMgr
1191 * DragDropMgr is a singleton that tracks the element interaction for
1192 * all DragDrop items in the window. Generally, you will not call
1193 * this class directly, but it does have helper methods that could
1194 * be useful in your DragDrop implementations.
1197 Roo.dd.DragDropMgr = function() {
1199 var Event = Roo.EventManager;
1204 * Two dimensional Array of registered DragDrop objects. The first
1205 * dimension is the DragDrop item group, the second the DragDrop
1208 * @type {string: string}
1215 * Array of element ids defined as drag handles. Used to determine
1216 * if the element that generated the mousedown event is actually the
1217 * handle and not the html element itself.
1218 * @property handleIds
1219 * @type {string: string}
1226 * the DragDrop object that is currently being dragged
1227 * @property dragCurrent
1235 * the DragDrop object(s) that are being hovered over
1236 * @property dragOvers
1244 * the X distance between the cursor and the object being dragged
1253 * the Y distance between the cursor and the object being dragged
1262 * Flag to determine if we should prevent the default behavior of the
1263 * events we define. By default this is true, but this can be set to
1264 * false if you need the default behavior (not recommended)
1265 * @property preventDefault
1269 preventDefault: true,
1272 * Flag to determine if we should stop the propagation of the events
1273 * we generate. This is true by default but you may want to set it to
1274 * false if the html element contains other features that require the
1276 * @property stopPropagation
1280 stopPropagation: true,
1283 * Internal flag that is set to true when drag and drop has been
1285 * @property initialized
1292 * All drag and drop can be disabled.
1300 * Called the first time an element is registered.
1306 this.initialized = true;
1310 * In point mode, drag and drop interaction is defined by the
1311 * location of the cursor during the drag/drop
1319 * In intersect mode, drag and drop interactio nis defined by the
1320 * overlap of two or more drag and drop objects.
1321 * @property INTERSECT
1328 * The current drag and drop mode. Default: POINT
1336 * Runs method on all drag and drop objects
1337 * @method _execOnAll
1341 _execOnAll: function(sMethod, args) {
1342 for (var i in this.ids) {
1343 for (var j in this.ids[i]) {
1344 var oDD = this.ids[i][j];
1345 if (! this.isTypeOfDD(oDD)) {
1348 oDD[sMethod].apply(oDD, args);
1354 * Drag and drop initialization. Sets up the global event handlers
1359 _onLoad: function() {
1364 Event.on(document, "mouseup", this.handleMouseUp, this, true);
1365 Event.on(document, "mousemove", this.handleMouseMove, this, true);
1366 Event.on(window, "unload", this._onUnload, this, true);
1367 Event.on(window, "resize", this._onResize, this, true);
1368 // Event.on(window, "mouseout", this._test);
1373 * Reset constraints on all drag and drop objs
1378 _onResize: function(e) {
1379 this._execOnAll("resetConstraints", []);
1383 * Lock all drag and drop functionality
1387 lock: function() { this.locked = true; },
1390 * Unlock all drag and drop functionality
1394 unlock: function() { this.locked = false; },
1397 * Is drag and drop locked?
1399 * @return {boolean} True if drag and drop is locked, false otherwise.
1402 isLocked: function() { return this.locked; },
1405 * Location cache that is set for all drag drop objects when a drag is
1406 * initiated, cleared when the drag is finished.
1407 * @property locationCache
1414 * Set useCache to false if you want to force object the lookup of each
1415 * drag and drop linked element constantly during a drag.
1416 * @property useCache
1423 * The number of pixels that the mouse needs to move after the
1424 * mousedown before the drag is initiated. Default=3;
1425 * @property clickPixelThresh
1429 clickPixelThresh: 3,
1432 * The number of milliseconds after the mousedown event to initiate the
1433 * drag if we don't get a mouseup event. Default=1000
1434 * @property clickTimeThresh
1438 clickTimeThresh: 350,
1441 * Flag that indicates that either the drag pixel threshold or the
1442 * mousdown time threshold has been met
1443 * @property dragThreshMet
1448 dragThreshMet: false,
1451 * Timeout used for the click time threshold
1452 * @property clickTimeout
1460 * The X position of the mousedown event stored for later use when a
1461 * drag threshold is met.
1470 * The Y position of the mousedown event stored for later use when a
1471 * drag threshold is met.
1480 * Each DragDrop instance must be registered with the DragDropMgr.
1481 * This is executed in DragDrop.init()
1482 * @method regDragDrop
1483 * @param {DragDrop} oDD the DragDrop object to register
1484 * @param {String} sGroup the name of the group this element belongs to
1487 regDragDrop: function(oDD, sGroup) {
1488 if (!this.initialized) { this.init(); }
1490 if (!this.ids[sGroup]) {
1491 this.ids[sGroup] = {};
1493 this.ids[sGroup][oDD.id] = oDD;
1497 * Removes the supplied dd instance from the supplied group. Executed
1498 * by DragDrop.removeFromGroup, so don't call this function directly.
1499 * @method removeDDFromGroup
1503 removeDDFromGroup: function(oDD, sGroup) {
1504 if (!this.ids[sGroup]) {
1505 this.ids[sGroup] = {};
1508 var obj = this.ids[sGroup];
1509 if (obj && obj[oDD.id]) {
1515 * Unregisters a drag and drop item. This is executed in
1516 * DragDrop.unreg, use that method instead of calling this directly.
1521 _remove: function(oDD) {
1522 for (var g in oDD.groups) {
1523 if (g && this.ids[g][oDD.id]) {
1524 delete this.ids[g][oDD.id];
1527 delete this.handleIds[oDD.id];
1531 * Each DragDrop handle element must be registered. This is done
1532 * automatically when executing DragDrop.setHandleElId()
1534 * @param {String} sDDId the DragDrop id this element is a handle for
1535 * @param {String} sHandleId the id of the element that is the drag
1539 regHandle: function(sDDId, sHandleId) {
1540 if (!this.handleIds[sDDId]) {
1541 this.handleIds[sDDId] = {};
1543 this.handleIds[sDDId][sHandleId] = sHandleId;
1547 * Utility function to determine if a given element has been
1548 * registered as a drag drop item.
1549 * @method isDragDrop
1550 * @param {String} id the element id to check
1551 * @return {boolean} true if this element is a DragDrop item,
1555 isDragDrop: function(id) {
1556 return ( this.getDDById(id) ) ? true : false;
1560 * Returns the drag and drop instances that are in all groups the
1561 * passed in instance belongs to.
1562 * @method getRelated
1563 * @param {DragDrop} p_oDD the obj to get related data for
1564 * @param {boolean} bTargetsOnly if true, only return targetable objs
1565 * @return {DragDrop[]} the related instances
1568 getRelated: function(p_oDD, bTargetsOnly) {
1570 for (var i in p_oDD.groups) {
1571 for (j in this.ids[i]) {
1572 var dd = this.ids[i][j];
1573 if (! this.isTypeOfDD(dd)) {
1576 if (!bTargetsOnly || dd.isTarget) {
1577 oDDs[oDDs.length] = dd;
1586 * Returns true if the specified dd target is a legal target for
1587 * the specifice drag obj
1588 * @method isLegalTarget
1589 * @param {DragDrop} the drag obj
1590 * @param {DragDrop} the target
1591 * @return {boolean} true if the target is a legal target for the
1595 isLegalTarget: function (oDD, oTargetDD) {
1596 var targets = this.getRelated(oDD, true);
1597 for (var i=0, len=targets.length;i<len;++i) {
1598 if (targets[i].id == oTargetDD.id) {
1607 * My goal is to be able to transparently determine if an object is
1608 * typeof DragDrop, and the exact subclass of DragDrop. typeof
1609 * returns "object", oDD.constructor.toString() always returns
1610 * "DragDrop" and not the name of the subclass. So for now it just
1611 * evaluates a well-known variable in DragDrop.
1612 * @method isTypeOfDD
1613 * @param {Object} the object to evaluate
1614 * @return {boolean} true if typeof oDD = DragDrop
1617 isTypeOfDD: function (oDD) {
1618 return (oDD && oDD.__ygDragDrop);
1622 * Utility function to determine if a given element has been
1623 * registered as a drag drop handle for the given Drag Drop object.
1625 * @param {String} id the element id to check
1626 * @return {boolean} true if this element is a DragDrop handle, false
1630 isHandle: function(sDDId, sHandleId) {
1631 return ( this.handleIds[sDDId] &&
1632 this.handleIds[sDDId][sHandleId] );
1636 * Returns the DragDrop instance for a given id
1638 * @param {String} id the id of the DragDrop object
1639 * @return {DragDrop} the drag drop object, null if it is not found
1642 getDDById: function(id) {
1643 for (var i in this.ids) {
1644 if (this.ids[i][id]) {
1645 return this.ids[i][id];
1652 * Fired after a registered DragDrop object gets the mousedown event.
1653 * Sets up the events required to track the object being dragged
1654 * @method handleMouseDown
1655 * @param {Event} e the event
1656 * @param oDD the DragDrop object being dragged
1660 handleMouseDown: function(e, oDD) {
1662 Roo.QuickTips.disable();
1664 this.currentTarget = e.getTarget();
1666 this.dragCurrent = oDD;
1668 var el = oDD.getEl();
1670 // track start position
1671 this.startX = e.getPageX();
1672 this.startY = e.getPageY();
1674 this.deltaX = this.startX - el.offsetLeft;
1675 this.deltaY = this.startY - el.offsetTop;
1677 this.dragThreshMet = false;
1679 this.clickTimeout = setTimeout(
1681 var DDM = Roo.dd.DDM;
1682 DDM.startDrag(DDM.startX, DDM.startY);
1684 this.clickTimeThresh );
1688 * Fired when either the drag pixel threshol or the mousedown hold
1689 * time threshold has been met.
1691 * @param x {int} the X position of the original mousedown
1692 * @param y {int} the Y position of the original mousedown
1695 startDrag: function(x, y) {
1696 clearTimeout(this.clickTimeout);
1697 if (this.dragCurrent) {
1698 this.dragCurrent.b4StartDrag(x, y);
1699 this.dragCurrent.startDrag(x, y);
1701 this.dragThreshMet = true;
1705 * Internal function to handle the mouseup event. Will be invoked
1706 * from the context of the document.
1707 * @method handleMouseUp
1708 * @param {Event} e the event
1712 handleMouseUp: function(e) {
1715 Roo.QuickTips.enable();
1717 if (! this.dragCurrent) {
1721 clearTimeout(this.clickTimeout);
1723 if (this.dragThreshMet) {
1724 this.fireEvents(e, true);
1734 * Utility to stop event propagation and event default, if these
1735 * features are turned on.
1737 * @param {Event} e the event as returned by this.getEvent()
1740 stopEvent: function(e){
1741 if(this.stopPropagation) {
1742 e.stopPropagation();
1745 if (this.preventDefault) {
1751 * Internal function to clean up event handlers after the drag
1752 * operation is complete
1754 * @param {Event} e the event
1758 stopDrag: function(e) {
1759 // Fire the drag end event for the item that was dragged
1760 if (this.dragCurrent) {
1761 if (this.dragThreshMet) {
1762 this.dragCurrent.b4EndDrag(e);
1763 this.dragCurrent.endDrag(e);
1766 this.dragCurrent.onMouseUp(e);
1769 this.dragCurrent = null;
1770 this.dragOvers = {};
1774 * Internal function to handle the mousemove event. Will be invoked
1775 * from the context of the html element.
1777 * @TODO figure out what we can do about mouse events lost when the
1778 * user drags objects beyond the window boundary. Currently we can
1779 * detect this in internet explorer by verifying that the mouse is
1780 * down during the mousemove event. Firefox doesn't give us the
1781 * button state on the mousemove event.
1782 * @method handleMouseMove
1783 * @param {Event} e the event
1787 handleMouseMove: function(e) {
1788 if (! this.dragCurrent) {
1792 // var button = e.which || e.button;
1794 // check for IE mouseup outside of page boundary
1795 if (Roo.isIE && (e.button !== 0 && e.button !== 1 && e.button !== 2)) {
1797 return this.handleMouseUp(e);
1800 if (!this.dragThreshMet) {
1801 var diffX = Math.abs(this.startX - e.getPageX());
1802 var diffY = Math.abs(this.startY - e.getPageY());
1803 if (diffX > this.clickPixelThresh ||
1804 diffY > this.clickPixelThresh) {
1805 this.startDrag(this.startX, this.startY);
1809 if (this.dragThreshMet) {
1810 this.dragCurrent.b4Drag(e);
1811 this.dragCurrent.onDrag(e);
1812 if(!this.dragCurrent.moveOnly){
1813 this.fireEvents(e, false);
1823 * Iterates over all of the DragDrop elements to find ones we are
1824 * hovering over or dropping on
1825 * @method fireEvents
1826 * @param {Event} e the event
1827 * @param {boolean} isDrop is this a drop op or a mouseover op?
1831 fireEvents: function(e, isDrop) {
1832 var dc = this.dragCurrent;
1834 // If the user did the mouse up outside of the window, we could
1835 // get here even though we have ended the drag.
1836 if (!dc || dc.isLocked()) {
1840 var pt = e.getPoint();
1842 // cache the previous dragOver array
1850 // Check to see if the object(s) we were hovering over is no longer
1851 // being hovered over so we can fire the onDragOut event
1852 for (var i in this.dragOvers) {
1854 var ddo = this.dragOvers[i];
1856 if (! this.isTypeOfDD(ddo)) {
1860 if (! this.isOverTarget(pt, ddo, this.mode)) {
1861 outEvts.push( ddo );
1865 delete this.dragOvers[i];
1868 for (var sGroup in dc.groups) {
1870 if ("string" != typeof sGroup) {
1874 for (i in this.ids[sGroup]) {
1875 var oDD = this.ids[sGroup][i];
1876 if (! this.isTypeOfDD(oDD)) {
1880 if (oDD.isTarget && !oDD.isLocked() && oDD != dc) {
1881 if (this.isOverTarget(pt, oDD, this.mode)) {
1882 // look for drop interactions
1884 dropEvts.push( oDD );
1885 // look for drag enter and drag over interactions
1888 // initial drag over: dragEnter fires
1889 if (!oldOvers[oDD.id]) {
1890 enterEvts.push( oDD );
1891 // subsequent drag overs: dragOver fires
1893 overEvts.push( oDD );
1896 this.dragOvers[oDD.id] = oDD;
1904 if (outEvts.length) {
1905 dc.b4DragOut(e, outEvts);
1906 dc.onDragOut(e, outEvts);
1909 if (enterEvts.length) {
1910 dc.onDragEnter(e, enterEvts);
1913 if (overEvts.length) {
1914 dc.b4DragOver(e, overEvts);
1915 dc.onDragOver(e, overEvts);
1918 if (dropEvts.length) {
1919 dc.b4DragDrop(e, dropEvts);
1920 dc.onDragDrop(e, dropEvts);
1924 // fire dragout events
1926 for (i=0, len=outEvts.length; i<len; ++i) {
1927 dc.b4DragOut(e, outEvts[i].id);
1928 dc.onDragOut(e, outEvts[i].id);
1931 // fire enter events
1932 for (i=0,len=enterEvts.length; i<len; ++i) {
1933 // dc.b4DragEnter(e, oDD.id);
1934 dc.onDragEnter(e, enterEvts[i].id);
1938 for (i=0,len=overEvts.length; i<len; ++i) {
1939 dc.b4DragOver(e, overEvts[i].id);
1940 dc.onDragOver(e, overEvts[i].id);
1944 for (i=0, len=dropEvts.length; i<len; ++i) {
1945 dc.b4DragDrop(e, dropEvts[i].id);
1946 dc.onDragDrop(e, dropEvts[i].id);
1951 // notify about a drop that did not find a target
1952 if (isDrop && !dropEvts.length) {
1953 dc.onInvalidDrop(e);
1959 * Helper function for getting the best match from the list of drag
1960 * and drop objects returned by the drag and drop events when we are
1961 * in INTERSECT mode. It returns either the first object that the
1962 * cursor is over, or the object that has the greatest overlap with
1963 * the dragged element.
1964 * @method getBestMatch
1965 * @param {DragDrop[]} dds The array of drag and drop objects
1967 * @return {DragDrop} The best single match
1970 getBestMatch: function(dds) {
1972 // Return null if the input is not what we expect
1973 //if (!dds || !dds.length || dds.length == 0) {
1975 // If there is only one item, it wins
1976 //} else if (dds.length == 1) {
1978 var len = dds.length;
1983 // Loop through the targeted items
1984 for (var i=0; i<len; ++i) {
1986 // If the cursor is over the object, it wins. If the
1987 // cursor is over multiple matches, the first one we come
1989 if (dd.cursorIsOver) {
1992 // Otherwise the object with the most overlap wins
1995 winner.overlap.getArea() < dd.overlap.getArea()) {
2006 * Refreshes the cache of the top-left and bottom-right points of the
2007 * drag and drop objects in the specified group(s). This is in the
2008 * format that is stored in the drag and drop instance, so typical
2011 * Roo.dd.DragDropMgr.refreshCache(ddinstance.groups);
2015 * Roo.dd.DragDropMgr.refreshCache({group1:true, group2:true});
2017 * @TODO this really should be an indexed array. Alternatively this
2018 * method could accept both.
2019 * @method refreshCache
2020 * @param {Object} groups an associative array of groups to refresh
2023 refreshCache: function(groups) {
2024 for (var sGroup in groups) {
2025 if ("string" != typeof sGroup) {
2028 for (var i in this.ids[sGroup]) {
2029 var oDD = this.ids[sGroup][i];
2031 if (this.isTypeOfDD(oDD)) {
2032 // if (this.isTypeOfDD(oDD) && oDD.isTarget) {
2033 var loc = this.getLocation(oDD);
2035 this.locationCache[oDD.id] = loc;
2037 delete this.locationCache[oDD.id];
2038 // this will unregister the drag and drop object if
2039 // the element is not in a usable state
2048 * This checks to make sure an element exists and is in the DOM. The
2049 * main purpose is to handle cases where innerHTML is used to remove
2050 * drag and drop objects from the DOM. IE provides an 'unspecified
2051 * error' when trying to access the offsetParent of such an element
2053 * @param {HTMLElement} el the element to check
2054 * @return {boolean} true if the element looks usable
2057 verifyEl: function(el) {
2062 parent = el.offsetParent;
2065 parent = el.offsetParent;
2076 * Returns a Region object containing the drag and drop element's position
2077 * and size, including the padding configured for it
2078 * @method getLocation
2079 * @param {DragDrop} oDD the drag and drop object to get the
2081 * @return {Roo.lib.Region} a Region object representing the total area
2082 * the element occupies, including any padding
2083 * the instance is configured for.
2086 getLocation: function(oDD) {
2087 if (! this.isTypeOfDD(oDD)) {
2091 var el = oDD.getEl(), pos, x1, x2, y1, y2, t, r, b, l;
2094 pos= Roo.lib.Dom.getXY(el);
2102 x2 = x1 + el.offsetWidth;
2104 y2 = y1 + el.offsetHeight;
2106 t = y1 - oDD.padding[0];
2107 r = x2 + oDD.padding[1];
2108 b = y2 + oDD.padding[2];
2109 l = x1 - oDD.padding[3];
2111 return new Roo.lib.Region( t, r, b, l );
2115 * Checks the cursor location to see if it over the target
2116 * @method isOverTarget
2117 * @param {Roo.lib.Point} pt The point to evaluate
2118 * @param {DragDrop} oTarget the DragDrop object we are inspecting
2119 * @return {boolean} true if the mouse is over the target
2123 isOverTarget: function(pt, oTarget, intersect) {
2124 // use cache if available
2125 var loc = this.locationCache[oTarget.id];
2126 if (!loc || !this.useCache) {
2127 loc = this.getLocation(oTarget);
2128 this.locationCache[oTarget.id] = loc;
2136 oTarget.cursorIsOver = loc.contains( pt );
2138 // DragDrop is using this as a sanity check for the initial mousedown
2139 // in this case we are done. In POINT mode, if the drag obj has no
2140 // contraints, we are also done. Otherwise we need to evaluate the
2141 // location of the target as related to the actual location of the
2143 var dc = this.dragCurrent;
2144 if (!dc || !dc.getTargetCoord ||
2145 (!intersect && !dc.constrainX && !dc.constrainY)) {
2146 return oTarget.cursorIsOver;
2149 oTarget.overlap = null;
2151 // Get the current location of the drag element, this is the
2152 // location of the mouse event less the delta that represents
2153 // where the original mousedown happened on the element. We
2154 // need to consider constraints and ticks as well.
2155 var pos = dc.getTargetCoord(pt.x, pt.y);
2157 var el = dc.getDragEl();
2158 var curRegion = new Roo.lib.Region( pos.y,
2159 pos.x + el.offsetWidth,
2160 pos.y + el.offsetHeight,
2163 var overlap = curRegion.intersect(loc);
2166 oTarget.overlap = overlap;
2167 return (intersect) ? true : oTarget.cursorIsOver;
2174 * unload event handler
2179 _onUnload: function(e, me) {
2180 Roo.dd.DragDropMgr.unregAll();
2184 * Cleans up the drag and drop events and objects.
2189 unregAll: function() {
2191 if (this.dragCurrent) {
2193 this.dragCurrent = null;
2196 this._execOnAll("unreg", []);
2198 for (i in this.elementCache) {
2199 delete this.elementCache[i];
2202 this.elementCache = {};
2207 * A cache of DOM elements
2208 * @property elementCache
2215 * Get the wrapper for the DOM element specified
2216 * @method getElWrapper
2217 * @param {String} id the id of the element to get
2218 * @return {Roo.dd.DDM.ElementWrapper} the wrapped element
2220 * @deprecated This wrapper isn't that useful
2223 getElWrapper: function(id) {
2224 var oWrapper = this.elementCache[id];
2225 if (!oWrapper || !oWrapper.el) {
2226 oWrapper = this.elementCache[id] =
2227 new this.ElementWrapper(Roo.getDom(id));
2233 * Returns the actual DOM element
2234 * @method getElement
2235 * @param {String} id the id of the elment to get
2236 * @return {Object} The element
2237 * @deprecated use Roo.getDom instead
2240 getElement: function(id) {
2241 return Roo.getDom(id);
2245 * Returns the style property for the DOM element (i.e.,
2246 * document.getElById(id).style)
2248 * @param {String} id the id of the elment to get
2249 * @return {Object} The style property of the element
2250 * @deprecated use Roo.getDom instead
2253 getCss: function(id) {
2254 var el = Roo.getDom(id);
2255 return (el) ? el.style : null;
2259 * Inner class for cached elements
2260 * @class DragDropMgr.ElementWrapper
2265 ElementWrapper: function(el) {
2270 this.el = el || null;
2275 this.id = this.el && el.id;
2277 * A reference to the style property
2280 this.css = this.el && el.style;
2284 * Returns the X position of an html element
2286 * @param el the element for which to get the position
2287 * @return {int} the X coordinate
2289 * @deprecated use Roo.lib.Dom.getX instead
2292 getPosX: function(el) {
2293 return Roo.lib.Dom.getX(el);
2297 * Returns the Y position of an html element
2299 * @param el the element for which to get the position
2300 * @return {int} the Y coordinate
2301 * @deprecated use Roo.lib.Dom.getY instead
2304 getPosY: function(el) {
2305 return Roo.lib.Dom.getY(el);
2309 * Swap two nodes. In IE, we use the native method, for others we
2310 * emulate the IE behavior
2312 * @param n1 the first node to swap
2313 * @param n2 the other node to swap
2316 swapNode: function(n1, n2) {
2320 var p = n2.parentNode;
2321 var s = n2.nextSibling;
2324 p.insertBefore(n1, n2);
2325 } else if (n2 == n1.nextSibling) {
2326 p.insertBefore(n2, n1);
2328 n1.parentNode.replaceChild(n2, n1);
2329 p.insertBefore(n1, s);
2335 * Returns the current scroll position
2340 getScroll: function () {
2341 var t, l, dde=document.documentElement, db=document.body;
2342 if (dde && (dde.scrollTop || dde.scrollLeft)) {
2351 return { top: t, left: l };
2355 * Returns the specified element style property
2357 * @param {HTMLElement} el the element
2358 * @param {string} styleProp the style property
2359 * @return {string} The value of the style property
2360 * @deprecated use Roo.lib.Dom.getStyle
2363 getStyle: function(el, styleProp) {
2364 return Roo.fly(el).getStyle(styleProp);
2368 * Gets the scrollTop
2369 * @method getScrollTop
2370 * @return {int} the document's scrollTop
2373 getScrollTop: function () { return this.getScroll().top; },
2376 * Gets the scrollLeft
2377 * @method getScrollLeft
2378 * @return {int} the document's scrollTop
2381 getScrollLeft: function () { return this.getScroll().left; },
2384 * Sets the x/y position of an element to the location of the
2387 * @param {HTMLElement} moveEl The element to move
2388 * @param {HTMLElement} targetEl The position reference element
2391 moveToEl: function (moveEl, targetEl) {
2392 var aCoord = Roo.lib.Dom.getXY(targetEl);
2393 Roo.lib.Dom.setXY(moveEl, aCoord);
2397 * Numeric array sort function
2398 * @method numericSort
2401 numericSort: function(a, b) { return (a - b); },
2405 * @property _timeoutCount
2412 * Trying to make the load order less important. Without this we get
2413 * an error if this file is loaded before the Event Utility.
2414 * @method _addListeners
2418 _addListeners: function() {
2419 var DDM = Roo.dd.DDM;
2420 if ( Roo.lib.Event && document ) {
2423 if (DDM._timeoutCount > 2000) {
2425 setTimeout(DDM._addListeners, 10);
2426 if (document && document.body) {
2427 DDM._timeoutCount += 1;
2434 * Recursively searches the immediate parent and all child nodes for
2435 * the handle element in order to determine wheter or not it was
2437 * @method handleWasClicked
2438 * @param node the html element to inspect
2441 handleWasClicked: function(node, id) {
2442 if (this.isHandle(id, node.id)) {
2445 // check to see if this is a text node child of the one we want
2446 var p = node.parentNode;
2449 if (this.isHandle(id, p.id)) {
2464 // shorter alias, save a few bytes
2465 Roo.dd.DDM = Roo.dd.DragDropMgr;
2466 Roo.dd.DDM._addListeners();
2470 * Ext JS Library 1.1.1
2471 * Copyright(c) 2006-2007, Ext JS, LLC.
2473 * Originally Released Under LGPL - original licence link has changed is not relivant.
2476 * <script type="text/javascript">
2481 * A DragDrop implementation where the linked element follows the
2482 * mouse cursor during a drag.
2483 * @extends Roo.dd.DragDrop
2485 * @param {String} id the id of the linked element
2486 * @param {String} sGroup the group of related DragDrop items
2487 * @param {object} config an object containing configurable attributes
2488 * Valid properties for DD:
2491 Roo.dd.DD = function(id, sGroup, config) {
2493 this.init(id, sGroup, config);
2497 Roo.extend(Roo.dd.DD, Roo.dd.DragDrop, {
2500 * When set to true, the utility automatically tries to scroll the browser
2501 * window wehn a drag and drop element is dragged near the viewport boundary.
2509 * Sets the pointer offset to the distance between the linked element's top
2510 * left corner and the location the element was clicked
2511 * @method autoOffset
2512 * @param {int} iPageX the X coordinate of the click
2513 * @param {int} iPageY the Y coordinate of the click
2515 autoOffset: function(iPageX, iPageY) {
2516 var x = iPageX - this.startPageX;
2517 var y = iPageY - this.startPageY;
2518 this.setDelta(x, y);
2522 * Sets the pointer offset. You can call this directly to force the
2523 * offset to be in a particular location (e.g., pass in 0,0 to set it
2524 * to the center of the object)
2526 * @param {int} iDeltaX the distance from the left
2527 * @param {int} iDeltaY the distance from the top
2529 setDelta: function(iDeltaX, iDeltaY) {
2530 this.deltaX = iDeltaX;
2531 this.deltaY = iDeltaY;
2535 * Sets the drag element to the location of the mousedown or click event,
2536 * maintaining the cursor location relative to the location on the element
2537 * that was clicked. Override this if you want to place the element in a
2538 * location other than where the cursor is.
2539 * @method setDragElPos
2540 * @param {int} iPageX the X coordinate of the mousedown or drag event
2541 * @param {int} iPageY the Y coordinate of the mousedown or drag event
2543 setDragElPos: function(iPageX, iPageY) {
2544 // the first time we do this, we are going to check to make sure
2545 // the element has css positioning
2547 var el = this.getDragEl();
2548 this.alignElWithMouse(el, iPageX, iPageY);
2552 * Sets the element to the location of the mousedown or click event,
2553 * maintaining the cursor location relative to the location on the element
2554 * that was clicked. Override this if you want to place the element in a
2555 * location other than where the cursor is.
2556 * @method alignElWithMouse
2557 * @param {HTMLElement} el the element to move
2558 * @param {int} iPageX the X coordinate of the mousedown or drag event
2559 * @param {int} iPageY the Y coordinate of the mousedown or drag event
2561 alignElWithMouse: function(el, iPageX, iPageY) {
2562 var oCoord = this.getTargetCoord(iPageX, iPageY);
2563 var fly = el.dom ? el : Roo.fly(el);
2564 if (!this.deltaSetXY) {
2565 var aCoord = [oCoord.x, oCoord.y];
2567 var newLeft = fly.getLeft(true);
2568 var newTop = fly.getTop(true);
2569 this.deltaSetXY = [ newLeft - oCoord.x, newTop - oCoord.y ];
2571 fly.setLeftTop(oCoord.x + this.deltaSetXY[0], oCoord.y + this.deltaSetXY[1]);
2574 this.cachePosition(oCoord.x, oCoord.y);
2575 this.autoScroll(oCoord.x, oCoord.y, el.offsetHeight, el.offsetWidth);
2580 * Saves the most recent position so that we can reset the constraints and
2581 * tick marks on-demand. We need to know this so that we can calculate the
2582 * number of pixels the element is offset from its original position.
2583 * @method cachePosition
2584 * @param iPageX the current x position (optional, this just makes it so we
2585 * don't have to look it up again)
2586 * @param iPageY the current y position (optional, this just makes it so we
2587 * don't have to look it up again)
2589 cachePosition: function(iPageX, iPageY) {
2591 this.lastPageX = iPageX;
2592 this.lastPageY = iPageY;
2594 var aCoord = Roo.lib.Dom.getXY(this.getEl());
2595 this.lastPageX = aCoord[0];
2596 this.lastPageY = aCoord[1];
2601 * Auto-scroll the window if the dragged object has been moved beyond the
2602 * visible window boundary.
2603 * @method autoScroll
2604 * @param {int} x the drag element's x position
2605 * @param {int} y the drag element's y position
2606 * @param {int} h the height of the drag element
2607 * @param {int} w the width of the drag element
2610 autoScroll: function(x, y, h, w) {
2613 // The client height
2614 var clientH = Roo.lib.Dom.getViewWidth();
2617 var clientW = Roo.lib.Dom.getViewHeight();
2619 // The amt scrolled down
2620 var st = this.DDM.getScrollTop();
2622 // The amt scrolled right
2623 var sl = this.DDM.getScrollLeft();
2625 // Location of the bottom of the element
2628 // Location of the right of the element
2631 // The distance from the cursor to the bottom of the visible area,
2632 // adjusted so that we don't scroll if the cursor is beyond the
2633 // element drag constraints
2634 var toBot = (clientH + st - y - this.deltaY);
2636 // The distance from the cursor to the right of the visible area
2637 var toRight = (clientW + sl - x - this.deltaX);
2640 // How close to the edge the cursor must be before we scroll
2641 // var thresh = (document.all) ? 100 : 40;
2644 // How many pixels to scroll per autoscroll op. This helps to reduce
2645 // clunky scrolling. IE is more sensitive about this ... it needs this
2646 // value to be higher.
2647 var scrAmt = (document.all) ? 80 : 30;
2649 // Scroll down if we are near the bottom of the visible page and the
2650 // obj extends below the crease
2651 if ( bot > clientH && toBot < thresh ) {
2652 window.scrollTo(sl, st + scrAmt);
2655 // Scroll up if the window is scrolled down and the top of the object
2656 // goes above the top border
2657 if ( y < st && st > 0 && y - st < thresh ) {
2658 window.scrollTo(sl, st - scrAmt);
2661 // Scroll right if the obj is beyond the right border and the cursor is
2663 if ( right > clientW && toRight < thresh ) {
2664 window.scrollTo(sl + scrAmt, st);
2667 // Scroll left if the window has been scrolled to the right and the obj
2668 // extends past the left border
2669 if ( x < sl && sl > 0 && x - sl < thresh ) {
2670 window.scrollTo(sl - scrAmt, st);
2676 * Finds the location the element should be placed if we want to move
2677 * it to where the mouse location less the click offset would place us.
2678 * @method getTargetCoord
2679 * @param {int} iPageX the X coordinate of the click
2680 * @param {int} iPageY the Y coordinate of the click
2681 * @return an object that contains the coordinates (Object.x and Object.y)
2684 getTargetCoord: function(iPageX, iPageY) {
2687 var x = iPageX - this.deltaX;
2688 var y = iPageY - this.deltaY;
2690 if (this.constrainX) {
2691 if (x < this.minX) { x = this.minX; }
2692 if (x > this.maxX) { x = this.maxX; }
2695 if (this.constrainY) {
2696 if (y < this.minY) { y = this.minY; }
2697 if (y > this.maxY) { y = this.maxY; }
2700 x = this.getTick(x, this.xTicks);
2701 y = this.getTick(y, this.yTicks);
2708 * Sets up config options specific to this class. Overrides
2709 * Roo.dd.DragDrop, but all versions of this method through the
2710 * inheritance chain are called
2712 applyConfig: function() {
2713 Roo.dd.DD.superclass.applyConfig.call(this);
2714 this.scroll = (this.config.scroll !== false);
2718 * Event that fires prior to the onMouseDown event. Overrides
2721 b4MouseDown: function(e) {
2722 // this.resetConstraints();
2723 this.autoOffset(e.getPageX(),
2728 * Event that fires prior to the onDrag event. Overrides
2731 b4Drag: function(e) {
2732 this.setDragElPos(e.getPageX(),
2736 toString: function() {
2737 return ("DD " + this.id);
2740 //////////////////////////////////////////////////////////////////////////
2741 // Debugging ygDragDrop events that can be overridden
2742 //////////////////////////////////////////////////////////////////////////
2744 startDrag: function(x, y) {
2747 onDrag: function(e) {
2750 onDragEnter: function(e, id) {
2753 onDragOver: function(e, id) {
2756 onDragOut: function(e, id) {
2759 onDragDrop: function(e, id) {
2762 endDrag: function(e) {
2769 * Ext JS Library 1.1.1
2770 * Copyright(c) 2006-2007, Ext JS, LLC.
2772 * Originally Released Under LGPL - original licence link has changed is not relivant.
2775 * <script type="text/javascript">
2779 * @class Roo.dd.DDProxy
2780 * A DragDrop implementation that inserts an empty, bordered div into
2781 * the document that follows the cursor during drag operations. At the time of
2782 * the click, the frame div is resized to the dimensions of the linked html
2783 * element, and moved to the exact location of the linked element.
2785 * References to the "frame" element refer to the single proxy element that
2786 * was created to be dragged in place of all DDProxy elements on the
2789 * @extends Roo.dd.DD
2791 * @param {String} id the id of the linked html element
2792 * @param {String} sGroup the group of related DragDrop objects
2793 * @param {object} config an object containing configurable attributes
2794 * Valid properties for DDProxy in addition to those in DragDrop:
2795 * resizeFrame, centerFrame, dragElId
2797 Roo.dd.DDProxy = function(id, sGroup, config) {
2799 this.init(id, sGroup, config);
2805 * The default drag frame div id
2806 * @property Roo.dd.DDProxy.dragElId
2810 Roo.dd.DDProxy.dragElId = "ygddfdiv";
2812 Roo.extend(Roo.dd.DDProxy, Roo.dd.DD, {
2815 * By default we resize the drag frame to be the same size as the element
2816 * we want to drag (this is to get the frame effect). We can turn it off
2817 * if we want a different behavior.
2818 * @property resizeFrame
2824 * By default the frame is positioned exactly where the drag element is, so
2825 * we use the cursor offset provided by Roo.dd.DD. Another option that works only if
2826 * you do not have constraints on the obj is to have the drag frame centered
2827 * around the cursor. Set centerFrame to true for this effect.
2828 * @property centerFrame
2834 * Creates the proxy element if it does not yet exist
2835 * @method createFrame
2837 createFrame: function() {
2839 var body = document.body;
2841 if (!body || !body.firstChild) {
2842 setTimeout( function() { self.createFrame(); }, 50 );
2846 var div = this.getDragEl();
2849 div = document.createElement("div");
2850 div.id = this.dragElId;
2853 s.position = "absolute";
2854 s.visibility = "hidden";
2856 s.border = "2px solid #aaa";
2859 // appendChild can blow up IE if invoked prior to the window load event
2860 // while rendering a table. It is possible there are other scenarios
2861 // that would cause this to happen as well.
2862 body.insertBefore(div, body.firstChild);
2867 * Initialization for the drag frame element. Must be called in the
2868 * constructor of all subclasses
2871 initFrame: function() {
2875 applyConfig: function() {
2876 Roo.dd.DDProxy.superclass.applyConfig.call(this);
2878 this.resizeFrame = (this.config.resizeFrame !== false);
2879 this.centerFrame = (this.config.centerFrame);
2880 this.setDragElId(this.config.dragElId || Roo.dd.DDProxy.dragElId);
2884 * Resizes the drag frame to the dimensions of the clicked object, positions
2885 * it over the object, and finally displays it
2887 * @param {int} iPageX X click position
2888 * @param {int} iPageY Y click position
2891 showFrame: function(iPageX, iPageY) {
2892 var el = this.getEl();
2893 var dragEl = this.getDragEl();
2894 var s = dragEl.style;
2896 this._resizeProxy();
2898 if (this.centerFrame) {
2899 this.setDelta( Math.round(parseInt(s.width, 10)/2),
2900 Math.round(parseInt(s.height, 10)/2) );
2903 this.setDragElPos(iPageX, iPageY);
2905 Roo.fly(dragEl).show();
2909 * The proxy is automatically resized to the dimensions of the linked
2910 * element when a drag is initiated, unless resizeFrame is set to false
2911 * @method _resizeProxy
2914 _resizeProxy: function() {
2915 if (this.resizeFrame) {
2916 var el = this.getEl();
2917 Roo.fly(this.getDragEl()).setSize(el.offsetWidth, el.offsetHeight);
2921 // overrides Roo.dd.DragDrop
2922 b4MouseDown: function(e) {
2923 var x = e.getPageX();
2924 var y = e.getPageY();
2925 this.autoOffset(x, y);
2926 this.setDragElPos(x, y);
2929 // overrides Roo.dd.DragDrop
2930 b4StartDrag: function(x, y) {
2931 // show the drag frame
2932 this.showFrame(x, y);
2935 // overrides Roo.dd.DragDrop
2936 b4EndDrag: function(e) {
2937 Roo.fly(this.getDragEl()).hide();
2940 // overrides Roo.dd.DragDrop
2941 // By default we try to move the element to the last location of the frame.
2942 // This is so that the default behavior mirrors that of Roo.dd.DD.
2943 endDrag: function(e) {
2945 var lel = this.getEl();
2946 var del = this.getDragEl();
2948 // Show the drag frame briefly so we can get its position
2949 del.style.visibility = "";
2952 // Hide the linked element before the move to get around a Safari
2954 lel.style.visibility = "hidden";
2955 Roo.dd.DDM.moveToEl(lel, del);
2956 del.style.visibility = "hidden";
2957 lel.style.visibility = "";
2962 beforeMove : function(){
2966 afterDrag : function(){
2970 toString: function() {
2971 return ("DDProxy " + this.id);
2977 * Ext JS Library 1.1.1
2978 * Copyright(c) 2006-2007, Ext JS, LLC.
2980 * Originally Released Under LGPL - original licence link has changed is not relivant.
2983 * <script type="text/javascript">
2987 * @class Roo.dd.DDTarget
2988 * A DragDrop implementation that does not move, but can be a drop
2989 * target. You would get the same result by simply omitting implementation
2990 * for the event callbacks, but this way we reduce the processing cost of the
2991 * event listener and the callbacks.
2992 * @extends Roo.dd.DragDrop
2994 * @param {String} id the id of the element that is a drop target
2995 * @param {String} sGroup the group of related DragDrop objects
2996 * @param {object} config an object containing configurable attributes
2997 * Valid properties for DDTarget in addition to those in
3001 Roo.dd.DDTarget = function(id, sGroup, config) {
3003 this.initTarget(id, sGroup, config);
3005 if (config.listeners || config.events) {
3006 Roo.dd.DragDrop.superclass.constructor.call(this, {
3007 listeners : config.listeners || {},
3008 events : config.events || {}
3013 // Roo.dd.DDTarget.prototype = new Roo.dd.DragDrop();
3014 Roo.extend(Roo.dd.DDTarget, Roo.dd.DragDrop, {
3015 toString: function() {
3016 return ("DDTarget " + this.id);
3021 * Ext JS Library 1.1.1
3022 * Copyright(c) 2006-2007, Ext JS, LLC.
3024 * Originally Released Under LGPL - original licence link has changed is not relivant.
3027 * <script type="text/javascript">
3032 * @class Roo.dd.ScrollManager
3033 * Provides automatic scrolling of overflow regions in the page during drag operations.<br><br>
3034 * <b>Note: This class uses "Point Mode" and is untested in "Intersect Mode".</b>
3037 Roo.dd.ScrollManager = function(){
3038 var ddm = Roo.dd.DragDropMgr;
3045 var onStop = function(e){
3050 var triggerRefresh = function(){
3051 if(ddm.dragCurrent){
3052 ddm.refreshCache(ddm.dragCurrent.groups);
3056 var doScroll = function(){
3057 if(ddm.dragCurrent){
3058 var dds = Roo.dd.ScrollManager;
3060 if(proc.el.scroll(proc.dir, dds.increment)){
3064 proc.el.scroll(proc.dir, dds.increment, true, dds.animDuration, triggerRefresh);
3069 var clearProc = function(){
3071 clearInterval(proc.id);
3078 var startProc = function(el, dir){
3079 Roo.log('scroll startproc');
3083 proc.id = setInterval(doScroll, Roo.dd.ScrollManager.frequency);
3086 var onFire = function(e, isDrop){
3088 if(isDrop || !ddm.dragCurrent){ return; }
3089 var dds = Roo.dd.ScrollManager;
3090 if(!dragEl || dragEl != ddm.dragCurrent){
3091 dragEl = ddm.dragCurrent;
3092 // refresh regions on drag start
3096 var xy = Roo.lib.Event.getXY(e);
3097 var pt = new Roo.lib.Point(xy[0], xy[1]);
3099 var el = els[id], r = el._region;
3100 if(r && r.contains(pt) && el.isScrollable()){
3101 if(r.bottom - pt.y <= dds.thresh){
3103 startProc(el, "down");
3106 }else if(r.right - pt.x <= dds.thresh){
3108 startProc(el, "left");
3111 }else if(pt.y - r.top <= dds.thresh){
3113 startProc(el, "up");
3116 }else if(pt.x - r.left <= dds.thresh){
3118 startProc(el, "right");
3127 ddm.fireEvents = ddm.fireEvents.createSequence(onFire, ddm);
3128 ddm.stopDrag = ddm.stopDrag.createSequence(onStop, ddm);
3132 * Registers new overflow element(s) to auto scroll
3133 * @param {String/HTMLElement/Element/Array} el The id of or the element to be scrolled or an array of either
3135 register : function(el){
3136 if(el instanceof Array){
3137 for(var i = 0, len = el.length; i < len; i++) {
3138 this.register(el[i]);
3144 Roo.dd.ScrollManager.els = els;
3148 * Unregisters overflow element(s) so they are no longer scrolled
3149 * @param {String/HTMLElement/Element/Array} el The id of or the element to be removed or an array of either
3151 unregister : function(el){
3152 if(el instanceof Array){
3153 for(var i = 0, len = el.length; i < len; i++) {
3154 this.unregister(el[i]);
3163 * The number of pixels from the edge of a container the pointer needs to be to
3164 * trigger scrolling (defaults to 25)
3170 * The number of pixels to scroll in each scroll increment (defaults to 50)
3176 * The frequency of scrolls in milliseconds (defaults to 500)
3182 * True to animate the scroll (defaults to true)
3188 * The animation duration in seconds -
3189 * MUST BE less than Roo.dd.ScrollManager.frequency! (defaults to .4)
3195 * Manually trigger a cache refresh.
3197 refreshCache : function(){
3199 if(typeof els[id] == 'object'){ // for people extending the object prototype
3200 els[id]._region = els[id].getRegion();
3207 * Ext JS Library 1.1.1
3208 * Copyright(c) 2006-2007, Ext JS, LLC.
3210 * Originally Released Under LGPL - original licence link has changed is not relivant.
3213 * <script type="text/javascript">
3218 * @class Roo.dd.Registry
3219 * Provides easy access to all drag drop components that are registered on a page. Items can be retrieved either
3220 * directly by DOM node id, or by passing in the drag drop event that occurred and looking up the event target.
3223 Roo.dd.Registry = function(){
3228 var getId = function(el, autogen){
3229 if(typeof el == "string"){
3233 if(!id && autogen !== false){
3234 id = "roodd-" + (++autoIdSeed);
3242 * Register a drag drop element
3243 * @param {String|HTMLElement} element The id or DOM node to register
3244 * @param {Object} data (optional) A custom data object that will be passed between the elements that are involved
3245 * in drag drop operations. You can populate this object with any arbitrary properties that your own code
3246 * knows how to interpret, plus there are some specific properties known to the Registry that should be
3247 * populated in the data object (if applicable):
3249 Value Description<br />
3250 --------- ------------------------------------------<br />
3251 handles Array of DOM nodes that trigger dragging<br />
3252 for the element being registered<br />
3253 isHandle True if the element passed in triggers<br />
3254 dragging itself, else false
3257 register : function(el, data){
3259 if(typeof el == "string"){
3260 el = document.getElementById(el);
3263 elements[getId(el)] = data;
3264 if(data.isHandle !== false){
3265 handles[data.ddel.id] = data;
3268 var hs = data.handles;
3269 for(var i = 0, len = hs.length; i < len; i++){
3270 handles[getId(hs[i])] = data;
3276 * Unregister a drag drop element
3277 * @param {String|HTMLElement} element The id or DOM node to unregister
3279 unregister : function(el){
3280 var id = getId(el, false);
3281 var data = elements[id];
3283 delete elements[id];
3285 var hs = data.handles;
3286 for(var i = 0, len = hs.length; i < len; i++){
3287 delete handles[getId(hs[i], false)];
3294 * Returns the handle registered for a DOM Node by id
3295 * @param {String|HTMLElement} id The DOM node or id to look up
3296 * @return {Object} handle The custom handle data
3298 getHandle : function(id){
3299 if(typeof id != "string"){ // must be element?
3306 * Returns the handle that is registered for the DOM node that is the target of the event
3307 * @param {Event} e The event
3308 * @return {Object} handle The custom handle data
3310 getHandleFromEvent : function(e){
3311 var t = Roo.lib.Event.getTarget(e);
3312 return t ? handles[t.id] : null;
3316 * Returns a custom data object that is registered for a DOM node by id
3317 * @param {String|HTMLElement} id The DOM node or id to look up
3318 * @return {Object} data The custom data
3320 getTarget : function(id){
3321 if(typeof id != "string"){ // must be element?
3324 return elements[id];
3328 * Returns a custom data object that is registered for the DOM node that is the target of the event
3329 * @param {Event} e The event
3330 * @return {Object} data The custom data
3332 getTargetFromEvent : function(e){
3333 var t = Roo.lib.Event.getTarget(e);
3334 return t ? elements[t.id] || handles[t.id] : null;
3339 * Ext JS Library 1.1.1
3340 * Copyright(c) 2006-2007, Ext JS, LLC.
3342 * Originally Released Under LGPL - original licence link has changed is not relivant.
3345 * <script type="text/javascript">
3350 * @class Roo.dd.StatusProxy
3351 * A specialized drag proxy that supports a drop status icon, {@link Roo.Layer} styles and auto-repair. This is the
3352 * default drag proxy used by all Roo.dd components.
3354 * @param {Object} config
3356 Roo.dd.StatusProxy = function(config){
3357 Roo.apply(this, config);
3358 this.id = this.id || Roo.id();
3359 this.el = new Roo.Layer({
3361 id: this.id, tag: "div", cls: "x-dd-drag-proxy "+this.dropNotAllowed, children: [
3362 {tag: "div", cls: "x-dd-drop-icon"},
3363 {tag: "div", cls: "x-dd-drag-ghost"}
3366 shadow: !config || config.shadow !== false
3368 this.ghost = Roo.get(this.el.dom.childNodes[1]);
3369 this.dropStatus = this.dropNotAllowed;
3372 Roo.dd.StatusProxy.prototype = {
3374 * @cfg {String} dropAllowed
3375 * The CSS class to apply to the status element when drop is allowed (defaults to "x-dd-drop-ok").
3377 dropAllowed : "x-dd-drop-ok",
3379 * @cfg {String} dropNotAllowed
3380 * The CSS class to apply to the status element when drop is not allowed (defaults to "x-dd-drop-nodrop").
3382 dropNotAllowed : "x-dd-drop-nodrop",
3385 * Updates the proxy's visual element to indicate the status of whether or not drop is allowed
3386 * over the current target element.
3387 * @param {String} cssClass The css class for the new drop status indicator image
3389 setStatus : function(cssClass){
3390 cssClass = cssClass || this.dropNotAllowed;
3391 if(this.dropStatus != cssClass){
3392 this.el.replaceClass(this.dropStatus, cssClass);
3393 this.dropStatus = cssClass;
3398 * Resets the status indicator to the default dropNotAllowed value
3399 * @param {Boolean} clearGhost True to also remove all content from the ghost, false to preserve it
3401 reset : function(clearGhost){
3402 this.el.dom.className = "x-dd-drag-proxy " + this.dropNotAllowed;
3403 this.dropStatus = this.dropNotAllowed;
3405 this.ghost.update("");
3410 * Updates the contents of the ghost element
3411 * @param {String} html The html that will replace the current innerHTML of the ghost element
3413 update : function(html){
3414 if(typeof html == "string"){
3415 this.ghost.update(html);
3417 this.ghost.update("");
3418 html.style.margin = "0";
3419 this.ghost.dom.appendChild(html);
3421 // ensure float = none set?? cant remember why though.
3422 var el = this.ghost.dom.firstChild;
3424 Roo.fly(el).setStyle('float', 'none');
3429 * Returns the underlying proxy {@link Roo.Layer}
3430 * @return {Roo.Layer} el
3437 * Returns the ghost element
3438 * @return {Roo.Element} el
3440 getGhost : function(){
3446 * @param {Boolean} clear True to reset the status and clear the ghost contents, false to preserve them
3448 hide : function(clear){
3456 * Stops the repair animation if it's currently running
3459 if(this.anim && this.anim.isAnimated && this.anim.isAnimated()){
3465 * Displays this proxy
3472 * Force the Layer to sync its shadow and shim positions to the element
3479 * Causes the proxy to return to its position of origin via an animation. Should be called after an
3480 * invalid drop operation by the item being dragged.
3481 * @param {Array} xy The XY position of the element ([x, y])
3482 * @param {Function} callback The function to call after the repair is complete
3483 * @param {Object} scope The scope in which to execute the callback
3485 repair : function(xy, callback, scope){
3486 this.callback = callback;
3488 if(xy && this.animRepair !== false){
3489 this.el.addClass("x-dd-drag-repair");
3490 this.el.hideUnders(true);
3491 this.anim = this.el.shift({
3492 duration: this.repairDuration || .5,
3496 callback: this.afterRepair,
3505 afterRepair : function(){
3507 if(typeof this.callback == "function"){
3508 this.callback.call(this.scope || this);
3510 this.callback = null;
3515 * Ext JS Library 1.1.1
3516 * Copyright(c) 2006-2007, Ext JS, LLC.
3518 * Originally Released Under LGPL - original licence link has changed is not relivant.
3521 * <script type="text/javascript">
3525 * @class Roo.dd.DragSource
3526 * @extends Roo.dd.DDProxy
3527 * A simple class that provides the basic implementation needed to make any element draggable.
3529 * @param {String/HTMLElement/Element} el The container element
3530 * @param {Object} config
3532 Roo.dd.DragSource = function(el, config){
3533 this.el = Roo.get(el);
3536 Roo.apply(this, config);
3539 this.proxy = new Roo.dd.StatusProxy();
3542 Roo.dd.DragSource.superclass.constructor.call(this, this.el.dom, this.ddGroup || this.group,
3543 {dragElId : this.proxy.id, resizeFrame: false, isTarget: false, scroll: this.scroll === true});
3545 this.dragging = false;
3548 Roo.extend(Roo.dd.DragSource, Roo.dd.DDProxy, {
3550 * @cfg {String} dropAllowed
3551 * The CSS class returned to the drag source when drop is allowed (defaults to "x-dd-drop-ok").
3553 dropAllowed : "x-dd-drop-ok",
3555 * @cfg {String} dropNotAllowed
3556 * The CSS class returned to the drag source when drop is not allowed (defaults to "x-dd-drop-nodrop").
3558 dropNotAllowed : "x-dd-drop-nodrop",
3561 * Returns the data object associated with this drag source
3562 * @return {Object} data An object containing arbitrary data
3564 getDragData : function(e){
3565 return this.dragData;
3569 onDragEnter : function(e, id){
3570 var target = Roo.dd.DragDropMgr.getDDById(id);
3571 this.cachedTarget = target;
3572 if(this.beforeDragEnter(target, e, id) !== false){
3573 if(target.isNotifyTarget){
3574 var status = target.notifyEnter(this, e, this.dragData);
3575 this.proxy.setStatus(status);
3577 this.proxy.setStatus(this.dropAllowed);
3580 if(this.afterDragEnter){
3582 * An empty function by default, but provided so that you can perform a custom action
3583 * when the dragged item enters the drop target by providing an implementation.
3584 * @param {Roo.dd.DragDrop} target The drop target
3585 * @param {Event} e The event object
3586 * @param {String} id The id of the dragged element
3587 * @method afterDragEnter
3589 this.afterDragEnter(target, e, id);
3595 * An empty function by default, but provided so that you can perform a custom action
3596 * before the dragged item enters the drop target and optionally cancel the onDragEnter.
3597 * @param {Roo.dd.DragDrop} target The drop target
3598 * @param {Event} e The event object
3599 * @param {String} id The id of the dragged element
3600 * @return {Boolean} isValid True if the drag event is valid, else false to cancel
3602 beforeDragEnter : function(target, e, id){
3607 alignElWithMouse: function() {
3608 Roo.dd.DragSource.superclass.alignElWithMouse.apply(this, arguments);
3613 onDragOver : function(e, id){
3614 var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
3615 if(this.beforeDragOver(target, e, id) !== false){
3616 if(target.isNotifyTarget){
3617 var status = target.notifyOver(this, e, this.dragData);
3618 this.proxy.setStatus(status);
3621 if(this.afterDragOver){
3623 * An empty function by default, but provided so that you can perform a custom action
3624 * while the dragged item is over the drop target by providing an implementation.
3625 * @param {Roo.dd.DragDrop} target The drop target
3626 * @param {Event} e The event object
3627 * @param {String} id The id of the dragged element
3628 * @method afterDragOver
3630 this.afterDragOver(target, e, id);
3636 * An empty function by default, but provided so that you can perform a custom action
3637 * while the dragged item is over the drop target and optionally cancel the onDragOver.
3638 * @param {Roo.dd.DragDrop} target The drop target
3639 * @param {Event} e The event object
3640 * @param {String} id The id of the dragged element
3641 * @return {Boolean} isValid True if the drag event is valid, else false to cancel
3643 beforeDragOver : function(target, e, id){
3648 onDragOut : function(e, id){
3649 var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
3650 if(this.beforeDragOut(target, e, id) !== false){
3651 if(target.isNotifyTarget){
3652 target.notifyOut(this, e, this.dragData);
3655 if(this.afterDragOut){
3657 * An empty function by default, but provided so that you can perform a custom action
3658 * after the dragged item is dragged out of the target without dropping.
3659 * @param {Roo.dd.DragDrop} target The drop target
3660 * @param {Event} e The event object
3661 * @param {String} id The id of the dragged element
3662 * @method afterDragOut
3664 this.afterDragOut(target, e, id);
3667 this.cachedTarget = null;
3671 * An empty function by default, but provided so that you can perform a custom action before the dragged
3672 * item is dragged out of the target without dropping, and optionally cancel the onDragOut.
3673 * @param {Roo.dd.DragDrop} target The drop target
3674 * @param {Event} e The event object
3675 * @param {String} id The id of the dragged element
3676 * @return {Boolean} isValid True if the drag event is valid, else false to cancel
3678 beforeDragOut : function(target, e, id){
3683 onDragDrop : function(e, id){
3684 var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
3685 if(this.beforeDragDrop(target, e, id) !== false){
3686 if(target.isNotifyTarget){
3687 if(target.notifyDrop(this, e, this.dragData)){ // valid drop?
3688 this.onValidDrop(target, e, id);
3690 this.onInvalidDrop(target, e, id);
3693 this.onValidDrop(target, e, id);
3696 if(this.afterDragDrop){
3698 * An empty function by default, but provided so that you can perform a custom action
3699 * after a valid drag drop has occurred by providing an implementation.
3700 * @param {Roo.dd.DragDrop} target The drop target
3701 * @param {Event} e The event object
3702 * @param {String} id The id of the dropped element
3703 * @method afterDragDrop
3705 this.afterDragDrop(target, e, id);
3708 delete this.cachedTarget;
3712 * An empty function by default, but provided so that you can perform a custom action before the dragged
3713 * item is dropped onto the target and optionally cancel the onDragDrop.
3714 * @param {Roo.dd.DragDrop} target The drop target
3715 * @param {Event} e The event object
3716 * @param {String} id The id of the dragged element
3717 * @return {Boolean} isValid True if the drag drop event is valid, else false to cancel
3719 beforeDragDrop : function(target, e, id){
3724 onValidDrop : function(target, e, id){
3726 if(this.afterValidDrop){
3728 * An empty function by default, but provided so that you can perform a custom action
3729 * after a valid drop has occurred by providing an implementation.
3730 * @param {Object} target The target DD
3731 * @param {Event} e The event object
3732 * @param {String} id The id of the dropped element
3733 * @method afterInvalidDrop
3735 this.afterValidDrop(target, e, id);
3740 getRepairXY : function(e, data){
3741 return this.el.getXY();
3745 onInvalidDrop : function(target, e, id){
3746 this.beforeInvalidDrop(target, e, id);
3747 if(this.cachedTarget){
3748 if(this.cachedTarget.isNotifyTarget){
3749 this.cachedTarget.notifyOut(this, e, this.dragData);
3751 this.cacheTarget = null;
3753 this.proxy.repair(this.getRepairXY(e, this.dragData), this.afterRepair, this);
3755 if(this.afterInvalidDrop){
3757 * An empty function by default, but provided so that you can perform a custom action
3758 * after an invalid drop has occurred by providing an implementation.
3759 * @param {Event} e The event object
3760 * @param {String} id The id of the dropped element
3761 * @method afterInvalidDrop
3763 this.afterInvalidDrop(e, id);
3768 afterRepair : function(){
3770 this.el.highlight(this.hlColor || "c3daf9");
3772 this.dragging = false;
3776 * An empty function by default, but provided so that you can perform a custom action after an invalid
3777 * drop has occurred.
3778 * @param {Roo.dd.DragDrop} target The drop target
3779 * @param {Event} e The event object
3780 * @param {String} id The id of the dragged element
3781 * @return {Boolean} isValid True if the invalid drop should proceed, else false to cancel
3783 beforeInvalidDrop : function(target, e, id){
3788 handleMouseDown : function(e){
3792 var data = this.getDragData(e);
3793 if(data && this.onBeforeDrag(data, e) !== false){
3794 this.dragData = data;
3796 Roo.dd.DragSource.superclass.handleMouseDown.apply(this, arguments);
3801 * An empty function by default, but provided so that you can perform a custom action before the initial
3802 * drag event begins and optionally cancel it.
3803 * @param {Object} data An object containing arbitrary data to be shared with drop targets
3804 * @param {Event} e The event object
3805 * @return {Boolean} isValid True if the drag event is valid, else false to cancel
3807 onBeforeDrag : function(data, e){
3812 * An empty function by default, but provided so that you can perform a custom action once the initial
3813 * drag event has begun. The drag cannot be canceled from this function.
3814 * @param {Number} x The x position of the click on the dragged object
3815 * @param {Number} y The y position of the click on the dragged object
3817 onStartDrag : Roo.emptyFn,
3819 // private - YUI override
3820 startDrag : function(x, y){
3822 this.dragging = true;
3823 this.proxy.update("");
3824 this.onInitDrag(x, y);
3829 onInitDrag : function(x, y){
3830 var clone = this.el.dom.cloneNode(true);
3831 clone.id = Roo.id(); // prevent duplicate ids
3832 this.proxy.update(clone);
3833 this.onStartDrag(x, y);
3838 * Returns the drag source's underlying {@link Roo.dd.StatusProxy}
3839 * @return {Roo.dd.StatusProxy} proxy The StatusProxy
3841 getProxy : function(){
3846 * Hides the drag source's {@link Roo.dd.StatusProxy}
3848 hideProxy : function(){
3850 this.proxy.reset(true);
3851 this.dragging = false;
3855 triggerCacheRefresh : function(){
3856 Roo.dd.DDM.refreshCache(this.groups);
3859 // private - override to prevent hiding
3860 b4EndDrag: function(e) {
3863 // private - override to prevent moving
3864 endDrag : function(e){
3865 this.onEndDrag(this.dragData, e);
3869 onEndDrag : function(data, e){
3872 // private - pin to cursor
3873 autoOffset : function(x, y) {
3874 this.setDelta(-12, -20);
3878 * Ext JS Library 1.1.1
3879 * Copyright(c) 2006-2007, Ext JS, LLC.
3881 * Originally Released Under LGPL - original licence link has changed is not relivant.
3884 * <script type="text/javascript">
3889 * @class Roo.dd.DropTarget
3890 * @extends Roo.dd.DDTarget
3891 * A simple class that provides the basic implementation needed to make any element a drop target that can have
3892 * draggable items dropped onto it. The drop has no effect until an implementation of notifyDrop is provided.
3894 * @param {String/HTMLElement/Element} el The container element
3895 * @param {Object} config
3897 Roo.dd.DropTarget = function(el, config){
3898 this.el = Roo.get(el);
3900 var listeners = false; ;
3901 if (config && config.listeners) {
3902 listeners= config.listeners;
3903 delete config.listeners;
3905 Roo.apply(this, config);
3907 if(this.containerScroll){
3908 Roo.dd.ScrollManager.register(this.el);
3912 * @scope Roo.dd.DropTarget
3917 * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the source is now over the
3918 * target. This default implementation adds the CSS class specified by overClass (if any) to the drop element
3919 * and returns the dropAllowed config value. This method should be overridden if drop validation is required.
3921 * IMPORTANT : it should set this.overClass and this.dropAllowed
3923 * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
3924 * @param {Event} e The event
3925 * @param {Object} data An object containing arbitrary data supplied by the drag source
3931 * The function a {@link Roo.dd.DragSource} calls continuously while it is being dragged over the target.
3932 * This method will be called on every mouse movement while the drag source is over the drop target.
3933 * This default implementation simply returns the dropAllowed config value.
3935 * IMPORTANT : it should set this.dropAllowed
3937 * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
3938 * @param {Event} e The event
3939 * @param {Object} data An object containing arbitrary data supplied by the drag source
3945 * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the source has been dragged
3946 * out of the target without dropping. This default implementation simply removes the CSS class specified by
3947 * overClass (if any) from the drop element.
3948 * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
3949 * @param {Event} e The event
3950 * @param {Object} data An object containing arbitrary data supplied by the drag source
3956 * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the dragged item has
3957 * been dropped on it. This method has no default implementation and returns false, so you must provide an
3958 * implementation that does something to process the drop event and returns true so that the drag source's
3959 * repair action does not run.
3961 * IMPORTANT : it should set this.success
3963 * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
3964 * @param {Event} e The event
3965 * @param {Object} data An object containing arbitrary data supplied by the drag source
3971 Roo.dd.DropTarget.superclass.constructor.call( this,
3973 this.ddGroup || this.group,
3976 listeners : listeners || {}
3984 Roo.extend(Roo.dd.DropTarget, Roo.dd.DDTarget, {
3986 * @cfg {String} overClass
3987 * The CSS class applied to the drop target element while the drag source is over it (defaults to "").
3990 * @cfg {String} ddGroup
3991 * The drag drop group to handle drop events for
3995 * @cfg {String} dropAllowed
3996 * The CSS class returned to the drag source when drop is allowed (defaults to "x-dd-drop-ok").
3998 dropAllowed : "x-dd-drop-ok",
4000 * @cfg {String} dropNotAllowed
4001 * The CSS class returned to the drag source when drop is not allowed (defaults to "x-dd-drop-nodrop").
4003 dropNotAllowed : "x-dd-drop-nodrop",
4005 * @cfg {boolean} success
4006 * set this after drop listener..
4010 * @cfg {boolean|String} valid true/false or string (ok-add/ok-sub/ok/nodrop)
4011 * if the drop point is valid for over/enter..
4018 isNotifyTarget : true,
4023 notifyEnter : function(dd, e, data)
4026 this.fireEvent('enter', dd, e, data);
4028 this.el.addClass(this.overClass);
4030 return typeof(this.valid) == 'string' ? 'x-dd-drop-' + this.valid : (
4031 this.valid ? this.dropAllowed : this.dropNotAllowed
4038 notifyOver : function(dd, e, data)
4041 this.fireEvent('over', dd, e, data);
4042 return typeof(this.valid) == 'string' ? 'x-dd-drop-' + this.valid : (
4043 this.valid ? this.dropAllowed : this.dropNotAllowed
4050 notifyOut : function(dd, e, data)
4052 this.fireEvent('out', dd, e, data);
4054 this.el.removeClass(this.overClass);
4061 notifyDrop : function(dd, e, data)
4063 this.success = false;
4064 this.fireEvent('drop', dd, e, data);
4065 return this.success;
4069 * Ext JS Library 1.1.1
4070 * Copyright(c) 2006-2007, Ext JS, LLC.
4072 * Originally Released Under LGPL - original licence link has changed is not relivant.
4075 * <script type="text/javascript">
4080 * @class Roo.dd.DragZone
4081 * @extends Roo.dd.DragSource
4082 * This class provides a container DD instance that proxies for multiple child node sources.<br />
4083 * By default, this class requires that draggable child nodes are registered with {@link Roo.dd.Registry}.
4085 * @param {String/HTMLElement/Element} el The container element
4086 * @param {Object} config
4088 Roo.dd.DragZone = function(el, config){
4089 Roo.dd.DragZone.superclass.constructor.call(this, el, config);
4090 if(this.containerScroll){
4091 Roo.dd.ScrollManager.register(this.el);
4095 Roo.extend(Roo.dd.DragZone, Roo.dd.DragSource, {
4097 * @cfg {Boolean} containerScroll True to register this container with the Scrollmanager
4098 * for auto scrolling during drag operations.
4101 * @cfg {String} hlColor The color to use when visually highlighting the drag source in the afterRepair
4102 * method after a failed drop (defaults to "c3daf9" - light blue)
4106 * Called when a mousedown occurs in this container. Looks in {@link Roo.dd.Registry}
4107 * for a valid target to drag based on the mouse down. Override this method
4108 * to provide your own lookup logic (e.g. finding a child by class name). Make sure your returned
4109 * object has a "ddel" attribute (with an HTML Element) for other functions to work.
4110 * @param {EventObject} e The mouse down event
4111 * @return {Object} The dragData
4113 getDragData : function(e){
4114 return Roo.dd.Registry.getHandleFromEvent(e);
4118 * Called once drag threshold has been reached to initialize the proxy element. By default, it clones the
4119 * this.dragData.ddel
4120 * @param {Number} x The x position of the click on the dragged object
4121 * @param {Number} y The y position of the click on the dragged object
4122 * @return {Boolean} true to continue the drag, false to cancel
4124 onInitDrag : function(x, y){
4125 this.proxy.update(this.dragData.ddel.cloneNode(true));
4126 this.onStartDrag(x, y);
4131 * Called after a repair of an invalid drop. By default, highlights this.dragData.ddel
4133 afterRepair : function(){
4135 Roo.Element.fly(this.dragData.ddel).highlight(this.hlColor || "c3daf9");
4137 this.dragging = false;
4141 * Called before a repair of an invalid drop to get the XY to animate to. By default returns
4142 * the XY of this.dragData.ddel
4143 * @param {EventObject} e The mouse up event
4144 * @return {Array} The xy location (e.g. [100, 200])
4146 getRepairXY : function(e){
4147 return Roo.Element.fly(this.dragData.ddel).getXY();
4151 * Ext JS Library 1.1.1
4152 * Copyright(c) 2006-2007, Ext JS, LLC.
4154 * Originally Released Under LGPL - original licence link has changed is not relivant.
4157 * <script type="text/javascript">
4160 * @class Roo.dd.DropZone
4161 * @extends Roo.dd.DropTarget
4162 * This class provides a container DD instance that proxies for multiple child node targets.<br />
4163 * By default, this class requires that child nodes accepting drop are registered with {@link Roo.dd.Registry}.
4165 * @param {String/HTMLElement/Element} el The container element
4166 * @param {Object} config
4168 Roo.dd.DropZone = function(el, config){
4169 Roo.dd.DropZone.superclass.constructor.call(this, el, config);
4172 Roo.extend(Roo.dd.DropZone, Roo.dd.DropTarget, {
4174 * Returns a custom data object associated with the DOM node that is the target of the event. By default
4175 * this looks up the event target in the {@link Roo.dd.Registry}, although you can override this method to
4176 * provide your own custom lookup.
4177 * @param {Event} e The event
4178 * @return {Object} data The custom data
4180 getTargetFromEvent : function(e){
4181 return Roo.dd.Registry.getTargetFromEvent(e);
4185 * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has entered a drop node
4186 * that it has registered. This method has no default implementation and should be overridden to provide
4187 * node-specific processing if necessary.
4188 * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
4189 * {@link #getTargetFromEvent} for this node)
4190 * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4191 * @param {Event} e The event
4192 * @param {Object} data An object containing arbitrary data supplied by the drag source
4194 onNodeEnter : function(n, dd, e, data){
4199 * Called internally while the DropZone determines that a {@link Roo.dd.DragSource} is over a drop node
4200 * that it has registered. The default implementation returns this.dropNotAllowed, so it should be
4201 * overridden to provide the proper feedback.
4202 * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
4203 * {@link #getTargetFromEvent} for this node)
4204 * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4205 * @param {Event} e The event
4206 * @param {Object} data An object containing arbitrary data supplied by the drag source
4207 * @return {String} status The CSS class that communicates the drop status back to the source so that the
4208 * underlying {@link Roo.dd.StatusProxy} can be updated
4210 onNodeOver : function(n, dd, e, data){
4211 return this.dropAllowed;
4215 * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dragged out of
4216 * the drop node without dropping. This method has no default implementation and should be overridden to provide
4217 * node-specific processing if necessary.
4218 * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
4219 * {@link #getTargetFromEvent} for this node)
4220 * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4221 * @param {Event} e The event
4222 * @param {Object} data An object containing arbitrary data supplied by the drag source
4224 onNodeOut : function(n, dd, e, data){
4229 * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dropped onto
4230 * the drop node. The default implementation returns false, so it should be overridden to provide the
4231 * appropriate processing of the drop event and return true so that the drag source's repair action does not run.
4232 * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
4233 * {@link #getTargetFromEvent} for this node)
4234 * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4235 * @param {Event} e The event
4236 * @param {Object} data An object containing arbitrary data supplied by the drag source
4237 * @return {Boolean} True if the drop was valid, else false
4239 onNodeDrop : function(n, dd, e, data){
4244 * Called internally while the DropZone determines that a {@link Roo.dd.DragSource} is being dragged over it,
4245 * but not over any of its registered drop nodes. The default implementation returns this.dropNotAllowed, so
4246 * it should be overridden to provide the proper feedback if necessary.
4247 * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4248 * @param {Event} e The event
4249 * @param {Object} data An object containing arbitrary data supplied by the drag source
4250 * @return {String} status The CSS class that communicates the drop status back to the source so that the
4251 * underlying {@link Roo.dd.StatusProxy} can be updated
4253 onContainerOver : function(dd, e, data){
4254 return this.dropNotAllowed;
4258 * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dropped on it,
4259 * but not on any of its registered drop nodes. The default implementation returns false, so it should be
4260 * overridden to provide the appropriate processing of the drop event if you need the drop zone itself to
4261 * be able to accept drops. It should return true when valid so that the drag source's repair action does not run.
4262 * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4263 * @param {Event} e The event
4264 * @param {Object} data An object containing arbitrary data supplied by the drag source
4265 * @return {Boolean} True if the drop was valid, else false
4267 onContainerDrop : function(dd, e, data){
4272 * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the source is now over
4273 * the zone. The default implementation returns this.dropNotAllowed and expects that only registered drop
4274 * nodes can process drag drop operations, so if you need the drop zone itself to be able to process drops
4275 * you should override this method and provide a custom implementation.
4276 * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4277 * @param {Event} e The event
4278 * @param {Object} data An object containing arbitrary data supplied by the drag source
4279 * @return {String} status The CSS class that communicates the drop status back to the source so that the
4280 * underlying {@link Roo.dd.StatusProxy} can be updated
4282 notifyEnter : function(dd, e, data){
4283 return this.dropNotAllowed;
4287 * The function a {@link Roo.dd.DragSource} calls continuously while it is being dragged over the drop zone.
4288 * This method will be called on every mouse movement while the drag source is over the drop zone.
4289 * It will call {@link #onNodeOver} while the drag source is over a registered node, and will also automatically
4290 * delegate to the appropriate node-specific methods as necessary when the drag source enters and exits
4291 * registered nodes ({@link #onNodeEnter}, {@link #onNodeOut}). If the drag source is not currently over a
4292 * registered node, it will call {@link #onContainerOver}.
4293 * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4294 * @param {Event} e The event
4295 * @param {Object} data An object containing arbitrary data supplied by the drag source
4296 * @return {String} status The CSS class that communicates the drop status back to the source so that the
4297 * underlying {@link Roo.dd.StatusProxy} can be updated
4299 notifyOver : function(dd, e, data){
4300 var n = this.getTargetFromEvent(e);
4301 if(!n){ // not over valid drop target
4302 if(this.lastOverNode){
4303 this.onNodeOut(this.lastOverNode, dd, e, data);
4304 this.lastOverNode = null;
4306 return this.onContainerOver(dd, e, data);
4308 if(this.lastOverNode != n){
4309 if(this.lastOverNode){
4310 this.onNodeOut(this.lastOverNode, dd, e, data);
4312 this.onNodeEnter(n, dd, e, data);
4313 this.lastOverNode = n;
4315 return this.onNodeOver(n, dd, e, data);
4319 * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the source has been dragged
4320 * out of the zone without dropping. If the drag source is currently over a registered node, the notification
4321 * will be delegated to {@link #onNodeOut} for node-specific handling, otherwise it will be ignored.
4322 * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
4323 * @param {Event} e The event
4324 * @param {Object} data An object containing arbitrary data supplied by the drag zone
4326 notifyOut : function(dd, e, data){
4327 if(this.lastOverNode){
4328 this.onNodeOut(this.lastOverNode, dd, e, data);
4329 this.lastOverNode = null;
4334 * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the dragged item has
4335 * been dropped on it. The drag zone will look up the target node based on the event passed in, and if there
4336 * is a node registered for that event, it will delegate to {@link #onNodeDrop} for node-specific handling,
4337 * otherwise it will call {@link #onContainerDrop}.
4338 * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4339 * @param {Event} e The event
4340 * @param {Object} data An object containing arbitrary data supplied by the drag source
4341 * @return {Boolean} True if the drop was valid, else false
4343 notifyDrop : function(dd, e, data){
4344 if(this.lastOverNode){
4345 this.onNodeOut(this.lastOverNode, dd, e, data);
4346 this.lastOverNode = null;
4348 var n = this.getTargetFromEvent(e);
4350 this.onNodeDrop(n, dd, e, data) :
4351 this.onContainerDrop(dd, e, data);
4355 triggerCacheRefresh : function(){
4356 Roo.dd.DDM.refreshCache(this.groups);
4360 * Ext JS Library 1.1.1
4361 * Copyright(c) 2006-2007, Ext JS, LLC.
4363 * Originally Released Under LGPL - original licence link has changed is not relivant.
4366 * <script type="text/javascript">
4371 * @class Roo.data.SortTypes
4373 * Defines the default sorting (casting?) comparison functions used when sorting data.
4375 Roo.data.SortTypes = {
4377 * Default sort that does nothing
4378 * @param {Mixed} s The value being converted
4379 * @return {Mixed} The comparison value
4386 * The regular expression used to strip tags
4390 stripTagsRE : /<\/?[^>]+>/gi,
4393 * Strips all HTML tags to sort on text only
4394 * @param {Mixed} s The value being converted
4395 * @return {String} The comparison value
4397 asText : function(s){
4398 return String(s).replace(this.stripTagsRE, "");
4402 * Strips all HTML tags to sort on text only - Case insensitive
4403 * @param {Mixed} s The value being converted
4404 * @return {String} The comparison value
4406 asUCText : function(s){
4407 return String(s).toUpperCase().replace(this.stripTagsRE, "");
4411 * Case insensitive string
4412 * @param {Mixed} s The value being converted
4413 * @return {String} The comparison value
4415 asUCString : function(s) {
4416 return String(s).toUpperCase();
4421 * @param {Mixed} s The value being converted
4422 * @return {Number} The comparison value
4424 asDate : function(s) {
4428 if(s instanceof Date){
4431 return Date.parse(String(s));
4436 * @param {Mixed} s The value being converted
4437 * @return {Float} The comparison value
4439 asFloat : function(s) {
4440 var val = parseFloat(String(s).replace(/,/g, ""));
4441 if(isNaN(val)) val = 0;
4447 * @param {Mixed} s The value being converted
4448 * @return {Number} The comparison value
4450 asInt : function(s) {
4451 var val = parseInt(String(s).replace(/,/g, ""));
4452 if(isNaN(val)) val = 0;
4457 * Ext JS Library 1.1.1
4458 * Copyright(c) 2006-2007, Ext JS, LLC.
4460 * Originally Released Under LGPL - original licence link has changed is not relivant.
4463 * <script type="text/javascript">
4467 * @class Roo.data.Record
4468 * Instances of this class encapsulate both record <em>definition</em> information, and record
4469 * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
4470 * to access Records cached in an {@link Roo.data.Store} object.<br>
4472 * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
4473 * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
4476 * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
4478 * This constructor should not be used to create Record objects. Instead, use the constructor generated by
4479 * {@link #create}. The parameters are the same.
4480 * @param {Array} data An associative Array of data values keyed by the field name.
4481 * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
4482 * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
4483 * not specified an integer id is generated.
4485 Roo.data.Record = function(data, id){
4486 this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
4491 * Generate a constructor for a specific record layout.
4492 * @param {Array} o An Array of field definition objects which specify field names, and optionally,
4493 * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
4494 * Each field definition object may contain the following properties: <ul>
4495 * <li><b>name</b> : String<p style="margin-left:1em">The name by which the field is referenced within the Record. This is referenced by,
4496 * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
4497 * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
4498 * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
4499 * is being used, then this is a string containing the javascript expression to reference the data relative to
4500 * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
4501 * to the data item relative to the record element. If the mapping expression is the same as the field name,
4502 * this may be omitted.</p></li>
4503 * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
4504 * <ul><li>auto (Default, implies no conversion)</li>
4509 * <li>date</li></ul></p></li>
4510 * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
4511 * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
4512 * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
4513 * by the Reader into an object that will be stored in the Record. It is passed the
4514 * following parameters:<ul>
4515 * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
4517 * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
4519 * <br>usage:<br><pre><code>
4520 var TopicRecord = Roo.data.Record.create(
4521 {name: 'title', mapping: 'topic_title'},
4522 {name: 'author', mapping: 'username'},
4523 {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
4524 {name: 'lastPost', mapping: 'post_time', type: 'date'},
4525 {name: 'lastPoster', mapping: 'user2'},
4526 {name: 'excerpt', mapping: 'post_text'}
4529 var myNewRecord = new TopicRecord({
4530 title: 'Do my job please',
4533 lastPost: new Date(),
4534 lastPoster: 'Animal',
4535 excerpt: 'No way dude!'
4537 myStore.add(myNewRecord);
4542 Roo.data.Record.create = function(o){
4544 f.superclass.constructor.apply(this, arguments);
4546 Roo.extend(f, Roo.data.Record);
4547 var p = f.prototype;
4548 p.fields = new Roo.util.MixedCollection(false, function(field){
4551 for(var i = 0, len = o.length; i < len; i++){
4552 p.fields.add(new Roo.data.Field(o[i]));
4554 f.getField = function(name){
4555 return p.fields.get(name);
4560 Roo.data.Record.AUTO_ID = 1000;
4561 Roo.data.Record.EDIT = 'edit';
4562 Roo.data.Record.REJECT = 'reject';
4563 Roo.data.Record.COMMIT = 'commit';
4565 Roo.data.Record.prototype = {
4567 * Readonly flag - true if this record has been modified.
4576 join : function(store){
4581 * Set the named field to the specified value.
4582 * @param {String} name The name of the field to set.
4583 * @param {Object} value The value to set the field to.
4585 set : function(name, value){
4586 if(this.data[name] == value){
4593 if(typeof this.modified[name] == 'undefined'){
4594 this.modified[name] = this.data[name];
4596 this.data[name] = value;
4597 if(!this.editing && this.store){
4598 this.store.afterEdit(this);
4603 * Get the value of the named field.
4604 * @param {String} name The name of the field to get the value of.
4605 * @return {Object} The value of the field.
4607 get : function(name){
4608 return this.data[name];
4612 beginEdit : function(){
4613 this.editing = true;
4618 cancelEdit : function(){
4619 this.editing = false;
4620 delete this.modified;
4624 endEdit : function(){
4625 this.editing = false;
4626 if(this.dirty && this.store){
4627 this.store.afterEdit(this);
4632 * Usually called by the {@link Roo.data.Store} which owns the Record.
4633 * Rejects all changes made to the Record since either creation, or the last commit operation.
4634 * Modified fields are reverted to their original values.
4636 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
4637 * of reject operations.
4639 reject : function(){
4640 var m = this.modified;
4642 if(typeof m[n] != "function"){
4643 this.data[n] = m[n];
4647 delete this.modified;
4648 this.editing = false;
4650 this.store.afterReject(this);
4655 * Usually called by the {@link Roo.data.Store} which owns the Record.
4656 * Commits all changes made to the Record since either creation, or the last commit operation.
4658 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
4659 * of commit operations.
4661 commit : function(){
4663 delete this.modified;
4664 this.editing = false;
4666 this.store.afterCommit(this);
4671 hasError : function(){
4672 return this.error != null;
4676 clearError : function(){
4681 * Creates a copy of this record.
4682 * @param {String} id (optional) A new record id if you don't want to use this record's id
4685 copy : function(newId) {
4686 return new this.constructor(Roo.apply({}, this.data), newId || this.id);
4690 * Ext JS Library 1.1.1
4691 * Copyright(c) 2006-2007, Ext JS, LLC.
4693 * Originally Released Under LGPL - original licence link has changed is not relivant.
4696 * <script type="text/javascript">
4702 * @class Roo.data.Store
4703 * @extends Roo.util.Observable
4704 * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
4705 * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
4707 * A Store object uses an implementation of {@link Roo.data.DataProxy} to access a data object unless you call loadData() directly and pass in your data. The Store object
4708 * has no knowledge of the format of the data returned by the Proxy.<br>
4710 * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
4711 * instances from the data object. These records are cached and made available through accessor functions.
4713 * Creates a new Store.
4714 * @param {Object} config A config object containing the objects needed for the Store to access data,
4715 * and read the data into Records.
4717 Roo.data.Store = function(config){
4718 this.data = new Roo.util.MixedCollection(false);
4719 this.data.getKey = function(o){
4722 this.baseParams = {};
4729 "multisort" : "_multisort"
4732 if(config && config.data){
4733 this.inlineData = config.data;
4737 Roo.apply(this, config);
4739 if(this.reader){ // reader passed
4740 this.reader = Roo.factory(this.reader, Roo.data);
4741 this.reader.xmodule = this.xmodule || false;
4742 if(!this.recordType){
4743 this.recordType = this.reader.recordType;
4745 if(this.reader.onMetaChange){
4746 this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
4750 if(this.recordType){
4751 this.fields = this.recordType.prototype.fields;
4757 * @event datachanged
4758 * Fires when the data cache has changed, and a widget which is using this Store
4759 * as a Record cache should refresh its view.
4760 * @param {Store} this
4765 * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
4766 * @param {Store} this
4767 * @param {Object} meta The JSON metadata
4772 * Fires when Records have been added to the Store
4773 * @param {Store} this
4774 * @param {Roo.data.Record[]} records The array of Records added
4775 * @param {Number} index The index at which the record(s) were added
4780 * Fires when a Record has been removed from the Store
4781 * @param {Store} this
4782 * @param {Roo.data.Record} record The Record that was removed
4783 * @param {Number} index The index at which the record was removed
4788 * Fires when a Record has been updated
4789 * @param {Store} this
4790 * @param {Roo.data.Record} record The Record that was updated
4791 * @param {String} operation The update operation being performed. Value may be one of:
4793 Roo.data.Record.EDIT
4794 Roo.data.Record.REJECT
4795 Roo.data.Record.COMMIT
4801 * Fires when the data cache has been cleared.
4802 * @param {Store} this
4807 * Fires before a request is made for a new data object. If the beforeload handler returns false
4808 * the load action will be canceled.
4809 * @param {Store} this
4810 * @param {Object} options The loading options that were specified (see {@link #load} for details)
4815 * Fires after a new set of Records has been loaded.
4816 * @param {Store} this
4817 * @param {Roo.data.Record[]} records The Records that were loaded
4818 * @param {Object} options The loading options that were specified (see {@link #load} for details)
4822 * @event loadexception
4823 * Fires if an exception occurs in the Proxy during loading.
4824 * Called with the signature of the Proxy's "loadexception" event.
4825 * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
4828 * @param {Object} return from JsonData.reader() - success, totalRecords, records
4829 * @param {Object} load options
4830 * @param {Object} jsonData from your request (normally this contains the Exception)
4832 loadexception : true
4836 this.proxy = Roo.factory(this.proxy, Roo.data);
4837 this.proxy.xmodule = this.xmodule || false;
4838 this.relayEvents(this.proxy, ["loadexception"]);
4840 this.sortToggle = {};
4841 this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
4843 Roo.data.Store.superclass.constructor.call(this);
4845 if(this.inlineData){
4846 this.loadData(this.inlineData);
4847 delete this.inlineData;
4850 Roo.extend(Roo.data.Store, Roo.util.Observable, {
4852 * @cfg {boolean} isLocal flag if data is locally available (and can be always looked up
4853 * without a remote query - used by combo/forms at present.
4857 * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
4860 * @cfg {Array} data Inline data to be loaded when the store is initialized.
4863 * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
4864 * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
4867 * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
4868 * on any HTTP request
4871 * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
4874 * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
4878 * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
4879 * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
4884 * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
4885 * loaded or when a record is removed. (defaults to false).
4887 pruneModifiedRecords : false,
4893 * Add Records to the Store and fires the add event.
4894 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
4896 add : function(records){
4897 records = [].concat(records);
4898 for(var i = 0, len = records.length; i < len; i++){
4899 records[i].join(this);
4901 var index = this.data.length;
4902 this.data.addAll(records);
4903 this.fireEvent("add", this, records, index);
4907 * Remove a Record from the Store and fires the remove event.
4908 * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
4910 remove : function(record){
4911 var index = this.data.indexOf(record);
4912 this.data.removeAt(index);
4913 if(this.pruneModifiedRecords){
4914 this.modified.remove(record);
4916 this.fireEvent("remove", this, record, index);
4920 * Remove all Records from the Store and fires the clear event.
4922 removeAll : function(){
4924 if(this.pruneModifiedRecords){
4927 this.fireEvent("clear", this);
4931 * Inserts Records to the Store at the given index and fires the add event.
4932 * @param {Number} index The start index at which to insert the passed Records.
4933 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
4935 insert : function(index, records){
4936 records = [].concat(records);
4937 for(var i = 0, len = records.length; i < len; i++){
4938 this.data.insert(index, records[i]);
4939 records[i].join(this);
4941 this.fireEvent("add", this, records, index);
4945 * Get the index within the cache of the passed Record.
4946 * @param {Roo.data.Record} record The Roo.data.Record object to to find.
4947 * @return {Number} The index of the passed Record. Returns -1 if not found.
4949 indexOf : function(record){
4950 return this.data.indexOf(record);
4954 * Get the index within the cache of the Record with the passed id.
4955 * @param {String} id The id of the Record to find.
4956 * @return {Number} The index of the Record. Returns -1 if not found.
4958 indexOfId : function(id){
4959 return this.data.indexOfKey(id);
4963 * Get the Record with the specified id.
4964 * @param {String} id The id of the Record to find.
4965 * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
4967 getById : function(id){
4968 return this.data.key(id);
4972 * Get the Record at the specified index.
4973 * @param {Number} index The index of the Record to find.
4974 * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
4976 getAt : function(index){
4977 return this.data.itemAt(index);
4981 * Returns a range of Records between specified indices.
4982 * @param {Number} startIndex (optional) The starting index (defaults to 0)
4983 * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
4984 * @return {Roo.data.Record[]} An array of Records
4986 getRange : function(start, end){
4987 return this.data.getRange(start, end);
4991 storeOptions : function(o){
4992 o = Roo.apply({}, o);
4995 this.lastOptions = o;
4999 * Loads the Record cache from the configured Proxy using the configured Reader.
5001 * If using remote paging, then the first load call must specify the <em>start</em>
5002 * and <em>limit</em> properties in the options.params property to establish the initial
5003 * position within the dataset, and the number of Records to cache on each read from the Proxy.
5005 * <strong>It is important to note that for remote data sources, loading is asynchronous,
5006 * and this call will return before the new data has been loaded. Perform any post-processing
5007 * in a callback function, or in a "load" event handler.</strong>
5009 * @param {Object} options An object containing properties which control loading options:<ul>
5010 * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
5011 * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
5012 * passed the following arguments:<ul>
5013 * <li>r : Roo.data.Record[]</li>
5014 * <li>options: Options object from the load call</li>
5015 * <li>success: Boolean success indicator</li></ul></li>
5016 * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
5017 * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
5020 load : function(options){
5021 options = options || {};
5022 if(this.fireEvent("beforeload", this, options) !== false){
5023 this.storeOptions(options);
5024 var p = Roo.apply(options.params || {}, this.baseParams);
5025 // if meta was not loaded from remote source.. try requesting it.
5026 if (!this.reader.metaFromRemote) {
5029 if(this.sortInfo && this.remoteSort){
5030 var pn = this.paramNames;
5031 p[pn["sort"]] = this.sortInfo.field;
5032 p[pn["dir"]] = this.sortInfo.direction;
5034 if (this.multiSort) {
5035 var pn = this.paramNames;
5036 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
5039 this.proxy.load(p, this.reader, this.loadRecords, this, options);
5044 * Reloads the Record cache from the configured Proxy using the configured Reader and
5045 * the options from the last load operation performed.
5046 * @param {Object} options (optional) An object containing properties which may override the options
5047 * used in the last load operation. See {@link #load} for details (defaults to null, in which case
5048 * the most recently used options are reused).
5050 reload : function(options){
5051 this.load(Roo.applyIf(options||{}, this.lastOptions));
5055 // Called as a callback by the Reader during a load operation.
5056 loadRecords : function(o, options, success){
5057 if(!o || success === false){
5058 if(success !== false){
5059 this.fireEvent("load", this, [], options);
5061 if(options.callback){
5062 options.callback.call(options.scope || this, [], options, false);
5066 // if data returned failure - throw an exception.
5067 if (o.success === false) {
5068 // show a message if no listener is registered.
5069 if (!this.hasListener('loadexception') && typeof(this.reader.jsonData.errorMsg) != 'undefined') {
5070 Roo.MessageBox.alert("Error loading",this.reader.jsonData.errorMsg);
5072 // loadmask wil be hooked into this..
5073 this.fireEvent("loadexception", this, o, options, this.reader.jsonData);
5076 var r = o.records, t = o.totalRecords || r.length;
5077 if(!options || options.add !== true){
5078 if(this.pruneModifiedRecords){
5081 for(var i = 0, len = r.length; i < len; i++){
5085 this.data = this.snapshot;
5086 delete this.snapshot;
5089 this.data.addAll(r);
5090 this.totalLength = t;
5092 this.fireEvent("datachanged", this);
5094 this.totalLength = Math.max(t, this.data.length+r.length);
5097 this.fireEvent("load", this, r, options);
5098 if(options.callback){
5099 options.callback.call(options.scope || this, r, options, true);
5105 * Loads data from a passed data block. A Reader which understands the format of the data
5106 * must have been configured in the constructor.
5107 * @param {Object} data The data block from which to read the Records. The format of the data expected
5108 * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
5109 * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
5111 loadData : function(o, append){
5112 var r = this.reader.readRecords(o);
5113 this.loadRecords(r, {add: append}, true);
5117 * Gets the number of cached records.
5119 * <em>If using paging, this may not be the total size of the dataset. If the data object
5120 * used by the Reader contains the dataset size, then the getTotalCount() function returns
5121 * the data set size</em>
5123 getCount : function(){
5124 return this.data.length || 0;
5128 * Gets the total number of records in the dataset as returned by the server.
5130 * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
5131 * the dataset size</em>
5133 getTotalCount : function(){
5134 return this.totalLength || 0;
5138 * Returns the sort state of the Store as an object with two properties:
5140 field {String} The name of the field by which the Records are sorted
5141 direction {String} The sort order, "ASC" or "DESC"
5144 getSortState : function(){
5145 return this.sortInfo;
5149 applySort : function(){
5150 if(this.sortInfo && !this.remoteSort){
5151 var s = this.sortInfo, f = s.field;
5152 var st = this.fields.get(f).sortType;
5153 var fn = function(r1, r2){
5154 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
5155 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
5157 this.data.sort(s.direction, fn);
5158 if(this.snapshot && this.snapshot != this.data){
5159 this.snapshot.sort(s.direction, fn);
5165 * Sets the default sort column and order to be used by the next load operation.
5166 * @param {String} fieldName The name of the field to sort by.
5167 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
5169 setDefaultSort : function(field, dir){
5170 this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
5175 * If remote sorting is used, the sort is performed on the server, and the cache is
5176 * reloaded. If local sorting is used, the cache is sorted internally.
5177 * @param {String} fieldName The name of the field to sort by.
5178 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
5180 sort : function(fieldName, dir){
5181 var f = this.fields.get(fieldName);
5183 this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
5185 if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
5186 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
5191 this.sortToggle[f.name] = dir;
5192 this.sortInfo = {field: f.name, direction: dir};
5193 if(!this.remoteSort){
5195 this.fireEvent("datachanged", this);
5197 this.load(this.lastOptions);
5202 * Calls the specified function for each of the Records in the cache.
5203 * @param {Function} fn The function to call. The Record is passed as the first parameter.
5204 * Returning <em>false</em> aborts and exits the iteration.
5205 * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
5207 each : function(fn, scope){
5208 this.data.each(fn, scope);
5212 * Gets all records modified since the last commit. Modified records are persisted across load operations
5213 * (e.g., during paging).
5214 * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
5216 getModifiedRecords : function(){
5217 return this.modified;
5221 createFilterFn : function(property, value, anyMatch){
5222 if(!value.exec){ // not a regex
5223 value = String(value);
5224 if(value.length == 0){
5227 value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
5230 return value.test(r.data[property]);
5235 * Sums the value of <i>property</i> for each record between start and end and returns the result.
5236 * @param {String} property A field on your records
5237 * @param {Number} start The record index to start at (defaults to 0)
5238 * @param {Number} end The last record index to include (defaults to length - 1)
5239 * @return {Number} The sum
5241 sum : function(property, start, end){
5242 var rs = this.data.items, v = 0;
5244 end = (end || end === 0) ? end : rs.length-1;
5246 for(var i = start; i <= end; i++){
5247 v += (rs[i].data[property] || 0);
5253 * Filter the records by a specified property.
5254 * @param {String} field A field on your records
5255 * @param {String/RegExp} value Either a string that the field
5256 * should start with or a RegExp to test against the field
5257 * @param {Boolean} anyMatch True to match any part not just the beginning
5259 filter : function(property, value, anyMatch){
5260 var fn = this.createFilterFn(property, value, anyMatch);
5261 return fn ? this.filterBy(fn) : this.clearFilter();
5265 * Filter by a function. The specified function will be called with each
5266 * record in this data source. If the function returns true the record is included,
5267 * otherwise it is filtered.
5268 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
5269 * @param {Object} scope (optional) The scope of the function (defaults to this)
5271 filterBy : function(fn, scope){
5272 this.snapshot = this.snapshot || this.data;
5273 this.data = this.queryBy(fn, scope||this);
5274 this.fireEvent("datachanged", this);
5278 * Query the records by a specified property.
5279 * @param {String} field A field on your records
5280 * @param {String/RegExp} value Either a string that the field
5281 * should start with or a RegExp to test against the field
5282 * @param {Boolean} anyMatch True to match any part not just the beginning
5283 * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
5285 query : function(property, value, anyMatch){
5286 var fn = this.createFilterFn(property, value, anyMatch);
5287 return fn ? this.queryBy(fn) : this.data.clone();
5291 * Query by a function. The specified function will be called with each
5292 * record in this data source. If the function returns true the record is included
5294 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
5295 * @param {Object} scope (optional) The scope of the function (defaults to this)
5296 @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
5298 queryBy : function(fn, scope){
5299 var data = this.snapshot || this.data;
5300 return data.filterBy(fn, scope||this);
5304 * Collects unique values for a particular dataIndex from this store.
5305 * @param {String} dataIndex The property to collect
5306 * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
5307 * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
5308 * @return {Array} An array of the unique values
5310 collect : function(dataIndex, allowNull, bypassFilter){
5311 var d = (bypassFilter === true && this.snapshot) ?
5312 this.snapshot.items : this.data.items;
5313 var v, sv, r = [], l = {};
5314 for(var i = 0, len = d.length; i < len; i++){
5315 v = d[i].data[dataIndex];
5317 if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
5326 * Revert to a view of the Record cache with no filtering applied.
5327 * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
5329 clearFilter : function(suppressEvent){
5330 if(this.snapshot && this.snapshot != this.data){
5331 this.data = this.snapshot;
5332 delete this.snapshot;
5333 if(suppressEvent !== true){
5334 this.fireEvent("datachanged", this);
5340 afterEdit : function(record){
5341 if(this.modified.indexOf(record) == -1){
5342 this.modified.push(record);
5344 this.fireEvent("update", this, record, Roo.data.Record.EDIT);
5348 afterReject : function(record){
5349 this.modified.remove(record);
5350 this.fireEvent("update", this, record, Roo.data.Record.REJECT);
5354 afterCommit : function(record){
5355 this.modified.remove(record);
5356 this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
5360 * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
5361 * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
5363 commitChanges : function(){
5364 var m = this.modified.slice(0);
5366 for(var i = 0, len = m.length; i < len; i++){
5372 * Cancel outstanding changes on all changed records.
5374 rejectChanges : function(){
5375 var m = this.modified.slice(0);
5377 for(var i = 0, len = m.length; i < len; i++){
5382 onMetaChange : function(meta, rtype, o){
5383 this.recordType = rtype;
5384 this.fields = rtype.prototype.fields;
5385 delete this.snapshot;
5386 this.sortInfo = meta.sortInfo || this.sortInfo;
5388 this.fireEvent('metachange', this, this.reader.meta);
5392 * Ext JS Library 1.1.1
5393 * Copyright(c) 2006-2007, Ext JS, LLC.
5395 * Originally Released Under LGPL - original licence link has changed is not relivant.
5398 * <script type="text/javascript">
5402 * @class Roo.data.SimpleStore
5403 * @extends Roo.data.Store
5404 * Small helper class to make creating Stores from Array data easier.
5405 * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
5406 * @cfg {Array} fields An array of field definition objects, or field name strings.
5407 * @cfg {Array} data The multi-dimensional array of data
5409 * @param {Object} config
5411 Roo.data.SimpleStore = function(config){
5412 Roo.data.SimpleStore.superclass.constructor.call(this, {
5414 reader: new Roo.data.ArrayReader({
5417 Roo.data.Record.create(config.fields)
5419 proxy : new Roo.data.MemoryProxy(config.data)
5423 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
5425 * Ext JS Library 1.1.1
5426 * Copyright(c) 2006-2007, Ext JS, LLC.
5428 * Originally Released Under LGPL - original licence link has changed is not relivant.
5431 * <script type="text/javascript">
5436 * @extends Roo.data.Store
5437 * @class Roo.data.JsonStore
5438 * Small helper class to make creating Stores for JSON data easier. <br/>
5440 var store = new Roo.data.JsonStore({
5441 url: 'get-images.php',
5443 fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
5446 * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
5447 * JsonReader and HttpProxy (unless inline data is provided).</b>
5448 * @cfg {Array} fields An array of field definition objects, or field name strings.
5450 * @param {Object} config
5452 Roo.data.JsonStore = function(c){
5453 Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
5454 proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
5455 reader: new Roo.data.JsonReader(c, c.fields)
5458 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
5460 * Ext JS Library 1.1.1
5461 * Copyright(c) 2006-2007, Ext JS, LLC.
5463 * Originally Released Under LGPL - original licence link has changed is not relivant.
5466 * <script type="text/javascript">
5470 Roo.data.Field = function(config){
5471 if(typeof config == "string"){
5472 config = {name: config};
5474 Roo.apply(this, config);
5480 var st = Roo.data.SortTypes;
5481 // named sortTypes are supported, here we look them up
5482 if(typeof this.sortType == "string"){
5483 this.sortType = st[this.sortType];
5486 // set default sortType for strings and dates
5490 this.sortType = st.asUCString;
5493 this.sortType = st.asDate;
5496 this.sortType = st.none;
5501 var stripRe = /[\$,%]/g;
5503 // prebuilt conversion function for this field, instead of
5504 // switching every time we're reading a value
5506 var cv, dateFormat = this.dateFormat;
5511 cv = function(v){ return v; };
5514 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
5518 return v !== undefined && v !== null && v !== '' ?
5519 parseInt(String(v).replace(stripRe, ""), 10) : '';
5524 return v !== undefined && v !== null && v !== '' ?
5525 parseFloat(String(v).replace(stripRe, ""), 10) : '';
5530 cv = function(v){ return v === true || v === "true" || v == 1; };
5537 if(v instanceof Date){
5541 if(dateFormat == "timestamp"){
5542 return new Date(v*1000);
5544 return Date.parseDate(v, dateFormat);
5546 var parsed = Date.parse(v);
5547 return parsed ? new Date(parsed) : null;
5556 Roo.data.Field.prototype = {
5564 * Ext JS Library 1.1.1
5565 * Copyright(c) 2006-2007, Ext JS, LLC.
5567 * Originally Released Under LGPL - original licence link has changed is not relivant.
5570 * <script type="text/javascript">
5573 // Base class for reading structured data from a data source. This class is intended to be
5574 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
5577 * @class Roo.data.DataReader
5578 * Base class for reading structured data from a data source. This class is intended to be
5579 * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
5582 Roo.data.DataReader = function(meta, recordType){
5586 this.recordType = recordType instanceof Array ?
5587 Roo.data.Record.create(recordType) : recordType;
5590 Roo.data.DataReader.prototype = {
5592 * Create an empty record
5593 * @param {Object} data (optional) - overlay some values
5594 * @return {Roo.data.Record} record created.
5596 newRow : function(d) {
5598 this.recordType.prototype.fields.each(function(c) {
5600 case 'int' : da[c.name] = 0; break;
5601 case 'date' : da[c.name] = new Date(); break;
5602 case 'float' : da[c.name] = 0.0; break;
5603 case 'boolean' : da[c.name] = false; break;
5604 default : da[c.name] = ""; break;
5608 return new this.recordType(Roo.apply(da, d));
5613 * Ext JS Library 1.1.1
5614 * Copyright(c) 2006-2007, Ext JS, LLC.
5616 * Originally Released Under LGPL - original licence link has changed is not relivant.
5619 * <script type="text/javascript">
5623 * @class Roo.data.DataProxy
5624 * @extends Roo.data.Observable
5625 * This class is an abstract base class for implementations which provide retrieval of
5626 * unformatted data objects.<br>
5628 * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
5629 * (of the appropriate type which knows how to parse the data object) to provide a block of
5630 * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
5632 * Custom implementations must implement the load method as described in
5633 * {@link Roo.data.HttpProxy#load}.
5635 Roo.data.DataProxy = function(){
5639 * Fires before a network request is made to retrieve a data object.
5640 * @param {Object} This DataProxy object.
5641 * @param {Object} params The params parameter to the load function.
5646 * Fires before the load method's callback is called.
5647 * @param {Object} This DataProxy object.
5648 * @param {Object} o The data object.
5649 * @param {Object} arg The callback argument object passed to the load function.
5653 * @event loadexception
5654 * Fires if an Exception occurs during data retrieval.
5655 * @param {Object} This DataProxy object.
5656 * @param {Object} o The data object.
5657 * @param {Object} arg The callback argument object passed to the load function.
5658 * @param {Object} e The Exception.
5660 loadexception : true
5662 Roo.data.DataProxy.superclass.constructor.call(this);
5665 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
5668 * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
5672 * Ext JS Library 1.1.1
5673 * Copyright(c) 2006-2007, Ext JS, LLC.
5675 * Originally Released Under LGPL - original licence link has changed is not relivant.
5678 * <script type="text/javascript">
5681 * @class Roo.data.MemoryProxy
5682 * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
5683 * to the Reader when its load method is called.
5685 * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
5687 Roo.data.MemoryProxy = function(data){
5691 Roo.data.MemoryProxy.superclass.constructor.call(this);
5695 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
5697 * Load data from the requested source (in this case an in-memory
5698 * data object passed to the constructor), read the data object into
5699 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
5700 * process that block using the passed callback.
5701 * @param {Object} params This parameter is not used by the MemoryProxy class.
5702 * @param {Roo.data.DataReader} reader The Reader object which converts the data
5703 * object into a block of Roo.data.Records.
5704 * @param {Function} callback The function into which to pass the block of Roo.data.records.
5705 * The function must be passed <ul>
5706 * <li>The Record block object</li>
5707 * <li>The "arg" argument from the load function</li>
5708 * <li>A boolean success indicator</li>
5710 * @param {Object} scope The scope in which to call the callback
5711 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
5713 load : function(params, reader, callback, scope, arg){
5714 params = params || {};
5717 result = reader.readRecords(this.data);
5719 this.fireEvent("loadexception", this, arg, null, e);
5720 callback.call(scope, null, arg, false);
5723 callback.call(scope, result, arg, true);
5727 update : function(params, records){
5732 * Ext JS Library 1.1.1
5733 * Copyright(c) 2006-2007, Ext JS, LLC.
5735 * Originally Released Under LGPL - original licence link has changed is not relivant.
5738 * <script type="text/javascript">
5741 * @class Roo.data.HttpProxy
5742 * @extends Roo.data.DataProxy
5743 * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
5744 * configured to reference a certain URL.<br><br>
5746 * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
5747 * from which the running page was served.<br><br>
5749 * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
5751 * Be aware that to enable the browser to parse an XML document, the server must set
5752 * the Content-Type header in the HTTP response to "text/xml".
5754 * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
5755 * an {@link Roo.data.Connection} object. If a Connection config is passed, the singleton {@link Roo.Ajax} object
5756 * will be used to make the request.
5758 Roo.data.HttpProxy = function(conn){
5759 Roo.data.HttpProxy.superclass.constructor.call(this);
5760 // is conn a conn config or a real conn?
5762 this.useAjax = !conn || !conn.events;
5766 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
5767 // thse are take from connection...
5770 * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
5773 * @cfg {Object} extraParams (Optional) An object containing properties which are used as
5774 * extra parameters to each request made by this object. (defaults to undefined)
5777 * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
5778 * to each request made by this object. (defaults to undefined)
5781 * @cfg {String} method (Optional) The default HTTP method to be used for requests. (defaults to undefined; if not set but parms are present will use POST, otherwise GET)
5784 * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
5787 * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
5793 * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
5797 * Return the {@link Roo.data.Connection} object being used by this Proxy.
5798 * @return {Connection} The Connection object. This object may be used to subscribe to events on
5799 * a finer-grained basis than the DataProxy events.
5801 getConnection : function(){
5802 return this.useAjax ? Roo.Ajax : this.conn;
5806 * Load data from the configured {@link Roo.data.Connection}, read the data object into
5807 * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
5808 * process that block using the passed callback.
5809 * @param {Object} params An object containing properties which are to be used as HTTP parameters
5810 * for the request to the remote server.
5811 * @param {Roo.data.DataReader} reader The Reader object which converts the data
5812 * object into a block of Roo.data.Records.
5813 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
5814 * The function must be passed <ul>
5815 * <li>The Record block object</li>
5816 * <li>The "arg" argument from the load function</li>
5817 * <li>A boolean success indicator</li>
5819 * @param {Object} scope The scope in which to call the callback
5820 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
5822 load : function(params, reader, callback, scope, arg){
5823 if(this.fireEvent("beforeload", this, params) !== false){
5825 params : params || {},
5827 callback : callback,
5832 callback : this.loadResponse,
5836 Roo.applyIf(o, this.conn);
5837 if(this.activeRequest){
5838 Roo.Ajax.abort(this.activeRequest);
5840 this.activeRequest = Roo.Ajax.request(o);
5842 this.conn.request(o);
5845 callback.call(scope||this, null, arg, false);
5850 loadResponse : function(o, success, response){
5851 delete this.activeRequest;
5853 this.fireEvent("loadexception", this, o, response);
5854 o.request.callback.call(o.request.scope, null, o.request.arg, false);
5859 result = o.reader.read(response);
5861 this.fireEvent("loadexception", this, o, response, e);
5862 o.request.callback.call(o.request.scope, null, o.request.arg, false);
5866 this.fireEvent("load", this, o, o.request.arg);
5867 o.request.callback.call(o.request.scope, result, o.request.arg, true);
5871 update : function(dataSet){
5876 updateResponse : function(dataSet){
5881 * Ext JS Library 1.1.1
5882 * Copyright(c) 2006-2007, Ext JS, LLC.
5884 * Originally Released Under LGPL - original licence link has changed is not relivant.
5887 * <script type="text/javascript">
5891 * @class Roo.data.ScriptTagProxy
5892 * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
5893 * other than the originating domain of the running page.<br><br>
5895 * <em>Note that if you are retrieving data from a page that is in a domain that is NOT the same as the originating domain
5896 * of the running page, you must use this class, rather than DataProxy.</em><br><br>
5898 * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
5899 * source code that is used as the source inside a <script> tag.<br><br>
5901 * In order for the browser to process the returned data, the server must wrap the data object
5902 * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
5903 * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
5904 * depending on whether the callback name was passed:
5907 boolean scriptTag = false;
5908 String cb = request.getParameter("callback");
5911 response.setContentType("text/javascript");
5913 response.setContentType("application/x-json");
5915 Writer out = response.getWriter();
5917 out.write(cb + "(");
5919 out.print(dataBlock.toJsonString());
5926 * @param {Object} config A configuration object.
5928 Roo.data.ScriptTagProxy = function(config){
5929 Roo.data.ScriptTagProxy.superclass.constructor.call(this);
5930 Roo.apply(this, config);
5931 this.head = document.getElementsByTagName("head")[0];
5934 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
5936 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
5938 * @cfg {String} url The URL from which to request the data object.
5941 * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
5945 * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
5946 * the server the name of the callback function set up by the load call to process the returned data object.
5947 * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
5948 * javascript output which calls this named function passing the data object as its only parameter.
5950 callbackParam : "callback",
5952 * @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
5953 * name to the request.
5958 * Load data from the configured URL, read the data object into
5959 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
5960 * process that block using the passed callback.
5961 * @param {Object} params An object containing properties which are to be used as HTTP parameters
5962 * for the request to the remote server.
5963 * @param {Roo.data.DataReader} reader The Reader object which converts the data
5964 * object into a block of Roo.data.Records.
5965 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
5966 * The function must be passed <ul>
5967 * <li>The Record block object</li>
5968 * <li>The "arg" argument from the load function</li>
5969 * <li>A boolean success indicator</li>
5971 * @param {Object} scope The scope in which to call the callback
5972 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
5974 load : function(params, reader, callback, scope, arg){
5975 if(this.fireEvent("beforeload", this, params) !== false){
5977 var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
5980 url += (url.indexOf("?") != -1 ? "&" : "?") + p;
5982 url += "&_dc=" + (new Date().getTime());
5984 var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
5987 cb : "stcCallback"+transId,
5988 scriptId : "stcScript"+transId,
5992 callback : callback,
5998 window[trans.cb] = function(o){
5999 conn.handleResponse(o, trans);
6002 url += String.format("&{0}={1}", this.callbackParam, trans.cb);
6004 if(this.autoAbort !== false){
6008 trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
6010 var script = document.createElement("script");
6011 script.setAttribute("src", url);
6012 script.setAttribute("type", "text/javascript");
6013 script.setAttribute("id", trans.scriptId);
6014 this.head.appendChild(script);
6018 callback.call(scope||this, null, arg, false);
6023 isLoading : function(){
6024 return this.trans ? true : false;
6028 * Abort the current server request.
6031 if(this.isLoading()){
6032 this.destroyTrans(this.trans);
6037 destroyTrans : function(trans, isLoaded){
6038 this.head.removeChild(document.getElementById(trans.scriptId));
6039 clearTimeout(trans.timeoutId);
6041 window[trans.cb] = undefined;
6043 delete window[trans.cb];
6046 // if hasn't been loaded, wait for load to remove it to prevent script error
6047 window[trans.cb] = function(){
6048 window[trans.cb] = undefined;
6050 delete window[trans.cb];
6057 handleResponse : function(o, trans){
6059 this.destroyTrans(trans, true);
6062 result = trans.reader.readRecords(o);
6064 this.fireEvent("loadexception", this, o, trans.arg, e);
6065 trans.callback.call(trans.scope||window, null, trans.arg, false);
6068 this.fireEvent("load", this, o, trans.arg);
6069 trans.callback.call(trans.scope||window, result, trans.arg, true);
6073 handleFailure : function(trans){
6075 this.destroyTrans(trans, false);
6076 this.fireEvent("loadexception", this, null, trans.arg);
6077 trans.callback.call(trans.scope||window, null, trans.arg, false);
6081 * Ext JS Library 1.1.1
6082 * Copyright(c) 2006-2007, Ext JS, LLC.
6084 * Originally Released Under LGPL - original licence link has changed is not relivant.
6087 * <script type="text/javascript">
6091 * @class Roo.data.JsonReader
6092 * @extends Roo.data.DataReader
6093 * Data reader class to create an Array of Roo.data.Record objects from a JSON response
6094 * based on mappings in a provided Roo.data.Record constructor.
6096 * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
6097 * in the reply previously.
6102 var RecordDef = Roo.data.Record.create([
6103 {name: 'name', mapping: 'name'}, // "mapping" property not needed if it's the same as "name"
6104 {name: 'occupation'} // This field will use "occupation" as the mapping.
6106 var myReader = new Roo.data.JsonReader({
6107 totalProperty: "results", // The property which contains the total dataset size (optional)
6108 root: "rows", // The property which contains an Array of row objects
6109 id: "id" // The property within each row object that provides an ID for the record (optional)
6113 * This would consume a JSON file like this:
6115 { 'results': 2, 'rows': [
6116 { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
6117 { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
6120 * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
6121 * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
6122 * paged from the remote server.
6123 * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
6124 * @cfg {String} root name of the property which contains the Array of row objects.
6125 * @cfg {String} id Name of the property within a row object that contains a record identifier value.
6127 * Create a new JsonReader
6128 * @param {Object} meta Metadata configuration options
6129 * @param {Object} recordType Either an Array of field definition objects,
6130 * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
6132 Roo.data.JsonReader = function(meta, recordType){
6135 // set some defaults:
6137 totalProperty: 'total',
6138 successProperty : 'success',
6143 Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
6145 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
6148 * @prop {Boolean} metaFromRemote - if the meta data was loaded from the remote source.
6149 * Used by Store query builder to append _requestMeta to params.
6152 metaFromRemote : false,
6154 * This method is only used by a DataProxy which has retrieved data from a remote server.
6155 * @param {Object} response The XHR object which contains the JSON data in its responseText.
6156 * @return {Object} data A data block which is used by an Roo.data.Store object as
6157 * a cache of Roo.data.Records.
6159 read : function(response){
6160 var json = response.responseText;
6162 var o = /* eval:var:o */ eval("("+json+")");
6164 throw {message: "JsonReader.read: Json object not found"};
6170 this.metaFromRemote = true;
6171 this.meta = o.metaData;
6172 this.recordType = Roo.data.Record.create(o.metaData.fields);
6173 this.onMetaChange(this.meta, this.recordType, o);
6175 return this.readRecords(o);
6178 // private function a store will implement
6179 onMetaChange : function(meta, recordType, o){
6186 simpleAccess: function(obj, subsc) {
6193 getJsonAccessor: function(){
6195 return function(expr) {
6197 return(re.test(expr))
6198 ? new Function("obj", "return obj." + expr)
6208 * Create a data block containing Roo.data.Records from an XML document.
6209 * @param {Object} o An object which contains an Array of row objects in the property specified
6210 * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
6211 * which contains the total size of the dataset.
6212 * @return {Object} data A data block which is used by an Roo.data.Store object as
6213 * a cache of Roo.data.Records.
6215 readRecords : function(o){
6217 * After any data loads, the raw JSON data is available for further custom processing.
6221 var s = this.meta, Record = this.recordType,
6222 f = Record.prototype.fields, fi = f.items, fl = f.length;
6224 // Generate extraction functions for the totalProperty, the root, the id, and for each field
6226 if(s.totalProperty) {
6227 this.getTotal = this.getJsonAccessor(s.totalProperty);
6229 if(s.successProperty) {
6230 this.getSuccess = this.getJsonAccessor(s.successProperty);
6232 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
6234 var g = this.getJsonAccessor(s.id);
6235 this.getId = function(rec) {
6237 return (r === undefined || r === "") ? null : r;
6240 this.getId = function(){return null;};
6243 for(var jj = 0; jj < fl; jj++){
6245 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
6246 this.ef[jj] = this.getJsonAccessor(map);
6250 var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
6251 if(s.totalProperty){
6252 var vt = parseInt(this.getTotal(o), 10);
6257 if(s.successProperty){
6258 var vs = this.getSuccess(o);
6259 if(vs === false || vs === 'false'){
6264 for(var i = 0; i < c; i++){
6267 var id = this.getId(n);
6268 for(var j = 0; j < fl; j++){
6270 var v = this.ef[j](n);
6272 Roo.log('missing convert for ' + f.name);
6276 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
6278 var record = new Record(values, id);
6280 records[i] = record;
6285 totalRecords : totalRecords
6290 * Ext JS Library 1.1.1
6291 * Copyright(c) 2006-2007, Ext JS, LLC.
6293 * Originally Released Under LGPL - original licence link has changed is not relivant.
6296 * <script type="text/javascript">
6300 * @class Roo.data.XmlReader
6301 * @extends Roo.data.DataReader
6302 * Data reader class to create an Array of {@link Roo.data.Record} objects from an XML document
6303 * based on mappings in a provided Roo.data.Record constructor.<br><br>
6305 * <em>Note that in order for the browser to parse a returned XML document, the Content-Type
6306 * header in the HTTP response must be set to "text/xml".</em>
6310 var RecordDef = Roo.data.Record.create([
6311 {name: 'name', mapping: 'name'}, // "mapping" property not needed if it's the same as "name"
6312 {name: 'occupation'} // This field will use "occupation" as the mapping.
6314 var myReader = new Roo.data.XmlReader({
6315 totalRecords: "results", // The element which contains the total dataset size (optional)
6316 record: "row", // The repeated element which contains row information
6317 id: "id" // The element within the row that provides an ID for the record (optional)
6321 * This would consume an XML file like this:
6325 <results>2</results>
6328 <name>Bill</name>
6329 <occupation>Gardener</occupation>
6333 <name>Ben</name>
6334 <occupation>Horticulturalist</occupation>
6338 * @cfg {String} totalRecords The DomQuery path from which to retrieve the total number of records
6339 * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
6340 * paged from the remote server.
6341 * @cfg {String} record The DomQuery path to the repeated element which contains record information.
6342 * @cfg {String} success The DomQuery path to the success attribute used by forms.
6343 * @cfg {String} id The DomQuery path relative from the record element to the element that contains
6344 * a record identifier value.
6346 * Create a new XmlReader
6347 * @param {Object} meta Metadata configuration options
6348 * @param {Mixed} recordType The definition of the data record type to produce. This can be either a valid
6349 * Record subclass created with {@link Roo.data.Record#create}, or an array of objects with which to call
6350 * Roo.data.Record.create. See the {@link Roo.data.Record} class for more details.
6352 Roo.data.XmlReader = function(meta, recordType){
6354 Roo.data.XmlReader.superclass.constructor.call(this, meta, recordType||meta.fields);
6356 Roo.extend(Roo.data.XmlReader, Roo.data.DataReader, {
6358 * This method is only used by a DataProxy which has retrieved data from a remote server.
6359 * @param {Object} response The XHR object which contains the parsed XML document. The response is expected
6360 * to contain a method called 'responseXML' that returns an XML document object.
6361 * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
6362 * a cache of Roo.data.Records.
6364 read : function(response){
6365 var doc = response.responseXML;
6367 throw {message: "XmlReader.read: XML Document not available"};
6369 return this.readRecords(doc);
6373 * Create a data block containing Roo.data.Records from an XML document.
6374 * @param {Object} doc A parsed XML document.
6375 * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
6376 * a cache of Roo.data.Records.
6378 readRecords : function(doc){
6380 * After any data loads/reads, the raw XML Document is available for further custom processing.
6384 var root = doc.documentElement || doc;
6385 var q = Roo.DomQuery;
6386 var recordType = this.recordType, fields = recordType.prototype.fields;
6387 var sid = this.meta.id;
6388 var totalRecords = 0, success = true;
6389 if(this.meta.totalRecords){
6390 totalRecords = q.selectNumber(this.meta.totalRecords, root, 0);
6393 if(this.meta.success){
6394 var sv = q.selectValue(this.meta.success, root, true);
6395 success = sv !== false && sv !== 'false';
6398 var ns = q.select(this.meta.record, root);
6399 for(var i = 0, len = ns.length; i < len; i++) {
6402 var id = sid ? q.selectValue(sid, n) : undefined;
6403 for(var j = 0, jlen = fields.length; j < jlen; j++){
6404 var f = fields.items[j];
6405 var v = q.selectValue(f.mapping || f.name, n, f.defaultValue);
6409 var record = new recordType(values, id);
6411 records[records.length] = record;
6417 totalRecords : totalRecords || records.length
6422 * Ext JS Library 1.1.1
6423 * Copyright(c) 2006-2007, Ext JS, LLC.
6425 * Originally Released Under LGPL - original licence link has changed is not relivant.
6428 * <script type="text/javascript">
6432 * @class Roo.data.ArrayReader
6433 * @extends Roo.data.DataReader
6434 * Data reader class to create an Array of Roo.data.Record objects from an Array.
6435 * Each element of that Array represents a row of data fields. The
6436 * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
6437 * of the field definition if it exists, or the field's ordinal position in the definition.<br>
6441 var RecordDef = Roo.data.Record.create([
6442 {name: 'name', mapping: 1}, // "mapping" only needed if an "id" field is present which
6443 {name: 'occupation', mapping: 2} // precludes using the ordinal position as the index.
6445 var myReader = new Roo.data.ArrayReader({
6446 id: 0 // The subscript within row Array that provides an ID for the Record (optional)
6450 * This would consume an Array like this:
6452 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
6454 * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
6456 * Create a new JsonReader
6457 * @param {Object} meta Metadata configuration options.
6458 * @param {Object} recordType Either an Array of field definition objects
6459 * as specified to {@link Roo.data.Record#create},
6460 * or an {@link Roo.data.Record} object
6461 * created using {@link Roo.data.Record#create}.
6463 Roo.data.ArrayReader = function(meta, recordType){
6464 Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
6467 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
6469 * Create a data block containing Roo.data.Records from an XML document.
6470 * @param {Object} o An Array of row objects which represents the dataset.
6471 * @return {Object} data A data block which is used by an Roo.data.Store object as
6472 * a cache of Roo.data.Records.
6474 readRecords : function(o){
6475 var sid = this.meta ? this.meta.id : null;
6476 var recordType = this.recordType, fields = recordType.prototype.fields;
6479 for(var i = 0; i < root.length; i++){
6482 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
6483 for(var j = 0, jlen = fields.length; j < jlen; j++){
6484 var f = fields.items[j];
6485 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
6486 var v = n[k] !== undefined ? n[k] : f.defaultValue;
6490 var record = new recordType(values, id);
6492 records[records.length] = record;
6496 totalRecords : records.length
6501 * Ext JS Library 1.1.1
6502 * Copyright(c) 2006-2007, Ext JS, LLC.
6504 * Originally Released Under LGPL - original licence link has changed is not relivant.
6507 * <script type="text/javascript">
6512 * @class Roo.data.Tree
6513 * @extends Roo.util.Observable
6514 * Represents a tree data structure and bubbles all the events for its nodes. The nodes
6515 * in the tree have most standard DOM functionality.
6517 * @param {Node} root (optional) The root node
6519 Roo.data.Tree = function(root){
6522 * The root node for this tree
6527 this.setRootNode(root);
6532 * Fires when a new child node is appended to a node in this tree.
6533 * @param {Tree} tree The owner tree
6534 * @param {Node} parent The parent node
6535 * @param {Node} node The newly appended node
6536 * @param {Number} index The index of the newly appended node
6541 * Fires when a child node is removed from a node in this tree.
6542 * @param {Tree} tree The owner tree
6543 * @param {Node} parent The parent node
6544 * @param {Node} node The child node removed
6549 * Fires when a node is moved to a new location in the tree
6550 * @param {Tree} tree The owner tree
6551 * @param {Node} node The node moved
6552 * @param {Node} oldParent The old parent of this node
6553 * @param {Node} newParent The new parent of this node
6554 * @param {Number} index The index it was moved to
6559 * Fires when a new child node is inserted in a node in this tree.
6560 * @param {Tree} tree The owner tree
6561 * @param {Node} parent The parent node
6562 * @param {Node} node The child node inserted
6563 * @param {Node} refNode The child node the node was inserted before
6567 * @event beforeappend
6568 * Fires before a new child is appended to a node in this tree, return false to cancel the append.
6569 * @param {Tree} tree The owner tree
6570 * @param {Node} parent The parent node
6571 * @param {Node} node The child node to be appended
6573 "beforeappend" : true,
6575 * @event beforeremove
6576 * Fires before a child is removed from a node in this tree, return false to cancel the remove.
6577 * @param {Tree} tree The owner tree
6578 * @param {Node} parent The parent node
6579 * @param {Node} node The child node to be removed
6581 "beforeremove" : true,
6584 * Fires before a node is moved to a new location in the tree. Return false to cancel the move.
6585 * @param {Tree} tree The owner tree
6586 * @param {Node} node The node being moved
6587 * @param {Node} oldParent The parent of the node
6588 * @param {Node} newParent The new parent the node is moving to
6589 * @param {Number} index The index it is being moved to
6591 "beforemove" : true,
6593 * @event beforeinsert
6594 * Fires before a new child is inserted in a node in this tree, return false to cancel the insert.
6595 * @param {Tree} tree The owner tree
6596 * @param {Node} parent The parent node
6597 * @param {Node} node The child node to be inserted
6598 * @param {Node} refNode The child node the node is being inserted before
6600 "beforeinsert" : true
6603 Roo.data.Tree.superclass.constructor.call(this);
6606 Roo.extend(Roo.data.Tree, Roo.util.Observable, {
6609 proxyNodeEvent : function(){
6610 return this.fireEvent.apply(this, arguments);
6614 * Returns the root node for this tree.
6617 getRootNode : function(){
6622 * Sets the root node for this tree.
6623 * @param {Node} node
6626 setRootNode : function(node){
6628 node.ownerTree = this;
6630 this.registerNode(node);
6635 * Gets a node in this tree by its id.
6636 * @param {String} id
6639 getNodeById : function(id){
6640 return this.nodeHash[id];
6643 registerNode : function(node){
6644 this.nodeHash[node.id] = node;
6647 unregisterNode : function(node){
6648 delete this.nodeHash[node.id];
6651 toString : function(){
6652 return "[Tree"+(this.id?" "+this.id:"")+"]";
6657 * @class Roo.data.Node
6658 * @extends Roo.util.Observable
6659 * @cfg {Boolean} leaf true if this node is a leaf and does not have children
6660 * @cfg {String} id The id for this node. If one is not specified, one is generated.
6662 * @param {Object} attributes The attributes/config for the node
6664 Roo.data.Node = function(attributes){
6666 * The attributes supplied for the node. You can use this property to access any custom attributes you supplied.
6669 this.attributes = attributes || {};
6670 this.leaf = this.attributes.leaf;
6672 * The node id. @type String
6674 this.id = this.attributes.id;
6676 this.id = Roo.id(null, "ynode-");
6677 this.attributes.id = this.id;
6682 * All child nodes of this node. @type Array
6684 this.childNodes = [];
6685 if(!this.childNodes.indexOf){ // indexOf is a must
6686 this.childNodes.indexOf = function(o){
6687 for(var i = 0, len = this.length; i < len; i++){
6696 * The parent node for this node. @type Node
6698 this.parentNode = null;
6700 * The first direct child node of this node, or null if this node has no child nodes. @type Node
6702 this.firstChild = null;
6704 * The last direct child node of this node, or null if this node has no child nodes. @type Node
6706 this.lastChild = null;
6708 * The node immediately preceding this node in the tree, or null if there is no sibling node. @type Node
6710 this.previousSibling = null;
6712 * The node immediately following this node in the tree, or null if there is no sibling node. @type Node
6714 this.nextSibling = null;
6719 * Fires when a new child node is appended
6720 * @param {Tree} tree The owner tree
6721 * @param {Node} this This node
6722 * @param {Node} node The newly appended node
6723 * @param {Number} index The index of the newly appended node
6728 * Fires when a child node is removed
6729 * @param {Tree} tree The owner tree
6730 * @param {Node} this This node
6731 * @param {Node} node The removed node
6736 * Fires when this node is moved to a new location in the tree
6737 * @param {Tree} tree The owner tree
6738 * @param {Node} this This node
6739 * @param {Node} oldParent The old parent of this node
6740 * @param {Node} newParent The new parent of this node
6741 * @param {Number} index The index it was moved to
6746 * Fires when a new child node is inserted.
6747 * @param {Tree} tree The owner tree
6748 * @param {Node} this This node
6749 * @param {Node} node The child node inserted
6750 * @param {Node} refNode The child node the node was inserted before
6754 * @event beforeappend
6755 * Fires before a new child is appended, return false to cancel the append.
6756 * @param {Tree} tree The owner tree
6757 * @param {Node} this This node
6758 * @param {Node} node The child node to be appended
6760 "beforeappend" : true,
6762 * @event beforeremove
6763 * Fires before a child is removed, return false to cancel the remove.
6764 * @param {Tree} tree The owner tree
6765 * @param {Node} this This node
6766 * @param {Node} node The child node to be removed
6768 "beforeremove" : true,
6771 * Fires before this node is moved to a new location in the tree. Return false to cancel the move.
6772 * @param {Tree} tree The owner tree
6773 * @param {Node} this This node
6774 * @param {Node} oldParent The parent of this node
6775 * @param {Node} newParent The new parent this node is moving to
6776 * @param {Number} index The index it is being moved to
6778 "beforemove" : true,
6780 * @event beforeinsert
6781 * Fires before a new child is inserted, return false to cancel the insert.
6782 * @param {Tree} tree The owner tree
6783 * @param {Node} this This node
6784 * @param {Node} node The child node to be inserted
6785 * @param {Node} refNode The child node the node is being inserted before
6787 "beforeinsert" : true
6789 this.listeners = this.attributes.listeners;
6790 Roo.data.Node.superclass.constructor.call(this);
6793 Roo.extend(Roo.data.Node, Roo.util.Observable, {
6794 fireEvent : function(evtName){
6795 // first do standard event for this node
6796 if(Roo.data.Node.superclass.fireEvent.apply(this, arguments) === false){
6799 // then bubble it up to the tree if the event wasn't cancelled
6800 var ot = this.getOwnerTree();
6802 if(ot.proxyNodeEvent.apply(ot, arguments) === false){
6810 * Returns true if this node is a leaf
6813 isLeaf : function(){
6814 return this.leaf === true;
6818 setFirstChild : function(node){
6819 this.firstChild = node;
6823 setLastChild : function(node){
6824 this.lastChild = node;
6829 * Returns true if this node is the last child of its parent
6832 isLast : function(){
6833 return (!this.parentNode ? true : this.parentNode.lastChild == this);
6837 * Returns true if this node is the first child of its parent
6840 isFirst : function(){
6841 return (!this.parentNode ? true : this.parentNode.firstChild == this);
6844 hasChildNodes : function(){
6845 return !this.isLeaf() && this.childNodes.length > 0;
6849 * Insert node(s) as the last child node of this node.
6850 * @param {Node/Array} node The node or Array of nodes to append
6851 * @return {Node} The appended node if single append, or null if an array was passed
6853 appendChild : function(node){
6855 if(node instanceof Array){
6857 }else if(arguments.length > 1){
6860 // if passed an array or multiple args do them one by one
6862 for(var i = 0, len = multi.length; i < len; i++) {
6863 this.appendChild(multi[i]);
6866 if(this.fireEvent("beforeappend", this.ownerTree, this, node) === false){
6869 var index = this.childNodes.length;
6870 var oldParent = node.parentNode;
6871 // it's a move, make sure we move it cleanly
6873 if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index) === false){
6876 oldParent.removeChild(node);
6878 index = this.childNodes.length;
6880 this.setFirstChild(node);
6882 this.childNodes.push(node);
6883 node.parentNode = this;
6884 var ps = this.childNodes[index-1];
6886 node.previousSibling = ps;
6887 ps.nextSibling = node;
6889 node.previousSibling = null;
6891 node.nextSibling = null;
6892 this.setLastChild(node);
6893 node.setOwnerTree(this.getOwnerTree());
6894 this.fireEvent("append", this.ownerTree, this, node, index);
6896 node.fireEvent("move", this.ownerTree, node, oldParent, this, index);
6903 * Removes a child node from this node.
6904 * @param {Node} node The node to remove
6905 * @return {Node} The removed node
6907 removeChild : function(node){
6908 var index = this.childNodes.indexOf(node);
6912 if(this.fireEvent("beforeremove", this.ownerTree, this, node) === false){
6916 // remove it from childNodes collection
6917 this.childNodes.splice(index, 1);
6920 if(node.previousSibling){
6921 node.previousSibling.nextSibling = node.nextSibling;
6923 if(node.nextSibling){
6924 node.nextSibling.previousSibling = node.previousSibling;
6927 // update child refs
6928 if(this.firstChild == node){
6929 this.setFirstChild(node.nextSibling);
6931 if(this.lastChild == node){
6932 this.setLastChild(node.previousSibling);
6935 node.setOwnerTree(null);
6936 // clear any references from the node
6937 node.parentNode = null;
6938 node.previousSibling = null;
6939 node.nextSibling = null;
6940 this.fireEvent("remove", this.ownerTree, this, node);
6945 * Inserts the first node before the second node in this nodes childNodes collection.
6946 * @param {Node} node The node to insert
6947 * @param {Node} refNode The node to insert before (if null the node is appended)
6948 * @return {Node} The inserted node
6950 insertBefore : function(node, refNode){
6951 if(!refNode){ // like standard Dom, refNode can be null for append
6952 return this.appendChild(node);
6955 if(node == refNode){
6959 if(this.fireEvent("beforeinsert", this.ownerTree, this, node, refNode) === false){
6962 var index = this.childNodes.indexOf(refNode);
6963 var oldParent = node.parentNode;
6964 var refIndex = index;
6966 // when moving internally, indexes will change after remove
6967 if(oldParent == this && this.childNodes.indexOf(node) < index){
6971 // it's a move, make sure we move it cleanly
6973 if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index, refNode) === false){
6976 oldParent.removeChild(node);
6979 this.setFirstChild(node);
6981 this.childNodes.splice(refIndex, 0, node);
6982 node.parentNode = this;
6983 var ps = this.childNodes[refIndex-1];
6985 node.previousSibling = ps;
6986 ps.nextSibling = node;
6988 node.previousSibling = null;
6990 node.nextSibling = refNode;
6991 refNode.previousSibling = node;
6992 node.setOwnerTree(this.getOwnerTree());
6993 this.fireEvent("insert", this.ownerTree, this, node, refNode);
6995 node.fireEvent("move", this.ownerTree, node, oldParent, this, refIndex, refNode);
7001 * Returns the child node at the specified index.
7002 * @param {Number} index
7005 item : function(index){
7006 return this.childNodes[index];
7010 * Replaces one child node in this node with another.
7011 * @param {Node} newChild The replacement node
7012 * @param {Node} oldChild The node to replace
7013 * @return {Node} The replaced node
7015 replaceChild : function(newChild, oldChild){
7016 this.insertBefore(newChild, oldChild);
7017 this.removeChild(oldChild);
7022 * Returns the index of a child node
7023 * @param {Node} node
7024 * @return {Number} The index of the node or -1 if it was not found
7026 indexOf : function(child){
7027 return this.childNodes.indexOf(child);
7031 * Returns the tree this node is in.
7034 getOwnerTree : function(){
7035 // if it doesn't have one, look for one
7036 if(!this.ownerTree){
7040 this.ownerTree = p.ownerTree;
7046 return this.ownerTree;
7050 * Returns depth of this node (the root node has a depth of 0)
7053 getDepth : function(){
7056 while(p.parentNode){
7064 setOwnerTree : function(tree){
7065 // if it's move, we need to update everyone
7066 if(tree != this.ownerTree){
7068 this.ownerTree.unregisterNode(this);
7070 this.ownerTree = tree;
7071 var cs = this.childNodes;
7072 for(var i = 0, len = cs.length; i < len; i++) {
7073 cs[i].setOwnerTree(tree);
7076 tree.registerNode(this);
7082 * Returns the path for this node. The path can be used to expand or select this node programmatically.
7083 * @param {String} attr (optional) The attr to use for the path (defaults to the node's id)
7084 * @return {String} The path
7086 getPath : function(attr){
7087 attr = attr || "id";
7088 var p = this.parentNode;
7089 var b = [this.attributes[attr]];
7091 b.unshift(p.attributes[attr]);
7094 var sep = this.getOwnerTree().pathSeparator;
7095 return sep + b.join(sep);
7099 * Bubbles up the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
7100 * function call will be the scope provided or the current node. The arguments to the function
7101 * will be the args provided or the current node. If the function returns false at any point,
7102 * the bubble is stopped.
7103 * @param {Function} fn The function to call
7104 * @param {Object} scope (optional) The scope of the function (defaults to current node)
7105 * @param {Array} args (optional) The args to call the function with (default to passing the current node)
7107 bubble : function(fn, scope, args){
7110 if(fn.call(scope || p, args || p) === false){
7118 * Cascades down the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
7119 * function call will be the scope provided or the current node. The arguments to the function
7120 * will be the args provided or the current node. If the function returns false at any point,
7121 * the cascade is stopped on that branch.
7122 * @param {Function} fn The function to call
7123 * @param {Object} scope (optional) The scope of the function (defaults to current node)
7124 * @param {Array} args (optional) The args to call the function with (default to passing the current node)
7126 cascade : function(fn, scope, args){
7127 if(fn.call(scope || this, args || this) !== false){
7128 var cs = this.childNodes;
7129 for(var i = 0, len = cs.length; i < len; i++) {
7130 cs[i].cascade(fn, scope, args);
7136 * Interates the child nodes of this node, calling the specified function with each node. The scope (<i>this</i>) of
7137 * function call will be the scope provided or the current node. The arguments to the function
7138 * will be the args provided or the current node. If the function returns false at any point,
7139 * the iteration stops.
7140 * @param {Function} fn The function to call
7141 * @param {Object} scope (optional) The scope of the function (defaults to current node)
7142 * @param {Array} args (optional) The args to call the function with (default to passing the current node)
7144 eachChild : function(fn, scope, args){
7145 var cs = this.childNodes;
7146 for(var i = 0, len = cs.length; i < len; i++) {
7147 if(fn.call(scope || this, args || cs[i]) === false){
7154 * Finds the first child that has the attribute with the specified value.
7155 * @param {String} attribute The attribute name
7156 * @param {Mixed} value The value to search for
7157 * @return {Node} The found child or null if none was found
7159 findChild : function(attribute, value){
7160 var cs = this.childNodes;
7161 for(var i = 0, len = cs.length; i < len; i++) {
7162 if(cs[i].attributes[attribute] == value){
7170 * Finds the first child by a custom function. The child matches if the function passed
7172 * @param {Function} fn
7173 * @param {Object} scope (optional)
7174 * @return {Node} The found child or null if none was found
7176 findChildBy : function(fn, scope){
7177 var cs = this.childNodes;
7178 for(var i = 0, len = cs.length; i < len; i++) {
7179 if(fn.call(scope||cs[i], cs[i]) === true){
7187 * Sorts this nodes children using the supplied sort function
7188 * @param {Function} fn
7189 * @param {Object} scope (optional)
7191 sort : function(fn, scope){
7192 var cs = this.childNodes;
7193 var len = cs.length;
7195 var sortFn = scope ? function(){fn.apply(scope, arguments);} : fn;
7197 for(var i = 0; i < len; i++){
7199 n.previousSibling = cs[i-1];
7200 n.nextSibling = cs[i+1];
7202 this.setFirstChild(n);
7205 this.setLastChild(n);
7212 * Returns true if this node is an ancestor (at any point) of the passed node.
7213 * @param {Node} node
7216 contains : function(node){
7217 return node.isAncestor(this);
7221 * Returns true if the passed node is an ancestor (at any point) of this node.
7222 * @param {Node} node
7225 isAncestor : function(node){
7226 var p = this.parentNode;
7236 toString : function(){
7237 return "[Node"+(this.id?" "+this.id:"")+"]";
7241 * Ext JS Library 1.1.1
7242 * Copyright(c) 2006-2007, Ext JS, LLC.
7244 * Originally Released Under LGPL - original licence link has changed is not relivant.
7247 * <script type="text/javascript">
7252 * @class Roo.ComponentMgr
7253 * Provides a common registry of all components on a page so that they can be easily accessed by component id (see {@link Roo.getCmp}).
7256 Roo.ComponentMgr = function(){
7257 var all = new Roo.util.MixedCollection();
7261 * Registers a component.
7262 * @param {Roo.Component} c The component
7264 register : function(c){
7269 * Unregisters a component.
7270 * @param {Roo.Component} c The component
7272 unregister : function(c){
7277 * Returns a component by id
7278 * @param {String} id The component id
7285 * Registers a function that will be called when a specified component is added to ComponentMgr
7286 * @param {String} id The component id
7287 * @param {Funtction} fn The callback function
7288 * @param {Object} scope The scope of the callback
7290 onAvailable : function(id, fn, scope){
7291 all.on("add", function(index, o){
7293 fn.call(scope || o, o);
7294 all.un("add", fn, scope);
7301 * Ext JS Library 1.1.1
7302 * Copyright(c) 2006-2007, Ext JS, LLC.
7304 * Originally Released Under LGPL - original licence link has changed is not relivant.
7307 * <script type="text/javascript">
7311 * @class Roo.Component
7312 * @extends Roo.util.Observable
7313 * Base class for all major Roo components. All subclasses of Component can automatically participate in the standard
7314 * Roo component lifecycle of creation, rendering and destruction. They also have automatic support for basic hide/show
7315 * and enable/disable behavior. Component allows any subclass to be lazy-rendered into any {@link Roo.Container} and
7316 * to be automatically registered with the {@link Roo.ComponentMgr} so that it can be referenced at any time via {@link Roo.getCmp}.
7317 * All visual components (widgets) that require rendering into a layout should subclass Component.
7319 * @param {Roo.Element/String/Object} config The configuration options. If an element is passed, it is set as the internal
7320 * element and its id used as the component id. If a string is passed, it is assumed to be the id of an existing element
7321 * and is used as the component id. Otherwise, it is assumed to be a standard config object and is applied to the component.
7323 Roo.Component = function(config){
7324 config = config || {};
7325 if(config.tagName || config.dom || typeof config == "string"){ // element object
7326 config = {el: config, id: config.id || config};
7328 this.initialConfig = config;
7330 Roo.apply(this, config);
7334 * Fires after the component is disabled.
7335 * @param {Roo.Component} this
7340 * Fires after the component is enabled.
7341 * @param {Roo.Component} this
7346 * Fires before the component is shown. Return false to stop the show.
7347 * @param {Roo.Component} this
7352 * Fires after the component is shown.
7353 * @param {Roo.Component} this
7358 * Fires before the component is hidden. Return false to stop the hide.
7359 * @param {Roo.Component} this
7364 * Fires after the component is hidden.
7365 * @param {Roo.Component} this
7369 * @event beforerender
7370 * Fires before the component is rendered. Return false to stop the render.
7371 * @param {Roo.Component} this
7373 beforerender : true,
7376 * Fires after the component is rendered.
7377 * @param {Roo.Component} this
7381 * @event beforedestroy
7382 * Fires before the component is destroyed. Return false to stop the destroy.
7383 * @param {Roo.Component} this
7385 beforedestroy : true,
7388 * Fires after the component is destroyed.
7389 * @param {Roo.Component} this
7394 this.id = "ext-comp-" + (++Roo.Component.AUTO_ID);
7396 Roo.ComponentMgr.register(this);
7397 Roo.Component.superclass.constructor.call(this);
7398 this.initComponent();
7399 if(this.renderTo){ // not supported by all components yet. use at your own risk!
7400 this.render(this.renderTo);
7401 delete this.renderTo;
7406 Roo.Component.AUTO_ID = 1000;
7408 Roo.extend(Roo.Component, Roo.util.Observable, {
7410 * @scope Roo.Component.prototype
7412 * true if this component is hidden. Read-only.
7417 * true if this component is disabled. Read-only.
7422 * true if this component has been rendered. Read-only.
7426 /** @cfg {String} disableClass
7427 * CSS class added to the component when it is disabled (defaults to "x-item-disabled").
7429 disabledClass : "x-item-disabled",
7430 /** @cfg {Boolean} allowDomMove
7431 * Whether the component can move the Dom node when rendering (defaults to true).
7433 allowDomMove : true,
7434 /** @cfg {String} hideMode
7435 * How this component should hidden. Supported values are
7436 * "visibility" (css visibility), "offsets" (negative offset position) and
7437 * "display" (css display) - defaults to "display".
7439 hideMode: 'display',
7442 ctype : "Roo.Component",
7445 * @cfg {String} actionMode
7446 * which property holds the element that used for hide() / show() / disable() / enable()
7452 getActionEl : function(){
7453 return this[this.actionMode];
7456 initComponent : Roo.emptyFn,
7458 * If this is a lazy rendering component, render it to its container element.
7459 * @param {String/HTMLElement/Element} container (optional) The element this component should be rendered into. If it is being applied to existing markup, this should be left off.
7461 render : function(container, position){
7462 if(!this.rendered && this.fireEvent("beforerender", this) !== false){
7463 if(!container && this.el){
7464 this.el = Roo.get(this.el);
7465 container = this.el.dom.parentNode;
7466 this.allowDomMove = false;
7468 this.container = Roo.get(container);
7469 this.rendered = true;
7470 if(position !== undefined){
7471 if(typeof position == 'number'){
7472 position = this.container.dom.childNodes[position];
7474 position = Roo.getDom(position);
7477 this.onRender(this.container, position || null);
7479 this.el.addClass(this.cls);
7483 this.el.applyStyles(this.style);
7486 this.fireEvent("render", this);
7487 this.afterRender(this.container);
7499 // default function is not really useful
7500 onRender : function(ct, position){
7502 this.el = Roo.get(this.el);
7503 if(this.allowDomMove !== false){
7504 ct.dom.insertBefore(this.el.dom, position);
7510 getAutoCreate : function(){
7511 var cfg = typeof this.autoCreate == "object" ?
7512 this.autoCreate : Roo.apply({}, this.defaultAutoCreate);
7513 if(this.id && !cfg.id){
7520 afterRender : Roo.emptyFn,
7523 * Destroys this component by purging any event listeners, removing the component's element from the DOM,
7524 * removing the component from its {@link Roo.Container} (if applicable) and unregistering it from {@link Roo.ComponentMgr}.
7526 destroy : function(){
7527 if(this.fireEvent("beforedestroy", this) !== false){
7528 this.purgeListeners();
7529 this.beforeDestroy();
7531 this.el.removeAllListeners();
7533 if(this.actionMode == "container"){
7534 this.container.remove();
7538 Roo.ComponentMgr.unregister(this);
7539 this.fireEvent("destroy", this);
7544 beforeDestroy : function(){
7549 onDestroy : function(){
7554 * Returns the underlying {@link Roo.Element}.
7555 * @return {Roo.Element} The element
7562 * Returns the id of this component.
7570 * Try to focus this component.
7571 * @param {Boolean} selectText True to also select the text in this component (if applicable)
7572 * @return {Roo.Component} this
7574 focus : function(selectText){
7577 if(selectText === true){
7578 this.el.dom.select();
7593 * Disable this component.
7594 * @return {Roo.Component} this
7596 disable : function(){
7600 this.disabled = true;
7601 this.fireEvent("disable", this);
7606 onDisable : function(){
7607 this.getActionEl().addClass(this.disabledClass);
7608 this.el.dom.disabled = true;
7612 * Enable this component.
7613 * @return {Roo.Component} this
7615 enable : function(){
7619 this.disabled = false;
7620 this.fireEvent("enable", this);
7625 onEnable : function(){
7626 this.getActionEl().removeClass(this.disabledClass);
7627 this.el.dom.disabled = false;
7631 * Convenience function for setting disabled/enabled by boolean.
7632 * @param {Boolean} disabled
7634 setDisabled : function(disabled){
7635 this[disabled ? "disable" : "enable"]();
7639 * Show this component.
7640 * @return {Roo.Component} this
7643 if(this.fireEvent("beforeshow", this) !== false){
7644 this.hidden = false;
7648 this.fireEvent("show", this);
7654 onShow : function(){
7655 var ae = this.getActionEl();
7656 if(this.hideMode == 'visibility'){
7657 ae.dom.style.visibility = "visible";
7658 }else if(this.hideMode == 'offsets'){
7659 ae.removeClass('x-hidden');
7661 ae.dom.style.display = "";
7666 * Hide this component.
7667 * @return {Roo.Component} this
7670 if(this.fireEvent("beforehide", this) !== false){
7675 this.fireEvent("hide", this);
7681 onHide : function(){
7682 var ae = this.getActionEl();
7683 if(this.hideMode == 'visibility'){
7684 ae.dom.style.visibility = "hidden";
7685 }else if(this.hideMode == 'offsets'){
7686 ae.addClass('x-hidden');
7688 ae.dom.style.display = "none";
7693 * Convenience function to hide or show this component by boolean.
7694 * @param {Boolean} visible True to show, false to hide
7695 * @return {Roo.Component} this
7697 setVisible: function(visible){
7707 * Returns true if this component is visible.
7709 isVisible : function(){
7710 return this.getActionEl().isVisible();
7713 cloneConfig : function(overrides){
7714 overrides = overrides || {};
7715 var id = overrides.id || Roo.id();
7716 var cfg = Roo.applyIf(overrides, this.initialConfig);
7717 cfg.id = id; // prevent dup id
7718 return new this.constructor(cfg);
7722 * Ext JS Library 1.1.1
7723 * Copyright(c) 2006-2007, Ext JS, LLC.
7725 * Originally Released Under LGPL - original licence link has changed is not relivant.
7728 * <script type="text/javascript">
7733 * @extends Roo.Element
7734 * An extended {@link Roo.Element} object that supports a shadow and shim, constrain to viewport and
7735 * automatic maintaining of shadow/shim positions.
7736 * @cfg {Boolean} shim False to disable the iframe shim in browsers which need one (defaults to true)
7737 * @cfg {String/Boolean} shadow True to create a shadow element with default class "x-layer-shadow", or
7738 * you can pass a string with a CSS class name. False turns off the shadow.
7739 * @cfg {Object} dh DomHelper object config to create element with (defaults to {tag: "div", cls: "x-layer"}).
7740 * @cfg {Boolean} constrain False to disable constrain to viewport (defaults to true)
7741 * @cfg {String} cls CSS class to add to the element
7742 * @cfg {Number} zindex Starting z-index (defaults to 11000)
7743 * @cfg {Number} shadowOffset Number of pixels to offset the shadow (defaults to 3)
7745 * @param {Object} config An object with config options.
7746 * @param {String/HTMLElement} existingEl (optional) Uses an existing DOM element. If the element is not found it creates it.
7749 Roo.Layer = function(config, existingEl){
7750 config = config || {};
7751 var dh = Roo.DomHelper;
7752 var cp = config.parentEl, pel = cp ? Roo.getDom(cp) : document.body;
7754 this.dom = Roo.getDom(existingEl);
7757 var o = config.dh || {tag: "div", cls: "x-layer"};
7758 this.dom = dh.append(pel, o);
7761 this.addClass(config.cls);
7763 this.constrain = config.constrain !== false;
7764 this.visibilityMode = Roo.Element.VISIBILITY;
7766 this.id = this.dom.id = config.id;
7768 this.id = Roo.id(this.dom);
7770 this.zindex = config.zindex || this.getZIndex();
7771 this.position("absolute", this.zindex);
7773 this.shadowOffset = config.shadowOffset || 4;
7774 this.shadow = new Roo.Shadow({
7775 offset : this.shadowOffset,
7776 mode : config.shadow
7779 this.shadowOffset = 0;
7781 this.useShim = config.shim !== false && Roo.useShims;
7782 this.useDisplay = config.useDisplay;
7786 var supr = Roo.Element.prototype;
7788 // shims are shared among layer to keep from having 100 iframes
7791 Roo.extend(Roo.Layer, Roo.Element, {
7793 getZIndex : function(){
7794 return this.zindex || parseInt(this.getStyle("z-index"), 10) || 11000;
7797 getShim : function(){
7804 var shim = shims.shift();
7806 shim = this.createShim();
7807 shim.enableDisplayMode('block');
7808 shim.dom.style.display = 'none';
7809 shim.dom.style.visibility = 'visible';
7811 var pn = this.dom.parentNode;
7812 if(shim.dom.parentNode != pn){
7813 pn.insertBefore(shim.dom, this.dom);
7815 shim.setStyle('z-index', this.getZIndex()-2);
7820 hideShim : function(){
7822 this.shim.setDisplayed(false);
7823 shims.push(this.shim);
7828 disableShadow : function(){
7830 this.shadowDisabled = true;
7832 this.lastShadowOffset = this.shadowOffset;
7833 this.shadowOffset = 0;
7837 enableShadow : function(show){
7839 this.shadowDisabled = false;
7840 this.shadowOffset = this.lastShadowOffset;
7841 delete this.lastShadowOffset;
7849 // this code can execute repeatedly in milliseconds (i.e. during a drag) so
7850 // code size was sacrificed for effeciency (e.g. no getBox/setBox, no XY calls)
7851 sync : function(doShow){
7852 var sw = this.shadow;
7853 if(!this.updating && this.isVisible() && (sw || this.useShim)){
7854 var sh = this.getShim();
7856 var w = this.getWidth(),
7857 h = this.getHeight();
7859 var l = this.getLeft(true),
7860 t = this.getTop(true);
7862 if(sw && !this.shadowDisabled){
7863 if(doShow && !sw.isVisible()){
7866 sw.realign(l, t, w, h);
7872 // fit the shim behind the shadow, so it is shimmed too
7873 var a = sw.adjusts, s = sh.dom.style;
7874 s.left = (Math.min(l, l+a.l))+"px";
7875 s.top = (Math.min(t, t+a.t))+"px";
7876 s.width = (w+a.w)+"px";
7877 s.height = (h+a.h)+"px";
7884 sh.setLeftTop(l, t);
7891 destroy : function(){
7896 this.removeAllListeners();
7897 var pn = this.dom.parentNode;
7899 pn.removeChild(this.dom);
7901 Roo.Element.uncache(this.id);
7904 remove : function(){
7909 beginUpdate : function(){
7910 this.updating = true;
7914 endUpdate : function(){
7915 this.updating = false;
7920 hideUnders : function(negOffset){
7928 constrainXY : function(){
7930 var vw = Roo.lib.Dom.getViewWidth(),
7931 vh = Roo.lib.Dom.getViewHeight();
7932 var s = Roo.get(document).getScroll();
7934 var xy = this.getXY();
7935 var x = xy[0], y = xy[1];
7936 var w = this.dom.offsetWidth+this.shadowOffset, h = this.dom.offsetHeight+this.shadowOffset;
7937 // only move it if it needs it
7939 // first validate right/bottom
7940 if((x + w) > vw+s.left){
7941 x = vw - w - this.shadowOffset;
7944 if((y + h) > vh+s.top){
7945 y = vh - h - this.shadowOffset;
7948 // then make sure top/left isn't negative
7959 var ay = this.avoidY;
7960 if(y <= ay && (y+h) >= ay){
7966 supr.setXY.call(this, xy);
7972 isVisible : function(){
7973 return this.visible;
7977 showAction : function(){
7978 this.visible = true; // track visibility to prevent getStyle calls
7979 if(this.useDisplay === true){
7980 this.setDisplayed("");
7981 }else if(this.lastXY){
7982 supr.setXY.call(this, this.lastXY);
7983 }else if(this.lastLT){
7984 supr.setLeftTop.call(this, this.lastLT[0], this.lastLT[1]);
7989 hideAction : function(){
7990 this.visible = false;
7991 if(this.useDisplay === true){
7992 this.setDisplayed(false);
7994 this.setLeftTop(-10000,-10000);
7998 // overridden Element method
7999 setVisible : function(v, a, d, c, e){
8004 var cb = function(){
8009 }.createDelegate(this);
8010 supr.setVisible.call(this, true, true, d, cb, e);
8013 this.hideUnders(true);
8022 }.createDelegate(this);
8024 supr.setVisible.call(this, v, a, d, cb, e);
8033 storeXY : function(xy){
8038 storeLeftTop : function(left, top){
8040 this.lastLT = [left, top];
8044 beforeFx : function(){
8045 this.beforeAction();
8046 return Roo.Layer.superclass.beforeFx.apply(this, arguments);
8050 afterFx : function(){
8051 Roo.Layer.superclass.afterFx.apply(this, arguments);
8052 this.sync(this.isVisible());
8056 beforeAction : function(){
8057 if(!this.updating && this.shadow){
8062 // overridden Element method
8063 setLeft : function(left){
8064 this.storeLeftTop(left, this.getTop(true));
8065 supr.setLeft.apply(this, arguments);
8069 setTop : function(top){
8070 this.storeLeftTop(this.getLeft(true), top);
8071 supr.setTop.apply(this, arguments);
8075 setLeftTop : function(left, top){
8076 this.storeLeftTop(left, top);
8077 supr.setLeftTop.apply(this, arguments);
8081 setXY : function(xy, a, d, c, e){
8083 this.beforeAction();
8085 var cb = this.createCB(c);
8086 supr.setXY.call(this, xy, a, d, cb, e);
8093 createCB : function(c){
8104 // overridden Element method
8105 setX : function(x, a, d, c, e){
8106 this.setXY([x, this.getY()], a, d, c, e);
8109 // overridden Element method
8110 setY : function(y, a, d, c, e){
8111 this.setXY([this.getX(), y], a, d, c, e);
8114 // overridden Element method
8115 setSize : function(w, h, a, d, c, e){
8116 this.beforeAction();
8117 var cb = this.createCB(c);
8118 supr.setSize.call(this, w, h, a, d, cb, e);
8124 // overridden Element method
8125 setWidth : function(w, a, d, c, e){
8126 this.beforeAction();
8127 var cb = this.createCB(c);
8128 supr.setWidth.call(this, w, a, d, cb, e);
8134 // overridden Element method
8135 setHeight : function(h, a, d, c, e){
8136 this.beforeAction();
8137 var cb = this.createCB(c);
8138 supr.setHeight.call(this, h, a, d, cb, e);
8144 // overridden Element method
8145 setBounds : function(x, y, w, h, a, d, c, e){
8146 this.beforeAction();
8147 var cb = this.createCB(c);
8149 this.storeXY([x, y]);
8150 supr.setXY.call(this, [x, y]);
8151 supr.setSize.call(this, w, h, a, d, cb, e);
8154 supr.setBounds.call(this, x, y, w, h, a, d, cb, e);
8160 * Sets the z-index of this layer and adjusts any shadow and shim z-indexes. The layer z-index is automatically
8161 * incremented by two more than the value passed in so that it always shows above any shadow or shim (the shadow
8162 * element, if any, will be assigned z-index + 1, and the shim element, if any, will be assigned the unmodified z-index).
8163 * @param {Number} zindex The new z-index to set
8164 * @return {this} The Layer
8166 setZIndex : function(zindex){
8167 this.zindex = zindex;
8168 this.setStyle("z-index", zindex + 2);
8170 this.shadow.setZIndex(zindex + 1);
8173 this.shim.setStyle("z-index", zindex);
8179 * Ext JS Library 1.1.1
8180 * Copyright(c) 2006-2007, Ext JS, LLC.
8182 * Originally Released Under LGPL - original licence link has changed is not relivant.
8185 * <script type="text/javascript">
8191 * Simple class that can provide a shadow effect for any element. Note that the element MUST be absolutely positioned,
8192 * and the shadow does not provide any shimming. This should be used only in simple cases -- for more advanced
8193 * functionality that can also provide the same shadow effect, see the {@link Roo.Layer} class.
8195 * Create a new Shadow
8196 * @param {Object} config The config object
8198 Roo.Shadow = function(config){
8199 Roo.apply(this, config);
8200 if(typeof this.mode != "string"){
8201 this.mode = this.defaultMode;
8203 var o = this.offset, a = {h: 0};
8204 var rad = Math.floor(this.offset/2);
8205 switch(this.mode.toLowerCase()){ // all this hideous nonsense calculates the various offsets for shadows
8211 a.l -= this.offset + rad;
8212 a.t -= this.offset + rad;
8223 a.l -= (this.offset - rad);
8224 a.t -= this.offset + rad;
8226 a.w -= (this.offset - rad)*2;
8237 a.l -= (this.offset - rad);
8238 a.t -= (this.offset - rad);
8240 a.w -= (this.offset + rad + 1);
8241 a.h -= (this.offset + rad);
8250 Roo.Shadow.prototype = {
8252 * @cfg {String} mode
8253 * The shadow display mode. Supports the following options:<br />
8254 * sides: Shadow displays on both sides and bottom only<br />
8255 * frame: Shadow displays equally on all four sides<br />
8256 * drop: Traditional bottom-right drop shadow (default)
8259 * @cfg {String} offset
8260 * The number of pixels to offset the shadow from the element (defaults to 4)
8265 defaultMode: "drop",
8268 * Displays the shadow under the target element
8269 * @param {String/HTMLElement/Element} targetEl The id or element under which the shadow should display
8271 show : function(target){
8272 target = Roo.get(target);
8274 this.el = Roo.Shadow.Pool.pull();
8275 if(this.el.dom.nextSibling != target.dom){
8276 this.el.insertBefore(target);
8279 this.el.setStyle("z-index", this.zIndex || parseInt(target.getStyle("z-index"), 10)-1);
8281 this.el.dom.style.filter="progid:DXImageTransform.Microsoft.alpha(opacity=50) progid:DXImageTransform.Microsoft.Blur(pixelradius="+(this.offset)+")";
8284 target.getLeft(true),
8285 target.getTop(true),
8289 this.el.dom.style.display = "block";
8293 * Returns true if the shadow is visible, else false
8295 isVisible : function(){
8296 return this.el ? true : false;
8300 * Direct alignment when values are already available. Show must be called at least once before
8301 * calling this method to ensure it is initialized.
8302 * @param {Number} left The target element left position
8303 * @param {Number} top The target element top position
8304 * @param {Number} width The target element width
8305 * @param {Number} height The target element height
8307 realign : function(l, t, w, h){
8311 var a = this.adjusts, d = this.el.dom, s = d.style;
8313 s.left = (l+a.l)+"px";
8314 s.top = (t+a.t)+"px";
8315 var sw = (w+a.w), sh = (h+a.h), sws = sw +"px", shs = sh + "px";
8317 if(s.width != sws || s.height != shs){
8321 var cn = d.childNodes;
8322 var sww = Math.max(0, (sw-12))+"px";
8323 cn[0].childNodes[1].style.width = sww;
8324 cn[1].childNodes[1].style.width = sww;
8325 cn[2].childNodes[1].style.width = sww;
8326 cn[1].style.height = Math.max(0, (sh-12))+"px";
8336 this.el.dom.style.display = "none";
8337 Roo.Shadow.Pool.push(this.el);
8343 * Adjust the z-index of this shadow
8344 * @param {Number} zindex The new z-index
8346 setZIndex : function(z){
8349 this.el.setStyle("z-index", z);
8354 // Private utility class that manages the internal Shadow cache
8355 Roo.Shadow.Pool = function(){
8357 var markup = Roo.isIE ?
8358 '<div class="x-ie-shadow"></div>' :
8359 '<div class="x-shadow"><div class="xst"><div class="xstl"></div><div class="xstc"></div><div class="xstr"></div></div><div class="xsc"><div class="xsml"></div><div class="xsmc"></div><div class="xsmr"></div></div><div class="xsb"><div class="xsbl"></div><div class="xsbc"></div><div class="xsbr"></div></div></div>';
8364 sh = Roo.get(Roo.DomHelper.insertHtml("beforeBegin", document.body.firstChild, markup));
8365 sh.autoBoxAdjust = false;
8370 push : function(sh){
8376 * Ext JS Library 1.1.1
8377 * Copyright(c) 2006-2007, Ext JS, LLC.
8379 * Originally Released Under LGPL - original licence link has changed is not relivant.
8382 * <script type="text/javascript">
8386 * @class Roo.BoxComponent
8387 * @extends Roo.Component
8388 * Base class for any visual {@link Roo.Component} that uses a box container. BoxComponent provides automatic box
8389 * model adjustments for sizing and positioning and will work correctly withnin the Component rendering model. All
8390 * container classes should subclass BoxComponent so that they will work consistently when nested within other Ext
8391 * layout containers.
8393 * @param {Roo.Element/String/Object} config The configuration options.
8395 Roo.BoxComponent = function(config){
8396 Roo.Component.call(this, config);
8400 * Fires after the component is resized.
8401 * @param {Roo.Component} this
8402 * @param {Number} adjWidth The box-adjusted width that was set
8403 * @param {Number} adjHeight The box-adjusted height that was set
8404 * @param {Number} rawWidth The width that was originally specified
8405 * @param {Number} rawHeight The height that was originally specified
8410 * Fires after the component is moved.
8411 * @param {Roo.Component} this
8412 * @param {Number} x The new x position
8413 * @param {Number} y The new y position
8419 Roo.extend(Roo.BoxComponent, Roo.Component, {
8420 // private, set in afterRender to signify that the component has been rendered
8422 // private, used to defer height settings to subclasses
8424 /** @cfg {Number} width
8425 * width (optional) size of component
8427 /** @cfg {Number} height
8428 * height (optional) size of component
8432 * Sets the width and height of the component. This method fires the resize event. This method can accept
8433 * either width and height as separate numeric arguments, or you can pass a size object like {width:10, height:20}.
8434 * @param {Number/Object} width The new width to set, or a size object in the format {width, height}
8435 * @param {Number} height The new height to set (not required if a size object is passed as the first arg)
8436 * @return {Roo.BoxComponent} this
8438 setSize : function(w, h){
8439 // support for standard size objects
8440 if(typeof w == 'object'){
8451 // prevent recalcs when not needed
8452 if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
8455 this.lastSize = {width: w, height: h};
8457 var adj = this.adjustSize(w, h);
8458 var aw = adj.width, ah = adj.height;
8459 if(aw !== undefined || ah !== undefined){ // this code is nasty but performs better with floaters
8460 var rz = this.getResizeEl();
8461 if(!this.deferHeight && aw !== undefined && ah !== undefined){
8463 }else if(!this.deferHeight && ah !== undefined){
8465 }else if(aw !== undefined){
8468 this.onResize(aw, ah, w, h);
8469 this.fireEvent('resize', this, aw, ah, w, h);
8475 * Gets the current size of the component's underlying element.
8476 * @return {Object} An object containing the element's size {width: (element width), height: (element height)}
8478 getSize : function(){
8479 return this.el.getSize();
8483 * Gets the current XY position of the component's underlying element.
8484 * @param {Boolean} local (optional) If true the element's left and top are returned instead of page XY (defaults to false)
8485 * @return {Array} The XY position of the element (e.g., [100, 200])
8487 getPosition : function(local){
8489 return [this.el.getLeft(true), this.el.getTop(true)];
8491 return this.xy || this.el.getXY();
8495 * Gets the current box measurements of the component's underlying element.
8496 * @param {Boolean} local (optional) If true the element's left and top are returned instead of page XY (defaults to false)
8497 * @returns {Object} box An object in the format {x, y, width, height}
8499 getBox : function(local){
8500 var s = this.el.getSize();
8502 s.x = this.el.getLeft(true);
8503 s.y = this.el.getTop(true);
8505 var xy = this.xy || this.el.getXY();
8513 * Sets the current box measurements of the component's underlying element.
8514 * @param {Object} box An object in the format {x, y, width, height}
8515 * @returns {Roo.BoxComponent} this
8517 updateBox : function(box){
8518 this.setSize(box.width, box.height);
8519 this.setPagePosition(box.x, box.y);
8524 getResizeEl : function(){
8525 return this.resizeEl || this.el;
8529 getPositionEl : function(){
8530 return this.positionEl || this.el;
8534 * Sets the left and top of the component. To set the page XY position instead, use {@link #setPagePosition}.
8535 * This method fires the move event.
8536 * @param {Number} left The new left
8537 * @param {Number} top The new top
8538 * @returns {Roo.BoxComponent} this
8540 setPosition : function(x, y){
8546 var adj = this.adjustPosition(x, y);
8547 var ax = adj.x, ay = adj.y;
8549 var el = this.getPositionEl();
8550 if(ax !== undefined || ay !== undefined){
8551 if(ax !== undefined && ay !== undefined){
8552 el.setLeftTop(ax, ay);
8553 }else if(ax !== undefined){
8555 }else if(ay !== undefined){
8558 this.onPosition(ax, ay);
8559 this.fireEvent('move', this, ax, ay);
8565 * Sets the page XY position of the component. To set the left and top instead, use {@link #setPosition}.
8566 * This method fires the move event.
8567 * @param {Number} x The new x position
8568 * @param {Number} y The new y position
8569 * @returns {Roo.BoxComponent} this
8571 setPagePosition : function(x, y){
8577 if(x === undefined || y === undefined){ // cannot translate undefined points
8580 var p = this.el.translatePoints(x, y);
8581 this.setPosition(p.left, p.top);
8586 onRender : function(ct, position){
8587 Roo.BoxComponent.superclass.onRender.call(this, ct, position);
8589 this.resizeEl = Roo.get(this.resizeEl);
8591 if(this.positionEl){
8592 this.positionEl = Roo.get(this.positionEl);
8597 afterRender : function(){
8598 Roo.BoxComponent.superclass.afterRender.call(this);
8599 this.boxReady = true;
8600 this.setSize(this.width, this.height);
8601 if(this.x || this.y){
8602 this.setPosition(this.x, this.y);
8604 if(this.pageX || this.pageY){
8605 this.setPagePosition(this.pageX, this.pageY);
8610 * Force the component's size to recalculate based on the underlying element's current height and width.
8611 * @returns {Roo.BoxComponent} this
8613 syncSize : function(){
8614 delete this.lastSize;
8615 this.setSize(this.el.getWidth(), this.el.getHeight());
8620 * Called after the component is resized, this method is empty by default but can be implemented by any
8621 * subclass that needs to perform custom logic after a resize occurs.
8622 * @param {Number} adjWidth The box-adjusted width that was set
8623 * @param {Number} adjHeight The box-adjusted height that was set
8624 * @param {Number} rawWidth The width that was originally specified
8625 * @param {Number} rawHeight The height that was originally specified
8627 onResize : function(adjWidth, adjHeight, rawWidth, rawHeight){
8632 * Called after the component is moved, this method is empty by default but can be implemented by any
8633 * subclass that needs to perform custom logic after a move occurs.
8634 * @param {Number} x The new x position
8635 * @param {Number} y The new y position
8637 onPosition : function(x, y){
8642 adjustSize : function(w, h){
8646 if(this.autoHeight){
8649 return {width : w, height: h};
8653 adjustPosition : function(x, y){
8654 return {x : x, y: y};
8658 * Ext JS Library 1.1.1
8659 * Copyright(c) 2006-2007, Ext JS, LLC.
8661 * Originally Released Under LGPL - original licence link has changed is not relivant.
8664 * <script type="text/javascript">
8669 * @class Roo.SplitBar
8670 * @extends Roo.util.Observable
8671 * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
8675 var split = new Roo.SplitBar("elementToDrag", "elementToSize",
8676 Roo.SplitBar.HORIZONTAL, Roo.SplitBar.LEFT);
8677 split.setAdapter(new Roo.SplitBar.AbsoluteLayoutAdapter("container"));
8678 split.minSize = 100;
8679 split.maxSize = 600;
8680 split.animate = true;
8681 split.on('moved', splitterMoved);
8684 * Create a new SplitBar
8685 * @param {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar.
8686 * @param {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged
8687 * @param {Number} orientation (optional) Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
8688 * @param {Number} placement (optional) Either Roo.SplitBar.LEFT or Roo.SplitBar.RIGHT for horizontal or
8689 Roo.SplitBar.TOP or Roo.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
8690 position of the SplitBar).
8692 Roo.SplitBar = function(dragElement, resizingElement, orientation, placement, existingProxy){
8695 this.el = Roo.get(dragElement, true);
8696 this.el.dom.unselectable = "on";
8698 this.resizingEl = Roo.get(resizingElement, true);
8702 * The orientation of the split. Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
8703 * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
8706 this.orientation = orientation || Roo.SplitBar.HORIZONTAL;
8709 * The minimum size of the resizing element. (Defaults to 0)
8715 * The maximum size of the resizing element. (Defaults to 2000)
8718 this.maxSize = 2000;
8721 * Whether to animate the transition to the new size
8724 this.animate = false;
8727 * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
8730 this.useShim = false;
8737 this.proxy = Roo.SplitBar.createProxy(this.orientation);
8739 this.proxy = Roo.get(existingProxy).dom;
8742 this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
8745 this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
8748 this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
8751 this.dragSpecs = {};
8754 * @private The adapter to use to positon and resize elements
8756 this.adapter = new Roo.SplitBar.BasicLayoutAdapter();
8757 this.adapter.init(this);
8759 if(this.orientation == Roo.SplitBar.HORIZONTAL){
8761 this.placement = placement || (this.el.getX() > this.resizingEl.getX() ? Roo.SplitBar.LEFT : Roo.SplitBar.RIGHT);
8762 this.el.addClass("x-splitbar-h");
8765 this.placement = placement || (this.el.getY() > this.resizingEl.getY() ? Roo.SplitBar.TOP : Roo.SplitBar.BOTTOM);
8766 this.el.addClass("x-splitbar-v");
8772 * Fires when the splitter is moved (alias for {@link #event-moved})
8773 * @param {Roo.SplitBar} this
8774 * @param {Number} newSize the new width or height
8779 * Fires when the splitter is moved
8780 * @param {Roo.SplitBar} this
8781 * @param {Number} newSize the new width or height
8785 * @event beforeresize
8786 * Fires before the splitter is dragged
8787 * @param {Roo.SplitBar} this
8789 "beforeresize" : true,
8791 "beforeapply" : true
8794 Roo.util.Observable.call(this);
8797 Roo.extend(Roo.SplitBar, Roo.util.Observable, {
8798 onStartProxyDrag : function(x, y){
8799 this.fireEvent("beforeresize", this);
8801 var o = Roo.DomHelper.insertFirst(document.body, {cls: "x-drag-overlay", html: " "}, true);
8803 o.enableDisplayMode("block");
8804 // all splitbars share the same overlay
8805 Roo.SplitBar.prototype.overlay = o;
8807 this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
8808 this.overlay.show();
8809 Roo.get(this.proxy).setDisplayed("block");
8810 var size = this.adapter.getElementSize(this);
8811 this.activeMinSize = this.getMinimumSize();;
8812 this.activeMaxSize = this.getMaximumSize();;
8813 var c1 = size - this.activeMinSize;
8814 var c2 = Math.max(this.activeMaxSize - size, 0);
8815 if(this.orientation == Roo.SplitBar.HORIZONTAL){
8816 this.dd.resetConstraints();
8817 this.dd.setXConstraint(
8818 this.placement == Roo.SplitBar.LEFT ? c1 : c2,
8819 this.placement == Roo.SplitBar.LEFT ? c2 : c1
8821 this.dd.setYConstraint(0, 0);
8823 this.dd.resetConstraints();
8824 this.dd.setXConstraint(0, 0);
8825 this.dd.setYConstraint(
8826 this.placement == Roo.SplitBar.TOP ? c1 : c2,
8827 this.placement == Roo.SplitBar.TOP ? c2 : c1
8830 this.dragSpecs.startSize = size;
8831 this.dragSpecs.startPoint = [x, y];
8832 Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
8836 * @private Called after the drag operation by the DDProxy
8838 onEndProxyDrag : function(e){
8839 Roo.get(this.proxy).setDisplayed(false);
8840 var endPoint = Roo.lib.Event.getXY(e);
8842 this.overlay.hide();
8845 if(this.orientation == Roo.SplitBar.HORIZONTAL){
8846 newSize = this.dragSpecs.startSize +
8847 (this.placement == Roo.SplitBar.LEFT ?
8848 endPoint[0] - this.dragSpecs.startPoint[0] :
8849 this.dragSpecs.startPoint[0] - endPoint[0]
8852 newSize = this.dragSpecs.startSize +
8853 (this.placement == Roo.SplitBar.TOP ?
8854 endPoint[1] - this.dragSpecs.startPoint[1] :
8855 this.dragSpecs.startPoint[1] - endPoint[1]
8858 newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
8859 if(newSize != this.dragSpecs.startSize){
8860 if(this.fireEvent('beforeapply', this, newSize) !== false){
8861 this.adapter.setElementSize(this, newSize);
8862 this.fireEvent("moved", this, newSize);
8863 this.fireEvent("resize", this, newSize);
8869 * Get the adapter this SplitBar uses
8870 * @return The adapter object
8872 getAdapter : function(){
8873 return this.adapter;
8877 * Set the adapter this SplitBar uses
8878 * @param {Object} adapter A SplitBar adapter object
8880 setAdapter : function(adapter){
8881 this.adapter = adapter;
8882 this.adapter.init(this);
8886 * Gets the minimum size for the resizing element
8887 * @return {Number} The minimum size
8889 getMinimumSize : function(){
8890 return this.minSize;
8894 * Sets the minimum size for the resizing element
8895 * @param {Number} minSize The minimum size
8897 setMinimumSize : function(minSize){
8898 this.minSize = minSize;
8902 * Gets the maximum size for the resizing element
8903 * @return {Number} The maximum size
8905 getMaximumSize : function(){
8906 return this.maxSize;
8910 * Sets the maximum size for the resizing element
8911 * @param {Number} maxSize The maximum size
8913 setMaximumSize : function(maxSize){
8914 this.maxSize = maxSize;
8918 * Sets the initialize size for the resizing element
8919 * @param {Number} size The initial size
8921 setCurrentSize : function(size){
8922 var oldAnimate = this.animate;
8923 this.animate = false;
8924 this.adapter.setElementSize(this, size);
8925 this.animate = oldAnimate;
8929 * Destroy this splitbar.
8930 * @param {Boolean} removeEl True to remove the element
8932 destroy : function(removeEl){
8937 this.proxy.parentNode.removeChild(this.proxy);
8945 * @private static Create our own proxy element element. So it will be the same same size on all browsers, we won't use borders. Instead we use a background color.
8947 Roo.SplitBar.createProxy = function(dir){
8948 var proxy = new Roo.Element(document.createElement("div"));
8949 proxy.unselectable();
8950 var cls = 'x-splitbar-proxy';
8951 proxy.addClass(cls + ' ' + (dir == Roo.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
8952 document.body.appendChild(proxy.dom);
8957 * @class Roo.SplitBar.BasicLayoutAdapter
8958 * Default Adapter. It assumes the splitter and resizing element are not positioned
8959 * elements and only gets/sets the width of the element. Generally used for table based layouts.
8961 Roo.SplitBar.BasicLayoutAdapter = function(){
8964 Roo.SplitBar.BasicLayoutAdapter.prototype = {
8965 // do nothing for now
8970 * Called before drag operations to get the current size of the resizing element.
8971 * @param {Roo.SplitBar} s The SplitBar using this adapter
8973 getElementSize : function(s){
8974 if(s.orientation == Roo.SplitBar.HORIZONTAL){
8975 return s.resizingEl.getWidth();
8977 return s.resizingEl.getHeight();
8982 * Called after drag operations to set the size of the resizing element.
8983 * @param {Roo.SplitBar} s The SplitBar using this adapter
8984 * @param {Number} newSize The new size to set
8985 * @param {Function} onComplete A function to be invoked when resizing is complete
8987 setElementSize : function(s, newSize, onComplete){
8988 if(s.orientation == Roo.SplitBar.HORIZONTAL){
8990 s.resizingEl.setWidth(newSize);
8992 onComplete(s, newSize);
8995 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
9000 s.resizingEl.setHeight(newSize);
9002 onComplete(s, newSize);
9005 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
9012 *@class Roo.SplitBar.AbsoluteLayoutAdapter
9013 * @extends Roo.SplitBar.BasicLayoutAdapter
9014 * Adapter that moves the splitter element to align with the resized sizing element.
9015 * Used with an absolute positioned SplitBar.
9016 * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
9017 * document.body, make sure you assign an id to the body element.
9019 Roo.SplitBar.AbsoluteLayoutAdapter = function(container){
9020 this.basic = new Roo.SplitBar.BasicLayoutAdapter();
9021 this.container = Roo.get(container);
9024 Roo.SplitBar.AbsoluteLayoutAdapter.prototype = {
9029 getElementSize : function(s){
9030 return this.basic.getElementSize(s);
9033 setElementSize : function(s, newSize, onComplete){
9034 this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
9037 moveSplitter : function(s){
9038 var yes = Roo.SplitBar;
9039 switch(s.placement){
9041 s.el.setX(s.resizingEl.getRight());
9044 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
9047 s.el.setY(s.resizingEl.getBottom());
9050 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
9057 * Orientation constant - Create a vertical SplitBar
9061 Roo.SplitBar.VERTICAL = 1;
9064 * Orientation constant - Create a horizontal SplitBar
9068 Roo.SplitBar.HORIZONTAL = 2;
9071 * Placement constant - The resizing element is to the left of the splitter element
9075 Roo.SplitBar.LEFT = 1;
9078 * Placement constant - The resizing element is to the right of the splitter element
9082 Roo.SplitBar.RIGHT = 2;
9085 * Placement constant - The resizing element is positioned above the splitter element
9089 Roo.SplitBar.TOP = 3;
9092 * Placement constant - The resizing element is positioned under splitter element
9096 Roo.SplitBar.BOTTOM = 4;
9099 * Ext JS Library 1.1.1
9100 * Copyright(c) 2006-2007, Ext JS, LLC.
9102 * Originally Released Under LGPL - original licence link has changed is not relivant.
9105 * <script type="text/javascript">
9110 * @extends Roo.util.Observable
9111 * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template.
9112 * This class also supports single and multi selection modes. <br>
9113 * Create a data model bound view:
9115 var store = new Roo.data.Store(...);
9117 var view = new Roo.View({
9119 tpl : '<div id="{0}">{2} - {1}</div>', // auto create template
9122 selectedClass: "ydataview-selected",
9126 // listen for node click?
9127 view.on("click", function(vw, index, node, e){
9128 alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
9132 dataModel.load("foobar.xml");
9134 For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
9136 * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
9137 * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
9139 * Note: old style constructor is still suported (container, template, config)
9143 * @param {Object} config The config object
9146 Roo.View = function(config, depreciated_tpl, depreciated_config){
9148 if (typeof(depreciated_tpl) == 'undefined') {
9149 // new way.. - universal constructor.
9150 Roo.apply(this, config);
9151 this.el = Roo.get(this.el);
9154 this.el = Roo.get(config);
9155 this.tpl = depreciated_tpl;
9156 Roo.apply(this, depreciated_config);
9160 if(typeof(this.tpl) == "string"){
9161 this.tpl = new Roo.Template(this.tpl);
9163 // support xtype ctors..
9164 this.tpl = new Roo.factory(this.tpl, Roo);
9175 * @event beforeclick
9176 * Fires before a click is processed. Returns false to cancel the default action.
9177 * @param {Roo.View} this
9178 * @param {Number} index The index of the target node
9179 * @param {HTMLElement} node The target node
9180 * @param {Roo.EventObject} e The raw event object
9182 "beforeclick" : true,
9185 * Fires when a template node is clicked.
9186 * @param {Roo.View} this
9187 * @param {Number} index The index of the target node
9188 * @param {HTMLElement} node The target node
9189 * @param {Roo.EventObject} e The raw event object
9194 * Fires when a template node is double clicked.
9195 * @param {Roo.View} this
9196 * @param {Number} index The index of the target node
9197 * @param {HTMLElement} node The target node
9198 * @param {Roo.EventObject} e The raw event object
9202 * @event contextmenu
9203 * Fires when a template node is right clicked.
9204 * @param {Roo.View} this
9205 * @param {Number} index The index of the target node
9206 * @param {HTMLElement} node The target node
9207 * @param {Roo.EventObject} e The raw event object
9209 "contextmenu" : true,
9211 * @event selectionchange
9212 * Fires when the selected nodes change.
9213 * @param {Roo.View} this
9214 * @param {Array} selections Array of the selected nodes
9216 "selectionchange" : true,
9219 * @event beforeselect
9220 * Fires before a selection is made. If any handlers return false, the selection is cancelled.
9221 * @param {Roo.View} this
9222 * @param {HTMLElement} node The node to be selected
9223 * @param {Array} selections Array of currently selected nodes
9225 "beforeselect" : true,
9227 * @event preparedata
9228 * Fires on every row to render, to allow you to change the data.
9229 * @param {Roo.View} this
9230 * @param {Object} data to be rendered (change this)
9232 "preparedata" : true
9236 "click": this.onClick,
9237 "dblclick": this.onDblClick,
9238 "contextmenu": this.onContextMenu,
9242 this.selections = [];
9244 this.cmp = new Roo.CompositeElementLite([]);
9246 this.store = Roo.factory(this.store, Roo.data);
9247 this.setStore(this.store, true);
9249 Roo.View.superclass.constructor.call(this);
9252 Roo.extend(Roo.View, Roo.util.Observable, {
9255 * @cfg {Roo.data.Store} store Data store to load data from.
9260 * @cfg {String|Roo.Element} el The container element.
9265 * @cfg {String|Roo.Template} tpl The template used by this View
9269 * @cfg {String} dataName the named area of the template to use as the data area
9270 * Works with domtemplates roo-name="name"
9274 * @cfg {String} selectedClass The css class to add to selected nodes
9276 selectedClass : "x-view-selected",
9278 * @cfg {String} emptyText The empty text to show when nothing is loaded.
9282 * @cfg {Boolean} multiSelect Allow multiple selection
9284 multiSelect : false,
9286 * @cfg {Boolean} singleSelect Allow single selection
9288 singleSelect: false,
9291 * @cfg {Boolean} toggleSelect - selecting
9293 toggleSelect : false,
9296 * Returns the element this view is bound to.
9297 * @return {Roo.Element}
9304 * Refreshes the view.
9306 refresh : function(){
9309 // if we are using something like 'domtemplate', then
9310 // the what gets used is:
9311 // t.applySubtemplate(NAME, data, wrapping data..)
9312 // the outer template then get' applied with
9313 // the store 'extra data'
9314 // and the body get's added to the
9315 // roo-name="data" node?
9316 // <span class='roo-tpl-{name}'></span> ?????
9320 this.clearSelections();
9323 var records = this.store.getRange();
9324 if(records.length < 1) {
9326 // is this valid?? = should it render a template??
9328 this.el.update(this.emptyText);
9332 if (this.dataName) {
9333 this.el.update(t.apply(this.store.meta)); //????
9334 el = this.el.child('.roo-tpl-' + this.dataName);
9337 for(var i = 0, len = records.length; i < len; i++){
9338 var data = this.prepareData(records[i].data, i, records[i]);
9339 this.fireEvent("preparedata", this, data, i, records[i]);
9340 html[html.length] = Roo.util.Format.trim(
9342 t.applySubtemplate(this.dataName, data, this.store.meta) :
9349 el.update(html.join(""));
9350 this.nodes = el.dom.childNodes;
9351 this.updateIndexes(0);
9355 * Function to override to reformat the data that is sent to
9356 * the template for each node.
9357 * DEPRICATED - use the preparedata event handler.
9358 * @param {Array/Object} data The raw data (array of colData for a data model bound view or
9359 * a JSON object for an UpdateManager bound view).
9361 prepareData : function(data, index, record)
9363 this.fireEvent("preparedata", this, data, index, record);
9367 onUpdate : function(ds, record){
9368 this.clearSelections();
9369 var index = this.store.indexOf(record);
9370 var n = this.nodes[index];
9371 this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
9372 n.parentNode.removeChild(n);
9373 this.updateIndexes(index, index);
9379 onAdd : function(ds, records, index)
9381 this.clearSelections();
9382 if(this.nodes.length == 0){
9386 var n = this.nodes[index];
9387 for(var i = 0, len = records.length; i < len; i++){
9388 var d = this.prepareData(records[i].data, i, records[i]);
9390 this.tpl.insertBefore(n, d);
9393 this.tpl.append(this.el, d);
9396 this.updateIndexes(index);
9399 onRemove : function(ds, record, index){
9400 this.clearSelections();
9401 var el = this.dataName ?
9402 this.el.child('.roo-tpl-' + this.dataName) :
9404 el.dom.removeChild(this.nodes[index]);
9405 this.updateIndexes(index);
9409 * Refresh an individual node.
9410 * @param {Number} index
9412 refreshNode : function(index){
9413 this.onUpdate(this.store, this.store.getAt(index));
9416 updateIndexes : function(startIndex, endIndex){
9417 var ns = this.nodes;
9418 startIndex = startIndex || 0;
9419 endIndex = endIndex || ns.length - 1;
9420 for(var i = startIndex; i <= endIndex; i++){
9421 ns[i].nodeIndex = i;
9426 * Changes the data store this view uses and refresh the view.
9427 * @param {Store} store
9429 setStore : function(store, initial){
9430 if(!initial && this.store){
9431 this.store.un("datachanged", this.refresh);
9432 this.store.un("add", this.onAdd);
9433 this.store.un("remove", this.onRemove);
9434 this.store.un("update", this.onUpdate);
9435 this.store.un("clear", this.refresh);
9439 store.on("datachanged", this.refresh, this);
9440 store.on("add", this.onAdd, this);
9441 store.on("remove", this.onRemove, this);
9442 store.on("update", this.onUpdate, this);
9443 store.on("clear", this.refresh, this);
9452 * Returns the template node the passed child belongs to or null if it doesn't belong to one.
9453 * @param {HTMLElement} node
9454 * @return {HTMLElement} The template node
9456 findItemFromChild : function(node){
9457 var el = this.dataName ?
9458 this.el.child('.roo-tpl-' + this.dataName,true) :
9461 if(!node || node.parentNode == el){
9464 var p = node.parentNode;
9465 while(p && p != el){
9466 if(p.parentNode == el){
9475 onClick : function(e){
9476 var item = this.findItemFromChild(e.getTarget());
9478 var index = this.indexOf(item);
9479 if(this.onItemClick(item, index, e) !== false){
9480 this.fireEvent("click", this, index, item, e);
9483 this.clearSelections();
9488 onContextMenu : function(e){
9489 var item = this.findItemFromChild(e.getTarget());
9491 this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
9496 onDblClick : function(e){
9497 var item = this.findItemFromChild(e.getTarget());
9499 this.fireEvent("dblclick", this, this.indexOf(item), item, e);
9503 onItemClick : function(item, index, e)
9505 if(this.fireEvent("beforeclick", this, index, item, e) === false){
9508 if (this.toggleSelect) {
9509 var m = this.isSelected(item) ? 'unselect' : 'select';
9512 _t[m](item, true, false);
9515 if(this.multiSelect || this.singleSelect){
9516 if(this.multiSelect && e.shiftKey && this.lastSelection){
9517 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
9519 this.select(item, this.multiSelect && e.ctrlKey);
9520 this.lastSelection = item;
9528 * Get the number of selected nodes.
9531 getSelectionCount : function(){
9532 return this.selections.length;
9536 * Get the currently selected nodes.
9537 * @return {Array} An array of HTMLElements
9539 getSelectedNodes : function(){
9540 return this.selections;
9544 * Get the indexes of the selected nodes.
9547 getSelectedIndexes : function(){
9548 var indexes = [], s = this.selections;
9549 for(var i = 0, len = s.length; i < len; i++){
9550 indexes.push(s[i].nodeIndex);
9556 * Clear all selections
9557 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
9559 clearSelections : function(suppressEvent){
9560 if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
9561 this.cmp.elements = this.selections;
9562 this.cmp.removeClass(this.selectedClass);
9563 this.selections = [];
9565 this.fireEvent("selectionchange", this, this.selections);
9571 * Returns true if the passed node is selected
9572 * @param {HTMLElement/Number} node The node or node index
9575 isSelected : function(node){
9576 var s = this.selections;
9580 node = this.getNode(node);
9581 return s.indexOf(node) !== -1;
9586 * @param {Array/HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node, id of a template node or an array of any of those to select
9587 * @param {Boolean} keepExisting (optional) true to keep existing selections
9588 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
9590 select : function(nodeInfo, keepExisting, suppressEvent){
9591 if(nodeInfo instanceof Array){
9593 this.clearSelections(true);
9595 for(var i = 0, len = nodeInfo.length; i < len; i++){
9596 this.select(nodeInfo[i], true, true);
9600 var node = this.getNode(nodeInfo);
9601 if(!node || this.isSelected(node)){
9602 return; // already selected.
9605 this.clearSelections(true);
9607 if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
9608 Roo.fly(node).addClass(this.selectedClass);
9609 this.selections.push(node);
9611 this.fireEvent("selectionchange", this, this.selections);
9619 * @param {Array/HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node, id of a template node or an array of any of those to select
9620 * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
9621 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
9623 unselect : function(nodeInfo, keepExisting, suppressEvent)
9625 if(nodeInfo instanceof Array){
9626 Roo.each(this.selections, function(s) {
9627 this.unselect(s, nodeInfo);
9631 var node = this.getNode(nodeInfo);
9632 if(!node || !this.isSelected(node)){
9633 Roo.log("not selected");
9634 return; // not selected.
9638 Roo.each(this.selections, function(s) {
9640 Roo.fly(node).removeClass(this.selectedClass);
9647 this.selections= ns;
9648 this.fireEvent("selectionchange", this, this.selections);
9652 * Gets a template node.
9653 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
9654 * @return {HTMLElement} The node or null if it wasn't found
9656 getNode : function(nodeInfo){
9657 if(typeof nodeInfo == "string"){
9658 return document.getElementById(nodeInfo);
9659 }else if(typeof nodeInfo == "number"){
9660 return this.nodes[nodeInfo];
9666 * Gets a range template nodes.
9667 * @param {Number} startIndex
9668 * @param {Number} endIndex
9669 * @return {Array} An array of nodes
9671 getNodes : function(start, end){
9672 var ns = this.nodes;
9674 end = typeof end == "undefined" ? ns.length - 1 : end;
9677 for(var i = start; i <= end; i++){
9681 for(var i = start; i >= end; i--){
9689 * Finds the index of the passed node
9690 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
9691 * @return {Number} The index of the node or -1
9693 indexOf : function(node){
9694 node = this.getNode(node);
9695 if(typeof node.nodeIndex == "number"){
9696 return node.nodeIndex;
9698 var ns = this.nodes;
9699 for(var i = 0, len = ns.length; i < len; i++){
9709 * Ext JS Library 1.1.1
9710 * Copyright(c) 2006-2007, Ext JS, LLC.
9712 * Originally Released Under LGPL - original licence link has changed is not relivant.
9715 * <script type="text/javascript">
9719 * @class Roo.JsonView
9721 * Shortcut class to create a JSON + {@link Roo.UpdateManager} template view. Usage:
9723 var view = new Roo.JsonView({
9724 container: "my-element",
9725 tpl: '<div id="{id}">{foo} - {bar}</div>', // auto create template
9730 // listen for node click?
9731 view.on("click", function(vw, index, node, e){
9732 alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
9735 // direct load of JSON data
9736 view.load("foobar.php");
9738 // Example from my blog list
9739 var tpl = new Roo.Template(
9740 '<div class="entry">' +
9741 '<a class="entry-title" href="{link}">{title}</a>' +
9742 "<h4>{date} by {author} | {comments} Comments</h4>{description}" +
9743 "</div><hr />"
9746 var moreView = new Roo.JsonView({
9747 container : "entry-list",
9751 moreView.on("beforerender", this.sortEntries, this);
9753 url: "/blog/get-posts.php",
9754 params: "allposts=true",
9755 text: "Loading Blog Entries..."
9759 * Note: old code is supported with arguments : (container, template, config)
9763 * Create a new JsonView
9765 * @param {Object} config The config object
9768 Roo.JsonView = function(config, depreciated_tpl, depreciated_config){
9771 Roo.JsonView.superclass.constructor.call(this, config, depreciated_tpl, depreciated_config);
9773 var um = this.el.getUpdateManager();
9774 um.setRenderer(this);
9775 um.on("update", this.onLoad, this);
9776 um.on("failure", this.onLoadException, this);
9779 * @event beforerender
9780 * Fires before rendering of the downloaded JSON data.
9781 * @param {Roo.JsonView} this
9782 * @param {Object} data The JSON data loaded
9786 * Fires when data is loaded.
9787 * @param {Roo.JsonView} this
9788 * @param {Object} data The JSON data loaded
9789 * @param {Object} response The raw Connect response object
9792 * @event loadexception
9793 * Fires when loading fails.
9794 * @param {Roo.JsonView} this
9795 * @param {Object} response The raw Connect response object
9798 'beforerender' : true,
9800 'loadexception' : true
9803 Roo.extend(Roo.JsonView, Roo.View, {
9805 * @type {String} The root property in the loaded JSON object that contains the data
9810 * Refreshes the view.
9812 refresh : function(){
9813 this.clearSelections();
9816 var o = this.jsonData;
9817 if(o && o.length > 0){
9818 for(var i = 0, len = o.length; i < len; i++){
9819 var data = this.prepareData(o[i], i, o);
9820 html[html.length] = this.tpl.apply(data);
9823 html.push(this.emptyText);
9825 this.el.update(html.join(""));
9826 this.nodes = this.el.dom.childNodes;
9827 this.updateIndexes(0);
9831 * Performs an async HTTP request, and loads the JSON from the response. If <i>params</i> are specified it uses POST, otherwise it uses GET.
9832 * @param {Object/String/Function} url The URL for this request, or a function to call to get the URL, or a config object containing any of the following options:
9835 url: "your-url.php",
9836 params: {param1: "foo", param2: "bar"}, // or a URL encoded string
9837 callback: yourFunction,
9838 scope: yourObject, //(optional scope)
9846 * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
9847 * are respectively shorthand for <i>disableCaching</i>, <i>indicatorText</i>, and <i>loadScripts</i> and are used to set their associated property on this UpdateManager instance.
9848 * @param {String/Object} params (optional) The parameters to pass, as either a URL encoded string "param1=1&param2=2" or an object {param1: 1, param2: 2}
9849 * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
9850 * @param {Boolean} discardUrl (optional) By default when you execute an update the defaultUrl is changed to the last used URL. If true, it will not store the URL.
9853 var um = this.el.getUpdateManager();
9854 um.update.apply(um, arguments);
9857 render : function(el, response){
9858 this.clearSelections();
9862 o = Roo.util.JSON.decode(response.responseText);
9865 o = o[this.jsonRoot];
9870 * The current JSON data or null
9873 this.beforeRender();
9878 * Get the number of records in the current JSON dataset
9881 getCount : function(){
9882 return this.jsonData ? this.jsonData.length : 0;
9886 * Returns the JSON object for the specified node(s)
9887 * @param {HTMLElement/Array} node The node or an array of nodes
9888 * @return {Object/Array} If you pass in an array, you get an array back, otherwise
9889 * you get the JSON object for the node
9891 getNodeData : function(node){
9892 if(node instanceof Array){
9894 for(var i = 0, len = node.length; i < len; i++){
9895 data.push(this.getNodeData(node[i]));
9899 return this.jsonData[this.indexOf(node)] || null;
9902 beforeRender : function(){
9903 this.snapshot = this.jsonData;
9905 this.sort.apply(this, this.sortInfo);
9907 this.fireEvent("beforerender", this, this.jsonData);
9910 onLoad : function(el, o){
9911 this.fireEvent("load", this, this.jsonData, o);
9914 onLoadException : function(el, o){
9915 this.fireEvent("loadexception", this, o);
9919 * Filter the data by a specific property.
9920 * @param {String} property A property on your JSON objects
9921 * @param {String/RegExp} value Either string that the property values
9922 * should start with, or a RegExp to test against the property
9924 filter : function(property, value){
9927 var ss = this.snapshot;
9928 if(typeof value == "string"){
9929 var vlen = value.length;
9934 value = value.toLowerCase();
9935 for(var i = 0, len = ss.length; i < len; i++){
9937 if(o[property].substr(0, vlen).toLowerCase() == value){
9941 } else if(value.exec){ // regex?
9942 for(var i = 0, len = ss.length; i < len; i++){
9944 if(value.test(o[property])){
9951 this.jsonData = data;
9957 * Filter by a function. The passed function will be called with each
9958 * object in the current dataset. If the function returns true the value is kept,
9959 * otherwise it is filtered.
9960 * @param {Function} fn
9961 * @param {Object} scope (optional) The scope of the function (defaults to this JsonView)
9963 filterBy : function(fn, scope){
9966 var ss = this.snapshot;
9967 for(var i = 0, len = ss.length; i < len; i++){
9969 if(fn.call(scope || this, o)){
9973 this.jsonData = data;
9979 * Clears the current filter.
9981 clearFilter : function(){
9982 if(this.snapshot && this.jsonData != this.snapshot){
9983 this.jsonData = this.snapshot;
9990 * Sorts the data for this view and refreshes it.
9991 * @param {String} property A property on your JSON objects to sort on
9992 * @param {String} direction (optional) "desc" or "asc" (defaults to "asc")
9993 * @param {Function} sortType (optional) A function to call to convert the data to a sortable value.
9995 sort : function(property, dir, sortType){
9996 this.sortInfo = Array.prototype.slice.call(arguments, 0);
9999 var dsc = dir && dir.toLowerCase() == "desc";
10000 var f = function(o1, o2){
10001 var v1 = sortType ? sortType(o1[p]) : o1[p];
10002 var v2 = sortType ? sortType(o2[p]) : o2[p];
10005 return dsc ? +1 : -1;
10006 } else if(v1 > v2){
10007 return dsc ? -1 : +1;
10012 this.jsonData.sort(f);
10014 if(this.jsonData != this.snapshot){
10015 this.snapshot.sort(f);
10021 * Ext JS Library 1.1.1
10022 * Copyright(c) 2006-2007, Ext JS, LLC.
10024 * Originally Released Under LGPL - original licence link has changed is not relivant.
10027 * <script type="text/javascript">
10032 * @class Roo.ColorPalette
10033 * @extends Roo.Component
10034 * Simple color palette class for choosing colors. The palette can be rendered to any container.<br />
10035 * Here's an example of typical usage:
10037 var cp = new Roo.ColorPalette({value:'993300'}); // initial selected color
10038 cp.render('my-div');
10040 cp.on('select', function(palette, selColor){
10041 // do something with selColor
10045 * Create a new ColorPalette
10046 * @param {Object} config The config object
10048 Roo.ColorPalette = function(config){
10049 Roo.ColorPalette.superclass.constructor.call(this, config);
10053 * Fires when a color is selected
10054 * @param {ColorPalette} this
10055 * @param {String} color The 6-digit color hex code (without the # symbol)
10061 this.on("select", this.handler, this.scope, true);
10064 Roo.extend(Roo.ColorPalette, Roo.Component, {
10066 * @cfg {String} itemCls
10067 * The CSS class to apply to the containing element (defaults to "x-color-palette")
10069 itemCls : "x-color-palette",
10071 * @cfg {String} value
10072 * The initial color to highlight (should be a valid 6-digit color hex code without the # symbol). Note that
10073 * the hex codes are case-sensitive.
10076 clickEvent:'click',
10078 ctype: "Roo.ColorPalette",
10081 * @cfg {Boolean} allowReselect If set to true then reselecting a color that is already selected fires the selection event
10083 allowReselect : false,
10086 * <p>An array of 6-digit color hex code strings (without the # symbol). This array can contain any number
10087 * of colors, and each hex code should be unique. The width of the palette is controlled via CSS by adjusting
10088 * the width property of the 'x-color-palette' class (or assigning a custom class), so you can balance the number
10089 * of colors with the width setting until the box is symmetrical.</p>
10090 * <p>You can override individual colors if needed:</p>
10092 var cp = new Roo.ColorPalette();
10093 cp.colors[0] = "FF0000"; // change the first box to red
10096 Or you can provide a custom array of your own for complete control:
10098 var cp = new Roo.ColorPalette();
10099 cp.colors = ["000000", "993300", "333300"];
10104 "000000", "993300", "333300", "003300", "003366", "000080", "333399", "333333",
10105 "800000", "FF6600", "808000", "008000", "008080", "0000FF", "666699", "808080",
10106 "FF0000", "FF9900", "99CC00", "339966", "33CCCC", "3366FF", "800080", "969696",
10107 "FF00FF", "FFCC00", "FFFF00", "00FF00", "00FFFF", "00CCFF", "993366", "C0C0C0",
10108 "FF99CC", "FFCC99", "FFFF99", "CCFFCC", "CCFFFF", "99CCFF", "CC99FF", "FFFFFF"
10112 onRender : function(container, position){
10113 var t = new Roo.MasterTemplate(
10114 '<tpl><a href="#" class="color-{0}" hidefocus="on"><em><span style="background:#{0}" unselectable="on"> </span></em></a></tpl>'
10116 var c = this.colors;
10117 for(var i = 0, len = c.length; i < len; i++){
10120 var el = document.createElement("div");
10121 el.className = this.itemCls;
10123 container.dom.insertBefore(el, position);
10124 this.el = Roo.get(el);
10125 this.el.on(this.clickEvent, this.handleClick, this, {delegate: "a"});
10126 if(this.clickEvent != 'click'){
10127 this.el.on('click', Roo.emptyFn, this, {delegate: "a", preventDefault:true});
10132 afterRender : function(){
10133 Roo.ColorPalette.superclass.afterRender.call(this);
10135 var s = this.value;
10142 handleClick : function(e, t){
10143 e.preventDefault();
10144 if(!this.disabled){
10145 var c = t.className.match(/(?:^|\s)color-(.{6})(?:\s|$)/)[1];
10146 this.select(c.toUpperCase());
10151 * Selects the specified color in the palette (fires the select event)
10152 * @param {String} color A valid 6-digit color hex code (# will be stripped if included)
10154 select : function(color){
10155 color = color.replace("#", "");
10156 if(color != this.value || this.allowReselect){
10159 el.child("a.color-"+this.value).removeClass("x-color-palette-sel");
10161 el.child("a.color-"+color).addClass("x-color-palette-sel");
10162 this.value = color;
10163 this.fireEvent("select", this, color);
10168 * Ext JS Library 1.1.1
10169 * Copyright(c) 2006-2007, Ext JS, LLC.
10171 * Originally Released Under LGPL - original licence link has changed is not relivant.
10174 * <script type="text/javascript">
10178 * @class Roo.DatePicker
10179 * @extends Roo.Component
10180 * Simple date picker class.
10182 * Create a new DatePicker
10183 * @param {Object} config The config object
10185 Roo.DatePicker = function(config){
10186 Roo.DatePicker.superclass.constructor.call(this, config);
10188 this.value = config && config.value ?
10189 config.value.clearTime() : new Date().clearTime();
10194 * Fires when a date is selected
10195 * @param {DatePicker} this
10196 * @param {Date} date The selected date
10200 * @event monthchange
10201 * Fires when the displayed month changes
10202 * @param {DatePicker} this
10203 * @param {Date} date The selected month
10205 'monthchange': true
10209 this.on("select", this.handler, this.scope || this);
10211 // build the disabledDatesRE
10212 if(!this.disabledDatesRE && this.disabledDates){
10213 var dd = this.disabledDates;
10215 for(var i = 0; i < dd.length; i++){
10217 if(i != dd.length-1) re += "|";
10219 this.disabledDatesRE = new RegExp(re + ")");
10223 Roo.extend(Roo.DatePicker, Roo.Component, {
10225 * @cfg {String} todayText
10226 * The text to display on the button that selects the current date (defaults to "Today")
10228 todayText : "Today",
10230 * @cfg {String} okText
10231 * The text to display on the ok button
10233 okText : " OK ", //   to give the user extra clicking room
10235 * @cfg {String} cancelText
10236 * The text to display on the cancel button
10238 cancelText : "Cancel",
10240 * @cfg {String} todayTip
10241 * The tooltip to display for the button that selects the current date (defaults to "{current date} (Spacebar)")
10243 todayTip : "{0} (Spacebar)",
10245 * @cfg {Date} minDate
10246 * Minimum allowable date (JavaScript date object, defaults to null)
10250 * @cfg {Date} maxDate
10251 * Maximum allowable date (JavaScript date object, defaults to null)
10255 * @cfg {String} minText
10256 * The error text to display if the minDate validation fails (defaults to "This date is before the minimum date")
10258 minText : "This date is before the minimum date",
10260 * @cfg {String} maxText
10261 * The error text to display if the maxDate validation fails (defaults to "This date is after the maximum date")
10263 maxText : "This date is after the maximum date",
10265 * @cfg {String} format
10266 * The default date format string which can be overriden for localization support. The format must be
10267 * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
10271 * @cfg {Array} disabledDays
10272 * An array of days to disable, 0-based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
10274 disabledDays : null,
10276 * @cfg {String} disabledDaysText
10277 * The tooltip to display when the date falls on a disabled day (defaults to "")
10279 disabledDaysText : "",
10281 * @cfg {RegExp} disabledDatesRE
10282 * JavaScript regular expression used to disable a pattern of dates (defaults to null)
10284 disabledDatesRE : null,
10286 * @cfg {String} disabledDatesText
10287 * The tooltip text to display when the date falls on a disabled date (defaults to "")
10289 disabledDatesText : "",
10291 * @cfg {Boolean} constrainToViewport
10292 * True to constrain the date picker to the viewport (defaults to true)
10294 constrainToViewport : true,
10296 * @cfg {Array} monthNames
10297 * An array of textual month names which can be overriden for localization support (defaults to Date.monthNames)
10299 monthNames : Date.monthNames,
10301 * @cfg {Array} dayNames
10302 * An array of textual day names which can be overriden for localization support (defaults to Date.dayNames)
10304 dayNames : Date.dayNames,
10306 * @cfg {String} nextText
10307 * The next month navigation button tooltip (defaults to 'Next Month (Control+Right)')
10309 nextText: 'Next Month (Control+Right)',
10311 * @cfg {String} prevText
10312 * The previous month navigation button tooltip (defaults to 'Previous Month (Control+Left)')
10314 prevText: 'Previous Month (Control+Left)',
10316 * @cfg {String} monthYearText
10317 * The header month selector tooltip (defaults to 'Choose a month (Control+Up/Down to move years)')
10319 monthYearText: 'Choose a month (Control+Up/Down to move years)',
10321 * @cfg {Number} startDay
10322 * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
10326 * @cfg {Bool} showClear
10327 * Show a clear button (usefull for date form elements that can be blank.)
10333 * Sets the value of the date field
10334 * @param {Date} value The date to set
10336 setValue : function(value){
10337 var old = this.value;
10338 this.value = value.clearTime(true);
10340 this.update(this.value);
10345 * Gets the current selected value of the date field
10346 * @return {Date} The selected date
10348 getValue : function(){
10353 focus : function(){
10355 this.update(this.activeDate);
10360 onRender : function(container, position){
10362 '<table cellspacing="0">',
10363 '<tr><td class="x-date-left"><a href="#" title="', this.prevText ,'"> </a></td><td class="x-date-middle" align="center"></td><td class="x-date-right"><a href="#" title="', this.nextText ,'"> </a></td></tr>',
10364 '<tr><td colspan="3"><table class="x-date-inner" cellspacing="0"><thead><tr>'];
10365 var dn = this.dayNames;
10366 for(var i = 0; i < 7; i++){
10367 var d = this.startDay+i;
10371 m.push("<th><span>", dn[d].substr(0,1), "</span></th>");
10373 m[m.length] = "</tr></thead><tbody><tr>";
10374 for(var i = 0; i < 42; i++) {
10375 if(i % 7 == 0 && i != 0){
10376 m[m.length] = "</tr><tr>";
10378 m[m.length] = '<td><a href="#" hidefocus="on" class="x-date-date" tabIndex="1"><em><span></span></em></a></td>';
10380 m[m.length] = '</tr></tbody></table></td></tr><tr>'+
10381 '<td colspan="3" class="x-date-bottom" align="center"></td></tr></table><div class="x-date-mp"></div>';
10383 var el = document.createElement("div");
10384 el.className = "x-date-picker";
10385 el.innerHTML = m.join("");
10387 container.dom.insertBefore(el, position);
10389 this.el = Roo.get(el);
10390 this.eventEl = Roo.get(el.firstChild);
10392 new Roo.util.ClickRepeater(this.el.child("td.x-date-left a"), {
10393 handler: this.showPrevMonth,
10395 preventDefault:true,
10399 new Roo.util.ClickRepeater(this.el.child("td.x-date-right a"), {
10400 handler: this.showNextMonth,
10402 preventDefault:true,
10406 this.eventEl.on("mousewheel", this.handleMouseWheel, this);
10408 this.monthPicker = this.el.down('div.x-date-mp');
10409 this.monthPicker.enableDisplayMode('block');
10411 var kn = new Roo.KeyNav(this.eventEl, {
10412 "left" : function(e){
10414 this.showPrevMonth() :
10415 this.update(this.activeDate.add("d", -1));
10418 "right" : function(e){
10420 this.showNextMonth() :
10421 this.update(this.activeDate.add("d", 1));
10424 "up" : function(e){
10426 this.showNextYear() :
10427 this.update(this.activeDate.add("d", -7));
10430 "down" : function(e){
10432 this.showPrevYear() :
10433 this.update(this.activeDate.add("d", 7));
10436 "pageUp" : function(e){
10437 this.showNextMonth();
10440 "pageDown" : function(e){
10441 this.showPrevMonth();
10444 "enter" : function(e){
10445 e.stopPropagation();
10452 this.eventEl.on("click", this.handleDateClick, this, {delegate: "a.x-date-date"});
10454 this.eventEl.addKeyListener(Roo.EventObject.SPACE, this.selectToday, this);
10456 this.el.unselectable();
10458 this.cells = this.el.select("table.x-date-inner tbody td");
10459 this.textNodes = this.el.query("table.x-date-inner tbody span");
10461 this.mbtn = new Roo.Button(this.el.child("td.x-date-middle", true), {
10463 tooltip: this.monthYearText
10466 this.mbtn.on('click', this.showMonthPicker, this);
10467 this.mbtn.el.child(this.mbtn.menuClassTarget).addClass("x-btn-with-menu");
10470 var today = (new Date()).dateFormat(this.format);
10472 var baseTb = new Roo.Toolbar(this.el.child("td.x-date-bottom", true));
10473 if (this.showClear) {
10474 baseTb.add( new Roo.Toolbar.Fill());
10477 text: String.format(this.todayText, today),
10478 tooltip: String.format(this.todayTip, today),
10479 handler: this.selectToday,
10483 //var todayBtn = new Roo.Button(this.el.child("td.x-date-bottom", true), {
10486 if (this.showClear) {
10488 baseTb.add( new Roo.Toolbar.Fill());
10491 cls: 'x-btn-icon x-btn-clear',
10492 handler: function() {
10494 this.fireEvent("select", this, '');
10504 this.update(this.value);
10507 createMonthPicker : function(){
10508 if(!this.monthPicker.dom.firstChild){
10509 var buf = ['<table border="0" cellspacing="0">'];
10510 for(var i = 0; i < 6; i++){
10512 '<tr><td class="x-date-mp-month"><a href="#">', this.monthNames[i].substr(0, 3), '</a></td>',
10513 '<td class="x-date-mp-month x-date-mp-sep"><a href="#">', this.monthNames[i+6].substr(0, 3), '</a></td>',
10515 '<td class="x-date-mp-ybtn" align="center"><a class="x-date-mp-prev"></a></td><td class="x-date-mp-ybtn" align="center"><a class="x-date-mp-next"></a></td></tr>' :
10516 '<td class="x-date-mp-year"><a href="#"></a></td><td class="x-date-mp-year"><a href="#"></a></td></tr>'
10520 '<tr class="x-date-mp-btns"><td colspan="4"><button type="button" class="x-date-mp-ok">',
10522 '</button><button type="button" class="x-date-mp-cancel">',
10524 '</button></td></tr>',
10527 this.monthPicker.update(buf.join(''));
10528 this.monthPicker.on('click', this.onMonthClick, this);
10529 this.monthPicker.on('dblclick', this.onMonthDblClick, this);
10531 this.mpMonths = this.monthPicker.select('td.x-date-mp-month');
10532 this.mpYears = this.monthPicker.select('td.x-date-mp-year');
10534 this.mpMonths.each(function(m, a, i){
10537 m.dom.xmonth = 5 + Math.round(i * .5);
10539 m.dom.xmonth = Math.round((i-1) * .5);
10545 showMonthPicker : function(){
10546 this.createMonthPicker();
10547 var size = this.el.getSize();
10548 this.monthPicker.setSize(size);
10549 this.monthPicker.child('table').setSize(size);
10551 this.mpSelMonth = (this.activeDate || this.value).getMonth();
10552 this.updateMPMonth(this.mpSelMonth);
10553 this.mpSelYear = (this.activeDate || this.value).getFullYear();
10554 this.updateMPYear(this.mpSelYear);
10556 this.monthPicker.slideIn('t', {duration:.2});
10559 updateMPYear : function(y){
10561 var ys = this.mpYears.elements;
10562 for(var i = 1; i <= 10; i++){
10563 var td = ys[i-1], y2;
10565 y2 = y + Math.round(i * .5);
10566 td.firstChild.innerHTML = y2;
10569 y2 = y - (5-Math.round(i * .5));
10570 td.firstChild.innerHTML = y2;
10573 this.mpYears.item(i-1)[y2 == this.mpSelYear ? 'addClass' : 'removeClass']('x-date-mp-sel');
10577 updateMPMonth : function(sm){
10578 this.mpMonths.each(function(m, a, i){
10579 m[m.dom.xmonth == sm ? 'addClass' : 'removeClass']('x-date-mp-sel');
10583 selectMPMonth: function(m){
10587 onMonthClick : function(e, t){
10589 var el = new Roo.Element(t), pn;
10590 if(el.is('button.x-date-mp-cancel')){
10591 this.hideMonthPicker();
10593 else if(el.is('button.x-date-mp-ok')){
10594 this.update(new Date(this.mpSelYear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
10595 this.hideMonthPicker();
10597 else if(pn = el.up('td.x-date-mp-month', 2)){
10598 this.mpMonths.removeClass('x-date-mp-sel');
10599 pn.addClass('x-date-mp-sel');
10600 this.mpSelMonth = pn.dom.xmonth;
10602 else if(pn = el.up('td.x-date-mp-year', 2)){
10603 this.mpYears.removeClass('x-date-mp-sel');
10604 pn.addClass('x-date-mp-sel');
10605 this.mpSelYear = pn.dom.xyear;
10607 else if(el.is('a.x-date-mp-prev')){
10608 this.updateMPYear(this.mpyear-10);
10610 else if(el.is('a.x-date-mp-next')){
10611 this.updateMPYear(this.mpyear+10);
10615 onMonthDblClick : function(e, t){
10617 var el = new Roo.Element(t), pn;
10618 if(pn = el.up('td.x-date-mp-month', 2)){
10619 this.update(new Date(this.mpSelYear, pn.dom.xmonth, (this.activeDate || this.value).getDate()));
10620 this.hideMonthPicker();
10622 else if(pn = el.up('td.x-date-mp-year', 2)){
10623 this.update(new Date(pn.dom.xyear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
10624 this.hideMonthPicker();
10628 hideMonthPicker : function(disableAnim){
10629 if(this.monthPicker){
10630 if(disableAnim === true){
10631 this.monthPicker.hide();
10633 this.monthPicker.slideOut('t', {duration:.2});
10639 showPrevMonth : function(e){
10640 this.update(this.activeDate.add("mo", -1));
10644 showNextMonth : function(e){
10645 this.update(this.activeDate.add("mo", 1));
10649 showPrevYear : function(){
10650 this.update(this.activeDate.add("y", -1));
10654 showNextYear : function(){
10655 this.update(this.activeDate.add("y", 1));
10659 handleMouseWheel : function(e){
10660 var delta = e.getWheelDelta();
10662 this.showPrevMonth();
10664 } else if(delta < 0){
10665 this.showNextMonth();
10671 handleDateClick : function(e, t){
10673 if(t.dateValue && !Roo.fly(t.parentNode).hasClass("x-date-disabled")){
10674 this.setValue(new Date(t.dateValue));
10675 this.fireEvent("select", this, this.value);
10680 selectToday : function(){
10681 this.setValue(new Date().clearTime());
10682 this.fireEvent("select", this, this.value);
10686 update : function(date)
10688 var vd = this.activeDate;
10689 this.activeDate = date;
10691 var t = date.getTime();
10692 if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
10693 this.cells.removeClass("x-date-selected");
10694 this.cells.each(function(c){
10695 if(c.dom.firstChild.dateValue == t){
10696 c.addClass("x-date-selected");
10697 setTimeout(function(){
10698 try{c.dom.firstChild.focus();}catch(e){}
10707 var days = date.getDaysInMonth();
10708 var firstOfMonth = date.getFirstDateOfMonth();
10709 var startingPos = firstOfMonth.getDay()-this.startDay;
10711 if(startingPos <= this.startDay){
10715 var pm = date.add("mo", -1);
10716 var prevStart = pm.getDaysInMonth()-startingPos;
10718 var cells = this.cells.elements;
10719 var textEls = this.textNodes;
10720 days += startingPos;
10722 // convert everything to numbers so it's fast
10723 var day = 86400000;
10724 var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
10725 var today = new Date().clearTime().getTime();
10726 var sel = date.clearTime().getTime();
10727 var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
10728 var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
10729 var ddMatch = this.disabledDatesRE;
10730 var ddText = this.disabledDatesText;
10731 var ddays = this.disabledDays ? this.disabledDays.join("") : false;
10732 var ddaysText = this.disabledDaysText;
10733 var format = this.format;
10735 var setCellClass = function(cal, cell){
10737 var t = d.getTime();
10738 cell.firstChild.dateValue = t;
10740 cell.className += " x-date-today";
10741 cell.title = cal.todayText;
10744 cell.className += " x-date-selected";
10745 setTimeout(function(){
10746 try{cell.firstChild.focus();}catch(e){}
10751 cell.className = " x-date-disabled";
10752 cell.title = cal.minText;
10756 cell.className = " x-date-disabled";
10757 cell.title = cal.maxText;
10761 if(ddays.indexOf(d.getDay()) != -1){
10762 cell.title = ddaysText;
10763 cell.className = " x-date-disabled";
10766 if(ddMatch && format){
10767 var fvalue = d.dateFormat(format);
10768 if(ddMatch.test(fvalue)){
10769 cell.title = ddText.replace("%0", fvalue);
10770 cell.className = " x-date-disabled";
10776 for(; i < startingPos; i++) {
10777 textEls[i].innerHTML = (++prevStart);
10778 d.setDate(d.getDate()+1);
10779 cells[i].className = "x-date-prevday";
10780 setCellClass(this, cells[i]);
10782 for(; i < days; i++){
10783 intDay = i - startingPos + 1;
10784 textEls[i].innerHTML = (intDay);
10785 d.setDate(d.getDate()+1);
10786 cells[i].className = "x-date-active";
10787 setCellClass(this, cells[i]);
10790 for(; i < 42; i++) {
10791 textEls[i].innerHTML = (++extraDays);
10792 d.setDate(d.getDate()+1);
10793 cells[i].className = "x-date-nextday";
10794 setCellClass(this, cells[i]);
10797 this.mbtn.setText(this.monthNames[date.getMonth()] + " " + date.getFullYear());
10798 this.fireEvent('monthchange', this, date);
10800 if(!this.internalRender){
10801 var main = this.el.dom.firstChild;
10802 var w = main.offsetWidth;
10803 this.el.setWidth(w + this.el.getBorderWidth("lr"));
10804 Roo.fly(main).setWidth(w);
10805 this.internalRender = true;
10806 // opera does not respect the auto grow header center column
10807 // then, after it gets a width opera refuses to recalculate
10808 // without a second pass
10809 if(Roo.isOpera && !this.secondPass){
10810 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
10811 this.secondPass = true;
10812 this.update.defer(10, this, [date]);
10820 * Ext JS Library 1.1.1
10821 * Copyright(c) 2006-2007, Ext JS, LLC.
10823 * Originally Released Under LGPL - original licence link has changed is not relivant.
10826 * <script type="text/javascript">
10829 * @class Roo.TabPanel
10830 * @extends Roo.util.Observable
10831 * A lightweight tab container.
10835 // basic tabs 1, built from existing content
10836 var tabs = new Roo.TabPanel("tabs1");
10837 tabs.addTab("script", "View Script");
10838 tabs.addTab("markup", "View Markup");
10839 tabs.activate("script");
10841 // more advanced tabs, built from javascript
10842 var jtabs = new Roo.TabPanel("jtabs");
10843 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
10845 // set up the UpdateManager
10846 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
10847 var updater = tab2.getUpdateManager();
10848 updater.setDefaultUrl("ajax1.htm");
10849 tab2.on('activate', updater.refresh, updater, true);
10851 // Use setUrl for Ajax loading
10852 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
10853 tab3.setUrl("ajax2.htm", null, true);
10856 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
10859 jtabs.activate("jtabs-1");
10862 * Create a new TabPanel.
10863 * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
10864 * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
10866 Roo.TabPanel = function(container, config){
10868 * The container element for this TabPanel.
10869 * @type Roo.Element
10871 this.el = Roo.get(container, true);
10873 if(typeof config == "boolean"){
10874 this.tabPosition = config ? "bottom" : "top";
10876 Roo.apply(this, config);
10879 if(this.tabPosition == "bottom"){
10880 this.bodyEl = Roo.get(this.createBody(this.el.dom));
10881 this.el.addClass("x-tabs-bottom");
10883 this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
10884 this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
10885 this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
10887 Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
10889 if(this.tabPosition != "bottom"){
10890 /** The body element that contains {@link Roo.TabPanelItem} bodies. +
10891 * @type Roo.Element
10893 this.bodyEl = Roo.get(this.createBody(this.el.dom));
10894 this.el.addClass("x-tabs-top");
10898 this.bodyEl.setStyle("position", "relative");
10900 this.active = null;
10901 this.activateDelegate = this.activate.createDelegate(this);
10906 * Fires when the active tab changes
10907 * @param {Roo.TabPanel} this
10908 * @param {Roo.TabPanelItem} activePanel The new active tab
10912 * @event beforetabchange
10913 * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
10914 * @param {Roo.TabPanel} this
10915 * @param {Object} e Set cancel to true on this object to cancel the tab change
10916 * @param {Roo.TabPanelItem} tab The tab being changed to
10918 "beforetabchange" : true
10921 Roo.EventManager.onWindowResize(this.onResize, this);
10922 this.cpad = this.el.getPadding("lr");
10923 this.hiddenCount = 0;
10926 // toolbar on the tabbar support...
10927 if (this.toolbar) {
10928 var tcfg = this.toolbar;
10929 tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');
10930 this.toolbar = new Roo.Toolbar(tcfg);
10931 if (Roo.isSafari) {
10932 var tbl = tcfg.container.child('table', true);
10933 tbl.setAttribute('width', '100%');
10940 Roo.TabPanel.superclass.constructor.call(this);
10943 Roo.extend(Roo.TabPanel, Roo.util.Observable, {
10945 *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
10947 tabPosition : "top",
10949 *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
10951 currentTabWidth : 0,
10953 *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
10957 *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
10961 *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
10963 preferredTabWidth : 175,
10965 *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
10967 resizeTabs : false,
10969 *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
10971 monitorResize : true,
10973 *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar.
10978 * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
10979 * @param {String} id The id of the div to use <b>or create</b>
10980 * @param {String} text The text for the tab
10981 * @param {String} content (optional) Content to put in the TabPanelItem body
10982 * @param {Boolean} closable (optional) True to create a close icon on the tab
10983 * @return {Roo.TabPanelItem} The created TabPanelItem
10985 addTab : function(id, text, content, closable){
10986 var item = new Roo.TabPanelItem(this, id, text, closable);
10987 this.addTabItem(item);
10989 item.setContent(content);
10995 * Returns the {@link Roo.TabPanelItem} with the specified id/index
10996 * @param {String/Number} id The id or index of the TabPanelItem to fetch.
10997 * @return {Roo.TabPanelItem}
10999 getTab : function(id){
11000 return this.items[id];
11004 * Hides the {@link Roo.TabPanelItem} with the specified id/index
11005 * @param {String/Number} id The id or index of the TabPanelItem to hide.
11007 hideTab : function(id){
11008 var t = this.items[id];
11011 this.hiddenCount++;
11012 this.autoSizeTabs();
11017 * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
11018 * @param {String/Number} id The id or index of the TabPanelItem to unhide.
11020 unhideTab : function(id){
11021 var t = this.items[id];
11023 t.setHidden(false);
11024 this.hiddenCount--;
11025 this.autoSizeTabs();
11030 * Adds an existing {@link Roo.TabPanelItem}.
11031 * @param {Roo.TabPanelItem} item The TabPanelItem to add
11033 addTabItem : function(item){
11034 this.items[item.id] = item;
11035 this.items.push(item);
11036 if(this.resizeTabs){
11037 item.setWidth(this.currentTabWidth || this.preferredTabWidth);
11038 this.autoSizeTabs();
11045 * Removes a {@link Roo.TabPanelItem}.
11046 * @param {String/Number} id The id or index of the TabPanelItem to remove.
11048 removeTab : function(id){
11049 var items = this.items;
11050 var tab = items[id];
11051 if(!tab) { return; }
11052 var index = items.indexOf(tab);
11053 if(this.active == tab && items.length > 1){
11054 var newTab = this.getNextAvailable(index);
11059 this.stripEl.dom.removeChild(tab.pnode.dom);
11060 if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
11061 this.bodyEl.dom.removeChild(tab.bodyEl.dom);
11063 items.splice(index, 1);
11064 delete this.items[tab.id];
11065 tab.fireEvent("close", tab);
11066 tab.purgeListeners();
11067 this.autoSizeTabs();
11070 getNextAvailable : function(start){
11071 var items = this.items;
11073 // look for a next tab that will slide over to
11074 // replace the one being removed
11075 while(index < items.length){
11076 var item = items[++index];
11077 if(item && !item.isHidden()){
11081 // if one isn't found select the previous tab (on the left)
11084 var item = items[--index];
11085 if(item && !item.isHidden()){
11093 * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
11094 * @param {String/Number} id The id or index of the TabPanelItem to disable.
11096 disableTab : function(id){
11097 var tab = this.items[id];
11098 if(tab && this.active != tab){
11104 * Enables a {@link Roo.TabPanelItem} that is disabled.
11105 * @param {String/Number} id The id or index of the TabPanelItem to enable.
11107 enableTab : function(id){
11108 var tab = this.items[id];
11113 * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
11114 * @param {String/Number} id The id or index of the TabPanelItem to activate.
11115 * @return {Roo.TabPanelItem} The TabPanelItem.
11117 activate : function(id){
11118 var tab = this.items[id];
11122 if(tab == this.active || tab.disabled){
11126 this.fireEvent("beforetabchange", this, e, tab);
11127 if(e.cancel !== true && !tab.disabled){
11129 this.active.hide();
11131 this.active = this.items[id];
11132 this.active.show();
11133 this.fireEvent("tabchange", this, this.active);
11139 * Gets the active {@link Roo.TabPanelItem}.
11140 * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
11142 getActiveTab : function(){
11143 return this.active;
11147 * Updates the tab body element to fit the height of the container element
11148 * for overflow scrolling
11149 * @param {Number} targetHeight (optional) Override the starting height from the elements height
11151 syncHeight : function(targetHeight){
11152 var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
11153 var bm = this.bodyEl.getMargins();
11154 var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
11155 this.bodyEl.setHeight(newHeight);
11159 onResize : function(){
11160 if(this.monitorResize){
11161 this.autoSizeTabs();
11166 * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
11168 beginUpdate : function(){
11169 this.updating = true;
11173 * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
11175 endUpdate : function(){
11176 this.updating = false;
11177 this.autoSizeTabs();
11181 * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
11183 autoSizeTabs : function(){
11184 var count = this.items.length;
11185 var vcount = count - this.hiddenCount;
11186 if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) return;
11187 var w = Math.max(this.el.getWidth() - this.cpad, 10);
11188 var availWidth = Math.floor(w / vcount);
11189 var b = this.stripBody;
11190 if(b.getWidth() > w){
11191 var tabs = this.items;
11192 this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
11193 if(availWidth < this.minTabWidth){
11194 /*if(!this.sleft){ // incomplete scrolling code
11195 this.createScrollButtons();
11198 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
11201 if(this.currentTabWidth < this.preferredTabWidth){
11202 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
11208 * Returns the number of tabs in this TabPanel.
11211 getCount : function(){
11212 return this.items.length;
11216 * Resizes all the tabs to the passed width
11217 * @param {Number} The new width
11219 setTabWidth : function(width){
11220 this.currentTabWidth = width;
11221 for(var i = 0, len = this.items.length; i < len; i++) {
11222 if(!this.items[i].isHidden())this.items[i].setWidth(width);
11227 * Destroys this TabPanel
11228 * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
11230 destroy : function(removeEl){
11231 Roo.EventManager.removeResizeListener(this.onResize, this);
11232 for(var i = 0, len = this.items.length; i < len; i++){
11233 this.items[i].purgeListeners();
11235 if(removeEl === true){
11236 this.el.update("");
11243 * @class Roo.TabPanelItem
11244 * @extends Roo.util.Observable
11245 * Represents an individual item (tab plus body) in a TabPanel.
11246 * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
11247 * @param {String} id The id of this TabPanelItem
11248 * @param {String} text The text for the tab of this TabPanelItem
11249 * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
11251 Roo.TabPanelItem = function(tabPanel, id, text, closable){
11253 * The {@link Roo.TabPanel} this TabPanelItem belongs to
11254 * @type Roo.TabPanel
11256 this.tabPanel = tabPanel;
11258 * The id for this TabPanelItem
11263 this.disabled = false;
11267 this.loaded = false;
11268 this.closable = closable;
11271 * The body element for this TabPanelItem.
11272 * @type Roo.Element
11274 this.bodyEl = Roo.get(tabPanel.createItemBody(tabPanel.bodyEl.dom, id));
11275 this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
11276 this.bodyEl.setStyle("display", "block");
11277 this.bodyEl.setStyle("zoom", "1");
11280 var els = tabPanel.createStripElements(tabPanel.stripEl.dom, text, closable);
11282 this.el = Roo.get(els.el, true);
11283 this.inner = Roo.get(els.inner, true);
11284 this.textEl = Roo.get(this.el.dom.firstChild.firstChild.firstChild, true);
11285 this.pnode = Roo.get(els.el.parentNode, true);
11286 this.el.on("mousedown", this.onTabMouseDown, this);
11287 this.el.on("click", this.onTabClick, this);
11290 var c = Roo.get(els.close, true);
11291 c.dom.title = this.closeText;
11292 c.addClassOnOver("close-over");
11293 c.on("click", this.closeClick, this);
11299 * Fires when this tab becomes the active tab.
11300 * @param {Roo.TabPanel} tabPanel The parent TabPanel
11301 * @param {Roo.TabPanelItem} this
11305 * @event beforeclose
11306 * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
11307 * @param {Roo.TabPanelItem} this
11308 * @param {Object} e Set cancel to true on this object to cancel the close.
11310 "beforeclose": true,
11313 * Fires when this tab is closed.
11314 * @param {Roo.TabPanelItem} this
11318 * @event deactivate
11319 * Fires when this tab is no longer the active tab.
11320 * @param {Roo.TabPanel} tabPanel The parent TabPanel
11321 * @param {Roo.TabPanelItem} this
11323 "deactivate" : true
11325 this.hidden = false;
11327 Roo.TabPanelItem.superclass.constructor.call(this);
11330 Roo.extend(Roo.TabPanelItem, Roo.util.Observable, {
11331 purgeListeners : function(){
11332 Roo.util.Observable.prototype.purgeListeners.call(this);
11333 this.el.removeAllListeners();
11336 * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
11339 this.pnode.addClass("on");
11342 this.tabPanel.stripWrap.repaint();
11344 this.fireEvent("activate", this.tabPanel, this);
11348 * Returns true if this tab is the active tab.
11349 * @return {Boolean}
11351 isActive : function(){
11352 return this.tabPanel.getActiveTab() == this;
11356 * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
11359 this.pnode.removeClass("on");
11361 this.fireEvent("deactivate", this.tabPanel, this);
11364 hideAction : function(){
11365 this.bodyEl.hide();
11366 this.bodyEl.setStyle("position", "absolute");
11367 this.bodyEl.setLeft("-20000px");
11368 this.bodyEl.setTop("-20000px");
11371 showAction : function(){
11372 this.bodyEl.setStyle("position", "relative");
11373 this.bodyEl.setTop("");
11374 this.bodyEl.setLeft("");
11375 this.bodyEl.show();
11379 * Set the tooltip for the tab.
11380 * @param {String} tooltip The tab's tooltip
11382 setTooltip : function(text){
11383 if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
11384 this.textEl.dom.qtip = text;
11385 this.textEl.dom.removeAttribute('title');
11387 this.textEl.dom.title = text;
11391 onTabClick : function(e){
11392 e.preventDefault();
11393 this.tabPanel.activate(this.id);
11396 onTabMouseDown : function(e){
11397 e.preventDefault();
11398 this.tabPanel.activate(this.id);
11401 getWidth : function(){
11402 return this.inner.getWidth();
11405 setWidth : function(width){
11406 var iwidth = width - this.pnode.getPadding("lr");
11407 this.inner.setWidth(iwidth);
11408 this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
11409 this.pnode.setWidth(width);
11413 * Show or hide the tab
11414 * @param {Boolean} hidden True to hide or false to show.
11416 setHidden : function(hidden){
11417 this.hidden = hidden;
11418 this.pnode.setStyle("display", hidden ? "none" : "");
11422 * Returns true if this tab is "hidden"
11423 * @return {Boolean}
11425 isHidden : function(){
11426 return this.hidden;
11430 * Returns the text for this tab
11433 getText : function(){
11437 autoSize : function(){
11438 //this.el.beginMeasure();
11439 this.textEl.setWidth(1);
11440 this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr"));
11441 //this.el.endMeasure();
11445 * Sets the text for the tab (Note: this also sets the tooltip text)
11446 * @param {String} text The tab's text and tooltip
11448 setText : function(text){
11450 this.textEl.update(text);
11451 this.setTooltip(text);
11452 if(!this.tabPanel.resizeTabs){
11457 * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
11459 activate : function(){
11460 this.tabPanel.activate(this.id);
11464 * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
11466 disable : function(){
11467 if(this.tabPanel.active != this){
11468 this.disabled = true;
11469 this.pnode.addClass("disabled");
11474 * Enables this TabPanelItem if it was previously disabled.
11476 enable : function(){
11477 this.disabled = false;
11478 this.pnode.removeClass("disabled");
11482 * Sets the content for this TabPanelItem.
11483 * @param {String} content The content
11484 * @param {Boolean} loadScripts true to look for and load scripts
11486 setContent : function(content, loadScripts){
11487 this.bodyEl.update(content, loadScripts);
11491 * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
11492 * @return {Roo.UpdateManager} The UpdateManager
11494 getUpdateManager : function(){
11495 return this.bodyEl.getUpdateManager();
11499 * Set a URL to be used to load the content for this TabPanelItem.
11500 * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
11501 * @param {String/Object} params (optional) The string params for the update call or an object of the params. See {@link Roo.UpdateManager#update} for more details. (Defaults to null)
11502 * @param {Boolean} loadOnce (optional) Whether to only load the content once. If this is false it makes the Ajax call every time this TabPanelItem is activated. (Defaults to false)
11503 * @return {Roo.UpdateManager} The UpdateManager
11505 setUrl : function(url, params, loadOnce){
11506 if(this.refreshDelegate){
11507 this.un('activate', this.refreshDelegate);
11509 this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
11510 this.on("activate", this.refreshDelegate);
11511 return this.bodyEl.getUpdateManager();
11515 _handleRefresh : function(url, params, loadOnce){
11516 if(!loadOnce || !this.loaded){
11517 var updater = this.bodyEl.getUpdateManager();
11518 updater.update(url, params, this._setLoaded.createDelegate(this));
11523 * Forces a content refresh from the URL specified in the {@link #setUrl} method.
11524 * Will fail silently if the setUrl method has not been called.
11525 * This does not activate the panel, just updates its content.
11527 refresh : function(){
11528 if(this.refreshDelegate){
11529 this.loaded = false;
11530 this.refreshDelegate();
11535 _setLoaded : function(){
11536 this.loaded = true;
11540 closeClick : function(e){
11543 this.fireEvent("beforeclose", this, o);
11544 if(o.cancel !== true){
11545 this.tabPanel.removeTab(this.id);
11549 * The text displayed in the tooltip for the close icon.
11552 closeText : "Close this tab"
11556 Roo.TabPanel.prototype.createStrip = function(container){
11557 var strip = document.createElement("div");
11558 strip.className = "x-tabs-wrap";
11559 container.appendChild(strip);
11563 Roo.TabPanel.prototype.createStripList = function(strip){
11564 // div wrapper for retard IE
11565 // returns the "tr" element.
11566 strip.innerHTML = '<div class="x-tabs-strip-wrap">'+
11567 '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
11568 '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
11569 return strip.firstChild.firstChild.firstChild.firstChild;
11572 Roo.TabPanel.prototype.createBody = function(container){
11573 var body = document.createElement("div");
11574 Roo.id(body, "tab-body");
11575 Roo.fly(body).addClass("x-tabs-body");
11576 container.appendChild(body);
11580 Roo.TabPanel.prototype.createItemBody = function(bodyEl, id){
11581 var body = Roo.getDom(id);
11583 body = document.createElement("div");
11586 Roo.fly(body).addClass("x-tabs-item-body");
11587 bodyEl.insertBefore(body, bodyEl.firstChild);
11591 Roo.TabPanel.prototype.createStripElements = function(stripEl, text, closable){
11592 var td = document.createElement("td");
11593 stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
11594 //stripEl.appendChild(td);
11596 td.className = "x-tabs-closable";
11597 if(!this.closeTpl){
11598 this.closeTpl = new Roo.Template(
11599 '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
11600 '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
11601 '<div unselectable="on" class="close-icon"> </div></em></span></a>'
11604 var el = this.closeTpl.overwrite(td, {"text": text});
11605 var close = el.getElementsByTagName("div")[0];
11606 var inner = el.getElementsByTagName("em")[0];
11607 return {"el": el, "close": close, "inner": inner};
11610 this.tabTpl = new Roo.Template(
11611 '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
11612 '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
11615 var el = this.tabTpl.overwrite(td, {"text": text});
11616 var inner = el.getElementsByTagName("em")[0];
11617 return {"el": el, "inner": inner};
11621 * Ext JS Library 1.1.1
11622 * Copyright(c) 2006-2007, Ext JS, LLC.
11624 * Originally Released Under LGPL - original licence link has changed is not relivant.
11627 * <script type="text/javascript">
11631 * @class Roo.Button
11632 * @extends Roo.util.Observable
11633 * Simple Button class
11634 * @cfg {String} text The button text
11635 * @cfg {String} icon The path to an image to display in the button (the image will be set as the background-image
11636 * CSS property of the button by default, so if you want a mixed icon/text button, set cls:"x-btn-text-icon")
11637 * @cfg {Function} handler A function called when the button is clicked (can be used instead of click event)
11638 * @cfg {Object} scope The scope of the handler
11639 * @cfg {Number} minWidth The minimum width for this button (used to give a set of buttons a common width)
11640 * @cfg {String/Object} tooltip The tooltip for the button - can be a string or QuickTips config object
11641 * @cfg {Boolean} hidden True to start hidden (defaults to false)
11642 * @cfg {Boolean} disabled True to start disabled (defaults to false)
11643 * @cfg {Boolean} pressed True to start pressed (only if enableToggle = true)
11644 * @cfg {String} toggleGroup The group this toggle button is a member of (only 1 per group can be pressed, only
11645 applies if enableToggle = true)
11646 * @cfg {String/HTMLElement/Element} renderTo The element to append the button to
11647 * @cfg {Boolean/Object} repeat True to repeat fire the click event while the mouse is down. This can also be
11648 an {@link Roo.util.ClickRepeater} config object (defaults to false).
11650 * Create a new button
11651 * @param {Object} config The config object
11653 Roo.Button = function(renderTo, config)
11657 renderTo = config.renderTo || false;
11660 Roo.apply(this, config);
11664 * Fires when this button is clicked
11665 * @param {Button} this
11666 * @param {EventObject} e The click event
11671 * Fires when the "pressed" state of this button changes (only if enableToggle = true)
11672 * @param {Button} this
11673 * @param {Boolean} pressed
11678 * Fires when the mouse hovers over the button
11679 * @param {Button} this
11680 * @param {Event} e The event object
11682 'mouseover' : true,
11685 * Fires when the mouse exits the button
11686 * @param {Button} this
11687 * @param {Event} e The event object
11692 * Fires when the button is rendered
11693 * @param {Button} this
11698 this.menu = Roo.menu.MenuMgr.get(this.menu);
11700 // register listeners first!! - so render can be captured..
11701 Roo.util.Observable.call(this);
11703 this.render(renderTo);
11709 Roo.extend(Roo.Button, Roo.util.Observable, {
11715 * Read-only. True if this button is hidden
11720 * Read-only. True if this button is disabled
11725 * Read-only. True if this button is pressed (only if enableToggle = true)
11731 * @cfg {Number} tabIndex
11732 * The DOM tabIndex for this button (defaults to undefined)
11734 tabIndex : undefined,
11737 * @cfg {Boolean} enableToggle
11738 * True to enable pressed/not pressed toggling (defaults to false)
11740 enableToggle: false,
11742 * @cfg {Mixed} menu
11743 * Standard menu attribute consisting of a reference to a menu object, a menu id or a menu config blob (defaults to undefined).
11747 * @cfg {String} menuAlign
11748 * The position to align the menu to (see {@link Roo.Element#alignTo} for more details, defaults to 'tl-bl?').
11750 menuAlign : "tl-bl?",
11753 * @cfg {String} iconCls
11754 * A css class which sets a background image to be used as the icon for this button (defaults to undefined).
11756 iconCls : undefined,
11758 * @cfg {String} type
11759 * The button's type, corresponding to the DOM input element type attribute. Either "submit," "reset" or "button" (default).
11764 menuClassTarget: 'tr',
11767 * @cfg {String} clickEvent
11768 * The type of event to map to the button's event handler (defaults to 'click')
11770 clickEvent : 'click',
11773 * @cfg {Boolean} handleMouseEvents
11774 * False to disable visual cues on mouseover, mouseout and mousedown (defaults to true)
11776 handleMouseEvents : true,
11779 * @cfg {String} tooltipType
11780 * The type of tooltip to use. Either "qtip" (default) for QuickTips or "title" for title attribute.
11782 tooltipType : 'qtip',
11785 * @cfg {String} cls
11786 * A CSS class to apply to the button's main element.
11790 * @cfg {Roo.Template} template (Optional)
11791 * An {@link Roo.Template} with which to create the Button's main element. This Template must
11792 * contain numeric substitution parameter 0 if it is to display the tRoo property. Changing the template could
11793 * require code modifications if required elements (e.g. a button) aren't present.
11797 render : function(renderTo){
11799 if(this.hideParent){
11800 this.parentEl = Roo.get(renderTo);
11802 if(!this.dhconfig){
11803 if(!this.template){
11804 if(!Roo.Button.buttonTemplate){
11805 // hideous table template
11806 Roo.Button.buttonTemplate = new Roo.Template(
11807 '<table border="0" cellpadding="0" cellspacing="0" class="x-btn-wrap"><tbody><tr>',
11808 '<td class="x-btn-left"><i> </i></td><td class="x-btn-center"><em unselectable="on"><button class="x-btn-text" type="{1}">{0}</button></em></td><td class="x-btn-right"><i> </i></td>',
11809 "</tr></tbody></table>");
11811 this.template = Roo.Button.buttonTemplate;
11813 btn = this.template.append(renderTo, [this.text || ' ', this.type], true);
11814 var btnEl = btn.child("button:first");
11815 btnEl.on('focus', this.onFocus, this);
11816 btnEl.on('blur', this.onBlur, this);
11818 btn.addClass(this.cls);
11821 btnEl.setStyle('background-image', 'url(' +this.icon +')');
11824 btnEl.addClass(this.iconCls);
11826 btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
11829 if(this.tabIndex !== undefined){
11830 btnEl.dom.tabIndex = this.tabIndex;
11833 if(typeof this.tooltip == 'object'){
11834 Roo.QuickTips.tips(Roo.apply({
11838 btnEl.dom[this.tooltipType] = this.tooltip;
11842 btn = Roo.DomHelper.append(Roo.get(renderTo).dom, this.dhconfig, true);
11846 this.el.dom.id = this.el.id = this.id;
11849 this.el.child(this.menuClassTarget).addClass("x-btn-with-menu");
11850 this.menu.on("show", this.onMenuShow, this);
11851 this.menu.on("hide", this.onMenuHide, this);
11853 btn.addClass("x-btn");
11854 if(Roo.isIE && !Roo.isIE7){
11855 this.autoWidth.defer(1, this);
11859 if(this.handleMouseEvents){
11860 btn.on("mouseover", this.onMouseOver, this);
11861 btn.on("mouseout", this.onMouseOut, this);
11862 btn.on("mousedown", this.onMouseDown, this);
11864 btn.on(this.clickEvent, this.onClick, this);
11865 //btn.on("mouseup", this.onMouseUp, this);
11872 Roo.ButtonToggleMgr.register(this);
11874 this.el.addClass("x-btn-pressed");
11877 var repeater = new Roo.util.ClickRepeater(btn,
11878 typeof this.repeat == "object" ? this.repeat : {}
11880 repeater.on("click", this.onClick, this);
11883 this.fireEvent('render', this);
11887 * Returns the button's underlying element
11888 * @return {Roo.Element} The element
11890 getEl : function(){
11895 * Destroys this Button and removes any listeners.
11897 destroy : function(){
11898 Roo.ButtonToggleMgr.unregister(this);
11899 this.el.removeAllListeners();
11900 this.purgeListeners();
11905 autoWidth : function(){
11907 this.el.setWidth("auto");
11908 if(Roo.isIE7 && Roo.isStrict){
11909 var ib = this.el.child('button');
11910 if(ib && ib.getWidth() > 20){
11912 ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
11917 this.el.beginMeasure();
11919 if(this.el.getWidth() < this.minWidth){
11920 this.el.setWidth(this.minWidth);
11923 this.el.endMeasure();
11930 * Assigns this button's click handler
11931 * @param {Function} handler The function to call when the button is clicked
11932 * @param {Object} scope (optional) Scope for the function passed in
11934 setHandler : function(handler, scope){
11935 this.handler = handler;
11936 this.scope = scope;
11940 * Sets this button's text
11941 * @param {String} text The button text
11943 setText : function(text){
11946 this.el.child("td.x-btn-center button.x-btn-text").update(text);
11952 * Gets the text for this button
11953 * @return {String} The button text
11955 getText : function(){
11963 this.hidden = false;
11965 this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "");
11973 this.hidden = true;
11975 this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "none");
11980 * Convenience function for boolean show/hide
11981 * @param {Boolean} visible True to show, false to hide
11983 setVisible: function(visible){
11992 * If a state it passed, it becomes the pressed state otherwise the current state is toggled.
11993 * @param {Boolean} state (optional) Force a particular state
11995 toggle : function(state){
11996 state = state === undefined ? !this.pressed : state;
11997 if(state != this.pressed){
11999 this.el.addClass("x-btn-pressed");
12000 this.pressed = true;
12001 this.fireEvent("toggle", this, true);
12003 this.el.removeClass("x-btn-pressed");
12004 this.pressed = false;
12005 this.fireEvent("toggle", this, false);
12007 if(this.toggleHandler){
12008 this.toggleHandler.call(this.scope || this, this, state);
12016 focus : function(){
12017 this.el.child('button:first').focus();
12021 * Disable this button
12023 disable : function(){
12025 this.el.addClass("x-btn-disabled");
12027 this.disabled = true;
12031 * Enable this button
12033 enable : function(){
12035 this.el.removeClass("x-btn-disabled");
12037 this.disabled = false;
12041 * Convenience function for boolean enable/disable
12042 * @param {Boolean} enabled True to enable, false to disable
12044 setDisabled : function(v){
12045 this[v !== true ? "enable" : "disable"]();
12049 onClick : function(e){
12051 e.preventDefault();
12056 if(!this.disabled){
12057 if(this.enableToggle){
12060 if(this.menu && !this.menu.isVisible()){
12061 this.menu.show(this.el, this.menuAlign);
12063 this.fireEvent("click", this, e);
12065 this.el.removeClass("x-btn-over");
12066 this.handler.call(this.scope || this, this, e);
12071 onMouseOver : function(e){
12072 if(!this.disabled){
12073 this.el.addClass("x-btn-over");
12074 this.fireEvent('mouseover', this, e);
12078 onMouseOut : function(e){
12079 if(!e.within(this.el, true)){
12080 this.el.removeClass("x-btn-over");
12081 this.fireEvent('mouseout', this, e);
12085 onFocus : function(e){
12086 if(!this.disabled){
12087 this.el.addClass("x-btn-focus");
12091 onBlur : function(e){
12092 this.el.removeClass("x-btn-focus");
12095 onMouseDown : function(e){
12096 if(!this.disabled && e.button == 0){
12097 this.el.addClass("x-btn-click");
12098 Roo.get(document).on('mouseup', this.onMouseUp, this);
12102 onMouseUp : function(e){
12104 this.el.removeClass("x-btn-click");
12105 Roo.get(document).un('mouseup', this.onMouseUp, this);
12109 onMenuShow : function(e){
12110 this.el.addClass("x-btn-menu-active");
12113 onMenuHide : function(e){
12114 this.el.removeClass("x-btn-menu-active");
12118 // Private utility class used by Button
12119 Roo.ButtonToggleMgr = function(){
12122 function toggleGroup(btn, state){
12124 var g = groups[btn.toggleGroup];
12125 for(var i = 0, l = g.length; i < l; i++){
12127 g[i].toggle(false);
12134 register : function(btn){
12135 if(!btn.toggleGroup){
12138 var g = groups[btn.toggleGroup];
12140 g = groups[btn.toggleGroup] = [];
12143 btn.on("toggle", toggleGroup);
12146 unregister : function(btn){
12147 if(!btn.toggleGroup){
12150 var g = groups[btn.toggleGroup];
12153 btn.un("toggle", toggleGroup);
12159 * Ext JS Library 1.1.1
12160 * Copyright(c) 2006-2007, Ext JS, LLC.
12162 * Originally Released Under LGPL - original licence link has changed is not relivant.
12165 * <script type="text/javascript">
12169 * @class Roo.SplitButton
12170 * @extends Roo.Button
12171 * A split button that provides a built-in dropdown arrow that can fire an event separately from the default
12172 * click event of the button. Typically this would be used to display a dropdown menu that provides additional
12173 * options to the primary button action, but any custom handler can provide the arrowclick implementation.
12174 * @cfg {Function} arrowHandler A function called when the arrow button is clicked (can be used instead of click event)
12175 * @cfg {String} arrowTooltip The title attribute of the arrow
12177 * Create a new menu button
12178 * @param {String/HTMLElement/Element} renderTo The element to append the button to
12179 * @param {Object} config The config object
12181 Roo.SplitButton = function(renderTo, config){
12182 Roo.SplitButton.superclass.constructor.call(this, renderTo, config);
12184 * @event arrowclick
12185 * Fires when this button's arrow is clicked
12186 * @param {SplitButton} this
12187 * @param {EventObject} e The click event
12189 this.addEvents({"arrowclick":true});
12192 Roo.extend(Roo.SplitButton, Roo.Button, {
12193 render : function(renderTo){
12194 // this is one sweet looking template!
12195 var tpl = new Roo.Template(
12196 '<table cellspacing="0" class="x-btn-menu-wrap x-btn"><tr><td>',
12197 '<table cellspacing="0" class="x-btn-wrap x-btn-menu-text-wrap"><tbody>',
12198 '<tr><td class="x-btn-left"><i> </i></td><td class="x-btn-center"><button class="x-btn-text" type="{1}">{0}</button></td></tr>',
12199 "</tbody></table></td><td>",
12200 '<table cellspacing="0" class="x-btn-wrap x-btn-menu-arrow-wrap"><tbody>',
12201 '<tr><td class="x-btn-center"><button class="x-btn-menu-arrow-el" type="button"> </button></td><td class="x-btn-right"><i> </i></td></tr>',
12202 "</tbody></table></td></tr></table>"
12204 var btn = tpl.append(renderTo, [this.text, this.type], true);
12205 var btnEl = btn.child("button");
12207 btn.addClass(this.cls);
12210 btnEl.setStyle('background-image', 'url(' +this.icon +')');
12213 btnEl.addClass(this.iconCls);
12215 btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
12219 if(this.handleMouseEvents){
12220 btn.on("mouseover", this.onMouseOver, this);
12221 btn.on("mouseout", this.onMouseOut, this);
12222 btn.on("mousedown", this.onMouseDown, this);
12223 btn.on("mouseup", this.onMouseUp, this);
12225 btn.on(this.clickEvent, this.onClick, this);
12227 if(typeof this.tooltip == 'object'){
12228 Roo.QuickTips.tips(Roo.apply({
12232 btnEl.dom[this.tooltipType] = this.tooltip;
12235 if(this.arrowTooltip){
12236 btn.child("button:nth(2)").dom[this.tooltipType] = this.arrowTooltip;
12245 this.el.addClass("x-btn-pressed");
12247 if(Roo.isIE && !Roo.isIE7){
12248 this.autoWidth.defer(1, this);
12253 this.menu.on("show", this.onMenuShow, this);
12254 this.menu.on("hide", this.onMenuHide, this);
12256 this.fireEvent('render', this);
12260 autoWidth : function(){
12262 var tbl = this.el.child("table:first");
12263 var tbl2 = this.el.child("table:last");
12264 this.el.setWidth("auto");
12265 tbl.setWidth("auto");
12266 if(Roo.isIE7 && Roo.isStrict){
12267 var ib = this.el.child('button:first');
12268 if(ib && ib.getWidth() > 20){
12270 ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
12275 this.el.beginMeasure();
12277 if((tbl.getWidth()+tbl2.getWidth()) < this.minWidth){
12278 tbl.setWidth(this.minWidth-tbl2.getWidth());
12281 this.el.endMeasure();
12284 this.el.setWidth(tbl.getWidth()+tbl2.getWidth());
12288 * Sets this button's click handler
12289 * @param {Function} handler The function to call when the button is clicked
12290 * @param {Object} scope (optional) Scope for the function passed above
12292 setHandler : function(handler, scope){
12293 this.handler = handler;
12294 this.scope = scope;
12298 * Sets this button's arrow click handler
12299 * @param {Function} handler The function to call when the arrow is clicked
12300 * @param {Object} scope (optional) Scope for the function passed above
12302 setArrowHandler : function(handler, scope){
12303 this.arrowHandler = handler;
12304 this.scope = scope;
12310 focus : function(){
12312 this.el.child("button:first").focus();
12317 onClick : function(e){
12318 e.preventDefault();
12319 if(!this.disabled){
12320 if(e.getTarget(".x-btn-menu-arrow-wrap")){
12321 if(this.menu && !this.menu.isVisible()){
12322 this.menu.show(this.el, this.menuAlign);
12324 this.fireEvent("arrowclick", this, e);
12325 if(this.arrowHandler){
12326 this.arrowHandler.call(this.scope || this, this, e);
12329 this.fireEvent("click", this, e);
12331 this.handler.call(this.scope || this, this, e);
12337 onMouseDown : function(e){
12338 if(!this.disabled){
12339 Roo.fly(e.getTarget("table")).addClass("x-btn-click");
12343 onMouseUp : function(e){
12344 Roo.fly(e.getTarget("table")).removeClass("x-btn-click");
12349 // backwards compat
12350 Roo.MenuButton = Roo.SplitButton;/*
12352 * Ext JS Library 1.1.1
12353 * Copyright(c) 2006-2007, Ext JS, LLC.
12355 * Originally Released Under LGPL - original licence link has changed is not relivant.
12358 * <script type="text/javascript">
12362 * @class Roo.Toolbar
12363 * Basic Toolbar class.
12365 * Creates a new Toolbar
12366 * @param {Object} container The config object
12368 Roo.Toolbar = function(container, buttons, config)
12370 /// old consturctor format still supported..
12371 if(container instanceof Array){ // omit the container for later rendering
12372 buttons = container;
12376 if (typeof(container) == 'object' && container.xtype) {
12377 config = container;
12378 container = config.container;
12379 buttons = config.buttons || []; // not really - use items!!
12382 if (config && config.items) {
12383 xitems = config.items;
12384 delete config.items;
12386 Roo.apply(this, config);
12387 this.buttons = buttons;
12390 this.render(container);
12392 this.xitems = xitems;
12393 Roo.each(xitems, function(b) {
12399 Roo.Toolbar.prototype = {
12401 * @cfg {Array} items
12402 * array of button configs or elements to add (will be converted to a MixedCollection)
12406 * @cfg {String/HTMLElement/Element} container
12407 * The id or element that will contain the toolbar
12410 render : function(ct){
12411 this.el = Roo.get(ct);
12413 this.el.addClass(this.cls);
12415 // using a table allows for vertical alignment
12416 // 100% width is needed by Safari...
12417 this.el.update('<div class="x-toolbar x-small-editor"><table cellspacing="0"><tr></tr></table></div>');
12418 this.tr = this.el.child("tr", true);
12420 this.items = new Roo.util.MixedCollection(false, function(o){
12421 return o.id || ("item" + (++autoId));
12424 this.add.apply(this, this.buttons);
12425 delete this.buttons;
12430 * Adds element(s) to the toolbar -- this function takes a variable number of
12431 * arguments of mixed type and adds them to the toolbar.
12432 * @param {Mixed} arg1 The following types of arguments are all valid:<br />
12434 * <li>{@link Roo.Toolbar.Button} config: A valid button config object (equivalent to {@link #addButton})</li>
12435 * <li>HtmlElement: Any standard HTML element (equivalent to {@link #addElement})</li>
12436 * <li>Field: Any form field (equivalent to {@link #addField})</li>
12437 * <li>Item: Any subclass of {@link Roo.Toolbar.Item} (equivalent to {@link #addItem})</li>
12438 * <li>String: Any generic string (gets wrapped in a {@link Roo.Toolbar.TextItem}, equivalent to {@link #addText}).
12439 * Note that there are a few special strings that are treated differently as explained nRoo.</li>
12440 * <li>'separator' or '-': Creates a separator element (equivalent to {@link #addSeparator})</li>
12441 * <li>' ': Creates a spacer element (equivalent to {@link #addSpacer})</li>
12442 * <li>'->': Creates a fill element (equivalent to {@link #addFill})</li>
12444 * @param {Mixed} arg2
12445 * @param {Mixed} etc.
12448 var a = arguments, l = a.length;
12449 for(var i = 0; i < l; i++){
12454 _add : function(el) {
12457 el = Roo.factory(el, typeof(Roo.Toolbar[el.xtype]) == 'undefined' ? Roo.form : Roo.Toolbar);
12460 if (el.applyTo){ // some kind of form field
12461 return this.addField(el);
12463 if (el.render){ // some kind of Toolbar.Item
12464 return this.addItem(el);
12466 if (typeof el == "string"){ // string
12467 if(el == "separator" || el == "-"){
12468 return this.addSeparator();
12471 return this.addSpacer();
12474 return this.addFill();
12476 return this.addText(el);
12479 if(el.tagName){ // element
12480 return this.addElement(el);
12482 if(typeof el == "object"){ // must be button config?
12483 return this.addButton(el);
12485 // and now what?!?!
12491 * Add an Xtype element
12492 * @param {Object} xtype Xtype Object
12493 * @return {Object} created Object
12495 addxtype : function(e){
12496 return this.add(e);
12500 * Returns the Element for this toolbar.
12501 * @return {Roo.Element}
12503 getEl : function(){
12509 * @return {Roo.Toolbar.Item} The separator item
12511 addSeparator : function(){
12512 return this.addItem(new Roo.Toolbar.Separator());
12516 * Adds a spacer element
12517 * @return {Roo.Toolbar.Spacer} The spacer item
12519 addSpacer : function(){
12520 return this.addItem(new Roo.Toolbar.Spacer());
12524 * Adds a fill element that forces subsequent additions to the right side of the toolbar
12525 * @return {Roo.Toolbar.Fill} The fill item
12527 addFill : function(){
12528 return this.addItem(new Roo.Toolbar.Fill());
12532 * Adds any standard HTML element to the toolbar
12533 * @param {String/HTMLElement/Element} el The element or id of the element to add
12534 * @return {Roo.Toolbar.Item} The element's item
12536 addElement : function(el){
12537 return this.addItem(new Roo.Toolbar.Item(el));
12540 * Collection of items on the toolbar.. (only Toolbar Items, so use fields to retrieve fields)
12541 * @type Roo.util.MixedCollection
12546 * Adds any Toolbar.Item or subclass
12547 * @param {Roo.Toolbar.Item} item
12548 * @return {Roo.Toolbar.Item} The item
12550 addItem : function(item){
12551 var td = this.nextBlock();
12553 this.items.add(item);
12558 * Adds a button (or buttons). See {@link Roo.Toolbar.Button} for more info on the config.
12559 * @param {Object/Array} config A button config or array of configs
12560 * @return {Roo.Toolbar.Button/Array}
12562 addButton : function(config){
12563 if(config instanceof Array){
12565 for(var i = 0, len = config.length; i < len; i++) {
12566 buttons.push(this.addButton(config[i]));
12571 if(!(config instanceof Roo.Toolbar.Button)){
12573 new Roo.Toolbar.SplitButton(config) :
12574 new Roo.Toolbar.Button(config);
12576 var td = this.nextBlock();
12583 * Adds text to the toolbar
12584 * @param {String} text The text to add
12585 * @return {Roo.Toolbar.Item} The element's item
12587 addText : function(text){
12588 return this.addItem(new Roo.Toolbar.TextItem(text));
12592 * Inserts any {@link Roo.Toolbar.Item}/{@link Roo.Toolbar.Button} at the specified index.
12593 * @param {Number} index The index where the item is to be inserted
12594 * @param {Object/Roo.Toolbar.Item/Roo.Toolbar.Button (may be Array)} item The button, or button config object to be inserted.
12595 * @return {Roo.Toolbar.Button/Item}
12597 insertButton : function(index, item){
12598 if(item instanceof Array){
12600 for(var i = 0, len = item.length; i < len; i++) {
12601 buttons.push(this.insertButton(index + i, item[i]));
12605 if (!(item instanceof Roo.Toolbar.Button)){
12606 item = new Roo.Toolbar.Button(item);
12608 var td = document.createElement("td");
12609 this.tr.insertBefore(td, this.tr.childNodes[index]);
12611 this.items.insert(index, item);
12616 * Adds a new element to the toolbar from the passed {@link Roo.DomHelper} config.
12617 * @param {Object} config
12618 * @return {Roo.Toolbar.Item} The element's item
12620 addDom : function(config, returnEl){
12621 var td = this.nextBlock();
12622 Roo.DomHelper.overwrite(td, config);
12623 var ti = new Roo.Toolbar.Item(td.firstChild);
12625 this.items.add(ti);
12630 * Collection of fields on the toolbar.. usefull for quering (value is false if there are no fields)
12631 * @type Roo.util.MixedCollection
12636 * Adds a dynamically rendered Roo.form field (TextField, ComboBox, etc).
12637 * Note: the field should not have been rendered yet. For a field that has already been
12638 * rendered, use {@link #addElement}.
12639 * @param {Roo.form.Field} field
12640 * @return {Roo.ToolbarItem}
12644 addField : function(field) {
12645 if (!this.fields) {
12647 this.fields = new Roo.util.MixedCollection(false, function(o){
12648 return o.id || ("item" + (++autoId));
12653 var td = this.nextBlock();
12655 var ti = new Roo.Toolbar.Item(td.firstChild);
12657 this.items.add(ti);
12658 this.fields.add(field);
12669 this.el.child('div').setVisibilityMode(Roo.Element.DISPLAY);
12670 this.el.child('div').hide();
12678 this.el.child('div').show();
12682 nextBlock : function(){
12683 var td = document.createElement("td");
12684 this.tr.appendChild(td);
12689 destroy : function(){
12690 if(this.items){ // rendered?
12691 Roo.destroy.apply(Roo, this.items.items);
12693 if(this.fields){ // rendered?
12694 Roo.destroy.apply(Roo, this.fields.items);
12696 Roo.Element.uncache(this.el, this.tr);
12701 * @class Roo.Toolbar.Item
12702 * The base class that other classes should extend in order to get some basic common toolbar item functionality.
12704 * Creates a new Item
12705 * @param {HTMLElement} el
12707 Roo.Toolbar.Item = function(el){
12708 this.el = Roo.getDom(el);
12709 this.id = Roo.id(this.el);
12710 this.hidden = false;
12713 Roo.Toolbar.Item.prototype = {
12716 * Get this item's HTML Element
12717 * @return {HTMLElement}
12719 getEl : function(){
12724 render : function(td){
12726 td.appendChild(this.el);
12730 * Removes and destroys this item.
12732 destroy : function(){
12733 this.td.parentNode.removeChild(this.td);
12740 this.hidden = false;
12741 this.td.style.display = "";
12748 this.hidden = true;
12749 this.td.style.display = "none";
12753 * Convenience function for boolean show/hide.
12754 * @param {Boolean} visible true to show/false to hide
12756 setVisible: function(visible){
12765 * Try to focus this item.
12767 focus : function(){
12768 Roo.fly(this.el).focus();
12772 * Disables this item.
12774 disable : function(){
12775 Roo.fly(this.td).addClass("x-item-disabled");
12776 this.disabled = true;
12777 this.el.disabled = true;
12781 * Enables this item.
12783 enable : function(){
12784 Roo.fly(this.td).removeClass("x-item-disabled");
12785 this.disabled = false;
12786 this.el.disabled = false;
12792 * @class Roo.Toolbar.Separator
12793 * @extends Roo.Toolbar.Item
12794 * A simple toolbar separator class
12796 * Creates a new Separator
12798 Roo.Toolbar.Separator = function(){
12799 var s = document.createElement("span");
12800 s.className = "ytb-sep";
12801 Roo.Toolbar.Separator.superclass.constructor.call(this, s);
12803 Roo.extend(Roo.Toolbar.Separator, Roo.Toolbar.Item, {
12804 enable:Roo.emptyFn,
12805 disable:Roo.emptyFn,
12810 * @class Roo.Toolbar.Spacer
12811 * @extends Roo.Toolbar.Item
12812 * A simple element that adds extra horizontal space to a toolbar.
12814 * Creates a new Spacer
12816 Roo.Toolbar.Spacer = function(){
12817 var s = document.createElement("div");
12818 s.className = "ytb-spacer";
12819 Roo.Toolbar.Spacer.superclass.constructor.call(this, s);
12821 Roo.extend(Roo.Toolbar.Spacer, Roo.Toolbar.Item, {
12822 enable:Roo.emptyFn,
12823 disable:Roo.emptyFn,
12828 * @class Roo.Toolbar.Fill
12829 * @extends Roo.Toolbar.Spacer
12830 * A simple element that adds a greedy (100% width) horizontal space to a toolbar.
12832 * Creates a new Spacer
12834 Roo.Toolbar.Fill = Roo.extend(Roo.Toolbar.Spacer, {
12836 render : function(td){
12837 td.style.width = '100%';
12838 Roo.Toolbar.Fill.superclass.render.call(this, td);
12843 * @class Roo.Toolbar.TextItem
12844 * @extends Roo.Toolbar.Item
12845 * A simple class that renders text directly into a toolbar.
12847 * Creates a new TextItem
12848 * @param {String} text
12850 Roo.Toolbar.TextItem = function(text){
12851 if (typeof(text) == 'object') {
12854 var s = document.createElement("span");
12855 s.className = "ytb-text";
12856 s.innerHTML = text;
12857 Roo.Toolbar.TextItem.superclass.constructor.call(this, s);
12859 Roo.extend(Roo.Toolbar.TextItem, Roo.Toolbar.Item, {
12860 enable:Roo.emptyFn,
12861 disable:Roo.emptyFn,
12866 * @class Roo.Toolbar.Button
12867 * @extends Roo.Button
12868 * A button that renders into a toolbar.
12870 * Creates a new Button
12871 * @param {Object} config A standard {@link Roo.Button} config object
12873 Roo.Toolbar.Button = function(config){
12874 Roo.Toolbar.Button.superclass.constructor.call(this, null, config);
12876 Roo.extend(Roo.Toolbar.Button, Roo.Button, {
12877 render : function(td){
12879 Roo.Toolbar.Button.superclass.render.call(this, td);
12883 * Removes and destroys this button
12885 destroy : function(){
12886 Roo.Toolbar.Button.superclass.destroy.call(this);
12887 this.td.parentNode.removeChild(this.td);
12891 * Shows this button
12894 this.hidden = false;
12895 this.td.style.display = "";
12899 * Hides this button
12902 this.hidden = true;
12903 this.td.style.display = "none";
12907 * Disables this item
12909 disable : function(){
12910 Roo.fly(this.td).addClass("x-item-disabled");
12911 this.disabled = true;
12915 * Enables this item
12917 enable : function(){
12918 Roo.fly(this.td).removeClass("x-item-disabled");
12919 this.disabled = false;
12922 // backwards compat
12923 Roo.ToolbarButton = Roo.Toolbar.Button;
12926 * @class Roo.Toolbar.SplitButton
12927 * @extends Roo.SplitButton
12928 * A menu button that renders into a toolbar.
12930 * Creates a new SplitButton
12931 * @param {Object} config A standard {@link Roo.SplitButton} config object
12933 Roo.Toolbar.SplitButton = function(config){
12934 Roo.Toolbar.SplitButton.superclass.constructor.call(this, null, config);
12936 Roo.extend(Roo.Toolbar.SplitButton, Roo.SplitButton, {
12937 render : function(td){
12939 Roo.Toolbar.SplitButton.superclass.render.call(this, td);
12943 * Removes and destroys this button
12945 destroy : function(){
12946 Roo.Toolbar.SplitButton.superclass.destroy.call(this);
12947 this.td.parentNode.removeChild(this.td);
12951 * Shows this button
12954 this.hidden = false;
12955 this.td.style.display = "";
12959 * Hides this button
12962 this.hidden = true;
12963 this.td.style.display = "none";
12967 // backwards compat
12968 Roo.Toolbar.MenuButton = Roo.Toolbar.SplitButton;/*
12970 * Ext JS Library 1.1.1
12971 * Copyright(c) 2006-2007, Ext JS, LLC.
12973 * Originally Released Under LGPL - original licence link has changed is not relivant.
12976 * <script type="text/javascript">
12980 * @class Roo.PagingToolbar
12981 * @extends Roo.Toolbar
12982 * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
12984 * Create a new PagingToolbar
12985 * @param {Object} config The config object
12987 Roo.PagingToolbar = function(el, ds, config)
12989 // old args format still supported... - xtype is prefered..
12990 if (typeof(el) == 'object' && el.xtype) {
12991 // created from xtype...
12993 ds = el.dataSource;
12994 el = config.container;
12997 if (config.items) {
12998 items = config.items;
13002 Roo.PagingToolbar.superclass.constructor.call(this, el, null, config);
13005 this.renderButtons(this.el);
13008 // supprot items array.
13010 Roo.each(items, function(e) {
13011 this.add(Roo.factory(e));
13016 Roo.extend(Roo.PagingToolbar, Roo.Toolbar, {
13018 * @cfg {Roo.data.Store} dataSource
13019 * The underlying data store providing the paged data
13022 * @cfg {String/HTMLElement/Element} container
13023 * container The id or element that will contain the toolbar
13026 * @cfg {Boolean} displayInfo
13027 * True to display the displayMsg (defaults to false)
13030 * @cfg {Number} pageSize
13031 * The number of records to display per page (defaults to 20)
13035 * @cfg {String} displayMsg
13036 * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
13038 displayMsg : 'Displaying {0} - {1} of {2}',
13040 * @cfg {String} emptyMsg
13041 * The message to display when no records are found (defaults to "No data to display")
13043 emptyMsg : 'No data to display',
13045 * Customizable piece of the default paging text (defaults to "Page")
13048 beforePageText : "Page",
13050 * Customizable piece of the default paging text (defaults to "of %0")
13053 afterPageText : "of {0}",
13055 * Customizable piece of the default paging text (defaults to "First Page")
13058 firstText : "First Page",
13060 * Customizable piece of the default paging text (defaults to "Previous Page")
13063 prevText : "Previous Page",
13065 * Customizable piece of the default paging text (defaults to "Next Page")
13068 nextText : "Next Page",
13070 * Customizable piece of the default paging text (defaults to "Last Page")
13073 lastText : "Last Page",
13075 * Customizable piece of the default paging text (defaults to "Refresh")
13078 refreshText : "Refresh",
13081 renderButtons : function(el){
13082 Roo.PagingToolbar.superclass.render.call(this, el);
13083 this.first = this.addButton({
13084 tooltip: this.firstText,
13085 cls: "x-btn-icon x-grid-page-first",
13087 handler: this.onClick.createDelegate(this, ["first"])
13089 this.prev = this.addButton({
13090 tooltip: this.prevText,
13091 cls: "x-btn-icon x-grid-page-prev",
13093 handler: this.onClick.createDelegate(this, ["prev"])
13095 //this.addSeparator();
13096 this.add(this.beforePageText);
13097 this.field = Roo.get(this.addDom({
13102 cls: "x-grid-page-number"
13104 this.field.on("keydown", this.onPagingKeydown, this);
13105 this.field.on("focus", function(){this.dom.select();});
13106 this.afterTextEl = this.addText(String.format(this.afterPageText, 1));
13107 this.field.setHeight(18);
13108 //this.addSeparator();
13109 this.next = this.addButton({
13110 tooltip: this.nextText,
13111 cls: "x-btn-icon x-grid-page-next",
13113 handler: this.onClick.createDelegate(this, ["next"])
13115 this.last = this.addButton({
13116 tooltip: this.lastText,
13117 cls: "x-btn-icon x-grid-page-last",
13119 handler: this.onClick.createDelegate(this, ["last"])
13121 //this.addSeparator();
13122 this.loading = this.addButton({
13123 tooltip: this.refreshText,
13124 cls: "x-btn-icon x-grid-loading",
13125 handler: this.onClick.createDelegate(this, ["refresh"])
13128 if(this.displayInfo){
13129 this.displayEl = Roo.fly(this.el.dom.firstChild).createChild({cls:'x-paging-info'});
13134 updateInfo : function(){
13135 if(this.displayEl){
13136 var count = this.ds.getCount();
13137 var msg = count == 0 ?
13141 this.cursor+1, this.cursor+count, this.ds.getTotalCount()
13143 this.displayEl.update(msg);
13148 onLoad : function(ds, r, o){
13149 this.cursor = o.params ? o.params.start : 0;
13150 var d = this.getPageData(), ap = d.activePage, ps = d.pages;
13152 this.afterTextEl.el.innerHTML = String.format(this.afterPageText, d.pages);
13153 this.field.dom.value = ap;
13154 this.first.setDisabled(ap == 1);
13155 this.prev.setDisabled(ap == 1);
13156 this.next.setDisabled(ap == ps);
13157 this.last.setDisabled(ap == ps);
13158 this.loading.enable();
13163 getPageData : function(){
13164 var total = this.ds.getTotalCount();
13167 activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
13168 pages : total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
13173 onLoadError : function(){
13174 this.loading.enable();
13178 onPagingKeydown : function(e){
13179 var k = e.getKey();
13180 var d = this.getPageData();
13182 var v = this.field.dom.value, pageNum;
13183 if(!v || isNaN(pageNum = parseInt(v, 10))){
13184 this.field.dom.value = d.activePage;
13187 pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
13188 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
13191 else if(k == e.HOME || (k == e.UP && e.ctrlKey) || (k == e.PAGEUP && e.ctrlKey) || (k == e.RIGHT && e.ctrlKey) || k == e.END || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey))
13193 var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
13194 this.field.dom.value = pageNum;
13195 this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
13198 else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
13200 var v = this.field.dom.value, pageNum;
13201 var increment = (e.shiftKey) ? 10 : 1;
13202 if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
13204 if(!v || isNaN(pageNum = parseInt(v, 10))) {
13205 this.field.dom.value = d.activePage;
13208 else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
13210 this.field.dom.value = parseInt(v, 10) + increment;
13211 pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
13212 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
13219 beforeLoad : function(){
13221 this.loading.disable();
13226 onClick : function(which){
13230 ds.load({params:{start: 0, limit: this.pageSize}});
13233 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
13236 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
13239 var total = ds.getTotalCount();
13240 var extra = total % this.pageSize;
13241 var lastStart = extra ? (total - extra) : total-this.pageSize;
13242 ds.load({params:{start: lastStart, limit: this.pageSize}});
13245 ds.load({params:{start: this.cursor, limit: this.pageSize}});
13251 * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
13252 * @param {Roo.data.Store} store The data store to unbind
13254 unbind : function(ds){
13255 ds.un("beforeload", this.beforeLoad, this);
13256 ds.un("load", this.onLoad, this);
13257 ds.un("loadexception", this.onLoadError, this);
13258 ds.un("remove", this.updateInfo, this);
13259 ds.un("add", this.updateInfo, this);
13260 this.ds = undefined;
13264 * Binds the paging toolbar to the specified {@link Roo.data.Store}
13265 * @param {Roo.data.Store} store The data store to bind
13267 bind : function(ds){
13268 ds.on("beforeload", this.beforeLoad, this);
13269 ds.on("load", this.onLoad, this);
13270 ds.on("loadexception", this.onLoadError, this);
13271 ds.on("remove", this.updateInfo, this);
13272 ds.on("add", this.updateInfo, this);
13277 * Ext JS Library 1.1.1
13278 * Copyright(c) 2006-2007, Ext JS, LLC.
13280 * Originally Released Under LGPL - original licence link has changed is not relivant.
13283 * <script type="text/javascript">
13287 * @class Roo.Resizable
13288 * @extends Roo.util.Observable
13289 * <p>Applies drag handles to an element to make it resizable. The drag handles are inserted into the element
13290 * and positioned absolute. Some elements, such as a textarea or image, don't support this. To overcome that, you can wrap
13291 * the textarea in a div and set "resizeChild" to true (or to the id of the element), <b>or</b> set wrap:true in your config and
13292 * the element will be wrapped for you automatically.</p>
13293 * <p>Here is the list of valid resize handles:</p>
13296 ------ -------------------
13305 'hd' horizontal drag
13308 * <p>Here's an example showing the creation of a typical Resizable:</p>
13310 var resizer = new Roo.Resizable("element-id", {
13318 resizer.on("resize", myHandler);
13320 * <p>To hide a particular handle, set its display to none in CSS, or through script:<br>
13321 * resizer.east.setDisplayed(false);</p>
13322 * @cfg {Boolean/String/Element} resizeChild True to resize the first child, or id/element to resize (defaults to false)
13323 * @cfg {Array/String} adjustments String "auto" or an array [width, height] with values to be <b>added</b> to the
13324 * resize operation's new size (defaults to [0, 0])
13325 * @cfg {Number} minWidth The minimum width for the element (defaults to 5)
13326 * @cfg {Number} minHeight The minimum height for the element (defaults to 5)
13327 * @cfg {Number} maxWidth The maximum width for the element (defaults to 10000)
13328 * @cfg {Number} maxHeight The maximum height for the element (defaults to 10000)
13329 * @cfg {Boolean} enabled False to disable resizing (defaults to true)
13330 * @cfg {Boolean} wrap True to wrap an element with a div if needed (required for textareas and images, defaults to false)
13331 * @cfg {Number} width The width of the element in pixels (defaults to null)
13332 * @cfg {Number} height The height of the element in pixels (defaults to null)
13333 * @cfg {Boolean} animate True to animate the resize (not compatible with dynamic sizing, defaults to false)
13334 * @cfg {Number} duration Animation duration if animate = true (defaults to .35)
13335 * @cfg {Boolean} dynamic True to resize the element while dragging instead of using a proxy (defaults to false)
13336 * @cfg {String} handles String consisting of the resize handles to display (defaults to undefined)
13337 * @cfg {Boolean} multiDirectional <b>Deprecated</b>. The old style of adding multi-direction resize handles, deprecated
13338 * in favor of the handles config option (defaults to false)
13339 * @cfg {Boolean} disableTrackOver True to disable mouse tracking. This is only applied at config time. (defaults to false)
13340 * @cfg {String} easing Animation easing if animate = true (defaults to 'easingOutStrong')
13341 * @cfg {Number} widthIncrement The increment to snap the width resize in pixels (dynamic must be true, defaults to 0)
13342 * @cfg {Number} heightIncrement The increment to snap the height resize in pixels (dynamic must be true, defaults to 0)
13343 * @cfg {Boolean} pinned True to ensure that the resize handles are always visible, false to display them only when the
13344 * user mouses over the resizable borders. This is only applied at config time. (defaults to false)
13345 * @cfg {Boolean} preserveRatio True to preserve the original ratio between height and width during resize (defaults to false)
13346 * @cfg {Boolean} transparent True for transparent handles. This is only applied at config time. (defaults to false)
13347 * @cfg {Number} minX The minimum allowed page X for the element (only used for west resizing, defaults to 0)
13348 * @cfg {Number} minY The minimum allowed page Y for the element (only used for north resizing, defaults to 0)
13349 * @cfg {Boolean} draggable Convenience to initialize drag drop (defaults to false)
13351 * Create a new resizable component
13352 * @param {String/HTMLElement/Roo.Element} el The id or element to resize
13353 * @param {Object} config configuration options
13355 Roo.Resizable = function(el, config)
13357 this.el = Roo.get(el);
13359 if(config && config.wrap){
13360 config.resizeChild = this.el;
13361 this.el = this.el.wrap(typeof config.wrap == "object" ? config.wrap : {cls:"xresizable-wrap"});
13362 this.el.id = this.el.dom.id = config.resizeChild.id + "-rzwrap";
13363 this.el.setStyle("overflow", "hidden");
13364 this.el.setPositioning(config.resizeChild.getPositioning());
13365 config.resizeChild.clearPositioning();
13366 if(!config.width || !config.height){
13367 var csize = config.resizeChild.getSize();
13368 this.el.setSize(csize.width, csize.height);
13370 if(config.pinned && !config.adjustments){
13371 config.adjustments = "auto";
13375 this.proxy = this.el.createProxy({tag: "div", cls: "x-resizable-proxy", id: this.el.id + "-rzproxy"});
13376 this.proxy.unselectable();
13377 this.proxy.enableDisplayMode('block');
13379 Roo.apply(this, config);
13382 this.disableTrackOver = true;
13383 this.el.addClass("x-resizable-pinned");
13385 // if the element isn't positioned, make it relative
13386 var position = this.el.getStyle("position");
13387 if(position != "absolute" && position != "fixed"){
13388 this.el.setStyle("position", "relative");
13390 if(!this.handles){ // no handles passed, must be legacy style
13391 this.handles = 's,e,se';
13392 if(this.multiDirectional){
13393 this.handles += ',n,w';
13396 if(this.handles == "all"){
13397 this.handles = "n s e w ne nw se sw";
13399 var hs = this.handles.split(/\s*?[,;]\s*?| /);
13400 var ps = Roo.Resizable.positions;
13401 for(var i = 0, len = hs.length; i < len; i++){
13402 if(hs[i] && ps[hs[i]]){
13403 var pos = ps[hs[i]];
13404 this[pos] = new Roo.Resizable.Handle(this, pos, this.disableTrackOver, this.transparent);
13408 this.corner = this.southeast;
13410 // updateBox = the box can move..
13411 if(this.handles.indexOf("n") != -1 || this.handles.indexOf("w") != -1 || this.handles.indexOf("hd") != -1) {
13412 this.updateBox = true;
13415 this.activeHandle = null;
13417 if(this.resizeChild){
13418 if(typeof this.resizeChild == "boolean"){
13419 this.resizeChild = Roo.get(this.el.dom.firstChild, true);
13421 this.resizeChild = Roo.get(this.resizeChild, true);
13425 if(this.adjustments == "auto"){
13426 var rc = this.resizeChild;
13427 var hw = this.west, he = this.east, hn = this.north, hs = this.south;
13428 if(rc && (hw || hn)){
13429 rc.position("relative");
13430 rc.setLeft(hw ? hw.el.getWidth() : 0);
13431 rc.setTop(hn ? hn.el.getHeight() : 0);
13433 this.adjustments = [
13434 (he ? -he.el.getWidth() : 0) + (hw ? -hw.el.getWidth() : 0),
13435 (hn ? -hn.el.getHeight() : 0) + (hs ? -hs.el.getHeight() : 0) -1
13439 if(this.draggable){
13440 this.dd = this.dynamic ?
13441 this.el.initDD(null) : this.el.initDDProxy(null, {dragElId: this.proxy.id});
13442 this.dd.setHandleElId(this.resizeChild ? this.resizeChild.id : this.el.id);
13448 * @event beforeresize
13449 * Fired before resize is allowed. Set enabled to false to cancel resize.
13450 * @param {Roo.Resizable} this
13451 * @param {Roo.EventObject} e The mousedown event
13453 "beforeresize" : true,
13456 * Fired after a resize.
13457 * @param {Roo.Resizable} this
13458 * @param {Number} width The new width
13459 * @param {Number} height The new height
13460 * @param {Roo.EventObject} e The mouseup event
13465 if(this.width !== null && this.height !== null){
13466 this.resizeTo(this.width, this.height);
13468 this.updateChildSize();
13471 this.el.dom.style.zoom = 1;
13473 Roo.Resizable.superclass.constructor.call(this);
13476 Roo.extend(Roo.Resizable, Roo.util.Observable, {
13477 resizeChild : false,
13478 adjustments : [0, 0],
13488 multiDirectional : false,
13489 disableTrackOver : false,
13490 easing : 'easeOutStrong',
13491 widthIncrement : 0,
13492 heightIncrement : 0,
13496 preserveRatio : false,
13497 transparent: false,
13503 * @cfg {String/HTMLElement/Element} constrainTo Constrain the resize to a particular element
13505 constrainTo: undefined,
13507 * @cfg {Roo.lib.Region} resizeRegion Constrain the resize to a particular region
13509 resizeRegion: undefined,
13513 * Perform a manual resize
13514 * @param {Number} width
13515 * @param {Number} height
13517 resizeTo : function(width, height){
13518 this.el.setSize(width, height);
13519 this.updateChildSize();
13520 this.fireEvent("resize", this, width, height, null);
13524 startSizing : function(e, handle){
13525 this.fireEvent("beforeresize", this, e);
13526 if(this.enabled){ // 2nd enabled check in case disabled before beforeresize handler
13529 this.overlay = this.el.createProxy({tag: "div", cls: "x-resizable-overlay", html: " "});
13530 this.overlay.unselectable();
13531 this.overlay.enableDisplayMode("block");
13532 this.overlay.on("mousemove", this.onMouseMove, this);
13533 this.overlay.on("mouseup", this.onMouseUp, this);
13535 this.overlay.setStyle("cursor", handle.el.getStyle("cursor"));
13537 this.resizing = true;
13538 this.startBox = this.el.getBox();
13539 this.startPoint = e.getXY();
13540 this.offsets = [(this.startBox.x + this.startBox.width) - this.startPoint[0],
13541 (this.startBox.y + this.startBox.height) - this.startPoint[1]];
13543 this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
13544 this.overlay.show();
13546 if(this.constrainTo) {
13547 var ct = Roo.get(this.constrainTo);
13548 this.resizeRegion = ct.getRegion().adjust(
13549 ct.getFrameWidth('t'),
13550 ct.getFrameWidth('l'),
13551 -ct.getFrameWidth('b'),
13552 -ct.getFrameWidth('r')
13556 this.proxy.setStyle('visibility', 'hidden'); // workaround display none
13558 this.proxy.setBox(this.startBox);
13560 this.proxy.setStyle('visibility', 'visible');
13566 onMouseDown : function(handle, e){
13569 this.activeHandle = handle;
13570 this.startSizing(e, handle);
13575 onMouseUp : function(e){
13576 var size = this.resizeElement();
13577 this.resizing = false;
13579 this.overlay.hide();
13581 this.fireEvent("resize", this, size.width, size.height, e);
13585 updateChildSize : function(){
13586 if(this.resizeChild){
13588 var child = this.resizeChild;
13589 var adj = this.adjustments;
13590 if(el.dom.offsetWidth){
13591 var b = el.getSize(true);
13592 child.setSize(b.width+adj[0], b.height+adj[1]);
13594 // Second call here for IE
13595 // The first call enables instant resizing and
13596 // the second call corrects scroll bars if they
13599 setTimeout(function(){
13600 if(el.dom.offsetWidth){
13601 var b = el.getSize(true);
13602 child.setSize(b.width+adj[0], b.height+adj[1]);
13610 snap : function(value, inc, min){
13611 if(!inc || !value) return value;
13612 var newValue = value;
13613 var m = value % inc;
13616 newValue = value + (inc-m);
13618 newValue = value - m;
13621 return Math.max(min, newValue);
13625 resizeElement : function(){
13626 var box = this.proxy.getBox();
13627 if(this.updateBox){
13628 this.el.setBox(box, false, this.animate, this.duration, null, this.easing);
13630 this.el.setSize(box.width, box.height, this.animate, this.duration, null, this.easing);
13632 this.updateChildSize();
13640 constrain : function(v, diff, m, mx){
13643 }else if(v - diff > mx){
13650 onMouseMove : function(e){
13652 try{// try catch so if something goes wrong the user doesn't get hung
13654 if(this.resizeRegion && !this.resizeRegion.contains(e.getPoint())) {
13658 //var curXY = this.startPoint;
13659 var curSize = this.curSize || this.startBox;
13660 var x = this.startBox.x, y = this.startBox.y;
13661 var ox = x, oy = y;
13662 var w = curSize.width, h = curSize.height;
13663 var ow = w, oh = h;
13664 var mw = this.minWidth, mh = this.minHeight;
13665 var mxw = this.maxWidth, mxh = this.maxHeight;
13666 var wi = this.widthIncrement;
13667 var hi = this.heightIncrement;
13669 var eventXY = e.getXY();
13670 var diffX = -(this.startPoint[0] - Math.max(this.minX, eventXY[0]));
13671 var diffY = -(this.startPoint[1] - Math.max(this.minY, eventXY[1]));
13673 var pos = this.activeHandle.position;
13678 w = Math.min(Math.max(mw, w), mxw);
13683 h = Math.min(Math.max(mh, h), mxh);
13688 w = Math.min(Math.max(mw, w), mxw);
13689 h = Math.min(Math.max(mh, h), mxh);
13692 diffY = this.constrain(h, diffY, mh, mxh);
13699 var adiffX = Math.abs(diffX);
13700 var sub = (adiffX % wi); // how much
13701 if (sub > (wi/2)) { // far enough to snap
13702 diffX = (diffX > 0) ? diffX-sub + wi : diffX+sub - wi;
13704 // remove difference..
13705 diffX = (diffX > 0) ? diffX-sub : diffX+sub;
13709 x = Math.max(this.minX, x);
13712 diffX = this.constrain(w, diffX, mw, mxw);
13718 w = Math.min(Math.max(mw, w), mxw);
13719 diffY = this.constrain(h, diffY, mh, mxh);
13724 diffX = this.constrain(w, diffX, mw, mxw);
13725 diffY = this.constrain(h, diffY, mh, mxh);
13732 diffX = this.constrain(w, diffX, mw, mxw);
13734 h = Math.min(Math.max(mh, h), mxh);
13740 var sw = this.snap(w, wi, mw);
13741 var sh = this.snap(h, hi, mh);
13742 if(sw != w || sh != h){
13765 if(this.preserveRatio){
13770 h = Math.min(Math.max(mh, h), mxh);
13775 w = Math.min(Math.max(mw, w), mxw);
13780 w = Math.min(Math.max(mw, w), mxw);
13786 w = Math.min(Math.max(mw, w), mxw);
13792 h = Math.min(Math.max(mh, h), mxh);
13800 h = Math.min(Math.max(mh, h), mxh);
13810 h = Math.min(Math.max(mh, h), mxh);
13818 if (pos == 'hdrag') {
13821 this.proxy.setBounds(x, y, w, h);
13823 this.resizeElement();
13830 handleOver : function(){
13832 this.el.addClass("x-resizable-over");
13837 handleOut : function(){
13838 if(!this.resizing){
13839 this.el.removeClass("x-resizable-over");
13844 * Returns the element this component is bound to.
13845 * @return {Roo.Element}
13847 getEl : function(){
13852 * Returns the resizeChild element (or null).
13853 * @return {Roo.Element}
13855 getResizeChild : function(){
13856 return this.resizeChild;
13860 * Destroys this resizable. If the element was wrapped and
13861 * removeEl is not true then the element remains.
13862 * @param {Boolean} removeEl (optional) true to remove the element from the DOM
13864 destroy : function(removeEl){
13865 this.proxy.remove();
13867 this.overlay.removeAllListeners();
13868 this.overlay.remove();
13870 var ps = Roo.Resizable.positions;
13872 if(typeof ps[k] != "function" && this[ps[k]]){
13873 var h = this[ps[k]];
13874 h.el.removeAllListeners();
13879 this.el.update("");
13886 // hash to map config positions to true positions
13887 Roo.Resizable.positions = {
13888 n: "north", s: "south", e: "east", w: "west", se: "southeast", sw: "southwest", nw: "northwest", ne: "northeast",
13893 Roo.Resizable.Handle = function(rz, pos, disableTrackOver, transparent){
13895 // only initialize the template if resizable is used
13896 var tpl = Roo.DomHelper.createTemplate(
13897 {tag: "div", cls: "x-resizable-handle x-resizable-handle-{0}"}
13900 Roo.Resizable.Handle.prototype.tpl = tpl;
13902 this.position = pos;
13904 // show north drag fro topdra
13905 var handlepos = pos == 'hdrag' ? 'north' : pos;
13907 this.el = this.tpl.append(rz.el.dom, [handlepos], true);
13908 if (pos == 'hdrag') {
13909 this.el.setStyle('cursor', 'pointer');
13911 this.el.unselectable();
13913 this.el.setOpacity(0);
13915 this.el.on("mousedown", this.onMouseDown, this);
13916 if(!disableTrackOver){
13917 this.el.on("mouseover", this.onMouseOver, this);
13918 this.el.on("mouseout", this.onMouseOut, this);
13923 Roo.Resizable.Handle.prototype = {
13924 afterResize : function(rz){
13928 onMouseDown : function(e){
13929 this.rz.onMouseDown(this, e);
13932 onMouseOver : function(e){
13933 this.rz.handleOver(this, e);
13936 onMouseOut : function(e){
13937 this.rz.handleOut(this, e);
13941 * Ext JS Library 1.1.1
13942 * Copyright(c) 2006-2007, Ext JS, LLC.
13944 * Originally Released Under LGPL - original licence link has changed is not relivant.
13947 * <script type="text/javascript">
13951 * @class Roo.Editor
13952 * @extends Roo.Component
13953 * A base editor field that handles displaying/hiding on demand and has some built-in sizing and event handling logic.
13955 * Create a new Editor
13956 * @param {Roo.form.Field} field The Field object (or descendant)
13957 * @param {Object} config The config object
13959 Roo.Editor = function(field, config){
13960 Roo.Editor.superclass.constructor.call(this, config);
13961 this.field = field;
13964 * @event beforestartedit
13965 * Fires when editing is initiated, but before the value changes. Editing can be canceled by returning
13966 * false from the handler of this event.
13967 * @param {Editor} this
13968 * @param {Roo.Element} boundEl The underlying element bound to this editor
13969 * @param {Mixed} value The field value being set
13971 "beforestartedit" : true,
13974 * Fires when this editor is displayed
13975 * @param {Roo.Element} boundEl The underlying element bound to this editor
13976 * @param {Mixed} value The starting field value
13978 "startedit" : true,
13980 * @event beforecomplete
13981 * Fires after a change has been made to the field, but before the change is reflected in the underlying
13982 * field. Saving the change to the field can be canceled by returning false from the handler of this event.
13983 * Note that if the value has not changed and ignoreNoChange = true, the editing will still end but this
13984 * event will not fire since no edit actually occurred.
13985 * @param {Editor} this
13986 * @param {Mixed} value The current field value
13987 * @param {Mixed} startValue The original field value
13989 "beforecomplete" : true,
13992 * Fires after editing is complete and any changed value has been written to the underlying field.
13993 * @param {Editor} this
13994 * @param {Mixed} value The current field value
13995 * @param {Mixed} startValue The original field value
13999 * @event specialkey
14000 * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed. You can check
14001 * {@link Roo.EventObject#getKey} to determine which key was pressed.
14002 * @param {Roo.form.Field} this
14003 * @param {Roo.EventObject} e The event object
14005 "specialkey" : true
14009 Roo.extend(Roo.Editor, Roo.Component, {
14011 * @cfg {Boolean/String} autosize
14012 * True for the editor to automatically adopt the size of the underlying field, "width" to adopt the width only,
14013 * or "height" to adopt the height only (defaults to false)
14016 * @cfg {Boolean} revertInvalid
14017 * True to automatically revert the field value and cancel the edit when the user completes an edit and the field
14018 * validation fails (defaults to true)
14021 * @cfg {Boolean} ignoreNoChange
14022 * True to skip the the edit completion process (no save, no events fired) if the user completes an edit and
14023 * the value has not changed (defaults to false). Applies only to string values - edits for other data types
14024 * will never be ignored.
14027 * @cfg {Boolean} hideEl
14028 * False to keep the bound element visible while the editor is displayed (defaults to true)
14031 * @cfg {Mixed} value
14032 * The data value of the underlying field (defaults to "")
14036 * @cfg {String} alignment
14037 * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "c-c?").
14041 * @cfg {Boolean/String} shadow "sides" for sides/bottom only, "frame" for 4-way shadow, and "drop"
14042 * for bottom-right shadow (defaults to "frame")
14046 * @cfg {Boolean} constrain True to constrain the editor to the viewport
14050 * @cfg {Boolean} completeOnEnter True to complete the edit when the enter key is pressed (defaults to false)
14052 completeOnEnter : false,
14054 * @cfg {Boolean} cancelOnEsc True to cancel the edit when the escape key is pressed (defaults to false)
14056 cancelOnEsc : false,
14058 * @cfg {Boolean} updateEl True to update the innerHTML of the bound element when the update completes (defaults to false)
14063 onRender : function(ct, position){
14064 this.el = new Roo.Layer({
14065 shadow: this.shadow,
14071 constrain: this.constrain
14073 this.el.setStyle("overflow", Roo.isGecko ? "auto" : "hidden");
14074 if(this.field.msgTarget != 'title'){
14075 this.field.msgTarget = 'qtip';
14077 this.field.render(this.el);
14079 this.field.el.dom.setAttribute('autocomplete', 'off');
14081 this.field.on("specialkey", this.onSpecialKey, this);
14082 if(this.swallowKeys){
14083 this.field.el.swallowEvent(['keydown','keypress']);
14086 this.field.on("blur", this.onBlur, this);
14087 if(this.field.grow){
14088 this.field.on("autosize", this.el.sync, this.el, {delay:1});
14092 onSpecialKey : function(field, e)
14094 //Roo.log('editor onSpecialKey');
14095 if(this.completeOnEnter && e.getKey() == e.ENTER){
14097 this.completeEdit();
14100 // do not fire special key otherwise it might hide close the editor...
14101 if(e.getKey() == e.ENTER){
14104 if(this.cancelOnEsc && e.getKey() == e.ESC){
14108 this.fireEvent('specialkey', field, e);
14113 * Starts the editing process and shows the editor.
14114 * @param {String/HTMLElement/Element} el The element to edit
14115 * @param {String} value (optional) A value to initialize the editor with. If a value is not provided, it defaults
14116 * to the innerHTML of el.
14118 startEdit : function(el, value){
14120 this.completeEdit();
14122 this.boundEl = Roo.get(el);
14123 var v = value !== undefined ? value : this.boundEl.dom.innerHTML;
14124 if(!this.rendered){
14125 this.render(this.parentEl || document.body);
14127 if(this.fireEvent("beforestartedit", this, this.boundEl, v) === false){
14130 this.startValue = v;
14131 this.field.setValue(v);
14133 var sz = this.boundEl.getSize();
14134 switch(this.autoSize){
14136 this.setSize(sz.width, "");
14139 this.setSize("", sz.height);
14142 this.setSize(sz.width, sz.height);
14145 this.el.alignTo(this.boundEl, this.alignment);
14146 this.editing = true;
14148 Roo.QuickTips.disable();
14154 * Sets the height and width of this editor.
14155 * @param {Number} width The new width
14156 * @param {Number} height The new height
14158 setSize : function(w, h){
14159 this.field.setSize(w, h);
14166 * Realigns the editor to the bound field based on the current alignment config value.
14168 realign : function(){
14169 this.el.alignTo(this.boundEl, this.alignment);
14173 * Ends the editing process, persists the changed value to the underlying field, and hides the editor.
14174 * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after edit (defaults to false)
14176 completeEdit : function(remainVisible){
14180 var v = this.getValue();
14181 if(this.revertInvalid !== false && !this.field.isValid()){
14182 v = this.startValue;
14183 this.cancelEdit(true);
14185 if(String(v) === String(this.startValue) && this.ignoreNoChange){
14186 this.editing = false;
14190 if(this.fireEvent("beforecomplete", this, v, this.startValue) !== false){
14191 this.editing = false;
14192 if(this.updateEl && this.boundEl){
14193 this.boundEl.update(v);
14195 if(remainVisible !== true){
14198 this.fireEvent("complete", this, v, this.startValue);
14203 onShow : function(){
14205 if(this.hideEl !== false){
14206 this.boundEl.hide();
14209 if(Roo.isIE && !this.fixIEFocus){ // IE has problems with focusing the first time
14210 this.fixIEFocus = true;
14211 this.deferredFocus.defer(50, this);
14213 this.field.focus();
14215 this.fireEvent("startedit", this.boundEl, this.startValue);
14218 deferredFocus : function(){
14220 this.field.focus();
14225 * Cancels the editing process and hides the editor without persisting any changes. The field value will be
14226 * reverted to the original starting value.
14227 * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after
14228 * cancel (defaults to false)
14230 cancelEdit : function(remainVisible){
14232 this.setValue(this.startValue);
14233 if(remainVisible !== true){
14240 onBlur : function(){
14241 if(this.allowBlur !== true && this.editing){
14242 this.completeEdit();
14247 onHide : function(){
14249 this.completeEdit();
14253 if(this.field.collapse){
14254 this.field.collapse();
14257 if(this.hideEl !== false){
14258 this.boundEl.show();
14261 Roo.QuickTips.enable();
14266 * Sets the data value of the editor
14267 * @param {Mixed} value Any valid value supported by the underlying field
14269 setValue : function(v){
14270 this.field.setValue(v);
14274 * Gets the data value of the editor
14275 * @return {Mixed} The data value
14277 getValue : function(){
14278 return this.field.getValue();
14282 * Ext JS Library 1.1.1
14283 * Copyright(c) 2006-2007, Ext JS, LLC.
14285 * Originally Released Under LGPL - original licence link has changed is not relivant.
14288 * <script type="text/javascript">
14292 * @class Roo.BasicDialog
14293 * @extends Roo.util.Observable
14294 * Lightweight Dialog Class. The code below shows the creation of a typical dialog using existing HTML markup:
14296 var dlg = new Roo.BasicDialog("my-dlg", {
14305 dlg.addKeyListener(27, dlg.hide, dlg); // ESC can also close the dialog
14306 dlg.addButton('OK', dlg.hide, dlg); // Could call a save function instead of hiding
14307 dlg.addButton('Cancel', dlg.hide, dlg);
14310 <b>A Dialog should always be a direct child of the body element.</b>
14311 * @cfg {Boolean/DomHelper} autoCreate True to auto create from scratch, or using a DomHelper Object (defaults to false)
14312 * @cfg {String} title Default text to display in the title bar (defaults to null)
14313 * @cfg {Number} width Width of the dialog in pixels (can also be set via CSS). Determined by browser if unspecified.
14314 * @cfg {Number} height Height of the dialog in pixels (can also be set via CSS). Determined by browser if unspecified.
14315 * @cfg {Number} x The default left page coordinate of the dialog (defaults to center screen)
14316 * @cfg {Number} y The default top page coordinate of the dialog (defaults to center screen)
14317 * @cfg {String/Element} animateTarget Id or element from which the dialog should animate while opening
14318 * (defaults to null with no animation)
14319 * @cfg {Boolean} resizable False to disable manual dialog resizing (defaults to true)
14320 * @cfg {String} resizeHandles Which resize handles to display - see the {@link Roo.Resizable} handles config
14321 * property for valid values (defaults to 'all')
14322 * @cfg {Number} minHeight The minimum allowable height for a resizable dialog (defaults to 80)
14323 * @cfg {Number} minWidth The minimum allowable width for a resizable dialog (defaults to 200)
14324 * @cfg {Boolean} modal True to show the dialog modally, preventing user interaction with the rest of the page (defaults to false)
14325 * @cfg {Boolean} autoScroll True to allow the dialog body contents to overflow and display scrollbars (defaults to false)
14326 * @cfg {Boolean} closable False to remove the built-in top-right corner close button (defaults to true)
14327 * @cfg {Boolean} collapsible False to remove the built-in top-right corner collapse button (defaults to true)
14328 * @cfg {Boolean} constraintoviewport True to keep the dialog constrained within the visible viewport boundaries (defaults to true)
14329 * @cfg {Boolean} syncHeightBeforeShow True to cause the dimensions to be recalculated before the dialog is shown (defaults to false)
14330 * @cfg {Boolean} draggable False to disable dragging of the dialog within the viewport (defaults to true)
14331 * @cfg {Boolean} autoTabs If true, all elements with class 'x-dlg-tab' will get automatically converted to tabs (defaults to false)
14332 * @cfg {String} tabTag The tag name of tab elements, used when autoTabs = true (defaults to 'div')
14333 * @cfg {Boolean} proxyDrag True to drag a lightweight proxy element rather than the dialog itself, used when
14334 * draggable = true (defaults to false)
14335 * @cfg {Boolean} fixedcenter True to ensure that anytime the dialog is shown or resized it gets centered (defaults to false)
14336 * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
14337 * shadow (defaults to false)
14338 * @cfg {Number} shadowOffset The number of pixels to offset the shadow if displayed (defaults to 5)
14339 * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "right")
14340 * @cfg {Number} minButtonWidth Minimum width of all dialog buttons (defaults to 75)
14341 * @cfg {Array} buttons Array of buttons
14342 * @cfg {Boolean} shim True to create an iframe shim that prevents selects from showing through (defaults to false)
14344 * Create a new BasicDialog.
14345 * @param {String/HTMLElement/Roo.Element} el The container element or DOM node, or its id
14346 * @param {Object} config Configuration options
14348 Roo.BasicDialog = function(el, config){
14349 this.el = Roo.get(el);
14350 var dh = Roo.DomHelper;
14351 if(!this.el && config && config.autoCreate){
14352 if(typeof config.autoCreate == "object"){
14353 if(!config.autoCreate.id){
14354 config.autoCreate.id = el;
14356 this.el = dh.append(document.body,
14357 config.autoCreate, true);
14359 this.el = dh.append(document.body,
14360 {tag: "div", id: el, style:'visibility:hidden;'}, true);
14364 el.setDisplayed(true);
14365 el.hide = this.hideAction;
14367 el.addClass("x-dlg");
14369 Roo.apply(this, config);
14371 this.proxy = el.createProxy("x-dlg-proxy");
14372 this.proxy.hide = this.hideAction;
14373 this.proxy.setOpacity(.5);
14377 el.setWidth(config.width);
14380 el.setHeight(config.height);
14382 this.size = el.getSize();
14383 if(typeof config.x != "undefined" && typeof config.y != "undefined"){
14384 this.xy = [config.x,config.y];
14386 this.xy = el.getCenterXY(true);
14388 /** The header element @type Roo.Element */
14389 this.header = el.child("> .x-dlg-hd");
14390 /** The body element @type Roo.Element */
14391 this.body = el.child("> .x-dlg-bd");
14392 /** The footer element @type Roo.Element */
14393 this.footer = el.child("> .x-dlg-ft");
14396 this.header = el.createChild({tag: "div", cls:"x-dlg-hd", html: " "}, this.body ? this.body.dom : null);
14399 this.body = el.createChild({tag: "div", cls:"x-dlg-bd"});
14402 this.header.unselectable();
14404 this.header.update(this.title);
14406 // this element allows the dialog to be focused for keyboard event
14407 this.focusEl = el.createChild({tag: "a", href:"#", cls:"x-dlg-focus", tabIndex:"-1"});
14408 this.focusEl.swallowEvent("click", true);
14410 this.header.wrap({cls:"x-dlg-hd-right"}).wrap({cls:"x-dlg-hd-left"}, true);
14412 // wrap the body and footer for special rendering
14413 this.bwrap = this.body.wrap({tag: "div", cls:"x-dlg-dlg-body"});
14415 this.bwrap.dom.appendChild(this.footer.dom);
14418 this.bg = this.el.createChild({
14419 tag: "div", cls:"x-dlg-bg",
14420 html: '<div class="x-dlg-bg-left"><div class="x-dlg-bg-right"><div class="x-dlg-bg-center"> </div></div></div>'
14422 this.centerBg = this.bg.child("div.x-dlg-bg-center");
14425 if(this.autoScroll !== false && !this.autoTabs){
14426 this.body.setStyle("overflow", "auto");
14429 this.toolbox = this.el.createChild({cls: "x-dlg-toolbox"});
14431 if(this.closable !== false){
14432 this.el.addClass("x-dlg-closable");
14433 this.close = this.toolbox.createChild({cls:"x-dlg-close"});
14434 this.close.on("click", this.closeClick, this);
14435 this.close.addClassOnOver("x-dlg-close-over");
14437 if(this.collapsible !== false){
14438 this.collapseBtn = this.toolbox.createChild({cls:"x-dlg-collapse"});
14439 this.collapseBtn.on("click", this.collapseClick, this);
14440 this.collapseBtn.addClassOnOver("x-dlg-collapse-over");
14441 this.header.on("dblclick", this.collapseClick, this);
14443 if(this.resizable !== false){
14444 this.el.addClass("x-dlg-resizable");
14445 this.resizer = new Roo.Resizable(el, {
14446 minWidth: this.minWidth || 80,
14447 minHeight:this.minHeight || 80,
14448 handles: this.resizeHandles || "all",
14451 this.resizer.on("beforeresize", this.beforeResize, this);
14452 this.resizer.on("resize", this.onResize, this);
14454 if(this.draggable !== false){
14455 el.addClass("x-dlg-draggable");
14456 if (!this.proxyDrag) {
14457 var dd = new Roo.dd.DD(el.dom.id, "WindowDrag");
14460 var dd = new Roo.dd.DDProxy(el.dom.id, "WindowDrag", {dragElId: this.proxy.id});
14462 dd.setHandleElId(this.header.id);
14463 dd.endDrag = this.endMove.createDelegate(this);
14464 dd.startDrag = this.startMove.createDelegate(this);
14465 dd.onDrag = this.onDrag.createDelegate(this);
14470 this.mask = dh.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
14471 this.mask.enableDisplayMode("block");
14473 this.el.addClass("x-dlg-modal");
14476 this.shadow = new Roo.Shadow({
14477 mode : typeof this.shadow == "string" ? this.shadow : "sides",
14478 offset : this.shadowOffset
14481 this.shadowOffset = 0;
14483 if(Roo.useShims && this.shim !== false){
14484 this.shim = this.el.createShim();
14485 this.shim.hide = this.hideAction;
14493 if (this.buttons) {
14494 var bts= this.buttons;
14496 Roo.each(bts, function(b) {
14505 * Fires when a key is pressed
14506 * @param {Roo.BasicDialog} this
14507 * @param {Roo.EventObject} e
14512 * Fires when this dialog is moved by the user.
14513 * @param {Roo.BasicDialog} this
14514 * @param {Number} x The new page X
14515 * @param {Number} y The new page Y
14520 * Fires when this dialog is resized by the user.
14521 * @param {Roo.BasicDialog} this
14522 * @param {Number} width The new width
14523 * @param {Number} height The new height
14527 * @event beforehide
14528 * Fires before this dialog is hidden.
14529 * @param {Roo.BasicDialog} this
14531 "beforehide" : true,
14534 * Fires when this dialog is hidden.
14535 * @param {Roo.BasicDialog} this
14539 * @event beforeshow
14540 * Fires before this dialog is shown.
14541 * @param {Roo.BasicDialog} this
14543 "beforeshow" : true,
14546 * Fires when this dialog is shown.
14547 * @param {Roo.BasicDialog} this
14551 el.on("keydown", this.onKeyDown, this);
14552 el.on("mousedown", this.toFront, this);
14553 Roo.EventManager.onWindowResize(this.adjustViewport, this, true);
14555 Roo.DialogManager.register(this);
14556 Roo.BasicDialog.superclass.constructor.call(this);
14559 Roo.extend(Roo.BasicDialog, Roo.util.Observable, {
14560 shadowOffset: Roo.isIE ? 6 : 5,
14563 minButtonWidth: 75,
14564 defaultButton: null,
14565 buttonAlign: "right",
14570 * Sets the dialog title text
14571 * @param {String} text The title text to display
14572 * @return {Roo.BasicDialog} this
14574 setTitle : function(text){
14575 this.header.update(text);
14580 closeClick : function(){
14585 collapseClick : function(){
14586 this[this.collapsed ? "expand" : "collapse"]();
14590 * Collapses the dialog to its minimized state (only the title bar is visible).
14591 * Equivalent to the user clicking the collapse dialog button.
14593 collapse : function(){
14594 if(!this.collapsed){
14595 this.collapsed = true;
14596 this.el.addClass("x-dlg-collapsed");
14597 this.restoreHeight = this.el.getHeight();
14598 this.resizeTo(this.el.getWidth(), this.header.getHeight());
14603 * Expands a collapsed dialog back to its normal state. Equivalent to the user
14604 * clicking the expand dialog button.
14606 expand : function(){
14607 if(this.collapsed){
14608 this.collapsed = false;
14609 this.el.removeClass("x-dlg-collapsed");
14610 this.resizeTo(this.el.getWidth(), this.restoreHeight);
14615 * Reinitializes the tabs component, clearing out old tabs and finding new ones.
14616 * @return {Roo.TabPanel} The tabs component
14618 initTabs : function(){
14619 var tabs = this.getTabs();
14620 while(tabs.getTab(0)){
14623 this.el.select(this.tabTag+'.x-dlg-tab').each(function(el){
14625 tabs.addTab(Roo.id(dom), dom.title);
14633 beforeResize : function(){
14634 this.resizer.minHeight = Math.max(this.minHeight, this.getHeaderFooterHeight(true)+40);
14638 onResize : function(){
14639 this.refreshSize();
14640 this.syncBodyHeight();
14641 this.adjustAssets();
14643 this.fireEvent("resize", this, this.size.width, this.size.height);
14647 onKeyDown : function(e){
14648 if(this.isVisible()){
14649 this.fireEvent("keydown", this, e);
14654 * Resizes the dialog.
14655 * @param {Number} width
14656 * @param {Number} height
14657 * @return {Roo.BasicDialog} this
14659 resizeTo : function(width, height){
14660 this.el.setSize(width, height);
14661 this.size = {width: width, height: height};
14662 this.syncBodyHeight();
14663 if(this.fixedcenter){
14666 if(this.isVisible()){
14667 this.constrainXY();
14668 this.adjustAssets();
14670 this.fireEvent("resize", this, width, height);
14676 * Resizes the dialog to fit the specified content size.
14677 * @param {Number} width
14678 * @param {Number} height
14679 * @return {Roo.BasicDialog} this
14681 setContentSize : function(w, h){
14682 h += this.getHeaderFooterHeight() + this.body.getMargins("tb");
14683 w += this.body.getMargins("lr") + this.bwrap.getMargins("lr") + this.centerBg.getPadding("lr");
14684 //if(!this.el.isBorderBox()){
14685 h += this.body.getPadding("tb") + this.bwrap.getBorderWidth("tb") + this.body.getBorderWidth("tb") + this.el.getBorderWidth("tb");
14686 w += this.body.getPadding("lr") + this.bwrap.getBorderWidth("lr") + this.body.getBorderWidth("lr") + this.bwrap.getPadding("lr") + this.el.getBorderWidth("lr");
14689 h += this.tabs.stripWrap.getHeight() + this.tabs.bodyEl.getMargins("tb") + this.tabs.bodyEl.getPadding("tb");
14690 w += this.tabs.bodyEl.getMargins("lr") + this.tabs.bodyEl.getPadding("lr");
14692 this.resizeTo(w, h);
14697 * Adds a key listener for when this dialog is displayed. This allows you to hook in a function that will be
14698 * executed in response to a particular key being pressed while the dialog is active.
14699 * @param {Number/Array/Object} key Either the numeric key code, array of key codes or an object with the following options:
14700 * {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
14701 * @param {Function} fn The function to call
14702 * @param {Object} scope (optional) The scope of the function
14703 * @return {Roo.BasicDialog} this
14705 addKeyListener : function(key, fn, scope){
14706 var keyCode, shift, ctrl, alt;
14707 if(typeof key == "object" && !(key instanceof Array)){
14708 keyCode = key["key"];
14709 shift = key["shift"];
14710 ctrl = key["ctrl"];
14715 var handler = function(dlg, e){
14716 if((!shift || e.shiftKey) && (!ctrl || e.ctrlKey) && (!alt || e.altKey)){
14717 var k = e.getKey();
14718 if(keyCode instanceof Array){
14719 for(var i = 0, len = keyCode.length; i < len; i++){
14720 if(keyCode[i] == k){
14721 fn.call(scope || window, dlg, k, e);
14727 fn.call(scope || window, dlg, k, e);
14732 this.on("keydown", handler);
14737 * Returns the TabPanel component (creates it if it doesn't exist).
14738 * Note: If you wish to simply check for the existence of tabs without creating them,
14739 * check for a null 'tabs' property.
14740 * @return {Roo.TabPanel} The tabs component
14742 getTabs : function(){
14744 this.el.addClass("x-dlg-auto-tabs");
14745 this.body.addClass(this.tabPosition == "bottom" ? "x-tabs-bottom" : "x-tabs-top");
14746 this.tabs = new Roo.TabPanel(this.body.dom, this.tabPosition == "bottom");
14752 * Adds a button to the footer section of the dialog.
14753 * @param {String/Object} config A string becomes the button text, an object can either be a Button config
14754 * object or a valid Roo.DomHelper element config
14755 * @param {Function} handler The function called when the button is clicked
14756 * @param {Object} scope (optional) The scope of the handler function (accepts position as a property)
14757 * @return {Roo.Button} The new button
14759 addButton : function(config, handler, scope){
14760 var dh = Roo.DomHelper;
14762 this.footer = dh.append(this.bwrap, {tag: "div", cls:"x-dlg-ft"}, true);
14764 if(!this.btnContainer){
14765 var tb = this.footer.createChild({
14767 cls:"x-dlg-btns x-dlg-btns-"+this.buttonAlign,
14768 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
14770 this.btnContainer = tb.firstChild.firstChild.firstChild;
14775 minWidth: this.minButtonWidth,
14778 if(typeof config == "string"){
14779 bconfig.text = config;
14782 bconfig.dhconfig = config;
14784 Roo.apply(bconfig, config);
14788 if ((typeof(bconfig.position) != 'undefined') && bconfig.position < this.btnContainer.childNodes.length-1) {
14789 bconfig.position = Math.max(0, bconfig.position);
14790 fc = this.btnContainer.childNodes[bconfig.position];
14793 var btn = new Roo.Button(
14795 this.btnContainer.insertBefore(document.createElement("td"),fc)
14796 : this.btnContainer.appendChild(document.createElement("td")),
14797 //Roo.get(this.btnContainer).createChild( { tag: 'td'}, fc ),
14800 this.syncBodyHeight();
14803 * Array of all the buttons that have been added to this dialog via addButton
14808 this.buttons.push(btn);
14813 * Sets the default button to be focused when the dialog is displayed.
14814 * @param {Roo.BasicDialog.Button} btn The button object returned by {@link #addButton}
14815 * @return {Roo.BasicDialog} this
14817 setDefaultButton : function(btn){
14818 this.defaultButton = btn;
14823 getHeaderFooterHeight : function(safe){
14826 height += this.header.getHeight();
14829 var fm = this.footer.getMargins();
14830 height += (this.footer.getHeight()+fm.top+fm.bottom);
14832 height += this.bwrap.getPadding("tb")+this.bwrap.getBorderWidth("tb");
14833 height += this.centerBg.getPadding("tb");
14838 syncBodyHeight : function(){
14839 var bd = this.body, cb = this.centerBg, bw = this.bwrap;
14840 var height = this.size.height - this.getHeaderFooterHeight(false);
14841 bd.setHeight(height-bd.getMargins("tb"));
14842 var hh = this.header.getHeight();
14843 var h = this.size.height-hh;
14845 bw.setLeftTop(cb.getPadding("l"), hh+cb.getPadding("t"));
14846 bw.setHeight(h-cb.getPadding("tb"));
14847 bw.setWidth(this.el.getWidth(true)-cb.getPadding("lr"));
14848 bd.setWidth(bw.getWidth(true));
14850 this.tabs.syncHeight();
14852 this.tabs.el.repaint();
14858 * Restores the previous state of the dialog if Roo.state is configured.
14859 * @return {Roo.BasicDialog} this
14861 restoreState : function(){
14862 var box = Roo.state.Manager.get(this.stateId || (this.el.id + "-state"));
14863 if(box && box.width){
14864 this.xy = [box.x, box.y];
14865 this.resizeTo(box.width, box.height);
14871 beforeShow : function(){
14873 if(this.fixedcenter){
14874 this.xy = this.el.getCenterXY(true);
14877 Roo.get(document.body).addClass("x-body-masked");
14878 this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
14881 this.constrainXY();
14885 animShow : function(){
14886 var b = Roo.get(this.animateTarget).getBox();
14887 this.proxy.setSize(b.width, b.height);
14888 this.proxy.setLocation(b.x, b.y);
14890 this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height,
14891 true, .35, this.showEl.createDelegate(this));
14895 * Shows the dialog.
14896 * @param {String/HTMLElement/Roo.Element} animateTarget (optional) Reset the animation target
14897 * @return {Roo.BasicDialog} this
14899 show : function(animateTarget){
14900 if (this.fireEvent("beforeshow", this) === false){
14903 if(this.syncHeightBeforeShow){
14904 this.syncBodyHeight();
14905 }else if(this.firstShow){
14906 this.firstShow = false;
14907 this.syncBodyHeight(); // sync the height on the first show instead of in the constructor
14909 this.animateTarget = animateTarget || this.animateTarget;
14910 if(!this.el.isVisible()){
14912 if(this.animateTarget && Roo.get(this.animateTarget)){
14922 showEl : function(){
14924 this.el.setXY(this.xy);
14926 this.adjustAssets(true);
14929 // IE peekaboo bug - fix found by Dave Fenwick
14933 this.fireEvent("show", this);
14937 * Focuses the dialog. If a defaultButton is set, it will receive focus, otherwise the
14938 * dialog itself will receive focus.
14940 focus : function(){
14941 if(this.defaultButton){
14942 this.defaultButton.focus();
14944 this.focusEl.focus();
14949 constrainXY : function(){
14950 if(this.constraintoviewport !== false){
14951 if(!this.viewSize){
14952 if(this.container){
14953 var s = this.container.getSize();
14954 this.viewSize = [s.width, s.height];
14956 this.viewSize = [Roo.lib.Dom.getViewWidth(),Roo.lib.Dom.getViewHeight()];
14959 var s = Roo.get(this.container||document).getScroll();
14961 var x = this.xy[0], y = this.xy[1];
14962 var w = this.size.width, h = this.size.height;
14963 var vw = this.viewSize[0], vh = this.viewSize[1];
14964 // only move it if it needs it
14966 // first validate right/bottom
14967 if(x + w > vw+s.left){
14971 if(y + h > vh+s.top){
14975 // then make sure top/left isn't negative
14987 if(this.isVisible()){
14988 this.el.setLocation(x, y);
14989 this.adjustAssets();
14996 onDrag : function(){
14997 if(!this.proxyDrag){
14998 this.xy = this.el.getXY();
14999 this.adjustAssets();
15004 adjustAssets : function(doShow){
15005 var x = this.xy[0], y = this.xy[1];
15006 var w = this.size.width, h = this.size.height;
15007 if(doShow === true){
15009 this.shadow.show(this.el);
15015 if(this.shadow && this.shadow.isVisible()){
15016 this.shadow.show(this.el);
15018 if(this.shim && this.shim.isVisible()){
15019 this.shim.setBounds(x, y, w, h);
15024 adjustViewport : function(w, h){
15026 w = Roo.lib.Dom.getViewWidth();
15027 h = Roo.lib.Dom.getViewHeight();
15030 this.viewSize = [w, h];
15031 if(this.modal && this.mask.isVisible()){
15032 this.mask.setSize(w, h); // first make sure the mask isn't causing overflow
15033 this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
15035 if(this.isVisible()){
15036 this.constrainXY();
15041 * Destroys this dialog and all its supporting elements (including any tabs, shim,
15042 * shadow, proxy, mask, etc.) Also removes all event listeners.
15043 * @param {Boolean} removeEl (optional) true to remove the element from the DOM
15045 destroy : function(removeEl){
15046 if(this.isVisible()){
15047 this.animateTarget = null;
15050 Roo.EventManager.removeResizeListener(this.adjustViewport, this);
15052 this.tabs.destroy(removeEl);
15065 for(var i = 0, len = this.buttons.length; i < len; i++){
15066 this.buttons[i].destroy();
15069 this.el.removeAllListeners();
15070 if(removeEl === true){
15071 this.el.update("");
15074 Roo.DialogManager.unregister(this);
15078 startMove : function(){
15079 if(this.proxyDrag){
15082 if(this.constraintoviewport !== false){
15083 this.dd.constrainTo(document.body, {right: this.shadowOffset, bottom: this.shadowOffset});
15088 endMove : function(){
15089 if(!this.proxyDrag){
15090 Roo.dd.DD.prototype.endDrag.apply(this.dd, arguments);
15092 Roo.dd.DDProxy.prototype.endDrag.apply(this.dd, arguments);
15095 this.refreshSize();
15096 this.adjustAssets();
15098 this.fireEvent("move", this, this.xy[0], this.xy[1]);
15102 * Brings this dialog to the front of any other visible dialogs
15103 * @return {Roo.BasicDialog} this
15105 toFront : function(){
15106 Roo.DialogManager.bringToFront(this);
15111 * Sends this dialog to the back (under) of any other visible dialogs
15112 * @return {Roo.BasicDialog} this
15114 toBack : function(){
15115 Roo.DialogManager.sendToBack(this);
15120 * Centers this dialog in the viewport
15121 * @return {Roo.BasicDialog} this
15123 center : function(){
15124 var xy = this.el.getCenterXY(true);
15125 this.moveTo(xy[0], xy[1]);
15130 * Moves the dialog's top-left corner to the specified point
15131 * @param {Number} x
15132 * @param {Number} y
15133 * @return {Roo.BasicDialog} this
15135 moveTo : function(x, y){
15137 if(this.isVisible()){
15138 this.el.setXY(this.xy);
15139 this.adjustAssets();
15145 * Aligns the dialog to the specified element
15146 * @param {String/HTMLElement/Roo.Element} element The element to align to.
15147 * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details).
15148 * @param {Array} offsets (optional) Offset the positioning by [x, y]
15149 * @return {Roo.BasicDialog} this
15151 alignTo : function(element, position, offsets){
15152 this.xy = this.el.getAlignToXY(element, position, offsets);
15153 if(this.isVisible()){
15154 this.el.setXY(this.xy);
15155 this.adjustAssets();
15161 * Anchors an element to another element and realigns it when the window is resized.
15162 * @param {String/HTMLElement/Roo.Element} element The element to align to.
15163 * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details)
15164 * @param {Array} offsets (optional) Offset the positioning by [x, y]
15165 * @param {Boolean/Number} monitorScroll (optional) true to monitor body scroll and reposition. If this parameter
15166 * is a number, it is used as the buffer delay (defaults to 50ms).
15167 * @return {Roo.BasicDialog} this
15169 anchorTo : function(el, alignment, offsets, monitorScroll){
15170 var action = function(){
15171 this.alignTo(el, alignment, offsets);
15173 Roo.EventManager.onWindowResize(action, this);
15174 var tm = typeof monitorScroll;
15175 if(tm != 'undefined'){
15176 Roo.EventManager.on(window, 'scroll', action, this,
15177 {buffer: tm == 'number' ? monitorScroll : 50});
15184 * Returns true if the dialog is visible
15185 * @return {Boolean}
15187 isVisible : function(){
15188 return this.el.isVisible();
15192 animHide : function(callback){
15193 var b = Roo.get(this.animateTarget).getBox();
15195 this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height);
15197 this.proxy.setBounds(b.x, b.y, b.width, b.height, true, .35,
15198 this.hideEl.createDelegate(this, [callback]));
15202 * Hides the dialog.
15203 * @param {Function} callback (optional) Function to call when the dialog is hidden
15204 * @return {Roo.BasicDialog} this
15206 hide : function(callback){
15207 if (this.fireEvent("beforehide", this) === false){
15211 this.shadow.hide();
15216 // sometimes animateTarget seems to get set.. causing problems...
15217 // this just double checks..
15218 if(this.animateTarget && Roo.get(this.animateTarget)) {
15219 this.animHide(callback);
15222 this.hideEl(callback);
15228 hideEl : function(callback){
15232 Roo.get(document.body).removeClass("x-body-masked");
15234 this.fireEvent("hide", this);
15235 if(typeof callback == "function"){
15241 hideAction : function(){
15242 this.setLeft("-10000px");
15243 this.setTop("-10000px");
15244 this.setStyle("visibility", "hidden");
15248 refreshSize : function(){
15249 this.size = this.el.getSize();
15250 this.xy = this.el.getXY();
15251 Roo.state.Manager.set(this.stateId || this.el.id + "-state", this.el.getBox());
15255 // z-index is managed by the DialogManager and may be overwritten at any time
15256 setZIndex : function(index){
15258 this.mask.setStyle("z-index", index);
15261 this.shim.setStyle("z-index", ++index);
15264 this.shadow.setZIndex(++index);
15266 this.el.setStyle("z-index", ++index);
15268 this.proxy.setStyle("z-index", ++index);
15271 this.resizer.proxy.setStyle("z-index", ++index);
15274 this.lastZIndex = index;
15278 * Returns the element for this dialog
15279 * @return {Roo.Element} The underlying dialog Element
15281 getEl : function(){
15287 * @class Roo.DialogManager
15288 * Provides global access to BasicDialogs that have been created and
15289 * support for z-indexing (layering) multiple open dialogs.
15291 Roo.DialogManager = function(){
15293 var accessList = [];
15297 var sortDialogs = function(d1, d2){
15298 return (!d1._lastAccess || d1._lastAccess < d2._lastAccess) ? -1 : 1;
15302 var orderDialogs = function(){
15303 accessList.sort(sortDialogs);
15304 var seed = Roo.DialogManager.zseed;
15305 for(var i = 0, len = accessList.length; i < len; i++){
15306 var dlg = accessList[i];
15308 dlg.setZIndex(seed + (i*10));
15315 * The starting z-index for BasicDialogs (defaults to 9000)
15316 * @type Number The z-index value
15321 register : function(dlg){
15322 list[dlg.id] = dlg;
15323 accessList.push(dlg);
15327 unregister : function(dlg){
15328 delete list[dlg.id];
15331 if(!accessList.indexOf){
15332 for( i = 0, len = accessList.length; i < len; i++){
15333 if(accessList[i] == dlg){
15334 accessList.splice(i, 1);
15339 i = accessList.indexOf(dlg);
15341 accessList.splice(i, 1);
15347 * Gets a registered dialog by id
15348 * @param {String/Object} id The id of the dialog or a dialog
15349 * @return {Roo.BasicDialog} this
15351 get : function(id){
15352 return typeof id == "object" ? id : list[id];
15356 * Brings the specified dialog to the front
15357 * @param {String/Object} dlg The id of the dialog or a dialog
15358 * @return {Roo.BasicDialog} this
15360 bringToFront : function(dlg){
15361 dlg = this.get(dlg);
15364 dlg._lastAccess = new Date().getTime();
15371 * Sends the specified dialog to the back
15372 * @param {String/Object} dlg The id of the dialog or a dialog
15373 * @return {Roo.BasicDialog} this
15375 sendToBack : function(dlg){
15376 dlg = this.get(dlg);
15377 dlg._lastAccess = -(new Date().getTime());
15383 * Hides all dialogs
15385 hideAll : function(){
15386 for(var id in list){
15387 if(list[id] && typeof list[id] != "function" && list[id].isVisible()){
15396 * @class Roo.LayoutDialog
15397 * @extends Roo.BasicDialog
15398 * Dialog which provides adjustments for working with a layout in a Dialog.
15399 * Add your necessary layout config options to the dialog's config.<br>
15400 * Example usage (including a nested layout):
15403 dialog = new Roo.LayoutDialog("download-dlg", {
15412 // layout config merges with the dialog config
15414 tabPosition: "top",
15415 alwaysShowTabs: true
15418 dialog.addKeyListener(27, dialog.hide, dialog);
15419 dialog.setDefaultButton(dialog.addButton("Close", dialog.hide, dialog));
15420 dialog.addButton("Build It!", this.getDownload, this);
15422 // we can even add nested layouts
15423 var innerLayout = new Roo.BorderLayout("dl-inner", {
15433 innerLayout.beginUpdate();
15434 innerLayout.add("east", new Roo.ContentPanel("dl-details"));
15435 innerLayout.add("center", new Roo.ContentPanel("selection-panel"));
15436 innerLayout.endUpdate(true);
15438 var layout = dialog.getLayout();
15439 layout.beginUpdate();
15440 layout.add("center", new Roo.ContentPanel("standard-panel",
15441 {title: "Download the Source", fitToFrame:true}));
15442 layout.add("center", new Roo.NestedLayoutPanel(innerLayout,
15443 {title: "Build your own roo.js"}));
15444 layout.getRegion("center").showPanel(sp);
15445 layout.endUpdate();
15449 * @param {String/HTMLElement/Roo.Element} el The id of or container element, or config
15450 * @param {Object} config configuration options
15452 Roo.LayoutDialog = function(el, cfg){
15455 if (typeof(cfg) == 'undefined') {
15456 config = Roo.apply({}, el);
15457 // not sure why we use documentElement here.. - it should always be body.
15458 // IE7 borks horribly if we use documentElement.
15459 // webkit also does not like documentElement - it creates a body element...
15460 el = Roo.get( document.body || document.documentElement ).createChild();
15461 //config.autoCreate = true;
15465 config.autoTabs = false;
15466 Roo.LayoutDialog.superclass.constructor.call(this, el, config);
15467 this.body.setStyle({overflow:"hidden", position:"relative"});
15468 this.layout = new Roo.BorderLayout(this.body.dom, config);
15469 this.layout.monitorWindowResize = false;
15470 this.el.addClass("x-dlg-auto-layout");
15471 // fix case when center region overwrites center function
15472 this.center = Roo.BasicDialog.prototype.center;
15473 this.on("show", this.layout.layout, this.layout, true);
15474 if (config.items) {
15475 var xitems = config.items;
15476 delete config.items;
15477 Roo.each(xitems, this.addxtype, this);
15482 Roo.extend(Roo.LayoutDialog, Roo.BasicDialog, {
15484 * Ends update of the layout <strike>and resets display to none</strike>. Use standard beginUpdate/endUpdate on the layout.
15487 endUpdate : function(){
15488 this.layout.endUpdate();
15492 * Begins an update of the layout <strike>and sets display to block and visibility to hidden</strike>. Use standard beginUpdate/endUpdate on the layout.
15495 beginUpdate : function(){
15496 this.layout.beginUpdate();
15500 * Get the BorderLayout for this dialog
15501 * @return {Roo.BorderLayout}
15503 getLayout : function(){
15504 return this.layout;
15507 showEl : function(){
15508 Roo.LayoutDialog.superclass.showEl.apply(this, arguments);
15510 this.layout.layout();
15515 // Use the syncHeightBeforeShow config option to control this automatically
15516 syncBodyHeight : function(){
15517 Roo.LayoutDialog.superclass.syncBodyHeight.call(this);
15518 if(this.layout){this.layout.layout();}
15522 * Add an xtype element (actually adds to the layout.)
15523 * @return {Object} xdata xtype object data.
15526 addxtype : function(c) {
15527 return this.layout.addxtype(c);
15531 * Ext JS Library 1.1.1
15532 * Copyright(c) 2006-2007, Ext JS, LLC.
15534 * Originally Released Under LGPL - original licence link has changed is not relivant.
15537 * <script type="text/javascript">
15541 * @class Roo.MessageBox
15542 * Utility class for generating different styles of message boxes. The alias Roo.Msg can also be used.
15546 Roo.Msg.alert('Status', 'Changes saved successfully.');
15548 // Prompt for user data:
15549 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
15551 // process text value...
15555 // Show a dialog using config options:
15557 title:'Save Changes?',
15558 msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
15559 buttons: Roo.Msg.YESNOCANCEL,
15566 Roo.MessageBox = function(){
15567 var dlg, opt, mask, waitTimer;
15568 var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
15569 var buttons, activeTextEl, bwidth;
15572 var handleButton = function(button){
15574 Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
15578 var handleHide = function(){
15579 if(opt && opt.cls){
15580 dlg.el.removeClass(opt.cls);
15583 Roo.TaskMgr.stop(waitTimer);
15589 var updateButtons = function(b){
15592 buttons["ok"].hide();
15593 buttons["cancel"].hide();
15594 buttons["yes"].hide();
15595 buttons["no"].hide();
15596 dlg.footer.dom.style.display = 'none';
15599 dlg.footer.dom.style.display = '';
15600 for(var k in buttons){
15601 if(typeof buttons[k] != "function"){
15604 buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.MessageBox.buttonText[k]);
15605 width += buttons[k].el.getWidth()+15;
15615 var handleEsc = function(d, k, e){
15616 if(opt && opt.closable !== false){
15626 * Returns a reference to the underlying {@link Roo.BasicDialog} element
15627 * @return {Roo.BasicDialog} The BasicDialog element
15629 getDialog : function(){
15631 dlg = new Roo.BasicDialog("x-msg-box", {
15636 constraintoviewport:false,
15638 collapsible : false,
15641 width:400, height:100,
15642 buttonAlign:"center",
15643 closeClick : function(){
15644 if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
15645 handleButton("no");
15647 handleButton("cancel");
15651 dlg.on("hide", handleHide);
15653 dlg.addKeyListener(27, handleEsc);
15655 var bt = this.buttonText;
15656 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
15657 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
15658 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
15659 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
15660 bodyEl = dlg.body.createChild({
15662 html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" /><textarea class="roo-mb-textarea"></textarea><div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar"> </div></div></div>'
15664 msgEl = bodyEl.dom.firstChild;
15665 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
15666 textboxEl.enableDisplayMode();
15667 textboxEl.addKeyListener([10,13], function(){
15668 if(dlg.isVisible() && opt && opt.buttons){
15669 if(opt.buttons.ok){
15670 handleButton("ok");
15671 }else if(opt.buttons.yes){
15672 handleButton("yes");
15676 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
15677 textareaEl.enableDisplayMode();
15678 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
15679 progressEl.enableDisplayMode();
15680 var pf = progressEl.dom.firstChild;
15682 pp = Roo.get(pf.firstChild);
15683 pp.setHeight(pf.offsetHeight);
15691 * Updates the message box body text
15692 * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
15693 * the XHTML-compliant non-breaking space character '&#160;')
15694 * @return {Roo.MessageBox} This message box
15696 updateText : function(text){
15697 if(!dlg.isVisible() && !opt.width){
15698 dlg.resizeTo(this.maxWidth, 100); // resize first so content is never clipped from previous shows
15700 msgEl.innerHTML = text || ' ';
15702 var cw = Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
15703 //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
15705 Math.min(opt.width || cw , this.maxWidth),
15706 Math.max(opt.minWidth || this.minWidth, bwidth)
15709 activeTextEl.setWidth(w);
15711 if(dlg.isVisible()){
15712 dlg.fixedcenter = false;
15714 // to big, make it scroll. = But as usual stupid IE does not support
15717 if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
15718 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
15719 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
15721 bodyEl.dom.style.height = '';
15722 bodyEl.dom.style.overflowY = '';
15725 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
15727 bodyEl.dom.style.overflowX = '';
15730 dlg.setContentSize(w, bodyEl.getHeight());
15731 if(dlg.isVisible()){
15732 dlg.fixedcenter = true;
15738 * Updates a progress-style message box's text and progress bar. Only relevant on message boxes
15739 * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
15740 * @param {Number} value Any number between 0 and 1 (e.g., .5)
15741 * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
15742 * @return {Roo.MessageBox} This message box
15744 updateProgress : function(value, text){
15746 this.updateText(text);
15748 if (pp) { // weird bug on my firefox - for some reason this is not defined
15749 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
15755 * Returns true if the message box is currently displayed
15756 * @return {Boolean} True if the message box is visible, else false
15758 isVisible : function(){
15759 return dlg && dlg.isVisible();
15763 * Hides the message box if it is displayed
15766 if(this.isVisible()){
15772 * Displays a new message box, or reinitializes an existing message box, based on the config options
15773 * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
15774 * The following config object properties are supported:
15776 Property Type Description
15777 ---------- --------------- ------------------------------------------------------------------------------------
15778 animEl String/Element An id or Element from which the message box should animate as it opens and
15779 closes (defaults to undefined)
15780 buttons Object/Boolean A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
15781 cancel:'Bar'}), or false to not show any buttons (defaults to false)
15782 closable Boolean False to hide the top-right close button (defaults to true). Note that
15783 progress and wait dialogs will ignore this property and always hide the
15784 close button as they can only be closed programmatically.
15785 cls String A custom CSS class to apply to the message box element
15786 defaultTextHeight Number The default height in pixels of the message box's multiline textarea if
15787 displayed (defaults to 75)
15788 fn Function A callback function to execute after closing the dialog. The arguments to the
15789 function will be btn (the name of the button that was clicked, if applicable,
15790 e.g. "ok"), and text (the value of the active text field, if applicable).
15791 Progress and wait dialogs will ignore this option since they do not respond to
15792 user actions and can only be closed programmatically, so any required function
15793 should be called by the same code after it closes the dialog.
15794 icon String A CSS class that provides a background image to be used as an icon for
15795 the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
15796 maxWidth Number The maximum width in pixels of the message box (defaults to 600)
15797 minWidth Number The minimum width in pixels of the message box (defaults to 100)
15798 modal Boolean False to allow user interaction with the page while the message box is
15799 displayed (defaults to true)
15800 msg String A string that will replace the existing message box body text (defaults
15801 to the XHTML-compliant non-breaking space character ' ')
15802 multiline Boolean True to prompt the user to enter multi-line text (defaults to false)
15803 progress Boolean True to display a progress bar (defaults to false)
15804 progressText String The text to display inside the progress bar if progress = true (defaults to '')
15805 prompt Boolean True to prompt the user to enter single-line text (defaults to false)
15806 proxyDrag Boolean True to display a lightweight proxy while dragging (defaults to false)
15807 title String The title text
15808 value String The string value to set into the active textbox element if displayed
15809 wait Boolean True to display a progress bar (defaults to false)
15810 width Number The width of the dialog in pixels
15817 msg: 'Please enter your address:',
15819 buttons: Roo.MessageBox.OKCANCEL,
15822 animEl: 'addAddressBtn'
15825 * @param {Object} config Configuration options
15826 * @return {Roo.MessageBox} This message box
15828 show : function(options)
15831 // this causes nightmares if you show one dialog after another
15832 // especially on callbacks..
15834 if(this.isVisible()){
15837 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
15838 Roo.log("Old Dialog Message:" + msgEl.innerHTML );
15839 Roo.log("New Dialog Message:" + options.msg )
15840 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
15841 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
15844 var d = this.getDialog();
15846 d.setTitle(opt.title || " ");
15847 d.close.setDisplayed(opt.closable !== false);
15848 activeTextEl = textboxEl;
15849 opt.prompt = opt.prompt || (opt.multiline ? true : false);
15854 textareaEl.setHeight(typeof opt.multiline == "number" ?
15855 opt.multiline : this.defaultTextHeight);
15856 activeTextEl = textareaEl;
15865 progressEl.setDisplayed(opt.progress === true);
15866 this.updateProgress(0);
15867 activeTextEl.dom.value = opt.value || "";
15869 dlg.setDefaultButton(activeTextEl);
15871 var bs = opt.buttons;
15874 db = buttons["ok"];
15875 }else if(bs && bs.yes){
15876 db = buttons["yes"];
15878 dlg.setDefaultButton(db);
15880 bwidth = updateButtons(opt.buttons);
15881 this.updateText(opt.msg);
15883 d.el.addClass(opt.cls);
15885 d.proxyDrag = opt.proxyDrag === true;
15886 d.modal = opt.modal !== false;
15887 d.mask = opt.modal !== false ? mask : false;
15888 if(!d.isVisible()){
15889 // force it to the end of the z-index stack so it gets a cursor in FF
15890 document.body.appendChild(dlg.el.dom);
15891 d.animateTarget = null;
15892 d.show(options.animEl);
15898 * Displays a message box with a progress bar. This message box has no buttons and is not closeable by
15899 * the user. You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
15900 * and closing the message box when the process is complete.
15901 * @param {String} title The title bar text
15902 * @param {String} msg The message box body text
15903 * @return {Roo.MessageBox} This message box
15905 progress : function(title, msg){
15912 minWidth: this.minProgressWidth,
15919 * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
15920 * If a callback function is passed it will be called after the user clicks the button, and the
15921 * id of the button that was clicked will be passed as the only parameter to the callback
15922 * (could also be the top-right close button).
15923 * @param {String} title The title bar text
15924 * @param {String} msg The message box body text
15925 * @param {Function} fn (optional) The callback function invoked after the message box is closed
15926 * @param {Object} scope (optional) The scope of the callback function
15927 * @return {Roo.MessageBox} This message box
15929 alert : function(title, msg, fn, scope){
15942 * Displays a message box with an infinitely auto-updating progress bar. This can be used to block user
15943 * interaction while waiting for a long-running process to complete that does not have defined intervals.
15944 * You are responsible for closing the message box when the process is complete.
15945 * @param {String} msg The message box body text
15946 * @param {String} title (optional) The title bar text
15947 * @return {Roo.MessageBox} This message box
15949 wait : function(msg, title){
15960 waitTimer = Roo.TaskMgr.start({
15962 Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
15970 * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
15971 * If a callback function is passed it will be called after the user clicks either button, and the id of the
15972 * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
15973 * @param {String} title The title bar text
15974 * @param {String} msg The message box body text
15975 * @param {Function} fn (optional) The callback function invoked after the message box is closed
15976 * @param {Object} scope (optional) The scope of the callback function
15977 * @return {Roo.MessageBox} This message box
15979 confirm : function(title, msg, fn, scope){
15983 buttons: this.YESNO,
15992 * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
15993 * JavaScript's Window.prompt). The prompt can be a single-line or multi-line textbox. If a callback function
15994 * is passed it will be called after the user clicks either button, and the id of the button that was clicked
15995 * (could also be the top-right close button) and the text that was entered will be passed as the two
15996 * parameters to the callback.
15997 * @param {String} title The title bar text
15998 * @param {String} msg The message box body text
15999 * @param {Function} fn (optional) The callback function invoked after the message box is closed
16000 * @param {Object} scope (optional) The scope of the callback function
16001 * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
16002 * property, or the height in pixels to create the textbox (defaults to false / single-line)
16003 * @return {Roo.MessageBox} This message box
16005 prompt : function(title, msg, fn, scope, multiline){
16009 buttons: this.OKCANCEL,
16014 multiline: multiline,
16021 * Button config that displays a single OK button
16026 * Button config that displays Yes and No buttons
16029 YESNO : {yes:true, no:true},
16031 * Button config that displays OK and Cancel buttons
16034 OKCANCEL : {ok:true, cancel:true},
16036 * Button config that displays Yes, No and Cancel buttons
16039 YESNOCANCEL : {yes:true, no:true, cancel:true},
16042 * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
16045 defaultTextHeight : 75,
16047 * The maximum width in pixels of the message box (defaults to 600)
16052 * The minimum width in pixels of the message box (defaults to 100)
16057 * The minimum width in pixels of the message box if it is a progress-style dialog. This is useful
16058 * for setting a different minimum width than text-only dialogs may need (defaults to 250)
16061 minProgressWidth : 250,
16063 * An object containing the default button text strings that can be overriden for localized language support.
16064 * Supported properties are: ok, cancel, yes and no.
16065 * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
16078 * Shorthand for {@link Roo.MessageBox}
16080 Roo.Msg = Roo.MessageBox;/*
16082 * Ext JS Library 1.1.1
16083 * Copyright(c) 2006-2007, Ext JS, LLC.
16085 * Originally Released Under LGPL - original licence link has changed is not relivant.
16088 * <script type="text/javascript">
16091 * @class Roo.QuickTips
16092 * Provides attractive and customizable tooltips for any element.
16095 Roo.QuickTips = function(){
16096 var el, tipBody, tipBodyText, tipTitle, tm, cfg, close, tagEls = {}, esc, removeCls = null, bdLeft, bdRight;
16097 var ce, bd, xy, dd;
16098 var visible = false, disabled = true, inited = false;
16099 var showProc = 1, hideProc = 1, dismissProc = 1, locks = [];
16101 var onOver = function(e){
16105 var t = e.getTarget();
16106 if(!t || t.nodeType !== 1 || t == document || t == document.body){
16109 if(ce && t == ce.el){
16110 clearTimeout(hideProc);
16113 if(t && tagEls[t.id]){
16114 tagEls[t.id].el = t;
16115 showProc = show.defer(tm.showDelay, tm, [tagEls[t.id]]);
16118 var ttp, et = Roo.fly(t);
16119 var ns = cfg.namespace;
16120 if(tm.interceptTitles && t.title){
16123 t.removeAttribute("title");
16124 e.preventDefault();
16126 ttp = t.qtip || et.getAttributeNS(ns, cfg.attribute);
16129 showProc = show.defer(tm.showDelay, tm, [{
16132 width: et.getAttributeNS(ns, cfg.width),
16133 autoHide: et.getAttributeNS(ns, cfg.hide) != "user",
16134 title: et.getAttributeNS(ns, cfg.title),
16135 cls: et.getAttributeNS(ns, cfg.cls)
16140 var onOut = function(e){
16141 clearTimeout(showProc);
16142 var t = e.getTarget();
16143 if(t && ce && ce.el == t && (tm.autoHide && ce.autoHide !== false)){
16144 hideProc = setTimeout(hide, tm.hideDelay);
16148 var onMove = function(e){
16154 if(tm.trackMouse && ce){
16159 var onDown = function(e){
16160 clearTimeout(showProc);
16161 clearTimeout(hideProc);
16163 if(tm.hideOnClick){
16166 tm.enable.defer(100, tm);
16171 var getPad = function(){
16172 return 2;//bdLeft.getPadding('l')+bdRight.getPadding('r');
16175 var show = function(o){
16179 clearTimeout(dismissProc);
16181 if(removeCls){ // in case manually hidden
16182 el.removeClass(removeCls);
16186 el.addClass(ce.cls);
16187 removeCls = ce.cls;
16190 tipTitle.update(ce.title);
16193 tipTitle.update('');
16196 el.dom.style.width = tm.maxWidth+'px';
16197 //tipBody.dom.style.width = '';
16198 tipBodyText.update(o.text);
16199 var p = getPad(), w = ce.width;
16201 var td = tipBodyText.dom;
16202 var aw = Math.max(td.offsetWidth, td.clientWidth, td.scrollWidth);
16203 if(aw > tm.maxWidth){
16205 }else if(aw < tm.minWidth){
16211 //tipBody.setWidth(w);
16212 el.setWidth(parseInt(w, 10) + p);
16213 if(ce.autoHide === false){
16214 close.setDisplayed(true);
16219 close.setDisplayed(false);
16225 el.avoidY = xy[1]-18;
16230 el.setStyle("visibility", "visible");
16231 el.fadeIn({callback: afterShow});
16237 var afterShow = function(){
16241 if(tm.autoDismiss && ce.autoHide !== false){
16242 dismissProc = setTimeout(hide, tm.autoDismissDelay);
16247 var hide = function(noanim){
16248 clearTimeout(dismissProc);
16249 clearTimeout(hideProc);
16251 if(el.isVisible()){
16253 if(noanim !== true && tm.animate){
16254 el.fadeOut({callback: afterHide});
16261 var afterHide = function(){
16264 el.removeClass(removeCls);
16271 * @cfg {Number} minWidth
16272 * The minimum width of the quick tip (defaults to 40)
16276 * @cfg {Number} maxWidth
16277 * The maximum width of the quick tip (defaults to 300)
16281 * @cfg {Boolean} interceptTitles
16282 * True to automatically use the element's DOM title value if available (defaults to false)
16284 interceptTitles : false,
16286 * @cfg {Boolean} trackMouse
16287 * True to have the quick tip follow the mouse as it moves over the target element (defaults to false)
16289 trackMouse : false,
16291 * @cfg {Boolean} hideOnClick
16292 * True to hide the quick tip if the user clicks anywhere in the document (defaults to true)
16294 hideOnClick : true,
16296 * @cfg {Number} showDelay
16297 * Delay in milliseconds before the quick tip displays after the mouse enters the target element (defaults to 500)
16301 * @cfg {Number} hideDelay
16302 * Delay in milliseconds before the quick tip hides when autoHide = true (defaults to 200)
16306 * @cfg {Boolean} autoHide
16307 * True to automatically hide the quick tip after the mouse exits the target element (defaults to true).
16308 * Used in conjunction with hideDelay.
16313 * True to automatically hide the quick tip after a set period of time, regardless of the user's actions
16314 * (defaults to true). Used in conjunction with autoDismissDelay.
16316 autoDismiss : true,
16319 * Delay in milliseconds before the quick tip hides when autoDismiss = true (defaults to 5000)
16321 autoDismissDelay : 5000,
16323 * @cfg {Boolean} animate
16324 * True to turn on fade animation. Defaults to false (ClearType/scrollbar flicker issues in IE7).
16329 * @cfg {String} title
16330 * Title text to display (defaults to ''). This can be any valid HTML markup.
16334 * @cfg {String} text
16335 * Body text to display (defaults to ''). This can be any valid HTML markup.
16339 * @cfg {String} cls
16340 * A CSS class to apply to the base quick tip element (defaults to '').
16344 * @cfg {Number} width
16345 * Width in pixels of the quick tip (defaults to auto). Width will be ignored if it exceeds the bounds of
16346 * minWidth or maxWidth.
16351 * Initialize and enable QuickTips for first use. This should be called once before the first attempt to access
16352 * or display QuickTips in a page.
16355 tm = Roo.QuickTips;
16356 cfg = tm.tagConfig;
16358 if(!Roo.isReady){ // allow calling of init() before onReady
16359 Roo.onReady(Roo.QuickTips.init, Roo.QuickTips);
16362 el = new Roo.Layer({cls:"x-tip", shadow:"drop", shim: true, constrain:true, shadowOffset:4});
16363 el.fxDefaults = {stopFx: true};
16364 // maximum custom styling
16365 //el.update('<div class="x-tip-top-left"><div class="x-tip-top-right"><div class="x-tip-top"></div></div></div><div class="x-tip-bd-left"><div class="x-tip-bd-right"><div class="x-tip-bd"><div class="x-tip-close"></div><h3></h3><div class="x-tip-bd-inner"></div><div class="x-clear"></div></div></div></div><div class="x-tip-ft-left"><div class="x-tip-ft-right"><div class="x-tip-ft"></div></div></div>');
16366 el.update('<div class="x-tip-bd"><div class="x-tip-close"></div><h3></h3><div class="x-tip-bd-inner"></div><div class="x-clear"></div></div>');
16367 tipTitle = el.child('h3');
16368 tipTitle.enableDisplayMode("block");
16369 tipBody = el.child('div.x-tip-bd');
16370 tipBodyText = el.child('div.x-tip-bd-inner');
16371 //bdLeft = el.child('div.x-tip-bd-left');
16372 //bdRight = el.child('div.x-tip-bd-right');
16373 close = el.child('div.x-tip-close');
16374 close.enableDisplayMode("block");
16375 close.on("click", hide);
16376 var d = Roo.get(document);
16377 d.on("mousedown", onDown);
16378 d.on("mouseover", onOver);
16379 d.on("mouseout", onOut);
16380 d.on("mousemove", onMove);
16381 esc = d.addKeyListener(27, hide);
16384 dd = el.initDD("default", null, {
16385 onDrag : function(){
16389 dd.setHandleElId(tipTitle.id);
16398 * Configures a new quick tip instance and assigns it to a target element. The following config options
16401 Property Type Description
16402 ---------- --------------------- ------------------------------------------------------------------------
16403 target Element/String/Array An Element, id or array of ids that this quick tip should be tied to
16405 * @param {Object} config The config object
16407 register : function(config){
16408 var cs = config instanceof Array ? config : arguments;
16409 for(var i = 0, len = cs.length; i < len; i++) {
16411 var target = c.target;
16413 if(target instanceof Array){
16414 for(var j = 0, jlen = target.length; j < jlen; j++){
16415 tagEls[target[j]] = c;
16418 tagEls[typeof target == 'string' ? target : Roo.id(target)] = c;
16425 * Removes this quick tip from its element and destroys it.
16426 * @param {String/HTMLElement/Element} el The element from which the quick tip is to be removed.
16428 unregister : function(el){
16429 delete tagEls[Roo.id(el)];
16433 * Enable this quick tip.
16435 enable : function(){
16436 if(inited && disabled){
16438 if(locks.length < 1){
16445 * Disable this quick tip.
16447 disable : function(){
16449 clearTimeout(showProc);
16450 clearTimeout(hideProc);
16451 clearTimeout(dismissProc);
16459 * Returns true if the quick tip is enabled, else false.
16461 isEnabled : function(){
16468 attribute : "qtip",
16478 // backwards compat
16479 Roo.QuickTips.tips = Roo.QuickTips.register;/*
16481 * Ext JS Library 1.1.1
16482 * Copyright(c) 2006-2007, Ext JS, LLC.
16484 * Originally Released Under LGPL - original licence link has changed is not relivant.
16487 * <script type="text/javascript">
16492 * @class Roo.tree.TreePanel
16493 * @extends Roo.data.Tree
16495 * @cfg {Boolean} rootVisible false to hide the root node (defaults to true)
16496 * @cfg {Boolean} lines false to disable tree lines (defaults to true)
16497 * @cfg {Boolean} enableDD true to enable drag and drop
16498 * @cfg {Boolean} enableDrag true to enable just drag
16499 * @cfg {Boolean} enableDrop true to enable just drop
16500 * @cfg {Object} dragConfig Custom config to pass to the {@link Roo.tree.TreeDragZone} instance
16501 * @cfg {Object} dropConfig Custom config to pass to the {@link Roo.tree.TreeDropZone} instance
16502 * @cfg {String} ddGroup The DD group this TreePanel belongs to
16503 * @cfg {String} ddAppendOnly True if the tree should only allow append drops (use for trees which are sorted)
16504 * @cfg {Boolean} ddScroll true to enable YUI body scrolling
16505 * @cfg {Boolean} containerScroll true to register this container with ScrollManager
16506 * @cfg {Boolean} hlDrop false to disable node highlight on drop (defaults to the value of Roo.enableFx)
16507 * @cfg {String} hlColor The color of the node highlight (defaults to C3DAF9)
16508 * @cfg {Boolean} animate true to enable animated expand/collapse (defaults to the value of Roo.enableFx)
16509 * @cfg {Boolean} singleExpand true if only 1 node per branch may be expanded
16510 * @cfg {Boolean} selModel A tree selection model to use with this TreePanel (defaults to a {@link Roo.tree.DefaultSelectionModel})
16511 * @cfg {Boolean} loader A TreeLoader for use with this TreePanel
16512 * @cfg {Object|Roo.tree.TreeEditor} editor The TreeEditor or xtype data to display when clicked.
16513 * @cfg {String} pathSeparator The token used to separate sub-paths in path strings (defaults to '/')
16514 * @cfg {Function} renderer DEPRECATED - use TreeLoader:create event / Sets the rendering (formatting) function for the nodes. to return HTML markup for the tree view. The render function is called with the following parameters:<ul><li>The {Object} The data for the node.</li></ul>
16515 * @cfg {Function} rendererTip DEPRECATED - use TreeLoader:create event / Sets the rendering (formatting) function for the nodes hovertip to return HTML markup for the tree view. The render function is called with the following parameters:<ul><li>The {Object} The data for the node.</li></ul>
16518 * @param {String/HTMLElement/Element} el The container element
16519 * @param {Object} config
16521 Roo.tree.TreePanel = function(el, config){
16523 var loader = false;
16525 root = config.root;
16526 delete config.root;
16528 if (config.loader) {
16529 loader = config.loader;
16530 delete config.loader;
16533 Roo.apply(this, config);
16534 Roo.tree.TreePanel.superclass.constructor.call(this);
16535 this.el = Roo.get(el);
16536 this.el.addClass('x-tree');
16537 //console.log(root);
16539 this.setRootNode( Roo.factory(root, Roo.tree));
16542 this.loader = Roo.factory(loader, Roo.tree);
16545 * Read-only. The id of the container element becomes this TreePanel's id.
16547 this.id = this.el.id;
16550 * @event beforeload
16551 * Fires before a node is loaded, return false to cancel
16552 * @param {Node} node The node being loaded
16554 "beforeload" : true,
16557 * Fires when a node is loaded
16558 * @param {Node} node The node that was loaded
16562 * @event textchange
16563 * Fires when the text for a node is changed
16564 * @param {Node} node The node
16565 * @param {String} text The new text
16566 * @param {String} oldText The old text
16568 "textchange" : true,
16570 * @event beforeexpand
16571 * Fires before a node is expanded, return false to cancel.
16572 * @param {Node} node The node
16573 * @param {Boolean} deep
16574 * @param {Boolean} anim
16576 "beforeexpand" : true,
16578 * @event beforecollapse
16579 * Fires before a node is collapsed, return false to cancel.
16580 * @param {Node} node The node
16581 * @param {Boolean} deep
16582 * @param {Boolean} anim
16584 "beforecollapse" : true,
16587 * Fires when a node is expanded
16588 * @param {Node} node The node
16592 * @event disabledchange
16593 * Fires when the disabled status of a node changes
16594 * @param {Node} node The node
16595 * @param {Boolean} disabled
16597 "disabledchange" : true,
16600 * Fires when a node is collapsed
16601 * @param {Node} node The node
16605 * @event beforeclick
16606 * Fires before click processing on a node. Return false to cancel the default action.
16607 * @param {Node} node The node
16608 * @param {Roo.EventObject} e The event object
16610 "beforeclick":true,
16612 * @event checkchange
16613 * Fires when a node with a checkbox's checked property changes
16614 * @param {Node} this This node
16615 * @param {Boolean} checked
16617 "checkchange":true,
16620 * Fires when a node is clicked
16621 * @param {Node} node The node
16622 * @param {Roo.EventObject} e The event object
16627 * Fires when a node is double clicked
16628 * @param {Node} node The node
16629 * @param {Roo.EventObject} e The event object
16633 * @event contextmenu
16634 * Fires when a node is right clicked
16635 * @param {Node} node The node
16636 * @param {Roo.EventObject} e The event object
16638 "contextmenu":true,
16640 * @event beforechildrenrendered
16641 * Fires right before the child nodes for a node are rendered
16642 * @param {Node} node The node
16644 "beforechildrenrendered":true,
16647 * Fires when a node starts being dragged
16648 * @param {Roo.tree.TreePanel} this
16649 * @param {Roo.tree.TreeNode} node
16650 * @param {event} e The raw browser event
16652 "startdrag" : true,
16655 * Fires when a drag operation is complete
16656 * @param {Roo.tree.TreePanel} this
16657 * @param {Roo.tree.TreeNode} node
16658 * @param {event} e The raw browser event
16663 * Fires when a dragged node is dropped on a valid DD target
16664 * @param {Roo.tree.TreePanel} this
16665 * @param {Roo.tree.TreeNode} node
16666 * @param {DD} dd The dd it was dropped on
16667 * @param {event} e The raw browser event
16671 * @event beforenodedrop
16672 * Fires when a DD object is dropped on a node in this tree for preprocessing. Return false to cancel the drop. The dropEvent
16673 * passed to handlers has the following properties:<br />
16674 * <ul style="padding:5px;padding-left:16px;">
16675 * <li>tree - The TreePanel</li>
16676 * <li>target - The node being targeted for the drop</li>
16677 * <li>data - The drag data from the drag source</li>
16678 * <li>point - The point of the drop - append, above or below</li>
16679 * <li>source - The drag source</li>
16680 * <li>rawEvent - Raw mouse event</li>
16681 * <li>dropNode - Drop node(s) provided by the source <b>OR</b> you can supply node(s)
16682 * to be inserted by setting them on this object.</li>
16683 * <li>cancel - Set this to true to cancel the drop.</li>
16685 * @param {Object} dropEvent
16687 "beforenodedrop" : true,
16690 * Fires after a DD object is dropped on a node in this tree. The dropEvent
16691 * passed to handlers has the following properties:<br />
16692 * <ul style="padding:5px;padding-left:16px;">
16693 * <li>tree - The TreePanel</li>
16694 * <li>target - The node being targeted for the drop</li>
16695 * <li>data - The drag data from the drag source</li>
16696 * <li>point - The point of the drop - append, above or below</li>
16697 * <li>source - The drag source</li>
16698 * <li>rawEvent - Raw mouse event</li>
16699 * <li>dropNode - Dropped node(s).</li>
16701 * @param {Object} dropEvent
16705 * @event nodedragover
16706 * Fires when a tree node is being targeted for a drag drop, return false to signal drop not allowed. The dragOverEvent
16707 * passed to handlers has the following properties:<br />
16708 * <ul style="padding:5px;padding-left:16px;">
16709 * <li>tree - The TreePanel</li>
16710 * <li>target - The node being targeted for the drop</li>
16711 * <li>data - The drag data from the drag source</li>
16712 * <li>point - The point of the drop - append, above or below</li>
16713 * <li>source - The drag source</li>
16714 * <li>rawEvent - Raw mouse event</li>
16715 * <li>dropNode - Drop node(s) provided by the source.</li>
16716 * <li>cancel - Set this to true to signal drop not allowed.</li>
16718 * @param {Object} dragOverEvent
16720 "nodedragover" : true
16723 if(this.singleExpand){
16724 this.on("beforeexpand", this.restrictExpand, this);
16727 this.editor.tree = this;
16728 this.editor = Roo.factory(this.editor, Roo.tree);
16731 if (this.selModel) {
16732 this.selModel = Roo.factory(this.selModel, Roo.tree);
16736 Roo.extend(Roo.tree.TreePanel, Roo.data.Tree, {
16737 rootVisible : true,
16738 animate: Roo.enableFx,
16741 hlDrop : Roo.enableFx,
16745 rendererTip: false,
16747 restrictExpand : function(node){
16748 var p = node.parentNode;
16750 if(p.expandedChild && p.expandedChild.parentNode == p){
16751 p.expandedChild.collapse();
16753 p.expandedChild = node;
16757 // private override
16758 setRootNode : function(node){
16759 Roo.tree.TreePanel.superclass.setRootNode.call(this, node);
16760 if(!this.rootVisible){
16761 node.ui = new Roo.tree.RootTreeNodeUI(node);
16767 * Returns the container element for this TreePanel
16769 getEl : function(){
16774 * Returns the default TreeLoader for this TreePanel
16776 getLoader : function(){
16777 return this.loader;
16783 expandAll : function(){
16784 this.root.expand(true);
16788 * Collapse all nodes
16790 collapseAll : function(){
16791 this.root.collapse(true);
16795 * Returns the selection model used by this TreePanel
16797 getSelectionModel : function(){
16798 if(!this.selModel){
16799 this.selModel = new Roo.tree.DefaultSelectionModel();
16801 return this.selModel;
16805 * Retrieve an array of checked nodes, or an array of a specific attribute of checked nodes (e.g. "id")
16806 * @param {String} attribute (optional) Defaults to null (return the actual nodes)
16807 * @param {TreeNode} startNode (optional) The node to start from, defaults to the root
16810 getChecked : function(a, startNode){
16811 startNode = startNode || this.root;
16813 var f = function(){
16814 if(this.attributes.checked){
16815 r.push(!a ? this : (a == 'id' ? this.id : this.attributes[a]));
16818 startNode.cascade(f);
16823 * Expands a specified path in this TreePanel. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
16824 * @param {String} path
16825 * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
16826 * @param {Function} callback (optional) The callback to call when the expand is complete. The callback will be called with
16827 * (bSuccess, oLastNode) where bSuccess is if the expand was successful and oLastNode is the last node that was expanded.
16829 expandPath : function(path, attr, callback){
16830 attr = attr || "id";
16831 var keys = path.split(this.pathSeparator);
16832 var curNode = this.root;
16833 if(curNode.attributes[attr] != keys[1]){ // invalid root
16835 callback(false, null);
16840 var f = function(){
16841 if(++index == keys.length){
16843 callback(true, curNode);
16847 var c = curNode.findChild(attr, keys[index]);
16850 callback(false, curNode);
16855 c.expand(false, false, f);
16857 curNode.expand(false, false, f);
16861 * Selects the node in this tree at the specified path. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
16862 * @param {String} path
16863 * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
16864 * @param {Function} callback (optional) The callback to call when the selection is complete. The callback will be called with
16865 * (bSuccess, oSelNode) where bSuccess is if the selection was successful and oSelNode is the selected node.
16867 selectPath : function(path, attr, callback){
16868 attr = attr || "id";
16869 var keys = path.split(this.pathSeparator);
16870 var v = keys.pop();
16871 if(keys.length > 0){
16872 var f = function(success, node){
16873 if(success && node){
16874 var n = node.findChild(attr, v);
16880 }else if(callback){
16881 callback(false, n);
16885 callback(false, n);
16889 this.expandPath(keys.join(this.pathSeparator), attr, f);
16891 this.root.select();
16893 callback(true, this.root);
16898 getTreeEl : function(){
16903 * Trigger rendering of this TreePanel
16905 render : function(){
16906 if (this.innerCt) {
16907 return this; // stop it rendering more than once!!
16910 this.innerCt = this.el.createChild({tag:"ul",
16911 cls:"x-tree-root-ct " +
16912 (this.lines ? "x-tree-lines" : "x-tree-no-lines")});
16914 if(this.containerScroll){
16915 Roo.dd.ScrollManager.register(this.el);
16917 if((this.enableDD || this.enableDrop) && !this.dropZone){
16919 * The dropZone used by this tree if drop is enabled
16920 * @type Roo.tree.TreeDropZone
16922 this.dropZone = new Roo.tree.TreeDropZone(this, this.dropConfig || {
16923 ddGroup: this.ddGroup || "TreeDD", appendOnly: this.ddAppendOnly === true
16926 if((this.enableDD || this.enableDrag) && !this.dragZone){
16928 * The dragZone used by this tree if drag is enabled
16929 * @type Roo.tree.TreeDragZone
16931 this.dragZone = new Roo.tree.TreeDragZone(this, this.dragConfig || {
16932 ddGroup: this.ddGroup || "TreeDD",
16933 scroll: this.ddScroll
16936 this.getSelectionModel().init(this);
16938 Roo.log("ROOT not set in tree");
16941 this.root.render();
16942 if(!this.rootVisible){
16943 this.root.renderChildren();
16949 * Ext JS Library 1.1.1
16950 * Copyright(c) 2006-2007, Ext JS, LLC.
16952 * Originally Released Under LGPL - original licence link has changed is not relivant.
16955 * <script type="text/javascript">
16960 * @class Roo.tree.DefaultSelectionModel
16961 * @extends Roo.util.Observable
16962 * The default single selection for a TreePanel.
16963 * @param {Object} cfg Configuration
16965 Roo.tree.DefaultSelectionModel = function(cfg){
16966 this.selNode = null;
16972 * @event selectionchange
16973 * Fires when the selected node changes
16974 * @param {DefaultSelectionModel} this
16975 * @param {TreeNode} node the new selection
16977 "selectionchange" : true,
16980 * @event beforeselect
16981 * Fires before the selected node changes, return false to cancel the change
16982 * @param {DefaultSelectionModel} this
16983 * @param {TreeNode} node the new selection
16984 * @param {TreeNode} node the old selection
16986 "beforeselect" : true
16989 Roo.tree.DefaultSelectionModel.superclass.constructor.call(this,cfg);
16992 Roo.extend(Roo.tree.DefaultSelectionModel, Roo.util.Observable, {
16993 init : function(tree){
16995 tree.getTreeEl().on("keydown", this.onKeyDown, this);
16996 tree.on("click", this.onNodeClick, this);
16999 onNodeClick : function(node, e){
17000 if (e.ctrlKey && this.selNode == node) {
17001 this.unselect(node);
17009 * @param {TreeNode} node The node to select
17010 * @return {TreeNode} The selected node
17012 select : function(node){
17013 var last = this.selNode;
17014 if(last != node && this.fireEvent('beforeselect', this, node, last) !== false){
17016 last.ui.onSelectedChange(false);
17018 this.selNode = node;
17019 node.ui.onSelectedChange(true);
17020 this.fireEvent("selectionchange", this, node, last);
17027 * @param {TreeNode} node The node to unselect
17029 unselect : function(node){
17030 if(this.selNode == node){
17031 this.clearSelections();
17036 * Clear all selections
17038 clearSelections : function(){
17039 var n = this.selNode;
17041 n.ui.onSelectedChange(false);
17042 this.selNode = null;
17043 this.fireEvent("selectionchange", this, null);
17049 * Get the selected node
17050 * @return {TreeNode} The selected node
17052 getSelectedNode : function(){
17053 return this.selNode;
17057 * Returns true if the node is selected
17058 * @param {TreeNode} node The node to check
17059 * @return {Boolean}
17061 isSelected : function(node){
17062 return this.selNode == node;
17066 * Selects the node above the selected node in the tree, intelligently walking the nodes
17067 * @return TreeNode The new selection
17069 selectPrevious : function(){
17070 var s = this.selNode || this.lastSelNode;
17074 var ps = s.previousSibling;
17076 if(!ps.isExpanded() || ps.childNodes.length < 1){
17077 return this.select(ps);
17079 var lc = ps.lastChild;
17080 while(lc && lc.isExpanded() && lc.childNodes.length > 0){
17083 return this.select(lc);
17085 } else if(s.parentNode && (this.tree.rootVisible || !s.parentNode.isRoot)){
17086 return this.select(s.parentNode);
17092 * Selects the node above the selected node in the tree, intelligently walking the nodes
17093 * @return TreeNode The new selection
17095 selectNext : function(){
17096 var s = this.selNode || this.lastSelNode;
17100 if(s.firstChild && s.isExpanded()){
17101 return this.select(s.firstChild);
17102 }else if(s.nextSibling){
17103 return this.select(s.nextSibling);
17104 }else if(s.parentNode){
17106 s.parentNode.bubble(function(){
17107 if(this.nextSibling){
17108 newS = this.getOwnerTree().selModel.select(this.nextSibling);
17117 onKeyDown : function(e){
17118 var s = this.selNode || this.lastSelNode;
17119 // undesirable, but required
17124 var k = e.getKey();
17132 this.selectPrevious();
17135 e.preventDefault();
17136 if(s.hasChildNodes()){
17137 if(!s.isExpanded()){
17139 }else if(s.firstChild){
17140 this.select(s.firstChild, e);
17145 e.preventDefault();
17146 if(s.hasChildNodes() && s.isExpanded()){
17148 }else if(s.parentNode && (this.tree.rootVisible || s.parentNode != this.tree.getRootNode())){
17149 this.select(s.parentNode, e);
17157 * @class Roo.tree.MultiSelectionModel
17158 * @extends Roo.util.Observable
17159 * Multi selection for a TreePanel.
17160 * @param {Object} cfg Configuration
17162 Roo.tree.MultiSelectionModel = function(){
17163 this.selNodes = [];
17167 * @event selectionchange
17168 * Fires when the selected nodes change
17169 * @param {MultiSelectionModel} this
17170 * @param {Array} nodes Array of the selected nodes
17172 "selectionchange" : true
17174 Roo.tree.MultiSelectionModel.superclass.constructor.call(this,cfg);
17178 Roo.extend(Roo.tree.MultiSelectionModel, Roo.util.Observable, {
17179 init : function(tree){
17181 tree.getTreeEl().on("keydown", this.onKeyDown, this);
17182 tree.on("click", this.onNodeClick, this);
17185 onNodeClick : function(node, e){
17186 this.select(node, e, e.ctrlKey);
17191 * @param {TreeNode} node The node to select
17192 * @param {EventObject} e (optional) An event associated with the selection
17193 * @param {Boolean} keepExisting True to retain existing selections
17194 * @return {TreeNode} The selected node
17196 select : function(node, e, keepExisting){
17197 if(keepExisting !== true){
17198 this.clearSelections(true);
17200 if(this.isSelected(node)){
17201 this.lastSelNode = node;
17204 this.selNodes.push(node);
17205 this.selMap[node.id] = node;
17206 this.lastSelNode = node;
17207 node.ui.onSelectedChange(true);
17208 this.fireEvent("selectionchange", this, this.selNodes);
17214 * @param {TreeNode} node The node to unselect
17216 unselect : function(node){
17217 if(this.selMap[node.id]){
17218 node.ui.onSelectedChange(false);
17219 var sn = this.selNodes;
17222 index = sn.indexOf(node);
17224 for(var i = 0, len = sn.length; i < len; i++){
17232 this.selNodes.splice(index, 1);
17234 delete this.selMap[node.id];
17235 this.fireEvent("selectionchange", this, this.selNodes);
17240 * Clear all selections
17242 clearSelections : function(suppressEvent){
17243 var sn = this.selNodes;
17245 for(var i = 0, len = sn.length; i < len; i++){
17246 sn[i].ui.onSelectedChange(false);
17248 this.selNodes = [];
17250 if(suppressEvent !== true){
17251 this.fireEvent("selectionchange", this, this.selNodes);
17257 * Returns true if the node is selected
17258 * @param {TreeNode} node The node to check
17259 * @return {Boolean}
17261 isSelected : function(node){
17262 return this.selMap[node.id] ? true : false;
17266 * Returns an array of the selected nodes
17269 getSelectedNodes : function(){
17270 return this.selNodes;
17273 onKeyDown : Roo.tree.DefaultSelectionModel.prototype.onKeyDown,
17275 selectNext : Roo.tree.DefaultSelectionModel.prototype.selectNext,
17277 selectPrevious : Roo.tree.DefaultSelectionModel.prototype.selectPrevious
17280 * Ext JS Library 1.1.1
17281 * Copyright(c) 2006-2007, Ext JS, LLC.
17283 * Originally Released Under LGPL - original licence link has changed is not relivant.
17286 * <script type="text/javascript">
17290 * @class Roo.tree.TreeNode
17291 * @extends Roo.data.Node
17292 * @cfg {String} text The text for this node
17293 * @cfg {Boolean} expanded true to start the node expanded
17294 * @cfg {Boolean} allowDrag false to make this node undraggable if DD is on (defaults to true)
17295 * @cfg {Boolean} allowDrop false if this node cannot be drop on
17296 * @cfg {Boolean} disabled true to start the node disabled
17297 * @cfg {String} icon The path to an icon for the node. The preferred way to do this
17298 * is to use the cls or iconCls attributes and add the icon via a CSS background image.
17299 * @cfg {String} cls A css class to be added to the node
17300 * @cfg {String} iconCls A css class to be added to the nodes icon element for applying css background images
17301 * @cfg {String} href URL of the link used for the node (defaults to #)
17302 * @cfg {String} hrefTarget target frame for the link
17303 * @cfg {String} qtip An Ext QuickTip for the node
17304 * @cfg {String} qtipCfg An Ext QuickTip config for the node (used instead of qtip)
17305 * @cfg {Boolean} singleClickExpand True for single click expand on this node
17306 * @cfg {Function} uiProvider A UI <b>class</b> to use for this node (defaults to Roo.tree.TreeNodeUI)
17307 * @cfg {Boolean} checked True to render a checked checkbox for this node, false to render an unchecked checkbox
17308 * (defaults to undefined with no checkbox rendered)
17310 * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node
17312 Roo.tree.TreeNode = function(attributes){
17313 attributes = attributes || {};
17314 if(typeof attributes == "string"){
17315 attributes = {text: attributes};
17317 this.childrenRendered = false;
17318 this.rendered = false;
17319 Roo.tree.TreeNode.superclass.constructor.call(this, attributes);
17320 this.expanded = attributes.expanded === true;
17321 this.isTarget = attributes.isTarget !== false;
17322 this.draggable = attributes.draggable !== false && attributes.allowDrag !== false;
17323 this.allowChildren = attributes.allowChildren !== false && attributes.allowDrop !== false;
17326 * Read-only. The text for this node. To change it use setText().
17329 this.text = attributes.text;
17331 * True if this node is disabled.
17334 this.disabled = attributes.disabled === true;
17338 * @event textchange
17339 * Fires when the text for this node is changed
17340 * @param {Node} this This node
17341 * @param {String} text The new text
17342 * @param {String} oldText The old text
17344 "textchange" : true,
17346 * @event beforeexpand
17347 * Fires before this node is expanded, return false to cancel.
17348 * @param {Node} this This node
17349 * @param {Boolean} deep
17350 * @param {Boolean} anim
17352 "beforeexpand" : true,
17354 * @event beforecollapse
17355 * Fires before this node is collapsed, return false to cancel.
17356 * @param {Node} this This node
17357 * @param {Boolean} deep
17358 * @param {Boolean} anim
17360 "beforecollapse" : true,
17363 * Fires when this node is expanded
17364 * @param {Node} this This node
17368 * @event disabledchange
17369 * Fires when the disabled status of this node changes
17370 * @param {Node} this This node
17371 * @param {Boolean} disabled
17373 "disabledchange" : true,
17376 * Fires when this node is collapsed
17377 * @param {Node} this This node
17381 * @event beforeclick
17382 * Fires before click processing. Return false to cancel the default action.
17383 * @param {Node} this This node
17384 * @param {Roo.EventObject} e The event object
17386 "beforeclick":true,
17388 * @event checkchange
17389 * Fires when a node with a checkbox's checked property changes
17390 * @param {Node} this This node
17391 * @param {Boolean} checked
17393 "checkchange":true,
17396 * Fires when this node is clicked
17397 * @param {Node} this This node
17398 * @param {Roo.EventObject} e The event object
17403 * Fires when this node is double clicked
17404 * @param {Node} this This node
17405 * @param {Roo.EventObject} e The event object
17409 * @event contextmenu
17410 * Fires when this node is right clicked
17411 * @param {Node} this This node
17412 * @param {Roo.EventObject} e The event object
17414 "contextmenu":true,
17416 * @event beforechildrenrendered
17417 * Fires right before the child nodes for this node are rendered
17418 * @param {Node} this This node
17420 "beforechildrenrendered":true
17423 var uiClass = this.attributes.uiProvider || Roo.tree.TreeNodeUI;
17426 * Read-only. The UI for this node
17429 this.ui = new uiClass(this);
17431 // finally support items[]
17432 if (typeof(this.attributes.items) == 'undefined' || !this.attributes.items) {
17437 Roo.each(this.attributes.items, function(c) {
17438 this.appendChild(Roo.factory(c,Roo.Tree));
17440 delete this.attributes.items;
17445 Roo.extend(Roo.tree.TreeNode, Roo.data.Node, {
17446 preventHScroll: true,
17448 * Returns true if this node is expanded
17449 * @return {Boolean}
17451 isExpanded : function(){
17452 return this.expanded;
17456 * Returns the UI object for this node
17457 * @return {TreeNodeUI}
17459 getUI : function(){
17463 // private override
17464 setFirstChild : function(node){
17465 var of = this.firstChild;
17466 Roo.tree.TreeNode.superclass.setFirstChild.call(this, node);
17467 if(this.childrenRendered && of && node != of){
17468 of.renderIndent(true, true);
17471 this.renderIndent(true, true);
17475 // private override
17476 setLastChild : function(node){
17477 var ol = this.lastChild;
17478 Roo.tree.TreeNode.superclass.setLastChild.call(this, node);
17479 if(this.childrenRendered && ol && node != ol){
17480 ol.renderIndent(true, true);
17483 this.renderIndent(true, true);
17487 // these methods are overridden to provide lazy rendering support
17488 // private override
17489 appendChild : function()
17491 var node = Roo.tree.TreeNode.superclass.appendChild.apply(this, arguments);
17492 if(node && this.childrenRendered){
17495 this.ui.updateExpandIcon();
17499 // private override
17500 removeChild : function(node){
17501 this.ownerTree.getSelectionModel().unselect(node);
17502 Roo.tree.TreeNode.superclass.removeChild.apply(this, arguments);
17503 // if it's been rendered remove dom node
17504 if(this.childrenRendered){
17507 if(this.childNodes.length < 1){
17508 this.collapse(false, false);
17510 this.ui.updateExpandIcon();
17512 if(!this.firstChild) {
17513 this.childrenRendered = false;
17518 // private override
17519 insertBefore : function(node, refNode){
17520 var newNode = Roo.tree.TreeNode.superclass.insertBefore.apply(this, arguments);
17521 if(newNode && refNode && this.childrenRendered){
17524 this.ui.updateExpandIcon();
17529 * Sets the text for this node
17530 * @param {String} text
17532 setText : function(text){
17533 var oldText = this.text;
17535 this.attributes.text = text;
17536 if(this.rendered){ // event without subscribing
17537 this.ui.onTextChange(this, text, oldText);
17539 this.fireEvent("textchange", this, text, oldText);
17543 * Triggers selection of this node
17545 select : function(){
17546 this.getOwnerTree().getSelectionModel().select(this);
17550 * Triggers deselection of this node
17552 unselect : function(){
17553 this.getOwnerTree().getSelectionModel().unselect(this);
17557 * Returns true if this node is selected
17558 * @return {Boolean}
17560 isSelected : function(){
17561 return this.getOwnerTree().getSelectionModel().isSelected(this);
17565 * Expand this node.
17566 * @param {Boolean} deep (optional) True to expand all children as well
17567 * @param {Boolean} anim (optional) false to cancel the default animation
17568 * @param {Function} callback (optional) A callback to be called when
17569 * expanding this node completes (does not wait for deep expand to complete).
17570 * Called with 1 parameter, this node.
17572 expand : function(deep, anim, callback){
17573 if(!this.expanded){
17574 if(this.fireEvent("beforeexpand", this, deep, anim) === false){
17577 if(!this.childrenRendered){
17578 this.renderChildren();
17580 this.expanded = true;
17581 if(!this.isHiddenRoot() && (this.getOwnerTree().animate && anim !== false) || anim){
17582 this.ui.animExpand(function(){
17583 this.fireEvent("expand", this);
17584 if(typeof callback == "function"){
17588 this.expandChildNodes(true);
17590 }.createDelegate(this));
17594 this.fireEvent("expand", this);
17595 if(typeof callback == "function"){
17600 if(typeof callback == "function"){
17605 this.expandChildNodes(true);
17609 isHiddenRoot : function(){
17610 return this.isRoot && !this.getOwnerTree().rootVisible;
17614 * Collapse this node.
17615 * @param {Boolean} deep (optional) True to collapse all children as well
17616 * @param {Boolean} anim (optional) false to cancel the default animation
17618 collapse : function(deep, anim){
17619 if(this.expanded && !this.isHiddenRoot()){
17620 if(this.fireEvent("beforecollapse", this, deep, anim) === false){
17623 this.expanded = false;
17624 if((this.getOwnerTree().animate && anim !== false) || anim){
17625 this.ui.animCollapse(function(){
17626 this.fireEvent("collapse", this);
17628 this.collapseChildNodes(true);
17630 }.createDelegate(this));
17633 this.ui.collapse();
17634 this.fireEvent("collapse", this);
17638 var cs = this.childNodes;
17639 for(var i = 0, len = cs.length; i < len; i++) {
17640 cs[i].collapse(true, false);
17646 delayedExpand : function(delay){
17647 if(!this.expandProcId){
17648 this.expandProcId = this.expand.defer(delay, this);
17653 cancelExpand : function(){
17654 if(this.expandProcId){
17655 clearTimeout(this.expandProcId);
17657 this.expandProcId = false;
17661 * Toggles expanded/collapsed state of the node
17663 toggle : function(){
17672 * Ensures all parent nodes are expanded
17674 ensureVisible : function(callback){
17675 var tree = this.getOwnerTree();
17676 tree.expandPath(this.parentNode.getPath(), false, function(){
17677 tree.getTreeEl().scrollChildIntoView(this.ui.anchor);
17678 Roo.callback(callback);
17679 }.createDelegate(this));
17683 * Expand all child nodes
17684 * @param {Boolean} deep (optional) true if the child nodes should also expand their child nodes
17686 expandChildNodes : function(deep){
17687 var cs = this.childNodes;
17688 for(var i = 0, len = cs.length; i < len; i++) {
17689 cs[i].expand(deep);
17694 * Collapse all child nodes
17695 * @param {Boolean} deep (optional) true if the child nodes should also collapse their child nodes
17697 collapseChildNodes : function(deep){
17698 var cs = this.childNodes;
17699 for(var i = 0, len = cs.length; i < len; i++) {
17700 cs[i].collapse(deep);
17705 * Disables this node
17707 disable : function(){
17708 this.disabled = true;
17710 if(this.rendered && this.ui.onDisableChange){ // event without subscribing
17711 this.ui.onDisableChange(this, true);
17713 this.fireEvent("disabledchange", this, true);
17717 * Enables this node
17719 enable : function(){
17720 this.disabled = false;
17721 if(this.rendered && this.ui.onDisableChange){ // event without subscribing
17722 this.ui.onDisableChange(this, false);
17724 this.fireEvent("disabledchange", this, false);
17728 renderChildren : function(suppressEvent){
17729 if(suppressEvent !== false){
17730 this.fireEvent("beforechildrenrendered", this);
17732 var cs = this.childNodes;
17733 for(var i = 0, len = cs.length; i < len; i++){
17734 cs[i].render(true);
17736 this.childrenRendered = true;
17740 sort : function(fn, scope){
17741 Roo.tree.TreeNode.superclass.sort.apply(this, arguments);
17742 if(this.childrenRendered){
17743 var cs = this.childNodes;
17744 for(var i = 0, len = cs.length; i < len; i++){
17745 cs[i].render(true);
17751 render : function(bulkRender){
17752 this.ui.render(bulkRender);
17753 if(!this.rendered){
17754 this.rendered = true;
17756 this.expanded = false;
17757 this.expand(false, false);
17763 renderIndent : function(deep, refresh){
17765 this.ui.childIndent = null;
17767 this.ui.renderIndent();
17768 if(deep === true && this.childrenRendered){
17769 var cs = this.childNodes;
17770 for(var i = 0, len = cs.length; i < len; i++){
17771 cs[i].renderIndent(true, refresh);
17777 * Ext JS Library 1.1.1
17778 * Copyright(c) 2006-2007, Ext JS, LLC.
17780 * Originally Released Under LGPL - original licence link has changed is not relivant.
17783 * <script type="text/javascript">
17787 * @class Roo.tree.AsyncTreeNode
17788 * @extends Roo.tree.TreeNode
17789 * @cfg {TreeLoader} loader A TreeLoader to be used by this node (defaults to the loader defined on the tree)
17791 * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node
17793 Roo.tree.AsyncTreeNode = function(config){
17794 this.loaded = false;
17795 this.loading = false;
17796 Roo.tree.AsyncTreeNode.superclass.constructor.apply(this, arguments);
17798 * @event beforeload
17799 * Fires before this node is loaded, return false to cancel
17800 * @param {Node} this This node
17802 this.addEvents({'beforeload':true, 'load': true});
17805 * Fires when this node is loaded
17806 * @param {Node} this This node
17809 * The loader used by this node (defaults to using the tree's defined loader)
17814 Roo.extend(Roo.tree.AsyncTreeNode, Roo.tree.TreeNode, {
17815 expand : function(deep, anim, callback){
17816 if(this.loading){ // if an async load is already running, waiting til it's done
17818 var f = function(){
17819 if(!this.loading){ // done loading
17820 clearInterval(timer);
17821 this.expand(deep, anim, callback);
17823 }.createDelegate(this);
17824 timer = setInterval(f, 200);
17828 if(this.fireEvent("beforeload", this) === false){
17831 this.loading = true;
17832 this.ui.beforeLoad(this);
17833 var loader = this.loader || this.attributes.loader || this.getOwnerTree().getLoader();
17835 loader.load(this, this.loadComplete.createDelegate(this, [deep, anim, callback]));
17839 Roo.tree.AsyncTreeNode.superclass.expand.call(this, deep, anim, callback);
17843 * Returns true if this node is currently loading
17844 * @return {Boolean}
17846 isLoading : function(){
17847 return this.loading;
17850 loadComplete : function(deep, anim, callback){
17851 this.loading = false;
17852 this.loaded = true;
17853 this.ui.afterLoad(this);
17854 this.fireEvent("load", this);
17855 this.expand(deep, anim, callback);
17859 * Returns true if this node has been loaded
17860 * @return {Boolean}
17862 isLoaded : function(){
17863 return this.loaded;
17866 hasChildNodes : function(){
17867 if(!this.isLeaf() && !this.loaded){
17870 return Roo.tree.AsyncTreeNode.superclass.hasChildNodes.call(this);
17875 * Trigger a reload for this node
17876 * @param {Function} callback
17878 reload : function(callback){
17879 this.collapse(false, false);
17880 while(this.firstChild){
17881 this.removeChild(this.firstChild);
17883 this.childrenRendered = false;
17884 this.loaded = false;
17885 if(this.isHiddenRoot()){
17886 this.expanded = false;
17888 this.expand(false, false, callback);
17892 * Ext JS Library 1.1.1
17893 * Copyright(c) 2006-2007, Ext JS, LLC.
17895 * Originally Released Under LGPL - original licence link has changed is not relivant.
17898 * <script type="text/javascript">
17902 * @class Roo.tree.TreeNodeUI
17904 * @param {Object} node The node to render
17905 * The TreeNode UI implementation is separate from the
17906 * tree implementation. Unless you are customizing the tree UI,
17907 * you should never have to use this directly.
17909 Roo.tree.TreeNodeUI = function(node){
17911 this.rendered = false;
17912 this.animating = false;
17913 this.emptyIcon = Roo.BLANK_IMAGE_URL;
17916 Roo.tree.TreeNodeUI.prototype = {
17917 removeChild : function(node){
17919 this.ctNode.removeChild(node.ui.getEl());
17923 beforeLoad : function(){
17924 this.addClass("x-tree-node-loading");
17927 afterLoad : function(){
17928 this.removeClass("x-tree-node-loading");
17931 onTextChange : function(node, text, oldText){
17933 this.textNode.innerHTML = text;
17937 onDisableChange : function(node, state){
17938 this.disabled = state;
17940 this.addClass("x-tree-node-disabled");
17942 this.removeClass("x-tree-node-disabled");
17946 onSelectedChange : function(state){
17949 this.addClass("x-tree-selected");
17952 this.removeClass("x-tree-selected");
17956 onMove : function(tree, node, oldParent, newParent, index, refNode){
17957 this.childIndent = null;
17959 var targetNode = newParent.ui.getContainer();
17960 if(!targetNode){//target not rendered
17961 this.holder = document.createElement("div");
17962 this.holder.appendChild(this.wrap);
17965 var insertBefore = refNode ? refNode.ui.getEl() : null;
17967 targetNode.insertBefore(this.wrap, insertBefore);
17969 targetNode.appendChild(this.wrap);
17971 this.node.renderIndent(true);
17975 addClass : function(cls){
17977 Roo.fly(this.elNode).addClass(cls);
17981 removeClass : function(cls){
17983 Roo.fly(this.elNode).removeClass(cls);
17987 remove : function(){
17989 this.holder = document.createElement("div");
17990 this.holder.appendChild(this.wrap);
17994 fireEvent : function(){
17995 return this.node.fireEvent.apply(this.node, arguments);
17998 initEvents : function(){
17999 this.node.on("move", this.onMove, this);
18000 var E = Roo.EventManager;
18001 var a = this.anchor;
18003 var el = Roo.fly(a, '_treeui');
18005 if(Roo.isOpera){ // opera render bug ignores the CSS
18006 el.setStyle("text-decoration", "none");
18009 el.on("click", this.onClick, this);
18010 el.on("dblclick", this.onDblClick, this);
18013 Roo.EventManager.on(this.checkbox,
18014 Roo.isIE ? 'click' : 'change', this.onCheckChange, this);
18017 el.on("contextmenu", this.onContextMenu, this);
18019 var icon = Roo.fly(this.iconNode);
18020 icon.on("click", this.onClick, this);
18021 icon.on("dblclick", this.onDblClick, this);
18022 icon.on("contextmenu", this.onContextMenu, this);
18023 E.on(this.ecNode, "click", this.ecClick, this, true);
18025 if(this.node.disabled){
18026 this.addClass("x-tree-node-disabled");
18028 if(this.node.hidden){
18029 this.addClass("x-tree-node-disabled");
18031 var ot = this.node.getOwnerTree();
18032 var dd = ot.enableDD || ot.enableDrag || ot.enableDrop;
18033 if(dd && (!this.node.isRoot || ot.rootVisible)){
18034 Roo.dd.Registry.register(this.elNode, {
18036 handles: this.getDDHandles(),
18042 getDDHandles : function(){
18043 return [this.iconNode, this.textNode];
18048 this.wrap.style.display = "none";
18054 this.wrap.style.display = "";
18058 onContextMenu : function(e){
18059 if (this.node.hasListener("contextmenu") || this.node.getOwnerTree().hasListener("contextmenu")) {
18060 e.preventDefault();
18062 this.fireEvent("contextmenu", this.node, e);
18066 onClick : function(e){
18071 if(this.fireEvent("beforeclick", this.node, e) !== false){
18072 if(!this.disabled && this.node.attributes.href){
18073 this.fireEvent("click", this.node, e);
18076 e.preventDefault();
18081 if(this.node.attributes.singleClickExpand && !this.animating && this.node.hasChildNodes()){
18082 this.node.toggle();
18085 this.fireEvent("click", this.node, e);
18091 onDblClick : function(e){
18092 e.preventDefault();
18097 this.toggleCheck();
18099 if(!this.animating && this.node.hasChildNodes()){
18100 this.node.toggle();
18102 this.fireEvent("dblclick", this.node, e);
18105 onCheckChange : function(){
18106 var checked = this.checkbox.checked;
18107 this.node.attributes.checked = checked;
18108 this.fireEvent('checkchange', this.node, checked);
18111 ecClick : function(e){
18112 if(!this.animating && this.node.hasChildNodes()){
18113 this.node.toggle();
18117 startDrop : function(){
18118 this.dropping = true;
18121 // delayed drop so the click event doesn't get fired on a drop
18122 endDrop : function(){
18123 setTimeout(function(){
18124 this.dropping = false;
18125 }.createDelegate(this), 50);
18128 expand : function(){
18129 this.updateExpandIcon();
18130 this.ctNode.style.display = "";
18133 focus : function(){
18134 if(!this.node.preventHScroll){
18135 try{this.anchor.focus();
18137 }else if(!Roo.isIE){
18139 var noscroll = this.node.getOwnerTree().getTreeEl().dom;
18140 var l = noscroll.scrollLeft;
18141 this.anchor.focus();
18142 noscroll.scrollLeft = l;
18147 toggleCheck : function(value){
18148 var cb = this.checkbox;
18150 cb.checked = (value === undefined ? !cb.checked : value);
18156 this.anchor.blur();
18160 animExpand : function(callback){
18161 var ct = Roo.get(this.ctNode);
18163 if(!this.node.hasChildNodes()){
18164 this.updateExpandIcon();
18165 this.ctNode.style.display = "";
18166 Roo.callback(callback);
18169 this.animating = true;
18170 this.updateExpandIcon();
18173 callback : function(){
18174 this.animating = false;
18175 Roo.callback(callback);
18178 duration: this.node.ownerTree.duration || .25
18182 highlight : function(){
18183 var tree = this.node.getOwnerTree();
18184 Roo.fly(this.wrap).highlight(
18185 tree.hlColor || "C3DAF9",
18186 {endColor: tree.hlBaseColor}
18190 collapse : function(){
18191 this.updateExpandIcon();
18192 this.ctNode.style.display = "none";
18195 animCollapse : function(callback){
18196 var ct = Roo.get(this.ctNode);
18197 ct.enableDisplayMode('block');
18200 this.animating = true;
18201 this.updateExpandIcon();
18204 callback : function(){
18205 this.animating = false;
18206 Roo.callback(callback);
18209 duration: this.node.ownerTree.duration || .25
18213 getContainer : function(){
18214 return this.ctNode;
18217 getEl : function(){
18221 appendDDGhost : function(ghostNode){
18222 ghostNode.appendChild(this.elNode.cloneNode(true));
18225 getDDRepairXY : function(){
18226 return Roo.lib.Dom.getXY(this.iconNode);
18229 onRender : function(){
18233 render : function(bulkRender){
18234 var n = this.node, a = n.attributes;
18235 var targetNode = n.parentNode ?
18236 n.parentNode.ui.getContainer() : n.ownerTree.innerCt.dom;
18238 if(!this.rendered){
18239 this.rendered = true;
18241 this.renderElements(n, a, targetNode, bulkRender);
18244 if(this.textNode.setAttributeNS){
18245 this.textNode.setAttributeNS("ext", "qtip", a.qtip);
18247 this.textNode.setAttributeNS("ext", "qtitle", a.qtipTitle);
18250 this.textNode.setAttribute("ext:qtip", a.qtip);
18252 this.textNode.setAttribute("ext:qtitle", a.qtipTitle);
18255 }else if(a.qtipCfg){
18256 a.qtipCfg.target = Roo.id(this.textNode);
18257 Roo.QuickTips.register(a.qtipCfg);
18260 if(!this.node.expanded){
18261 this.updateExpandIcon();
18264 if(bulkRender === true) {
18265 targetNode.appendChild(this.wrap);
18270 renderElements : function(n, a, targetNode, bulkRender)
18272 // add some indent caching, this helps performance when rendering a large tree
18273 this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
18274 var t = n.getOwnerTree();
18275 var txt = t.renderer ? t.renderer(n.attributes) : Roo.util.Format.htmlEncode(n.text);
18276 if (typeof(n.attributes.html) != 'undefined') {
18277 txt = n.attributes.html;
18279 var tip = t.rendererTip ? t.rendererTip(n.attributes) : txt;
18280 var cb = typeof a.checked == 'boolean';
18281 var href = a.href ? a.href : Roo.isGecko ? "" : "#";
18282 var buf = ['<li class="x-tree-node"><div class="x-tree-node-el ', a.cls,'">',
18283 '<span class="x-tree-node-indent">',this.indentMarkup,"</span>",
18284 '<img src="', this.emptyIcon, '" class="x-tree-ec-icon" />',
18285 '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',(a.icon ? " x-tree-node-inline-icon" : ""),(a.iconCls ? " "+a.iconCls : ""),'" unselectable="on" />',
18286 cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + (a.checked ? 'checked="checked" />' : ' />')) : '',
18287 '<a hidefocus="on" href="',href,'" tabIndex="1" ',
18288 a.hrefTarget ? ' target="'+a.hrefTarget+'"' : "",
18289 '><span unselectable="on" qtip="' , tip ,'">',txt,"</span></a></div>",
18290 '<ul class="x-tree-node-ct" style="display:none;"></ul>',
18293 if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
18294 this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
18295 n.nextSibling.ui.getEl(), buf.join(""));
18297 this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
18300 this.elNode = this.wrap.childNodes[0];
18301 this.ctNode = this.wrap.childNodes[1];
18302 var cs = this.elNode.childNodes;
18303 this.indentNode = cs[0];
18304 this.ecNode = cs[1];
18305 this.iconNode = cs[2];
18308 this.checkbox = cs[3];
18311 this.anchor = cs[index];
18312 this.textNode = cs[index].firstChild;
18315 getAnchor : function(){
18316 return this.anchor;
18319 getTextEl : function(){
18320 return this.textNode;
18323 getIconEl : function(){
18324 return this.iconNode;
18327 isChecked : function(){
18328 return this.checkbox ? this.checkbox.checked : false;
18331 updateExpandIcon : function(){
18333 var n = this.node, c1, c2;
18334 var cls = n.isLast() ? "x-tree-elbow-end" : "x-tree-elbow";
18335 var hasChild = n.hasChildNodes();
18339 c1 = "x-tree-node-collapsed";
18340 c2 = "x-tree-node-expanded";
18343 c1 = "x-tree-node-expanded";
18344 c2 = "x-tree-node-collapsed";
18347 this.removeClass("x-tree-node-leaf");
18348 this.wasLeaf = false;
18350 if(this.c1 != c1 || this.c2 != c2){
18351 Roo.fly(this.elNode).replaceClass(c1, c2);
18352 this.c1 = c1; this.c2 = c2;
18355 // this changes non-leafs into leafs if they have no children.
18356 // it's not very rational behaviour..
18358 if(!this.wasLeaf && this.node.leaf){
18359 Roo.fly(this.elNode).replaceClass("x-tree-node-expanded", "x-tree-node-leaf");
18362 this.wasLeaf = true;
18365 var ecc = "x-tree-ec-icon "+cls;
18366 if(this.ecc != ecc){
18367 this.ecNode.className = ecc;
18373 getChildIndent : function(){
18374 if(!this.childIndent){
18378 if(!p.isRoot || (p.isRoot && p.ownerTree.rootVisible)){
18380 buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-elbow-line" />');
18382 buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-icon" />');
18387 this.childIndent = buf.join("");
18389 return this.childIndent;
18392 renderIndent : function(){
18395 var p = this.node.parentNode;
18397 indent = p.ui.getChildIndent();
18399 if(this.indentMarkup != indent){ // don't rerender if not required
18400 this.indentNode.innerHTML = indent;
18401 this.indentMarkup = indent;
18403 this.updateExpandIcon();
18408 Roo.tree.RootTreeNodeUI = function(){
18409 Roo.tree.RootTreeNodeUI.superclass.constructor.apply(this, arguments);
18411 Roo.extend(Roo.tree.RootTreeNodeUI, Roo.tree.TreeNodeUI, {
18412 render : function(){
18413 if(!this.rendered){
18414 var targetNode = this.node.ownerTree.innerCt.dom;
18415 this.node.expanded = true;
18416 targetNode.innerHTML = '<div class="x-tree-root-node"></div>';
18417 this.wrap = this.ctNode = targetNode.firstChild;
18420 collapse : function(){
18422 expand : function(){
18426 * Ext JS Library 1.1.1
18427 * Copyright(c) 2006-2007, Ext JS, LLC.
18429 * Originally Released Under LGPL - original licence link has changed is not relivant.
18432 * <script type="text/javascript">
18435 * @class Roo.tree.TreeLoader
18436 * @extends Roo.util.Observable
18437 * A TreeLoader provides for lazy loading of an {@link Roo.tree.TreeNode}'s child
18438 * nodes from a specified URL. The response must be a javascript Array definition
18439 * who's elements are node definition objects. eg:
18444 { 'id': 1, 'text': 'A folder Node', 'leaf': false },
18445 { 'id': 2, 'text': 'A leaf Node', 'leaf': true }
18452 * The old style respose with just an array is still supported, but not recommended.
18455 * A server request is sent, and child nodes are loaded only when a node is expanded.
18456 * The loading node's id is passed to the server under the parameter name "node" to
18457 * enable the server to produce the correct child nodes.
18459 * To pass extra parameters, an event handler may be attached to the "beforeload"
18460 * event, and the parameters specified in the TreeLoader's baseParams property:
18462 myTreeLoader.on("beforeload", function(treeLoader, node) {
18463 this.baseParams.category = node.attributes.category;
18466 * This would pass an HTTP parameter called "category" to the server containing
18467 * the value of the Node's "category" attribute.
18469 * Creates a new Treeloader.
18470 * @param {Object} config A config object containing config properties.
18472 Roo.tree.TreeLoader = function(config){
18473 this.baseParams = {};
18474 this.requestMethod = "POST";
18475 Roo.apply(this, config);
18480 * @event beforeload
18481 * Fires before a network request is made to retrieve the Json text which specifies a node's children.
18482 * @param {Object} This TreeLoader object.
18483 * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
18484 * @param {Object} callback The callback function specified in the {@link #load} call.
18489 * Fires when the node has been successfuly loaded.
18490 * @param {Object} This TreeLoader object.
18491 * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
18492 * @param {Object} response The response object containing the data from the server.
18496 * @event loadexception
18497 * Fires if the network request failed.
18498 * @param {Object} This TreeLoader object.
18499 * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
18500 * @param {Object} response The response object containing the data from the server.
18502 loadexception : true,
18505 * Fires before a node is created, enabling you to return custom Node types
18506 * @param {Object} This TreeLoader object.
18507 * @param {Object} attr - the data returned from the AJAX call (modify it to suit)
18512 Roo.tree.TreeLoader.superclass.constructor.call(this);
18515 Roo.extend(Roo.tree.TreeLoader, Roo.util.Observable, {
18517 * @cfg {String} dataUrl The URL from which to request a Json string which
18518 * specifies an array of node definition object representing the child nodes
18522 * @cfg {String} requestMethod either GET or POST
18523 * defaults to POST (due to BC)
18527 * @cfg {Object} baseParams (optional) An object containing properties which
18528 * specify HTTP parameters to be passed to each request for child nodes.
18531 * @cfg {Object} baseAttrs (optional) An object containing attributes to be added to all nodes
18532 * created by this loader. If the attributes sent by the server have an attribute in this object,
18533 * they take priority.
18536 * @cfg {Object} uiProviders (optional) An object containing properties which
18538 * DEPRECATED - use 'create' event handler to modify attributes - which affect creation.
18539 * specify custom {@link Roo.tree.TreeNodeUI} implementations. If the optional
18540 * <i>uiProvider</i> attribute of a returned child node is a string rather
18541 * than a reference to a TreeNodeUI implementation, this that string value
18542 * is used as a property name in the uiProviders object. You can define the provider named
18543 * 'default' , and this will be used for all nodes (if no uiProvider is delivered by the node data)
18548 * @cfg {Boolean} clearOnLoad (optional) Default to true. Remove previously existing
18549 * child nodes before loading.
18551 clearOnLoad : true,
18554 * @cfg {String} root (optional) Default to false. Use this to read data from an object
18555 * property on loading, rather than expecting an array. (eg. more compatible to a standard
18556 * Grid query { data : [ .....] }
18561 * @cfg {String} queryParam (optional)
18562 * Name of the query as it will be passed on the querystring (defaults to 'node')
18563 * eg. the request will be ?node=[id]
18570 * Load an {@link Roo.tree.TreeNode} from the URL specified in the constructor.
18571 * This is called automatically when a node is expanded, but may be used to reload
18572 * a node (or append new children if the {@link #clearOnLoad} option is false.)
18573 * @param {Roo.tree.TreeNode} node
18574 * @param {Function} callback
18576 load : function(node, callback){
18577 if(this.clearOnLoad){
18578 while(node.firstChild){
18579 node.removeChild(node.firstChild);
18582 if(node.attributes.children){ // preloaded json children
18583 var cs = node.attributes.children;
18584 for(var i = 0, len = cs.length; i < len; i++){
18585 node.appendChild(this.createNode(cs[i]));
18587 if(typeof callback == "function"){
18590 }else if(this.dataUrl){
18591 this.requestData(node, callback);
18595 getParams: function(node){
18596 var buf = [], bp = this.baseParams;
18597 for(var key in bp){
18598 if(typeof bp[key] != "function"){
18599 buf.push(encodeURIComponent(key), "=", encodeURIComponent(bp[key]), "&");
18602 var n = this.queryParam === false ? 'node' : this.queryParam;
18603 buf.push(n + "=", encodeURIComponent(node.id));
18604 return buf.join("");
18607 requestData : function(node, callback){
18608 if(this.fireEvent("beforeload", this, node, callback) !== false){
18609 this.transId = Roo.Ajax.request({
18610 method:this.requestMethod,
18611 url: this.dataUrl||this.url,
18612 success: this.handleResponse,
18613 failure: this.handleFailure,
18615 argument: {callback: callback, node: node},
18616 params: this.getParams(node)
18619 // if the load is cancelled, make sure we notify
18620 // the node that we are done
18621 if(typeof callback == "function"){
18627 isLoading : function(){
18628 return this.transId ? true : false;
18631 abort : function(){
18632 if(this.isLoading()){
18633 Roo.Ajax.abort(this.transId);
18638 createNode : function(attr)
18640 // apply baseAttrs, nice idea Corey!
18641 if(this.baseAttrs){
18642 Roo.applyIf(attr, this.baseAttrs);
18644 if(this.applyLoader !== false){
18645 attr.loader = this;
18647 // uiProvider = depreciated..
18649 if(typeof(attr.uiProvider) == 'string'){
18650 attr.uiProvider = this.uiProviders[attr.uiProvider] ||
18651 /** eval:var:attr */ eval(attr.uiProvider);
18653 if(typeof(this.uiProviders['default']) != 'undefined') {
18654 attr.uiProvider = this.uiProviders['default'];
18657 this.fireEvent('create', this, attr);
18659 attr.leaf = typeof(attr.leaf) == 'string' ? attr.leaf * 1 : attr.leaf;
18661 new Roo.tree.TreeNode(attr) :
18662 new Roo.tree.AsyncTreeNode(attr));
18665 processResponse : function(response, node, callback)
18667 var json = response.responseText;
18670 var o = Roo.decode(json);
18672 if (this.root === false && typeof(o.success) != undefined) {
18673 this.root = 'data'; // the default behaviour for list like data..
18676 if (this.root !== false && !o.success) {
18677 // it's a failure condition.
18678 var a = response.argument;
18679 this.fireEvent("loadexception", this, a.node, response);
18680 Roo.log("Load failed - should have a handler really");
18686 if (this.root !== false) {
18690 for(var i = 0, len = o.length; i < len; i++){
18691 var n = this.createNode(o[i]);
18693 node.appendChild(n);
18696 if(typeof callback == "function"){
18697 callback(this, node);
18700 this.handleFailure(response);
18704 handleResponse : function(response){
18705 this.transId = false;
18706 var a = response.argument;
18707 this.processResponse(response, a.node, a.callback);
18708 this.fireEvent("load", this, a.node, response);
18711 handleFailure : function(response)
18713 // should handle failure better..
18714 this.transId = false;
18715 var a = response.argument;
18716 this.fireEvent("loadexception", this, a.node, response);
18717 if(typeof a.callback == "function"){
18718 a.callback(this, a.node);
18723 * Ext JS Library 1.1.1
18724 * Copyright(c) 2006-2007, Ext JS, LLC.
18726 * Originally Released Under LGPL - original licence link has changed is not relivant.
18729 * <script type="text/javascript">
18733 * @class Roo.tree.TreeFilter
18734 * Note this class is experimental and doesn't update the indent (lines) or expand collapse icons of the nodes
18735 * @param {TreePanel} tree
18736 * @param {Object} config (optional)
18738 Roo.tree.TreeFilter = function(tree, config){
18740 this.filtered = {};
18741 Roo.apply(this, config);
18744 Roo.tree.TreeFilter.prototype = {
18751 * Filter the data by a specific attribute.
18752 * @param {String/RegExp} value Either string that the attribute value
18753 * should start with or a RegExp to test against the attribute
18754 * @param {String} attr (optional) The attribute passed in your node's attributes collection. Defaults to "text".
18755 * @param {TreeNode} startNode (optional) The node to start the filter at.
18757 filter : function(value, attr, startNode){
18758 attr = attr || "text";
18760 if(typeof value == "string"){
18761 var vlen = value.length;
18762 // auto clear empty filter
18763 if(vlen == 0 && this.clearBlank){
18767 value = value.toLowerCase();
18769 return n.attributes[attr].substr(0, vlen).toLowerCase() == value;
18771 }else if(value.exec){ // regex?
18773 return value.test(n.attributes[attr]);
18776 throw 'Illegal filter type, must be string or regex';
18778 this.filterBy(f, null, startNode);
18782 * Filter by a function. The passed function will be called with each
18783 * node in the tree (or from the startNode). If the function returns true, the node is kept
18784 * otherwise it is filtered. If a node is filtered, its children are also filtered.
18785 * @param {Function} fn The filter function
18786 * @param {Object} scope (optional) The scope of the function (defaults to the current node)
18788 filterBy : function(fn, scope, startNode){
18789 startNode = startNode || this.tree.root;
18790 if(this.autoClear){
18793 var af = this.filtered, rv = this.reverse;
18794 var f = function(n){
18795 if(n == startNode){
18801 var m = fn.call(scope || n, n);
18809 startNode.cascade(f);
18812 if(typeof id != "function"){
18814 if(n && n.parentNode){
18815 n.parentNode.removeChild(n);
18823 * Clears the current filter. Note: with the "remove" option
18824 * set a filter cannot be cleared.
18826 clear : function(){
18828 var af = this.filtered;
18830 if(typeof id != "function"){
18837 this.filtered = {};
18842 * Ext JS Library 1.1.1
18843 * Copyright(c) 2006-2007, Ext JS, LLC.
18845 * Originally Released Under LGPL - original licence link has changed is not relivant.
18848 * <script type="text/javascript">
18853 * @class Roo.tree.TreeSorter
18854 * Provides sorting of nodes in a TreePanel
18856 * @cfg {Boolean} folderSort True to sort leaf nodes under non leaf nodes
18857 * @cfg {String} property The named attribute on the node to sort by (defaults to text)
18858 * @cfg {String} dir The direction to sort (asc or desc) (defaults to asc)
18859 * @cfg {String} leafAttr The attribute used to determine leaf nodes in folder sort (defaults to "leaf")
18860 * @cfg {Boolean} caseSensitive true for case sensitive sort (defaults to false)
18861 * @cfg {Function} sortType A custom "casting" function used to convert node values before sorting
18863 * @param {TreePanel} tree
18864 * @param {Object} config
18866 Roo.tree.TreeSorter = function(tree, config){
18867 Roo.apply(this, config);
18868 tree.on("beforechildrenrendered", this.doSort, this);
18869 tree.on("append", this.updateSort, this);
18870 tree.on("insert", this.updateSort, this);
18872 var dsc = this.dir && this.dir.toLowerCase() == "desc";
18873 var p = this.property || "text";
18874 var sortType = this.sortType;
18875 var fs = this.folderSort;
18876 var cs = this.caseSensitive === true;
18877 var leafAttr = this.leafAttr || 'leaf';
18879 this.sortFn = function(n1, n2){
18881 if(n1.attributes[leafAttr] && !n2.attributes[leafAttr]){
18884 if(!n1.attributes[leafAttr] && n2.attributes[leafAttr]){
18888 var v1 = sortType ? sortType(n1) : (cs ? n1.attributes[p] : n1.attributes[p].toUpperCase());
18889 var v2 = sortType ? sortType(n2) : (cs ? n2.attributes[p] : n2.attributes[p].toUpperCase());
18891 return dsc ? +1 : -1;
18893 return dsc ? -1 : +1;
18900 Roo.tree.TreeSorter.prototype = {
18901 doSort : function(node){
18902 node.sort(this.sortFn);
18905 compareNodes : function(n1, n2){
18906 return (n1.text.toUpperCase() > n2.text.toUpperCase() ? 1 : -1);
18909 updateSort : function(tree, node){
18910 if(node.childrenRendered){
18911 this.doSort.defer(1, this, [node]);
18916 * Ext JS Library 1.1.1
18917 * Copyright(c) 2006-2007, Ext JS, LLC.
18919 * Originally Released Under LGPL - original licence link has changed is not relivant.
18922 * <script type="text/javascript">
18925 if(Roo.dd.DropZone){
18927 Roo.tree.TreeDropZone = function(tree, config){
18928 this.allowParentInsert = false;
18929 this.allowContainerDrop = false;
18930 this.appendOnly = false;
18931 Roo.tree.TreeDropZone.superclass.constructor.call(this, tree.innerCt, config);
18933 this.lastInsertClass = "x-tree-no-status";
18934 this.dragOverData = {};
18937 Roo.extend(Roo.tree.TreeDropZone, Roo.dd.DropZone, {
18938 ddGroup : "TreeDD",
18940 expandDelay : 1000,
18942 expandNode : function(node){
18943 if(node.hasChildNodes() && !node.isExpanded()){
18944 node.expand(false, null, this.triggerCacheRefresh.createDelegate(this));
18948 queueExpand : function(node){
18949 this.expandProcId = this.expandNode.defer(this.expandDelay, this, [node]);
18952 cancelExpand : function(){
18953 if(this.expandProcId){
18954 clearTimeout(this.expandProcId);
18955 this.expandProcId = false;
18959 isValidDropPoint : function(n, pt, dd, e, data){
18960 if(!n || !data){ return false; }
18961 var targetNode = n.node;
18962 var dropNode = data.node;
18963 // default drop rules
18964 if(!(targetNode && targetNode.isTarget && pt)){
18967 if(pt == "append" && targetNode.allowChildren === false){
18970 if((pt == "above" || pt == "below") && (targetNode.parentNode && targetNode.parentNode.allowChildren === false)){
18973 if(dropNode && (targetNode == dropNode || dropNode.contains(targetNode))){
18976 // reuse the object
18977 var overEvent = this.dragOverData;
18978 overEvent.tree = this.tree;
18979 overEvent.target = targetNode;
18980 overEvent.data = data;
18981 overEvent.point = pt;
18982 overEvent.source = dd;
18983 overEvent.rawEvent = e;
18984 overEvent.dropNode = dropNode;
18985 overEvent.cancel = false;
18986 var result = this.tree.fireEvent("nodedragover", overEvent);
18987 return overEvent.cancel === false && result !== false;
18990 getDropPoint : function(e, n, dd)
18994 return tn.allowChildren !== false ? "append" : false; // always append for root
18996 var dragEl = n.ddel;
18997 var t = Roo.lib.Dom.getY(dragEl), b = t + dragEl.offsetHeight;
18998 var y = Roo.lib.Event.getPageY(e);
18999 //var noAppend = tn.allowChildren === false || tn.isLeaf();
19001 // we may drop nodes anywhere, as long as allowChildren has not been set to false..
19002 var noAppend = tn.allowChildren === false;
19003 if(this.appendOnly || tn.parentNode.allowChildren === false){
19004 return noAppend ? false : "append";
19006 var noBelow = false;
19007 if(!this.allowParentInsert){
19008 noBelow = tn.hasChildNodes() && tn.isExpanded();
19010 var q = (b - t) / (noAppend ? 2 : 3);
19011 if(y >= t && y < (t + q)){
19013 }else if(!noBelow && (noAppend || y >= b-q && y <= b)){
19020 onNodeEnter : function(n, dd, e, data)
19022 this.cancelExpand();
19025 onNodeOver : function(n, dd, e, data)
19028 var pt = this.getDropPoint(e, n, dd);
19031 // auto node expand check
19032 if(!this.expandProcId && pt == "append" && node.hasChildNodes() && !n.node.isExpanded()){
19033 this.queueExpand(node);
19034 }else if(pt != "append"){
19035 this.cancelExpand();
19038 // set the insert point style on the target node
19039 var returnCls = this.dropNotAllowed;
19040 if(this.isValidDropPoint(n, pt, dd, e, data)){
19045 returnCls = n.node.isFirst() ? "x-tree-drop-ok-above" : "x-tree-drop-ok-between";
19046 cls = "x-tree-drag-insert-above";
19047 }else if(pt == "below"){
19048 returnCls = n.node.isLast() ? "x-tree-drop-ok-below" : "x-tree-drop-ok-between";
19049 cls = "x-tree-drag-insert-below";
19051 returnCls = "x-tree-drop-ok-append";
19052 cls = "x-tree-drag-append";
19054 if(this.lastInsertClass != cls){
19055 Roo.fly(el).replaceClass(this.lastInsertClass, cls);
19056 this.lastInsertClass = cls;
19063 onNodeOut : function(n, dd, e, data){
19065 this.cancelExpand();
19066 this.removeDropIndicators(n);
19069 onNodeDrop : function(n, dd, e, data){
19070 var point = this.getDropPoint(e, n, dd);
19071 var targetNode = n.node;
19072 targetNode.ui.startDrop();
19073 if(!this.isValidDropPoint(n, point, dd, e, data)){
19074 targetNode.ui.endDrop();
19077 // first try to find the drop node
19078 var dropNode = data.node || (dd.getTreeNode ? dd.getTreeNode(data, targetNode, point, e) : null);
19081 target: targetNode,
19086 dropNode: dropNode,
19089 var retval = this.tree.fireEvent("beforenodedrop", dropEvent);
19090 if(retval === false || dropEvent.cancel === true || !dropEvent.dropNode){
19091 targetNode.ui.endDrop();
19094 // allow target changing
19095 targetNode = dropEvent.target;
19096 if(point == "append" && !targetNode.isExpanded()){
19097 targetNode.expand(false, null, function(){
19098 this.completeDrop(dropEvent);
19099 }.createDelegate(this));
19101 this.completeDrop(dropEvent);
19106 completeDrop : function(de){
19107 var ns = de.dropNode, p = de.point, t = de.target;
19108 if(!(ns instanceof Array)){
19112 for(var i = 0, len = ns.length; i < len; i++){
19115 t.parentNode.insertBefore(n, t);
19116 }else if(p == "below"){
19117 t.parentNode.insertBefore(n, t.nextSibling);
19123 if(this.tree.hlDrop){
19127 this.tree.fireEvent("nodedrop", de);
19130 afterNodeMoved : function(dd, data, e, targetNode, dropNode){
19131 if(this.tree.hlDrop){
19132 dropNode.ui.focus();
19133 dropNode.ui.highlight();
19135 this.tree.fireEvent("nodedrop", this.tree, targetNode, data, dd, e);
19138 getTree : function(){
19142 removeDropIndicators : function(n){
19145 Roo.fly(el).removeClass([
19146 "x-tree-drag-insert-above",
19147 "x-tree-drag-insert-below",
19148 "x-tree-drag-append"]);
19149 this.lastInsertClass = "_noclass";
19153 beforeDragDrop : function(target, e, id){
19154 this.cancelExpand();
19158 afterRepair : function(data){
19159 if(data && Roo.enableFx){
19160 data.node.ui.highlight();
19170 * Ext JS Library 1.1.1
19171 * Copyright(c) 2006-2007, Ext JS, LLC.
19173 * Originally Released Under LGPL - original licence link has changed is not relivant.
19176 * <script type="text/javascript">
19180 if(Roo.dd.DragZone){
19181 Roo.tree.TreeDragZone = function(tree, config){
19182 Roo.tree.TreeDragZone.superclass.constructor.call(this, tree.getTreeEl(), config);
19186 Roo.extend(Roo.tree.TreeDragZone, Roo.dd.DragZone, {
19187 ddGroup : "TreeDD",
19189 onBeforeDrag : function(data, e){
19191 return n && n.draggable && !n.disabled;
19195 onInitDrag : function(e){
19196 var data = this.dragData;
19197 this.tree.getSelectionModel().select(data.node);
19198 this.proxy.update("");
19199 data.node.ui.appendDDGhost(this.proxy.ghost.dom);
19200 this.tree.fireEvent("startdrag", this.tree, data.node, e);
19203 getRepairXY : function(e, data){
19204 return data.node.ui.getDDRepairXY();
19207 onEndDrag : function(data, e){
19208 this.tree.fireEvent("enddrag", this.tree, data.node, e);
19209 if (this.scroller !== false) {
19210 Roo.log('clear scroller');
19211 window.clearInterval(this.scroller);
19212 this.scroller =false;
19218 onValidDrop : function(dd, e, id){
19219 this.tree.fireEvent("dragdrop", this.tree, this.dragData.node, dd, e);
19223 beforeInvalidDrop : function(e, id){
19224 // this scrolls the original position back into view
19225 var sm = this.tree.getSelectionModel();
19226 sm.clearSelections();
19227 sm.select(this.dragData.node);
19229 autoScroll: function(x, y, h, w) {
19230 Roo.log("drop zone - autoscroll called");
19232 Roo.log(this.scroll ? "scroll=y": "scroll=m" );
19234 // The client height
19235 var clientH = Roo.lib.Dom.getViewWidth();
19237 // The client width
19238 var clientW = Roo.lib.Dom.getViewHeight();
19240 // The amt scrolled down
19241 var st = this.DDM.getScrollTop();
19243 // The amt scrolled right
19244 var sl = this.DDM.getScrollLeft();
19246 // Location of the bottom of the element
19249 // Location of the right of the element
19252 // The distance from the cursor to the bottom of the visible area,
19253 // adjusted so that we don't scroll if the cursor is beyond the
19254 // element drag constraints
19255 var toBot = (clientH + st - y - this.deltaY);
19257 // The distance from the cursor to the right of the visible area
19258 var toRight = (clientW + sl - x - this.deltaX);
19261 // How close to the edge the cursor must be before we scroll
19262 // var thresh = (document.all) ? 100 : 40;
19265 // How many pixels to scroll per autoscroll op. This helps to reduce
19266 // clunky scrolling. IE is more sensitive about this ... it needs this
19267 // value to be higher.
19268 var scrAmt = (document.all) ? 80 : 30;
19270 // Scroll down if we are near the bottom of the visible page and the
19271 // obj extends below the crease
19272 if ( bot > clientH && toBot < thresh ) {
19273 window.scrollTo(sl, st + scrAmt);
19276 // Scroll up if the window is scrolled down and the top of the object
19277 // goes above the top border
19278 if ( y < st && st > 0 && y - st < thresh ) {
19279 window.scrollTo(sl, st - scrAmt);
19282 // Scroll right if the obj is beyond the right border and the cursor is
19283 // near the border.
19284 if ( right > clientW && toRight < thresh ) {
19285 window.scrollTo(sl + scrAmt, st);
19288 // Scroll left if the window has been scrolled to the right and the obj
19289 // extends past the left border
19290 if ( x < sl && sl > 0 && x - sl < thresh ) {
19291 window.scrollTo(sl - scrAmt, st);
19298 * Ext JS Library 1.1.1
19299 * Copyright(c) 2006-2007, Ext JS, LLC.
19301 * Originally Released Under LGPL - original licence link has changed is not relivant.
19304 * <script type="text/javascript">
19307 * @class Roo.tree.TreeEditor
19308 * @extends Roo.Editor
19309 * Provides editor functionality for inline tree node editing. Any valid {@link Roo.form.Field} can be used
19310 * as the editor field.
19312 * @param {Object} config (used to be the tree panel.)
19313 * @param {Object} oldconfig DEPRECIATED Either a prebuilt {@link Roo.form.Field} instance or a Field config object
19315 * @cfg {Roo.tree.TreePanel} tree The tree to bind to.
19316 * @cfg {Roo.form.TextField|Object} field The field configuration
19320 Roo.tree.TreeEditor = function(config, oldconfig) { // was -- (tree, config){
19323 if (oldconfig) { // old style..
19324 field = oldconfig.events ? oldconfig : new Roo.form.TextField(oldconfig);
19327 tree = config.tree;
19328 config.field = config.field || {};
19329 config.field.xtype = 'TextField';
19330 field = Roo.factory(config.field, Roo.form);
19332 config = config || {};
19337 * @event beforenodeedit
19338 * Fires when editing is initiated, but before the value changes. Editing can be canceled by returning
19339 * false from the handler of this event.
19340 * @param {Editor} this
19341 * @param {Roo.tree.Node} node
19343 "beforenodeedit" : true
19347 Roo.tree.TreeEditor.superclass.constructor.call(this, field, config);
19351 tree.on('beforeclick', this.beforeNodeClick, this);
19352 tree.getTreeEl().on('mousedown', this.hide, this);
19353 this.on('complete', this.updateNode, this);
19354 this.on('beforestartedit', this.fitToTree, this);
19355 this.on('startedit', this.bindScroll, this, {delay:10});
19356 this.on('specialkey', this.onSpecialKey, this);
19359 Roo.extend(Roo.tree.TreeEditor, Roo.Editor, {
19361 * @cfg {String} alignment
19362 * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "l-l").
19368 * @cfg {Boolean} hideEl
19369 * True to hide the bound element while the editor is displayed (defaults to false)
19373 * @cfg {String} cls
19374 * CSS class to apply to the editor (defaults to "x-small-editor x-tree-editor")
19376 cls: "x-small-editor x-tree-editor",
19378 * @cfg {Boolean} shim
19379 * True to shim the editor if selects/iframes could be displayed beneath it (defaults to false)
19385 * @cfg {Number} maxWidth
19386 * The maximum width in pixels of the editor field (defaults to 250). Note that if the maxWidth would exceed
19387 * the containing tree element's size, it will be automatically limited for you to the container width, taking
19388 * scroll and client offsets into account prior to each edit.
19395 fitToTree : function(ed, el){
19396 var td = this.tree.getTreeEl().dom, nd = el.dom;
19397 if(td.scrollLeft > nd.offsetLeft){ // ensure the node left point is visible
19398 td.scrollLeft = nd.offsetLeft;
19402 (td.clientWidth > 20 ? td.clientWidth : td.offsetWidth) - Math.max(0, nd.offsetLeft-td.scrollLeft) - /*cushion*/5);
19403 this.setSize(w, '');
19405 return this.fireEvent('beforenodeedit', this, this.editNode);
19410 triggerEdit : function(node){
19411 this.completeEdit();
19412 this.editNode = node;
19413 this.startEdit(node.ui.textNode, node.text);
19417 bindScroll : function(){
19418 this.tree.getTreeEl().on('scroll', this.cancelEdit, this);
19422 beforeNodeClick : function(node, e){
19423 var sinceLast = (this.lastClick ? this.lastClick.getElapsed() : 0);
19424 this.lastClick = new Date();
19425 if(sinceLast > this.editDelay && this.tree.getSelectionModel().isSelected(node)){
19427 this.triggerEdit(node);
19434 updateNode : function(ed, value){
19435 this.tree.getTreeEl().un('scroll', this.cancelEdit, this);
19436 this.editNode.setText(value);
19440 onHide : function(){
19441 Roo.tree.TreeEditor.superclass.onHide.call(this);
19443 this.editNode.ui.focus();
19448 onSpecialKey : function(field, e){
19449 var k = e.getKey();
19453 }else if(k == e.ENTER && !e.hasModifier()){
19455 this.completeEdit();
19458 });//<Script type="text/javascript">
19461 * Ext JS Library 1.1.1
19462 * Copyright(c) 2006-2007, Ext JS, LLC.
19464 * Originally Released Under LGPL - original licence link has changed is not relivant.
19467 * <script type="text/javascript">
19471 * Not documented??? - probably should be...
19474 Roo.tree.ColumnNodeUI = Roo.extend(Roo.tree.TreeNodeUI, {
19475 //focus: Roo.emptyFn, // prevent odd scrolling behavior
19477 renderElements : function(n, a, targetNode, bulkRender){
19478 //consel.log("renderElements?");
19479 this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
19481 var t = n.getOwnerTree();
19482 var tid = Pman.Tab.Document_TypesTree.tree.el.id;
19484 var cols = t.columns;
19485 var bw = t.borderWidth;
19487 var href = a.href ? a.href : Roo.isGecko ? "" : "#";
19488 var cb = typeof a.checked == "boolean";
19489 var tx = String.format('{0}',n.text || (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
19490 var colcls = 'x-t-' + tid + '-c0';
19492 '<li class="x-tree-node">',
19495 '<div class="x-tree-node-el ', a.cls,'">',
19497 '<div class="x-tree-col ', colcls, '" style="width:', c.width-bw, 'px;">',
19500 '<span class="x-tree-node-indent">',this.indentMarkup,'</span>',
19501 '<img src="', this.emptyIcon, '" class="x-tree-ec-icon " />',
19502 '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',
19503 (a.icon ? ' x-tree-node-inline-icon' : ''),
19504 (a.iconCls ? ' '+a.iconCls : ''),
19505 '" unselectable="on" />',
19506 (cb ? ('<input class="x-tree-node-cb" type="checkbox" ' +
19507 (a.checked ? 'checked="checked" />' : ' />')) : ''),
19509 '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
19510 (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>',
19511 '<span unselectable="on" qtip="' + tx + '">',
19515 '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
19516 (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>'
19518 for(var i = 1, len = cols.length; i < len; i++){
19520 colcls = 'x-t-' + tid + '-c' +i;
19521 tx = String.format('{0}', (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
19522 buf.push('<div class="x-tree-col ', colcls, ' ' ,(c.cls?c.cls:''),'" style="width:',c.width-bw,'px;">',
19523 '<div class="x-tree-col-text" qtip="' + tx +'">',tx,"</div>",
19529 '<div class="x-clear"></div></div>',
19530 '<ul class="x-tree-node-ct" style="display:none;"></ul>',
19533 if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
19534 this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
19535 n.nextSibling.ui.getEl(), buf.join(""));
19537 this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
19539 var el = this.wrap.firstChild;
19541 this.elNode = el.firstChild;
19542 this.ranchor = el.childNodes[1];
19543 this.ctNode = this.wrap.childNodes[1];
19544 var cs = el.firstChild.childNodes;
19545 this.indentNode = cs[0];
19546 this.ecNode = cs[1];
19547 this.iconNode = cs[2];
19550 this.checkbox = cs[3];
19553 this.anchor = cs[index];
19555 this.textNode = cs[index].firstChild;
19557 //el.on("click", this.onClick, this);
19558 //el.on("dblclick", this.onDblClick, this);
19561 // console.log(this);
19563 initEvents : function(){
19564 Roo.tree.ColumnNodeUI.superclass.initEvents.call(this);
19567 var a = this.ranchor;
19569 var el = Roo.get(a);
19571 if(Roo.isOpera){ // opera render bug ignores the CSS
19572 el.setStyle("text-decoration", "none");
19575 el.on("click", this.onClick, this);
19576 el.on("dblclick", this.onDblClick, this);
19577 el.on("contextmenu", this.onContextMenu, this);
19581 /*onSelectedChange : function(state){
19584 this.addClass("x-tree-selected");
19587 this.removeClass("x-tree-selected");
19590 addClass : function(cls){
19592 Roo.fly(this.elRow).addClass(cls);
19598 removeClass : function(cls){
19600 Roo.fly(this.elRow).removeClass(cls);
19606 });//<Script type="text/javascript">
19610 * Ext JS Library 1.1.1
19611 * Copyright(c) 2006-2007, Ext JS, LLC.
19613 * Originally Released Under LGPL - original licence link has changed is not relivant.
19616 * <script type="text/javascript">
19621 * @class Roo.tree.ColumnTree
19622 * @extends Roo.data.TreePanel
19623 * @cfg {Object} columns Including width, header, renderer, cls, dataIndex
19624 * @cfg {int} borderWidth compined right/left border allowance
19626 * @param {String/HTMLElement/Element} el The container element
19627 * @param {Object} config
19629 Roo.tree.ColumnTree = function(el, config)
19631 Roo.tree.ColumnTree.superclass.constructor.call(this, el , config);
19635 * Fire this event on a container when it resizes
19636 * @param {int} w Width
19637 * @param {int} h Height
19641 this.on('resize', this.onResize, this);
19644 Roo.extend(Roo.tree.ColumnTree, Roo.tree.TreePanel, {
19648 borderWidth: Roo.isBorderBox ? 0 : 2,
19651 render : function(){
19652 // add the header.....
19654 Roo.tree.ColumnTree.superclass.render.apply(this);
19656 this.el.addClass('x-column-tree');
19658 this.headers = this.el.createChild(
19659 {cls:'x-tree-headers'},this.innerCt.dom);
19661 var cols = this.columns, c;
19662 var totalWidth = 0;
19664 var len = cols.length;
19665 for(var i = 0; i < len; i++){
19667 totalWidth += c.width;
19668 this.headEls.push(this.headers.createChild({
19669 cls:'x-tree-hd ' + (c.cls?c.cls+'-hd':''),
19671 cls:'x-tree-hd-text',
19674 style:'width:'+(c.width-this.borderWidth)+'px;'
19677 this.headers.createChild({cls:'x-clear'});
19678 // prevent floats from wrapping when clipped
19679 this.headers.setWidth(totalWidth);
19680 //this.innerCt.setWidth(totalWidth);
19681 this.innerCt.setStyle({ overflow: 'auto' });
19682 this.onResize(this.width, this.height);
19686 onResize : function(w,h)
19691 this.innerCt.setWidth(this.width);
19692 this.innerCt.setHeight(this.height-20);
19695 var cols = this.columns, c;
19696 var totalWidth = 0;
19698 var len = cols.length;
19699 for(var i = 0; i < len; i++){
19701 if (this.autoExpandColumn !== false && c.dataIndex == this.autoExpandColumn) {
19702 // it's the expander..
19703 expEl = this.headEls[i];
19706 totalWidth += c.width;
19710 expEl.setWidth( ((w - totalWidth)-this.borderWidth - 20));
19712 this.headers.setWidth(w-20);
19721 * Ext JS Library 1.1.1
19722 * Copyright(c) 2006-2007, Ext JS, LLC.
19724 * Originally Released Under LGPL - original licence link has changed is not relivant.
19727 * <script type="text/javascript">
19731 * @class Roo.menu.Menu
19732 * @extends Roo.util.Observable
19733 * A menu object. This is the container to which you add all other menu items. Menu can also serve a as a base class
19734 * when you want a specialzed menu based off of another component (like {@link Roo.menu.DateMenu} for example).
19736 * Creates a new Menu
19737 * @param {Object} config Configuration options
19739 Roo.menu.Menu = function(config){
19740 Roo.apply(this, config);
19741 this.id = this.id || Roo.id();
19744 * @event beforeshow
19745 * Fires before this menu is displayed
19746 * @param {Roo.menu.Menu} this
19750 * @event beforehide
19751 * Fires before this menu is hidden
19752 * @param {Roo.menu.Menu} this
19757 * Fires after this menu is displayed
19758 * @param {Roo.menu.Menu} this
19763 * Fires after this menu is hidden
19764 * @param {Roo.menu.Menu} this
19769 * Fires when this menu is clicked (or when the enter key is pressed while it is active)
19770 * @param {Roo.menu.Menu} this
19771 * @param {Roo.menu.Item} menuItem The menu item that was clicked
19772 * @param {Roo.EventObject} e
19777 * Fires when the mouse is hovering over this menu
19778 * @param {Roo.menu.Menu} this
19779 * @param {Roo.EventObject} e
19780 * @param {Roo.menu.Item} menuItem The menu item that was clicked
19785 * Fires when the mouse exits this menu
19786 * @param {Roo.menu.Menu} this
19787 * @param {Roo.EventObject} e
19788 * @param {Roo.menu.Item} menuItem The menu item that was clicked
19793 * Fires when a menu item contained in this menu is clicked
19794 * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
19795 * @param {Roo.EventObject} e
19799 if (this.registerMenu) {
19800 Roo.menu.MenuMgr.register(this);
19803 var mis = this.items;
19804 this.items = new Roo.util.MixedCollection();
19806 this.add.apply(this, mis);
19810 Roo.extend(Roo.menu.Menu, Roo.util.Observable, {
19812 * @cfg {Number} minWidth The minimum width of the menu in pixels (defaults to 120)
19816 * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop"
19817 * for bottom-right shadow (defaults to "sides")
19821 * @cfg {String} subMenuAlign The {@link Roo.Element#alignTo} anchor position value to use for submenus of
19822 * this menu (defaults to "tl-tr?")
19824 subMenuAlign : "tl-tr?",
19826 * @cfg {String} defaultAlign The default {@link Roo.Element#alignTo) anchor position value for this menu
19827 * relative to its element of origin (defaults to "tl-bl?")
19829 defaultAlign : "tl-bl?",
19831 * @cfg {Boolean} allowOtherMenus True to allow multiple menus to be displayed at the same time (defaults to false)
19833 allowOtherMenus : false,
19835 * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
19837 registerMenu : true,
19842 render : function(){
19846 var el = this.el = new Roo.Layer({
19848 shadow:this.shadow,
19850 parentEl: this.parentEl || document.body,
19854 this.keyNav = new Roo.menu.MenuNav(this);
19857 el.addClass("x-menu-plain");
19860 el.addClass(this.cls);
19862 // generic focus element
19863 this.focusEl = el.createChild({
19864 tag: "a", cls: "x-menu-focus", href: "#", onclick: "return false;", tabIndex:"-1"
19866 var ul = el.createChild({tag: "ul", cls: "x-menu-list"});
19867 ul.on("click", this.onClick, this);
19868 ul.on("mouseover", this.onMouseOver, this);
19869 ul.on("mouseout", this.onMouseOut, this);
19870 this.items.each(function(item){
19871 var li = document.createElement("li");
19872 li.className = "x-menu-list-item";
19873 ul.dom.appendChild(li);
19874 item.render(li, this);
19881 autoWidth : function(){
19882 var el = this.el, ul = this.ul;
19886 var w = this.width;
19889 }else if(Roo.isIE){
19890 el.setWidth(this.minWidth);
19891 var t = el.dom.offsetWidth; // force recalc
19892 el.setWidth(ul.getWidth()+el.getFrameWidth("lr"));
19897 delayAutoWidth : function(){
19900 this.awTask = new Roo.util.DelayedTask(this.autoWidth, this);
19902 this.awTask.delay(20);
19907 findTargetItem : function(e){
19908 var t = e.getTarget(".x-menu-list-item", this.ul, true);
19909 if(t && t.menuItemId){
19910 return this.items.get(t.menuItemId);
19915 onClick : function(e){
19917 if(t = this.findTargetItem(e)){
19919 this.fireEvent("click", this, t, e);
19924 setActiveItem : function(item, autoExpand){
19925 if(item != this.activeItem){
19926 if(this.activeItem){
19927 this.activeItem.deactivate();
19929 this.activeItem = item;
19930 item.activate(autoExpand);
19931 }else if(autoExpand){
19937 tryActivate : function(start, step){
19938 var items = this.items;
19939 for(var i = start, len = items.length; i >= 0 && i < len; i+= step){
19940 var item = items.get(i);
19941 if(!item.disabled && item.canActivate){
19942 this.setActiveItem(item, false);
19950 onMouseOver : function(e){
19952 if(t = this.findTargetItem(e)){
19953 if(t.canActivate && !t.disabled){
19954 this.setActiveItem(t, true);
19957 this.fireEvent("mouseover", this, e, t);
19961 onMouseOut : function(e){
19963 if(t = this.findTargetItem(e)){
19964 if(t == this.activeItem && t.shouldDeactivate(e)){
19965 this.activeItem.deactivate();
19966 delete this.activeItem;
19969 this.fireEvent("mouseout", this, e, t);
19973 * Read-only. Returns true if the menu is currently displayed, else false.
19976 isVisible : function(){
19977 return this.el && !this.hidden;
19981 * Displays this menu relative to another element
19982 * @param {String/HTMLElement/Roo.Element} element The element to align to
19983 * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
19984 * the element (defaults to this.defaultAlign)
19985 * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
19987 show : function(el, pos, parentMenu){
19988 this.parentMenu = parentMenu;
19992 this.fireEvent("beforeshow", this);
19993 this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
19997 * Displays this menu at a specific xy position
19998 * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
19999 * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
20001 showAt : function(xy, parentMenu, /* private: */_e){
20002 this.parentMenu = parentMenu;
20007 this.fireEvent("beforeshow", this);
20008 xy = this.el.adjustForConstraints(xy);
20012 this.hidden = false;
20014 this.fireEvent("show", this);
20017 focus : function(){
20019 this.doFocus.defer(50, this);
20023 doFocus : function(){
20025 this.focusEl.focus();
20030 * Hides this menu and optionally all parent menus
20031 * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
20033 hide : function(deep){
20034 if(this.el && this.isVisible()){
20035 this.fireEvent("beforehide", this);
20036 if(this.activeItem){
20037 this.activeItem.deactivate();
20038 this.activeItem = null;
20041 this.hidden = true;
20042 this.fireEvent("hide", this);
20044 if(deep === true && this.parentMenu){
20045 this.parentMenu.hide(true);
20050 * Addds one or more items of any type supported by the Menu class, or that can be converted into menu items.
20051 * Any of the following are valid:
20053 * <li>Any menu item object based on {@link Roo.menu.Item}</li>
20054 * <li>An HTMLElement object which will be converted to a menu item</li>
20055 * <li>A menu item config object that will be created as a new menu item</li>
20056 * <li>A string, which can either be '-' or 'separator' to add a menu separator, otherwise
20057 * it will be converted into a {@link Roo.menu.TextItem} and added</li>
20062 var menu = new Roo.menu.Menu();
20064 // Create a menu item to add by reference
20065 var menuItem = new Roo.menu.Item({ text: 'New Item!' });
20067 // Add a bunch of items at once using different methods.
20068 // Only the last item added will be returned.
20069 var item = menu.add(
20070 menuItem, // add existing item by ref
20071 'Dynamic Item', // new TextItem
20072 '-', // new separator
20073 { text: 'Config Item' } // new item by config
20076 * @param {Mixed} args One or more menu items, menu item configs or other objects that can be converted to menu items
20077 * @return {Roo.menu.Item} The menu item that was added, or the last one if multiple items were added
20080 var a = arguments, l = a.length, item;
20081 for(var i = 0; i < l; i++){
20083 if ((typeof(el) == "object") && el.xtype && el.xns) {
20084 el = Roo.factory(el, Roo.menu);
20087 if(el.render){ // some kind of Item
20088 item = this.addItem(el);
20089 }else if(typeof el == "string"){ // string
20090 if(el == "separator" || el == "-"){
20091 item = this.addSeparator();
20093 item = this.addText(el);
20095 }else if(el.tagName || el.el){ // element
20096 item = this.addElement(el);
20097 }else if(typeof el == "object"){ // must be menu item config?
20098 item = this.addMenuItem(el);
20105 * Returns this menu's underlying {@link Roo.Element} object
20106 * @return {Roo.Element} The element
20108 getEl : function(){
20116 * Adds a separator bar to the menu
20117 * @return {Roo.menu.Item} The menu item that was added
20119 addSeparator : function(){
20120 return this.addItem(new Roo.menu.Separator());
20124 * Adds an {@link Roo.Element} object to the menu
20125 * @param {String/HTMLElement/Roo.Element} el The element or DOM node to add, or its id
20126 * @return {Roo.menu.Item} The menu item that was added
20128 addElement : function(el){
20129 return this.addItem(new Roo.menu.BaseItem(el));
20133 * Adds an existing object based on {@link Roo.menu.Item} to the menu
20134 * @param {Roo.menu.Item} item The menu item to add
20135 * @return {Roo.menu.Item} The menu item that was added
20137 addItem : function(item){
20138 this.items.add(item);
20140 var li = document.createElement("li");
20141 li.className = "x-menu-list-item";
20142 this.ul.dom.appendChild(li);
20143 item.render(li, this);
20144 this.delayAutoWidth();
20150 * Creates a new {@link Roo.menu.Item} based an the supplied config object and adds it to the menu
20151 * @param {Object} config A MenuItem config object
20152 * @return {Roo.menu.Item} The menu item that was added
20154 addMenuItem : function(config){
20155 if(!(config instanceof Roo.menu.Item)){
20156 if(typeof config.checked == "boolean"){ // must be check menu item config?
20157 config = new Roo.menu.CheckItem(config);
20159 config = new Roo.menu.Item(config);
20162 return this.addItem(config);
20166 * Creates a new {@link Roo.menu.TextItem} with the supplied text and adds it to the menu
20167 * @param {String} text The text to display in the menu item
20168 * @return {Roo.menu.Item} The menu item that was added
20170 addText : function(text){
20171 return this.addItem(new Roo.menu.TextItem({ text : text }));
20175 * Inserts an existing object based on {@link Roo.menu.Item} to the menu at a specified index
20176 * @param {Number} index The index in the menu's list of current items where the new item should be inserted
20177 * @param {Roo.menu.Item} item The menu item to add
20178 * @return {Roo.menu.Item} The menu item that was added
20180 insert : function(index, item){
20181 this.items.insert(index, item);
20183 var li = document.createElement("li");
20184 li.className = "x-menu-list-item";
20185 this.ul.dom.insertBefore(li, this.ul.dom.childNodes[index]);
20186 item.render(li, this);
20187 this.delayAutoWidth();
20193 * Removes an {@link Roo.menu.Item} from the menu and destroys the object
20194 * @param {Roo.menu.Item} item The menu item to remove
20196 remove : function(item){
20197 this.items.removeKey(item.id);
20202 * Removes and destroys all items in the menu
20204 removeAll : function(){
20206 while(f = this.items.first()){
20212 // MenuNav is a private utility class used internally by the Menu
20213 Roo.menu.MenuNav = function(menu){
20214 Roo.menu.MenuNav.superclass.constructor.call(this, menu.el);
20215 this.scope = this.menu = menu;
20218 Roo.extend(Roo.menu.MenuNav, Roo.KeyNav, {
20219 doRelay : function(e, h){
20220 var k = e.getKey();
20221 if(!this.menu.activeItem && e.isNavKeyPress() && k != e.SPACE && k != e.RETURN){
20222 this.menu.tryActivate(0, 1);
20225 return h.call(this.scope || this, e, this.menu);
20228 up : function(e, m){
20229 if(!m.tryActivate(m.items.indexOf(m.activeItem)-1, -1)){
20230 m.tryActivate(m.items.length-1, -1);
20234 down : function(e, m){
20235 if(!m.tryActivate(m.items.indexOf(m.activeItem)+1, 1)){
20236 m.tryActivate(0, 1);
20240 right : function(e, m){
20242 m.activeItem.expandMenu(true);
20246 left : function(e, m){
20248 if(m.parentMenu && m.parentMenu.activeItem){
20249 m.parentMenu.activeItem.activate();
20253 enter : function(e, m){
20255 e.stopPropagation();
20256 m.activeItem.onClick(e);
20257 m.fireEvent("click", this, m.activeItem);
20263 * Ext JS Library 1.1.1
20264 * Copyright(c) 2006-2007, Ext JS, LLC.
20266 * Originally Released Under LGPL - original licence link has changed is not relivant.
20269 * <script type="text/javascript">
20273 * @class Roo.menu.MenuMgr
20274 * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
20277 Roo.menu.MenuMgr = function(){
20278 var menus, active, groups = {}, attached = false, lastShow = new Date();
20280 // private - called when first menu is created
20283 active = new Roo.util.MixedCollection();
20284 Roo.get(document).addKeyListener(27, function(){
20285 if(active.length > 0){
20292 function hideAll(){
20293 if(active && active.length > 0){
20294 var c = active.clone();
20295 c.each(function(m){
20302 function onHide(m){
20304 if(active.length < 1){
20305 Roo.get(document).un("mousedown", onMouseDown);
20311 function onShow(m){
20312 var last = active.last();
20313 lastShow = new Date();
20316 Roo.get(document).on("mousedown", onMouseDown);
20320 m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
20321 m.parentMenu.activeChild = m;
20322 }else if(last && last.isVisible()){
20323 m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
20328 function onBeforeHide(m){
20330 m.activeChild.hide();
20332 if(m.autoHideTimer){
20333 clearTimeout(m.autoHideTimer);
20334 delete m.autoHideTimer;
20339 function onBeforeShow(m){
20340 var pm = m.parentMenu;
20341 if(!pm && !m.allowOtherMenus){
20343 }else if(pm && pm.activeChild && active != m){
20344 pm.activeChild.hide();
20349 function onMouseDown(e){
20350 if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".x-menu")){
20356 function onBeforeCheck(mi, state){
20358 var g = groups[mi.group];
20359 for(var i = 0, l = g.length; i < l; i++){
20361 g[i].setChecked(false);
20370 * Hides all menus that are currently visible
20372 hideAll : function(){
20377 register : function(menu){
20381 menus[menu.id] = menu;
20382 menu.on("beforehide", onBeforeHide);
20383 menu.on("hide", onHide);
20384 menu.on("beforeshow", onBeforeShow);
20385 menu.on("show", onShow);
20386 var g = menu.group;
20387 if(g && menu.events["checkchange"]){
20391 groups[g].push(menu);
20392 menu.on("checkchange", onCheck);
20397 * Returns a {@link Roo.menu.Menu} object
20398 * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
20399 * be used to generate and return a new Menu instance.
20401 get : function(menu){
20402 if(typeof menu == "string"){ // menu id
20403 return menus[menu];
20404 }else if(menu.events){ // menu instance
20406 }else if(typeof menu.length == 'number'){ // array of menu items?
20407 return new Roo.menu.Menu({items:menu});
20408 }else{ // otherwise, must be a config
20409 return new Roo.menu.Menu(menu);
20414 unregister : function(menu){
20415 delete menus[menu.id];
20416 menu.un("beforehide", onBeforeHide);
20417 menu.un("hide", onHide);
20418 menu.un("beforeshow", onBeforeShow);
20419 menu.un("show", onShow);
20420 var g = menu.group;
20421 if(g && menu.events["checkchange"]){
20422 groups[g].remove(menu);
20423 menu.un("checkchange", onCheck);
20428 registerCheckable : function(menuItem){
20429 var g = menuItem.group;
20434 groups[g].push(menuItem);
20435 menuItem.on("beforecheckchange", onBeforeCheck);
20440 unregisterCheckable : function(menuItem){
20441 var g = menuItem.group;
20443 groups[g].remove(menuItem);
20444 menuItem.un("beforecheckchange", onBeforeCheck);
20450 * Ext JS Library 1.1.1
20451 * Copyright(c) 2006-2007, Ext JS, LLC.
20453 * Originally Released Under LGPL - original licence link has changed is not relivant.
20456 * <script type="text/javascript">
20461 * @class Roo.menu.BaseItem
20462 * @extends Roo.Component
20463 * The base class for all items that render into menus. BaseItem provides default rendering, activated state
20464 * management and base configuration options shared by all menu components.
20466 * Creates a new BaseItem
20467 * @param {Object} config Configuration options
20469 Roo.menu.BaseItem = function(config){
20470 Roo.menu.BaseItem.superclass.constructor.call(this, config);
20475 * Fires when this item is clicked
20476 * @param {Roo.menu.BaseItem} this
20477 * @param {Roo.EventObject} e
20482 * Fires when this item is activated
20483 * @param {Roo.menu.BaseItem} this
20487 * @event deactivate
20488 * Fires when this item is deactivated
20489 * @param {Roo.menu.BaseItem} this
20495 this.on("click", this.handler, this.scope, true);
20499 Roo.extend(Roo.menu.BaseItem, Roo.Component, {
20501 * @cfg {Function} handler
20502 * A function that will handle the click event of this menu item (defaults to undefined)
20505 * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to false)
20507 canActivate : false,
20509 * @cfg {String} activeClass The CSS class to use when the item becomes activated (defaults to "x-menu-item-active")
20511 activeClass : "x-menu-item-active",
20513 * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to true)
20515 hideOnClick : true,
20517 * @cfg {Number} hideDelay Length of time in milliseconds to wait before hiding after a click (defaults to 100)
20522 ctype: "Roo.menu.BaseItem",
20525 actionMode : "container",
20528 render : function(container, parentMenu){
20529 this.parentMenu = parentMenu;
20530 Roo.menu.BaseItem.superclass.render.call(this, container);
20531 this.container.menuItemId = this.id;
20535 onRender : function(container, position){
20536 this.el = Roo.get(this.el);
20537 container.dom.appendChild(this.el.dom);
20541 onClick : function(e){
20542 if(!this.disabled && this.fireEvent("click", this, e) !== false
20543 && this.parentMenu.fireEvent("itemclick", this, e) !== false){
20544 this.handleClick(e);
20551 activate : function(){
20555 var li = this.container;
20556 li.addClass(this.activeClass);
20557 this.region = li.getRegion().adjust(2, 2, -2, -2);
20558 this.fireEvent("activate", this);
20563 deactivate : function(){
20564 this.container.removeClass(this.activeClass);
20565 this.fireEvent("deactivate", this);
20569 shouldDeactivate : function(e){
20570 return !this.region || !this.region.contains(e.getPoint());
20574 handleClick : function(e){
20575 if(this.hideOnClick){
20576 this.parentMenu.hide.defer(this.hideDelay, this.parentMenu, [true]);
20581 expandMenu : function(autoActivate){
20586 hideMenu : function(){
20591 * Ext JS Library 1.1.1
20592 * Copyright(c) 2006-2007, Ext JS, LLC.
20594 * Originally Released Under LGPL - original licence link has changed is not relivant.
20597 * <script type="text/javascript">
20601 * @class Roo.menu.Adapter
20602 * @extends Roo.menu.BaseItem
20603 * A base utility class that adapts a non-menu component so that it can be wrapped by a menu item and added to a menu.
20604 * It provides basic rendering, activation management and enable/disable logic required to work in menus.
20606 * Creates a new Adapter
20607 * @param {Object} config Configuration options
20609 Roo.menu.Adapter = function(component, config){
20610 Roo.menu.Adapter.superclass.constructor.call(this, config);
20611 this.component = component;
20613 Roo.extend(Roo.menu.Adapter, Roo.menu.BaseItem, {
20615 canActivate : true,
20618 onRender : function(container, position){
20619 this.component.render(container);
20620 this.el = this.component.getEl();
20624 activate : function(){
20628 this.component.focus();
20629 this.fireEvent("activate", this);
20634 deactivate : function(){
20635 this.fireEvent("deactivate", this);
20639 disable : function(){
20640 this.component.disable();
20641 Roo.menu.Adapter.superclass.disable.call(this);
20645 enable : function(){
20646 this.component.enable();
20647 Roo.menu.Adapter.superclass.enable.call(this);
20651 * Ext JS Library 1.1.1
20652 * Copyright(c) 2006-2007, Ext JS, LLC.
20654 * Originally Released Under LGPL - original licence link has changed is not relivant.
20657 * <script type="text/javascript">
20661 * @class Roo.menu.TextItem
20662 * @extends Roo.menu.BaseItem
20663 * Adds a static text string to a menu, usually used as either a heading or group separator.
20664 * Note: old style constructor with text is still supported.
20667 * Creates a new TextItem
20668 * @param {Object} cfg Configuration
20670 Roo.menu.TextItem = function(cfg){
20671 if (typeof(cfg) == 'string') {
20674 Roo.apply(this,cfg);
20677 Roo.menu.TextItem.superclass.constructor.call(this);
20680 Roo.extend(Roo.menu.TextItem, Roo.menu.BaseItem, {
20682 * @cfg {Boolean} text Text to show on item.
20687 * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
20689 hideOnClick : false,
20691 * @cfg {String} itemCls The default CSS class to use for text items (defaults to "x-menu-text")
20693 itemCls : "x-menu-text",
20696 onRender : function(){
20697 var s = document.createElement("span");
20698 s.className = this.itemCls;
20699 s.innerHTML = this.text;
20701 Roo.menu.TextItem.superclass.onRender.apply(this, arguments);
20705 * Ext JS Library 1.1.1
20706 * Copyright(c) 2006-2007, Ext JS, LLC.
20708 * Originally Released Under LGPL - original licence link has changed is not relivant.
20711 * <script type="text/javascript">
20715 * @class Roo.menu.Separator
20716 * @extends Roo.menu.BaseItem
20717 * Adds a separator bar to a menu, used to divide logical groups of menu items. Generally you will
20718 * add one of these by using "-" in you call to add() or in your items config rather than creating one directly.
20720 * @param {Object} config Configuration options
20722 Roo.menu.Separator = function(config){
20723 Roo.menu.Separator.superclass.constructor.call(this, config);
20726 Roo.extend(Roo.menu.Separator, Roo.menu.BaseItem, {
20728 * @cfg {String} itemCls The default CSS class to use for separators (defaults to "x-menu-sep")
20730 itemCls : "x-menu-sep",
20732 * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
20734 hideOnClick : false,
20737 onRender : function(li){
20738 var s = document.createElement("span");
20739 s.className = this.itemCls;
20740 s.innerHTML = " ";
20742 li.addClass("x-menu-sep-li");
20743 Roo.menu.Separator.superclass.onRender.apply(this, arguments);
20747 * Ext JS Library 1.1.1
20748 * Copyright(c) 2006-2007, Ext JS, LLC.
20750 * Originally Released Under LGPL - original licence link has changed is not relivant.
20753 * <script type="text/javascript">
20756 * @class Roo.menu.Item
20757 * @extends Roo.menu.BaseItem
20758 * A base class for all menu items that require menu-related functionality (like sub-menus) and are not static
20759 * display items. Item extends the base functionality of {@link Roo.menu.BaseItem} by adding menu-specific
20760 * activation and click handling.
20762 * Creates a new Item
20763 * @param {Object} config Configuration options
20765 Roo.menu.Item = function(config){
20766 Roo.menu.Item.superclass.constructor.call(this, config);
20768 this.menu = Roo.menu.MenuMgr.get(this.menu);
20771 Roo.extend(Roo.menu.Item, Roo.menu.BaseItem, {
20774 * @cfg {String} text
20775 * The text to show on the menu item.
20779 * @cfg {String} HTML to render in menu
20780 * The text to show on the menu item (HTML version).
20784 * @cfg {String} icon
20785 * The path to an icon to display in this menu item (defaults to Roo.BLANK_IMAGE_URL)
20789 * @cfg {String} itemCls The default CSS class to use for menu items (defaults to "x-menu-item")
20791 itemCls : "x-menu-item",
20793 * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to true)
20795 canActivate : true,
20797 * @cfg {Number} showDelay Length of time in milliseconds to wait before showing this item (defaults to 200)
20800 // doc'd in BaseItem
20804 ctype: "Roo.menu.Item",
20807 onRender : function(container, position){
20808 var el = document.createElement("a");
20809 el.hideFocus = true;
20810 el.unselectable = "on";
20811 el.href = this.href || "#";
20812 if(this.hrefTarget){
20813 el.target = this.hrefTarget;
20815 el.className = this.itemCls + (this.menu ? " x-menu-item-arrow" : "") + (this.cls ? " " + this.cls : "");
20817 var html = this.html.length ? this.html : String.format('{0}',this.text);
20819 el.innerHTML = String.format(
20820 '<img src="{0}" class="x-menu-item-icon {1}" />' + html,
20821 this.icon || Roo.BLANK_IMAGE_URL, this.iconCls || '');
20823 Roo.menu.Item.superclass.onRender.call(this, container, position);
20827 * Sets the text to display in this menu item
20828 * @param {String} text The text to display
20829 * @param {Boolean} isHTML true to indicate text is pure html.
20831 setText : function(text, isHTML){
20839 var html = this.html.length ? this.html : String.format('{0}',this.text);
20841 this.el.update(String.format(
20842 '<img src="{0}" class="x-menu-item-icon {2}">' + html,
20843 this.icon || Roo.BLANK_IMAGE_URL, this.text, this.iconCls || ''));
20844 this.parentMenu.autoWidth();
20849 handleClick : function(e){
20850 if(!this.href){ // if no link defined, stop the event automatically
20853 Roo.menu.Item.superclass.handleClick.apply(this, arguments);
20857 activate : function(autoExpand){
20858 if(Roo.menu.Item.superclass.activate.apply(this, arguments)){
20868 shouldDeactivate : function(e){
20869 if(Roo.menu.Item.superclass.shouldDeactivate.call(this, e)){
20870 if(this.menu && this.menu.isVisible()){
20871 return !this.menu.getEl().getRegion().contains(e.getPoint());
20879 deactivate : function(){
20880 Roo.menu.Item.superclass.deactivate.apply(this, arguments);
20885 expandMenu : function(autoActivate){
20886 if(!this.disabled && this.menu){
20887 clearTimeout(this.hideTimer);
20888 delete this.hideTimer;
20889 if(!this.menu.isVisible() && !this.showTimer){
20890 this.showTimer = this.deferExpand.defer(this.showDelay, this, [autoActivate]);
20891 }else if (this.menu.isVisible() && autoActivate){
20892 this.menu.tryActivate(0, 1);
20898 deferExpand : function(autoActivate){
20899 delete this.showTimer;
20900 this.menu.show(this.container, this.parentMenu.subMenuAlign || "tl-tr?", this.parentMenu);
20902 this.menu.tryActivate(0, 1);
20907 hideMenu : function(){
20908 clearTimeout(this.showTimer);
20909 delete this.showTimer;
20910 if(!this.hideTimer && this.menu && this.menu.isVisible()){
20911 this.hideTimer = this.deferHide.defer(this.hideDelay, this);
20916 deferHide : function(){
20917 delete this.hideTimer;
20922 * Ext JS Library 1.1.1
20923 * Copyright(c) 2006-2007, Ext JS, LLC.
20925 * Originally Released Under LGPL - original licence link has changed is not relivant.
20928 * <script type="text/javascript">
20932 * @class Roo.menu.CheckItem
20933 * @extends Roo.menu.Item
20934 * Adds a menu item that contains a checkbox by default, but can also be part of a radio group.
20936 * Creates a new CheckItem
20937 * @param {Object} config Configuration options
20939 Roo.menu.CheckItem = function(config){
20940 Roo.menu.CheckItem.superclass.constructor.call(this, config);
20943 * @event beforecheckchange
20944 * Fires before the checked value is set, providing an opportunity to cancel if needed
20945 * @param {Roo.menu.CheckItem} this
20946 * @param {Boolean} checked The new checked value that will be set
20948 "beforecheckchange" : true,
20950 * @event checkchange
20951 * Fires after the checked value has been set
20952 * @param {Roo.menu.CheckItem} this
20953 * @param {Boolean} checked The checked value that was set
20955 "checkchange" : true
20957 if(this.checkHandler){
20958 this.on('checkchange', this.checkHandler, this.scope);
20961 Roo.extend(Roo.menu.CheckItem, Roo.menu.Item, {
20963 * @cfg {String} group
20964 * All check items with the same group name will automatically be grouped into a single-select
20965 * radio button group (defaults to '')
20968 * @cfg {String} itemCls The default CSS class to use for check items (defaults to "x-menu-item x-menu-check-item")
20970 itemCls : "x-menu-item x-menu-check-item",
20972 * @cfg {String} groupClass The default CSS class to use for radio group check items (defaults to "x-menu-group-item")
20974 groupClass : "x-menu-group-item",
20977 * @cfg {Boolean} checked True to initialize this checkbox as checked (defaults to false). Note that
20978 * if this checkbox is part of a radio group (group = true) only the last item in the group that is
20979 * initialized with checked = true will be rendered as checked.
20984 ctype: "Roo.menu.CheckItem",
20987 onRender : function(c){
20988 Roo.menu.CheckItem.superclass.onRender.apply(this, arguments);
20990 this.el.addClass(this.groupClass);
20992 Roo.menu.MenuMgr.registerCheckable(this);
20994 this.checked = false;
20995 this.setChecked(true, true);
21000 destroy : function(){
21002 Roo.menu.MenuMgr.unregisterCheckable(this);
21004 Roo.menu.CheckItem.superclass.destroy.apply(this, arguments);
21008 * Set the checked state of this item
21009 * @param {Boolean} checked The new checked value
21010 * @param {Boolean} suppressEvent (optional) True to prevent the checkchange event from firing (defaults to false)
21012 setChecked : function(state, suppressEvent){
21013 if(this.checked != state && this.fireEvent("beforecheckchange", this, state) !== false){
21014 if(this.container){
21015 this.container[state ? "addClass" : "removeClass"]("x-menu-item-checked");
21017 this.checked = state;
21018 if(suppressEvent !== true){
21019 this.fireEvent("checkchange", this, state);
21025 handleClick : function(e){
21026 if(!this.disabled && !(this.checked && this.group)){// disable unselect on radio item
21027 this.setChecked(!this.checked);
21029 Roo.menu.CheckItem.superclass.handleClick.apply(this, arguments);
21033 * Ext JS Library 1.1.1
21034 * Copyright(c) 2006-2007, Ext JS, LLC.
21036 * Originally Released Under LGPL - original licence link has changed is not relivant.
21039 * <script type="text/javascript">
21043 * @class Roo.menu.DateItem
21044 * @extends Roo.menu.Adapter
21045 * A menu item that wraps the {@link Roo.DatPicker} component.
21047 * Creates a new DateItem
21048 * @param {Object} config Configuration options
21050 Roo.menu.DateItem = function(config){
21051 Roo.menu.DateItem.superclass.constructor.call(this, new Roo.DatePicker(config), config);
21052 /** The Roo.DatePicker object @type Roo.DatePicker */
21053 this.picker = this.component;
21054 this.addEvents({select: true});
21056 this.picker.on("render", function(picker){
21057 picker.getEl().swallowEvent("click");
21058 picker.container.addClass("x-menu-date-item");
21061 this.picker.on("select", this.onSelect, this);
21064 Roo.extend(Roo.menu.DateItem, Roo.menu.Adapter, {
21066 onSelect : function(picker, date){
21067 this.fireEvent("select", this, date, picker);
21068 Roo.menu.DateItem.superclass.handleClick.call(this);
21072 * Ext JS Library 1.1.1
21073 * Copyright(c) 2006-2007, Ext JS, LLC.
21075 * Originally Released Under LGPL - original licence link has changed is not relivant.
21078 * <script type="text/javascript">
21082 * @class Roo.menu.ColorItem
21083 * @extends Roo.menu.Adapter
21084 * A menu item that wraps the {@link Roo.ColorPalette} component.
21086 * Creates a new ColorItem
21087 * @param {Object} config Configuration options
21089 Roo.menu.ColorItem = function(config){
21090 Roo.menu.ColorItem.superclass.constructor.call(this, new Roo.ColorPalette(config), config);
21091 /** The Roo.ColorPalette object @type Roo.ColorPalette */
21092 this.palette = this.component;
21093 this.relayEvents(this.palette, ["select"]);
21094 if(this.selectHandler){
21095 this.on('select', this.selectHandler, this.scope);
21098 Roo.extend(Roo.menu.ColorItem, Roo.menu.Adapter);/*
21100 * Ext JS Library 1.1.1
21101 * Copyright(c) 2006-2007, Ext JS, LLC.
21103 * Originally Released Under LGPL - original licence link has changed is not relivant.
21106 * <script type="text/javascript">
21111 * @class Roo.menu.DateMenu
21112 * @extends Roo.menu.Menu
21113 * A menu containing a {@link Roo.menu.DateItem} component (which provides a date picker).
21115 * Creates a new DateMenu
21116 * @param {Object} config Configuration options
21118 Roo.menu.DateMenu = function(config){
21119 Roo.menu.DateMenu.superclass.constructor.call(this, config);
21121 var di = new Roo.menu.DateItem(config);
21124 * The {@link Roo.DatePicker} instance for this DateMenu
21127 this.picker = di.picker;
21130 * @param {DatePicker} picker
21131 * @param {Date} date
21133 this.relayEvents(di, ["select"]);
21135 this.on('beforeshow', function(){
21137 this.picker.hideMonthPicker(true);
21141 Roo.extend(Roo.menu.DateMenu, Roo.menu.Menu, {
21145 * Ext JS Library 1.1.1
21146 * Copyright(c) 2006-2007, Ext JS, LLC.
21148 * Originally Released Under LGPL - original licence link has changed is not relivant.
21151 * <script type="text/javascript">
21156 * @class Roo.menu.ColorMenu
21157 * @extends Roo.menu.Menu
21158 * A menu containing a {@link Roo.menu.ColorItem} component (which provides a basic color picker).
21160 * Creates a new ColorMenu
21161 * @param {Object} config Configuration options
21163 Roo.menu.ColorMenu = function(config){
21164 Roo.menu.ColorMenu.superclass.constructor.call(this, config);
21166 var ci = new Roo.menu.ColorItem(config);
21169 * The {@link Roo.ColorPalette} instance for this ColorMenu
21170 * @type ColorPalette
21172 this.palette = ci.palette;
21175 * @param {ColorPalette} palette
21176 * @param {String} color
21178 this.relayEvents(ci, ["select"]);
21180 Roo.extend(Roo.menu.ColorMenu, Roo.menu.Menu);/*
21182 * Ext JS Library 1.1.1
21183 * Copyright(c) 2006-2007, Ext JS, LLC.
21185 * Originally Released Under LGPL - original licence link has changed is not relivant.
21188 * <script type="text/javascript">
21192 * @class Roo.form.Field
21193 * @extends Roo.BoxComponent
21194 * Base class for form fields that provides default event handling, sizing, value handling and other functionality.
21196 * Creates a new Field
21197 * @param {Object} config Configuration options
21199 Roo.form.Field = function(config){
21200 Roo.form.Field.superclass.constructor.call(this, config);
21203 Roo.extend(Roo.form.Field, Roo.BoxComponent, {
21205 * @cfg {String} fieldLabel Label to use when rendering a form.
21208 * @cfg {String} qtip Mouse over tip
21212 * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
21214 invalidClass : "x-form-invalid",
21216 * @cfg {String} invalidText The error text to use when marking a field invalid and no message is provided (defaults to "The value in this field is invalid")
21218 invalidText : "The value in this field is invalid",
21220 * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
21222 focusClass : "x-form-focus",
21224 * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
21225 automatic validation (defaults to "keyup").
21227 validationEvent : "keyup",
21229 * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
21231 validateOnBlur : true,
21233 * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
21235 validationDelay : 250,
21237 * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
21238 * {tag: "input", type: "text", size: "20", autocomplete: "off"})
21240 defaultAutoCreate : {tag: "input", type: "text", size: "20", autocomplete: "off"},
21242 * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field")
21244 fieldClass : "x-form-field",
21246 * @cfg {String} msgTarget The location where error text should display. Should be one of the following values (defaults to 'qtip'):
21249 ----------- ----------------------------------------------------------------------
21250 qtip Display a quick tip when the user hovers over the field
21251 title Display a default browser title attribute popup
21252 under Add a block div beneath the field containing the error text
21253 side Add an error icon to the right of the field with a popup on hover
21254 [element id] Add the error text directly to the innerHTML of the specified element
21257 msgTarget : 'qtip',
21259 * @cfg {String} msgFx <b>Experimental</b> The effect used when displaying a validation message under the field (defaults to 'normal').
21264 * @cfg {Boolean} readOnly True to mark the field as readOnly in HTML (defaults to false) -- Note: this only sets the element's readOnly DOM attribute.
21269 * @cfg {Boolean} disabled True to disable the field (defaults to false).
21274 * @cfg {String} inputType The type attribute for input fields -- e.g. radio, text, password (defaults to "text").
21276 inputType : undefined,
21279 * @cfg {Number} tabIndex The tabIndex for this field. Note this only applies to fields that are rendered, not those which are built via applyTo (defaults to undefined).
21281 tabIndex : undefined,
21284 isFormField : true,
21289 * @property {Roo.Element} fieldEl
21290 * Element Containing the rendered Field (with label etc.)
21293 * @cfg {Mixed} value A value to initialize this field with.
21298 * @cfg {String} name The field's HTML name attribute.
21301 * @cfg {String} cls A CSS class to apply to the field's underlying element.
21305 initComponent : function(){
21306 Roo.form.Field.superclass.initComponent.call(this);
21310 * Fires when this field receives input focus.
21311 * @param {Roo.form.Field} this
21316 * Fires when this field loses input focus.
21317 * @param {Roo.form.Field} this
21321 * @event specialkey
21322 * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed. You can check
21323 * {@link Roo.EventObject#getKey} to determine which key was pressed.
21324 * @param {Roo.form.Field} this
21325 * @param {Roo.EventObject} e The event object
21330 * Fires just before the field blurs if the field value has changed.
21331 * @param {Roo.form.Field} this
21332 * @param {Mixed} newValue The new value
21333 * @param {Mixed} oldValue The original value
21338 * Fires after the field has been marked as invalid.
21339 * @param {Roo.form.Field} this
21340 * @param {String} msg The validation message
21345 * Fires after the field has been validated with no errors.
21346 * @param {Roo.form.Field} this
21351 * Fires after the key up
21352 * @param {Roo.form.Field} this
21353 * @param {Roo.EventObject} e The event Object
21360 * Returns the name attribute of the field if available
21361 * @return {String} name The field name
21363 getName: function(){
21364 return this.rendered && this.el.dom.name ? this.el.dom.name : (this.hiddenName || '');
21368 onRender : function(ct, position){
21369 Roo.form.Field.superclass.onRender.call(this, ct, position);
21371 var cfg = this.getAutoCreate();
21373 cfg.name = this.name || this.id;
21375 if(this.inputType){
21376 cfg.type = this.inputType;
21378 this.el = ct.createChild(cfg, position);
21380 var type = this.el.dom.type;
21382 if(type == 'password'){
21385 this.el.addClass('x-form-'+type);
21388 this.el.dom.readOnly = true;
21390 if(this.tabIndex !== undefined){
21391 this.el.dom.setAttribute('tabIndex', this.tabIndex);
21394 this.el.addClass([this.fieldClass, this.cls]);
21399 * Apply the behaviors of this component to an existing element. <b>This is used instead of render().</b>
21400 * @param {String/HTMLElement/Element} el The id of the node, a DOM node or an existing Element
21401 * @return {Roo.form.Field} this
21403 applyTo : function(target){
21404 this.allowDomMove = false;
21405 this.el = Roo.get(target);
21406 this.render(this.el.dom.parentNode);
21411 initValue : function(){
21412 if(this.value !== undefined){
21413 this.setValue(this.value);
21414 }else if(this.el.dom.value.length > 0){
21415 this.setValue(this.el.dom.value);
21420 * Returns true if this field has been changed since it was originally loaded and is not disabled.
21422 isDirty : function() {
21423 if(this.disabled) {
21426 return String(this.getValue()) !== String(this.originalValue);
21430 afterRender : function(){
21431 Roo.form.Field.superclass.afterRender.call(this);
21436 fireKey : function(e){
21437 //Roo.log('field ' + e.getKey());
21438 if(e.isNavKeyPress()){
21439 this.fireEvent("specialkey", this, e);
21444 * Resets the current field value to the originally loaded value and clears any validation messages
21446 reset : function(){
21447 this.setValue(this.originalValue);
21448 this.clearInvalid();
21452 initEvents : function(){
21453 // safari killled keypress - so keydown is now used..
21454 this.el.on("keydown" , this.fireKey, this);
21455 this.el.on("focus", this.onFocus, this);
21456 this.el.on("blur", this.onBlur, this);
21457 this.el.relayEvent('keyup', this);
21459 // reference to original value for reset
21460 this.originalValue = this.getValue();
21464 onFocus : function(){
21465 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
21466 this.el.addClass(this.focusClass);
21468 if(!this.hasFocus){
21469 this.hasFocus = true;
21470 this.startValue = this.getValue();
21471 this.fireEvent("focus", this);
21475 beforeBlur : Roo.emptyFn,
21478 onBlur : function(){
21480 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
21481 this.el.removeClass(this.focusClass);
21483 this.hasFocus = false;
21484 if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
21487 var v = this.getValue();
21488 if(String(v) !== String(this.startValue)){
21489 this.fireEvent('change', this, v, this.startValue);
21491 this.fireEvent("blur", this);
21495 * Returns whether or not the field value is currently valid
21496 * @param {Boolean} preventMark True to disable marking the field invalid
21497 * @return {Boolean} True if the value is valid, else false
21499 isValid : function(preventMark){
21503 var restore = this.preventMark;
21504 this.preventMark = preventMark === true;
21505 var v = this.validateValue(this.processValue(this.getRawValue()));
21506 this.preventMark = restore;
21511 * Validates the field value
21512 * @return {Boolean} True if the value is valid, else false
21514 validate : function(){
21515 if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
21516 this.clearInvalid();
21522 processValue : function(value){
21527 // Subclasses should provide the validation implementation by overriding this
21528 validateValue : function(value){
21533 * Mark this field as invalid
21534 * @param {String} msg The validation message
21536 markInvalid : function(msg){
21537 if(!this.rendered || this.preventMark){ // not rendered
21540 this.el.addClass(this.invalidClass);
21541 msg = msg || this.invalidText;
21542 switch(this.msgTarget){
21544 this.el.dom.qtip = msg;
21545 this.el.dom.qclass = 'x-form-invalid-tip';
21546 if(Roo.QuickTips){ // fix for floating editors interacting with DND
21547 Roo.QuickTips.enable();
21551 this.el.dom.title = msg;
21555 var elp = this.el.findParent('.x-form-element', 5, true);
21556 this.errorEl = elp.createChild({cls:'x-form-invalid-msg'});
21557 this.errorEl.setWidth(elp.getWidth(true)-20);
21559 this.errorEl.update(msg);
21560 Roo.form.Field.msgFx[this.msgFx].show(this.errorEl, this);
21563 if(!this.errorIcon){
21564 var elp = this.el.findParent('.x-form-element', 5, true);
21565 this.errorIcon = elp.createChild({cls:'x-form-invalid-icon'});
21567 this.alignErrorIcon();
21568 this.errorIcon.dom.qtip = msg;
21569 this.errorIcon.dom.qclass = 'x-form-invalid-tip';
21570 this.errorIcon.show();
21571 this.on('resize', this.alignErrorIcon, this);
21574 var t = Roo.getDom(this.msgTarget);
21576 t.style.display = this.msgDisplay;
21579 this.fireEvent('invalid', this, msg);
21583 alignErrorIcon : function(){
21584 this.errorIcon.alignTo(this.el, 'tl-tr', [2, 0]);
21588 * Clear any invalid styles/messages for this field
21590 clearInvalid : function(){
21591 if(!this.rendered || this.preventMark){ // not rendered
21594 this.el.removeClass(this.invalidClass);
21595 switch(this.msgTarget){
21597 this.el.dom.qtip = '';
21600 this.el.dom.title = '';
21604 Roo.form.Field.msgFx[this.msgFx].hide(this.errorEl, this);
21608 if(this.errorIcon){
21609 this.errorIcon.dom.qtip = '';
21610 this.errorIcon.hide();
21611 this.un('resize', this.alignErrorIcon, this);
21615 var t = Roo.getDom(this.msgTarget);
21617 t.style.display = 'none';
21620 this.fireEvent('valid', this);
21624 * Returns the raw data value which may or may not be a valid, defined value. To return a normalized value see {@link #getValue}.
21625 * @return {Mixed} value The field value
21627 getRawValue : function(){
21628 var v = this.el.getValue();
21629 if(v === this.emptyText){
21636 * Returns the normalized data value (undefined or emptyText will be returned as ''). To return the raw value see {@link #getRawValue}.
21637 * @return {Mixed} value The field value
21639 getValue : function(){
21640 var v = this.el.getValue();
21641 if(v === this.emptyText || v === undefined){
21648 * Sets the underlying DOM field's value directly, bypassing validation. To set the value with validation see {@link #setValue}.
21649 * @param {Mixed} value The value to set
21651 setRawValue : function(v){
21652 return this.el.dom.value = (v === null || v === undefined ? '' : v);
21656 * Sets a data value into the field and validates it. To set the value directly without validation see {@link #setRawValue}.
21657 * @param {Mixed} value The value to set
21659 setValue : function(v){
21662 this.el.dom.value = (v === null || v === undefined ? '' : v);
21667 adjustSize : function(w, h){
21668 var s = Roo.form.Field.superclass.adjustSize.call(this, w, h);
21669 s.width = this.adjustWidth(this.el.dom.tagName, s.width);
21673 adjustWidth : function(tag, w){
21674 tag = tag.toLowerCase();
21675 if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
21676 if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
21677 if(tag == 'input'){
21680 if(tag = 'textarea'){
21683 }else if(Roo.isOpera){
21684 if(tag == 'input'){
21687 if(tag = 'textarea'){
21697 // anything other than normal should be considered experimental
21698 Roo.form.Field.msgFx = {
21700 show: function(msgEl, f){
21701 msgEl.setDisplayed('block');
21704 hide : function(msgEl, f){
21705 msgEl.setDisplayed(false).update('');
21710 show: function(msgEl, f){
21711 msgEl.slideIn('t', {stopFx:true});
21714 hide : function(msgEl, f){
21715 msgEl.slideOut('t', {stopFx:true,useDisplay:true});
21720 show: function(msgEl, f){
21721 msgEl.fixDisplay();
21722 msgEl.alignTo(f.el, 'tl-tr');
21723 msgEl.slideIn('l', {stopFx:true});
21726 hide : function(msgEl, f){
21727 msgEl.slideOut('l', {stopFx:true,useDisplay:true});
21732 * Ext JS Library 1.1.1
21733 * Copyright(c) 2006-2007, Ext JS, LLC.
21735 * Originally Released Under LGPL - original licence link has changed is not relivant.
21738 * <script type="text/javascript">
21743 * @class Roo.form.TextField
21744 * @extends Roo.form.Field
21745 * Basic text field. Can be used as a direct replacement for traditional text inputs, or as the base
21746 * class for more sophisticated input controls (like {@link Roo.form.TextArea} and {@link Roo.form.ComboBox}).
21748 * Creates a new TextField
21749 * @param {Object} config Configuration options
21751 Roo.form.TextField = function(config){
21752 Roo.form.TextField.superclass.constructor.call(this, config);
21756 * Fires when the autosize function is triggered. The field may or may not have actually changed size
21757 * according to the default logic, but this event provides a hook for the developer to apply additional
21758 * logic at runtime to resize the field if needed.
21759 * @param {Roo.form.Field} this This text field
21760 * @param {Number} width The new field width
21766 Roo.extend(Roo.form.TextField, Roo.form.Field, {
21768 * @cfg {Boolean} grow True if this field should automatically grow and shrink to its content
21772 * @cfg {Number} growMin The minimum width to allow when grow = true (defaults to 30)
21776 * @cfg {Number} growMax The maximum width to allow when grow = true (defaults to 800)
21780 * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
21784 * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
21788 * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
21790 disableKeyFilter : false,
21792 * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
21796 * @cfg {Number} minLength Minimum input field length required (defaults to 0)
21800 * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
21802 maxLength : Number.MAX_VALUE,
21804 * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
21806 minLengthText : "The minimum length for this field is {0}",
21808 * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
21810 maxLengthText : "The maximum length for this field is {0}",
21812 * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
21814 selectOnFocus : false,
21816 * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
21818 blankText : "This field is required",
21820 * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
21821 * If available, this function will be called only after the basic validators all return true, and will be passed the
21822 * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
21826 * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
21827 * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
21828 * current field value. If the test fails, the field will be marked invalid using {@link #regexText}.
21832 * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
21836 * @cfg {String} emptyText The default text to display in an empty field (defaults to null).
21840 * @cfg {String} emptyClass The CSS class to apply to an empty field to style the {@link #emptyText} (defaults to
21841 * 'x-form-empty-field'). This class is automatically added and removed as needed depending on the current field value.
21843 emptyClass : 'x-form-empty-field',
21846 initEvents : function(){
21847 Roo.form.TextField.superclass.initEvents.call(this);
21848 if(this.validationEvent == 'keyup'){
21849 this.validationTask = new Roo.util.DelayedTask(this.validate, this);
21850 this.el.on('keyup', this.filterValidation, this);
21852 else if(this.validationEvent !== false){
21853 this.el.on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
21855 if(this.selectOnFocus || this.emptyText){
21856 this.on("focus", this.preFocus, this);
21857 if(this.emptyText){
21858 this.on('blur', this.postBlur, this);
21859 this.applyEmptyText();
21862 if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
21863 this.el.on("keypress", this.filterKeys, this);
21866 this.el.on("keyup", this.onKeyUp, this, {buffer:50});
21867 this.el.on("click", this.autoSize, this);
21871 processValue : function(value){
21872 if(this.stripCharsRe){
21873 var newValue = value.replace(this.stripCharsRe, '');
21874 if(newValue !== value){
21875 this.setRawValue(newValue);
21882 filterValidation : function(e){
21883 if(!e.isNavKeyPress()){
21884 this.validationTask.delay(this.validationDelay);
21889 onKeyUp : function(e){
21890 if(!e.isNavKeyPress()){
21896 * Resets the current field value to the originally-loaded value and clears any validation messages.
21897 * Also adds emptyText and emptyClass if the original value was blank.
21899 reset : function(){
21900 Roo.form.TextField.superclass.reset.call(this);
21901 this.applyEmptyText();
21904 applyEmptyText : function(){
21905 if(this.rendered && this.emptyText && this.getRawValue().length < 1){
21906 this.setRawValue(this.emptyText);
21907 this.el.addClass(this.emptyClass);
21912 preFocus : function(){
21913 if(this.emptyText){
21914 if(this.el.dom.value == this.emptyText){
21915 this.setRawValue('');
21917 this.el.removeClass(this.emptyClass);
21919 if(this.selectOnFocus){
21920 this.el.dom.select();
21925 postBlur : function(){
21926 this.applyEmptyText();
21930 filterKeys : function(e){
21931 var k = e.getKey();
21932 if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
21935 var c = e.getCharCode(), cc = String.fromCharCode(c);
21936 if(Roo.isIE && (e.isSpecialKey() || !cc)){
21939 if(!this.maskRe.test(cc)){
21944 setValue : function(v){
21945 if(this.emptyText && this.el && v !== undefined && v !== null && v !== ''){
21946 this.el.removeClass(this.emptyClass);
21948 Roo.form.TextField.superclass.setValue.apply(this, arguments);
21949 this.applyEmptyText();
21954 * Validates a value according to the field's validation rules and marks the field as invalid
21955 * if the validation fails
21956 * @param {Mixed} value The value to validate
21957 * @return {Boolean} True if the value is valid, else false
21959 validateValue : function(value){
21960 if(value.length < 1 || value === this.emptyText){ // if it's blank
21961 if(this.allowBlank){
21962 this.clearInvalid();
21965 this.markInvalid(this.blankText);
21969 if(value.length < this.minLength){
21970 this.markInvalid(String.format(this.minLengthText, this.minLength));
21973 if(value.length > this.maxLength){
21974 this.markInvalid(String.format(this.maxLengthText, this.maxLength));
21978 var vt = Roo.form.VTypes;
21979 if(!vt[this.vtype](value, this)){
21980 this.markInvalid(this.vtypeText || vt[this.vtype +'Text']);
21984 if(typeof this.validator == "function"){
21985 var msg = this.validator(value);
21987 this.markInvalid(msg);
21991 if(this.regex && !this.regex.test(value)){
21992 this.markInvalid(this.regexText);
21999 * Selects text in this field
22000 * @param {Number} start (optional) The index where the selection should start (defaults to 0)
22001 * @param {Number} end (optional) The index where the selection should end (defaults to the text length)
22003 selectText : function(start, end){
22004 var v = this.getRawValue();
22006 start = start === undefined ? 0 : start;
22007 end = end === undefined ? v.length : end;
22008 var d = this.el.dom;
22009 if(d.setSelectionRange){
22010 d.setSelectionRange(start, end);
22011 }else if(d.createTextRange){
22012 var range = d.createTextRange();
22013 range.moveStart("character", start);
22014 range.moveEnd("character", v.length-end);
22021 * Automatically grows the field to accomodate the width of the text up to the maximum field width allowed.
22022 * This only takes effect if grow = true, and fires the autosize event.
22024 autoSize : function(){
22025 if(!this.grow || !this.rendered){
22029 this.metrics = Roo.util.TextMetrics.createInstance(this.el);
22032 var v = el.dom.value;
22033 var d = document.createElement('div');
22034 d.appendChild(document.createTextNode(v));
22038 var w = Math.min(this.growMax, Math.max(this.metrics.getWidth(v) + /* add extra padding */ 10, this.growMin));
22039 this.el.setWidth(w);
22040 this.fireEvent("autosize", this, w);
22044 * Ext JS Library 1.1.1
22045 * Copyright(c) 2006-2007, Ext JS, LLC.
22047 * Originally Released Under LGPL - original licence link has changed is not relivant.
22050 * <script type="text/javascript">
22054 * @class Roo.form.Hidden
22055 * @extends Roo.form.TextField
22056 * Simple Hidden element used on forms
22058 * usage: form.add(new Roo.form.HiddenField({ 'name' : 'test1' }));
22061 * Creates a new Hidden form element.
22062 * @param {Object} config Configuration options
22067 // easy hidden field...
22068 Roo.form.Hidden = function(config){
22069 Roo.form.Hidden.superclass.constructor.call(this, config);
22072 Roo.extend(Roo.form.Hidden, Roo.form.TextField, {
22074 inputType: 'hidden',
22077 labelSeparator: '',
22079 itemCls : 'x-form-item-display-none'
22087 * Ext JS Library 1.1.1
22088 * Copyright(c) 2006-2007, Ext JS, LLC.
22090 * Originally Released Under LGPL - original licence link has changed is not relivant.
22093 * <script type="text/javascript">
22097 * @class Roo.form.TriggerField
22098 * @extends Roo.form.TextField
22099 * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
22100 * The trigger has no default action, so you must assign a function to implement the trigger click handler by
22101 * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
22102 * for which you can provide a custom implementation. For example:
22104 var trigger = new Roo.form.TriggerField();
22105 trigger.onTriggerClick = myTriggerFn;
22106 trigger.applyTo('my-field');
22109 * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
22110 * {@link Roo.form.DateField} and {@link Roo.form.ComboBox} are perfect examples of this.
22111 * @cfg {String} triggerClass An additional CSS class used to style the trigger button. The trigger will always get the
22112 * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
22114 * Create a new TriggerField.
22115 * @param {Object} config Configuration options (valid {@Roo.form.TextField} config options will also be applied
22116 * to the base TextField)
22118 Roo.form.TriggerField = function(config){
22119 this.mimicing = false;
22120 Roo.form.TriggerField.superclass.constructor.call(this, config);
22123 Roo.extend(Roo.form.TriggerField, Roo.form.TextField, {
22125 * @cfg {String} triggerClass A CSS class to apply to the trigger
22128 * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
22129 * {tag: "input", type: "text", size: "16", autocomplete: "off"})
22131 defaultAutoCreate : {tag: "input", type: "text", size: "16", autocomplete: "off"},
22133 * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
22137 /** @cfg {Boolean} grow @hide */
22138 /** @cfg {Number} growMin @hide */
22139 /** @cfg {Number} growMax @hide */
22145 autoSize: Roo.emptyFn,
22149 deferHeight : true,
22152 actionMode : 'wrap',
22154 onResize : function(w, h){
22155 Roo.form.TriggerField.superclass.onResize.apply(this, arguments);
22156 if(typeof w == 'number'){
22157 var x = w - this.trigger.getWidth();
22158 this.el.setWidth(this.adjustWidth('input', x));
22159 this.trigger.setStyle('left', x+'px');
22164 adjustSize : Roo.BoxComponent.prototype.adjustSize,
22167 getResizeEl : function(){
22172 getPositionEl : function(){
22177 alignErrorIcon : function(){
22178 this.errorIcon.alignTo(this.wrap, 'tl-tr', [2, 0]);
22182 onRender : function(ct, position){
22183 Roo.form.TriggerField.superclass.onRender.call(this, ct, position);
22184 this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
22185 this.trigger = this.wrap.createChild(this.triggerConfig ||
22186 {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.triggerClass});
22187 if(this.hideTrigger){
22188 this.trigger.setDisplayed(false);
22190 this.initTrigger();
22192 this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
22197 initTrigger : function(){
22198 this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
22199 this.trigger.addClassOnOver('x-form-trigger-over');
22200 this.trigger.addClassOnClick('x-form-trigger-click');
22204 onDestroy : function(){
22206 this.trigger.removeAllListeners();
22207 this.trigger.remove();
22210 this.wrap.remove();
22212 Roo.form.TriggerField.superclass.onDestroy.call(this);
22216 onFocus : function(){
22217 Roo.form.TriggerField.superclass.onFocus.call(this);
22218 if(!this.mimicing){
22219 this.wrap.addClass('x-trigger-wrap-focus');
22220 this.mimicing = true;
22221 Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
22222 if(this.monitorTab){
22223 this.el.on("keydown", this.checkTab, this);
22229 checkTab : function(e){
22230 if(e.getKey() == e.TAB){
22231 this.triggerBlur();
22236 onBlur : function(){
22241 mimicBlur : function(e, t){
22242 if(!this.wrap.contains(t) && this.validateBlur()){
22243 this.triggerBlur();
22248 triggerBlur : function(){
22249 this.mimicing = false;
22250 Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
22251 if(this.monitorTab){
22252 this.el.un("keydown", this.checkTab, this);
22254 this.wrap.removeClass('x-trigger-wrap-focus');
22255 Roo.form.TriggerField.superclass.onBlur.call(this);
22259 // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
22260 validateBlur : function(e, t){
22265 onDisable : function(){
22266 Roo.form.TriggerField.superclass.onDisable.call(this);
22268 this.wrap.addClass('x-item-disabled');
22273 onEnable : function(){
22274 Roo.form.TriggerField.superclass.onEnable.call(this);
22276 this.wrap.removeClass('x-item-disabled');
22281 onShow : function(){
22282 var ae = this.getActionEl();
22285 ae.dom.style.display = '';
22286 ae.dom.style.visibility = 'visible';
22292 onHide : function(){
22293 var ae = this.getActionEl();
22294 ae.dom.style.display = 'none';
22298 * The function that should handle the trigger's click event. This method does nothing by default until overridden
22299 * by an implementing function.
22301 * @param {EventObject} e
22303 onTriggerClick : Roo.emptyFn
22306 // TwinTriggerField is not a public class to be used directly. It is meant as an abstract base class
22307 // to be extended by an implementing class. For an example of implementing this class, see the custom
22308 // SearchField implementation here: http://extjs.com/deploy/ext/examples/form/custom.html
22309 Roo.form.TwinTriggerField = Roo.extend(Roo.form.TriggerField, {
22310 initComponent : function(){
22311 Roo.form.TwinTriggerField.superclass.initComponent.call(this);
22313 this.triggerConfig = {
22314 tag:'span', cls:'x-form-twin-triggers', cn:[
22315 {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger1Class},
22316 {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger2Class}
22320 getTrigger : function(index){
22321 return this.triggers[index];
22324 initTrigger : function(){
22325 var ts = this.trigger.select('.x-form-trigger', true);
22326 this.wrap.setStyle('overflow', 'hidden');
22327 var triggerField = this;
22328 ts.each(function(t, all, index){
22329 t.hide = function(){
22330 var w = triggerField.wrap.getWidth();
22331 this.dom.style.display = 'none';
22332 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
22334 t.show = function(){
22335 var w = triggerField.wrap.getWidth();
22336 this.dom.style.display = '';
22337 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
22339 var triggerIndex = 'Trigger'+(index+1);
22341 if(this['hide'+triggerIndex]){
22342 t.dom.style.display = 'none';
22344 t.on("click", this['on'+triggerIndex+'Click'], this, {preventDefault:true});
22345 t.addClassOnOver('x-form-trigger-over');
22346 t.addClassOnClick('x-form-trigger-click');
22348 this.triggers = ts.elements;
22351 onTrigger1Click : Roo.emptyFn,
22352 onTrigger2Click : Roo.emptyFn
22355 * Ext JS Library 1.1.1
22356 * Copyright(c) 2006-2007, Ext JS, LLC.
22358 * Originally Released Under LGPL - original licence link has changed is not relivant.
22361 * <script type="text/javascript">
22365 * @class Roo.form.TextArea
22366 * @extends Roo.form.TextField
22367 * Multiline text field. Can be used as a direct replacement for traditional textarea fields, plus adds
22368 * support for auto-sizing.
22370 * Creates a new TextArea
22371 * @param {Object} config Configuration options
22373 Roo.form.TextArea = function(config){
22374 Roo.form.TextArea.superclass.constructor.call(this, config);
22375 // these are provided exchanges for backwards compat
22376 // minHeight/maxHeight were replaced by growMin/growMax to be
22377 // compatible with TextField growing config values
22378 if(this.minHeight !== undefined){
22379 this.growMin = this.minHeight;
22381 if(this.maxHeight !== undefined){
22382 this.growMax = this.maxHeight;
22386 Roo.extend(Roo.form.TextArea, Roo.form.TextField, {
22388 * @cfg {Number} growMin The minimum height to allow when grow = true (defaults to 60)
22392 * @cfg {Number} growMax The maximum height to allow when grow = true (defaults to 1000)
22396 * @cfg {Boolean} preventScrollbars True to prevent scrollbars from appearing regardless of how much text is
22397 * in the field (equivalent to setting overflow: hidden, defaults to false)
22399 preventScrollbars: false,
22401 * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
22402 * {tag: "textarea", style: "width:300px;height:60px;", autocomplete: "off"})
22406 onRender : function(ct, position){
22408 this.defaultAutoCreate = {
22410 style:"width:300px;height:60px;",
22411 autocomplete: "off"
22414 Roo.form.TextArea.superclass.onRender.call(this, ct, position);
22416 this.textSizeEl = Roo.DomHelper.append(document.body, {
22417 tag: "pre", cls: "x-form-grow-sizer"
22419 if(this.preventScrollbars){
22420 this.el.setStyle("overflow", "hidden");
22422 this.el.setHeight(this.growMin);
22426 onDestroy : function(){
22427 if(this.textSizeEl){
22428 this.textSizeEl.parentNode.removeChild(this.textSizeEl);
22430 Roo.form.TextArea.superclass.onDestroy.call(this);
22434 onKeyUp : function(e){
22435 if(!e.isNavKeyPress() || e.getKey() == e.ENTER){
22441 * Automatically grows the field to accomodate the height of the text up to the maximum field height allowed.
22442 * This only takes effect if grow = true, and fires the autosize event if the height changes.
22444 autoSize : function(){
22445 if(!this.grow || !this.textSizeEl){
22449 var v = el.dom.value;
22450 var ts = this.textSizeEl;
22453 ts.appendChild(document.createTextNode(v));
22456 Roo.fly(ts).setWidth(this.el.getWidth());
22458 v = "  ";
22461 v = v.replace(/\n/g, '<p> </p>');
22463 v += " \n ";
22466 var h = Math.min(this.growMax, Math.max(ts.offsetHeight, this.growMin));
22467 if(h != this.lastHeight){
22468 this.lastHeight = h;
22469 this.el.setHeight(h);
22470 this.fireEvent("autosize", this, h);
22475 * Ext JS Library 1.1.1
22476 * Copyright(c) 2006-2007, Ext JS, LLC.
22478 * Originally Released Under LGPL - original licence link has changed is not relivant.
22481 * <script type="text/javascript">
22486 * @class Roo.form.NumberField
22487 * @extends Roo.form.TextField
22488 * Numeric text field that provides automatic keystroke filtering and numeric validation.
22490 * Creates a new NumberField
22491 * @param {Object} config Configuration options
22493 Roo.form.NumberField = function(config){
22494 Roo.form.NumberField.superclass.constructor.call(this, config);
22497 Roo.extend(Roo.form.NumberField, Roo.form.TextField, {
22499 * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field x-form-num-field")
22501 fieldClass: "x-form-field x-form-num-field",
22503 * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
22505 allowDecimals : true,
22507 * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
22509 decimalSeparator : ".",
22511 * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
22513 decimalPrecision : 2,
22515 * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
22517 allowNegative : true,
22519 * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
22521 minValue : Number.NEGATIVE_INFINITY,
22523 * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
22525 maxValue : Number.MAX_VALUE,
22527 * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
22529 minText : "The minimum value for this field is {0}",
22531 * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
22533 maxText : "The maximum value for this field is {0}",
22535 * @cfg {String} nanText Error text to display if the value is not a valid number. For example, this can happen
22536 * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
22538 nanText : "{0} is not a valid number",
22541 initEvents : function(){
22542 Roo.form.NumberField.superclass.initEvents.call(this);
22543 var allowed = "0123456789";
22544 if(this.allowDecimals){
22545 allowed += this.decimalSeparator;
22547 if(this.allowNegative){
22550 this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
22551 var keyPress = function(e){
22552 var k = e.getKey();
22553 if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
22556 var c = e.getCharCode();
22557 if(allowed.indexOf(String.fromCharCode(c)) === -1){
22561 this.el.on("keypress", keyPress, this);
22565 validateValue : function(value){
22566 if(!Roo.form.NumberField.superclass.validateValue.call(this, value)){
22569 if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
22572 var num = this.parseValue(value);
22574 this.markInvalid(String.format(this.nanText, value));
22577 if(num < this.minValue){
22578 this.markInvalid(String.format(this.minText, this.minValue));
22581 if(num > this.maxValue){
22582 this.markInvalid(String.format(this.maxText, this.maxValue));
22588 getValue : function(){
22589 return this.fixPrecision(this.parseValue(Roo.form.NumberField.superclass.getValue.call(this)));
22593 parseValue : function(value){
22594 value = parseFloat(String(value).replace(this.decimalSeparator, "."));
22595 return isNaN(value) ? '' : value;
22599 fixPrecision : function(value){
22600 var nan = isNaN(value);
22601 if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
22602 return nan ? '' : value;
22604 return parseFloat(value).toFixed(this.decimalPrecision);
22607 setValue : function(v){
22608 v = this.fixPrecision(v);
22609 Roo.form.NumberField.superclass.setValue.call(this, String(v).replace(".", this.decimalSeparator));
22613 decimalPrecisionFcn : function(v){
22614 return Math.floor(v);
22617 beforeBlur : function(){
22618 var v = this.parseValue(this.getRawValue());
22625 * Ext JS Library 1.1.1
22626 * Copyright(c) 2006-2007, Ext JS, LLC.
22628 * Originally Released Under LGPL - original licence link has changed is not relivant.
22631 * <script type="text/javascript">
22635 * @class Roo.form.DateField
22636 * @extends Roo.form.TriggerField
22637 * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
22639 * Create a new DateField
22640 * @param {Object} config
22642 Roo.form.DateField = function(config){
22643 Roo.form.DateField.superclass.constructor.call(this, config);
22649 * Fires when a date is selected
22650 * @param {Roo.form.DateField} combo This combo box
22651 * @param {Date} date The date selected
22658 if(typeof this.minValue == "string") this.minValue = this.parseDate(this.minValue);
22659 if(typeof this.maxValue == "string") this.maxValue = this.parseDate(this.maxValue);
22660 this.ddMatch = null;
22661 if(this.disabledDates){
22662 var dd = this.disabledDates;
22664 for(var i = 0; i < dd.length; i++){
22666 if(i != dd.length-1) re += "|";
22668 this.ddMatch = new RegExp(re + ")");
22672 Roo.extend(Roo.form.DateField, Roo.form.TriggerField, {
22674 * @cfg {String} format
22675 * The default date format string which can be overriden for localization support. The format must be
22676 * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
22680 * @cfg {String} altFormats
22681 * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
22682 * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
22684 altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
22686 * @cfg {Array} disabledDays
22687 * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
22689 disabledDays : null,
22691 * @cfg {String} disabledDaysText
22692 * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
22694 disabledDaysText : "Disabled",
22696 * @cfg {Array} disabledDates
22697 * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
22698 * expression so they are very powerful. Some examples:
22700 * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
22701 * <li>["03/08", "09/16"] would disable those days for every year</li>
22702 * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
22703 * <li>["03/../2006"] would disable every day in March 2006</li>
22704 * <li>["^03"] would disable every day in every March</li>
22706 * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
22707 * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
22709 disabledDates : null,
22711 * @cfg {String} disabledDatesText
22712 * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
22714 disabledDatesText : "Disabled",
22716 * @cfg {Date/String} minValue
22717 * The minimum allowed date. Can be either a Javascript date object or a string date in a
22718 * valid format (defaults to null).
22722 * @cfg {Date/String} maxValue
22723 * The maximum allowed date. Can be either a Javascript date object or a string date in a
22724 * valid format (defaults to null).
22728 * @cfg {String} minText
22729 * The error text to display when the date in the cell is before minValue (defaults to
22730 * 'The date in this field must be after {minValue}').
22732 minText : "The date in this field must be equal to or after {0}",
22734 * @cfg {String} maxText
22735 * The error text to display when the date in the cell is after maxValue (defaults to
22736 * 'The date in this field must be before {maxValue}').
22738 maxText : "The date in this field must be equal to or before {0}",
22740 * @cfg {String} invalidText
22741 * The error text to display when the date in the field is invalid (defaults to
22742 * '{value} is not a valid date - it must be in the format {format}').
22744 invalidText : "{0} is not a valid date - it must be in the format {1}",
22746 * @cfg {String} triggerClass
22747 * An additional CSS class used to style the trigger button. The trigger will always get the
22748 * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
22749 * which displays a calendar icon).
22751 triggerClass : 'x-form-date-trigger',
22755 * @cfg {bool} useIso
22756 * if enabled, then the date field will use a hidden field to store the
22757 * real value as iso formated date. default (false)
22761 * @cfg {String/Object} autoCreate
22762 * A DomHelper element spec, or true for a default element spec (defaults to
22763 * {tag: "input", type: "text", size: "10", autocomplete: "off"})
22766 defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "off"},
22769 hiddenField: false,
22771 onRender : function(ct, position)
22773 Roo.form.DateField.superclass.onRender.call(this, ct, position);
22775 this.el.dom.removeAttribute('name');
22776 this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
22778 this.hiddenField.value = this.formatDate(this.value, 'Y-m-d');
22779 // prevent input submission
22780 this.hiddenName = this.name;
22787 validateValue : function(value)
22789 value = this.formatDate(value);
22790 if(!Roo.form.DateField.superclass.validateValue.call(this, value)){
22793 if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
22796 var svalue = value;
22797 value = this.parseDate(value);
22799 this.markInvalid(String.format(this.invalidText, svalue, this.format));
22802 var time = value.getTime();
22803 if(this.minValue && time < this.minValue.getTime()){
22804 this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
22807 if(this.maxValue && time > this.maxValue.getTime()){
22808 this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
22811 if(this.disabledDays){
22812 var day = value.getDay();
22813 for(var i = 0; i < this.disabledDays.length; i++) {
22814 if(day === this.disabledDays[i]){
22815 this.markInvalid(this.disabledDaysText);
22820 var fvalue = this.formatDate(value);
22821 if(this.ddMatch && this.ddMatch.test(fvalue)){
22822 this.markInvalid(String.format(this.disabledDatesText, fvalue));
22829 // Provides logic to override the default TriggerField.validateBlur which just returns true
22830 validateBlur : function(){
22831 return !this.menu || !this.menu.isVisible();
22835 * Returns the current date value of the date field.
22836 * @return {Date} The date value
22838 getValue : function(){
22840 return this.hiddenField ? this.hiddenField.value : this.parseDate(Roo.form.DateField.superclass.getValue.call(this)) || "";
22844 * Sets the value of the date field. You can pass a date object or any string that can be parsed into a valid
22845 * date, using DateField.format as the date format, according to the same rules as {@link Date#parseDate}
22846 * (the default format used is "m/d/y").
22849 //All of these calls set the same date value (May 4, 2006)
22851 //Pass a date object:
22852 var dt = new Date('5/4/06');
22853 dateField.setValue(dt);
22855 //Pass a date string (default format):
22856 dateField.setValue('5/4/06');
22858 //Pass a date string (custom format):
22859 dateField.format = 'Y-m-d';
22860 dateField.setValue('2006-5-4');
22862 * @param {String/Date} date The date or valid date string
22864 setValue : function(date){
22865 if (this.hiddenField) {
22866 this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
22868 Roo.form.DateField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
22872 parseDate : function(value){
22873 if(!value || value instanceof Date){
22876 var v = Date.parseDate(value, this.format);
22877 if(!v && this.altFormats){
22878 if(!this.altFormatsArray){
22879 this.altFormatsArray = this.altFormats.split("|");
22881 for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
22882 v = Date.parseDate(value, this.altFormatsArray[i]);
22889 formatDate : function(date, fmt){
22890 return (!date || !(date instanceof Date)) ?
22891 date : date.dateFormat(fmt || this.format);
22896 select: function(m, d){
22898 this.fireEvent('select', this, d);
22900 show : function(){ // retain focus styling
22904 this.focus.defer(10, this);
22905 var ml = this.menuListeners;
22906 this.menu.un("select", ml.select, this);
22907 this.menu.un("show", ml.show, this);
22908 this.menu.un("hide", ml.hide, this);
22913 // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
22914 onTriggerClick : function(){
22918 if(this.menu == null){
22919 this.menu = new Roo.menu.DateMenu();
22921 Roo.apply(this.menu.picker, {
22922 showClear: this.allowBlank,
22923 minDate : this.minValue,
22924 maxDate : this.maxValue,
22925 disabledDatesRE : this.ddMatch,
22926 disabledDatesText : this.disabledDatesText,
22927 disabledDays : this.disabledDays,
22928 disabledDaysText : this.disabledDaysText,
22929 format : this.format,
22930 minText : String.format(this.minText, this.formatDate(this.minValue)),
22931 maxText : String.format(this.maxText, this.formatDate(this.maxValue))
22933 this.menu.on(Roo.apply({}, this.menuListeners, {
22936 this.menu.picker.setValue(this.getValue() || new Date());
22937 this.menu.show(this.el, "tl-bl?");
22940 beforeBlur : function(){
22941 var v = this.parseDate(this.getRawValue());
22947 /** @cfg {Boolean} grow @hide */
22948 /** @cfg {Number} growMin @hide */
22949 /** @cfg {Number} growMax @hide */
22956 * Ext JS Library 1.1.1
22957 * Copyright(c) 2006-2007, Ext JS, LLC.
22959 * Originally Released Under LGPL - original licence link has changed is not relivant.
22962 * <script type="text/javascript">
22967 * @class Roo.form.ComboBox
22968 * @extends Roo.form.TriggerField
22969 * A combobox control with support for autocomplete, remote-loading, paging and many other features.
22971 * Create a new ComboBox.
22972 * @param {Object} config Configuration options
22974 Roo.form.ComboBox = function(config){
22975 Roo.form.ComboBox.superclass.constructor.call(this, config);
22979 * Fires when the dropdown list is expanded
22980 * @param {Roo.form.ComboBox} combo This combo box
22985 * Fires when the dropdown list is collapsed
22986 * @param {Roo.form.ComboBox} combo This combo box
22990 * @event beforeselect
22991 * Fires before a list item is selected. Return false to cancel the selection.
22992 * @param {Roo.form.ComboBox} combo This combo box
22993 * @param {Roo.data.Record} record The data record returned from the underlying store
22994 * @param {Number} index The index of the selected item in the dropdown list
22996 'beforeselect' : true,
22999 * Fires when a list item is selected
23000 * @param {Roo.form.ComboBox} combo This combo box
23001 * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
23002 * @param {Number} index The index of the selected item in the dropdown list
23006 * @event beforequery
23007 * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
23008 * The event object passed has these properties:
23009 * @param {Roo.form.ComboBox} combo This combo box
23010 * @param {String} query The query
23011 * @param {Boolean} forceAll true to force "all" query
23012 * @param {Boolean} cancel true to cancel the query
23013 * @param {Object} e The query event object
23015 'beforequery': true,
23018 * Fires when the 'add' icon is pressed (add a listener to enable add button)
23019 * @param {Roo.form.ComboBox} combo This combo box
23024 * Fires when the 'edit' icon is pressed (add a listener to enable add button)
23025 * @param {Roo.form.ComboBox} combo This combo box
23026 * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
23032 if(this.transform){
23033 this.allowDomMove = false;
23034 var s = Roo.getDom(this.transform);
23035 if(!this.hiddenName){
23036 this.hiddenName = s.name;
23039 this.mode = 'local';
23040 var d = [], opts = s.options;
23041 for(var i = 0, len = opts.length;i < len; i++){
23043 var value = (Roo.isIE ? o.getAttributeNode('value').specified : o.hasAttribute('value')) ? o.value : o.text;
23045 this.value = value;
23047 d.push([value, o.text]);
23049 this.store = new Roo.data.SimpleStore({
23051 fields: ['value', 'text'],
23054 this.valueField = 'value';
23055 this.displayField = 'text';
23057 s.name = Roo.id(); // wipe out the name in case somewhere else they have a reference
23058 if(!this.lazyRender){
23059 this.target = true;
23060 this.el = Roo.DomHelper.insertBefore(s, this.autoCreate || this.defaultAutoCreate);
23061 s.parentNode.removeChild(s); // remove it
23062 this.render(this.el.parentNode);
23064 s.parentNode.removeChild(s); // remove it
23069 this.store = Roo.factory(this.store, Roo.data);
23072 this.selectedIndex = -1;
23073 if(this.mode == 'local'){
23074 if(config.queryDelay === undefined){
23075 this.queryDelay = 10;
23077 if(config.minChars === undefined){
23083 Roo.extend(Roo.form.ComboBox, Roo.form.TriggerField, {
23085 * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
23088 * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
23089 * rendering into an Roo.Editor, defaults to false)
23092 * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
23093 * {tag: "input", type: "text", size: "24", autocomplete: "off"})
23096 * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
23099 * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
23100 * the dropdown list (defaults to undefined, with no header element)
23104 * @cfg {String/Roo.Template} tpl The template to use to render the output
23108 defaultAutoCreate : {tag: "input", type: "text", size: "24", autocomplete: "off"},
23110 * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
23112 listWidth: undefined,
23114 * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
23115 * mode = 'remote' or 'text' if mode = 'local')
23117 displayField: undefined,
23119 * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
23120 * mode = 'remote' or 'value' if mode = 'local').
23121 * Note: use of a valueField requires the user make a selection
23122 * in order for a value to be mapped.
23124 valueField: undefined,
23128 * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
23129 * field's data value (defaults to the underlying DOM element's name)
23131 hiddenName: undefined,
23133 * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
23137 * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
23139 selectedClass: 'x-combo-selected',
23141 * @cfg {String} triggerClass An additional CSS class used to style the trigger button. The trigger will always get the
23142 * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
23143 * which displays a downward arrow icon).
23145 triggerClass : 'x-form-arrow-trigger',
23147 * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
23151 * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
23152 * anchor positions (defaults to 'tl-bl')
23154 listAlign: 'tl-bl?',
23156 * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
23160 * @cfg {String} triggerAction The action to execute when the trigger field is activated. Use 'all' to run the
23161 * query specified by the allQuery config option (defaults to 'query')
23163 triggerAction: 'query',
23165 * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
23166 * (defaults to 4, does not apply if editable = false)
23170 * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
23171 * delay (typeAheadDelay) if it matches a known value (defaults to false)
23175 * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
23176 * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
23180 * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
23181 * filter queries will execute with page start and limit parameters. Only applies when mode = 'remote' (defaults to 0)
23185 * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus. Only applies
23186 * when editable = true (defaults to false)
23188 selectOnFocus:false,
23190 * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
23192 queryParam: 'query',
23194 * @cfg {String} loadingText The text to display in the dropdown list while data is loading. Only applies
23195 * when mode = 'remote' (defaults to 'Loading...')
23197 loadingText: 'Loading...',
23199 * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
23203 * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
23207 * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
23208 * traditional select (defaults to true)
23212 * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
23216 * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
23220 * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
23221 * listWidth has a higher value)
23225 * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
23226 * allow the user to set arbitrary text into the field (defaults to false)
23228 forceSelection:false,
23230 * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
23231 * if typeAhead = true (defaults to 250)
23233 typeAheadDelay : 250,
23235 * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
23236 * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
23238 valueNotFoundText : undefined,
23240 * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
23242 blockFocus : false,
23245 * @cfg {Boolean} disableClear Disable showing of clear button.
23247 disableClear : false,
23249 * @cfg {Boolean} alwaysQuery Disable caching of results, and always send query
23251 alwaysQuery : false,
23257 // element that contains real text value.. (when hidden is used..)
23260 onRender : function(ct, position){
23261 Roo.form.ComboBox.superclass.onRender.call(this, ct, position);
23262 if(this.hiddenName){
23263 this.hiddenField = this.el.insertSibling({tag:'input', type:'hidden', name: this.hiddenName, id: (this.hiddenId||this.hiddenName)},
23265 this.hiddenField.value =
23266 this.hiddenValue !== undefined ? this.hiddenValue :
23267 this.value !== undefined ? this.value : '';
23269 // prevent input submission
23270 this.el.dom.removeAttribute('name');
23275 this.el.dom.setAttribute('autocomplete', 'off');
23278 var cls = 'x-combo-list';
23280 this.list = new Roo.Layer({
23281 shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
23284 var lw = this.listWidth || Math.max(this.wrap.getWidth(), this.minListWidth);
23285 this.list.setWidth(lw);
23286 this.list.swallowEvent('mousewheel');
23287 this.assetHeight = 0;
23290 this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
23291 this.assetHeight += this.header.getHeight();
23294 this.innerList = this.list.createChild({cls:cls+'-inner'});
23295 this.innerList.on('mouseover', this.onViewOver, this);
23296 this.innerList.on('mousemove', this.onViewMove, this);
23297 this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
23299 if(this.allowBlank && !this.pageSize && !this.disableClear){
23300 this.footer = this.list.createChild({cls:cls+'-ft'});
23301 this.pageTb = new Roo.Toolbar(this.footer);
23305 this.footer = this.list.createChild({cls:cls+'-ft'});
23306 this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
23307 {pageSize: this.pageSize});
23311 if (this.pageTb && this.allowBlank && !this.disableClear) {
23313 this.pageTb.add(new Roo.Toolbar.Fill(), {
23314 cls: 'x-btn-icon x-btn-clear',
23316 handler: function()
23319 _this.clearValue();
23320 _this.onSelect(false, -1);
23325 this.assetHeight += this.footer.getHeight();
23330 this.tpl = '<div class="'+cls+'-item">{' + this.displayField + '}</div>';
23333 this.view = new Roo.View(this.innerList, this.tpl, {
23334 singleSelect:true, store: this.store, selectedClass: this.selectedClass
23337 this.view.on('click', this.onViewClick, this);
23339 this.store.on('beforeload', this.onBeforeLoad, this);
23340 this.store.on('load', this.onLoad, this);
23341 this.store.on('loadexception', this.onLoadException, this);
23343 if(this.resizable){
23344 this.resizer = new Roo.Resizable(this.list, {
23345 pinned:true, handles:'se'
23347 this.resizer.on('resize', function(r, w, h){
23348 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
23349 this.listWidth = w;
23350 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
23351 this.restrictHeight();
23353 this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
23355 if(!this.editable){
23356 this.editable = true;
23357 this.setEditable(false);
23361 if (typeof(this.events.add.listeners) != 'undefined') {
23363 this.addicon = this.wrap.createChild(
23364 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });
23366 this.addicon.on('click', function(e) {
23367 this.fireEvent('add', this);
23370 if (typeof(this.events.edit.listeners) != 'undefined') {
23372 this.editicon = this.wrap.createChild(
23373 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });
23374 if (this.addicon) {
23375 this.editicon.setStyle('margin-left', '40px');
23377 this.editicon.on('click', function(e) {
23379 // we fire even if inothing is selected..
23380 this.fireEvent('edit', this, this.lastData );
23390 initEvents : function(){
23391 Roo.form.ComboBox.superclass.initEvents.call(this);
23393 this.keyNav = new Roo.KeyNav(this.el, {
23394 "up" : function(e){
23395 this.inKeyMode = true;
23399 "down" : function(e){
23400 if(!this.isExpanded()){
23401 this.onTriggerClick();
23403 this.inKeyMode = true;
23408 "enter" : function(e){
23409 this.onViewClick();
23413 "esc" : function(e){
23417 "tab" : function(e){
23418 this.onViewClick(false);
23419 this.fireEvent("specialkey", this, e);
23425 doRelay : function(foo, bar, hname){
23426 if(hname == 'down' || this.scope.isExpanded()){
23427 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
23434 this.queryDelay = Math.max(this.queryDelay || 10,
23435 this.mode == 'local' ? 10 : 250);
23436 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
23437 if(this.typeAhead){
23438 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
23440 if(this.editable !== false){
23441 this.el.on("keyup", this.onKeyUp, this);
23443 if(this.forceSelection){
23444 this.on('blur', this.doForce, this);
23448 onDestroy : function(){
23450 this.view.setStore(null);
23451 this.view.el.removeAllListeners();
23452 this.view.el.remove();
23453 this.view.purgeListeners();
23456 this.list.destroy();
23459 this.store.un('beforeload', this.onBeforeLoad, this);
23460 this.store.un('load', this.onLoad, this);
23461 this.store.un('loadexception', this.onLoadException, this);
23463 Roo.form.ComboBox.superclass.onDestroy.call(this);
23467 fireKey : function(e){
23468 if(e.isNavKeyPress() && !this.list.isVisible()){
23469 this.fireEvent("specialkey", this, e);
23474 onResize: function(w, h){
23475 Roo.form.ComboBox.superclass.onResize.apply(this, arguments);
23477 if(typeof w != 'number'){
23478 // we do not handle it!?!?
23481 var tw = this.trigger.getWidth();
23482 tw += this.addicon ? this.addicon.getWidth() : 0;
23483 tw += this.editicon ? this.editicon.getWidth() : 0;
23485 this.el.setWidth( this.adjustWidth('input', x));
23487 this.trigger.setStyle('left', x+'px');
23489 if(this.list && this.listWidth === undefined){
23490 var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
23491 this.list.setWidth(lw);
23492 this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
23500 * Allow or prevent the user from directly editing the field text. If false is passed,
23501 * the user will only be able to select from the items defined in the dropdown list. This method
23502 * is the runtime equivalent of setting the 'editable' config option at config time.
23503 * @param {Boolean} value True to allow the user to directly edit the field text
23505 setEditable : function(value){
23506 if(value == this.editable){
23509 this.editable = value;
23511 this.el.dom.setAttribute('readOnly', true);
23512 this.el.on('mousedown', this.onTriggerClick, this);
23513 this.el.addClass('x-combo-noedit');
23515 this.el.dom.setAttribute('readOnly', false);
23516 this.el.un('mousedown', this.onTriggerClick, this);
23517 this.el.removeClass('x-combo-noedit');
23522 onBeforeLoad : function(){
23523 if(!this.hasFocus){
23526 this.innerList.update(this.loadingText ?
23527 '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
23528 this.restrictHeight();
23529 this.selectedIndex = -1;
23533 onLoad : function(){
23534 if(!this.hasFocus){
23537 if(this.store.getCount() > 0){
23539 this.restrictHeight();
23540 if(this.lastQuery == this.allQuery){
23542 this.el.dom.select();
23544 if(!this.selectByValue(this.value, true)){
23545 this.select(0, true);
23549 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
23550 this.taTask.delay(this.typeAheadDelay);
23554 this.onEmptyResults();
23559 onLoadException : function()
23562 Roo.log(this.store.reader.jsonData);
23563 if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
23564 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
23570 onTypeAhead : function(){
23571 if(this.store.getCount() > 0){
23572 var r = this.store.getAt(0);
23573 var newValue = r.data[this.displayField];
23574 var len = newValue.length;
23575 var selStart = this.getRawValue().length;
23576 if(selStart != len){
23577 this.setRawValue(newValue);
23578 this.selectText(selStart, newValue.length);
23584 onSelect : function(record, index){
23585 if(this.fireEvent('beforeselect', this, record, index) !== false){
23586 this.setFromData(index > -1 ? record.data : false);
23588 this.fireEvent('select', this, record, index);
23593 * Returns the currently selected field value or empty string if no value is set.
23594 * @return {String} value The selected value
23596 getValue : function(){
23597 if(this.valueField){
23598 return typeof this.value != 'undefined' ? this.value : '';
23600 return Roo.form.ComboBox.superclass.getValue.call(this);
23605 * Clears any text/value currently set in the field
23607 clearValue : function(){
23608 if(this.hiddenField){
23609 this.hiddenField.value = '';
23612 this.setRawValue('');
23613 this.lastSelectionText = '';
23614 this.applyEmptyText();
23618 * Sets the specified value into the field. If the value finds a match, the corresponding record text
23619 * will be displayed in the field. If the value does not match the data value of an existing item,
23620 * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
23621 * Otherwise the field will be blank (although the value will still be set).
23622 * @param {String} value The value to match
23624 setValue : function(v){
23626 if(this.valueField){
23627 var r = this.findRecord(this.valueField, v);
23629 text = r.data[this.displayField];
23630 }else if(this.valueNotFoundText !== undefined){
23631 text = this.valueNotFoundText;
23634 this.lastSelectionText = text;
23635 if(this.hiddenField){
23636 this.hiddenField.value = v;
23638 Roo.form.ComboBox.superclass.setValue.call(this, text);
23642 * @property {Object} the last set data for the element
23647 * Sets the value of the field based on a object which is related to the record format for the store.
23648 * @param {Object} value the value to set as. or false on reset?
23650 setFromData : function(o){
23651 var dv = ''; // display value
23652 var vv = ''; // value value..
23654 if (this.displayField) {
23655 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
23657 // this is an error condition!!!
23658 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
23661 if(this.valueField){
23662 vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
23664 if(this.hiddenField){
23665 this.hiddenField.value = vv;
23667 this.lastSelectionText = dv;
23668 Roo.form.ComboBox.superclass.setValue.call(this, dv);
23672 // no hidden field.. - we store the value in 'value', but still display
23673 // display field!!!!
23674 this.lastSelectionText = dv;
23675 Roo.form.ComboBox.superclass.setValue.call(this, dv);
23681 reset : function(){
23682 // overridden so that last data is reset..
23683 this.setValue(this.originalValue);
23684 this.clearInvalid();
23685 this.lastData = false;
23688 findRecord : function(prop, value){
23690 if(this.store.getCount() > 0){
23691 this.store.each(function(r){
23692 if(r.data[prop] == value){
23702 getName: function()
23704 // returns hidden if it's set..
23705 if (!this.rendered) {return ''};
23706 return !this.hiddenName && this.el.dom.name ? this.el.dom.name : (this.hiddenName || '');
23710 onViewMove : function(e, t){
23711 this.inKeyMode = false;
23715 onViewOver : function(e, t){
23716 if(this.inKeyMode){ // prevent key nav and mouse over conflicts
23719 var item = this.view.findItemFromChild(t);
23721 var index = this.view.indexOf(item);
23722 this.select(index, false);
23727 onViewClick : function(doFocus)
23729 var index = this.view.getSelectedIndexes()[0];
23730 var r = this.store.getAt(index);
23732 this.onSelect(r, index);
23734 if(doFocus !== false && !this.blockFocus){
23740 restrictHeight : function(){
23741 this.innerList.dom.style.height = '';
23742 var inner = this.innerList.dom;
23743 var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
23744 this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
23745 this.list.beginUpdate();
23746 this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
23747 this.list.alignTo(this.el, this.listAlign);
23748 this.list.endUpdate();
23752 onEmptyResults : function(){
23757 * Returns true if the dropdown list is expanded, else false.
23759 isExpanded : function(){
23760 return this.list.isVisible();
23764 * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
23765 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
23766 * @param {String} value The data value of the item to select
23767 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
23768 * selected item if it is not currently in view (defaults to true)
23769 * @return {Boolean} True if the value matched an item in the list, else false
23771 selectByValue : function(v, scrollIntoView){
23772 if(v !== undefined && v !== null){
23773 var r = this.findRecord(this.valueField || this.displayField, v);
23775 this.select(this.store.indexOf(r), scrollIntoView);
23783 * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
23784 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
23785 * @param {Number} index The zero-based index of the list item to select
23786 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
23787 * selected item if it is not currently in view (defaults to true)
23789 select : function(index, scrollIntoView){
23790 this.selectedIndex = index;
23791 this.view.select(index);
23792 if(scrollIntoView !== false){
23793 var el = this.view.getNode(index);
23795 this.innerList.scrollChildIntoView(el, false);
23801 selectNext : function(){
23802 var ct = this.store.getCount();
23804 if(this.selectedIndex == -1){
23806 }else if(this.selectedIndex < ct-1){
23807 this.select(this.selectedIndex+1);
23813 selectPrev : function(){
23814 var ct = this.store.getCount();
23816 if(this.selectedIndex == -1){
23818 }else if(this.selectedIndex != 0){
23819 this.select(this.selectedIndex-1);
23825 onKeyUp : function(e){
23826 if(this.editable !== false && !e.isSpecialKey()){
23827 this.lastKey = e.getKey();
23828 this.dqTask.delay(this.queryDelay);
23833 validateBlur : function(){
23834 return !this.list || !this.list.isVisible();
23838 initQuery : function(){
23839 this.doQuery(this.getRawValue());
23843 doForce : function(){
23844 if(this.el.dom.value.length > 0){
23845 this.el.dom.value =
23846 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
23847 this.applyEmptyText();
23852 * Execute a query to filter the dropdown list. Fires the beforequery event prior to performing the
23853 * query allowing the query action to be canceled if needed.
23854 * @param {String} query The SQL query to execute
23855 * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
23856 * in the field than the minimum specified by the minChars config option. It also clears any filter previously
23857 * saved in the current store (defaults to false)
23859 doQuery : function(q, forceAll){
23860 if(q === undefined || q === null){
23865 forceAll: forceAll,
23869 if(this.fireEvent('beforequery', qe)===false || qe.cancel){
23873 forceAll = qe.forceAll;
23874 if(forceAll === true || (q.length >= this.minChars)){
23875 if(this.lastQuery != q || this.alwaysQuery){
23876 this.lastQuery = q;
23877 if(this.mode == 'local'){
23878 this.selectedIndex = -1;
23880 this.store.clearFilter();
23882 this.store.filter(this.displayField, q);
23886 this.store.baseParams[this.queryParam] = q;
23888 params: this.getParams(q)
23893 this.selectedIndex = -1;
23900 getParams : function(q){
23902 //p[this.queryParam] = q;
23905 p.limit = this.pageSize;
23911 * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
23913 collapse : function(){
23914 if(!this.isExpanded()){
23918 Roo.get(document).un('mousedown', this.collapseIf, this);
23919 Roo.get(document).un('mousewheel', this.collapseIf, this);
23920 if (!this.editable) {
23921 Roo.get(document).un('keydown', this.listKeyPress, this);
23923 this.fireEvent('collapse', this);
23927 collapseIf : function(e){
23928 if(!e.within(this.wrap) && !e.within(this.list)){
23934 * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
23936 expand : function(){
23937 if(this.isExpanded() || !this.hasFocus){
23940 this.list.alignTo(this.el, this.listAlign);
23942 Roo.get(document).on('mousedown', this.collapseIf, this);
23943 Roo.get(document).on('mousewheel', this.collapseIf, this);
23944 if (!this.editable) {
23945 Roo.get(document).on('keydown', this.listKeyPress, this);
23948 this.fireEvent('expand', this);
23952 // Implements the default empty TriggerField.onTriggerClick function
23953 onTriggerClick : function(){
23957 if(this.isExpanded()){
23959 if (!this.blockFocus) {
23964 this.hasFocus = true;
23965 if(this.triggerAction == 'all') {
23966 this.doQuery(this.allQuery, true);
23968 this.doQuery(this.getRawValue());
23970 if (!this.blockFocus) {
23975 listKeyPress : function(e)
23977 //Roo.log('listkeypress');
23978 // scroll to first matching element based on key pres..
23979 if (e.isSpecialKey()) {
23982 var k = String.fromCharCode(e.getKey()).toUpperCase();
23985 var csel = this.view.getSelectedNodes();
23986 var cselitem = false;
23988 var ix = this.view.indexOf(csel[0]);
23989 cselitem = this.store.getAt(ix);
23990 if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
23996 this.store.each(function(v) {
23998 // start at existing selection.
23999 if (cselitem.id == v.id) {
24005 if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
24006 match = this.store.indexOf(v);
24011 if (match === false) {
24012 return true; // no more action?
24015 this.view.select(match);
24016 var sn = Roo.get(this.view.getSelectedNodes()[0])
24017 sn.scrollIntoView(sn.dom.parentNode, false);
24021 * @cfg {Boolean} grow
24025 * @cfg {Number} growMin
24029 * @cfg {Number} growMax
24037 * Copyright(c) 2010-2012, Roo J Solutions Limited
24044 * @class Roo.form.ComboBoxArray
24045 * @extends Roo.form.TextField
24046 * A facebook style adder... for lists of email / people / countries etc...
24047 * pick multiple items from a combo box, and shows each one.
24049 * Fred [x] Brian [x] [Pick another |v]
24052 * For this to work: it needs various extra information
24053 * - normal combo problay has
24055 * + displayField, valueField
24057 * For our purpose...
24060 * If we change from 'extends' to wrapping...
24067 * Create a new ComboBoxArray.
24068 * @param {Object} config Configuration options
24072 Roo.form.ComboBoxArray = function(config)
24075 Roo.form.ComboBoxArray.superclass.constructor.call(this, config);
24077 this.items = new Roo.util.MixedCollection(false);
24079 // construct the child combo...
24089 Roo.extend(Roo.form.ComboBoxArray, Roo.form.TextField,
24092 * @cfg {Roo.form.Combo} combo The combo box that is wrapped
24097 // behavies liek a hiddne field
24098 inputType: 'hidden',
24100 * @cfg {Number} width The width of the box that displays the selected element
24107 * @cfg {String} name The name of the visable items on this form (eg. titles not ids)
24111 * @cfg {String} hiddenName The hidden name of the field, often contains an comma seperated list of names
24113 hiddenName : false,
24116 // private the array of items that are displayed..
24118 // private - the hidden field el.
24120 // private - the filed el..
24123 //validateValue : function() { return true; }, // all values are ok!
24124 //onAddClick: function() { },
24126 onRender : function(ct, position)
24129 // create the standard hidden element
24130 //Roo.form.ComboBoxArray.superclass.onRender.call(this, ct, position);
24133 // give fake names to child combo;
24134 this.combo.hiddenName = this.hiddenName ? (this.hiddenName+'-subcombo') : this.hiddenName;
24135 this.combo.name = this.name? (this.name+'-subcombo') : this.name;
24137 this.combo = Roo.factory(this.combo, Roo.form);
24138 this.combo.onRender(ct, position);
24140 // assigned so form know we need to do this..
24141 this.store = this.combo.store;
24142 this.valueField = this.combo.valueField;
24143 this.displayField = this.combo.displayField ;
24146 this.combo.wrap.addClass('x-cbarray-grp');
24148 var cbwrap = this.combo.wrap.createChild(
24149 {tag: 'div', cls: 'x-cbarray-cb'},
24154 this.hiddenEl = this.combo.wrap.createChild({
24155 tag: 'input', type:'hidden' , name: this.hiddenName, value : ''
24157 this.el = this.combo.wrap.createChild({
24158 tag: 'input', type:'hidden' , name: this.name, value : ''
24160 // this.el.dom.removeAttribute("name");
24163 this.outerWrap = this.combo.wrap;
24164 this.wrap = cbwrap;
24166 this.outerWrap.setWidth(this.width);
24167 this.outerWrap.dom.removeChild(this.el.dom);
24169 this.wrap.dom.appendChild(this.el.dom);
24170 this.outerWrap.dom.removeChild(this.combo.trigger.dom);
24171 this.combo.wrap.dom.appendChild(this.combo.trigger.dom);
24173 this.combo.trigger.setStyle('position','relative');
24174 this.combo.trigger.setStyle('left', '0px');
24175 this.combo.trigger.setStyle('top', '2px');
24177 this.combo.el.setStyle('vertical-align', 'text-bottom');
24179 //this.trigger.setStyle('vertical-align', 'top');
24181 // this should use the code from combo really... on('add' ....)
24185 this.adder = this.outerWrap.createChild(
24186 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-adder', style: 'margin-left:2px'});
24188 this.adder.on('click', function(e) {
24189 _t.fireEvent('adderclick', this, e);
24193 //this.adder.on('click', this.onAddClick, _t);
24196 this.combo.on('select', function(cb, rec, ix) {
24197 this.addItem(rec.data);
24200 cb.el.dom.value = '';
24201 //cb.lastData = rec.data;
24210 getName: function()
24212 // returns hidden if it's set..
24213 if (!this.rendered) {return ''};
24214 return this.hiddenName ? this.hiddenName : this.name;
24219 onResize: function(w, h){
24222 // not sure if this is needed..
24223 //this.combo.onResize(w,h);
24225 if(typeof w != 'number'){
24226 // we do not handle it!?!?
24229 var tw = this.combo.trigger.getWidth();
24230 tw += this.addicon ? this.addicon.getWidth() : 0;
24231 tw += this.editicon ? this.editicon.getWidth() : 0;
24233 this.combo.el.setWidth( this.combo.adjustWidth('input', x));
24235 this.combo.trigger.setStyle('left', '0px');
24237 if(this.list && this.listWidth === undefined){
24238 var lw = Math.max(x + this.combo.trigger.getWidth(), this.combo.minListWidth);
24239 this.list.setWidth(lw);
24240 this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
24247 addItem: function(rec)
24249 var valueField = this.combo.valueField;
24250 var displayField = this.combo.displayField;
24251 if (this.items.indexOfKey(rec[valueField]) > -1) {
24252 //console.log("GOT " + rec.data.id);
24256 var x = new Roo.form.ComboBoxArray.Item({
24257 //id : rec[this.idField],
24259 displayField : displayField ,
24260 tipField : displayField ,
24264 this.items.add(rec[valueField],x);
24265 // add it before the element..
24266 this.updateHiddenEl();
24267 x.render(this.outerWrap, this.wrap.dom);
24268 // add the image handler..
24271 updateHiddenEl : function()
24274 if (!this.hiddenEl) {
24278 var idField = this.combo.valueField;
24280 this.items.each(function(f) {
24281 ar.push(f.data[idField]);
24284 this.hiddenEl.dom.value = ar.join(',');
24290 //Roo.form.ComboBoxArray.superclass.reset.call(this);
24291 this.items.each(function(f) {
24294 this.el.dom.value = '';
24295 if (this.hiddenEl) {
24296 this.hiddenEl.dom.value = '';
24300 getValue: function()
24302 return this.hiddenEl ? this.hiddenEl.dom.value : '';
24304 setValue: function(v) // not a valid action - must use addItems..
24311 if (this.store.isLocal && (typeof(v) == 'string')) {
24312 // then we can use the store to find the values..
24313 // comma seperated at present.. this needs to allow JSON based encoding..
24314 this.hiddenEl.value = v;
24316 Roo.each(v.split(','), function(k) {
24317 Roo.log("CHECK " + this.valueField + ',' + k);
24318 var li = this.store.query(this.valueField, k);
24323 add[this.valueField] = k;
24324 add[this.displayField] = li.item(0).data[this.displayField];
24330 if (typeof(v) == 'object') {
24331 // then let's assume it's an array of objects..
24332 Roo.each(v, function(l) {
24340 setFromData: function(v)
24342 // this recieves an object, if setValues is called.
24344 this.el.dom.value = v[this.displayField];
24345 this.hiddenEl.dom.value = v[this.valueField];
24346 if (typeof(v[this.valueField]) != 'string' || !v[this.valueField].length) {
24349 var kv = v[this.valueField];
24350 var dv = v[this.displayField];
24351 kv = typeof(kv) != 'string' ? '' : kv;
24352 dv = typeof(dv) != 'string' ? '' : dv;
24355 var keys = kv.split(',');
24356 var display = dv.split(',');
24357 for (var i = 0 ; i < keys.length; i++) {
24360 add[this.valueField] = keys[i];
24361 add[this.displayField] = display[i];
24369 validateValue : function(value){
24370 return Roo.form.ComboBoxArray.superclass.validateValue.call(this, this.getValue());
24379 * @class Roo.form.ComboBoxArray.Item
24380 * @extends Roo.BoxComponent
24381 * A selected item in the list
24382 * Fred [x] Brian [x] [Pick another |v]
24385 * Create a new item.
24386 * @param {Object} config Configuration options
24389 Roo.form.ComboBoxArray.Item = function(config) {
24390 config.id = Roo.id();
24391 Roo.form.ComboBoxArray.Item.superclass.constructor.call(this, config);
24394 Roo.extend(Roo.form.ComboBoxArray.Item, Roo.BoxComponent, {
24397 displayField : false,
24401 defaultAutoCreate : {
24403 cls: 'x-cbarray-item',
24410 src : Roo.BLANK_IMAGE_URL ,
24418 onRender : function(ct, position)
24420 Roo.form.Field.superclass.onRender.call(this, ct, position);
24423 var cfg = this.getAutoCreate();
24424 this.el = ct.createChild(cfg, position);
24427 this.el.child('img').dom.setAttribute('src', Roo.BLANK_IMAGE_URL);
24429 this.el.child('div').dom.innerHTML = this.cb.renderer ?
24430 this.cb.renderer(this.data) :
24431 String.format('{0}',this.data[this.displayField]);
24434 this.el.child('div').dom.setAttribute('qtip',
24435 String.format('{0}',this.data[this.tipField])
24438 this.el.child('img').on('click', this.remove, this);
24442 remove : function()
24445 this.cb.items.remove(this);
24446 this.el.child('img').un('click', this.remove, this);
24448 this.cb.updateHiddenEl();
24454 * Ext JS Library 1.1.1
24455 * Copyright(c) 2006-2007, Ext JS, LLC.
24457 * Originally Released Under LGPL - original licence link has changed is not relivant.
24460 * <script type="text/javascript">
24463 * @class Roo.form.Checkbox
24464 * @extends Roo.form.Field
24465 * Single checkbox field. Can be used as a direct replacement for traditional checkbox fields.
24467 * Creates a new Checkbox
24468 * @param {Object} config Configuration options
24470 Roo.form.Checkbox = function(config){
24471 Roo.form.Checkbox.superclass.constructor.call(this, config);
24475 * Fires when the checkbox is checked or unchecked.
24476 * @param {Roo.form.Checkbox} this This checkbox
24477 * @param {Boolean} checked The new checked value
24483 Roo.extend(Roo.form.Checkbox, Roo.form.Field, {
24485 * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
24487 focusClass : undefined,
24489 * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
24491 fieldClass: "x-form-field",
24493 * @cfg {Boolean} checked True if the the checkbox should render already checked (defaults to false)
24497 * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
24498 * {tag: "input", type: "checkbox", autocomplete: "off"})
24500 defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "off"},
24502 * @cfg {String} boxLabel The text that appears beside the checkbox
24506 * @cfg {String} inputValue The value that should go into the generated input element's value attribute
24510 * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
24512 valueOff: '0', // value when not checked..
24514 actionMode : 'viewEl',
24517 itemCls : 'x-menu-check-item x-form-item',
24518 groupClass : 'x-menu-group-item',
24519 inputType : 'hidden',
24522 inSetChecked: false, // check that we are not calling self...
24524 inputElement: false, // real input element?
24525 basedOn: false, // ????
24527 isFormField: true, // not sure where this is needed!!!!
24529 onResize : function(){
24530 Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
24531 if(!this.boxLabel){
24532 this.el.alignTo(this.wrap, 'c-c');
24536 initEvents : function(){
24537 Roo.form.Checkbox.superclass.initEvents.call(this);
24538 this.el.on("click", this.onClick, this);
24539 this.el.on("change", this.onClick, this);
24543 getResizeEl : function(){
24547 getPositionEl : function(){
24552 onRender : function(ct, position){
24553 Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
24555 if(this.inputValue !== undefined){
24556 this.el.dom.value = this.inputValue;
24559 //this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
24560 this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
24561 var viewEl = this.wrap.createChild({
24562 tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
24563 this.viewEl = viewEl;
24564 this.wrap.on('click', this.onClick, this);
24566 this.el.on('DOMAttrModified', this.setFromHidden, this); //ff
24567 this.el.on('propertychange', this.setFromHidden, this); //ie
24572 this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
24573 // viewEl.on('click', this.onClick, this);
24575 //if(this.checked){
24576 this.setChecked(this.checked);
24578 //this.checked = this.el.dom;
24584 initValue : Roo.emptyFn,
24587 * Returns the checked state of the checkbox.
24588 * @return {Boolean} True if checked, else false
24590 getValue : function(){
24592 return String(this.el.dom.value) == String(this.inputValue ) ? this.inputValue : this.valueOff;
24594 return this.valueOff;
24599 onClick : function(){
24600 this.setChecked(!this.checked);
24602 //if(this.el.dom.checked != this.checked){
24603 // this.setValue(this.el.dom.checked);
24608 * Sets the checked state of the checkbox.
24609 * On is always based on a string comparison between inputValue and the param.
24610 * @param {Boolean/String} value - the value to set
24611 * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
24613 setValue : function(v,suppressEvent){
24616 //this.checked = (v === true || v === 'true' || v == '1' || String(v).toLowerCase() == 'on');
24617 //if(this.el && this.el.dom){
24618 // this.el.dom.checked = this.checked;
24619 // this.el.dom.defaultChecked = this.checked;
24621 this.setChecked(String(v) === String(this.inputValue), suppressEvent);
24622 //this.fireEvent("check", this, this.checked);
24625 setChecked : function(state,suppressEvent)
24627 if (this.inSetChecked) {
24628 this.checked = state;
24634 this.wrap[state ? 'addClass' : 'removeClass']('x-menu-item-checked');
24636 this.checked = state;
24637 if(suppressEvent !== true){
24638 this.fireEvent('check', this, state);
24640 this.inSetChecked = true;
24641 this.el.dom.value = state ? this.inputValue : this.valueOff;
24642 this.inSetChecked = false;
24645 // handle setting of hidden value by some other method!!?!?
24646 setFromHidden: function()
24651 //console.log("SET FROM HIDDEN");
24652 //alert('setFrom hidden');
24653 this.setValue(this.el.dom.value);
24656 onDestroy : function()
24659 Roo.get(this.viewEl).remove();
24662 Roo.form.Checkbox.superclass.onDestroy.call(this);
24667 * Ext JS Library 1.1.1
24668 * Copyright(c) 2006-2007, Ext JS, LLC.
24670 * Originally Released Under LGPL - original licence link has changed is not relivant.
24673 * <script type="text/javascript">
24677 * @class Roo.form.Radio
24678 * @extends Roo.form.Checkbox
24679 * Single radio field. Same as Checkbox, but provided as a convenience for automatically setting the input type.
24680 * Radio grouping is handled automatically by the browser if you give each radio in a group the same name.
24682 * Creates a new Radio
24683 * @param {Object} config Configuration options
24685 Roo.form.Radio = function(){
24686 Roo.form.Radio.superclass.constructor.apply(this, arguments);
24688 Roo.extend(Roo.form.Radio, Roo.form.Checkbox, {
24689 inputType: 'radio',
24692 * If this radio is part of a group, it will return the selected value
24695 getGroupValue : function(){
24696 return this.el.up('form').child('input[name='+this.el.dom.name+']:checked', true).value;
24698 });//<script type="text/javascript">
24701 * Ext JS Library 1.1.1
24702 * Copyright(c) 2006-2007, Ext JS, LLC.
24703 * licensing@extjs.com
24705 * http://www.extjs.com/license
24711 * Default CSS appears to render it as fixed text by default (should really be Sans-Serif)
24712 * - IE ? - no idea how much works there.
24720 * @class Ext.form.HtmlEditor
24721 * @extends Ext.form.Field
24722 * Provides a lightweight HTML Editor component.
24724 * This has been tested on Fireforx / Chrome.. IE may not be so great..
24726 * <br><br><b>Note: The focus/blur and validation marking functionality inherited from Ext.form.Field is NOT
24727 * supported by this editor.</b><br/><br/>
24728 * An Editor is a sensitive component that can't be used in all spots standard fields can be used. Putting an Editor within
24729 * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
24731 Roo.form.HtmlEditor = Roo.extend(Roo.form.Field, {
24733 * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
24737 * @cfg {String} createLinkText The default text for the create link prompt
24739 createLinkText : 'Please enter the URL for the link:',
24741 * @cfg {String} defaultLinkValue The default value for the create link prompt (defaults to http:/ /)
24743 defaultLinkValue : 'http:/'+'/',
24746 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
24751 * @cfg {Number} height (in pixels)
24755 * @cfg {Number} width (in pixels)
24760 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
24763 stylesheets: false,
24768 // private properties
24769 validationEvent : false,
24771 initialized : false,
24773 sourceEditMode : false,
24774 onFocus : Roo.emptyFn,
24776 hideMode:'offsets',
24778 defaultAutoCreate : { // modified by initCompnoent..
24780 style:"width:500px;height:300px;",
24781 autocomplete: "off"
24785 initComponent : function(){
24788 * @event initialize
24789 * Fires when the editor is fully initialized (including the iframe)
24790 * @param {HtmlEditor} this
24795 * Fires when the editor is first receives the focus. Any insertion must wait
24796 * until after this event.
24797 * @param {HtmlEditor} this
24801 * @event beforesync
24802 * Fires before the textarea is updated with content from the editor iframe. Return false
24803 * to cancel the sync.
24804 * @param {HtmlEditor} this
24805 * @param {String} html
24809 * @event beforepush
24810 * Fires before the iframe editor is updated with content from the textarea. Return false
24811 * to cancel the push.
24812 * @param {HtmlEditor} this
24813 * @param {String} html
24818 * Fires when the textarea is updated with content from the editor iframe.
24819 * @param {HtmlEditor} this
24820 * @param {String} html
24825 * Fires when the iframe editor is updated with content from the textarea.
24826 * @param {HtmlEditor} this
24827 * @param {String} html
24831 * @event editmodechange
24832 * Fires when the editor switches edit modes
24833 * @param {HtmlEditor} this
24834 * @param {Boolean} sourceEdit True if source edit, false if standard editing.
24836 editmodechange: true,
24838 * @event editorevent
24839 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
24840 * @param {HtmlEditor} this
24844 this.defaultAutoCreate = {
24846 style:'width: ' + this.width + 'px;height: ' + this.height + 'px;',
24847 autocomplete: "off"
24852 * Protected method that will not generally be called directly. It
24853 * is called when the editor creates its toolbar. Override this method if you need to
24854 * add custom toolbar buttons.
24855 * @param {HtmlEditor} editor
24857 createToolbar : function(editor){
24858 if (!editor.toolbars || !editor.toolbars.length) {
24859 editor.toolbars = [ new Roo.form.HtmlEditor.ToolbarStandard() ]; // can be empty?
24862 for (var i =0 ; i < editor.toolbars.length;i++) {
24863 editor.toolbars[i] = Roo.factory(editor.toolbars[i], Roo.form.HtmlEditor);
24864 editor.toolbars[i].init(editor);
24871 * Protected method that will not generally be called directly. It
24872 * is called when the editor initializes the iframe with HTML contents. Override this method if you
24873 * want to change the initialization markup of the iframe (e.g. to add stylesheets).
24875 getDocMarkup : function(){
24878 if (this.stylesheets === false) {
24880 Roo.get(document.head).select('style').each(function(node) {
24881 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
24884 Roo.get(document.head).select('link').each(function(node) {
24885 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
24888 } else if (!this.stylesheets.length) {
24890 st = '<style type="text/css">' +
24891 'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
24894 Roo.each(this.stylesheets, function(s) {
24895 st += '<link rel="stylesheet" type="text/css" href="' + s +'" />'
24900 st += '<style type="text/css">' +
24901 'IMG { cursor: pointer } ' +
24905 return '<html><head>' + st +
24906 //<style type="text/css">' +
24907 //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
24909 ' </head><body class="roo-htmleditor-body"></body></html>';
24913 onRender : function(ct, position)
24916 Roo.form.HtmlEditor.superclass.onRender.call(this, ct, position);
24917 this.el.dom.style.border = '0 none';
24918 this.el.dom.setAttribute('tabIndex', -1);
24919 this.el.addClass('x-hidden');
24920 if(Roo.isIE){ // fix IE 1px bogus margin
24921 this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
24923 this.wrap = this.el.wrap({
24924 cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
24927 if (this.resizable) {
24928 this.resizeEl = new Roo.Resizable(this.wrap, {
24932 minHeight : this.height,
24933 height: this.height,
24934 handles : this.resizable,
24937 resize : function(r, w, h) {
24938 _t.onResize(w,h); // -something
24945 this.frameId = Roo.id();
24947 this.createToolbar(this);
24951 var iframe = this.wrap.createChild({
24954 name: this.frameId,
24955 frameBorder : 'no',
24956 'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL : "javascript:false"
24960 // console.log(iframe);
24961 //this.wrap.dom.appendChild(iframe);
24963 this.iframe = iframe.dom;
24965 this.assignDocWin();
24967 this.doc.designMode = 'on';
24970 this.doc.write(this.getDocMarkup());
24974 var task = { // must defer to wait for browser to be ready
24976 //console.log("run task?" + this.doc.readyState);
24977 this.assignDocWin();
24978 if(this.doc.body || this.doc.readyState == 'complete'){
24980 this.doc.designMode="on";
24984 Roo.TaskMgr.stop(task);
24985 this.initEditor.defer(10, this);
24992 Roo.TaskMgr.start(task);
24995 this.setSize(this.wrap.getSize());
24997 if (this.resizeEl) {
24998 this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
24999 // should trigger onReize..
25004 onResize : function(w, h)
25006 //Roo.log('resize: ' +w + ',' + h );
25007 Roo.form.HtmlEditor.superclass.onResize.apply(this, arguments);
25008 if(this.el && this.iframe){
25009 if(typeof w == 'number'){
25010 var aw = w - this.wrap.getFrameWidth('lr');
25011 this.el.setWidth(this.adjustWidth('textarea', aw));
25012 this.iframe.style.width = aw + 'px';
25014 if(typeof h == 'number'){
25016 for (var i =0; i < this.toolbars.length;i++) {
25017 // fixme - ask toolbars for heights?
25018 tbh += this.toolbars[i].tb.el.getHeight();
25019 if (this.toolbars[i].footer) {
25020 tbh += this.toolbars[i].footer.el.getHeight();
25027 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
25028 ah -= 5; // knock a few pixes off for look..
25029 this.el.setHeight(this.adjustWidth('textarea', ah));
25030 this.iframe.style.height = ah + 'px';
25032 (this.doc.body || this.doc.documentElement).style.height = (ah - (this.iframePad*2)) + 'px';
25039 * Toggles the editor between standard and source edit mode.
25040 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
25042 toggleSourceEdit : function(sourceEditMode){
25044 this.sourceEditMode = sourceEditMode === true;
25046 if(this.sourceEditMode){
25049 this.iframe.className = 'x-hidden';
25050 this.el.removeClass('x-hidden');
25051 this.el.dom.removeAttribute('tabIndex');
25056 this.iframe.className = '';
25057 this.el.addClass('x-hidden');
25058 this.el.dom.setAttribute('tabIndex', -1);
25061 this.setSize(this.wrap.getSize());
25062 this.fireEvent('editmodechange', this, this.sourceEditMode);
25065 // private used internally
25066 createLink : function(){
25067 var url = prompt(this.createLinkText, this.defaultLinkValue);
25068 if(url && url != 'http:/'+'/'){
25069 this.relayCmd('createlink', url);
25073 // private (for BoxComponent)
25074 adjustSize : Roo.BoxComponent.prototype.adjustSize,
25076 // private (for BoxComponent)
25077 getResizeEl : function(){
25081 // private (for BoxComponent)
25082 getPositionEl : function(){
25087 initEvents : function(){
25088 this.originalValue = this.getValue();
25092 * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
25095 markInvalid : Roo.emptyFn,
25097 * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
25100 clearInvalid : Roo.emptyFn,
25102 setValue : function(v){
25103 Roo.form.HtmlEditor.superclass.setValue.call(this, v);
25108 * Protected method that will not generally be called directly. If you need/want
25109 * custom HTML cleanup, this is the method you should override.
25110 * @param {String} html The HTML to be cleaned
25111 * return {String} The cleaned HTML
25113 cleanHtml : function(html){
25114 html = String(html);
25115 if(html.length > 5){
25116 if(Roo.isSafari){ // strip safari nonsense
25117 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
25120 if(html == ' '){
25127 * Protected method that will not generally be called directly. Syncs the contents
25128 * of the editor iframe with the textarea.
25130 syncValue : function(){
25131 if(this.initialized){
25132 var bd = (this.doc.body || this.doc.documentElement);
25133 //this.cleanUpPaste(); -- this is done else where and causes havoc..
25134 var html = bd.innerHTML;
25136 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
25137 var m = bs.match(/text-align:(.*?);/i);
25139 html = '<div style="'+m[0]+'">' + html + '</div>';
25142 html = this.cleanHtml(html);
25143 // fix up the special chars..
25144 html = html.replace(/([\x80-\uffff])/g, function (a, b) {
25145 return "&#"+b.charCodeAt()+";"
25147 if(this.fireEvent('beforesync', this, html) !== false){
25148 this.el.dom.value = html;
25149 this.fireEvent('sync', this, html);
25155 * Protected method that will not generally be called directly. Pushes the value of the textarea
25156 * into the iframe editor.
25158 pushValue : function(){
25159 if(this.initialized){
25160 var v = this.el.dom.value;
25165 if(this.fireEvent('beforepush', this, v) !== false){
25166 var d = (this.doc.body || this.doc.documentElement);
25168 this.cleanUpPaste();
25169 this.el.dom.value = d.innerHTML;
25170 this.fireEvent('push', this, v);
25176 deferFocus : function(){
25177 this.focus.defer(10, this);
25181 focus : function(){
25182 if(this.win && !this.sourceEditMode){
25189 assignDocWin: function()
25191 var iframe = this.iframe;
25194 this.doc = iframe.contentWindow.document;
25195 this.win = iframe.contentWindow;
25197 if (!Roo.get(this.frameId)) {
25200 this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
25201 this.win = Roo.get(this.frameId).dom.contentWindow;
25206 initEditor : function(){
25207 //console.log("INIT EDITOR");
25208 this.assignDocWin();
25212 this.doc.designMode="on";
25214 this.doc.write(this.getDocMarkup());
25217 var dbody = (this.doc.body || this.doc.documentElement);
25218 //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
25219 // this copies styles from the containing element into thsi one..
25220 // not sure why we need all of this..
25221 var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
25222 ss['background-attachment'] = 'fixed'; // w3c
25223 dbody.bgProperties = 'fixed'; // ie
25224 Roo.DomHelper.applyStyles(dbody, ss);
25225 Roo.EventManager.on(this.doc, {
25226 //'mousedown': this.onEditorEvent,
25227 'mouseup': this.onEditorEvent,
25228 'dblclick': this.onEditorEvent,
25229 'click': this.onEditorEvent,
25230 'keyup': this.onEditorEvent,
25235 Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
25237 if(Roo.isIE || Roo.isSafari || Roo.isOpera){
25238 Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
25240 this.initialized = true;
25242 this.fireEvent('initialize', this);
25247 onDestroy : function(){
25253 for (var i =0; i < this.toolbars.length;i++) {
25254 // fixme - ask toolbars for heights?
25255 this.toolbars[i].onDestroy();
25258 this.wrap.dom.innerHTML = '';
25259 this.wrap.remove();
25264 onFirstFocus : function(){
25266 this.assignDocWin();
25269 this.activated = true;
25270 for (var i =0; i < this.toolbars.length;i++) {
25271 this.toolbars[i].onFirstFocus();
25274 if(Roo.isGecko){ // prevent silly gecko errors
25276 var s = this.win.getSelection();
25277 if(!s.focusNode || s.focusNode.nodeType != 3){
25278 var r = s.getRangeAt(0);
25279 r.selectNodeContents((this.doc.body || this.doc.documentElement));
25284 this.execCmd('useCSS', true);
25285 this.execCmd('styleWithCSS', false);
25288 this.fireEvent('activate', this);
25292 adjustFont: function(btn){
25293 var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
25294 //if(Roo.isSafari){ // safari
25297 var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
25298 if(Roo.isSafari){ // safari
25299 var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
25300 v = (v < 10) ? 10 : v;
25301 v = (v > 48) ? 48 : v;
25302 v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
25307 v = Math.max(1, v+adjust);
25309 this.execCmd('FontSize', v );
25312 onEditorEvent : function(e){
25313 this.fireEvent('editorevent', this, e);
25314 // this.updateToolbar();
25315 this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
25318 insertTag : function(tg)
25320 // could be a bit smarter... -> wrap the current selected tRoo..
25322 this.execCmd("formatblock", tg);
25326 insertText : function(txt)
25330 range = this.createRange();
25331 range.deleteContents();
25332 //alert(Sender.getAttribute('label'));
25334 range.insertNode(this.doc.createTextNode(txt));
25338 relayBtnCmd : function(btn){
25339 this.relayCmd(btn.cmd);
25343 * Executes a Midas editor command on the editor document and performs necessary focus and
25344 * toolbar updates. <b>This should only be called after the editor is initialized.</b>
25345 * @param {String} cmd The Midas command
25346 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
25348 relayCmd : function(cmd, value){
25350 this.execCmd(cmd, value);
25351 this.fireEvent('editorevent', this);
25352 //this.updateToolbar();
25357 * Executes a Midas editor command directly on the editor document.
25358 * For visual commands, you should use {@link #relayCmd} instead.
25359 * <b>This should only be called after the editor is initialized.</b>
25360 * @param {String} cmd The Midas command
25361 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
25363 execCmd : function(cmd, value){
25364 this.doc.execCommand(cmd, false, value === undefined ? null : value);
25371 * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
25373 * @param {String} text | dom node..
25375 insertAtCursor : function(text)
25380 if(!this.activated){
25386 var r = this.doc.selection.createRange();
25397 if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
25401 // from jquery ui (MIT licenced)
25403 var win = this.win;
25405 if (win.getSelection && win.getSelection().getRangeAt) {
25406 range = win.getSelection().getRangeAt(0);
25407 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
25408 range.insertNode(node);
25409 } else if (win.document.selection && win.document.selection.createRange) {
25410 // no firefox support
25411 var txt = typeof(text) == 'string' ? text : text.outerHTML;
25412 win.document.selection.createRange().pasteHTML(txt);
25414 // no firefox support
25415 var txt = typeof(text) == 'string' ? text : text.outerHTML;
25416 this.execCmd('InsertHTML', txt);
25425 mozKeyPress : function(e){
25427 var c = e.getCharCode(), cmd;
25430 c = String.fromCharCode(c).toLowerCase();
25444 this.cleanUpPaste.defer(100, this);
25452 e.preventDefault();
25460 fixKeys : function(){ // load time branching for fastest keydown performance
25462 return function(e){
25463 var k = e.getKey(), r;
25466 r = this.doc.selection.createRange();
25469 r.pasteHTML('    ');
25476 r = this.doc.selection.createRange();
25478 var target = r.parentElement();
25479 if(!target || target.tagName.toLowerCase() != 'li'){
25481 r.pasteHTML('<br />');
25487 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
25488 this.cleanUpPaste.defer(100, this);
25494 }else if(Roo.isOpera){
25495 return function(e){
25496 var k = e.getKey();
25500 this.execCmd('InsertHTML','    ');
25503 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
25504 this.cleanUpPaste.defer(100, this);
25509 }else if(Roo.isSafari){
25510 return function(e){
25511 var k = e.getKey();
25515 this.execCmd('InsertText','\t');
25519 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
25520 this.cleanUpPaste.defer(100, this);
25528 getAllAncestors: function()
25530 var p = this.getSelectedNode();
25533 a.push(p); // push blank onto stack..
25534 p = this.getParentElement();
25538 while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
25542 a.push(this.doc.body);
25546 lastSelNode : false,
25549 getSelection : function()
25551 this.assignDocWin();
25552 return Roo.isIE ? this.doc.selection : this.win.getSelection();
25555 getSelectedNode: function()
25557 // this may only work on Gecko!!!
25559 // should we cache this!!!!
25564 var range = this.createRange(this.getSelection()).cloneRange();
25567 var parent = range.parentElement();
25569 var testRange = range.duplicate();
25570 testRange.moveToElementText(parent);
25571 if (testRange.inRange(range)) {
25574 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
25577 parent = parent.parentElement;
25582 // is ancestor a text element.
25583 var ac = range.commonAncestorContainer;
25584 if (ac.nodeType == 3) {
25585 ac = ac.parentNode;
25588 var ar = ac.childNodes;
25591 var other_nodes = [];
25592 var has_other_nodes = false;
25593 for (var i=0;i<ar.length;i++) {
25594 if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ?
25597 // fullly contained node.
25599 if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
25604 // probably selected..
25605 if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
25606 other_nodes.push(ar[i]);
25610 if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0)) {
25615 has_other_nodes = true;
25617 if (!nodes.length && other_nodes.length) {
25618 nodes= other_nodes;
25620 if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
25626 createRange: function(sel)
25628 // this has strange effects when using with
25629 // top toolbar - not sure if it's a great idea.
25630 //this.editor.contentWindow.focus();
25631 if (typeof sel != "undefined") {
25633 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
25635 return this.doc.createRange();
25638 return this.doc.createRange();
25641 getParentElement: function()
25644 this.assignDocWin();
25645 var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
25647 var range = this.createRange(sel);
25650 var p = range.commonAncestorContainer;
25651 while (p.nodeType == 3) { // text node
25662 * Range intersection.. the hard stuff...
25666 * [ -- selected range --- ]
25670 * if end is before start or hits it. fail.
25671 * if start is after end or hits it fail.
25673 * if either hits (but other is outside. - then it's not
25679 // @see http://www.thismuchiknow.co.uk/?p=64.
25680 rangeIntersectsNode : function(range, node)
25682 var nodeRange = node.ownerDocument.createRange();
25684 nodeRange.selectNode(node);
25686 nodeRange.selectNodeContents(node);
25689 var rangeStartRange = range.cloneRange();
25690 rangeStartRange.collapse(true);
25692 var rangeEndRange = range.cloneRange();
25693 rangeEndRange.collapse(false);
25695 var nodeStartRange = nodeRange.cloneRange();
25696 nodeStartRange.collapse(true);
25698 var nodeEndRange = nodeRange.cloneRange();
25699 nodeEndRange.collapse(false);
25701 return rangeStartRange.compareBoundaryPoints(
25702 Range.START_TO_START, nodeEndRange) == -1 &&
25703 rangeEndRange.compareBoundaryPoints(
25704 Range.START_TO_START, nodeStartRange) == 1;
25708 rangeCompareNode : function(range, node)
25710 var nodeRange = node.ownerDocument.createRange();
25712 nodeRange.selectNode(node);
25714 nodeRange.selectNodeContents(node);
25718 range.collapse(true);
25720 nodeRange.collapse(true);
25722 var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
25723 var ee = range.compareBoundaryPoints( Range.END_TO_END, nodeRange);
25725 //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
25727 var nodeIsBefore = ss == 1;
25728 var nodeIsAfter = ee == -1;
25730 if (nodeIsBefore && nodeIsAfter)
25732 if (!nodeIsBefore && nodeIsAfter)
25733 return 1; //right trailed.
25735 if (nodeIsBefore && !nodeIsAfter)
25736 return 2; // left trailed.
25741 // private? - in a new class?
25742 cleanUpPaste : function()
25744 // cleans up the whole document..
25745 Roo.log('cleanuppaste');
25746 this.cleanUpChildren(this.doc.body);
25747 var clean = this.cleanWordChars(this.doc.body.innerHTML);
25748 if (clean != this.doc.body.innerHTML) {
25749 this.doc.body.innerHTML = clean;
25754 cleanWordChars : function(input) {
25755 var he = Roo.form.HtmlEditor;
25757 var output = input;
25758 Roo.each(he.swapCodes, function(sw) {
25760 var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
25761 output = output.replace(swapper, sw[1]);
25767 cleanUpChildren : function (n)
25769 if (!n.childNodes.length) {
25772 for (var i = n.childNodes.length-1; i > -1 ; i--) {
25773 this.cleanUpChild(n.childNodes[i]);
25780 cleanUpChild : function (node)
25782 //console.log(node);
25783 if (node.nodeName == "#text") {
25784 // clean up silly Windows -- stuff?
25787 if (node.nodeName == "#comment") {
25788 node.parentNode.removeChild(node);
25789 // clean up silly Windows -- stuff?
25793 if (Roo.form.HtmlEditor.black.indexOf(node.tagName.toLowerCase()) > -1) {
25795 node.parentNode.removeChild(node);
25800 var remove_keep_children= Roo.form.HtmlEditor.remove.indexOf(node.tagName.toLowerCase()) > -1;
25802 // remove <a name=....> as rendering on yahoo mailer is bored with this.
25804 if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
25805 remove_keep_children = true;
25808 if (remove_keep_children) {
25809 this.cleanUpChildren(node);
25810 // inserts everything just before this node...
25811 while (node.childNodes.length) {
25812 var cn = node.childNodes[0];
25813 node.removeChild(cn);
25814 node.parentNode.insertBefore(cn, node);
25816 node.parentNode.removeChild(node);
25820 if (!node.attributes || !node.attributes.length) {
25821 this.cleanUpChildren(node);
25825 function cleanAttr(n,v)
25828 if (v.match(/^\./) || v.match(/^\//)) {
25831 if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
25834 Roo.log("(REMOVE)"+ node.tagName +'.' + n + '=' + v);
25835 node.removeAttribute(n);
25839 function cleanStyle(n,v)
25841 if (v.match(/expression/)) { //XSS?? should we even bother..
25842 node.removeAttribute(n);
25847 var parts = v.split(/;/);
25848 Roo.each(parts, function(p) {
25849 p = p.replace(/\s+/g,'');
25853 var l = p.split(':').shift().replace(/\s+/g,'');
25855 // only allow 'c whitelisted system attributes'
25856 if (Roo.form.HtmlEditor.cwhite.indexOf(l) < 0) {
25857 Roo.log('(REMOVE)' + node.tagName +'.' + n + ':'+l + '=' + v);
25858 node.removeAttribute(n);
25868 for (var i = node.attributes.length-1; i > -1 ; i--) {
25869 var a = node.attributes[i];
25871 if (Roo.form.HtmlEditor.ablack.indexOf(a.name.toLowerCase()) > -1) {
25872 node.removeAttribute(a.name);
25875 if (Roo.form.HtmlEditor.aclean.indexOf(a.name.toLowerCase()) > -1) {
25876 cleanAttr(a.name,a.value); // fixme..
25879 if (a.name == 'style') {
25880 cleanStyle(a.name,a.value);
25882 /// clean up MS crap..
25883 // tecnically this should be a list of valid class'es..
25886 if (a.name == 'class') {
25887 if (a.value.match(/^Mso/)) {
25888 node.className = '';
25891 if (a.value.match(/body/)) {
25892 node.className = '';
25902 this.cleanUpChildren(node);
25908 // hide stuff that is not compatible
25922 * @event specialkey
25926 * @cfg {String} fieldClass @hide
25929 * @cfg {String} focusClass @hide
25932 * @cfg {String} autoCreate @hide
25935 * @cfg {String} inputType @hide
25938 * @cfg {String} invalidClass @hide
25941 * @cfg {String} invalidText @hide
25944 * @cfg {String} msgFx @hide
25947 * @cfg {String} validateOnBlur @hide
25951 Roo.form.HtmlEditor.white = [
25952 'area', 'br', 'img', 'input', 'hr', 'wbr',
25954 'address', 'blockquote', 'center', 'dd', 'dir', 'div',
25955 'dl', 'dt', 'h1', 'h2', 'h3', 'h4',
25956 'h5', 'h6', 'hr', 'isindex', 'listing', 'marquee',
25957 'menu', 'multicol', 'ol', 'p', 'plaintext', 'pre',
25958 'table', 'ul', 'xmp',
25960 'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th',
25963 'dir', 'menu', 'ol', 'ul', 'dl',
25969 Roo.form.HtmlEditor.black = [
25970 // 'embed', 'object', // enable - backend responsiblity to clean thiese
25972 'base', 'basefont', 'bgsound', 'blink', 'body',
25973 'frame', 'frameset', 'head', 'html', 'ilayer',
25974 'iframe', 'layer', 'link', 'meta', 'object',
25975 'script', 'style' ,'title', 'xml' // clean later..
25977 Roo.form.HtmlEditor.clean = [
25978 'script', 'style', 'title', 'xml'
25980 Roo.form.HtmlEditor.remove = [
25985 Roo.form.HtmlEditor.ablack = [
25989 Roo.form.HtmlEditor.aclean = [
25990 'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc'
25994 Roo.form.HtmlEditor.pwhite= [
25995 'http', 'https', 'mailto'
25998 // white listed style attributes.
25999 Roo.form.HtmlEditor.cwhite= [
26005 Roo.form.HtmlEditor.swapCodes =[
26016 // <script type="text/javascript">
26019 * Ext JS Library 1.1.1
26020 * Copyright(c) 2006-2007, Ext JS, LLC.
26026 * @class Roo.form.HtmlEditorToolbar1
26031 new Roo.form.HtmlEditor({
26034 new Roo.form.HtmlEditorToolbar1({
26035 disable : { fonts: 1 , format: 1, ..., ... , ...],
26041 * @cfg {Object} disable List of elements to disable..
26042 * @cfg {Array} btns List of additional buttons.
26046 * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
26049 Roo.form.HtmlEditor.ToolbarStandard = function(config)
26052 Roo.apply(this, config);
26054 // default disabled, based on 'good practice'..
26055 this.disable = this.disable || {};
26056 Roo.applyIf(this.disable, {
26059 specialElements : true
26063 //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
26064 // dont call parent... till later.
26067 Roo.apply(Roo.form.HtmlEditor.ToolbarStandard.prototype, {
26075 * @cfg {Object} disable List of toolbar elements to disable
26080 * @cfg {Array} fontFamilies An array of available font families
26098 // "á" , ?? a acute?
26103 "°" // , // degrees
26105 // "é" , // e ecute
26106 // "ú" , // u ecute?
26109 specialElements : [
26111 text: "Insert Table",
26114 ihtml : '<table><tr><td>Cell</td></tr></table>'
26118 text: "Insert Image",
26121 ihtml : '<img src="about:blank"/>'
26130 "form", "input:text", "input:hidden", "input:checkbox", "input:radio", "input:password",
26131 "input:submit", "input:button", "select", "textarea", "label" ],
26134 ["h1"],["h2"],["h3"],["h4"],["h5"],["h6"],
26136 ["abbr"],[ "acronym"],[ "address"],[ "cite"],[ "samp"],[ "var"]
26139 * @cfg {String} defaultFont default font to use.
26141 defaultFont: 'tahoma',
26143 fontSelect : false,
26146 formatCombo : false,
26148 init : function(editor)
26150 this.editor = editor;
26153 var fid = editor.frameId;
26155 function btn(id, toggle, handler){
26156 var xid = fid + '-'+ id ;
26160 cls : 'x-btn-icon x-edit-'+id,
26161 enableToggle:toggle !== false,
26162 scope: editor, // was editor...
26163 handler:handler||editor.relayBtnCmd,
26164 clickEvent:'mousedown',
26165 tooltip: etb.buttonTips[id] || undefined, ///tips ???
26172 var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
26174 // stop form submits
26175 tb.el.on('click', function(e){
26176 e.preventDefault(); // what does this do?
26179 if(!this.disable.font && !Roo.isSafari){
26180 /* why no safari for fonts
26181 editor.fontSelect = tb.el.createChild({
26184 cls:'x-font-select',
26185 html: editor.createFontOptions()
26187 editor.fontSelect.on('change', function(){
26188 var font = editor.fontSelect.dom.value;
26189 editor.relayCmd('fontname', font);
26190 editor.deferFocus();
26193 editor.fontSelect.dom,
26198 if(!this.disable.formats){
26199 this.formatCombo = new Roo.form.ComboBox({
26200 store: new Roo.data.SimpleStore({
26203 data : this.formats // from states.js
26206 //autoCreate : {tag: "div", size: "20"},
26207 displayField:'tag',
26211 triggerAction: 'all',
26212 emptyText:'Add tag',
26213 selectOnFocus:true,
26216 'select': function(c, r, i) {
26217 editor.insertTag(r.get('tag'));
26223 tb.addField(this.formatCombo);
26227 if(!this.disable.format){
26234 if(!this.disable.fontSize){
26239 btn('increasefontsize', false, editor.adjustFont),
26240 btn('decreasefontsize', false, editor.adjustFont)
26245 if(!this.disable.colors){
26248 id:editor.frameId +'-forecolor',
26249 cls:'x-btn-icon x-edit-forecolor',
26250 clickEvent:'mousedown',
26251 tooltip: this.buttonTips['forecolor'] || undefined,
26253 menu : new Roo.menu.ColorMenu({
26254 allowReselect: true,
26255 focus: Roo.emptyFn,
26258 selectHandler: function(cp, color){
26259 editor.execCmd('forecolor', Roo.isSafari || Roo.isIE ? '#'+color : color);
26260 editor.deferFocus();
26263 clickEvent:'mousedown'
26266 id:editor.frameId +'backcolor',
26267 cls:'x-btn-icon x-edit-backcolor',
26268 clickEvent:'mousedown',
26269 tooltip: this.buttonTips['backcolor'] || undefined,
26271 menu : new Roo.menu.ColorMenu({
26272 focus: Roo.emptyFn,
26275 allowReselect: true,
26276 selectHandler: function(cp, color){
26278 editor.execCmd('useCSS', false);
26279 editor.execCmd('hilitecolor', color);
26280 editor.execCmd('useCSS', true);
26281 editor.deferFocus();
26283 editor.execCmd(Roo.isOpera ? 'hilitecolor' : 'backcolor',
26284 Roo.isSafari || Roo.isIE ? '#'+color : color);
26285 editor.deferFocus();
26289 clickEvent:'mousedown'
26294 // now add all the items...
26297 if(!this.disable.alignments){
26300 btn('justifyleft'),
26301 btn('justifycenter'),
26302 btn('justifyright')
26306 //if(!Roo.isSafari){
26307 if(!this.disable.links){
26310 btn('createlink', false, editor.createLink) /// MOVE TO HERE?!!?!?!?!
26314 if(!this.disable.lists){
26317 btn('insertorderedlist'),
26318 btn('insertunorderedlist')
26321 if(!this.disable.sourceEdit){
26324 btn('sourceedit', true, function(btn){
26325 this.toggleSourceEdit(btn.pressed);
26332 // special menu.. - needs to be tidied up..
26333 if (!this.disable.special) {
26336 cls: 'x-edit-none',
26342 for (var i =0; i < this.specialChars.length; i++) {
26343 smenu.menu.items.push({
26345 html: this.specialChars[i],
26346 handler: function(a,b) {
26347 editor.insertAtCursor(String.fromCharCode(a.html.replace('&#','').replace(';', '')));
26348 //editor.insertAtCursor(a.html);
26361 if (!this.disable.specialElements) {
26364 cls: 'x-edit-none',
26369 for (var i =0; i < this.specialElements.length; i++) {
26370 semenu.menu.items.push(
26372 handler: function(a,b) {
26373 editor.insertAtCursor(this.ihtml);
26375 }, this.specialElements[i])
26387 for(var i =0; i< this.btns.length;i++) {
26388 var b = Roo.factory(this.btns[i],Roo.form);
26389 b.cls = 'x-edit-none';
26398 // disable everything...
26400 this.tb.items.each(function(item){
26401 if(item.id != editor.frameId+ '-sourceedit'){
26405 this.rendered = true;
26407 // the all the btns;
26408 editor.on('editorevent', this.updateToolbar, this);
26409 // other toolbars need to implement this..
26410 //editor.on('editmodechange', this.updateToolbar, this);
26416 * Protected method that will not generally be called directly. It triggers
26417 * a toolbar update by reading the markup state of the current selection in the editor.
26419 updateToolbar: function(){
26421 if(!this.editor.activated){
26422 this.editor.onFirstFocus();
26426 var btns = this.tb.items.map,
26427 doc = this.editor.doc,
26428 frameId = this.editor.frameId;
26430 if(!this.disable.font && !Roo.isSafari){
26432 var name = (doc.queryCommandValue('FontName')||this.editor.defaultFont).toLowerCase();
26433 if(name != this.fontSelect.dom.value){
26434 this.fontSelect.dom.value = name;
26438 if(!this.disable.format){
26439 btns[frameId + '-bold'].toggle(doc.queryCommandState('bold'));
26440 btns[frameId + '-italic'].toggle(doc.queryCommandState('italic'));
26441 btns[frameId + '-underline'].toggle(doc.queryCommandState('underline'));
26443 if(!this.disable.alignments){
26444 btns[frameId + '-justifyleft'].toggle(doc.queryCommandState('justifyleft'));
26445 btns[frameId + '-justifycenter'].toggle(doc.queryCommandState('justifycenter'));
26446 btns[frameId + '-justifyright'].toggle(doc.queryCommandState('justifyright'));
26448 if(!Roo.isSafari && !this.disable.lists){
26449 btns[frameId + '-insertorderedlist'].toggle(doc.queryCommandState('insertorderedlist'));
26450 btns[frameId + '-insertunorderedlist'].toggle(doc.queryCommandState('insertunorderedlist'));
26453 var ans = this.editor.getAllAncestors();
26454 if (this.formatCombo) {
26457 var store = this.formatCombo.store;
26458 this.formatCombo.setValue("");
26459 for (var i =0; i < ans.length;i++) {
26460 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
26462 this.formatCombo.setValue(ans[i].tagName.toLowerCase());
26470 // hides menus... - so this cant be on a menu...
26471 Roo.menu.MenuMgr.hideAll();
26473 //this.editorsyncValue();
26477 createFontOptions : function(){
26478 var buf = [], fs = this.fontFamilies, ff, lc;
26479 for(var i = 0, len = fs.length; i< len; i++){
26481 lc = ff.toLowerCase();
26483 '<option value="',lc,'" style="font-family:',ff,';"',
26484 (this.defaultFont == lc ? ' selected="true">' : '>'),
26489 return buf.join('');
26492 toggleSourceEdit : function(sourceEditMode){
26493 if(sourceEditMode === undefined){
26494 sourceEditMode = !this.sourceEditMode;
26496 this.sourceEditMode = sourceEditMode === true;
26497 var btn = this.tb.items.get(this.editor.frameId +'-sourceedit');
26498 // just toggle the button?
26499 if(btn.pressed !== this.editor.sourceEditMode){
26500 btn.toggle(this.editor.sourceEditMode);
26504 if(this.sourceEditMode){
26505 this.tb.items.each(function(item){
26506 if(item.cmd != 'sourceedit'){
26512 if(this.initialized){
26513 this.tb.items.each(function(item){
26519 // tell the editor that it's been pressed..
26520 this.editor.toggleSourceEdit(sourceEditMode);
26524 * Object collection of toolbar tooltips for the buttons in the editor. The key
26525 * is the command id associated with that button and the value is a valid QuickTips object.
26530 title: 'Bold (Ctrl+B)',
26531 text: 'Make the selected text bold.',
26532 cls: 'x-html-editor-tip'
26535 title: 'Italic (Ctrl+I)',
26536 text: 'Make the selected text italic.',
26537 cls: 'x-html-editor-tip'
26545 title: 'Bold (Ctrl+B)',
26546 text: 'Make the selected text bold.',
26547 cls: 'x-html-editor-tip'
26550 title: 'Italic (Ctrl+I)',
26551 text: 'Make the selected text italic.',
26552 cls: 'x-html-editor-tip'
26555 title: 'Underline (Ctrl+U)',
26556 text: 'Underline the selected text.',
26557 cls: 'x-html-editor-tip'
26559 increasefontsize : {
26560 title: 'Grow Text',
26561 text: 'Increase the font size.',
26562 cls: 'x-html-editor-tip'
26564 decreasefontsize : {
26565 title: 'Shrink Text',
26566 text: 'Decrease the font size.',
26567 cls: 'x-html-editor-tip'
26570 title: 'Text Highlight Color',
26571 text: 'Change the background color of the selected text.',
26572 cls: 'x-html-editor-tip'
26575 title: 'Font Color',
26576 text: 'Change the color of the selected text.',
26577 cls: 'x-html-editor-tip'
26580 title: 'Align Text Left',
26581 text: 'Align text to the left.',
26582 cls: 'x-html-editor-tip'
26585 title: 'Center Text',
26586 text: 'Center text in the editor.',
26587 cls: 'x-html-editor-tip'
26590 title: 'Align Text Right',
26591 text: 'Align text to the right.',
26592 cls: 'x-html-editor-tip'
26594 insertunorderedlist : {
26595 title: 'Bullet List',
26596 text: 'Start a bulleted list.',
26597 cls: 'x-html-editor-tip'
26599 insertorderedlist : {
26600 title: 'Numbered List',
26601 text: 'Start a numbered list.',
26602 cls: 'x-html-editor-tip'
26605 title: 'Hyperlink',
26606 text: 'Make the selected text a hyperlink.',
26607 cls: 'x-html-editor-tip'
26610 title: 'Source Edit',
26611 text: 'Switch to source editing mode.',
26612 cls: 'x-html-editor-tip'
26616 onDestroy : function(){
26619 this.tb.items.each(function(item){
26621 item.menu.removeAll();
26623 item.menu.el.destroy();
26631 onFirstFocus: function() {
26632 this.tb.items.each(function(item){
26641 // <script type="text/javascript">
26644 * Ext JS Library 1.1.1
26645 * Copyright(c) 2006-2007, Ext JS, LLC.
26652 * @class Roo.form.HtmlEditor.ToolbarContext
26657 new Roo.form.HtmlEditor({
26660 { xtype: 'ToolbarStandard', styles : {} }
26661 { xtype: 'ToolbarContext', disable : {} }
26667 * @config : {Object} disable List of elements to disable.. (not done yet.)
26668 * @config : {Object} styles Map of styles available.
26672 Roo.form.HtmlEditor.ToolbarContext = function(config)
26675 Roo.apply(this, config);
26676 //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
26677 // dont call parent... till later.
26678 this.styles = this.styles || {};
26680 Roo.form.HtmlEditor.ToolbarContext.types = {
26692 opts : [ [""],[ "left"],[ "right"],[ "center"],[ "top"]],
26754 opts : [[""],[ "left"],[ "center"],[ "right"],[ "justify"],[ "char"]],
26759 opts : [[""],[ "top"],[ "middle"],[ "bottom"],[ "baseline"]],
26813 // should we really allow this??
26814 // should this just be
26829 Roo.apply(Roo.form.HtmlEditor.ToolbarContext.prototype, {
26837 * @cfg {Object} disable List of toolbar elements to disable
26842 * @cfg {Object} styles List of styles
26843 * eg. { '*' : [ 'headline' ] , 'TD' : [ 'underline', 'double-underline' ] }
26845 * These must be defined in the page, so they get rendered correctly..
26856 init : function(editor)
26858 this.editor = editor;
26861 var fid = editor.frameId;
26863 function btn(id, toggle, handler){
26864 var xid = fid + '-'+ id ;
26868 cls : 'x-btn-icon x-edit-'+id,
26869 enableToggle:toggle !== false,
26870 scope: editor, // was editor...
26871 handler:handler||editor.relayBtnCmd,
26872 clickEvent:'mousedown',
26873 tooltip: etb.buttonTips[id] || undefined, ///tips ???
26877 // create a new element.
26878 var wdiv = editor.wrap.createChild({
26880 }, editor.wrap.dom.firstChild.nextSibling, true);
26882 // can we do this more than once??
26884 // stop form submits
26887 // disable everything...
26888 var ty= Roo.form.HtmlEditor.ToolbarContext.types;
26889 this.toolbars = {};
26891 for (var i in ty) {
26893 this.toolbars[i] = this.buildToolbar(ty[i],i);
26895 this.tb = this.toolbars.BODY;
26897 this.buildFooter();
26898 this.footer.show();
26899 editor.on('hide', function( ) { this.footer.hide() }, this);
26900 editor.on('show', function( ) { this.footer.show() }, this);
26903 this.rendered = true;
26905 // the all the btns;
26906 editor.on('editorevent', this.updateToolbar, this);
26907 // other toolbars need to implement this..
26908 //editor.on('editmodechange', this.updateToolbar, this);
26914 * Protected method that will not generally be called directly. It triggers
26915 * a toolbar update by reading the markup state of the current selection in the editor.
26917 updateToolbar: function(editor,ev,sel){
26920 // capture mouse up - this is handy for selecting images..
26921 // perhaps should go somewhere else...
26922 if(!this.editor.activated){
26923 this.editor.onFirstFocus();
26927 // http://developer.yahoo.com/yui/docs/simple-editor.js.html
26928 // selectNode - might want to handle IE?
26930 (ev.type == 'mouseup' || ev.type == 'click' ) &&
26931 ev.target && ev.target.tagName == 'IMG') {
26932 // they have click on an image...
26933 // let's see if we can change the selection...
26936 var nodeRange = sel.ownerDocument.createRange();
26938 nodeRange.selectNode(sel);
26940 nodeRange.selectNodeContents(sel);
26942 //nodeRange.collapse(true);
26943 var s = editor.win.getSelection();
26944 s.removeAllRanges();
26945 s.addRange(nodeRange);
26949 var updateFooter = sel ? false : true;
26952 var ans = this.editor.getAllAncestors();
26955 var ty= Roo.form.HtmlEditor.ToolbarContext.types;
26958 sel = ans.length ? (ans[0] ? ans[0] : ans[1]) : this.editor.doc.body;
26959 sel = sel ? sel : this.editor.doc.body;
26960 sel = sel.tagName.length ? sel : this.editor.doc.body;
26963 // pick a menu that exists..
26964 var tn = sel.tagName.toUpperCase();
26965 //sel = typeof(ty[tn]) != 'undefined' ? sel : this.editor.doc.body;
26967 tn = sel.tagName.toUpperCase();
26969 var lastSel = this.tb.selectedNode
26971 this.tb.selectedNode = sel;
26973 // if current menu does not match..
26974 if ((this.tb.name != tn) || (lastSel != this.tb.selectedNode)) {
26977 ///console.log("show: " + tn);
26978 this.tb = typeof(ty[tn]) != 'undefined' ? this.toolbars[tn] : this.toolbars['*'];
26981 this.tb.items.first().el.innerHTML = tn + ': ';
26984 // update attributes
26985 if (this.tb.fields) {
26986 this.tb.fields.each(function(e) {
26987 e.setValue(sel.getAttribute(e.attrname));
26991 var hasStyles = false;
26992 for(var i in this.styles) {
26999 var st = this.tb.fields.item(0);
27001 st.store.removeAll();
27004 var cn = sel.className.split(/\s+/);
27007 if (this.styles['*']) {
27009 Roo.each(this.styles['*'], function(v) {
27010 avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );
27013 if (this.styles[tn]) {
27014 Roo.each(this.styles[tn], function(v) {
27015 avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );
27019 st.store.loadData(avs);
27023 // flag our selected Node.
27024 this.tb.selectedNode = sel;
27027 Roo.menu.MenuMgr.hideAll();
27031 if (!updateFooter) {
27034 // update the footer
27038 this.footerEls = ans.reverse();
27039 Roo.each(this.footerEls, function(a,i) {
27040 if (!a) { return; }
27041 html += html.length ? ' > ' : '';
27043 html += '<span class="x-ed-loc-' + i + '">' + a.tagName + '</span>';
27048 var sz = this.footDisp.up('td').getSize();
27049 this.footDisp.dom.style.width = (sz.width -10) + 'px';
27050 this.footDisp.dom.style.marginLeft = '5px';
27052 this.footDisp.dom.style.overflow = 'hidden';
27054 this.footDisp.dom.innerHTML = html;
27056 //this.editorsyncValue();
27061 onDestroy : function(){
27064 this.tb.items.each(function(item){
27066 item.menu.removeAll();
27068 item.menu.el.destroy();
27076 onFirstFocus: function() {
27077 // need to do this for all the toolbars..
27078 this.tb.items.each(function(item){
27082 buildToolbar: function(tlist, nm)
27084 var editor = this.editor;
27085 // create a new element.
27086 var wdiv = editor.wrap.createChild({
27088 }, editor.wrap.dom.firstChild.nextSibling, true);
27091 var tb = new Roo.Toolbar(wdiv);
27094 tb.add(nm+ ": ");
27097 for(var i in this.styles) {
27102 if (styles && styles.length) {
27104 // this needs a multi-select checkbox...
27105 tb.addField( new Roo.form.ComboBox({
27106 store: new Roo.data.SimpleStore({
27108 fields: ['val', 'selected'],
27111 name : '-roo-edit-className',
27112 attrname : 'className',
27113 displayField:'val',
27117 triggerAction: 'all',
27118 emptyText:'Select Style',
27119 selectOnFocus:true,
27122 'select': function(c, r, i) {
27123 // initial support only for on class per el..
27124 tb.selectedNode.className = r ? r.get('val') : '';
27125 editor.syncValue();
27134 for (var i in tlist) {
27136 var item = tlist[i];
27137 tb.add(item.title + ": ");
27143 // opts == pulldown..
27144 tb.addField( new Roo.form.ComboBox({
27145 store: new Roo.data.SimpleStore({
27150 name : '-roo-edit-' + i,
27152 displayField:'val',
27156 triggerAction: 'all',
27157 emptyText:'Select',
27158 selectOnFocus:true,
27159 width: item.width ? item.width : 130,
27161 'select': function(c, r, i) {
27162 tb.selectedNode.setAttribute(c.attrname, r.get('val'));
27171 tb.addField( new Roo.form.TextField({
27174 //allowBlank:false,
27179 tb.addField( new Roo.form.TextField({
27180 name: '-roo-edit-' + i,
27187 'change' : function(f, nv, ov) {
27188 tb.selectedNode.setAttribute(f.attrname, nv);
27194 tb.el.on('click', function(e){
27195 e.preventDefault(); // what does this do?
27197 tb.el.setVisibilityMode( Roo.Element.DISPLAY);
27200 // dont need to disable them... as they will get hidden
27205 buildFooter : function()
27208 var fel = this.editor.wrap.createChild();
27209 this.footer = new Roo.Toolbar(fel);
27210 // toolbar has scrolly on left / right?
27211 var footDisp= new Roo.Toolbar.Fill();
27217 handler : function() {
27218 _t.footDisp.scrollTo('left',0,true)
27222 this.footer.add( footDisp );
27227 handler : function() {
27229 _t.footDisp.select('span').last().scrollIntoView(_t.footDisp,true);
27233 var fel = Roo.get(footDisp.el);
27234 fel.addClass('x-editor-context');
27235 this.footDispWrap = fel;
27236 this.footDispWrap.overflow = 'hidden';
27238 this.footDisp = fel.createChild();
27239 this.footDispWrap.on('click', this.onContextClick, this)
27243 onContextClick : function (ev,dom)
27245 ev.preventDefault();
27246 var cn = dom.className;
27248 if (!cn.match(/x-ed-loc-/)) {
27251 var n = cn.split('-').pop();
27252 var ans = this.footerEls;
27256 var range = this.editor.createRange();
27258 range.selectNodeContents(sel);
27259 //range.selectNode(sel);
27262 var selection = this.editor.getSelection();
27263 selection.removeAllRanges();
27264 selection.addRange(range);
27268 this.updateToolbar(null, null, sel);
27285 * Ext JS Library 1.1.1
27286 * Copyright(c) 2006-2007, Ext JS, LLC.
27288 * Originally Released Under LGPL - original licence link has changed is not relivant.
27291 * <script type="text/javascript">
27295 * @class Roo.form.BasicForm
27296 * @extends Roo.util.Observable
27297 * Supplies the functionality to do "actions" on forms and initialize Roo.form.Field types on existing markup.
27299 * @param {String/HTMLElement/Roo.Element} el The form element or its id
27300 * @param {Object} config Configuration options
27302 Roo.form.BasicForm = function(el, config){
27303 this.allItems = [];
27304 this.childForms = [];
27305 Roo.apply(this, config);
27307 * The Roo.form.Field items in this form.
27308 * @type MixedCollection
27312 this.items = new Roo.util.MixedCollection(false, function(o){
27313 return o.id || (o.id = Roo.id());
27317 * @event beforeaction
27318 * Fires before any action is performed. Return false to cancel the action.
27319 * @param {Form} this
27320 * @param {Action} action The action to be performed
27322 beforeaction: true,
27324 * @event actionfailed
27325 * Fires when an action fails.
27326 * @param {Form} this
27327 * @param {Action} action The action that failed
27329 actionfailed : true,
27331 * @event actioncomplete
27332 * Fires when an action is completed.
27333 * @param {Form} this
27334 * @param {Action} action The action that completed
27336 actioncomplete : true
27341 Roo.form.BasicForm.superclass.constructor.call(this);
27344 Roo.extend(Roo.form.BasicForm, Roo.util.Observable, {
27346 * @cfg {String} method
27347 * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
27350 * @cfg {DataReader} reader
27351 * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when executing "load" actions.
27352 * This is optional as there is built-in support for processing JSON.
27355 * @cfg {DataReader} errorReader
27356 * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when reading validation errors on "submit" actions.
27357 * This is completely optional as there is built-in support for processing JSON.
27360 * @cfg {String} url
27361 * The URL to use for form actions if one isn't supplied in the action options.
27364 * @cfg {Boolean} fileUpload
27365 * Set to true if this form is a file upload.
27369 * @cfg {Object} baseParams
27370 * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
27375 * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
27380 activeAction : null,
27383 * @cfg {Boolean} trackResetOnLoad If set to true, form.reset() resets to the last loaded
27384 * or setValues() data instead of when the form was first created.
27386 trackResetOnLoad : false,
27390 * childForms - used for multi-tab forms
27393 childForms : false,
27396 * allItems - full list of fields.
27402 * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
27403 * element by passing it or its id or mask the form itself by passing in true.
27406 waitMsgTarget : false,
27409 initEl : function(el){
27410 this.el = Roo.get(el);
27411 this.id = this.el.id || Roo.id();
27412 this.el.on('submit', this.onSubmit, this);
27413 this.el.addClass('x-form');
27417 onSubmit : function(e){
27422 * Returns true if client-side validation on the form is successful.
27425 isValid : function(){
27427 this.items.each(function(f){
27436 * Returns true if any fields in this form have changed since their original load.
27439 isDirty : function(){
27441 this.items.each(function(f){
27451 * Performs a predefined action (submit or load) or custom actions you define on this form.
27452 * @param {String} actionName The name of the action type
27453 * @param {Object} options (optional) The options to pass to the action. All of the config options listed
27454 * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
27455 * accept other config options):
27457 Property Type Description
27458 ---------------- --------------- ----------------------------------------------------------------------------------
27459 url String The url for the action (defaults to the form's url)
27460 method String The form method to use (defaults to the form's method, or POST if not defined)
27461 params String/Object The params to pass (defaults to the form's baseParams, or none if not defined)
27462 clientValidation Boolean Applies to submit only. Pass true to call form.isValid() prior to posting to
27463 validate the form on the client (defaults to false)
27465 * @return {BasicForm} this
27467 doAction : function(action, options){
27468 if(typeof action == 'string'){
27469 action = new Roo.form.Action.ACTION_TYPES[action](this, options);
27471 if(this.fireEvent('beforeaction', this, action) !== false){
27472 this.beforeAction(action);
27473 action.run.defer(100, action);
27479 * Shortcut to do a submit action.
27480 * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
27481 * @return {BasicForm} this
27483 submit : function(options){
27484 this.doAction('submit', options);
27489 * Shortcut to do a load action.
27490 * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
27491 * @return {BasicForm} this
27493 load : function(options){
27494 this.doAction('load', options);
27499 * Persists the values in this form into the passed Roo.data.Record object in a beginEdit/endEdit block.
27500 * @param {Record} record The record to edit
27501 * @return {BasicForm} this
27503 updateRecord : function(record){
27504 record.beginEdit();
27505 var fs = record.fields;
27506 fs.each(function(f){
27507 var field = this.findField(f.name);
27509 record.set(f.name, field.getValue());
27517 * Loads an Roo.data.Record into this form.
27518 * @param {Record} record The record to load
27519 * @return {BasicForm} this
27521 loadRecord : function(record){
27522 this.setValues(record.data);
27527 beforeAction : function(action){
27528 var o = action.options;
27531 if(this.waitMsgTarget === true){
27532 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
27533 }else if(this.waitMsgTarget){
27534 this.waitMsgTarget = Roo.get(this.waitMsgTarget);
27535 this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
27537 Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
27543 afterAction : function(action, success){
27544 this.activeAction = null;
27545 var o = action.options;
27547 if(this.waitMsgTarget === true){
27549 }else if(this.waitMsgTarget){
27550 this.waitMsgTarget.unmask();
27552 Roo.MessageBox.updateProgress(1);
27553 Roo.MessageBox.hide();
27560 Roo.callback(o.success, o.scope, [this, action]);
27561 this.fireEvent('actioncomplete', this, action);
27565 // failure condition..
27566 // we have a scenario where updates need confirming.
27567 // eg. if a locking scenario exists..
27568 // we look for { errors : { needs_confirm : true }} in the response.
27570 (typeof(action.result) != 'undefined') &&
27571 (typeof(action.result.errors) != 'undefined') &&
27572 (typeof(action.result.errors.needs_confirm) != 'undefined')
27575 Roo.MessageBox.confirm(
27576 "Change requires confirmation",
27577 action.result.errorMsg,
27582 _t.doAction('submit', { params : { _submit_confirmed : 1 } } );
27592 Roo.callback(o.failure, o.scope, [this, action]);
27593 // show an error message if no failed handler is set..
27594 if (!this.hasListener('actionfailed')) {
27595 Roo.MessageBox.alert("Error",
27596 (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
27597 action.result.errorMsg :
27598 "Saving Failed, please check your entries or try again"
27602 this.fireEvent('actionfailed', this, action);
27608 * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
27609 * @param {String} id The value to search for
27612 findField : function(id){
27613 var field = this.items.get(id);
27615 this.items.each(function(f){
27616 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
27622 return field || null;
27626 * Add a secondary form to this one,
27627 * Used to provide tabbed forms. One form is primary, with hidden values
27628 * which mirror the elements from the other forms.
27630 * @param {Roo.form.Form} form to add.
27633 addForm : function(form)
27636 if (this.childForms.indexOf(form) > -1) {
27640 this.childForms.push(form);
27642 Roo.each(form.allItems, function (fe) {
27644 n = typeof(fe.getName) == 'undefined' ? fe.name : fe.getName();
27645 if (this.findField(n)) { // already added..
27648 var add = new Roo.form.Hidden({
27651 add.render(this.el);
27658 * Mark fields in this form invalid in bulk.
27659 * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
27660 * @return {BasicForm} this
27662 markInvalid : function(errors){
27663 if(errors instanceof Array){
27664 for(var i = 0, len = errors.length; i < len; i++){
27665 var fieldError = errors[i];
27666 var f = this.findField(fieldError.id);
27668 f.markInvalid(fieldError.msg);
27674 if(typeof errors[id] != 'function' && (field = this.findField(id))){
27675 field.markInvalid(errors[id]);
27679 Roo.each(this.childForms || [], function (f) {
27680 f.markInvalid(errors);
27687 * Set values for fields in this form in bulk.
27688 * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
27689 * @return {BasicForm} this
27691 setValues : function(values){
27692 if(values instanceof Array){ // array of objects
27693 for(var i = 0, len = values.length; i < len; i++){
27695 var f = this.findField(v.id);
27697 f.setValue(v.value);
27698 if(this.trackResetOnLoad){
27699 f.originalValue = f.getValue();
27703 }else{ // object hash
27706 if(typeof values[id] != 'function' && (field = this.findField(id))){
27708 if (field.setFromData &&
27709 field.valueField &&
27710 field.displayField &&
27711 // combos' with local stores can
27712 // be queried via setValue()
27713 // to set their value..
27714 (field.store && !field.store.isLocal)
27718 sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
27719 sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
27720 field.setFromData(sd);
27723 field.setValue(values[id]);
27727 if(this.trackResetOnLoad){
27728 field.originalValue = field.getValue();
27734 Roo.each(this.childForms || [], function (f) {
27735 f.setValues(values);
27742 * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
27743 * they are returned as an array.
27744 * @param {Boolean} asString
27747 getValues : function(asString){
27748 if (this.childForms) {
27749 // copy values from the child forms
27750 Roo.each(this.childForms, function (f) {
27751 this.setValues(f.getValues());
27757 var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
27758 if(asString === true){
27761 return Roo.urlDecode(fs);
27765 * Returns the fields in this form as an object with key/value pairs.
27766 * This differs from getValues as it calls getValue on each child item, rather than using dom data.
27769 getFieldValues : function(with_hidden)
27771 if (this.childForms) {
27772 // copy values from the child forms
27773 // should this call getFieldValues - probably not as we do not currently copy
27774 // hidden fields when we generate..
27775 Roo.each(this.childForms, function (f) {
27776 this.setValues(f.getValues());
27781 this.items.each(function(f){
27782 if (!f.getName()) {
27785 var v = f.getValue();
27786 // not sure if this supported any more..
27787 if ((typeof(v) == 'object') && f.getRawValue) {
27788 v = f.getRawValue() ; // dates..
27790 // combo boxes where name != hiddenName...
27791 if (f.name != f.getName()) {
27792 ret[f.name] = f.getRawValue();
27794 ret[f.getName()] = v;
27801 * Clears all invalid messages in this form.
27802 * @return {BasicForm} this
27804 clearInvalid : function(){
27805 this.items.each(function(f){
27809 Roo.each(this.childForms || [], function (f) {
27818 * Resets this form.
27819 * @return {BasicForm} this
27821 reset : function(){
27822 this.items.each(function(f){
27826 Roo.each(this.childForms || [], function (f) {
27835 * Add Roo.form components to this form.
27836 * @param {Field} field1
27837 * @param {Field} field2 (optional)
27838 * @param {Field} etc (optional)
27839 * @return {BasicForm} this
27842 this.items.addAll(Array.prototype.slice.call(arguments, 0));
27848 * Removes a field from the items collection (does NOT remove its markup).
27849 * @param {Field} field
27850 * @return {BasicForm} this
27852 remove : function(field){
27853 this.items.remove(field);
27858 * Looks at the fields in this form, checks them for an id attribute,
27859 * and calls applyTo on the existing dom element with that id.
27860 * @return {BasicForm} this
27862 render : function(){
27863 this.items.each(function(f){
27864 if(f.isFormField && !f.rendered && document.getElementById(f.id)){ // if the element exists
27872 * Calls {@link Ext#apply} for all fields in this form with the passed object.
27873 * @param {Object} values
27874 * @return {BasicForm} this
27876 applyToFields : function(o){
27877 this.items.each(function(f){
27884 * Calls {@link Ext#applyIf} for all field in this form with the passed object.
27885 * @param {Object} values
27886 * @return {BasicForm} this
27888 applyIfToFields : function(o){
27889 this.items.each(function(f){
27897 Roo.BasicForm = Roo.form.BasicForm;/*
27899 * Ext JS Library 1.1.1
27900 * Copyright(c) 2006-2007, Ext JS, LLC.
27902 * Originally Released Under LGPL - original licence link has changed is not relivant.
27905 * <script type="text/javascript">
27909 * @class Roo.form.Form
27910 * @extends Roo.form.BasicForm
27911 * Adds the ability to dynamically render forms with JavaScript to {@link Roo.form.BasicForm}.
27913 * @param {Object} config Configuration options
27915 Roo.form.Form = function(config){
27917 if (config.items) {
27918 xitems = config.items;
27919 delete config.items;
27923 Roo.form.Form.superclass.constructor.call(this, null, config);
27924 this.url = this.url || this.action;
27926 this.root = new Roo.form.Layout(Roo.applyIf({
27930 this.active = this.root;
27932 * Array of all the buttons that have been added to this form via {@link addButton}
27936 this.allItems = [];
27939 * @event clientvalidation
27940 * If the monitorValid config option is true, this event fires repetitively to notify of valid state
27941 * @param {Form} this
27942 * @param {Boolean} valid true if the form has passed client-side validation
27944 clientvalidation: true,
27947 * Fires when the form is rendered
27948 * @param {Roo.form.Form} form
27953 if (this.progressUrl) {
27954 // push a hidden field onto the list of fields..
27958 name : 'UPLOAD_IDENTIFIER'
27963 Roo.each(xitems, this.addxtype, this);
27969 Roo.extend(Roo.form.Form, Roo.form.BasicForm, {
27971 * @cfg {Number} labelWidth The width of labels. This property cascades to child containers.
27974 * @cfg {String} itemCls A css class to apply to the x-form-item of fields. This property cascades to child containers.
27977 * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "center")
27979 buttonAlign:'center',
27982 * @cfg {Number} minButtonWidth Minimum width of all buttons in pixels (defaults to 75)
27987 * @cfg {String} labelAlign Valid values are "left," "top" and "right" (defaults to "left").
27988 * This property cascades to child containers if not set.
27993 * @cfg {Boolean} monitorValid If true the form monitors its valid state <b>client-side</b> and
27994 * fires a looping event with that state. This is required to bind buttons to the valid
27995 * state using the config value formBind:true on the button.
27997 monitorValid : false,
28000 * @cfg {Number} monitorPoll The milliseconds to poll valid state, ignored if monitorValid is not true (defaults to 200)
28005 * @cfg {String} progressUrl - Url to return progress data
28008 progressUrl : false,
28011 * Opens a new {@link Roo.form.Column} container in the layout stack. If fields are passed after the config, the
28012 * fields are added and the column is closed. If no fields are passed the column remains open
28013 * until end() is called.
28014 * @param {Object} config The config to pass to the column
28015 * @param {Field} field1 (optional)
28016 * @param {Field} field2 (optional)
28017 * @param {Field} etc (optional)
28018 * @return Column The column container object
28020 column : function(c){
28021 var col = new Roo.form.Column(c);
28023 if(arguments.length > 1){ // duplicate code required because of Opera
28024 this.add.apply(this, Array.prototype.slice.call(arguments, 1));
28031 * Opens a new {@link Roo.form.FieldSet} container in the layout stack. If fields are passed after the config, the
28032 * fields are added and the fieldset is closed. If no fields are passed the fieldset remains open
28033 * until end() is called.
28034 * @param {Object} config The config to pass to the fieldset
28035 * @param {Field} field1 (optional)
28036 * @param {Field} field2 (optional)
28037 * @param {Field} etc (optional)
28038 * @return FieldSet The fieldset container object
28040 fieldset : function(c){
28041 var fs = new Roo.form.FieldSet(c);
28043 if(arguments.length > 1){ // duplicate code required because of Opera
28044 this.add.apply(this, Array.prototype.slice.call(arguments, 1));
28051 * Opens a new {@link Roo.form.Layout} container in the layout stack. If fields are passed after the config, the
28052 * fields are added and the container is closed. If no fields are passed the container remains open
28053 * until end() is called.
28054 * @param {Object} config The config to pass to the Layout
28055 * @param {Field} field1 (optional)
28056 * @param {Field} field2 (optional)
28057 * @param {Field} etc (optional)
28058 * @return Layout The container object
28060 container : function(c){
28061 var l = new Roo.form.Layout(c);
28063 if(arguments.length > 1){ // duplicate code required because of Opera
28064 this.add.apply(this, Array.prototype.slice.call(arguments, 1));
28071 * Opens the passed container in the layout stack. The container can be any {@link Roo.form.Layout} or subclass.
28072 * @param {Object} container A Roo.form.Layout or subclass of Layout
28073 * @return {Form} this
28075 start : function(c){
28076 // cascade label info
28077 Roo.applyIf(c, {'labelAlign': this.active.labelAlign, 'labelWidth': this.active.labelWidth, 'itemCls': this.active.itemCls});
28078 this.active.stack.push(c);
28079 c.ownerCt = this.active;
28085 * Closes the current open container
28086 * @return {Form} this
28089 if(this.active == this.root){
28092 this.active = this.active.ownerCt;
28097 * Add Roo.form components to the current open container (e.g. column, fieldset, etc.). Fields added via this method
28098 * can also be passed with an additional property of fieldLabel, which if supplied, will provide the text to display
28099 * as the label of the field.
28100 * @param {Field} field1
28101 * @param {Field} field2 (optional)
28102 * @param {Field} etc. (optional)
28103 * @return {Form} this
28106 this.active.stack.push.apply(this.active.stack, arguments);
28107 this.allItems.push.apply(this.allItems,arguments);
28109 for(var i = 0, a = arguments, len = a.length; i < len; i++) {
28110 if(a[i].isFormField){
28115 Roo.form.Form.superclass.add.apply(this, r);
28125 * Find any element that has been added to a form, using it's ID or name
28126 * This can include framesets, columns etc. along with regular fields..
28127 * @param {String} id - id or name to find.
28129 * @return {Element} e - or false if nothing found.
28131 findbyId : function(id)
28137 Roo.each(this.allItems, function(f){
28138 if (f.id == id || f.name == id ){
28149 * Render this form into the passed container. This should only be called once!
28150 * @param {String/HTMLElement/Element} container The element this component should be rendered into
28151 * @return {Form} this
28153 render : function(ct)
28159 var o = this.autoCreate || {
28161 method : this.method || 'POST',
28162 id : this.id || Roo.id()
28164 this.initEl(ct.createChild(o));
28166 this.root.render(this.el);
28170 this.items.each(function(f){
28171 f.render('x-form-el-'+f.id);
28174 if(this.buttons.length > 0){
28175 // tables are required to maintain order and for correct IE layout
28176 var tb = this.el.createChild({cls:'x-form-btns-ct', cn: {
28177 cls:"x-form-btns x-form-btns-"+this.buttonAlign,
28178 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
28180 var tr = tb.getElementsByTagName('tr')[0];
28181 for(var i = 0, len = this.buttons.length; i < len; i++) {
28182 var b = this.buttons[i];
28183 var td = document.createElement('td');
28184 td.className = 'x-form-btn-td';
28185 b.render(tr.appendChild(td));
28188 if(this.monitorValid){ // initialize after render
28189 this.startMonitoring();
28191 this.fireEvent('rendered', this);
28196 * Adds a button to the footer of the form - this <b>must</b> be called before the form is rendered.
28197 * @param {String/Object} config A string becomes the button text, an object can either be a Button config
28198 * object or a valid Roo.DomHelper element config
28199 * @param {Function} handler The function called when the button is clicked
28200 * @param {Object} scope (optional) The scope of the handler function
28201 * @return {Roo.Button}
28203 addButton : function(config, handler, scope){
28207 minWidth: this.minButtonWidth,
28210 if(typeof config == "string"){
28213 Roo.apply(bc, config);
28215 var btn = new Roo.Button(null, bc);
28216 this.buttons.push(btn);
28221 * Adds a series of form elements (using the xtype property as the factory method.
28222 * Valid xtypes are: TextField, TextArea .... Button, Layout, FieldSet, Column, (and 'end' to close a block)
28223 * @param {Object} config
28226 addxtype : function()
28228 var ar = Array.prototype.slice.call(arguments, 0);
28230 for(var i = 0; i < ar.length; i++) {
28232 continue; // skip -- if this happends something invalid got sent, we
28233 // should ignore it, as basically that interface element will not show up
28234 // and that should be pretty obvious!!
28237 if (Roo.form[ar[i].xtype]) {
28239 var fe = Roo.factory(ar[i], Roo.form);
28245 fe.store.form = this;
28250 this.allItems.push(fe);
28251 if (fe.items && fe.addxtype) {
28252 fe.addxtype.apply(fe, fe.items);
28262 // console.log('adding ' + ar[i].xtype);
28264 if (ar[i].xtype == 'Button') {
28265 //console.log('adding button');
28266 //console.log(ar[i]);
28267 this.addButton(ar[i]);
28268 this.allItems.push(fe);
28272 if (ar[i].xtype == 'end') { // so we can add fieldsets... / layout etc.
28273 alert('end is not supported on xtype any more, use items');
28275 // //console.log('adding end');
28283 * Starts monitoring of the valid state of this form. Usually this is done by passing the config
28284 * option "monitorValid"
28286 startMonitoring : function(){
28289 Roo.TaskMgr.start({
28290 run : this.bindHandler,
28291 interval : this.monitorPoll || 200,
28298 * Stops monitoring of the valid state of this form
28300 stopMonitoring : function(){
28301 this.bound = false;
28305 bindHandler : function(){
28307 return false; // stops binding
28310 this.items.each(function(f){
28311 if(!f.isValid(true)){
28316 for(var i = 0, len = this.buttons.length; i < len; i++){
28317 var btn = this.buttons[i];
28318 if(btn.formBind === true && btn.disabled === valid){
28319 btn.setDisabled(!valid);
28322 this.fireEvent('clientvalidation', this, valid);
28336 Roo.Form = Roo.form.Form;
28339 * Ext JS Library 1.1.1
28340 * Copyright(c) 2006-2007, Ext JS, LLC.
28342 * Originally Released Under LGPL - original licence link has changed is not relivant.
28345 * <script type="text/javascript">
28349 * @class Roo.form.Action
28350 * Internal Class used to handle form actions
28352 * @param {Roo.form.BasicForm} el The form element or its id
28353 * @param {Object} config Configuration options
28357 // define the action interface
28358 Roo.form.Action = function(form, options){
28360 this.options = options || {};
28363 * Client Validation Failed
28366 Roo.form.Action.CLIENT_INVALID = 'client';
28368 * Server Validation Failed
28371 Roo.form.Action.SERVER_INVALID = 'server';
28373 * Connect to Server Failed
28376 Roo.form.Action.CONNECT_FAILURE = 'connect';
28378 * Reading Data from Server Failed
28381 Roo.form.Action.LOAD_FAILURE = 'load';
28383 Roo.form.Action.prototype = {
28385 failureType : undefined,
28386 response : undefined,
28387 result : undefined,
28389 // interface method
28390 run : function(options){
28394 // interface method
28395 success : function(response){
28399 // interface method
28400 handleResponse : function(response){
28404 // default connection failure
28405 failure : function(response){
28407 this.response = response;
28408 this.failureType = Roo.form.Action.CONNECT_FAILURE;
28409 this.form.afterAction(this, false);
28412 processResponse : function(response){
28413 this.response = response;
28414 if(!response.responseText){
28417 this.result = this.handleResponse(response);
28418 return this.result;
28421 // utility functions used internally
28422 getUrl : function(appendParams){
28423 var url = this.options.url || this.form.url || this.form.el.dom.action;
28425 var p = this.getParams();
28427 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
28433 getMethod : function(){
28434 return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
28437 getParams : function(){
28438 var bp = this.form.baseParams;
28439 var p = this.options.params;
28441 if(typeof p == "object"){
28442 p = Roo.urlEncode(Roo.applyIf(p, bp));
28443 }else if(typeof p == 'string' && bp){
28444 p += '&' + Roo.urlEncode(bp);
28447 p = Roo.urlEncode(bp);
28452 createCallback : function(){
28454 success: this.success,
28455 failure: this.failure,
28457 timeout: (this.form.timeout*1000),
28458 upload: this.form.fileUpload ? this.success : undefined
28463 Roo.form.Action.Submit = function(form, options){
28464 Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
28467 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
28470 haveProgress : false,
28471 uploadComplete : false,
28473 // uploadProgress indicator.
28474 uploadProgress : function()
28476 if (!this.form.progressUrl) {
28480 if (!this.haveProgress) {
28481 Roo.MessageBox.progress("Uploading", "Uploading");
28483 if (this.uploadComplete) {
28484 Roo.MessageBox.hide();
28488 this.haveProgress = true;
28490 var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
28492 var c = new Roo.data.Connection();
28494 url : this.form.progressUrl,
28499 success : function(req){
28500 //console.log(data);
28504 rdata = Roo.decode(req.responseText)
28506 Roo.log("Invalid data from server..");
28510 if (!rdata || !rdata.success) {
28514 var data = rdata.data;
28516 if (this.uploadComplete) {
28517 Roo.MessageBox.hide();
28522 Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
28523 Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
28526 this.uploadProgress.defer(2000,this);
28529 failure: function(data) {
28530 Roo.log('progress url failed ');
28541 // run get Values on the form, so it syncs any secondary forms.
28542 this.form.getValues();
28544 var o = this.options;
28545 var method = this.getMethod();
28546 var isPost = method == 'POST';
28547 if(o.clientValidation === false || this.form.isValid()){
28549 if (this.form.progressUrl) {
28550 this.form.findField('UPLOAD_IDENTIFIER').setValue(
28551 (new Date() * 1) + '' + Math.random());
28556 Roo.Ajax.request(Roo.apply(this.createCallback(), {
28557 form:this.form.el.dom,
28558 url:this.getUrl(!isPost),
28560 params:isPost ? this.getParams() : null,
28561 isUpload: this.form.fileUpload
28564 this.uploadProgress();
28566 }else if (o.clientValidation !== false){ // client validation failed
28567 this.failureType = Roo.form.Action.CLIENT_INVALID;
28568 this.form.afterAction(this, false);
28572 success : function(response)
28574 this.uploadComplete= true;
28575 if (this.haveProgress) {
28576 Roo.MessageBox.hide();
28580 var result = this.processResponse(response);
28581 if(result === true || result.success){
28582 this.form.afterAction(this, true);
28586 this.form.markInvalid(result.errors);
28587 this.failureType = Roo.form.Action.SERVER_INVALID;
28589 this.form.afterAction(this, false);
28591 failure : function(response)
28593 this.uploadComplete= true;
28594 if (this.haveProgress) {
28595 Roo.MessageBox.hide();
28598 this.response = response;
28599 this.failureType = Roo.form.Action.CONNECT_FAILURE;
28600 this.form.afterAction(this, false);
28603 handleResponse : function(response){
28604 if(this.form.errorReader){
28605 var rs = this.form.errorReader.read(response);
28608 for(var i = 0, len = rs.records.length; i < len; i++) {
28609 var r = rs.records[i];
28610 errors[i] = r.data;
28613 if(errors.length < 1){
28617 success : rs.success,
28623 ret = Roo.decode(response.responseText);
28627 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
28637 Roo.form.Action.Load = function(form, options){
28638 Roo.form.Action.Load.superclass.constructor.call(this, form, options);
28639 this.reader = this.form.reader;
28642 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
28647 Roo.Ajax.request(Roo.apply(
28648 this.createCallback(), {
28649 method:this.getMethod(),
28650 url:this.getUrl(false),
28651 params:this.getParams()
28655 success : function(response){
28657 var result = this.processResponse(response);
28658 if(result === true || !result.success || !result.data){
28659 this.failureType = Roo.form.Action.LOAD_FAILURE;
28660 this.form.afterAction(this, false);
28663 this.form.clearInvalid();
28664 this.form.setValues(result.data);
28665 this.form.afterAction(this, true);
28668 handleResponse : function(response){
28669 if(this.form.reader){
28670 var rs = this.form.reader.read(response);
28671 var data = rs.records && rs.records[0] ? rs.records[0].data : null;
28673 success : rs.success,
28677 return Roo.decode(response.responseText);
28681 Roo.form.Action.ACTION_TYPES = {
28682 'load' : Roo.form.Action.Load,
28683 'submit' : Roo.form.Action.Submit
28686 * Ext JS Library 1.1.1
28687 * Copyright(c) 2006-2007, Ext JS, LLC.
28689 * Originally Released Under LGPL - original licence link has changed is not relivant.
28692 * <script type="text/javascript">
28696 * @class Roo.form.Layout
28697 * @extends Roo.Component
28698 * Creates a container for layout and rendering of fields in an {@link Roo.form.Form}.
28700 * @param {Object} config Configuration options
28702 Roo.form.Layout = function(config){
28704 if (config.items) {
28705 xitems = config.items;
28706 delete config.items;
28708 Roo.form.Layout.superclass.constructor.call(this, config);
28710 Roo.each(xitems, this.addxtype, this);
28714 Roo.extend(Roo.form.Layout, Roo.Component, {
28716 * @cfg {String/Object} autoCreate
28717 * A DomHelper element spec used to autocreate the layout (defaults to {tag: 'div', cls: 'x-form-ct'})
28720 * @cfg {String/Object/Function} style
28721 * A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
28722 * a function which returns such a specification.
28725 * @cfg {String} labelAlign
28726 * Valid values are "left," "top" and "right" (defaults to "left")
28729 * @cfg {Number} labelWidth
28730 * Fixed width in pixels of all field labels (defaults to undefined)
28733 * @cfg {Boolean} clear
28734 * True to add a clearing element at the end of this layout, equivalent to CSS clear: both (defaults to true)
28738 * @cfg {String} labelSeparator
28739 * The separator to use after field labels (defaults to ':')
28741 labelSeparator : ':',
28743 * @cfg {Boolean} hideLabels
28744 * True to suppress the display of field labels in this layout (defaults to false)
28746 hideLabels : false,
28749 defaultAutoCreate : {tag: 'div', cls: 'x-form-ct'},
28754 onRender : function(ct, position){
28755 if(this.el){ // from markup
28756 this.el = Roo.get(this.el);
28757 }else { // generate
28758 var cfg = this.getAutoCreate();
28759 this.el = ct.createChild(cfg, position);
28762 this.el.applyStyles(this.style);
28764 if(this.labelAlign){
28765 this.el.addClass('x-form-label-'+this.labelAlign);
28767 if(this.hideLabels){
28768 this.labelStyle = "display:none";
28769 this.elementStyle = "padding-left:0;";
28771 if(typeof this.labelWidth == 'number'){
28772 this.labelStyle = "width:"+this.labelWidth+"px;";
28773 this.elementStyle = "padding-left:"+((this.labelWidth+(typeof this.labelPad == 'number' ? this.labelPad : 5))+'px')+";";
28775 if(this.labelAlign == 'top'){
28776 this.labelStyle = "width:auto;";
28777 this.elementStyle = "padding-left:0;";
28780 var stack = this.stack;
28781 var slen = stack.length;
28783 if(!this.fieldTpl){
28784 var t = new Roo.Template(
28785 '<div class="x-form-item {5}">',
28786 '<label for="{0}" style="{2}">{1}{4}</label>',
28787 '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
28789 '</div><div class="x-form-clear-left"></div>'
28791 t.disableFormats = true;
28793 Roo.form.Layout.prototype.fieldTpl = t;
28795 for(var i = 0; i < slen; i++) {
28796 if(stack[i].isFormField){
28797 this.renderField(stack[i]);
28799 this.renderComponent(stack[i]);
28804 this.el.createChild({cls:'x-form-clear'});
28809 renderField : function(f){
28810 f.fieldEl = Roo.get(this.fieldTpl.append(this.el, [
28813 f.labelStyle||this.labelStyle||'', //2
28814 this.elementStyle||'', //3
28815 typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator, //4
28816 f.itemCls||this.itemCls||'' //5
28817 ], true).getPrevSibling());
28821 renderComponent : function(c){
28822 c.render(c.isLayout ? this.el : this.el.createChild());
28825 * Adds a object form elements (using the xtype property as the factory method.)
28826 * Valid xtypes are: TextField, TextArea .... Button, Layout, FieldSet, Column
28827 * @param {Object} config
28829 addxtype : function(o)
28831 // create the lement.
28832 o.form = this.form;
28833 var fe = Roo.factory(o, Roo.form);
28834 this.form.allItems.push(fe);
28835 this.stack.push(fe);
28837 if (fe.isFormField) {
28838 this.form.items.add(fe);
28846 * @class Roo.form.Column
28847 * @extends Roo.form.Layout
28848 * Creates a column container for layout and rendering of fields in an {@link Roo.form.Form}.
28850 * @param {Object} config Configuration options
28852 Roo.form.Column = function(config){
28853 Roo.form.Column.superclass.constructor.call(this, config);
28856 Roo.extend(Roo.form.Column, Roo.form.Layout, {
28858 * @cfg {Number/String} width
28859 * The fixed width of the column in pixels or CSS value (defaults to "auto")
28862 * @cfg {String/Object} autoCreate
28863 * A DomHelper element spec used to autocreate the column (defaults to {tag: 'div', cls: 'x-form-ct x-form-column'})
28867 defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-column'},
28870 onRender : function(ct, position){
28871 Roo.form.Column.superclass.onRender.call(this, ct, position);
28873 this.el.setWidth(this.width);
28880 * @class Roo.form.Row
28881 * @extends Roo.form.Layout
28882 * Creates a row container for layout and rendering of fields in an {@link Roo.form.Form}.
28884 * @param {Object} config Configuration options
28888 Roo.form.Row = function(config){
28889 Roo.form.Row.superclass.constructor.call(this, config);
28892 Roo.extend(Roo.form.Row, Roo.form.Layout, {
28894 * @cfg {Number/String} width
28895 * The fixed width of the column in pixels or CSS value (defaults to "auto")
28898 * @cfg {Number/String} height
28899 * The fixed height of the column in pixels or CSS value (defaults to "auto")
28901 defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-row'},
28905 onRender : function(ct, position){
28906 //console.log('row render');
28908 var t = new Roo.Template(
28909 '<div class="x-form-item {5}" style="float:left;width:{6}px">',
28910 '<label for="{0}" style="{2}">{1}{4}</label>',
28911 '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
28915 t.disableFormats = true;
28917 Roo.form.Layout.prototype.rowTpl = t;
28919 this.fieldTpl = this.rowTpl;
28921 //console.log('lw' + this.labelWidth +', la:' + this.labelAlign);
28922 var labelWidth = 100;
28924 if ((this.labelAlign != 'top')) {
28925 if (typeof this.labelWidth == 'number') {
28926 labelWidth = this.labelWidth
28928 this.padWidth = 20 + labelWidth;
28932 Roo.form.Column.superclass.onRender.call(this, ct, position);
28934 this.el.setWidth(this.width);
28937 this.el.setHeight(this.height);
28942 renderField : function(f){
28943 f.fieldEl = this.fieldTpl.append(this.el, [
28944 f.id, f.fieldLabel,
28945 f.labelStyle||this.labelStyle||'',
28946 this.elementStyle||'',
28947 typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator,
28948 f.itemCls||this.itemCls||'',
28949 f.width ? f.width + this.padWidth : 160 + this.padWidth
28956 * @class Roo.form.FieldSet
28957 * @extends Roo.form.Layout
28958 * Creates a fieldset container for layout and rendering of fields in an {@link Roo.form.Form}.
28960 * @param {Object} config Configuration options
28962 Roo.form.FieldSet = function(config){
28963 Roo.form.FieldSet.superclass.constructor.call(this, config);
28966 Roo.extend(Roo.form.FieldSet, Roo.form.Layout, {
28968 * @cfg {String} legend
28969 * The text to display as the legend for the FieldSet (defaults to '')
28972 * @cfg {String/Object} autoCreate
28973 * A DomHelper element spec used to autocreate the fieldset (defaults to {tag: 'fieldset', cn: {tag:'legend'}})
28977 defaultAutoCreate : {tag: 'fieldset', cn: {tag:'legend'}},
28980 onRender : function(ct, position){
28981 Roo.form.FieldSet.superclass.onRender.call(this, ct, position);
28983 this.setLegend(this.legend);
28988 setLegend : function(text){
28990 this.el.child('legend').update(text);
28995 * Ext JS Library 1.1.1
28996 * Copyright(c) 2006-2007, Ext JS, LLC.
28998 * Originally Released Under LGPL - original licence link has changed is not relivant.
29001 * <script type="text/javascript">
29004 * @class Roo.form.VTypes
29005 * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
29008 Roo.form.VTypes = function(){
29009 // closure these in so they are only created once.
29010 var alpha = /^[a-zA-Z_]+$/;
29011 var alphanum = /^[a-zA-Z0-9_]+$/;
29012 var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,4}$/;
29013 var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
29015 // All these messages and functions are configurable
29018 * The function used to validate email addresses
29019 * @param {String} value The email address
29021 'email' : function(v){
29022 return email.test(v);
29025 * The error text to display when the email validation function returns false
29028 'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
29030 * The keystroke filter mask to be applied on email input
29033 'emailMask' : /[a-z0-9_\.\-@]/i,
29036 * The function used to validate URLs
29037 * @param {String} value The URL
29039 'url' : function(v){
29040 return url.test(v);
29043 * The error text to display when the url validation function returns false
29046 'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
29049 * The function used to validate alpha values
29050 * @param {String} value The value
29052 'alpha' : function(v){
29053 return alpha.test(v);
29056 * The error text to display when the alpha validation function returns false
29059 'alphaText' : 'This field should only contain letters and _',
29061 * The keystroke filter mask to be applied on alpha input
29064 'alphaMask' : /[a-z_]/i,
29067 * The function used to validate alphanumeric values
29068 * @param {String} value The value
29070 'alphanum' : function(v){
29071 return alphanum.test(v);
29074 * The error text to display when the alphanumeric validation function returns false
29077 'alphanumText' : 'This field should only contain letters, numbers and _',
29079 * The keystroke filter mask to be applied on alphanumeric input
29082 'alphanumMask' : /[a-z0-9_]/i
29084 }();//<script type="text/javascript">
29087 * @class Roo.form.FCKeditor
29088 * @extends Roo.form.TextArea
29089 * Wrapper around the FCKEditor http://www.fckeditor.net
29091 * Creates a new FCKeditor
29092 * @param {Object} config Configuration options
29094 Roo.form.FCKeditor = function(config){
29095 Roo.form.FCKeditor.superclass.constructor.call(this, config);
29098 * @event editorinit
29099 * Fired when the editor is initialized - you can add extra handlers here..
29100 * @param {FCKeditor} this
29101 * @param {Object} the FCK object.
29108 Roo.form.FCKeditor.editors = { };
29109 Roo.extend(Roo.form.FCKeditor, Roo.form.TextArea,
29111 //defaultAutoCreate : {
29112 // tag : "textarea",style : "width:100px;height:60px;" ,autocomplete : "off"
29116 * @cfg {Object} fck options - see fck manual for details.
29121 * @cfg {Object} fck toolbar set (Basic or Default)
29123 toolbarSet : 'Basic',
29125 * @cfg {Object} fck BasePath
29127 basePath : '/fckeditor/',
29135 onRender : function(ct, position)
29138 this.defaultAutoCreate = {
29140 style:"width:300px;height:60px;",
29141 autocomplete: "off"
29144 Roo.form.FCKeditor.superclass.onRender.call(this, ct, position);
29147 this.textSizeEl = Roo.DomHelper.append(document.body, {tag: "pre", cls: "x-form-grow-sizer"});
29148 if(this.preventScrollbars){
29149 this.el.setStyle("overflow", "hidden");
29151 this.el.setHeight(this.growMin);
29154 //console.log('onrender' + this.getId() );
29155 Roo.form.FCKeditor.editors[this.getId()] = this;
29158 this.replaceTextarea() ;
29162 getEditor : function() {
29163 return this.fckEditor;
29166 * Sets a data value into the field and validates it. To set the value directly without validation see {@link #setRawValue}.
29167 * @param {Mixed} value The value to set
29171 setValue : function(value)
29173 //console.log('setValue: ' + value);
29175 if(typeof(value) == 'undefined') { // not sure why this is happending...
29178 Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
29180 //if(!this.el || !this.getEditor()) {
29181 // this.value = value;
29182 //this.setValue.defer(100,this,[value]);
29186 if(!this.getEditor()) {
29190 this.getEditor().SetData(value);
29197 * Returns the normalized data value (undefined or emptyText will be returned as ''). To return the raw value see {@link #getRawValue}.
29198 * @return {Mixed} value The field value
29200 getValue : function()
29203 if (this.frame && this.frame.dom.style.display == 'none') {
29204 return Roo.form.FCKeditor.superclass.getValue.call(this);
29207 if(!this.el || !this.getEditor()) {
29209 // this.getValue.defer(100,this);
29214 var value=this.getEditor().GetData();
29215 Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
29216 return Roo.form.FCKeditor.superclass.getValue.call(this);
29222 * Returns the raw data value which may or may not be a valid, defined value. To return a normalized value see {@link #getValue}.
29223 * @return {Mixed} value The field value
29225 getRawValue : function()
29227 if (this.frame && this.frame.dom.style.display == 'none') {
29228 return Roo.form.FCKeditor.superclass.getRawValue.call(this);
29231 if(!this.el || !this.getEditor()) {
29232 //this.getRawValue.defer(100,this);
29239 var value=this.getEditor().GetData();
29240 Roo.form.FCKeditor.superclass.setRawValue.apply(this,[value]);
29241 return Roo.form.FCKeditor.superclass.getRawValue.call(this);
29245 setSize : function(w,h) {
29249 //if (this.frame && this.frame.dom.style.display == 'none') {
29250 // Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
29253 //if(!this.el || !this.getEditor()) {
29254 // this.setSize.defer(100,this, [w,h]);
29260 Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
29262 this.frame.dom.setAttribute('width', w);
29263 this.frame.dom.setAttribute('height', h);
29264 this.frame.setSize(w,h);
29268 toggleSourceEdit : function(value) {
29272 this.el.dom.style.display = value ? '' : 'none';
29273 this.frame.dom.style.display = value ? 'none' : '';
29278 focus: function(tag)
29280 if (this.frame.dom.style.display == 'none') {
29281 return Roo.form.FCKeditor.superclass.focus.call(this);
29283 if(!this.el || !this.getEditor()) {
29284 this.focus.defer(100,this, [tag]);
29291 var tgs = this.getEditor().EditorDocument.getElementsByTagName(tag);
29292 this.getEditor().Focus();
29294 if (!this.getEditor().Selection.GetSelection()) {
29295 this.focus.defer(100,this, [tag]);
29300 var r = this.getEditor().EditorDocument.createRange();
29301 r.setStart(tgs[0],0);
29302 r.setEnd(tgs[0],0);
29303 this.getEditor().Selection.GetSelection().removeAllRanges();
29304 this.getEditor().Selection.GetSelection().addRange(r);
29305 this.getEditor().Focus();
29312 replaceTextarea : function()
29314 if ( document.getElementById( this.getId() + '___Frame' ) )
29316 //if ( !this.checkBrowser || this._isCompatibleBrowser() )
29318 // We must check the elements firstly using the Id and then the name.
29319 var oTextarea = document.getElementById( this.getId() );
29321 var colElementsByName = document.getElementsByName( this.getId() ) ;
29323 oTextarea.style.display = 'none' ;
29325 if ( oTextarea.tabIndex ) {
29326 this.TabIndex = oTextarea.tabIndex ;
29329 this._insertHtmlBefore( this._getConfigHtml(), oTextarea ) ;
29330 this._insertHtmlBefore( this._getIFrameHtml(), oTextarea ) ;
29331 this.frame = Roo.get(this.getId() + '___Frame')
29334 _getConfigHtml : function()
29338 for ( var o in this.fckconfig ) {
29339 sConfig += sConfig.length > 0 ? '&' : '';
29340 sConfig += encodeURIComponent( o ) + '=' + encodeURIComponent( this.fckconfig[o] ) ;
29343 return '<input type="hidden" id="' + this.getId() + '___Config" value="' + sConfig + '" style="display:none" />' ;
29347 _getIFrameHtml : function()
29349 var sFile = 'fckeditor.html' ;
29350 /* no idea what this is about..
29353 if ( (/fcksource=true/i).test( window.top.location.search ) )
29354 sFile = 'fckeditor.original.html' ;
29359 var sLink = this.basePath + 'editor/' + sFile + '?InstanceName=' + encodeURIComponent( this.getId() ) ;
29360 sLink += this.toolbarSet ? ( '&Toolbar=' + this.toolbarSet) : '';
29363 var html = '<iframe id="' + this.getId() +
29364 '___Frame" src="' + sLink +
29365 '" width="' + this.width +
29366 '" height="' + this.height + '"' +
29367 (this.tabIndex ? ' tabindex="' + this.tabIndex + '"' :'' ) +
29368 ' frameborder="0" scrolling="no"></iframe>' ;
29373 _insertHtmlBefore : function( html, element )
29375 if ( element.insertAdjacentHTML ) {
29377 element.insertAdjacentHTML( 'beforeBegin', html ) ;
29379 var oRange = document.createRange() ;
29380 oRange.setStartBefore( element ) ;
29381 var oFragment = oRange.createContextualFragment( html );
29382 element.parentNode.insertBefore( oFragment, element ) ;
29395 //Roo.reg('fckeditor', Roo.form.FCKeditor);
29397 function FCKeditor_OnComplete(editorInstance){
29398 var f = Roo.form.FCKeditor.editors[editorInstance.Name];
29399 f.fckEditor = editorInstance;
29400 //console.log("loaded");
29401 f.fireEvent('editorinit', f, editorInstance);
29421 //<script type="text/javascript">
29423 * @class Roo.form.GridField
29424 * @extends Roo.form.Field
29425 * Embed a grid (or editable grid into a form)
29428 * This embeds a grid in a form, the value of the field should be the json encoded array of rows
29430 * xgrid.store = Roo.data.Store
29431 * xgrid.store.proxy = Roo.data.MemoryProxy (data = [] )
29432 * xgrid.store.reader = Roo.data.JsonReader
29436 * Creates a new GridField
29437 * @param {Object} config Configuration options
29439 Roo.form.GridField = function(config){
29440 Roo.form.GridField.superclass.constructor.call(this, config);
29444 Roo.extend(Roo.form.GridField, Roo.form.Field, {
29446 * @cfg {Number} width - used to restrict width of grid..
29450 * @cfg {Number} height - used to restrict height of grid..
29454 * @cfg {Object} xgrid (xtype'd description of grid) { xtype : 'Grid', dataSource: .... }
29460 * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
29461 * {tag: "input", type: "checkbox", autocomplete: "off"})
29463 // defaultAutoCreate : { tag: 'div' },
29464 defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'off'},
29466 * @cfg {String} addTitle Text to include for adding a title.
29470 onResize : function(){
29471 Roo.form.Field.superclass.onResize.apply(this, arguments);
29474 initEvents : function(){
29475 // Roo.form.Checkbox.superclass.initEvents.call(this);
29476 // has no events...
29481 getResizeEl : function(){
29485 getPositionEl : function(){
29490 onRender : function(ct, position){
29492 this.style = this.style || 'overflow: hidden; border:1px solid #c3daf9;';
29493 var style = this.style;
29496 Roo.form.GridField.superclass.onRender.call(this, ct, position);
29497 this.wrap = this.el.wrap({cls: ''}); // not sure why ive done thsi...
29498 this.viewEl = this.wrap.createChild({ tag: 'div' });
29500 this.viewEl.applyStyles(style);
29503 this.viewEl.setWidth(this.width);
29506 this.viewEl.setHeight(this.height);
29508 //if(this.inputValue !== undefined){
29509 //this.setValue(this.value);
29512 this.grid = new Roo.grid[this.xgrid.xtype](this.viewEl, this.xgrid);
29515 this.grid.render();
29516 this.grid.getDataSource().on('remove', this.refreshValue, this);
29517 this.grid.getDataSource().on('update', this.refreshValue, this);
29518 this.grid.on('afteredit', this.refreshValue, this);
29524 * Sets the value of the item.
29525 * @param {String} either an object or a string..
29527 setValue : function(v){
29529 v = v || []; // empty set..
29530 // this does not seem smart - it really only affects memoryproxy grids..
29531 if (this.grid && this.grid.getDataSource() && typeof(v) != 'undefined') {
29532 var ds = this.grid.getDataSource();
29533 // assumes a json reader..
29535 data[ds.reader.meta.root ] = typeof(v) == 'string' ? Roo.decode(v) : v;
29536 ds.loadData( data);
29538 // clear selection so it does not get stale.
29539 if (this.grid.sm) {
29540 this.grid.sm.clearSelections();
29543 Roo.form.GridField.superclass.setValue.call(this, v);
29544 this.refreshValue();
29545 // should load data in the grid really....
29549 refreshValue: function() {
29551 this.grid.getDataSource().each(function(r) {
29554 this.el.dom.value = Roo.encode(val);
29562 * Ext JS Library 1.1.1
29563 * Copyright(c) 2006-2007, Ext JS, LLC.
29565 * Originally Released Under LGPL - original licence link has changed is not relivant.
29568 * <script type="text/javascript">
29571 * @class Roo.form.DisplayField
29572 * @extends Roo.form.Field
29573 * A generic Field to display non-editable data.
29575 * Creates a new Display Field item.
29576 * @param {Object} config Configuration options
29578 Roo.form.DisplayField = function(config){
29579 Roo.form.DisplayField.superclass.constructor.call(this, config);
29583 Roo.extend(Roo.form.DisplayField, Roo.form.TextField, {
29584 inputType: 'hidden',
29590 * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
29592 focusClass : undefined,
29594 * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
29596 fieldClass: 'x-form-field',
29599 * @cfg {Function} valueRenderer The renderer for the field (so you can reformat output). should return raw HTML
29601 valueRenderer: undefined,
29605 * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
29606 * {tag: "input", type: "checkbox", autocomplete: "off"})
29609 // defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'off'},
29611 onResize : function(){
29612 Roo.form.DisplayField.superclass.onResize.apply(this, arguments);
29616 initEvents : function(){
29617 // Roo.form.Checkbox.superclass.initEvents.call(this);
29618 // has no events...
29623 getResizeEl : function(){
29627 getPositionEl : function(){
29632 onRender : function(ct, position){
29634 Roo.form.DisplayField.superclass.onRender.call(this, ct, position);
29635 //if(this.inputValue !== undefined){
29636 this.wrap = this.el.wrap();
29638 this.viewEl = this.wrap.createChild({ tag: 'div', cls: 'x-form-displayfield'});
29640 if (this.bodyStyle) {
29641 this.viewEl.applyStyles(this.bodyStyle);
29643 //this.viewEl.setStyle('padding', '2px');
29645 this.setValue(this.value);
29650 initValue : Roo.emptyFn,
29655 onClick : function(){
29660 * Sets the checked state of the checkbox.
29661 * @param {Boolean/String} checked True, 'true', '1', or 'on' to check the checkbox, any other value will uncheck it.
29663 setValue : function(v){
29665 var html = this.valueRenderer ? this.valueRenderer(v) : String.format('{0}', v);
29666 // this might be called before we have a dom element..
29667 if (!this.viewEl) {
29670 this.viewEl.dom.innerHTML = html;
29671 Roo.form.DisplayField.superclass.setValue.call(this, v);
29681 * @class Roo.form.DayPicker
29682 * @extends Roo.form.Field
29683 * A Day picker show [M] [T] [W] ....
29685 * Creates a new Day Picker
29686 * @param {Object} config Configuration options
29688 Roo.form.DayPicker= function(config){
29689 Roo.form.DayPicker.superclass.constructor.call(this, config);
29693 Roo.extend(Roo.form.DayPicker, Roo.form.Field, {
29695 * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
29697 focusClass : undefined,
29699 * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
29701 fieldClass: "x-form-field",
29704 * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
29705 * {tag: "input", type: "checkbox", autocomplete: "off"})
29707 defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "off"},
29710 actionMode : 'viewEl',
29714 inputType : 'hidden',
29717 inputElement: false, // real input element?
29718 basedOn: false, // ????
29720 isFormField: true, // not sure where this is needed!!!!
29722 onResize : function(){
29723 Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
29724 if(!this.boxLabel){
29725 this.el.alignTo(this.wrap, 'c-c');
29729 initEvents : function(){
29730 Roo.form.Checkbox.superclass.initEvents.call(this);
29731 this.el.on("click", this.onClick, this);
29732 this.el.on("change", this.onClick, this);
29736 getResizeEl : function(){
29740 getPositionEl : function(){
29746 onRender : function(ct, position){
29747 Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
29749 this.wrap = this.el.wrap({cls: 'x-form-daypick-item '});
29751 var r1 = '<table><tr>';
29752 var r2 = '<tr class="x-form-daypick-icons">';
29753 for (var i=0; i < 7; i++) {
29754 r1+= '<td><div>' + Date.dayNames[i].substring(0,3) + '</div></td>';
29755 r2+= '<td><img class="x-menu-item-icon" src="' + Roo.BLANK_IMAGE_URL +'"></td>';
29758 var viewEl = this.wrap.createChild( r1 + '</tr>' + r2 + '</tr></table>');
29759 viewEl.select('img').on('click', this.onClick, this);
29760 this.viewEl = viewEl;
29763 // this will not work on Chrome!!!
29764 this.el.on('DOMAttrModified', this.setFromHidden, this); //ff
29765 this.el.on('propertychange', this.setFromHidden, this); //ie
29773 initValue : Roo.emptyFn,
29776 * Returns the checked state of the checkbox.
29777 * @return {Boolean} True if checked, else false
29779 getValue : function(){
29780 return this.el.dom.value;
29785 onClick : function(e){
29786 //this.setChecked(!this.checked);
29787 Roo.get(e.target).toggleClass('x-menu-item-checked');
29788 this.refreshValue();
29789 //if(this.el.dom.checked != this.checked){
29790 // this.setValue(this.el.dom.checked);
29795 refreshValue : function()
29798 this.viewEl.select('img',true).each(function(e,i,n) {
29799 val += e.is(".x-menu-item-checked") ? String(n) : '';
29801 this.setValue(val, true);
29805 * Sets the checked state of the checkbox.
29806 * On is always based on a string comparison between inputValue and the param.
29807 * @param {Boolean/String} value - the value to set
29808 * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
29810 setValue : function(v,suppressEvent){
29811 if (!this.el.dom) {
29814 var old = this.el.dom.value ;
29815 this.el.dom.value = v;
29816 if (suppressEvent) {
29820 // update display..
29821 this.viewEl.select('img',true).each(function(e,i,n) {
29823 var on = e.is(".x-menu-item-checked");
29824 var newv = v.indexOf(String(n)) > -1;
29826 e.toggleClass('x-menu-item-checked');
29832 this.fireEvent('change', this, v, old);
29837 // handle setting of hidden value by some other method!!?!?
29838 setFromHidden: function()
29843 //console.log("SET FROM HIDDEN");
29844 //alert('setFrom hidden');
29845 this.setValue(this.el.dom.value);
29848 onDestroy : function()
29851 Roo.get(this.viewEl).remove();
29854 Roo.form.DayPicker.superclass.onDestroy.call(this);
29858 * RooJS Library 1.1.1
29859 * Copyright(c) 2008-2011 Alan Knowles
29866 * @class Roo.form.ComboCheck
29867 * @extends Roo.form.ComboBox
29868 * A combobox for multiple select items.
29870 * FIXME - could do with a reset button..
29873 * Create a new ComboCheck
29874 * @param {Object} config Configuration options
29876 Roo.form.ComboCheck = function(config){
29877 Roo.form.ComboCheck.superclass.constructor.call(this, config);
29878 // should verify some data...
29880 // hiddenName = required..
29881 // displayField = required
29882 // valudField == required
29883 var req= [ 'hiddenName', 'displayField', 'valueField' ];
29885 Roo.each(req, function(e) {
29886 if ((typeof(_t[e]) == 'undefined' ) || !_t[e].length) {
29887 throw "Roo.form.ComboCheck : missing value for: " + e;
29894 Roo.extend(Roo.form.ComboCheck, Roo.form.ComboBox, {
29899 selectedClass: 'x-menu-item-checked',
29902 onRender : function(ct, position){
29908 var cls = 'x-combo-list';
29911 this.tpl = new Roo.Template({
29912 html : '<div class="'+cls+'-item x-menu-check-item">' +
29913 '<img class="x-menu-item-icon" style="margin: 0px;" src="' + Roo.BLANK_IMAGE_URL + '">' +
29914 '<span>{' + this.displayField + '}</span>' +
29921 Roo.form.ComboCheck.superclass.onRender.call(this, ct, position);
29922 this.view.singleSelect = false;
29923 this.view.multiSelect = true;
29924 this.view.toggleSelect = true;
29925 this.pageTb.add(new Roo.Toolbar.Fill(), {
29928 handler: function()
29935 onViewOver : function(e, t){
29941 onViewClick : function(doFocus,index){
29945 select: function () {
29946 //Roo.log("SELECT CALLED");
29949 selectByValue : function(xv, scrollIntoView){
29950 var ar = this.getValueArray();
29953 Roo.each(ar, function(v) {
29954 if(v === undefined || v === null){
29957 var r = this.findRecord(this.valueField, v);
29959 sels.push(this.store.indexOf(r))
29963 this.view.select(sels);
29969 onSelect : function(record, index){
29970 // Roo.log("onselect Called");
29971 // this is only called by the clear button now..
29972 this.view.clearSelections();
29973 this.setValue('[]');
29974 if (this.value != this.valueBefore) {
29975 this.fireEvent('change', this, this.value, this.valueBefore);
29978 getValueArray : function()
29983 //Roo.log(this.value);
29984 if (typeof(this.value) == 'undefined') {
29987 var ar = Roo.decode(this.value);
29988 return ar instanceof Array ? ar : []; //?? valid?
29991 Roo.log(e + "\nRoo.form.ComboCheck:getValueArray invalid data:" + this.getValue());
29996 expand : function ()
29998 Roo.form.ComboCheck.superclass.expand.call(this);
29999 this.valueBefore = this.value;
30004 collapse : function(){
30005 Roo.form.ComboCheck.superclass.collapse.call(this);
30006 var sl = this.view.getSelectedIndexes();
30007 var st = this.store;
30011 Roo.each(sl, function(i) {
30013 nv.push(r.get(this.valueField));
30015 this.setValue(Roo.encode(nv));
30016 if (this.value != this.valueBefore) {
30018 this.fireEvent('change', this, this.value, this.valueBefore);
30023 setValue : function(v){
30027 var vals = this.getValueArray();
30029 Roo.each(vals, function(k) {
30030 var r = this.findRecord(this.valueField, k);
30032 tv.push(r.data[this.displayField]);
30033 }else if(this.valueNotFoundText !== undefined){
30034 tv.push( this.valueNotFoundText );
30039 Roo.form.ComboBox.superclass.setValue.call(this, tv.join(', '));
30040 this.hiddenField.value = v;
30044 });//<script type="text/javasscript">
30048 * @class Roo.DDView
30049 * A DnD enabled version of Roo.View.
30050 * @param {Element/String} container The Element in which to create the View.
30051 * @param {String} tpl The template string used to create the markup for each element of the View
30052 * @param {Object} config The configuration properties. These include all the config options of
30053 * {@link Roo.View} plus some specific to this class.<br>
30055 * Drag/drop is implemented by adding {@link Roo.data.Record}s to the target DDView. If copying is
30056 * not being performed, the original {@link Roo.data.Record} is removed from the source DDView.<br>
30058 * The following extra CSS rules are needed to provide insertion point highlighting:<pre><code>
30059 .x-view-drag-insert-above {
30060 border-top:1px dotted #3366cc;
30062 .x-view-drag-insert-below {
30063 border-bottom:1px dotted #3366cc;
30069 Roo.DDView = function(container, tpl, config) {
30070 Roo.DDView.superclass.constructor.apply(this, arguments);
30071 this.getEl().setStyle("outline", "0px none");
30072 this.getEl().unselectable();
30073 if (this.dragGroup) {
30074 this.setDraggable(this.dragGroup.split(","));
30076 if (this.dropGroup) {
30077 this.setDroppable(this.dropGroup.split(","));
30079 if (this.deletable) {
30080 this.setDeletable();
30082 this.isDirtyFlag = false;
30088 Roo.extend(Roo.DDView, Roo.View, {
30089 /** @cfg {String/Array} dragGroup The ddgroup name(s) for the View's DragZone. */
30090 /** @cfg {String/Array} dropGroup The ddgroup name(s) for the View's DropZone. */
30091 /** @cfg {Boolean} copy Causes drag operations to copy nodes rather than move. */
30092 /** @cfg {Boolean} allowCopy Causes ctrl/drag operations to copy nodes rather than move. */
30096 reset: Roo.emptyFn,
30098 clearInvalid: Roo.form.Field.prototype.clearInvalid,
30100 validate: function() {
30104 destroy: function() {
30105 this.purgeListeners();
30106 this.getEl.removeAllListeners();
30107 this.getEl().remove();
30108 if (this.dragZone) {
30109 if (this.dragZone.destroy) {
30110 this.dragZone.destroy();
30113 if (this.dropZone) {
30114 if (this.dropZone.destroy) {
30115 this.dropZone.destroy();
30120 /** Allows this class to be an Roo.form.Field so it can be found using {@link Roo.form.BasicForm#findField}. */
30121 getName: function() {
30125 /** Loads the View from a JSON string representing the Records to put into the Store. */
30126 setValue: function(v) {
30128 throw "DDView.setValue(). DDView must be constructed with a valid Store";
30131 data[this.store.reader.meta.root] = v ? [].concat(v) : [];
30132 this.store.proxy = new Roo.data.MemoryProxy(data);
30136 /** @return {String} a parenthesised list of the ids of the Records in the View. */
30137 getValue: function() {
30139 this.store.each(function(rec) {
30140 result += rec.id + ',';
30142 return result.substr(0, result.length - 1) + ')';
30145 getIds: function() {
30146 var i = 0, result = new Array(this.store.getCount());
30147 this.store.each(function(rec) {
30148 result[i++] = rec.id;
30153 isDirty: function() {
30154 return this.isDirtyFlag;
30158 * Part of the Roo.dd.DropZone interface. If no target node is found, the
30159 * whole Element becomes the target, and this causes the drop gesture to append.
30161 getTargetFromEvent : function(e) {
30162 var target = e.getTarget();
30163 while ((target !== null) && (target.parentNode != this.el.dom)) {
30164 target = target.parentNode;
30167 target = this.el.dom.lastChild || this.el.dom;
30173 * Create the drag data which consists of an object which has the property "ddel" as
30174 * the drag proxy element.
30176 getDragData : function(e) {
30177 var target = this.findItemFromChild(e.getTarget());
30179 this.handleSelection(e);
30180 var selNodes = this.getSelectedNodes();
30183 copy: this.copy || (this.allowCopy && e.ctrlKey),
30187 var selectedIndices = this.getSelectedIndexes();
30188 for (var i = 0; i < selectedIndices.length; i++) {
30189 dragData.records.push(this.store.getAt(selectedIndices[i]));
30191 if (selNodes.length == 1) {
30192 dragData.ddel = target.cloneNode(true); // the div element
30194 var div = document.createElement('div'); // create the multi element drag "ghost"
30195 div.className = 'multi-proxy';
30196 for (var i = 0, len = selNodes.length; i < len; i++) {
30197 div.appendChild(selNodes[i].cloneNode(true));
30199 dragData.ddel = div;
30201 //console.log(dragData)
30202 //console.log(dragData.ddel.innerHTML)
30205 //console.log('nodragData')
30209 /** Specify to which ddGroup items in this DDView may be dragged. */
30210 setDraggable: function(ddGroup) {
30211 if (ddGroup instanceof Array) {
30212 Roo.each(ddGroup, this.setDraggable, this);
30215 if (this.dragZone) {
30216 this.dragZone.addToGroup(ddGroup);
30218 this.dragZone = new Roo.dd.DragZone(this.getEl(), {
30219 containerScroll: true,
30223 // Draggability implies selection. DragZone's mousedown selects the element.
30224 if (!this.multiSelect) { this.singleSelect = true; }
30226 // Wire the DragZone's handlers up to methods in *this*
30227 this.dragZone.getDragData = this.getDragData.createDelegate(this);
30231 /** Specify from which ddGroup this DDView accepts drops. */
30232 setDroppable: function(ddGroup) {
30233 if (ddGroup instanceof Array) {
30234 Roo.each(ddGroup, this.setDroppable, this);
30237 if (this.dropZone) {
30238 this.dropZone.addToGroup(ddGroup);
30240 this.dropZone = new Roo.dd.DropZone(this.getEl(), {
30241 containerScroll: true,
30245 // Wire the DropZone's handlers up to methods in *this*
30246 this.dropZone.getTargetFromEvent = this.getTargetFromEvent.createDelegate(this);
30247 this.dropZone.onNodeEnter = this.onNodeEnter.createDelegate(this);
30248 this.dropZone.onNodeOver = this.onNodeOver.createDelegate(this);
30249 this.dropZone.onNodeOut = this.onNodeOut.createDelegate(this);
30250 this.dropZone.onNodeDrop = this.onNodeDrop.createDelegate(this);
30254 /** Decide whether to drop above or below a View node. */
30255 getDropPoint : function(e, n, dd){
30256 if (n == this.el.dom) { return "above"; }
30257 var t = Roo.lib.Dom.getY(n), b = t + n.offsetHeight;
30258 var c = t + (b - t) / 2;
30259 var y = Roo.lib.Event.getPageY(e);
30267 onNodeEnter : function(n, dd, e, data){
30271 onNodeOver : function(n, dd, e, data){
30272 var pt = this.getDropPoint(e, n, dd);
30273 // set the insert point style on the target node
30274 var dragElClass = this.dropNotAllowed;
30277 if (pt == "above"){
30278 dragElClass = n.previousSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-above";
30279 targetElClass = "x-view-drag-insert-above";
30281 dragElClass = n.nextSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-below";
30282 targetElClass = "x-view-drag-insert-below";
30284 if (this.lastInsertClass != targetElClass){
30285 Roo.fly(n).replaceClass(this.lastInsertClass, targetElClass);
30286 this.lastInsertClass = targetElClass;
30289 return dragElClass;
30292 onNodeOut : function(n, dd, e, data){
30293 this.removeDropIndicators(n);
30296 onNodeDrop : function(n, dd, e, data){
30297 if (this.fireEvent("drop", this, n, dd, e, data) === false) {
30300 var pt = this.getDropPoint(e, n, dd);
30301 var insertAt = (n == this.el.dom) ? this.nodes.length : n.nodeIndex;
30302 if (pt == "below") { insertAt++; }
30303 for (var i = 0; i < data.records.length; i++) {
30304 var r = data.records[i];
30305 var dup = this.store.getById(r.id);
30306 if (dup && (dd != this.dragZone)) {
30307 Roo.fly(this.getNode(this.store.indexOf(dup))).frame("red", 1);
30310 this.store.insert(insertAt++, r.copy());
30312 data.source.isDirtyFlag = true;
30314 this.store.insert(insertAt++, r);
30316 this.isDirtyFlag = true;
30319 this.dragZone.cachedTarget = null;
30323 removeDropIndicators : function(n){
30325 Roo.fly(n).removeClass([
30326 "x-view-drag-insert-above",
30327 "x-view-drag-insert-below"]);
30328 this.lastInsertClass = "_noclass";
30333 * Utility method. Add a delete option to the DDView's context menu.
30334 * @param {String} imageUrl The URL of the "delete" icon image.
30336 setDeletable: function(imageUrl) {
30337 if (!this.singleSelect && !this.multiSelect) {
30338 this.singleSelect = true;
30340 var c = this.getContextMenu();
30341 this.contextMenu.on("itemclick", function(item) {
30344 this.remove(this.getSelectedIndexes());
30348 this.contextMenu.add({
30355 /** Return the context menu for this DDView. */
30356 getContextMenu: function() {
30357 if (!this.contextMenu) {
30358 // Create the View's context menu
30359 this.contextMenu = new Roo.menu.Menu({
30360 id: this.id + "-contextmenu"
30362 this.el.on("contextmenu", this.showContextMenu, this);
30364 return this.contextMenu;
30367 disableContextMenu: function() {
30368 if (this.contextMenu) {
30369 this.el.un("contextmenu", this.showContextMenu, this);
30373 showContextMenu: function(e, item) {
30374 item = this.findItemFromChild(e.getTarget());
30377 this.select(this.getNode(item), this.multiSelect && e.ctrlKey, true);
30378 this.contextMenu.showAt(e.getXY());
30383 * Remove {@link Roo.data.Record}s at the specified indices.
30384 * @param {Array/Number} selectedIndices The index (or Array of indices) of Records to remove.
30386 remove: function(selectedIndices) {
30387 selectedIndices = [].concat(selectedIndices);
30388 for (var i = 0; i < selectedIndices.length; i++) {
30389 var rec = this.store.getAt(selectedIndices[i]);
30390 this.store.remove(rec);
30395 * Double click fires the event, but also, if this is draggable, and there is only one other
30396 * related DropZone, it transfers the selected node.
30398 onDblClick : function(e){
30399 var item = this.findItemFromChild(e.getTarget());
30401 if (this.fireEvent("dblclick", this, this.indexOf(item), item, e) === false) {
30404 if (this.dragGroup) {
30405 var targets = Roo.dd.DragDropMgr.getRelated(this.dragZone, true);
30406 while (targets.indexOf(this.dropZone) > -1) {
30407 targets.remove(this.dropZone);
30409 if (targets.length == 1) {
30410 this.dragZone.cachedTarget = null;
30411 var el = Roo.get(targets[0].getEl());
30412 var box = el.getBox(true);
30413 targets[0].onNodeDrop(el.dom, {
30415 xy: [box.x, box.y + box.height - 1]
30416 }, null, this.getDragData(e));
30422 handleSelection: function(e) {
30423 this.dragZone.cachedTarget = null;
30424 var item = this.findItemFromChild(e.getTarget());
30426 this.clearSelections(true);
30429 if (item && (this.multiSelect || this.singleSelect)){
30430 if(this.multiSelect && e.shiftKey && (!e.ctrlKey) && this.lastSelection){
30431 this.select(this.getNodes(this.indexOf(this.lastSelection), item.nodeIndex), false);
30432 }else if (this.isSelected(this.getNode(item)) && e.ctrlKey){
30433 this.unselect(item);
30435 this.select(item, this.multiSelect && e.ctrlKey);
30436 this.lastSelection = item;
30441 onItemClick : function(item, index, e){
30442 if(this.fireEvent("beforeclick", this, index, item, e) === false){
30448 unselect : function(nodeInfo, suppressEvent){
30449 var node = this.getNode(nodeInfo);
30450 if(node && this.isSelected(node)){
30451 if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
30452 Roo.fly(node).removeClass(this.selectedClass);
30453 this.selections.remove(node);
30454 if(!suppressEvent){
30455 this.fireEvent("selectionchange", this, this.selections);
30463 * Ext JS Library 1.1.1
30464 * Copyright(c) 2006-2007, Ext JS, LLC.
30466 * Originally Released Under LGPL - original licence link has changed is not relivant.
30469 * <script type="text/javascript">
30473 * @class Roo.LayoutManager
30474 * @extends Roo.util.Observable
30475 * Base class for layout managers.
30477 Roo.LayoutManager = function(container, config){
30478 Roo.LayoutManager.superclass.constructor.call(this);
30479 this.el = Roo.get(container);
30480 // ie scrollbar fix
30481 if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
30482 document.body.scroll = "no";
30483 }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
30484 this.el.position('relative');
30486 this.id = this.el.id;
30487 this.el.addClass("x-layout-container");
30488 /** false to disable window resize monitoring @type Boolean */
30489 this.monitorWindowResize = true;
30494 * Fires when a layout is performed.
30495 * @param {Roo.LayoutManager} this
30499 * @event regionresized
30500 * Fires when the user resizes a region.
30501 * @param {Roo.LayoutRegion} region The resized region
30502 * @param {Number} newSize The new size (width for east/west, height for north/south)
30504 "regionresized" : true,
30506 * @event regioncollapsed
30507 * Fires when a region is collapsed.
30508 * @param {Roo.LayoutRegion} region The collapsed region
30510 "regioncollapsed" : true,
30512 * @event regionexpanded
30513 * Fires when a region is expanded.
30514 * @param {Roo.LayoutRegion} region The expanded region
30516 "regionexpanded" : true
30518 this.updating = false;
30519 Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
30522 Roo.extend(Roo.LayoutManager, Roo.util.Observable, {
30524 * Returns true if this layout is currently being updated
30525 * @return {Boolean}
30527 isUpdating : function(){
30528 return this.updating;
30532 * Suspend the LayoutManager from doing auto-layouts while
30533 * making multiple add or remove calls
30535 beginUpdate : function(){
30536 this.updating = true;
30540 * Restore auto-layouts and optionally disable the manager from performing a layout
30541 * @param {Boolean} noLayout true to disable a layout update
30543 endUpdate : function(noLayout){
30544 this.updating = false;
30550 layout: function(){
30554 onRegionResized : function(region, newSize){
30555 this.fireEvent("regionresized", region, newSize);
30559 onRegionCollapsed : function(region){
30560 this.fireEvent("regioncollapsed", region);
30563 onRegionExpanded : function(region){
30564 this.fireEvent("regionexpanded", region);
30568 * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
30569 * performs box-model adjustments.
30570 * @return {Object} The size as an object {width: (the width), height: (the height)}
30572 getViewSize : function(){
30574 if(this.el.dom != document.body){
30575 size = this.el.getSize();
30577 size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
30579 size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
30580 size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
30585 * Returns the Element this layout is bound to.
30586 * @return {Roo.Element}
30588 getEl : function(){
30593 * Returns the specified region.
30594 * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
30595 * @return {Roo.LayoutRegion}
30597 getRegion : function(target){
30598 return this.regions[target.toLowerCase()];
30601 onWindowResize : function(){
30602 if(this.monitorWindowResize){
30608 * Ext JS Library 1.1.1
30609 * Copyright(c) 2006-2007, Ext JS, LLC.
30611 * Originally Released Under LGPL - original licence link has changed is not relivant.
30614 * <script type="text/javascript">
30617 * @class Roo.BorderLayout
30618 * @extends Roo.LayoutManager
30619 * This class represents a common layout manager used in desktop applications. For screenshots and more details,
30620 * please see: <br><br>
30621 * <a href="http://www.jackslocum.com/yui/2006/10/19/cross-browser-web-20-layouts-with-yahoo-ui/">Cross Browser Layouts - Part 1</a><br>
30622 * <a href="http://www.jackslocum.com/yui/2006/10/28/cross-browser-web-20-layouts-part-2-ajax-feed-viewer-20/">Cross Browser Layouts - Part 2</a><br><br>
30625 var layout = new Roo.BorderLayout(document.body, {
30659 preferredTabWidth: 150
30664 var CP = Roo.ContentPanel;
30666 layout.beginUpdate();
30667 layout.add("north", new CP("north", "North"));
30668 layout.add("south", new CP("south", {title: "South", closable: true}));
30669 layout.add("west", new CP("west", {title: "West"}));
30670 layout.add("east", new CP("autoTabs", {title: "Auto Tabs", closable: true}));
30671 layout.add("center", new CP("center1", {title: "Close Me", closable: true}));
30672 layout.add("center", new CP("center2", {title: "Center Panel", closable: false}));
30673 layout.getRegion("center").showPanel("center1");
30674 layout.endUpdate();
30677 <b>The container the layout is rendered into can be either the body element or any other element.
30678 If it is not the body element, the container needs to either be an absolute positioned element,
30679 or you will need to add "position:relative" to the css of the container. You will also need to specify
30680 the container size if it is not the body element.</b>
30683 * Create a new BorderLayout
30684 * @param {String/HTMLElement/Element} container The container this layout is bound to
30685 * @param {Object} config Configuration options
30687 Roo.BorderLayout = function(container, config){
30688 config = config || {};
30689 Roo.BorderLayout.superclass.constructor.call(this, container, config);
30690 this.factory = config.factory || Roo.BorderLayout.RegionFactory;
30691 for(var i = 0, len = this.factory.validRegions.length; i < len; i++) {
30692 var target = this.factory.validRegions[i];
30693 if(config[target]){
30694 this.addRegion(target, config[target]);
30699 Roo.extend(Roo.BorderLayout, Roo.LayoutManager, {
30701 * Creates and adds a new region if it doesn't already exist.
30702 * @param {String} target The target region key (north, south, east, west or center).
30703 * @param {Object} config The regions config object
30704 * @return {BorderLayoutRegion} The new region
30706 addRegion : function(target, config){
30707 if(!this.regions[target]){
30708 var r = this.factory.create(target, this, config);
30709 this.bindRegion(target, r);
30711 return this.regions[target];
30715 bindRegion : function(name, r){
30716 this.regions[name] = r;
30717 r.on("visibilitychange", this.layout, this);
30718 r.on("paneladded", this.layout, this);
30719 r.on("panelremoved", this.layout, this);
30720 r.on("invalidated", this.layout, this);
30721 r.on("resized", this.onRegionResized, this);
30722 r.on("collapsed", this.onRegionCollapsed, this);
30723 r.on("expanded", this.onRegionExpanded, this);
30727 * Performs a layout update.
30729 layout : function(){
30730 if(this.updating) return;
30731 var size = this.getViewSize();
30732 var w = size.width;
30733 var h = size.height;
30738 //var x = 0, y = 0;
30740 var rs = this.regions;
30741 var north = rs["north"];
30742 var south = rs["south"];
30743 var west = rs["west"];
30744 var east = rs["east"];
30745 var center = rs["center"];
30746 //if(this.hideOnLayout){ // not supported anymore
30747 //c.el.setStyle("display", "none");
30749 if(north && north.isVisible()){
30750 var b = north.getBox();
30751 var m = north.getMargins();
30752 b.width = w - (m.left+m.right);
30755 centerY = b.height + b.y + m.bottom;
30756 centerH -= centerY;
30757 north.updateBox(this.safeBox(b));
30759 if(south && south.isVisible()){
30760 var b = south.getBox();
30761 var m = south.getMargins();
30762 b.width = w - (m.left+m.right);
30764 var totalHeight = (b.height + m.top + m.bottom);
30765 b.y = h - totalHeight + m.top;
30766 centerH -= totalHeight;
30767 south.updateBox(this.safeBox(b));
30769 if(west && west.isVisible()){
30770 var b = west.getBox();
30771 var m = west.getMargins();
30772 b.height = centerH - (m.top+m.bottom);
30774 b.y = centerY + m.top;
30775 var totalWidth = (b.width + m.left + m.right);
30776 centerX += totalWidth;
30777 centerW -= totalWidth;
30778 west.updateBox(this.safeBox(b));
30780 if(east && east.isVisible()){
30781 var b = east.getBox();
30782 var m = east.getMargins();
30783 b.height = centerH - (m.top+m.bottom);
30784 var totalWidth = (b.width + m.left + m.right);
30785 b.x = w - totalWidth + m.left;
30786 b.y = centerY + m.top;
30787 centerW -= totalWidth;
30788 east.updateBox(this.safeBox(b));
30791 var m = center.getMargins();
30793 x: centerX + m.left,
30794 y: centerY + m.top,
30795 width: centerW - (m.left+m.right),
30796 height: centerH - (m.top+m.bottom)
30798 //if(this.hideOnLayout){
30799 //center.el.setStyle("display", "block");
30801 center.updateBox(this.safeBox(centerBox));
30804 this.fireEvent("layout", this);
30808 safeBox : function(box){
30809 box.width = Math.max(0, box.width);
30810 box.height = Math.max(0, box.height);
30815 * Adds a ContentPanel (or subclass) to this layout.
30816 * @param {String} target The target region key (north, south, east, west or center).
30817 * @param {Roo.ContentPanel} panel The panel to add
30818 * @return {Roo.ContentPanel} The added panel
30820 add : function(target, panel){
30822 target = target.toLowerCase();
30823 return this.regions[target].add(panel);
30827 * Remove a ContentPanel (or subclass) to this layout.
30828 * @param {String} target The target region key (north, south, east, west or center).
30829 * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
30830 * @return {Roo.ContentPanel} The removed panel
30832 remove : function(target, panel){
30833 target = target.toLowerCase();
30834 return this.regions[target].remove(panel);
30838 * Searches all regions for a panel with the specified id
30839 * @param {String} panelId
30840 * @return {Roo.ContentPanel} The panel or null if it wasn't found
30842 findPanel : function(panelId){
30843 var rs = this.regions;
30844 for(var target in rs){
30845 if(typeof rs[target] != "function"){
30846 var p = rs[target].getPanel(panelId);
30856 * Searches all regions for a panel with the specified id and activates (shows) it.
30857 * @param {String/ContentPanel} panelId The panels id or the panel itself
30858 * @return {Roo.ContentPanel} The shown panel or null
30860 showPanel : function(panelId) {
30861 var rs = this.regions;
30862 for(var target in rs){
30863 var r = rs[target];
30864 if(typeof r != "function"){
30865 if(r.hasPanel(panelId)){
30866 return r.showPanel(panelId);
30874 * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
30875 * @param {Roo.state.Provider} provider (optional) An alternate state provider
30877 restoreState : function(provider){
30879 provider = Roo.state.Manager;
30881 var sm = new Roo.LayoutStateManager();
30882 sm.init(this, provider);
30886 * Adds a batch of multiple ContentPanels dynamically by passing a special regions config object. This config
30887 * object should contain properties for each region to add ContentPanels to, and each property's value should be
30888 * a valid ContentPanel config object. Example:
30890 // Create the main layout
30891 var layout = new Roo.BorderLayout('main-ct', {
30902 // Create and add multiple ContentPanels at once via configs
30905 id: 'source-files',
30907 title:'Ext Source Files',
30920 * @param {Object} regions An object containing ContentPanel configs by region name
30922 batchAdd : function(regions){
30923 this.beginUpdate();
30924 for(var rname in regions){
30925 var lr = this.regions[rname];
30927 this.addTypedPanels(lr, regions[rname]);
30934 addTypedPanels : function(lr, ps){
30935 if(typeof ps == 'string'){
30936 lr.add(new Roo.ContentPanel(ps));
30938 else if(ps instanceof Array){
30939 for(var i =0, len = ps.length; i < len; i++){
30940 this.addTypedPanels(lr, ps[i]);
30943 else if(!ps.events){ // raw config?
30945 delete ps.el; // prevent conflict
30946 lr.add(new Roo.ContentPanel(el || Roo.id(), ps));
30948 else { // panel object assumed!
30953 * Adds a xtype elements to the layout.
30957 xtype : 'ContentPanel',
30964 xtype : 'NestedLayoutPanel',
30970 items : [ ... list of content panels or nested layout panels.. ]
30974 * @param {Object} cfg Xtype definition of item to add.
30976 addxtype : function(cfg)
30978 // basically accepts a pannel...
30979 // can accept a layout region..!?!?
30980 //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
30982 if (!cfg.xtype.match(/Panel$/)) {
30987 if (typeof(cfg.region) == 'undefined') {
30988 Roo.log("Failed to add Panel, region was not set");
30992 var region = cfg.region;
30998 xitems = cfg.items;
31005 case 'ContentPanel': // ContentPanel (el, cfg)
31006 case 'ScrollPanel': // ContentPanel (el, cfg)
31007 if(cfg.autoCreate) {
31008 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
31010 var el = this.el.createChild();
31011 ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
31014 this.add(region, ret);
31018 case 'TreePanel': // our new panel!
31019 cfg.el = this.el.createChild();
31020 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
31021 this.add(region, ret);
31024 case 'NestedLayoutPanel':
31025 // create a new Layout (which is a Border Layout...
31026 var el = this.el.createChild();
31027 var clayout = cfg.layout;
31029 clayout.items = clayout.items || [];
31030 // replace this exitems with the clayout ones..
31031 xitems = clayout.items;
31034 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
31035 cfg.background = false;
31037 var layout = new Roo.BorderLayout(el, clayout);
31039 ret = new Roo[cfg.xtype](layout, cfg); // new panel!!!!!
31040 //console.log('adding nested layout panel ' + cfg.toSource());
31041 this.add(region, ret);
31042 nb = {}; /// find first...
31047 // needs grid and region
31049 //var el = this.getRegion(region).el.createChild();
31050 var el = this.el.createChild();
31051 // create the grid first...
31053 var grid = new Roo.grid[cfg.grid.xtype](el, cfg.grid);
31055 if (region == 'center' && this.active ) {
31056 cfg.background = false;
31058 ret = new Roo[cfg.xtype](grid, cfg); // new panel!!!!!
31060 this.add(region, ret);
31061 if (cfg.background) {
31062 ret.on('activate', function(gp) {
31063 if (!gp.grid.rendered) {
31076 alert("Can not add '" + cfg.xtype + "' to BorderLayout");
31078 // GridPanel (grid, cfg)
31081 this.beginUpdate();
31085 Roo.each(xitems, function(i) {
31086 region = nb && i.region ? i.region : false;
31088 var add = ret.addxtype(i);
31091 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
31092 if (!i.background) {
31093 abn[region] = nb[region] ;
31100 // make the last non-background panel active..
31101 //if (nb) { Roo.log(abn); }
31104 for(var r in abn) {
31105 region = this.getRegion(r);
31107 // tried using nb[r], but it does not work..
31109 region.showPanel(abn[r]);
31120 * Shortcut for creating a new BorderLayout object and adding one or more ContentPanels to it in a single step, handling
31121 * the beginUpdate and endUpdate calls internally. The key to this method is the <b>panels</b> property that can be
31122 * provided with each region config, which allows you to add ContentPanel configs in addition to the region configs
31123 * during creation. The following code is equivalent to the constructor-based example at the beginning of this class:
31126 var CP = Roo.ContentPanel;
31128 var layout = Roo.BorderLayout.create({
31132 panels: [new CP("north", "North")]
31141 panels: [new CP("west", {title: "West"})]
31150 panels: [new CP("autoTabs", {title: "Auto Tabs", closable: true})]
31159 panels: [new CP("south", {title: "South", closable: true})]
31166 preferredTabWidth: 150,
31168 new CP("center1", {title: "Close Me", closable: true}),
31169 new CP("center2", {title: "Center Panel", closable: false})
31174 layout.getRegion("center").showPanel("center1");
31179 Roo.BorderLayout.create = function(config, targetEl){
31180 var layout = new Roo.BorderLayout(targetEl || document.body, config);
31181 layout.beginUpdate();
31182 var regions = Roo.BorderLayout.RegionFactory.validRegions;
31183 for(var j = 0, jlen = regions.length; j < jlen; j++){
31184 var lr = regions[j];
31185 if(layout.regions[lr] && config[lr].panels){
31186 var r = layout.regions[lr];
31187 var ps = config[lr].panels;
31188 layout.addTypedPanels(r, ps);
31191 layout.endUpdate();
31196 Roo.BorderLayout.RegionFactory = {
31198 validRegions : ["north","south","east","west","center"],
31201 create : function(target, mgr, config){
31202 target = target.toLowerCase();
31203 if(config.lightweight || config.basic){
31204 return new Roo.BasicLayoutRegion(mgr, config, target);
31208 return new Roo.NorthLayoutRegion(mgr, config);
31210 return new Roo.SouthLayoutRegion(mgr, config);
31212 return new Roo.EastLayoutRegion(mgr, config);
31214 return new Roo.WestLayoutRegion(mgr, config);
31216 return new Roo.CenterLayoutRegion(mgr, config);
31218 throw 'Layout region "'+target+'" not supported.';
31222 * Ext JS Library 1.1.1
31223 * Copyright(c) 2006-2007, Ext JS, LLC.
31225 * Originally Released Under LGPL - original licence link has changed is not relivant.
31228 * <script type="text/javascript">
31232 * @class Roo.BasicLayoutRegion
31233 * @extends Roo.util.Observable
31234 * This class represents a lightweight region in a layout manager. This region does not move dom nodes
31235 * and does not have a titlebar, tabs or any other features. All it does is size and position
31236 * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
31238 Roo.BasicLayoutRegion = function(mgr, config, pos, skipConfig){
31240 this.position = pos;
31243 * @scope Roo.BasicLayoutRegion
31247 * @event beforeremove
31248 * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
31249 * @param {Roo.LayoutRegion} this
31250 * @param {Roo.ContentPanel} panel The panel
31251 * @param {Object} e The cancel event object
31253 "beforeremove" : true,
31255 * @event invalidated
31256 * Fires when the layout for this region is changed.
31257 * @param {Roo.LayoutRegion} this
31259 "invalidated" : true,
31261 * @event visibilitychange
31262 * Fires when this region is shown or hidden
31263 * @param {Roo.LayoutRegion} this
31264 * @param {Boolean} visibility true or false
31266 "visibilitychange" : true,
31268 * @event paneladded
31269 * Fires when a panel is added.
31270 * @param {Roo.LayoutRegion} this
31271 * @param {Roo.ContentPanel} panel The panel
31273 "paneladded" : true,
31275 * @event panelremoved
31276 * Fires when a panel is removed.
31277 * @param {Roo.LayoutRegion} this
31278 * @param {Roo.ContentPanel} panel The panel
31280 "panelremoved" : true,
31283 * Fires when this region is collapsed.
31284 * @param {Roo.LayoutRegion} this
31286 "collapsed" : true,
31289 * Fires when this region is expanded.
31290 * @param {Roo.LayoutRegion} this
31295 * Fires when this region is slid into view.
31296 * @param {Roo.LayoutRegion} this
31298 "slideshow" : true,
31301 * Fires when this region slides out of view.
31302 * @param {Roo.LayoutRegion} this
31304 "slidehide" : true,
31306 * @event panelactivated
31307 * Fires when a panel is activated.
31308 * @param {Roo.LayoutRegion} this
31309 * @param {Roo.ContentPanel} panel The activated panel
31311 "panelactivated" : true,
31314 * Fires when the user resizes this region.
31315 * @param {Roo.LayoutRegion} this
31316 * @param {Number} newSize The new size (width for east/west, height for north/south)
31320 /** A collection of panels in this region. @type Roo.util.MixedCollection */
31321 this.panels = new Roo.util.MixedCollection();
31322 this.panels.getKey = this.getPanelId.createDelegate(this);
31324 this.activePanel = null;
31325 // ensure listeners are added...
31327 if (config.listeners || config.events) {
31328 Roo.BasicLayoutRegion.superclass.constructor.call(this, {
31329 listeners : config.listeners || {},
31330 events : config.events || {}
31334 if(skipConfig !== true){
31335 this.applyConfig(config);
31339 Roo.extend(Roo.BasicLayoutRegion, Roo.util.Observable, {
31340 getPanelId : function(p){
31344 applyConfig : function(config){
31345 this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
31346 this.config = config;
31351 * Resizes the region to the specified size. For vertical regions (west, east) this adjusts
31352 * the width, for horizontal (north, south) the height.
31353 * @param {Number} newSize The new width or height
31355 resizeTo : function(newSize){
31356 var el = this.el ? this.el :
31357 (this.activePanel ? this.activePanel.getEl() : null);
31359 switch(this.position){
31362 el.setWidth(newSize);
31363 this.fireEvent("resized", this, newSize);
31367 el.setHeight(newSize);
31368 this.fireEvent("resized", this, newSize);
31374 getBox : function(){
31375 return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
31378 getMargins : function(){
31379 return this.margins;
31382 updateBox : function(box){
31384 var el = this.activePanel.getEl();
31385 el.dom.style.left = box.x + "px";
31386 el.dom.style.top = box.y + "px";
31387 this.activePanel.setSize(box.width, box.height);
31391 * Returns the container element for this region.
31392 * @return {Roo.Element}
31394 getEl : function(){
31395 return this.activePanel;
31399 * Returns true if this region is currently visible.
31400 * @return {Boolean}
31402 isVisible : function(){
31403 return this.activePanel ? true : false;
31406 setActivePanel : function(panel){
31407 panel = this.getPanel(panel);
31408 if(this.activePanel && this.activePanel != panel){
31409 this.activePanel.setActiveState(false);
31410 this.activePanel.getEl().setLeftTop(-10000,-10000);
31412 this.activePanel = panel;
31413 panel.setActiveState(true);
31415 panel.setSize(this.box.width, this.box.height);
31417 this.fireEvent("panelactivated", this, panel);
31418 this.fireEvent("invalidated");
31422 * Show the specified panel.
31423 * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
31424 * @return {Roo.ContentPanel} The shown panel or null
31426 showPanel : function(panel){
31427 if(panel = this.getPanel(panel)){
31428 this.setActivePanel(panel);
31434 * Get the active panel for this region.
31435 * @return {Roo.ContentPanel} The active panel or null
31437 getActivePanel : function(){
31438 return this.activePanel;
31442 * Add the passed ContentPanel(s)
31443 * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
31444 * @return {Roo.ContentPanel} The panel added (if only one was added)
31446 add : function(panel){
31447 if(arguments.length > 1){
31448 for(var i = 0, len = arguments.length; i < len; i++) {
31449 this.add(arguments[i]);
31453 if(this.hasPanel(panel)){
31454 this.showPanel(panel);
31457 var el = panel.getEl();
31458 if(el.dom.parentNode != this.mgr.el.dom){
31459 this.mgr.el.dom.appendChild(el.dom);
31461 if(panel.setRegion){
31462 panel.setRegion(this);
31464 this.panels.add(panel);
31465 el.setStyle("position", "absolute");
31466 if(!panel.background){
31467 this.setActivePanel(panel);
31468 if(this.config.initialSize && this.panels.getCount()==1){
31469 this.resizeTo(this.config.initialSize);
31472 this.fireEvent("paneladded", this, panel);
31477 * Returns true if the panel is in this region.
31478 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
31479 * @return {Boolean}
31481 hasPanel : function(panel){
31482 if(typeof panel == "object"){ // must be panel obj
31483 panel = panel.getId();
31485 return this.getPanel(panel) ? true : false;
31489 * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
31490 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
31491 * @param {Boolean} preservePanel Overrides the config preservePanel option
31492 * @return {Roo.ContentPanel} The panel that was removed
31494 remove : function(panel, preservePanel){
31495 panel = this.getPanel(panel);
31500 this.fireEvent("beforeremove", this, panel, e);
31501 if(e.cancel === true){
31504 var panelId = panel.getId();
31505 this.panels.removeKey(panelId);
31510 * Returns the panel specified or null if it's not in this region.
31511 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
31512 * @return {Roo.ContentPanel}
31514 getPanel : function(id){
31515 if(typeof id == "object"){ // must be panel obj
31518 return this.panels.get(id);
31522 * Returns this regions position (north/south/east/west/center).
31525 getPosition: function(){
31526 return this.position;
31530 * Ext JS Library 1.1.1
31531 * Copyright(c) 2006-2007, Ext JS, LLC.
31533 * Originally Released Under LGPL - original licence link has changed is not relivant.
31536 * <script type="text/javascript">
31540 * @class Roo.LayoutRegion
31541 * @extends Roo.BasicLayoutRegion
31542 * This class represents a region in a layout manager.
31543 * @cfg {Boolean} collapsible False to disable collapsing (defaults to true)
31544 * @cfg {Boolean} collapsed True to set the initial display to collapsed (defaults to false)
31545 * @cfg {Boolean} floatable False to disable floating (defaults to true)
31546 * @cfg {Object} margins Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
31547 * @cfg {Object} cmargins Margins for the element when collapsed (defaults to: north/south {top: 2, left: 0, right:0, bottom: 2} or east/west {top: 0, left: 2, right:2, bottom: 0})
31548 * @cfg {String} tabPosition "top" or "bottom" (defaults to "bottom")
31549 * @cfg {String} collapsedTitle Optional string message to display in the collapsed block of a north or south region
31550 * @cfg {Boolean} alwaysShowTabs True to always display tabs even when there is only 1 panel (defaults to false)
31551 * @cfg {Boolean} autoScroll True to enable overflow scrolling (defaults to false)
31552 * @cfg {Boolean} titlebar True to display a title bar (defaults to true)
31553 * @cfg {String} title The title for the region (overrides panel titles)
31554 * @cfg {Boolean} animate True to animate expand/collapse (defaults to false)
31555 * @cfg {Boolean} autoHide False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
31556 * @cfg {Boolean} preservePanels True to preserve removed panels so they can be readded later (defaults to false)
31557 * @cfg {Boolean} closeOnTab True to place the close icon on the tabs instead of the region titlebar (defaults to false)
31558 * @cfg {Boolean} hideTabs True to hide the tab strip (defaults to false)
31559 * @cfg {Boolean} resizeTabs True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
31560 * the space available, similar to FireFox 1.5 tabs (defaults to false)
31561 * @cfg {Number} minTabWidth The minimum tab width (defaults to 40)
31562 * @cfg {Number} preferredTabWidth The preferred tab width (defaults to 150)
31563 * @cfg {Boolean} showPin True to show a pin button
31564 * @cfg {Boolean} hidden True to start the region hidden (defaults to false)
31565 * @cfg {Boolean} hideWhenEmpty True to hide the region when it has no panels
31566 * @cfg {Boolean} disableTabTips True to disable tab tooltips
31567 * @cfg {Number} width For East/West panels
31568 * @cfg {Number} height For North/South panels
31569 * @cfg {Boolean} split To show the splitter
31570 * @cfg {Boolean} toolbar xtype configuration for a toolbar - shows on right of tabbar
31572 Roo.LayoutRegion = function(mgr, config, pos){
31573 Roo.LayoutRegion.superclass.constructor.call(this, mgr, config, pos, true);
31574 var dh = Roo.DomHelper;
31575 /** This region's container element
31576 * @type Roo.Element */
31577 this.el = dh.append(mgr.el.dom, {tag: "div", cls: "x-layout-panel x-layout-panel-" + this.position}, true);
31578 /** This region's title element
31579 * @type Roo.Element */
31581 this.titleEl = dh.append(this.el.dom, {tag: "div", unselectable: "on", cls: "x-unselectable x-layout-panel-hd x-layout-title-"+this.position, children:[
31582 {tag: "span", cls: "x-unselectable x-layout-panel-hd-text", unselectable: "on", html: " "},
31583 {tag: "div", cls: "x-unselectable x-layout-panel-hd-tools", unselectable: "on"}
31585 this.titleEl.enableDisplayMode();
31586 /** This region's title text element
31587 * @type HTMLElement */
31588 this.titleTextEl = this.titleEl.dom.firstChild;
31589 this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
31590 this.closeBtn = this.createTool(this.tools.dom, "x-layout-close");
31591 this.closeBtn.enableDisplayMode();
31592 this.closeBtn.on("click", this.closeClicked, this);
31593 this.closeBtn.hide();
31595 this.createBody(config);
31596 this.visible = true;
31597 this.collapsed = false;
31599 if(config.hideWhenEmpty){
31601 this.on("paneladded", this.validateVisibility, this);
31602 this.on("panelremoved", this.validateVisibility, this);
31604 this.applyConfig(config);
31607 Roo.extend(Roo.LayoutRegion, Roo.BasicLayoutRegion, {
31609 createBody : function(){
31610 /** This region's body element
31611 * @type Roo.Element */
31612 this.bodyEl = this.el.createChild({tag: "div", cls: "x-layout-panel-body"});
31615 applyConfig : function(c){
31616 if(c.collapsible && this.position != "center" && !this.collapsedEl){
31617 var dh = Roo.DomHelper;
31618 if(c.titlebar !== false){
31619 this.collapseBtn = this.createTool(this.tools.dom, "x-layout-collapse-"+this.position);
31620 this.collapseBtn.on("click", this.collapse, this);
31621 this.collapseBtn.enableDisplayMode();
31623 if(c.showPin === true || this.showPin){
31624 this.stickBtn = this.createTool(this.tools.dom, "x-layout-stick");
31625 this.stickBtn.enableDisplayMode();
31626 this.stickBtn.on("click", this.expand, this);
31627 this.stickBtn.hide();
31630 /** This region's collapsed element
31631 * @type Roo.Element */
31632 this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
31633 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
31635 if(c.floatable !== false){
31636 this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
31637 this.collapsedEl.on("click", this.collapseClick, this);
31640 if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
31641 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
31642 id: "message", unselectable: "on", style:{"float":"left"}});
31643 this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
31645 this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
31646 this.expandBtn.on("click", this.expand, this);
31648 if(this.collapseBtn){
31649 this.collapseBtn.setVisible(c.collapsible == true);
31651 this.cmargins = c.cmargins || this.cmargins ||
31652 (this.position == "west" || this.position == "east" ?
31653 {top: 0, left: 2, right:2, bottom: 0} :
31654 {top: 2, left: 0, right:0, bottom: 2});
31655 this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
31656 this.bottomTabs = c.tabPosition != "top";
31657 this.autoScroll = c.autoScroll || false;
31658 if(this.autoScroll){
31659 this.bodyEl.setStyle("overflow", "auto");
31661 this.bodyEl.setStyle("overflow", "hidden");
31663 //if(c.titlebar !== false){
31664 if((!c.titlebar && !c.title) || c.titlebar === false){
31665 this.titleEl.hide();
31667 this.titleEl.show();
31669 this.titleTextEl.innerHTML = c.title;
31673 this.duration = c.duration || .30;
31674 this.slideDuration = c.slideDuration || .45;
31677 this.collapse(true);
31684 * Returns true if this region is currently visible.
31685 * @return {Boolean}
31687 isVisible : function(){
31688 return this.visible;
31692 * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
31693 * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&#160;")
31695 setCollapsedTitle : function(title){
31696 title = title || " ";
31697 if(this.collapsedTitleTextEl){
31698 this.collapsedTitleTextEl.innerHTML = title;
31702 getBox : function(){
31704 if(!this.collapsed){
31705 b = this.el.getBox(false, true);
31707 b = this.collapsedEl.getBox(false, true);
31712 getMargins : function(){
31713 return this.collapsed ? this.cmargins : this.margins;
31716 highlight : function(){
31717 this.el.addClass("x-layout-panel-dragover");
31720 unhighlight : function(){
31721 this.el.removeClass("x-layout-panel-dragover");
31724 updateBox : function(box){
31726 if(!this.collapsed){
31727 this.el.dom.style.left = box.x + "px";
31728 this.el.dom.style.top = box.y + "px";
31729 this.updateBody(box.width, box.height);
31731 this.collapsedEl.dom.style.left = box.x + "px";
31732 this.collapsedEl.dom.style.top = box.y + "px";
31733 this.collapsedEl.setSize(box.width, box.height);
31736 this.tabs.autoSizeTabs();
31740 updateBody : function(w, h){
31742 this.el.setWidth(w);
31743 w -= this.el.getBorderWidth("rl");
31744 if(this.config.adjustments){
31745 w += this.config.adjustments[0];
31749 this.el.setHeight(h);
31750 h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
31751 h -= this.el.getBorderWidth("tb");
31752 if(this.config.adjustments){
31753 h += this.config.adjustments[1];
31755 this.bodyEl.setHeight(h);
31757 h = this.tabs.syncHeight(h);
31760 if(this.panelSize){
31761 w = w !== null ? w : this.panelSize.width;
31762 h = h !== null ? h : this.panelSize.height;
31764 if(this.activePanel){
31765 var el = this.activePanel.getEl();
31766 w = w !== null ? w : el.getWidth();
31767 h = h !== null ? h : el.getHeight();
31768 this.panelSize = {width: w, height: h};
31769 this.activePanel.setSize(w, h);
31771 if(Roo.isIE && this.tabs){
31772 this.tabs.el.repaint();
31777 * Returns the container element for this region.
31778 * @return {Roo.Element}
31780 getEl : function(){
31785 * Hides this region.
31788 if(!this.collapsed){
31789 this.el.dom.style.left = "-2000px";
31792 this.collapsedEl.dom.style.left = "-2000px";
31793 this.collapsedEl.hide();
31795 this.visible = false;
31796 this.fireEvent("visibilitychange", this, false);
31800 * Shows this region if it was previously hidden.
31803 if(!this.collapsed){
31806 this.collapsedEl.show();
31808 this.visible = true;
31809 this.fireEvent("visibilitychange", this, true);
31812 closeClicked : function(){
31813 if(this.activePanel){
31814 this.remove(this.activePanel);
31818 collapseClick : function(e){
31820 e.stopPropagation();
31823 e.stopPropagation();
31829 * Collapses this region.
31830 * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
31832 collapse : function(skipAnim){
31833 if(this.collapsed) return;
31834 this.collapsed = true;
31836 this.split.el.hide();
31838 if(this.config.animate && skipAnim !== true){
31839 this.fireEvent("invalidated", this);
31840 this.animateCollapse();
31842 this.el.setLocation(-20000,-20000);
31844 this.collapsedEl.show();
31845 this.fireEvent("collapsed", this);
31846 this.fireEvent("invalidated", this);
31850 animateCollapse : function(){
31855 * Expands this region if it was previously collapsed.
31856 * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
31857 * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
31859 expand : function(e, skipAnim){
31860 if(e) e.stopPropagation();
31861 if(!this.collapsed || this.el.hasActiveFx()) return;
31863 this.afterSlideIn();
31866 this.collapsed = false;
31867 if(this.config.animate && skipAnim !== true){
31868 this.animateExpand();
31872 this.split.el.show();
31874 this.collapsedEl.setLocation(-2000,-2000);
31875 this.collapsedEl.hide();
31876 this.fireEvent("invalidated", this);
31877 this.fireEvent("expanded", this);
31881 animateExpand : function(){
31885 initTabs : function()
31887 this.bodyEl.setStyle("overflow", "hidden");
31888 var ts = new Roo.TabPanel(
31891 tabPosition: this.bottomTabs ? 'bottom' : 'top',
31892 disableTooltips: this.config.disableTabTips,
31893 toolbar : this.config.toolbar
31896 if(this.config.hideTabs){
31897 ts.stripWrap.setDisplayed(false);
31900 ts.resizeTabs = this.config.resizeTabs === true;
31901 ts.minTabWidth = this.config.minTabWidth || 40;
31902 ts.maxTabWidth = this.config.maxTabWidth || 250;
31903 ts.preferredTabWidth = this.config.preferredTabWidth || 150;
31904 ts.monitorResize = false;
31905 ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
31906 ts.bodyEl.addClass('x-layout-tabs-body');
31907 this.panels.each(this.initPanelAsTab, this);
31910 initPanelAsTab : function(panel){
31911 var ti = this.tabs.addTab(panel.getEl().id, panel.getTitle(), null,
31912 this.config.closeOnTab && panel.isClosable());
31913 if(panel.tabTip !== undefined){
31914 ti.setTooltip(panel.tabTip);
31916 ti.on("activate", function(){
31917 this.setActivePanel(panel);
31919 if(this.config.closeOnTab){
31920 ti.on("beforeclose", function(t, e){
31922 this.remove(panel);
31928 updatePanelTitle : function(panel, title){
31929 if(this.activePanel == panel){
31930 this.updateTitle(title);
31933 var ti = this.tabs.getTab(panel.getEl().id);
31935 if(panel.tabTip !== undefined){
31936 ti.setTooltip(panel.tabTip);
31941 updateTitle : function(title){
31942 if(this.titleTextEl && !this.config.title){
31943 this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : " ");
31947 setActivePanel : function(panel){
31948 panel = this.getPanel(panel);
31949 if(this.activePanel && this.activePanel != panel){
31950 this.activePanel.setActiveState(false);
31952 this.activePanel = panel;
31953 panel.setActiveState(true);
31954 if(this.panelSize){
31955 panel.setSize(this.panelSize.width, this.panelSize.height);
31958 this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
31960 this.updateTitle(panel.getTitle());
31962 this.fireEvent("invalidated", this);
31964 this.fireEvent("panelactivated", this, panel);
31968 * Shows the specified panel.
31969 * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
31970 * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
31972 showPanel : function(panel){
31973 if(panel = this.getPanel(panel)){
31975 var tab = this.tabs.getTab(panel.getEl().id);
31976 if(tab.isHidden()){
31977 this.tabs.unhideTab(tab.id);
31981 this.setActivePanel(panel);
31988 * Get the active panel for this region.
31989 * @return {Roo.ContentPanel} The active panel or null
31991 getActivePanel : function(){
31992 return this.activePanel;
31995 validateVisibility : function(){
31996 if(this.panels.getCount() < 1){
31997 this.updateTitle(" ");
31998 this.closeBtn.hide();
32001 if(!this.isVisible()){
32008 * Adds the passed ContentPanel(s) to this region.
32009 * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
32010 * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
32012 add : function(panel){
32013 if(arguments.length > 1){
32014 for(var i = 0, len = arguments.length; i < len; i++) {
32015 this.add(arguments[i]);
32019 if(this.hasPanel(panel)){
32020 this.showPanel(panel);
32023 panel.setRegion(this);
32024 this.panels.add(panel);
32025 if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
32026 this.bodyEl.dom.appendChild(panel.getEl().dom);
32027 if(panel.background !== true){
32028 this.setActivePanel(panel);
32030 this.fireEvent("paneladded", this, panel);
32036 this.initPanelAsTab(panel);
32038 if(panel.background !== true){
32039 this.tabs.activate(panel.getEl().id);
32041 this.fireEvent("paneladded", this, panel);
32046 * Hides the tab for the specified panel.
32047 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
32049 hidePanel : function(panel){
32050 if(this.tabs && (panel = this.getPanel(panel))){
32051 this.tabs.hideTab(panel.getEl().id);
32056 * Unhides the tab for a previously hidden panel.
32057 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
32059 unhidePanel : function(panel){
32060 if(this.tabs && (panel = this.getPanel(panel))){
32061 this.tabs.unhideTab(panel.getEl().id);
32065 clearPanels : function(){
32066 while(this.panels.getCount() > 0){
32067 this.remove(this.panels.first());
32072 * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
32073 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
32074 * @param {Boolean} preservePanel Overrides the config preservePanel option
32075 * @return {Roo.ContentPanel} The panel that was removed
32077 remove : function(panel, preservePanel){
32078 panel = this.getPanel(panel);
32083 this.fireEvent("beforeremove", this, panel, e);
32084 if(e.cancel === true){
32087 preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
32088 var panelId = panel.getId();
32089 this.panels.removeKey(panelId);
32091 document.body.appendChild(panel.getEl().dom);
32094 this.tabs.removeTab(panel.getEl().id);
32095 }else if (!preservePanel){
32096 this.bodyEl.dom.removeChild(panel.getEl().dom);
32098 if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
32099 var p = this.panels.first();
32100 var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
32101 tempEl.appendChild(p.getEl().dom);
32102 this.bodyEl.update("");
32103 this.bodyEl.dom.appendChild(p.getEl().dom);
32105 this.updateTitle(p.getTitle());
32107 this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
32108 this.setActivePanel(p);
32110 panel.setRegion(null);
32111 if(this.activePanel == panel){
32112 this.activePanel = null;
32114 if(this.config.autoDestroy !== false && preservePanel !== true){
32115 try{panel.destroy();}catch(e){}
32117 this.fireEvent("panelremoved", this, panel);
32122 * Returns the TabPanel component used by this region
32123 * @return {Roo.TabPanel}
32125 getTabs : function(){
32129 createTool : function(parentEl, className){
32130 var btn = Roo.DomHelper.append(parentEl, {tag: "div", cls: "x-layout-tools-button",
32131 children: [{tag: "div", cls: "x-layout-tools-button-inner " + className, html: " "}]}, true);
32132 btn.addClassOnOver("x-layout-tools-button-over");
32137 * Ext JS Library 1.1.1
32138 * Copyright(c) 2006-2007, Ext JS, LLC.
32140 * Originally Released Under LGPL - original licence link has changed is not relivant.
32143 * <script type="text/javascript">
32149 * @class Roo.SplitLayoutRegion
32150 * @extends Roo.LayoutRegion
32151 * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
32153 Roo.SplitLayoutRegion = function(mgr, config, pos, cursor){
32154 this.cursor = cursor;
32155 Roo.SplitLayoutRegion.superclass.constructor.call(this, mgr, config, pos);
32158 Roo.extend(Roo.SplitLayoutRegion, Roo.LayoutRegion, {
32159 splitTip : "Drag to resize.",
32160 collapsibleSplitTip : "Drag to resize. Double click to hide.",
32161 useSplitTips : false,
32163 applyConfig : function(config){
32164 Roo.SplitLayoutRegion.superclass.applyConfig.call(this, config);
32167 var splitEl = Roo.DomHelper.append(this.mgr.el.dom,
32168 {tag: "div", id: this.el.id + "-split", cls: "x-layout-split x-layout-split-"+this.position, html: " "});
32169 /** The SplitBar for this region
32170 * @type Roo.SplitBar */
32171 this.split = new Roo.SplitBar(splitEl, this.el, this.orientation);
32172 this.split.on("moved", this.onSplitMove, this);
32173 this.split.useShim = config.useShim === true;
32174 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
32175 if(this.useSplitTips){
32176 this.split.el.dom.title = config.collapsible ? this.collapsibleSplitTip : this.splitTip;
32178 if(config.collapsible){
32179 this.split.el.on("dblclick", this.collapse, this);
32182 if(typeof config.minSize != "undefined"){
32183 this.split.minSize = config.minSize;
32185 if(typeof config.maxSize != "undefined"){
32186 this.split.maxSize = config.maxSize;
32188 if(config.hideWhenEmpty || config.hidden || config.collapsed){
32189 this.hideSplitter();
32194 getHMaxSize : function(){
32195 var cmax = this.config.maxSize || 10000;
32196 var center = this.mgr.getRegion("center");
32197 return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
32200 getVMaxSize : function(){
32201 var cmax = this.config.maxSize || 10000;
32202 var center = this.mgr.getRegion("center");
32203 return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
32206 onSplitMove : function(split, newSize){
32207 this.fireEvent("resized", this, newSize);
32211 * Returns the {@link Roo.SplitBar} for this region.
32212 * @return {Roo.SplitBar}
32214 getSplitBar : function(){
32219 this.hideSplitter();
32220 Roo.SplitLayoutRegion.superclass.hide.call(this);
32223 hideSplitter : function(){
32225 this.split.el.setLocation(-2000,-2000);
32226 this.split.el.hide();
32232 this.split.el.show();
32234 Roo.SplitLayoutRegion.superclass.show.call(this);
32237 beforeSlide: function(){
32238 if(Roo.isGecko){// firefox overflow auto bug workaround
32239 this.bodyEl.clip();
32240 if(this.tabs) this.tabs.bodyEl.clip();
32241 if(this.activePanel){
32242 this.activePanel.getEl().clip();
32244 if(this.activePanel.beforeSlide){
32245 this.activePanel.beforeSlide();
32251 afterSlide : function(){
32252 if(Roo.isGecko){// firefox overflow auto bug workaround
32253 this.bodyEl.unclip();
32254 if(this.tabs) this.tabs.bodyEl.unclip();
32255 if(this.activePanel){
32256 this.activePanel.getEl().unclip();
32257 if(this.activePanel.afterSlide){
32258 this.activePanel.afterSlide();
32264 initAutoHide : function(){
32265 if(this.autoHide !== false){
32266 if(!this.autoHideHd){
32267 var st = new Roo.util.DelayedTask(this.slideIn, this);
32268 this.autoHideHd = {
32269 "mouseout": function(e){
32270 if(!e.within(this.el, true)){
32274 "mouseover" : function(e){
32280 this.el.on(this.autoHideHd);
32284 clearAutoHide : function(){
32285 if(this.autoHide !== false){
32286 this.el.un("mouseout", this.autoHideHd.mouseout);
32287 this.el.un("mouseover", this.autoHideHd.mouseover);
32291 clearMonitor : function(){
32292 Roo.get(document).un("click", this.slideInIf, this);
32295 // these names are backwards but not changed for compat
32296 slideOut : function(){
32297 if(this.isSlid || this.el.hasActiveFx()){
32300 this.isSlid = true;
32301 if(this.collapseBtn){
32302 this.collapseBtn.hide();
32304 this.closeBtnState = this.closeBtn.getStyle('display');
32305 this.closeBtn.hide();
32307 this.stickBtn.show();
32310 this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
32311 this.beforeSlide();
32312 this.el.setStyle("z-index", 10001);
32313 this.el.slideIn(this.getSlideAnchor(), {
32314 callback: function(){
32316 this.initAutoHide();
32317 Roo.get(document).on("click", this.slideInIf, this);
32318 this.fireEvent("slideshow", this);
32325 afterSlideIn : function(){
32326 this.clearAutoHide();
32327 this.isSlid = false;
32328 this.clearMonitor();
32329 this.el.setStyle("z-index", "");
32330 if(this.collapseBtn){
32331 this.collapseBtn.show();
32333 this.closeBtn.setStyle('display', this.closeBtnState);
32335 this.stickBtn.hide();
32337 this.fireEvent("slidehide", this);
32340 slideIn : function(cb){
32341 if(!this.isSlid || this.el.hasActiveFx()){
32345 this.isSlid = false;
32346 this.beforeSlide();
32347 this.el.slideOut(this.getSlideAnchor(), {
32348 callback: function(){
32349 this.el.setLeftTop(-10000, -10000);
32351 this.afterSlideIn();
32359 slideInIf : function(e){
32360 if(!e.within(this.el)){
32365 animateCollapse : function(){
32366 this.beforeSlide();
32367 this.el.setStyle("z-index", 20000);
32368 var anchor = this.getSlideAnchor();
32369 this.el.slideOut(anchor, {
32370 callback : function(){
32371 this.el.setStyle("z-index", "");
32372 this.collapsedEl.slideIn(anchor, {duration:.3});
32374 this.el.setLocation(-10000,-10000);
32376 this.fireEvent("collapsed", this);
32383 animateExpand : function(){
32384 this.beforeSlide();
32385 this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
32386 this.el.setStyle("z-index", 20000);
32387 this.collapsedEl.hide({
32390 this.el.slideIn(this.getSlideAnchor(), {
32391 callback : function(){
32392 this.el.setStyle("z-index", "");
32395 this.split.el.show();
32397 this.fireEvent("invalidated", this);
32398 this.fireEvent("expanded", this);
32426 getAnchor : function(){
32427 return this.anchors[this.position];
32430 getCollapseAnchor : function(){
32431 return this.canchors[this.position];
32434 getSlideAnchor : function(){
32435 return this.sanchors[this.position];
32438 getAlignAdj : function(){
32439 var cm = this.cmargins;
32440 switch(this.position){
32456 getExpandAdj : function(){
32457 var c = this.collapsedEl, cm = this.cmargins;
32458 switch(this.position){
32460 return [-(cm.right+c.getWidth()+cm.left), 0];
32463 return [cm.right+c.getWidth()+cm.left, 0];
32466 return [0, -(cm.top+cm.bottom+c.getHeight())];
32469 return [0, cm.top+cm.bottom+c.getHeight()];
32475 * Ext JS Library 1.1.1
32476 * Copyright(c) 2006-2007, Ext JS, LLC.
32478 * Originally Released Under LGPL - original licence link has changed is not relivant.
32481 * <script type="text/javascript">
32484 * These classes are private internal classes
32486 Roo.CenterLayoutRegion = function(mgr, config){
32487 Roo.LayoutRegion.call(this, mgr, config, "center");
32488 this.visible = true;
32489 this.minWidth = config.minWidth || 20;
32490 this.minHeight = config.minHeight || 20;
32493 Roo.extend(Roo.CenterLayoutRegion, Roo.LayoutRegion, {
32495 // center panel can't be hidden
32499 // center panel can't be hidden
32502 getMinWidth: function(){
32503 return this.minWidth;
32506 getMinHeight: function(){
32507 return this.minHeight;
32512 Roo.NorthLayoutRegion = function(mgr, config){
32513 Roo.LayoutRegion.call(this, mgr, config, "north", "n-resize");
32515 this.split.placement = Roo.SplitBar.TOP;
32516 this.split.orientation = Roo.SplitBar.VERTICAL;
32517 this.split.el.addClass("x-layout-split-v");
32519 var size = config.initialSize || config.height;
32520 if(typeof size != "undefined"){
32521 this.el.setHeight(size);
32524 Roo.extend(Roo.NorthLayoutRegion, Roo.SplitLayoutRegion, {
32525 orientation: Roo.SplitBar.VERTICAL,
32526 getBox : function(){
32527 if(this.collapsed){
32528 return this.collapsedEl.getBox();
32530 var box = this.el.getBox();
32532 box.height += this.split.el.getHeight();
32537 updateBox : function(box){
32538 if(this.split && !this.collapsed){
32539 box.height -= this.split.el.getHeight();
32540 this.split.el.setLeft(box.x);
32541 this.split.el.setTop(box.y+box.height);
32542 this.split.el.setWidth(box.width);
32544 if(this.collapsed){
32545 this.updateBody(box.width, null);
32547 Roo.LayoutRegion.prototype.updateBox.call(this, box);
32551 Roo.SouthLayoutRegion = function(mgr, config){
32552 Roo.SplitLayoutRegion.call(this, mgr, config, "south", "s-resize");
32554 this.split.placement = Roo.SplitBar.BOTTOM;
32555 this.split.orientation = Roo.SplitBar.VERTICAL;
32556 this.split.el.addClass("x-layout-split-v");
32558 var size = config.initialSize || config.height;
32559 if(typeof size != "undefined"){
32560 this.el.setHeight(size);
32563 Roo.extend(Roo.SouthLayoutRegion, Roo.SplitLayoutRegion, {
32564 orientation: Roo.SplitBar.VERTICAL,
32565 getBox : function(){
32566 if(this.collapsed){
32567 return this.collapsedEl.getBox();
32569 var box = this.el.getBox();
32571 var sh = this.split.el.getHeight();
32578 updateBox : function(box){
32579 if(this.split && !this.collapsed){
32580 var sh = this.split.el.getHeight();
32583 this.split.el.setLeft(box.x);
32584 this.split.el.setTop(box.y-sh);
32585 this.split.el.setWidth(box.width);
32587 if(this.collapsed){
32588 this.updateBody(box.width, null);
32590 Roo.LayoutRegion.prototype.updateBox.call(this, box);
32594 Roo.EastLayoutRegion = function(mgr, config){
32595 Roo.SplitLayoutRegion.call(this, mgr, config, "east", "e-resize");
32597 this.split.placement = Roo.SplitBar.RIGHT;
32598 this.split.orientation = Roo.SplitBar.HORIZONTAL;
32599 this.split.el.addClass("x-layout-split-h");
32601 var size = config.initialSize || config.width;
32602 if(typeof size != "undefined"){
32603 this.el.setWidth(size);
32606 Roo.extend(Roo.EastLayoutRegion, Roo.SplitLayoutRegion, {
32607 orientation: Roo.SplitBar.HORIZONTAL,
32608 getBox : function(){
32609 if(this.collapsed){
32610 return this.collapsedEl.getBox();
32612 var box = this.el.getBox();
32614 var sw = this.split.el.getWidth();
32621 updateBox : function(box){
32622 if(this.split && !this.collapsed){
32623 var sw = this.split.el.getWidth();
32625 this.split.el.setLeft(box.x);
32626 this.split.el.setTop(box.y);
32627 this.split.el.setHeight(box.height);
32630 if(this.collapsed){
32631 this.updateBody(null, box.height);
32633 Roo.LayoutRegion.prototype.updateBox.call(this, box);
32637 Roo.WestLayoutRegion = function(mgr, config){
32638 Roo.SplitLayoutRegion.call(this, mgr, config, "west", "w-resize");
32640 this.split.placement = Roo.SplitBar.LEFT;
32641 this.split.orientation = Roo.SplitBar.HORIZONTAL;
32642 this.split.el.addClass("x-layout-split-h");
32644 var size = config.initialSize || config.width;
32645 if(typeof size != "undefined"){
32646 this.el.setWidth(size);
32649 Roo.extend(Roo.WestLayoutRegion, Roo.SplitLayoutRegion, {
32650 orientation: Roo.SplitBar.HORIZONTAL,
32651 getBox : function(){
32652 if(this.collapsed){
32653 return this.collapsedEl.getBox();
32655 var box = this.el.getBox();
32657 box.width += this.split.el.getWidth();
32662 updateBox : function(box){
32663 if(this.split && !this.collapsed){
32664 var sw = this.split.el.getWidth();
32666 this.split.el.setLeft(box.x+box.width);
32667 this.split.el.setTop(box.y);
32668 this.split.el.setHeight(box.height);
32670 if(this.collapsed){
32671 this.updateBody(null, box.height);
32673 Roo.LayoutRegion.prototype.updateBox.call(this, box);
32678 * Ext JS Library 1.1.1
32679 * Copyright(c) 2006-2007, Ext JS, LLC.
32681 * Originally Released Under LGPL - original licence link has changed is not relivant.
32684 * <script type="text/javascript">
32689 * Private internal class for reading and applying state
32691 Roo.LayoutStateManager = function(layout){
32692 // default empty state
32701 Roo.LayoutStateManager.prototype = {
32702 init : function(layout, provider){
32703 this.provider = provider;
32704 var state = provider.get(layout.id+"-layout-state");
32706 var wasUpdating = layout.isUpdating();
32708 layout.beginUpdate();
32710 for(var key in state){
32711 if(typeof state[key] != "function"){
32712 var rstate = state[key];
32713 var r = layout.getRegion(key);
32716 r.resizeTo(rstate.size);
32718 if(rstate.collapsed == true){
32721 r.expand(null, true);
32727 layout.endUpdate();
32729 this.state = state;
32731 this.layout = layout;
32732 layout.on("regionresized", this.onRegionResized, this);
32733 layout.on("regioncollapsed", this.onRegionCollapsed, this);
32734 layout.on("regionexpanded", this.onRegionExpanded, this);
32737 storeState : function(){
32738 this.provider.set(this.layout.id+"-layout-state", this.state);
32741 onRegionResized : function(region, newSize){
32742 this.state[region.getPosition()].size = newSize;
32746 onRegionCollapsed : function(region){
32747 this.state[region.getPosition()].collapsed = true;
32751 onRegionExpanded : function(region){
32752 this.state[region.getPosition()].collapsed = false;
32757 * Ext JS Library 1.1.1
32758 * Copyright(c) 2006-2007, Ext JS, LLC.
32760 * Originally Released Under LGPL - original licence link has changed is not relivant.
32763 * <script type="text/javascript">
32766 * @class Roo.ContentPanel
32767 * @extends Roo.util.Observable
32768 * A basic ContentPanel element.
32769 * @cfg {Boolean} fitToFrame True for this panel to adjust its size to fit when the region resizes (defaults to false)
32770 * @cfg {Boolean} fitContainer When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container (defaults to false)
32771 * @cfg {Boolean/Object} autoCreate True to auto generate the DOM element for this panel, or a {@link Roo.DomHelper} config of the element to create
32772 * @cfg {Boolean} closable True if the panel can be closed/removed
32773 * @cfg {Boolean} background True if the panel should not be activated when it is added (defaults to false)
32774 * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
32775 * @cfg {Toolbar} toolbar A toolbar for this panel
32776 * @cfg {Boolean} autoScroll True to scroll overflow in this panel (use with {@link #fitToFrame})
32777 * @cfg {String} title The title for this panel
32778 * @cfg {Array} adjustments Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
32779 * @cfg {String} url Calls {@link #setUrl} with this value
32780 * @cfg {String} region (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
32781 * @cfg {String/Object} params When used with {@link #url}, calls {@link #setUrl} with this value
32782 * @cfg {Boolean} loadOnce When used with {@link #url}, calls {@link #setUrl} with this value
32783 * @cfg {String} content Raw content to fill content panel with (uses setContent on construction.)
32786 * Create a new ContentPanel.
32787 * @param {String/HTMLElement/Roo.Element} el The container element for this panel
32788 * @param {String/Object} config A string to set only the title or a config object
32789 * @param {String} content (optional) Set the HTML content for this panel
32790 * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
32792 Roo.ContentPanel = function(el, config, content){
32796 if(el.autoCreate || el.xtype){ // xtype is available if this is called from factory
32800 if (config && config.parentLayout) {
32801 el = config.parentLayout.el.createChild();
32804 if(el.autoCreate){ // xtype is available if this is called from factory
32808 this.el = Roo.get(el);
32809 if(!this.el && config && config.autoCreate){
32810 if(typeof config.autoCreate == "object"){
32811 if(!config.autoCreate.id){
32812 config.autoCreate.id = config.id||el;
32814 this.el = Roo.DomHelper.append(document.body,
32815 config.autoCreate, true);
32817 this.el = Roo.DomHelper.append(document.body,
32818 {tag: "div", cls: "x-layout-inactive-content", id: config.id||el}, true);
32821 this.closable = false;
32822 this.loaded = false;
32823 this.active = false;
32824 if(typeof config == "string"){
32825 this.title = config;
32827 Roo.apply(this, config);
32830 if (this.toolbar && !this.toolbar.el && this.toolbar.xtype) {
32831 this.wrapEl = this.el.wrap();
32832 this.toolbar.container = this.el.insertSibling(false, 'before');
32833 this.toolbar = new Roo.Toolbar(this.toolbar);
32839 this.resizeEl = Roo.get(this.resizeEl, true);
32841 this.resizeEl = this.el;
32846 * Fires when this panel is activated.
32847 * @param {Roo.ContentPanel} this
32851 * @event deactivate
32852 * Fires when this panel is activated.
32853 * @param {Roo.ContentPanel} this
32855 "deactivate" : true,
32859 * Fires when this panel is resized if fitToFrame is true.
32860 * @param {Roo.ContentPanel} this
32861 * @param {Number} width The width after any component adjustments
32862 * @param {Number} height The height after any component adjustments
32868 * Fires when this tab is created
32869 * @param {Roo.ContentPanel} this
32876 if(this.autoScroll){
32877 this.resizeEl.setStyle("overflow", "auto");
32879 // fix randome scrolling
32880 this.el.on('scroll', function() {
32881 Roo.log('fix random scolling');
32882 this.scrollTo('top',0);
32885 content = content || this.content;
32887 this.setContent(content);
32889 if(config && config.url){
32890 this.setUrl(this.url, this.params, this.loadOnce);
32895 Roo.ContentPanel.superclass.constructor.call(this);
32897 this.fireEvent('render', this);
32900 Roo.extend(Roo.ContentPanel, Roo.util.Observable, {
32902 setRegion : function(region){
32903 this.region = region;
32905 this.el.replaceClass("x-layout-inactive-content", "x-layout-active-content");
32907 this.el.replaceClass("x-layout-active-content", "x-layout-inactive-content");
32912 * Returns the toolbar for this Panel if one was configured.
32913 * @return {Roo.Toolbar}
32915 getToolbar : function(){
32916 return this.toolbar;
32919 setActiveState : function(active){
32920 this.active = active;
32922 this.fireEvent("deactivate", this);
32924 this.fireEvent("activate", this);
32928 * Updates this panel's element
32929 * @param {String} content The new content
32930 * @param {Boolean} loadScripts (optional) true to look for and process scripts
32932 setContent : function(content, loadScripts){
32933 this.el.update(content, loadScripts);
32936 ignoreResize : function(w, h){
32937 if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
32940 this.lastSize = {width: w, height: h};
32945 * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
32946 * @return {Roo.UpdateManager} The UpdateManager
32948 getUpdateManager : function(){
32949 return this.el.getUpdateManager();
32952 * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
32953 * @param {Object/String/Function} url The url for this request or a function to call to get the url or a config object containing any of the following options:
32956 url: "your-url.php",
32957 params: {param1: "foo", param2: "bar"}, // or a URL encoded string
32958 callback: yourFunction,
32959 scope: yourObject, //(optional scope)
32962 text: "Loading...",
32967 * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
32968 * are shorthand for <i>disableCaching</i>, <i>indicatorText</i> and <i>loadScripts</i> and are used to set their associated property on this panel UpdateManager instance.
32969 * @param {String/Object} params (optional) The parameters to pass as either a URL encoded string "param1=1&param2=2" or an object {param1: 1, param2: 2}
32970 * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
32971 * @param {Boolean} discardUrl (optional) By default when you execute an update the defaultUrl is changed to the last used URL. If true, it will not store the URL.
32972 * @return {Roo.ContentPanel} this
32975 var um = this.el.getUpdateManager();
32976 um.update.apply(um, arguments);
32982 * Set a URL to be used to load the content for this panel. When this panel is activated, the content will be loaded from that URL.
32983 * @param {String/Function} url The URL to load the content from or a function to call to get the URL
32984 * @param {String/Object} params (optional) The string params for the update call or an object of the params. See {@link Roo.UpdateManager#update} for more details. (Defaults to null)
32985 * @param {Boolean} loadOnce (optional) Whether to only load the content once. If this is false it makes the Ajax call every time this panel is activated. (Defaults to false)
32986 * @return {Roo.UpdateManager} The UpdateManager
32988 setUrl : function(url, params, loadOnce){
32989 if(this.refreshDelegate){
32990 this.removeListener("activate", this.refreshDelegate);
32992 this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
32993 this.on("activate", this.refreshDelegate);
32994 return this.el.getUpdateManager();
32997 _handleRefresh : function(url, params, loadOnce){
32998 if(!loadOnce || !this.loaded){
32999 var updater = this.el.getUpdateManager();
33000 updater.update(url, params, this._setLoaded.createDelegate(this));
33004 _setLoaded : function(){
33005 this.loaded = true;
33009 * Returns this panel's id
33012 getId : function(){
33017 * Returns this panel's element - used by regiosn to add.
33018 * @return {Roo.Element}
33020 getEl : function(){
33021 return this.wrapEl || this.el;
33024 adjustForComponents : function(width, height){
33025 if(this.resizeEl != this.el){
33026 width -= this.el.getFrameWidth('lr');
33027 height -= this.el.getFrameWidth('tb');
33030 var te = this.toolbar.getEl();
33031 height -= te.getHeight();
33032 te.setWidth(width);
33034 if(this.adjustments){
33035 width += this.adjustments[0];
33036 height += this.adjustments[1];
33038 return {"width": width, "height": height};
33041 setSize : function(width, height){
33042 if(this.fitToFrame && !this.ignoreResize(width, height)){
33043 if(this.fitContainer && this.resizeEl != this.el){
33044 this.el.setSize(width, height);
33046 var size = this.adjustForComponents(width, height);
33047 this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
33048 this.fireEvent('resize', this, size.width, size.height);
33053 * Returns this panel's title
33056 getTitle : function(){
33061 * Set this panel's title
33062 * @param {String} title
33064 setTitle : function(title){
33065 this.title = title;
33067 this.region.updatePanelTitle(this, title);
33072 * Returns true is this panel was configured to be closable
33073 * @return {Boolean}
33075 isClosable : function(){
33076 return this.closable;
33079 beforeSlide : function(){
33081 this.resizeEl.clip();
33084 afterSlide : function(){
33086 this.resizeEl.unclip();
33090 * Force a content refresh from the URL specified in the {@link #setUrl} method.
33091 * Will fail silently if the {@link #setUrl} method has not been called.
33092 * This does not activate the panel, just updates its content.
33094 refresh : function(){
33095 if(this.refreshDelegate){
33096 this.loaded = false;
33097 this.refreshDelegate();
33102 * Destroys this panel
33104 destroy : function(){
33105 this.el.removeAllListeners();
33106 var tempEl = document.createElement("span");
33107 tempEl.appendChild(this.el.dom);
33108 tempEl.innerHTML = "";
33114 * form - if the content panel contains a form - this is a reference to it.
33115 * @type {Roo.form.Form}
33119 * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
33120 * This contains a reference to it.
33126 * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
33136 * @param {Object} cfg Xtype definition of item to add.
33139 addxtype : function(cfg) {
33141 if (cfg.xtype.match(/^Form$/)) {
33142 var el = this.el.createChild();
33144 this.form = new Roo.form.Form(cfg);
33147 if ( this.form.allItems.length) this.form.render(el.dom);
33150 // should only have one of theses..
33151 if (['View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
33153 cfg.el = this.el.appendChild(document.createElement("div"));
33156 var ret = new Roo.factory(cfg);
33157 ret.render && ret.render(false, ''); // render blank..
33166 * @class Roo.GridPanel
33167 * @extends Roo.ContentPanel
33169 * Create a new GridPanel.
33170 * @param {Roo.grid.Grid} grid The grid for this panel
33171 * @param {String/Object} config A string to set only the panel's title, or a config object
33173 Roo.GridPanel = function(grid, config){
33176 this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
33177 {tag: "div", cls: "x-layout-grid-wrapper x-layout-inactive-content"}, true);
33179 this.wrapper.dom.appendChild(grid.getGridEl().dom);
33181 Roo.GridPanel.superclass.constructor.call(this, this.wrapper, config);
33184 this.toolbar.el.insertBefore(this.wrapper.dom.firstChild);
33186 // xtype created footer. - not sure if will work as we normally have to render first..
33187 if (this.footer && !this.footer.el && this.footer.xtype) {
33189 this.footer.container = this.grid.getView().getFooterPanel(true);
33190 this.footer.dataSource = this.grid.dataSource;
33191 this.footer = Roo.factory(this.footer, Roo);
33195 grid.monitorWindowResize = false; // turn off autosizing
33196 grid.autoHeight = false;
33197 grid.autoWidth = false;
33199 this.grid.getGridEl().replaceClass("x-layout-inactive-content", "x-layout-component-panel");
33202 Roo.extend(Roo.GridPanel, Roo.ContentPanel, {
33203 getId : function(){
33204 return this.grid.id;
33208 * Returns the grid for this panel
33209 * @return {Roo.grid.Grid}
33211 getGrid : function(){
33215 setSize : function(width, height){
33216 if(!this.ignoreResize(width, height)){
33217 var grid = this.grid;
33218 var size = this.adjustForComponents(width, height);
33219 grid.getGridEl().setSize(size.width, size.height);
33224 beforeSlide : function(){
33225 this.grid.getView().scroller.clip();
33228 afterSlide : function(){
33229 this.grid.getView().scroller.unclip();
33232 destroy : function(){
33233 this.grid.destroy();
33235 Roo.GridPanel.superclass.destroy.call(this);
33241 * @class Roo.NestedLayoutPanel
33242 * @extends Roo.ContentPanel
33244 * Create a new NestedLayoutPanel.
33247 * @param {Roo.BorderLayout} layout The layout for this panel
33248 * @param {String/Object} config A string to set only the title or a config object
33250 Roo.NestedLayoutPanel = function(layout, config)
33252 // construct with only one argument..
33253 /* FIXME - implement nicer consturctors
33254 if (layout.layout) {
33256 layout = config.layout;
33257 delete config.layout;
33259 if (layout.xtype && !layout.getEl) {
33260 // then layout needs constructing..
33261 layout = Roo.factory(layout, Roo);
33266 Roo.NestedLayoutPanel.superclass.constructor.call(this, layout.getEl(), config);
33268 layout.monitorWindowResize = false; // turn off autosizing
33269 this.layout = layout;
33270 this.layout.getEl().addClass("x-layout-nested-layout");
33277 Roo.extend(Roo.NestedLayoutPanel, Roo.ContentPanel, {
33279 setSize : function(width, height){
33280 if(!this.ignoreResize(width, height)){
33281 var size = this.adjustForComponents(width, height);
33282 var el = this.layout.getEl();
33283 el.setSize(size.width, size.height);
33284 var touch = el.dom.offsetWidth;
33285 this.layout.layout();
33286 // ie requires a double layout on the first pass
33287 if(Roo.isIE && !this.initialized){
33288 this.initialized = true;
33289 this.layout.layout();
33294 // activate all subpanels if not currently active..
33296 setActiveState : function(active){
33297 this.active = active;
33299 this.fireEvent("deactivate", this);
33303 this.fireEvent("activate", this);
33304 // not sure if this should happen before or after..
33305 if (!this.layout) {
33306 return; // should not happen..
33309 for (var r in this.layout.regions) {
33310 reg = this.layout.getRegion(r);
33311 if (reg.getActivePanel()) {
33312 //reg.showPanel(reg.getActivePanel()); // force it to activate..
33313 reg.setActivePanel(reg.getActivePanel());
33316 if (!reg.panels.length) {
33319 reg.showPanel(reg.getPanel(0));
33328 * Returns the nested BorderLayout for this panel
33329 * @return {Roo.BorderLayout}
33331 getLayout : function(){
33332 return this.layout;
33336 * Adds a xtype elements to the layout of the nested panel
33340 xtype : 'ContentPanel',
33347 xtype : 'NestedLayoutPanel',
33353 items : [ ... list of content panels or nested layout panels.. ]
33357 * @param {Object} cfg Xtype definition of item to add.
33359 addxtype : function(cfg) {
33360 return this.layout.addxtype(cfg);
33365 Roo.ScrollPanel = function(el, config, content){
33366 config = config || {};
33367 config.fitToFrame = true;
33368 Roo.ScrollPanel.superclass.constructor.call(this, el, config, content);
33370 this.el.dom.style.overflow = "hidden";
33371 var wrap = this.el.wrap({cls: "x-scroller x-layout-inactive-content"});
33372 this.el.removeClass("x-layout-inactive-content");
33373 this.el.on("mousewheel", this.onWheel, this);
33375 var up = wrap.createChild({cls: "x-scroller-up", html: " "}, this.el.dom);
33376 var down = wrap.createChild({cls: "x-scroller-down", html: " "});
33377 up.unselectable(); down.unselectable();
33378 up.on("click", this.scrollUp, this);
33379 down.on("click", this.scrollDown, this);
33380 up.addClassOnOver("x-scroller-btn-over");
33381 down.addClassOnOver("x-scroller-btn-over");
33382 up.addClassOnClick("x-scroller-btn-click");
33383 down.addClassOnClick("x-scroller-btn-click");
33384 this.adjustments = [0, -(up.getHeight() + down.getHeight())];
33386 this.resizeEl = this.el;
33387 this.el = wrap; this.up = up; this.down = down;
33390 Roo.extend(Roo.ScrollPanel, Roo.ContentPanel, {
33392 wheelIncrement : 5,
33393 scrollUp : function(){
33394 this.resizeEl.scroll("up", this.increment, {callback: this.afterScroll, scope: this});
33397 scrollDown : function(){
33398 this.resizeEl.scroll("down", this.increment, {callback: this.afterScroll, scope: this});
33401 afterScroll : function(){
33402 var el = this.resizeEl;
33403 var t = el.dom.scrollTop, h = el.dom.scrollHeight, ch = el.dom.clientHeight;
33404 this.up[t == 0 ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
33405 this.down[h - t <= ch ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
33408 setSize : function(){
33409 Roo.ScrollPanel.superclass.setSize.apply(this, arguments);
33410 this.afterScroll();
33413 onWheel : function(e){
33414 var d = e.getWheelDelta();
33415 this.resizeEl.dom.scrollTop -= (d*this.wheelIncrement);
33416 this.afterScroll();
33420 setContent : function(content, loadScripts){
33421 this.resizeEl.update(content, loadScripts);
33435 * @class Roo.TreePanel
33436 * @extends Roo.ContentPanel
33438 * Create a new TreePanel. - defaults to fit/scoll contents.
33439 * @param {String/Object} config A string to set only the panel's title, or a config object
33440 * @cfg {Roo.tree.TreePanel} tree The tree TreePanel, with config etc.
33442 Roo.TreePanel = function(config){
33443 var el = config.el;
33444 var tree = config.tree;
33445 delete config.tree;
33446 delete config.el; // hopefull!
33448 // wrapper for IE7 strict & safari scroll issue
33450 var treeEl = el.createChild();
33451 config.resizeEl = treeEl;
33455 Roo.TreePanel.superclass.constructor.call(this, el, config);
33458 this.tree = new Roo.tree.TreePanel(treeEl , tree);
33459 //console.log(tree);
33460 this.on('activate', function()
33462 if (this.tree.rendered) {
33465 //console.log('render tree');
33466 this.tree.render();
33469 this.on('resize', function (cp, w, h) {
33470 this.tree.innerCt.setWidth(w);
33471 this.tree.innerCt.setHeight(h);
33472 this.tree.innerCt.setStyle('overflow-y', 'auto');
33479 Roo.extend(Roo.TreePanel, Roo.ContentPanel, {
33496 * Ext JS Library 1.1.1
33497 * Copyright(c) 2006-2007, Ext JS, LLC.
33499 * Originally Released Under LGPL - original licence link has changed is not relivant.
33502 * <script type="text/javascript">
33507 * @class Roo.ReaderLayout
33508 * @extends Roo.BorderLayout
33509 * This is a pre-built layout that represents a classic, 5-pane application. It consists of a header, a primary
33510 * center region containing two nested regions (a top one for a list view and one for item preview below),
33511 * and regions on either side that can be used for navigation, application commands, informational displays, etc.
33512 * The setup and configuration work exactly the same as it does for a {@link Roo.BorderLayout} - this class simply
33513 * expedites the setup of the overall layout and regions for this common application style.
33516 var reader = new Roo.ReaderLayout();
33517 var CP = Roo.ContentPanel; // shortcut for adding
33519 reader.beginUpdate();
33520 reader.add("north", new CP("north", "North"));
33521 reader.add("west", new CP("west", {title: "West"}));
33522 reader.add("east", new CP("east", {title: "East"}));
33524 reader.regions.listView.add(new CP("listView", "List"));
33525 reader.regions.preview.add(new CP("preview", "Preview"));
33526 reader.endUpdate();
33529 * Create a new ReaderLayout
33530 * @param {Object} config Configuration options
33531 * @param {String/HTMLElement/Element} container (optional) The container this layout is bound to (defaults to
33532 * document.body if omitted)
33534 Roo.ReaderLayout = function(config, renderTo){
33535 var c = config || {size:{}};
33536 Roo.ReaderLayout.superclass.constructor.call(this, renderTo || document.body, {
33537 north: c.north !== false ? Roo.apply({
33541 }, c.north) : false,
33542 west: c.west !== false ? Roo.apply({
33550 margins:{left:5,right:0,bottom:5,top:5},
33551 cmargins:{left:5,right:5,bottom:5,top:5}
33552 }, c.west) : false,
33553 east: c.east !== false ? Roo.apply({
33561 margins:{left:0,right:5,bottom:5,top:5},
33562 cmargins:{left:5,right:5,bottom:5,top:5}
33563 }, c.east) : false,
33564 center: Roo.apply({
33565 tabPosition: 'top',
33569 margins:{left:c.west!==false ? 0 : 5,right:c.east!==false ? 0 : 5,bottom:5,top:2}
33573 this.el.addClass('x-reader');
33575 this.beginUpdate();
33577 var inner = new Roo.BorderLayout(Roo.get(document.body).createChild(), {
33578 south: c.preview !== false ? Roo.apply({
33585 cmargins:{top:5,left:0, right:0, bottom:0}
33586 }, c.preview) : false,
33587 center: Roo.apply({
33593 this.add('center', new Roo.NestedLayoutPanel(inner,
33594 Roo.apply({title: c.mainTitle || '',tabTip:''},c.innerPanelCfg)));
33598 this.regions.preview = inner.getRegion('south');
33599 this.regions.listView = inner.getRegion('center');
33602 Roo.extend(Roo.ReaderLayout, Roo.BorderLayout);/*
33604 * Ext JS Library 1.1.1
33605 * Copyright(c) 2006-2007, Ext JS, LLC.
33607 * Originally Released Under LGPL - original licence link has changed is not relivant.
33610 * <script type="text/javascript">
33614 * @class Roo.grid.Grid
33615 * @extends Roo.util.Observable
33616 * This class represents the primary interface of a component based grid control.
33617 * <br><br>Usage:<pre><code>
33618 var grid = new Roo.grid.Grid("my-container-id", {
33621 selModel: mySelectionModel,
33622 autoSizeColumns: true,
33623 monitorWindowResize: false,
33624 trackMouseOver: true
33629 * <b>Common Problems:</b><br/>
33630 * - Grid does not resize properly when going smaller: Setting overflow hidden on the container
33631 * element will correct this<br/>
33632 * - If you get el.style[camel]= NaNpx or -2px or something related, be certain you have given your container element
33633 * dimensions. The grid adapts to your container's size, if your container has no size defined then the results
33634 * are unpredictable.<br/>
33635 * - Do not render the grid into an element with display:none. Try using visibility:hidden. Otherwise there is no way for the
33636 * grid to calculate dimensions/offsets.<br/>
33638 * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
33639 * The container MUST have some type of size defined for the grid to fill. The container will be
33640 * automatically set to position relative if it isn't already.
33641 * @param {Object} config A config object that sets properties on this grid.
33643 Roo.grid.Grid = function(container, config){
33644 // initialize the container
33645 this.container = Roo.get(container);
33646 this.container.update("");
33647 this.container.setStyle("overflow", "hidden");
33648 this.container.addClass('x-grid-container');
33650 this.id = this.container.id;
33652 Roo.apply(this, config);
33653 // check and correct shorthanded configs
33655 this.dataSource = this.ds;
33659 this.colModel = this.cm;
33663 this.selModel = this.sm;
33667 if (this.selModel) {
33668 this.selModel = Roo.factory(this.selModel, Roo.grid);
33669 this.sm = this.selModel;
33670 this.sm.xmodule = this.xmodule || false;
33672 if (typeof(this.colModel.config) == 'undefined') {
33673 this.colModel = new Roo.grid.ColumnModel(this.colModel);
33674 this.cm = this.colModel;
33675 this.cm.xmodule = this.xmodule || false;
33677 if (this.dataSource) {
33678 this.dataSource= Roo.factory(this.dataSource, Roo.data);
33679 this.ds = this.dataSource;
33680 this.ds.xmodule = this.xmodule || false;
33687 this.container.setWidth(this.width);
33691 this.container.setHeight(this.height);
33698 * The raw click event for the entire grid.
33699 * @param {Roo.EventObject} e
33704 * The raw dblclick event for the entire grid.
33705 * @param {Roo.EventObject} e
33709 * @event contextmenu
33710 * The raw contextmenu event for the entire grid.
33711 * @param {Roo.EventObject} e
33713 "contextmenu" : true,
33716 * The raw mousedown event for the entire grid.
33717 * @param {Roo.EventObject} e
33719 "mousedown" : true,
33722 * The raw mouseup event for the entire grid.
33723 * @param {Roo.EventObject} e
33728 * The raw mouseover event for the entire grid.
33729 * @param {Roo.EventObject} e
33731 "mouseover" : true,
33734 * The raw mouseout event for the entire grid.
33735 * @param {Roo.EventObject} e
33740 * The raw keypress event for the entire grid.
33741 * @param {Roo.EventObject} e
33746 * The raw keydown event for the entire grid.
33747 * @param {Roo.EventObject} e
33755 * Fires when a cell is clicked
33756 * @param {Grid} this
33757 * @param {Number} rowIndex
33758 * @param {Number} columnIndex
33759 * @param {Roo.EventObject} e
33761 "cellclick" : true,
33763 * @event celldblclick
33764 * Fires when a cell is double clicked
33765 * @param {Grid} this
33766 * @param {Number} rowIndex
33767 * @param {Number} columnIndex
33768 * @param {Roo.EventObject} e
33770 "celldblclick" : true,
33773 * Fires when a row is clicked
33774 * @param {Grid} this
33775 * @param {Number} rowIndex
33776 * @param {Roo.EventObject} e
33780 * @event rowdblclick
33781 * Fires when a row is double clicked
33782 * @param {Grid} this
33783 * @param {Number} rowIndex
33784 * @param {Roo.EventObject} e
33786 "rowdblclick" : true,
33788 * @event headerclick
33789 * Fires when a header is clicked
33790 * @param {Grid} this
33791 * @param {Number} columnIndex
33792 * @param {Roo.EventObject} e
33794 "headerclick" : true,
33796 * @event headerdblclick
33797 * Fires when a header cell is double clicked
33798 * @param {Grid} this
33799 * @param {Number} columnIndex
33800 * @param {Roo.EventObject} e
33802 "headerdblclick" : true,
33804 * @event rowcontextmenu
33805 * Fires when a row is right clicked
33806 * @param {Grid} this
33807 * @param {Number} rowIndex
33808 * @param {Roo.EventObject} e
33810 "rowcontextmenu" : true,
33812 * @event cellcontextmenu
33813 * Fires when a cell is right clicked
33814 * @param {Grid} this
33815 * @param {Number} rowIndex
33816 * @param {Number} cellIndex
33817 * @param {Roo.EventObject} e
33819 "cellcontextmenu" : true,
33821 * @event headercontextmenu
33822 * Fires when a header is right clicked
33823 * @param {Grid} this
33824 * @param {Number} columnIndex
33825 * @param {Roo.EventObject} e
33827 "headercontextmenu" : true,
33829 * @event bodyscroll
33830 * Fires when the body element is scrolled
33831 * @param {Number} scrollLeft
33832 * @param {Number} scrollTop
33834 "bodyscroll" : true,
33836 * @event columnresize
33837 * Fires when the user resizes a column
33838 * @param {Number} columnIndex
33839 * @param {Number} newSize
33841 "columnresize" : true,
33843 * @event columnmove
33844 * Fires when the user moves a column
33845 * @param {Number} oldIndex
33846 * @param {Number} newIndex
33848 "columnmove" : true,
33851 * Fires when row(s) start being dragged
33852 * @param {Grid} this
33853 * @param {Roo.GridDD} dd The drag drop object
33854 * @param {event} e The raw browser event
33856 "startdrag" : true,
33859 * Fires when a drag operation is complete
33860 * @param {Grid} this
33861 * @param {Roo.GridDD} dd The drag drop object
33862 * @param {event} e The raw browser event
33867 * Fires when dragged row(s) are dropped on a valid DD target
33868 * @param {Grid} this
33869 * @param {Roo.GridDD} dd The drag drop object
33870 * @param {String} targetId The target drag drop object
33871 * @param {event} e The raw browser event
33876 * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
33877 * @param {Grid} this
33878 * @param {Roo.GridDD} dd The drag drop object
33879 * @param {String} targetId The target drag drop object
33880 * @param {event} e The raw browser event
33885 * Fires when the dragged row(s) first cross another DD target while being dragged
33886 * @param {Grid} this
33887 * @param {Roo.GridDD} dd The drag drop object
33888 * @param {String} targetId The target drag drop object
33889 * @param {event} e The raw browser event
33891 "dragenter" : true,
33894 * Fires when the dragged row(s) leave another DD target while being dragged
33895 * @param {Grid} this
33896 * @param {Roo.GridDD} dd The drag drop object
33897 * @param {String} targetId The target drag drop object
33898 * @param {event} e The raw browser event
33903 * Fires when a row is rendered, so you can change add a style to it.
33904 * @param {GridView} gridview The grid view
33905 * @param {Object} rowcfg contains record rowIndex and rowClass - set rowClass to add a style.
33911 * Fires when the grid is rendered
33912 * @param {Grid} grid
33917 Roo.grid.Grid.superclass.constructor.call(this);
33919 Roo.extend(Roo.grid.Grid, Roo.util.Observable, {
33922 * @cfg {String} ddGroup - drag drop group.
33926 * @cfg {Number} minColumnWidth The minimum width a column can be resized to. Default is 25.
33928 minColumnWidth : 25,
33931 * @cfg {Boolean} autoSizeColumns True to automatically resize the columns to fit their content
33932 * <b>on initial render.</b> It is more efficient to explicitly size the columns
33933 * through the ColumnModel's {@link Roo.grid.ColumnModel#width} config option. Default is false.
33935 autoSizeColumns : false,
33938 * @cfg {Boolean} autoSizeHeaders True to measure headers with column data when auto sizing columns. Default is true.
33940 autoSizeHeaders : true,
33943 * @cfg {Boolean} monitorWindowResize True to autoSize the grid when the window resizes. Default is true.
33945 monitorWindowResize : true,
33948 * @cfg {Boolean} maxRowsToMeasure If autoSizeColumns is on, maxRowsToMeasure can be used to limit the number of
33949 * rows measured to get a columns size. Default is 0 (all rows).
33951 maxRowsToMeasure : 0,
33954 * @cfg {Boolean} trackMouseOver True to highlight rows when the mouse is over. Default is true.
33956 trackMouseOver : true,
33959 * @cfg {Boolean} enableDrag True to enable drag of rows. Default is false. (double check if this is needed?)
33963 * @cfg {Boolean} enableDragDrop True to enable drag and drop of rows. Default is false.
33965 enableDragDrop : false,
33968 * @cfg {Boolean} enableColumnMove True to enable drag and drop reorder of columns. Default is true.
33970 enableColumnMove : true,
33973 * @cfg {Boolean} enableColumnHide True to enable hiding of columns with the header context menu. Default is true.
33975 enableColumnHide : true,
33978 * @cfg {Boolean} enableRowHeightSync True to manually sync row heights across locked and not locked rows. Default is false.
33980 enableRowHeightSync : false,
33983 * @cfg {Boolean} stripeRows True to stripe the rows. Default is true.
33988 * @cfg {Boolean} autoHeight True to fit the height of the grid container to the height of the data. Default is false.
33990 autoHeight : false,
33993 * @cfg {String} autoExpandColumn The id (or dataIndex) of a column in this grid that should expand to fill unused space. This id can not be 0. Default is false.
33995 autoExpandColumn : false,
33998 * @cfg {Number} autoExpandMin The minimum width the autoExpandColumn can have (if enabled).
34001 autoExpandMin : 50,
34004 * @cfg {Number} autoExpandMax The maximum width the autoExpandColumn can have (if enabled). Default is 1000.
34006 autoExpandMax : 1000,
34009 * @cfg {Object} view The {@link Roo.grid.GridView} used by the grid. This can be set before a call to render().
34014 * @cfg {Object} loadMask An {@link Roo.LoadMask} config or true to mask the grid while loading. Default is false.
34018 * @cfg {Roo.dd.DropTarget} dragTarget An {@link Roo.dd.DragTarget} config
34028 * @cfg {Boolean} autoWidth True to set the grid's width to the default total width of the grid's columns instead
34029 * of a fixed width. Default is false.
34032 * @cfg {Number} maxHeight Sets the maximum height of the grid - ignored if autoHeight is not on.
34035 * Called once after all setup has been completed and the grid is ready to be rendered.
34036 * @return {Roo.grid.Grid} this
34038 render : function()
34040 var c = this.container;
34041 // try to detect autoHeight/width mode
34042 if((!c.dom.offsetHeight || c.dom.offsetHeight < 20) || c.getStyle("height") == "auto"){
34043 this.autoHeight = true;
34045 var view = this.getView();
34048 c.on("click", this.onClick, this);
34049 c.on("dblclick", this.onDblClick, this);
34050 c.on("contextmenu", this.onContextMenu, this);
34051 c.on("keydown", this.onKeyDown, this);
34053 this.relayEvents(c, ["mousedown","mouseup","mouseover","mouseout","keypress"]);
34055 this.getSelectionModel().init(this);
34060 this.loadMask = new Roo.LoadMask(this.container,
34061 Roo.apply({store:this.dataSource}, this.loadMask));
34065 if (this.toolbar && this.toolbar.xtype) {
34066 this.toolbar.container = this.getView().getHeaderPanel(true);
34067 this.toolbar = new Roo.Toolbar(this.toolbar);
34069 if (this.footer && this.footer.xtype) {
34070 this.footer.dataSource = this.getDataSource();
34071 this.footer.container = this.getView().getFooterPanel(true);
34072 this.footer = Roo.factory(this.footer, Roo);
34074 if (this.dropTarget && this.dropTarget.xtype) {
34075 delete this.dropTarget.xtype;
34076 this.dropTarget = new Ext.dd.DropTarget(this.getView().mainBody, this.dropTarget);
34080 this.rendered = true;
34081 this.fireEvent('render', this);
34086 * Reconfigures the grid to use a different Store and Column Model.
34087 * The View will be bound to the new objects and refreshed.
34088 * @param {Roo.data.Store} dataSource The new {@link Roo.data.Store} object
34089 * @param {Roo.grid.ColumnModel} The new {@link Roo.grid.ColumnModel} object
34091 reconfigure : function(dataSource, colModel){
34093 this.loadMask.destroy();
34094 this.loadMask = new Roo.LoadMask(this.container,
34095 Roo.apply({store:dataSource}, this.loadMask));
34097 this.view.bind(dataSource, colModel);
34098 this.dataSource = dataSource;
34099 this.colModel = colModel;
34100 this.view.refresh(true);
34104 onKeyDown : function(e){
34105 this.fireEvent("keydown", e);
34109 * Destroy this grid.
34110 * @param {Boolean} removeEl True to remove the element
34112 destroy : function(removeEl, keepListeners){
34114 this.loadMask.destroy();
34116 var c = this.container;
34117 c.removeAllListeners();
34118 this.view.destroy();
34119 this.colModel.purgeListeners();
34120 if(!keepListeners){
34121 this.purgeListeners();
34124 if(removeEl === true){
34130 processEvent : function(name, e){
34131 this.fireEvent(name, e);
34132 var t = e.getTarget();
34134 var header = v.findHeaderIndex(t);
34135 if(header !== false){
34136 this.fireEvent("header" + name, this, header, e);
34138 var row = v.findRowIndex(t);
34139 var cell = v.findCellIndex(t);
34141 this.fireEvent("row" + name, this, row, e);
34142 if(cell !== false){
34143 this.fireEvent("cell" + name, this, row, cell, e);
34150 onClick : function(e){
34151 this.processEvent("click", e);
34155 onContextMenu : function(e, t){
34156 this.processEvent("contextmenu", e);
34160 onDblClick : function(e){
34161 this.processEvent("dblclick", e);
34165 walkCells : function(row, col, step, fn, scope){
34166 var cm = this.colModel, clen = cm.getColumnCount();
34167 var ds = this.dataSource, rlen = ds.getCount(), first = true;
34179 if(fn.call(scope || this, row, col, cm) === true){
34197 if(fn.call(scope || this, row, col, cm) === true){
34209 getSelections : function(){
34210 return this.selModel.getSelections();
34214 * Causes the grid to manually recalculate its dimensions. Generally this is done automatically,
34215 * but if manual update is required this method will initiate it.
34217 autoSize : function(){
34219 this.view.layout();
34220 if(this.view.adjustForScroll){
34221 this.view.adjustForScroll();
34227 * Returns the grid's underlying element.
34228 * @return {Element} The element
34230 getGridEl : function(){
34231 return this.container;
34234 // private for compatibility, overridden by editor grid
34235 stopEditing : function(){},
34238 * Returns the grid's SelectionModel.
34239 * @return {SelectionModel}
34241 getSelectionModel : function(){
34242 if(!this.selModel){
34243 this.selModel = new Roo.grid.RowSelectionModel();
34245 return this.selModel;
34249 * Returns the grid's DataSource.
34250 * @return {DataSource}
34252 getDataSource : function(){
34253 return this.dataSource;
34257 * Returns the grid's ColumnModel.
34258 * @return {ColumnModel}
34260 getColumnModel : function(){
34261 return this.colModel;
34265 * Returns the grid's GridView object.
34266 * @return {GridView}
34268 getView : function(){
34270 this.view = new Roo.grid.GridView(this.viewConfig);
34275 * Called to get grid's drag proxy text, by default returns this.ddText.
34278 getDragDropText : function(){
34279 var count = this.selModel.getCount();
34280 return String.format(this.ddText, count, count == 1 ? '' : 's');
34284 * Configures the text is the drag proxy (defaults to "%0 selected row(s)").
34285 * %0 is replaced with the number of selected rows.
34288 Roo.grid.Grid.prototype.ddText = "{0} selected row{1}";/*
34290 * Ext JS Library 1.1.1
34291 * Copyright(c) 2006-2007, Ext JS, LLC.
34293 * Originally Released Under LGPL - original licence link has changed is not relivant.
34296 * <script type="text/javascript">
34299 Roo.grid.AbstractGridView = function(){
34303 "beforerowremoved" : true,
34304 "beforerowsinserted" : true,
34305 "beforerefresh" : true,
34306 "rowremoved" : true,
34307 "rowsinserted" : true,
34308 "rowupdated" : true,
34311 Roo.grid.AbstractGridView.superclass.constructor.call(this);
34314 Roo.extend(Roo.grid.AbstractGridView, Roo.util.Observable, {
34315 rowClass : "x-grid-row",
34316 cellClass : "x-grid-cell",
34317 tdClass : "x-grid-td",
34318 hdClass : "x-grid-hd",
34319 splitClass : "x-grid-hd-split",
34321 init: function(grid){
34323 var cid = this.grid.getGridEl().id;
34324 this.colSelector = "#" + cid + " ." + this.cellClass + "-";
34325 this.tdSelector = "#" + cid + " ." + this.tdClass + "-";
34326 this.hdSelector = "#" + cid + " ." + this.hdClass + "-";
34327 this.splitSelector = "#" + cid + " ." + this.splitClass + "-";
34330 getColumnRenderers : function(){
34331 var renderers = [];
34332 var cm = this.grid.colModel;
34333 var colCount = cm.getColumnCount();
34334 for(var i = 0; i < colCount; i++){
34335 renderers[i] = cm.getRenderer(i);
34340 getColumnIds : function(){
34342 var cm = this.grid.colModel;
34343 var colCount = cm.getColumnCount();
34344 for(var i = 0; i < colCount; i++){
34345 ids[i] = cm.getColumnId(i);
34350 getDataIndexes : function(){
34351 if(!this.indexMap){
34352 this.indexMap = this.buildIndexMap();
34354 return this.indexMap.colToData;
34357 getColumnIndexByDataIndex : function(dataIndex){
34358 if(!this.indexMap){
34359 this.indexMap = this.buildIndexMap();
34361 return this.indexMap.dataToCol[dataIndex];
34365 * Set a css style for a column dynamically.
34366 * @param {Number} colIndex The index of the column
34367 * @param {String} name The css property name
34368 * @param {String} value The css value
34370 setCSSStyle : function(colIndex, name, value){
34371 var selector = "#" + this.grid.id + " .x-grid-col-" + colIndex;
34372 Roo.util.CSS.updateRule(selector, name, value);
34375 generateRules : function(cm){
34376 var ruleBuf = [], rulesId = this.grid.id + '-cssrules';
34377 Roo.util.CSS.removeStyleSheet(rulesId);
34378 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
34379 var cid = cm.getColumnId(i);
34380 ruleBuf.push(this.colSelector, cid, " {\n", cm.config[i].css, "}\n",
34381 this.tdSelector, cid, " {\n}\n",
34382 this.hdSelector, cid, " {\n}\n",
34383 this.splitSelector, cid, " {\n}\n");
34385 return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
34389 * Ext JS Library 1.1.1
34390 * Copyright(c) 2006-2007, Ext JS, LLC.
34392 * Originally Released Under LGPL - original licence link has changed is not relivant.
34395 * <script type="text/javascript">
34399 // This is a support class used internally by the Grid components
34400 Roo.grid.HeaderDragZone = function(grid, hd, hd2){
34402 this.view = grid.getView();
34403 this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
34404 Roo.grid.HeaderDragZone.superclass.constructor.call(this, hd);
34406 this.setHandleElId(Roo.id(hd));
34407 this.setOuterHandleElId(Roo.id(hd2));
34409 this.scroll = false;
34411 Roo.extend(Roo.grid.HeaderDragZone, Roo.dd.DragZone, {
34413 getDragData : function(e){
34414 var t = Roo.lib.Event.getTarget(e);
34415 var h = this.view.findHeaderCell(t);
34417 return {ddel: h.firstChild, header:h};
34422 onInitDrag : function(e){
34423 this.view.headersDisabled = true;
34424 var clone = this.dragData.ddel.cloneNode(true);
34425 clone.id = Roo.id();
34426 clone.style.width = Math.min(this.dragData.header.offsetWidth,this.maxDragWidth) + "px";
34427 this.proxy.update(clone);
34431 afterValidDrop : function(){
34433 setTimeout(function(){
34434 v.headersDisabled = false;
34438 afterInvalidDrop : function(){
34440 setTimeout(function(){
34441 v.headersDisabled = false;
34447 * Ext JS Library 1.1.1
34448 * Copyright(c) 2006-2007, Ext JS, LLC.
34450 * Originally Released Under LGPL - original licence link has changed is not relivant.
34453 * <script type="text/javascript">
34456 // This is a support class used internally by the Grid components
34457 Roo.grid.HeaderDropZone = function(grid, hd, hd2){
34459 this.view = grid.getView();
34460 // split the proxies so they don't interfere with mouse events
34461 this.proxyTop = Roo.DomHelper.append(document.body, {
34462 cls:"col-move-top", html:" "
34464 this.proxyBottom = Roo.DomHelper.append(document.body, {
34465 cls:"col-move-bottom", html:" "
34467 this.proxyTop.hide = this.proxyBottom.hide = function(){
34468 this.setLeftTop(-100,-100);
34469 this.setStyle("visibility", "hidden");
34471 this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
34472 // temporarily disabled
34473 //Roo.dd.ScrollManager.register(this.view.scroller.dom);
34474 Roo.grid.HeaderDropZone.superclass.constructor.call(this, grid.getGridEl().dom);
34476 Roo.extend(Roo.grid.HeaderDropZone, Roo.dd.DropZone, {
34477 proxyOffsets : [-4, -9],
34478 fly: Roo.Element.fly,
34480 getTargetFromEvent : function(e){
34481 var t = Roo.lib.Event.getTarget(e);
34482 var cindex = this.view.findCellIndex(t);
34483 if(cindex !== false){
34484 return this.view.getHeaderCell(cindex);
34489 nextVisible : function(h){
34490 var v = this.view, cm = this.grid.colModel;
34493 if(!cm.isHidden(v.getCellIndex(h))){
34501 prevVisible : function(h){
34502 var v = this.view, cm = this.grid.colModel;
34505 if(!cm.isHidden(v.getCellIndex(h))){
34513 positionIndicator : function(h, n, e){
34514 var x = Roo.lib.Event.getPageX(e);
34515 var r = Roo.lib.Dom.getRegion(n.firstChild);
34516 var px, pt, py = r.top + this.proxyOffsets[1];
34517 if((r.right - x) <= (r.right-r.left)/2){
34518 px = r.right+this.view.borderWidth;
34524 var oldIndex = this.view.getCellIndex(h);
34525 var newIndex = this.view.getCellIndex(n);
34527 if(this.grid.colModel.isFixed(newIndex)){
34531 var locked = this.grid.colModel.isLocked(newIndex);
34536 if(oldIndex < newIndex){
34539 if(oldIndex == newIndex && (locked == this.grid.colModel.isLocked(oldIndex))){
34542 px += this.proxyOffsets[0];
34543 this.proxyTop.setLeftTop(px, py);
34544 this.proxyTop.show();
34545 if(!this.bottomOffset){
34546 this.bottomOffset = this.view.mainHd.getHeight();
34548 this.proxyBottom.setLeftTop(px, py+this.proxyTop.dom.offsetHeight+this.bottomOffset);
34549 this.proxyBottom.show();
34553 onNodeEnter : function(n, dd, e, data){
34554 if(data.header != n){
34555 this.positionIndicator(data.header, n, e);
34559 onNodeOver : function(n, dd, e, data){
34560 var result = false;
34561 if(data.header != n){
34562 result = this.positionIndicator(data.header, n, e);
34565 this.proxyTop.hide();
34566 this.proxyBottom.hide();
34568 return result ? this.dropAllowed : this.dropNotAllowed;
34571 onNodeOut : function(n, dd, e, data){
34572 this.proxyTop.hide();
34573 this.proxyBottom.hide();
34576 onNodeDrop : function(n, dd, e, data){
34577 var h = data.header;
34579 var cm = this.grid.colModel;
34580 var x = Roo.lib.Event.getPageX(e);
34581 var r = Roo.lib.Dom.getRegion(n.firstChild);
34582 var pt = (r.right - x) <= ((r.right-r.left)/2) ? "after" : "before";
34583 var oldIndex = this.view.getCellIndex(h);
34584 var newIndex = this.view.getCellIndex(n);
34585 var locked = cm.isLocked(newIndex);
34589 if(oldIndex < newIndex){
34592 if(oldIndex == newIndex && (locked == cm.isLocked(oldIndex))){
34595 cm.setLocked(oldIndex, locked, true);
34596 cm.moveColumn(oldIndex, newIndex);
34597 this.grid.fireEvent("columnmove", oldIndex, newIndex);
34605 * Ext JS Library 1.1.1
34606 * Copyright(c) 2006-2007, Ext JS, LLC.
34608 * Originally Released Under LGPL - original licence link has changed is not relivant.
34611 * <script type="text/javascript">
34615 * @class Roo.grid.GridView
34616 * @extends Roo.util.Observable
34619 * @param {Object} config
34621 Roo.grid.GridView = function(config){
34622 Roo.grid.GridView.superclass.constructor.call(this);
34625 Roo.apply(this, config);
34628 Roo.extend(Roo.grid.GridView, Roo.grid.AbstractGridView, {
34631 rowClass : "x-grid-row",
34633 cellClass : "x-grid-col",
34635 tdClass : "x-grid-td",
34637 hdClass : "x-grid-hd",
34639 splitClass : "x-grid-split",
34641 sortClasses : ["sort-asc", "sort-desc"],
34643 enableMoveAnim : false,
34647 dh : Roo.DomHelper,
34649 fly : Roo.Element.fly,
34651 css : Roo.util.CSS,
34657 scrollIncrement : 22,
34659 cellRE: /(?:.*?)x-grid-(?:hd|cell|csplit)-(?:[\d]+)-([\d]+)(?:.*?)/,
34661 findRE: /\s?(?:x-grid-hd|x-grid-col|x-grid-csplit)\s/,
34663 bind : function(ds, cm){
34665 this.ds.un("load", this.onLoad, this);
34666 this.ds.un("datachanged", this.onDataChange, this);
34667 this.ds.un("add", this.onAdd, this);
34668 this.ds.un("remove", this.onRemove, this);
34669 this.ds.un("update", this.onUpdate, this);
34670 this.ds.un("clear", this.onClear, this);
34673 ds.on("load", this.onLoad, this);
34674 ds.on("datachanged", this.onDataChange, this);
34675 ds.on("add", this.onAdd, this);
34676 ds.on("remove", this.onRemove, this);
34677 ds.on("update", this.onUpdate, this);
34678 ds.on("clear", this.onClear, this);
34683 this.cm.un("widthchange", this.onColWidthChange, this);
34684 this.cm.un("headerchange", this.onHeaderChange, this);
34685 this.cm.un("hiddenchange", this.onHiddenChange, this);
34686 this.cm.un("columnmoved", this.onColumnMove, this);
34687 this.cm.un("columnlockchange", this.onColumnLock, this);
34690 this.generateRules(cm);
34691 cm.on("widthchange", this.onColWidthChange, this);
34692 cm.on("headerchange", this.onHeaderChange, this);
34693 cm.on("hiddenchange", this.onHiddenChange, this);
34694 cm.on("columnmoved", this.onColumnMove, this);
34695 cm.on("columnlockchange", this.onColumnLock, this);
34700 init: function(grid){
34701 Roo.grid.GridView.superclass.init.call(this, grid);
34703 this.bind(grid.dataSource, grid.colModel);
34705 grid.on("headerclick", this.handleHeaderClick, this);
34707 if(grid.trackMouseOver){
34708 grid.on("mouseover", this.onRowOver, this);
34709 grid.on("mouseout", this.onRowOut, this);
34711 grid.cancelTextSelection = function(){};
34712 this.gridId = grid.id;
34714 var tpls = this.templates || {};
34717 tpls.master = new Roo.Template(
34718 '<div class="x-grid" hidefocus="true">',
34719 '<a href="#" class="x-grid-focus" tabIndex="-1"></a>',
34720 '<div class="x-grid-topbar"></div>',
34721 '<div class="x-grid-scroller"><div></div></div>',
34722 '<div class="x-grid-locked">',
34723 '<div class="x-grid-header">{lockedHeader}</div>',
34724 '<div class="x-grid-body">{lockedBody}</div>',
34726 '<div class="x-grid-viewport">',
34727 '<div class="x-grid-header">{header}</div>',
34728 '<div class="x-grid-body">{body}</div>',
34730 '<div class="x-grid-bottombar"></div>',
34732 '<div class="x-grid-resize-proxy"> </div>',
34735 tpls.master.disableformats = true;
34739 tpls.header = new Roo.Template(
34740 '<table border="0" cellspacing="0" cellpadding="0">',
34741 '<tbody><tr class="x-grid-hd-row">{cells}</tr></tbody>',
34744 tpls.header.disableformats = true;
34746 tpls.header.compile();
34749 tpls.hcell = new Roo.Template(
34750 '<td class="x-grid-hd x-grid-td-{id} {cellId}"><div title="{title}" class="x-grid-hd-inner x-grid-hd-{id}">',
34751 '<div class="x-grid-hd-text" unselectable="on">{value}<img class="x-grid-sort-icon" src="', Roo.BLANK_IMAGE_URL, '" /></div>',
34754 tpls.hcell.disableFormats = true;
34756 tpls.hcell.compile();
34759 tpls.hsplit = new Roo.Template('<div class="x-grid-split {splitId} x-grid-split-{id}" style="{style}" unselectable="on"> </div>');
34760 tpls.hsplit.disableFormats = true;
34762 tpls.hsplit.compile();
34765 tpls.body = new Roo.Template(
34766 '<table border="0" cellspacing="0" cellpadding="0">',
34767 "<tbody>{rows}</tbody>",
34770 tpls.body.disableFormats = true;
34772 tpls.body.compile();
34775 tpls.row = new Roo.Template('<tr class="x-grid-row {alt}">{cells}</tr>');
34776 tpls.row.disableFormats = true;
34778 tpls.row.compile();
34781 tpls.cell = new Roo.Template(
34782 '<td class="x-grid-col x-grid-td-{id} {cellId} {css}" tabIndex="0">',
34783 '<div class="x-grid-col-{id} x-grid-cell-inner"><div class="x-grid-cell-text" unselectable="on" {attr}>{value}</div></div>',
34786 tpls.cell.disableFormats = true;
34788 tpls.cell.compile();
34790 this.templates = tpls;
34793 // remap these for backwards compat
34794 onColWidthChange : function(){
34795 this.updateColumns.apply(this, arguments);
34797 onHeaderChange : function(){
34798 this.updateHeaders.apply(this, arguments);
34800 onHiddenChange : function(){
34801 this.handleHiddenChange.apply(this, arguments);
34803 onColumnMove : function(){
34804 this.handleColumnMove.apply(this, arguments);
34806 onColumnLock : function(){
34807 this.handleLockChange.apply(this, arguments);
34810 onDataChange : function(){
34812 this.updateHeaderSortState();
34815 onClear : function(){
34819 onUpdate : function(ds, record){
34820 this.refreshRow(record);
34823 refreshRow : function(record){
34824 var ds = this.ds, index;
34825 if(typeof record == 'number'){
34827 record = ds.getAt(index);
34829 index = ds.indexOf(record);
34831 this.insertRows(ds, index, index, true);
34832 this.onRemove(ds, record, index+1, true);
34833 this.syncRowHeights(index, index);
34835 this.fireEvent("rowupdated", this, index, record);
34838 onAdd : function(ds, records, index){
34839 this.insertRows(ds, index, index + (records.length-1));
34842 onRemove : function(ds, record, index, isUpdate){
34843 if(isUpdate !== true){
34844 this.fireEvent("beforerowremoved", this, index, record);
34846 var bt = this.getBodyTable(), lt = this.getLockedTable();
34847 if(bt.rows[index]){
34848 bt.firstChild.removeChild(bt.rows[index]);
34850 if(lt.rows[index]){
34851 lt.firstChild.removeChild(lt.rows[index]);
34853 if(isUpdate !== true){
34854 this.stripeRows(index);
34855 this.syncRowHeights(index, index);
34857 this.fireEvent("rowremoved", this, index, record);
34861 onLoad : function(){
34862 this.scrollToTop();
34866 * Scrolls the grid to the top
34868 scrollToTop : function(){
34870 this.scroller.dom.scrollTop = 0;
34876 * Gets a panel in the header of the grid that can be used for toolbars etc.
34877 * After modifying the contents of this panel a call to grid.autoSize() may be
34878 * required to register any changes in size.
34879 * @param {Boolean} doShow By default the header is hidden. Pass true to show the panel
34880 * @return Roo.Element
34882 getHeaderPanel : function(doShow){
34884 this.headerPanel.show();
34886 return this.headerPanel;
34890 * Gets a panel in the footer of the grid that can be used for toolbars etc.
34891 * After modifying the contents of this panel a call to grid.autoSize() may be
34892 * required to register any changes in size.
34893 * @param {Boolean} doShow By default the footer is hidden. Pass true to show the panel
34894 * @return Roo.Element
34896 getFooterPanel : function(doShow){
34898 this.footerPanel.show();
34900 return this.footerPanel;
34903 initElements : function(){
34904 var E = Roo.Element;
34905 var el = this.grid.getGridEl().dom.firstChild;
34906 var cs = el.childNodes;
34908 this.el = new E(el);
34910 this.focusEl = new E(el.firstChild);
34911 this.focusEl.swallowEvent("click", true);
34913 this.headerPanel = new E(cs[1]);
34914 this.headerPanel.enableDisplayMode("block");
34916 this.scroller = new E(cs[2]);
34917 this.scrollSizer = new E(this.scroller.dom.firstChild);
34919 this.lockedWrap = new E(cs[3]);
34920 this.lockedHd = new E(this.lockedWrap.dom.firstChild);
34921 this.lockedBody = new E(this.lockedWrap.dom.childNodes[1]);
34923 this.mainWrap = new E(cs[4]);
34924 this.mainHd = new E(this.mainWrap.dom.firstChild);
34925 this.mainBody = new E(this.mainWrap.dom.childNodes[1]);
34927 this.footerPanel = new E(cs[5]);
34928 this.footerPanel.enableDisplayMode("block");
34930 this.resizeProxy = new E(cs[6]);
34932 this.headerSelector = String.format(
34933 '#{0} td.x-grid-hd, #{1} td.x-grid-hd',
34934 this.lockedHd.id, this.mainHd.id
34937 this.splitterSelector = String.format(
34938 '#{0} div.x-grid-split, #{1} div.x-grid-split',
34939 this.idToCssName(this.lockedHd.id), this.idToCssName(this.mainHd.id)
34942 idToCssName : function(s)
34944 return s.replace(/[^a-z0-9]+/ig, '-');
34947 getHeaderCell : function(index){
34948 return Roo.DomQuery.select(this.headerSelector)[index];
34951 getHeaderCellMeasure : function(index){
34952 return this.getHeaderCell(index).firstChild;
34955 getHeaderCellText : function(index){
34956 return this.getHeaderCell(index).firstChild.firstChild;
34959 getLockedTable : function(){
34960 return this.lockedBody.dom.firstChild;
34963 getBodyTable : function(){
34964 return this.mainBody.dom.firstChild;
34967 getLockedRow : function(index){
34968 return this.getLockedTable().rows[index];
34971 getRow : function(index){
34972 return this.getBodyTable().rows[index];
34975 getRowComposite : function(index){
34977 this.rowEl = new Roo.CompositeElementLite();
34979 var els = [], lrow, mrow;
34980 if(lrow = this.getLockedRow(index)){
34983 if(mrow = this.getRow(index)){
34986 this.rowEl.elements = els;
34990 * Gets the 'td' of the cell
34992 * @param {Integer} rowIndex row to select
34993 * @param {Integer} colIndex column to select
34997 getCell : function(rowIndex, colIndex){
34998 var locked = this.cm.getLockedCount();
35000 if(colIndex < locked){
35001 source = this.lockedBody.dom.firstChild;
35003 source = this.mainBody.dom.firstChild;
35004 colIndex -= locked;
35006 return source.rows[rowIndex].childNodes[colIndex];
35009 getCellText : function(rowIndex, colIndex){
35010 return this.getCell(rowIndex, colIndex).firstChild.firstChild;
35013 getCellBox : function(cell){
35014 var b = this.fly(cell).getBox();
35015 if(Roo.isOpera){ // opera fails to report the Y
35016 b.y = cell.offsetTop + this.mainBody.getY();
35021 getCellIndex : function(cell){
35022 var id = String(cell.className).match(this.cellRE);
35024 return parseInt(id[1], 10);
35029 findHeaderIndex : function(n){
35030 var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
35031 return r ? this.getCellIndex(r) : false;
35034 findHeaderCell : function(n){
35035 var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
35036 return r ? r : false;
35039 findRowIndex : function(n){
35043 var r = Roo.fly(n).findParent("tr." + this.rowClass, 6);
35044 return r ? r.rowIndex : false;
35047 findCellIndex : function(node){
35048 var stop = this.el.dom;
35049 while(node && node != stop){
35050 if(this.findRE.test(node.className)){
35051 return this.getCellIndex(node);
35053 node = node.parentNode;
35058 getColumnId : function(index){
35059 return this.cm.getColumnId(index);
35062 getSplitters : function()
35064 if(this.splitterSelector){
35065 return Roo.DomQuery.select(this.splitterSelector);
35071 getSplitter : function(index){
35072 return this.getSplitters()[index];
35075 onRowOver : function(e, t){
35077 if((row = this.findRowIndex(t)) !== false){
35078 this.getRowComposite(row).addClass("x-grid-row-over");
35082 onRowOut : function(e, t){
35084 if((row = this.findRowIndex(t)) !== false && row !== this.findRowIndex(e.getRelatedTarget())){
35085 this.getRowComposite(row).removeClass("x-grid-row-over");
35089 renderHeaders : function(){
35091 var ct = this.templates.hcell, ht = this.templates.header, st = this.templates.hsplit;
35092 var cb = [], lb = [], sb = [], lsb = [], p = {};
35093 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
35094 p.cellId = "x-grid-hd-0-" + i;
35095 p.splitId = "x-grid-csplit-0-" + i;
35096 p.id = cm.getColumnId(i);
35097 p.title = cm.getColumnTooltip(i) || "";
35098 p.value = cm.getColumnHeader(i) || "";
35099 p.style = (this.grid.enableColumnResize === false || !cm.isResizable(i) || cm.isFixed(i)) ? 'cursor:default' : '';
35100 if(!cm.isLocked(i)){
35101 cb[cb.length] = ct.apply(p);
35102 sb[sb.length] = st.apply(p);
35104 lb[lb.length] = ct.apply(p);
35105 lsb[lsb.length] = st.apply(p);
35108 return [ht.apply({cells: lb.join(""), splits:lsb.join("")}),
35109 ht.apply({cells: cb.join(""), splits:sb.join("")})];
35112 updateHeaders : function(){
35113 var html = this.renderHeaders();
35114 this.lockedHd.update(html[0]);
35115 this.mainHd.update(html[1]);
35119 * Focuses the specified row.
35120 * @param {Number} row The row index
35122 focusRow : function(row)
35124 //Roo.log('GridView.focusRow');
35125 var x = this.scroller.dom.scrollLeft;
35126 this.focusCell(row, 0, false);
35127 this.scroller.dom.scrollLeft = x;
35131 * Focuses the specified cell.
35132 * @param {Number} row The row index
35133 * @param {Number} col The column index
35134 * @param {Boolean} hscroll false to disable horizontal scrolling
35136 focusCell : function(row, col, hscroll)
35138 //Roo.log('GridView.focusCell');
35139 var el = this.ensureVisible(row, col, hscroll);
35140 this.focusEl.alignTo(el, "tl-tl");
35142 this.focusEl.focus();
35144 this.focusEl.focus.defer(1, this.focusEl);
35149 * Scrolls the specified cell into view
35150 * @param {Number} row The row index
35151 * @param {Number} col The column index
35152 * @param {Boolean} hscroll false to disable horizontal scrolling
35154 ensureVisible : function(row, col, hscroll)
35156 //Roo.log('GridView.ensureVisible,' + row + ',' + col);
35157 //return null; //disable for testing.
35158 if(typeof row != "number"){
35159 row = row.rowIndex;
35161 if(row < 0 && row >= this.ds.getCount()){
35164 col = (col !== undefined ? col : 0);
35165 var cm = this.grid.colModel;
35166 while(cm.isHidden(col)){
35170 var el = this.getCell(row, col);
35174 var c = this.scroller.dom;
35176 var ctop = parseInt(el.offsetTop, 10);
35177 var cleft = parseInt(el.offsetLeft, 10);
35178 var cbot = ctop + el.offsetHeight;
35179 var cright = cleft + el.offsetWidth;
35181 var ch = c.clientHeight - this.mainHd.dom.offsetHeight;
35182 var stop = parseInt(c.scrollTop, 10);
35183 var sleft = parseInt(c.scrollLeft, 10);
35184 var sbot = stop + ch;
35185 var sright = sleft + c.clientWidth;
35187 Roo.log('GridView.ensureVisible:' +
35189 ' c.clientHeight:' + c.clientHeight +
35190 ' this.mainHd.dom.offsetHeight:' + this.mainHd.dom.offsetHeight +
35198 c.scrollTop = ctop;
35199 //Roo.log("set scrolltop to ctop DISABLE?");
35200 }else if(cbot > sbot){
35201 //Roo.log("set scrolltop to cbot-ch");
35202 c.scrollTop = cbot-ch;
35205 if(hscroll !== false){
35207 c.scrollLeft = cleft;
35208 }else if(cright > sright){
35209 c.scrollLeft = cright-c.clientWidth;
35216 updateColumns : function(){
35217 this.grid.stopEditing();
35218 var cm = this.grid.colModel, colIds = this.getColumnIds();
35219 //var totalWidth = cm.getTotalWidth();
35221 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
35222 //if(cm.isHidden(i)) continue;
35223 var w = cm.getColumnWidth(i);
35224 this.css.updateRule(this.colSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
35225 this.css.updateRule(this.hdSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
35227 this.updateSplitters();
35230 generateRules : function(cm){
35231 var ruleBuf = [], rulesId = this.idToCssName(this.grid.id)+ '-cssrules';
35232 Roo.util.CSS.removeStyleSheet(rulesId);
35233 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
35234 var cid = cm.getColumnId(i);
35236 if(cm.config[i].align){
35237 align = 'text-align:'+cm.config[i].align+';';
35240 if(cm.isHidden(i)){
35241 hidden = 'display:none;';
35243 var width = "width:" + (cm.getColumnWidth(i) - this.borderWidth) + "px;";
35245 this.colSelector, cid, " {\n", cm.config[i].css, align, width, "\n}\n",
35246 this.hdSelector, cid, " {\n", align, width, "}\n",
35247 this.tdSelector, cid, " {\n",hidden,"\n}\n",
35248 this.splitSelector, cid, " {\n", hidden , "\n}\n");
35250 return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
35253 updateSplitters : function(){
35254 var cm = this.cm, s = this.getSplitters();
35255 if(s){ // splitters not created yet
35256 var pos = 0, locked = true;
35257 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
35258 if(cm.isHidden(i)) continue;
35259 var w = cm.getColumnWidth(i); // make sure it's a number
35260 if(!cm.isLocked(i) && locked){
35265 s[i].style.left = (pos-this.splitOffset) + "px";
35270 handleHiddenChange : function(colModel, colIndex, hidden){
35272 this.hideColumn(colIndex);
35274 this.unhideColumn(colIndex);
35278 hideColumn : function(colIndex){
35279 var cid = this.getColumnId(colIndex);
35280 this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "none");
35281 this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "none");
35283 this.updateHeaders();
35285 this.updateSplitters();
35289 unhideColumn : function(colIndex){
35290 var cid = this.getColumnId(colIndex);
35291 this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "");
35292 this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "");
35295 this.updateHeaders();
35297 this.updateSplitters();
35301 insertRows : function(dm, firstRow, lastRow, isUpdate){
35302 if(firstRow == 0 && lastRow == dm.getCount()-1){
35306 this.fireEvent("beforerowsinserted", this, firstRow, lastRow);
35308 var s = this.getScrollState();
35309 var markup = this.renderRows(firstRow, lastRow);
35310 this.bufferRows(markup[0], this.getLockedTable(), firstRow);
35311 this.bufferRows(markup[1], this.getBodyTable(), firstRow);
35312 this.restoreScroll(s);
35314 this.fireEvent("rowsinserted", this, firstRow, lastRow);
35315 this.syncRowHeights(firstRow, lastRow);
35316 this.stripeRows(firstRow);
35322 bufferRows : function(markup, target, index){
35323 var before = null, trows = target.rows, tbody = target.tBodies[0];
35324 if(index < trows.length){
35325 before = trows[index];
35327 var b = document.createElement("div");
35328 b.innerHTML = "<table><tbody>"+markup+"</tbody></table>";
35329 var rows = b.firstChild.rows;
35330 for(var i = 0, len = rows.length; i < len; i++){
35332 tbody.insertBefore(rows[0], before);
35334 tbody.appendChild(rows[0]);
35341 deleteRows : function(dm, firstRow, lastRow){
35342 if(dm.getRowCount()<1){
35343 this.fireEvent("beforerefresh", this);
35344 this.mainBody.update("");
35345 this.lockedBody.update("");
35346 this.fireEvent("refresh", this);
35348 this.fireEvent("beforerowsdeleted", this, firstRow, lastRow);
35349 var bt = this.getBodyTable();
35350 var tbody = bt.firstChild;
35351 var rows = bt.rows;
35352 for(var rowIndex = firstRow; rowIndex <= lastRow; rowIndex++){
35353 tbody.removeChild(rows[firstRow]);
35355 this.stripeRows(firstRow);
35356 this.fireEvent("rowsdeleted", this, firstRow, lastRow);
35360 updateRows : function(dataSource, firstRow, lastRow){
35361 var s = this.getScrollState();
35363 this.restoreScroll(s);
35366 handleSort : function(dataSource, sortColumnIndex, sortDir, noRefresh){
35370 this.updateHeaderSortState();
35373 getScrollState : function(){
35375 var sb = this.scroller.dom;
35376 return {left: sb.scrollLeft, top: sb.scrollTop};
35379 stripeRows : function(startRow){
35380 if(!this.grid.stripeRows || this.ds.getCount() < 1){
35383 startRow = startRow || 0;
35384 var rows = this.getBodyTable().rows;
35385 var lrows = this.getLockedTable().rows;
35386 var cls = ' x-grid-row-alt ';
35387 for(var i = startRow, len = rows.length; i < len; i++){
35388 var row = rows[i], lrow = lrows[i];
35389 var isAlt = ((i+1) % 2 == 0);
35390 var hasAlt = (' '+row.className + ' ').indexOf(cls) != -1;
35391 if(isAlt == hasAlt){
35395 row.className += " x-grid-row-alt";
35397 row.className = row.className.replace("x-grid-row-alt", "");
35400 lrow.className = row.className;
35405 restoreScroll : function(state){
35406 //Roo.log('GridView.restoreScroll');
35407 var sb = this.scroller.dom;
35408 sb.scrollLeft = state.left;
35409 sb.scrollTop = state.top;
35413 syncScroll : function(){
35414 //Roo.log('GridView.syncScroll');
35415 var sb = this.scroller.dom;
35416 var sh = this.mainHd.dom;
35417 var bs = this.mainBody.dom;
35418 var lv = this.lockedBody.dom;
35419 sh.scrollLeft = bs.scrollLeft = sb.scrollLeft;
35420 lv.scrollTop = bs.scrollTop = sb.scrollTop;
35423 handleScroll : function(e){
35425 var sb = this.scroller.dom;
35426 this.grid.fireEvent("bodyscroll", sb.scrollLeft, sb.scrollTop);
35430 handleWheel : function(e){
35431 var d = e.getWheelDelta();
35432 this.scroller.dom.scrollTop -= d*22;
35433 // set this here to prevent jumpy scrolling on large tables
35434 this.lockedBody.dom.scrollTop = this.mainBody.dom.scrollTop = this.scroller.dom.scrollTop;
35438 renderRows : function(startRow, endRow){
35439 // pull in all the crap needed to render rows
35440 var g = this.grid, cm = g.colModel, ds = g.dataSource, stripe = g.stripeRows;
35441 var colCount = cm.getColumnCount();
35443 if(ds.getCount() < 1){
35447 // build a map for all the columns
35449 for(var i = 0; i < colCount; i++){
35450 var name = cm.getDataIndex(i);
35452 name : typeof name == 'undefined' ? ds.fields.get(i).name : name,
35453 renderer : cm.getRenderer(i),
35454 id : cm.getColumnId(i),
35455 locked : cm.isLocked(i)
35459 startRow = startRow || 0;
35460 endRow = typeof endRow == "undefined"? ds.getCount()-1 : endRow;
35462 // records to render
35463 var rs = ds.getRange(startRow, endRow);
35465 return this.doRender(cs, rs, ds, startRow, colCount, stripe);
35468 // As much as I hate to duplicate code, this was branched because FireFox really hates
35469 // [].join("") on strings. The performance difference was substantial enough to
35470 // branch this function
35471 doRender : Roo.isGecko ?
35472 function(cs, rs, ds, startRow, colCount, stripe){
35473 var ts = this.templates, ct = ts.cell, rt = ts.row;
35475 var buf = "", lbuf = "", cb, lcb, c, p = {}, rp = {}, r, rowIndex;
35477 var hasListener = this.grid.hasListener('rowclass');
35479 for(var j = 0, len = rs.length; j < len; j++){
35480 r = rs[j]; cb = ""; lcb = ""; rowIndex = (j+startRow);
35481 for(var i = 0; i < colCount; i++){
35483 p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
35485 p.css = p.attr = "";
35486 p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
35487 if(p.value == undefined || p.value === "") p.value = " ";
35488 if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
35489 p.css += p.css ? ' x-grid-dirty-cell' : 'x-grid-dirty-cell';
35491 var markup = ct.apply(p);
35499 if(stripe && ((rowIndex+1) % 2 == 0)){
35500 alt.push("x-grid-row-alt")
35503 alt.push( " x-grid-dirty-row");
35506 if(this.getRowClass){
35507 alt.push(this.getRowClass(r, rowIndex));
35513 rowIndex : rowIndex,
35516 this.grid.fireEvent('rowclass', this, rowcfg);
35517 alt.push(rowcfg.rowClass);
35519 rp.alt = alt.join(" ");
35520 lbuf+= rt.apply(rp);
35522 buf+= rt.apply(rp);
35524 return [lbuf, buf];
35526 function(cs, rs, ds, startRow, colCount, stripe){
35527 var ts = this.templates, ct = ts.cell, rt = ts.row;
35529 var buf = [], lbuf = [], cb, lcb, c, p = {}, rp = {}, r, rowIndex;
35530 var hasListener = this.grid.hasListener('rowclass');
35533 for(var j = 0, len = rs.length; j < len; j++){
35534 r = rs[j]; cb = []; lcb = []; rowIndex = (j+startRow);
35535 for(var i = 0; i < colCount; i++){
35537 p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
35539 p.css = p.attr = "";
35540 p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
35541 if(p.value == undefined || p.value === "") p.value = " ";
35542 if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
35543 p.css += p.css ? ' x-grid-dirty-cell' : 'x-grid-dirty-cell';
35546 var markup = ct.apply(p);
35548 cb[cb.length] = markup;
35550 lcb[lcb.length] = markup;
35554 if(stripe && ((rowIndex+1) % 2 == 0)){
35555 alt.push( "x-grid-row-alt");
35558 alt.push(" x-grid-dirty-row");
35561 if(this.getRowClass){
35562 alt.push( this.getRowClass(r, rowIndex));
35568 rowIndex : rowIndex,
35571 this.grid.fireEvent('rowclass', this, rowcfg);
35572 alt.push(rowcfg.rowClass);
35574 rp.alt = alt.join(" ");
35575 rp.cells = lcb.join("");
35576 lbuf[lbuf.length] = rt.apply(rp);
35577 rp.cells = cb.join("");
35578 buf[buf.length] = rt.apply(rp);
35580 return [lbuf.join(""), buf.join("")];
35583 renderBody : function(){
35584 var markup = this.renderRows();
35585 var bt = this.templates.body;
35586 return [bt.apply({rows: markup[0]}), bt.apply({rows: markup[1]})];
35590 * Refreshes the grid
35591 * @param {Boolean} headersToo
35593 refresh : function(headersToo){
35594 this.fireEvent("beforerefresh", this);
35595 this.grid.stopEditing();
35596 var result = this.renderBody();
35597 this.lockedBody.update(result[0]);
35598 this.mainBody.update(result[1]);
35599 if(headersToo === true){
35600 this.updateHeaders();
35601 this.updateColumns();
35602 this.updateSplitters();
35603 this.updateHeaderSortState();
35605 this.syncRowHeights();
35607 this.fireEvent("refresh", this);
35610 handleColumnMove : function(cm, oldIndex, newIndex){
35611 this.indexMap = null;
35612 var s = this.getScrollState();
35613 this.refresh(true);
35614 this.restoreScroll(s);
35615 this.afterMove(newIndex);
35618 afterMove : function(colIndex){
35619 if(this.enableMoveAnim && Roo.enableFx){
35620 this.fly(this.getHeaderCell(colIndex).firstChild).highlight(this.hlColor);
35622 // if multisort - fix sortOrder, and reload..
35623 if (this.grid.dataSource.multiSort) {
35624 // the we can call sort again..
35625 var dm = this.grid.dataSource;
35626 var cm = this.grid.colModel;
35628 for(var i = 0; i < cm.config.length; i++ ) {
35630 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined')) {
35631 continue; // dont' bother, it's not in sort list or being set.
35634 so.push(cm.config[i].dataIndex);
35637 dm.load(dm.lastOptions);
35644 updateCell : function(dm, rowIndex, dataIndex){
35645 var colIndex = this.getColumnIndexByDataIndex(dataIndex);
35646 if(typeof colIndex == "undefined"){ // not present in grid
35649 var cm = this.grid.colModel;
35650 var cell = this.getCell(rowIndex, colIndex);
35651 var cellText = this.getCellText(rowIndex, colIndex);
35654 cellId : "x-grid-cell-" + rowIndex + "-" + colIndex,
35655 id : cm.getColumnId(colIndex),
35656 css: colIndex == cm.getColumnCount()-1 ? "x-grid-col-last" : ""
35658 var renderer = cm.getRenderer(colIndex);
35659 var val = renderer(dm.getValueAt(rowIndex, dataIndex), p, rowIndex, colIndex, dm);
35660 if(typeof val == "undefined" || val === "") val = " ";
35661 cellText.innerHTML = val;
35662 cell.className = this.cellClass + " " + this.idToCssName(p.cellId) + " " + p.css;
35663 this.syncRowHeights(rowIndex, rowIndex);
35666 calcColumnWidth : function(colIndex, maxRowsToMeasure){
35668 if(this.grid.autoSizeHeaders){
35669 var h = this.getHeaderCellMeasure(colIndex);
35670 maxWidth = Math.max(maxWidth, h.scrollWidth);
35673 if(this.cm.isLocked(colIndex)){
35674 tb = this.getLockedTable();
35677 tb = this.getBodyTable();
35678 index = colIndex - this.cm.getLockedCount();
35681 var rows = tb.rows;
35682 var stopIndex = Math.min(maxRowsToMeasure || rows.length, rows.length);
35683 for(var i = 0; i < stopIndex; i++){
35684 var cell = rows[i].childNodes[index].firstChild;
35685 maxWidth = Math.max(maxWidth, cell.scrollWidth);
35688 return maxWidth + /*margin for error in IE*/ 5;
35691 * Autofit a column to its content.
35692 * @param {Number} colIndex
35693 * @param {Boolean} forceMinSize true to force the column to go smaller if possible
35695 autoSizeColumn : function(colIndex, forceMinSize, suppressEvent){
35696 if(this.cm.isHidden(colIndex)){
35697 return; // can't calc a hidden column
35700 var cid = this.cm.getColumnId(colIndex);
35701 this.css.updateRule(this.colSelector +this.idToCssName( cid), "width", this.grid.minColumnWidth + "px");
35702 if(this.grid.autoSizeHeaders){
35703 this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", this.grid.minColumnWidth + "px");
35706 var newWidth = this.calcColumnWidth(colIndex);
35707 this.cm.setColumnWidth(colIndex,
35708 Math.max(this.grid.minColumnWidth, newWidth), suppressEvent);
35709 if(!suppressEvent){
35710 this.grid.fireEvent("columnresize", colIndex, newWidth);
35715 * Autofits all columns to their content and then expands to fit any extra space in the grid
35717 autoSizeColumns : function(){
35718 var cm = this.grid.colModel;
35719 var colCount = cm.getColumnCount();
35720 for(var i = 0; i < colCount; i++){
35721 this.autoSizeColumn(i, true, true);
35723 if(cm.getTotalWidth() < this.scroller.dom.clientWidth){
35726 this.updateColumns();
35732 * Autofits all columns to the grid's width proportionate with their current size
35733 * @param {Boolean} reserveScrollSpace Reserve space for a scrollbar
35735 fitColumns : function(reserveScrollSpace){
35736 var cm = this.grid.colModel;
35737 var colCount = cm.getColumnCount();
35741 for (i = 0; i < colCount; i++){
35742 if(!cm.isHidden(i) && !cm.isFixed(i)){
35743 w = cm.getColumnWidth(i);
35749 var avail = Math.min(this.scroller.dom.clientWidth, this.el.getWidth());
35750 if(reserveScrollSpace){
35753 var frac = (avail - cm.getTotalWidth())/width;
35754 while (cols.length){
35757 cm.setColumnWidth(i, Math.floor(w + w*frac), true);
35759 this.updateColumns();
35763 onRowSelect : function(rowIndex){
35764 var row = this.getRowComposite(rowIndex);
35765 row.addClass("x-grid-row-selected");
35768 onRowDeselect : function(rowIndex){
35769 var row = this.getRowComposite(rowIndex);
35770 row.removeClass("x-grid-row-selected");
35773 onCellSelect : function(row, col){
35774 var cell = this.getCell(row, col);
35776 Roo.fly(cell).addClass("x-grid-cell-selected");
35780 onCellDeselect : function(row, col){
35781 var cell = this.getCell(row, col);
35783 Roo.fly(cell).removeClass("x-grid-cell-selected");
35787 updateHeaderSortState : function(){
35789 // sort state can be single { field: xxx, direction : yyy}
35790 // or { xxx=>ASC , yyy : DESC ..... }
35793 if (!this.ds.multiSort) {
35794 var state = this.ds.getSortState();
35798 mstate[state.field] = state.direction;
35799 // FIXME... - this is not used here.. but might be elsewhere..
35800 this.sortState = state;
35803 mstate = this.ds.sortToggle;
35805 //remove existing sort classes..
35807 var sc = this.sortClasses;
35808 var hds = this.el.select(this.headerSelector).removeClass(sc);
35810 for(var f in mstate) {
35812 var sortColumn = this.cm.findColumnIndex(f);
35814 if(sortColumn != -1){
35815 var sortDir = mstate[f];
35816 hds.item(sortColumn).addClass(sc[sortDir == "DESC" ? 1 : 0]);
35825 handleHeaderClick : function(g, index){
35826 if(this.headersDisabled){
35829 var dm = g.dataSource, cm = g.colModel;
35830 if(!cm.isSortable(index)){
35835 if (dm.multiSort) {
35836 // update the sortOrder
35838 for(var i = 0; i < cm.config.length; i++ ) {
35840 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined') && (index != i)) {
35841 continue; // dont' bother, it's not in sort list or being set.
35844 so.push(cm.config[i].dataIndex);
35850 dm.sort(cm.getDataIndex(index));
35854 destroy : function(){
35856 this.colMenu.removeAll();
35857 Roo.menu.MenuMgr.unregister(this.colMenu);
35858 this.colMenu.getEl().remove();
35859 delete this.colMenu;
35862 this.hmenu.removeAll();
35863 Roo.menu.MenuMgr.unregister(this.hmenu);
35864 this.hmenu.getEl().remove();
35867 if(this.grid.enableColumnMove){
35868 var dds = Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
35870 for(var dd in dds){
35871 if(!dds[dd].config.isTarget && dds[dd].dragElId){
35872 var elid = dds[dd].dragElId;
35874 Roo.get(elid).remove();
35875 } else if(dds[dd].config.isTarget){
35876 dds[dd].proxyTop.remove();
35877 dds[dd].proxyBottom.remove();
35880 if(Roo.dd.DDM.locationCache[dd]){
35881 delete Roo.dd.DDM.locationCache[dd];
35884 delete Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
35887 Roo.util.CSS.removeStyleSheet(this.idToCssName(this.grid.id) + '-cssrules');
35888 this.bind(null, null);
35889 Roo.EventManager.removeResizeListener(this.onWindowResize, this);
35892 handleLockChange : function(){
35893 this.refresh(true);
35896 onDenyColumnLock : function(){
35900 onDenyColumnHide : function(){
35904 handleHdMenuClick : function(item){
35905 var index = this.hdCtxIndex;
35906 var cm = this.cm, ds = this.ds;
35909 ds.sort(cm.getDataIndex(index), "ASC");
35912 ds.sort(cm.getDataIndex(index), "DESC");
35915 var lc = cm.getLockedCount();
35916 if(cm.getColumnCount(true) <= lc+1){
35917 this.onDenyColumnLock();
35921 cm.setLocked(index, true, true);
35922 cm.moveColumn(index, lc);
35923 this.grid.fireEvent("columnmove", index, lc);
35925 cm.setLocked(index, true);
35929 var lc = cm.getLockedCount();
35930 if((lc-1) != index){
35931 cm.setLocked(index, false, true);
35932 cm.moveColumn(index, lc-1);
35933 this.grid.fireEvent("columnmove", index, lc-1);
35935 cm.setLocked(index, false);
35939 index = cm.getIndexById(item.id.substr(4));
35941 if(item.checked && cm.getColumnCount(true) <= 1){
35942 this.onDenyColumnHide();
35945 cm.setHidden(index, item.checked);
35951 beforeColMenuShow : function(){
35952 var cm = this.cm, colCount = cm.getColumnCount();
35953 this.colMenu.removeAll();
35954 for(var i = 0; i < colCount; i++){
35955 this.colMenu.add(new Roo.menu.CheckItem({
35956 id: "col-"+cm.getColumnId(i),
35957 text: cm.getColumnHeader(i),
35958 checked: !cm.isHidden(i),
35964 handleHdCtx : function(g, index, e){
35966 var hd = this.getHeaderCell(index);
35967 this.hdCtxIndex = index;
35968 var ms = this.hmenu.items, cm = this.cm;
35969 ms.get("asc").setDisabled(!cm.isSortable(index));
35970 ms.get("desc").setDisabled(!cm.isSortable(index));
35971 if(this.grid.enableColLock !== false){
35972 ms.get("lock").setDisabled(cm.isLocked(index));
35973 ms.get("unlock").setDisabled(!cm.isLocked(index));
35975 this.hmenu.show(hd, "tl-bl");
35978 handleHdOver : function(e){
35979 var hd = this.findHeaderCell(e.getTarget());
35980 if(hd && !this.headersDisabled){
35981 if(this.grid.colModel.isSortable(this.getCellIndex(hd))){
35982 this.fly(hd).addClass("x-grid-hd-over");
35987 handleHdOut : function(e){
35988 var hd = this.findHeaderCell(e.getTarget());
35990 this.fly(hd).removeClass("x-grid-hd-over");
35994 handleSplitDblClick : function(e, t){
35995 var i = this.getCellIndex(t);
35996 if(this.grid.enableColumnResize !== false && this.cm.isResizable(i) && !this.cm.isFixed(i)){
35997 this.autoSizeColumn(i, true);
36002 render : function(){
36005 var colCount = cm.getColumnCount();
36007 if(this.grid.monitorWindowResize === true){
36008 Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
36010 var header = this.renderHeaders();
36011 var body = this.templates.body.apply({rows:""});
36012 var html = this.templates.master.apply({
36015 lockedHeader: header[0],
36019 //this.updateColumns();
36021 this.grid.getGridEl().dom.innerHTML = html;
36023 this.initElements();
36025 // a kludge to fix the random scolling effect in webkit
36026 this.el.on("scroll", function() {
36027 this.el.dom.scrollTop=0; // hopefully not recursive..
36030 this.scroller.on("scroll", this.handleScroll, this);
36031 this.lockedBody.on("mousewheel", this.handleWheel, this);
36032 this.mainBody.on("mousewheel", this.handleWheel, this);
36034 this.mainHd.on("mouseover", this.handleHdOver, this);
36035 this.mainHd.on("mouseout", this.handleHdOut, this);
36036 this.mainHd.on("dblclick", this.handleSplitDblClick, this,
36037 {delegate: "."+this.splitClass});
36039 this.lockedHd.on("mouseover", this.handleHdOver, this);
36040 this.lockedHd.on("mouseout", this.handleHdOut, this);
36041 this.lockedHd.on("dblclick", this.handleSplitDblClick, this,
36042 {delegate: "."+this.splitClass});
36044 if(this.grid.enableColumnResize !== false && Roo.grid.SplitDragZone){
36045 new Roo.grid.SplitDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
36048 this.updateSplitters();
36050 if(this.grid.enableColumnMove && Roo.grid.HeaderDragZone){
36051 new Roo.grid.HeaderDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
36052 new Roo.grid.HeaderDropZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
36055 if(this.grid.enableCtxMenu !== false && Roo.menu.Menu){
36056 this.hmenu = new Roo.menu.Menu({id: this.grid.id + "-hctx"});
36058 {id:"asc", text: this.sortAscText, cls: "xg-hmenu-sort-asc"},
36059 {id:"desc", text: this.sortDescText, cls: "xg-hmenu-sort-desc"}
36061 if(this.grid.enableColLock !== false){
36062 this.hmenu.add('-',
36063 {id:"lock", text: this.lockText, cls: "xg-hmenu-lock"},
36064 {id:"unlock", text: this.unlockText, cls: "xg-hmenu-unlock"}
36067 if(this.grid.enableColumnHide !== false){
36069 this.colMenu = new Roo.menu.Menu({id:this.grid.id + "-hcols-menu"});
36070 this.colMenu.on("beforeshow", this.beforeColMenuShow, this);
36071 this.colMenu.on("itemclick", this.handleHdMenuClick, this);
36073 this.hmenu.add('-',
36074 {id:"columns", text: this.columnsText, menu: this.colMenu}
36077 this.hmenu.on("itemclick", this.handleHdMenuClick, this);
36079 this.grid.on("headercontextmenu", this.handleHdCtx, this);
36082 if((this.grid.enableDragDrop || this.grid.enableDrag) && Roo.grid.GridDragZone){
36083 this.dd = new Roo.grid.GridDragZone(this.grid, {
36084 ddGroup : this.grid.ddGroup || 'GridDD'
36089 for(var i = 0; i < colCount; i++){
36090 if(cm.isHidden(i)){
36091 this.hideColumn(i);
36093 if(cm.config[i].align){
36094 this.css.updateRule(this.colSelector + i, "textAlign", cm.config[i].align);
36095 this.css.updateRule(this.hdSelector + i, "textAlign", cm.config[i].align);
36099 this.updateHeaderSortState();
36101 this.beforeInitialResize();
36104 // two part rendering gives faster view to the user
36105 this.renderPhase2.defer(1, this);
36108 renderPhase2 : function(){
36109 // render the rows now
36111 if(this.grid.autoSizeColumns){
36112 this.autoSizeColumns();
36116 beforeInitialResize : function(){
36120 onColumnSplitterMoved : function(i, w){
36121 this.userResized = true;
36122 var cm = this.grid.colModel;
36123 cm.setColumnWidth(i, w, true);
36124 var cid = cm.getColumnId(i);
36125 this.css.updateRule(this.colSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
36126 this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
36127 this.updateSplitters();
36129 this.grid.fireEvent("columnresize", i, w);
36132 syncRowHeights : function(startIndex, endIndex){
36133 if(this.grid.enableRowHeightSync === true && this.cm.getLockedCount() > 0){
36134 startIndex = startIndex || 0;
36135 var mrows = this.getBodyTable().rows;
36136 var lrows = this.getLockedTable().rows;
36137 var len = mrows.length-1;
36138 endIndex = Math.min(endIndex || len, len);
36139 for(var i = startIndex; i <= endIndex; i++){
36140 var m = mrows[i], l = lrows[i];
36141 var h = Math.max(m.offsetHeight, l.offsetHeight);
36142 m.style.height = l.style.height = h + "px";
36147 layout : function(initialRender, is2ndPass){
36149 var auto = g.autoHeight;
36150 var scrollOffset = 16;
36151 var c = g.getGridEl(), cm = this.cm,
36152 expandCol = g.autoExpandColumn,
36154 //c.beginMeasure();
36156 if(!c.dom.offsetWidth){ // display:none?
36158 this.lockedWrap.show();
36159 this.mainWrap.show();
36164 var hasLock = this.cm.isLocked(0);
36166 var tbh = this.headerPanel.getHeight();
36167 var bbh = this.footerPanel.getHeight();
36170 var ch = this.getBodyTable().offsetHeight + tbh + bbh + this.mainHd.getHeight();
36171 var newHeight = ch + c.getBorderWidth("tb");
36173 newHeight = Math.min(g.maxHeight, newHeight);
36175 c.setHeight(newHeight);
36179 c.setWidth(cm.getTotalWidth()+c.getBorderWidth('lr'));
36182 var s = this.scroller;
36184 var csize = c.getSize(true);
36186 this.el.setSize(csize.width, csize.height);
36188 this.headerPanel.setWidth(csize.width);
36189 this.footerPanel.setWidth(csize.width);
36191 var hdHeight = this.mainHd.getHeight();
36192 var vw = csize.width;
36193 var vh = csize.height - (tbh + bbh);
36197 var bt = this.getBodyTable();
36198 var ltWidth = hasLock ?
36199 Math.max(this.getLockedTable().offsetWidth, this.lockedHd.dom.firstChild.offsetWidth) : 0;
36201 var scrollHeight = bt.offsetHeight;
36202 var scrollWidth = ltWidth + bt.offsetWidth;
36203 var vscroll = false, hscroll = false;
36205 this.scrollSizer.setSize(scrollWidth, scrollHeight+hdHeight);
36207 var lw = this.lockedWrap, mw = this.mainWrap;
36208 var lb = this.lockedBody, mb = this.mainBody;
36210 setTimeout(function(){
36211 var t = s.dom.offsetTop;
36212 var w = s.dom.clientWidth,
36213 h = s.dom.clientHeight;
36216 lw.setSize(ltWidth, h);
36218 mw.setLeftTop(ltWidth, t);
36219 mw.setSize(w-ltWidth, h);
36221 lb.setHeight(h-hdHeight);
36222 mb.setHeight(h-hdHeight);
36224 if(is2ndPass !== true && !gv.userResized && expandCol){
36225 // high speed resize without full column calculation
36227 var ci = cm.getIndexById(expandCol);
36229 ci = cm.findColumnIndex(expandCol);
36231 ci = Math.max(0, ci); // make sure it's got at least the first col.
36232 var expandId = cm.getColumnId(ci);
36233 var tw = cm.getTotalWidth(false);
36234 var currentWidth = cm.getColumnWidth(ci);
36235 var cw = Math.min(Math.max(((w-tw)+currentWidth-2)-/*scrollbar*/(w <= s.dom.offsetWidth ? 0 : 18), g.autoExpandMin), g.autoExpandMax);
36236 if(currentWidth != cw){
36237 cm.setColumnWidth(ci, cw, true);
36238 gv.css.updateRule(gv.colSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
36239 gv.css.updateRule(gv.hdSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
36240 gv.updateSplitters();
36241 gv.layout(false, true);
36253 onWindowResize : function(){
36254 if(!this.grid.monitorWindowResize || this.grid.autoHeight){
36260 appendFooter : function(parentEl){
36264 sortAscText : "Sort Ascending",
36265 sortDescText : "Sort Descending",
36266 lockText : "Lock Column",
36267 unlockText : "Unlock Column",
36268 columnsText : "Columns"
36272 Roo.grid.GridView.ColumnDragZone = function(grid, hd){
36273 Roo.grid.GridView.ColumnDragZone.superclass.constructor.call(this, grid, hd, null);
36274 this.proxy.el.addClass('x-grid3-col-dd');
36277 Roo.extend(Roo.grid.GridView.ColumnDragZone, Roo.grid.HeaderDragZone, {
36278 handleMouseDown : function(e){
36282 callHandleMouseDown : function(e){
36283 Roo.grid.GridView.ColumnDragZone.superclass.handleMouseDown.call(this, e);
36288 * Ext JS Library 1.1.1
36289 * Copyright(c) 2006-2007, Ext JS, LLC.
36291 * Originally Released Under LGPL - original licence link has changed is not relivant.
36294 * <script type="text/javascript">
36298 // This is a support class used internally by the Grid components
36299 Roo.grid.SplitDragZone = function(grid, hd, hd2){
36301 this.view = grid.getView();
36302 this.proxy = this.view.resizeProxy;
36303 Roo.grid.SplitDragZone.superclass.constructor.call(this, hd,
36304 "gridSplitters" + this.grid.getGridEl().id, {
36305 dragElId : Roo.id(this.proxy.dom), resizeFrame:false
36307 this.setHandleElId(Roo.id(hd));
36308 this.setOuterHandleElId(Roo.id(hd2));
36309 this.scroll = false;
36311 Roo.extend(Roo.grid.SplitDragZone, Roo.dd.DDProxy, {
36312 fly: Roo.Element.fly,
36314 b4StartDrag : function(x, y){
36315 this.view.headersDisabled = true;
36316 this.proxy.setHeight(this.view.mainWrap.getHeight());
36317 var w = this.cm.getColumnWidth(this.cellIndex);
36318 var minw = Math.max(w-this.grid.minColumnWidth, 0);
36319 this.resetConstraints();
36320 this.setXConstraint(minw, 1000);
36321 this.setYConstraint(0, 0);
36322 this.minX = x - minw;
36323 this.maxX = x + 1000;
36325 Roo.dd.DDProxy.prototype.b4StartDrag.call(this, x, y);
36329 handleMouseDown : function(e){
36330 ev = Roo.EventObject.setEvent(e);
36331 var t = this.fly(ev.getTarget());
36332 if(t.hasClass("x-grid-split")){
36333 this.cellIndex = this.view.getCellIndex(t.dom);
36334 this.split = t.dom;
36335 this.cm = this.grid.colModel;
36336 if(this.cm.isResizable(this.cellIndex) && !this.cm.isFixed(this.cellIndex)){
36337 Roo.grid.SplitDragZone.superclass.handleMouseDown.apply(this, arguments);
36342 endDrag : function(e){
36343 this.view.headersDisabled = false;
36344 var endX = Math.max(this.minX, Roo.lib.Event.getPageX(e));
36345 var diff = endX - this.startPos;
36346 this.view.onColumnSplitterMoved(this.cellIndex, this.cm.getColumnWidth(this.cellIndex)+diff);
36349 autoOffset : function(){
36350 this.setDelta(0,0);
36354 * Ext JS Library 1.1.1
36355 * Copyright(c) 2006-2007, Ext JS, LLC.
36357 * Originally Released Under LGPL - original licence link has changed is not relivant.
36360 * <script type="text/javascript">
36364 // This is a support class used internally by the Grid components
36365 Roo.grid.GridDragZone = function(grid, config){
36366 this.view = grid.getView();
36367 Roo.grid.GridDragZone.superclass.constructor.call(this, this.view.mainBody.dom, config);
36368 if(this.view.lockedBody){
36369 this.setHandleElId(Roo.id(this.view.mainBody.dom));
36370 this.setOuterHandleElId(Roo.id(this.view.lockedBody.dom));
36372 this.scroll = false;
36374 this.ddel = document.createElement('div');
36375 this.ddel.className = 'x-grid-dd-wrap';
36378 Roo.extend(Roo.grid.GridDragZone, Roo.dd.DragZone, {
36379 ddGroup : "GridDD",
36381 getDragData : function(e){
36382 var t = Roo.lib.Event.getTarget(e);
36383 var rowIndex = this.view.findRowIndex(t);
36384 if(rowIndex !== false){
36385 var sm = this.grid.selModel;
36386 //if(!sm.isSelected(rowIndex) || e.hasModifier()){
36387 // sm.mouseDown(e, t);
36389 if (e.hasModifier()){
36390 sm.handleMouseDown(e, t); // non modifier buttons are handled by row select.
36392 return {grid: this.grid, ddel: this.ddel, rowIndex: rowIndex, selections:sm.getSelections()};
36397 onInitDrag : function(e){
36398 var data = this.dragData;
36399 this.ddel.innerHTML = this.grid.getDragDropText();
36400 this.proxy.update(this.ddel);
36401 // fire start drag?
36404 afterRepair : function(){
36405 this.dragging = false;
36408 getRepairXY : function(e, data){
36412 onEndDrag : function(data, e){
36416 onValidDrop : function(dd, e, id){
36421 beforeInvalidDrop : function(e, id){
36426 * Ext JS Library 1.1.1
36427 * Copyright(c) 2006-2007, Ext JS, LLC.
36429 * Originally Released Under LGPL - original licence link has changed is not relivant.
36432 * <script type="text/javascript">
36437 * @class Roo.grid.ColumnModel
36438 * @extends Roo.util.Observable
36439 * This is the default implementation of a ColumnModel used by the Grid. It defines
36440 * the columns in the grid.
36443 var colModel = new Roo.grid.ColumnModel([
36444 {header: "Ticker", width: 60, sortable: true, locked: true},
36445 {header: "Company Name", width: 150, sortable: true},
36446 {header: "Market Cap.", width: 100, sortable: true},
36447 {header: "$ Sales", width: 100, sortable: true, renderer: money},
36448 {header: "Employees", width: 100, sortable: true, resizable: false}
36453 * The config options listed for this class are options which may appear in each
36454 * individual column definition.
36455 * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
36457 * @param {Object} config An Array of column config objects. See this class's
36458 * config objects for details.
36460 Roo.grid.ColumnModel = function(config){
36462 * The config passed into the constructor
36464 this.config = config;
36467 // if no id, create one
36468 // if the column does not have a dataIndex mapping,
36469 // map it to the order it is in the config
36470 for(var i = 0, len = config.length; i < len; i++){
36472 if(typeof c.dataIndex == "undefined"){
36475 if(typeof c.renderer == "string"){
36476 c.renderer = Roo.util.Format[c.renderer];
36478 if(typeof c.id == "undefined"){
36481 if(c.editor && c.editor.xtype){
36482 c.editor = Roo.factory(c.editor, Roo.grid);
36484 if(c.editor && c.editor.isFormField){
36485 c.editor = new Roo.grid.GridEditor(c.editor);
36487 this.lookup[c.id] = c;
36491 * The width of columns which have no width specified (defaults to 100)
36494 this.defaultWidth = 100;
36497 * Default sortable of columns which have no sortable specified (defaults to false)
36500 this.defaultSortable = false;
36504 * @event widthchange
36505 * Fires when the width of a column changes.
36506 * @param {ColumnModel} this
36507 * @param {Number} columnIndex The column index
36508 * @param {Number} newWidth The new width
36510 "widthchange": true,
36512 * @event headerchange
36513 * Fires when the text of a header changes.
36514 * @param {ColumnModel} this
36515 * @param {Number} columnIndex The column index
36516 * @param {Number} newText The new header text
36518 "headerchange": true,
36520 * @event hiddenchange
36521 * Fires when a column is hidden or "unhidden".
36522 * @param {ColumnModel} this
36523 * @param {Number} columnIndex The column index
36524 * @param {Boolean} hidden true if hidden, false otherwise
36526 "hiddenchange": true,
36528 * @event columnmoved
36529 * Fires when a column is moved.
36530 * @param {ColumnModel} this
36531 * @param {Number} oldIndex
36532 * @param {Number} newIndex
36534 "columnmoved" : true,
36536 * @event columlockchange
36537 * Fires when a column's locked state is changed
36538 * @param {ColumnModel} this
36539 * @param {Number} colIndex
36540 * @param {Boolean} locked true if locked
36542 "columnlockchange" : true
36544 Roo.grid.ColumnModel.superclass.constructor.call(this);
36546 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
36548 * @cfg {String} header The header text to display in the Grid view.
36551 * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
36552 * {@link Roo.data.Record} definition from which to draw the column's value. If not
36553 * specified, the column's index is used as an index into the Record's data Array.
36556 * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
36557 * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
36560 * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
36561 * Defaults to the value of the {@link #defaultSortable} property.
36562 * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
36565 * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid. Defaults to false.
36568 * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed. Defaults to false.
36571 * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
36574 * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
36577 * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
36578 * given the cell's data value. See {@link #setRenderer}. If not specified, the
36579 * default renderer uses the raw data value.
36582 * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor
36585 * @cfg {String} align (Optional) Set the CSS text-align property of the column. Defaults to undefined.
36589 * Returns the id of the column at the specified index.
36590 * @param {Number} index The column index
36591 * @return {String} the id
36593 getColumnId : function(index){
36594 return this.config[index].id;
36598 * Returns the column for a specified id.
36599 * @param {String} id The column id
36600 * @return {Object} the column
36602 getColumnById : function(id){
36603 return this.lookup[id];
36608 * Returns the column for a specified dataIndex.
36609 * @param {String} dataIndex The column dataIndex
36610 * @return {Object|Boolean} the column or false if not found
36612 getColumnByDataIndex: function(dataIndex){
36613 var index = this.findColumnIndex(dataIndex);
36614 return index > -1 ? this.config[index] : false;
36618 * Returns the index for a specified column id.
36619 * @param {String} id The column id
36620 * @return {Number} the index, or -1 if not found
36622 getIndexById : function(id){
36623 for(var i = 0, len = this.config.length; i < len; i++){
36624 if(this.config[i].id == id){
36632 * Returns the index for a specified column dataIndex.
36633 * @param {String} dataIndex The column dataIndex
36634 * @return {Number} the index, or -1 if not found
36637 findColumnIndex : function(dataIndex){
36638 for(var i = 0, len = this.config.length; i < len; i++){
36639 if(this.config[i].dataIndex == dataIndex){
36647 moveColumn : function(oldIndex, newIndex){
36648 var c = this.config[oldIndex];
36649 this.config.splice(oldIndex, 1);
36650 this.config.splice(newIndex, 0, c);
36651 this.dataMap = null;
36652 this.fireEvent("columnmoved", this, oldIndex, newIndex);
36655 isLocked : function(colIndex){
36656 return this.config[colIndex].locked === true;
36659 setLocked : function(colIndex, value, suppressEvent){
36660 if(this.isLocked(colIndex) == value){
36663 this.config[colIndex].locked = value;
36664 if(!suppressEvent){
36665 this.fireEvent("columnlockchange", this, colIndex, value);
36669 getTotalLockedWidth : function(){
36670 var totalWidth = 0;
36671 for(var i = 0; i < this.config.length; i++){
36672 if(this.isLocked(i) && !this.isHidden(i)){
36673 this.totalWidth += this.getColumnWidth(i);
36679 getLockedCount : function(){
36680 for(var i = 0, len = this.config.length; i < len; i++){
36681 if(!this.isLocked(i)){
36688 * Returns the number of columns.
36691 getColumnCount : function(visibleOnly){
36692 if(visibleOnly === true){
36694 for(var i = 0, len = this.config.length; i < len; i++){
36695 if(!this.isHidden(i)){
36701 return this.config.length;
36705 * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
36706 * @param {Function} fn
36707 * @param {Object} scope (optional)
36708 * @return {Array} result
36710 getColumnsBy : function(fn, scope){
36712 for(var i = 0, len = this.config.length; i < len; i++){
36713 var c = this.config[i];
36714 if(fn.call(scope||this, c, i) === true){
36722 * Returns true if the specified column is sortable.
36723 * @param {Number} col The column index
36724 * @return {Boolean}
36726 isSortable : function(col){
36727 if(typeof this.config[col].sortable == "undefined"){
36728 return this.defaultSortable;
36730 return this.config[col].sortable;
36734 * Returns the rendering (formatting) function defined for the column.
36735 * @param {Number} col The column index.
36736 * @return {Function} The function used to render the cell. See {@link #setRenderer}.
36738 getRenderer : function(col){
36739 if(!this.config[col].renderer){
36740 return Roo.grid.ColumnModel.defaultRenderer;
36742 return this.config[col].renderer;
36746 * Sets the rendering (formatting) function for a column.
36747 * @param {Number} col The column index
36748 * @param {Function} fn The function to use to process the cell's raw data
36749 * to return HTML markup for the grid view. The render function is called with
36750 * the following parameters:<ul>
36751 * <li>Data value.</li>
36752 * <li>Cell metadata. An object in which you may set the following attributes:<ul>
36753 * <li>css A CSS style string to apply to the table cell.</li>
36754 * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
36755 * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
36756 * <li>Row index</li>
36757 * <li>Column index</li>
36758 * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
36760 setRenderer : function(col, fn){
36761 this.config[col].renderer = fn;
36765 * Returns the width for the specified column.
36766 * @param {Number} col The column index
36769 getColumnWidth : function(col){
36770 return this.config[col].width * 1 || this.defaultWidth;
36774 * Sets the width for a column.
36775 * @param {Number} col The column index
36776 * @param {Number} width The new width
36778 setColumnWidth : function(col, width, suppressEvent){
36779 this.config[col].width = width;
36780 this.totalWidth = null;
36781 if(!suppressEvent){
36782 this.fireEvent("widthchange", this, col, width);
36787 * Returns the total width of all columns.
36788 * @param {Boolean} includeHidden True to include hidden column widths
36791 getTotalWidth : function(includeHidden){
36792 if(!this.totalWidth){
36793 this.totalWidth = 0;
36794 for(var i = 0, len = this.config.length; i < len; i++){
36795 if(includeHidden || !this.isHidden(i)){
36796 this.totalWidth += this.getColumnWidth(i);
36800 return this.totalWidth;
36804 * Returns the header for the specified column.
36805 * @param {Number} col The column index
36808 getColumnHeader : function(col){
36809 return this.config[col].header;
36813 * Sets the header for a column.
36814 * @param {Number} col The column index
36815 * @param {String} header The new header
36817 setColumnHeader : function(col, header){
36818 this.config[col].header = header;
36819 this.fireEvent("headerchange", this, col, header);
36823 * Returns the tooltip for the specified column.
36824 * @param {Number} col The column index
36827 getColumnTooltip : function(col){
36828 return this.config[col].tooltip;
36831 * Sets the tooltip for a column.
36832 * @param {Number} col The column index
36833 * @param {String} tooltip The new tooltip
36835 setColumnTooltip : function(col, tooltip){
36836 this.config[col].tooltip = tooltip;
36840 * Returns the dataIndex for the specified column.
36841 * @param {Number} col The column index
36844 getDataIndex : function(col){
36845 return this.config[col].dataIndex;
36849 * Sets the dataIndex for a column.
36850 * @param {Number} col The column index
36851 * @param {Number} dataIndex The new dataIndex
36853 setDataIndex : function(col, dataIndex){
36854 this.config[col].dataIndex = dataIndex;
36860 * Returns true if the cell is editable.
36861 * @param {Number} colIndex The column index
36862 * @param {Number} rowIndex The row index
36863 * @return {Boolean}
36865 isCellEditable : function(colIndex, rowIndex){
36866 return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
36870 * Returns the editor defined for the cell/column.
36871 * return false or null to disable editing.
36872 * @param {Number} colIndex The column index
36873 * @param {Number} rowIndex The row index
36876 getCellEditor : function(colIndex, rowIndex){
36877 return this.config[colIndex].editor;
36881 * Sets if a column is editable.
36882 * @param {Number} col The column index
36883 * @param {Boolean} editable True if the column is editable
36885 setEditable : function(col, editable){
36886 this.config[col].editable = editable;
36891 * Returns true if the column is hidden.
36892 * @param {Number} colIndex The column index
36893 * @return {Boolean}
36895 isHidden : function(colIndex){
36896 return this.config[colIndex].hidden;
36901 * Returns true if the column width cannot be changed
36903 isFixed : function(colIndex){
36904 return this.config[colIndex].fixed;
36908 * Returns true if the column can be resized
36909 * @return {Boolean}
36911 isResizable : function(colIndex){
36912 return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
36915 * Sets if a column is hidden.
36916 * @param {Number} colIndex The column index
36917 * @param {Boolean} hidden True if the column is hidden
36919 setHidden : function(colIndex, hidden){
36920 this.config[colIndex].hidden = hidden;
36921 this.totalWidth = null;
36922 this.fireEvent("hiddenchange", this, colIndex, hidden);
36926 * Sets the editor for a column.
36927 * @param {Number} col The column index
36928 * @param {Object} editor The editor object
36930 setEditor : function(col, editor){
36931 this.config[col].editor = editor;
36935 Roo.grid.ColumnModel.defaultRenderer = function(value){
36936 if(typeof value == "string" && value.length < 1){
36942 // Alias for backwards compatibility
36943 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
36946 * Ext JS Library 1.1.1
36947 * Copyright(c) 2006-2007, Ext JS, LLC.
36949 * Originally Released Under LGPL - original licence link has changed is not relivant.
36952 * <script type="text/javascript">
36956 * @class Roo.grid.AbstractSelectionModel
36957 * @extends Roo.util.Observable
36958 * Abstract base class for grid SelectionModels. It provides the interface that should be
36959 * implemented by descendant classes. This class should not be directly instantiated.
36962 Roo.grid.AbstractSelectionModel = function(){
36963 this.locked = false;
36964 Roo.grid.AbstractSelectionModel.superclass.constructor.call(this);
36967 Roo.extend(Roo.grid.AbstractSelectionModel, Roo.util.Observable, {
36968 /** @ignore Called by the grid automatically. Do not call directly. */
36969 init : function(grid){
36975 * Locks the selections.
36978 this.locked = true;
36982 * Unlocks the selections.
36984 unlock : function(){
36985 this.locked = false;
36989 * Returns true if the selections are locked.
36990 * @return {Boolean}
36992 isLocked : function(){
36993 return this.locked;
36997 * Ext JS Library 1.1.1
36998 * Copyright(c) 2006-2007, Ext JS, LLC.
37000 * Originally Released Under LGPL - original licence link has changed is not relivant.
37003 * <script type="text/javascript">
37006 * @extends Roo.grid.AbstractSelectionModel
37007 * @class Roo.grid.RowSelectionModel
37008 * The default SelectionModel used by {@link Roo.grid.Grid}.
37009 * It supports multiple selections and keyboard selection/navigation.
37011 * @param {Object} config
37013 Roo.grid.RowSelectionModel = function(config){
37014 Roo.apply(this, config);
37015 this.selections = new Roo.util.MixedCollection(false, function(o){
37020 this.lastActive = false;
37024 * @event selectionchange
37025 * Fires when the selection changes
37026 * @param {SelectionModel} this
37028 "selectionchange" : true,
37030 * @event afterselectionchange
37031 * Fires after the selection changes (eg. by key press or clicking)
37032 * @param {SelectionModel} this
37034 "afterselectionchange" : true,
37036 * @event beforerowselect
37037 * Fires when a row is selected being selected, return false to cancel.
37038 * @param {SelectionModel} this
37039 * @param {Number} rowIndex The selected index
37040 * @param {Boolean} keepExisting False if other selections will be cleared
37042 "beforerowselect" : true,
37045 * Fires when a row is selected.
37046 * @param {SelectionModel} this
37047 * @param {Number} rowIndex The selected index
37048 * @param {Roo.data.Record} r The record
37050 "rowselect" : true,
37052 * @event rowdeselect
37053 * Fires when a row is deselected.
37054 * @param {SelectionModel} this
37055 * @param {Number} rowIndex The selected index
37057 "rowdeselect" : true
37059 Roo.grid.RowSelectionModel.superclass.constructor.call(this);
37060 this.locked = false;
37063 Roo.extend(Roo.grid.RowSelectionModel, Roo.grid.AbstractSelectionModel, {
37065 * @cfg {Boolean} singleSelect
37066 * True to allow selection of only one row at a time (defaults to false)
37068 singleSelect : false,
37071 initEvents : function(){
37073 if(!this.grid.enableDragDrop && !this.grid.enableDrag){
37074 this.grid.on("mousedown", this.handleMouseDown, this);
37075 }else{ // allow click to work like normal
37076 this.grid.on("rowclick", this.handleDragableRowClick, this);
37079 this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
37080 "up" : function(e){
37082 this.selectPrevious(e.shiftKey);
37083 }else if(this.last !== false && this.lastActive !== false){
37084 var last = this.last;
37085 this.selectRange(this.last, this.lastActive-1);
37086 this.grid.getView().focusRow(this.lastActive);
37087 if(last !== false){
37091 this.selectFirstRow();
37093 this.fireEvent("afterselectionchange", this);
37095 "down" : function(e){
37097 this.selectNext(e.shiftKey);
37098 }else if(this.last !== false && this.lastActive !== false){
37099 var last = this.last;
37100 this.selectRange(this.last, this.lastActive+1);
37101 this.grid.getView().focusRow(this.lastActive);
37102 if(last !== false){
37106 this.selectFirstRow();
37108 this.fireEvent("afterselectionchange", this);
37113 var view = this.grid.view;
37114 view.on("refresh", this.onRefresh, this);
37115 view.on("rowupdated", this.onRowUpdated, this);
37116 view.on("rowremoved", this.onRemove, this);
37120 onRefresh : function(){
37121 var ds = this.grid.dataSource, i, v = this.grid.view;
37122 var s = this.selections;
37123 s.each(function(r){
37124 if((i = ds.indexOfId(r.id)) != -1){
37133 onRemove : function(v, index, r){
37134 this.selections.remove(r);
37138 onRowUpdated : function(v, index, r){
37139 if(this.isSelected(r)){
37140 v.onRowSelect(index);
37146 * @param {Array} records The records to select
37147 * @param {Boolean} keepExisting (optional) True to keep existing selections
37149 selectRecords : function(records, keepExisting){
37151 this.clearSelections();
37153 var ds = this.grid.dataSource;
37154 for(var i = 0, len = records.length; i < len; i++){
37155 this.selectRow(ds.indexOf(records[i]), true);
37160 * Gets the number of selected rows.
37163 getCount : function(){
37164 return this.selections.length;
37168 * Selects the first row in the grid.
37170 selectFirstRow : function(){
37175 * Select the last row.
37176 * @param {Boolean} keepExisting (optional) True to keep existing selections
37178 selectLastRow : function(keepExisting){
37179 this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
37183 * Selects the row immediately following the last selected row.
37184 * @param {Boolean} keepExisting (optional) True to keep existing selections
37186 selectNext : function(keepExisting){
37187 if(this.last !== false && (this.last+1) < this.grid.dataSource.getCount()){
37188 this.selectRow(this.last+1, keepExisting);
37189 this.grid.getView().focusRow(this.last);
37194 * Selects the row that precedes the last selected row.
37195 * @param {Boolean} keepExisting (optional) True to keep existing selections
37197 selectPrevious : function(keepExisting){
37199 this.selectRow(this.last-1, keepExisting);
37200 this.grid.getView().focusRow(this.last);
37205 * Returns the selected records
37206 * @return {Array} Array of selected records
37208 getSelections : function(){
37209 return [].concat(this.selections.items);
37213 * Returns the first selected record.
37216 getSelected : function(){
37217 return this.selections.itemAt(0);
37222 * Clears all selections.
37224 clearSelections : function(fast){
37225 if(this.locked) return;
37227 var ds = this.grid.dataSource;
37228 var s = this.selections;
37229 s.each(function(r){
37230 this.deselectRow(ds.indexOfId(r.id));
37234 this.selections.clear();
37241 * Selects all rows.
37243 selectAll : function(){
37244 if(this.locked) return;
37245 this.selections.clear();
37246 for(var i = 0, len = this.grid.dataSource.getCount(); i < len; i++){
37247 this.selectRow(i, true);
37252 * Returns True if there is a selection.
37253 * @return {Boolean}
37255 hasSelection : function(){
37256 return this.selections.length > 0;
37260 * Returns True if the specified row is selected.
37261 * @param {Number/Record} record The record or index of the record to check
37262 * @return {Boolean}
37264 isSelected : function(index){
37265 var r = typeof index == "number" ? this.grid.dataSource.getAt(index) : index;
37266 return (r && this.selections.key(r.id) ? true : false);
37270 * Returns True if the specified record id is selected.
37271 * @param {String} id The id of record to check
37272 * @return {Boolean}
37274 isIdSelected : function(id){
37275 return (this.selections.key(id) ? true : false);
37279 handleMouseDown : function(e, t){
37280 var view = this.grid.getView(), rowIndex;
37281 if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
37284 if(e.shiftKey && this.last !== false){
37285 var last = this.last;
37286 this.selectRange(last, rowIndex, e.ctrlKey);
37287 this.last = last; // reset the last
37288 view.focusRow(rowIndex);
37290 var isSelected = this.isSelected(rowIndex);
37291 if(e.button !== 0 && isSelected){
37292 view.focusRow(rowIndex);
37293 }else if(e.ctrlKey && isSelected){
37294 this.deselectRow(rowIndex);
37295 }else if(!isSelected){
37296 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
37297 view.focusRow(rowIndex);
37300 this.fireEvent("afterselectionchange", this);
37303 handleDragableRowClick : function(grid, rowIndex, e)
37305 if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
37306 this.selectRow(rowIndex, false);
37307 grid.view.focusRow(rowIndex);
37308 this.fireEvent("afterselectionchange", this);
37313 * Selects multiple rows.
37314 * @param {Array} rows Array of the indexes of the row to select
37315 * @param {Boolean} keepExisting (optional) True to keep existing selections
37317 selectRows : function(rows, keepExisting){
37319 this.clearSelections();
37321 for(var i = 0, len = rows.length; i < len; i++){
37322 this.selectRow(rows[i], true);
37327 * Selects a range of rows. All rows in between startRow and endRow are also selected.
37328 * @param {Number} startRow The index of the first row in the range
37329 * @param {Number} endRow The index of the last row in the range
37330 * @param {Boolean} keepExisting (optional) True to retain existing selections
37332 selectRange : function(startRow, endRow, keepExisting){
37333 if(this.locked) return;
37335 this.clearSelections();
37337 if(startRow <= endRow){
37338 for(var i = startRow; i <= endRow; i++){
37339 this.selectRow(i, true);
37342 for(var i = startRow; i >= endRow; i--){
37343 this.selectRow(i, true);
37349 * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
37350 * @param {Number} startRow The index of the first row in the range
37351 * @param {Number} endRow The index of the last row in the range
37353 deselectRange : function(startRow, endRow, preventViewNotify){
37354 if(this.locked) return;
37355 for(var i = startRow; i <= endRow; i++){
37356 this.deselectRow(i, preventViewNotify);
37362 * @param {Number} row The index of the row to select
37363 * @param {Boolean} keepExisting (optional) True to keep existing selections
37365 selectRow : function(index, keepExisting, preventViewNotify){
37366 if(this.locked || (index < 0 || index >= this.grid.dataSource.getCount())) return;
37367 if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
37368 if(!keepExisting || this.singleSelect){
37369 this.clearSelections();
37371 var r = this.grid.dataSource.getAt(index);
37372 this.selections.add(r);
37373 this.last = this.lastActive = index;
37374 if(!preventViewNotify){
37375 this.grid.getView().onRowSelect(index);
37377 this.fireEvent("rowselect", this, index, r);
37378 this.fireEvent("selectionchange", this);
37384 * @param {Number} row The index of the row to deselect
37386 deselectRow : function(index, preventViewNotify){
37387 if(this.locked) return;
37388 if(this.last == index){
37391 if(this.lastActive == index){
37392 this.lastActive = false;
37394 var r = this.grid.dataSource.getAt(index);
37395 this.selections.remove(r);
37396 if(!preventViewNotify){
37397 this.grid.getView().onRowDeselect(index);
37399 this.fireEvent("rowdeselect", this, index);
37400 this.fireEvent("selectionchange", this);
37404 restoreLast : function(){
37406 this.last = this._last;
37411 acceptsNav : function(row, col, cm){
37412 return !cm.isHidden(col) && cm.isCellEditable(col, row);
37416 onEditorKey : function(field, e){
37417 var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
37422 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
37424 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
37426 }else if(k == e.ENTER && !e.ctrlKey){
37430 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
37432 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
37434 }else if(k == e.ESC){
37438 g.startEditing(newCell[0], newCell[1]);
37443 * Ext JS Library 1.1.1
37444 * Copyright(c) 2006-2007, Ext JS, LLC.
37446 * Originally Released Under LGPL - original licence link has changed is not relivant.
37449 * <script type="text/javascript">
37452 * @class Roo.grid.CellSelectionModel
37453 * @extends Roo.grid.AbstractSelectionModel
37454 * This class provides the basic implementation for cell selection in a grid.
37456 * @param {Object} config The object containing the configuration of this model.
37457 * @cfg {Boolean} enter_is_tab Enter behaves the same as tab. (eg. goes to next cell) default: false
37459 Roo.grid.CellSelectionModel = function(config){
37460 Roo.apply(this, config);
37462 this.selection = null;
37466 * @event beforerowselect
37467 * Fires before a cell is selected.
37468 * @param {SelectionModel} this
37469 * @param {Number} rowIndex The selected row index
37470 * @param {Number} colIndex The selected cell index
37472 "beforecellselect" : true,
37474 * @event cellselect
37475 * Fires when a cell is selected.
37476 * @param {SelectionModel} this
37477 * @param {Number} rowIndex The selected row index
37478 * @param {Number} colIndex The selected cell index
37480 "cellselect" : true,
37482 * @event selectionchange
37483 * Fires when the active selection changes.
37484 * @param {SelectionModel} this
37485 * @param {Object} selection null for no selection or an object (o) with two properties
37487 <li>o.record: the record object for the row the selection is in</li>
37488 <li>o.cell: An array of [rowIndex, columnIndex]</li>
37491 "selectionchange" : true,
37494 * Fires when the tab (or enter) was pressed on the last editable cell
37495 * You can use this to trigger add new row.
37496 * @param {SelectionModel} this
37500 * @event beforeeditnext
37501 * Fires before the next editable sell is made active
37502 * You can use this to skip to another cell or fire the tabend
37503 * if you set cell to false
37504 * @param {Object} eventdata object : { cell : [ row, col ] }
37506 "beforeeditnext" : true
37508 Roo.grid.CellSelectionModel.superclass.constructor.call(this);
37511 Roo.extend(Roo.grid.CellSelectionModel, Roo.grid.AbstractSelectionModel, {
37513 enter_is_tab: false,
37516 initEvents : function(){
37517 this.grid.on("mousedown", this.handleMouseDown, this);
37518 this.grid.getGridEl().on(Roo.isIE ? "keydown" : "keypress", this.handleKeyDown, this);
37519 var view = this.grid.view;
37520 view.on("refresh", this.onViewChange, this);
37521 view.on("rowupdated", this.onRowUpdated, this);
37522 view.on("beforerowremoved", this.clearSelections, this);
37523 view.on("beforerowsinserted", this.clearSelections, this);
37524 if(this.grid.isEditor){
37525 this.grid.on("beforeedit", this.beforeEdit, this);
37530 beforeEdit : function(e){
37531 this.select(e.row, e.column, false, true, e.record);
37535 onRowUpdated : function(v, index, r){
37536 if(this.selection && this.selection.record == r){
37537 v.onCellSelect(index, this.selection.cell[1]);
37542 onViewChange : function(){
37543 this.clearSelections(true);
37547 * Returns the currently selected cell,.
37548 * @return {Array} The selected cell (row, column) or null if none selected.
37550 getSelectedCell : function(){
37551 return this.selection ? this.selection.cell : null;
37555 * Clears all selections.
37556 * @param {Boolean} true to prevent the gridview from being notified about the change.
37558 clearSelections : function(preventNotify){
37559 var s = this.selection;
37561 if(preventNotify !== true){
37562 this.grid.view.onCellDeselect(s.cell[0], s.cell[1]);
37564 this.selection = null;
37565 this.fireEvent("selectionchange", this, null);
37570 * Returns true if there is a selection.
37571 * @return {Boolean}
37573 hasSelection : function(){
37574 return this.selection ? true : false;
37578 handleMouseDown : function(e, t){
37579 var v = this.grid.getView();
37580 if(this.isLocked()){
37583 var row = v.findRowIndex(t);
37584 var cell = v.findCellIndex(t);
37585 if(row !== false && cell !== false){
37586 this.select(row, cell);
37592 * @param {Number} rowIndex
37593 * @param {Number} collIndex
37595 select : function(rowIndex, colIndex, preventViewNotify, preventFocus, /*internal*/ r){
37596 if(this.fireEvent("beforecellselect", this, rowIndex, colIndex) !== false){
37597 this.clearSelections();
37598 r = r || this.grid.dataSource.getAt(rowIndex);
37601 cell : [rowIndex, colIndex]
37603 if(!preventViewNotify){
37604 var v = this.grid.getView();
37605 v.onCellSelect(rowIndex, colIndex);
37606 if(preventFocus !== true){
37607 v.focusCell(rowIndex, colIndex);
37610 this.fireEvent("cellselect", this, rowIndex, colIndex);
37611 this.fireEvent("selectionchange", this, this.selection);
37616 isSelectable : function(rowIndex, colIndex, cm){
37617 return !cm.isHidden(colIndex);
37621 handleKeyDown : function(e){
37622 //Roo.log('Cell Sel Model handleKeyDown');
37623 if(!e.isNavKeyPress()){
37626 var g = this.grid, s = this.selection;
37629 var cell = g.walkCells(0, 0, 1, this.isSelectable, this);
37631 this.select(cell[0], cell[1]);
37636 var walk = function(row, col, step){
37637 return g.walkCells(row, col, step, sm.isSelectable, sm);
37639 var k = e.getKey(), r = s.cell[0], c = s.cell[1];
37646 // handled by onEditorKey
37647 if (g.isEditor && g.editing) {
37651 newCell = walk(r, c-1, -1);
37653 newCell = walk(r, c+1, 1);
37658 newCell = walk(r+1, c, 1);
37662 newCell = walk(r-1, c, -1);
37666 newCell = walk(r, c+1, 1);
37670 newCell = walk(r, c-1, -1);
37675 if(g.isEditor && !g.editing){
37676 g.startEditing(r, c);
37685 this.select(newCell[0], newCell[1]);
37691 acceptsNav : function(row, col, cm){
37692 return !cm.isHidden(col) && cm.isCellEditable(col, row);
37696 * @param {Number} field (not used) - as it's normally used as a listener
37697 * @param {Number} e - event - fake it by using
37699 * var e = Roo.EventObjectImpl.prototype;
37700 * e.keyCode = e.TAB
37704 onEditorKey : function(field, e){
37706 var k = e.getKey(),
37709 ed = g.activeEditor,
37711 ///Roo.log('onEditorKey' + k);
37714 if (this.enter_is_tab && k == e.ENTER) {
37720 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
37722 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
37728 } else if(k == e.ENTER && !e.ctrlKey){
37731 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
37733 } else if(k == e.ESC){
37738 var ecall = { cell : newCell, forward : forward };
37739 this.fireEvent('beforeeditnext', ecall );
37740 newCell = ecall.cell;
37741 forward = ecall.forward;
37745 //Roo.log('next cell after edit');
37746 g.startEditing.defer(100, g, [newCell[0], newCell[1]]);
37747 } else if (forward) {
37748 // tabbed past last
37749 this.fireEvent.defer(100, this, ['tabend',this]);
37754 * Ext JS Library 1.1.1
37755 * Copyright(c) 2006-2007, Ext JS, LLC.
37757 * Originally Released Under LGPL - original licence link has changed is not relivant.
37760 * <script type="text/javascript">
37764 * @class Roo.grid.EditorGrid
37765 * @extends Roo.grid.Grid
37766 * Class for creating and editable grid.
37767 * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
37768 * The container MUST have some type of size defined for the grid to fill. The container will be
37769 * automatically set to position relative if it isn't already.
37770 * @param {Object} dataSource The data model to bind to
37771 * @param {Object} colModel The column model with info about this grid's columns
37773 Roo.grid.EditorGrid = function(container, config){
37774 Roo.grid.EditorGrid.superclass.constructor.call(this, container, config);
37775 this.getGridEl().addClass("xedit-grid");
37777 if(!this.selModel){
37778 this.selModel = new Roo.grid.CellSelectionModel();
37781 this.activeEditor = null;
37785 * @event beforeedit
37786 * Fires before cell editing is triggered. The edit event object has the following properties <br />
37787 * <ul style="padding:5px;padding-left:16px;">
37788 * <li>grid - This grid</li>
37789 * <li>record - The record being edited</li>
37790 * <li>field - The field name being edited</li>
37791 * <li>value - The value for the field being edited.</li>
37792 * <li>row - The grid row index</li>
37793 * <li>column - The grid column index</li>
37794 * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
37796 * @param {Object} e An edit event (see above for description)
37798 "beforeedit" : true,
37801 * Fires after a cell is edited. <br />
37802 * <ul style="padding:5px;padding-left:16px;">
37803 * <li>grid - This grid</li>
37804 * <li>record - The record being edited</li>
37805 * <li>field - The field name being edited</li>
37806 * <li>value - The value being set</li>
37807 * <li>originalValue - The original value for the field, before the edit.</li>
37808 * <li>row - The grid row index</li>
37809 * <li>column - The grid column index</li>
37811 * @param {Object} e An edit event (see above for description)
37813 "afteredit" : true,
37815 * @event validateedit
37816 * Fires after a cell is edited, but before the value is set in the record.
37817 * You can use this to modify the value being set in the field, Return false
37818 * to cancel the change. The edit event object has the following properties <br />
37819 * <ul style="padding:5px;padding-left:16px;">
37820 * <li>editor - This editor</li>
37821 * <li>grid - This grid</li>
37822 * <li>record - The record being edited</li>
37823 * <li>field - The field name being edited</li>
37824 * <li>value - The value being set</li>
37825 * <li>originalValue - The original value for the field, before the edit.</li>
37826 * <li>row - The grid row index</li>
37827 * <li>column - The grid column index</li>
37828 * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
37830 * @param {Object} e An edit event (see above for description)
37832 "validateedit" : true
37834 this.on("bodyscroll", this.stopEditing, this);
37835 this.on(this.clicksToEdit == 1 ? "cellclick" : "celldblclick", this.onCellDblClick, this);
37838 Roo.extend(Roo.grid.EditorGrid, Roo.grid.Grid, {
37840 * @cfg {Number} clicksToEdit
37841 * The number of clicks on a cell required to display the cell's editor (defaults to 2)
37848 trackMouseOver: false, // causes very odd FF errors
37850 onCellDblClick : function(g, row, col){
37851 this.startEditing(row, col);
37854 onEditComplete : function(ed, value, startValue){
37855 this.editing = false;
37856 this.activeEditor = null;
37857 ed.un("specialkey", this.selModel.onEditorKey, this.selModel);
37859 var field = this.colModel.getDataIndex(ed.col);
37864 originalValue: startValue,
37871 var cell = Roo.get(this.view.getCell(ed.row,ed.col))
37874 if(String(value) !== String(startValue)){
37876 if(this.fireEvent("validateedit", e) !== false && !e.cancel){
37877 r.set(field, e.value);
37878 // if we are dealing with a combo box..
37879 // then we also set the 'name' colum to be the displayField
37880 if (ed.field.displayField && ed.field.name) {
37881 r.set(ed.field.name, ed.field.el.dom.value);
37884 delete e.cancel; //?? why!!!
37885 this.fireEvent("afteredit", e);
37888 this.fireEvent("afteredit", e); // always fire it!
37890 this.view.focusCell(ed.row, ed.col);
37894 * Starts editing the specified for the specified row/column
37895 * @param {Number} rowIndex
37896 * @param {Number} colIndex
37898 startEditing : function(row, col){
37899 this.stopEditing();
37900 if(this.colModel.isCellEditable(col, row)){
37901 this.view.ensureVisible(row, col, true);
37903 var r = this.dataSource.getAt(row);
37904 var field = this.colModel.getDataIndex(col);
37905 var cell = Roo.get(this.view.getCell(row,col));
37910 value: r.data[field],
37915 if(this.fireEvent("beforeedit", e) !== false && !e.cancel){
37916 this.editing = true;
37917 var ed = this.colModel.getCellEditor(col, row);
37923 ed.render(ed.parentEl || document.body);
37929 (function(){ // complex but required for focus issues in safari, ie and opera
37933 ed.on("complete", this.onEditComplete, this, {single: true});
37934 ed.on("specialkey", this.selModel.onEditorKey, this.selModel);
37935 this.activeEditor = ed;
37936 var v = r.data[field];
37937 ed.startEdit(this.view.getCell(row, col), v);
37938 // combo's with 'displayField and name set
37939 if (ed.field.displayField && ed.field.name) {
37940 ed.field.el.dom.value = r.data[ed.field.name];
37944 }).defer(50, this);
37950 * Stops any active editing
37952 stopEditing : function(){
37953 if(this.activeEditor){
37954 this.activeEditor.completeEdit();
37956 this.activeEditor = null;
37960 * Ext JS Library 1.1.1
37961 * Copyright(c) 2006-2007, Ext JS, LLC.
37963 * Originally Released Under LGPL - original licence link has changed is not relivant.
37966 * <script type="text/javascript">
37969 // private - not really -- you end up using it !
37970 // This is a support class used internally by the Grid components
37973 * @class Roo.grid.GridEditor
37974 * @extends Roo.Editor
37975 * Class for creating and editable grid elements.
37976 * @param {Object} config any settings (must include field)
37978 Roo.grid.GridEditor = function(field, config){
37979 if (!config && field.field) {
37981 field = Roo.factory(config.field, Roo.form);
37983 Roo.grid.GridEditor.superclass.constructor.call(this, field, config);
37984 field.monitorTab = false;
37987 Roo.extend(Roo.grid.GridEditor, Roo.Editor, {
37990 * @cfg {Roo.form.Field} field Field to wrap (or xtyped)
37993 alignment: "tl-tl",
37996 cls: "x-small-editor x-grid-editor",
38001 * Ext JS Library 1.1.1
38002 * Copyright(c) 2006-2007, Ext JS, LLC.
38004 * Originally Released Under LGPL - original licence link has changed is not relivant.
38007 * <script type="text/javascript">
38012 Roo.grid.PropertyRecord = Roo.data.Record.create([
38013 {name:'name',type:'string'}, 'value'
38017 Roo.grid.PropertyStore = function(grid, source){
38019 this.store = new Roo.data.Store({
38020 recordType : Roo.grid.PropertyRecord
38022 this.store.on('update', this.onUpdate, this);
38024 this.setSource(source);
38026 Roo.grid.PropertyStore.superclass.constructor.call(this);
38031 Roo.extend(Roo.grid.PropertyStore, Roo.util.Observable, {
38032 setSource : function(o){
38034 this.store.removeAll();
38037 if(this.isEditableValue(o[k])){
38038 data.push(new Roo.grid.PropertyRecord({name: k, value: o[k]}, k));
38041 this.store.loadRecords({records: data}, {}, true);
38044 onUpdate : function(ds, record, type){
38045 if(type == Roo.data.Record.EDIT){
38046 var v = record.data['value'];
38047 var oldValue = record.modified['value'];
38048 if(this.grid.fireEvent('beforepropertychange', this.source, record.id, v, oldValue) !== false){
38049 this.source[record.id] = v;
38051 this.grid.fireEvent('propertychange', this.source, record.id, v, oldValue);
38058 getProperty : function(row){
38059 return this.store.getAt(row);
38062 isEditableValue: function(val){
38063 if(val && val instanceof Date){
38065 }else if(typeof val == 'object' || typeof val == 'function'){
38071 setValue : function(prop, value){
38072 this.source[prop] = value;
38073 this.store.getById(prop).set('value', value);
38076 getSource : function(){
38077 return this.source;
38081 Roo.grid.PropertyColumnModel = function(grid, store){
38084 g.PropertyColumnModel.superclass.constructor.call(this, [
38085 {header: this.nameText, sortable: true, dataIndex:'name', id: 'name'},
38086 {header: this.valueText, resizable:false, dataIndex: 'value', id: 'value'}
38088 this.store = store;
38089 this.bselect = Roo.DomHelper.append(document.body, {
38090 tag: 'select', style:'display:none', cls: 'x-grid-editor', children: [
38091 {tag: 'option', value: 'true', html: 'true'},
38092 {tag: 'option', value: 'false', html: 'false'}
38095 Roo.id(this.bselect);
38098 'date' : new g.GridEditor(new f.DateField({selectOnFocus:true})),
38099 'string' : new g.GridEditor(new f.TextField({selectOnFocus:true})),
38100 'number' : new g.GridEditor(new f.NumberField({selectOnFocus:true, style:'text-align:left;'})),
38101 'int' : new g.GridEditor(new f.NumberField({selectOnFocus:true, allowDecimals:false, style:'text-align:left;'})),
38102 'boolean' : new g.GridEditor(new f.Field({el:this.bselect,selectOnFocus:true}))
38104 this.renderCellDelegate = this.renderCell.createDelegate(this);
38105 this.renderPropDelegate = this.renderProp.createDelegate(this);
38108 Roo.extend(Roo.grid.PropertyColumnModel, Roo.grid.ColumnModel, {
38112 valueText : 'Value',
38114 dateFormat : 'm/j/Y',
38117 renderDate : function(dateVal){
38118 return dateVal.dateFormat(this.dateFormat);
38121 renderBool : function(bVal){
38122 return bVal ? 'true' : 'false';
38125 isCellEditable : function(colIndex, rowIndex){
38126 return colIndex == 1;
38129 getRenderer : function(col){
38131 this.renderCellDelegate : this.renderPropDelegate;
38134 renderProp : function(v){
38135 return this.getPropertyName(v);
38138 renderCell : function(val){
38140 if(val instanceof Date){
38141 rv = this.renderDate(val);
38142 }else if(typeof val == 'boolean'){
38143 rv = this.renderBool(val);
38145 return Roo.util.Format.htmlEncode(rv);
38148 getPropertyName : function(name){
38149 var pn = this.grid.propertyNames;
38150 return pn && pn[name] ? pn[name] : name;
38153 getCellEditor : function(colIndex, rowIndex){
38154 var p = this.store.getProperty(rowIndex);
38155 var n = p.data['name'], val = p.data['value'];
38157 if(typeof(this.grid.customEditors[n]) == 'string'){
38158 return this.editors[this.grid.customEditors[n]];
38160 if(typeof(this.grid.customEditors[n]) != 'undefined'){
38161 return this.grid.customEditors[n];
38163 if(val instanceof Date){
38164 return this.editors['date'];
38165 }else if(typeof val == 'number'){
38166 return this.editors['number'];
38167 }else if(typeof val == 'boolean'){
38168 return this.editors['boolean'];
38170 return this.editors['string'];
38176 * @class Roo.grid.PropertyGrid
38177 * @extends Roo.grid.EditorGrid
38178 * This class represents the interface of a component based property grid control.
38179 * <br><br>Usage:<pre><code>
38180 var grid = new Roo.grid.PropertyGrid("my-container-id", {
38188 * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
38189 * The container MUST have some type of size defined for the grid to fill. The container will be
38190 * automatically set to position relative if it isn't already.
38191 * @param {Object} config A config object that sets properties on this grid.
38193 Roo.grid.PropertyGrid = function(container, config){
38194 config = config || {};
38195 var store = new Roo.grid.PropertyStore(this);
38196 this.store = store;
38197 var cm = new Roo.grid.PropertyColumnModel(this, store);
38198 store.store.sort('name', 'ASC');
38199 Roo.grid.PropertyGrid.superclass.constructor.call(this, container, Roo.apply({
38202 enableColLock:false,
38203 enableColumnMove:false,
38205 trackMouseOver: false,
38208 this.getGridEl().addClass('x-props-grid');
38209 this.lastEditRow = null;
38210 this.on('columnresize', this.onColumnResize, this);
38213 * @event beforepropertychange
38214 * Fires before a property changes (return false to stop?)
38215 * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
38216 * @param {String} id Record Id
38217 * @param {String} newval New Value
38218 * @param {String} oldval Old Value
38220 "beforepropertychange": true,
38222 * @event propertychange
38223 * Fires after a property changes
38224 * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
38225 * @param {String} id Record Id
38226 * @param {String} newval New Value
38227 * @param {String} oldval Old Value
38229 "propertychange": true
38231 this.customEditors = this.customEditors || {};
38233 Roo.extend(Roo.grid.PropertyGrid, Roo.grid.EditorGrid, {
38236 * @cfg {Object} customEditors map of colnames=> custom editors.
38237 * the custom editor can be one of the standard ones (date|string|number|int|boolean), or a
38238 * grid editor eg. Roo.grid.GridEditor(new Roo.form.TextArea({selectOnFocus:true})),
38239 * false disables editing of the field.
38243 * @cfg {Object} propertyNames map of property Names to their displayed value
38246 render : function(){
38247 Roo.grid.PropertyGrid.superclass.render.call(this);
38248 this.autoSize.defer(100, this);
38251 autoSize : function(){
38252 Roo.grid.PropertyGrid.superclass.autoSize.call(this);
38254 this.view.fitColumns();
38258 onColumnResize : function(){
38259 this.colModel.setColumnWidth(1, this.container.getWidth(true)-this.colModel.getColumnWidth(0));
38263 * Sets the data for the Grid
38264 * accepts a Key => Value object of all the elements avaiable.
38265 * @param {Object} data to appear in grid.
38267 setSource : function(source){
38268 this.store.setSource(source);
38272 * Gets all the data from the grid.
38273 * @return {Object} data data stored in grid
38275 getSource : function(){
38276 return this.store.getSource();
38280 * Ext JS Library 1.1.1
38281 * Copyright(c) 2006-2007, Ext JS, LLC.
38283 * Originally Released Under LGPL - original licence link has changed is not relivant.
38286 * <script type="text/javascript">
38290 * @class Roo.LoadMask
38291 * A simple utility class for generically masking elements while loading data. If the element being masked has
38292 * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
38293 * process and the mask element will be cached for reuse. For all other elements, this mask will replace the
38294 * element's UpdateManager load indicator and will be destroyed after the initial load.
38296 * Create a new LoadMask
38297 * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
38298 * @param {Object} config The config object
38300 Roo.LoadMask = function(el, config){
38301 this.el = Roo.get(el);
38302 Roo.apply(this, config);
38304 this.store.on('beforeload', this.onBeforeLoad, this);
38305 this.store.on('load', this.onLoad, this);
38306 this.store.on('loadexception', this.onLoadException, this);
38307 this.removeMask = false;
38309 var um = this.el.getUpdateManager();
38310 um.showLoadIndicator = false; // disable the default indicator
38311 um.on('beforeupdate', this.onBeforeLoad, this);
38312 um.on('update', this.onLoad, this);
38313 um.on('failure', this.onLoad, this);
38314 this.removeMask = true;
38318 Roo.LoadMask.prototype = {
38320 * @cfg {Boolean} removeMask
38321 * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
38322 * False to persist the mask element reference for multiple uses (e.g., for paged data widgets). Defaults to false.
38325 * @cfg {String} msg
38326 * The text to display in a centered loading message box (defaults to 'Loading...')
38328 msg : 'Loading...',
38330 * @cfg {String} msgCls
38331 * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
38333 msgCls : 'x-mask-loading',
38336 * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
38342 * Disables the mask to prevent it from being displayed
38344 disable : function(){
38345 this.disabled = true;
38349 * Enables the mask so that it can be displayed
38351 enable : function(){
38352 this.disabled = false;
38355 onLoadException : function()
38357 if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
38358 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
38360 this.el.unmask(this.removeMask);
38363 onLoad : function()
38365 this.el.unmask(this.removeMask);
38369 onBeforeLoad : function(){
38370 if(!this.disabled){
38371 this.el.mask(this.msg, this.msgCls);
38376 destroy : function(){
38378 this.store.un('beforeload', this.onBeforeLoad, this);
38379 this.store.un('load', this.onLoad, this);
38380 this.store.un('loadexception', this.onLoadException, this);
38382 var um = this.el.getUpdateManager();
38383 um.un('beforeupdate', this.onBeforeLoad, this);
38384 um.un('update', this.onLoad, this);
38385 um.un('failure', this.onLoad, this);
38390 * Ext JS Library 1.1.1
38391 * Copyright(c) 2006-2007, Ext JS, LLC.
38393 * Originally Released Under LGPL - original licence link has changed is not relivant.
38396 * <script type="text/javascript">
38401 * @class Roo.XTemplate
38402 * @extends Roo.Template
38403 * Provides a template that can have nested templates for loops or conditionals. The syntax is:
38405 var t = new Roo.XTemplate(
38406 '<select name="{name}">',
38407 '<tpl for="options"><option value="{value:trim}">{text:ellipsis(10)}</option></tpl>',
38411 // then append, applying the master template values
38414 * Supported features:
38419 {a_variable} - output encoded.
38420 {a_variable.format:("Y-m-d")} - call a method on the variable
38421 {a_variable:raw} - unencoded output
38422 {a_variable:toFixed(1,2)} - Roo.util.Format."toFixed"
38423 {a_variable:this.method_on_template(...)} - call a method on the template object.
38428 <tpl for="a_variable or condition.."></tpl>
38429 <tpl if="a_variable or condition"></tpl>
38430 <tpl exec="some javascript"></tpl>
38431 <tpl name="named_template"></tpl> (experimental)
38433 <tpl for="."></tpl> - just iterate the property..
38434 <tpl for=".."></tpl> - iterates with the parent (probably the template)
38438 Roo.XTemplate = function()
38440 Roo.XTemplate.superclass.constructor.apply(this, arguments);
38447 Roo.extend(Roo.XTemplate, Roo.Template, {
38450 * The various sub templates
38455 * basic tag replacing syntax
38458 * // you can fake an object call by doing this
38462 re : /\{([\w-\.]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
38465 * compile the template
38467 * This is not recursive, so I'm not sure how nested templates are really going to be handled..
38470 compile: function()
38474 s = ['<tpl>', s, '</tpl>'].join('');
38476 var re = /<tpl\b[^>]*>((?:(?=([^<]+))\2|<(?!tpl\b[^>]*>))*?)<\/tpl>/,
38477 nameRe = /^<tpl\b[^>]*?for="(.*?)"/,
38478 ifRe = /^<tpl\b[^>]*?if="(.*?)"/,
38479 execRe = /^<tpl\b[^>]*?exec="(.*?)"/,
38480 namedRe = /^<tpl\b[^>]*?name="(\w+)"/, // named templates..
38485 while(true == !!(m = s.match(re))){
38486 var forMatch = m[0].match(nameRe),
38487 ifMatch = m[0].match(ifRe),
38488 execMatch = m[0].match(execRe),
38489 namedMatch = m[0].match(namedRe),
38494 name = forMatch && forMatch[1] ? forMatch[1] : '';
38497 // if - puts fn into test..
38498 exp = ifMatch && ifMatch[1] ? ifMatch[1] : null;
38500 fn = new Function('values', 'parent', 'with(values){ return '+(Roo.util.Format.htmlDecode(exp))+'; }');
38505 // exec - calls a function... returns empty if true is returned.
38506 exp = execMatch && execMatch[1] ? execMatch[1] : null;
38508 exec = new Function('values', 'parent', 'with(values){ '+(Roo.util.Format.htmlDecode(exp))+'; }');
38516 case '.': name = new Function('values', 'parent', 'with(values){ return values; }'); break;
38517 case '..': name = new Function('values', 'parent', 'with(values){ return parent; }'); break;
38518 default: name = new Function('values', 'parent', 'with(values){ return '+name+'; }');
38521 var uid = namedMatch ? namedMatch[1] : id;
38525 id: namedMatch ? namedMatch[1] : id,
38532 s = s.replace(m[0], '');
38534 s = s.replace(m[0], '{xtpl'+ id + '}');
38539 for(var i = tpls.length-1; i >= 0; --i){
38540 this.compileTpl(tpls[i]);
38541 this.tpls[tpls[i].id] = tpls[i];
38543 this.master = tpls[tpls.length-1];
38547 * same as applyTemplate, except it's done to one of the subTemplates
38548 * when using named templates, you can do:
38550 * var str = pl.applySubTemplate('your-name', values);
38553 * @param {Number} id of the template
38554 * @param {Object} values to apply to template
38555 * @param {Object} parent (normaly the instance of this object)
38557 applySubTemplate : function(id, values, parent)
38561 var t = this.tpls[id];
38565 if(t.test && !t.test.call(this, values, parent)){
38569 Roo.log("Xtemplate.applySubTemplate 'test': Exception thrown");
38570 Roo.log(e.toString());
38576 if(t.exec && t.exec.call(this, values, parent)){
38580 Roo.log("Xtemplate.applySubTemplate 'exec': Exception thrown");
38581 Roo.log(e.toString());
38586 var vs = t.target ? t.target.call(this, values, parent) : values;
38587 parent = t.target ? values : parent;
38588 if(t.target && vs instanceof Array){
38590 for(var i = 0, len = vs.length; i < len; i++){
38591 buf[buf.length] = t.compiled.call(this, vs[i], parent);
38593 return buf.join('');
38595 return t.compiled.call(this, vs, parent);
38597 Roo.log("Xtemplate.applySubTemplate : Exception thrown");
38598 Roo.log(e.toString());
38599 Roo.log(t.compiled);
38604 compileTpl : function(tpl)
38606 var fm = Roo.util.Format;
38607 var useF = this.disableFormats !== true;
38608 var sep = Roo.isGecko ? "+" : ",";
38609 var undef = function(str) {
38610 Roo.log("Property not found :" + str);
38614 var fn = function(m, name, format, args)
38616 //Roo.log(arguments);
38617 args = args ? args.replace(/\\'/g,"'") : args;
38618 //["{TEST:(a,b,c)}", "TEST", "", "a,b,c", 0, "{TEST:(a,b,c)}"]
38619 if (typeof(format) == 'undefined') {
38620 format= 'htmlEncode';
38622 if (format == 'raw' ) {
38626 if(name.substr(0, 4) == 'xtpl'){
38627 return "'"+ sep +'this.applySubTemplate('+name.substr(4)+', values, parent)'+sep+"'";
38630 // build an array of options to determine if value is undefined..
38632 // basically get 'xxxx.yyyy' then do
38633 // (typeof(xxxx) == 'undefined' || typeof(xxx.yyyy) == 'undefined') ?
38634 // (function () { Roo.log("Property not found"); return ''; })() :
38639 Roo.each(name.split('.'), function(st) {
38640 lookfor += (lookfor.length ? '.': '') + st;
38641 udef_ar.push( "(typeof(" + lookfor + ") == 'undefined')" );
38644 var udef_st = '((' + udef_ar.join(" || ") +") ? undef('" + name + "') : "; // .. needs )
38647 if(format && useF){
38649 args = args ? ',' + args : "";
38651 if(format.substr(0, 5) != "this."){
38652 format = "fm." + format + '(';
38654 format = 'this.call("'+ format.substr(5) + '", ';
38658 return "'"+ sep + udef_st + format + name + args + "))"+sep+"'";
38662 // called with xxyx.yuu:(test,test)
38664 return "'"+ sep + udef_st + name + '(' + args + "))"+sep+"'";
38666 // raw.. - :raw modifier..
38667 return "'"+ sep + udef_st + name + ")"+sep+"'";
38671 // branched to use + in gecko and [].join() in others
38673 body = "tpl.compiled = function(values, parent){ with(values) { return '" +
38674 tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
38677 body = ["tpl.compiled = function(values, parent){ with (values) { return ['"];
38678 body.push(tpl.body.replace(/(\r\n|\n)/g,
38679 '\\n').replace(/'/g, "\\'").replace(this.re, fn));
38680 body.push("'].join('');};};");
38681 body = body.join('');
38684 Roo.debug && Roo.log(body.replace(/\\n/,'\n'));
38686 /** eval:var:tpl eval:var:fm eval:var:useF eval:var:undef */
38692 applyTemplate : function(values){
38693 return this.master.compiled.call(this, values, {});
38694 //var s = this.subs;
38697 apply : function(){
38698 return this.applyTemplate.apply(this, arguments);
38703 Roo.XTemplate.from = function(el){
38704 el = Roo.getDom(el);
38705 return new Roo.XTemplate(el.value || el.innerHTML);
38707 * Original code for Roojs - LGPL
38708 * <script type="text/javascript">
38712 * @class Roo.XComponent
38713 * A delayed Element creator...
38714 * Or a way to group chunks of interface together.
38716 * Mypart.xyx = new Roo.XComponent({
38718 parent : 'Mypart.xyz', // empty == document.element.!!
38722 disabled : function() {}
38724 tree : function() { // return an tree of xtype declared components
38728 xtype : 'NestedLayoutPanel',
38735 * It can be used to build a big heiracy, with parent etc.
38736 * or you can just use this to render a single compoent to a dom element
38737 * MYPART.render(Roo.Element | String(id) | dom_element )
38739 * @extends Roo.util.Observable
38741 * @param cfg {Object} configuration of component
38744 Roo.XComponent = function(cfg) {
38745 Roo.apply(this, cfg);
38749 * Fires when this the componnt is built
38750 * @param {Roo.XComponent} c the component
38755 this.region = this.region || 'center'; // default..
38756 Roo.XComponent.register(this);
38757 this.modules = false;
38758 this.el = false; // where the layout goes..
38762 Roo.extend(Roo.XComponent, Roo.util.Observable, {
38765 * The created element (with Roo.factory())
38766 * @type {Roo.Layout}
38772 * for BC - use el in new code
38773 * @type {Roo.Layout}
38779 * for BC - use el in new code
38780 * @type {Roo.Layout}
38785 * @cfg {Function|boolean} disabled
38786 * If this module is disabled by some rule, return true from the funtion
38791 * @cfg {String} parent
38792 * Name of parent element which it get xtype added to..
38797 * @cfg {String} order
38798 * Used to set the order in which elements are created (usefull for multiple tabs)
38803 * @cfg {String} name
38804 * String to display while loading.
38808 * @cfg {String} region
38809 * Region to render component to (defaults to center)
38814 * @cfg {Array} items
38815 * A single item array - the first element is the root of the tree..
38816 * It's done this way to stay compatible with the Xtype system...
38822 * The method that retuns the tree of parts that make up this compoennt
38829 * render element to dom or tree
38830 * @param {Roo.Element|String|DomElement} optional render to if parent is not set.
38833 render : function(el)
38837 var hp = this.parent ? 1 : 0;
38839 if (!el && typeof(this.parent) == 'string' && this.parent.substring(0,1) == '#') {
38840 // if parent is a '#.....' string, then let's use that..
38841 var ename = this.parent.substr(1)
38842 this.parent = false;
38843 el = Roo.get(ename);
38845 Roo.log("Warning - element can not be found :#" + ename );
38851 if (!this.parent) {
38853 el = el ? Roo.get(el) : false;
38855 // it's a top level one..
38857 el : new Roo.BorderLayout(el || document.body, {
38863 tabPosition: 'top',
38864 //resizeTabs: true,
38865 alwaysShowTabs: el && hp? false : true,
38866 hideTabs: el || !hp ? true : false,
38873 if (!this.parent.el) {
38874 // probably an old style ctor, which has been disabled.
38878 // The 'tree' method is '_tree now'
38880 var tree = this._tree ? this._tree() : this.tree();
38881 tree.region = tree.region || this.region;
38882 this.el = this.parent.el.addxtype(tree);
38883 this.fireEvent('built', this);
38885 this.panel = this.el;
38886 this.layout = this.panel.layout;
38887 this.parentLayout = this.parent.layout || false;
38893 Roo.apply(Roo.XComponent, {
38895 * @property hideProgress
38896 * true to disable the building progress bar.. usefull on single page renders.
38899 hideProgress : false,
38901 * @property buildCompleted
38902 * True when the builder has completed building the interface.
38905 buildCompleted : false,
38908 * @property topModule
38909 * the upper most module - uses document.element as it's constructor.
38916 * @property modules
38917 * array of modules to be created by registration system.
38918 * @type {Array} of Roo.XComponent
38923 * @property elmodules
38924 * array of modules to be created by which use #ID
38925 * @type {Array} of Roo.XComponent
38932 * Register components to be built later.
38934 * This solves the following issues
38935 * - Building is not done on page load, but after an authentication process has occured.
38936 * - Interface elements are registered on page load
38937 * - Parent Interface elements may not be loaded before child, so this handles that..
38944 module : 'Pman.Tab.projectMgr',
38946 parent : 'Pman.layout',
38947 disabled : false, // or use a function..
38950 * * @param {Object} details about module
38952 register : function(obj) {
38954 Roo.XComponent.event.fireEvent('register', obj);
38955 switch(typeof(obj.disabled) ) {
38961 if ( obj.disabled() ) {
38967 if (obj.disabled) {
38973 this.modules.push(obj);
38977 * convert a string to an object..
38978 * eg. 'AAA.BBB' -> finds AAA.BBB
38982 toObject : function(str)
38984 if (!str || typeof(str) == 'object') {
38987 if (str.substring(0,1) == '#') {
38991 var ar = str.split('.');
38996 eval('if (typeof ' + rt + ' == "undefined"){ o = false;} o = ' + rt + ';');
38998 throw "Module not found : " + str;
39002 throw "Module not found : " + str;
39004 Roo.each(ar, function(e) {
39005 if (typeof(o[e]) == 'undefined') {
39006 throw "Module not found : " + str;
39017 * move modules into their correct place in the tree..
39020 preBuild : function ()
39023 Roo.each(this.modules , function (obj)
39025 Roo.XComponent.event.fireEvent('beforebuild', obj);
39027 var opar = obj.parent;
39029 obj.parent = this.toObject(opar);
39031 Roo.log("parent:toObject failed: " + e.toString());
39036 Roo.debug && Roo.log("GOT top level module");
39037 Roo.debug && Roo.log(obj);
39038 obj.modules = new Roo.util.MixedCollection(false,
39039 function(o) { return o.order + '' }
39041 this.topModule = obj;
39044 // parent is a string (usually a dom element name..)
39045 if (typeof(obj.parent) == 'string') {
39046 this.elmodules.push(obj);
39049 if (obj.parent.constructor != Roo.XComponent) {
39050 Roo.log("Warning : Object Parent is not instance of XComponent:" + obj.name)
39052 if (!obj.parent.modules) {
39053 obj.parent.modules = new Roo.util.MixedCollection(false,
39054 function(o) { return o.order + '' }
39057 if (obj.parent.disabled) {
39058 obj.disabled = true;
39060 obj.parent.modules.add(obj);
39065 * make a list of modules to build.
39066 * @return {Array} list of modules.
39069 buildOrder : function()
39072 var cmp = function(a,b) {
39073 return String(a).toUpperCase() > String(b).toUpperCase() ? 1 : -1;
39075 if ((!this.topModule || !this.topModule.modules) && !this.elmodules.length) {
39076 throw "No top level modules to build";
39079 // make a flat list in order of modules to build.
39080 var mods = this.topModule ? [ this.topModule ] : [];
39082 // elmodules (is a list of DOM based modules )
39083 Roo.each(this.elmodules, function(e) {
39088 // add modules to their parents..
39089 var addMod = function(m) {
39090 Roo.debug && Roo.log("build Order: add: " + m.name);
39093 if (m.modules && !m.disabled) {
39094 Roo.debug && Roo.log("build Order: " + m.modules.length + " child modules");
39095 m.modules.keySort('ASC', cmp );
39096 Roo.debug && Roo.log("build Order: " + m.modules.length + " child modules (after sort)");
39098 m.modules.each(addMod);
39100 Roo.debug && Roo.log("build Order: no child modules");
39102 // not sure if this is used any more..
39104 m.finalize.name = m.name + " (clean up) ";
39105 mods.push(m.finalize);
39109 if (this.topModule) {
39110 this.topModule.modules.keySort('ASC', cmp );
39111 this.topModule.modules.each(addMod);
39117 * Build the registered modules.
39118 * @param {Object} parent element.
39119 * @param {Function} optional method to call after module has been added.
39127 var mods = this.buildOrder();
39129 //this.allmods = mods;
39130 //Roo.debug && Roo.log(mods);
39132 if (!mods.length) { // should not happen
39133 throw "NO modules!!!";
39137 var msg = "Building Interface...";
39138 // flash it up as modal - so we store the mask!?
39139 if (!this.hideProgress) {
39140 Roo.MessageBox.show({ title: 'loading' });
39141 Roo.MessageBox.show({
39142 title: "Please wait...",
39151 var total = mods.length;
39154 var progressRun = function() {
39155 if (!mods.length) {
39156 Roo.debug && Roo.log('hide?');
39157 if (!this.hideProgress) {
39158 Roo.MessageBox.hide();
39160 Roo.XComponent.event.fireEvent('buildcomplete', _this.topModule);
39166 var m = mods.shift();
39169 Roo.debug && Roo.log(m);
39170 // not sure if this is supported any more.. - modules that are are just function
39171 if (typeof(m) == 'function') {
39173 return progressRun.defer(10, _this);
39177 msg = "Building Interface " + (total - mods.length) +
39179 (m.name ? (' - ' + m.name) : '');
39180 Roo.debug && Roo.log(msg);
39181 if (!this.hideProgress) {
39182 Roo.MessageBox.updateProgress( (total - mods.length)/total, msg );
39186 // is the module disabled?
39187 var disabled = (typeof(m.disabled) == 'function') ?
39188 m.disabled.call(m.module.disabled) : m.disabled;
39192 return progressRun(); // we do not update the display!
39200 // it's 10 on top level, and 1 on others??? why...
39201 return progressRun.defer(10, _this);
39204 progressRun.defer(1, _this);
39218 * wrapper for event.on - aliased later..
39219 * Typically use to register a event handler for register:
39221 * eg. Roo.XComponent.on('register', function(comp) { comp.disable = true } );
39230 Roo.XComponent.event = new Roo.util.Observable({
39234 * Fires when an Component is registered,
39235 * set the disable property on the Component to stop registration.
39236 * @param {Roo.XComponent} c the component being registerd.
39241 * @event beforebuild
39242 * Fires before each Component is built
39243 * can be used to apply permissions.
39244 * @param {Roo.XComponent} c the component being registerd.
39247 'beforebuild' : true,
39249 * @event buildcomplete
39250 * Fires on the top level element when all elements have been built
39251 * @param {Roo.XComponent} the top level component.
39253 'buildcomplete' : true
39258 Roo.XComponent.on = Roo.XComponent.event.on.createDelegate(Roo.XComponent.event);
39259 //<script type="text/javascript">
39264 * @extends Roo.LayoutDialog
39265 * A generic Login Dialog..... - only one needed in theory!?!?
39267 * Fires XComponent builder on success...
39270 * username,password, lang = for login actions.
39271 * check = 1 for periodic checking that sesion is valid.
39272 * passwordRequest = email request password
39273 * logout = 1 = to logout
39275 * Affects: (this id="????" elements)
39276 * loading (removed) (used to indicate application is loading)
39277 * loading-mask (hides) (used to hide application when it's building loading)
39283 * Myapp.login = Roo.Login({
39299 Roo.Login = function(cfg)
39305 Roo.apply(this,cfg);
39307 Roo.onReady(function() {
39313 Roo.Login.superclass.constructor.call(this, this);
39314 //this.addxtype(this.items[0]);
39320 Roo.extend(Roo.Login, Roo.LayoutDialog, {
39323 * @cfg {String} method
39324 * Method used to query for login details.
39329 * @cfg {String} url
39330 * URL to query login data. - eg. baseURL + '/Login.php'
39336 * The user data - if user.id < 0 then login will be bypassed. (used for inital setup situation.
39341 * @property checkFails
39342 * Number of times we have attempted to get authentication check, and failed.
39347 * @property intervalID
39348 * The window interval that does the constant login checking.
39354 onLoad : function() // called on page load...
39358 if (Roo.get('loading')) { // clear any loading indicator..
39359 Roo.get('loading').remove();
39362 //this.switchLang('en'); // set the language to english..
39365 success: function(response, opts) { // check successfull...
39367 var res = this.processResponse(response);
39368 this.checkFails =0;
39369 if (!res.success) { // error!
39370 this.checkFails = 5;
39371 //console.log('call failure');
39372 return this.failure(response,opts);
39375 if (!res.data.id) { // id=0 == login failure.
39376 return this.show();
39380 //console.log(success);
39381 this.fillAuth(res.data);
39382 this.checkFails =0;
39383 Roo.XComponent.build();
39385 failure : this.show
39391 check: function(cfg) // called every so often to refresh cookie etc..
39393 if (cfg.again) { // could be undefined..
39396 this.checkFails = 0;
39399 if (this.sending) {
39400 if ( this.checkFails > 4) {
39401 Roo.MessageBox.alert("Error",
39402 "Error getting authentication status. - try reloading, or wait a while", function() {
39403 _this.sending = false;
39408 _this.check.defer(10000, _this, [ cfg ]); // check in 10 secs.
39411 this.sending = true;
39418 method: this.method,
39419 success: cfg.success || this.success,
39420 failure : cfg.failure || this.failure,
39430 window.onbeforeunload = function() { }; // false does not work for IE..
39440 failure : function() {
39441 Roo.MessageBox.alert("Error", "Error logging out. - continuing anyway.", function() {
39442 document.location = document.location.toString() + '?ts=' + Math.random();
39446 success : function() {
39447 _this.user = false;
39448 this.checkFails =0;
39450 document.location = document.location.toString() + '?ts=' + Math.random();
39457 processResponse : function (response)
39461 res = Roo.decode(response.responseText);
39463 if (typeof(res) != 'object') {
39464 res = { success : false, errorMsg : res, errors : true };
39466 if (typeof(res.success) == 'undefined') {
39467 res.success = false;
39471 res = { success : false, errorMsg : response.responseText, errors : true };
39476 success : function(response, opts) // check successfull...
39478 this.sending = false;
39479 var res = this.processResponse(response);
39480 if (!res.success) {
39481 return this.failure(response, opts);
39483 if (!res.data || !res.data.id) {
39484 return this.failure(response,opts);
39486 //console.log(res);
39487 this.fillAuth(res.data);
39489 this.checkFails =0;
39494 failure : function (response, opts) // called if login 'check' fails.. (causes re-check)
39496 this.authUser = -1;
39497 this.sending = false;
39498 var res = this.processResponse(response);
39499 //console.log(res);
39500 if ( this.checkFails > 2) {
39502 Roo.MessageBox.alert("Error", res.errorMsg ? res.errorMsg :
39503 "Error getting authentication status. - try reloading");
39506 opts.callCfg.again = true;
39507 this.check.defer(1000, this, [ opts.callCfg ]);
39513 fillAuth: function(au) {
39514 this.startAuthCheck();
39515 this.authUserId = au.id;
39516 this.authUser = au;
39517 this.lastChecked = new Date();
39518 this.fireEvent('refreshed', au);
39519 //Pman.Tab.FaxQueue.newMaxId(au.faxMax);
39520 //Pman.Tab.FaxTab.setTitle(au.faxNumPending);
39521 au.lang = au.lang || 'en';
39522 //this.switchLang(Roo.state.Manager.get('Pman.Login.lang', 'en'));
39523 Roo.state.Manager.set( this.realm + 'lang' , au.lang);
39524 this.switchLang(au.lang );
39527 // open system... - -on setyp..
39528 if (this.authUserId < 0) {
39529 Roo.MessageBox.alert("Warning",
39530 "This is an open system - please set up a admin user with a password.");
39533 //Pman.onload(); // which should do nothing if it's a re-auth result...
39538 startAuthCheck : function() // starter for timeout checking..
39540 if (this.intervalID) { // timer already in place...
39544 this.intervalID = window.setInterval(function() {
39545 _this.check(false);
39546 }, 120000); // every 120 secs = 2mins..
39552 switchLang : function (lang)
39554 _T = typeof(_T) == 'undefined' ? false : _T;
39555 if (!_T || !lang.length) {
39559 if (!_T && lang != 'en') {
39560 Roo.MessageBox.alert("Sorry", "Language not available yet (" + lang +')');
39564 if (typeof(_T.en) == 'undefined') {
39566 Roo.apply(_T.en, _T);
39569 if (typeof(_T[lang]) == 'undefined') {
39570 Roo.MessageBox.alert("Sorry", "Language not available yet (" + lang +')');
39575 Roo.apply(_T, _T[lang]);
39576 // just need to set the text values for everything...
39578 /* this will not work ...
39582 function formLabel(name, val) {
39583 _this.form.findField(name).fieldEl.child('label').dom.innerHTML = val;
39586 formLabel('password', "Password"+':');
39587 formLabel('username', "Email Address"+':');
39588 formLabel('lang', "Language"+':');
39589 this.dialog.setTitle("Login");
39590 this.dialog.buttons[0].setText("Forgot Password");
39591 this.dialog.buttons[1].setText("Login");
39610 collapsible: false,
39612 center: { // needed??
39615 // tabPosition: 'top',
39618 alwaysShowTabs: false
39622 show : function(dlg)
39624 //console.log(this);
39625 this.form = this.layout.getRegion('center').activePanel.form;
39626 this.form.dialog = dlg;
39627 this.buttons[0].form = this.form;
39628 this.buttons[0].dialog = dlg;
39629 this.buttons[1].form = this.form;
39630 this.buttons[1].dialog = dlg;
39632 //this.resizeToLogo.defer(1000,this);
39633 // this is all related to resizing for logos..
39634 //var sz = Roo.get(Pman.Login.form.el.query('img')[0]).getSize();
39636 // this.resizeToLogo.defer(1000,this);
39639 //var w = Ext.lib.Dom.getViewWidth() - 100;
39640 //var h = Ext.lib.Dom.getViewHeight() - 100;
39641 //this.resizeTo(Math.max(350, Math.min(sz.width + 30, w)),Math.min(sz.height+200, h));
39643 if (this.disabled) {
39648 if (this.user.id < 0) { // used for inital setup situations.
39652 if (this.intervalID) {
39653 // remove the timer
39654 window.clearInterval(this.intervalID);
39655 this.intervalID = false;
39659 if (Roo.get('loading')) {
39660 Roo.get('loading').remove();
39662 if (Roo.get('loading-mask')) {
39663 Roo.get('loading-mask').hide();
39666 //incomming._node = tnode;
39668 //this.dialog.modal = !modal;
39669 //this.dialog.show();
39673 this.form.setValues({
39674 'username' : Roo.state.Manager.get(this.realm + '.username', ''),
39675 'lang' : Roo.state.Manager.get(this.realm + '.lang', 'en')
39678 this.switchLang(Roo.state.Manager.get(this.realm + '.lang', 'en'));
39679 if (this.form.findField('username').getValue().length > 0 ){
39680 this.form.findField('password').focus();
39682 this.form.findField('username').focus();
39690 xtype : 'ContentPanel',
39702 style : 'margin: 10px;',
39705 actionfailed : function(f, act) {
39706 // form can return { errors: .... }
39708 //act.result.errors // invalid form element list...
39709 //act.result.errorMsg// invalid form element list...
39711 this.dialog.el.unmask();
39712 Roo.MessageBox.alert("Error", act.result.errorMsg ? act.result.errorMsg :
39713 "Login failed - communication error - try again.");
39716 actioncomplete: function(re, act) {
39718 Roo.state.Manager.set(
39719 this.dialog.realm + '.username',
39720 this.findField('username').getValue()
39722 Roo.state.Manager.set(
39723 this.dialog.realm + '.lang',
39724 this.findField('lang').getValue()
39727 this.dialog.fillAuth(act.result.data);
39729 this.dialog.hide();
39731 if (Roo.get('loading-mask')) {
39732 Roo.get('loading-mask').show();
39734 Roo.XComponent.build();
39742 xtype : 'TextField',
39744 fieldLabel: "Email Address",
39747 autoCreate : {tag: "input", type: "text", size: "20"}
39750 xtype : 'TextField',
39752 fieldLabel: "Password",
39753 inputType: 'password',
39756 autoCreate : {tag: "input", type: "text", size: "20"},
39758 specialkey : function(e,ev) {
39759 if (ev.keyCode == 13) {
39760 this.form.dialog.el.mask("Logging in");
39761 this.form.doAction('submit', {
39762 url: this.form.dialog.url,
39763 method: this.form.dialog.method
39770 xtype : 'ComboBox',
39772 fieldLabel: "Language",
39775 xtype : 'SimpleStore',
39776 fields: ['lang', 'ldisp'],
39778 [ 'en', 'English' ],
39779 [ 'zh_HK' , '\u7E41\u4E2D' ],
39780 [ 'zh_CN', '\u7C21\u4E2D' ]
39784 valueField : 'lang',
39785 hiddenName: 'lang',
39787 displayField:'ldisp',
39791 triggerAction: 'all',
39792 emptyText:'Select a Language...',
39793 selectOnFocus:true,
39795 select : function(cb, rec, ix) {
39796 this.form.switchLang(rec.data.lang);
39812 text : "Forgot Password",
39814 click : function() {
39815 //console.log(this);
39816 var n = this.form.findField('username').getValue();
39818 Roo.MessageBox.alert("Error", "Fill in your email address");
39822 url: this.dialog.url,
39826 method: this.dialog.method,
39827 success: function(response, opts) { // check successfull...
39829 var res = this.dialog.processResponse(response);
39830 if (!res.success) { // error!
39831 Roo.MessageBox.alert("Error" ,
39832 res.errorMsg ? res.errorMsg : "Problem Requesting Password Reset");
39835 Roo.MessageBox.alert("Notice" ,
39836 "Please check you email for the Password Reset message");
39838 failure : function() {
39839 Roo.MessageBox.alert("Error" , "Problem Requesting Password Reset");
39852 click : function () {
39854 this.dialog.el.mask("Logging in");
39855 this.form.doAction('submit', {
39856 url: this.dialog.url,
39857 method: this.dialog.method