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);
569 Event.on(this.id, "mousedown", this.handleMouseDown, this);
571 Event.on(this.id, "touchstart", this.handleMouseDown, this);
572 // Event.on(this.id, "selectstart", Event.preventDefault);
576 * Initializes Targeting functionality only... the object does not
577 * get a mousedown handler.
579 * @param id the id of the linked element
580 * @param {String} sGroup the group of related items
581 * @param {object} config configuration attributes
583 initTarget: function(id, sGroup, config) {
585 // configuration attributes
586 this.config = config || {};
588 // create a local reference to the drag and drop manager
589 this.DDM = Roo.dd.DDM;
590 // initialize the groups array
593 // assume that we have an element reference instead of an id if the
594 // parameter is not a string
595 if (typeof id !== "string") {
602 // add to an interaction group
603 this.addToGroup((sGroup) ? sGroup : "default");
605 // We don't want to register this as the handle with the manager
606 // so we just set the id rather than calling the setter.
607 this.handleElId = id;
609 // the linked element is the element that gets dragged by default
610 this.setDragElId(id);
612 // by default, clicked anchors will not start drag operations.
613 this.invalidHandleTypes = { A: "A" };
614 this.invalidHandleIds = {};
615 this.invalidHandleClasses = [];
619 this.handleOnAvailable();
623 * Applies the configuration parameters that were passed into the constructor.
624 * This is supposed to happen at each level through the inheritance chain. So
625 * a DDProxy implentation will execute apply config on DDProxy, DD, and
626 * DragDrop in order to get all of the parameters that are available in
628 * @method applyConfig
630 applyConfig: function() {
632 // configurable properties:
633 // padding, isTarget, maintainOffset, primaryButtonOnly
634 this.padding = this.config.padding || [0, 0, 0, 0];
635 this.isTarget = (this.config.isTarget !== false);
636 this.maintainOffset = (this.config.maintainOffset);
637 this.primaryButtonOnly = (this.config.primaryButtonOnly !== false);
642 * Executed when the linked element is available
643 * @method handleOnAvailable
646 handleOnAvailable: function() {
647 this.available = true;
648 this.resetConstraints();
653 * Configures the padding for the target zone in px. Effectively expands
654 * (or reduces) the virtual object size for targeting calculations.
655 * Supports css-style shorthand; if only one parameter is passed, all sides
656 * will have that padding, and if only two are passed, the top and bottom
657 * will have the first param, the left and right the second.
659 * @param {int} iTop Top pad
660 * @param {int} iRight Right pad
661 * @param {int} iBot Bot pad
662 * @param {int} iLeft Left pad
664 setPadding: function(iTop, iRight, iBot, iLeft) {
665 // this.padding = [iLeft, iRight, iTop, iBot];
666 if (!iRight && 0 !== iRight) {
667 this.padding = [iTop, iTop, iTop, iTop];
668 } else if (!iBot && 0 !== iBot) {
669 this.padding = [iTop, iRight, iTop, iRight];
671 this.padding = [iTop, iRight, iBot, iLeft];
676 * Stores the initial placement of the linked element.
677 * @method setInitialPosition
678 * @param {int} diffX the X offset, default 0
679 * @param {int} diffY the Y offset, default 0
681 setInitPosition: function(diffX, diffY) {
682 var el = this.getEl();
684 if (!this.DDM.verifyEl(el)) {
691 var p = Dom.getXY( el );
693 this.initPageX = p[0] - dx;
694 this.initPageY = p[1] - dy;
696 this.lastPageX = p[0];
697 this.lastPageY = p[1];
700 this.setStartPosition(p);
704 * Sets the start position of the element. This is set when the obj
705 * is initialized, the reset when a drag is started.
706 * @method setStartPosition
707 * @param pos current position (from previous lookup)
710 setStartPosition: function(pos) {
711 var p = pos || Dom.getXY( this.getEl() );
712 this.deltaSetXY = null;
714 this.startPageX = p[0];
715 this.startPageY = p[1];
719 * Add this instance to a group of related drag/drop objects. All
720 * instances belong to at least one group, and can belong to as many
723 * @param sGroup {string} the name of the group
725 addToGroup: function(sGroup) {
726 this.groups[sGroup] = true;
727 this.DDM.regDragDrop(this, sGroup);
731 * Remove's this instance from the supplied interaction group
732 * @method removeFromGroup
733 * @param {string} sGroup The group to drop
735 removeFromGroup: function(sGroup) {
736 if (this.groups[sGroup]) {
737 delete this.groups[sGroup];
740 this.DDM.removeDDFromGroup(this, sGroup);
744 * Allows you to specify that an element other than the linked element
745 * will be moved with the cursor during a drag
746 * @method setDragElId
747 * @param id {string} the id of the element that will be used to initiate the drag
749 setDragElId: function(id) {
754 * Allows you to specify a child of the linked element that should be
755 * used to initiate the drag operation. An example of this would be if
756 * you have a content div with text and links. Clicking anywhere in the
757 * content area would normally start the drag operation. Use this method
758 * to specify that an element inside of the content div is the element
759 * that starts the drag operation.
760 * @method setHandleElId
761 * @param id {string} the id of the element that will be used to
764 setHandleElId: function(id) {
765 if (typeof id !== "string") {
768 this.handleElId = id;
769 this.DDM.regHandle(this.id, id);
773 * Allows you to set an element outside of the linked element as a drag
775 * @method setOuterHandleElId
776 * @param id the id of the element that will be used to initiate the drag
778 setOuterHandleElId: function(id) {
779 if (typeof id !== "string") {
782 Event.on(id, "mousedown",
783 this.handleMouseDown, this);
784 this.setHandleElId(id);
786 this.hasOuterHandles = true;
790 * Remove all drag and drop hooks for this element
794 Event.un(this.id, "mousedown",
795 this.handleMouseDown);
796 Event.un(this.id, "touchstart",
797 this.handleMouseDown);
799 this.DDM._remove(this);
802 destroy : function(){
807 * Returns true if this instance is locked, or the drag drop mgr is locked
808 * (meaning that all drag/drop is disabled on the page.)
810 * @return {boolean} true if this obj or all drag/drop is locked, else
813 isLocked: function() {
814 return (this.DDM.isLocked() || this.locked);
818 * Fired when this object is clicked
819 * @method handleMouseDown
821 * @param {Roo.dd.DragDrop} oDD the clicked dd object (this dd obj)
824 handleMouseDown: function(e, oDD){
825 if (this.primaryButtonOnly && e.button != 0) {
829 if (this.isLocked()) {
833 this.DDM.refreshCache(this.groups);
835 var pt = new Roo.lib.Point(Roo.lib.Event.getPageX(e), Roo.lib.Event.getPageY(e));
836 if (!this.hasOuterHandles && !this.DDM.isOverTarget(pt, this) ) {
838 if (this.clickValidator(e)) {
840 // set the initial element position
841 this.setStartPosition();
847 this.DDM.handleMouseDown(e, this);
849 this.DDM.stopEvent(e);
857 clickValidator: function(e) {
858 var target = e.getTarget();
859 return ( this.isValidHandleChild(target) &&
860 (this.id == this.handleElId ||
861 this.DDM.handleWasClicked(target, this.id)) );
865 * Allows you to specify a tag name that should not start a drag operation
866 * when clicked. This is designed to facilitate embedding links within a
867 * drag handle that do something other than start the drag.
868 * @method addInvalidHandleType
869 * @param {string} tagName the type of element to exclude
871 addInvalidHandleType: function(tagName) {
872 var type = tagName.toUpperCase();
873 this.invalidHandleTypes[type] = type;
877 * Lets you to specify an element id for a child of a drag handle
878 * that should not initiate a drag
879 * @method addInvalidHandleId
880 * @param {string} id the element id of the element you wish to ignore
882 addInvalidHandleId: function(id) {
883 if (typeof id !== "string") {
886 this.invalidHandleIds[id] = id;
890 * Lets you specify a css class of elements that will not initiate a drag
891 * @method addInvalidHandleClass
892 * @param {string} cssClass the class of the elements you wish to ignore
894 addInvalidHandleClass: function(cssClass) {
895 this.invalidHandleClasses.push(cssClass);
899 * Unsets an excluded tag name set by addInvalidHandleType
900 * @method removeInvalidHandleType
901 * @param {string} tagName the type of element to unexclude
903 removeInvalidHandleType: function(tagName) {
904 var type = tagName.toUpperCase();
905 // this.invalidHandleTypes[type] = null;
906 delete this.invalidHandleTypes[type];
910 * Unsets an invalid handle id
911 * @method removeInvalidHandleId
912 * @param {string} id the id of the element to re-enable
914 removeInvalidHandleId: function(id) {
915 if (typeof id !== "string") {
918 delete this.invalidHandleIds[id];
922 * Unsets an invalid css class
923 * @method removeInvalidHandleClass
924 * @param {string} cssClass the class of the element(s) you wish to
927 removeInvalidHandleClass: function(cssClass) {
928 for (var i=0, len=this.invalidHandleClasses.length; i<len; ++i) {
929 if (this.invalidHandleClasses[i] == cssClass) {
930 delete this.invalidHandleClasses[i];
936 * Checks the tag exclusion list to see if this click should be ignored
937 * @method isValidHandleChild
938 * @param {HTMLElement} node the HTMLElement to evaluate
939 * @return {boolean} true if this is a valid tag type, false if not
941 isValidHandleChild: function(node) {
944 // var n = (node.nodeName == "#text") ? node.parentNode : node;
947 nodeName = node.nodeName.toUpperCase();
949 nodeName = node.nodeName;
951 valid = valid && !this.invalidHandleTypes[nodeName];
952 valid = valid && !this.invalidHandleIds[node.id];
954 for (var i=0, len=this.invalidHandleClasses.length; valid && i<len; ++i) {
955 valid = !Dom.hasClass(node, this.invalidHandleClasses[i]);
964 * Create the array of horizontal tick marks if an interval was specified
965 * in setXConstraint().
969 setXTicks: function(iStartX, iTickSize) {
971 this.xTickSize = iTickSize;
975 for (var i = this.initPageX; i >= this.minX; i = i - iTickSize) {
977 this.xTicks[this.xTicks.length] = i;
982 for (i = this.initPageX; i <= this.maxX; i = i + iTickSize) {
984 this.xTicks[this.xTicks.length] = i;
989 this.xTicks.sort(this.DDM.numericSort) ;
993 * Create the array of vertical tick marks if an interval was specified in
998 setYTicks: function(iStartY, iTickSize) {
1000 this.yTickSize = iTickSize;
1004 for (var i = this.initPageY; i >= this.minY; i = i - iTickSize) {
1006 this.yTicks[this.yTicks.length] = i;
1011 for (i = this.initPageY; i <= this.maxY; i = i + iTickSize) {
1013 this.yTicks[this.yTicks.length] = i;
1018 this.yTicks.sort(this.DDM.numericSort) ;
1022 * By default, the element can be dragged any place on the screen. Use
1023 * this method to limit the horizontal travel of the element. Pass in
1024 * 0,0 for the parameters if you want to lock the drag to the y axis.
1025 * @method setXConstraint
1026 * @param {int} iLeft the number of pixels the element can move to the left
1027 * @param {int} iRight the number of pixels the element can move to the
1029 * @param {int} iTickSize optional parameter for specifying that the
1031 * should move iTickSize pixels at a time.
1033 setXConstraint: function(iLeft, iRight, iTickSize) {
1034 this.leftConstraint = iLeft;
1035 this.rightConstraint = iRight;
1037 this.minX = this.initPageX - iLeft;
1038 this.maxX = this.initPageX + iRight;
1039 if (iTickSize) { this.setXTicks(this.initPageX, iTickSize); }
1041 this.constrainX = true;
1045 * Clears any constraints applied to this instance. Also clears ticks
1046 * since they can't exist independent of a constraint at this time.
1047 * @method clearConstraints
1049 clearConstraints: function() {
1050 this.constrainX = false;
1051 this.constrainY = false;
1056 * Clears any tick interval defined for this instance
1057 * @method clearTicks
1059 clearTicks: function() {
1067 * By default, the element can be dragged any place on the screen. Set
1068 * this to limit the vertical travel of the element. Pass in 0,0 for the
1069 * parameters if you want to lock the drag to the x axis.
1070 * @method setYConstraint
1071 * @param {int} iUp the number of pixels the element can move up
1072 * @param {int} iDown the number of pixels the element can move down
1073 * @param {int} iTickSize optional parameter for specifying that the
1074 * element should move iTickSize pixels at a time.
1076 setYConstraint: function(iUp, iDown, iTickSize) {
1077 this.topConstraint = iUp;
1078 this.bottomConstraint = iDown;
1080 this.minY = this.initPageY - iUp;
1081 this.maxY = this.initPageY + iDown;
1082 if (iTickSize) { this.setYTicks(this.initPageY, iTickSize); }
1084 this.constrainY = true;
1089 * resetConstraints must be called if you manually reposition a dd element.
1090 * @method resetConstraints
1091 * @param {boolean} maintainOffset
1093 resetConstraints: function() {
1096 // Maintain offsets if necessary
1097 if (this.initPageX || this.initPageX === 0) {
1098 // figure out how much this thing has moved
1099 var dx = (this.maintainOffset) ? this.lastPageX - this.initPageX : 0;
1100 var dy = (this.maintainOffset) ? this.lastPageY - this.initPageY : 0;
1102 this.setInitPosition(dx, dy);
1104 // This is the first time we have detected the element's position
1106 this.setInitPosition();
1109 if (this.constrainX) {
1110 this.setXConstraint( this.leftConstraint,
1111 this.rightConstraint,
1115 if (this.constrainY) {
1116 this.setYConstraint( this.topConstraint,
1117 this.bottomConstraint,
1123 * Normally the drag element is moved pixel by pixel, but we can specify
1124 * that it move a number of pixels at a time. This method resolves the
1125 * location when we have it set up like this.
1127 * @param {int} val where we want to place the object
1128 * @param {int[]} tickArray sorted array of valid points
1129 * @return {int} the closest tick
1132 getTick: function(val, tickArray) {
1135 // If tick interval is not defined, it is effectively 1 pixel,
1136 // so we return the value passed to us.
1138 } else if (tickArray[0] >= val) {
1139 // The value is lower than the first tick, so we return the first
1141 return tickArray[0];
1143 for (var i=0, len=tickArray.length; i<len; ++i) {
1145 if (tickArray[next] && tickArray[next] >= val) {
1146 var diff1 = val - tickArray[i];
1147 var diff2 = tickArray[next] - val;
1148 return (diff2 > diff1) ? tickArray[i] : tickArray[next];
1152 // The value is larger than the last tick, so we return the last
1154 return tickArray[tickArray.length - 1];
1161 * @return {string} string representation of the dd obj
1163 toString: function() {
1164 return ("DragDrop " + this.id);
1172 * Ext JS Library 1.1.1
1173 * Copyright(c) 2006-2007, Ext JS, LLC.
1175 * Originally Released Under LGPL - original licence link has changed is not relivant.
1178 * <script type="text/javascript">
1183 * The drag and drop utility provides a framework for building drag and drop
1184 * applications. In addition to enabling drag and drop for specific elements,
1185 * the drag and drop elements are tracked by the manager class, and the
1186 * interactions between the various elements are tracked during the drag and
1187 * the implementing code is notified about these important moments.
1190 // Only load the library once. Rewriting the manager class would orphan
1191 // existing drag and drop instances.
1192 if (!Roo.dd.DragDropMgr) {
1195 * @class Roo.dd.DragDropMgr
1196 * DragDropMgr is a singleton that tracks the element interaction for
1197 * all DragDrop items in the window. Generally, you will not call
1198 * this class directly, but it does have helper methods that could
1199 * be useful in your DragDrop implementations.
1202 Roo.dd.DragDropMgr = function() {
1204 var Event = Roo.EventManager;
1209 * Two dimensional Array of registered DragDrop objects. The first
1210 * dimension is the DragDrop item group, the second the DragDrop
1213 * @type {string: string}
1220 * Array of element ids defined as drag handles. Used to determine
1221 * if the element that generated the mousedown event is actually the
1222 * handle and not the html element itself.
1223 * @property handleIds
1224 * @type {string: string}
1231 * the DragDrop object that is currently being dragged
1232 * @property dragCurrent
1240 * the DragDrop object(s) that are being hovered over
1241 * @property dragOvers
1249 * the X distance between the cursor and the object being dragged
1258 * the Y distance between the cursor and the object being dragged
1267 * Flag to determine if we should prevent the default behavior of the
1268 * events we define. By default this is true, but this can be set to
1269 * false if you need the default behavior (not recommended)
1270 * @property preventDefault
1274 preventDefault: true,
1277 * Flag to determine if we should stop the propagation of the events
1278 * we generate. This is true by default but you may want to set it to
1279 * false if the html element contains other features that require the
1281 * @property stopPropagation
1285 stopPropagation: true,
1288 * Internal flag that is set to true when drag and drop has been
1290 * @property initialized
1297 * All drag and drop can be disabled.
1305 * Called the first time an element is registered.
1311 this.initialized = true;
1315 * In point mode, drag and drop interaction is defined by the
1316 * location of the cursor during the drag/drop
1324 * In intersect mode, drag and drop interactio nis defined by the
1325 * overlap of two or more drag and drop objects.
1326 * @property INTERSECT
1333 * The current drag and drop mode. Default: POINT
1341 * Runs method on all drag and drop objects
1342 * @method _execOnAll
1346 _execOnAll: function(sMethod, args) {
1347 for (var i in this.ids) {
1348 for (var j in this.ids[i]) {
1349 var oDD = this.ids[i][j];
1350 if (! this.isTypeOfDD(oDD)) {
1353 oDD[sMethod].apply(oDD, args);
1359 * Drag and drop initialization. Sets up the global event handlers
1364 _onLoad: function() {
1369 Event.on(document, "mouseup", this.handleMouseUp, this, true);
1370 Event.on(document, "mousemove", this.handleMouseMove, this, true);
1372 Event.on(document, "touchend", this.handleMouseUp, this, true);
1373 Event.on(document, "touchmove", this.handleMouseMove, this, true);
1375 Event.on(window, "unload", this._onUnload, this, true);
1376 Event.on(window, "resize", this._onResize, this, true);
1377 // Event.on(window, "mouseout", this._test);
1382 * Reset constraints on all drag and drop objs
1387 _onResize: function(e) {
1388 this._execOnAll("resetConstraints", []);
1392 * Lock all drag and drop functionality
1396 lock: function() { this.locked = true; },
1399 * Unlock all drag and drop functionality
1403 unlock: function() { this.locked = false; },
1406 * Is drag and drop locked?
1408 * @return {boolean} True if drag and drop is locked, false otherwise.
1411 isLocked: function() { return this.locked; },
1414 * Location cache that is set for all drag drop objects when a drag is
1415 * initiated, cleared when the drag is finished.
1416 * @property locationCache
1423 * Set useCache to false if you want to force object the lookup of each
1424 * drag and drop linked element constantly during a drag.
1425 * @property useCache
1432 * The number of pixels that the mouse needs to move after the
1433 * mousedown before the drag is initiated. Default=3;
1434 * @property clickPixelThresh
1438 clickPixelThresh: 3,
1441 * The number of milliseconds after the mousedown event to initiate the
1442 * drag if we don't get a mouseup event. Default=1000
1443 * @property clickTimeThresh
1447 clickTimeThresh: 350,
1450 * Flag that indicates that either the drag pixel threshold or the
1451 * mousdown time threshold has been met
1452 * @property dragThreshMet
1457 dragThreshMet: false,
1460 * Timeout used for the click time threshold
1461 * @property clickTimeout
1469 * The X position of the mousedown event stored for later use when a
1470 * drag threshold is met.
1479 * The Y position of the mousedown event stored for later use when a
1480 * drag threshold is met.
1489 * Each DragDrop instance must be registered with the DragDropMgr.
1490 * This is executed in DragDrop.init()
1491 * @method regDragDrop
1492 * @param {DragDrop} oDD the DragDrop object to register
1493 * @param {String} sGroup the name of the group this element belongs to
1496 regDragDrop: function(oDD, sGroup) {
1497 if (!this.initialized) { this.init(); }
1499 if (!this.ids[sGroup]) {
1500 this.ids[sGroup] = {};
1502 this.ids[sGroup][oDD.id] = oDD;
1506 * Removes the supplied dd instance from the supplied group. Executed
1507 * by DragDrop.removeFromGroup, so don't call this function directly.
1508 * @method removeDDFromGroup
1512 removeDDFromGroup: function(oDD, sGroup) {
1513 if (!this.ids[sGroup]) {
1514 this.ids[sGroup] = {};
1517 var obj = this.ids[sGroup];
1518 if (obj && obj[oDD.id]) {
1524 * Unregisters a drag and drop item. This is executed in
1525 * DragDrop.unreg, use that method instead of calling this directly.
1530 _remove: function(oDD) {
1531 for (var g in oDD.groups) {
1532 if (g && this.ids[g][oDD.id]) {
1533 delete this.ids[g][oDD.id];
1536 delete this.handleIds[oDD.id];
1540 * Each DragDrop handle element must be registered. This is done
1541 * automatically when executing DragDrop.setHandleElId()
1543 * @param {String} sDDId the DragDrop id this element is a handle for
1544 * @param {String} sHandleId the id of the element that is the drag
1548 regHandle: function(sDDId, sHandleId) {
1549 if (!this.handleIds[sDDId]) {
1550 this.handleIds[sDDId] = {};
1552 this.handleIds[sDDId][sHandleId] = sHandleId;
1556 * Utility function to determine if a given element has been
1557 * registered as a drag drop item.
1558 * @method isDragDrop
1559 * @param {String} id the element id to check
1560 * @return {boolean} true if this element is a DragDrop item,
1564 isDragDrop: function(id) {
1565 return ( this.getDDById(id) ) ? true : false;
1569 * Returns the drag and drop instances that are in all groups the
1570 * passed in instance belongs to.
1571 * @method getRelated
1572 * @param {DragDrop} p_oDD the obj to get related data for
1573 * @param {boolean} bTargetsOnly if true, only return targetable objs
1574 * @return {DragDrop[]} the related instances
1577 getRelated: function(p_oDD, bTargetsOnly) {
1579 for (var i in p_oDD.groups) {
1580 for (j in this.ids[i]) {
1581 var dd = this.ids[i][j];
1582 if (! this.isTypeOfDD(dd)) {
1585 if (!bTargetsOnly || dd.isTarget) {
1586 oDDs[oDDs.length] = dd;
1595 * Returns true if the specified dd target is a legal target for
1596 * the specifice drag obj
1597 * @method isLegalTarget
1598 * @param {DragDrop} the drag obj
1599 * @param {DragDrop} the target
1600 * @return {boolean} true if the target is a legal target for the
1604 isLegalTarget: function (oDD, oTargetDD) {
1605 var targets = this.getRelated(oDD, true);
1606 for (var i=0, len=targets.length;i<len;++i) {
1607 if (targets[i].id == oTargetDD.id) {
1616 * My goal is to be able to transparently determine if an object is
1617 * typeof DragDrop, and the exact subclass of DragDrop. typeof
1618 * returns "object", oDD.constructor.toString() always returns
1619 * "DragDrop" and not the name of the subclass. So for now it just
1620 * evaluates a well-known variable in DragDrop.
1621 * @method isTypeOfDD
1622 * @param {Object} the object to evaluate
1623 * @return {boolean} true if typeof oDD = DragDrop
1626 isTypeOfDD: function (oDD) {
1627 return (oDD && oDD.__ygDragDrop);
1631 * Utility function to determine if a given element has been
1632 * registered as a drag drop handle for the given Drag Drop object.
1634 * @param {String} id the element id to check
1635 * @return {boolean} true if this element is a DragDrop handle, false
1639 isHandle: function(sDDId, sHandleId) {
1640 return ( this.handleIds[sDDId] &&
1641 this.handleIds[sDDId][sHandleId] );
1645 * Returns the DragDrop instance for a given id
1647 * @param {String} id the id of the DragDrop object
1648 * @return {DragDrop} the drag drop object, null if it is not found
1651 getDDById: function(id) {
1652 for (var i in this.ids) {
1653 if (this.ids[i][id]) {
1654 return this.ids[i][id];
1661 * Fired after a registered DragDrop object gets the mousedown event.
1662 * Sets up the events required to track the object being dragged
1663 * @method handleMouseDown
1664 * @param {Event} e the event
1665 * @param oDD the DragDrop object being dragged
1669 handleMouseDown: function(e, oDD) {
1671 Roo.QuickTips.disable();
1673 this.currentTarget = e.getTarget();
1675 this.dragCurrent = oDD;
1677 var el = oDD.getEl();
1679 // track start position
1680 this.startX = e.getPageX();
1681 this.startY = e.getPageY();
1683 this.deltaX = this.startX - el.offsetLeft;
1684 this.deltaY = this.startY - el.offsetTop;
1686 this.dragThreshMet = false;
1688 this.clickTimeout = setTimeout(
1690 var DDM = Roo.dd.DDM;
1691 DDM.startDrag(DDM.startX, DDM.startY);
1693 this.clickTimeThresh );
1697 * Fired when either the drag pixel threshol or the mousedown hold
1698 * time threshold has been met.
1700 * @param x {int} the X position of the original mousedown
1701 * @param y {int} the Y position of the original mousedown
1704 startDrag: function(x, y) {
1705 clearTimeout(this.clickTimeout);
1706 if (this.dragCurrent) {
1707 this.dragCurrent.b4StartDrag(x, y);
1708 this.dragCurrent.startDrag(x, y);
1710 this.dragThreshMet = true;
1714 * Internal function to handle the mouseup event. Will be invoked
1715 * from the context of the document.
1716 * @method handleMouseUp
1717 * @param {Event} e the event
1721 handleMouseUp: function(e) {
1724 Roo.QuickTips.enable();
1726 if (! this.dragCurrent) {
1730 clearTimeout(this.clickTimeout);
1732 if (this.dragThreshMet) {
1733 this.fireEvents(e, true);
1743 * Utility to stop event propagation and event default, if these
1744 * features are turned on.
1746 * @param {Event} e the event as returned by this.getEvent()
1749 stopEvent: function(e){
1750 if(this.stopPropagation) {
1751 e.stopPropagation();
1754 if (this.preventDefault) {
1760 * Internal function to clean up event handlers after the drag
1761 * operation is complete
1763 * @param {Event} e the event
1767 stopDrag: function(e) {
1768 // Fire the drag end event for the item that was dragged
1769 if (this.dragCurrent) {
1770 if (this.dragThreshMet) {
1771 this.dragCurrent.b4EndDrag(e);
1772 this.dragCurrent.endDrag(e);
1775 this.dragCurrent.onMouseUp(e);
1778 this.dragCurrent = null;
1779 this.dragOvers = {};
1783 * Internal function to handle the mousemove event. Will be invoked
1784 * from the context of the html element.
1786 * @TODO figure out what we can do about mouse events lost when the
1787 * user drags objects beyond the window boundary. Currently we can
1788 * detect this in internet explorer by verifying that the mouse is
1789 * down during the mousemove event. Firefox doesn't give us the
1790 * button state on the mousemove event.
1791 * @method handleMouseMove
1792 * @param {Event} e the event
1796 handleMouseMove: function(e) {
1797 if (! this.dragCurrent) {
1801 // var button = e.which || e.button;
1803 // check for IE mouseup outside of page boundary
1804 if (Roo.isIE && (e.button !== 0 && e.button !== 1 && e.button !== 2)) {
1806 return this.handleMouseUp(e);
1809 if (!this.dragThreshMet) {
1810 var diffX = Math.abs(this.startX - e.getPageX());
1811 var diffY = Math.abs(this.startY - e.getPageY());
1812 if (diffX > this.clickPixelThresh ||
1813 diffY > this.clickPixelThresh) {
1814 this.startDrag(this.startX, this.startY);
1818 if (this.dragThreshMet) {
1819 this.dragCurrent.b4Drag(e);
1820 this.dragCurrent.onDrag(e);
1821 if(!this.dragCurrent.moveOnly){
1822 this.fireEvents(e, false);
1832 * Iterates over all of the DragDrop elements to find ones we are
1833 * hovering over or dropping on
1834 * @method fireEvents
1835 * @param {Event} e the event
1836 * @param {boolean} isDrop is this a drop op or a mouseover op?
1840 fireEvents: function(e, isDrop) {
1841 var dc = this.dragCurrent;
1843 // If the user did the mouse up outside of the window, we could
1844 // get here even though we have ended the drag.
1845 if (!dc || dc.isLocked()) {
1849 var pt = e.getPoint();
1851 // cache the previous dragOver array
1859 // Check to see if the object(s) we were hovering over is no longer
1860 // being hovered over so we can fire the onDragOut event
1861 for (var i in this.dragOvers) {
1863 var ddo = this.dragOvers[i];
1865 if (! this.isTypeOfDD(ddo)) {
1869 if (! this.isOverTarget(pt, ddo, this.mode)) {
1870 outEvts.push( ddo );
1874 delete this.dragOvers[i];
1877 for (var sGroup in dc.groups) {
1879 if ("string" != typeof sGroup) {
1883 for (i in this.ids[sGroup]) {
1884 var oDD = this.ids[sGroup][i];
1885 if (! this.isTypeOfDD(oDD)) {
1889 if (oDD.isTarget && !oDD.isLocked() && oDD != dc) {
1890 if (this.isOverTarget(pt, oDD, this.mode)) {
1891 // look for drop interactions
1893 dropEvts.push( oDD );
1894 // look for drag enter and drag over interactions
1897 // initial drag over: dragEnter fires
1898 if (!oldOvers[oDD.id]) {
1899 enterEvts.push( oDD );
1900 // subsequent drag overs: dragOver fires
1902 overEvts.push( oDD );
1905 this.dragOvers[oDD.id] = oDD;
1913 if (outEvts.length) {
1914 dc.b4DragOut(e, outEvts);
1915 dc.onDragOut(e, outEvts);
1918 if (enterEvts.length) {
1919 dc.onDragEnter(e, enterEvts);
1922 if (overEvts.length) {
1923 dc.b4DragOver(e, overEvts);
1924 dc.onDragOver(e, overEvts);
1927 if (dropEvts.length) {
1928 dc.b4DragDrop(e, dropEvts);
1929 dc.onDragDrop(e, dropEvts);
1933 // fire dragout events
1935 for (i=0, len=outEvts.length; i<len; ++i) {
1936 dc.b4DragOut(e, outEvts[i].id);
1937 dc.onDragOut(e, outEvts[i].id);
1940 // fire enter events
1941 for (i=0,len=enterEvts.length; i<len; ++i) {
1942 // dc.b4DragEnter(e, oDD.id);
1943 dc.onDragEnter(e, enterEvts[i].id);
1947 for (i=0,len=overEvts.length; i<len; ++i) {
1948 dc.b4DragOver(e, overEvts[i].id);
1949 dc.onDragOver(e, overEvts[i].id);
1953 for (i=0, len=dropEvts.length; i<len; ++i) {
1954 dc.b4DragDrop(e, dropEvts[i].id);
1955 dc.onDragDrop(e, dropEvts[i].id);
1960 // notify about a drop that did not find a target
1961 if (isDrop && !dropEvts.length) {
1962 dc.onInvalidDrop(e);
1968 * Helper function for getting the best match from the list of drag
1969 * and drop objects returned by the drag and drop events when we are
1970 * in INTERSECT mode. It returns either the first object that the
1971 * cursor is over, or the object that has the greatest overlap with
1972 * the dragged element.
1973 * @method getBestMatch
1974 * @param {DragDrop[]} dds The array of drag and drop objects
1976 * @return {DragDrop} The best single match
1979 getBestMatch: function(dds) {
1981 // Return null if the input is not what we expect
1982 //if (!dds || !dds.length || dds.length == 0) {
1984 // If there is only one item, it wins
1985 //} else if (dds.length == 1) {
1987 var len = dds.length;
1992 // Loop through the targeted items
1993 for (var i=0; i<len; ++i) {
1995 // If the cursor is over the object, it wins. If the
1996 // cursor is over multiple matches, the first one we come
1998 if (dd.cursorIsOver) {
2001 // Otherwise the object with the most overlap wins
2004 winner.overlap.getArea() < dd.overlap.getArea()) {
2015 * Refreshes the cache of the top-left and bottom-right points of the
2016 * drag and drop objects in the specified group(s). This is in the
2017 * format that is stored in the drag and drop instance, so typical
2020 * Roo.dd.DragDropMgr.refreshCache(ddinstance.groups);
2024 * Roo.dd.DragDropMgr.refreshCache({group1:true, group2:true});
2026 * @TODO this really should be an indexed array. Alternatively this
2027 * method could accept both.
2028 * @method refreshCache
2029 * @param {Object} groups an associative array of groups to refresh
2032 refreshCache: function(groups) {
2033 for (var sGroup in groups) {
2034 if ("string" != typeof sGroup) {
2037 for (var i in this.ids[sGroup]) {
2038 var oDD = this.ids[sGroup][i];
2040 if (this.isTypeOfDD(oDD)) {
2041 // if (this.isTypeOfDD(oDD) && oDD.isTarget) {
2042 var loc = this.getLocation(oDD);
2044 this.locationCache[oDD.id] = loc;
2046 delete this.locationCache[oDD.id];
2047 // this will unregister the drag and drop object if
2048 // the element is not in a usable state
2057 * This checks to make sure an element exists and is in the DOM. The
2058 * main purpose is to handle cases where innerHTML is used to remove
2059 * drag and drop objects from the DOM. IE provides an 'unspecified
2060 * error' when trying to access the offsetParent of such an element
2062 * @param {HTMLElement} el the element to check
2063 * @return {boolean} true if the element looks usable
2066 verifyEl: function(el) {
2071 parent = el.offsetParent;
2074 parent = el.offsetParent;
2085 * Returns a Region object containing the drag and drop element's position
2086 * and size, including the padding configured for it
2087 * @method getLocation
2088 * @param {DragDrop} oDD the drag and drop object to get the
2090 * @return {Roo.lib.Region} a Region object representing the total area
2091 * the element occupies, including any padding
2092 * the instance is configured for.
2095 getLocation: function(oDD) {
2096 if (! this.isTypeOfDD(oDD)) {
2100 var el = oDD.getEl(), pos, x1, x2, y1, y2, t, r, b, l;
2103 pos= Roo.lib.Dom.getXY(el);
2111 x2 = x1 + el.offsetWidth;
2113 y2 = y1 + el.offsetHeight;
2115 t = y1 - oDD.padding[0];
2116 r = x2 + oDD.padding[1];
2117 b = y2 + oDD.padding[2];
2118 l = x1 - oDD.padding[3];
2120 return new Roo.lib.Region( t, r, b, l );
2124 * Checks the cursor location to see if it over the target
2125 * @method isOverTarget
2126 * @param {Roo.lib.Point} pt The point to evaluate
2127 * @param {DragDrop} oTarget the DragDrop object we are inspecting
2128 * @return {boolean} true if the mouse is over the target
2132 isOverTarget: function(pt, oTarget, intersect) {
2133 // use cache if available
2134 var loc = this.locationCache[oTarget.id];
2135 if (!loc || !this.useCache) {
2136 loc = this.getLocation(oTarget);
2137 this.locationCache[oTarget.id] = loc;
2145 oTarget.cursorIsOver = loc.contains( pt );
2147 // DragDrop is using this as a sanity check for the initial mousedown
2148 // in this case we are done. In POINT mode, if the drag obj has no
2149 // contraints, we are also done. Otherwise we need to evaluate the
2150 // location of the target as related to the actual location of the
2152 var dc = this.dragCurrent;
2153 if (!dc || !dc.getTargetCoord ||
2154 (!intersect && !dc.constrainX && !dc.constrainY)) {
2155 return oTarget.cursorIsOver;
2158 oTarget.overlap = null;
2160 // Get the current location of the drag element, this is the
2161 // location of the mouse event less the delta that represents
2162 // where the original mousedown happened on the element. We
2163 // need to consider constraints and ticks as well.
2164 var pos = dc.getTargetCoord(pt.x, pt.y);
2166 var el = dc.getDragEl();
2167 var curRegion = new Roo.lib.Region( pos.y,
2168 pos.x + el.offsetWidth,
2169 pos.y + el.offsetHeight,
2172 var overlap = curRegion.intersect(loc);
2175 oTarget.overlap = overlap;
2176 return (intersect) ? true : oTarget.cursorIsOver;
2183 * unload event handler
2188 _onUnload: function(e, me) {
2189 Roo.dd.DragDropMgr.unregAll();
2193 * Cleans up the drag and drop events and objects.
2198 unregAll: function() {
2200 if (this.dragCurrent) {
2202 this.dragCurrent = null;
2205 this._execOnAll("unreg", []);
2207 for (i in this.elementCache) {
2208 delete this.elementCache[i];
2211 this.elementCache = {};
2216 * A cache of DOM elements
2217 * @property elementCache
2224 * Get the wrapper for the DOM element specified
2225 * @method getElWrapper
2226 * @param {String} id the id of the element to get
2227 * @return {Roo.dd.DDM.ElementWrapper} the wrapped element
2229 * @deprecated This wrapper isn't that useful
2232 getElWrapper: function(id) {
2233 var oWrapper = this.elementCache[id];
2234 if (!oWrapper || !oWrapper.el) {
2235 oWrapper = this.elementCache[id] =
2236 new this.ElementWrapper(Roo.getDom(id));
2242 * Returns the actual DOM element
2243 * @method getElement
2244 * @param {String} id the id of the elment to get
2245 * @return {Object} The element
2246 * @deprecated use Roo.getDom instead
2249 getElement: function(id) {
2250 return Roo.getDom(id);
2254 * Returns the style property for the DOM element (i.e.,
2255 * document.getElById(id).style)
2257 * @param {String} id the id of the elment to get
2258 * @return {Object} The style property of the element
2259 * @deprecated use Roo.getDom instead
2262 getCss: function(id) {
2263 var el = Roo.getDom(id);
2264 return (el) ? el.style : null;
2268 * Inner class for cached elements
2269 * @class DragDropMgr.ElementWrapper
2274 ElementWrapper: function(el) {
2279 this.el = el || null;
2284 this.id = this.el && el.id;
2286 * A reference to the style property
2289 this.css = this.el && el.style;
2293 * Returns the X position of an html element
2295 * @param el the element for which to get the position
2296 * @return {int} the X coordinate
2298 * @deprecated use Roo.lib.Dom.getX instead
2301 getPosX: function(el) {
2302 return Roo.lib.Dom.getX(el);
2306 * Returns the Y position of an html element
2308 * @param el the element for which to get the position
2309 * @return {int} the Y coordinate
2310 * @deprecated use Roo.lib.Dom.getY instead
2313 getPosY: function(el) {
2314 return Roo.lib.Dom.getY(el);
2318 * Swap two nodes. In IE, we use the native method, for others we
2319 * emulate the IE behavior
2321 * @param n1 the first node to swap
2322 * @param n2 the other node to swap
2325 swapNode: function(n1, n2) {
2329 var p = n2.parentNode;
2330 var s = n2.nextSibling;
2333 p.insertBefore(n1, n2);
2334 } else if (n2 == n1.nextSibling) {
2335 p.insertBefore(n2, n1);
2337 n1.parentNode.replaceChild(n2, n1);
2338 p.insertBefore(n1, s);
2344 * Returns the current scroll position
2349 getScroll: function () {
2350 var t, l, dde=document.documentElement, db=document.body;
2351 if (dde && (dde.scrollTop || dde.scrollLeft)) {
2360 return { top: t, left: l };
2364 * Returns the specified element style property
2366 * @param {HTMLElement} el the element
2367 * @param {string} styleProp the style property
2368 * @return {string} The value of the style property
2369 * @deprecated use Roo.lib.Dom.getStyle
2372 getStyle: function(el, styleProp) {
2373 return Roo.fly(el).getStyle(styleProp);
2377 * Gets the scrollTop
2378 * @method getScrollTop
2379 * @return {int} the document's scrollTop
2382 getScrollTop: function () { return this.getScroll().top; },
2385 * Gets the scrollLeft
2386 * @method getScrollLeft
2387 * @return {int} the document's scrollTop
2390 getScrollLeft: function () { return this.getScroll().left; },
2393 * Sets the x/y position of an element to the location of the
2396 * @param {HTMLElement} moveEl The element to move
2397 * @param {HTMLElement} targetEl The position reference element
2400 moveToEl: function (moveEl, targetEl) {
2401 var aCoord = Roo.lib.Dom.getXY(targetEl);
2402 Roo.lib.Dom.setXY(moveEl, aCoord);
2406 * Numeric array sort function
2407 * @method numericSort
2410 numericSort: function(a, b) { return (a - b); },
2414 * @property _timeoutCount
2421 * Trying to make the load order less important. Without this we get
2422 * an error if this file is loaded before the Event Utility.
2423 * @method _addListeners
2427 _addListeners: function() {
2428 var DDM = Roo.dd.DDM;
2429 if ( Roo.lib.Event && document ) {
2432 if (DDM._timeoutCount > 2000) {
2434 setTimeout(DDM._addListeners, 10);
2435 if (document && document.body) {
2436 DDM._timeoutCount += 1;
2443 * Recursively searches the immediate parent and all child nodes for
2444 * the handle element in order to determine wheter or not it was
2446 * @method handleWasClicked
2447 * @param node the html element to inspect
2450 handleWasClicked: function(node, id) {
2451 if (this.isHandle(id, node.id)) {
2454 // check to see if this is a text node child of the one we want
2455 var p = node.parentNode;
2458 if (this.isHandle(id, p.id)) {
2473 // shorter alias, save a few bytes
2474 Roo.dd.DDM = Roo.dd.DragDropMgr;
2475 Roo.dd.DDM._addListeners();
2479 * Ext JS Library 1.1.1
2480 * Copyright(c) 2006-2007, Ext JS, LLC.
2482 * Originally Released Under LGPL - original licence link has changed is not relivant.
2485 * <script type="text/javascript">
2490 * A DragDrop implementation where the linked element follows the
2491 * mouse cursor during a drag.
2492 * @extends Roo.dd.DragDrop
2494 * @param {String} id the id of the linked element
2495 * @param {String} sGroup the group of related DragDrop items
2496 * @param {object} config an object containing configurable attributes
2497 * Valid properties for DD:
2500 Roo.dd.DD = function(id, sGroup, config) {
2502 this.init(id, sGroup, config);
2506 Roo.extend(Roo.dd.DD, Roo.dd.DragDrop, {
2509 * When set to true, the utility automatically tries to scroll the browser
2510 * window wehn a drag and drop element is dragged near the viewport boundary.
2518 * Sets the pointer offset to the distance between the linked element's top
2519 * left corner and the location the element was clicked
2520 * @method autoOffset
2521 * @param {int} iPageX the X coordinate of the click
2522 * @param {int} iPageY the Y coordinate of the click
2524 autoOffset: function(iPageX, iPageY) {
2525 var x = iPageX - this.startPageX;
2526 var y = iPageY - this.startPageY;
2527 this.setDelta(x, y);
2531 * Sets the pointer offset. You can call this directly to force the
2532 * offset to be in a particular location (e.g., pass in 0,0 to set it
2533 * to the center of the object)
2535 * @param {int} iDeltaX the distance from the left
2536 * @param {int} iDeltaY the distance from the top
2538 setDelta: function(iDeltaX, iDeltaY) {
2539 this.deltaX = iDeltaX;
2540 this.deltaY = iDeltaY;
2544 * Sets the drag element to the location of the mousedown or click event,
2545 * maintaining the cursor location relative to the location on the element
2546 * that was clicked. Override this if you want to place the element in a
2547 * location other than where the cursor is.
2548 * @method setDragElPos
2549 * @param {int} iPageX the X coordinate of the mousedown or drag event
2550 * @param {int} iPageY the Y coordinate of the mousedown or drag event
2552 setDragElPos: function(iPageX, iPageY) {
2553 // the first time we do this, we are going to check to make sure
2554 // the element has css positioning
2556 var el = this.getDragEl();
2557 this.alignElWithMouse(el, iPageX, iPageY);
2561 * Sets the element to the location of the mousedown or click event,
2562 * maintaining the cursor location relative to the location on the element
2563 * that was clicked. Override this if you want to place the element in a
2564 * location other than where the cursor is.
2565 * @method alignElWithMouse
2566 * @param {HTMLElement} el the element to move
2567 * @param {int} iPageX the X coordinate of the mousedown or drag event
2568 * @param {int} iPageY the Y coordinate of the mousedown or drag event
2570 alignElWithMouse: function(el, iPageX, iPageY) {
2571 var oCoord = this.getTargetCoord(iPageX, iPageY);
2572 var fly = el.dom ? el : Roo.fly(el);
2573 if (!this.deltaSetXY) {
2574 var aCoord = [oCoord.x, oCoord.y];
2576 var newLeft = fly.getLeft(true);
2577 var newTop = fly.getTop(true);
2578 this.deltaSetXY = [ newLeft - oCoord.x, newTop - oCoord.y ];
2580 fly.setLeftTop(oCoord.x + this.deltaSetXY[0], oCoord.y + this.deltaSetXY[1]);
2583 this.cachePosition(oCoord.x, oCoord.y);
2584 this.autoScroll(oCoord.x, oCoord.y, el.offsetHeight, el.offsetWidth);
2589 * Saves the most recent position so that we can reset the constraints and
2590 * tick marks on-demand. We need to know this so that we can calculate the
2591 * number of pixels the element is offset from its original position.
2592 * @method cachePosition
2593 * @param iPageX the current x position (optional, this just makes it so we
2594 * don't have to look it up again)
2595 * @param iPageY the current y position (optional, this just makes it so we
2596 * don't have to look it up again)
2598 cachePosition: function(iPageX, iPageY) {
2600 this.lastPageX = iPageX;
2601 this.lastPageY = iPageY;
2603 var aCoord = Roo.lib.Dom.getXY(this.getEl());
2604 this.lastPageX = aCoord[0];
2605 this.lastPageY = aCoord[1];
2610 * Auto-scroll the window if the dragged object has been moved beyond the
2611 * visible window boundary.
2612 * @method autoScroll
2613 * @param {int} x the drag element's x position
2614 * @param {int} y the drag element's y position
2615 * @param {int} h the height of the drag element
2616 * @param {int} w the width of the drag element
2619 autoScroll: function(x, y, h, w) {
2622 // The client height
2623 var clientH = Roo.lib.Dom.getViewWidth();
2626 var clientW = Roo.lib.Dom.getViewHeight();
2628 // The amt scrolled down
2629 var st = this.DDM.getScrollTop();
2631 // The amt scrolled right
2632 var sl = this.DDM.getScrollLeft();
2634 // Location of the bottom of the element
2637 // Location of the right of the element
2640 // The distance from the cursor to the bottom of the visible area,
2641 // adjusted so that we don't scroll if the cursor is beyond the
2642 // element drag constraints
2643 var toBot = (clientH + st - y - this.deltaY);
2645 // The distance from the cursor to the right of the visible area
2646 var toRight = (clientW + sl - x - this.deltaX);
2649 // How close to the edge the cursor must be before we scroll
2650 // var thresh = (document.all) ? 100 : 40;
2653 // How many pixels to scroll per autoscroll op. This helps to reduce
2654 // clunky scrolling. IE is more sensitive about this ... it needs this
2655 // value to be higher.
2656 var scrAmt = (document.all) ? 80 : 30;
2658 // Scroll down if we are near the bottom of the visible page and the
2659 // obj extends below the crease
2660 if ( bot > clientH && toBot < thresh ) {
2661 window.scrollTo(sl, st + scrAmt);
2664 // Scroll up if the window is scrolled down and the top of the object
2665 // goes above the top border
2666 if ( y < st && st > 0 && y - st < thresh ) {
2667 window.scrollTo(sl, st - scrAmt);
2670 // Scroll right if the obj is beyond the right border and the cursor is
2672 if ( right > clientW && toRight < thresh ) {
2673 window.scrollTo(sl + scrAmt, st);
2676 // Scroll left if the window has been scrolled to the right and the obj
2677 // extends past the left border
2678 if ( x < sl && sl > 0 && x - sl < thresh ) {
2679 window.scrollTo(sl - scrAmt, st);
2685 * Finds the location the element should be placed if we want to move
2686 * it to where the mouse location less the click offset would place us.
2687 * @method getTargetCoord
2688 * @param {int} iPageX the X coordinate of the click
2689 * @param {int} iPageY the Y coordinate of the click
2690 * @return an object that contains the coordinates (Object.x and Object.y)
2693 getTargetCoord: function(iPageX, iPageY) {
2696 var x = iPageX - this.deltaX;
2697 var y = iPageY - this.deltaY;
2699 if (this.constrainX) {
2700 if (x < this.minX) { x = this.minX; }
2701 if (x > this.maxX) { x = this.maxX; }
2704 if (this.constrainY) {
2705 if (y < this.minY) { y = this.minY; }
2706 if (y > this.maxY) { y = this.maxY; }
2709 x = this.getTick(x, this.xTicks);
2710 y = this.getTick(y, this.yTicks);
2717 * Sets up config options specific to this class. Overrides
2718 * Roo.dd.DragDrop, but all versions of this method through the
2719 * inheritance chain are called
2721 applyConfig: function() {
2722 Roo.dd.DD.superclass.applyConfig.call(this);
2723 this.scroll = (this.config.scroll !== false);
2727 * Event that fires prior to the onMouseDown event. Overrides
2730 b4MouseDown: function(e) {
2731 // this.resetConstraints();
2732 this.autoOffset(e.getPageX(),
2737 * Event that fires prior to the onDrag event. Overrides
2740 b4Drag: function(e) {
2741 this.setDragElPos(e.getPageX(),
2745 toString: function() {
2746 return ("DD " + this.id);
2749 //////////////////////////////////////////////////////////////////////////
2750 // Debugging ygDragDrop events that can be overridden
2751 //////////////////////////////////////////////////////////////////////////
2753 startDrag: function(x, y) {
2756 onDrag: function(e) {
2759 onDragEnter: function(e, id) {
2762 onDragOver: function(e, id) {
2765 onDragOut: function(e, id) {
2768 onDragDrop: function(e, id) {
2771 endDrag: function(e) {
2778 * Ext JS Library 1.1.1
2779 * Copyright(c) 2006-2007, Ext JS, LLC.
2781 * Originally Released Under LGPL - original licence link has changed is not relivant.
2784 * <script type="text/javascript">
2788 * @class Roo.dd.DDProxy
2789 * A DragDrop implementation that inserts an empty, bordered div into
2790 * the document that follows the cursor during drag operations. At the time of
2791 * the click, the frame div is resized to the dimensions of the linked html
2792 * element, and moved to the exact location of the linked element.
2794 * References to the "frame" element refer to the single proxy element that
2795 * was created to be dragged in place of all DDProxy elements on the
2798 * @extends Roo.dd.DD
2800 * @param {String} id the id of the linked html element
2801 * @param {String} sGroup the group of related DragDrop objects
2802 * @param {object} config an object containing configurable attributes
2803 * Valid properties for DDProxy in addition to those in DragDrop:
2804 * resizeFrame, centerFrame, dragElId
2806 Roo.dd.DDProxy = function(id, sGroup, config) {
2808 this.init(id, sGroup, config);
2814 * The default drag frame div id
2815 * @property Roo.dd.DDProxy.dragElId
2819 Roo.dd.DDProxy.dragElId = "ygddfdiv";
2821 Roo.extend(Roo.dd.DDProxy, Roo.dd.DD, {
2824 * By default we resize the drag frame to be the same size as the element
2825 * we want to drag (this is to get the frame effect). We can turn it off
2826 * if we want a different behavior.
2827 * @property resizeFrame
2833 * By default the frame is positioned exactly where the drag element is, so
2834 * we use the cursor offset provided by Roo.dd.DD. Another option that works only if
2835 * you do not have constraints on the obj is to have the drag frame centered
2836 * around the cursor. Set centerFrame to true for this effect.
2837 * @property centerFrame
2843 * Creates the proxy element if it does not yet exist
2844 * @method createFrame
2846 createFrame: function() {
2848 var body = document.body;
2850 if (!body || !body.firstChild) {
2851 setTimeout( function() { self.createFrame(); }, 50 );
2855 var div = this.getDragEl();
2858 div = document.createElement("div");
2859 div.id = this.dragElId;
2862 s.position = "absolute";
2863 s.visibility = "hidden";
2865 s.border = "2px solid #aaa";
2868 // appendChild can blow up IE if invoked prior to the window load event
2869 // while rendering a table. It is possible there are other scenarios
2870 // that would cause this to happen as well.
2871 body.insertBefore(div, body.firstChild);
2876 * Initialization for the drag frame element. Must be called in the
2877 * constructor of all subclasses
2880 initFrame: function() {
2884 applyConfig: function() {
2885 Roo.dd.DDProxy.superclass.applyConfig.call(this);
2887 this.resizeFrame = (this.config.resizeFrame !== false);
2888 this.centerFrame = (this.config.centerFrame);
2889 this.setDragElId(this.config.dragElId || Roo.dd.DDProxy.dragElId);
2893 * Resizes the drag frame to the dimensions of the clicked object, positions
2894 * it over the object, and finally displays it
2896 * @param {int} iPageX X click position
2897 * @param {int} iPageY Y click position
2900 showFrame: function(iPageX, iPageY) {
2901 var el = this.getEl();
2902 var dragEl = this.getDragEl();
2903 var s = dragEl.style;
2905 this._resizeProxy();
2907 if (this.centerFrame) {
2908 this.setDelta( Math.round(parseInt(s.width, 10)/2),
2909 Math.round(parseInt(s.height, 10)/2) );
2912 this.setDragElPos(iPageX, iPageY);
2914 Roo.fly(dragEl).show();
2918 * The proxy is automatically resized to the dimensions of the linked
2919 * element when a drag is initiated, unless resizeFrame is set to false
2920 * @method _resizeProxy
2923 _resizeProxy: function() {
2924 if (this.resizeFrame) {
2925 var el = this.getEl();
2926 Roo.fly(this.getDragEl()).setSize(el.offsetWidth, el.offsetHeight);
2930 // overrides Roo.dd.DragDrop
2931 b4MouseDown: function(e) {
2932 var x = e.getPageX();
2933 var y = e.getPageY();
2934 this.autoOffset(x, y);
2935 this.setDragElPos(x, y);
2938 // overrides Roo.dd.DragDrop
2939 b4StartDrag: function(x, y) {
2940 // show the drag frame
2941 this.showFrame(x, y);
2944 // overrides Roo.dd.DragDrop
2945 b4EndDrag: function(e) {
2946 Roo.fly(this.getDragEl()).hide();
2949 // overrides Roo.dd.DragDrop
2950 // By default we try to move the element to the last location of the frame.
2951 // This is so that the default behavior mirrors that of Roo.dd.DD.
2952 endDrag: function(e) {
2954 var lel = this.getEl();
2955 var del = this.getDragEl();
2957 // Show the drag frame briefly so we can get its position
2958 del.style.visibility = "";
2961 // Hide the linked element before the move to get around a Safari
2963 lel.style.visibility = "hidden";
2964 Roo.dd.DDM.moveToEl(lel, del);
2965 del.style.visibility = "hidden";
2966 lel.style.visibility = "";
2971 beforeMove : function(){
2975 afterDrag : function(){
2979 toString: function() {
2980 return ("DDProxy " + this.id);
2986 * Ext JS Library 1.1.1
2987 * Copyright(c) 2006-2007, Ext JS, LLC.
2989 * Originally Released Under LGPL - original licence link has changed is not relivant.
2992 * <script type="text/javascript">
2996 * @class Roo.dd.DDTarget
2997 * A DragDrop implementation that does not move, but can be a drop
2998 * target. You would get the same result by simply omitting implementation
2999 * for the event callbacks, but this way we reduce the processing cost of the
3000 * event listener and the callbacks.
3001 * @extends Roo.dd.DragDrop
3003 * @param {String} id the id of the element that is a drop target
3004 * @param {String} sGroup the group of related DragDrop objects
3005 * @param {object} config an object containing configurable attributes
3006 * Valid properties for DDTarget in addition to those in
3010 Roo.dd.DDTarget = function(id, sGroup, config) {
3012 this.initTarget(id, sGroup, config);
3014 if (config.listeners || config.events) {
3015 Roo.dd.DragDrop.superclass.constructor.call(this, {
3016 listeners : config.listeners || {},
3017 events : config.events || {}
3022 // Roo.dd.DDTarget.prototype = new Roo.dd.DragDrop();
3023 Roo.extend(Roo.dd.DDTarget, Roo.dd.DragDrop, {
3024 toString: function() {
3025 return ("DDTarget " + this.id);
3030 * Ext JS Library 1.1.1
3031 * Copyright(c) 2006-2007, Ext JS, LLC.
3033 * Originally Released Under LGPL - original licence link has changed is not relivant.
3036 * <script type="text/javascript">
3041 * @class Roo.dd.ScrollManager
3042 * Provides automatic scrolling of overflow regions in the page during drag operations.<br><br>
3043 * <b>Note: This class uses "Point Mode" and is untested in "Intersect Mode".</b>
3046 Roo.dd.ScrollManager = function(){
3047 var ddm = Roo.dd.DragDropMgr;
3054 var onStop = function(e){
3059 var triggerRefresh = function(){
3060 if(ddm.dragCurrent){
3061 ddm.refreshCache(ddm.dragCurrent.groups);
3065 var doScroll = function(){
3066 if(ddm.dragCurrent){
3067 var dds = Roo.dd.ScrollManager;
3069 if(proc.el.scroll(proc.dir, dds.increment)){
3073 proc.el.scroll(proc.dir, dds.increment, true, dds.animDuration, triggerRefresh);
3078 var clearProc = function(){
3080 clearInterval(proc.id);
3087 var startProc = function(el, dir){
3088 Roo.log('scroll startproc');
3092 proc.id = setInterval(doScroll, Roo.dd.ScrollManager.frequency);
3095 var onFire = function(e, isDrop){
3097 if(isDrop || !ddm.dragCurrent){ return; }
3098 var dds = Roo.dd.ScrollManager;
3099 if(!dragEl || dragEl != ddm.dragCurrent){
3100 dragEl = ddm.dragCurrent;
3101 // refresh regions on drag start
3105 var xy = Roo.lib.Event.getXY(e);
3106 var pt = new Roo.lib.Point(xy[0], xy[1]);
3108 var el = els[id], r = el._region;
3109 if(r && r.contains(pt) && el.isScrollable()){
3110 if(r.bottom - pt.y <= dds.thresh){
3112 startProc(el, "down");
3115 }else if(r.right - pt.x <= dds.thresh){
3117 startProc(el, "left");
3120 }else if(pt.y - r.top <= dds.thresh){
3122 startProc(el, "up");
3125 }else if(pt.x - r.left <= dds.thresh){
3127 startProc(el, "right");
3136 ddm.fireEvents = ddm.fireEvents.createSequence(onFire, ddm);
3137 ddm.stopDrag = ddm.stopDrag.createSequence(onStop, ddm);
3141 * Registers new overflow element(s) to auto scroll
3142 * @param {String/HTMLElement/Element/Array} el The id of or the element to be scrolled or an array of either
3144 register : function(el){
3145 if(el instanceof Array){
3146 for(var i = 0, len = el.length; i < len; i++) {
3147 this.register(el[i]);
3153 Roo.dd.ScrollManager.els = els;
3157 * Unregisters overflow element(s) so they are no longer scrolled
3158 * @param {String/HTMLElement/Element/Array} el The id of or the element to be removed or an array of either
3160 unregister : function(el){
3161 if(el instanceof Array){
3162 for(var i = 0, len = el.length; i < len; i++) {
3163 this.unregister(el[i]);
3172 * The number of pixels from the edge of a container the pointer needs to be to
3173 * trigger scrolling (defaults to 25)
3179 * The number of pixels to scroll in each scroll increment (defaults to 50)
3185 * The frequency of scrolls in milliseconds (defaults to 500)
3191 * True to animate the scroll (defaults to true)
3197 * The animation duration in seconds -
3198 * MUST BE less than Roo.dd.ScrollManager.frequency! (defaults to .4)
3204 * Manually trigger a cache refresh.
3206 refreshCache : function(){
3208 if(typeof els[id] == 'object'){ // for people extending the object prototype
3209 els[id]._region = els[id].getRegion();
3216 * Ext JS Library 1.1.1
3217 * Copyright(c) 2006-2007, Ext JS, LLC.
3219 * Originally Released Under LGPL - original licence link has changed is not relivant.
3222 * <script type="text/javascript">
3227 * @class Roo.dd.Registry
3228 * Provides easy access to all drag drop components that are registered on a page. Items can be retrieved either
3229 * directly by DOM node id, or by passing in the drag drop event that occurred and looking up the event target.
3232 Roo.dd.Registry = function(){
3237 var getId = function(el, autogen){
3238 if(typeof el == "string"){
3242 if(!id && autogen !== false){
3243 id = "roodd-" + (++autoIdSeed);
3251 * Register a drag drop element
3252 * @param {String|HTMLElement} element The id or DOM node to register
3253 * @param {Object} data (optional) A custom data object that will be passed between the elements that are involved
3254 * in drag drop operations. You can populate this object with any arbitrary properties that your own code
3255 * knows how to interpret, plus there are some specific properties known to the Registry that should be
3256 * populated in the data object (if applicable):
3258 Value Description<br />
3259 --------- ------------------------------------------<br />
3260 handles Array of DOM nodes that trigger dragging<br />
3261 for the element being registered<br />
3262 isHandle True if the element passed in triggers<br />
3263 dragging itself, else false
3266 register : function(el, data){
3268 if(typeof el == "string"){
3269 el = document.getElementById(el);
3272 elements[getId(el)] = data;
3273 if(data.isHandle !== false){
3274 handles[data.ddel.id] = data;
3277 var hs = data.handles;
3278 for(var i = 0, len = hs.length; i < len; i++){
3279 handles[getId(hs[i])] = data;
3285 * Unregister a drag drop element
3286 * @param {String|HTMLElement} element The id or DOM node to unregister
3288 unregister : function(el){
3289 var id = getId(el, false);
3290 var data = elements[id];
3292 delete elements[id];
3294 var hs = data.handles;
3295 for(var i = 0, len = hs.length; i < len; i++){
3296 delete handles[getId(hs[i], false)];
3303 * Returns the handle registered for a DOM Node by id
3304 * @param {String|HTMLElement} id The DOM node or id to look up
3305 * @return {Object} handle The custom handle data
3307 getHandle : function(id){
3308 if(typeof id != "string"){ // must be element?
3315 * Returns the handle that is registered for the DOM node that is the target of the event
3316 * @param {Event} e The event
3317 * @return {Object} handle The custom handle data
3319 getHandleFromEvent : function(e){
3320 var t = Roo.lib.Event.getTarget(e);
3321 return t ? handles[t.id] : null;
3325 * Returns a custom data object that is registered for a DOM node by id
3326 * @param {String|HTMLElement} id The DOM node or id to look up
3327 * @return {Object} data The custom data
3329 getTarget : function(id){
3330 if(typeof id != "string"){ // must be element?
3333 return elements[id];
3337 * Returns a custom data object that is registered for the DOM node that is the target of the event
3338 * @param {Event} e The event
3339 * @return {Object} data The custom data
3341 getTargetFromEvent : function(e){
3342 var t = Roo.lib.Event.getTarget(e);
3343 return t ? elements[t.id] || handles[t.id] : null;
3348 * Ext JS Library 1.1.1
3349 * Copyright(c) 2006-2007, Ext JS, LLC.
3351 * Originally Released Under LGPL - original licence link has changed is not relivant.
3354 * <script type="text/javascript">
3359 * @class Roo.dd.StatusProxy
3360 * A specialized drag proxy that supports a drop status icon, {@link Roo.Layer} styles and auto-repair. This is the
3361 * default drag proxy used by all Roo.dd components.
3363 * @param {Object} config
3365 Roo.dd.StatusProxy = function(config){
3366 Roo.apply(this, config);
3367 this.id = this.id || Roo.id();
3368 this.el = new Roo.Layer({
3370 id: this.id, tag: "div", cls: "x-dd-drag-proxy "+this.dropNotAllowed, children: [
3371 {tag: "div", cls: "x-dd-drop-icon"},
3372 {tag: "div", cls: "x-dd-drag-ghost"}
3375 shadow: !config || config.shadow !== false
3377 this.ghost = Roo.get(this.el.dom.childNodes[1]);
3378 this.dropStatus = this.dropNotAllowed;
3381 Roo.dd.StatusProxy.prototype = {
3383 * @cfg {String} dropAllowed
3384 * The CSS class to apply to the status element when drop is allowed (defaults to "x-dd-drop-ok").
3386 dropAllowed : "x-dd-drop-ok",
3388 * @cfg {String} dropNotAllowed
3389 * The CSS class to apply to the status element when drop is not allowed (defaults to "x-dd-drop-nodrop").
3391 dropNotAllowed : "x-dd-drop-nodrop",
3394 * Updates the proxy's visual element to indicate the status of whether or not drop is allowed
3395 * over the current target element.
3396 * @param {String} cssClass The css class for the new drop status indicator image
3398 setStatus : function(cssClass){
3399 cssClass = cssClass || this.dropNotAllowed;
3400 if(this.dropStatus != cssClass){
3401 this.el.replaceClass(this.dropStatus, cssClass);
3402 this.dropStatus = cssClass;
3407 * Resets the status indicator to the default dropNotAllowed value
3408 * @param {Boolean} clearGhost True to also remove all content from the ghost, false to preserve it
3410 reset : function(clearGhost){
3411 this.el.dom.className = "x-dd-drag-proxy " + this.dropNotAllowed;
3412 this.dropStatus = this.dropNotAllowed;
3414 this.ghost.update("");
3419 * Updates the contents of the ghost element
3420 * @param {String} html The html that will replace the current innerHTML of the ghost element
3422 update : function(html){
3423 if(typeof html == "string"){
3424 this.ghost.update(html);
3426 this.ghost.update("");
3427 html.style.margin = "0";
3428 this.ghost.dom.appendChild(html);
3430 // ensure float = none set?? cant remember why though.
3431 var el = this.ghost.dom.firstChild;
3433 Roo.fly(el).setStyle('float', 'none');
3438 * Returns the underlying proxy {@link Roo.Layer}
3439 * @return {Roo.Layer} el
3446 * Returns the ghost element
3447 * @return {Roo.Element} el
3449 getGhost : function(){
3455 * @param {Boolean} clear True to reset the status and clear the ghost contents, false to preserve them
3457 hide : function(clear){
3465 * Stops the repair animation if it's currently running
3468 if(this.anim && this.anim.isAnimated && this.anim.isAnimated()){
3474 * Displays this proxy
3481 * Force the Layer to sync its shadow and shim positions to the element
3488 * Causes the proxy to return to its position of origin via an animation. Should be called after an
3489 * invalid drop operation by the item being dragged.
3490 * @param {Array} xy The XY position of the element ([x, y])
3491 * @param {Function} callback The function to call after the repair is complete
3492 * @param {Object} scope The scope in which to execute the callback
3494 repair : function(xy, callback, scope){
3495 this.callback = callback;
3497 if(xy && this.animRepair !== false){
3498 this.el.addClass("x-dd-drag-repair");
3499 this.el.hideUnders(true);
3500 this.anim = this.el.shift({
3501 duration: this.repairDuration || .5,
3505 callback: this.afterRepair,
3514 afterRepair : function(){
3516 if(typeof this.callback == "function"){
3517 this.callback.call(this.scope || this);
3519 this.callback = null;
3524 * Ext JS Library 1.1.1
3525 * Copyright(c) 2006-2007, Ext JS, LLC.
3527 * Originally Released Under LGPL - original licence link has changed is not relivant.
3530 * <script type="text/javascript">
3534 * @class Roo.dd.DragSource
3535 * @extends Roo.dd.DDProxy
3536 * A simple class that provides the basic implementation needed to make any element draggable.
3538 * @param {String/HTMLElement/Element} el The container element
3539 * @param {Object} config
3541 Roo.dd.DragSource = function(el, config){
3542 this.el = Roo.get(el);
3545 Roo.apply(this, config);
3548 this.proxy = new Roo.dd.StatusProxy();
3551 Roo.dd.DragSource.superclass.constructor.call(this, this.el.dom, this.ddGroup || this.group,
3552 {dragElId : this.proxy.id, resizeFrame: false, isTarget: false, scroll: this.scroll === true});
3554 this.dragging = false;
3557 Roo.extend(Roo.dd.DragSource, Roo.dd.DDProxy, {
3559 * @cfg {String} dropAllowed
3560 * The CSS class returned to the drag source when drop is allowed (defaults to "x-dd-drop-ok").
3562 dropAllowed : "x-dd-drop-ok",
3564 * @cfg {String} dropNotAllowed
3565 * The CSS class returned to the drag source when drop is not allowed (defaults to "x-dd-drop-nodrop").
3567 dropNotAllowed : "x-dd-drop-nodrop",
3570 * Returns the data object associated with this drag source
3571 * @return {Object} data An object containing arbitrary data
3573 getDragData : function(e){
3574 return this.dragData;
3578 onDragEnter : function(e, id){
3579 var target = Roo.dd.DragDropMgr.getDDById(id);
3580 this.cachedTarget = target;
3581 if(this.beforeDragEnter(target, e, id) !== false){
3582 if(target.isNotifyTarget){
3583 var status = target.notifyEnter(this, e, this.dragData);
3584 this.proxy.setStatus(status);
3586 this.proxy.setStatus(this.dropAllowed);
3589 if(this.afterDragEnter){
3591 * An empty function by default, but provided so that you can perform a custom action
3592 * when the dragged item enters the drop target by providing an implementation.
3593 * @param {Roo.dd.DragDrop} target The drop target
3594 * @param {Event} e The event object
3595 * @param {String} id The id of the dragged element
3596 * @method afterDragEnter
3598 this.afterDragEnter(target, e, id);
3604 * An empty function by default, but provided so that you can perform a custom action
3605 * before the dragged item enters the drop target and optionally cancel the onDragEnter.
3606 * @param {Roo.dd.DragDrop} target The drop target
3607 * @param {Event} e The event object
3608 * @param {String} id The id of the dragged element
3609 * @return {Boolean} isValid True if the drag event is valid, else false to cancel
3611 beforeDragEnter : function(target, e, id){
3616 alignElWithMouse: function() {
3617 Roo.dd.DragSource.superclass.alignElWithMouse.apply(this, arguments);
3622 onDragOver : function(e, id){
3623 var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
3624 if(this.beforeDragOver(target, e, id) !== false){
3625 if(target.isNotifyTarget){
3626 var status = target.notifyOver(this, e, this.dragData);
3627 this.proxy.setStatus(status);
3630 if(this.afterDragOver){
3632 * An empty function by default, but provided so that you can perform a custom action
3633 * while the dragged item is over the drop target by providing an implementation.
3634 * @param {Roo.dd.DragDrop} target The drop target
3635 * @param {Event} e The event object
3636 * @param {String} id The id of the dragged element
3637 * @method afterDragOver
3639 this.afterDragOver(target, e, id);
3645 * An empty function by default, but provided so that you can perform a custom action
3646 * while the dragged item is over the drop target and optionally cancel the onDragOver.
3647 * @param {Roo.dd.DragDrop} target The drop target
3648 * @param {Event} e The event object
3649 * @param {String} id The id of the dragged element
3650 * @return {Boolean} isValid True if the drag event is valid, else false to cancel
3652 beforeDragOver : function(target, e, id){
3657 onDragOut : function(e, id){
3658 var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
3659 if(this.beforeDragOut(target, e, id) !== false){
3660 if(target.isNotifyTarget){
3661 target.notifyOut(this, e, this.dragData);
3664 if(this.afterDragOut){
3666 * An empty function by default, but provided so that you can perform a custom action
3667 * after the dragged item is dragged out of the target without dropping.
3668 * @param {Roo.dd.DragDrop} target The drop target
3669 * @param {Event} e The event object
3670 * @param {String} id The id of the dragged element
3671 * @method afterDragOut
3673 this.afterDragOut(target, e, id);
3676 this.cachedTarget = null;
3680 * An empty function by default, but provided so that you can perform a custom action before the dragged
3681 * item is dragged out of the target without dropping, and optionally cancel the onDragOut.
3682 * @param {Roo.dd.DragDrop} target The drop target
3683 * @param {Event} e The event object
3684 * @param {String} id The id of the dragged element
3685 * @return {Boolean} isValid True if the drag event is valid, else false to cancel
3687 beforeDragOut : function(target, e, id){
3692 onDragDrop : function(e, id){
3693 var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
3694 if(this.beforeDragDrop(target, e, id) !== false){
3695 if(target.isNotifyTarget){
3696 if(target.notifyDrop(this, e, this.dragData)){ // valid drop?
3697 this.onValidDrop(target, e, id);
3699 this.onInvalidDrop(target, e, id);
3702 this.onValidDrop(target, e, id);
3705 if(this.afterDragDrop){
3707 * An empty function by default, but provided so that you can perform a custom action
3708 * after a valid drag drop has occurred by providing an implementation.
3709 * @param {Roo.dd.DragDrop} target The drop target
3710 * @param {Event} e The event object
3711 * @param {String} id The id of the dropped element
3712 * @method afterDragDrop
3714 this.afterDragDrop(target, e, id);
3717 delete this.cachedTarget;
3721 * An empty function by default, but provided so that you can perform a custom action before the dragged
3722 * item is dropped onto the target and optionally cancel the onDragDrop.
3723 * @param {Roo.dd.DragDrop} target The drop target
3724 * @param {Event} e The event object
3725 * @param {String} id The id of the dragged element
3726 * @return {Boolean} isValid True if the drag drop event is valid, else false to cancel
3728 beforeDragDrop : function(target, e, id){
3733 onValidDrop : function(target, e, id){
3735 if(this.afterValidDrop){
3737 * An empty function by default, but provided so that you can perform a custom action
3738 * after a valid drop has occurred by providing an implementation.
3739 * @param {Object} target The target DD
3740 * @param {Event} e The event object
3741 * @param {String} id The id of the dropped element
3742 * @method afterInvalidDrop
3744 this.afterValidDrop(target, e, id);
3749 getRepairXY : function(e, data){
3750 return this.el.getXY();
3754 onInvalidDrop : function(target, e, id){
3755 this.beforeInvalidDrop(target, e, id);
3756 if(this.cachedTarget){
3757 if(this.cachedTarget.isNotifyTarget){
3758 this.cachedTarget.notifyOut(this, e, this.dragData);
3760 this.cacheTarget = null;
3762 this.proxy.repair(this.getRepairXY(e, this.dragData), this.afterRepair, this);
3764 if(this.afterInvalidDrop){
3766 * An empty function by default, but provided so that you can perform a custom action
3767 * after an invalid drop has occurred by providing an implementation.
3768 * @param {Event} e The event object
3769 * @param {String} id The id of the dropped element
3770 * @method afterInvalidDrop
3772 this.afterInvalidDrop(e, id);
3777 afterRepair : function(){
3779 this.el.highlight(this.hlColor || "c3daf9");
3781 this.dragging = false;
3785 * An empty function by default, but provided so that you can perform a custom action after an invalid
3786 * drop has occurred.
3787 * @param {Roo.dd.DragDrop} target The drop target
3788 * @param {Event} e The event object
3789 * @param {String} id The id of the dragged element
3790 * @return {Boolean} isValid True if the invalid drop should proceed, else false to cancel
3792 beforeInvalidDrop : function(target, e, id){
3797 handleMouseDown : function(e){
3801 var data = this.getDragData(e);
3802 if(data && this.onBeforeDrag(data, e) !== false){
3803 this.dragData = data;
3805 Roo.dd.DragSource.superclass.handleMouseDown.apply(this, arguments);
3810 * An empty function by default, but provided so that you can perform a custom action before the initial
3811 * drag event begins and optionally cancel it.
3812 * @param {Object} data An object containing arbitrary data to be shared with drop targets
3813 * @param {Event} e The event object
3814 * @return {Boolean} isValid True if the drag event is valid, else false to cancel
3816 onBeforeDrag : function(data, e){
3821 * An empty function by default, but provided so that you can perform a custom action once the initial
3822 * drag event has begun. The drag cannot be canceled from this function.
3823 * @param {Number} x The x position of the click on the dragged object
3824 * @param {Number} y The y position of the click on the dragged object
3826 onStartDrag : Roo.emptyFn,
3828 // private - YUI override
3829 startDrag : function(x, y){
3831 this.dragging = true;
3832 this.proxy.update("");
3833 this.onInitDrag(x, y);
3838 onInitDrag : function(x, y){
3839 var clone = this.el.dom.cloneNode(true);
3840 clone.id = Roo.id(); // prevent duplicate ids
3841 this.proxy.update(clone);
3842 this.onStartDrag(x, y);
3847 * Returns the drag source's underlying {@link Roo.dd.StatusProxy}
3848 * @return {Roo.dd.StatusProxy} proxy The StatusProxy
3850 getProxy : function(){
3855 * Hides the drag source's {@link Roo.dd.StatusProxy}
3857 hideProxy : function(){
3859 this.proxy.reset(true);
3860 this.dragging = false;
3864 triggerCacheRefresh : function(){
3865 Roo.dd.DDM.refreshCache(this.groups);
3868 // private - override to prevent hiding
3869 b4EndDrag: function(e) {
3872 // private - override to prevent moving
3873 endDrag : function(e){
3874 this.onEndDrag(this.dragData, e);
3878 onEndDrag : function(data, e){
3881 // private - pin to cursor
3882 autoOffset : function(x, y) {
3883 this.setDelta(-12, -20);
3887 * Ext JS Library 1.1.1
3888 * Copyright(c) 2006-2007, Ext JS, LLC.
3890 * Originally Released Under LGPL - original licence link has changed is not relivant.
3893 * <script type="text/javascript">
3898 * @class Roo.dd.DropTarget
3899 * @extends Roo.dd.DDTarget
3900 * A simple class that provides the basic implementation needed to make any element a drop target that can have
3901 * draggable items dropped onto it. The drop has no effect until an implementation of notifyDrop is provided.
3903 * @param {String/HTMLElement/Element} el The container element
3904 * @param {Object} config
3906 Roo.dd.DropTarget = function(el, config){
3907 this.el = Roo.get(el);
3909 var listeners = false; ;
3910 if (config && config.listeners) {
3911 listeners= config.listeners;
3912 delete config.listeners;
3914 Roo.apply(this, config);
3916 if(this.containerScroll){
3917 Roo.dd.ScrollManager.register(this.el);
3921 * @scope Roo.dd.DropTarget
3926 * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the source is now over the
3927 * target. This default implementation adds the CSS class specified by overClass (if any) to the drop element
3928 * and returns the dropAllowed config value. This method should be overridden if drop validation is required.
3930 * IMPORTANT : it should set this.overClass and this.dropAllowed
3932 * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
3933 * @param {Event} e The event
3934 * @param {Object} data An object containing arbitrary data supplied by the drag source
3940 * The function a {@link Roo.dd.DragSource} calls continuously while it is being dragged over the target.
3941 * This method will be called on every mouse movement while the drag source is over the drop target.
3942 * This default implementation simply returns the dropAllowed config value.
3944 * IMPORTANT : it should set this.dropAllowed
3946 * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
3947 * @param {Event} e The event
3948 * @param {Object} data An object containing arbitrary data supplied by the drag source
3954 * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the source has been dragged
3955 * out of the target without dropping. This default implementation simply removes the CSS class specified by
3956 * overClass (if any) from the drop element.
3958 * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
3959 * @param {Event} e The event
3960 * @param {Object} data An object containing arbitrary data supplied by the drag source
3966 * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the dragged item has
3967 * been dropped on it. This method has no default implementation and returns false, so you must provide an
3968 * implementation that does something to process the drop event and returns true so that the drag source's
3969 * repair action does not run.
3971 * IMPORTANT : it should set this.success
3973 * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
3974 * @param {Event} e The event
3975 * @param {Object} data An object containing arbitrary data supplied by the drag source
3981 Roo.dd.DropTarget.superclass.constructor.call( this,
3983 this.ddGroup || this.group,
3986 listeners : listeners || {}
3994 Roo.extend(Roo.dd.DropTarget, Roo.dd.DDTarget, {
3996 * @cfg {String} overClass
3997 * The CSS class applied to the drop target element while the drag source is over it (defaults to "").
4000 * @cfg {String} ddGroup
4001 * The drag drop group to handle drop events for
4005 * @cfg {String} dropAllowed
4006 * The CSS class returned to the drag source when drop is allowed (defaults to "x-dd-drop-ok").
4008 dropAllowed : "x-dd-drop-ok",
4010 * @cfg {String} dropNotAllowed
4011 * The CSS class returned to the drag source when drop is not allowed (defaults to "x-dd-drop-nodrop").
4013 dropNotAllowed : "x-dd-drop-nodrop",
4015 * @cfg {boolean} success
4016 * set this after drop listener..
4020 * @cfg {boolean|String} valid true/false or string (ok-add/ok-sub/ok/nodrop)
4021 * if the drop point is valid for over/enter..
4028 isNotifyTarget : true,
4033 notifyEnter : function(dd, e, data)
4036 this.fireEvent('enter', dd, e, data);
4038 this.el.addClass(this.overClass);
4040 return typeof(this.valid) == 'string' ? 'x-dd-drop-' + this.valid : (
4041 this.valid ? this.dropAllowed : this.dropNotAllowed
4048 notifyOver : function(dd, e, data)
4051 this.fireEvent('over', dd, e, data);
4052 return typeof(this.valid) == 'string' ? 'x-dd-drop-' + this.valid : (
4053 this.valid ? this.dropAllowed : this.dropNotAllowed
4060 notifyOut : function(dd, e, data)
4062 this.fireEvent('out', dd, e, data);
4064 this.el.removeClass(this.overClass);
4071 notifyDrop : function(dd, e, data)
4073 this.success = false;
4074 this.fireEvent('drop', dd, e, data);
4075 return this.success;
4079 * Ext JS Library 1.1.1
4080 * Copyright(c) 2006-2007, Ext JS, LLC.
4082 * Originally Released Under LGPL - original licence link has changed is not relivant.
4085 * <script type="text/javascript">
4090 * @class Roo.dd.DragZone
4091 * @extends Roo.dd.DragSource
4092 * This class provides a container DD instance that proxies for multiple child node sources.<br />
4093 * By default, this class requires that draggable child nodes are registered with {@link Roo.dd.Registry}.
4095 * @param {String/HTMLElement/Element} el The container element
4096 * @param {Object} config
4098 Roo.dd.DragZone = function(el, config){
4099 Roo.dd.DragZone.superclass.constructor.call(this, el, config);
4100 if(this.containerScroll){
4101 Roo.dd.ScrollManager.register(this.el);
4105 Roo.extend(Roo.dd.DragZone, Roo.dd.DragSource, {
4107 * @cfg {Boolean} containerScroll True to register this container with the Scrollmanager
4108 * for auto scrolling during drag operations.
4111 * @cfg {String} hlColor The color to use when visually highlighting the drag source in the afterRepair
4112 * method after a failed drop (defaults to "c3daf9" - light blue)
4116 * Called when a mousedown occurs in this container. Looks in {@link Roo.dd.Registry}
4117 * for a valid target to drag based on the mouse down. Override this method
4118 * to provide your own lookup logic (e.g. finding a child by class name). Make sure your returned
4119 * object has a "ddel" attribute (with an HTML Element) for other functions to work.
4120 * @param {EventObject} e The mouse down event
4121 * @return {Object} The dragData
4123 getDragData : function(e){
4124 return Roo.dd.Registry.getHandleFromEvent(e);
4128 * Called once drag threshold has been reached to initialize the proxy element. By default, it clones the
4129 * this.dragData.ddel
4130 * @param {Number} x The x position of the click on the dragged object
4131 * @param {Number} y The y position of the click on the dragged object
4132 * @return {Boolean} true to continue the drag, false to cancel
4134 onInitDrag : function(x, y){
4135 this.proxy.update(this.dragData.ddel.cloneNode(true));
4136 this.onStartDrag(x, y);
4141 * Called after a repair of an invalid drop. By default, highlights this.dragData.ddel
4143 afterRepair : function(){
4145 Roo.Element.fly(this.dragData.ddel).highlight(this.hlColor || "c3daf9");
4147 this.dragging = false;
4151 * Called before a repair of an invalid drop to get the XY to animate to. By default returns
4152 * the XY of this.dragData.ddel
4153 * @param {EventObject} e The mouse up event
4154 * @return {Array} The xy location (e.g. [100, 200])
4156 getRepairXY : function(e){
4157 return Roo.Element.fly(this.dragData.ddel).getXY();
4161 * Ext JS Library 1.1.1
4162 * Copyright(c) 2006-2007, Ext JS, LLC.
4164 * Originally Released Under LGPL - original licence link has changed is not relivant.
4167 * <script type="text/javascript">
4170 * @class Roo.dd.DropZone
4171 * @extends Roo.dd.DropTarget
4172 * This class provides a container DD instance that proxies for multiple child node targets.<br />
4173 * By default, this class requires that child nodes accepting drop are registered with {@link Roo.dd.Registry}.
4175 * @param {String/HTMLElement/Element} el The container element
4176 * @param {Object} config
4178 Roo.dd.DropZone = function(el, config){
4179 Roo.dd.DropZone.superclass.constructor.call(this, el, config);
4182 Roo.extend(Roo.dd.DropZone, Roo.dd.DropTarget, {
4184 * Returns a custom data object associated with the DOM node that is the target of the event. By default
4185 * this looks up the event target in the {@link Roo.dd.Registry}, although you can override this method to
4186 * provide your own custom lookup.
4187 * @param {Event} e The event
4188 * @return {Object} data The custom data
4190 getTargetFromEvent : function(e){
4191 return Roo.dd.Registry.getTargetFromEvent(e);
4195 * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has entered a drop node
4196 * that it has registered. This method has no default implementation and should be overridden to provide
4197 * node-specific processing if necessary.
4198 * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
4199 * {@link #getTargetFromEvent} for this node)
4200 * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4201 * @param {Event} e The event
4202 * @param {Object} data An object containing arbitrary data supplied by the drag source
4204 onNodeEnter : function(n, dd, e, data){
4209 * Called internally while the DropZone determines that a {@link Roo.dd.DragSource} is over a drop node
4210 * that it has registered. The default implementation returns this.dropNotAllowed, so it should be
4211 * overridden to provide the proper feedback.
4212 * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
4213 * {@link #getTargetFromEvent} for this node)
4214 * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4215 * @param {Event} e The event
4216 * @param {Object} data An object containing arbitrary data supplied by the drag source
4217 * @return {String} status The CSS class that communicates the drop status back to the source so that the
4218 * underlying {@link Roo.dd.StatusProxy} can be updated
4220 onNodeOver : function(n, dd, e, data){
4221 return this.dropAllowed;
4225 * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dragged out of
4226 * the drop node without dropping. This method has no default implementation and should be overridden to provide
4227 * node-specific processing if necessary.
4228 * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
4229 * {@link #getTargetFromEvent} for this node)
4230 * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4231 * @param {Event} e The event
4232 * @param {Object} data An object containing arbitrary data supplied by the drag source
4234 onNodeOut : function(n, dd, e, data){
4239 * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dropped onto
4240 * the drop node. The default implementation returns false, so it should be overridden to provide the
4241 * appropriate processing of the drop event and return true so that the drag source's repair action does not run.
4242 * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
4243 * {@link #getTargetFromEvent} for this node)
4244 * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4245 * @param {Event} e The event
4246 * @param {Object} data An object containing arbitrary data supplied by the drag source
4247 * @return {Boolean} True if the drop was valid, else false
4249 onNodeDrop : function(n, dd, e, data){
4254 * Called internally while the DropZone determines that a {@link Roo.dd.DragSource} is being dragged over it,
4255 * but not over any of its registered drop nodes. The default implementation returns this.dropNotAllowed, so
4256 * it should be overridden to provide the proper feedback if necessary.
4257 * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4258 * @param {Event} e The event
4259 * @param {Object} data An object containing arbitrary data supplied by the drag source
4260 * @return {String} status The CSS class that communicates the drop status back to the source so that the
4261 * underlying {@link Roo.dd.StatusProxy} can be updated
4263 onContainerOver : function(dd, e, data){
4264 return this.dropNotAllowed;
4268 * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dropped on it,
4269 * but not on any of its registered drop nodes. The default implementation returns false, so it should be
4270 * overridden to provide the appropriate processing of the drop event if you need the drop zone itself to
4271 * be able to accept drops. It should return true when valid so that the drag source's repair action does not run.
4272 * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4273 * @param {Event} e The event
4274 * @param {Object} data An object containing arbitrary data supplied by the drag source
4275 * @return {Boolean} True if the drop was valid, else false
4277 onContainerDrop : function(dd, e, data){
4282 * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the source is now over
4283 * the zone. The default implementation returns this.dropNotAllowed and expects that only registered drop
4284 * nodes can process drag drop operations, so if you need the drop zone itself to be able to process drops
4285 * you should override this method and provide a custom implementation.
4286 * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4287 * @param {Event} e The event
4288 * @param {Object} data An object containing arbitrary data supplied by the drag source
4289 * @return {String} status The CSS class that communicates the drop status back to the source so that the
4290 * underlying {@link Roo.dd.StatusProxy} can be updated
4292 notifyEnter : function(dd, e, data){
4293 return this.dropNotAllowed;
4297 * The function a {@link Roo.dd.DragSource} calls continuously while it is being dragged over the drop zone.
4298 * This method will be called on every mouse movement while the drag source is over the drop zone.
4299 * It will call {@link #onNodeOver} while the drag source is over a registered node, and will also automatically
4300 * delegate to the appropriate node-specific methods as necessary when the drag source enters and exits
4301 * registered nodes ({@link #onNodeEnter}, {@link #onNodeOut}). If the drag source is not currently over a
4302 * registered node, it will call {@link #onContainerOver}.
4303 * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4304 * @param {Event} e The event
4305 * @param {Object} data An object containing arbitrary data supplied by the drag source
4306 * @return {String} status The CSS class that communicates the drop status back to the source so that the
4307 * underlying {@link Roo.dd.StatusProxy} can be updated
4309 notifyOver : function(dd, e, data){
4310 var n = this.getTargetFromEvent(e);
4311 if(!n){ // not over valid drop target
4312 if(this.lastOverNode){
4313 this.onNodeOut(this.lastOverNode, dd, e, data);
4314 this.lastOverNode = null;
4316 return this.onContainerOver(dd, e, data);
4318 if(this.lastOverNode != n){
4319 if(this.lastOverNode){
4320 this.onNodeOut(this.lastOverNode, dd, e, data);
4322 this.onNodeEnter(n, dd, e, data);
4323 this.lastOverNode = n;
4325 return this.onNodeOver(n, dd, e, data);
4329 * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the source has been dragged
4330 * out of the zone without dropping. If the drag source is currently over a registered node, the notification
4331 * will be delegated to {@link #onNodeOut} for node-specific handling, otherwise it will be ignored.
4332 * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
4333 * @param {Event} e The event
4334 * @param {Object} data An object containing arbitrary data supplied by the drag zone
4336 notifyOut : function(dd, e, data){
4337 if(this.lastOverNode){
4338 this.onNodeOut(this.lastOverNode, dd, e, data);
4339 this.lastOverNode = null;
4344 * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the dragged item has
4345 * been dropped on it. The drag zone will look up the target node based on the event passed in, and if there
4346 * is a node registered for that event, it will delegate to {@link #onNodeDrop} for node-specific handling,
4347 * otherwise it will call {@link #onContainerDrop}.
4348 * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4349 * @param {Event} e The event
4350 * @param {Object} data An object containing arbitrary data supplied by the drag source
4351 * @return {Boolean} True if the drop was valid, else false
4353 notifyDrop : function(dd, e, data){
4354 if(this.lastOverNode){
4355 this.onNodeOut(this.lastOverNode, dd, e, data);
4356 this.lastOverNode = null;
4358 var n = this.getTargetFromEvent(e);
4360 this.onNodeDrop(n, dd, e, data) :
4361 this.onContainerDrop(dd, e, data);
4365 triggerCacheRefresh : function(){
4366 Roo.dd.DDM.refreshCache(this.groups);
4370 * Ext JS Library 1.1.1
4371 * Copyright(c) 2006-2007, Ext JS, LLC.
4373 * Originally Released Under LGPL - original licence link has changed is not relivant.
4376 * <script type="text/javascript">
4381 * @class Roo.data.SortTypes
4383 * Defines the default sorting (casting?) comparison functions used when sorting data.
4385 Roo.data.SortTypes = {
4387 * Default sort that does nothing
4388 * @param {Mixed} s The value being converted
4389 * @return {Mixed} The comparison value
4396 * The regular expression used to strip tags
4400 stripTagsRE : /<\/?[^>]+>/gi,
4403 * Strips all HTML tags to sort on text only
4404 * @param {Mixed} s The value being converted
4405 * @return {String} The comparison value
4407 asText : function(s){
4408 return String(s).replace(this.stripTagsRE, "");
4412 * Strips all HTML tags to sort on text only - Case insensitive
4413 * @param {Mixed} s The value being converted
4414 * @return {String} The comparison value
4416 asUCText : function(s){
4417 return String(s).toUpperCase().replace(this.stripTagsRE, "");
4421 * Case insensitive string
4422 * @param {Mixed} s The value being converted
4423 * @return {String} The comparison value
4425 asUCString : function(s) {
4426 return String(s).toUpperCase();
4431 * @param {Mixed} s The value being converted
4432 * @return {Number} The comparison value
4434 asDate : function(s) {
4438 if(s instanceof Date){
4441 return Date.parse(String(s));
4446 * @param {Mixed} s The value being converted
4447 * @return {Float} The comparison value
4449 asFloat : function(s) {
4450 var val = parseFloat(String(s).replace(/,/g, ""));
4451 if(isNaN(val)) val = 0;
4457 * @param {Mixed} s The value being converted
4458 * @return {Number} The comparison value
4460 asInt : function(s) {
4461 var val = parseInt(String(s).replace(/,/g, ""));
4462 if(isNaN(val)) val = 0;
4467 * Ext JS Library 1.1.1
4468 * Copyright(c) 2006-2007, Ext JS, LLC.
4470 * Originally Released Under LGPL - original licence link has changed is not relivant.
4473 * <script type="text/javascript">
4477 * @class Roo.data.Record
4478 * Instances of this class encapsulate both record <em>definition</em> information, and record
4479 * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
4480 * to access Records cached in an {@link Roo.data.Store} object.<br>
4482 * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
4483 * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
4486 * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
4488 * This constructor should not be used to create Record objects. Instead, use the constructor generated by
4489 * {@link #create}. The parameters are the same.
4490 * @param {Array} data An associative Array of data values keyed by the field name.
4491 * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
4492 * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
4493 * not specified an integer id is generated.
4495 Roo.data.Record = function(data, id){
4496 this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
4501 * Generate a constructor for a specific record layout.
4502 * @param {Array} o An Array of field definition objects which specify field names, and optionally,
4503 * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
4504 * Each field definition object may contain the following properties: <ul>
4505 * <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,
4506 * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
4507 * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
4508 * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
4509 * is being used, then this is a string containing the javascript expression to reference the data relative to
4510 * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
4511 * to the data item relative to the record element. If the mapping expression is the same as the field name,
4512 * this may be omitted.</p></li>
4513 * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
4514 * <ul><li>auto (Default, implies no conversion)</li>
4519 * <li>date</li></ul></p></li>
4520 * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
4521 * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
4522 * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
4523 * by the Reader into an object that will be stored in the Record. It is passed the
4524 * following parameters:<ul>
4525 * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
4527 * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
4529 * <br>usage:<br><pre><code>
4530 var TopicRecord = Roo.data.Record.create(
4531 {name: 'title', mapping: 'topic_title'},
4532 {name: 'author', mapping: 'username'},
4533 {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
4534 {name: 'lastPost', mapping: 'post_time', type: 'date'},
4535 {name: 'lastPoster', mapping: 'user2'},
4536 {name: 'excerpt', mapping: 'post_text'}
4539 var myNewRecord = new TopicRecord({
4540 title: 'Do my job please',
4543 lastPost: new Date(),
4544 lastPoster: 'Animal',
4545 excerpt: 'No way dude!'
4547 myStore.add(myNewRecord);
4552 Roo.data.Record.create = function(o){
4554 f.superclass.constructor.apply(this, arguments);
4556 Roo.extend(f, Roo.data.Record);
4557 var p = f.prototype;
4558 p.fields = new Roo.util.MixedCollection(false, function(field){
4561 for(var i = 0, len = o.length; i < len; i++){
4562 p.fields.add(new Roo.data.Field(o[i]));
4564 f.getField = function(name){
4565 return p.fields.get(name);
4570 Roo.data.Record.AUTO_ID = 1000;
4571 Roo.data.Record.EDIT = 'edit';
4572 Roo.data.Record.REJECT = 'reject';
4573 Roo.data.Record.COMMIT = 'commit';
4575 Roo.data.Record.prototype = {
4577 * Readonly flag - true if this record has been modified.
4586 join : function(store){
4591 * Set the named field to the specified value.
4592 * @param {String} name The name of the field to set.
4593 * @param {Object} value The value to set the field to.
4595 set : function(name, value){
4596 if(this.data[name] == value){
4603 if(typeof this.modified[name] == 'undefined'){
4604 this.modified[name] = this.data[name];
4606 this.data[name] = value;
4607 if(!this.editing && this.store){
4608 this.store.afterEdit(this);
4613 * Get the value of the named field.
4614 * @param {String} name The name of the field to get the value of.
4615 * @return {Object} The value of the field.
4617 get : function(name){
4618 return this.data[name];
4622 beginEdit : function(){
4623 this.editing = true;
4628 cancelEdit : function(){
4629 this.editing = false;
4630 delete this.modified;
4634 endEdit : function(){
4635 this.editing = false;
4636 if(this.dirty && this.store){
4637 this.store.afterEdit(this);
4642 * Usually called by the {@link Roo.data.Store} which owns the Record.
4643 * Rejects all changes made to the Record since either creation, or the last commit operation.
4644 * Modified fields are reverted to their original values.
4646 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
4647 * of reject operations.
4649 reject : function(){
4650 var m = this.modified;
4652 if(typeof m[n] != "function"){
4653 this.data[n] = m[n];
4657 delete this.modified;
4658 this.editing = false;
4660 this.store.afterReject(this);
4665 * Usually called by the {@link Roo.data.Store} which owns the Record.
4666 * Commits all changes made to the Record since either creation, or the last commit operation.
4668 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
4669 * of commit operations.
4671 commit : function(){
4673 delete this.modified;
4674 this.editing = false;
4676 this.store.afterCommit(this);
4681 hasError : function(){
4682 return this.error != null;
4686 clearError : function(){
4691 * Creates a copy of this record.
4692 * @param {String} id (optional) A new record id if you don't want to use this record's id
4695 copy : function(newId) {
4696 return new this.constructor(Roo.apply({}, this.data), newId || this.id);
4700 * Ext JS Library 1.1.1
4701 * Copyright(c) 2006-2007, Ext JS, LLC.
4703 * Originally Released Under LGPL - original licence link has changed is not relivant.
4706 * <script type="text/javascript">
4712 * @class Roo.data.Store
4713 * @extends Roo.util.Observable
4714 * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
4715 * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
4717 * 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
4718 * has no knowledge of the format of the data returned by the Proxy.<br>
4720 * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
4721 * instances from the data object. These records are cached and made available through accessor functions.
4723 * Creates a new Store.
4724 * @param {Object} config A config object containing the objects needed for the Store to access data,
4725 * and read the data into Records.
4727 Roo.data.Store = function(config){
4728 this.data = new Roo.util.MixedCollection(false);
4729 this.data.getKey = function(o){
4732 this.baseParams = {};
4739 "multisort" : "_multisort"
4742 if(config && config.data){
4743 this.inlineData = config.data;
4747 Roo.apply(this, config);
4749 if(this.reader){ // reader passed
4750 this.reader = Roo.factory(this.reader, Roo.data);
4751 this.reader.xmodule = this.xmodule || false;
4752 if(!this.recordType){
4753 this.recordType = this.reader.recordType;
4755 if(this.reader.onMetaChange){
4756 this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
4760 if(this.recordType){
4761 this.fields = this.recordType.prototype.fields;
4767 * @event datachanged
4768 * Fires when the data cache has changed, and a widget which is using this Store
4769 * as a Record cache should refresh its view.
4770 * @param {Store} this
4775 * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
4776 * @param {Store} this
4777 * @param {Object} meta The JSON metadata
4782 * Fires when Records have been added to the Store
4783 * @param {Store} this
4784 * @param {Roo.data.Record[]} records The array of Records added
4785 * @param {Number} index The index at which the record(s) were added
4790 * Fires when a Record has been removed from the Store
4791 * @param {Store} this
4792 * @param {Roo.data.Record} record The Record that was removed
4793 * @param {Number} index The index at which the record was removed
4798 * Fires when a Record has been updated
4799 * @param {Store} this
4800 * @param {Roo.data.Record} record The Record that was updated
4801 * @param {String} operation The update operation being performed. Value may be one of:
4803 Roo.data.Record.EDIT
4804 Roo.data.Record.REJECT
4805 Roo.data.Record.COMMIT
4811 * Fires when the data cache has been cleared.
4812 * @param {Store} this
4817 * Fires before a request is made for a new data object. If the beforeload handler returns false
4818 * the load action will be canceled.
4819 * @param {Store} this
4820 * @param {Object} options The loading options that were specified (see {@link #load} for details)
4824 * @event beforeloadadd
4825 * Fires after a new set of Records has been loaded.
4826 * @param {Store} this
4827 * @param {Roo.data.Record[]} records The Records that were loaded
4828 * @param {Object} options The loading options that were specified (see {@link #load} for details)
4830 beforeloadadd : true,
4833 * Fires after a new set of Records has been loaded, before they are added to the store.
4834 * @param {Store} this
4835 * @param {Roo.data.Record[]} records The Records that were loaded
4836 * @param {Object} options The loading options that were specified (see {@link #load} for details)
4837 * @params {Object} return from reader
4841 * @event loadexception
4842 * Fires if an exception occurs in the Proxy during loading.
4843 * Called with the signature of the Proxy's "loadexception" event.
4844 * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
4847 * @param {Object} return from JsonData.reader() - success, totalRecords, records
4848 * @param {Object} load options
4849 * @param {Object} jsonData from your request (normally this contains the Exception)
4851 loadexception : true
4855 this.proxy = Roo.factory(this.proxy, Roo.data);
4856 this.proxy.xmodule = this.xmodule || false;
4857 this.relayEvents(this.proxy, ["loadexception"]);
4859 this.sortToggle = {};
4860 this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
4862 Roo.data.Store.superclass.constructor.call(this);
4864 if(this.inlineData){
4865 this.loadData(this.inlineData);
4866 delete this.inlineData;
4870 Roo.extend(Roo.data.Store, Roo.util.Observable, {
4872 * @cfg {boolean} isLocal flag if data is locally available (and can be always looked up
4873 * without a remote query - used by combo/forms at present.
4877 * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
4880 * @cfg {Array} data Inline data to be loaded when the store is initialized.
4883 * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
4884 * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
4887 * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
4888 * on any HTTP request
4891 * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
4894 * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
4898 * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
4899 * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
4904 * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
4905 * loaded or when a record is removed. (defaults to false).
4907 pruneModifiedRecords : false,
4913 * Add Records to the Store and fires the add event.
4914 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
4916 add : function(records){
4917 records = [].concat(records);
4918 for(var i = 0, len = records.length; i < len; i++){
4919 records[i].join(this);
4921 var index = this.data.length;
4922 this.data.addAll(records);
4923 this.fireEvent("add", this, records, index);
4927 * Remove a Record from the Store and fires the remove event.
4928 * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
4930 remove : function(record){
4931 var index = this.data.indexOf(record);
4932 this.data.removeAt(index);
4933 if(this.pruneModifiedRecords){
4934 this.modified.remove(record);
4936 this.fireEvent("remove", this, record, index);
4940 * Remove all Records from the Store and fires the clear event.
4942 removeAll : function(){
4944 if(this.pruneModifiedRecords){
4947 this.fireEvent("clear", this);
4951 * Inserts Records to the Store at the given index and fires the add event.
4952 * @param {Number} index The start index at which to insert the passed Records.
4953 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
4955 insert : function(index, records){
4956 records = [].concat(records);
4957 for(var i = 0, len = records.length; i < len; i++){
4958 this.data.insert(index, records[i]);
4959 records[i].join(this);
4961 this.fireEvent("add", this, records, index);
4965 * Get the index within the cache of the passed Record.
4966 * @param {Roo.data.Record} record The Roo.data.Record object to to find.
4967 * @return {Number} The index of the passed Record. Returns -1 if not found.
4969 indexOf : function(record){
4970 return this.data.indexOf(record);
4974 * Get the index within the cache of the Record with the passed id.
4975 * @param {String} id The id of the Record to find.
4976 * @return {Number} The index of the Record. Returns -1 if not found.
4978 indexOfId : function(id){
4979 return this.data.indexOfKey(id);
4983 * Get the Record with the specified id.
4984 * @param {String} id The id of the Record to find.
4985 * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
4987 getById : function(id){
4988 return this.data.key(id);
4992 * Get the Record at the specified index.
4993 * @param {Number} index The index of the Record to find.
4994 * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
4996 getAt : function(index){
4997 return this.data.itemAt(index);
5001 * Returns a range of Records between specified indices.
5002 * @param {Number} startIndex (optional) The starting index (defaults to 0)
5003 * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
5004 * @return {Roo.data.Record[]} An array of Records
5006 getRange : function(start, end){
5007 return this.data.getRange(start, end);
5011 storeOptions : function(o){
5012 o = Roo.apply({}, o);
5015 this.lastOptions = o;
5019 * Loads the Record cache from the configured Proxy using the configured Reader.
5021 * If using remote paging, then the first load call must specify the <em>start</em>
5022 * and <em>limit</em> properties in the options.params property to establish the initial
5023 * position within the dataset, and the number of Records to cache on each read from the Proxy.
5025 * <strong>It is important to note that for remote data sources, loading is asynchronous,
5026 * and this call will return before the new data has been loaded. Perform any post-processing
5027 * in a callback function, or in a "load" event handler.</strong>
5029 * @param {Object} options An object containing properties which control loading options:<ul>
5030 * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
5031 * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
5032 * passed the following arguments:<ul>
5033 * <li>r : Roo.data.Record[]</li>
5034 * <li>options: Options object from the load call</li>
5035 * <li>success: Boolean success indicator</li></ul></li>
5036 * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
5037 * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
5040 load : function(options){
5041 options = options || {};
5042 if(this.fireEvent("beforeload", this, options) !== false){
5043 this.storeOptions(options);
5044 var p = Roo.apply(options.params || {}, this.baseParams);
5045 // if meta was not loaded from remote source.. try requesting it.
5046 if (!this.reader.metaFromRemote) {
5049 if(this.sortInfo && this.remoteSort){
5050 var pn = this.paramNames;
5051 p[pn["sort"]] = this.sortInfo.field;
5052 p[pn["dir"]] = this.sortInfo.direction;
5054 if (this.multiSort) {
5055 var pn = this.paramNames;
5056 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
5059 this.proxy.load(p, this.reader, this.loadRecords, this, options);
5064 * Reloads the Record cache from the configured Proxy using the configured Reader and
5065 * the options from the last load operation performed.
5066 * @param {Object} options (optional) An object containing properties which may override the options
5067 * used in the last load operation. See {@link #load} for details (defaults to null, in which case
5068 * the most recently used options are reused).
5070 reload : function(options){
5071 this.load(Roo.applyIf(options||{}, this.lastOptions));
5075 // Called as a callback by the Reader during a load operation.
5076 loadRecords : function(o, options, success){
5077 if(!o || success === false){
5078 if(success !== false){
5079 this.fireEvent("load", this, [], options, o);
5081 if(options.callback){
5082 options.callback.call(options.scope || this, [], options, false);
5086 // if data returned failure - throw an exception.
5087 if (o.success === false) {
5088 // show a message if no listener is registered.
5089 if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
5090 Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
5092 // loadmask wil be hooked into this..
5093 this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
5096 var r = o.records, t = o.totalRecords || r.length;
5098 this.fireEvent("beforeloadadd", this, r, options, o);
5100 if(!options || options.add !== true){
5101 if(this.pruneModifiedRecords){
5104 for(var i = 0, len = r.length; i < len; i++){
5108 this.data = this.snapshot;
5109 delete this.snapshot;
5112 this.data.addAll(r);
5113 this.totalLength = t;
5115 this.fireEvent("datachanged", this);
5117 this.totalLength = Math.max(t, this.data.length+r.length);
5120 this.fireEvent("load", this, r, options, o);
5121 if(options.callback){
5122 options.callback.call(options.scope || this, r, options, true);
5128 * Loads data from a passed data block. A Reader which understands the format of the data
5129 * must have been configured in the constructor.
5130 * @param {Object} data The data block from which to read the Records. The format of the data expected
5131 * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
5132 * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
5134 loadData : function(o, append){
5135 var r = this.reader.readRecords(o);
5136 this.loadRecords(r, {add: append}, true);
5140 * Gets the number of cached records.
5142 * <em>If using paging, this may not be the total size of the dataset. If the data object
5143 * used by the Reader contains the dataset size, then the getTotalCount() function returns
5144 * the data set size</em>
5146 getCount : function(){
5147 return this.data.length || 0;
5151 * Gets the total number of records in the dataset as returned by the server.
5153 * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
5154 * the dataset size</em>
5156 getTotalCount : function(){
5157 return this.totalLength || 0;
5161 * Returns the sort state of the Store as an object with two properties:
5163 field {String} The name of the field by which the Records are sorted
5164 direction {String} The sort order, "ASC" or "DESC"
5167 getSortState : function(){
5168 return this.sortInfo;
5172 applySort : function(){
5173 if(this.sortInfo && !this.remoteSort){
5174 var s = this.sortInfo, f = s.field;
5175 var st = this.fields.get(f).sortType;
5176 var fn = function(r1, r2){
5177 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
5178 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
5180 this.data.sort(s.direction, fn);
5181 if(this.snapshot && this.snapshot != this.data){
5182 this.snapshot.sort(s.direction, fn);
5188 * Sets the default sort column and order to be used by the next load operation.
5189 * @param {String} fieldName The name of the field to sort by.
5190 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
5192 setDefaultSort : function(field, dir){
5193 this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
5198 * If remote sorting is used, the sort is performed on the server, and the cache is
5199 * reloaded. If local sorting is used, the cache is sorted internally.
5200 * @param {String} fieldName The name of the field to sort by.
5201 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
5203 sort : function(fieldName, dir){
5204 var f = this.fields.get(fieldName);
5206 this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
5208 if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
5209 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
5214 this.sortToggle[f.name] = dir;
5215 this.sortInfo = {field: f.name, direction: dir};
5216 if(!this.remoteSort){
5218 this.fireEvent("datachanged", this);
5220 this.load(this.lastOptions);
5225 * Calls the specified function for each of the Records in the cache.
5226 * @param {Function} fn The function to call. The Record is passed as the first parameter.
5227 * Returning <em>false</em> aborts and exits the iteration.
5228 * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
5230 each : function(fn, scope){
5231 this.data.each(fn, scope);
5235 * Gets all records modified since the last commit. Modified records are persisted across load operations
5236 * (e.g., during paging).
5237 * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
5239 getModifiedRecords : function(){
5240 return this.modified;
5244 createFilterFn : function(property, value, anyMatch){
5245 if(!value.exec){ // not a regex
5246 value = String(value);
5247 if(value.length == 0){
5250 value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
5253 return value.test(r.data[property]);
5258 * Sums the value of <i>property</i> for each record between start and end and returns the result.
5259 * @param {String} property A field on your records
5260 * @param {Number} start The record index to start at (defaults to 0)
5261 * @param {Number} end The last record index to include (defaults to length - 1)
5262 * @return {Number} The sum
5264 sum : function(property, start, end){
5265 var rs = this.data.items, v = 0;
5267 end = (end || end === 0) ? end : rs.length-1;
5269 for(var i = start; i <= end; i++){
5270 v += (rs[i].data[property] || 0);
5276 * Filter the records by a specified property.
5277 * @param {String} field A field on your records
5278 * @param {String/RegExp} value Either a string that the field
5279 * should start with or a RegExp to test against the field
5280 * @param {Boolean} anyMatch True to match any part not just the beginning
5282 filter : function(property, value, anyMatch){
5283 var fn = this.createFilterFn(property, value, anyMatch);
5284 return fn ? this.filterBy(fn) : this.clearFilter();
5288 * Filter by a function. The specified function will be called with each
5289 * record in this data source. If the function returns true the record is included,
5290 * otherwise it is filtered.
5291 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
5292 * @param {Object} scope (optional) The scope of the function (defaults to this)
5294 filterBy : function(fn, scope){
5295 this.snapshot = this.snapshot || this.data;
5296 this.data = this.queryBy(fn, scope||this);
5297 this.fireEvent("datachanged", this);
5301 * Query the records by a specified property.
5302 * @param {String} field A field on your records
5303 * @param {String/RegExp} value Either a string that the field
5304 * should start with or a RegExp to test against the field
5305 * @param {Boolean} anyMatch True to match any part not just the beginning
5306 * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
5308 query : function(property, value, anyMatch){
5309 var fn = this.createFilterFn(property, value, anyMatch);
5310 return fn ? this.queryBy(fn) : this.data.clone();
5314 * Query by a function. The specified function will be called with each
5315 * record in this data source. If the function returns true the record is included
5317 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
5318 * @param {Object} scope (optional) The scope of the function (defaults to this)
5319 @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
5321 queryBy : function(fn, scope){
5322 var data = this.snapshot || this.data;
5323 return data.filterBy(fn, scope||this);
5327 * Collects unique values for a particular dataIndex from this store.
5328 * @param {String} dataIndex The property to collect
5329 * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
5330 * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
5331 * @return {Array} An array of the unique values
5333 collect : function(dataIndex, allowNull, bypassFilter){
5334 var d = (bypassFilter === true && this.snapshot) ?
5335 this.snapshot.items : this.data.items;
5336 var v, sv, r = [], l = {};
5337 for(var i = 0, len = d.length; i < len; i++){
5338 v = d[i].data[dataIndex];
5340 if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
5349 * Revert to a view of the Record cache with no filtering applied.
5350 * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
5352 clearFilter : function(suppressEvent){
5353 if(this.snapshot && this.snapshot != this.data){
5354 this.data = this.snapshot;
5355 delete this.snapshot;
5356 if(suppressEvent !== true){
5357 this.fireEvent("datachanged", this);
5363 afterEdit : function(record){
5364 if(this.modified.indexOf(record) == -1){
5365 this.modified.push(record);
5367 this.fireEvent("update", this, record, Roo.data.Record.EDIT);
5371 afterReject : function(record){
5372 this.modified.remove(record);
5373 this.fireEvent("update", this, record, Roo.data.Record.REJECT);
5377 afterCommit : function(record){
5378 this.modified.remove(record);
5379 this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
5383 * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
5384 * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
5386 commitChanges : function(){
5387 var m = this.modified.slice(0);
5389 for(var i = 0, len = m.length; i < len; i++){
5395 * Cancel outstanding changes on all changed records.
5397 rejectChanges : function(){
5398 var m = this.modified.slice(0);
5400 for(var i = 0, len = m.length; i < len; i++){
5405 onMetaChange : function(meta, rtype, o){
5406 this.recordType = rtype;
5407 this.fields = rtype.prototype.fields;
5408 delete this.snapshot;
5409 this.sortInfo = meta.sortInfo || this.sortInfo;
5411 this.fireEvent('metachange', this, this.reader.meta);
5415 * Ext JS Library 1.1.1
5416 * Copyright(c) 2006-2007, Ext JS, LLC.
5418 * Originally Released Under LGPL - original licence link has changed is not relivant.
5421 * <script type="text/javascript">
5425 * @class Roo.data.SimpleStore
5426 * @extends Roo.data.Store
5427 * Small helper class to make creating Stores from Array data easier.
5428 * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
5429 * @cfg {Array} fields An array of field definition objects, or field name strings.
5430 * @cfg {Array} data The multi-dimensional array of data
5432 * @param {Object} config
5434 Roo.data.SimpleStore = function(config){
5435 Roo.data.SimpleStore.superclass.constructor.call(this, {
5437 reader: new Roo.data.ArrayReader({
5440 Roo.data.Record.create(config.fields)
5442 proxy : new Roo.data.MemoryProxy(config.data)
5446 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
5448 * Ext JS Library 1.1.1
5449 * Copyright(c) 2006-2007, Ext JS, LLC.
5451 * Originally Released Under LGPL - original licence link has changed is not relivant.
5454 * <script type="text/javascript">
5459 * @extends Roo.data.Store
5460 * @class Roo.data.JsonStore
5461 * Small helper class to make creating Stores for JSON data easier. <br/>
5463 var store = new Roo.data.JsonStore({
5464 url: 'get-images.php',
5466 fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
5469 * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
5470 * JsonReader and HttpProxy (unless inline data is provided).</b>
5471 * @cfg {Array} fields An array of field definition objects, or field name strings.
5473 * @param {Object} config
5475 Roo.data.JsonStore = function(c){
5476 Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
5477 proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
5478 reader: new Roo.data.JsonReader(c, c.fields)
5481 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
5483 * Ext JS Library 1.1.1
5484 * Copyright(c) 2006-2007, Ext JS, LLC.
5486 * Originally Released Under LGPL - original licence link has changed is not relivant.
5489 * <script type="text/javascript">
5493 Roo.data.Field = function(config){
5494 if(typeof config == "string"){
5495 config = {name: config};
5497 Roo.apply(this, config);
5503 var st = Roo.data.SortTypes;
5504 // named sortTypes are supported, here we look them up
5505 if(typeof this.sortType == "string"){
5506 this.sortType = st[this.sortType];
5509 // set default sortType for strings and dates
5513 this.sortType = st.asUCString;
5516 this.sortType = st.asDate;
5519 this.sortType = st.none;
5524 var stripRe = /[\$,%]/g;
5526 // prebuilt conversion function for this field, instead of
5527 // switching every time we're reading a value
5529 var cv, dateFormat = this.dateFormat;
5534 cv = function(v){ return v; };
5537 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
5541 return v !== undefined && v !== null && v !== '' ?
5542 parseInt(String(v).replace(stripRe, ""), 10) : '';
5547 return v !== undefined && v !== null && v !== '' ?
5548 parseFloat(String(v).replace(stripRe, ""), 10) : '';
5553 cv = function(v){ return v === true || v === "true" || v == 1; };
5560 if(v instanceof Date){
5564 if(dateFormat == "timestamp"){
5565 return new Date(v*1000);
5567 return Date.parseDate(v, dateFormat);
5569 var parsed = Date.parse(v);
5570 return parsed ? new Date(parsed) : null;
5579 Roo.data.Field.prototype = {
5587 * Ext JS Library 1.1.1
5588 * Copyright(c) 2006-2007, Ext JS, LLC.
5590 * Originally Released Under LGPL - original licence link has changed is not relivant.
5593 * <script type="text/javascript">
5596 // Base class for reading structured data from a data source. This class is intended to be
5597 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
5600 * @class Roo.data.DataReader
5601 * Base class for reading structured data from a data source. This class is intended to be
5602 * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
5605 Roo.data.DataReader = function(meta, recordType){
5609 this.recordType = recordType instanceof Array ?
5610 Roo.data.Record.create(recordType) : recordType;
5613 Roo.data.DataReader.prototype = {
5615 * Create an empty record
5616 * @param {Object} data (optional) - overlay some values
5617 * @return {Roo.data.Record} record created.
5619 newRow : function(d) {
5621 this.recordType.prototype.fields.each(function(c) {
5623 case 'int' : da[c.name] = 0; break;
5624 case 'date' : da[c.name] = new Date(); break;
5625 case 'float' : da[c.name] = 0.0; break;
5626 case 'boolean' : da[c.name] = false; break;
5627 default : da[c.name] = ""; break;
5631 return new this.recordType(Roo.apply(da, d));
5636 * Ext JS Library 1.1.1
5637 * Copyright(c) 2006-2007, Ext JS, LLC.
5639 * Originally Released Under LGPL - original licence link has changed is not relivant.
5642 * <script type="text/javascript">
5646 * @class Roo.data.DataProxy
5647 * @extends Roo.data.Observable
5648 * This class is an abstract base class for implementations which provide retrieval of
5649 * unformatted data objects.<br>
5651 * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
5652 * (of the appropriate type which knows how to parse the data object) to provide a block of
5653 * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
5655 * Custom implementations must implement the load method as described in
5656 * {@link Roo.data.HttpProxy#load}.
5658 Roo.data.DataProxy = function(){
5662 * Fires before a network request is made to retrieve a data object.
5663 * @param {Object} This DataProxy object.
5664 * @param {Object} params The params parameter to the load function.
5669 * Fires before the load method's callback is called.
5670 * @param {Object} This DataProxy object.
5671 * @param {Object} o The data object.
5672 * @param {Object} arg The callback argument object passed to the load function.
5676 * @event loadexception
5677 * Fires if an Exception occurs during data retrieval.
5678 * @param {Object} This DataProxy object.
5679 * @param {Object} o The data object.
5680 * @param {Object} arg The callback argument object passed to the load function.
5681 * @param {Object} e The Exception.
5683 loadexception : true
5685 Roo.data.DataProxy.superclass.constructor.call(this);
5688 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
5691 * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
5695 * Ext JS Library 1.1.1
5696 * Copyright(c) 2006-2007, Ext JS, LLC.
5698 * Originally Released Under LGPL - original licence link has changed is not relivant.
5701 * <script type="text/javascript">
5704 * @class Roo.data.MemoryProxy
5705 * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
5706 * to the Reader when its load method is called.
5708 * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
5710 Roo.data.MemoryProxy = function(data){
5714 Roo.data.MemoryProxy.superclass.constructor.call(this);
5718 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
5720 * Load data from the requested source (in this case an in-memory
5721 * data object passed to the constructor), read the data object into
5722 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
5723 * process that block using the passed callback.
5724 * @param {Object} params This parameter is not used by the MemoryProxy class.
5725 * @param {Roo.data.DataReader} reader The Reader object which converts the data
5726 * object into a block of Roo.data.Records.
5727 * @param {Function} callback The function into which to pass the block of Roo.data.records.
5728 * The function must be passed <ul>
5729 * <li>The Record block object</li>
5730 * <li>The "arg" argument from the load function</li>
5731 * <li>A boolean success indicator</li>
5733 * @param {Object} scope The scope in which to call the callback
5734 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
5736 load : function(params, reader, callback, scope, arg){
5737 params = params || {};
5740 result = reader.readRecords(this.data);
5742 this.fireEvent("loadexception", this, arg, null, e);
5743 callback.call(scope, null, arg, false);
5746 callback.call(scope, result, arg, true);
5750 update : function(params, records){
5755 * Ext JS Library 1.1.1
5756 * Copyright(c) 2006-2007, Ext JS, LLC.
5758 * Originally Released Under LGPL - original licence link has changed is not relivant.
5761 * <script type="text/javascript">
5764 * @class Roo.data.HttpProxy
5765 * @extends Roo.data.DataProxy
5766 * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
5767 * configured to reference a certain URL.<br><br>
5769 * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
5770 * from which the running page was served.<br><br>
5772 * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
5774 * Be aware that to enable the browser to parse an XML document, the server must set
5775 * the Content-Type header in the HTTP response to "text/xml".
5777 * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
5778 * an {@link Roo.data.Connection} object. If a Connection config is passed, the singleton {@link Roo.Ajax} object
5779 * will be used to make the request.
5781 Roo.data.HttpProxy = function(conn){
5782 Roo.data.HttpProxy.superclass.constructor.call(this);
5783 // is conn a conn config or a real conn?
5785 this.useAjax = !conn || !conn.events;
5789 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
5790 // thse are take from connection...
5793 * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
5796 * @cfg {Object} extraParams (Optional) An object containing properties which are used as
5797 * extra parameters to each request made by this object. (defaults to undefined)
5800 * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
5801 * to each request made by this object. (defaults to undefined)
5804 * @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)
5807 * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
5810 * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
5816 * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
5820 * Return the {@link Roo.data.Connection} object being used by this Proxy.
5821 * @return {Connection} The Connection object. This object may be used to subscribe to events on
5822 * a finer-grained basis than the DataProxy events.
5824 getConnection : function(){
5825 return this.useAjax ? Roo.Ajax : this.conn;
5829 * Load data from the configured {@link Roo.data.Connection}, read the data object into
5830 * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
5831 * process that block using the passed callback.
5832 * @param {Object} params An object containing properties which are to be used as HTTP parameters
5833 * for the request to the remote server.
5834 * @param {Roo.data.DataReader} reader The Reader object which converts the data
5835 * object into a block of Roo.data.Records.
5836 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
5837 * The function must be passed <ul>
5838 * <li>The Record block object</li>
5839 * <li>The "arg" argument from the load function</li>
5840 * <li>A boolean success indicator</li>
5842 * @param {Object} scope The scope in which to call the callback
5843 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
5845 load : function(params, reader, callback, scope, arg){
5846 if(this.fireEvent("beforeload", this, params) !== false){
5848 params : params || {},
5850 callback : callback,
5855 callback : this.loadResponse,
5859 Roo.applyIf(o, this.conn);
5860 if(this.activeRequest){
5861 Roo.Ajax.abort(this.activeRequest);
5863 this.activeRequest = Roo.Ajax.request(o);
5865 this.conn.request(o);
5868 callback.call(scope||this, null, arg, false);
5873 loadResponse : function(o, success, response){
5874 delete this.activeRequest;
5876 this.fireEvent("loadexception", this, o, response);
5877 o.request.callback.call(o.request.scope, null, o.request.arg, false);
5882 result = o.reader.read(response);
5884 this.fireEvent("loadexception", this, o, response, e);
5885 o.request.callback.call(o.request.scope, null, o.request.arg, false);
5889 this.fireEvent("load", this, o, o.request.arg);
5890 o.request.callback.call(o.request.scope, result, o.request.arg, true);
5894 update : function(dataSet){
5899 updateResponse : function(dataSet){
5904 * Ext JS Library 1.1.1
5905 * Copyright(c) 2006-2007, Ext JS, LLC.
5907 * Originally Released Under LGPL - original licence link has changed is not relivant.
5910 * <script type="text/javascript">
5914 * @class Roo.data.ScriptTagProxy
5915 * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
5916 * other than the originating domain of the running page.<br><br>
5918 * <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
5919 * of the running page, you must use this class, rather than DataProxy.</em><br><br>
5921 * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
5922 * source code that is used as the source inside a <script> tag.<br><br>
5924 * In order for the browser to process the returned data, the server must wrap the data object
5925 * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
5926 * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
5927 * depending on whether the callback name was passed:
5930 boolean scriptTag = false;
5931 String cb = request.getParameter("callback");
5934 response.setContentType("text/javascript");
5936 response.setContentType("application/x-json");
5938 Writer out = response.getWriter();
5940 out.write(cb + "(");
5942 out.print(dataBlock.toJsonString());
5949 * @param {Object} config A configuration object.
5951 Roo.data.ScriptTagProxy = function(config){
5952 Roo.data.ScriptTagProxy.superclass.constructor.call(this);
5953 Roo.apply(this, config);
5954 this.head = document.getElementsByTagName("head")[0];
5957 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
5959 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
5961 * @cfg {String} url The URL from which to request the data object.
5964 * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
5968 * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
5969 * the server the name of the callback function set up by the load call to process the returned data object.
5970 * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
5971 * javascript output which calls this named function passing the data object as its only parameter.
5973 callbackParam : "callback",
5975 * @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
5976 * name to the request.
5981 * Load data from the configured URL, read the data object into
5982 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
5983 * process that block using the passed callback.
5984 * @param {Object} params An object containing properties which are to be used as HTTP parameters
5985 * for the request to the remote server.
5986 * @param {Roo.data.DataReader} reader The Reader object which converts the data
5987 * object into a block of Roo.data.Records.
5988 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
5989 * The function must be passed <ul>
5990 * <li>The Record block object</li>
5991 * <li>The "arg" argument from the load function</li>
5992 * <li>A boolean success indicator</li>
5994 * @param {Object} scope The scope in which to call the callback
5995 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
5997 load : function(params, reader, callback, scope, arg){
5998 if(this.fireEvent("beforeload", this, params) !== false){
6000 var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
6003 url += (url.indexOf("?") != -1 ? "&" : "?") + p;
6005 url += "&_dc=" + (new Date().getTime());
6007 var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
6010 cb : "stcCallback"+transId,
6011 scriptId : "stcScript"+transId,
6015 callback : callback,
6021 window[trans.cb] = function(o){
6022 conn.handleResponse(o, trans);
6025 url += String.format("&{0}={1}", this.callbackParam, trans.cb);
6027 if(this.autoAbort !== false){
6031 trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
6033 var script = document.createElement("script");
6034 script.setAttribute("src", url);
6035 script.setAttribute("type", "text/javascript");
6036 script.setAttribute("id", trans.scriptId);
6037 this.head.appendChild(script);
6041 callback.call(scope||this, null, arg, false);
6046 isLoading : function(){
6047 return this.trans ? true : false;
6051 * Abort the current server request.
6054 if(this.isLoading()){
6055 this.destroyTrans(this.trans);
6060 destroyTrans : function(trans, isLoaded){
6061 this.head.removeChild(document.getElementById(trans.scriptId));
6062 clearTimeout(trans.timeoutId);
6064 window[trans.cb] = undefined;
6066 delete window[trans.cb];
6069 // if hasn't been loaded, wait for load to remove it to prevent script error
6070 window[trans.cb] = function(){
6071 window[trans.cb] = undefined;
6073 delete window[trans.cb];
6080 handleResponse : function(o, trans){
6082 this.destroyTrans(trans, true);
6085 result = trans.reader.readRecords(o);
6087 this.fireEvent("loadexception", this, o, trans.arg, e);
6088 trans.callback.call(trans.scope||window, null, trans.arg, false);
6091 this.fireEvent("load", this, o, trans.arg);
6092 trans.callback.call(trans.scope||window, result, trans.arg, true);
6096 handleFailure : function(trans){
6098 this.destroyTrans(trans, false);
6099 this.fireEvent("loadexception", this, null, trans.arg);
6100 trans.callback.call(trans.scope||window, null, trans.arg, false);
6104 * Ext JS Library 1.1.1
6105 * Copyright(c) 2006-2007, Ext JS, LLC.
6107 * Originally Released Under LGPL - original licence link has changed is not relivant.
6110 * <script type="text/javascript">
6114 * @class Roo.data.JsonReader
6115 * @extends Roo.data.DataReader
6116 * Data reader class to create an Array of Roo.data.Record objects from a JSON response
6117 * based on mappings in a provided Roo.data.Record constructor.
6119 * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
6120 * in the reply previously.
6125 var RecordDef = Roo.data.Record.create([
6126 {name: 'name', mapping: 'name'}, // "mapping" property not needed if it's the same as "name"
6127 {name: 'occupation'} // This field will use "occupation" as the mapping.
6129 var myReader = new Roo.data.JsonReader({
6130 totalProperty: "results", // The property which contains the total dataset size (optional)
6131 root: "rows", // The property which contains an Array of row objects
6132 id: "id" // The property within each row object that provides an ID for the record (optional)
6136 * This would consume a JSON file like this:
6138 { 'results': 2, 'rows': [
6139 { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
6140 { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
6143 * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
6144 * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
6145 * paged from the remote server.
6146 * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
6147 * @cfg {String} root name of the property which contains the Array of row objects.
6148 * @cfg {String} id Name of the property within a row object that contains a record identifier value.
6150 * Create a new JsonReader
6151 * @param {Object} meta Metadata configuration options
6152 * @param {Object} recordType Either an Array of field definition objects,
6153 * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
6155 Roo.data.JsonReader = function(meta, recordType){
6158 // set some defaults:
6160 totalProperty: 'total',
6161 successProperty : 'success',
6166 Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
6168 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
6171 * @prop {Boolean} metaFromRemote - if the meta data was loaded from the remote source.
6172 * Used by Store query builder to append _requestMeta to params.
6175 metaFromRemote : false,
6177 * This method is only used by a DataProxy which has retrieved data from a remote server.
6178 * @param {Object} response The XHR object which contains the JSON data in its responseText.
6179 * @return {Object} data A data block which is used by an Roo.data.Store object as
6180 * a cache of Roo.data.Records.
6182 read : function(response){
6183 var json = response.responseText;
6185 var o = /* eval:var:o */ eval("("+json+")");
6187 throw {message: "JsonReader.read: Json object not found"};
6193 this.metaFromRemote = true;
6194 this.meta = o.metaData;
6195 this.recordType = Roo.data.Record.create(o.metaData.fields);
6196 this.onMetaChange(this.meta, this.recordType, o);
6198 return this.readRecords(o);
6201 // private function a store will implement
6202 onMetaChange : function(meta, recordType, o){
6209 simpleAccess: function(obj, subsc) {
6216 getJsonAccessor: function(){
6218 return function(expr) {
6220 return(re.test(expr))
6221 ? new Function("obj", "return obj." + expr)
6231 * Create a data block containing Roo.data.Records from an XML document.
6232 * @param {Object} o An object which contains an Array of row objects in the property specified
6233 * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
6234 * which contains the total size of the dataset.
6235 * @return {Object} data A data block which is used by an Roo.data.Store object as
6236 * a cache of Roo.data.Records.
6238 readRecords : function(o){
6240 * After any data loads, the raw JSON data is available for further custom processing.
6244 var s = this.meta, Record = this.recordType,
6245 f = Record.prototype.fields, fi = f.items, fl = f.length;
6247 // Generate extraction functions for the totalProperty, the root, the id, and for each field
6249 if(s.totalProperty) {
6250 this.getTotal = this.getJsonAccessor(s.totalProperty);
6252 if(s.successProperty) {
6253 this.getSuccess = this.getJsonAccessor(s.successProperty);
6255 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
6257 var g = this.getJsonAccessor(s.id);
6258 this.getId = function(rec) {
6260 return (r === undefined || r === "") ? null : r;
6263 this.getId = function(){return null;};
6266 for(var jj = 0; jj < fl; jj++){
6268 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
6269 this.ef[jj] = this.getJsonAccessor(map);
6273 var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
6274 if(s.totalProperty){
6275 var vt = parseInt(this.getTotal(o), 10);
6280 if(s.successProperty){
6281 var vs = this.getSuccess(o);
6282 if(vs === false || vs === 'false'){
6287 for(var i = 0; i < c; i++){
6290 var id = this.getId(n);
6291 for(var j = 0; j < fl; j++){
6293 var v = this.ef[j](n);
6295 Roo.log('missing convert for ' + f.name);
6299 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
6301 var record = new Record(values, id);
6303 records[i] = record;
6309 totalRecords : totalRecords
6314 * Ext JS Library 1.1.1
6315 * Copyright(c) 2006-2007, Ext JS, LLC.
6317 * Originally Released Under LGPL - original licence link has changed is not relivant.
6320 * <script type="text/javascript">
6324 * @class Roo.data.XmlReader
6325 * @extends Roo.data.DataReader
6326 * Data reader class to create an Array of {@link Roo.data.Record} objects from an XML document
6327 * based on mappings in a provided Roo.data.Record constructor.<br><br>
6329 * <em>Note that in order for the browser to parse a returned XML document, the Content-Type
6330 * header in the HTTP response must be set to "text/xml".</em>
6334 var RecordDef = Roo.data.Record.create([
6335 {name: 'name', mapping: 'name'}, // "mapping" property not needed if it's the same as "name"
6336 {name: 'occupation'} // This field will use "occupation" as the mapping.
6338 var myReader = new Roo.data.XmlReader({
6339 totalRecords: "results", // The element which contains the total dataset size (optional)
6340 record: "row", // The repeated element which contains row information
6341 id: "id" // The element within the row that provides an ID for the record (optional)
6345 * This would consume an XML file like this:
6349 <results>2</results>
6352 <name>Bill</name>
6353 <occupation>Gardener</occupation>
6357 <name>Ben</name>
6358 <occupation>Horticulturalist</occupation>
6362 * @cfg {String} totalRecords The DomQuery path from which to retrieve the total number of records
6363 * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
6364 * paged from the remote server.
6365 * @cfg {String} record The DomQuery path to the repeated element which contains record information.
6366 * @cfg {String} success The DomQuery path to the success attribute used by forms.
6367 * @cfg {String} id The DomQuery path relative from the record element to the element that contains
6368 * a record identifier value.
6370 * Create a new XmlReader
6371 * @param {Object} meta Metadata configuration options
6372 * @param {Mixed} recordType The definition of the data record type to produce. This can be either a valid
6373 * Record subclass created with {@link Roo.data.Record#create}, or an array of objects with which to call
6374 * Roo.data.Record.create. See the {@link Roo.data.Record} class for more details.
6376 Roo.data.XmlReader = function(meta, recordType){
6378 Roo.data.XmlReader.superclass.constructor.call(this, meta, recordType||meta.fields);
6380 Roo.extend(Roo.data.XmlReader, Roo.data.DataReader, {
6382 * This method is only used by a DataProxy which has retrieved data from a remote server.
6383 * @param {Object} response The XHR object which contains the parsed XML document. The response is expected
6384 * to contain a method called 'responseXML' that returns an XML document object.
6385 * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
6386 * a cache of Roo.data.Records.
6388 read : function(response){
6389 var doc = response.responseXML;
6391 throw {message: "XmlReader.read: XML Document not available"};
6393 return this.readRecords(doc);
6397 * Create a data block containing Roo.data.Records from an XML document.
6398 * @param {Object} doc A parsed XML document.
6399 * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
6400 * a cache of Roo.data.Records.
6402 readRecords : function(doc){
6404 * After any data loads/reads, the raw XML Document is available for further custom processing.
6408 var root = doc.documentElement || doc;
6409 var q = Roo.DomQuery;
6410 var recordType = this.recordType, fields = recordType.prototype.fields;
6411 var sid = this.meta.id;
6412 var totalRecords = 0, success = true;
6413 if(this.meta.totalRecords){
6414 totalRecords = q.selectNumber(this.meta.totalRecords, root, 0);
6417 if(this.meta.success){
6418 var sv = q.selectValue(this.meta.success, root, true);
6419 success = sv !== false && sv !== 'false';
6422 var ns = q.select(this.meta.record, root);
6423 for(var i = 0, len = ns.length; i < len; i++) {
6426 var id = sid ? q.selectValue(sid, n) : undefined;
6427 for(var j = 0, jlen = fields.length; j < jlen; j++){
6428 var f = fields.items[j];
6429 var v = q.selectValue(f.mapping || f.name, n, f.defaultValue);
6433 var record = new recordType(values, id);
6435 records[records.length] = record;
6441 totalRecords : totalRecords || records.length
6446 * Ext JS Library 1.1.1
6447 * Copyright(c) 2006-2007, Ext JS, LLC.
6449 * Originally Released Under LGPL - original licence link has changed is not relivant.
6452 * <script type="text/javascript">
6456 * @class Roo.data.ArrayReader
6457 * @extends Roo.data.DataReader
6458 * Data reader class to create an Array of Roo.data.Record objects from an Array.
6459 * Each element of that Array represents a row of data fields. The
6460 * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
6461 * of the field definition if it exists, or the field's ordinal position in the definition.<br>
6465 var RecordDef = Roo.data.Record.create([
6466 {name: 'name', mapping: 1}, // "mapping" only needed if an "id" field is present which
6467 {name: 'occupation', mapping: 2} // precludes using the ordinal position as the index.
6469 var myReader = new Roo.data.ArrayReader({
6470 id: 0 // The subscript within row Array that provides an ID for the Record (optional)
6474 * This would consume an Array like this:
6476 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
6478 * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
6480 * Create a new JsonReader
6481 * @param {Object} meta Metadata configuration options.
6482 * @param {Object} recordType Either an Array of field definition objects
6483 * as specified to {@link Roo.data.Record#create},
6484 * or an {@link Roo.data.Record} object
6485 * created using {@link Roo.data.Record#create}.
6487 Roo.data.ArrayReader = function(meta, recordType){
6488 Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
6491 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
6493 * Create a data block containing Roo.data.Records from an XML document.
6494 * @param {Object} o An Array of row objects which represents the dataset.
6495 * @return {Object} data A data block which is used by an Roo.data.Store object as
6496 * a cache of Roo.data.Records.
6498 readRecords : function(o){
6499 var sid = this.meta ? this.meta.id : null;
6500 var recordType = this.recordType, fields = recordType.prototype.fields;
6503 for(var i = 0; i < root.length; i++){
6506 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
6507 for(var j = 0, jlen = fields.length; j < jlen; j++){
6508 var f = fields.items[j];
6509 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
6510 var v = n[k] !== undefined ? n[k] : f.defaultValue;
6514 var record = new recordType(values, id);
6516 records[records.length] = record;
6520 totalRecords : records.length
6525 * Ext JS Library 1.1.1
6526 * Copyright(c) 2006-2007, Ext JS, LLC.
6528 * Originally Released Under LGPL - original licence link has changed is not relivant.
6531 * <script type="text/javascript">
6536 * @class Roo.data.Tree
6537 * @extends Roo.util.Observable
6538 * Represents a tree data structure and bubbles all the events for its nodes. The nodes
6539 * in the tree have most standard DOM functionality.
6541 * @param {Node} root (optional) The root node
6543 Roo.data.Tree = function(root){
6546 * The root node for this tree
6551 this.setRootNode(root);
6556 * Fires when a new child node is appended to a node in this tree.
6557 * @param {Tree} tree The owner tree
6558 * @param {Node} parent The parent node
6559 * @param {Node} node The newly appended node
6560 * @param {Number} index The index of the newly appended node
6565 * Fires when a child node is removed from a node in this tree.
6566 * @param {Tree} tree The owner tree
6567 * @param {Node} parent The parent node
6568 * @param {Node} node The child node removed
6573 * Fires when a node is moved to a new location in the tree
6574 * @param {Tree} tree The owner tree
6575 * @param {Node} node The node moved
6576 * @param {Node} oldParent The old parent of this node
6577 * @param {Node} newParent The new parent of this node
6578 * @param {Number} index The index it was moved to
6583 * Fires when a new child node is inserted in a node in this tree.
6584 * @param {Tree} tree The owner tree
6585 * @param {Node} parent The parent node
6586 * @param {Node} node The child node inserted
6587 * @param {Node} refNode The child node the node was inserted before
6591 * @event beforeappend
6592 * Fires before a new child is appended to a node in this tree, return false to cancel the append.
6593 * @param {Tree} tree The owner tree
6594 * @param {Node} parent The parent node
6595 * @param {Node} node The child node to be appended
6597 "beforeappend" : true,
6599 * @event beforeremove
6600 * Fires before a child is removed from a node in this tree, return false to cancel the remove.
6601 * @param {Tree} tree The owner tree
6602 * @param {Node} parent The parent node
6603 * @param {Node} node The child node to be removed
6605 "beforeremove" : true,
6608 * Fires before a node is moved to a new location in the tree. Return false to cancel the move.
6609 * @param {Tree} tree The owner tree
6610 * @param {Node} node The node being moved
6611 * @param {Node} oldParent The parent of the node
6612 * @param {Node} newParent The new parent the node is moving to
6613 * @param {Number} index The index it is being moved to
6615 "beforemove" : true,
6617 * @event beforeinsert
6618 * Fires before a new child is inserted in a node in this tree, return false to cancel the insert.
6619 * @param {Tree} tree The owner tree
6620 * @param {Node} parent The parent node
6621 * @param {Node} node The child node to be inserted
6622 * @param {Node} refNode The child node the node is being inserted before
6624 "beforeinsert" : true
6627 Roo.data.Tree.superclass.constructor.call(this);
6630 Roo.extend(Roo.data.Tree, Roo.util.Observable, {
6633 proxyNodeEvent : function(){
6634 return this.fireEvent.apply(this, arguments);
6638 * Returns the root node for this tree.
6641 getRootNode : function(){
6646 * Sets the root node for this tree.
6647 * @param {Node} node
6650 setRootNode : function(node){
6652 node.ownerTree = this;
6654 this.registerNode(node);
6659 * Gets a node in this tree by its id.
6660 * @param {String} id
6663 getNodeById : function(id){
6664 return this.nodeHash[id];
6667 registerNode : function(node){
6668 this.nodeHash[node.id] = node;
6671 unregisterNode : function(node){
6672 delete this.nodeHash[node.id];
6675 toString : function(){
6676 return "[Tree"+(this.id?" "+this.id:"")+"]";
6681 * @class Roo.data.Node
6682 * @extends Roo.util.Observable
6683 * @cfg {Boolean} leaf true if this node is a leaf and does not have children
6684 * @cfg {String} id The id for this node. If one is not specified, one is generated.
6686 * @param {Object} attributes The attributes/config for the node
6688 Roo.data.Node = function(attributes){
6690 * The attributes supplied for the node. You can use this property to access any custom attributes you supplied.
6693 this.attributes = attributes || {};
6694 this.leaf = this.attributes.leaf;
6696 * The node id. @type String
6698 this.id = this.attributes.id;
6700 this.id = Roo.id(null, "ynode-");
6701 this.attributes.id = this.id;
6706 * All child nodes of this node. @type Array
6708 this.childNodes = [];
6709 if(!this.childNodes.indexOf){ // indexOf is a must
6710 this.childNodes.indexOf = function(o){
6711 for(var i = 0, len = this.length; i < len; i++){
6720 * The parent node for this node. @type Node
6722 this.parentNode = null;
6724 * The first direct child node of this node, or null if this node has no child nodes. @type Node
6726 this.firstChild = null;
6728 * The last direct child node of this node, or null if this node has no child nodes. @type Node
6730 this.lastChild = null;
6732 * The node immediately preceding this node in the tree, or null if there is no sibling node. @type Node
6734 this.previousSibling = null;
6736 * The node immediately following this node in the tree, or null if there is no sibling node. @type Node
6738 this.nextSibling = null;
6743 * Fires when a new child node is appended
6744 * @param {Tree} tree The owner tree
6745 * @param {Node} this This node
6746 * @param {Node} node The newly appended node
6747 * @param {Number} index The index of the newly appended node
6752 * Fires when a child node is removed
6753 * @param {Tree} tree The owner tree
6754 * @param {Node} this This node
6755 * @param {Node} node The removed node
6760 * Fires when this node is moved to a new location in the tree
6761 * @param {Tree} tree The owner tree
6762 * @param {Node} this This node
6763 * @param {Node} oldParent The old parent of this node
6764 * @param {Node} newParent The new parent of this node
6765 * @param {Number} index The index it was moved to
6770 * Fires when a new child node is inserted.
6771 * @param {Tree} tree The owner tree
6772 * @param {Node} this This node
6773 * @param {Node} node The child node inserted
6774 * @param {Node} refNode The child node the node was inserted before
6778 * @event beforeappend
6779 * Fires before a new child is appended, return false to cancel the append.
6780 * @param {Tree} tree The owner tree
6781 * @param {Node} this This node
6782 * @param {Node} node The child node to be appended
6784 "beforeappend" : true,
6786 * @event beforeremove
6787 * Fires before a child is removed, return false to cancel the remove.
6788 * @param {Tree} tree The owner tree
6789 * @param {Node} this This node
6790 * @param {Node} node The child node to be removed
6792 "beforeremove" : true,
6795 * Fires before this node is moved to a new location in the tree. Return false to cancel the move.
6796 * @param {Tree} tree The owner tree
6797 * @param {Node} this This node
6798 * @param {Node} oldParent The parent of this node
6799 * @param {Node} newParent The new parent this node is moving to
6800 * @param {Number} index The index it is being moved to
6802 "beforemove" : true,
6804 * @event beforeinsert
6805 * Fires before a new child is inserted, return false to cancel the insert.
6806 * @param {Tree} tree The owner tree
6807 * @param {Node} this This node
6808 * @param {Node} node The child node to be inserted
6809 * @param {Node} refNode The child node the node is being inserted before
6811 "beforeinsert" : true
6813 this.listeners = this.attributes.listeners;
6814 Roo.data.Node.superclass.constructor.call(this);
6817 Roo.extend(Roo.data.Node, Roo.util.Observable, {
6818 fireEvent : function(evtName){
6819 // first do standard event for this node
6820 if(Roo.data.Node.superclass.fireEvent.apply(this, arguments) === false){
6823 // then bubble it up to the tree if the event wasn't cancelled
6824 var ot = this.getOwnerTree();
6826 if(ot.proxyNodeEvent.apply(ot, arguments) === false){
6834 * Returns true if this node is a leaf
6837 isLeaf : function(){
6838 return this.leaf === true;
6842 setFirstChild : function(node){
6843 this.firstChild = node;
6847 setLastChild : function(node){
6848 this.lastChild = node;
6853 * Returns true if this node is the last child of its parent
6856 isLast : function(){
6857 return (!this.parentNode ? true : this.parentNode.lastChild == this);
6861 * Returns true if this node is the first child of its parent
6864 isFirst : function(){
6865 return (!this.parentNode ? true : this.parentNode.firstChild == this);
6868 hasChildNodes : function(){
6869 return !this.isLeaf() && this.childNodes.length > 0;
6873 * Insert node(s) as the last child node of this node.
6874 * @param {Node/Array} node The node or Array of nodes to append
6875 * @return {Node} The appended node if single append, or null if an array was passed
6877 appendChild : function(node){
6879 if(node instanceof Array){
6881 }else if(arguments.length > 1){
6884 // if passed an array or multiple args do them one by one
6886 for(var i = 0, len = multi.length; i < len; i++) {
6887 this.appendChild(multi[i]);
6890 if(this.fireEvent("beforeappend", this.ownerTree, this, node) === false){
6893 var index = this.childNodes.length;
6894 var oldParent = node.parentNode;
6895 // it's a move, make sure we move it cleanly
6897 if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index) === false){
6900 oldParent.removeChild(node);
6902 index = this.childNodes.length;
6904 this.setFirstChild(node);
6906 this.childNodes.push(node);
6907 node.parentNode = this;
6908 var ps = this.childNodes[index-1];
6910 node.previousSibling = ps;
6911 ps.nextSibling = node;
6913 node.previousSibling = null;
6915 node.nextSibling = null;
6916 this.setLastChild(node);
6917 node.setOwnerTree(this.getOwnerTree());
6918 this.fireEvent("append", this.ownerTree, this, node, index);
6920 node.fireEvent("move", this.ownerTree, node, oldParent, this, index);
6927 * Removes a child node from this node.
6928 * @param {Node} node The node to remove
6929 * @return {Node} The removed node
6931 removeChild : function(node){
6932 var index = this.childNodes.indexOf(node);
6936 if(this.fireEvent("beforeremove", this.ownerTree, this, node) === false){
6940 // remove it from childNodes collection
6941 this.childNodes.splice(index, 1);
6944 if(node.previousSibling){
6945 node.previousSibling.nextSibling = node.nextSibling;
6947 if(node.nextSibling){
6948 node.nextSibling.previousSibling = node.previousSibling;
6951 // update child refs
6952 if(this.firstChild == node){
6953 this.setFirstChild(node.nextSibling);
6955 if(this.lastChild == node){
6956 this.setLastChild(node.previousSibling);
6959 node.setOwnerTree(null);
6960 // clear any references from the node
6961 node.parentNode = null;
6962 node.previousSibling = null;
6963 node.nextSibling = null;
6964 this.fireEvent("remove", this.ownerTree, this, node);
6969 * Inserts the first node before the second node in this nodes childNodes collection.
6970 * @param {Node} node The node to insert
6971 * @param {Node} refNode The node to insert before (if null the node is appended)
6972 * @return {Node} The inserted node
6974 insertBefore : function(node, refNode){
6975 if(!refNode){ // like standard Dom, refNode can be null for append
6976 return this.appendChild(node);
6979 if(node == refNode){
6983 if(this.fireEvent("beforeinsert", this.ownerTree, this, node, refNode) === false){
6986 var index = this.childNodes.indexOf(refNode);
6987 var oldParent = node.parentNode;
6988 var refIndex = index;
6990 // when moving internally, indexes will change after remove
6991 if(oldParent == this && this.childNodes.indexOf(node) < index){
6995 // it's a move, make sure we move it cleanly
6997 if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index, refNode) === false){
7000 oldParent.removeChild(node);
7003 this.setFirstChild(node);
7005 this.childNodes.splice(refIndex, 0, node);
7006 node.parentNode = this;
7007 var ps = this.childNodes[refIndex-1];
7009 node.previousSibling = ps;
7010 ps.nextSibling = node;
7012 node.previousSibling = null;
7014 node.nextSibling = refNode;
7015 refNode.previousSibling = node;
7016 node.setOwnerTree(this.getOwnerTree());
7017 this.fireEvent("insert", this.ownerTree, this, node, refNode);
7019 node.fireEvent("move", this.ownerTree, node, oldParent, this, refIndex, refNode);
7025 * Returns the child node at the specified index.
7026 * @param {Number} index
7029 item : function(index){
7030 return this.childNodes[index];
7034 * Replaces one child node in this node with another.
7035 * @param {Node} newChild The replacement node
7036 * @param {Node} oldChild The node to replace
7037 * @return {Node} The replaced node
7039 replaceChild : function(newChild, oldChild){
7040 this.insertBefore(newChild, oldChild);
7041 this.removeChild(oldChild);
7046 * Returns the index of a child node
7047 * @param {Node} node
7048 * @return {Number} The index of the node or -1 if it was not found
7050 indexOf : function(child){
7051 return this.childNodes.indexOf(child);
7055 * Returns the tree this node is in.
7058 getOwnerTree : function(){
7059 // if it doesn't have one, look for one
7060 if(!this.ownerTree){
7064 this.ownerTree = p.ownerTree;
7070 return this.ownerTree;
7074 * Returns depth of this node (the root node has a depth of 0)
7077 getDepth : function(){
7080 while(p.parentNode){
7088 setOwnerTree : function(tree){
7089 // if it's move, we need to update everyone
7090 if(tree != this.ownerTree){
7092 this.ownerTree.unregisterNode(this);
7094 this.ownerTree = tree;
7095 var cs = this.childNodes;
7096 for(var i = 0, len = cs.length; i < len; i++) {
7097 cs[i].setOwnerTree(tree);
7100 tree.registerNode(this);
7106 * Returns the path for this node. The path can be used to expand or select this node programmatically.
7107 * @param {String} attr (optional) The attr to use for the path (defaults to the node's id)
7108 * @return {String} The path
7110 getPath : function(attr){
7111 attr = attr || "id";
7112 var p = this.parentNode;
7113 var b = [this.attributes[attr]];
7115 b.unshift(p.attributes[attr]);
7118 var sep = this.getOwnerTree().pathSeparator;
7119 return sep + b.join(sep);
7123 * Bubbles up the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
7124 * function call will be the scope provided or the current node. The arguments to the function
7125 * will be the args provided or the current node. If the function returns false at any point,
7126 * the bubble is stopped.
7127 * @param {Function} fn The function to call
7128 * @param {Object} scope (optional) The scope of the function (defaults to current node)
7129 * @param {Array} args (optional) The args to call the function with (default to passing the current node)
7131 bubble : function(fn, scope, args){
7134 if(fn.call(scope || p, args || p) === false){
7142 * Cascades down the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
7143 * function call will be the scope provided or the current node. The arguments to the function
7144 * will be the args provided or the current node. If the function returns false at any point,
7145 * the cascade is stopped on that branch.
7146 * @param {Function} fn The function to call
7147 * @param {Object} scope (optional) The scope of the function (defaults to current node)
7148 * @param {Array} args (optional) The args to call the function with (default to passing the current node)
7150 cascade : function(fn, scope, args){
7151 if(fn.call(scope || this, args || this) !== false){
7152 var cs = this.childNodes;
7153 for(var i = 0, len = cs.length; i < len; i++) {
7154 cs[i].cascade(fn, scope, args);
7160 * Interates the child nodes of this node, calling the specified function with each node. The scope (<i>this</i>) of
7161 * function call will be the scope provided or the current node. The arguments to the function
7162 * will be the args provided or the current node. If the function returns false at any point,
7163 * the iteration stops.
7164 * @param {Function} fn The function to call
7165 * @param {Object} scope (optional) The scope of the function (defaults to current node)
7166 * @param {Array} args (optional) The args to call the function with (default to passing the current node)
7168 eachChild : function(fn, scope, args){
7169 var cs = this.childNodes;
7170 for(var i = 0, len = cs.length; i < len; i++) {
7171 if(fn.call(scope || this, args || cs[i]) === false){
7178 * Finds the first child that has the attribute with the specified value.
7179 * @param {String} attribute The attribute name
7180 * @param {Mixed} value The value to search for
7181 * @return {Node} The found child or null if none was found
7183 findChild : function(attribute, value){
7184 var cs = this.childNodes;
7185 for(var i = 0, len = cs.length; i < len; i++) {
7186 if(cs[i].attributes[attribute] == value){
7194 * Finds the first child by a custom function. The child matches if the function passed
7196 * @param {Function} fn
7197 * @param {Object} scope (optional)
7198 * @return {Node} The found child or null if none was found
7200 findChildBy : function(fn, scope){
7201 var cs = this.childNodes;
7202 for(var i = 0, len = cs.length; i < len; i++) {
7203 if(fn.call(scope||cs[i], cs[i]) === true){
7211 * Sorts this nodes children using the supplied sort function
7212 * @param {Function} fn
7213 * @param {Object} scope (optional)
7215 sort : function(fn, scope){
7216 var cs = this.childNodes;
7217 var len = cs.length;
7219 var sortFn = scope ? function(){fn.apply(scope, arguments);} : fn;
7221 for(var i = 0; i < len; i++){
7223 n.previousSibling = cs[i-1];
7224 n.nextSibling = cs[i+1];
7226 this.setFirstChild(n);
7229 this.setLastChild(n);
7236 * Returns true if this node is an ancestor (at any point) of the passed node.
7237 * @param {Node} node
7240 contains : function(node){
7241 return node.isAncestor(this);
7245 * Returns true if the passed node is an ancestor (at any point) of this node.
7246 * @param {Node} node
7249 isAncestor : function(node){
7250 var p = this.parentNode;
7260 toString : function(){
7261 return "[Node"+(this.id?" "+this.id:"")+"]";
7265 * Ext JS Library 1.1.1
7266 * Copyright(c) 2006-2007, Ext JS, LLC.
7268 * Originally Released Under LGPL - original licence link has changed is not relivant.
7271 * <script type="text/javascript">
7276 * @class Roo.ComponentMgr
7277 * Provides a common registry of all components on a page so that they can be easily accessed by component id (see {@link Roo.getCmp}).
7280 Roo.ComponentMgr = function(){
7281 var all = new Roo.util.MixedCollection();
7285 * Registers a component.
7286 * @param {Roo.Component} c The component
7288 register : function(c){
7293 * Unregisters a component.
7294 * @param {Roo.Component} c The component
7296 unregister : function(c){
7301 * Returns a component by id
7302 * @param {String} id The component id
7309 * Registers a function that will be called when a specified component is added to ComponentMgr
7310 * @param {String} id The component id
7311 * @param {Funtction} fn The callback function
7312 * @param {Object} scope The scope of the callback
7314 onAvailable : function(id, fn, scope){
7315 all.on("add", function(index, o){
7317 fn.call(scope || o, o);
7318 all.un("add", fn, scope);
7325 * Ext JS Library 1.1.1
7326 * Copyright(c) 2006-2007, Ext JS, LLC.
7328 * Originally Released Under LGPL - original licence link has changed is not relivant.
7331 * <script type="text/javascript">
7335 * @class Roo.Component
7336 * @extends Roo.util.Observable
7337 * Base class for all major Roo components. All subclasses of Component can automatically participate in the standard
7338 * Roo component lifecycle of creation, rendering and destruction. They also have automatic support for basic hide/show
7339 * and enable/disable behavior. Component allows any subclass to be lazy-rendered into any {@link Roo.Container} and
7340 * to be automatically registered with the {@link Roo.ComponentMgr} so that it can be referenced at any time via {@link Roo.getCmp}.
7341 * All visual components (widgets) that require rendering into a layout should subclass Component.
7343 * @param {Roo.Element/String/Object} config The configuration options. If an element is passed, it is set as the internal
7344 * 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
7345 * and is used as the component id. Otherwise, it is assumed to be a standard config object and is applied to the component.
7347 Roo.Component = function(config){
7348 config = config || {};
7349 if(config.tagName || config.dom || typeof config == "string"){ // element object
7350 config = {el: config, id: config.id || config};
7352 this.initialConfig = config;
7354 Roo.apply(this, config);
7358 * Fires after the component is disabled.
7359 * @param {Roo.Component} this
7364 * Fires after the component is enabled.
7365 * @param {Roo.Component} this
7370 * Fires before the component is shown. Return false to stop the show.
7371 * @param {Roo.Component} this
7376 * Fires after the component is shown.
7377 * @param {Roo.Component} this
7382 * Fires before the component is hidden. Return false to stop the hide.
7383 * @param {Roo.Component} this
7388 * Fires after the component is hidden.
7389 * @param {Roo.Component} this
7393 * @event beforerender
7394 * Fires before the component is rendered. Return false to stop the render.
7395 * @param {Roo.Component} this
7397 beforerender : true,
7400 * Fires after the component is rendered.
7401 * @param {Roo.Component} this
7405 * @event beforedestroy
7406 * Fires before the component is destroyed. Return false to stop the destroy.
7407 * @param {Roo.Component} this
7409 beforedestroy : true,
7412 * Fires after the component is destroyed.
7413 * @param {Roo.Component} this
7418 this.id = "ext-comp-" + (++Roo.Component.AUTO_ID);
7420 Roo.ComponentMgr.register(this);
7421 Roo.Component.superclass.constructor.call(this);
7422 this.initComponent();
7423 if(this.renderTo){ // not supported by all components yet. use at your own risk!
7424 this.render(this.renderTo);
7425 delete this.renderTo;
7430 Roo.Component.AUTO_ID = 1000;
7432 Roo.extend(Roo.Component, Roo.util.Observable, {
7434 * @scope Roo.Component.prototype
7436 * true if this component is hidden. Read-only.
7441 * true if this component is disabled. Read-only.
7446 * true if this component has been rendered. Read-only.
7450 /** @cfg {String} disableClass
7451 * CSS class added to the component when it is disabled (defaults to "x-item-disabled").
7453 disabledClass : "x-item-disabled",
7454 /** @cfg {Boolean} allowDomMove
7455 * Whether the component can move the Dom node when rendering (defaults to true).
7457 allowDomMove : true,
7458 /** @cfg {String} hideMode
7459 * How this component should hidden. Supported values are
7460 * "visibility" (css visibility), "offsets" (negative offset position) and
7461 * "display" (css display) - defaults to "display".
7463 hideMode: 'display',
7466 ctype : "Roo.Component",
7469 * @cfg {String} actionMode
7470 * which property holds the element that used for hide() / show() / disable() / enable()
7476 getActionEl : function(){
7477 return this[this.actionMode];
7480 initComponent : Roo.emptyFn,
7482 * If this is a lazy rendering component, render it to its container element.
7483 * @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.
7485 render : function(container, position){
7486 if(!this.rendered && this.fireEvent("beforerender", this) !== false){
7487 if(!container && this.el){
7488 this.el = Roo.get(this.el);
7489 container = this.el.dom.parentNode;
7490 this.allowDomMove = false;
7492 this.container = Roo.get(container);
7493 this.rendered = true;
7494 if(position !== undefined){
7495 if(typeof position == 'number'){
7496 position = this.container.dom.childNodes[position];
7498 position = Roo.getDom(position);
7501 this.onRender(this.container, position || null);
7503 this.el.addClass(this.cls);
7507 this.el.applyStyles(this.style);
7510 this.fireEvent("render", this);
7511 this.afterRender(this.container);
7523 // default function is not really useful
7524 onRender : function(ct, position){
7526 this.el = Roo.get(this.el);
7527 if(this.allowDomMove !== false){
7528 ct.dom.insertBefore(this.el.dom, position);
7534 getAutoCreate : function(){
7535 var cfg = typeof this.autoCreate == "object" ?
7536 this.autoCreate : Roo.apply({}, this.defaultAutoCreate);
7537 if(this.id && !cfg.id){
7544 afterRender : Roo.emptyFn,
7547 * Destroys this component by purging any event listeners, removing the component's element from the DOM,
7548 * removing the component from its {@link Roo.Container} (if applicable) and unregistering it from {@link Roo.ComponentMgr}.
7550 destroy : function(){
7551 if(this.fireEvent("beforedestroy", this) !== false){
7552 this.purgeListeners();
7553 this.beforeDestroy();
7555 this.el.removeAllListeners();
7557 if(this.actionMode == "container"){
7558 this.container.remove();
7562 Roo.ComponentMgr.unregister(this);
7563 this.fireEvent("destroy", this);
7568 beforeDestroy : function(){
7573 onDestroy : function(){
7578 * Returns the underlying {@link Roo.Element}.
7579 * @return {Roo.Element} The element
7586 * Returns the id of this component.
7594 * Try to focus this component.
7595 * @param {Boolean} selectText True to also select the text in this component (if applicable)
7596 * @return {Roo.Component} this
7598 focus : function(selectText){
7601 if(selectText === true){
7602 this.el.dom.select();
7617 * Disable this component.
7618 * @return {Roo.Component} this
7620 disable : function(){
7624 this.disabled = true;
7625 this.fireEvent("disable", this);
7630 onDisable : function(){
7631 this.getActionEl().addClass(this.disabledClass);
7632 this.el.dom.disabled = true;
7636 * Enable this component.
7637 * @return {Roo.Component} this
7639 enable : function(){
7643 this.disabled = false;
7644 this.fireEvent("enable", this);
7649 onEnable : function(){
7650 this.getActionEl().removeClass(this.disabledClass);
7651 this.el.dom.disabled = false;
7655 * Convenience function for setting disabled/enabled by boolean.
7656 * @param {Boolean} disabled
7658 setDisabled : function(disabled){
7659 this[disabled ? "disable" : "enable"]();
7663 * Show this component.
7664 * @return {Roo.Component} this
7667 if(this.fireEvent("beforeshow", this) !== false){
7668 this.hidden = false;
7672 this.fireEvent("show", this);
7678 onShow : function(){
7679 var ae = this.getActionEl();
7680 if(this.hideMode == 'visibility'){
7681 ae.dom.style.visibility = "visible";
7682 }else if(this.hideMode == 'offsets'){
7683 ae.removeClass('x-hidden');
7685 ae.dom.style.display = "";
7690 * Hide this component.
7691 * @return {Roo.Component} this
7694 if(this.fireEvent("beforehide", this) !== false){
7699 this.fireEvent("hide", this);
7705 onHide : function(){
7706 var ae = this.getActionEl();
7707 if(this.hideMode == 'visibility'){
7708 ae.dom.style.visibility = "hidden";
7709 }else if(this.hideMode == 'offsets'){
7710 ae.addClass('x-hidden');
7712 ae.dom.style.display = "none";
7717 * Convenience function to hide or show this component by boolean.
7718 * @param {Boolean} visible True to show, false to hide
7719 * @return {Roo.Component} this
7721 setVisible: function(visible){
7731 * Returns true if this component is visible.
7733 isVisible : function(){
7734 return this.getActionEl().isVisible();
7737 cloneConfig : function(overrides){
7738 overrides = overrides || {};
7739 var id = overrides.id || Roo.id();
7740 var cfg = Roo.applyIf(overrides, this.initialConfig);
7741 cfg.id = id; // prevent dup id
7742 return new this.constructor(cfg);
7746 * Ext JS Library 1.1.1
7747 * Copyright(c) 2006-2007, Ext JS, LLC.
7749 * Originally Released Under LGPL - original licence link has changed is not relivant.
7752 * <script type="text/javascript">
7757 * @extends Roo.Element
7758 * An extended {@link Roo.Element} object that supports a shadow and shim, constrain to viewport and
7759 * automatic maintaining of shadow/shim positions.
7760 * @cfg {Boolean} shim False to disable the iframe shim in browsers which need one (defaults to true)
7761 * @cfg {String/Boolean} shadow True to create a shadow element with default class "x-layer-shadow", or
7762 * you can pass a string with a CSS class name. False turns off the shadow.
7763 * @cfg {Object} dh DomHelper object config to create element with (defaults to {tag: "div", cls: "x-layer"}).
7764 * @cfg {Boolean} constrain False to disable constrain to viewport (defaults to true)
7765 * @cfg {String} cls CSS class to add to the element
7766 * @cfg {Number} zindex Starting z-index (defaults to 11000)
7767 * @cfg {Number} shadowOffset Number of pixels to offset the shadow (defaults to 3)
7769 * @param {Object} config An object with config options.
7770 * @param {String/HTMLElement} existingEl (optional) Uses an existing DOM element. If the element is not found it creates it.
7773 Roo.Layer = function(config, existingEl){
7774 config = config || {};
7775 var dh = Roo.DomHelper;
7776 var cp = config.parentEl, pel = cp ? Roo.getDom(cp) : document.body;
7778 this.dom = Roo.getDom(existingEl);
7781 var o = config.dh || {tag: "div", cls: "x-layer"};
7782 this.dom = dh.append(pel, o);
7785 this.addClass(config.cls);
7787 this.constrain = config.constrain !== false;
7788 this.visibilityMode = Roo.Element.VISIBILITY;
7790 this.id = this.dom.id = config.id;
7792 this.id = Roo.id(this.dom);
7794 this.zindex = config.zindex || this.getZIndex();
7795 this.position("absolute", this.zindex);
7797 this.shadowOffset = config.shadowOffset || 4;
7798 this.shadow = new Roo.Shadow({
7799 offset : this.shadowOffset,
7800 mode : config.shadow
7803 this.shadowOffset = 0;
7805 this.useShim = config.shim !== false && Roo.useShims;
7806 this.useDisplay = config.useDisplay;
7810 var supr = Roo.Element.prototype;
7812 // shims are shared among layer to keep from having 100 iframes
7815 Roo.extend(Roo.Layer, Roo.Element, {
7817 getZIndex : function(){
7818 return this.zindex || parseInt(this.getStyle("z-index"), 10) || 11000;
7821 getShim : function(){
7828 var shim = shims.shift();
7830 shim = this.createShim();
7831 shim.enableDisplayMode('block');
7832 shim.dom.style.display = 'none';
7833 shim.dom.style.visibility = 'visible';
7835 var pn = this.dom.parentNode;
7836 if(shim.dom.parentNode != pn){
7837 pn.insertBefore(shim.dom, this.dom);
7839 shim.setStyle('z-index', this.getZIndex()-2);
7844 hideShim : function(){
7846 this.shim.setDisplayed(false);
7847 shims.push(this.shim);
7852 disableShadow : function(){
7854 this.shadowDisabled = true;
7856 this.lastShadowOffset = this.shadowOffset;
7857 this.shadowOffset = 0;
7861 enableShadow : function(show){
7863 this.shadowDisabled = false;
7864 this.shadowOffset = this.lastShadowOffset;
7865 delete this.lastShadowOffset;
7873 // this code can execute repeatedly in milliseconds (i.e. during a drag) so
7874 // code size was sacrificed for effeciency (e.g. no getBox/setBox, no XY calls)
7875 sync : function(doShow){
7876 var sw = this.shadow;
7877 if(!this.updating && this.isVisible() && (sw || this.useShim)){
7878 var sh = this.getShim();
7880 var w = this.getWidth(),
7881 h = this.getHeight();
7883 var l = this.getLeft(true),
7884 t = this.getTop(true);
7886 if(sw && !this.shadowDisabled){
7887 if(doShow && !sw.isVisible()){
7890 sw.realign(l, t, w, h);
7896 // fit the shim behind the shadow, so it is shimmed too
7897 var a = sw.adjusts, s = sh.dom.style;
7898 s.left = (Math.min(l, l+a.l))+"px";
7899 s.top = (Math.min(t, t+a.t))+"px";
7900 s.width = (w+a.w)+"px";
7901 s.height = (h+a.h)+"px";
7908 sh.setLeftTop(l, t);
7915 destroy : function(){
7920 this.removeAllListeners();
7921 var pn = this.dom.parentNode;
7923 pn.removeChild(this.dom);
7925 Roo.Element.uncache(this.id);
7928 remove : function(){
7933 beginUpdate : function(){
7934 this.updating = true;
7938 endUpdate : function(){
7939 this.updating = false;
7944 hideUnders : function(negOffset){
7952 constrainXY : function(){
7954 var vw = Roo.lib.Dom.getViewWidth(),
7955 vh = Roo.lib.Dom.getViewHeight();
7956 var s = Roo.get(document).getScroll();
7958 var xy = this.getXY();
7959 var x = xy[0], y = xy[1];
7960 var w = this.dom.offsetWidth+this.shadowOffset, h = this.dom.offsetHeight+this.shadowOffset;
7961 // only move it if it needs it
7963 // first validate right/bottom
7964 if((x + w) > vw+s.left){
7965 x = vw - w - this.shadowOffset;
7968 if((y + h) > vh+s.top){
7969 y = vh - h - this.shadowOffset;
7972 // then make sure top/left isn't negative
7983 var ay = this.avoidY;
7984 if(y <= ay && (y+h) >= ay){
7990 supr.setXY.call(this, xy);
7996 isVisible : function(){
7997 return this.visible;
8001 showAction : function(){
8002 this.visible = true; // track visibility to prevent getStyle calls
8003 if(this.useDisplay === true){
8004 this.setDisplayed("");
8005 }else if(this.lastXY){
8006 supr.setXY.call(this, this.lastXY);
8007 }else if(this.lastLT){
8008 supr.setLeftTop.call(this, this.lastLT[0], this.lastLT[1]);
8013 hideAction : function(){
8014 this.visible = false;
8015 if(this.useDisplay === true){
8016 this.setDisplayed(false);
8018 this.setLeftTop(-10000,-10000);
8022 // overridden Element method
8023 setVisible : function(v, a, d, c, e){
8028 var cb = function(){
8033 }.createDelegate(this);
8034 supr.setVisible.call(this, true, true, d, cb, e);
8037 this.hideUnders(true);
8046 }.createDelegate(this);
8048 supr.setVisible.call(this, v, a, d, cb, e);
8057 storeXY : function(xy){
8062 storeLeftTop : function(left, top){
8064 this.lastLT = [left, top];
8068 beforeFx : function(){
8069 this.beforeAction();
8070 return Roo.Layer.superclass.beforeFx.apply(this, arguments);
8074 afterFx : function(){
8075 Roo.Layer.superclass.afterFx.apply(this, arguments);
8076 this.sync(this.isVisible());
8080 beforeAction : function(){
8081 if(!this.updating && this.shadow){
8086 // overridden Element method
8087 setLeft : function(left){
8088 this.storeLeftTop(left, this.getTop(true));
8089 supr.setLeft.apply(this, arguments);
8093 setTop : function(top){
8094 this.storeLeftTop(this.getLeft(true), top);
8095 supr.setTop.apply(this, arguments);
8099 setLeftTop : function(left, top){
8100 this.storeLeftTop(left, top);
8101 supr.setLeftTop.apply(this, arguments);
8105 setXY : function(xy, a, d, c, e){
8107 this.beforeAction();
8109 var cb = this.createCB(c);
8110 supr.setXY.call(this, xy, a, d, cb, e);
8117 createCB : function(c){
8128 // overridden Element method
8129 setX : function(x, a, d, c, e){
8130 this.setXY([x, this.getY()], a, d, c, e);
8133 // overridden Element method
8134 setY : function(y, a, d, c, e){
8135 this.setXY([this.getX(), y], a, d, c, e);
8138 // overridden Element method
8139 setSize : function(w, h, a, d, c, e){
8140 this.beforeAction();
8141 var cb = this.createCB(c);
8142 supr.setSize.call(this, w, h, a, d, cb, e);
8148 // overridden Element method
8149 setWidth : function(w, a, d, c, e){
8150 this.beforeAction();
8151 var cb = this.createCB(c);
8152 supr.setWidth.call(this, w, a, d, cb, e);
8158 // overridden Element method
8159 setHeight : function(h, a, d, c, e){
8160 this.beforeAction();
8161 var cb = this.createCB(c);
8162 supr.setHeight.call(this, h, a, d, cb, e);
8168 // overridden Element method
8169 setBounds : function(x, y, w, h, a, d, c, e){
8170 this.beforeAction();
8171 var cb = this.createCB(c);
8173 this.storeXY([x, y]);
8174 supr.setXY.call(this, [x, y]);
8175 supr.setSize.call(this, w, h, a, d, cb, e);
8178 supr.setBounds.call(this, x, y, w, h, a, d, cb, e);
8184 * Sets the z-index of this layer and adjusts any shadow and shim z-indexes. The layer z-index is automatically
8185 * incremented by two more than the value passed in so that it always shows above any shadow or shim (the shadow
8186 * element, if any, will be assigned z-index + 1, and the shim element, if any, will be assigned the unmodified z-index).
8187 * @param {Number} zindex The new z-index to set
8188 * @return {this} The Layer
8190 setZIndex : function(zindex){
8191 this.zindex = zindex;
8192 this.setStyle("z-index", zindex + 2);
8194 this.shadow.setZIndex(zindex + 1);
8197 this.shim.setStyle("z-index", zindex);
8203 * Ext JS Library 1.1.1
8204 * Copyright(c) 2006-2007, Ext JS, LLC.
8206 * Originally Released Under LGPL - original licence link has changed is not relivant.
8209 * <script type="text/javascript">
8215 * Simple class that can provide a shadow effect for any element. Note that the element MUST be absolutely positioned,
8216 * and the shadow does not provide any shimming. This should be used only in simple cases -- for more advanced
8217 * functionality that can also provide the same shadow effect, see the {@link Roo.Layer} class.
8219 * Create a new Shadow
8220 * @param {Object} config The config object
8222 Roo.Shadow = function(config){
8223 Roo.apply(this, config);
8224 if(typeof this.mode != "string"){
8225 this.mode = this.defaultMode;
8227 var o = this.offset, a = {h: 0};
8228 var rad = Math.floor(this.offset/2);
8229 switch(this.mode.toLowerCase()){ // all this hideous nonsense calculates the various offsets for shadows
8235 a.l -= this.offset + rad;
8236 a.t -= this.offset + rad;
8247 a.l -= (this.offset - rad);
8248 a.t -= this.offset + rad;
8250 a.w -= (this.offset - rad)*2;
8261 a.l -= (this.offset - rad);
8262 a.t -= (this.offset - rad);
8264 a.w -= (this.offset + rad + 1);
8265 a.h -= (this.offset + rad);
8274 Roo.Shadow.prototype = {
8276 * @cfg {String} mode
8277 * The shadow display mode. Supports the following options:<br />
8278 * sides: Shadow displays on both sides and bottom only<br />
8279 * frame: Shadow displays equally on all four sides<br />
8280 * drop: Traditional bottom-right drop shadow (default)
8283 * @cfg {String} offset
8284 * The number of pixels to offset the shadow from the element (defaults to 4)
8289 defaultMode: "drop",
8292 * Displays the shadow under the target element
8293 * @param {String/HTMLElement/Element} targetEl The id or element under which the shadow should display
8295 show : function(target){
8296 target = Roo.get(target);
8298 this.el = Roo.Shadow.Pool.pull();
8299 if(this.el.dom.nextSibling != target.dom){
8300 this.el.insertBefore(target);
8303 this.el.setStyle("z-index", this.zIndex || parseInt(target.getStyle("z-index"), 10)-1);
8305 this.el.dom.style.filter="progid:DXImageTransform.Microsoft.alpha(opacity=50) progid:DXImageTransform.Microsoft.Blur(pixelradius="+(this.offset)+")";
8308 target.getLeft(true),
8309 target.getTop(true),
8313 this.el.dom.style.display = "block";
8317 * Returns true if the shadow is visible, else false
8319 isVisible : function(){
8320 return this.el ? true : false;
8324 * Direct alignment when values are already available. Show must be called at least once before
8325 * calling this method to ensure it is initialized.
8326 * @param {Number} left The target element left position
8327 * @param {Number} top The target element top position
8328 * @param {Number} width The target element width
8329 * @param {Number} height The target element height
8331 realign : function(l, t, w, h){
8335 var a = this.adjusts, d = this.el.dom, s = d.style;
8337 s.left = (l+a.l)+"px";
8338 s.top = (t+a.t)+"px";
8339 var sw = (w+a.w), sh = (h+a.h), sws = sw +"px", shs = sh + "px";
8341 if(s.width != sws || s.height != shs){
8345 var cn = d.childNodes;
8346 var sww = Math.max(0, (sw-12))+"px";
8347 cn[0].childNodes[1].style.width = sww;
8348 cn[1].childNodes[1].style.width = sww;
8349 cn[2].childNodes[1].style.width = sww;
8350 cn[1].style.height = Math.max(0, (sh-12))+"px";
8360 this.el.dom.style.display = "none";
8361 Roo.Shadow.Pool.push(this.el);
8367 * Adjust the z-index of this shadow
8368 * @param {Number} zindex The new z-index
8370 setZIndex : function(z){
8373 this.el.setStyle("z-index", z);
8378 // Private utility class that manages the internal Shadow cache
8379 Roo.Shadow.Pool = function(){
8381 var markup = Roo.isIE ?
8382 '<div class="x-ie-shadow"></div>' :
8383 '<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>';
8388 sh = Roo.get(Roo.DomHelper.insertHtml("beforeBegin", document.body.firstChild, markup));
8389 sh.autoBoxAdjust = false;
8394 push : function(sh){
8400 * Ext JS Library 1.1.1
8401 * Copyright(c) 2006-2007, Ext JS, LLC.
8403 * Originally Released Under LGPL - original licence link has changed is not relivant.
8406 * <script type="text/javascript">
8410 * @class Roo.BoxComponent
8411 * @extends Roo.Component
8412 * Base class for any visual {@link Roo.Component} that uses a box container. BoxComponent provides automatic box
8413 * model adjustments for sizing and positioning and will work correctly withnin the Component rendering model. All
8414 * container classes should subclass BoxComponent so that they will work consistently when nested within other Ext
8415 * layout containers.
8417 * @param {Roo.Element/String/Object} config The configuration options.
8419 Roo.BoxComponent = function(config){
8420 Roo.Component.call(this, config);
8424 * Fires after the component is resized.
8425 * @param {Roo.Component} this
8426 * @param {Number} adjWidth The box-adjusted width that was set
8427 * @param {Number} adjHeight The box-adjusted height that was set
8428 * @param {Number} rawWidth The width that was originally specified
8429 * @param {Number} rawHeight The height that was originally specified
8434 * Fires after the component is moved.
8435 * @param {Roo.Component} this
8436 * @param {Number} x The new x position
8437 * @param {Number} y The new y position
8443 Roo.extend(Roo.BoxComponent, Roo.Component, {
8444 // private, set in afterRender to signify that the component has been rendered
8446 // private, used to defer height settings to subclasses
8448 /** @cfg {Number} width
8449 * width (optional) size of component
8451 /** @cfg {Number} height
8452 * height (optional) size of component
8456 * Sets the width and height of the component. This method fires the resize event. This method can accept
8457 * either width and height as separate numeric arguments, or you can pass a size object like {width:10, height:20}.
8458 * @param {Number/Object} width The new width to set, or a size object in the format {width, height}
8459 * @param {Number} height The new height to set (not required if a size object is passed as the first arg)
8460 * @return {Roo.BoxComponent} this
8462 setSize : function(w, h){
8463 // support for standard size objects
8464 if(typeof w == 'object'){
8475 // prevent recalcs when not needed
8476 if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
8479 this.lastSize = {width: w, height: h};
8481 var adj = this.adjustSize(w, h);
8482 var aw = adj.width, ah = adj.height;
8483 if(aw !== undefined || ah !== undefined){ // this code is nasty but performs better with floaters
8484 var rz = this.getResizeEl();
8485 if(!this.deferHeight && aw !== undefined && ah !== undefined){
8487 }else if(!this.deferHeight && ah !== undefined){
8489 }else if(aw !== undefined){
8492 this.onResize(aw, ah, w, h);
8493 this.fireEvent('resize', this, aw, ah, w, h);
8499 * Gets the current size of the component's underlying element.
8500 * @return {Object} An object containing the element's size {width: (element width), height: (element height)}
8502 getSize : function(){
8503 return this.el.getSize();
8507 * Gets the current XY position of the component's underlying element.
8508 * @param {Boolean} local (optional) If true the element's left and top are returned instead of page XY (defaults to false)
8509 * @return {Array} The XY position of the element (e.g., [100, 200])
8511 getPosition : function(local){
8513 return [this.el.getLeft(true), this.el.getTop(true)];
8515 return this.xy || this.el.getXY();
8519 * Gets the current box measurements of the component's underlying element.
8520 * @param {Boolean} local (optional) If true the element's left and top are returned instead of page XY (defaults to false)
8521 * @returns {Object} box An object in the format {x, y, width, height}
8523 getBox : function(local){
8524 var s = this.el.getSize();
8526 s.x = this.el.getLeft(true);
8527 s.y = this.el.getTop(true);
8529 var xy = this.xy || this.el.getXY();
8537 * Sets the current box measurements of the component's underlying element.
8538 * @param {Object} box An object in the format {x, y, width, height}
8539 * @returns {Roo.BoxComponent} this
8541 updateBox : function(box){
8542 this.setSize(box.width, box.height);
8543 this.setPagePosition(box.x, box.y);
8548 getResizeEl : function(){
8549 return this.resizeEl || this.el;
8553 getPositionEl : function(){
8554 return this.positionEl || this.el;
8558 * Sets the left and top of the component. To set the page XY position instead, use {@link #setPagePosition}.
8559 * This method fires the move event.
8560 * @param {Number} left The new left
8561 * @param {Number} top The new top
8562 * @returns {Roo.BoxComponent} this
8564 setPosition : function(x, y){
8570 var adj = this.adjustPosition(x, y);
8571 var ax = adj.x, ay = adj.y;
8573 var el = this.getPositionEl();
8574 if(ax !== undefined || ay !== undefined){
8575 if(ax !== undefined && ay !== undefined){
8576 el.setLeftTop(ax, ay);
8577 }else if(ax !== undefined){
8579 }else if(ay !== undefined){
8582 this.onPosition(ax, ay);
8583 this.fireEvent('move', this, ax, ay);
8589 * Sets the page XY position of the component. To set the left and top instead, use {@link #setPosition}.
8590 * This method fires the move event.
8591 * @param {Number} x The new x position
8592 * @param {Number} y The new y position
8593 * @returns {Roo.BoxComponent} this
8595 setPagePosition : function(x, y){
8601 if(x === undefined || y === undefined){ // cannot translate undefined points
8604 var p = this.el.translatePoints(x, y);
8605 this.setPosition(p.left, p.top);
8610 onRender : function(ct, position){
8611 Roo.BoxComponent.superclass.onRender.call(this, ct, position);
8613 this.resizeEl = Roo.get(this.resizeEl);
8615 if(this.positionEl){
8616 this.positionEl = Roo.get(this.positionEl);
8621 afterRender : function(){
8622 Roo.BoxComponent.superclass.afterRender.call(this);
8623 this.boxReady = true;
8624 this.setSize(this.width, this.height);
8625 if(this.x || this.y){
8626 this.setPosition(this.x, this.y);
8628 if(this.pageX || this.pageY){
8629 this.setPagePosition(this.pageX, this.pageY);
8634 * Force the component's size to recalculate based on the underlying element's current height and width.
8635 * @returns {Roo.BoxComponent} this
8637 syncSize : function(){
8638 delete this.lastSize;
8639 this.setSize(this.el.getWidth(), this.el.getHeight());
8644 * Called after the component is resized, this method is empty by default but can be implemented by any
8645 * subclass that needs to perform custom logic after a resize occurs.
8646 * @param {Number} adjWidth The box-adjusted width that was set
8647 * @param {Number} adjHeight The box-adjusted height that was set
8648 * @param {Number} rawWidth The width that was originally specified
8649 * @param {Number} rawHeight The height that was originally specified
8651 onResize : function(adjWidth, adjHeight, rawWidth, rawHeight){
8656 * Called after the component is moved, this method is empty by default but can be implemented by any
8657 * subclass that needs to perform custom logic after a move occurs.
8658 * @param {Number} x The new x position
8659 * @param {Number} y The new y position
8661 onPosition : function(x, y){
8666 adjustSize : function(w, h){
8670 if(this.autoHeight){
8673 return {width : w, height: h};
8677 adjustPosition : function(x, y){
8678 return {x : x, y: y};
8682 * Ext JS Library 1.1.1
8683 * Copyright(c) 2006-2007, Ext JS, LLC.
8685 * Originally Released Under LGPL - original licence link has changed is not relivant.
8688 * <script type="text/javascript">
8693 * @class Roo.SplitBar
8694 * @extends Roo.util.Observable
8695 * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
8699 var split = new Roo.SplitBar("elementToDrag", "elementToSize",
8700 Roo.SplitBar.HORIZONTAL, Roo.SplitBar.LEFT);
8701 split.setAdapter(new Roo.SplitBar.AbsoluteLayoutAdapter("container"));
8702 split.minSize = 100;
8703 split.maxSize = 600;
8704 split.animate = true;
8705 split.on('moved', splitterMoved);
8708 * Create a new SplitBar
8709 * @param {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar.
8710 * @param {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged
8711 * @param {Number} orientation (optional) Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
8712 * @param {Number} placement (optional) Either Roo.SplitBar.LEFT or Roo.SplitBar.RIGHT for horizontal or
8713 Roo.SplitBar.TOP or Roo.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
8714 position of the SplitBar).
8716 Roo.SplitBar = function(dragElement, resizingElement, orientation, placement, existingProxy){
8719 this.el = Roo.get(dragElement, true);
8720 this.el.dom.unselectable = "on";
8722 this.resizingEl = Roo.get(resizingElement, true);
8726 * The orientation of the split. Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
8727 * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
8730 this.orientation = orientation || Roo.SplitBar.HORIZONTAL;
8733 * The minimum size of the resizing element. (Defaults to 0)
8739 * The maximum size of the resizing element. (Defaults to 2000)
8742 this.maxSize = 2000;
8745 * Whether to animate the transition to the new size
8748 this.animate = false;
8751 * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
8754 this.useShim = false;
8761 this.proxy = Roo.SplitBar.createProxy(this.orientation);
8763 this.proxy = Roo.get(existingProxy).dom;
8766 this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
8769 this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
8772 this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
8775 this.dragSpecs = {};
8778 * @private The adapter to use to positon and resize elements
8780 this.adapter = new Roo.SplitBar.BasicLayoutAdapter();
8781 this.adapter.init(this);
8783 if(this.orientation == Roo.SplitBar.HORIZONTAL){
8785 this.placement = placement || (this.el.getX() > this.resizingEl.getX() ? Roo.SplitBar.LEFT : Roo.SplitBar.RIGHT);
8786 this.el.addClass("x-splitbar-h");
8789 this.placement = placement || (this.el.getY() > this.resizingEl.getY() ? Roo.SplitBar.TOP : Roo.SplitBar.BOTTOM);
8790 this.el.addClass("x-splitbar-v");
8796 * Fires when the splitter is moved (alias for {@link #event-moved})
8797 * @param {Roo.SplitBar} this
8798 * @param {Number} newSize the new width or height
8803 * Fires when the splitter is moved
8804 * @param {Roo.SplitBar} this
8805 * @param {Number} newSize the new width or height
8809 * @event beforeresize
8810 * Fires before the splitter is dragged
8811 * @param {Roo.SplitBar} this
8813 "beforeresize" : true,
8815 "beforeapply" : true
8818 Roo.util.Observable.call(this);
8821 Roo.extend(Roo.SplitBar, Roo.util.Observable, {
8822 onStartProxyDrag : function(x, y){
8823 this.fireEvent("beforeresize", this);
8825 var o = Roo.DomHelper.insertFirst(document.body, {cls: "x-drag-overlay", html: " "}, true);
8827 o.enableDisplayMode("block");
8828 // all splitbars share the same overlay
8829 Roo.SplitBar.prototype.overlay = o;
8831 this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
8832 this.overlay.show();
8833 Roo.get(this.proxy).setDisplayed("block");
8834 var size = this.adapter.getElementSize(this);
8835 this.activeMinSize = this.getMinimumSize();;
8836 this.activeMaxSize = this.getMaximumSize();;
8837 var c1 = size - this.activeMinSize;
8838 var c2 = Math.max(this.activeMaxSize - size, 0);
8839 if(this.orientation == Roo.SplitBar.HORIZONTAL){
8840 this.dd.resetConstraints();
8841 this.dd.setXConstraint(
8842 this.placement == Roo.SplitBar.LEFT ? c1 : c2,
8843 this.placement == Roo.SplitBar.LEFT ? c2 : c1
8845 this.dd.setYConstraint(0, 0);
8847 this.dd.resetConstraints();
8848 this.dd.setXConstraint(0, 0);
8849 this.dd.setYConstraint(
8850 this.placement == Roo.SplitBar.TOP ? c1 : c2,
8851 this.placement == Roo.SplitBar.TOP ? c2 : c1
8854 this.dragSpecs.startSize = size;
8855 this.dragSpecs.startPoint = [x, y];
8856 Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
8860 * @private Called after the drag operation by the DDProxy
8862 onEndProxyDrag : function(e){
8863 Roo.get(this.proxy).setDisplayed(false);
8864 var endPoint = Roo.lib.Event.getXY(e);
8866 this.overlay.hide();
8869 if(this.orientation == Roo.SplitBar.HORIZONTAL){
8870 newSize = this.dragSpecs.startSize +
8871 (this.placement == Roo.SplitBar.LEFT ?
8872 endPoint[0] - this.dragSpecs.startPoint[0] :
8873 this.dragSpecs.startPoint[0] - endPoint[0]
8876 newSize = this.dragSpecs.startSize +
8877 (this.placement == Roo.SplitBar.TOP ?
8878 endPoint[1] - this.dragSpecs.startPoint[1] :
8879 this.dragSpecs.startPoint[1] - endPoint[1]
8882 newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
8883 if(newSize != this.dragSpecs.startSize){
8884 if(this.fireEvent('beforeapply', this, newSize) !== false){
8885 this.adapter.setElementSize(this, newSize);
8886 this.fireEvent("moved", this, newSize);
8887 this.fireEvent("resize", this, newSize);
8893 * Get the adapter this SplitBar uses
8894 * @return The adapter object
8896 getAdapter : function(){
8897 return this.adapter;
8901 * Set the adapter this SplitBar uses
8902 * @param {Object} adapter A SplitBar adapter object
8904 setAdapter : function(adapter){
8905 this.adapter = adapter;
8906 this.adapter.init(this);
8910 * Gets the minimum size for the resizing element
8911 * @return {Number} The minimum size
8913 getMinimumSize : function(){
8914 return this.minSize;
8918 * Sets the minimum size for the resizing element
8919 * @param {Number} minSize The minimum size
8921 setMinimumSize : function(minSize){
8922 this.minSize = minSize;
8926 * Gets the maximum size for the resizing element
8927 * @return {Number} The maximum size
8929 getMaximumSize : function(){
8930 return this.maxSize;
8934 * Sets the maximum size for the resizing element
8935 * @param {Number} maxSize The maximum size
8937 setMaximumSize : function(maxSize){
8938 this.maxSize = maxSize;
8942 * Sets the initialize size for the resizing element
8943 * @param {Number} size The initial size
8945 setCurrentSize : function(size){
8946 var oldAnimate = this.animate;
8947 this.animate = false;
8948 this.adapter.setElementSize(this, size);
8949 this.animate = oldAnimate;
8953 * Destroy this splitbar.
8954 * @param {Boolean} removeEl True to remove the element
8956 destroy : function(removeEl){
8961 this.proxy.parentNode.removeChild(this.proxy);
8969 * @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.
8971 Roo.SplitBar.createProxy = function(dir){
8972 var proxy = new Roo.Element(document.createElement("div"));
8973 proxy.unselectable();
8974 var cls = 'x-splitbar-proxy';
8975 proxy.addClass(cls + ' ' + (dir == Roo.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
8976 document.body.appendChild(proxy.dom);
8981 * @class Roo.SplitBar.BasicLayoutAdapter
8982 * Default Adapter. It assumes the splitter and resizing element are not positioned
8983 * elements and only gets/sets the width of the element. Generally used for table based layouts.
8985 Roo.SplitBar.BasicLayoutAdapter = function(){
8988 Roo.SplitBar.BasicLayoutAdapter.prototype = {
8989 // do nothing for now
8994 * Called before drag operations to get the current size of the resizing element.
8995 * @param {Roo.SplitBar} s The SplitBar using this adapter
8997 getElementSize : function(s){
8998 if(s.orientation == Roo.SplitBar.HORIZONTAL){
8999 return s.resizingEl.getWidth();
9001 return s.resizingEl.getHeight();
9006 * Called after drag operations to set the size of the resizing element.
9007 * @param {Roo.SplitBar} s The SplitBar using this adapter
9008 * @param {Number} newSize The new size to set
9009 * @param {Function} onComplete A function to be invoked when resizing is complete
9011 setElementSize : function(s, newSize, onComplete){
9012 if(s.orientation == Roo.SplitBar.HORIZONTAL){
9014 s.resizingEl.setWidth(newSize);
9016 onComplete(s, newSize);
9019 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
9024 s.resizingEl.setHeight(newSize);
9026 onComplete(s, newSize);
9029 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
9036 *@class Roo.SplitBar.AbsoluteLayoutAdapter
9037 * @extends Roo.SplitBar.BasicLayoutAdapter
9038 * Adapter that moves the splitter element to align with the resized sizing element.
9039 * Used with an absolute positioned SplitBar.
9040 * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
9041 * document.body, make sure you assign an id to the body element.
9043 Roo.SplitBar.AbsoluteLayoutAdapter = function(container){
9044 this.basic = new Roo.SplitBar.BasicLayoutAdapter();
9045 this.container = Roo.get(container);
9048 Roo.SplitBar.AbsoluteLayoutAdapter.prototype = {
9053 getElementSize : function(s){
9054 return this.basic.getElementSize(s);
9057 setElementSize : function(s, newSize, onComplete){
9058 this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
9061 moveSplitter : function(s){
9062 var yes = Roo.SplitBar;
9063 switch(s.placement){
9065 s.el.setX(s.resizingEl.getRight());
9068 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
9071 s.el.setY(s.resizingEl.getBottom());
9074 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
9081 * Orientation constant - Create a vertical SplitBar
9085 Roo.SplitBar.VERTICAL = 1;
9088 * Orientation constant - Create a horizontal SplitBar
9092 Roo.SplitBar.HORIZONTAL = 2;
9095 * Placement constant - The resizing element is to the left of the splitter element
9099 Roo.SplitBar.LEFT = 1;
9102 * Placement constant - The resizing element is to the right of the splitter element
9106 Roo.SplitBar.RIGHT = 2;
9109 * Placement constant - The resizing element is positioned above the splitter element
9113 Roo.SplitBar.TOP = 3;
9116 * Placement constant - The resizing element is positioned under splitter element
9120 Roo.SplitBar.BOTTOM = 4;
9123 * Ext JS Library 1.1.1
9124 * Copyright(c) 2006-2007, Ext JS, LLC.
9126 * Originally Released Under LGPL - original licence link has changed is not relivant.
9129 * <script type="text/javascript">
9134 * @extends Roo.util.Observable
9135 * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template.
9136 * This class also supports single and multi selection modes. <br>
9137 * Create a data model bound view:
9139 var store = new Roo.data.Store(...);
9141 var view = new Roo.View({
9143 tpl : '<div id="{0}">{2} - {1}</div>', // auto create template
9146 selectedClass: "ydataview-selected",
9150 // listen for node click?
9151 view.on("click", function(vw, index, node, e){
9152 alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
9156 dataModel.load("foobar.xml");
9158 For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
9160 * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
9161 * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
9163 * Note: old style constructor is still suported (container, template, config)
9167 * @param {Object} config The config object
9170 Roo.View = function(config, depreciated_tpl, depreciated_config){
9172 if (typeof(depreciated_tpl) == 'undefined') {
9173 // new way.. - universal constructor.
9174 Roo.apply(this, config);
9175 this.el = Roo.get(this.el);
9178 this.el = Roo.get(config);
9179 this.tpl = depreciated_tpl;
9180 Roo.apply(this, depreciated_config);
9182 this.wrapEl = this.el.wrap().wrap();
9183 ///this.el = this.wrapEla.appendChild(document.createElement("div"));
9186 if(typeof(this.tpl) == "string"){
9187 this.tpl = new Roo.Template(this.tpl);
9189 // support xtype ctors..
9190 this.tpl = new Roo.factory(this.tpl, Roo);
9202 * @event beforeclick
9203 * Fires before a click is processed. Returns false to cancel the default action.
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 "beforeclick" : true,
9212 * Fires when a template node is clicked.
9213 * @param {Roo.View} this
9214 * @param {Number} index The index of the target node
9215 * @param {HTMLElement} node The target node
9216 * @param {Roo.EventObject} e The raw event object
9221 * Fires when a template node is double clicked.
9222 * @param {Roo.View} this
9223 * @param {Number} index The index of the target node
9224 * @param {HTMLElement} node The target node
9225 * @param {Roo.EventObject} e The raw event object
9229 * @event contextmenu
9230 * Fires when a template node is right clicked.
9231 * @param {Roo.View} this
9232 * @param {Number} index The index of the target node
9233 * @param {HTMLElement} node The target node
9234 * @param {Roo.EventObject} e The raw event object
9236 "contextmenu" : true,
9238 * @event selectionchange
9239 * Fires when the selected nodes change.
9240 * @param {Roo.View} this
9241 * @param {Array} selections Array of the selected nodes
9243 "selectionchange" : true,
9246 * @event beforeselect
9247 * Fires before a selection is made. If any handlers return false, the selection is cancelled.
9248 * @param {Roo.View} this
9249 * @param {HTMLElement} node The node to be selected
9250 * @param {Array} selections Array of currently selected nodes
9252 "beforeselect" : true,
9254 * @event preparedata
9255 * Fires on every row to render, to allow you to change the data.
9256 * @param {Roo.View} this
9257 * @param {Object} data to be rendered (change this)
9259 "preparedata" : true
9267 "click": this.onClick,
9268 "dblclick": this.onDblClick,
9269 "contextmenu": this.onContextMenu,
9273 this.selections = [];
9275 this.cmp = new Roo.CompositeElementLite([]);
9277 this.store = Roo.factory(this.store, Roo.data);
9278 this.setStore(this.store, true);
9281 if ( this.footer && this.footer.xtype) {
9283 var fctr = this.wrapEl.appendChild(document.createElement("div"));
9285 this.footer.dataSource = this.store
9286 this.footer.container = fctr;
9287 this.footer = Roo.factory(this.footer, Roo);
9288 fctr.insertFirst(this.el);
9290 // this is a bit insane - as the paging toolbar seems to detach the el..
9291 // dom.parentNode.parentNode.parentNode
9292 // they get detached?
9296 Roo.View.superclass.constructor.call(this);
9301 Roo.extend(Roo.View, Roo.util.Observable, {
9304 * @cfg {Roo.data.Store} store Data store to load data from.
9309 * @cfg {String|Roo.Element} el The container element.
9314 * @cfg {String|Roo.Template} tpl The template used by this View
9318 * @cfg {String} dataName the named area of the template to use as the data area
9319 * Works with domtemplates roo-name="name"
9323 * @cfg {String} selectedClass The css class to add to selected nodes
9325 selectedClass : "x-view-selected",
9327 * @cfg {String} emptyText The empty text to show when nothing is loaded.
9332 * @cfg {String} text to display on mask (default Loading)
9336 * @cfg {Boolean} multiSelect Allow multiple selection
9338 multiSelect : false,
9340 * @cfg {Boolean} singleSelect Allow single selection
9342 singleSelect: false,
9345 * @cfg {Boolean} toggleSelect - selecting
9347 toggleSelect : false,
9350 * Returns the element this view is bound to.
9351 * @return {Roo.Element}
9360 * Refreshes the view. - called by datachanged on the store. - do not call directly.
9362 refresh : function(){
9365 // if we are using something like 'domtemplate', then
9366 // the what gets used is:
9367 // t.applySubtemplate(NAME, data, wrapping data..)
9368 // the outer template then get' applied with
9369 // the store 'extra data'
9370 // and the body get's added to the
9371 // roo-name="data" node?
9372 // <span class='roo-tpl-{name}'></span> ?????
9376 this.clearSelections();
9379 var records = this.store.getRange();
9380 if(records.length < 1) {
9382 // is this valid?? = should it render a template??
9384 this.el.update(this.emptyText);
9388 if (this.dataName) {
9389 this.el.update(t.apply(this.store.meta)); //????
9390 el = this.el.child('.roo-tpl-' + this.dataName);
9393 for(var i = 0, len = records.length; i < len; i++){
9394 var data = this.prepareData(records[i].data, i, records[i]);
9395 this.fireEvent("preparedata", this, data, i, records[i]);
9396 html[html.length] = Roo.util.Format.trim(
9398 t.applySubtemplate(this.dataName, data, this.store.meta) :
9405 el.update(html.join(""));
9406 this.nodes = el.dom.childNodes;
9407 this.updateIndexes(0);
9411 * Function to override to reformat the data that is sent to
9412 * the template for each node.
9413 * DEPRICATED - use the preparedata event handler.
9414 * @param {Array/Object} data The raw data (array of colData for a data model bound view or
9415 * a JSON object for an UpdateManager bound view).
9417 prepareData : function(data, index, record)
9419 this.fireEvent("preparedata", this, data, index, record);
9423 onUpdate : function(ds, record){
9424 this.clearSelections();
9425 var index = this.store.indexOf(record);
9426 var n = this.nodes[index];
9427 this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
9428 n.parentNode.removeChild(n);
9429 this.updateIndexes(index, index);
9435 onAdd : function(ds, records, index)
9437 this.clearSelections();
9438 if(this.nodes.length == 0){
9442 var n = this.nodes[index];
9443 for(var i = 0, len = records.length; i < len; i++){
9444 var d = this.prepareData(records[i].data, i, records[i]);
9446 this.tpl.insertBefore(n, d);
9449 this.tpl.append(this.el, d);
9452 this.updateIndexes(index);
9455 onRemove : function(ds, record, index){
9456 this.clearSelections();
9457 var el = this.dataName ?
9458 this.el.child('.roo-tpl-' + this.dataName) :
9460 el.dom.removeChild(this.nodes[index]);
9461 this.updateIndexes(index);
9465 * Refresh an individual node.
9466 * @param {Number} index
9468 refreshNode : function(index){
9469 this.onUpdate(this.store, this.store.getAt(index));
9472 updateIndexes : function(startIndex, endIndex){
9473 var ns = this.nodes;
9474 startIndex = startIndex || 0;
9475 endIndex = endIndex || ns.length - 1;
9476 for(var i = startIndex; i <= endIndex; i++){
9477 ns[i].nodeIndex = i;
9482 * Changes the data store this view uses and refresh the view.
9483 * @param {Store} store
9485 setStore : function(store, initial){
9486 if(!initial && this.store){
9487 this.store.un("datachanged", this.refresh);
9488 this.store.un("add", this.onAdd);
9489 this.store.un("remove", this.onRemove);
9490 this.store.un("update", this.onUpdate);
9491 this.store.un("clear", this.refresh);
9492 this.store.un("beforeload", this.onBeforeLoad);
9493 this.store.un("load", this.onLoad);
9494 this.store.un("loadexception", this.onLoad);
9498 store.on("datachanged", this.refresh, this);
9499 store.on("add", this.onAdd, this);
9500 store.on("remove", this.onRemove, this);
9501 store.on("update", this.onUpdate, this);
9502 store.on("clear", this.refresh, this);
9503 store.on("beforeload", this.onBeforeLoad, this);
9504 store.on("load", this.onLoad, this);
9505 store.on("loadexception", this.onLoad, this);
9513 * onbeforeLoad - masks the loading area.
9516 onBeforeLoad : function()
9519 this.el.mask(this.mask ? this.mask : "Loading" );
9521 onLoad : function ()
9528 * Returns the template node the passed child belongs to or null if it doesn't belong to one.
9529 * @param {HTMLElement} node
9530 * @return {HTMLElement} The template node
9532 findItemFromChild : function(node){
9533 var el = this.dataName ?
9534 this.el.child('.roo-tpl-' + this.dataName,true) :
9537 if(!node || node.parentNode == el){
9540 var p = node.parentNode;
9541 while(p && p != el){
9542 if(p.parentNode == el){
9551 onClick : function(e){
9552 var item = this.findItemFromChild(e.getTarget());
9554 var index = this.indexOf(item);
9555 if(this.onItemClick(item, index, e) !== false){
9556 this.fireEvent("click", this, index, item, e);
9559 this.clearSelections();
9564 onContextMenu : function(e){
9565 var item = this.findItemFromChild(e.getTarget());
9567 this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
9572 onDblClick : function(e){
9573 var item = this.findItemFromChild(e.getTarget());
9575 this.fireEvent("dblclick", this, this.indexOf(item), item, e);
9579 onItemClick : function(item, index, e)
9581 if(this.fireEvent("beforeclick", this, index, item, e) === false){
9584 if (this.toggleSelect) {
9585 var m = this.isSelected(item) ? 'unselect' : 'select';
9588 _t[m](item, true, false);
9591 if(this.multiSelect || this.singleSelect){
9592 if(this.multiSelect && e.shiftKey && this.lastSelection){
9593 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
9595 this.select(item, this.multiSelect && e.ctrlKey);
9596 this.lastSelection = item;
9604 * Get the number of selected nodes.
9607 getSelectionCount : function(){
9608 return this.selections.length;
9612 * Get the currently selected nodes.
9613 * @return {Array} An array of HTMLElements
9615 getSelectedNodes : function(){
9616 return this.selections;
9620 * Get the indexes of the selected nodes.
9623 getSelectedIndexes : function(){
9624 var indexes = [], s = this.selections;
9625 for(var i = 0, len = s.length; i < len; i++){
9626 indexes.push(s[i].nodeIndex);
9632 * Clear all selections
9633 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
9635 clearSelections : function(suppressEvent){
9636 if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
9637 this.cmp.elements = this.selections;
9638 this.cmp.removeClass(this.selectedClass);
9639 this.selections = [];
9641 this.fireEvent("selectionchange", this, this.selections);
9647 * Returns true if the passed node is selected
9648 * @param {HTMLElement/Number} node The node or node index
9651 isSelected : function(node){
9652 var s = this.selections;
9656 node = this.getNode(node);
9657 return s.indexOf(node) !== -1;
9662 * @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
9663 * @param {Boolean} keepExisting (optional) true to keep existing selections
9664 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
9666 select : function(nodeInfo, keepExisting, suppressEvent){
9667 if(nodeInfo instanceof Array){
9669 this.clearSelections(true);
9671 for(var i = 0, len = nodeInfo.length; i < len; i++){
9672 this.select(nodeInfo[i], true, true);
9676 var node = this.getNode(nodeInfo);
9677 if(!node || this.isSelected(node)){
9678 return; // already selected.
9681 this.clearSelections(true);
9683 if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
9684 Roo.fly(node).addClass(this.selectedClass);
9685 this.selections.push(node);
9687 this.fireEvent("selectionchange", this, this.selections);
9695 * @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
9696 * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
9697 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
9699 unselect : function(nodeInfo, keepExisting, suppressEvent)
9701 if(nodeInfo instanceof Array){
9702 Roo.each(this.selections, function(s) {
9703 this.unselect(s, nodeInfo);
9707 var node = this.getNode(nodeInfo);
9708 if(!node || !this.isSelected(node)){
9709 Roo.log("not selected");
9710 return; // not selected.
9714 Roo.each(this.selections, function(s) {
9716 Roo.fly(node).removeClass(this.selectedClass);
9723 this.selections= ns;
9724 this.fireEvent("selectionchange", this, this.selections);
9728 * Gets a template node.
9729 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
9730 * @return {HTMLElement} The node or null if it wasn't found
9732 getNode : function(nodeInfo){
9733 if(typeof nodeInfo == "string"){
9734 return document.getElementById(nodeInfo);
9735 }else if(typeof nodeInfo == "number"){
9736 return this.nodes[nodeInfo];
9742 * Gets a range template nodes.
9743 * @param {Number} startIndex
9744 * @param {Number} endIndex
9745 * @return {Array} An array of nodes
9747 getNodes : function(start, end){
9748 var ns = this.nodes;
9750 end = typeof end == "undefined" ? ns.length - 1 : end;
9753 for(var i = start; i <= end; i++){
9757 for(var i = start; i >= end; i--){
9765 * Finds the index of the passed node
9766 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
9767 * @return {Number} The index of the node or -1
9769 indexOf : function(node){
9770 node = this.getNode(node);
9771 if(typeof node.nodeIndex == "number"){
9772 return node.nodeIndex;
9774 var ns = this.nodes;
9775 for(var i = 0, len = ns.length; i < len; i++){
9785 * Ext JS Library 1.1.1
9786 * Copyright(c) 2006-2007, Ext JS, LLC.
9788 * Originally Released Under LGPL - original licence link has changed is not relivant.
9791 * <script type="text/javascript">
9795 * @class Roo.JsonView
9797 * Shortcut class to create a JSON + {@link Roo.UpdateManager} template view. Usage:
9799 var view = new Roo.JsonView({
9800 container: "my-element",
9801 tpl: '<div id="{id}">{foo} - {bar}</div>', // auto create template
9806 // listen for node click?
9807 view.on("click", function(vw, index, node, e){
9808 alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
9811 // direct load of JSON data
9812 view.load("foobar.php");
9814 // Example from my blog list
9815 var tpl = new Roo.Template(
9816 '<div class="entry">' +
9817 '<a class="entry-title" href="{link}">{title}</a>' +
9818 "<h4>{date} by {author} | {comments} Comments</h4>{description}" +
9819 "</div><hr />"
9822 var moreView = new Roo.JsonView({
9823 container : "entry-list",
9827 moreView.on("beforerender", this.sortEntries, this);
9829 url: "/blog/get-posts.php",
9830 params: "allposts=true",
9831 text: "Loading Blog Entries..."
9835 * Note: old code is supported with arguments : (container, template, config)
9839 * Create a new JsonView
9841 * @param {Object} config The config object
9844 Roo.JsonView = function(config, depreciated_tpl, depreciated_config){
9847 Roo.JsonView.superclass.constructor.call(this, config, depreciated_tpl, depreciated_config);
9849 var um = this.el.getUpdateManager();
9850 um.setRenderer(this);
9851 um.on("update", this.onLoad, this);
9852 um.on("failure", this.onLoadException, this);
9855 * @event beforerender
9856 * Fires before rendering of the downloaded JSON data.
9857 * @param {Roo.JsonView} this
9858 * @param {Object} data The JSON data loaded
9862 * Fires when data is loaded.
9863 * @param {Roo.JsonView} this
9864 * @param {Object} data The JSON data loaded
9865 * @param {Object} response The raw Connect response object
9868 * @event loadexception
9869 * Fires when loading fails.
9870 * @param {Roo.JsonView} this
9871 * @param {Object} response The raw Connect response object
9874 'beforerender' : true,
9876 'loadexception' : true
9879 Roo.extend(Roo.JsonView, Roo.View, {
9881 * @type {String} The root property in the loaded JSON object that contains the data
9886 * Refreshes the view.
9888 refresh : function(){
9889 this.clearSelections();
9892 var o = this.jsonData;
9893 if(o && o.length > 0){
9894 for(var i = 0, len = o.length; i < len; i++){
9895 var data = this.prepareData(o[i], i, o);
9896 html[html.length] = this.tpl.apply(data);
9899 html.push(this.emptyText);
9901 this.el.update(html.join(""));
9902 this.nodes = this.el.dom.childNodes;
9903 this.updateIndexes(0);
9907 * 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.
9908 * @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:
9911 url: "your-url.php",
9912 params: {param1: "foo", param2: "bar"}, // or a URL encoded string
9913 callback: yourFunction,
9914 scope: yourObject, //(optional scope)
9922 * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
9923 * 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.
9924 * @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}
9925 * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
9926 * @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.
9929 var um = this.el.getUpdateManager();
9930 um.update.apply(um, arguments);
9933 render : function(el, response){
9934 this.clearSelections();
9938 o = Roo.util.JSON.decode(response.responseText);
9941 o = o[this.jsonRoot];
9946 * The current JSON data or null
9949 this.beforeRender();
9954 * Get the number of records in the current JSON dataset
9957 getCount : function(){
9958 return this.jsonData ? this.jsonData.length : 0;
9962 * Returns the JSON object for the specified node(s)
9963 * @param {HTMLElement/Array} node The node or an array of nodes
9964 * @return {Object/Array} If you pass in an array, you get an array back, otherwise
9965 * you get the JSON object for the node
9967 getNodeData : function(node){
9968 if(node instanceof Array){
9970 for(var i = 0, len = node.length; i < len; i++){
9971 data.push(this.getNodeData(node[i]));
9975 return this.jsonData[this.indexOf(node)] || null;
9978 beforeRender : function(){
9979 this.snapshot = this.jsonData;
9981 this.sort.apply(this, this.sortInfo);
9983 this.fireEvent("beforerender", this, this.jsonData);
9986 onLoad : function(el, o){
9987 this.fireEvent("load", this, this.jsonData, o);
9990 onLoadException : function(el, o){
9991 this.fireEvent("loadexception", this, o);
9995 * Filter the data by a specific property.
9996 * @param {String} property A property on your JSON objects
9997 * @param {String/RegExp} value Either string that the property values
9998 * should start with, or a RegExp to test against the property
10000 filter : function(property, value){
10003 var ss = this.snapshot;
10004 if(typeof value == "string"){
10005 var vlen = value.length;
10007 this.clearFilter();
10010 value = value.toLowerCase();
10011 for(var i = 0, len = ss.length; i < len; i++){
10013 if(o[property].substr(0, vlen).toLowerCase() == value){
10017 } else if(value.exec){ // regex?
10018 for(var i = 0, len = ss.length; i < len; i++){
10020 if(value.test(o[property])){
10027 this.jsonData = data;
10033 * Filter by a function. The passed function will be called with each
10034 * object in the current dataset. If the function returns true the value is kept,
10035 * otherwise it is filtered.
10036 * @param {Function} fn
10037 * @param {Object} scope (optional) The scope of the function (defaults to this JsonView)
10039 filterBy : function(fn, scope){
10042 var ss = this.snapshot;
10043 for(var i = 0, len = ss.length; i < len; i++){
10045 if(fn.call(scope || this, o)){
10049 this.jsonData = data;
10055 * Clears the current filter.
10057 clearFilter : function(){
10058 if(this.snapshot && this.jsonData != this.snapshot){
10059 this.jsonData = this.snapshot;
10066 * Sorts the data for this view and refreshes it.
10067 * @param {String} property A property on your JSON objects to sort on
10068 * @param {String} direction (optional) "desc" or "asc" (defaults to "asc")
10069 * @param {Function} sortType (optional) A function to call to convert the data to a sortable value.
10071 sort : function(property, dir, sortType){
10072 this.sortInfo = Array.prototype.slice.call(arguments, 0);
10075 var dsc = dir && dir.toLowerCase() == "desc";
10076 var f = function(o1, o2){
10077 var v1 = sortType ? sortType(o1[p]) : o1[p];
10078 var v2 = sortType ? sortType(o2[p]) : o2[p];
10081 return dsc ? +1 : -1;
10082 } else if(v1 > v2){
10083 return dsc ? -1 : +1;
10088 this.jsonData.sort(f);
10090 if(this.jsonData != this.snapshot){
10091 this.snapshot.sort(f);
10097 * Ext JS Library 1.1.1
10098 * Copyright(c) 2006-2007, Ext JS, LLC.
10100 * Originally Released Under LGPL - original licence link has changed is not relivant.
10103 * <script type="text/javascript">
10108 * @class Roo.ColorPalette
10109 * @extends Roo.Component
10110 * Simple color palette class for choosing colors. The palette can be rendered to any container.<br />
10111 * Here's an example of typical usage:
10113 var cp = new Roo.ColorPalette({value:'993300'}); // initial selected color
10114 cp.render('my-div');
10116 cp.on('select', function(palette, selColor){
10117 // do something with selColor
10121 * Create a new ColorPalette
10122 * @param {Object} config The config object
10124 Roo.ColorPalette = function(config){
10125 Roo.ColorPalette.superclass.constructor.call(this, config);
10129 * Fires when a color is selected
10130 * @param {ColorPalette} this
10131 * @param {String} color The 6-digit color hex code (without the # symbol)
10137 this.on("select", this.handler, this.scope, true);
10140 Roo.extend(Roo.ColorPalette, Roo.Component, {
10142 * @cfg {String} itemCls
10143 * The CSS class to apply to the containing element (defaults to "x-color-palette")
10145 itemCls : "x-color-palette",
10147 * @cfg {String} value
10148 * The initial color to highlight (should be a valid 6-digit color hex code without the # symbol). Note that
10149 * the hex codes are case-sensitive.
10152 clickEvent:'click',
10154 ctype: "Roo.ColorPalette",
10157 * @cfg {Boolean} allowReselect If set to true then reselecting a color that is already selected fires the selection event
10159 allowReselect : false,
10162 * <p>An array of 6-digit color hex code strings (without the # symbol). This array can contain any number
10163 * of colors, and each hex code should be unique. The width of the palette is controlled via CSS by adjusting
10164 * the width property of the 'x-color-palette' class (or assigning a custom class), so you can balance the number
10165 * of colors with the width setting until the box is symmetrical.</p>
10166 * <p>You can override individual colors if needed:</p>
10168 var cp = new Roo.ColorPalette();
10169 cp.colors[0] = "FF0000"; // change the first box to red
10172 Or you can provide a custom array of your own for complete control:
10174 var cp = new Roo.ColorPalette();
10175 cp.colors = ["000000", "993300", "333300"];
10180 "000000", "993300", "333300", "003300", "003366", "000080", "333399", "333333",
10181 "800000", "FF6600", "808000", "008000", "008080", "0000FF", "666699", "808080",
10182 "FF0000", "FF9900", "99CC00", "339966", "33CCCC", "3366FF", "800080", "969696",
10183 "FF00FF", "FFCC00", "FFFF00", "00FF00", "00FFFF", "00CCFF", "993366", "C0C0C0",
10184 "FF99CC", "FFCC99", "FFFF99", "CCFFCC", "CCFFFF", "99CCFF", "CC99FF", "FFFFFF"
10188 onRender : function(container, position){
10189 var t = new Roo.MasterTemplate(
10190 '<tpl><a href="#" class="color-{0}" hidefocus="on"><em><span style="background:#{0}" unselectable="on"> </span></em></a></tpl>'
10192 var c = this.colors;
10193 for(var i = 0, len = c.length; i < len; i++){
10196 var el = document.createElement("div");
10197 el.className = this.itemCls;
10199 container.dom.insertBefore(el, position);
10200 this.el = Roo.get(el);
10201 this.el.on(this.clickEvent, this.handleClick, this, {delegate: "a"});
10202 if(this.clickEvent != 'click'){
10203 this.el.on('click', Roo.emptyFn, this, {delegate: "a", preventDefault:true});
10208 afterRender : function(){
10209 Roo.ColorPalette.superclass.afterRender.call(this);
10211 var s = this.value;
10218 handleClick : function(e, t){
10219 e.preventDefault();
10220 if(!this.disabled){
10221 var c = t.className.match(/(?:^|\s)color-(.{6})(?:\s|$)/)[1];
10222 this.select(c.toUpperCase());
10227 * Selects the specified color in the palette (fires the select event)
10228 * @param {String} color A valid 6-digit color hex code (# will be stripped if included)
10230 select : function(color){
10231 color = color.replace("#", "");
10232 if(color != this.value || this.allowReselect){
10235 el.child("a.color-"+this.value).removeClass("x-color-palette-sel");
10237 el.child("a.color-"+color).addClass("x-color-palette-sel");
10238 this.value = color;
10239 this.fireEvent("select", this, color);
10244 * Ext JS Library 1.1.1
10245 * Copyright(c) 2006-2007, Ext JS, LLC.
10247 * Originally Released Under LGPL - original licence link has changed is not relivant.
10250 * <script type="text/javascript">
10254 * @class Roo.DatePicker
10255 * @extends Roo.Component
10256 * Simple date picker class.
10258 * Create a new DatePicker
10259 * @param {Object} config The config object
10261 Roo.DatePicker = function(config){
10262 Roo.DatePicker.superclass.constructor.call(this, config);
10264 this.value = config && config.value ?
10265 config.value.clearTime() : new Date().clearTime();
10270 * Fires when a date is selected
10271 * @param {DatePicker} this
10272 * @param {Date} date The selected date
10276 * @event monthchange
10277 * Fires when the displayed month changes
10278 * @param {DatePicker} this
10279 * @param {Date} date The selected month
10281 'monthchange': true
10285 this.on("select", this.handler, this.scope || this);
10287 // build the disabledDatesRE
10288 if(!this.disabledDatesRE && this.disabledDates){
10289 var dd = this.disabledDates;
10291 for(var i = 0; i < dd.length; i++){
10293 if(i != dd.length-1) re += "|";
10295 this.disabledDatesRE = new RegExp(re + ")");
10299 Roo.extend(Roo.DatePicker, Roo.Component, {
10301 * @cfg {String} todayText
10302 * The text to display on the button that selects the current date (defaults to "Today")
10304 todayText : "Today",
10306 * @cfg {String} okText
10307 * The text to display on the ok button
10309 okText : " OK ", //   to give the user extra clicking room
10311 * @cfg {String} cancelText
10312 * The text to display on the cancel button
10314 cancelText : "Cancel",
10316 * @cfg {String} todayTip
10317 * The tooltip to display for the button that selects the current date (defaults to "{current date} (Spacebar)")
10319 todayTip : "{0} (Spacebar)",
10321 * @cfg {Date} minDate
10322 * Minimum allowable date (JavaScript date object, defaults to null)
10326 * @cfg {Date} maxDate
10327 * Maximum allowable date (JavaScript date object, defaults to null)
10331 * @cfg {String} minText
10332 * The error text to display if the minDate validation fails (defaults to "This date is before the minimum date")
10334 minText : "This date is before the minimum date",
10336 * @cfg {String} maxText
10337 * The error text to display if the maxDate validation fails (defaults to "This date is after the maximum date")
10339 maxText : "This date is after the maximum date",
10341 * @cfg {String} format
10342 * The default date format string which can be overriden for localization support. The format must be
10343 * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
10347 * @cfg {Array} disabledDays
10348 * An array of days to disable, 0-based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
10350 disabledDays : null,
10352 * @cfg {String} disabledDaysText
10353 * The tooltip to display when the date falls on a disabled day (defaults to "")
10355 disabledDaysText : "",
10357 * @cfg {RegExp} disabledDatesRE
10358 * JavaScript regular expression used to disable a pattern of dates (defaults to null)
10360 disabledDatesRE : null,
10362 * @cfg {String} disabledDatesText
10363 * The tooltip text to display when the date falls on a disabled date (defaults to "")
10365 disabledDatesText : "",
10367 * @cfg {Boolean} constrainToViewport
10368 * True to constrain the date picker to the viewport (defaults to true)
10370 constrainToViewport : true,
10372 * @cfg {Array} monthNames
10373 * An array of textual month names which can be overriden for localization support (defaults to Date.monthNames)
10375 monthNames : Date.monthNames,
10377 * @cfg {Array} dayNames
10378 * An array of textual day names which can be overriden for localization support (defaults to Date.dayNames)
10380 dayNames : Date.dayNames,
10382 * @cfg {String} nextText
10383 * The next month navigation button tooltip (defaults to 'Next Month (Control+Right)')
10385 nextText: 'Next Month (Control+Right)',
10387 * @cfg {String} prevText
10388 * The previous month navigation button tooltip (defaults to 'Previous Month (Control+Left)')
10390 prevText: 'Previous Month (Control+Left)',
10392 * @cfg {String} monthYearText
10393 * The header month selector tooltip (defaults to 'Choose a month (Control+Up/Down to move years)')
10395 monthYearText: 'Choose a month (Control+Up/Down to move years)',
10397 * @cfg {Number} startDay
10398 * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
10402 * @cfg {Bool} showClear
10403 * Show a clear button (usefull for date form elements that can be blank.)
10409 * Sets the value of the date field
10410 * @param {Date} value The date to set
10412 setValue : function(value){
10413 var old = this.value;
10415 if (typeof(value) == 'string') {
10417 value = Date.parseDate(value, this.format);
10420 value = new Date();
10423 this.value = value.clearTime(true);
10425 this.update(this.value);
10430 * Gets the current selected value of the date field
10431 * @return {Date} The selected date
10433 getValue : function(){
10438 focus : function(){
10440 this.update(this.activeDate);
10445 onRender : function(container, position){
10448 '<table cellspacing="0">',
10449 '<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>',
10450 '<tr><td colspan="3"><table class="x-date-inner" cellspacing="0"><thead><tr>'];
10451 var dn = this.dayNames;
10452 for(var i = 0; i < 7; i++){
10453 var d = this.startDay+i;
10457 m.push("<th><span>", dn[d].substr(0,1), "</span></th>");
10459 m[m.length] = "</tr></thead><tbody><tr>";
10460 for(var i = 0; i < 42; i++) {
10461 if(i % 7 == 0 && i != 0){
10462 m[m.length] = "</tr><tr>";
10464 m[m.length] = '<td><a href="#" hidefocus="on" class="x-date-date" tabIndex="1"><em><span></span></em></a></td>';
10466 m[m.length] = '</tr></tbody></table></td></tr><tr>'+
10467 '<td colspan="3" class="x-date-bottom" align="center"></td></tr></table><div class="x-date-mp"></div>';
10469 var el = document.createElement("div");
10470 el.className = "x-date-picker";
10471 el.innerHTML = m.join("");
10473 container.dom.insertBefore(el, position);
10475 this.el = Roo.get(el);
10476 this.eventEl = Roo.get(el.firstChild);
10478 new Roo.util.ClickRepeater(this.el.child("td.x-date-left a"), {
10479 handler: this.showPrevMonth,
10481 preventDefault:true,
10485 new Roo.util.ClickRepeater(this.el.child("td.x-date-right a"), {
10486 handler: this.showNextMonth,
10488 preventDefault:true,
10492 this.eventEl.on("mousewheel", this.handleMouseWheel, this);
10494 this.monthPicker = this.el.down('div.x-date-mp');
10495 this.monthPicker.enableDisplayMode('block');
10497 var kn = new Roo.KeyNav(this.eventEl, {
10498 "left" : function(e){
10500 this.showPrevMonth() :
10501 this.update(this.activeDate.add("d", -1));
10504 "right" : function(e){
10506 this.showNextMonth() :
10507 this.update(this.activeDate.add("d", 1));
10510 "up" : function(e){
10512 this.showNextYear() :
10513 this.update(this.activeDate.add("d", -7));
10516 "down" : function(e){
10518 this.showPrevYear() :
10519 this.update(this.activeDate.add("d", 7));
10522 "pageUp" : function(e){
10523 this.showNextMonth();
10526 "pageDown" : function(e){
10527 this.showPrevMonth();
10530 "enter" : function(e){
10531 e.stopPropagation();
10538 this.eventEl.on("click", this.handleDateClick, this, {delegate: "a.x-date-date"});
10540 this.eventEl.addKeyListener(Roo.EventObject.SPACE, this.selectToday, this);
10542 this.el.unselectable();
10544 this.cells = this.el.select("table.x-date-inner tbody td");
10545 this.textNodes = this.el.query("table.x-date-inner tbody span");
10547 this.mbtn = new Roo.Button(this.el.child("td.x-date-middle", true), {
10549 tooltip: this.monthYearText
10552 this.mbtn.on('click', this.showMonthPicker, this);
10553 this.mbtn.el.child(this.mbtn.menuClassTarget).addClass("x-btn-with-menu");
10556 var today = (new Date()).dateFormat(this.format);
10558 var baseTb = new Roo.Toolbar(this.el.child("td.x-date-bottom", true));
10559 if (this.showClear) {
10560 baseTb.add( new Roo.Toolbar.Fill());
10563 text: String.format(this.todayText, today),
10564 tooltip: String.format(this.todayTip, today),
10565 handler: this.selectToday,
10569 //var todayBtn = new Roo.Button(this.el.child("td.x-date-bottom", true), {
10572 if (this.showClear) {
10574 baseTb.add( new Roo.Toolbar.Fill());
10577 cls: 'x-btn-icon x-btn-clear',
10578 handler: function() {
10580 this.fireEvent("select", this, '');
10590 this.update(this.value);
10593 createMonthPicker : function(){
10594 if(!this.monthPicker.dom.firstChild){
10595 var buf = ['<table border="0" cellspacing="0">'];
10596 for(var i = 0; i < 6; i++){
10598 '<tr><td class="x-date-mp-month"><a href="#">', this.monthNames[i].substr(0, 3), '</a></td>',
10599 '<td class="x-date-mp-month x-date-mp-sep"><a href="#">', this.monthNames[i+6].substr(0, 3), '</a></td>',
10601 '<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>' :
10602 '<td class="x-date-mp-year"><a href="#"></a></td><td class="x-date-mp-year"><a href="#"></a></td></tr>'
10606 '<tr class="x-date-mp-btns"><td colspan="4"><button type="button" class="x-date-mp-ok">',
10608 '</button><button type="button" class="x-date-mp-cancel">',
10610 '</button></td></tr>',
10613 this.monthPicker.update(buf.join(''));
10614 this.monthPicker.on('click', this.onMonthClick, this);
10615 this.monthPicker.on('dblclick', this.onMonthDblClick, this);
10617 this.mpMonths = this.monthPicker.select('td.x-date-mp-month');
10618 this.mpYears = this.monthPicker.select('td.x-date-mp-year');
10620 this.mpMonths.each(function(m, a, i){
10623 m.dom.xmonth = 5 + Math.round(i * .5);
10625 m.dom.xmonth = Math.round((i-1) * .5);
10631 showMonthPicker : function(){
10632 this.createMonthPicker();
10633 var size = this.el.getSize();
10634 this.monthPicker.setSize(size);
10635 this.monthPicker.child('table').setSize(size);
10637 this.mpSelMonth = (this.activeDate || this.value).getMonth();
10638 this.updateMPMonth(this.mpSelMonth);
10639 this.mpSelYear = (this.activeDate || this.value).getFullYear();
10640 this.updateMPYear(this.mpSelYear);
10642 this.monthPicker.slideIn('t', {duration:.2});
10645 updateMPYear : function(y){
10647 var ys = this.mpYears.elements;
10648 for(var i = 1; i <= 10; i++){
10649 var td = ys[i-1], y2;
10651 y2 = y + Math.round(i * .5);
10652 td.firstChild.innerHTML = y2;
10655 y2 = y - (5-Math.round(i * .5));
10656 td.firstChild.innerHTML = y2;
10659 this.mpYears.item(i-1)[y2 == this.mpSelYear ? 'addClass' : 'removeClass']('x-date-mp-sel');
10663 updateMPMonth : function(sm){
10664 this.mpMonths.each(function(m, a, i){
10665 m[m.dom.xmonth == sm ? 'addClass' : 'removeClass']('x-date-mp-sel');
10669 selectMPMonth: function(m){
10673 onMonthClick : function(e, t){
10675 var el = new Roo.Element(t), pn;
10676 if(el.is('button.x-date-mp-cancel')){
10677 this.hideMonthPicker();
10679 else if(el.is('button.x-date-mp-ok')){
10680 this.update(new Date(this.mpSelYear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
10681 this.hideMonthPicker();
10683 else if(pn = el.up('td.x-date-mp-month', 2)){
10684 this.mpMonths.removeClass('x-date-mp-sel');
10685 pn.addClass('x-date-mp-sel');
10686 this.mpSelMonth = pn.dom.xmonth;
10688 else if(pn = el.up('td.x-date-mp-year', 2)){
10689 this.mpYears.removeClass('x-date-mp-sel');
10690 pn.addClass('x-date-mp-sel');
10691 this.mpSelYear = pn.dom.xyear;
10693 else if(el.is('a.x-date-mp-prev')){
10694 this.updateMPYear(this.mpyear-10);
10696 else if(el.is('a.x-date-mp-next')){
10697 this.updateMPYear(this.mpyear+10);
10701 onMonthDblClick : function(e, t){
10703 var el = new Roo.Element(t), pn;
10704 if(pn = el.up('td.x-date-mp-month', 2)){
10705 this.update(new Date(this.mpSelYear, pn.dom.xmonth, (this.activeDate || this.value).getDate()));
10706 this.hideMonthPicker();
10708 else if(pn = el.up('td.x-date-mp-year', 2)){
10709 this.update(new Date(pn.dom.xyear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
10710 this.hideMonthPicker();
10714 hideMonthPicker : function(disableAnim){
10715 if(this.monthPicker){
10716 if(disableAnim === true){
10717 this.monthPicker.hide();
10719 this.monthPicker.slideOut('t', {duration:.2});
10725 showPrevMonth : function(e){
10726 this.update(this.activeDate.add("mo", -1));
10730 showNextMonth : function(e){
10731 this.update(this.activeDate.add("mo", 1));
10735 showPrevYear : function(){
10736 this.update(this.activeDate.add("y", -1));
10740 showNextYear : function(){
10741 this.update(this.activeDate.add("y", 1));
10745 handleMouseWheel : function(e){
10746 var delta = e.getWheelDelta();
10748 this.showPrevMonth();
10750 } else if(delta < 0){
10751 this.showNextMonth();
10757 handleDateClick : function(e, t){
10759 if(t.dateValue && !Roo.fly(t.parentNode).hasClass("x-date-disabled")){
10760 this.setValue(new Date(t.dateValue));
10761 this.fireEvent("select", this, this.value);
10766 selectToday : function(){
10767 this.setValue(new Date().clearTime());
10768 this.fireEvent("select", this, this.value);
10772 update : function(date)
10774 var vd = this.activeDate;
10775 this.activeDate = date;
10777 var t = date.getTime();
10778 if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
10779 this.cells.removeClass("x-date-selected");
10780 this.cells.each(function(c){
10781 if(c.dom.firstChild.dateValue == t){
10782 c.addClass("x-date-selected");
10783 setTimeout(function(){
10784 try{c.dom.firstChild.focus();}catch(e){}
10793 var days = date.getDaysInMonth();
10794 var firstOfMonth = date.getFirstDateOfMonth();
10795 var startingPos = firstOfMonth.getDay()-this.startDay;
10797 if(startingPos <= this.startDay){
10801 var pm = date.add("mo", -1);
10802 var prevStart = pm.getDaysInMonth()-startingPos;
10804 var cells = this.cells.elements;
10805 var textEls = this.textNodes;
10806 days += startingPos;
10808 // convert everything to numbers so it's fast
10809 var day = 86400000;
10810 var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
10811 var today = new Date().clearTime().getTime();
10812 var sel = date.clearTime().getTime();
10813 var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
10814 var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
10815 var ddMatch = this.disabledDatesRE;
10816 var ddText = this.disabledDatesText;
10817 var ddays = this.disabledDays ? this.disabledDays.join("") : false;
10818 var ddaysText = this.disabledDaysText;
10819 var format = this.format;
10821 var setCellClass = function(cal, cell){
10823 var t = d.getTime();
10824 cell.firstChild.dateValue = t;
10826 cell.className += " x-date-today";
10827 cell.title = cal.todayText;
10830 cell.className += " x-date-selected";
10831 setTimeout(function(){
10832 try{cell.firstChild.focus();}catch(e){}
10837 cell.className = " x-date-disabled";
10838 cell.title = cal.minText;
10842 cell.className = " x-date-disabled";
10843 cell.title = cal.maxText;
10847 if(ddays.indexOf(d.getDay()) != -1){
10848 cell.title = ddaysText;
10849 cell.className = " x-date-disabled";
10852 if(ddMatch && format){
10853 var fvalue = d.dateFormat(format);
10854 if(ddMatch.test(fvalue)){
10855 cell.title = ddText.replace("%0", fvalue);
10856 cell.className = " x-date-disabled";
10862 for(; i < startingPos; i++) {
10863 textEls[i].innerHTML = (++prevStart);
10864 d.setDate(d.getDate()+1);
10865 cells[i].className = "x-date-prevday";
10866 setCellClass(this, cells[i]);
10868 for(; i < days; i++){
10869 intDay = i - startingPos + 1;
10870 textEls[i].innerHTML = (intDay);
10871 d.setDate(d.getDate()+1);
10872 cells[i].className = "x-date-active";
10873 setCellClass(this, cells[i]);
10876 for(; i < 42; i++) {
10877 textEls[i].innerHTML = (++extraDays);
10878 d.setDate(d.getDate()+1);
10879 cells[i].className = "x-date-nextday";
10880 setCellClass(this, cells[i]);
10883 this.mbtn.setText(this.monthNames[date.getMonth()] + " " + date.getFullYear());
10884 this.fireEvent('monthchange', this, date);
10886 if(!this.internalRender){
10887 var main = this.el.dom.firstChild;
10888 var w = main.offsetWidth;
10889 this.el.setWidth(w + this.el.getBorderWidth("lr"));
10890 Roo.fly(main).setWidth(w);
10891 this.internalRender = true;
10892 // opera does not respect the auto grow header center column
10893 // then, after it gets a width opera refuses to recalculate
10894 // without a second pass
10895 if(Roo.isOpera && !this.secondPass){
10896 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
10897 this.secondPass = true;
10898 this.update.defer(10, this, [date]);
10906 * Ext JS Library 1.1.1
10907 * Copyright(c) 2006-2007, Ext JS, LLC.
10909 * Originally Released Under LGPL - original licence link has changed is not relivant.
10912 * <script type="text/javascript">
10915 * @class Roo.TabPanel
10916 * @extends Roo.util.Observable
10917 * A lightweight tab container.
10921 // basic tabs 1, built from existing content
10922 var tabs = new Roo.TabPanel("tabs1");
10923 tabs.addTab("script", "View Script");
10924 tabs.addTab("markup", "View Markup");
10925 tabs.activate("script");
10927 // more advanced tabs, built from javascript
10928 var jtabs = new Roo.TabPanel("jtabs");
10929 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
10931 // set up the UpdateManager
10932 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
10933 var updater = tab2.getUpdateManager();
10934 updater.setDefaultUrl("ajax1.htm");
10935 tab2.on('activate', updater.refresh, updater, true);
10937 // Use setUrl for Ajax loading
10938 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
10939 tab3.setUrl("ajax2.htm", null, true);
10942 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
10945 jtabs.activate("jtabs-1");
10948 * Create a new TabPanel.
10949 * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
10950 * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
10952 Roo.TabPanel = function(container, config){
10954 * The container element for this TabPanel.
10955 * @type Roo.Element
10957 this.el = Roo.get(container, true);
10959 if(typeof config == "boolean"){
10960 this.tabPosition = config ? "bottom" : "top";
10962 Roo.apply(this, config);
10965 if(this.tabPosition == "bottom"){
10966 this.bodyEl = Roo.get(this.createBody(this.el.dom));
10967 this.el.addClass("x-tabs-bottom");
10969 this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
10970 this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
10971 this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
10973 Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
10975 if(this.tabPosition != "bottom"){
10976 /** The body element that contains {@link Roo.TabPanelItem} bodies. +
10977 * @type Roo.Element
10979 this.bodyEl = Roo.get(this.createBody(this.el.dom));
10980 this.el.addClass("x-tabs-top");
10984 this.bodyEl.setStyle("position", "relative");
10986 this.active = null;
10987 this.activateDelegate = this.activate.createDelegate(this);
10992 * Fires when the active tab changes
10993 * @param {Roo.TabPanel} this
10994 * @param {Roo.TabPanelItem} activePanel The new active tab
10998 * @event beforetabchange
10999 * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
11000 * @param {Roo.TabPanel} this
11001 * @param {Object} e Set cancel to true on this object to cancel the tab change
11002 * @param {Roo.TabPanelItem} tab The tab being changed to
11004 "beforetabchange" : true
11007 Roo.EventManager.onWindowResize(this.onResize, this);
11008 this.cpad = this.el.getPadding("lr");
11009 this.hiddenCount = 0;
11012 // toolbar on the tabbar support...
11013 if (this.toolbar) {
11014 var tcfg = this.toolbar;
11015 tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');
11016 this.toolbar = new Roo.Toolbar(tcfg);
11017 if (Roo.isSafari) {
11018 var tbl = tcfg.container.child('table', true);
11019 tbl.setAttribute('width', '100%');
11026 Roo.TabPanel.superclass.constructor.call(this);
11029 Roo.extend(Roo.TabPanel, Roo.util.Observable, {
11031 *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
11033 tabPosition : "top",
11035 *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
11037 currentTabWidth : 0,
11039 *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
11043 *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
11047 *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
11049 preferredTabWidth : 175,
11051 *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
11053 resizeTabs : false,
11055 *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
11057 monitorResize : true,
11059 *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar.
11064 * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
11065 * @param {String} id The id of the div to use <b>or create</b>
11066 * @param {String} text The text for the tab
11067 * @param {String} content (optional) Content to put in the TabPanelItem body
11068 * @param {Boolean} closable (optional) True to create a close icon on the tab
11069 * @return {Roo.TabPanelItem} The created TabPanelItem
11071 addTab : function(id, text, content, closable){
11072 var item = new Roo.TabPanelItem(this, id, text, closable);
11073 this.addTabItem(item);
11075 item.setContent(content);
11081 * Returns the {@link Roo.TabPanelItem} with the specified id/index
11082 * @param {String/Number} id The id or index of the TabPanelItem to fetch.
11083 * @return {Roo.TabPanelItem}
11085 getTab : function(id){
11086 return this.items[id];
11090 * Hides the {@link Roo.TabPanelItem} with the specified id/index
11091 * @param {String/Number} id The id or index of the TabPanelItem to hide.
11093 hideTab : function(id){
11094 var t = this.items[id];
11097 this.hiddenCount++;
11098 this.autoSizeTabs();
11103 * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
11104 * @param {String/Number} id The id or index of the TabPanelItem to unhide.
11106 unhideTab : function(id){
11107 var t = this.items[id];
11109 t.setHidden(false);
11110 this.hiddenCount--;
11111 this.autoSizeTabs();
11116 * Adds an existing {@link Roo.TabPanelItem}.
11117 * @param {Roo.TabPanelItem} item The TabPanelItem to add
11119 addTabItem : function(item){
11120 this.items[item.id] = item;
11121 this.items.push(item);
11122 if(this.resizeTabs){
11123 item.setWidth(this.currentTabWidth || this.preferredTabWidth);
11124 this.autoSizeTabs();
11131 * Removes a {@link Roo.TabPanelItem}.
11132 * @param {String/Number} id The id or index of the TabPanelItem to remove.
11134 removeTab : function(id){
11135 var items = this.items;
11136 var tab = items[id];
11137 if(!tab) { return; }
11138 var index = items.indexOf(tab);
11139 if(this.active == tab && items.length > 1){
11140 var newTab = this.getNextAvailable(index);
11145 this.stripEl.dom.removeChild(tab.pnode.dom);
11146 if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
11147 this.bodyEl.dom.removeChild(tab.bodyEl.dom);
11149 items.splice(index, 1);
11150 delete this.items[tab.id];
11151 tab.fireEvent("close", tab);
11152 tab.purgeListeners();
11153 this.autoSizeTabs();
11156 getNextAvailable : function(start){
11157 var items = this.items;
11159 // look for a next tab that will slide over to
11160 // replace the one being removed
11161 while(index < items.length){
11162 var item = items[++index];
11163 if(item && !item.isHidden()){
11167 // if one isn't found select the previous tab (on the left)
11170 var item = items[--index];
11171 if(item && !item.isHidden()){
11179 * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
11180 * @param {String/Number} id The id or index of the TabPanelItem to disable.
11182 disableTab : function(id){
11183 var tab = this.items[id];
11184 if(tab && this.active != tab){
11190 * Enables a {@link Roo.TabPanelItem} that is disabled.
11191 * @param {String/Number} id The id or index of the TabPanelItem to enable.
11193 enableTab : function(id){
11194 var tab = this.items[id];
11199 * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
11200 * @param {String/Number} id The id or index of the TabPanelItem to activate.
11201 * @return {Roo.TabPanelItem} The TabPanelItem.
11203 activate : function(id){
11204 var tab = this.items[id];
11208 if(tab == this.active || tab.disabled){
11212 this.fireEvent("beforetabchange", this, e, tab);
11213 if(e.cancel !== true && !tab.disabled){
11215 this.active.hide();
11217 this.active = this.items[id];
11218 this.active.show();
11219 this.fireEvent("tabchange", this, this.active);
11225 * Gets the active {@link Roo.TabPanelItem}.
11226 * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
11228 getActiveTab : function(){
11229 return this.active;
11233 * Updates the tab body element to fit the height of the container element
11234 * for overflow scrolling
11235 * @param {Number} targetHeight (optional) Override the starting height from the elements height
11237 syncHeight : function(targetHeight){
11238 var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
11239 var bm = this.bodyEl.getMargins();
11240 var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
11241 this.bodyEl.setHeight(newHeight);
11245 onResize : function(){
11246 if(this.monitorResize){
11247 this.autoSizeTabs();
11252 * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
11254 beginUpdate : function(){
11255 this.updating = true;
11259 * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
11261 endUpdate : function(){
11262 this.updating = false;
11263 this.autoSizeTabs();
11267 * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
11269 autoSizeTabs : function(){
11270 var count = this.items.length;
11271 var vcount = count - this.hiddenCount;
11272 if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) return;
11273 var w = Math.max(this.el.getWidth() - this.cpad, 10);
11274 var availWidth = Math.floor(w / vcount);
11275 var b = this.stripBody;
11276 if(b.getWidth() > w){
11277 var tabs = this.items;
11278 this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
11279 if(availWidth < this.minTabWidth){
11280 /*if(!this.sleft){ // incomplete scrolling code
11281 this.createScrollButtons();
11284 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
11287 if(this.currentTabWidth < this.preferredTabWidth){
11288 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
11294 * Returns the number of tabs in this TabPanel.
11297 getCount : function(){
11298 return this.items.length;
11302 * Resizes all the tabs to the passed width
11303 * @param {Number} The new width
11305 setTabWidth : function(width){
11306 this.currentTabWidth = width;
11307 for(var i = 0, len = this.items.length; i < len; i++) {
11308 if(!this.items[i].isHidden())this.items[i].setWidth(width);
11313 * Destroys this TabPanel
11314 * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
11316 destroy : function(removeEl){
11317 Roo.EventManager.removeResizeListener(this.onResize, this);
11318 for(var i = 0, len = this.items.length; i < len; i++){
11319 this.items[i].purgeListeners();
11321 if(removeEl === true){
11322 this.el.update("");
11329 * @class Roo.TabPanelItem
11330 * @extends Roo.util.Observable
11331 * Represents an individual item (tab plus body) in a TabPanel.
11332 * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
11333 * @param {String} id The id of this TabPanelItem
11334 * @param {String} text The text for the tab of this TabPanelItem
11335 * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
11337 Roo.TabPanelItem = function(tabPanel, id, text, closable){
11339 * The {@link Roo.TabPanel} this TabPanelItem belongs to
11340 * @type Roo.TabPanel
11342 this.tabPanel = tabPanel;
11344 * The id for this TabPanelItem
11349 this.disabled = false;
11353 this.loaded = false;
11354 this.closable = closable;
11357 * The body element for this TabPanelItem.
11358 * @type Roo.Element
11360 this.bodyEl = Roo.get(tabPanel.createItemBody(tabPanel.bodyEl.dom, id));
11361 this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
11362 this.bodyEl.setStyle("display", "block");
11363 this.bodyEl.setStyle("zoom", "1");
11366 var els = tabPanel.createStripElements(tabPanel.stripEl.dom, text, closable);
11368 this.el = Roo.get(els.el, true);
11369 this.inner = Roo.get(els.inner, true);
11370 this.textEl = Roo.get(this.el.dom.firstChild.firstChild.firstChild, true);
11371 this.pnode = Roo.get(els.el.parentNode, true);
11372 this.el.on("mousedown", this.onTabMouseDown, this);
11373 this.el.on("click", this.onTabClick, this);
11376 var c = Roo.get(els.close, true);
11377 c.dom.title = this.closeText;
11378 c.addClassOnOver("close-over");
11379 c.on("click", this.closeClick, this);
11385 * Fires when this tab becomes the active tab.
11386 * @param {Roo.TabPanel} tabPanel The parent TabPanel
11387 * @param {Roo.TabPanelItem} this
11391 * @event beforeclose
11392 * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
11393 * @param {Roo.TabPanelItem} this
11394 * @param {Object} e Set cancel to true on this object to cancel the close.
11396 "beforeclose": true,
11399 * Fires when this tab is closed.
11400 * @param {Roo.TabPanelItem} this
11404 * @event deactivate
11405 * Fires when this tab is no longer the active tab.
11406 * @param {Roo.TabPanel} tabPanel The parent TabPanel
11407 * @param {Roo.TabPanelItem} this
11409 "deactivate" : true
11411 this.hidden = false;
11413 Roo.TabPanelItem.superclass.constructor.call(this);
11416 Roo.extend(Roo.TabPanelItem, Roo.util.Observable, {
11417 purgeListeners : function(){
11418 Roo.util.Observable.prototype.purgeListeners.call(this);
11419 this.el.removeAllListeners();
11422 * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
11425 this.pnode.addClass("on");
11428 this.tabPanel.stripWrap.repaint();
11430 this.fireEvent("activate", this.tabPanel, this);
11434 * Returns true if this tab is the active tab.
11435 * @return {Boolean}
11437 isActive : function(){
11438 return this.tabPanel.getActiveTab() == this;
11442 * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
11445 this.pnode.removeClass("on");
11447 this.fireEvent("deactivate", this.tabPanel, this);
11450 hideAction : function(){
11451 this.bodyEl.hide();
11452 this.bodyEl.setStyle("position", "absolute");
11453 this.bodyEl.setLeft("-20000px");
11454 this.bodyEl.setTop("-20000px");
11457 showAction : function(){
11458 this.bodyEl.setStyle("position", "relative");
11459 this.bodyEl.setTop("");
11460 this.bodyEl.setLeft("");
11461 this.bodyEl.show();
11465 * Set the tooltip for the tab.
11466 * @param {String} tooltip The tab's tooltip
11468 setTooltip : function(text){
11469 if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
11470 this.textEl.dom.qtip = text;
11471 this.textEl.dom.removeAttribute('title');
11473 this.textEl.dom.title = text;
11477 onTabClick : function(e){
11478 e.preventDefault();
11479 this.tabPanel.activate(this.id);
11482 onTabMouseDown : function(e){
11483 e.preventDefault();
11484 this.tabPanel.activate(this.id);
11487 getWidth : function(){
11488 return this.inner.getWidth();
11491 setWidth : function(width){
11492 var iwidth = width - this.pnode.getPadding("lr");
11493 this.inner.setWidth(iwidth);
11494 this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
11495 this.pnode.setWidth(width);
11499 * Show or hide the tab
11500 * @param {Boolean} hidden True to hide or false to show.
11502 setHidden : function(hidden){
11503 this.hidden = hidden;
11504 this.pnode.setStyle("display", hidden ? "none" : "");
11508 * Returns true if this tab is "hidden"
11509 * @return {Boolean}
11511 isHidden : function(){
11512 return this.hidden;
11516 * Returns the text for this tab
11519 getText : function(){
11523 autoSize : function(){
11524 //this.el.beginMeasure();
11525 this.textEl.setWidth(1);
11526 this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr"));
11527 //this.el.endMeasure();
11531 * Sets the text for the tab (Note: this also sets the tooltip text)
11532 * @param {String} text The tab's text and tooltip
11534 setText : function(text){
11536 this.textEl.update(text);
11537 this.setTooltip(text);
11538 if(!this.tabPanel.resizeTabs){
11543 * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
11545 activate : function(){
11546 this.tabPanel.activate(this.id);
11550 * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
11552 disable : function(){
11553 if(this.tabPanel.active != this){
11554 this.disabled = true;
11555 this.pnode.addClass("disabled");
11560 * Enables this TabPanelItem if it was previously disabled.
11562 enable : function(){
11563 this.disabled = false;
11564 this.pnode.removeClass("disabled");
11568 * Sets the content for this TabPanelItem.
11569 * @param {String} content The content
11570 * @param {Boolean} loadScripts true to look for and load scripts
11572 setContent : function(content, loadScripts){
11573 this.bodyEl.update(content, loadScripts);
11577 * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
11578 * @return {Roo.UpdateManager} The UpdateManager
11580 getUpdateManager : function(){
11581 return this.bodyEl.getUpdateManager();
11585 * Set a URL to be used to load the content for this TabPanelItem.
11586 * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
11587 * @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)
11588 * @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)
11589 * @return {Roo.UpdateManager} The UpdateManager
11591 setUrl : function(url, params, loadOnce){
11592 if(this.refreshDelegate){
11593 this.un('activate', this.refreshDelegate);
11595 this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
11596 this.on("activate", this.refreshDelegate);
11597 return this.bodyEl.getUpdateManager();
11601 _handleRefresh : function(url, params, loadOnce){
11602 if(!loadOnce || !this.loaded){
11603 var updater = this.bodyEl.getUpdateManager();
11604 updater.update(url, params, this._setLoaded.createDelegate(this));
11609 * Forces a content refresh from the URL specified in the {@link #setUrl} method.
11610 * Will fail silently if the setUrl method has not been called.
11611 * This does not activate the panel, just updates its content.
11613 refresh : function(){
11614 if(this.refreshDelegate){
11615 this.loaded = false;
11616 this.refreshDelegate();
11621 _setLoaded : function(){
11622 this.loaded = true;
11626 closeClick : function(e){
11629 this.fireEvent("beforeclose", this, o);
11630 if(o.cancel !== true){
11631 this.tabPanel.removeTab(this.id);
11635 * The text displayed in the tooltip for the close icon.
11638 closeText : "Close this tab"
11642 Roo.TabPanel.prototype.createStrip = function(container){
11643 var strip = document.createElement("div");
11644 strip.className = "x-tabs-wrap";
11645 container.appendChild(strip);
11649 Roo.TabPanel.prototype.createStripList = function(strip){
11650 // div wrapper for retard IE
11651 // returns the "tr" element.
11652 strip.innerHTML = '<div class="x-tabs-strip-wrap">'+
11653 '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
11654 '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
11655 return strip.firstChild.firstChild.firstChild.firstChild;
11658 Roo.TabPanel.prototype.createBody = function(container){
11659 var body = document.createElement("div");
11660 Roo.id(body, "tab-body");
11661 Roo.fly(body).addClass("x-tabs-body");
11662 container.appendChild(body);
11666 Roo.TabPanel.prototype.createItemBody = function(bodyEl, id){
11667 var body = Roo.getDom(id);
11669 body = document.createElement("div");
11672 Roo.fly(body).addClass("x-tabs-item-body");
11673 bodyEl.insertBefore(body, bodyEl.firstChild);
11677 Roo.TabPanel.prototype.createStripElements = function(stripEl, text, closable){
11678 var td = document.createElement("td");
11679 stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
11680 //stripEl.appendChild(td);
11682 td.className = "x-tabs-closable";
11683 if(!this.closeTpl){
11684 this.closeTpl = new Roo.Template(
11685 '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
11686 '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
11687 '<div unselectable="on" class="close-icon"> </div></em></span></a>'
11690 var el = this.closeTpl.overwrite(td, {"text": text});
11691 var close = el.getElementsByTagName("div")[0];
11692 var inner = el.getElementsByTagName("em")[0];
11693 return {"el": el, "close": close, "inner": inner};
11696 this.tabTpl = new Roo.Template(
11697 '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
11698 '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
11701 var el = this.tabTpl.overwrite(td, {"text": text});
11702 var inner = el.getElementsByTagName("em")[0];
11703 return {"el": el, "inner": inner};
11707 * Ext JS Library 1.1.1
11708 * Copyright(c) 2006-2007, Ext JS, LLC.
11710 * Originally Released Under LGPL - original licence link has changed is not relivant.
11713 * <script type="text/javascript">
11717 * @class Roo.Button
11718 * @extends Roo.util.Observable
11719 * Simple Button class
11720 * @cfg {String} text The button text
11721 * @cfg {String} icon The path to an image to display in the button (the image will be set as the background-image
11722 * CSS property of the button by default, so if you want a mixed icon/text button, set cls:"x-btn-text-icon")
11723 * @cfg {Function} handler A function called when the button is clicked (can be used instead of click event)
11724 * @cfg {Object} scope The scope of the handler
11725 * @cfg {Number} minWidth The minimum width for this button (used to give a set of buttons a common width)
11726 * @cfg {String/Object} tooltip The tooltip for the button - can be a string or QuickTips config object
11727 * @cfg {Boolean} hidden True to start hidden (defaults to false)
11728 * @cfg {Boolean} disabled True to start disabled (defaults to false)
11729 * @cfg {Boolean} pressed True to start pressed (only if enableToggle = true)
11730 * @cfg {String} toggleGroup The group this toggle button is a member of (only 1 per group can be pressed, only
11731 applies if enableToggle = true)
11732 * @cfg {String/HTMLElement/Element} renderTo The element to append the button to
11733 * @cfg {Boolean/Object} repeat True to repeat fire the click event while the mouse is down. This can also be
11734 an {@link Roo.util.ClickRepeater} config object (defaults to false).
11736 * Create a new button
11737 * @param {Object} config The config object
11739 Roo.Button = function(renderTo, config)
11743 renderTo = config.renderTo || false;
11746 Roo.apply(this, config);
11750 * Fires when this button is clicked
11751 * @param {Button} this
11752 * @param {EventObject} e The click event
11757 * Fires when the "pressed" state of this button changes (only if enableToggle = true)
11758 * @param {Button} this
11759 * @param {Boolean} pressed
11764 * Fires when the mouse hovers over the button
11765 * @param {Button} this
11766 * @param {Event} e The event object
11768 'mouseover' : true,
11771 * Fires when the mouse exits the button
11772 * @param {Button} this
11773 * @param {Event} e The event object
11778 * Fires when the button is rendered
11779 * @param {Button} this
11784 this.menu = Roo.menu.MenuMgr.get(this.menu);
11786 // register listeners first!! - so render can be captured..
11787 Roo.util.Observable.call(this);
11789 this.render(renderTo);
11795 Roo.extend(Roo.Button, Roo.util.Observable, {
11801 * Read-only. True if this button is hidden
11806 * Read-only. True if this button is disabled
11811 * Read-only. True if this button is pressed (only if enableToggle = true)
11817 * @cfg {Number} tabIndex
11818 * The DOM tabIndex for this button (defaults to undefined)
11820 tabIndex : undefined,
11823 * @cfg {Boolean} enableToggle
11824 * True to enable pressed/not pressed toggling (defaults to false)
11826 enableToggle: false,
11828 * @cfg {Mixed} menu
11829 * Standard menu attribute consisting of a reference to a menu object, a menu id or a menu config blob (defaults to undefined).
11833 * @cfg {String} menuAlign
11834 * The position to align the menu to (see {@link Roo.Element#alignTo} for more details, defaults to 'tl-bl?').
11836 menuAlign : "tl-bl?",
11839 * @cfg {String} iconCls
11840 * A css class which sets a background image to be used as the icon for this button (defaults to undefined).
11842 iconCls : undefined,
11844 * @cfg {String} type
11845 * The button's type, corresponding to the DOM input element type attribute. Either "submit," "reset" or "button" (default).
11850 menuClassTarget: 'tr',
11853 * @cfg {String} clickEvent
11854 * The type of event to map to the button's event handler (defaults to 'click')
11856 clickEvent : 'click',
11859 * @cfg {Boolean} handleMouseEvents
11860 * False to disable visual cues on mouseover, mouseout and mousedown (defaults to true)
11862 handleMouseEvents : true,
11865 * @cfg {String} tooltipType
11866 * The type of tooltip to use. Either "qtip" (default) for QuickTips or "title" for title attribute.
11868 tooltipType : 'qtip',
11871 * @cfg {String} cls
11872 * A CSS class to apply to the button's main element.
11876 * @cfg {Roo.Template} template (Optional)
11877 * An {@link Roo.Template} with which to create the Button's main element. This Template must
11878 * contain numeric substitution parameter 0 if it is to display the tRoo property. Changing the template could
11879 * require code modifications if required elements (e.g. a button) aren't present.
11883 render : function(renderTo){
11885 if(this.hideParent){
11886 this.parentEl = Roo.get(renderTo);
11888 if(!this.dhconfig){
11889 if(!this.template){
11890 if(!Roo.Button.buttonTemplate){
11891 // hideous table template
11892 Roo.Button.buttonTemplate = new Roo.Template(
11893 '<table border="0" cellpadding="0" cellspacing="0" class="x-btn-wrap"><tbody><tr>',
11894 '<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>',
11895 "</tr></tbody></table>");
11897 this.template = Roo.Button.buttonTemplate;
11899 btn = this.template.append(renderTo, [this.text || ' ', this.type], true);
11900 var btnEl = btn.child("button:first");
11901 btnEl.on('focus', this.onFocus, this);
11902 btnEl.on('blur', this.onBlur, this);
11904 btn.addClass(this.cls);
11907 btnEl.setStyle('background-image', 'url(' +this.icon +')');
11910 btnEl.addClass(this.iconCls);
11912 btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
11915 if(this.tabIndex !== undefined){
11916 btnEl.dom.tabIndex = this.tabIndex;
11919 if(typeof this.tooltip == 'object'){
11920 Roo.QuickTips.tips(Roo.apply({
11924 btnEl.dom[this.tooltipType] = this.tooltip;
11928 btn = Roo.DomHelper.append(Roo.get(renderTo).dom, this.dhconfig, true);
11932 this.el.dom.id = this.el.id = this.id;
11935 this.el.child(this.menuClassTarget).addClass("x-btn-with-menu");
11936 this.menu.on("show", this.onMenuShow, this);
11937 this.menu.on("hide", this.onMenuHide, this);
11939 btn.addClass("x-btn");
11940 if(Roo.isIE && !Roo.isIE7){
11941 this.autoWidth.defer(1, this);
11945 if(this.handleMouseEvents){
11946 btn.on("mouseover", this.onMouseOver, this);
11947 btn.on("mouseout", this.onMouseOut, this);
11948 btn.on("mousedown", this.onMouseDown, this);
11950 btn.on(this.clickEvent, this.onClick, this);
11951 //btn.on("mouseup", this.onMouseUp, this);
11958 Roo.ButtonToggleMgr.register(this);
11960 this.el.addClass("x-btn-pressed");
11963 var repeater = new Roo.util.ClickRepeater(btn,
11964 typeof this.repeat == "object" ? this.repeat : {}
11966 repeater.on("click", this.onClick, this);
11969 this.fireEvent('render', this);
11973 * Returns the button's underlying element
11974 * @return {Roo.Element} The element
11976 getEl : function(){
11981 * Destroys this Button and removes any listeners.
11983 destroy : function(){
11984 Roo.ButtonToggleMgr.unregister(this);
11985 this.el.removeAllListeners();
11986 this.purgeListeners();
11991 autoWidth : function(){
11993 this.el.setWidth("auto");
11994 if(Roo.isIE7 && Roo.isStrict){
11995 var ib = this.el.child('button');
11996 if(ib && ib.getWidth() > 20){
11998 ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
12003 this.el.beginMeasure();
12005 if(this.el.getWidth() < this.minWidth){
12006 this.el.setWidth(this.minWidth);
12009 this.el.endMeasure();
12016 * Assigns this button's click handler
12017 * @param {Function} handler The function to call when the button is clicked
12018 * @param {Object} scope (optional) Scope for the function passed in
12020 setHandler : function(handler, scope){
12021 this.handler = handler;
12022 this.scope = scope;
12026 * Sets this button's text
12027 * @param {String} text The button text
12029 setText : function(text){
12032 this.el.child("td.x-btn-center button.x-btn-text").update(text);
12038 * Gets the text for this button
12039 * @return {String} The button text
12041 getText : function(){
12049 this.hidden = false;
12051 this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "");
12059 this.hidden = true;
12061 this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "none");
12066 * Convenience function for boolean show/hide
12067 * @param {Boolean} visible True to show, false to hide
12069 setVisible: function(visible){
12078 * If a state it passed, it becomes the pressed state otherwise the current state is toggled.
12079 * @param {Boolean} state (optional) Force a particular state
12081 toggle : function(state){
12082 state = state === undefined ? !this.pressed : state;
12083 if(state != this.pressed){
12085 this.el.addClass("x-btn-pressed");
12086 this.pressed = true;
12087 this.fireEvent("toggle", this, true);
12089 this.el.removeClass("x-btn-pressed");
12090 this.pressed = false;
12091 this.fireEvent("toggle", this, false);
12093 if(this.toggleHandler){
12094 this.toggleHandler.call(this.scope || this, this, state);
12102 focus : function(){
12103 this.el.child('button:first').focus();
12107 * Disable this button
12109 disable : function(){
12111 this.el.addClass("x-btn-disabled");
12113 this.disabled = true;
12117 * Enable this button
12119 enable : function(){
12121 this.el.removeClass("x-btn-disabled");
12123 this.disabled = false;
12127 * Convenience function for boolean enable/disable
12128 * @param {Boolean} enabled True to enable, false to disable
12130 setDisabled : function(v){
12131 this[v !== true ? "enable" : "disable"]();
12135 onClick : function(e){
12137 e.preventDefault();
12142 if(!this.disabled){
12143 if(this.enableToggle){
12146 if(this.menu && !this.menu.isVisible()){
12147 this.menu.show(this.el, this.menuAlign);
12149 this.fireEvent("click", this, e);
12151 this.el.removeClass("x-btn-over");
12152 this.handler.call(this.scope || this, this, e);
12157 onMouseOver : function(e){
12158 if(!this.disabled){
12159 this.el.addClass("x-btn-over");
12160 this.fireEvent('mouseover', this, e);
12164 onMouseOut : function(e){
12165 if(!e.within(this.el, true)){
12166 this.el.removeClass("x-btn-over");
12167 this.fireEvent('mouseout', this, e);
12171 onFocus : function(e){
12172 if(!this.disabled){
12173 this.el.addClass("x-btn-focus");
12177 onBlur : function(e){
12178 this.el.removeClass("x-btn-focus");
12181 onMouseDown : function(e){
12182 if(!this.disabled && e.button == 0){
12183 this.el.addClass("x-btn-click");
12184 Roo.get(document).on('mouseup', this.onMouseUp, this);
12188 onMouseUp : function(e){
12190 this.el.removeClass("x-btn-click");
12191 Roo.get(document).un('mouseup', this.onMouseUp, this);
12195 onMenuShow : function(e){
12196 this.el.addClass("x-btn-menu-active");
12199 onMenuHide : function(e){
12200 this.el.removeClass("x-btn-menu-active");
12204 // Private utility class used by Button
12205 Roo.ButtonToggleMgr = function(){
12208 function toggleGroup(btn, state){
12210 var g = groups[btn.toggleGroup];
12211 for(var i = 0, l = g.length; i < l; i++){
12213 g[i].toggle(false);
12220 register : function(btn){
12221 if(!btn.toggleGroup){
12224 var g = groups[btn.toggleGroup];
12226 g = groups[btn.toggleGroup] = [];
12229 btn.on("toggle", toggleGroup);
12232 unregister : function(btn){
12233 if(!btn.toggleGroup){
12236 var g = groups[btn.toggleGroup];
12239 btn.un("toggle", toggleGroup);
12245 * Ext JS Library 1.1.1
12246 * Copyright(c) 2006-2007, Ext JS, LLC.
12248 * Originally Released Under LGPL - original licence link has changed is not relivant.
12251 * <script type="text/javascript">
12255 * @class Roo.SplitButton
12256 * @extends Roo.Button
12257 * A split button that provides a built-in dropdown arrow that can fire an event separately from the default
12258 * click event of the button. Typically this would be used to display a dropdown menu that provides additional
12259 * options to the primary button action, but any custom handler can provide the arrowclick implementation.
12260 * @cfg {Function} arrowHandler A function called when the arrow button is clicked (can be used instead of click event)
12261 * @cfg {String} arrowTooltip The title attribute of the arrow
12263 * Create a new menu button
12264 * @param {String/HTMLElement/Element} renderTo The element to append the button to
12265 * @param {Object} config The config object
12267 Roo.SplitButton = function(renderTo, config){
12268 Roo.SplitButton.superclass.constructor.call(this, renderTo, config);
12270 * @event arrowclick
12271 * Fires when this button's arrow is clicked
12272 * @param {SplitButton} this
12273 * @param {EventObject} e The click event
12275 this.addEvents({"arrowclick":true});
12278 Roo.extend(Roo.SplitButton, Roo.Button, {
12279 render : function(renderTo){
12280 // this is one sweet looking template!
12281 var tpl = new Roo.Template(
12282 '<table cellspacing="0" class="x-btn-menu-wrap x-btn"><tr><td>',
12283 '<table cellspacing="0" class="x-btn-wrap x-btn-menu-text-wrap"><tbody>',
12284 '<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>',
12285 "</tbody></table></td><td>",
12286 '<table cellspacing="0" class="x-btn-wrap x-btn-menu-arrow-wrap"><tbody>',
12287 '<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>',
12288 "</tbody></table></td></tr></table>"
12290 var btn = tpl.append(renderTo, [this.text, this.type], true);
12291 var btnEl = btn.child("button");
12293 btn.addClass(this.cls);
12296 btnEl.setStyle('background-image', 'url(' +this.icon +')');
12299 btnEl.addClass(this.iconCls);
12301 btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
12305 if(this.handleMouseEvents){
12306 btn.on("mouseover", this.onMouseOver, this);
12307 btn.on("mouseout", this.onMouseOut, this);
12308 btn.on("mousedown", this.onMouseDown, this);
12309 btn.on("mouseup", this.onMouseUp, this);
12311 btn.on(this.clickEvent, this.onClick, this);
12313 if(typeof this.tooltip == 'object'){
12314 Roo.QuickTips.tips(Roo.apply({
12318 btnEl.dom[this.tooltipType] = this.tooltip;
12321 if(this.arrowTooltip){
12322 btn.child("button:nth(2)").dom[this.tooltipType] = this.arrowTooltip;
12331 this.el.addClass("x-btn-pressed");
12333 if(Roo.isIE && !Roo.isIE7){
12334 this.autoWidth.defer(1, this);
12339 this.menu.on("show", this.onMenuShow, this);
12340 this.menu.on("hide", this.onMenuHide, this);
12342 this.fireEvent('render', this);
12346 autoWidth : function(){
12348 var tbl = this.el.child("table:first");
12349 var tbl2 = this.el.child("table:last");
12350 this.el.setWidth("auto");
12351 tbl.setWidth("auto");
12352 if(Roo.isIE7 && Roo.isStrict){
12353 var ib = this.el.child('button:first');
12354 if(ib && ib.getWidth() > 20){
12356 ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
12361 this.el.beginMeasure();
12363 if((tbl.getWidth()+tbl2.getWidth()) < this.minWidth){
12364 tbl.setWidth(this.minWidth-tbl2.getWidth());
12367 this.el.endMeasure();
12370 this.el.setWidth(tbl.getWidth()+tbl2.getWidth());
12374 * Sets this button's click handler
12375 * @param {Function} handler The function to call when the button is clicked
12376 * @param {Object} scope (optional) Scope for the function passed above
12378 setHandler : function(handler, scope){
12379 this.handler = handler;
12380 this.scope = scope;
12384 * Sets this button's arrow click handler
12385 * @param {Function} handler The function to call when the arrow is clicked
12386 * @param {Object} scope (optional) Scope for the function passed above
12388 setArrowHandler : function(handler, scope){
12389 this.arrowHandler = handler;
12390 this.scope = scope;
12396 focus : function(){
12398 this.el.child("button:first").focus();
12403 onClick : function(e){
12404 e.preventDefault();
12405 if(!this.disabled){
12406 if(e.getTarget(".x-btn-menu-arrow-wrap")){
12407 if(this.menu && !this.menu.isVisible()){
12408 this.menu.show(this.el, this.menuAlign);
12410 this.fireEvent("arrowclick", this, e);
12411 if(this.arrowHandler){
12412 this.arrowHandler.call(this.scope || this, this, e);
12415 this.fireEvent("click", this, e);
12417 this.handler.call(this.scope || this, this, e);
12423 onMouseDown : function(e){
12424 if(!this.disabled){
12425 Roo.fly(e.getTarget("table")).addClass("x-btn-click");
12429 onMouseUp : function(e){
12430 Roo.fly(e.getTarget("table")).removeClass("x-btn-click");
12435 // backwards compat
12436 Roo.MenuButton = Roo.SplitButton;/*
12438 * Ext JS Library 1.1.1
12439 * Copyright(c) 2006-2007, Ext JS, LLC.
12441 * Originally Released Under LGPL - original licence link has changed is not relivant.
12444 * <script type="text/javascript">
12448 * @class Roo.Toolbar
12449 * Basic Toolbar class.
12451 * Creates a new Toolbar
12452 * @param {Object} container The config object
12454 Roo.Toolbar = function(container, buttons, config)
12456 /// old consturctor format still supported..
12457 if(container instanceof Array){ // omit the container for later rendering
12458 buttons = container;
12462 if (typeof(container) == 'object' && container.xtype) {
12463 config = container;
12464 container = config.container;
12465 buttons = config.buttons || []; // not really - use items!!
12468 if (config && config.items) {
12469 xitems = config.items;
12470 delete config.items;
12472 Roo.apply(this, config);
12473 this.buttons = buttons;
12476 this.render(container);
12478 this.xitems = xitems;
12479 Roo.each(xitems, function(b) {
12485 Roo.Toolbar.prototype = {
12487 * @cfg {Array} items
12488 * array of button configs or elements to add (will be converted to a MixedCollection)
12492 * @cfg {String/HTMLElement/Element} container
12493 * The id or element that will contain the toolbar
12496 render : function(ct){
12497 this.el = Roo.get(ct);
12499 this.el.addClass(this.cls);
12501 // using a table allows for vertical alignment
12502 // 100% width is needed by Safari...
12503 this.el.update('<div class="x-toolbar x-small-editor"><table cellspacing="0"><tr></tr></table></div>');
12504 this.tr = this.el.child("tr", true);
12506 this.items = new Roo.util.MixedCollection(false, function(o){
12507 return o.id || ("item" + (++autoId));
12510 this.add.apply(this, this.buttons);
12511 delete this.buttons;
12516 * Adds element(s) to the toolbar -- this function takes a variable number of
12517 * arguments of mixed type and adds them to the toolbar.
12518 * @param {Mixed} arg1 The following types of arguments are all valid:<br />
12520 * <li>{@link Roo.Toolbar.Button} config: A valid button config object (equivalent to {@link #addButton})</li>
12521 * <li>HtmlElement: Any standard HTML element (equivalent to {@link #addElement})</li>
12522 * <li>Field: Any form field (equivalent to {@link #addField})</li>
12523 * <li>Item: Any subclass of {@link Roo.Toolbar.Item} (equivalent to {@link #addItem})</li>
12524 * <li>String: Any generic string (gets wrapped in a {@link Roo.Toolbar.TextItem}, equivalent to {@link #addText}).
12525 * Note that there are a few special strings that are treated differently as explained nRoo.</li>
12526 * <li>'separator' or '-': Creates a separator element (equivalent to {@link #addSeparator})</li>
12527 * <li>' ': Creates a spacer element (equivalent to {@link #addSpacer})</li>
12528 * <li>'->': Creates a fill element (equivalent to {@link #addFill})</li>
12530 * @param {Mixed} arg2
12531 * @param {Mixed} etc.
12534 var a = arguments, l = a.length;
12535 for(var i = 0; i < l; i++){
12540 _add : function(el) {
12543 el = Roo.factory(el, typeof(Roo.Toolbar[el.xtype]) == 'undefined' ? Roo.form : Roo.Toolbar);
12546 if (el.applyTo){ // some kind of form field
12547 return this.addField(el);
12549 if (el.render){ // some kind of Toolbar.Item
12550 return this.addItem(el);
12552 if (typeof el == "string"){ // string
12553 if(el == "separator" || el == "-"){
12554 return this.addSeparator();
12557 return this.addSpacer();
12560 return this.addFill();
12562 return this.addText(el);
12565 if(el.tagName){ // element
12566 return this.addElement(el);
12568 if(typeof el == "object"){ // must be button config?
12569 return this.addButton(el);
12571 // and now what?!?!
12577 * Add an Xtype element
12578 * @param {Object} xtype Xtype Object
12579 * @return {Object} created Object
12581 addxtype : function(e){
12582 return this.add(e);
12586 * Returns the Element for this toolbar.
12587 * @return {Roo.Element}
12589 getEl : function(){
12595 * @return {Roo.Toolbar.Item} The separator item
12597 addSeparator : function(){
12598 return this.addItem(new Roo.Toolbar.Separator());
12602 * Adds a spacer element
12603 * @return {Roo.Toolbar.Spacer} The spacer item
12605 addSpacer : function(){
12606 return this.addItem(new Roo.Toolbar.Spacer());
12610 * Adds a fill element that forces subsequent additions to the right side of the toolbar
12611 * @return {Roo.Toolbar.Fill} The fill item
12613 addFill : function(){
12614 return this.addItem(new Roo.Toolbar.Fill());
12618 * Adds any standard HTML element to the toolbar
12619 * @param {String/HTMLElement/Element} el The element or id of the element to add
12620 * @return {Roo.Toolbar.Item} The element's item
12622 addElement : function(el){
12623 return this.addItem(new Roo.Toolbar.Item(el));
12626 * Collection of items on the toolbar.. (only Toolbar Items, so use fields to retrieve fields)
12627 * @type Roo.util.MixedCollection
12632 * Adds any Toolbar.Item or subclass
12633 * @param {Roo.Toolbar.Item} item
12634 * @return {Roo.Toolbar.Item} The item
12636 addItem : function(item){
12637 var td = this.nextBlock();
12639 this.items.add(item);
12644 * Adds a button (or buttons). See {@link Roo.Toolbar.Button} for more info on the config.
12645 * @param {Object/Array} config A button config or array of configs
12646 * @return {Roo.Toolbar.Button/Array}
12648 addButton : function(config){
12649 if(config instanceof Array){
12651 for(var i = 0, len = config.length; i < len; i++) {
12652 buttons.push(this.addButton(config[i]));
12657 if(!(config instanceof Roo.Toolbar.Button)){
12659 new Roo.Toolbar.SplitButton(config) :
12660 new Roo.Toolbar.Button(config);
12662 var td = this.nextBlock();
12669 * Adds text to the toolbar
12670 * @param {String} text The text to add
12671 * @return {Roo.Toolbar.Item} The element's item
12673 addText : function(text){
12674 return this.addItem(new Roo.Toolbar.TextItem(text));
12678 * Inserts any {@link Roo.Toolbar.Item}/{@link Roo.Toolbar.Button} at the specified index.
12679 * @param {Number} index The index where the item is to be inserted
12680 * @param {Object/Roo.Toolbar.Item/Roo.Toolbar.Button (may be Array)} item The button, or button config object to be inserted.
12681 * @return {Roo.Toolbar.Button/Item}
12683 insertButton : function(index, item){
12684 if(item instanceof Array){
12686 for(var i = 0, len = item.length; i < len; i++) {
12687 buttons.push(this.insertButton(index + i, item[i]));
12691 if (!(item instanceof Roo.Toolbar.Button)){
12692 item = new Roo.Toolbar.Button(item);
12694 var td = document.createElement("td");
12695 this.tr.insertBefore(td, this.tr.childNodes[index]);
12697 this.items.insert(index, item);
12702 * Adds a new element to the toolbar from the passed {@link Roo.DomHelper} config.
12703 * @param {Object} config
12704 * @return {Roo.Toolbar.Item} The element's item
12706 addDom : function(config, returnEl){
12707 var td = this.nextBlock();
12708 Roo.DomHelper.overwrite(td, config);
12709 var ti = new Roo.Toolbar.Item(td.firstChild);
12711 this.items.add(ti);
12716 * Collection of fields on the toolbar.. usefull for quering (value is false if there are no fields)
12717 * @type Roo.util.MixedCollection
12722 * Adds a dynamically rendered Roo.form field (TextField, ComboBox, etc).
12723 * Note: the field should not have been rendered yet. For a field that has already been
12724 * rendered, use {@link #addElement}.
12725 * @param {Roo.form.Field} field
12726 * @return {Roo.ToolbarItem}
12730 addField : function(field) {
12731 if (!this.fields) {
12733 this.fields = new Roo.util.MixedCollection(false, function(o){
12734 return o.id || ("item" + (++autoId));
12739 var td = this.nextBlock();
12741 var ti = new Roo.Toolbar.Item(td.firstChild);
12743 this.items.add(ti);
12744 this.fields.add(field);
12755 this.el.child('div').setVisibilityMode(Roo.Element.DISPLAY);
12756 this.el.child('div').hide();
12764 this.el.child('div').show();
12768 nextBlock : function(){
12769 var td = document.createElement("td");
12770 this.tr.appendChild(td);
12775 destroy : function(){
12776 if(this.items){ // rendered?
12777 Roo.destroy.apply(Roo, this.items.items);
12779 if(this.fields){ // rendered?
12780 Roo.destroy.apply(Roo, this.fields.items);
12782 Roo.Element.uncache(this.el, this.tr);
12787 * @class Roo.Toolbar.Item
12788 * The base class that other classes should extend in order to get some basic common toolbar item functionality.
12790 * Creates a new Item
12791 * @param {HTMLElement} el
12793 Roo.Toolbar.Item = function(el){
12794 this.el = Roo.getDom(el);
12795 this.id = Roo.id(this.el);
12796 this.hidden = false;
12799 Roo.Toolbar.Item.prototype = {
12802 * Get this item's HTML Element
12803 * @return {HTMLElement}
12805 getEl : function(){
12810 render : function(td){
12812 td.appendChild(this.el);
12816 * Removes and destroys this item.
12818 destroy : function(){
12819 this.td.parentNode.removeChild(this.td);
12826 this.hidden = false;
12827 this.td.style.display = "";
12834 this.hidden = true;
12835 this.td.style.display = "none";
12839 * Convenience function for boolean show/hide.
12840 * @param {Boolean} visible true to show/false to hide
12842 setVisible: function(visible){
12851 * Try to focus this item.
12853 focus : function(){
12854 Roo.fly(this.el).focus();
12858 * Disables this item.
12860 disable : function(){
12861 Roo.fly(this.td).addClass("x-item-disabled");
12862 this.disabled = true;
12863 this.el.disabled = true;
12867 * Enables this item.
12869 enable : function(){
12870 Roo.fly(this.td).removeClass("x-item-disabled");
12871 this.disabled = false;
12872 this.el.disabled = false;
12878 * @class Roo.Toolbar.Separator
12879 * @extends Roo.Toolbar.Item
12880 * A simple toolbar separator class
12882 * Creates a new Separator
12884 Roo.Toolbar.Separator = function(){
12885 var s = document.createElement("span");
12886 s.className = "ytb-sep";
12887 Roo.Toolbar.Separator.superclass.constructor.call(this, s);
12889 Roo.extend(Roo.Toolbar.Separator, Roo.Toolbar.Item, {
12890 enable:Roo.emptyFn,
12891 disable:Roo.emptyFn,
12896 * @class Roo.Toolbar.Spacer
12897 * @extends Roo.Toolbar.Item
12898 * A simple element that adds extra horizontal space to a toolbar.
12900 * Creates a new Spacer
12902 Roo.Toolbar.Spacer = function(){
12903 var s = document.createElement("div");
12904 s.className = "ytb-spacer";
12905 Roo.Toolbar.Spacer.superclass.constructor.call(this, s);
12907 Roo.extend(Roo.Toolbar.Spacer, Roo.Toolbar.Item, {
12908 enable:Roo.emptyFn,
12909 disable:Roo.emptyFn,
12914 * @class Roo.Toolbar.Fill
12915 * @extends Roo.Toolbar.Spacer
12916 * A simple element that adds a greedy (100% width) horizontal space to a toolbar.
12918 * Creates a new Spacer
12920 Roo.Toolbar.Fill = Roo.extend(Roo.Toolbar.Spacer, {
12922 render : function(td){
12923 td.style.width = '100%';
12924 Roo.Toolbar.Fill.superclass.render.call(this, td);
12929 * @class Roo.Toolbar.TextItem
12930 * @extends Roo.Toolbar.Item
12931 * A simple class that renders text directly into a toolbar.
12933 * Creates a new TextItem
12934 * @param {String} text
12936 Roo.Toolbar.TextItem = function(text){
12937 if (typeof(text) == 'object') {
12940 var s = document.createElement("span");
12941 s.className = "ytb-text";
12942 s.innerHTML = text;
12943 Roo.Toolbar.TextItem.superclass.constructor.call(this, s);
12945 Roo.extend(Roo.Toolbar.TextItem, Roo.Toolbar.Item, {
12946 enable:Roo.emptyFn,
12947 disable:Roo.emptyFn,
12952 * @class Roo.Toolbar.Button
12953 * @extends Roo.Button
12954 * A button that renders into a toolbar.
12956 * Creates a new Button
12957 * @param {Object} config A standard {@link Roo.Button} config object
12959 Roo.Toolbar.Button = function(config){
12960 Roo.Toolbar.Button.superclass.constructor.call(this, null, config);
12962 Roo.extend(Roo.Toolbar.Button, Roo.Button, {
12963 render : function(td){
12965 Roo.Toolbar.Button.superclass.render.call(this, td);
12969 * Removes and destroys this button
12971 destroy : function(){
12972 Roo.Toolbar.Button.superclass.destroy.call(this);
12973 this.td.parentNode.removeChild(this.td);
12977 * Shows this button
12980 this.hidden = false;
12981 this.td.style.display = "";
12985 * Hides this button
12988 this.hidden = true;
12989 this.td.style.display = "none";
12993 * Disables this item
12995 disable : function(){
12996 Roo.fly(this.td).addClass("x-item-disabled");
12997 this.disabled = true;
13001 * Enables this item
13003 enable : function(){
13004 Roo.fly(this.td).removeClass("x-item-disabled");
13005 this.disabled = false;
13008 // backwards compat
13009 Roo.ToolbarButton = Roo.Toolbar.Button;
13012 * @class Roo.Toolbar.SplitButton
13013 * @extends Roo.SplitButton
13014 * A menu button that renders into a toolbar.
13016 * Creates a new SplitButton
13017 * @param {Object} config A standard {@link Roo.SplitButton} config object
13019 Roo.Toolbar.SplitButton = function(config){
13020 Roo.Toolbar.SplitButton.superclass.constructor.call(this, null, config);
13022 Roo.extend(Roo.Toolbar.SplitButton, Roo.SplitButton, {
13023 render : function(td){
13025 Roo.Toolbar.SplitButton.superclass.render.call(this, td);
13029 * Removes and destroys this button
13031 destroy : function(){
13032 Roo.Toolbar.SplitButton.superclass.destroy.call(this);
13033 this.td.parentNode.removeChild(this.td);
13037 * Shows this button
13040 this.hidden = false;
13041 this.td.style.display = "";
13045 * Hides this button
13048 this.hidden = true;
13049 this.td.style.display = "none";
13053 // backwards compat
13054 Roo.Toolbar.MenuButton = Roo.Toolbar.SplitButton;/*
13056 * Ext JS Library 1.1.1
13057 * Copyright(c) 2006-2007, Ext JS, LLC.
13059 * Originally Released Under LGPL - original licence link has changed is not relivant.
13062 * <script type="text/javascript">
13066 * @class Roo.PagingToolbar
13067 * @extends Roo.Toolbar
13068 * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
13070 * Create a new PagingToolbar
13071 * @param {Object} config The config object
13073 Roo.PagingToolbar = function(el, ds, config)
13075 // old args format still supported... - xtype is prefered..
13076 if (typeof(el) == 'object' && el.xtype) {
13077 // created from xtype...
13079 ds = el.dataSource;
13080 el = config.container;
13083 if (config.items) {
13084 items = config.items;
13088 Roo.PagingToolbar.superclass.constructor.call(this, el, null, config);
13091 this.renderButtons(this.el);
13094 // supprot items array.
13096 Roo.each(items, function(e) {
13097 this.add(Roo.factory(e));
13102 Roo.extend(Roo.PagingToolbar, Roo.Toolbar, {
13104 * @cfg {Roo.data.Store} dataSource
13105 * The underlying data store providing the paged data
13108 * @cfg {String/HTMLElement/Element} container
13109 * container The id or element that will contain the toolbar
13112 * @cfg {Boolean} displayInfo
13113 * True to display the displayMsg (defaults to false)
13116 * @cfg {Number} pageSize
13117 * The number of records to display per page (defaults to 20)
13121 * @cfg {String} displayMsg
13122 * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
13124 displayMsg : 'Displaying {0} - {1} of {2}',
13126 * @cfg {String} emptyMsg
13127 * The message to display when no records are found (defaults to "No data to display")
13129 emptyMsg : 'No data to display',
13131 * Customizable piece of the default paging text (defaults to "Page")
13134 beforePageText : "Page",
13136 * Customizable piece of the default paging text (defaults to "of %0")
13139 afterPageText : "of {0}",
13141 * Customizable piece of the default paging text (defaults to "First Page")
13144 firstText : "First Page",
13146 * Customizable piece of the default paging text (defaults to "Previous Page")
13149 prevText : "Previous Page",
13151 * Customizable piece of the default paging text (defaults to "Next Page")
13154 nextText : "Next Page",
13156 * Customizable piece of the default paging text (defaults to "Last Page")
13159 lastText : "Last Page",
13161 * Customizable piece of the default paging text (defaults to "Refresh")
13164 refreshText : "Refresh",
13167 renderButtons : function(el){
13168 Roo.PagingToolbar.superclass.render.call(this, el);
13169 this.first = this.addButton({
13170 tooltip: this.firstText,
13171 cls: "x-btn-icon x-grid-page-first",
13173 handler: this.onClick.createDelegate(this, ["first"])
13175 this.prev = this.addButton({
13176 tooltip: this.prevText,
13177 cls: "x-btn-icon x-grid-page-prev",
13179 handler: this.onClick.createDelegate(this, ["prev"])
13181 //this.addSeparator();
13182 this.add(this.beforePageText);
13183 this.field = Roo.get(this.addDom({
13188 cls: "x-grid-page-number"
13190 this.field.on("keydown", this.onPagingKeydown, this);
13191 this.field.on("focus", function(){this.dom.select();});
13192 this.afterTextEl = this.addText(String.format(this.afterPageText, 1));
13193 this.field.setHeight(18);
13194 //this.addSeparator();
13195 this.next = this.addButton({
13196 tooltip: this.nextText,
13197 cls: "x-btn-icon x-grid-page-next",
13199 handler: this.onClick.createDelegate(this, ["next"])
13201 this.last = this.addButton({
13202 tooltip: this.lastText,
13203 cls: "x-btn-icon x-grid-page-last",
13205 handler: this.onClick.createDelegate(this, ["last"])
13207 //this.addSeparator();
13208 this.loading = this.addButton({
13209 tooltip: this.refreshText,
13210 cls: "x-btn-icon x-grid-loading",
13211 handler: this.onClick.createDelegate(this, ["refresh"])
13214 if(this.displayInfo){
13215 this.displayEl = Roo.fly(this.el.dom.firstChild).createChild({cls:'x-paging-info'});
13220 updateInfo : function(){
13221 if(this.displayEl){
13222 var count = this.ds.getCount();
13223 var msg = count == 0 ?
13227 this.cursor+1, this.cursor+count, this.ds.getTotalCount()
13229 this.displayEl.update(msg);
13234 onLoad : function(ds, r, o){
13235 this.cursor = o.params ? o.params.start : 0;
13236 var d = this.getPageData(), ap = d.activePage, ps = d.pages;
13238 this.afterTextEl.el.innerHTML = String.format(this.afterPageText, d.pages);
13239 this.field.dom.value = ap;
13240 this.first.setDisabled(ap == 1);
13241 this.prev.setDisabled(ap == 1);
13242 this.next.setDisabled(ap == ps);
13243 this.last.setDisabled(ap == ps);
13244 this.loading.enable();
13249 getPageData : function(){
13250 var total = this.ds.getTotalCount();
13253 activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
13254 pages : total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
13259 onLoadError : function(){
13260 this.loading.enable();
13264 onPagingKeydown : function(e){
13265 var k = e.getKey();
13266 var d = this.getPageData();
13268 var v = this.field.dom.value, pageNum;
13269 if(!v || isNaN(pageNum = parseInt(v, 10))){
13270 this.field.dom.value = d.activePage;
13273 pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
13274 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
13277 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))
13279 var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
13280 this.field.dom.value = pageNum;
13281 this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
13284 else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
13286 var v = this.field.dom.value, pageNum;
13287 var increment = (e.shiftKey) ? 10 : 1;
13288 if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
13290 if(!v || isNaN(pageNum = parseInt(v, 10))) {
13291 this.field.dom.value = d.activePage;
13294 else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
13296 this.field.dom.value = parseInt(v, 10) + increment;
13297 pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
13298 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
13305 beforeLoad : function(){
13307 this.loading.disable();
13312 onClick : function(which){
13316 ds.load({params:{start: 0, limit: this.pageSize}});
13319 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
13322 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
13325 var total = ds.getTotalCount();
13326 var extra = total % this.pageSize;
13327 var lastStart = extra ? (total - extra) : total-this.pageSize;
13328 ds.load({params:{start: lastStart, limit: this.pageSize}});
13331 ds.load({params:{start: this.cursor, limit: this.pageSize}});
13337 * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
13338 * @param {Roo.data.Store} store The data store to unbind
13340 unbind : function(ds){
13341 ds.un("beforeload", this.beforeLoad, this);
13342 ds.un("load", this.onLoad, this);
13343 ds.un("loadexception", this.onLoadError, this);
13344 ds.un("remove", this.updateInfo, this);
13345 ds.un("add", this.updateInfo, this);
13346 this.ds = undefined;
13350 * Binds the paging toolbar to the specified {@link Roo.data.Store}
13351 * @param {Roo.data.Store} store The data store to bind
13353 bind : function(ds){
13354 ds.on("beforeload", this.beforeLoad, this);
13355 ds.on("load", this.onLoad, this);
13356 ds.on("loadexception", this.onLoadError, this);
13357 ds.on("remove", this.updateInfo, this);
13358 ds.on("add", this.updateInfo, this);
13363 * Ext JS Library 1.1.1
13364 * Copyright(c) 2006-2007, Ext JS, LLC.
13366 * Originally Released Under LGPL - original licence link has changed is not relivant.
13369 * <script type="text/javascript">
13373 * @class Roo.Resizable
13374 * @extends Roo.util.Observable
13375 * <p>Applies drag handles to an element to make it resizable. The drag handles are inserted into the element
13376 * and positioned absolute. Some elements, such as a textarea or image, don't support this. To overcome that, you can wrap
13377 * 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
13378 * the element will be wrapped for you automatically.</p>
13379 * <p>Here is the list of valid resize handles:</p>
13382 ------ -------------------
13391 'hd' horizontal drag
13394 * <p>Here's an example showing the creation of a typical Resizable:</p>
13396 var resizer = new Roo.Resizable("element-id", {
13404 resizer.on("resize", myHandler);
13406 * <p>To hide a particular handle, set its display to none in CSS, or through script:<br>
13407 * resizer.east.setDisplayed(false);</p>
13408 * @cfg {Boolean/String/Element} resizeChild True to resize the first child, or id/element to resize (defaults to false)
13409 * @cfg {Array/String} adjustments String "auto" or an array [width, height] with values to be <b>added</b> to the
13410 * resize operation's new size (defaults to [0, 0])
13411 * @cfg {Number} minWidth The minimum width for the element (defaults to 5)
13412 * @cfg {Number} minHeight The minimum height for the element (defaults to 5)
13413 * @cfg {Number} maxWidth The maximum width for the element (defaults to 10000)
13414 * @cfg {Number} maxHeight The maximum height for the element (defaults to 10000)
13415 * @cfg {Boolean} enabled False to disable resizing (defaults to true)
13416 * @cfg {Boolean} wrap True to wrap an element with a div if needed (required for textareas and images, defaults to false)
13417 * @cfg {Number} width The width of the element in pixels (defaults to null)
13418 * @cfg {Number} height The height of the element in pixels (defaults to null)
13419 * @cfg {Boolean} animate True to animate the resize (not compatible with dynamic sizing, defaults to false)
13420 * @cfg {Number} duration Animation duration if animate = true (defaults to .35)
13421 * @cfg {Boolean} dynamic True to resize the element while dragging instead of using a proxy (defaults to false)
13422 * @cfg {String} handles String consisting of the resize handles to display (defaults to undefined)
13423 * @cfg {Boolean} multiDirectional <b>Deprecated</b>. The old style of adding multi-direction resize handles, deprecated
13424 * in favor of the handles config option (defaults to false)
13425 * @cfg {Boolean} disableTrackOver True to disable mouse tracking. This is only applied at config time. (defaults to false)
13426 * @cfg {String} easing Animation easing if animate = true (defaults to 'easingOutStrong')
13427 * @cfg {Number} widthIncrement The increment to snap the width resize in pixels (dynamic must be true, defaults to 0)
13428 * @cfg {Number} heightIncrement The increment to snap the height resize in pixels (dynamic must be true, defaults to 0)
13429 * @cfg {Boolean} pinned True to ensure that the resize handles are always visible, false to display them only when the
13430 * user mouses over the resizable borders. This is only applied at config time. (defaults to false)
13431 * @cfg {Boolean} preserveRatio True to preserve the original ratio between height and width during resize (defaults to false)
13432 * @cfg {Boolean} transparent True for transparent handles. This is only applied at config time. (defaults to false)
13433 * @cfg {Number} minX The minimum allowed page X for the element (only used for west resizing, defaults to 0)
13434 * @cfg {Number} minY The minimum allowed page Y for the element (only used for north resizing, defaults to 0)
13435 * @cfg {Boolean} draggable Convenience to initialize drag drop (defaults to false)
13437 * Create a new resizable component
13438 * @param {String/HTMLElement/Roo.Element} el The id or element to resize
13439 * @param {Object} config configuration options
13441 Roo.Resizable = function(el, config)
13443 this.el = Roo.get(el);
13445 if(config && config.wrap){
13446 config.resizeChild = this.el;
13447 this.el = this.el.wrap(typeof config.wrap == "object" ? config.wrap : {cls:"xresizable-wrap"});
13448 this.el.id = this.el.dom.id = config.resizeChild.id + "-rzwrap";
13449 this.el.setStyle("overflow", "hidden");
13450 this.el.setPositioning(config.resizeChild.getPositioning());
13451 config.resizeChild.clearPositioning();
13452 if(!config.width || !config.height){
13453 var csize = config.resizeChild.getSize();
13454 this.el.setSize(csize.width, csize.height);
13456 if(config.pinned && !config.adjustments){
13457 config.adjustments = "auto";
13461 this.proxy = this.el.createProxy({tag: "div", cls: "x-resizable-proxy", id: this.el.id + "-rzproxy"});
13462 this.proxy.unselectable();
13463 this.proxy.enableDisplayMode('block');
13465 Roo.apply(this, config);
13468 this.disableTrackOver = true;
13469 this.el.addClass("x-resizable-pinned");
13471 // if the element isn't positioned, make it relative
13472 var position = this.el.getStyle("position");
13473 if(position != "absolute" && position != "fixed"){
13474 this.el.setStyle("position", "relative");
13476 if(!this.handles){ // no handles passed, must be legacy style
13477 this.handles = 's,e,se';
13478 if(this.multiDirectional){
13479 this.handles += ',n,w';
13482 if(this.handles == "all"){
13483 this.handles = "n s e w ne nw se sw";
13485 var hs = this.handles.split(/\s*?[,;]\s*?| /);
13486 var ps = Roo.Resizable.positions;
13487 for(var i = 0, len = hs.length; i < len; i++){
13488 if(hs[i] && ps[hs[i]]){
13489 var pos = ps[hs[i]];
13490 this[pos] = new Roo.Resizable.Handle(this, pos, this.disableTrackOver, this.transparent);
13494 this.corner = this.southeast;
13496 // updateBox = the box can move..
13497 if(this.handles.indexOf("n") != -1 || this.handles.indexOf("w") != -1 || this.handles.indexOf("hd") != -1) {
13498 this.updateBox = true;
13501 this.activeHandle = null;
13503 if(this.resizeChild){
13504 if(typeof this.resizeChild == "boolean"){
13505 this.resizeChild = Roo.get(this.el.dom.firstChild, true);
13507 this.resizeChild = Roo.get(this.resizeChild, true);
13511 if(this.adjustments == "auto"){
13512 var rc = this.resizeChild;
13513 var hw = this.west, he = this.east, hn = this.north, hs = this.south;
13514 if(rc && (hw || hn)){
13515 rc.position("relative");
13516 rc.setLeft(hw ? hw.el.getWidth() : 0);
13517 rc.setTop(hn ? hn.el.getHeight() : 0);
13519 this.adjustments = [
13520 (he ? -he.el.getWidth() : 0) + (hw ? -hw.el.getWidth() : 0),
13521 (hn ? -hn.el.getHeight() : 0) + (hs ? -hs.el.getHeight() : 0) -1
13525 if(this.draggable){
13526 this.dd = this.dynamic ?
13527 this.el.initDD(null) : this.el.initDDProxy(null, {dragElId: this.proxy.id});
13528 this.dd.setHandleElId(this.resizeChild ? this.resizeChild.id : this.el.id);
13534 * @event beforeresize
13535 * Fired before resize is allowed. Set enabled to false to cancel resize.
13536 * @param {Roo.Resizable} this
13537 * @param {Roo.EventObject} e The mousedown event
13539 "beforeresize" : true,
13542 * Fired a resizing.
13543 * @param {Roo.Resizable} this
13544 * @param {Number} x The new x position
13545 * @param {Number} y The new y position
13546 * @param {Number} w The new w width
13547 * @param {Number} h The new h hight
13548 * @param {Roo.EventObject} e The mouseup event
13553 * Fired after a resize.
13554 * @param {Roo.Resizable} this
13555 * @param {Number} width The new width
13556 * @param {Number} height The new height
13557 * @param {Roo.EventObject} e The mouseup event
13562 if(this.width !== null && this.height !== null){
13563 this.resizeTo(this.width, this.height);
13565 this.updateChildSize();
13568 this.el.dom.style.zoom = 1;
13570 Roo.Resizable.superclass.constructor.call(this);
13573 Roo.extend(Roo.Resizable, Roo.util.Observable, {
13574 resizeChild : false,
13575 adjustments : [0, 0],
13585 multiDirectional : false,
13586 disableTrackOver : false,
13587 easing : 'easeOutStrong',
13588 widthIncrement : 0,
13589 heightIncrement : 0,
13593 preserveRatio : false,
13594 transparent: false,
13600 * @cfg {String/HTMLElement/Element} constrainTo Constrain the resize to a particular element
13602 constrainTo: undefined,
13604 * @cfg {Roo.lib.Region} resizeRegion Constrain the resize to a particular region
13606 resizeRegion: undefined,
13610 * Perform a manual resize
13611 * @param {Number} width
13612 * @param {Number} height
13614 resizeTo : function(width, height){
13615 this.el.setSize(width, height);
13616 this.updateChildSize();
13617 this.fireEvent("resize", this, width, height, null);
13621 startSizing : function(e, handle){
13622 this.fireEvent("beforeresize", this, e);
13623 if(this.enabled){ // 2nd enabled check in case disabled before beforeresize handler
13626 this.overlay = this.el.createProxy({tag: "div", cls: "x-resizable-overlay", html: " "});
13627 this.overlay.unselectable();
13628 this.overlay.enableDisplayMode("block");
13629 this.overlay.on("mousemove", this.onMouseMove, this);
13630 this.overlay.on("mouseup", this.onMouseUp, this);
13632 this.overlay.setStyle("cursor", handle.el.getStyle("cursor"));
13634 this.resizing = true;
13635 this.startBox = this.el.getBox();
13636 this.startPoint = e.getXY();
13637 this.offsets = [(this.startBox.x + this.startBox.width) - this.startPoint[0],
13638 (this.startBox.y + this.startBox.height) - this.startPoint[1]];
13640 this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
13641 this.overlay.show();
13643 if(this.constrainTo) {
13644 var ct = Roo.get(this.constrainTo);
13645 this.resizeRegion = ct.getRegion().adjust(
13646 ct.getFrameWidth('t'),
13647 ct.getFrameWidth('l'),
13648 -ct.getFrameWidth('b'),
13649 -ct.getFrameWidth('r')
13653 this.proxy.setStyle('visibility', 'hidden'); // workaround display none
13655 this.proxy.setBox(this.startBox);
13657 this.proxy.setStyle('visibility', 'visible');
13663 onMouseDown : function(handle, e){
13666 this.activeHandle = handle;
13667 this.startSizing(e, handle);
13672 onMouseUp : function(e){
13673 var size = this.resizeElement();
13674 this.resizing = false;
13676 this.overlay.hide();
13678 this.fireEvent("resize", this, size.width, size.height, e);
13682 updateChildSize : function(){
13684 if(this.resizeChild){
13686 var child = this.resizeChild;
13687 var adj = this.adjustments;
13688 if(el.dom.offsetWidth){
13689 var b = el.getSize(true);
13690 child.setSize(b.width+adj[0], b.height+adj[1]);
13692 // Second call here for IE
13693 // The first call enables instant resizing and
13694 // the second call corrects scroll bars if they
13697 setTimeout(function(){
13698 if(el.dom.offsetWidth){
13699 var b = el.getSize(true);
13700 child.setSize(b.width+adj[0], b.height+adj[1]);
13708 snap : function(value, inc, min){
13709 if(!inc || !value) return value;
13710 var newValue = value;
13711 var m = value % inc;
13714 newValue = value + (inc-m);
13716 newValue = value - m;
13719 return Math.max(min, newValue);
13723 resizeElement : function(){
13724 var box = this.proxy.getBox();
13725 if(this.updateBox){
13726 this.el.setBox(box, false, this.animate, this.duration, null, this.easing);
13728 this.el.setSize(box.width, box.height, this.animate, this.duration, null, this.easing);
13730 this.updateChildSize();
13738 constrain : function(v, diff, m, mx){
13741 }else if(v - diff > mx){
13748 onMouseMove : function(e){
13751 try{// try catch so if something goes wrong the user doesn't get hung
13753 if(this.resizeRegion && !this.resizeRegion.contains(e.getPoint())) {
13757 //var curXY = this.startPoint;
13758 var curSize = this.curSize || this.startBox;
13759 var x = this.startBox.x, y = this.startBox.y;
13760 var ox = x, oy = y;
13761 var w = curSize.width, h = curSize.height;
13762 var ow = w, oh = h;
13763 var mw = this.minWidth, mh = this.minHeight;
13764 var mxw = this.maxWidth, mxh = this.maxHeight;
13765 var wi = this.widthIncrement;
13766 var hi = this.heightIncrement;
13768 var eventXY = e.getXY();
13769 var diffX = -(this.startPoint[0] - Math.max(this.minX, eventXY[0]));
13770 var diffY = -(this.startPoint[1] - Math.max(this.minY, eventXY[1]));
13772 var pos = this.activeHandle.position;
13777 w = Math.min(Math.max(mw, w), mxw);
13782 h = Math.min(Math.max(mh, h), mxh);
13787 w = Math.min(Math.max(mw, w), mxw);
13788 h = Math.min(Math.max(mh, h), mxh);
13791 diffY = this.constrain(h, diffY, mh, mxh);
13798 var adiffX = Math.abs(diffX);
13799 var sub = (adiffX % wi); // how much
13800 if (sub > (wi/2)) { // far enough to snap
13801 diffX = (diffX > 0) ? diffX-sub + wi : diffX+sub - wi;
13803 // remove difference..
13804 diffX = (diffX > 0) ? diffX-sub : diffX+sub;
13808 x = Math.max(this.minX, x);
13811 diffX = this.constrain(w, diffX, mw, mxw);
13817 w = Math.min(Math.max(mw, w), mxw);
13818 diffY = this.constrain(h, diffY, mh, mxh);
13823 diffX = this.constrain(w, diffX, mw, mxw);
13824 diffY = this.constrain(h, diffY, mh, mxh);
13831 diffX = this.constrain(w, diffX, mw, mxw);
13833 h = Math.min(Math.max(mh, h), mxh);
13839 var sw = this.snap(w, wi, mw);
13840 var sh = this.snap(h, hi, mh);
13841 if(sw != w || sh != h){
13864 if(this.preserveRatio){
13869 h = Math.min(Math.max(mh, h), mxh);
13874 w = Math.min(Math.max(mw, w), mxw);
13879 w = Math.min(Math.max(mw, w), mxw);
13885 w = Math.min(Math.max(mw, w), mxw);
13891 h = Math.min(Math.max(mh, h), mxh);
13899 h = Math.min(Math.max(mh, h), mxh);
13909 h = Math.min(Math.max(mh, h), mxh);
13917 if (pos == 'hdrag') {
13920 this.proxy.setBounds(x, y, w, h);
13922 this.resizeElement();
13926 this.fireEvent("resizing", this, x, y, w, h, e);
13930 handleOver : function(){
13932 this.el.addClass("x-resizable-over");
13937 handleOut : function(){
13938 if(!this.resizing){
13939 this.el.removeClass("x-resizable-over");
13944 * Returns the element this component is bound to.
13945 * @return {Roo.Element}
13947 getEl : function(){
13952 * Returns the resizeChild element (or null).
13953 * @return {Roo.Element}
13955 getResizeChild : function(){
13956 return this.resizeChild;
13958 groupHandler : function()
13963 * Destroys this resizable. If the element was wrapped and
13964 * removeEl is not true then the element remains.
13965 * @param {Boolean} removeEl (optional) true to remove the element from the DOM
13967 destroy : function(removeEl){
13968 this.proxy.remove();
13970 this.overlay.removeAllListeners();
13971 this.overlay.remove();
13973 var ps = Roo.Resizable.positions;
13975 if(typeof ps[k] != "function" && this[ps[k]]){
13976 var h = this[ps[k]];
13977 h.el.removeAllListeners();
13982 this.el.update("");
13989 // hash to map config positions to true positions
13990 Roo.Resizable.positions = {
13991 n: "north", s: "south", e: "east", w: "west", se: "southeast", sw: "southwest", nw: "northwest", ne: "northeast",
13996 Roo.Resizable.Handle = function(rz, pos, disableTrackOver, transparent){
13998 // only initialize the template if resizable is used
13999 var tpl = Roo.DomHelper.createTemplate(
14000 {tag: "div", cls: "x-resizable-handle x-resizable-handle-{0}"}
14003 Roo.Resizable.Handle.prototype.tpl = tpl;
14005 this.position = pos;
14007 // show north drag fro topdra
14008 var handlepos = pos == 'hdrag' ? 'north' : pos;
14010 this.el = this.tpl.append(rz.el.dom, [handlepos], true);
14011 if (pos == 'hdrag') {
14012 this.el.setStyle('cursor', 'pointer');
14014 this.el.unselectable();
14016 this.el.setOpacity(0);
14018 this.el.on("mousedown", this.onMouseDown, this);
14019 if(!disableTrackOver){
14020 this.el.on("mouseover", this.onMouseOver, this);
14021 this.el.on("mouseout", this.onMouseOut, this);
14026 Roo.Resizable.Handle.prototype = {
14027 afterResize : function(rz){
14031 onMouseDown : function(e){
14032 this.rz.onMouseDown(this, e);
14035 onMouseOver : function(e){
14036 this.rz.handleOver(this, e);
14039 onMouseOut : function(e){
14040 this.rz.handleOut(this, e);
14044 * Ext JS Library 1.1.1
14045 * Copyright(c) 2006-2007, Ext JS, LLC.
14047 * Originally Released Under LGPL - original licence link has changed is not relivant.
14050 * <script type="text/javascript">
14054 * @class Roo.Editor
14055 * @extends Roo.Component
14056 * A base editor field that handles displaying/hiding on demand and has some built-in sizing and event handling logic.
14058 * Create a new Editor
14059 * @param {Roo.form.Field} field The Field object (or descendant)
14060 * @param {Object} config The config object
14062 Roo.Editor = function(field, config){
14063 Roo.Editor.superclass.constructor.call(this, config);
14064 this.field = field;
14067 * @event beforestartedit
14068 * Fires when editing is initiated, but before the value changes. Editing can be canceled by returning
14069 * false from the handler of this event.
14070 * @param {Editor} this
14071 * @param {Roo.Element} boundEl The underlying element bound to this editor
14072 * @param {Mixed} value The field value being set
14074 "beforestartedit" : true,
14077 * Fires when this editor is displayed
14078 * @param {Roo.Element} boundEl The underlying element bound to this editor
14079 * @param {Mixed} value The starting field value
14081 "startedit" : true,
14083 * @event beforecomplete
14084 * Fires after a change has been made to the field, but before the change is reflected in the underlying
14085 * field. Saving the change to the field can be canceled by returning false from the handler of this event.
14086 * Note that if the value has not changed and ignoreNoChange = true, the editing will still end but this
14087 * event will not fire since no edit actually occurred.
14088 * @param {Editor} this
14089 * @param {Mixed} value The current field value
14090 * @param {Mixed} startValue The original field value
14092 "beforecomplete" : true,
14095 * Fires after editing is complete and any changed value has been written to the underlying field.
14096 * @param {Editor} this
14097 * @param {Mixed} value The current field value
14098 * @param {Mixed} startValue The original field value
14102 * @event specialkey
14103 * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed. You can check
14104 * {@link Roo.EventObject#getKey} to determine which key was pressed.
14105 * @param {Roo.form.Field} this
14106 * @param {Roo.EventObject} e The event object
14108 "specialkey" : true
14112 Roo.extend(Roo.Editor, Roo.Component, {
14114 * @cfg {Boolean/String} autosize
14115 * True for the editor to automatically adopt the size of the underlying field, "width" to adopt the width only,
14116 * or "height" to adopt the height only (defaults to false)
14119 * @cfg {Boolean} revertInvalid
14120 * True to automatically revert the field value and cancel the edit when the user completes an edit and the field
14121 * validation fails (defaults to true)
14124 * @cfg {Boolean} ignoreNoChange
14125 * True to skip the the edit completion process (no save, no events fired) if the user completes an edit and
14126 * the value has not changed (defaults to false). Applies only to string values - edits for other data types
14127 * will never be ignored.
14130 * @cfg {Boolean} hideEl
14131 * False to keep the bound element visible while the editor is displayed (defaults to true)
14134 * @cfg {Mixed} value
14135 * The data value of the underlying field (defaults to "")
14139 * @cfg {String} alignment
14140 * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "c-c?").
14144 * @cfg {Boolean/String} shadow "sides" for sides/bottom only, "frame" for 4-way shadow, and "drop"
14145 * for bottom-right shadow (defaults to "frame")
14149 * @cfg {Boolean} constrain True to constrain the editor to the viewport
14153 * @cfg {Boolean} completeOnEnter True to complete the edit when the enter key is pressed (defaults to false)
14155 completeOnEnter : false,
14157 * @cfg {Boolean} cancelOnEsc True to cancel the edit when the escape key is pressed (defaults to false)
14159 cancelOnEsc : false,
14161 * @cfg {Boolean} updateEl True to update the innerHTML of the bound element when the update completes (defaults to false)
14166 onRender : function(ct, position){
14167 this.el = new Roo.Layer({
14168 shadow: this.shadow,
14174 constrain: this.constrain
14176 this.el.setStyle("overflow", Roo.isGecko ? "auto" : "hidden");
14177 if(this.field.msgTarget != 'title'){
14178 this.field.msgTarget = 'qtip';
14180 this.field.render(this.el);
14182 this.field.el.dom.setAttribute('autocomplete', 'off');
14184 this.field.on("specialkey", this.onSpecialKey, this);
14185 if(this.swallowKeys){
14186 this.field.el.swallowEvent(['keydown','keypress']);
14189 this.field.on("blur", this.onBlur, this);
14190 if(this.field.grow){
14191 this.field.on("autosize", this.el.sync, this.el, {delay:1});
14195 onSpecialKey : function(field, e)
14197 //Roo.log('editor onSpecialKey');
14198 if(this.completeOnEnter && e.getKey() == e.ENTER){
14200 this.completeEdit();
14203 // do not fire special key otherwise it might hide close the editor...
14204 if(e.getKey() == e.ENTER){
14207 if(this.cancelOnEsc && e.getKey() == e.ESC){
14211 this.fireEvent('specialkey', field, e);
14216 * Starts the editing process and shows the editor.
14217 * @param {String/HTMLElement/Element} el The element to edit
14218 * @param {String} value (optional) A value to initialize the editor with. If a value is not provided, it defaults
14219 * to the innerHTML of el.
14221 startEdit : function(el, value){
14223 this.completeEdit();
14225 this.boundEl = Roo.get(el);
14226 var v = value !== undefined ? value : this.boundEl.dom.innerHTML;
14227 if(!this.rendered){
14228 this.render(this.parentEl || document.body);
14230 if(this.fireEvent("beforestartedit", this, this.boundEl, v) === false){
14233 this.startValue = v;
14234 this.field.setValue(v);
14236 var sz = this.boundEl.getSize();
14237 switch(this.autoSize){
14239 this.setSize(sz.width, "");
14242 this.setSize("", sz.height);
14245 this.setSize(sz.width, sz.height);
14248 this.el.alignTo(this.boundEl, this.alignment);
14249 this.editing = true;
14251 Roo.QuickTips.disable();
14257 * Sets the height and width of this editor.
14258 * @param {Number} width The new width
14259 * @param {Number} height The new height
14261 setSize : function(w, h){
14262 this.field.setSize(w, h);
14269 * Realigns the editor to the bound field based on the current alignment config value.
14271 realign : function(){
14272 this.el.alignTo(this.boundEl, this.alignment);
14276 * Ends the editing process, persists the changed value to the underlying field, and hides the editor.
14277 * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after edit (defaults to false)
14279 completeEdit : function(remainVisible){
14283 var v = this.getValue();
14284 if(this.revertInvalid !== false && !this.field.isValid()){
14285 v = this.startValue;
14286 this.cancelEdit(true);
14288 if(String(v) === String(this.startValue) && this.ignoreNoChange){
14289 this.editing = false;
14293 if(this.fireEvent("beforecomplete", this, v, this.startValue) !== false){
14294 this.editing = false;
14295 if(this.updateEl && this.boundEl){
14296 this.boundEl.update(v);
14298 if(remainVisible !== true){
14301 this.fireEvent("complete", this, v, this.startValue);
14306 onShow : function(){
14308 if(this.hideEl !== false){
14309 this.boundEl.hide();
14312 if(Roo.isIE && !this.fixIEFocus){ // IE has problems with focusing the first time
14313 this.fixIEFocus = true;
14314 this.deferredFocus.defer(50, this);
14316 this.field.focus();
14318 this.fireEvent("startedit", this.boundEl, this.startValue);
14321 deferredFocus : function(){
14323 this.field.focus();
14328 * Cancels the editing process and hides the editor without persisting any changes. The field value will be
14329 * reverted to the original starting value.
14330 * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after
14331 * cancel (defaults to false)
14333 cancelEdit : function(remainVisible){
14335 this.setValue(this.startValue);
14336 if(remainVisible !== true){
14343 onBlur : function(){
14344 if(this.allowBlur !== true && this.editing){
14345 this.completeEdit();
14350 onHide : function(){
14352 this.completeEdit();
14356 if(this.field.collapse){
14357 this.field.collapse();
14360 if(this.hideEl !== false){
14361 this.boundEl.show();
14364 Roo.QuickTips.enable();
14369 * Sets the data value of the editor
14370 * @param {Mixed} value Any valid value supported by the underlying field
14372 setValue : function(v){
14373 this.field.setValue(v);
14377 * Gets the data value of the editor
14378 * @return {Mixed} The data value
14380 getValue : function(){
14381 return this.field.getValue();
14385 * Ext JS Library 1.1.1
14386 * Copyright(c) 2006-2007, Ext JS, LLC.
14388 * Originally Released Under LGPL - original licence link has changed is not relivant.
14391 * <script type="text/javascript">
14395 * @class Roo.BasicDialog
14396 * @extends Roo.util.Observable
14397 * Lightweight Dialog Class. The code below shows the creation of a typical dialog using existing HTML markup:
14399 var dlg = new Roo.BasicDialog("my-dlg", {
14408 dlg.addKeyListener(27, dlg.hide, dlg); // ESC can also close the dialog
14409 dlg.addButton('OK', dlg.hide, dlg); // Could call a save function instead of hiding
14410 dlg.addButton('Cancel', dlg.hide, dlg);
14413 <b>A Dialog should always be a direct child of the body element.</b>
14414 * @cfg {Boolean/DomHelper} autoCreate True to auto create from scratch, or using a DomHelper Object (defaults to false)
14415 * @cfg {String} title Default text to display in the title bar (defaults to null)
14416 * @cfg {Number} width Width of the dialog in pixels (can also be set via CSS). Determined by browser if unspecified.
14417 * @cfg {Number} height Height of the dialog in pixels (can also be set via CSS). Determined by browser if unspecified.
14418 * @cfg {Number} x The default left page coordinate of the dialog (defaults to center screen)
14419 * @cfg {Number} y The default top page coordinate of the dialog (defaults to center screen)
14420 * @cfg {String/Element} animateTarget Id or element from which the dialog should animate while opening
14421 * (defaults to null with no animation)
14422 * @cfg {Boolean} resizable False to disable manual dialog resizing (defaults to true)
14423 * @cfg {String} resizeHandles Which resize handles to display - see the {@link Roo.Resizable} handles config
14424 * property for valid values (defaults to 'all')
14425 * @cfg {Number} minHeight The minimum allowable height for a resizable dialog (defaults to 80)
14426 * @cfg {Number} minWidth The minimum allowable width for a resizable dialog (defaults to 200)
14427 * @cfg {Boolean} modal True to show the dialog modally, preventing user interaction with the rest of the page (defaults to false)
14428 * @cfg {Boolean} autoScroll True to allow the dialog body contents to overflow and display scrollbars (defaults to false)
14429 * @cfg {Boolean} closable False to remove the built-in top-right corner close button (defaults to true)
14430 * @cfg {Boolean} collapsible False to remove the built-in top-right corner collapse button (defaults to true)
14431 * @cfg {Boolean} constraintoviewport True to keep the dialog constrained within the visible viewport boundaries (defaults to true)
14432 * @cfg {Boolean} syncHeightBeforeShow True to cause the dimensions to be recalculated before the dialog is shown (defaults to false)
14433 * @cfg {Boolean} draggable False to disable dragging of the dialog within the viewport (defaults to true)
14434 * @cfg {Boolean} autoTabs If true, all elements with class 'x-dlg-tab' will get automatically converted to tabs (defaults to false)
14435 * @cfg {String} tabTag The tag name of tab elements, used when autoTabs = true (defaults to 'div')
14436 * @cfg {Boolean} proxyDrag True to drag a lightweight proxy element rather than the dialog itself, used when
14437 * draggable = true (defaults to false)
14438 * @cfg {Boolean} fixedcenter True to ensure that anytime the dialog is shown or resized it gets centered (defaults to false)
14439 * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
14440 * shadow (defaults to false)
14441 * @cfg {Number} shadowOffset The number of pixels to offset the shadow if displayed (defaults to 5)
14442 * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "right")
14443 * @cfg {Number} minButtonWidth Minimum width of all dialog buttons (defaults to 75)
14444 * @cfg {Array} buttons Array of buttons
14445 * @cfg {Boolean} shim True to create an iframe shim that prevents selects from showing through (defaults to false)
14447 * Create a new BasicDialog.
14448 * @param {String/HTMLElement/Roo.Element} el The container element or DOM node, or its id
14449 * @param {Object} config Configuration options
14451 Roo.BasicDialog = function(el, config){
14452 this.el = Roo.get(el);
14453 var dh = Roo.DomHelper;
14454 if(!this.el && config && config.autoCreate){
14455 if(typeof config.autoCreate == "object"){
14456 if(!config.autoCreate.id){
14457 config.autoCreate.id = el;
14459 this.el = dh.append(document.body,
14460 config.autoCreate, true);
14462 this.el = dh.append(document.body,
14463 {tag: "div", id: el, style:'visibility:hidden;'}, true);
14467 el.setDisplayed(true);
14468 el.hide = this.hideAction;
14470 el.addClass("x-dlg");
14472 Roo.apply(this, config);
14474 this.proxy = el.createProxy("x-dlg-proxy");
14475 this.proxy.hide = this.hideAction;
14476 this.proxy.setOpacity(.5);
14480 el.setWidth(config.width);
14483 el.setHeight(config.height);
14485 this.size = el.getSize();
14486 if(typeof config.x != "undefined" && typeof config.y != "undefined"){
14487 this.xy = [config.x,config.y];
14489 this.xy = el.getCenterXY(true);
14491 /** The header element @type Roo.Element */
14492 this.header = el.child("> .x-dlg-hd");
14493 /** The body element @type Roo.Element */
14494 this.body = el.child("> .x-dlg-bd");
14495 /** The footer element @type Roo.Element */
14496 this.footer = el.child("> .x-dlg-ft");
14499 this.header = el.createChild({tag: "div", cls:"x-dlg-hd", html: " "}, this.body ? this.body.dom : null);
14502 this.body = el.createChild({tag: "div", cls:"x-dlg-bd"});
14505 this.header.unselectable();
14507 this.header.update(this.title);
14509 // this element allows the dialog to be focused for keyboard event
14510 this.focusEl = el.createChild({tag: "a", href:"#", cls:"x-dlg-focus", tabIndex:"-1"});
14511 this.focusEl.swallowEvent("click", true);
14513 this.header.wrap({cls:"x-dlg-hd-right"}).wrap({cls:"x-dlg-hd-left"}, true);
14515 // wrap the body and footer for special rendering
14516 this.bwrap = this.body.wrap({tag: "div", cls:"x-dlg-dlg-body"});
14518 this.bwrap.dom.appendChild(this.footer.dom);
14521 this.bg = this.el.createChild({
14522 tag: "div", cls:"x-dlg-bg",
14523 html: '<div class="x-dlg-bg-left"><div class="x-dlg-bg-right"><div class="x-dlg-bg-center"> </div></div></div>'
14525 this.centerBg = this.bg.child("div.x-dlg-bg-center");
14528 if(this.autoScroll !== false && !this.autoTabs){
14529 this.body.setStyle("overflow", "auto");
14532 this.toolbox = this.el.createChild({cls: "x-dlg-toolbox"});
14534 if(this.closable !== false){
14535 this.el.addClass("x-dlg-closable");
14536 this.close = this.toolbox.createChild({cls:"x-dlg-close"});
14537 this.close.on("click", this.closeClick, this);
14538 this.close.addClassOnOver("x-dlg-close-over");
14540 if(this.collapsible !== false){
14541 this.collapseBtn = this.toolbox.createChild({cls:"x-dlg-collapse"});
14542 this.collapseBtn.on("click", this.collapseClick, this);
14543 this.collapseBtn.addClassOnOver("x-dlg-collapse-over");
14544 this.header.on("dblclick", this.collapseClick, this);
14546 if(this.resizable !== false){
14547 this.el.addClass("x-dlg-resizable");
14548 this.resizer = new Roo.Resizable(el, {
14549 minWidth: this.minWidth || 80,
14550 minHeight:this.minHeight || 80,
14551 handles: this.resizeHandles || "all",
14554 this.resizer.on("beforeresize", this.beforeResize, this);
14555 this.resizer.on("resize", this.onResize, this);
14557 if(this.draggable !== false){
14558 el.addClass("x-dlg-draggable");
14559 if (!this.proxyDrag) {
14560 var dd = new Roo.dd.DD(el.dom.id, "WindowDrag");
14563 var dd = new Roo.dd.DDProxy(el.dom.id, "WindowDrag", {dragElId: this.proxy.id});
14565 dd.setHandleElId(this.header.id);
14566 dd.endDrag = this.endMove.createDelegate(this);
14567 dd.startDrag = this.startMove.createDelegate(this);
14568 dd.onDrag = this.onDrag.createDelegate(this);
14573 this.mask = dh.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
14574 this.mask.enableDisplayMode("block");
14576 this.el.addClass("x-dlg-modal");
14579 this.shadow = new Roo.Shadow({
14580 mode : typeof this.shadow == "string" ? this.shadow : "sides",
14581 offset : this.shadowOffset
14584 this.shadowOffset = 0;
14586 if(Roo.useShims && this.shim !== false){
14587 this.shim = this.el.createShim();
14588 this.shim.hide = this.hideAction;
14596 if (this.buttons) {
14597 var bts= this.buttons;
14599 Roo.each(bts, function(b) {
14608 * Fires when a key is pressed
14609 * @param {Roo.BasicDialog} this
14610 * @param {Roo.EventObject} e
14615 * Fires when this dialog is moved by the user.
14616 * @param {Roo.BasicDialog} this
14617 * @param {Number} x The new page X
14618 * @param {Number} y The new page Y
14623 * Fires when this dialog is resized by the user.
14624 * @param {Roo.BasicDialog} this
14625 * @param {Number} width The new width
14626 * @param {Number} height The new height
14630 * @event beforehide
14631 * Fires before this dialog is hidden.
14632 * @param {Roo.BasicDialog} this
14634 "beforehide" : true,
14637 * Fires when this dialog is hidden.
14638 * @param {Roo.BasicDialog} this
14642 * @event beforeshow
14643 * Fires before this dialog is shown.
14644 * @param {Roo.BasicDialog} this
14646 "beforeshow" : true,
14649 * Fires when this dialog is shown.
14650 * @param {Roo.BasicDialog} this
14654 el.on("keydown", this.onKeyDown, this);
14655 el.on("mousedown", this.toFront, this);
14656 Roo.EventManager.onWindowResize(this.adjustViewport, this, true);
14658 Roo.DialogManager.register(this);
14659 Roo.BasicDialog.superclass.constructor.call(this);
14662 Roo.extend(Roo.BasicDialog, Roo.util.Observable, {
14663 shadowOffset: Roo.isIE ? 6 : 5,
14666 minButtonWidth: 75,
14667 defaultButton: null,
14668 buttonAlign: "right",
14673 * Sets the dialog title text
14674 * @param {String} text The title text to display
14675 * @return {Roo.BasicDialog} this
14677 setTitle : function(text){
14678 this.header.update(text);
14683 closeClick : function(){
14688 collapseClick : function(){
14689 this[this.collapsed ? "expand" : "collapse"]();
14693 * Collapses the dialog to its minimized state (only the title bar is visible).
14694 * Equivalent to the user clicking the collapse dialog button.
14696 collapse : function(){
14697 if(!this.collapsed){
14698 this.collapsed = true;
14699 this.el.addClass("x-dlg-collapsed");
14700 this.restoreHeight = this.el.getHeight();
14701 this.resizeTo(this.el.getWidth(), this.header.getHeight());
14706 * Expands a collapsed dialog back to its normal state. Equivalent to the user
14707 * clicking the expand dialog button.
14709 expand : function(){
14710 if(this.collapsed){
14711 this.collapsed = false;
14712 this.el.removeClass("x-dlg-collapsed");
14713 this.resizeTo(this.el.getWidth(), this.restoreHeight);
14718 * Reinitializes the tabs component, clearing out old tabs and finding new ones.
14719 * @return {Roo.TabPanel} The tabs component
14721 initTabs : function(){
14722 var tabs = this.getTabs();
14723 while(tabs.getTab(0)){
14726 this.el.select(this.tabTag+'.x-dlg-tab').each(function(el){
14728 tabs.addTab(Roo.id(dom), dom.title);
14736 beforeResize : function(){
14737 this.resizer.minHeight = Math.max(this.minHeight, this.getHeaderFooterHeight(true)+40);
14741 onResize : function(){
14742 this.refreshSize();
14743 this.syncBodyHeight();
14744 this.adjustAssets();
14746 this.fireEvent("resize", this, this.size.width, this.size.height);
14750 onKeyDown : function(e){
14751 if(this.isVisible()){
14752 this.fireEvent("keydown", this, e);
14757 * Resizes the dialog.
14758 * @param {Number} width
14759 * @param {Number} height
14760 * @return {Roo.BasicDialog} this
14762 resizeTo : function(width, height){
14763 this.el.setSize(width, height);
14764 this.size = {width: width, height: height};
14765 this.syncBodyHeight();
14766 if(this.fixedcenter){
14769 if(this.isVisible()){
14770 this.constrainXY();
14771 this.adjustAssets();
14773 this.fireEvent("resize", this, width, height);
14779 * Resizes the dialog to fit the specified content size.
14780 * @param {Number} width
14781 * @param {Number} height
14782 * @return {Roo.BasicDialog} this
14784 setContentSize : function(w, h){
14785 h += this.getHeaderFooterHeight() + this.body.getMargins("tb");
14786 w += this.body.getMargins("lr") + this.bwrap.getMargins("lr") + this.centerBg.getPadding("lr");
14787 //if(!this.el.isBorderBox()){
14788 h += this.body.getPadding("tb") + this.bwrap.getBorderWidth("tb") + this.body.getBorderWidth("tb") + this.el.getBorderWidth("tb");
14789 w += this.body.getPadding("lr") + this.bwrap.getBorderWidth("lr") + this.body.getBorderWidth("lr") + this.bwrap.getPadding("lr") + this.el.getBorderWidth("lr");
14792 h += this.tabs.stripWrap.getHeight() + this.tabs.bodyEl.getMargins("tb") + this.tabs.bodyEl.getPadding("tb");
14793 w += this.tabs.bodyEl.getMargins("lr") + this.tabs.bodyEl.getPadding("lr");
14795 this.resizeTo(w, h);
14800 * Adds a key listener for when this dialog is displayed. This allows you to hook in a function that will be
14801 * executed in response to a particular key being pressed while the dialog is active.
14802 * @param {Number/Array/Object} key Either the numeric key code, array of key codes or an object with the following options:
14803 * {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
14804 * @param {Function} fn The function to call
14805 * @param {Object} scope (optional) The scope of the function
14806 * @return {Roo.BasicDialog} this
14808 addKeyListener : function(key, fn, scope){
14809 var keyCode, shift, ctrl, alt;
14810 if(typeof key == "object" && !(key instanceof Array)){
14811 keyCode = key["key"];
14812 shift = key["shift"];
14813 ctrl = key["ctrl"];
14818 var handler = function(dlg, e){
14819 if((!shift || e.shiftKey) && (!ctrl || e.ctrlKey) && (!alt || e.altKey)){
14820 var k = e.getKey();
14821 if(keyCode instanceof Array){
14822 for(var i = 0, len = keyCode.length; i < len; i++){
14823 if(keyCode[i] == k){
14824 fn.call(scope || window, dlg, k, e);
14830 fn.call(scope || window, dlg, k, e);
14835 this.on("keydown", handler);
14840 * Returns the TabPanel component (creates it if it doesn't exist).
14841 * Note: If you wish to simply check for the existence of tabs without creating them,
14842 * check for a null 'tabs' property.
14843 * @return {Roo.TabPanel} The tabs component
14845 getTabs : function(){
14847 this.el.addClass("x-dlg-auto-tabs");
14848 this.body.addClass(this.tabPosition == "bottom" ? "x-tabs-bottom" : "x-tabs-top");
14849 this.tabs = new Roo.TabPanel(this.body.dom, this.tabPosition == "bottom");
14855 * Adds a button to the footer section of the dialog.
14856 * @param {String/Object} config A string becomes the button text, an object can either be a Button config
14857 * object or a valid Roo.DomHelper element config
14858 * @param {Function} handler The function called when the button is clicked
14859 * @param {Object} scope (optional) The scope of the handler function (accepts position as a property)
14860 * @return {Roo.Button} The new button
14862 addButton : function(config, handler, scope){
14863 var dh = Roo.DomHelper;
14865 this.footer = dh.append(this.bwrap, {tag: "div", cls:"x-dlg-ft"}, true);
14867 if(!this.btnContainer){
14868 var tb = this.footer.createChild({
14870 cls:"x-dlg-btns x-dlg-btns-"+this.buttonAlign,
14871 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
14873 this.btnContainer = tb.firstChild.firstChild.firstChild;
14878 minWidth: this.minButtonWidth,
14881 if(typeof config == "string"){
14882 bconfig.text = config;
14885 bconfig.dhconfig = config;
14887 Roo.apply(bconfig, config);
14891 if ((typeof(bconfig.position) != 'undefined') && bconfig.position < this.btnContainer.childNodes.length-1) {
14892 bconfig.position = Math.max(0, bconfig.position);
14893 fc = this.btnContainer.childNodes[bconfig.position];
14896 var btn = new Roo.Button(
14898 this.btnContainer.insertBefore(document.createElement("td"),fc)
14899 : this.btnContainer.appendChild(document.createElement("td")),
14900 //Roo.get(this.btnContainer).createChild( { tag: 'td'}, fc ),
14903 this.syncBodyHeight();
14906 * Array of all the buttons that have been added to this dialog via addButton
14911 this.buttons.push(btn);
14916 * Sets the default button to be focused when the dialog is displayed.
14917 * @param {Roo.BasicDialog.Button} btn The button object returned by {@link #addButton}
14918 * @return {Roo.BasicDialog} this
14920 setDefaultButton : function(btn){
14921 this.defaultButton = btn;
14926 getHeaderFooterHeight : function(safe){
14929 height += this.header.getHeight();
14932 var fm = this.footer.getMargins();
14933 height += (this.footer.getHeight()+fm.top+fm.bottom);
14935 height += this.bwrap.getPadding("tb")+this.bwrap.getBorderWidth("tb");
14936 height += this.centerBg.getPadding("tb");
14941 syncBodyHeight : function()
14943 var bd = this.body, // the text
14944 cb = this.centerBg, // wrapper around bottom.. but does not seem to be used..
14946 var height = this.size.height - this.getHeaderFooterHeight(false);
14947 bd.setHeight(height-bd.getMargins("tb"));
14948 var hh = this.header.getHeight();
14949 var h = this.size.height-hh;
14952 bw.setLeftTop(cb.getPadding("l"), hh+cb.getPadding("t"));
14953 bw.setHeight(h-cb.getPadding("tb"));
14955 bw.setWidth(this.el.getWidth(true)-cb.getPadding("lr"));
14956 bd.setWidth(bw.getWidth(true));
14958 this.tabs.syncHeight();
14960 this.tabs.el.repaint();
14966 * Restores the previous state of the dialog if Roo.state is configured.
14967 * @return {Roo.BasicDialog} this
14969 restoreState : function(){
14970 var box = Roo.state.Manager.get(this.stateId || (this.el.id + "-state"));
14971 if(box && box.width){
14972 this.xy = [box.x, box.y];
14973 this.resizeTo(box.width, box.height);
14979 beforeShow : function(){
14981 if(this.fixedcenter){
14982 this.xy = this.el.getCenterXY(true);
14985 Roo.get(document.body).addClass("x-body-masked");
14986 this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
14989 this.constrainXY();
14993 animShow : function(){
14994 var b = Roo.get(this.animateTarget).getBox();
14995 this.proxy.setSize(b.width, b.height);
14996 this.proxy.setLocation(b.x, b.y);
14998 this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height,
14999 true, .35, this.showEl.createDelegate(this));
15003 * Shows the dialog.
15004 * @param {String/HTMLElement/Roo.Element} animateTarget (optional) Reset the animation target
15005 * @return {Roo.BasicDialog} this
15007 show : function(animateTarget){
15008 if (this.fireEvent("beforeshow", this) === false){
15011 if(this.syncHeightBeforeShow){
15012 this.syncBodyHeight();
15013 }else if(this.firstShow){
15014 this.firstShow = false;
15015 this.syncBodyHeight(); // sync the height on the first show instead of in the constructor
15017 this.animateTarget = animateTarget || this.animateTarget;
15018 if(!this.el.isVisible()){
15020 if(this.animateTarget && Roo.get(this.animateTarget)){
15030 showEl : function(){
15032 this.el.setXY(this.xy);
15034 this.adjustAssets(true);
15037 // IE peekaboo bug - fix found by Dave Fenwick
15041 this.fireEvent("show", this);
15045 * Focuses the dialog. If a defaultButton is set, it will receive focus, otherwise the
15046 * dialog itself will receive focus.
15048 focus : function(){
15049 if(this.defaultButton){
15050 this.defaultButton.focus();
15052 this.focusEl.focus();
15057 constrainXY : function(){
15058 if(this.constraintoviewport !== false){
15059 if(!this.viewSize){
15060 if(this.container){
15061 var s = this.container.getSize();
15062 this.viewSize = [s.width, s.height];
15064 this.viewSize = [Roo.lib.Dom.getViewWidth(),Roo.lib.Dom.getViewHeight()];
15067 var s = Roo.get(this.container||document).getScroll();
15069 var x = this.xy[0], y = this.xy[1];
15070 var w = this.size.width, h = this.size.height;
15071 var vw = this.viewSize[0], vh = this.viewSize[1];
15072 // only move it if it needs it
15074 // first validate right/bottom
15075 if(x + w > vw+s.left){
15079 if(y + h > vh+s.top){
15083 // then make sure top/left isn't negative
15095 if(this.isVisible()){
15096 this.el.setLocation(x, y);
15097 this.adjustAssets();
15104 onDrag : function(){
15105 if(!this.proxyDrag){
15106 this.xy = this.el.getXY();
15107 this.adjustAssets();
15112 adjustAssets : function(doShow){
15113 var x = this.xy[0], y = this.xy[1];
15114 var w = this.size.width, h = this.size.height;
15115 if(doShow === true){
15117 this.shadow.show(this.el);
15123 if(this.shadow && this.shadow.isVisible()){
15124 this.shadow.show(this.el);
15126 if(this.shim && this.shim.isVisible()){
15127 this.shim.setBounds(x, y, w, h);
15132 adjustViewport : function(w, h){
15134 w = Roo.lib.Dom.getViewWidth();
15135 h = Roo.lib.Dom.getViewHeight();
15138 this.viewSize = [w, h];
15139 if(this.modal && this.mask.isVisible()){
15140 this.mask.setSize(w, h); // first make sure the mask isn't causing overflow
15141 this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
15143 if(this.isVisible()){
15144 this.constrainXY();
15149 * Destroys this dialog and all its supporting elements (including any tabs, shim,
15150 * shadow, proxy, mask, etc.) Also removes all event listeners.
15151 * @param {Boolean} removeEl (optional) true to remove the element from the DOM
15153 destroy : function(removeEl){
15154 if(this.isVisible()){
15155 this.animateTarget = null;
15158 Roo.EventManager.removeResizeListener(this.adjustViewport, this);
15160 this.tabs.destroy(removeEl);
15173 for(var i = 0, len = this.buttons.length; i < len; i++){
15174 this.buttons[i].destroy();
15177 this.el.removeAllListeners();
15178 if(removeEl === true){
15179 this.el.update("");
15182 Roo.DialogManager.unregister(this);
15186 startMove : function(){
15187 if(this.proxyDrag){
15190 if(this.constraintoviewport !== false){
15191 this.dd.constrainTo(document.body, {right: this.shadowOffset, bottom: this.shadowOffset});
15196 endMove : function(){
15197 if(!this.proxyDrag){
15198 Roo.dd.DD.prototype.endDrag.apply(this.dd, arguments);
15200 Roo.dd.DDProxy.prototype.endDrag.apply(this.dd, arguments);
15203 this.refreshSize();
15204 this.adjustAssets();
15206 this.fireEvent("move", this, this.xy[0], this.xy[1]);
15210 * Brings this dialog to the front of any other visible dialogs
15211 * @return {Roo.BasicDialog} this
15213 toFront : function(){
15214 Roo.DialogManager.bringToFront(this);
15219 * Sends this dialog to the back (under) of any other visible dialogs
15220 * @return {Roo.BasicDialog} this
15222 toBack : function(){
15223 Roo.DialogManager.sendToBack(this);
15228 * Centers this dialog in the viewport
15229 * @return {Roo.BasicDialog} this
15231 center : function(){
15232 var xy = this.el.getCenterXY(true);
15233 this.moveTo(xy[0], xy[1]);
15238 * Moves the dialog's top-left corner to the specified point
15239 * @param {Number} x
15240 * @param {Number} y
15241 * @return {Roo.BasicDialog} this
15243 moveTo : function(x, y){
15245 if(this.isVisible()){
15246 this.el.setXY(this.xy);
15247 this.adjustAssets();
15253 * Aligns the dialog to the specified element
15254 * @param {String/HTMLElement/Roo.Element} element The element to align to.
15255 * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details).
15256 * @param {Array} offsets (optional) Offset the positioning by [x, y]
15257 * @return {Roo.BasicDialog} this
15259 alignTo : function(element, position, offsets){
15260 this.xy = this.el.getAlignToXY(element, position, offsets);
15261 if(this.isVisible()){
15262 this.el.setXY(this.xy);
15263 this.adjustAssets();
15269 * Anchors an element to another element and realigns it when the window is resized.
15270 * @param {String/HTMLElement/Roo.Element} element The element to align to.
15271 * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details)
15272 * @param {Array} offsets (optional) Offset the positioning by [x, y]
15273 * @param {Boolean/Number} monitorScroll (optional) true to monitor body scroll and reposition. If this parameter
15274 * is a number, it is used as the buffer delay (defaults to 50ms).
15275 * @return {Roo.BasicDialog} this
15277 anchorTo : function(el, alignment, offsets, monitorScroll){
15278 var action = function(){
15279 this.alignTo(el, alignment, offsets);
15281 Roo.EventManager.onWindowResize(action, this);
15282 var tm = typeof monitorScroll;
15283 if(tm != 'undefined'){
15284 Roo.EventManager.on(window, 'scroll', action, this,
15285 {buffer: tm == 'number' ? monitorScroll : 50});
15292 * Returns true if the dialog is visible
15293 * @return {Boolean}
15295 isVisible : function(){
15296 return this.el.isVisible();
15300 animHide : function(callback){
15301 var b = Roo.get(this.animateTarget).getBox();
15303 this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height);
15305 this.proxy.setBounds(b.x, b.y, b.width, b.height, true, .35,
15306 this.hideEl.createDelegate(this, [callback]));
15310 * Hides the dialog.
15311 * @param {Function} callback (optional) Function to call when the dialog is hidden
15312 * @return {Roo.BasicDialog} this
15314 hide : function(callback){
15315 if (this.fireEvent("beforehide", this) === false){
15319 this.shadow.hide();
15324 // sometimes animateTarget seems to get set.. causing problems...
15325 // this just double checks..
15326 if(this.animateTarget && Roo.get(this.animateTarget)) {
15327 this.animHide(callback);
15330 this.hideEl(callback);
15336 hideEl : function(callback){
15340 Roo.get(document.body).removeClass("x-body-masked");
15342 this.fireEvent("hide", this);
15343 if(typeof callback == "function"){
15349 hideAction : function(){
15350 this.setLeft("-10000px");
15351 this.setTop("-10000px");
15352 this.setStyle("visibility", "hidden");
15356 refreshSize : function(){
15357 this.size = this.el.getSize();
15358 this.xy = this.el.getXY();
15359 Roo.state.Manager.set(this.stateId || this.el.id + "-state", this.el.getBox());
15363 // z-index is managed by the DialogManager and may be overwritten at any time
15364 setZIndex : function(index){
15366 this.mask.setStyle("z-index", index);
15369 this.shim.setStyle("z-index", ++index);
15372 this.shadow.setZIndex(++index);
15374 this.el.setStyle("z-index", ++index);
15376 this.proxy.setStyle("z-index", ++index);
15379 this.resizer.proxy.setStyle("z-index", ++index);
15382 this.lastZIndex = index;
15386 * Returns the element for this dialog
15387 * @return {Roo.Element} The underlying dialog Element
15389 getEl : function(){
15395 * @class Roo.DialogManager
15396 * Provides global access to BasicDialogs that have been created and
15397 * support for z-indexing (layering) multiple open dialogs.
15399 Roo.DialogManager = function(){
15401 var accessList = [];
15405 var sortDialogs = function(d1, d2){
15406 return (!d1._lastAccess || d1._lastAccess < d2._lastAccess) ? -1 : 1;
15410 var orderDialogs = function(){
15411 accessList.sort(sortDialogs);
15412 var seed = Roo.DialogManager.zseed;
15413 for(var i = 0, len = accessList.length; i < len; i++){
15414 var dlg = accessList[i];
15416 dlg.setZIndex(seed + (i*10));
15423 * The starting z-index for BasicDialogs (defaults to 9000)
15424 * @type Number The z-index value
15429 register : function(dlg){
15430 list[dlg.id] = dlg;
15431 accessList.push(dlg);
15435 unregister : function(dlg){
15436 delete list[dlg.id];
15439 if(!accessList.indexOf){
15440 for( i = 0, len = accessList.length; i < len; i++){
15441 if(accessList[i] == dlg){
15442 accessList.splice(i, 1);
15447 i = accessList.indexOf(dlg);
15449 accessList.splice(i, 1);
15455 * Gets a registered dialog by id
15456 * @param {String/Object} id The id of the dialog or a dialog
15457 * @return {Roo.BasicDialog} this
15459 get : function(id){
15460 return typeof id == "object" ? id : list[id];
15464 * Brings the specified dialog to the front
15465 * @param {String/Object} dlg The id of the dialog or a dialog
15466 * @return {Roo.BasicDialog} this
15468 bringToFront : function(dlg){
15469 dlg = this.get(dlg);
15472 dlg._lastAccess = new Date().getTime();
15479 * Sends the specified dialog to the back
15480 * @param {String/Object} dlg The id of the dialog or a dialog
15481 * @return {Roo.BasicDialog} this
15483 sendToBack : function(dlg){
15484 dlg = this.get(dlg);
15485 dlg._lastAccess = -(new Date().getTime());
15491 * Hides all dialogs
15493 hideAll : function(){
15494 for(var id in list){
15495 if(list[id] && typeof list[id] != "function" && list[id].isVisible()){
15504 * @class Roo.LayoutDialog
15505 * @extends Roo.BasicDialog
15506 * Dialog which provides adjustments for working with a layout in a Dialog.
15507 * Add your necessary layout config options to the dialog's config.<br>
15508 * Example usage (including a nested layout):
15511 dialog = new Roo.LayoutDialog("download-dlg", {
15520 // layout config merges with the dialog config
15522 tabPosition: "top",
15523 alwaysShowTabs: true
15526 dialog.addKeyListener(27, dialog.hide, dialog);
15527 dialog.setDefaultButton(dialog.addButton("Close", dialog.hide, dialog));
15528 dialog.addButton("Build It!", this.getDownload, this);
15530 // we can even add nested layouts
15531 var innerLayout = new Roo.BorderLayout("dl-inner", {
15541 innerLayout.beginUpdate();
15542 innerLayout.add("east", new Roo.ContentPanel("dl-details"));
15543 innerLayout.add("center", new Roo.ContentPanel("selection-panel"));
15544 innerLayout.endUpdate(true);
15546 var layout = dialog.getLayout();
15547 layout.beginUpdate();
15548 layout.add("center", new Roo.ContentPanel("standard-panel",
15549 {title: "Download the Source", fitToFrame:true}));
15550 layout.add("center", new Roo.NestedLayoutPanel(innerLayout,
15551 {title: "Build your own roo.js"}));
15552 layout.getRegion("center").showPanel(sp);
15553 layout.endUpdate();
15557 * @param {String/HTMLElement/Roo.Element} el The id of or container element, or config
15558 * @param {Object} config configuration options
15560 Roo.LayoutDialog = function(el, cfg){
15563 if (typeof(cfg) == 'undefined') {
15564 config = Roo.apply({}, el);
15565 // not sure why we use documentElement here.. - it should always be body.
15566 // IE7 borks horribly if we use documentElement.
15567 // webkit also does not like documentElement - it creates a body element...
15568 el = Roo.get( document.body || document.documentElement ).createChild();
15569 //config.autoCreate = true;
15573 config.autoTabs = false;
15574 Roo.LayoutDialog.superclass.constructor.call(this, el, config);
15575 this.body.setStyle({overflow:"hidden", position:"relative"});
15576 this.layout = new Roo.BorderLayout(this.body.dom, config);
15577 this.layout.monitorWindowResize = false;
15578 this.el.addClass("x-dlg-auto-layout");
15579 // fix case when center region overwrites center function
15580 this.center = Roo.BasicDialog.prototype.center;
15581 this.on("show", this.layout.layout, this.layout, true);
15582 if (config.items) {
15583 var xitems = config.items;
15584 delete config.items;
15585 Roo.each(xitems, this.addxtype, this);
15590 Roo.extend(Roo.LayoutDialog, Roo.BasicDialog, {
15592 * Ends update of the layout <strike>and resets display to none</strike>. Use standard beginUpdate/endUpdate on the layout.
15595 endUpdate : function(){
15596 this.layout.endUpdate();
15600 * Begins an update of the layout <strike>and sets display to block and visibility to hidden</strike>. Use standard beginUpdate/endUpdate on the layout.
15603 beginUpdate : function(){
15604 this.layout.beginUpdate();
15608 * Get the BorderLayout for this dialog
15609 * @return {Roo.BorderLayout}
15611 getLayout : function(){
15612 return this.layout;
15615 showEl : function(){
15616 Roo.LayoutDialog.superclass.showEl.apply(this, arguments);
15618 this.layout.layout();
15623 // Use the syncHeightBeforeShow config option to control this automatically
15624 syncBodyHeight : function(){
15625 Roo.LayoutDialog.superclass.syncBodyHeight.call(this);
15626 if(this.layout){this.layout.layout();}
15630 * Add an xtype element (actually adds to the layout.)
15631 * @return {Object} xdata xtype object data.
15634 addxtype : function(c) {
15635 return this.layout.addxtype(c);
15639 * Ext JS Library 1.1.1
15640 * Copyright(c) 2006-2007, Ext JS, LLC.
15642 * Originally Released Under LGPL - original licence link has changed is not relivant.
15645 * <script type="text/javascript">
15649 * @class Roo.MessageBox
15650 * Utility class for generating different styles of message boxes. The alias Roo.Msg can also be used.
15654 Roo.Msg.alert('Status', 'Changes saved successfully.');
15656 // Prompt for user data:
15657 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
15659 // process text value...
15663 // Show a dialog using config options:
15665 title:'Save Changes?',
15666 msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
15667 buttons: Roo.Msg.YESNOCANCEL,
15674 Roo.MessageBox = function(){
15675 var dlg, opt, mask, waitTimer;
15676 var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
15677 var buttons, activeTextEl, bwidth;
15680 var handleButton = function(button){
15682 Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
15686 var handleHide = function(){
15687 if(opt && opt.cls){
15688 dlg.el.removeClass(opt.cls);
15691 Roo.TaskMgr.stop(waitTimer);
15697 var updateButtons = function(b){
15700 buttons["ok"].hide();
15701 buttons["cancel"].hide();
15702 buttons["yes"].hide();
15703 buttons["no"].hide();
15704 dlg.footer.dom.style.display = 'none';
15707 dlg.footer.dom.style.display = '';
15708 for(var k in buttons){
15709 if(typeof buttons[k] != "function"){
15712 buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.MessageBox.buttonText[k]);
15713 width += buttons[k].el.getWidth()+15;
15723 var handleEsc = function(d, k, e){
15724 if(opt && opt.closable !== false){
15734 * Returns a reference to the underlying {@link Roo.BasicDialog} element
15735 * @return {Roo.BasicDialog} The BasicDialog element
15737 getDialog : function(){
15739 dlg = new Roo.BasicDialog("x-msg-box", {
15744 constraintoviewport:false,
15746 collapsible : false,
15749 width:400, height:100,
15750 buttonAlign:"center",
15751 closeClick : function(){
15752 if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
15753 handleButton("no");
15755 handleButton("cancel");
15759 dlg.on("hide", handleHide);
15761 dlg.addKeyListener(27, handleEsc);
15763 var bt = this.buttonText;
15764 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
15765 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
15766 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
15767 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
15768 bodyEl = dlg.body.createChild({
15770 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>'
15772 msgEl = bodyEl.dom.firstChild;
15773 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
15774 textboxEl.enableDisplayMode();
15775 textboxEl.addKeyListener([10,13], function(){
15776 if(dlg.isVisible() && opt && opt.buttons){
15777 if(opt.buttons.ok){
15778 handleButton("ok");
15779 }else if(opt.buttons.yes){
15780 handleButton("yes");
15784 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
15785 textareaEl.enableDisplayMode();
15786 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
15787 progressEl.enableDisplayMode();
15788 var pf = progressEl.dom.firstChild;
15790 pp = Roo.get(pf.firstChild);
15791 pp.setHeight(pf.offsetHeight);
15799 * Updates the message box body text
15800 * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
15801 * the XHTML-compliant non-breaking space character '&#160;')
15802 * @return {Roo.MessageBox} This message box
15804 updateText : function(text){
15805 if(!dlg.isVisible() && !opt.width){
15806 dlg.resizeTo(this.maxWidth, 100); // resize first so content is never clipped from previous shows
15808 msgEl.innerHTML = text || ' ';
15810 var cw = Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
15811 //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
15813 Math.min(opt.width || cw , this.maxWidth),
15814 Math.max(opt.minWidth || this.minWidth, bwidth)
15817 activeTextEl.setWidth(w);
15819 if(dlg.isVisible()){
15820 dlg.fixedcenter = false;
15822 // to big, make it scroll. = But as usual stupid IE does not support
15825 if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
15826 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
15827 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
15829 bodyEl.dom.style.height = '';
15830 bodyEl.dom.style.overflowY = '';
15833 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
15835 bodyEl.dom.style.overflowX = '';
15838 dlg.setContentSize(w, bodyEl.getHeight());
15839 if(dlg.isVisible()){
15840 dlg.fixedcenter = true;
15846 * Updates a progress-style message box's text and progress bar. Only relevant on message boxes
15847 * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
15848 * @param {Number} value Any number between 0 and 1 (e.g., .5)
15849 * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
15850 * @return {Roo.MessageBox} This message box
15852 updateProgress : function(value, text){
15854 this.updateText(text);
15856 if (pp) { // weird bug on my firefox - for some reason this is not defined
15857 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
15863 * Returns true if the message box is currently displayed
15864 * @return {Boolean} True if the message box is visible, else false
15866 isVisible : function(){
15867 return dlg && dlg.isVisible();
15871 * Hides the message box if it is displayed
15874 if(this.isVisible()){
15880 * Displays a new message box, or reinitializes an existing message box, based on the config options
15881 * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
15882 * The following config object properties are supported:
15884 Property Type Description
15885 ---------- --------------- ------------------------------------------------------------------------------------
15886 animEl String/Element An id or Element from which the message box should animate as it opens and
15887 closes (defaults to undefined)
15888 buttons Object/Boolean A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
15889 cancel:'Bar'}), or false to not show any buttons (defaults to false)
15890 closable Boolean False to hide the top-right close button (defaults to true). Note that
15891 progress and wait dialogs will ignore this property and always hide the
15892 close button as they can only be closed programmatically.
15893 cls String A custom CSS class to apply to the message box element
15894 defaultTextHeight Number The default height in pixels of the message box's multiline textarea if
15895 displayed (defaults to 75)
15896 fn Function A callback function to execute after closing the dialog. The arguments to the
15897 function will be btn (the name of the button that was clicked, if applicable,
15898 e.g. "ok"), and text (the value of the active text field, if applicable).
15899 Progress and wait dialogs will ignore this option since they do not respond to
15900 user actions and can only be closed programmatically, so any required function
15901 should be called by the same code after it closes the dialog.
15902 icon String A CSS class that provides a background image to be used as an icon for
15903 the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
15904 maxWidth Number The maximum width in pixels of the message box (defaults to 600)
15905 minWidth Number The minimum width in pixels of the message box (defaults to 100)
15906 modal Boolean False to allow user interaction with the page while the message box is
15907 displayed (defaults to true)
15908 msg String A string that will replace the existing message box body text (defaults
15909 to the XHTML-compliant non-breaking space character ' ')
15910 multiline Boolean True to prompt the user to enter multi-line text (defaults to false)
15911 progress Boolean True to display a progress bar (defaults to false)
15912 progressText String The text to display inside the progress bar if progress = true (defaults to '')
15913 prompt Boolean True to prompt the user to enter single-line text (defaults to false)
15914 proxyDrag Boolean True to display a lightweight proxy while dragging (defaults to false)
15915 title String The title text
15916 value String The string value to set into the active textbox element if displayed
15917 wait Boolean True to display a progress bar (defaults to false)
15918 width Number The width of the dialog in pixels
15925 msg: 'Please enter your address:',
15927 buttons: Roo.MessageBox.OKCANCEL,
15930 animEl: 'addAddressBtn'
15933 * @param {Object} config Configuration options
15934 * @return {Roo.MessageBox} This message box
15936 show : function(options)
15939 // this causes nightmares if you show one dialog after another
15940 // especially on callbacks..
15942 if(this.isVisible()){
15945 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
15946 Roo.log("Old Dialog Message:" + msgEl.innerHTML );
15947 Roo.log("New Dialog Message:" + options.msg )
15948 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
15949 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
15952 var d = this.getDialog();
15954 d.setTitle(opt.title || " ");
15955 d.close.setDisplayed(opt.closable !== false);
15956 activeTextEl = textboxEl;
15957 opt.prompt = opt.prompt || (opt.multiline ? true : false);
15962 textareaEl.setHeight(typeof opt.multiline == "number" ?
15963 opt.multiline : this.defaultTextHeight);
15964 activeTextEl = textareaEl;
15973 progressEl.setDisplayed(opt.progress === true);
15974 this.updateProgress(0);
15975 activeTextEl.dom.value = opt.value || "";
15977 dlg.setDefaultButton(activeTextEl);
15979 var bs = opt.buttons;
15982 db = buttons["ok"];
15983 }else if(bs && bs.yes){
15984 db = buttons["yes"];
15986 dlg.setDefaultButton(db);
15988 bwidth = updateButtons(opt.buttons);
15989 this.updateText(opt.msg);
15991 d.el.addClass(opt.cls);
15993 d.proxyDrag = opt.proxyDrag === true;
15994 d.modal = opt.modal !== false;
15995 d.mask = opt.modal !== false ? mask : false;
15996 if(!d.isVisible()){
15997 // force it to the end of the z-index stack so it gets a cursor in FF
15998 document.body.appendChild(dlg.el.dom);
15999 d.animateTarget = null;
16000 d.show(options.animEl);
16006 * Displays a message box with a progress bar. This message box has no buttons and is not closeable by
16007 * the user. You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
16008 * and closing the message box when the process is complete.
16009 * @param {String} title The title bar text
16010 * @param {String} msg The message box body text
16011 * @return {Roo.MessageBox} This message box
16013 progress : function(title, msg){
16020 minWidth: this.minProgressWidth,
16027 * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
16028 * If a callback function is passed it will be called after the user clicks the button, and the
16029 * id of the button that was clicked will be passed as the only parameter to the callback
16030 * (could also be the top-right close button).
16031 * @param {String} title The title bar text
16032 * @param {String} msg The message box body text
16033 * @param {Function} fn (optional) The callback function invoked after the message box is closed
16034 * @param {Object} scope (optional) The scope of the callback function
16035 * @return {Roo.MessageBox} This message box
16037 alert : function(title, msg, fn, scope){
16050 * Displays a message box with an infinitely auto-updating progress bar. This can be used to block user
16051 * interaction while waiting for a long-running process to complete that does not have defined intervals.
16052 * You are responsible for closing the message box when the process is complete.
16053 * @param {String} msg The message box body text
16054 * @param {String} title (optional) The title bar text
16055 * @return {Roo.MessageBox} This message box
16057 wait : function(msg, title){
16068 waitTimer = Roo.TaskMgr.start({
16070 Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
16078 * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
16079 * If a callback function is passed it will be called after the user clicks either button, and the id of the
16080 * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
16081 * @param {String} title The title bar text
16082 * @param {String} msg The message box body text
16083 * @param {Function} fn (optional) The callback function invoked after the message box is closed
16084 * @param {Object} scope (optional) The scope of the callback function
16085 * @return {Roo.MessageBox} This message box
16087 confirm : function(title, msg, fn, scope){
16091 buttons: this.YESNO,
16100 * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
16101 * JavaScript's Window.prompt). The prompt can be a single-line or multi-line textbox. If a callback function
16102 * is passed it will be called after the user clicks either button, and the id of the button that was clicked
16103 * (could also be the top-right close button) and the text that was entered will be passed as the two
16104 * parameters to the callback.
16105 * @param {String} title The title bar text
16106 * @param {String} msg The message box body text
16107 * @param {Function} fn (optional) The callback function invoked after the message box is closed
16108 * @param {Object} scope (optional) The scope of the callback function
16109 * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
16110 * property, or the height in pixels to create the textbox (defaults to false / single-line)
16111 * @return {Roo.MessageBox} This message box
16113 prompt : function(title, msg, fn, scope, multiline){
16117 buttons: this.OKCANCEL,
16122 multiline: multiline,
16129 * Button config that displays a single OK button
16134 * Button config that displays Yes and No buttons
16137 YESNO : {yes:true, no:true},
16139 * Button config that displays OK and Cancel buttons
16142 OKCANCEL : {ok:true, cancel:true},
16144 * Button config that displays Yes, No and Cancel buttons
16147 YESNOCANCEL : {yes:true, no:true, cancel:true},
16150 * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
16153 defaultTextHeight : 75,
16155 * The maximum width in pixels of the message box (defaults to 600)
16160 * The minimum width in pixels of the message box (defaults to 100)
16165 * The minimum width in pixels of the message box if it is a progress-style dialog. This is useful
16166 * for setting a different minimum width than text-only dialogs may need (defaults to 250)
16169 minProgressWidth : 250,
16171 * An object containing the default button text strings that can be overriden for localized language support.
16172 * Supported properties are: ok, cancel, yes and no.
16173 * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
16186 * Shorthand for {@link Roo.MessageBox}
16188 Roo.Msg = Roo.MessageBox;/*
16190 * Ext JS Library 1.1.1
16191 * Copyright(c) 2006-2007, Ext JS, LLC.
16193 * Originally Released Under LGPL - original licence link has changed is not relivant.
16196 * <script type="text/javascript">
16199 * @class Roo.QuickTips
16200 * Provides attractive and customizable tooltips for any element.
16203 Roo.QuickTips = function(){
16204 var el, tipBody, tipBodyText, tipTitle, tm, cfg, close, tagEls = {}, esc, removeCls = null, bdLeft, bdRight;
16205 var ce, bd, xy, dd;
16206 var visible = false, disabled = true, inited = false;
16207 var showProc = 1, hideProc = 1, dismissProc = 1, locks = [];
16209 var onOver = function(e){
16213 var t = e.getTarget();
16214 if(!t || t.nodeType !== 1 || t == document || t == document.body){
16217 if(ce && t == ce.el){
16218 clearTimeout(hideProc);
16221 if(t && tagEls[t.id]){
16222 tagEls[t.id].el = t;
16223 showProc = show.defer(tm.showDelay, tm, [tagEls[t.id]]);
16226 var ttp, et = Roo.fly(t);
16227 var ns = cfg.namespace;
16228 if(tm.interceptTitles && t.title){
16231 t.removeAttribute("title");
16232 e.preventDefault();
16234 ttp = t.qtip || et.getAttributeNS(ns, cfg.attribute);
16237 showProc = show.defer(tm.showDelay, tm, [{
16240 width: et.getAttributeNS(ns, cfg.width),
16241 autoHide: et.getAttributeNS(ns, cfg.hide) != "user",
16242 title: et.getAttributeNS(ns, cfg.title),
16243 cls: et.getAttributeNS(ns, cfg.cls)
16248 var onOut = function(e){
16249 clearTimeout(showProc);
16250 var t = e.getTarget();
16251 if(t && ce && ce.el == t && (tm.autoHide && ce.autoHide !== false)){
16252 hideProc = setTimeout(hide, tm.hideDelay);
16256 var onMove = function(e){
16262 if(tm.trackMouse && ce){
16267 var onDown = function(e){
16268 clearTimeout(showProc);
16269 clearTimeout(hideProc);
16271 if(tm.hideOnClick){
16274 tm.enable.defer(100, tm);
16279 var getPad = function(){
16280 return 2;//bdLeft.getPadding('l')+bdRight.getPadding('r');
16283 var show = function(o){
16287 clearTimeout(dismissProc);
16289 if(removeCls){ // in case manually hidden
16290 el.removeClass(removeCls);
16294 el.addClass(ce.cls);
16295 removeCls = ce.cls;
16298 tipTitle.update(ce.title);
16301 tipTitle.update('');
16304 el.dom.style.width = tm.maxWidth+'px';
16305 //tipBody.dom.style.width = '';
16306 tipBodyText.update(o.text);
16307 var p = getPad(), w = ce.width;
16309 var td = tipBodyText.dom;
16310 var aw = Math.max(td.offsetWidth, td.clientWidth, td.scrollWidth);
16311 if(aw > tm.maxWidth){
16313 }else if(aw < tm.minWidth){
16319 //tipBody.setWidth(w);
16320 el.setWidth(parseInt(w, 10) + p);
16321 if(ce.autoHide === false){
16322 close.setDisplayed(true);
16327 close.setDisplayed(false);
16333 el.avoidY = xy[1]-18;
16338 el.setStyle("visibility", "visible");
16339 el.fadeIn({callback: afterShow});
16345 var afterShow = function(){
16349 if(tm.autoDismiss && ce.autoHide !== false){
16350 dismissProc = setTimeout(hide, tm.autoDismissDelay);
16355 var hide = function(noanim){
16356 clearTimeout(dismissProc);
16357 clearTimeout(hideProc);
16359 if(el.isVisible()){
16361 if(noanim !== true && tm.animate){
16362 el.fadeOut({callback: afterHide});
16369 var afterHide = function(){
16372 el.removeClass(removeCls);
16379 * @cfg {Number} minWidth
16380 * The minimum width of the quick tip (defaults to 40)
16384 * @cfg {Number} maxWidth
16385 * The maximum width of the quick tip (defaults to 300)
16389 * @cfg {Boolean} interceptTitles
16390 * True to automatically use the element's DOM title value if available (defaults to false)
16392 interceptTitles : false,
16394 * @cfg {Boolean} trackMouse
16395 * True to have the quick tip follow the mouse as it moves over the target element (defaults to false)
16397 trackMouse : false,
16399 * @cfg {Boolean} hideOnClick
16400 * True to hide the quick tip if the user clicks anywhere in the document (defaults to true)
16402 hideOnClick : true,
16404 * @cfg {Number} showDelay
16405 * Delay in milliseconds before the quick tip displays after the mouse enters the target element (defaults to 500)
16409 * @cfg {Number} hideDelay
16410 * Delay in milliseconds before the quick tip hides when autoHide = true (defaults to 200)
16414 * @cfg {Boolean} autoHide
16415 * True to automatically hide the quick tip after the mouse exits the target element (defaults to true).
16416 * Used in conjunction with hideDelay.
16421 * True to automatically hide the quick tip after a set period of time, regardless of the user's actions
16422 * (defaults to true). Used in conjunction with autoDismissDelay.
16424 autoDismiss : true,
16427 * Delay in milliseconds before the quick tip hides when autoDismiss = true (defaults to 5000)
16429 autoDismissDelay : 5000,
16431 * @cfg {Boolean} animate
16432 * True to turn on fade animation. Defaults to false (ClearType/scrollbar flicker issues in IE7).
16437 * @cfg {String} title
16438 * Title text to display (defaults to ''). This can be any valid HTML markup.
16442 * @cfg {String} text
16443 * Body text to display (defaults to ''). This can be any valid HTML markup.
16447 * @cfg {String} cls
16448 * A CSS class to apply to the base quick tip element (defaults to '').
16452 * @cfg {Number} width
16453 * Width in pixels of the quick tip (defaults to auto). Width will be ignored if it exceeds the bounds of
16454 * minWidth or maxWidth.
16459 * Initialize and enable QuickTips for first use. This should be called once before the first attempt to access
16460 * or display QuickTips in a page.
16463 tm = Roo.QuickTips;
16464 cfg = tm.tagConfig;
16466 if(!Roo.isReady){ // allow calling of init() before onReady
16467 Roo.onReady(Roo.QuickTips.init, Roo.QuickTips);
16470 el = new Roo.Layer({cls:"x-tip", shadow:"drop", shim: true, constrain:true, shadowOffset:4});
16471 el.fxDefaults = {stopFx: true};
16472 // maximum custom styling
16473 //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>');
16474 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>');
16475 tipTitle = el.child('h3');
16476 tipTitle.enableDisplayMode("block");
16477 tipBody = el.child('div.x-tip-bd');
16478 tipBodyText = el.child('div.x-tip-bd-inner');
16479 //bdLeft = el.child('div.x-tip-bd-left');
16480 //bdRight = el.child('div.x-tip-bd-right');
16481 close = el.child('div.x-tip-close');
16482 close.enableDisplayMode("block");
16483 close.on("click", hide);
16484 var d = Roo.get(document);
16485 d.on("mousedown", onDown);
16486 d.on("mouseover", onOver);
16487 d.on("mouseout", onOut);
16488 d.on("mousemove", onMove);
16489 esc = d.addKeyListener(27, hide);
16492 dd = el.initDD("default", null, {
16493 onDrag : function(){
16497 dd.setHandleElId(tipTitle.id);
16506 * Configures a new quick tip instance and assigns it to a target element. The following config options
16509 Property Type Description
16510 ---------- --------------------- ------------------------------------------------------------------------
16511 target Element/String/Array An Element, id or array of ids that this quick tip should be tied to
16513 * @param {Object} config The config object
16515 register : function(config){
16516 var cs = config instanceof Array ? config : arguments;
16517 for(var i = 0, len = cs.length; i < len; i++) {
16519 var target = c.target;
16521 if(target instanceof Array){
16522 for(var j = 0, jlen = target.length; j < jlen; j++){
16523 tagEls[target[j]] = c;
16526 tagEls[typeof target == 'string' ? target : Roo.id(target)] = c;
16533 * Removes this quick tip from its element and destroys it.
16534 * @param {String/HTMLElement/Element} el The element from which the quick tip is to be removed.
16536 unregister : function(el){
16537 delete tagEls[Roo.id(el)];
16541 * Enable this quick tip.
16543 enable : function(){
16544 if(inited && disabled){
16546 if(locks.length < 1){
16553 * Disable this quick tip.
16555 disable : function(){
16557 clearTimeout(showProc);
16558 clearTimeout(hideProc);
16559 clearTimeout(dismissProc);
16567 * Returns true if the quick tip is enabled, else false.
16569 isEnabled : function(){
16576 attribute : "qtip",
16586 // backwards compat
16587 Roo.QuickTips.tips = Roo.QuickTips.register;/*
16589 * Ext JS Library 1.1.1
16590 * Copyright(c) 2006-2007, Ext JS, LLC.
16592 * Originally Released Under LGPL - original licence link has changed is not relivant.
16595 * <script type="text/javascript">
16600 * @class Roo.tree.TreePanel
16601 * @extends Roo.data.Tree
16603 * @cfg {Boolean} rootVisible false to hide the root node (defaults to true)
16604 * @cfg {Boolean} lines false to disable tree lines (defaults to true)
16605 * @cfg {Boolean} enableDD true to enable drag and drop
16606 * @cfg {Boolean} enableDrag true to enable just drag
16607 * @cfg {Boolean} enableDrop true to enable just drop
16608 * @cfg {Object} dragConfig Custom config to pass to the {@link Roo.tree.TreeDragZone} instance
16609 * @cfg {Object} dropConfig Custom config to pass to the {@link Roo.tree.TreeDropZone} instance
16610 * @cfg {String} ddGroup The DD group this TreePanel belongs to
16611 * @cfg {String} ddAppendOnly True if the tree should only allow append drops (use for trees which are sorted)
16612 * @cfg {Boolean} ddScroll true to enable YUI body scrolling
16613 * @cfg {Boolean} containerScroll true to register this container with ScrollManager
16614 * @cfg {Boolean} hlDrop false to disable node highlight on drop (defaults to the value of Roo.enableFx)
16615 * @cfg {String} hlColor The color of the node highlight (defaults to C3DAF9)
16616 * @cfg {Boolean} animate true to enable animated expand/collapse (defaults to the value of Roo.enableFx)
16617 * @cfg {Boolean} singleExpand true if only 1 node per branch may be expanded
16618 * @cfg {Boolean} selModel A tree selection model to use with this TreePanel (defaults to a {@link Roo.tree.DefaultSelectionModel})
16619 * @cfg {Boolean} loader A TreeLoader for use with this TreePanel
16620 * @cfg {Object|Roo.tree.TreeEditor} editor The TreeEditor or xtype data to display when clicked.
16621 * @cfg {String} pathSeparator The token used to separate sub-paths in path strings (defaults to '/')
16622 * @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>
16623 * @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>
16626 * @param {String/HTMLElement/Element} el The container element
16627 * @param {Object} config
16629 Roo.tree.TreePanel = function(el, config){
16631 var loader = false;
16633 root = config.root;
16634 delete config.root;
16636 if (config.loader) {
16637 loader = config.loader;
16638 delete config.loader;
16641 Roo.apply(this, config);
16642 Roo.tree.TreePanel.superclass.constructor.call(this);
16643 this.el = Roo.get(el);
16644 this.el.addClass('x-tree');
16645 //console.log(root);
16647 this.setRootNode( Roo.factory(root, Roo.tree));
16650 this.loader = Roo.factory(loader, Roo.tree);
16653 * Read-only. The id of the container element becomes this TreePanel's id.
16655 this.id = this.el.id;
16658 * @event beforeload
16659 * Fires before a node is loaded, return false to cancel
16660 * @param {Node} node The node being loaded
16662 "beforeload" : true,
16665 * Fires when a node is loaded
16666 * @param {Node} node The node that was loaded
16670 * @event textchange
16671 * Fires when the text for a node is changed
16672 * @param {Node} node The node
16673 * @param {String} text The new text
16674 * @param {String} oldText The old text
16676 "textchange" : true,
16678 * @event beforeexpand
16679 * Fires before a node is expanded, return false to cancel.
16680 * @param {Node} node The node
16681 * @param {Boolean} deep
16682 * @param {Boolean} anim
16684 "beforeexpand" : true,
16686 * @event beforecollapse
16687 * Fires before a node is collapsed, return false to cancel.
16688 * @param {Node} node The node
16689 * @param {Boolean} deep
16690 * @param {Boolean} anim
16692 "beforecollapse" : true,
16695 * Fires when a node is expanded
16696 * @param {Node} node The node
16700 * @event disabledchange
16701 * Fires when the disabled status of a node changes
16702 * @param {Node} node The node
16703 * @param {Boolean} disabled
16705 "disabledchange" : true,
16708 * Fires when a node is collapsed
16709 * @param {Node} node The node
16713 * @event beforeclick
16714 * Fires before click processing on a node. Return false to cancel the default action.
16715 * @param {Node} node The node
16716 * @param {Roo.EventObject} e The event object
16718 "beforeclick":true,
16720 * @event checkchange
16721 * Fires when a node with a checkbox's checked property changes
16722 * @param {Node} this This node
16723 * @param {Boolean} checked
16725 "checkchange":true,
16728 * Fires when a node is clicked
16729 * @param {Node} node The node
16730 * @param {Roo.EventObject} e The event object
16735 * Fires when a node is double clicked
16736 * @param {Node} node The node
16737 * @param {Roo.EventObject} e The event object
16741 * @event contextmenu
16742 * Fires when a node is right clicked
16743 * @param {Node} node The node
16744 * @param {Roo.EventObject} e The event object
16746 "contextmenu":true,
16748 * @event beforechildrenrendered
16749 * Fires right before the child nodes for a node are rendered
16750 * @param {Node} node The node
16752 "beforechildrenrendered":true,
16755 * Fires when a node starts being dragged
16756 * @param {Roo.tree.TreePanel} this
16757 * @param {Roo.tree.TreeNode} node
16758 * @param {event} e The raw browser event
16760 "startdrag" : true,
16763 * Fires when a drag operation is complete
16764 * @param {Roo.tree.TreePanel} this
16765 * @param {Roo.tree.TreeNode} node
16766 * @param {event} e The raw browser event
16771 * Fires when a dragged node is dropped on a valid DD target
16772 * @param {Roo.tree.TreePanel} this
16773 * @param {Roo.tree.TreeNode} node
16774 * @param {DD} dd The dd it was dropped on
16775 * @param {event} e The raw browser event
16779 * @event beforenodedrop
16780 * Fires when a DD object is dropped on a node in this tree for preprocessing. Return false to cancel the drop. The dropEvent
16781 * passed to handlers has the following properties:<br />
16782 * <ul style="padding:5px;padding-left:16px;">
16783 * <li>tree - The TreePanel</li>
16784 * <li>target - The node being targeted for the drop</li>
16785 * <li>data - The drag data from the drag source</li>
16786 * <li>point - The point of the drop - append, above or below</li>
16787 * <li>source - The drag source</li>
16788 * <li>rawEvent - Raw mouse event</li>
16789 * <li>dropNode - Drop node(s) provided by the source <b>OR</b> you can supply node(s)
16790 * to be inserted by setting them on this object.</li>
16791 * <li>cancel - Set this to true to cancel the drop.</li>
16793 * @param {Object} dropEvent
16795 "beforenodedrop" : true,
16798 * Fires after a DD object is dropped on a node in this tree. The dropEvent
16799 * passed to handlers has the following properties:<br />
16800 * <ul style="padding:5px;padding-left:16px;">
16801 * <li>tree - The TreePanel</li>
16802 * <li>target - The node being targeted for the drop</li>
16803 * <li>data - The drag data from the drag source</li>
16804 * <li>point - The point of the drop - append, above or below</li>
16805 * <li>source - The drag source</li>
16806 * <li>rawEvent - Raw mouse event</li>
16807 * <li>dropNode - Dropped node(s).</li>
16809 * @param {Object} dropEvent
16813 * @event nodedragover
16814 * Fires when a tree node is being targeted for a drag drop, return false to signal drop not allowed. The dragOverEvent
16815 * passed to handlers has the following properties:<br />
16816 * <ul style="padding:5px;padding-left:16px;">
16817 * <li>tree - The TreePanel</li>
16818 * <li>target - The node being targeted for the drop</li>
16819 * <li>data - The drag data from the drag source</li>
16820 * <li>point - The point of the drop - append, above or below</li>
16821 * <li>source - The drag source</li>
16822 * <li>rawEvent - Raw mouse event</li>
16823 * <li>dropNode - Drop node(s) provided by the source.</li>
16824 * <li>cancel - Set this to true to signal drop not allowed.</li>
16826 * @param {Object} dragOverEvent
16828 "nodedragover" : true
16831 if(this.singleExpand){
16832 this.on("beforeexpand", this.restrictExpand, this);
16835 this.editor.tree = this;
16836 this.editor = Roo.factory(this.editor, Roo.tree);
16839 if (this.selModel) {
16840 this.selModel = Roo.factory(this.selModel, Roo.tree);
16844 Roo.extend(Roo.tree.TreePanel, Roo.data.Tree, {
16845 rootVisible : true,
16846 animate: Roo.enableFx,
16849 hlDrop : Roo.enableFx,
16853 rendererTip: false,
16855 restrictExpand : function(node){
16856 var p = node.parentNode;
16858 if(p.expandedChild && p.expandedChild.parentNode == p){
16859 p.expandedChild.collapse();
16861 p.expandedChild = node;
16865 // private override
16866 setRootNode : function(node){
16867 Roo.tree.TreePanel.superclass.setRootNode.call(this, node);
16868 if(!this.rootVisible){
16869 node.ui = new Roo.tree.RootTreeNodeUI(node);
16875 * Returns the container element for this TreePanel
16877 getEl : function(){
16882 * Returns the default TreeLoader for this TreePanel
16884 getLoader : function(){
16885 return this.loader;
16891 expandAll : function(){
16892 this.root.expand(true);
16896 * Collapse all nodes
16898 collapseAll : function(){
16899 this.root.collapse(true);
16903 * Returns the selection model used by this TreePanel
16905 getSelectionModel : function(){
16906 if(!this.selModel){
16907 this.selModel = new Roo.tree.DefaultSelectionModel();
16909 return this.selModel;
16913 * Retrieve an array of checked nodes, or an array of a specific attribute of checked nodes (e.g. "id")
16914 * @param {String} attribute (optional) Defaults to null (return the actual nodes)
16915 * @param {TreeNode} startNode (optional) The node to start from, defaults to the root
16918 getChecked : function(a, startNode){
16919 startNode = startNode || this.root;
16921 var f = function(){
16922 if(this.attributes.checked){
16923 r.push(!a ? this : (a == 'id' ? this.id : this.attributes[a]));
16926 startNode.cascade(f);
16931 * Expands a specified path in this TreePanel. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
16932 * @param {String} path
16933 * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
16934 * @param {Function} callback (optional) The callback to call when the expand is complete. The callback will be called with
16935 * (bSuccess, oLastNode) where bSuccess is if the expand was successful and oLastNode is the last node that was expanded.
16937 expandPath : function(path, attr, callback){
16938 attr = attr || "id";
16939 var keys = path.split(this.pathSeparator);
16940 var curNode = this.root;
16941 if(curNode.attributes[attr] != keys[1]){ // invalid root
16943 callback(false, null);
16948 var f = function(){
16949 if(++index == keys.length){
16951 callback(true, curNode);
16955 var c = curNode.findChild(attr, keys[index]);
16958 callback(false, curNode);
16963 c.expand(false, false, f);
16965 curNode.expand(false, false, f);
16969 * Selects the node in this tree at the specified path. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
16970 * @param {String} path
16971 * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
16972 * @param {Function} callback (optional) The callback to call when the selection is complete. The callback will be called with
16973 * (bSuccess, oSelNode) where bSuccess is if the selection was successful and oSelNode is the selected node.
16975 selectPath : function(path, attr, callback){
16976 attr = attr || "id";
16977 var keys = path.split(this.pathSeparator);
16978 var v = keys.pop();
16979 if(keys.length > 0){
16980 var f = function(success, node){
16981 if(success && node){
16982 var n = node.findChild(attr, v);
16988 }else if(callback){
16989 callback(false, n);
16993 callback(false, n);
16997 this.expandPath(keys.join(this.pathSeparator), attr, f);
16999 this.root.select();
17001 callback(true, this.root);
17006 getTreeEl : function(){
17011 * Trigger rendering of this TreePanel
17013 render : function(){
17014 if (this.innerCt) {
17015 return this; // stop it rendering more than once!!
17018 this.innerCt = this.el.createChild({tag:"ul",
17019 cls:"x-tree-root-ct " +
17020 (this.lines ? "x-tree-lines" : "x-tree-no-lines")});
17022 if(this.containerScroll){
17023 Roo.dd.ScrollManager.register(this.el);
17025 if((this.enableDD || this.enableDrop) && !this.dropZone){
17027 * The dropZone used by this tree if drop is enabled
17028 * @type Roo.tree.TreeDropZone
17030 this.dropZone = new Roo.tree.TreeDropZone(this, this.dropConfig || {
17031 ddGroup: this.ddGroup || "TreeDD", appendOnly: this.ddAppendOnly === true
17034 if((this.enableDD || this.enableDrag) && !this.dragZone){
17036 * The dragZone used by this tree if drag is enabled
17037 * @type Roo.tree.TreeDragZone
17039 this.dragZone = new Roo.tree.TreeDragZone(this, this.dragConfig || {
17040 ddGroup: this.ddGroup || "TreeDD",
17041 scroll: this.ddScroll
17044 this.getSelectionModel().init(this);
17046 Roo.log("ROOT not set in tree");
17049 this.root.render();
17050 if(!this.rootVisible){
17051 this.root.renderChildren();
17057 * Ext JS Library 1.1.1
17058 * Copyright(c) 2006-2007, Ext JS, LLC.
17060 * Originally Released Under LGPL - original licence link has changed is not relivant.
17063 * <script type="text/javascript">
17068 * @class Roo.tree.DefaultSelectionModel
17069 * @extends Roo.util.Observable
17070 * The default single selection for a TreePanel.
17071 * @param {Object} cfg Configuration
17073 Roo.tree.DefaultSelectionModel = function(cfg){
17074 this.selNode = null;
17080 * @event selectionchange
17081 * Fires when the selected node changes
17082 * @param {DefaultSelectionModel} this
17083 * @param {TreeNode} node the new selection
17085 "selectionchange" : true,
17088 * @event beforeselect
17089 * Fires before the selected node changes, return false to cancel the change
17090 * @param {DefaultSelectionModel} this
17091 * @param {TreeNode} node the new selection
17092 * @param {TreeNode} node the old selection
17094 "beforeselect" : true
17097 Roo.tree.DefaultSelectionModel.superclass.constructor.call(this,cfg);
17100 Roo.extend(Roo.tree.DefaultSelectionModel, Roo.util.Observable, {
17101 init : function(tree){
17103 tree.getTreeEl().on("keydown", this.onKeyDown, this);
17104 tree.on("click", this.onNodeClick, this);
17107 onNodeClick : function(node, e){
17108 if (e.ctrlKey && this.selNode == node) {
17109 this.unselect(node);
17117 * @param {TreeNode} node The node to select
17118 * @return {TreeNode} The selected node
17120 select : function(node){
17121 var last = this.selNode;
17122 if(last != node && this.fireEvent('beforeselect', this, node, last) !== false){
17124 last.ui.onSelectedChange(false);
17126 this.selNode = node;
17127 node.ui.onSelectedChange(true);
17128 this.fireEvent("selectionchange", this, node, last);
17135 * @param {TreeNode} node The node to unselect
17137 unselect : function(node){
17138 if(this.selNode == node){
17139 this.clearSelections();
17144 * Clear all selections
17146 clearSelections : function(){
17147 var n = this.selNode;
17149 n.ui.onSelectedChange(false);
17150 this.selNode = null;
17151 this.fireEvent("selectionchange", this, null);
17157 * Get the selected node
17158 * @return {TreeNode} The selected node
17160 getSelectedNode : function(){
17161 return this.selNode;
17165 * Returns true if the node is selected
17166 * @param {TreeNode} node The node to check
17167 * @return {Boolean}
17169 isSelected : function(node){
17170 return this.selNode == node;
17174 * Selects the node above the selected node in the tree, intelligently walking the nodes
17175 * @return TreeNode The new selection
17177 selectPrevious : function(){
17178 var s = this.selNode || this.lastSelNode;
17182 var ps = s.previousSibling;
17184 if(!ps.isExpanded() || ps.childNodes.length < 1){
17185 return this.select(ps);
17187 var lc = ps.lastChild;
17188 while(lc && lc.isExpanded() && lc.childNodes.length > 0){
17191 return this.select(lc);
17193 } else if(s.parentNode && (this.tree.rootVisible || !s.parentNode.isRoot)){
17194 return this.select(s.parentNode);
17200 * Selects the node above the selected node in the tree, intelligently walking the nodes
17201 * @return TreeNode The new selection
17203 selectNext : function(){
17204 var s = this.selNode || this.lastSelNode;
17208 if(s.firstChild && s.isExpanded()){
17209 return this.select(s.firstChild);
17210 }else if(s.nextSibling){
17211 return this.select(s.nextSibling);
17212 }else if(s.parentNode){
17214 s.parentNode.bubble(function(){
17215 if(this.nextSibling){
17216 newS = this.getOwnerTree().selModel.select(this.nextSibling);
17225 onKeyDown : function(e){
17226 var s = this.selNode || this.lastSelNode;
17227 // undesirable, but required
17232 var k = e.getKey();
17240 this.selectPrevious();
17243 e.preventDefault();
17244 if(s.hasChildNodes()){
17245 if(!s.isExpanded()){
17247 }else if(s.firstChild){
17248 this.select(s.firstChild, e);
17253 e.preventDefault();
17254 if(s.hasChildNodes() && s.isExpanded()){
17256 }else if(s.parentNode && (this.tree.rootVisible || s.parentNode != this.tree.getRootNode())){
17257 this.select(s.parentNode, e);
17265 * @class Roo.tree.MultiSelectionModel
17266 * @extends Roo.util.Observable
17267 * Multi selection for a TreePanel.
17268 * @param {Object} cfg Configuration
17270 Roo.tree.MultiSelectionModel = function(){
17271 this.selNodes = [];
17275 * @event selectionchange
17276 * Fires when the selected nodes change
17277 * @param {MultiSelectionModel} this
17278 * @param {Array} nodes Array of the selected nodes
17280 "selectionchange" : true
17282 Roo.tree.MultiSelectionModel.superclass.constructor.call(this,cfg);
17286 Roo.extend(Roo.tree.MultiSelectionModel, Roo.util.Observable, {
17287 init : function(tree){
17289 tree.getTreeEl().on("keydown", this.onKeyDown, this);
17290 tree.on("click", this.onNodeClick, this);
17293 onNodeClick : function(node, e){
17294 this.select(node, e, e.ctrlKey);
17299 * @param {TreeNode} node The node to select
17300 * @param {EventObject} e (optional) An event associated with the selection
17301 * @param {Boolean} keepExisting True to retain existing selections
17302 * @return {TreeNode} The selected node
17304 select : function(node, e, keepExisting){
17305 if(keepExisting !== true){
17306 this.clearSelections(true);
17308 if(this.isSelected(node)){
17309 this.lastSelNode = node;
17312 this.selNodes.push(node);
17313 this.selMap[node.id] = node;
17314 this.lastSelNode = node;
17315 node.ui.onSelectedChange(true);
17316 this.fireEvent("selectionchange", this, this.selNodes);
17322 * @param {TreeNode} node The node to unselect
17324 unselect : function(node){
17325 if(this.selMap[node.id]){
17326 node.ui.onSelectedChange(false);
17327 var sn = this.selNodes;
17330 index = sn.indexOf(node);
17332 for(var i = 0, len = sn.length; i < len; i++){
17340 this.selNodes.splice(index, 1);
17342 delete this.selMap[node.id];
17343 this.fireEvent("selectionchange", this, this.selNodes);
17348 * Clear all selections
17350 clearSelections : function(suppressEvent){
17351 var sn = this.selNodes;
17353 for(var i = 0, len = sn.length; i < len; i++){
17354 sn[i].ui.onSelectedChange(false);
17356 this.selNodes = [];
17358 if(suppressEvent !== true){
17359 this.fireEvent("selectionchange", this, this.selNodes);
17365 * Returns true if the node is selected
17366 * @param {TreeNode} node The node to check
17367 * @return {Boolean}
17369 isSelected : function(node){
17370 return this.selMap[node.id] ? true : false;
17374 * Returns an array of the selected nodes
17377 getSelectedNodes : function(){
17378 return this.selNodes;
17381 onKeyDown : Roo.tree.DefaultSelectionModel.prototype.onKeyDown,
17383 selectNext : Roo.tree.DefaultSelectionModel.prototype.selectNext,
17385 selectPrevious : Roo.tree.DefaultSelectionModel.prototype.selectPrevious
17388 * Ext JS Library 1.1.1
17389 * Copyright(c) 2006-2007, Ext JS, LLC.
17391 * Originally Released Under LGPL - original licence link has changed is not relivant.
17394 * <script type="text/javascript">
17398 * @class Roo.tree.TreeNode
17399 * @extends Roo.data.Node
17400 * @cfg {String} text The text for this node
17401 * @cfg {Boolean} expanded true to start the node expanded
17402 * @cfg {Boolean} allowDrag false to make this node undraggable if DD is on (defaults to true)
17403 * @cfg {Boolean} allowDrop false if this node cannot be drop on
17404 * @cfg {Boolean} disabled true to start the node disabled
17405 * @cfg {String} icon The path to an icon for the node. The preferred way to do this
17406 * is to use the cls or iconCls attributes and add the icon via a CSS background image.
17407 * @cfg {String} cls A css class to be added to the node
17408 * @cfg {String} iconCls A css class to be added to the nodes icon element for applying css background images
17409 * @cfg {String} href URL of the link used for the node (defaults to #)
17410 * @cfg {String} hrefTarget target frame for the link
17411 * @cfg {String} qtip An Ext QuickTip for the node
17412 * @cfg {String} qtipCfg An Ext QuickTip config for the node (used instead of qtip)
17413 * @cfg {Boolean} singleClickExpand True for single click expand on this node
17414 * @cfg {Function} uiProvider A UI <b>class</b> to use for this node (defaults to Roo.tree.TreeNodeUI)
17415 * @cfg {Boolean} checked True to render a checked checkbox for this node, false to render an unchecked checkbox
17416 * (defaults to undefined with no checkbox rendered)
17418 * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node
17420 Roo.tree.TreeNode = function(attributes){
17421 attributes = attributes || {};
17422 if(typeof attributes == "string"){
17423 attributes = {text: attributes};
17425 this.childrenRendered = false;
17426 this.rendered = false;
17427 Roo.tree.TreeNode.superclass.constructor.call(this, attributes);
17428 this.expanded = attributes.expanded === true;
17429 this.isTarget = attributes.isTarget !== false;
17430 this.draggable = attributes.draggable !== false && attributes.allowDrag !== false;
17431 this.allowChildren = attributes.allowChildren !== false && attributes.allowDrop !== false;
17434 * Read-only. The text for this node. To change it use setText().
17437 this.text = attributes.text;
17439 * True if this node is disabled.
17442 this.disabled = attributes.disabled === true;
17446 * @event textchange
17447 * Fires when the text for this node is changed
17448 * @param {Node} this This node
17449 * @param {String} text The new text
17450 * @param {String} oldText The old text
17452 "textchange" : true,
17454 * @event beforeexpand
17455 * Fires before this node is expanded, return false to cancel.
17456 * @param {Node} this This node
17457 * @param {Boolean} deep
17458 * @param {Boolean} anim
17460 "beforeexpand" : true,
17462 * @event beforecollapse
17463 * Fires before this node is collapsed, return false to cancel.
17464 * @param {Node} this This node
17465 * @param {Boolean} deep
17466 * @param {Boolean} anim
17468 "beforecollapse" : true,
17471 * Fires when this node is expanded
17472 * @param {Node} this This node
17476 * @event disabledchange
17477 * Fires when the disabled status of this node changes
17478 * @param {Node} this This node
17479 * @param {Boolean} disabled
17481 "disabledchange" : true,
17484 * Fires when this node is collapsed
17485 * @param {Node} this This node
17489 * @event beforeclick
17490 * Fires before click processing. Return false to cancel the default action.
17491 * @param {Node} this This node
17492 * @param {Roo.EventObject} e The event object
17494 "beforeclick":true,
17496 * @event checkchange
17497 * Fires when a node with a checkbox's checked property changes
17498 * @param {Node} this This node
17499 * @param {Boolean} checked
17501 "checkchange":true,
17504 * Fires when this node is clicked
17505 * @param {Node} this This node
17506 * @param {Roo.EventObject} e The event object
17511 * Fires when this node is double clicked
17512 * @param {Node} this This node
17513 * @param {Roo.EventObject} e The event object
17517 * @event contextmenu
17518 * Fires when this node is right clicked
17519 * @param {Node} this This node
17520 * @param {Roo.EventObject} e The event object
17522 "contextmenu":true,
17524 * @event beforechildrenrendered
17525 * Fires right before the child nodes for this node are rendered
17526 * @param {Node} this This node
17528 "beforechildrenrendered":true
17531 var uiClass = this.attributes.uiProvider || Roo.tree.TreeNodeUI;
17534 * Read-only. The UI for this node
17537 this.ui = new uiClass(this);
17539 // finally support items[]
17540 if (typeof(this.attributes.items) == 'undefined' || !this.attributes.items) {
17545 Roo.each(this.attributes.items, function(c) {
17546 this.appendChild(Roo.factory(c,Roo.Tree));
17548 delete this.attributes.items;
17553 Roo.extend(Roo.tree.TreeNode, Roo.data.Node, {
17554 preventHScroll: true,
17556 * Returns true if this node is expanded
17557 * @return {Boolean}
17559 isExpanded : function(){
17560 return this.expanded;
17564 * Returns the UI object for this node
17565 * @return {TreeNodeUI}
17567 getUI : function(){
17571 // private override
17572 setFirstChild : function(node){
17573 var of = this.firstChild;
17574 Roo.tree.TreeNode.superclass.setFirstChild.call(this, node);
17575 if(this.childrenRendered && of && node != of){
17576 of.renderIndent(true, true);
17579 this.renderIndent(true, true);
17583 // private override
17584 setLastChild : function(node){
17585 var ol = this.lastChild;
17586 Roo.tree.TreeNode.superclass.setLastChild.call(this, node);
17587 if(this.childrenRendered && ol && node != ol){
17588 ol.renderIndent(true, true);
17591 this.renderIndent(true, true);
17595 // these methods are overridden to provide lazy rendering support
17596 // private override
17597 appendChild : function()
17599 var node = Roo.tree.TreeNode.superclass.appendChild.apply(this, arguments);
17600 if(node && this.childrenRendered){
17603 this.ui.updateExpandIcon();
17607 // private override
17608 removeChild : function(node){
17609 this.ownerTree.getSelectionModel().unselect(node);
17610 Roo.tree.TreeNode.superclass.removeChild.apply(this, arguments);
17611 // if it's been rendered remove dom node
17612 if(this.childrenRendered){
17615 if(this.childNodes.length < 1){
17616 this.collapse(false, false);
17618 this.ui.updateExpandIcon();
17620 if(!this.firstChild) {
17621 this.childrenRendered = false;
17626 // private override
17627 insertBefore : function(node, refNode){
17628 var newNode = Roo.tree.TreeNode.superclass.insertBefore.apply(this, arguments);
17629 if(newNode && refNode && this.childrenRendered){
17632 this.ui.updateExpandIcon();
17637 * Sets the text for this node
17638 * @param {String} text
17640 setText : function(text){
17641 var oldText = this.text;
17643 this.attributes.text = text;
17644 if(this.rendered){ // event without subscribing
17645 this.ui.onTextChange(this, text, oldText);
17647 this.fireEvent("textchange", this, text, oldText);
17651 * Triggers selection of this node
17653 select : function(){
17654 this.getOwnerTree().getSelectionModel().select(this);
17658 * Triggers deselection of this node
17660 unselect : function(){
17661 this.getOwnerTree().getSelectionModel().unselect(this);
17665 * Returns true if this node is selected
17666 * @return {Boolean}
17668 isSelected : function(){
17669 return this.getOwnerTree().getSelectionModel().isSelected(this);
17673 * Expand this node.
17674 * @param {Boolean} deep (optional) True to expand all children as well
17675 * @param {Boolean} anim (optional) false to cancel the default animation
17676 * @param {Function} callback (optional) A callback to be called when
17677 * expanding this node completes (does not wait for deep expand to complete).
17678 * Called with 1 parameter, this node.
17680 expand : function(deep, anim, callback){
17681 if(!this.expanded){
17682 if(this.fireEvent("beforeexpand", this, deep, anim) === false){
17685 if(!this.childrenRendered){
17686 this.renderChildren();
17688 this.expanded = true;
17689 if(!this.isHiddenRoot() && (this.getOwnerTree().animate && anim !== false) || anim){
17690 this.ui.animExpand(function(){
17691 this.fireEvent("expand", this);
17692 if(typeof callback == "function"){
17696 this.expandChildNodes(true);
17698 }.createDelegate(this));
17702 this.fireEvent("expand", this);
17703 if(typeof callback == "function"){
17708 if(typeof callback == "function"){
17713 this.expandChildNodes(true);
17717 isHiddenRoot : function(){
17718 return this.isRoot && !this.getOwnerTree().rootVisible;
17722 * Collapse this node.
17723 * @param {Boolean} deep (optional) True to collapse all children as well
17724 * @param {Boolean} anim (optional) false to cancel the default animation
17726 collapse : function(deep, anim){
17727 if(this.expanded && !this.isHiddenRoot()){
17728 if(this.fireEvent("beforecollapse", this, deep, anim) === false){
17731 this.expanded = false;
17732 if((this.getOwnerTree().animate && anim !== false) || anim){
17733 this.ui.animCollapse(function(){
17734 this.fireEvent("collapse", this);
17736 this.collapseChildNodes(true);
17738 }.createDelegate(this));
17741 this.ui.collapse();
17742 this.fireEvent("collapse", this);
17746 var cs = this.childNodes;
17747 for(var i = 0, len = cs.length; i < len; i++) {
17748 cs[i].collapse(true, false);
17754 delayedExpand : function(delay){
17755 if(!this.expandProcId){
17756 this.expandProcId = this.expand.defer(delay, this);
17761 cancelExpand : function(){
17762 if(this.expandProcId){
17763 clearTimeout(this.expandProcId);
17765 this.expandProcId = false;
17769 * Toggles expanded/collapsed state of the node
17771 toggle : function(){
17780 * Ensures all parent nodes are expanded
17782 ensureVisible : function(callback){
17783 var tree = this.getOwnerTree();
17784 tree.expandPath(this.parentNode.getPath(), false, function(){
17785 tree.getTreeEl().scrollChildIntoView(this.ui.anchor);
17786 Roo.callback(callback);
17787 }.createDelegate(this));
17791 * Expand all child nodes
17792 * @param {Boolean} deep (optional) true if the child nodes should also expand their child nodes
17794 expandChildNodes : function(deep){
17795 var cs = this.childNodes;
17796 for(var i = 0, len = cs.length; i < len; i++) {
17797 cs[i].expand(deep);
17802 * Collapse all child nodes
17803 * @param {Boolean} deep (optional) true if the child nodes should also collapse their child nodes
17805 collapseChildNodes : function(deep){
17806 var cs = this.childNodes;
17807 for(var i = 0, len = cs.length; i < len; i++) {
17808 cs[i].collapse(deep);
17813 * Disables this node
17815 disable : function(){
17816 this.disabled = true;
17818 if(this.rendered && this.ui.onDisableChange){ // event without subscribing
17819 this.ui.onDisableChange(this, true);
17821 this.fireEvent("disabledchange", this, true);
17825 * Enables this node
17827 enable : function(){
17828 this.disabled = false;
17829 if(this.rendered && this.ui.onDisableChange){ // event without subscribing
17830 this.ui.onDisableChange(this, false);
17832 this.fireEvent("disabledchange", this, false);
17836 renderChildren : function(suppressEvent){
17837 if(suppressEvent !== false){
17838 this.fireEvent("beforechildrenrendered", this);
17840 var cs = this.childNodes;
17841 for(var i = 0, len = cs.length; i < len; i++){
17842 cs[i].render(true);
17844 this.childrenRendered = true;
17848 sort : function(fn, scope){
17849 Roo.tree.TreeNode.superclass.sort.apply(this, arguments);
17850 if(this.childrenRendered){
17851 var cs = this.childNodes;
17852 for(var i = 0, len = cs.length; i < len; i++){
17853 cs[i].render(true);
17859 render : function(bulkRender){
17860 this.ui.render(bulkRender);
17861 if(!this.rendered){
17862 this.rendered = true;
17864 this.expanded = false;
17865 this.expand(false, false);
17871 renderIndent : function(deep, refresh){
17873 this.ui.childIndent = null;
17875 this.ui.renderIndent();
17876 if(deep === true && this.childrenRendered){
17877 var cs = this.childNodes;
17878 for(var i = 0, len = cs.length; i < len; i++){
17879 cs[i].renderIndent(true, refresh);
17885 * Ext JS Library 1.1.1
17886 * Copyright(c) 2006-2007, Ext JS, LLC.
17888 * Originally Released Under LGPL - original licence link has changed is not relivant.
17891 * <script type="text/javascript">
17895 * @class Roo.tree.AsyncTreeNode
17896 * @extends Roo.tree.TreeNode
17897 * @cfg {TreeLoader} loader A TreeLoader to be used by this node (defaults to the loader defined on the tree)
17899 * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node
17901 Roo.tree.AsyncTreeNode = function(config){
17902 this.loaded = false;
17903 this.loading = false;
17904 Roo.tree.AsyncTreeNode.superclass.constructor.apply(this, arguments);
17906 * @event beforeload
17907 * Fires before this node is loaded, return false to cancel
17908 * @param {Node} this This node
17910 this.addEvents({'beforeload':true, 'load': true});
17913 * Fires when this node is loaded
17914 * @param {Node} this This node
17917 * The loader used by this node (defaults to using the tree's defined loader)
17922 Roo.extend(Roo.tree.AsyncTreeNode, Roo.tree.TreeNode, {
17923 expand : function(deep, anim, callback){
17924 if(this.loading){ // if an async load is already running, waiting til it's done
17926 var f = function(){
17927 if(!this.loading){ // done loading
17928 clearInterval(timer);
17929 this.expand(deep, anim, callback);
17931 }.createDelegate(this);
17932 timer = setInterval(f, 200);
17936 if(this.fireEvent("beforeload", this) === false){
17939 this.loading = true;
17940 this.ui.beforeLoad(this);
17941 var loader = this.loader || this.attributes.loader || this.getOwnerTree().getLoader();
17943 loader.load(this, this.loadComplete.createDelegate(this, [deep, anim, callback]));
17947 Roo.tree.AsyncTreeNode.superclass.expand.call(this, deep, anim, callback);
17951 * Returns true if this node is currently loading
17952 * @return {Boolean}
17954 isLoading : function(){
17955 return this.loading;
17958 loadComplete : function(deep, anim, callback){
17959 this.loading = false;
17960 this.loaded = true;
17961 this.ui.afterLoad(this);
17962 this.fireEvent("load", this);
17963 this.expand(deep, anim, callback);
17967 * Returns true if this node has been loaded
17968 * @return {Boolean}
17970 isLoaded : function(){
17971 return this.loaded;
17974 hasChildNodes : function(){
17975 if(!this.isLeaf() && !this.loaded){
17978 return Roo.tree.AsyncTreeNode.superclass.hasChildNodes.call(this);
17983 * Trigger a reload for this node
17984 * @param {Function} callback
17986 reload : function(callback){
17987 this.collapse(false, false);
17988 while(this.firstChild){
17989 this.removeChild(this.firstChild);
17991 this.childrenRendered = false;
17992 this.loaded = false;
17993 if(this.isHiddenRoot()){
17994 this.expanded = false;
17996 this.expand(false, false, callback);
18000 * Ext JS Library 1.1.1
18001 * Copyright(c) 2006-2007, Ext JS, LLC.
18003 * Originally Released Under LGPL - original licence link has changed is not relivant.
18006 * <script type="text/javascript">
18010 * @class Roo.tree.TreeNodeUI
18012 * @param {Object} node The node to render
18013 * The TreeNode UI implementation is separate from the
18014 * tree implementation. Unless you are customizing the tree UI,
18015 * you should never have to use this directly.
18017 Roo.tree.TreeNodeUI = function(node){
18019 this.rendered = false;
18020 this.animating = false;
18021 this.emptyIcon = Roo.BLANK_IMAGE_URL;
18024 Roo.tree.TreeNodeUI.prototype = {
18025 removeChild : function(node){
18027 this.ctNode.removeChild(node.ui.getEl());
18031 beforeLoad : function(){
18032 this.addClass("x-tree-node-loading");
18035 afterLoad : function(){
18036 this.removeClass("x-tree-node-loading");
18039 onTextChange : function(node, text, oldText){
18041 this.textNode.innerHTML = text;
18045 onDisableChange : function(node, state){
18046 this.disabled = state;
18048 this.addClass("x-tree-node-disabled");
18050 this.removeClass("x-tree-node-disabled");
18054 onSelectedChange : function(state){
18057 this.addClass("x-tree-selected");
18060 this.removeClass("x-tree-selected");
18064 onMove : function(tree, node, oldParent, newParent, index, refNode){
18065 this.childIndent = null;
18067 var targetNode = newParent.ui.getContainer();
18068 if(!targetNode){//target not rendered
18069 this.holder = document.createElement("div");
18070 this.holder.appendChild(this.wrap);
18073 var insertBefore = refNode ? refNode.ui.getEl() : null;
18075 targetNode.insertBefore(this.wrap, insertBefore);
18077 targetNode.appendChild(this.wrap);
18079 this.node.renderIndent(true);
18083 addClass : function(cls){
18085 Roo.fly(this.elNode).addClass(cls);
18089 removeClass : function(cls){
18091 Roo.fly(this.elNode).removeClass(cls);
18095 remove : function(){
18097 this.holder = document.createElement("div");
18098 this.holder.appendChild(this.wrap);
18102 fireEvent : function(){
18103 return this.node.fireEvent.apply(this.node, arguments);
18106 initEvents : function(){
18107 this.node.on("move", this.onMove, this);
18108 var E = Roo.EventManager;
18109 var a = this.anchor;
18111 var el = Roo.fly(a, '_treeui');
18113 if(Roo.isOpera){ // opera render bug ignores the CSS
18114 el.setStyle("text-decoration", "none");
18117 el.on("click", this.onClick, this);
18118 el.on("dblclick", this.onDblClick, this);
18121 Roo.EventManager.on(this.checkbox,
18122 Roo.isIE ? 'click' : 'change', this.onCheckChange, this);
18125 el.on("contextmenu", this.onContextMenu, this);
18127 var icon = Roo.fly(this.iconNode);
18128 icon.on("click", this.onClick, this);
18129 icon.on("dblclick", this.onDblClick, this);
18130 icon.on("contextmenu", this.onContextMenu, this);
18131 E.on(this.ecNode, "click", this.ecClick, this, true);
18133 if(this.node.disabled){
18134 this.addClass("x-tree-node-disabled");
18136 if(this.node.hidden){
18137 this.addClass("x-tree-node-disabled");
18139 var ot = this.node.getOwnerTree();
18140 var dd = ot.enableDD || ot.enableDrag || ot.enableDrop;
18141 if(dd && (!this.node.isRoot || ot.rootVisible)){
18142 Roo.dd.Registry.register(this.elNode, {
18144 handles: this.getDDHandles(),
18150 getDDHandles : function(){
18151 return [this.iconNode, this.textNode];
18156 this.wrap.style.display = "none";
18162 this.wrap.style.display = "";
18166 onContextMenu : function(e){
18167 if (this.node.hasListener("contextmenu") || this.node.getOwnerTree().hasListener("contextmenu")) {
18168 e.preventDefault();
18170 this.fireEvent("contextmenu", this.node, e);
18174 onClick : function(e){
18179 if(this.fireEvent("beforeclick", this.node, e) !== false){
18180 if(!this.disabled && this.node.attributes.href){
18181 this.fireEvent("click", this.node, e);
18184 e.preventDefault();
18189 if(this.node.attributes.singleClickExpand && !this.animating && this.node.hasChildNodes()){
18190 this.node.toggle();
18193 this.fireEvent("click", this.node, e);
18199 onDblClick : function(e){
18200 e.preventDefault();
18205 this.toggleCheck();
18207 if(!this.animating && this.node.hasChildNodes()){
18208 this.node.toggle();
18210 this.fireEvent("dblclick", this.node, e);
18213 onCheckChange : function(){
18214 var checked = this.checkbox.checked;
18215 this.node.attributes.checked = checked;
18216 this.fireEvent('checkchange', this.node, checked);
18219 ecClick : function(e){
18220 if(!this.animating && this.node.hasChildNodes()){
18221 this.node.toggle();
18225 startDrop : function(){
18226 this.dropping = true;
18229 // delayed drop so the click event doesn't get fired on a drop
18230 endDrop : function(){
18231 setTimeout(function(){
18232 this.dropping = false;
18233 }.createDelegate(this), 50);
18236 expand : function(){
18237 this.updateExpandIcon();
18238 this.ctNode.style.display = "";
18241 focus : function(){
18242 if(!this.node.preventHScroll){
18243 try{this.anchor.focus();
18245 }else if(!Roo.isIE){
18247 var noscroll = this.node.getOwnerTree().getTreeEl().dom;
18248 var l = noscroll.scrollLeft;
18249 this.anchor.focus();
18250 noscroll.scrollLeft = l;
18255 toggleCheck : function(value){
18256 var cb = this.checkbox;
18258 cb.checked = (value === undefined ? !cb.checked : value);
18264 this.anchor.blur();
18268 animExpand : function(callback){
18269 var ct = Roo.get(this.ctNode);
18271 if(!this.node.hasChildNodes()){
18272 this.updateExpandIcon();
18273 this.ctNode.style.display = "";
18274 Roo.callback(callback);
18277 this.animating = true;
18278 this.updateExpandIcon();
18281 callback : function(){
18282 this.animating = false;
18283 Roo.callback(callback);
18286 duration: this.node.ownerTree.duration || .25
18290 highlight : function(){
18291 var tree = this.node.getOwnerTree();
18292 Roo.fly(this.wrap).highlight(
18293 tree.hlColor || "C3DAF9",
18294 {endColor: tree.hlBaseColor}
18298 collapse : function(){
18299 this.updateExpandIcon();
18300 this.ctNode.style.display = "none";
18303 animCollapse : function(callback){
18304 var ct = Roo.get(this.ctNode);
18305 ct.enableDisplayMode('block');
18308 this.animating = true;
18309 this.updateExpandIcon();
18312 callback : function(){
18313 this.animating = false;
18314 Roo.callback(callback);
18317 duration: this.node.ownerTree.duration || .25
18321 getContainer : function(){
18322 return this.ctNode;
18325 getEl : function(){
18329 appendDDGhost : function(ghostNode){
18330 ghostNode.appendChild(this.elNode.cloneNode(true));
18333 getDDRepairXY : function(){
18334 return Roo.lib.Dom.getXY(this.iconNode);
18337 onRender : function(){
18341 render : function(bulkRender){
18342 var n = this.node, a = n.attributes;
18343 var targetNode = n.parentNode ?
18344 n.parentNode.ui.getContainer() : n.ownerTree.innerCt.dom;
18346 if(!this.rendered){
18347 this.rendered = true;
18349 this.renderElements(n, a, targetNode, bulkRender);
18352 if(this.textNode.setAttributeNS){
18353 this.textNode.setAttributeNS("ext", "qtip", a.qtip);
18355 this.textNode.setAttributeNS("ext", "qtitle", a.qtipTitle);
18358 this.textNode.setAttribute("ext:qtip", a.qtip);
18360 this.textNode.setAttribute("ext:qtitle", a.qtipTitle);
18363 }else if(a.qtipCfg){
18364 a.qtipCfg.target = Roo.id(this.textNode);
18365 Roo.QuickTips.register(a.qtipCfg);
18368 if(!this.node.expanded){
18369 this.updateExpandIcon();
18372 if(bulkRender === true) {
18373 targetNode.appendChild(this.wrap);
18378 renderElements : function(n, a, targetNode, bulkRender)
18380 // add some indent caching, this helps performance when rendering a large tree
18381 this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
18382 var t = n.getOwnerTree();
18383 var txt = t.renderer ? t.renderer(n.attributes) : Roo.util.Format.htmlEncode(n.text);
18384 if (typeof(n.attributes.html) != 'undefined') {
18385 txt = n.attributes.html;
18387 var tip = t.rendererTip ? t.rendererTip(n.attributes) : txt;
18388 var cb = typeof a.checked == 'boolean';
18389 var href = a.href ? a.href : Roo.isGecko ? "" : "#";
18390 var buf = ['<li class="x-tree-node"><div class="x-tree-node-el ', a.cls,'">',
18391 '<span class="x-tree-node-indent">',this.indentMarkup,"</span>",
18392 '<img src="', this.emptyIcon, '" class="x-tree-ec-icon" />',
18393 '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',(a.icon ? " x-tree-node-inline-icon" : ""),(a.iconCls ? " "+a.iconCls : ""),'" unselectable="on" />',
18394 cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + (a.checked ? 'checked="checked" />' : ' />')) : '',
18395 '<a hidefocus="on" href="',href,'" tabIndex="1" ',
18396 a.hrefTarget ? ' target="'+a.hrefTarget+'"' : "",
18397 '><span unselectable="on" qtip="' , tip ,'">',txt,"</span></a></div>",
18398 '<ul class="x-tree-node-ct" style="display:none;"></ul>',
18401 if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
18402 this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
18403 n.nextSibling.ui.getEl(), buf.join(""));
18405 this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
18408 this.elNode = this.wrap.childNodes[0];
18409 this.ctNode = this.wrap.childNodes[1];
18410 var cs = this.elNode.childNodes;
18411 this.indentNode = cs[0];
18412 this.ecNode = cs[1];
18413 this.iconNode = cs[2];
18416 this.checkbox = cs[3];
18419 this.anchor = cs[index];
18420 this.textNode = cs[index].firstChild;
18423 getAnchor : function(){
18424 return this.anchor;
18427 getTextEl : function(){
18428 return this.textNode;
18431 getIconEl : function(){
18432 return this.iconNode;
18435 isChecked : function(){
18436 return this.checkbox ? this.checkbox.checked : false;
18439 updateExpandIcon : function(){
18441 var n = this.node, c1, c2;
18442 var cls = n.isLast() ? "x-tree-elbow-end" : "x-tree-elbow";
18443 var hasChild = n.hasChildNodes();
18447 c1 = "x-tree-node-collapsed";
18448 c2 = "x-tree-node-expanded";
18451 c1 = "x-tree-node-expanded";
18452 c2 = "x-tree-node-collapsed";
18455 this.removeClass("x-tree-node-leaf");
18456 this.wasLeaf = false;
18458 if(this.c1 != c1 || this.c2 != c2){
18459 Roo.fly(this.elNode).replaceClass(c1, c2);
18460 this.c1 = c1; this.c2 = c2;
18463 // this changes non-leafs into leafs if they have no children.
18464 // it's not very rational behaviour..
18466 if(!this.wasLeaf && this.node.leaf){
18467 Roo.fly(this.elNode).replaceClass("x-tree-node-expanded", "x-tree-node-leaf");
18470 this.wasLeaf = true;
18473 var ecc = "x-tree-ec-icon "+cls;
18474 if(this.ecc != ecc){
18475 this.ecNode.className = ecc;
18481 getChildIndent : function(){
18482 if(!this.childIndent){
18486 if(!p.isRoot || (p.isRoot && p.ownerTree.rootVisible)){
18488 buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-elbow-line" />');
18490 buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-icon" />');
18495 this.childIndent = buf.join("");
18497 return this.childIndent;
18500 renderIndent : function(){
18503 var p = this.node.parentNode;
18505 indent = p.ui.getChildIndent();
18507 if(this.indentMarkup != indent){ // don't rerender if not required
18508 this.indentNode.innerHTML = indent;
18509 this.indentMarkup = indent;
18511 this.updateExpandIcon();
18516 Roo.tree.RootTreeNodeUI = function(){
18517 Roo.tree.RootTreeNodeUI.superclass.constructor.apply(this, arguments);
18519 Roo.extend(Roo.tree.RootTreeNodeUI, Roo.tree.TreeNodeUI, {
18520 render : function(){
18521 if(!this.rendered){
18522 var targetNode = this.node.ownerTree.innerCt.dom;
18523 this.node.expanded = true;
18524 targetNode.innerHTML = '<div class="x-tree-root-node"></div>';
18525 this.wrap = this.ctNode = targetNode.firstChild;
18528 collapse : function(){
18530 expand : function(){
18534 * Ext JS Library 1.1.1
18535 * Copyright(c) 2006-2007, Ext JS, LLC.
18537 * Originally Released Under LGPL - original licence link has changed is not relivant.
18540 * <script type="text/javascript">
18543 * @class Roo.tree.TreeLoader
18544 * @extends Roo.util.Observable
18545 * A TreeLoader provides for lazy loading of an {@link Roo.tree.TreeNode}'s child
18546 * nodes from a specified URL. The response must be a javascript Array definition
18547 * who's elements are node definition objects. eg:
18552 { 'id': 1, 'text': 'A folder Node', 'leaf': false },
18553 { 'id': 2, 'text': 'A leaf Node', 'leaf': true }
18560 * The old style respose with just an array is still supported, but not recommended.
18563 * A server request is sent, and child nodes are loaded only when a node is expanded.
18564 * The loading node's id is passed to the server under the parameter name "node" to
18565 * enable the server to produce the correct child nodes.
18567 * To pass extra parameters, an event handler may be attached to the "beforeload"
18568 * event, and the parameters specified in the TreeLoader's baseParams property:
18570 myTreeLoader.on("beforeload", function(treeLoader, node) {
18571 this.baseParams.category = node.attributes.category;
18574 * This would pass an HTTP parameter called "category" to the server containing
18575 * the value of the Node's "category" attribute.
18577 * Creates a new Treeloader.
18578 * @param {Object} config A config object containing config properties.
18580 Roo.tree.TreeLoader = function(config){
18581 this.baseParams = {};
18582 this.requestMethod = "POST";
18583 Roo.apply(this, config);
18588 * @event beforeload
18589 * Fires before a network request is made to retrieve the Json text which specifies a node's children.
18590 * @param {Object} This TreeLoader object.
18591 * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
18592 * @param {Object} callback The callback function specified in the {@link #load} call.
18597 * Fires when the node has been successfuly loaded.
18598 * @param {Object} This TreeLoader object.
18599 * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
18600 * @param {Object} response The response object containing the data from the server.
18604 * @event loadexception
18605 * Fires if the network request failed.
18606 * @param {Object} This TreeLoader object.
18607 * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
18608 * @param {Object} response The response object containing the data from the server.
18610 loadexception : true,
18613 * Fires before a node is created, enabling you to return custom Node types
18614 * @param {Object} This TreeLoader object.
18615 * @param {Object} attr - the data returned from the AJAX call (modify it to suit)
18620 Roo.tree.TreeLoader.superclass.constructor.call(this);
18623 Roo.extend(Roo.tree.TreeLoader, Roo.util.Observable, {
18625 * @cfg {String} dataUrl The URL from which to request a Json string which
18626 * specifies an array of node definition object representing the child nodes
18630 * @cfg {String} requestMethod either GET or POST
18631 * defaults to POST (due to BC)
18635 * @cfg {Object} baseParams (optional) An object containing properties which
18636 * specify HTTP parameters to be passed to each request for child nodes.
18639 * @cfg {Object} baseAttrs (optional) An object containing attributes to be added to all nodes
18640 * created by this loader. If the attributes sent by the server have an attribute in this object,
18641 * they take priority.
18644 * @cfg {Object} uiProviders (optional) An object containing properties which
18646 * DEPRECATED - use 'create' event handler to modify attributes - which affect creation.
18647 * specify custom {@link Roo.tree.TreeNodeUI} implementations. If the optional
18648 * <i>uiProvider</i> attribute of a returned child node is a string rather
18649 * than a reference to a TreeNodeUI implementation, this that string value
18650 * is used as a property name in the uiProviders object. You can define the provider named
18651 * 'default' , and this will be used for all nodes (if no uiProvider is delivered by the node data)
18656 * @cfg {Boolean} clearOnLoad (optional) Default to true. Remove previously existing
18657 * child nodes before loading.
18659 clearOnLoad : true,
18662 * @cfg {String} root (optional) Default to false. Use this to read data from an object
18663 * property on loading, rather than expecting an array. (eg. more compatible to a standard
18664 * Grid query { data : [ .....] }
18669 * @cfg {String} queryParam (optional)
18670 * Name of the query as it will be passed on the querystring (defaults to 'node')
18671 * eg. the request will be ?node=[id]
18678 * Load an {@link Roo.tree.TreeNode} from the URL specified in the constructor.
18679 * This is called automatically when a node is expanded, but may be used to reload
18680 * a node (or append new children if the {@link #clearOnLoad} option is false.)
18681 * @param {Roo.tree.TreeNode} node
18682 * @param {Function} callback
18684 load : function(node, callback){
18685 if(this.clearOnLoad){
18686 while(node.firstChild){
18687 node.removeChild(node.firstChild);
18690 if(node.attributes.children){ // preloaded json children
18691 var cs = node.attributes.children;
18692 for(var i = 0, len = cs.length; i < len; i++){
18693 node.appendChild(this.createNode(cs[i]));
18695 if(typeof callback == "function"){
18698 }else if(this.dataUrl){
18699 this.requestData(node, callback);
18703 getParams: function(node){
18704 var buf = [], bp = this.baseParams;
18705 for(var key in bp){
18706 if(typeof bp[key] != "function"){
18707 buf.push(encodeURIComponent(key), "=", encodeURIComponent(bp[key]), "&");
18710 var n = this.queryParam === false ? 'node' : this.queryParam;
18711 buf.push(n + "=", encodeURIComponent(node.id));
18712 return buf.join("");
18715 requestData : function(node, callback){
18716 if(this.fireEvent("beforeload", this, node, callback) !== false){
18717 this.transId = Roo.Ajax.request({
18718 method:this.requestMethod,
18719 url: this.dataUrl||this.url,
18720 success: this.handleResponse,
18721 failure: this.handleFailure,
18723 argument: {callback: callback, node: node},
18724 params: this.getParams(node)
18727 // if the load is cancelled, make sure we notify
18728 // the node that we are done
18729 if(typeof callback == "function"){
18735 isLoading : function(){
18736 return this.transId ? true : false;
18739 abort : function(){
18740 if(this.isLoading()){
18741 Roo.Ajax.abort(this.transId);
18746 createNode : function(attr)
18748 // apply baseAttrs, nice idea Corey!
18749 if(this.baseAttrs){
18750 Roo.applyIf(attr, this.baseAttrs);
18752 if(this.applyLoader !== false){
18753 attr.loader = this;
18755 // uiProvider = depreciated..
18757 if(typeof(attr.uiProvider) == 'string'){
18758 attr.uiProvider = this.uiProviders[attr.uiProvider] ||
18759 /** eval:var:attr */ eval(attr.uiProvider);
18761 if(typeof(this.uiProviders['default']) != 'undefined') {
18762 attr.uiProvider = this.uiProviders['default'];
18765 this.fireEvent('create', this, attr);
18767 attr.leaf = typeof(attr.leaf) == 'string' ? attr.leaf * 1 : attr.leaf;
18769 new Roo.tree.TreeNode(attr) :
18770 new Roo.tree.AsyncTreeNode(attr));
18773 processResponse : function(response, node, callback)
18775 var json = response.responseText;
18778 var o = Roo.decode(json);
18780 if (this.root === false && typeof(o.success) != undefined) {
18781 this.root = 'data'; // the default behaviour for list like data..
18784 if (this.root !== false && !o.success) {
18785 // it's a failure condition.
18786 var a = response.argument;
18787 this.fireEvent("loadexception", this, a.node, response);
18788 Roo.log("Load failed - should have a handler really");
18794 if (this.root !== false) {
18798 for(var i = 0, len = o.length; i < len; i++){
18799 var n = this.createNode(o[i]);
18801 node.appendChild(n);
18804 if(typeof callback == "function"){
18805 callback(this, node);
18808 this.handleFailure(response);
18812 handleResponse : function(response){
18813 this.transId = false;
18814 var a = response.argument;
18815 this.processResponse(response, a.node, a.callback);
18816 this.fireEvent("load", this, a.node, response);
18819 handleFailure : function(response)
18821 // should handle failure better..
18822 this.transId = false;
18823 var a = response.argument;
18824 this.fireEvent("loadexception", this, a.node, response);
18825 if(typeof a.callback == "function"){
18826 a.callback(this, a.node);
18831 * Ext JS Library 1.1.1
18832 * Copyright(c) 2006-2007, Ext JS, LLC.
18834 * Originally Released Under LGPL - original licence link has changed is not relivant.
18837 * <script type="text/javascript">
18841 * @class Roo.tree.TreeFilter
18842 * Note this class is experimental and doesn't update the indent (lines) or expand collapse icons of the nodes
18843 * @param {TreePanel} tree
18844 * @param {Object} config (optional)
18846 Roo.tree.TreeFilter = function(tree, config){
18848 this.filtered = {};
18849 Roo.apply(this, config);
18852 Roo.tree.TreeFilter.prototype = {
18859 * Filter the data by a specific attribute.
18860 * @param {String/RegExp} value Either string that the attribute value
18861 * should start with or a RegExp to test against the attribute
18862 * @param {String} attr (optional) The attribute passed in your node's attributes collection. Defaults to "text".
18863 * @param {TreeNode} startNode (optional) The node to start the filter at.
18865 filter : function(value, attr, startNode){
18866 attr = attr || "text";
18868 if(typeof value == "string"){
18869 var vlen = value.length;
18870 // auto clear empty filter
18871 if(vlen == 0 && this.clearBlank){
18875 value = value.toLowerCase();
18877 return n.attributes[attr].substr(0, vlen).toLowerCase() == value;
18879 }else if(value.exec){ // regex?
18881 return value.test(n.attributes[attr]);
18884 throw 'Illegal filter type, must be string or regex';
18886 this.filterBy(f, null, startNode);
18890 * Filter by a function. The passed function will be called with each
18891 * node in the tree (or from the startNode). If the function returns true, the node is kept
18892 * otherwise it is filtered. If a node is filtered, its children are also filtered.
18893 * @param {Function} fn The filter function
18894 * @param {Object} scope (optional) The scope of the function (defaults to the current node)
18896 filterBy : function(fn, scope, startNode){
18897 startNode = startNode || this.tree.root;
18898 if(this.autoClear){
18901 var af = this.filtered, rv = this.reverse;
18902 var f = function(n){
18903 if(n == startNode){
18909 var m = fn.call(scope || n, n);
18917 startNode.cascade(f);
18920 if(typeof id != "function"){
18922 if(n && n.parentNode){
18923 n.parentNode.removeChild(n);
18931 * Clears the current filter. Note: with the "remove" option
18932 * set a filter cannot be cleared.
18934 clear : function(){
18936 var af = this.filtered;
18938 if(typeof id != "function"){
18945 this.filtered = {};
18950 * Ext JS Library 1.1.1
18951 * Copyright(c) 2006-2007, Ext JS, LLC.
18953 * Originally Released Under LGPL - original licence link has changed is not relivant.
18956 * <script type="text/javascript">
18961 * @class Roo.tree.TreeSorter
18962 * Provides sorting of nodes in a TreePanel
18964 * @cfg {Boolean} folderSort True to sort leaf nodes under non leaf nodes
18965 * @cfg {String} property The named attribute on the node to sort by (defaults to text)
18966 * @cfg {String} dir The direction to sort (asc or desc) (defaults to asc)
18967 * @cfg {String} leafAttr The attribute used to determine leaf nodes in folder sort (defaults to "leaf")
18968 * @cfg {Boolean} caseSensitive true for case sensitive sort (defaults to false)
18969 * @cfg {Function} sortType A custom "casting" function used to convert node values before sorting
18971 * @param {TreePanel} tree
18972 * @param {Object} config
18974 Roo.tree.TreeSorter = function(tree, config){
18975 Roo.apply(this, config);
18976 tree.on("beforechildrenrendered", this.doSort, this);
18977 tree.on("append", this.updateSort, this);
18978 tree.on("insert", this.updateSort, this);
18980 var dsc = this.dir && this.dir.toLowerCase() == "desc";
18981 var p = this.property || "text";
18982 var sortType = this.sortType;
18983 var fs = this.folderSort;
18984 var cs = this.caseSensitive === true;
18985 var leafAttr = this.leafAttr || 'leaf';
18987 this.sortFn = function(n1, n2){
18989 if(n1.attributes[leafAttr] && !n2.attributes[leafAttr]){
18992 if(!n1.attributes[leafAttr] && n2.attributes[leafAttr]){
18996 var v1 = sortType ? sortType(n1) : (cs ? n1.attributes[p] : n1.attributes[p].toUpperCase());
18997 var v2 = sortType ? sortType(n2) : (cs ? n2.attributes[p] : n2.attributes[p].toUpperCase());
18999 return dsc ? +1 : -1;
19001 return dsc ? -1 : +1;
19008 Roo.tree.TreeSorter.prototype = {
19009 doSort : function(node){
19010 node.sort(this.sortFn);
19013 compareNodes : function(n1, n2){
19014 return (n1.text.toUpperCase() > n2.text.toUpperCase() ? 1 : -1);
19017 updateSort : function(tree, node){
19018 if(node.childrenRendered){
19019 this.doSort.defer(1, this, [node]);
19024 * Ext JS Library 1.1.1
19025 * Copyright(c) 2006-2007, Ext JS, LLC.
19027 * Originally Released Under LGPL - original licence link has changed is not relivant.
19030 * <script type="text/javascript">
19033 if(Roo.dd.DropZone){
19035 Roo.tree.TreeDropZone = function(tree, config){
19036 this.allowParentInsert = false;
19037 this.allowContainerDrop = false;
19038 this.appendOnly = false;
19039 Roo.tree.TreeDropZone.superclass.constructor.call(this, tree.innerCt, config);
19041 this.lastInsertClass = "x-tree-no-status";
19042 this.dragOverData = {};
19045 Roo.extend(Roo.tree.TreeDropZone, Roo.dd.DropZone, {
19046 ddGroup : "TreeDD",
19049 expandDelay : 1000,
19051 expandNode : function(node){
19052 if(node.hasChildNodes() && !node.isExpanded()){
19053 node.expand(false, null, this.triggerCacheRefresh.createDelegate(this));
19057 queueExpand : function(node){
19058 this.expandProcId = this.expandNode.defer(this.expandDelay, this, [node]);
19061 cancelExpand : function(){
19062 if(this.expandProcId){
19063 clearTimeout(this.expandProcId);
19064 this.expandProcId = false;
19068 isValidDropPoint : function(n, pt, dd, e, data){
19069 if(!n || !data){ return false; }
19070 var targetNode = n.node;
19071 var dropNode = data.node;
19072 // default drop rules
19073 if(!(targetNode && targetNode.isTarget && pt)){
19076 if(pt == "append" && targetNode.allowChildren === false){
19079 if((pt == "above" || pt == "below") && (targetNode.parentNode && targetNode.parentNode.allowChildren === false)){
19082 if(dropNode && (targetNode == dropNode || dropNode.contains(targetNode))){
19085 // reuse the object
19086 var overEvent = this.dragOverData;
19087 overEvent.tree = this.tree;
19088 overEvent.target = targetNode;
19089 overEvent.data = data;
19090 overEvent.point = pt;
19091 overEvent.source = dd;
19092 overEvent.rawEvent = e;
19093 overEvent.dropNode = dropNode;
19094 overEvent.cancel = false;
19095 var result = this.tree.fireEvent("nodedragover", overEvent);
19096 return overEvent.cancel === false && result !== false;
19099 getDropPoint : function(e, n, dd)
19103 return tn.allowChildren !== false ? "append" : false; // always append for root
19105 var dragEl = n.ddel;
19106 var t = Roo.lib.Dom.getY(dragEl), b = t + dragEl.offsetHeight;
19107 var y = Roo.lib.Event.getPageY(e);
19108 //var noAppend = tn.allowChildren === false || tn.isLeaf();
19110 // we may drop nodes anywhere, as long as allowChildren has not been set to false..
19111 var noAppend = tn.allowChildren === false;
19112 if(this.appendOnly || tn.parentNode.allowChildren === false){
19113 return noAppend ? false : "append";
19115 var noBelow = false;
19116 if(!this.allowParentInsert){
19117 noBelow = tn.hasChildNodes() && tn.isExpanded();
19119 var q = (b - t) / (noAppend ? 2 : 3);
19120 if(y >= t && y < (t + q)){
19122 }else if(!noBelow && (noAppend || y >= b-q && y <= b)){
19129 onNodeEnter : function(n, dd, e, data)
19131 this.cancelExpand();
19134 onNodeOver : function(n, dd, e, data)
19137 var pt = this.getDropPoint(e, n, dd);
19140 // auto node expand check
19141 if(!this.expandProcId && pt == "append" && node.hasChildNodes() && !n.node.isExpanded()){
19142 this.queueExpand(node);
19143 }else if(pt != "append"){
19144 this.cancelExpand();
19147 // set the insert point style on the target node
19148 var returnCls = this.dropNotAllowed;
19149 if(this.isValidDropPoint(n, pt, dd, e, data)){
19154 returnCls = n.node.isFirst() ? "x-tree-drop-ok-above" : "x-tree-drop-ok-between";
19155 cls = "x-tree-drag-insert-above";
19156 }else if(pt == "below"){
19157 returnCls = n.node.isLast() ? "x-tree-drop-ok-below" : "x-tree-drop-ok-between";
19158 cls = "x-tree-drag-insert-below";
19160 returnCls = "x-tree-drop-ok-append";
19161 cls = "x-tree-drag-append";
19163 if(this.lastInsertClass != cls){
19164 Roo.fly(el).replaceClass(this.lastInsertClass, cls);
19165 this.lastInsertClass = cls;
19172 onNodeOut : function(n, dd, e, data){
19174 this.cancelExpand();
19175 this.removeDropIndicators(n);
19178 onNodeDrop : function(n, dd, e, data){
19179 var point = this.getDropPoint(e, n, dd);
19180 var targetNode = n.node;
19181 targetNode.ui.startDrop();
19182 if(!this.isValidDropPoint(n, point, dd, e, data)){
19183 targetNode.ui.endDrop();
19186 // first try to find the drop node
19187 var dropNode = data.node || (dd.getTreeNode ? dd.getTreeNode(data, targetNode, point, e) : null);
19190 target: targetNode,
19195 dropNode: dropNode,
19198 var retval = this.tree.fireEvent("beforenodedrop", dropEvent);
19199 if(retval === false || dropEvent.cancel === true || !dropEvent.dropNode){
19200 targetNode.ui.endDrop();
19203 // allow target changing
19204 targetNode = dropEvent.target;
19205 if(point == "append" && !targetNode.isExpanded()){
19206 targetNode.expand(false, null, function(){
19207 this.completeDrop(dropEvent);
19208 }.createDelegate(this));
19210 this.completeDrop(dropEvent);
19215 completeDrop : function(de){
19216 var ns = de.dropNode, p = de.point, t = de.target;
19217 if(!(ns instanceof Array)){
19221 for(var i = 0, len = ns.length; i < len; i++){
19224 t.parentNode.insertBefore(n, t);
19225 }else if(p == "below"){
19226 t.parentNode.insertBefore(n, t.nextSibling);
19232 if(this.tree.hlDrop){
19236 this.tree.fireEvent("nodedrop", de);
19239 afterNodeMoved : function(dd, data, e, targetNode, dropNode){
19240 if(this.tree.hlDrop){
19241 dropNode.ui.focus();
19242 dropNode.ui.highlight();
19244 this.tree.fireEvent("nodedrop", this.tree, targetNode, data, dd, e);
19247 getTree : function(){
19251 removeDropIndicators : function(n){
19254 Roo.fly(el).removeClass([
19255 "x-tree-drag-insert-above",
19256 "x-tree-drag-insert-below",
19257 "x-tree-drag-append"]);
19258 this.lastInsertClass = "_noclass";
19262 beforeDragDrop : function(target, e, id){
19263 this.cancelExpand();
19267 afterRepair : function(data){
19268 if(data && Roo.enableFx){
19269 data.node.ui.highlight();
19279 * Ext JS Library 1.1.1
19280 * Copyright(c) 2006-2007, Ext JS, LLC.
19282 * Originally Released Under LGPL - original licence link has changed is not relivant.
19285 * <script type="text/javascript">
19289 if(Roo.dd.DragZone){
19290 Roo.tree.TreeDragZone = function(tree, config){
19291 Roo.tree.TreeDragZone.superclass.constructor.call(this, tree.getTreeEl(), config);
19295 Roo.extend(Roo.tree.TreeDragZone, Roo.dd.DragZone, {
19296 ddGroup : "TreeDD",
19298 onBeforeDrag : function(data, e){
19300 return n && n.draggable && !n.disabled;
19304 onInitDrag : function(e){
19305 var data = this.dragData;
19306 this.tree.getSelectionModel().select(data.node);
19307 this.proxy.update("");
19308 data.node.ui.appendDDGhost(this.proxy.ghost.dom);
19309 this.tree.fireEvent("startdrag", this.tree, data.node, e);
19312 getRepairXY : function(e, data){
19313 return data.node.ui.getDDRepairXY();
19316 onEndDrag : function(data, e){
19317 this.tree.fireEvent("enddrag", this.tree, data.node, e);
19322 onValidDrop : function(dd, e, id){
19323 this.tree.fireEvent("dragdrop", this.tree, this.dragData.node, dd, e);
19327 beforeInvalidDrop : function(e, id){
19328 // this scrolls the original position back into view
19329 var sm = this.tree.getSelectionModel();
19330 sm.clearSelections();
19331 sm.select(this.dragData.node);
19336 * Ext JS Library 1.1.1
19337 * Copyright(c) 2006-2007, Ext JS, LLC.
19339 * Originally Released Under LGPL - original licence link has changed is not relivant.
19342 * <script type="text/javascript">
19345 * @class Roo.tree.TreeEditor
19346 * @extends Roo.Editor
19347 * Provides editor functionality for inline tree node editing. Any valid {@link Roo.form.Field} can be used
19348 * as the editor field.
19350 * @param {Object} config (used to be the tree panel.)
19351 * @param {Object} oldconfig DEPRECIATED Either a prebuilt {@link Roo.form.Field} instance or a Field config object
19353 * @cfg {Roo.tree.TreePanel} tree The tree to bind to.
19354 * @cfg {Roo.form.TextField|Object} field The field configuration
19358 Roo.tree.TreeEditor = function(config, oldconfig) { // was -- (tree, config){
19361 if (oldconfig) { // old style..
19362 field = oldconfig.events ? oldconfig : new Roo.form.TextField(oldconfig);
19365 tree = config.tree;
19366 config.field = config.field || {};
19367 config.field.xtype = 'TextField';
19368 field = Roo.factory(config.field, Roo.form);
19370 config = config || {};
19375 * @event beforenodeedit
19376 * Fires when editing is initiated, but before the value changes. Editing can be canceled by returning
19377 * false from the handler of this event.
19378 * @param {Editor} this
19379 * @param {Roo.tree.Node} node
19381 "beforenodeedit" : true
19385 Roo.tree.TreeEditor.superclass.constructor.call(this, field, config);
19389 tree.on('beforeclick', this.beforeNodeClick, this);
19390 tree.getTreeEl().on('mousedown', this.hide, this);
19391 this.on('complete', this.updateNode, this);
19392 this.on('beforestartedit', this.fitToTree, this);
19393 this.on('startedit', this.bindScroll, this, {delay:10});
19394 this.on('specialkey', this.onSpecialKey, this);
19397 Roo.extend(Roo.tree.TreeEditor, Roo.Editor, {
19399 * @cfg {String} alignment
19400 * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "l-l").
19406 * @cfg {Boolean} hideEl
19407 * True to hide the bound element while the editor is displayed (defaults to false)
19411 * @cfg {String} cls
19412 * CSS class to apply to the editor (defaults to "x-small-editor x-tree-editor")
19414 cls: "x-small-editor x-tree-editor",
19416 * @cfg {Boolean} shim
19417 * True to shim the editor if selects/iframes could be displayed beneath it (defaults to false)
19423 * @cfg {Number} maxWidth
19424 * The maximum width in pixels of the editor field (defaults to 250). Note that if the maxWidth would exceed
19425 * the containing tree element's size, it will be automatically limited for you to the container width, taking
19426 * scroll and client offsets into account prior to each edit.
19433 fitToTree : function(ed, el){
19434 var td = this.tree.getTreeEl().dom, nd = el.dom;
19435 if(td.scrollLeft > nd.offsetLeft){ // ensure the node left point is visible
19436 td.scrollLeft = nd.offsetLeft;
19440 (td.clientWidth > 20 ? td.clientWidth : td.offsetWidth) - Math.max(0, nd.offsetLeft-td.scrollLeft) - /*cushion*/5);
19441 this.setSize(w, '');
19443 return this.fireEvent('beforenodeedit', this, this.editNode);
19448 triggerEdit : function(node){
19449 this.completeEdit();
19450 this.editNode = node;
19451 this.startEdit(node.ui.textNode, node.text);
19455 bindScroll : function(){
19456 this.tree.getTreeEl().on('scroll', this.cancelEdit, this);
19460 beforeNodeClick : function(node, e){
19461 var sinceLast = (this.lastClick ? this.lastClick.getElapsed() : 0);
19462 this.lastClick = new Date();
19463 if(sinceLast > this.editDelay && this.tree.getSelectionModel().isSelected(node)){
19465 this.triggerEdit(node);
19472 updateNode : function(ed, value){
19473 this.tree.getTreeEl().un('scroll', this.cancelEdit, this);
19474 this.editNode.setText(value);
19478 onHide : function(){
19479 Roo.tree.TreeEditor.superclass.onHide.call(this);
19481 this.editNode.ui.focus();
19486 onSpecialKey : function(field, e){
19487 var k = e.getKey();
19491 }else if(k == e.ENTER && !e.hasModifier()){
19493 this.completeEdit();
19496 });//<Script type="text/javascript">
19499 * Ext JS Library 1.1.1
19500 * Copyright(c) 2006-2007, Ext JS, LLC.
19502 * Originally Released Under LGPL - original licence link has changed is not relivant.
19505 * <script type="text/javascript">
19509 * Not documented??? - probably should be...
19512 Roo.tree.ColumnNodeUI = Roo.extend(Roo.tree.TreeNodeUI, {
19513 //focus: Roo.emptyFn, // prevent odd scrolling behavior
19515 renderElements : function(n, a, targetNode, bulkRender){
19516 //consel.log("renderElements?");
19517 this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
19519 var t = n.getOwnerTree();
19520 var tid = Pman.Tab.Document_TypesTree.tree.el.id;
19522 var cols = t.columns;
19523 var bw = t.borderWidth;
19525 var href = a.href ? a.href : Roo.isGecko ? "" : "#";
19526 var cb = typeof a.checked == "boolean";
19527 var tx = String.format('{0}',n.text || (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
19528 var colcls = 'x-t-' + tid + '-c0';
19530 '<li class="x-tree-node">',
19533 '<div class="x-tree-node-el ', a.cls,'">',
19535 '<div class="x-tree-col ', colcls, '" style="width:', c.width-bw, 'px;">',
19538 '<span class="x-tree-node-indent">',this.indentMarkup,'</span>',
19539 '<img src="', this.emptyIcon, '" class="x-tree-ec-icon " />',
19540 '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',
19541 (a.icon ? ' x-tree-node-inline-icon' : ''),
19542 (a.iconCls ? ' '+a.iconCls : ''),
19543 '" unselectable="on" />',
19544 (cb ? ('<input class="x-tree-node-cb" type="checkbox" ' +
19545 (a.checked ? 'checked="checked" />' : ' />')) : ''),
19547 '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
19548 (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>',
19549 '<span unselectable="on" qtip="' + tx + '">',
19553 '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
19554 (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>'
19556 for(var i = 1, len = cols.length; i < len; i++){
19558 colcls = 'x-t-' + tid + '-c' +i;
19559 tx = String.format('{0}', (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
19560 buf.push('<div class="x-tree-col ', colcls, ' ' ,(c.cls?c.cls:''),'" style="width:',c.width-bw,'px;">',
19561 '<div class="x-tree-col-text" qtip="' + tx +'">',tx,"</div>",
19567 '<div class="x-clear"></div></div>',
19568 '<ul class="x-tree-node-ct" style="display:none;"></ul>',
19571 if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
19572 this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
19573 n.nextSibling.ui.getEl(), buf.join(""));
19575 this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
19577 var el = this.wrap.firstChild;
19579 this.elNode = el.firstChild;
19580 this.ranchor = el.childNodes[1];
19581 this.ctNode = this.wrap.childNodes[1];
19582 var cs = el.firstChild.childNodes;
19583 this.indentNode = cs[0];
19584 this.ecNode = cs[1];
19585 this.iconNode = cs[2];
19588 this.checkbox = cs[3];
19591 this.anchor = cs[index];
19593 this.textNode = cs[index].firstChild;
19595 //el.on("click", this.onClick, this);
19596 //el.on("dblclick", this.onDblClick, this);
19599 // console.log(this);
19601 initEvents : function(){
19602 Roo.tree.ColumnNodeUI.superclass.initEvents.call(this);
19605 var a = this.ranchor;
19607 var el = Roo.get(a);
19609 if(Roo.isOpera){ // opera render bug ignores the CSS
19610 el.setStyle("text-decoration", "none");
19613 el.on("click", this.onClick, this);
19614 el.on("dblclick", this.onDblClick, this);
19615 el.on("contextmenu", this.onContextMenu, this);
19619 /*onSelectedChange : function(state){
19622 this.addClass("x-tree-selected");
19625 this.removeClass("x-tree-selected");
19628 addClass : function(cls){
19630 Roo.fly(this.elRow).addClass(cls);
19636 removeClass : function(cls){
19638 Roo.fly(this.elRow).removeClass(cls);
19644 });//<Script type="text/javascript">
19648 * Ext JS Library 1.1.1
19649 * Copyright(c) 2006-2007, Ext JS, LLC.
19651 * Originally Released Under LGPL - original licence link has changed is not relivant.
19654 * <script type="text/javascript">
19659 * @class Roo.tree.ColumnTree
19660 * @extends Roo.data.TreePanel
19661 * @cfg {Object} columns Including width, header, renderer, cls, dataIndex
19662 * @cfg {int} borderWidth compined right/left border allowance
19664 * @param {String/HTMLElement/Element} el The container element
19665 * @param {Object} config
19667 Roo.tree.ColumnTree = function(el, config)
19669 Roo.tree.ColumnTree.superclass.constructor.call(this, el , config);
19673 * Fire this event on a container when it resizes
19674 * @param {int} w Width
19675 * @param {int} h Height
19679 this.on('resize', this.onResize, this);
19682 Roo.extend(Roo.tree.ColumnTree, Roo.tree.TreePanel, {
19686 borderWidth: Roo.isBorderBox ? 0 : 2,
19689 render : function(){
19690 // add the header.....
19692 Roo.tree.ColumnTree.superclass.render.apply(this);
19694 this.el.addClass('x-column-tree');
19696 this.headers = this.el.createChild(
19697 {cls:'x-tree-headers'},this.innerCt.dom);
19699 var cols = this.columns, c;
19700 var totalWidth = 0;
19702 var len = cols.length;
19703 for(var i = 0; i < len; i++){
19705 totalWidth += c.width;
19706 this.headEls.push(this.headers.createChild({
19707 cls:'x-tree-hd ' + (c.cls?c.cls+'-hd':''),
19709 cls:'x-tree-hd-text',
19712 style:'width:'+(c.width-this.borderWidth)+'px;'
19715 this.headers.createChild({cls:'x-clear'});
19716 // prevent floats from wrapping when clipped
19717 this.headers.setWidth(totalWidth);
19718 //this.innerCt.setWidth(totalWidth);
19719 this.innerCt.setStyle({ overflow: 'auto' });
19720 this.onResize(this.width, this.height);
19724 onResize : function(w,h)
19729 this.innerCt.setWidth(this.width);
19730 this.innerCt.setHeight(this.height-20);
19733 var cols = this.columns, c;
19734 var totalWidth = 0;
19736 var len = cols.length;
19737 for(var i = 0; i < len; i++){
19739 if (this.autoExpandColumn !== false && c.dataIndex == this.autoExpandColumn) {
19740 // it's the expander..
19741 expEl = this.headEls[i];
19744 totalWidth += c.width;
19748 expEl.setWidth( ((w - totalWidth)-this.borderWidth - 20));
19750 this.headers.setWidth(w-20);
19759 * Ext JS Library 1.1.1
19760 * Copyright(c) 2006-2007, Ext JS, LLC.
19762 * Originally Released Under LGPL - original licence link has changed is not relivant.
19765 * <script type="text/javascript">
19769 * @class Roo.menu.Menu
19770 * @extends Roo.util.Observable
19771 * A menu object. This is the container to which you add all other menu items. Menu can also serve a as a base class
19772 * when you want a specialzed menu based off of another component (like {@link Roo.menu.DateMenu} for example).
19774 * Creates a new Menu
19775 * @param {Object} config Configuration options
19777 Roo.menu.Menu = function(config){
19778 Roo.apply(this, config);
19779 this.id = this.id || Roo.id();
19782 * @event beforeshow
19783 * Fires before this menu is displayed
19784 * @param {Roo.menu.Menu} this
19788 * @event beforehide
19789 * Fires before this menu is hidden
19790 * @param {Roo.menu.Menu} this
19795 * Fires after this menu is displayed
19796 * @param {Roo.menu.Menu} this
19801 * Fires after this menu is hidden
19802 * @param {Roo.menu.Menu} this
19807 * Fires when this menu is clicked (or when the enter key is pressed while it is active)
19808 * @param {Roo.menu.Menu} this
19809 * @param {Roo.menu.Item} menuItem The menu item that was clicked
19810 * @param {Roo.EventObject} e
19815 * Fires when the mouse is hovering over this menu
19816 * @param {Roo.menu.Menu} this
19817 * @param {Roo.EventObject} e
19818 * @param {Roo.menu.Item} menuItem The menu item that was clicked
19823 * Fires when the mouse exits this menu
19824 * @param {Roo.menu.Menu} this
19825 * @param {Roo.EventObject} e
19826 * @param {Roo.menu.Item} menuItem The menu item that was clicked
19831 * Fires when a menu item contained in this menu is clicked
19832 * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
19833 * @param {Roo.EventObject} e
19837 if (this.registerMenu) {
19838 Roo.menu.MenuMgr.register(this);
19841 var mis = this.items;
19842 this.items = new Roo.util.MixedCollection();
19844 this.add.apply(this, mis);
19848 Roo.extend(Roo.menu.Menu, Roo.util.Observable, {
19850 * @cfg {Number} minWidth The minimum width of the menu in pixels (defaults to 120)
19854 * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop"
19855 * for bottom-right shadow (defaults to "sides")
19859 * @cfg {String} subMenuAlign The {@link Roo.Element#alignTo} anchor position value to use for submenus of
19860 * this menu (defaults to "tl-tr?")
19862 subMenuAlign : "tl-tr?",
19864 * @cfg {String} defaultAlign The default {@link Roo.Element#alignTo) anchor position value for this menu
19865 * relative to its element of origin (defaults to "tl-bl?")
19867 defaultAlign : "tl-bl?",
19869 * @cfg {Boolean} allowOtherMenus True to allow multiple menus to be displayed at the same time (defaults to false)
19871 allowOtherMenus : false,
19873 * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
19875 registerMenu : true,
19880 render : function(){
19884 var el = this.el = new Roo.Layer({
19886 shadow:this.shadow,
19888 parentEl: this.parentEl || document.body,
19892 this.keyNav = new Roo.menu.MenuNav(this);
19895 el.addClass("x-menu-plain");
19898 el.addClass(this.cls);
19900 // generic focus element
19901 this.focusEl = el.createChild({
19902 tag: "a", cls: "x-menu-focus", href: "#", onclick: "return false;", tabIndex:"-1"
19904 var ul = el.createChild({tag: "ul", cls: "x-menu-list"});
19905 ul.on("click", this.onClick, this);
19906 ul.on("mouseover", this.onMouseOver, this);
19907 ul.on("mouseout", this.onMouseOut, this);
19908 this.items.each(function(item){
19913 var li = document.createElement("li");
19914 li.className = "x-menu-list-item";
19915 ul.dom.appendChild(li);
19916 item.render(li, this);
19923 autoWidth : function(){
19924 var el = this.el, ul = this.ul;
19928 var w = this.width;
19931 }else if(Roo.isIE){
19932 el.setWidth(this.minWidth);
19933 var t = el.dom.offsetWidth; // force recalc
19934 el.setWidth(ul.getWidth()+el.getFrameWidth("lr"));
19939 delayAutoWidth : function(){
19942 this.awTask = new Roo.util.DelayedTask(this.autoWidth, this);
19944 this.awTask.delay(20);
19949 findTargetItem : function(e){
19950 var t = e.getTarget(".x-menu-list-item", this.ul, true);
19951 if(t && t.menuItemId){
19952 return this.items.get(t.menuItemId);
19957 onClick : function(e){
19959 if(t = this.findTargetItem(e)){
19961 this.fireEvent("click", this, t, e);
19966 setActiveItem : function(item, autoExpand){
19967 if(item != this.activeItem){
19968 if(this.activeItem){
19969 this.activeItem.deactivate();
19971 this.activeItem = item;
19972 item.activate(autoExpand);
19973 }else if(autoExpand){
19979 tryActivate : function(start, step){
19980 var items = this.items;
19981 for(var i = start, len = items.length; i >= 0 && i < len; i+= step){
19982 var item = items.get(i);
19983 if(!item.disabled && item.canActivate){
19984 this.setActiveItem(item, false);
19992 onMouseOver : function(e){
19994 if(t = this.findTargetItem(e)){
19995 if(t.canActivate && !t.disabled){
19996 this.setActiveItem(t, true);
19999 this.fireEvent("mouseover", this, e, t);
20003 onMouseOut : function(e){
20005 if(t = this.findTargetItem(e)){
20006 if(t == this.activeItem && t.shouldDeactivate(e)){
20007 this.activeItem.deactivate();
20008 delete this.activeItem;
20011 this.fireEvent("mouseout", this, e, t);
20015 * Read-only. Returns true if the menu is currently displayed, else false.
20018 isVisible : function(){
20019 return this.el && !this.hidden;
20023 * Displays this menu relative to another element
20024 * @param {String/HTMLElement/Roo.Element} element The element to align to
20025 * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
20026 * the element (defaults to this.defaultAlign)
20027 * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
20029 show : function(el, pos, parentMenu){
20030 this.parentMenu = parentMenu;
20034 this.fireEvent("beforeshow", this);
20035 this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
20039 * Displays this menu at a specific xy position
20040 * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
20041 * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
20043 showAt : function(xy, parentMenu, /* private: */_e){
20044 this.parentMenu = parentMenu;
20049 this.fireEvent("beforeshow", this);
20050 xy = this.el.adjustForConstraints(xy);
20054 this.hidden = false;
20056 this.fireEvent("show", this);
20059 focus : function(){
20061 this.doFocus.defer(50, this);
20065 doFocus : function(){
20067 this.focusEl.focus();
20072 * Hides this menu and optionally all parent menus
20073 * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
20075 hide : function(deep){
20076 if(this.el && this.isVisible()){
20077 this.fireEvent("beforehide", this);
20078 if(this.activeItem){
20079 this.activeItem.deactivate();
20080 this.activeItem = null;
20083 this.hidden = true;
20084 this.fireEvent("hide", this);
20086 if(deep === true && this.parentMenu){
20087 this.parentMenu.hide(true);
20092 * Addds one or more items of any type supported by the Menu class, or that can be converted into menu items.
20093 * Any of the following are valid:
20095 * <li>Any menu item object based on {@link Roo.menu.Item}</li>
20096 * <li>An HTMLElement object which will be converted to a menu item</li>
20097 * <li>A menu item config object that will be created as a new menu item</li>
20098 * <li>A string, which can either be '-' or 'separator' to add a menu separator, otherwise
20099 * it will be converted into a {@link Roo.menu.TextItem} and added</li>
20104 var menu = new Roo.menu.Menu();
20106 // Create a menu item to add by reference
20107 var menuItem = new Roo.menu.Item({ text: 'New Item!' });
20109 // Add a bunch of items at once using different methods.
20110 // Only the last item added will be returned.
20111 var item = menu.add(
20112 menuItem, // add existing item by ref
20113 'Dynamic Item', // new TextItem
20114 '-', // new separator
20115 { text: 'Config Item' } // new item by config
20118 * @param {Mixed} args One or more menu items, menu item configs or other objects that can be converted to menu items
20119 * @return {Roo.menu.Item} The menu item that was added, or the last one if multiple items were added
20122 var a = arguments, l = a.length, item;
20123 for(var i = 0; i < l; i++){
20125 if ((typeof(el) == "object") && el.xtype && el.xns) {
20126 el = Roo.factory(el, Roo.menu);
20129 if(el.render){ // some kind of Item
20130 item = this.addItem(el);
20131 }else if(typeof el == "string"){ // string
20132 if(el == "separator" || el == "-"){
20133 item = this.addSeparator();
20135 item = this.addText(el);
20137 }else if(el.tagName || el.el){ // element
20138 item = this.addElement(el);
20139 }else if(typeof el == "object"){ // must be menu item config?
20140 item = this.addMenuItem(el);
20147 * Returns this menu's underlying {@link Roo.Element} object
20148 * @return {Roo.Element} The element
20150 getEl : function(){
20158 * Adds a separator bar to the menu
20159 * @return {Roo.menu.Item} The menu item that was added
20161 addSeparator : function(){
20162 return this.addItem(new Roo.menu.Separator());
20166 * Adds an {@link Roo.Element} object to the menu
20167 * @param {String/HTMLElement/Roo.Element} el The element or DOM node to add, or its id
20168 * @return {Roo.menu.Item} The menu item that was added
20170 addElement : function(el){
20171 return this.addItem(new Roo.menu.BaseItem(el));
20175 * Adds an existing object based on {@link Roo.menu.Item} to the menu
20176 * @param {Roo.menu.Item} item The menu item to add
20177 * @return {Roo.menu.Item} The menu item that was added
20179 addItem : function(item){
20180 this.items.add(item);
20182 var li = document.createElement("li");
20183 li.className = "x-menu-list-item";
20184 this.ul.dom.appendChild(li);
20185 item.render(li, this);
20186 this.delayAutoWidth();
20192 * Creates a new {@link Roo.menu.Item} based an the supplied config object and adds it to the menu
20193 * @param {Object} config A MenuItem config object
20194 * @return {Roo.menu.Item} The menu item that was added
20196 addMenuItem : function(config){
20197 if(!(config instanceof Roo.menu.Item)){
20198 if(typeof config.checked == "boolean"){ // must be check menu item config?
20199 config = new Roo.menu.CheckItem(config);
20201 config = new Roo.menu.Item(config);
20204 return this.addItem(config);
20208 * Creates a new {@link Roo.menu.TextItem} with the supplied text and adds it to the menu
20209 * @param {String} text The text to display in the menu item
20210 * @return {Roo.menu.Item} The menu item that was added
20212 addText : function(text){
20213 return this.addItem(new Roo.menu.TextItem({ text : text }));
20217 * Inserts an existing object based on {@link Roo.menu.Item} to the menu at a specified index
20218 * @param {Number} index The index in the menu's list of current items where the new item should be inserted
20219 * @param {Roo.menu.Item} item The menu item to add
20220 * @return {Roo.menu.Item} The menu item that was added
20222 insert : function(index, item){
20223 this.items.insert(index, item);
20225 var li = document.createElement("li");
20226 li.className = "x-menu-list-item";
20227 this.ul.dom.insertBefore(li, this.ul.dom.childNodes[index]);
20228 item.render(li, this);
20229 this.delayAutoWidth();
20235 * Removes an {@link Roo.menu.Item} from the menu and destroys the object
20236 * @param {Roo.menu.Item} item The menu item to remove
20238 remove : function(item){
20239 this.items.removeKey(item.id);
20244 * Removes and destroys all items in the menu
20246 removeAll : function(){
20248 while(f = this.items.first()){
20254 // MenuNav is a private utility class used internally by the Menu
20255 Roo.menu.MenuNav = function(menu){
20256 Roo.menu.MenuNav.superclass.constructor.call(this, menu.el);
20257 this.scope = this.menu = menu;
20260 Roo.extend(Roo.menu.MenuNav, Roo.KeyNav, {
20261 doRelay : function(e, h){
20262 var k = e.getKey();
20263 if(!this.menu.activeItem && e.isNavKeyPress() && k != e.SPACE && k != e.RETURN){
20264 this.menu.tryActivate(0, 1);
20267 return h.call(this.scope || this, e, this.menu);
20270 up : function(e, m){
20271 if(!m.tryActivate(m.items.indexOf(m.activeItem)-1, -1)){
20272 m.tryActivate(m.items.length-1, -1);
20276 down : function(e, m){
20277 if(!m.tryActivate(m.items.indexOf(m.activeItem)+1, 1)){
20278 m.tryActivate(0, 1);
20282 right : function(e, m){
20284 m.activeItem.expandMenu(true);
20288 left : function(e, m){
20290 if(m.parentMenu && m.parentMenu.activeItem){
20291 m.parentMenu.activeItem.activate();
20295 enter : function(e, m){
20297 e.stopPropagation();
20298 m.activeItem.onClick(e);
20299 m.fireEvent("click", this, m.activeItem);
20305 * Ext JS Library 1.1.1
20306 * Copyright(c) 2006-2007, Ext JS, LLC.
20308 * Originally Released Under LGPL - original licence link has changed is not relivant.
20311 * <script type="text/javascript">
20315 * @class Roo.menu.MenuMgr
20316 * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
20319 Roo.menu.MenuMgr = function(){
20320 var menus, active, groups = {}, attached = false, lastShow = new Date();
20322 // private - called when first menu is created
20325 active = new Roo.util.MixedCollection();
20326 Roo.get(document).addKeyListener(27, function(){
20327 if(active.length > 0){
20334 function hideAll(){
20335 if(active && active.length > 0){
20336 var c = active.clone();
20337 c.each(function(m){
20344 function onHide(m){
20346 if(active.length < 1){
20347 Roo.get(document).un("mousedown", onMouseDown);
20353 function onShow(m){
20354 var last = active.last();
20355 lastShow = new Date();
20358 Roo.get(document).on("mousedown", onMouseDown);
20362 m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
20363 m.parentMenu.activeChild = m;
20364 }else if(last && last.isVisible()){
20365 m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
20370 function onBeforeHide(m){
20372 m.activeChild.hide();
20374 if(m.autoHideTimer){
20375 clearTimeout(m.autoHideTimer);
20376 delete m.autoHideTimer;
20381 function onBeforeShow(m){
20382 var pm = m.parentMenu;
20383 if(!pm && !m.allowOtherMenus){
20385 }else if(pm && pm.activeChild && active != m){
20386 pm.activeChild.hide();
20391 function onMouseDown(e){
20392 if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".x-menu")){
20398 function onBeforeCheck(mi, state){
20400 var g = groups[mi.group];
20401 for(var i = 0, l = g.length; i < l; i++){
20403 g[i].setChecked(false);
20412 * Hides all menus that are currently visible
20414 hideAll : function(){
20419 register : function(menu){
20423 menus[menu.id] = menu;
20424 menu.on("beforehide", onBeforeHide);
20425 menu.on("hide", onHide);
20426 menu.on("beforeshow", onBeforeShow);
20427 menu.on("show", onShow);
20428 var g = menu.group;
20429 if(g && menu.events["checkchange"]){
20433 groups[g].push(menu);
20434 menu.on("checkchange", onCheck);
20439 * Returns a {@link Roo.menu.Menu} object
20440 * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
20441 * be used to generate and return a new Menu instance.
20443 get : function(menu){
20444 if(typeof menu == "string"){ // menu id
20445 return menus[menu];
20446 }else if(menu.events){ // menu instance
20448 }else if(typeof menu.length == 'number'){ // array of menu items?
20449 return new Roo.menu.Menu({items:menu});
20450 }else{ // otherwise, must be a config
20451 return new Roo.menu.Menu(menu);
20456 unregister : function(menu){
20457 delete menus[menu.id];
20458 menu.un("beforehide", onBeforeHide);
20459 menu.un("hide", onHide);
20460 menu.un("beforeshow", onBeforeShow);
20461 menu.un("show", onShow);
20462 var g = menu.group;
20463 if(g && menu.events["checkchange"]){
20464 groups[g].remove(menu);
20465 menu.un("checkchange", onCheck);
20470 registerCheckable : function(menuItem){
20471 var g = menuItem.group;
20476 groups[g].push(menuItem);
20477 menuItem.on("beforecheckchange", onBeforeCheck);
20482 unregisterCheckable : function(menuItem){
20483 var g = menuItem.group;
20485 groups[g].remove(menuItem);
20486 menuItem.un("beforecheckchange", onBeforeCheck);
20492 * Ext JS Library 1.1.1
20493 * Copyright(c) 2006-2007, Ext JS, LLC.
20495 * Originally Released Under LGPL - original licence link has changed is not relivant.
20498 * <script type="text/javascript">
20503 * @class Roo.menu.BaseItem
20504 * @extends Roo.Component
20505 * The base class for all items that render into menus. BaseItem provides default rendering, activated state
20506 * management and base configuration options shared by all menu components.
20508 * Creates a new BaseItem
20509 * @param {Object} config Configuration options
20511 Roo.menu.BaseItem = function(config){
20512 Roo.menu.BaseItem.superclass.constructor.call(this, config);
20517 * Fires when this item is clicked
20518 * @param {Roo.menu.BaseItem} this
20519 * @param {Roo.EventObject} e
20524 * Fires when this item is activated
20525 * @param {Roo.menu.BaseItem} this
20529 * @event deactivate
20530 * Fires when this item is deactivated
20531 * @param {Roo.menu.BaseItem} this
20537 this.on("click", this.handler, this.scope, true);
20541 Roo.extend(Roo.menu.BaseItem, Roo.Component, {
20543 * @cfg {Function} handler
20544 * A function that will handle the click event of this menu item (defaults to undefined)
20547 * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to false)
20549 canActivate : false,
20552 * @cfg {Boolean} hidden True to prevent creation of this menu item (defaults to false)
20557 * @cfg {String} activeClass The CSS class to use when the item becomes activated (defaults to "x-menu-item-active")
20559 activeClass : "x-menu-item-active",
20561 * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to true)
20563 hideOnClick : true,
20565 * @cfg {Number} hideDelay Length of time in milliseconds to wait before hiding after a click (defaults to 100)
20570 ctype: "Roo.menu.BaseItem",
20573 actionMode : "container",
20576 render : function(container, parentMenu){
20577 this.parentMenu = parentMenu;
20578 Roo.menu.BaseItem.superclass.render.call(this, container);
20579 this.container.menuItemId = this.id;
20583 onRender : function(container, position){
20584 this.el = Roo.get(this.el);
20585 container.dom.appendChild(this.el.dom);
20589 onClick : function(e){
20590 if(!this.disabled && this.fireEvent("click", this, e) !== false
20591 && this.parentMenu.fireEvent("itemclick", this, e) !== false){
20592 this.handleClick(e);
20599 activate : function(){
20603 var li = this.container;
20604 li.addClass(this.activeClass);
20605 this.region = li.getRegion().adjust(2, 2, -2, -2);
20606 this.fireEvent("activate", this);
20611 deactivate : function(){
20612 this.container.removeClass(this.activeClass);
20613 this.fireEvent("deactivate", this);
20617 shouldDeactivate : function(e){
20618 return !this.region || !this.region.contains(e.getPoint());
20622 handleClick : function(e){
20623 if(this.hideOnClick){
20624 this.parentMenu.hide.defer(this.hideDelay, this.parentMenu, [true]);
20629 expandMenu : function(autoActivate){
20634 hideMenu : function(){
20639 * Ext JS Library 1.1.1
20640 * Copyright(c) 2006-2007, Ext JS, LLC.
20642 * Originally Released Under LGPL - original licence link has changed is not relivant.
20645 * <script type="text/javascript">
20649 * @class Roo.menu.Adapter
20650 * @extends Roo.menu.BaseItem
20651 * 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.
20652 * It provides basic rendering, activation management and enable/disable logic required to work in menus.
20654 * Creates a new Adapter
20655 * @param {Object} config Configuration options
20657 Roo.menu.Adapter = function(component, config){
20658 Roo.menu.Adapter.superclass.constructor.call(this, config);
20659 this.component = component;
20661 Roo.extend(Roo.menu.Adapter, Roo.menu.BaseItem, {
20663 canActivate : true,
20666 onRender : function(container, position){
20667 this.component.render(container);
20668 this.el = this.component.getEl();
20672 activate : function(){
20676 this.component.focus();
20677 this.fireEvent("activate", this);
20682 deactivate : function(){
20683 this.fireEvent("deactivate", this);
20687 disable : function(){
20688 this.component.disable();
20689 Roo.menu.Adapter.superclass.disable.call(this);
20693 enable : function(){
20694 this.component.enable();
20695 Roo.menu.Adapter.superclass.enable.call(this);
20699 * Ext JS Library 1.1.1
20700 * Copyright(c) 2006-2007, Ext JS, LLC.
20702 * Originally Released Under LGPL - original licence link has changed is not relivant.
20705 * <script type="text/javascript">
20709 * @class Roo.menu.TextItem
20710 * @extends Roo.menu.BaseItem
20711 * Adds a static text string to a menu, usually used as either a heading or group separator.
20712 * Note: old style constructor with text is still supported.
20715 * Creates a new TextItem
20716 * @param {Object} cfg Configuration
20718 Roo.menu.TextItem = function(cfg){
20719 if (typeof(cfg) == 'string') {
20722 Roo.apply(this,cfg);
20725 Roo.menu.TextItem.superclass.constructor.call(this);
20728 Roo.extend(Roo.menu.TextItem, Roo.menu.BaseItem, {
20730 * @cfg {Boolean} text Text to show on item.
20735 * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
20737 hideOnClick : false,
20739 * @cfg {String} itemCls The default CSS class to use for text items (defaults to "x-menu-text")
20741 itemCls : "x-menu-text",
20744 onRender : function(){
20745 var s = document.createElement("span");
20746 s.className = this.itemCls;
20747 s.innerHTML = this.text;
20749 Roo.menu.TextItem.superclass.onRender.apply(this, arguments);
20753 * Ext JS Library 1.1.1
20754 * Copyright(c) 2006-2007, Ext JS, LLC.
20756 * Originally Released Under LGPL - original licence link has changed is not relivant.
20759 * <script type="text/javascript">
20763 * @class Roo.menu.Separator
20764 * @extends Roo.menu.BaseItem
20765 * Adds a separator bar to a menu, used to divide logical groups of menu items. Generally you will
20766 * add one of these by using "-" in you call to add() or in your items config rather than creating one directly.
20768 * @param {Object} config Configuration options
20770 Roo.menu.Separator = function(config){
20771 Roo.menu.Separator.superclass.constructor.call(this, config);
20774 Roo.extend(Roo.menu.Separator, Roo.menu.BaseItem, {
20776 * @cfg {String} itemCls The default CSS class to use for separators (defaults to "x-menu-sep")
20778 itemCls : "x-menu-sep",
20780 * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
20782 hideOnClick : false,
20785 onRender : function(li){
20786 var s = document.createElement("span");
20787 s.className = this.itemCls;
20788 s.innerHTML = " ";
20790 li.addClass("x-menu-sep-li");
20791 Roo.menu.Separator.superclass.onRender.apply(this, arguments);
20795 * Ext JS Library 1.1.1
20796 * Copyright(c) 2006-2007, Ext JS, LLC.
20798 * Originally Released Under LGPL - original licence link has changed is not relivant.
20801 * <script type="text/javascript">
20804 * @class Roo.menu.Item
20805 * @extends Roo.menu.BaseItem
20806 * A base class for all menu items that require menu-related functionality (like sub-menus) and are not static
20807 * display items. Item extends the base functionality of {@link Roo.menu.BaseItem} by adding menu-specific
20808 * activation and click handling.
20810 * Creates a new Item
20811 * @param {Object} config Configuration options
20813 Roo.menu.Item = function(config){
20814 Roo.menu.Item.superclass.constructor.call(this, config);
20816 this.menu = Roo.menu.MenuMgr.get(this.menu);
20819 Roo.extend(Roo.menu.Item, Roo.menu.BaseItem, {
20822 * @cfg {String} text
20823 * The text to show on the menu item.
20827 * @cfg {String} HTML to render in menu
20828 * The text to show on the menu item (HTML version).
20832 * @cfg {String} icon
20833 * The path to an icon to display in this menu item (defaults to Roo.BLANK_IMAGE_URL)
20837 * @cfg {String} itemCls The default CSS class to use for menu items (defaults to "x-menu-item")
20839 itemCls : "x-menu-item",
20841 * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to true)
20843 canActivate : true,
20845 * @cfg {Number} showDelay Length of time in milliseconds to wait before showing this item (defaults to 200)
20848 // doc'd in BaseItem
20852 ctype: "Roo.menu.Item",
20855 onRender : function(container, position){
20856 var el = document.createElement("a");
20857 el.hideFocus = true;
20858 el.unselectable = "on";
20859 el.href = this.href || "#";
20860 if(this.hrefTarget){
20861 el.target = this.hrefTarget;
20863 el.className = this.itemCls + (this.menu ? " x-menu-item-arrow" : "") + (this.cls ? " " + this.cls : "");
20865 var html = this.html.length ? this.html : String.format('{0}',this.text);
20867 el.innerHTML = String.format(
20868 '<img src="{0}" class="x-menu-item-icon {1}" />' + html,
20869 this.icon || Roo.BLANK_IMAGE_URL, this.iconCls || '');
20871 Roo.menu.Item.superclass.onRender.call(this, container, position);
20875 * Sets the text to display in this menu item
20876 * @param {String} text The text to display
20877 * @param {Boolean} isHTML true to indicate text is pure html.
20879 setText : function(text, isHTML){
20887 var html = this.html.length ? this.html : String.format('{0}',this.text);
20889 this.el.update(String.format(
20890 '<img src="{0}" class="x-menu-item-icon {2}">' + html,
20891 this.icon || Roo.BLANK_IMAGE_URL, this.text, this.iconCls || ''));
20892 this.parentMenu.autoWidth();
20897 handleClick : function(e){
20898 if(!this.href){ // if no link defined, stop the event automatically
20901 Roo.menu.Item.superclass.handleClick.apply(this, arguments);
20905 activate : function(autoExpand){
20906 if(Roo.menu.Item.superclass.activate.apply(this, arguments)){
20916 shouldDeactivate : function(e){
20917 if(Roo.menu.Item.superclass.shouldDeactivate.call(this, e)){
20918 if(this.menu && this.menu.isVisible()){
20919 return !this.menu.getEl().getRegion().contains(e.getPoint());
20927 deactivate : function(){
20928 Roo.menu.Item.superclass.deactivate.apply(this, arguments);
20933 expandMenu : function(autoActivate){
20934 if(!this.disabled && this.menu){
20935 clearTimeout(this.hideTimer);
20936 delete this.hideTimer;
20937 if(!this.menu.isVisible() && !this.showTimer){
20938 this.showTimer = this.deferExpand.defer(this.showDelay, this, [autoActivate]);
20939 }else if (this.menu.isVisible() && autoActivate){
20940 this.menu.tryActivate(0, 1);
20946 deferExpand : function(autoActivate){
20947 delete this.showTimer;
20948 this.menu.show(this.container, this.parentMenu.subMenuAlign || "tl-tr?", this.parentMenu);
20950 this.menu.tryActivate(0, 1);
20955 hideMenu : function(){
20956 clearTimeout(this.showTimer);
20957 delete this.showTimer;
20958 if(!this.hideTimer && this.menu && this.menu.isVisible()){
20959 this.hideTimer = this.deferHide.defer(this.hideDelay, this);
20964 deferHide : function(){
20965 delete this.hideTimer;
20970 * Ext JS Library 1.1.1
20971 * Copyright(c) 2006-2007, Ext JS, LLC.
20973 * Originally Released Under LGPL - original licence link has changed is not relivant.
20976 * <script type="text/javascript">
20980 * @class Roo.menu.CheckItem
20981 * @extends Roo.menu.Item
20982 * Adds a menu item that contains a checkbox by default, but can also be part of a radio group.
20984 * Creates a new CheckItem
20985 * @param {Object} config Configuration options
20987 Roo.menu.CheckItem = function(config){
20988 Roo.menu.CheckItem.superclass.constructor.call(this, config);
20991 * @event beforecheckchange
20992 * Fires before the checked value is set, providing an opportunity to cancel if needed
20993 * @param {Roo.menu.CheckItem} this
20994 * @param {Boolean} checked The new checked value that will be set
20996 "beforecheckchange" : true,
20998 * @event checkchange
20999 * Fires after the checked value has been set
21000 * @param {Roo.menu.CheckItem} this
21001 * @param {Boolean} checked The checked value that was set
21003 "checkchange" : true
21005 if(this.checkHandler){
21006 this.on('checkchange', this.checkHandler, this.scope);
21009 Roo.extend(Roo.menu.CheckItem, Roo.menu.Item, {
21011 * @cfg {String} group
21012 * All check items with the same group name will automatically be grouped into a single-select
21013 * radio button group (defaults to '')
21016 * @cfg {String} itemCls The default CSS class to use for check items (defaults to "x-menu-item x-menu-check-item")
21018 itemCls : "x-menu-item x-menu-check-item",
21020 * @cfg {String} groupClass The default CSS class to use for radio group check items (defaults to "x-menu-group-item")
21022 groupClass : "x-menu-group-item",
21025 * @cfg {Boolean} checked True to initialize this checkbox as checked (defaults to false). Note that
21026 * if this checkbox is part of a radio group (group = true) only the last item in the group that is
21027 * initialized with checked = true will be rendered as checked.
21032 ctype: "Roo.menu.CheckItem",
21035 onRender : function(c){
21036 Roo.menu.CheckItem.superclass.onRender.apply(this, arguments);
21038 this.el.addClass(this.groupClass);
21040 Roo.menu.MenuMgr.registerCheckable(this);
21042 this.checked = false;
21043 this.setChecked(true, true);
21048 destroy : function(){
21050 Roo.menu.MenuMgr.unregisterCheckable(this);
21052 Roo.menu.CheckItem.superclass.destroy.apply(this, arguments);
21056 * Set the checked state of this item
21057 * @param {Boolean} checked The new checked value
21058 * @param {Boolean} suppressEvent (optional) True to prevent the checkchange event from firing (defaults to false)
21060 setChecked : function(state, suppressEvent){
21061 if(this.checked != state && this.fireEvent("beforecheckchange", this, state) !== false){
21062 if(this.container){
21063 this.container[state ? "addClass" : "removeClass"]("x-menu-item-checked");
21065 this.checked = state;
21066 if(suppressEvent !== true){
21067 this.fireEvent("checkchange", this, state);
21073 handleClick : function(e){
21074 if(!this.disabled && !(this.checked && this.group)){// disable unselect on radio item
21075 this.setChecked(!this.checked);
21077 Roo.menu.CheckItem.superclass.handleClick.apply(this, arguments);
21081 * Ext JS Library 1.1.1
21082 * Copyright(c) 2006-2007, Ext JS, LLC.
21084 * Originally Released Under LGPL - original licence link has changed is not relivant.
21087 * <script type="text/javascript">
21091 * @class Roo.menu.DateItem
21092 * @extends Roo.menu.Adapter
21093 * A menu item that wraps the {@link Roo.DatPicker} component.
21095 * Creates a new DateItem
21096 * @param {Object} config Configuration options
21098 Roo.menu.DateItem = function(config){
21099 Roo.menu.DateItem.superclass.constructor.call(this, new Roo.DatePicker(config), config);
21100 /** The Roo.DatePicker object @type Roo.DatePicker */
21101 this.picker = this.component;
21102 this.addEvents({select: true});
21104 this.picker.on("render", function(picker){
21105 picker.getEl().swallowEvent("click");
21106 picker.container.addClass("x-menu-date-item");
21109 this.picker.on("select", this.onSelect, this);
21112 Roo.extend(Roo.menu.DateItem, Roo.menu.Adapter, {
21114 onSelect : function(picker, date){
21115 this.fireEvent("select", this, date, picker);
21116 Roo.menu.DateItem.superclass.handleClick.call(this);
21120 * Ext JS Library 1.1.1
21121 * Copyright(c) 2006-2007, Ext JS, LLC.
21123 * Originally Released Under LGPL - original licence link has changed is not relivant.
21126 * <script type="text/javascript">
21130 * @class Roo.menu.ColorItem
21131 * @extends Roo.menu.Adapter
21132 * A menu item that wraps the {@link Roo.ColorPalette} component.
21134 * Creates a new ColorItem
21135 * @param {Object} config Configuration options
21137 Roo.menu.ColorItem = function(config){
21138 Roo.menu.ColorItem.superclass.constructor.call(this, new Roo.ColorPalette(config), config);
21139 /** The Roo.ColorPalette object @type Roo.ColorPalette */
21140 this.palette = this.component;
21141 this.relayEvents(this.palette, ["select"]);
21142 if(this.selectHandler){
21143 this.on('select', this.selectHandler, this.scope);
21146 Roo.extend(Roo.menu.ColorItem, Roo.menu.Adapter);/*
21148 * Ext JS Library 1.1.1
21149 * Copyright(c) 2006-2007, Ext JS, LLC.
21151 * Originally Released Under LGPL - original licence link has changed is not relivant.
21154 * <script type="text/javascript">
21159 * @class Roo.menu.DateMenu
21160 * @extends Roo.menu.Menu
21161 * A menu containing a {@link Roo.menu.DateItem} component (which provides a date picker).
21163 * Creates a new DateMenu
21164 * @param {Object} config Configuration options
21166 Roo.menu.DateMenu = function(config){
21167 Roo.menu.DateMenu.superclass.constructor.call(this, config);
21169 var di = new Roo.menu.DateItem(config);
21172 * The {@link Roo.DatePicker} instance for this DateMenu
21175 this.picker = di.picker;
21178 * @param {DatePicker} picker
21179 * @param {Date} date
21181 this.relayEvents(di, ["select"]);
21182 this.on('beforeshow', function(){
21184 this.picker.hideMonthPicker(false);
21188 Roo.extend(Roo.menu.DateMenu, Roo.menu.Menu, {
21192 * Ext JS Library 1.1.1
21193 * Copyright(c) 2006-2007, Ext JS, LLC.
21195 * Originally Released Under LGPL - original licence link has changed is not relivant.
21198 * <script type="text/javascript">
21203 * @class Roo.menu.ColorMenu
21204 * @extends Roo.menu.Menu
21205 * A menu containing a {@link Roo.menu.ColorItem} component (which provides a basic color picker).
21207 * Creates a new ColorMenu
21208 * @param {Object} config Configuration options
21210 Roo.menu.ColorMenu = function(config){
21211 Roo.menu.ColorMenu.superclass.constructor.call(this, config);
21213 var ci = new Roo.menu.ColorItem(config);
21216 * The {@link Roo.ColorPalette} instance for this ColorMenu
21217 * @type ColorPalette
21219 this.palette = ci.palette;
21222 * @param {ColorPalette} palette
21223 * @param {String} color
21225 this.relayEvents(ci, ["select"]);
21227 Roo.extend(Roo.menu.ColorMenu, Roo.menu.Menu);/*
21229 * Ext JS Library 1.1.1
21230 * Copyright(c) 2006-2007, Ext JS, LLC.
21232 * Originally Released Under LGPL - original licence link has changed is not relivant.
21235 * <script type="text/javascript">
21239 * @class Roo.form.Field
21240 * @extends Roo.BoxComponent
21241 * Base class for form fields that provides default event handling, sizing, value handling and other functionality.
21243 * Creates a new Field
21244 * @param {Object} config Configuration options
21246 Roo.form.Field = function(config){
21247 Roo.form.Field.superclass.constructor.call(this, config);
21250 Roo.extend(Roo.form.Field, Roo.BoxComponent, {
21252 * @cfg {String} fieldLabel Label to use when rendering a form.
21255 * @cfg {String} qtip Mouse over tip
21259 * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
21261 invalidClass : "x-form-invalid",
21263 * @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")
21265 invalidText : "The value in this field is invalid",
21267 * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
21269 focusClass : "x-form-focus",
21271 * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
21272 automatic validation (defaults to "keyup").
21274 validationEvent : "keyup",
21276 * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
21278 validateOnBlur : true,
21280 * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
21282 validationDelay : 250,
21284 * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
21285 * {tag: "input", type: "text", size: "20", autocomplete: "off"})
21287 defaultAutoCreate : {tag: "input", type: "text", size: "20", autocomplete: "off"},
21289 * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field")
21291 fieldClass : "x-form-field",
21293 * @cfg {String} msgTarget The location where error text should display. Should be one of the following values (defaults to 'qtip'):
21296 ----------- ----------------------------------------------------------------------
21297 qtip Display a quick tip when the user hovers over the field
21298 title Display a default browser title attribute popup
21299 under Add a block div beneath the field containing the error text
21300 side Add an error icon to the right of the field with a popup on hover
21301 [element id] Add the error text directly to the innerHTML of the specified element
21304 msgTarget : 'qtip',
21306 * @cfg {String} msgFx <b>Experimental</b> The effect used when displaying a validation message under the field (defaults to 'normal').
21311 * @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.
21316 * @cfg {Boolean} disabled True to disable the field (defaults to false).
21321 * @cfg {String} inputType The type attribute for input fields -- e.g. radio, text, password (defaults to "text").
21323 inputType : undefined,
21326 * @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).
21328 tabIndex : undefined,
21331 isFormField : true,
21336 * @property {Roo.Element} fieldEl
21337 * Element Containing the rendered Field (with label etc.)
21340 * @cfg {Mixed} value A value to initialize this field with.
21345 * @cfg {String} name The field's HTML name attribute.
21348 * @cfg {String} cls A CSS class to apply to the field's underlying element.
21352 initComponent : function(){
21353 Roo.form.Field.superclass.initComponent.call(this);
21357 * Fires when this field receives input focus.
21358 * @param {Roo.form.Field} this
21363 * Fires when this field loses input focus.
21364 * @param {Roo.form.Field} this
21368 * @event specialkey
21369 * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed. You can check
21370 * {@link Roo.EventObject#getKey} to determine which key was pressed.
21371 * @param {Roo.form.Field} this
21372 * @param {Roo.EventObject} e The event object
21377 * Fires just before the field blurs if the field value has changed.
21378 * @param {Roo.form.Field} this
21379 * @param {Mixed} newValue The new value
21380 * @param {Mixed} oldValue The original value
21385 * Fires after the field has been marked as invalid.
21386 * @param {Roo.form.Field} this
21387 * @param {String} msg The validation message
21392 * Fires after the field has been validated with no errors.
21393 * @param {Roo.form.Field} this
21398 * Fires after the key up
21399 * @param {Roo.form.Field} this
21400 * @param {Roo.EventObject} e The event Object
21407 * Returns the name attribute of the field if available
21408 * @return {String} name The field name
21410 getName: function(){
21411 return this.rendered && this.el.dom.name ? this.el.dom.name : (this.hiddenName || '');
21415 onRender : function(ct, position){
21416 Roo.form.Field.superclass.onRender.call(this, ct, position);
21418 var cfg = this.getAutoCreate();
21420 cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
21422 if (!cfg.name.length) {
21425 if(this.inputType){
21426 cfg.type = this.inputType;
21428 this.el = ct.createChild(cfg, position);
21430 var type = this.el.dom.type;
21432 if(type == 'password'){
21435 this.el.addClass('x-form-'+type);
21438 this.el.dom.readOnly = true;
21440 if(this.tabIndex !== undefined){
21441 this.el.dom.setAttribute('tabIndex', this.tabIndex);
21444 this.el.addClass([this.fieldClass, this.cls]);
21449 * Apply the behaviors of this component to an existing element. <b>This is used instead of render().</b>
21450 * @param {String/HTMLElement/Element} el The id of the node, a DOM node or an existing Element
21451 * @return {Roo.form.Field} this
21453 applyTo : function(target){
21454 this.allowDomMove = false;
21455 this.el = Roo.get(target);
21456 this.render(this.el.dom.parentNode);
21461 initValue : function(){
21462 if(this.value !== undefined){
21463 this.setValue(this.value);
21464 }else if(this.el.dom.value.length > 0){
21465 this.setValue(this.el.dom.value);
21470 * Returns true if this field has been changed since it was originally loaded and is not disabled.
21472 isDirty : function() {
21473 if(this.disabled) {
21476 return String(this.getValue()) !== String(this.originalValue);
21480 afterRender : function(){
21481 Roo.form.Field.superclass.afterRender.call(this);
21486 fireKey : function(e){
21487 //Roo.log('field ' + e.getKey());
21488 if(e.isNavKeyPress()){
21489 this.fireEvent("specialkey", this, e);
21494 * Resets the current field value to the originally loaded value and clears any validation messages
21496 reset : function(){
21497 this.setValue(this.originalValue);
21498 this.clearInvalid();
21502 initEvents : function(){
21503 // safari killled keypress - so keydown is now used..
21504 this.el.on("keydown" , this.fireKey, this);
21505 this.el.on("focus", this.onFocus, this);
21506 this.el.on("blur", this.onBlur, this);
21507 this.el.relayEvent('keyup', this);
21509 // reference to original value for reset
21510 this.originalValue = this.getValue();
21514 onFocus : function(){
21515 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
21516 this.el.addClass(this.focusClass);
21518 if(!this.hasFocus){
21519 this.hasFocus = true;
21520 this.startValue = this.getValue();
21521 this.fireEvent("focus", this);
21525 beforeBlur : Roo.emptyFn,
21528 onBlur : function(){
21530 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
21531 this.el.removeClass(this.focusClass);
21533 this.hasFocus = false;
21534 if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
21537 var v = this.getValue();
21538 if(String(v) !== String(this.startValue)){
21539 this.fireEvent('change', this, v, this.startValue);
21541 this.fireEvent("blur", this);
21545 * Returns whether or not the field value is currently valid
21546 * @param {Boolean} preventMark True to disable marking the field invalid
21547 * @return {Boolean} True if the value is valid, else false
21549 isValid : function(preventMark){
21553 var restore = this.preventMark;
21554 this.preventMark = preventMark === true;
21555 var v = this.validateValue(this.processValue(this.getRawValue()));
21556 this.preventMark = restore;
21561 * Validates the field value
21562 * @return {Boolean} True if the value is valid, else false
21564 validate : function(){
21565 if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
21566 this.clearInvalid();
21572 processValue : function(value){
21577 // Subclasses should provide the validation implementation by overriding this
21578 validateValue : function(value){
21583 * Mark this field as invalid
21584 * @param {String} msg The validation message
21586 markInvalid : function(msg){
21587 if(!this.rendered || this.preventMark){ // not rendered
21590 this.el.addClass(this.invalidClass);
21591 msg = msg || this.invalidText;
21592 switch(this.msgTarget){
21594 this.el.dom.qtip = msg;
21595 this.el.dom.qclass = 'x-form-invalid-tip';
21596 if(Roo.QuickTips){ // fix for floating editors interacting with DND
21597 Roo.QuickTips.enable();
21601 this.el.dom.title = msg;
21605 var elp = this.el.findParent('.x-form-element', 5, true);
21606 this.errorEl = elp.createChild({cls:'x-form-invalid-msg'});
21607 this.errorEl.setWidth(elp.getWidth(true)-20);
21609 this.errorEl.update(msg);
21610 Roo.form.Field.msgFx[this.msgFx].show(this.errorEl, this);
21613 if(!this.errorIcon){
21614 var elp = this.el.findParent('.x-form-element', 5, true);
21615 this.errorIcon = elp.createChild({cls:'x-form-invalid-icon'});
21617 this.alignErrorIcon();
21618 this.errorIcon.dom.qtip = msg;
21619 this.errorIcon.dom.qclass = 'x-form-invalid-tip';
21620 this.errorIcon.show();
21621 this.on('resize', this.alignErrorIcon, this);
21624 var t = Roo.getDom(this.msgTarget);
21626 t.style.display = this.msgDisplay;
21629 this.fireEvent('invalid', this, msg);
21633 alignErrorIcon : function(){
21634 this.errorIcon.alignTo(this.el, 'tl-tr', [2, 0]);
21638 * Clear any invalid styles/messages for this field
21640 clearInvalid : function(){
21641 if(!this.rendered || this.preventMark){ // not rendered
21644 this.el.removeClass(this.invalidClass);
21645 switch(this.msgTarget){
21647 this.el.dom.qtip = '';
21650 this.el.dom.title = '';
21654 Roo.form.Field.msgFx[this.msgFx].hide(this.errorEl, this);
21658 if(this.errorIcon){
21659 this.errorIcon.dom.qtip = '';
21660 this.errorIcon.hide();
21661 this.un('resize', this.alignErrorIcon, this);
21665 var t = Roo.getDom(this.msgTarget);
21667 t.style.display = 'none';
21670 this.fireEvent('valid', this);
21674 * Returns the raw data value which may or may not be a valid, defined value. To return a normalized value see {@link #getValue}.
21675 * @return {Mixed} value The field value
21677 getRawValue : function(){
21678 var v = this.el.getValue();
21684 * Returns the normalized data value (undefined or emptyText will be returned as ''). To return the raw value see {@link #getRawValue}.
21685 * @return {Mixed} value The field value
21687 getValue : function(){
21688 var v = this.el.getValue();
21694 * Sets the underlying DOM field's value directly, bypassing validation. To set the value with validation see {@link #setValue}.
21695 * @param {Mixed} value The value to set
21697 setRawValue : function(v){
21698 return this.el.dom.value = (v === null || v === undefined ? '' : v);
21702 * Sets a data value into the field and validates it. To set the value directly without validation see {@link #setRawValue}.
21703 * @param {Mixed} value The value to set
21705 setValue : function(v){
21708 this.el.dom.value = (v === null || v === undefined ? '' : v);
21713 adjustSize : function(w, h){
21714 var s = Roo.form.Field.superclass.adjustSize.call(this, w, h);
21715 s.width = this.adjustWidth(this.el.dom.tagName, s.width);
21719 adjustWidth : function(tag, w){
21720 tag = tag.toLowerCase();
21721 if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
21722 if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
21723 if(tag == 'input'){
21726 if(tag == 'textarea'){
21729 }else if(Roo.isOpera){
21730 if(tag == 'input'){
21733 if(tag == 'textarea'){
21743 // anything other than normal should be considered experimental
21744 Roo.form.Field.msgFx = {
21746 show: function(msgEl, f){
21747 msgEl.setDisplayed('block');
21750 hide : function(msgEl, f){
21751 msgEl.setDisplayed(false).update('');
21756 show: function(msgEl, f){
21757 msgEl.slideIn('t', {stopFx:true});
21760 hide : function(msgEl, f){
21761 msgEl.slideOut('t', {stopFx:true,useDisplay:true});
21766 show: function(msgEl, f){
21767 msgEl.fixDisplay();
21768 msgEl.alignTo(f.el, 'tl-tr');
21769 msgEl.slideIn('l', {stopFx:true});
21772 hide : function(msgEl, f){
21773 msgEl.slideOut('l', {stopFx:true,useDisplay:true});
21778 * Ext JS Library 1.1.1
21779 * Copyright(c) 2006-2007, Ext JS, LLC.
21781 * Originally Released Under LGPL - original licence link has changed is not relivant.
21784 * <script type="text/javascript">
21789 * @class Roo.form.TextField
21790 * @extends Roo.form.Field
21791 * Basic text field. Can be used as a direct replacement for traditional text inputs, or as the base
21792 * class for more sophisticated input controls (like {@link Roo.form.TextArea} and {@link Roo.form.ComboBox}).
21794 * Creates a new TextField
21795 * @param {Object} config Configuration options
21797 Roo.form.TextField = function(config){
21798 Roo.form.TextField.superclass.constructor.call(this, config);
21802 * Fires when the autosize function is triggered. The field may or may not have actually changed size
21803 * according to the default logic, but this event provides a hook for the developer to apply additional
21804 * logic at runtime to resize the field if needed.
21805 * @param {Roo.form.Field} this This text field
21806 * @param {Number} width The new field width
21812 Roo.extend(Roo.form.TextField, Roo.form.Field, {
21814 * @cfg {Boolean} grow True if this field should automatically grow and shrink to its content
21818 * @cfg {Number} growMin The minimum width to allow when grow = true (defaults to 30)
21822 * @cfg {Number} growMax The maximum width to allow when grow = true (defaults to 800)
21826 * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
21830 * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
21834 * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
21836 disableKeyFilter : false,
21838 * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
21842 * @cfg {Number} minLength Minimum input field length required (defaults to 0)
21846 * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
21848 maxLength : Number.MAX_VALUE,
21850 * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
21852 minLengthText : "The minimum length for this field is {0}",
21854 * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
21856 maxLengthText : "The maximum length for this field is {0}",
21858 * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
21860 selectOnFocus : false,
21862 * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
21864 blankText : "This field is required",
21866 * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
21867 * If available, this function will be called only after the basic validators all return true, and will be passed the
21868 * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
21872 * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
21873 * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
21874 * current field value. If the test fails, the field will be marked invalid using {@link #regexText}.
21878 * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
21882 * @cfg {String} emptyText The default text to display in an empty field - placeholder... (defaults to null).
21888 initEvents : function()
21890 if (this.emptyText) {
21891 this.el.attr('placeholder', this.emptyText);
21894 Roo.form.TextField.superclass.initEvents.call(this);
21895 if(this.validationEvent == 'keyup'){
21896 this.validationTask = new Roo.util.DelayedTask(this.validate, this);
21897 this.el.on('keyup', this.filterValidation, this);
21899 else if(this.validationEvent !== false){
21900 this.el.on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
21903 if(this.selectOnFocus){
21904 this.on("focus", this.preFocus, this);
21907 if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
21908 this.el.on("keypress", this.filterKeys, this);
21911 this.el.on("keyup", this.onKeyUp, this, {buffer:50});
21912 this.el.on("click", this.autoSize, this);
21914 if(this.el.is('input[type=password]') && Roo.isSafari){
21915 this.el.on('keydown', this.SafariOnKeyDown, this);
21919 processValue : function(value){
21920 if(this.stripCharsRe){
21921 var newValue = value.replace(this.stripCharsRe, '');
21922 if(newValue !== value){
21923 this.setRawValue(newValue);
21930 filterValidation : function(e){
21931 if(!e.isNavKeyPress()){
21932 this.validationTask.delay(this.validationDelay);
21937 onKeyUp : function(e){
21938 if(!e.isNavKeyPress()){
21944 * Resets the current field value to the originally-loaded value and clears any validation messages.
21947 reset : function(){
21948 Roo.form.TextField.superclass.reset.call(this);
21954 preFocus : function(){
21956 if(this.selectOnFocus){
21957 this.el.dom.select();
21963 filterKeys : function(e){
21964 var k = e.getKey();
21965 if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
21968 var c = e.getCharCode(), cc = String.fromCharCode(c);
21969 if(Roo.isIE && (e.isSpecialKey() || !cc)){
21972 if(!this.maskRe.test(cc)){
21977 setValue : function(v){
21979 Roo.form.TextField.superclass.setValue.apply(this, arguments);
21985 * Validates a value according to the field's validation rules and marks the field as invalid
21986 * if the validation fails
21987 * @param {Mixed} value The value to validate
21988 * @return {Boolean} True if the value is valid, else false
21990 validateValue : function(value){
21991 if(value.length < 1) { // if it's blank
21992 if(this.allowBlank){
21993 this.clearInvalid();
21996 this.markInvalid(this.blankText);
22000 if(value.length < this.minLength){
22001 this.markInvalid(String.format(this.minLengthText, this.minLength));
22004 if(value.length > this.maxLength){
22005 this.markInvalid(String.format(this.maxLengthText, this.maxLength));
22009 var vt = Roo.form.VTypes;
22010 if(!vt[this.vtype](value, this)){
22011 this.markInvalid(this.vtypeText || vt[this.vtype +'Text']);
22015 if(typeof this.validator == "function"){
22016 var msg = this.validator(value);
22018 this.markInvalid(msg);
22022 if(this.regex && !this.regex.test(value)){
22023 this.markInvalid(this.regexText);
22030 * Selects text in this field
22031 * @param {Number} start (optional) The index where the selection should start (defaults to 0)
22032 * @param {Number} end (optional) The index where the selection should end (defaults to the text length)
22034 selectText : function(start, end){
22035 var v = this.getRawValue();
22037 start = start === undefined ? 0 : start;
22038 end = end === undefined ? v.length : end;
22039 var d = this.el.dom;
22040 if(d.setSelectionRange){
22041 d.setSelectionRange(start, end);
22042 }else if(d.createTextRange){
22043 var range = d.createTextRange();
22044 range.moveStart("character", start);
22045 range.moveEnd("character", v.length-end);
22052 * Automatically grows the field to accomodate the width of the text up to the maximum field width allowed.
22053 * This only takes effect if grow = true, and fires the autosize event.
22055 autoSize : function(){
22056 if(!this.grow || !this.rendered){
22060 this.metrics = Roo.util.TextMetrics.createInstance(this.el);
22063 var v = el.dom.value;
22064 var d = document.createElement('div');
22065 d.appendChild(document.createTextNode(v));
22069 var w = Math.min(this.growMax, Math.max(this.metrics.getWidth(v) + /* add extra padding */ 10, this.growMin));
22070 this.el.setWidth(w);
22071 this.fireEvent("autosize", this, w);
22075 SafariOnKeyDown : function(event)
22077 // this is a workaround for a password hang bug on chrome/ webkit.
22079 var isSelectAll = false;
22081 if(this.el.dom.selectionEnd > 0){
22082 isSelectAll = (this.el.dom.selectionEnd - this.el.dom.selectionStart - this.getValue().length == 0) ? true : false;
22084 if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
22085 event.preventDefault();
22090 if(isSelectAll){ // backspace and delete key
22092 event.preventDefault();
22093 // this is very hacky as keydown always get's upper case.
22095 var cc = String.fromCharCode(event.getCharCode());
22096 this.setValue( event.shiftKey ? cc : cc.toLowerCase());
22104 * Ext JS Library 1.1.1
22105 * Copyright(c) 2006-2007, Ext JS, LLC.
22107 * Originally Released Under LGPL - original licence link has changed is not relivant.
22110 * <script type="text/javascript">
22114 * @class Roo.form.Hidden
22115 * @extends Roo.form.TextField
22116 * Simple Hidden element used on forms
22118 * usage: form.add(new Roo.form.HiddenField({ 'name' : 'test1' }));
22121 * Creates a new Hidden form element.
22122 * @param {Object} config Configuration options
22127 // easy hidden field...
22128 Roo.form.Hidden = function(config){
22129 Roo.form.Hidden.superclass.constructor.call(this, config);
22132 Roo.extend(Roo.form.Hidden, Roo.form.TextField, {
22134 inputType: 'hidden',
22137 labelSeparator: '',
22139 itemCls : 'x-form-item-display-none'
22147 * Ext JS Library 1.1.1
22148 * Copyright(c) 2006-2007, Ext JS, LLC.
22150 * Originally Released Under LGPL - original licence link has changed is not relivant.
22153 * <script type="text/javascript">
22157 * @class Roo.form.TriggerField
22158 * @extends Roo.form.TextField
22159 * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
22160 * The trigger has no default action, so you must assign a function to implement the trigger click handler by
22161 * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
22162 * for which you can provide a custom implementation. For example:
22164 var trigger = new Roo.form.TriggerField();
22165 trigger.onTriggerClick = myTriggerFn;
22166 trigger.applyTo('my-field');
22169 * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
22170 * {@link Roo.form.DateField} and {@link Roo.form.ComboBox} are perfect examples of this.
22171 * @cfg {String} triggerClass An additional CSS class used to style the trigger button. The trigger will always get the
22172 * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
22174 * Create a new TriggerField.
22175 * @param {Object} config Configuration options (valid {@Roo.form.TextField} config options will also be applied
22176 * to the base TextField)
22178 Roo.form.TriggerField = function(config){
22179 this.mimicing = false;
22180 Roo.form.TriggerField.superclass.constructor.call(this, config);
22183 Roo.extend(Roo.form.TriggerField, Roo.form.TextField, {
22185 * @cfg {String} triggerClass A CSS class to apply to the trigger
22188 * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
22189 * {tag: "input", type: "text", size: "16", autocomplete: "off"})
22191 defaultAutoCreate : {tag: "input", type: "text", size: "16", autocomplete: "off"},
22193 * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
22197 /** @cfg {Boolean} grow @hide */
22198 /** @cfg {Number} growMin @hide */
22199 /** @cfg {Number} growMax @hide */
22205 autoSize: Roo.emptyFn,
22209 deferHeight : true,
22212 actionMode : 'wrap',
22214 onResize : function(w, h){
22215 Roo.form.TriggerField.superclass.onResize.apply(this, arguments);
22216 if(typeof w == 'number'){
22217 var x = w - this.trigger.getWidth();
22218 this.el.setWidth(this.adjustWidth('input', x));
22219 this.trigger.setStyle('left', x+'px');
22224 adjustSize : Roo.BoxComponent.prototype.adjustSize,
22227 getResizeEl : function(){
22232 getPositionEl : function(){
22237 alignErrorIcon : function(){
22238 this.errorIcon.alignTo(this.wrap, 'tl-tr', [2, 0]);
22242 onRender : function(ct, position){
22243 Roo.form.TriggerField.superclass.onRender.call(this, ct, position);
22244 this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
22245 this.trigger = this.wrap.createChild(this.triggerConfig ||
22246 {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.triggerClass});
22247 if(this.hideTrigger){
22248 this.trigger.setDisplayed(false);
22250 this.initTrigger();
22252 this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
22257 initTrigger : function(){
22258 this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
22259 this.trigger.addClassOnOver('x-form-trigger-over');
22260 this.trigger.addClassOnClick('x-form-trigger-click');
22264 onDestroy : function(){
22266 this.trigger.removeAllListeners();
22267 this.trigger.remove();
22270 this.wrap.remove();
22272 Roo.form.TriggerField.superclass.onDestroy.call(this);
22276 onFocus : function(){
22277 Roo.form.TriggerField.superclass.onFocus.call(this);
22278 if(!this.mimicing){
22279 this.wrap.addClass('x-trigger-wrap-focus');
22280 this.mimicing = true;
22281 Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
22282 if(this.monitorTab){
22283 this.el.on("keydown", this.checkTab, this);
22289 checkTab : function(e){
22290 if(e.getKey() == e.TAB){
22291 this.triggerBlur();
22296 onBlur : function(){
22301 mimicBlur : function(e, t){
22302 if(!this.wrap.contains(t) && this.validateBlur()){
22303 this.triggerBlur();
22308 triggerBlur : function(){
22309 this.mimicing = false;
22310 Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
22311 if(this.monitorTab){
22312 this.el.un("keydown", this.checkTab, this);
22314 this.wrap.removeClass('x-trigger-wrap-focus');
22315 Roo.form.TriggerField.superclass.onBlur.call(this);
22319 // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
22320 validateBlur : function(e, t){
22325 onDisable : function(){
22326 Roo.form.TriggerField.superclass.onDisable.call(this);
22328 this.wrap.addClass('x-item-disabled');
22333 onEnable : function(){
22334 Roo.form.TriggerField.superclass.onEnable.call(this);
22336 this.wrap.removeClass('x-item-disabled');
22341 onShow : function(){
22342 var ae = this.getActionEl();
22345 ae.dom.style.display = '';
22346 ae.dom.style.visibility = 'visible';
22352 onHide : function(){
22353 var ae = this.getActionEl();
22354 ae.dom.style.display = 'none';
22358 * The function that should handle the trigger's click event. This method does nothing by default until overridden
22359 * by an implementing function.
22361 * @param {EventObject} e
22363 onTriggerClick : Roo.emptyFn
22366 // TwinTriggerField is not a public class to be used directly. It is meant as an abstract base class
22367 // to be extended by an implementing class. For an example of implementing this class, see the custom
22368 // SearchField implementation here: http://extjs.com/deploy/ext/examples/form/custom.html
22369 Roo.form.TwinTriggerField = Roo.extend(Roo.form.TriggerField, {
22370 initComponent : function(){
22371 Roo.form.TwinTriggerField.superclass.initComponent.call(this);
22373 this.triggerConfig = {
22374 tag:'span', cls:'x-form-twin-triggers', cn:[
22375 {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger1Class},
22376 {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger2Class}
22380 getTrigger : function(index){
22381 return this.triggers[index];
22384 initTrigger : function(){
22385 var ts = this.trigger.select('.x-form-trigger', true);
22386 this.wrap.setStyle('overflow', 'hidden');
22387 var triggerField = this;
22388 ts.each(function(t, all, index){
22389 t.hide = function(){
22390 var w = triggerField.wrap.getWidth();
22391 this.dom.style.display = 'none';
22392 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
22394 t.show = function(){
22395 var w = triggerField.wrap.getWidth();
22396 this.dom.style.display = '';
22397 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
22399 var triggerIndex = 'Trigger'+(index+1);
22401 if(this['hide'+triggerIndex]){
22402 t.dom.style.display = 'none';
22404 t.on("click", this['on'+triggerIndex+'Click'], this, {preventDefault:true});
22405 t.addClassOnOver('x-form-trigger-over');
22406 t.addClassOnClick('x-form-trigger-click');
22408 this.triggers = ts.elements;
22411 onTrigger1Click : Roo.emptyFn,
22412 onTrigger2Click : Roo.emptyFn
22415 * Ext JS Library 1.1.1
22416 * Copyright(c) 2006-2007, Ext JS, LLC.
22418 * Originally Released Under LGPL - original licence link has changed is not relivant.
22421 * <script type="text/javascript">
22425 * @class Roo.form.TextArea
22426 * @extends Roo.form.TextField
22427 * Multiline text field. Can be used as a direct replacement for traditional textarea fields, plus adds
22428 * support for auto-sizing.
22430 * Creates a new TextArea
22431 * @param {Object} config Configuration options
22433 Roo.form.TextArea = function(config){
22434 Roo.form.TextArea.superclass.constructor.call(this, config);
22435 // these are provided exchanges for backwards compat
22436 // minHeight/maxHeight were replaced by growMin/growMax to be
22437 // compatible with TextField growing config values
22438 if(this.minHeight !== undefined){
22439 this.growMin = this.minHeight;
22441 if(this.maxHeight !== undefined){
22442 this.growMax = this.maxHeight;
22446 Roo.extend(Roo.form.TextArea, Roo.form.TextField, {
22448 * @cfg {Number} growMin The minimum height to allow when grow = true (defaults to 60)
22452 * @cfg {Number} growMax The maximum height to allow when grow = true (defaults to 1000)
22456 * @cfg {Boolean} preventScrollbars True to prevent scrollbars from appearing regardless of how much text is
22457 * in the field (equivalent to setting overflow: hidden, defaults to false)
22459 preventScrollbars: false,
22461 * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
22462 * {tag: "textarea", style: "width:300px;height:60px;", autocomplete: "off"})
22466 onRender : function(ct, position){
22468 this.defaultAutoCreate = {
22470 style:"width:300px;height:60px;",
22471 autocomplete: "off"
22474 Roo.form.TextArea.superclass.onRender.call(this, ct, position);
22476 this.textSizeEl = Roo.DomHelper.append(document.body, {
22477 tag: "pre", cls: "x-form-grow-sizer"
22479 if(this.preventScrollbars){
22480 this.el.setStyle("overflow", "hidden");
22482 this.el.setHeight(this.growMin);
22486 onDestroy : function(){
22487 if(this.textSizeEl){
22488 this.textSizeEl.parentNode.removeChild(this.textSizeEl);
22490 Roo.form.TextArea.superclass.onDestroy.call(this);
22494 onKeyUp : function(e){
22495 if(!e.isNavKeyPress() || e.getKey() == e.ENTER){
22501 * Automatically grows the field to accomodate the height of the text up to the maximum field height allowed.
22502 * This only takes effect if grow = true, and fires the autosize event if the height changes.
22504 autoSize : function(){
22505 if(!this.grow || !this.textSizeEl){
22509 var v = el.dom.value;
22510 var ts = this.textSizeEl;
22513 ts.appendChild(document.createTextNode(v));
22516 Roo.fly(ts).setWidth(this.el.getWidth());
22518 v = "  ";
22521 v = v.replace(/\n/g, '<p> </p>');
22523 v += " \n ";
22526 var h = Math.min(this.growMax, Math.max(ts.offsetHeight, this.growMin));
22527 if(h != this.lastHeight){
22528 this.lastHeight = h;
22529 this.el.setHeight(h);
22530 this.fireEvent("autosize", this, h);
22535 * Ext JS Library 1.1.1
22536 * Copyright(c) 2006-2007, Ext JS, LLC.
22538 * Originally Released Under LGPL - original licence link has changed is not relivant.
22541 * <script type="text/javascript">
22546 * @class Roo.form.NumberField
22547 * @extends Roo.form.TextField
22548 * Numeric text field that provides automatic keystroke filtering and numeric validation.
22550 * Creates a new NumberField
22551 * @param {Object} config Configuration options
22553 Roo.form.NumberField = function(config){
22554 Roo.form.NumberField.superclass.constructor.call(this, config);
22557 Roo.extend(Roo.form.NumberField, Roo.form.TextField, {
22559 * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field x-form-num-field")
22561 fieldClass: "x-form-field x-form-num-field",
22563 * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
22565 allowDecimals : true,
22567 * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
22569 decimalSeparator : ".",
22571 * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
22573 decimalPrecision : 2,
22575 * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
22577 allowNegative : true,
22579 * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
22581 minValue : Number.NEGATIVE_INFINITY,
22583 * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
22585 maxValue : Number.MAX_VALUE,
22587 * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
22589 minText : "The minimum value for this field is {0}",
22591 * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
22593 maxText : "The maximum value for this field is {0}",
22595 * @cfg {String} nanText Error text to display if the value is not a valid number. For example, this can happen
22596 * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
22598 nanText : "{0} is not a valid number",
22601 initEvents : function(){
22602 Roo.form.NumberField.superclass.initEvents.call(this);
22603 var allowed = "0123456789";
22604 if(this.allowDecimals){
22605 allowed += this.decimalSeparator;
22607 if(this.allowNegative){
22610 this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
22611 var keyPress = function(e){
22612 var k = e.getKey();
22613 if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
22616 var c = e.getCharCode();
22617 if(allowed.indexOf(String.fromCharCode(c)) === -1){
22621 this.el.on("keypress", keyPress, this);
22625 validateValue : function(value){
22626 if(!Roo.form.NumberField.superclass.validateValue.call(this, value)){
22629 if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
22632 var num = this.parseValue(value);
22634 this.markInvalid(String.format(this.nanText, value));
22637 if(num < this.minValue){
22638 this.markInvalid(String.format(this.minText, this.minValue));
22641 if(num > this.maxValue){
22642 this.markInvalid(String.format(this.maxText, this.maxValue));
22648 getValue : function(){
22649 return this.fixPrecision(this.parseValue(Roo.form.NumberField.superclass.getValue.call(this)));
22653 parseValue : function(value){
22654 value = parseFloat(String(value).replace(this.decimalSeparator, "."));
22655 return isNaN(value) ? '' : value;
22659 fixPrecision : function(value){
22660 var nan = isNaN(value);
22661 if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
22662 return nan ? '' : value;
22664 return parseFloat(value).toFixed(this.decimalPrecision);
22667 setValue : function(v){
22668 v = this.fixPrecision(v);
22669 Roo.form.NumberField.superclass.setValue.call(this, String(v).replace(".", this.decimalSeparator));
22673 decimalPrecisionFcn : function(v){
22674 return Math.floor(v);
22677 beforeBlur : function(){
22678 var v = this.parseValue(this.getRawValue());
22685 * Ext JS Library 1.1.1
22686 * Copyright(c) 2006-2007, Ext JS, LLC.
22688 * Originally Released Under LGPL - original licence link has changed is not relivant.
22691 * <script type="text/javascript">
22695 * @class Roo.form.DateField
22696 * @extends Roo.form.TriggerField
22697 * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
22699 * Create a new DateField
22700 * @param {Object} config
22702 Roo.form.DateField = function(config){
22703 Roo.form.DateField.superclass.constructor.call(this, config);
22709 * Fires when a date is selected
22710 * @param {Roo.form.DateField} combo This combo box
22711 * @param {Date} date The date selected
22718 if(typeof this.minValue == "string") this.minValue = this.parseDate(this.minValue);
22719 if(typeof this.maxValue == "string") this.maxValue = this.parseDate(this.maxValue);
22720 this.ddMatch = null;
22721 if(this.disabledDates){
22722 var dd = this.disabledDates;
22724 for(var i = 0; i < dd.length; i++){
22726 if(i != dd.length-1) re += "|";
22728 this.ddMatch = new RegExp(re + ")");
22732 Roo.extend(Roo.form.DateField, Roo.form.TriggerField, {
22734 * @cfg {String} format
22735 * The default date format string which can be overriden for localization support. The format must be
22736 * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
22740 * @cfg {String} altFormats
22741 * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
22742 * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
22744 altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
22746 * @cfg {Array} disabledDays
22747 * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
22749 disabledDays : null,
22751 * @cfg {String} disabledDaysText
22752 * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
22754 disabledDaysText : "Disabled",
22756 * @cfg {Array} disabledDates
22757 * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
22758 * expression so they are very powerful. Some examples:
22760 * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
22761 * <li>["03/08", "09/16"] would disable those days for every year</li>
22762 * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
22763 * <li>["03/../2006"] would disable every day in March 2006</li>
22764 * <li>["^03"] would disable every day in every March</li>
22766 * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
22767 * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
22769 disabledDates : null,
22771 * @cfg {String} disabledDatesText
22772 * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
22774 disabledDatesText : "Disabled",
22776 * @cfg {Date/String} minValue
22777 * The minimum allowed date. Can be either a Javascript date object or a string date in a
22778 * valid format (defaults to null).
22782 * @cfg {Date/String} maxValue
22783 * The maximum allowed date. Can be either a Javascript date object or a string date in a
22784 * valid format (defaults to null).
22788 * @cfg {String} minText
22789 * The error text to display when the date in the cell is before minValue (defaults to
22790 * 'The date in this field must be after {minValue}').
22792 minText : "The date in this field must be equal to or after {0}",
22794 * @cfg {String} maxText
22795 * The error text to display when the date in the cell is after maxValue (defaults to
22796 * 'The date in this field must be before {maxValue}').
22798 maxText : "The date in this field must be equal to or before {0}",
22800 * @cfg {String} invalidText
22801 * The error text to display when the date in the field is invalid (defaults to
22802 * '{value} is not a valid date - it must be in the format {format}').
22804 invalidText : "{0} is not a valid date - it must be in the format {1}",
22806 * @cfg {String} triggerClass
22807 * An additional CSS class used to style the trigger button. The trigger will always get the
22808 * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
22809 * which displays a calendar icon).
22811 triggerClass : 'x-form-date-trigger',
22815 * @cfg {Boolean} useIso
22816 * if enabled, then the date field will use a hidden field to store the
22817 * real value as iso formated date. default (false)
22821 * @cfg {String/Object} autoCreate
22822 * A DomHelper element spec, or true for a default element spec (defaults to
22823 * {tag: "input", type: "text", size: "10", autocomplete: "off"})
22826 defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "off"},
22829 hiddenField: false,
22831 onRender : function(ct, position)
22833 Roo.form.DateField.superclass.onRender.call(this, ct, position);
22835 //this.el.dom.removeAttribute('name');
22836 Roo.log("Changing name?");
22837 this.el.dom.setAttribute('name', this.name + '____hidden___' );
22838 this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
22840 this.hiddenField.value = this.value ? this.formatDate(this.value, 'Y-m-d') : '';
22841 // prevent input submission
22842 this.hiddenName = this.name;
22849 validateValue : function(value)
22851 value = this.formatDate(value);
22852 if(!Roo.form.DateField.superclass.validateValue.call(this, value)){
22853 Roo.log('super failed');
22856 if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
22859 var svalue = value;
22860 value = this.parseDate(value);
22862 Roo.log('parse date failed' + svalue);
22863 this.markInvalid(String.format(this.invalidText, svalue, this.format));
22866 var time = value.getTime();
22867 if(this.minValue && time < this.minValue.getTime()){
22868 this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
22871 if(this.maxValue && time > this.maxValue.getTime()){
22872 this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
22875 if(this.disabledDays){
22876 var day = value.getDay();
22877 for(var i = 0; i < this.disabledDays.length; i++) {
22878 if(day === this.disabledDays[i]){
22879 this.markInvalid(this.disabledDaysText);
22884 var fvalue = this.formatDate(value);
22885 if(this.ddMatch && this.ddMatch.test(fvalue)){
22886 this.markInvalid(String.format(this.disabledDatesText, fvalue));
22893 // Provides logic to override the default TriggerField.validateBlur which just returns true
22894 validateBlur : function(){
22895 return !this.menu || !this.menu.isVisible();
22898 getName: function()
22900 // returns hidden if it's set..
22901 if (!this.rendered) {return ''};
22902 return !this.hiddenName && this.el.dom.name ? this.el.dom.name : (this.hiddenName || '');
22907 * Returns the current date value of the date field.
22908 * @return {Date} The date value
22910 getValue : function(){
22912 return this.hiddenField ?
22913 this.hiddenField.value :
22914 this.parseDate(Roo.form.DateField.superclass.getValue.call(this)) || "";
22918 * Sets the value of the date field. You can pass a date object or any string that can be parsed into a valid
22919 * date, using DateField.format as the date format, according to the same rules as {@link Date#parseDate}
22920 * (the default format used is "m/d/y").
22923 //All of these calls set the same date value (May 4, 2006)
22925 //Pass a date object:
22926 var dt = new Date('5/4/06');
22927 dateField.setValue(dt);
22929 //Pass a date string (default format):
22930 dateField.setValue('5/4/06');
22932 //Pass a date string (custom format):
22933 dateField.format = 'Y-m-d';
22934 dateField.setValue('2006-5-4');
22936 * @param {String/Date} date The date or valid date string
22938 setValue : function(date){
22939 if (this.hiddenField) {
22940 this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
22942 Roo.form.DateField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
22943 // make sure the value field is always stored as a date..
22944 this.value = this.parseDate(date);
22950 parseDate : function(value){
22951 if(!value || value instanceof Date){
22954 var v = Date.parseDate(value, this.format);
22955 if (!v && this.useIso) {
22956 v = Date.parseDate(value, 'Y-m-d');
22958 if(!v && this.altFormats){
22959 if(!this.altFormatsArray){
22960 this.altFormatsArray = this.altFormats.split("|");
22962 for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
22963 v = Date.parseDate(value, this.altFormatsArray[i]);
22970 formatDate : function(date, fmt){
22971 return (!date || !(date instanceof Date)) ?
22972 date : date.dateFormat(fmt || this.format);
22977 select: function(m, d){
22980 this.fireEvent('select', this, d);
22982 show : function(){ // retain focus styling
22986 this.focus.defer(10, this);
22987 var ml = this.menuListeners;
22988 this.menu.un("select", ml.select, this);
22989 this.menu.un("show", ml.show, this);
22990 this.menu.un("hide", ml.hide, this);
22995 // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
22996 onTriggerClick : function(){
23000 if(this.menu == null){
23001 this.menu = new Roo.menu.DateMenu();
23003 Roo.apply(this.menu.picker, {
23004 showClear: this.allowBlank,
23005 minDate : this.minValue,
23006 maxDate : this.maxValue,
23007 disabledDatesRE : this.ddMatch,
23008 disabledDatesText : this.disabledDatesText,
23009 disabledDays : this.disabledDays,
23010 disabledDaysText : this.disabledDaysText,
23011 format : this.useIso ? 'Y-m-d' : this.format,
23012 minText : String.format(this.minText, this.formatDate(this.minValue)),
23013 maxText : String.format(this.maxText, this.formatDate(this.maxValue))
23015 this.menu.on(Roo.apply({}, this.menuListeners, {
23018 this.menu.picker.setValue(this.getValue() || new Date());
23019 this.menu.show(this.el, "tl-bl?");
23022 beforeBlur : function(){
23023 var v = this.parseDate(this.getRawValue());
23029 /** @cfg {Boolean} grow @hide */
23030 /** @cfg {Number} growMin @hide */
23031 /** @cfg {Number} growMax @hide */
23038 * Ext JS Library 1.1.1
23039 * Copyright(c) 2006-2007, Ext JS, LLC.
23041 * Originally Released Under LGPL - original licence link has changed is not relivant.
23044 * <script type="text/javascript">
23048 * @class Roo.form.MonthField
23049 * @extends Roo.form.TriggerField
23050 * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
23052 * Create a new MonthField
23053 * @param {Object} config
23055 Roo.form.MonthField = function(config){
23057 Roo.form.MonthField.superclass.constructor.call(this, config);
23063 * Fires when a date is selected
23064 * @param {Roo.form.MonthFieeld} combo This combo box
23065 * @param {Date} date The date selected
23072 if(typeof this.minValue == "string") this.minValue = this.parseDate(this.minValue);
23073 if(typeof this.maxValue == "string") this.maxValue = this.parseDate(this.maxValue);
23074 this.ddMatch = null;
23075 if(this.disabledDates){
23076 var dd = this.disabledDates;
23078 for(var i = 0; i < dd.length; i++){
23080 if(i != dd.length-1) re += "|";
23082 this.ddMatch = new RegExp(re + ")");
23086 Roo.extend(Roo.form.MonthField, Roo.form.TriggerField, {
23088 * @cfg {String} format
23089 * The default date format string which can be overriden for localization support. The format must be
23090 * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
23094 * @cfg {String} altFormats
23095 * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
23096 * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
23098 altFormats : "M Y|m/Y|m-y|m-Y|my|mY",
23100 * @cfg {Array} disabledDays
23101 * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
23103 disabledDays : [0,1,2,3,4,5,6],
23105 * @cfg {String} disabledDaysText
23106 * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
23108 disabledDaysText : "Disabled",
23110 * @cfg {Array} disabledDates
23111 * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
23112 * expression so they are very powerful. Some examples:
23114 * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
23115 * <li>["03/08", "09/16"] would disable those days for every year</li>
23116 * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
23117 * <li>["03/../2006"] would disable every day in March 2006</li>
23118 * <li>["^03"] would disable every day in every March</li>
23120 * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
23121 * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
23123 disabledDates : null,
23125 * @cfg {String} disabledDatesText
23126 * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
23128 disabledDatesText : "Disabled",
23130 * @cfg {Date/String} minValue
23131 * The minimum allowed date. Can be either a Javascript date object or a string date in a
23132 * valid format (defaults to null).
23136 * @cfg {Date/String} maxValue
23137 * The maximum allowed date. Can be either a Javascript date object or a string date in a
23138 * valid format (defaults to null).
23142 * @cfg {String} minText
23143 * The error text to display when the date in the cell is before minValue (defaults to
23144 * 'The date in this field must be after {minValue}').
23146 minText : "The date in this field must be equal to or after {0}",
23148 * @cfg {String} maxTextf
23149 * The error text to display when the date in the cell is after maxValue (defaults to
23150 * 'The date in this field must be before {maxValue}').
23152 maxText : "The date in this field must be equal to or before {0}",
23154 * @cfg {String} invalidText
23155 * The error text to display when the date in the field is invalid (defaults to
23156 * '{value} is not a valid date - it must be in the format {format}').
23158 invalidText : "{0} is not a valid date - it must be in the format {1}",
23160 * @cfg {String} triggerClass
23161 * An additional CSS class used to style the trigger button. The trigger will always get the
23162 * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
23163 * which displays a calendar icon).
23165 triggerClass : 'x-form-date-trigger',
23169 * @cfg {Boolean} useIso
23170 * if enabled, then the date field will use a hidden field to store the
23171 * real value as iso formated date. default (true)
23175 * @cfg {String/Object} autoCreate
23176 * A DomHelper element spec, or true for a default element spec (defaults to
23177 * {tag: "input", type: "text", size: "10", autocomplete: "off"})
23180 defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "off"},
23183 hiddenField: false,
23185 hideMonthPicker : false,
23187 onRender : function(ct, position)
23189 Roo.form.MonthField.superclass.onRender.call(this, ct, position);
23191 this.el.dom.removeAttribute('name');
23192 this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
23194 this.hiddenField.value = this.value ? this.formatDate(this.value, 'Y-m-d') : '';
23195 // prevent input submission
23196 this.hiddenName = this.name;
23203 validateValue : function(value)
23205 value = this.formatDate(value);
23206 if(!Roo.form.MonthField.superclass.validateValue.call(this, value)){
23209 if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
23212 var svalue = value;
23213 value = this.parseDate(value);
23215 this.markInvalid(String.format(this.invalidText, svalue, this.format));
23218 var time = value.getTime();
23219 if(this.minValue && time < this.minValue.getTime()){
23220 this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
23223 if(this.maxValue && time > this.maxValue.getTime()){
23224 this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
23227 /*if(this.disabledDays){
23228 var day = value.getDay();
23229 for(var i = 0; i < this.disabledDays.length; i++) {
23230 if(day === this.disabledDays[i]){
23231 this.markInvalid(this.disabledDaysText);
23237 var fvalue = this.formatDate(value);
23238 /*if(this.ddMatch && this.ddMatch.test(fvalue)){
23239 this.markInvalid(String.format(this.disabledDatesText, fvalue));
23247 // Provides logic to override the default TriggerField.validateBlur which just returns true
23248 validateBlur : function(){
23249 return !this.menu || !this.menu.isVisible();
23253 * Returns the current date value of the date field.
23254 * @return {Date} The date value
23256 getValue : function(){
23260 return this.hiddenField ?
23261 this.hiddenField.value :
23262 this.parseDate(Roo.form.MonthField.superclass.getValue.call(this)) || "";
23266 * Sets the value of the date field. You can pass a date object or any string that can be parsed into a valid
23267 * date, using MonthField.format as the date format, according to the same rules as {@link Date#parseDate}
23268 * (the default format used is "m/d/y").
23271 //All of these calls set the same date value (May 4, 2006)
23273 //Pass a date object:
23274 var dt = new Date('5/4/06');
23275 monthField.setValue(dt);
23277 //Pass a date string (default format):
23278 monthField.setValue('5/4/06');
23280 //Pass a date string (custom format):
23281 monthField.format = 'Y-m-d';
23282 monthField.setValue('2006-5-4');
23284 * @param {String/Date} date The date or valid date string
23286 setValue : function(date){
23287 Roo.log('month setValue' + date);
23288 // can only be first of month..
23290 var val = this.parseDate(date);
23292 if (this.hiddenField) {
23293 this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
23295 Roo.form.MonthField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
23296 this.value = this.parseDate(date);
23300 parseDate : function(value){
23301 if(!value || value instanceof Date){
23302 value = value ? Date.parseDate(value.format('Y-m') + '-01', 'Y-m-d') : null;
23305 var v = Date.parseDate(value, this.format);
23306 if (!v && this.useIso) {
23307 v = Date.parseDate(value, 'Y-m-d');
23311 v = Date.parseDate(v.format('Y-m') +'-01', 'Y-m-d');
23315 if(!v && this.altFormats){
23316 if(!this.altFormatsArray){
23317 this.altFormatsArray = this.altFormats.split("|");
23319 for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
23320 v = Date.parseDate(value, this.altFormatsArray[i]);
23327 formatDate : function(date, fmt){
23328 return (!date || !(date instanceof Date)) ?
23329 date : date.dateFormat(fmt || this.format);
23334 select: function(m, d){
23336 this.fireEvent('select', this, d);
23338 show : function(){ // retain focus styling
23342 this.focus.defer(10, this);
23343 var ml = this.menuListeners;
23344 this.menu.un("select", ml.select, this);
23345 this.menu.un("show", ml.show, this);
23346 this.menu.un("hide", ml.hide, this);
23350 // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
23351 onTriggerClick : function(){
23355 if(this.menu == null){
23356 this.menu = new Roo.menu.DateMenu();
23360 Roo.apply(this.menu.picker, {
23362 showClear: this.allowBlank,
23363 minDate : this.minValue,
23364 maxDate : this.maxValue,
23365 disabledDatesRE : this.ddMatch,
23366 disabledDatesText : this.disabledDatesText,
23368 format : this.useIso ? 'Y-m-d' : this.format,
23369 minText : String.format(this.minText, this.formatDate(this.minValue)),
23370 maxText : String.format(this.maxText, this.formatDate(this.maxValue))
23373 this.menu.on(Roo.apply({}, this.menuListeners, {
23381 // hide month picker get's called when we called by 'before hide';
23383 var ignorehide = true;
23384 p.hideMonthPicker = function(disableAnim){
23388 if(this.monthPicker){
23389 Roo.log("hideMonthPicker called");
23390 if(disableAnim === true){
23391 this.monthPicker.hide();
23393 this.monthPicker.slideOut('t', {duration:.2});
23394 p.setValue(new Date(m.picker.mpSelYear, m.picker.mpSelMonth, 1));
23395 p.fireEvent("select", this, this.value);
23401 Roo.log('picker set value');
23402 Roo.log(this.getValue());
23403 p.setValue(this.getValue() ? this.parseDate(this.getValue()) : new Date());
23404 m.show(this.el, 'tl-bl?');
23405 ignorehide = false;
23406 // this will trigger hideMonthPicker..
23409 // hidden the day picker
23410 Roo.select('.x-date-picker table', true).first().dom.style.visibility = "hidden";
23416 p.showMonthPicker.defer(100, p);
23422 beforeBlur : function(){
23423 var v = this.parseDate(this.getRawValue());
23429 /** @cfg {Boolean} grow @hide */
23430 /** @cfg {Number} growMin @hide */
23431 /** @cfg {Number} growMax @hide */
23438 * Ext JS Library 1.1.1
23439 * Copyright(c) 2006-2007, Ext JS, LLC.
23441 * Originally Released Under LGPL - original licence link has changed is not relivant.
23444 * <script type="text/javascript">
23449 * @class Roo.form.ComboBox
23450 * @extends Roo.form.TriggerField
23451 * A combobox control with support for autocomplete, remote-loading, paging and many other features.
23453 * Create a new ComboBox.
23454 * @param {Object} config Configuration options
23456 Roo.form.ComboBox = function(config){
23457 Roo.form.ComboBox.superclass.constructor.call(this, config);
23461 * Fires when the dropdown list is expanded
23462 * @param {Roo.form.ComboBox} combo This combo box
23467 * Fires when the dropdown list is collapsed
23468 * @param {Roo.form.ComboBox} combo This combo box
23472 * @event beforeselect
23473 * Fires before a list item is selected. Return false to cancel the selection.
23474 * @param {Roo.form.ComboBox} combo This combo box
23475 * @param {Roo.data.Record} record The data record returned from the underlying store
23476 * @param {Number} index The index of the selected item in the dropdown list
23478 'beforeselect' : true,
23481 * Fires when a list item is selected
23482 * @param {Roo.form.ComboBox} combo This combo box
23483 * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
23484 * @param {Number} index The index of the selected item in the dropdown list
23488 * @event beforequery
23489 * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
23490 * The event object passed has these properties:
23491 * @param {Roo.form.ComboBox} combo This combo box
23492 * @param {String} query The query
23493 * @param {Boolean} forceAll true to force "all" query
23494 * @param {Boolean} cancel true to cancel the query
23495 * @param {Object} e The query event object
23497 'beforequery': true,
23500 * Fires when the 'add' icon is pressed (add a listener to enable add button)
23501 * @param {Roo.form.ComboBox} combo This combo box
23506 * Fires when the 'edit' icon is pressed (add a listener to enable add button)
23507 * @param {Roo.form.ComboBox} combo This combo box
23508 * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
23514 if(this.transform){
23515 this.allowDomMove = false;
23516 var s = Roo.getDom(this.transform);
23517 if(!this.hiddenName){
23518 this.hiddenName = s.name;
23521 this.mode = 'local';
23522 var d = [], opts = s.options;
23523 for(var i = 0, len = opts.length;i < len; i++){
23525 var value = (Roo.isIE ? o.getAttributeNode('value').specified : o.hasAttribute('value')) ? o.value : o.text;
23527 this.value = value;
23529 d.push([value, o.text]);
23531 this.store = new Roo.data.SimpleStore({
23533 fields: ['value', 'text'],
23536 this.valueField = 'value';
23537 this.displayField = 'text';
23539 s.name = Roo.id(); // wipe out the name in case somewhere else they have a reference
23540 if(!this.lazyRender){
23541 this.target = true;
23542 this.el = Roo.DomHelper.insertBefore(s, this.autoCreate || this.defaultAutoCreate);
23543 s.parentNode.removeChild(s); // remove it
23544 this.render(this.el.parentNode);
23546 s.parentNode.removeChild(s); // remove it
23551 this.store = Roo.factory(this.store, Roo.data);
23554 this.selectedIndex = -1;
23555 if(this.mode == 'local'){
23556 if(config.queryDelay === undefined){
23557 this.queryDelay = 10;
23559 if(config.minChars === undefined){
23565 Roo.extend(Roo.form.ComboBox, Roo.form.TriggerField, {
23567 * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
23570 * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
23571 * rendering into an Roo.Editor, defaults to false)
23574 * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
23575 * {tag: "input", type: "text", size: "24", autocomplete: "off"})
23578 * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
23581 * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
23582 * the dropdown list (defaults to undefined, with no header element)
23586 * @cfg {String/Roo.Template} tpl The template to use to render the output
23590 defaultAutoCreate : {tag: "input", type: "text", size: "24", autocomplete: "off"},
23592 * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
23594 listWidth: undefined,
23596 * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
23597 * mode = 'remote' or 'text' if mode = 'local')
23599 displayField: undefined,
23601 * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
23602 * mode = 'remote' or 'value' if mode = 'local').
23603 * Note: use of a valueField requires the user make a selection
23604 * in order for a value to be mapped.
23606 valueField: undefined,
23610 * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
23611 * field's data value (defaults to the underlying DOM element's name)
23613 hiddenName: undefined,
23615 * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
23619 * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
23621 selectedClass: 'x-combo-selected',
23623 * @cfg {String} triggerClass An additional CSS class used to style the trigger button. The trigger will always get the
23624 * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
23625 * which displays a downward arrow icon).
23627 triggerClass : 'x-form-arrow-trigger',
23629 * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
23633 * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
23634 * anchor positions (defaults to 'tl-bl')
23636 listAlign: 'tl-bl?',
23638 * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
23642 * @cfg {String} triggerAction The action to execute when the trigger field is activated. Use 'all' to run the
23643 * query specified by the allQuery config option (defaults to 'query')
23645 triggerAction: 'query',
23647 * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
23648 * (defaults to 4, does not apply if editable = false)
23652 * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
23653 * delay (typeAheadDelay) if it matches a known value (defaults to false)
23657 * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
23658 * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
23662 * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
23663 * filter queries will execute with page start and limit parameters. Only applies when mode = 'remote' (defaults to 0)
23667 * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus. Only applies
23668 * when editable = true (defaults to false)
23670 selectOnFocus:false,
23672 * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
23674 queryParam: 'query',
23676 * @cfg {String} loadingText The text to display in the dropdown list while data is loading. Only applies
23677 * when mode = 'remote' (defaults to 'Loading...')
23679 loadingText: 'Loading...',
23681 * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
23685 * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
23689 * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
23690 * traditional select (defaults to true)
23694 * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
23698 * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
23702 * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
23703 * listWidth has a higher value)
23707 * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
23708 * allow the user to set arbitrary text into the field (defaults to false)
23710 forceSelection:false,
23712 * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
23713 * if typeAhead = true (defaults to 250)
23715 typeAheadDelay : 250,
23717 * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
23718 * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
23720 valueNotFoundText : undefined,
23722 * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
23724 blockFocus : false,
23727 * @cfg {Boolean} disableClear Disable showing of clear button.
23729 disableClear : false,
23731 * @cfg {Boolean} alwaysQuery Disable caching of results, and always send query
23733 alwaysQuery : false,
23739 // element that contains real text value.. (when hidden is used..)
23742 onRender : function(ct, position){
23743 Roo.form.ComboBox.superclass.onRender.call(this, ct, position);
23744 if(this.hiddenName){
23745 this.hiddenField = this.el.insertSibling({tag:'input', type:'hidden', name: this.hiddenName, id: (this.hiddenId||this.hiddenName)},
23747 this.hiddenField.value =
23748 this.hiddenValue !== undefined ? this.hiddenValue :
23749 this.value !== undefined ? this.value : '';
23751 // prevent input submission
23752 this.el.dom.removeAttribute('name');
23757 this.el.dom.setAttribute('autocomplete', 'off');
23760 var cls = 'x-combo-list';
23762 this.list = new Roo.Layer({
23763 shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
23766 var lw = this.listWidth || Math.max(this.wrap.getWidth(), this.minListWidth);
23767 this.list.setWidth(lw);
23768 this.list.swallowEvent('mousewheel');
23769 this.assetHeight = 0;
23772 this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
23773 this.assetHeight += this.header.getHeight();
23776 this.innerList = this.list.createChild({cls:cls+'-inner'});
23777 this.innerList.on('mouseover', this.onViewOver, this);
23778 this.innerList.on('mousemove', this.onViewMove, this);
23779 this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
23781 if(this.allowBlank && !this.pageSize && !this.disableClear){
23782 this.footer = this.list.createChild({cls:cls+'-ft'});
23783 this.pageTb = new Roo.Toolbar(this.footer);
23787 this.footer = this.list.createChild({cls:cls+'-ft'});
23788 this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
23789 {pageSize: this.pageSize});
23793 if (this.pageTb && this.allowBlank && !this.disableClear) {
23795 this.pageTb.add(new Roo.Toolbar.Fill(), {
23796 cls: 'x-btn-icon x-btn-clear',
23798 handler: function()
23801 _this.clearValue();
23802 _this.onSelect(false, -1);
23807 this.assetHeight += this.footer.getHeight();
23812 this.tpl = '<div class="'+cls+'-item">{' + this.displayField + '}</div>';
23815 this.view = new Roo.View(this.innerList, this.tpl, {
23816 singleSelect:true, store: this.store, selectedClass: this.selectedClass
23819 this.view.on('click', this.onViewClick, this);
23821 this.store.on('beforeload', this.onBeforeLoad, this);
23822 this.store.on('load', this.onLoad, this);
23823 this.store.on('loadexception', this.onLoadException, this);
23825 if(this.resizable){
23826 this.resizer = new Roo.Resizable(this.list, {
23827 pinned:true, handles:'se'
23829 this.resizer.on('resize', function(r, w, h){
23830 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
23831 this.listWidth = w;
23832 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
23833 this.restrictHeight();
23835 this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
23837 if(!this.editable){
23838 this.editable = true;
23839 this.setEditable(false);
23843 if (typeof(this.events.add.listeners) != 'undefined') {
23845 this.addicon = this.wrap.createChild(
23846 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });
23848 this.addicon.on('click', function(e) {
23849 this.fireEvent('add', this);
23852 if (typeof(this.events.edit.listeners) != 'undefined') {
23854 this.editicon = this.wrap.createChild(
23855 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });
23856 if (this.addicon) {
23857 this.editicon.setStyle('margin-left', '40px');
23859 this.editicon.on('click', function(e) {
23861 // we fire even if inothing is selected..
23862 this.fireEvent('edit', this, this.lastData );
23872 initEvents : function(){
23873 Roo.form.ComboBox.superclass.initEvents.call(this);
23875 this.keyNav = new Roo.KeyNav(this.el, {
23876 "up" : function(e){
23877 this.inKeyMode = true;
23881 "down" : function(e){
23882 if(!this.isExpanded()){
23883 this.onTriggerClick();
23885 this.inKeyMode = true;
23890 "enter" : function(e){
23891 this.onViewClick();
23895 "esc" : function(e){
23899 "tab" : function(e){
23900 this.onViewClick(false);
23901 this.fireEvent("specialkey", this, e);
23907 doRelay : function(foo, bar, hname){
23908 if(hname == 'down' || this.scope.isExpanded()){
23909 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
23916 this.queryDelay = Math.max(this.queryDelay || 10,
23917 this.mode == 'local' ? 10 : 250);
23918 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
23919 if(this.typeAhead){
23920 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
23922 if(this.editable !== false){
23923 this.el.on("keyup", this.onKeyUp, this);
23925 if(this.forceSelection){
23926 this.on('blur', this.doForce, this);
23930 onDestroy : function(){
23932 this.view.setStore(null);
23933 this.view.el.removeAllListeners();
23934 this.view.el.remove();
23935 this.view.purgeListeners();
23938 this.list.destroy();
23941 this.store.un('beforeload', this.onBeforeLoad, this);
23942 this.store.un('load', this.onLoad, this);
23943 this.store.un('loadexception', this.onLoadException, this);
23945 Roo.form.ComboBox.superclass.onDestroy.call(this);
23949 fireKey : function(e){
23950 if(e.isNavKeyPress() && !this.list.isVisible()){
23951 this.fireEvent("specialkey", this, e);
23956 onResize: function(w, h){
23957 Roo.form.ComboBox.superclass.onResize.apply(this, arguments);
23959 if(typeof w != 'number'){
23960 // we do not handle it!?!?
23963 var tw = this.trigger.getWidth();
23964 tw += this.addicon ? this.addicon.getWidth() : 0;
23965 tw += this.editicon ? this.editicon.getWidth() : 0;
23967 this.el.setWidth( this.adjustWidth('input', x));
23969 this.trigger.setStyle('left', x+'px');
23971 if(this.list && this.listWidth === undefined){
23972 var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
23973 this.list.setWidth(lw);
23974 this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
23982 * Allow or prevent the user from directly editing the field text. If false is passed,
23983 * the user will only be able to select from the items defined in the dropdown list. This method
23984 * is the runtime equivalent of setting the 'editable' config option at config time.
23985 * @param {Boolean} value True to allow the user to directly edit the field text
23987 setEditable : function(value){
23988 if(value == this.editable){
23991 this.editable = value;
23993 this.el.dom.setAttribute('readOnly', true);
23994 this.el.on('mousedown', this.onTriggerClick, this);
23995 this.el.addClass('x-combo-noedit');
23997 this.el.dom.setAttribute('readOnly', false);
23998 this.el.un('mousedown', this.onTriggerClick, this);
23999 this.el.removeClass('x-combo-noedit');
24004 onBeforeLoad : function(){
24005 if(!this.hasFocus){
24008 this.innerList.update(this.loadingText ?
24009 '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
24010 this.restrictHeight();
24011 this.selectedIndex = -1;
24015 onLoad : function(){
24016 if(!this.hasFocus){
24019 if(this.store.getCount() > 0){
24021 this.restrictHeight();
24022 if(this.lastQuery == this.allQuery){
24024 this.el.dom.select();
24026 if(!this.selectByValue(this.value, true)){
24027 this.select(0, true);
24031 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
24032 this.taTask.delay(this.typeAheadDelay);
24036 this.onEmptyResults();
24041 onLoadException : function()
24044 Roo.log(this.store.reader.jsonData);
24045 if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
24046 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
24052 onTypeAhead : function(){
24053 if(this.store.getCount() > 0){
24054 var r = this.store.getAt(0);
24055 var newValue = r.data[this.displayField];
24056 var len = newValue.length;
24057 var selStart = this.getRawValue().length;
24058 if(selStart != len){
24059 this.setRawValue(newValue);
24060 this.selectText(selStart, newValue.length);
24066 onSelect : function(record, index){
24067 if(this.fireEvent('beforeselect', this, record, index) !== false){
24068 this.setFromData(index > -1 ? record.data : false);
24070 this.fireEvent('select', this, record, index);
24075 * Returns the currently selected field value or empty string if no value is set.
24076 * @return {String} value The selected value
24078 getValue : function(){
24079 if(this.valueField){
24080 return typeof this.value != 'undefined' ? this.value : '';
24082 return Roo.form.ComboBox.superclass.getValue.call(this);
24087 * Clears any text/value currently set in the field
24089 clearValue : function(){
24090 if(this.hiddenField){
24091 this.hiddenField.value = '';
24094 this.setRawValue('');
24095 this.lastSelectionText = '';
24100 * Sets the specified value into the field. If the value finds a match, the corresponding record text
24101 * will be displayed in the field. If the value does not match the data value of an existing item,
24102 * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
24103 * Otherwise the field will be blank (although the value will still be set).
24104 * @param {String} value The value to match
24106 setValue : function(v){
24108 if(this.valueField){
24109 var r = this.findRecord(this.valueField, v);
24111 text = r.data[this.displayField];
24112 }else if(this.valueNotFoundText !== undefined){
24113 text = this.valueNotFoundText;
24116 this.lastSelectionText = text;
24117 if(this.hiddenField){
24118 this.hiddenField.value = v;
24120 Roo.form.ComboBox.superclass.setValue.call(this, text);
24124 * @property {Object} the last set data for the element
24129 * Sets the value of the field based on a object which is related to the record format for the store.
24130 * @param {Object} value the value to set as. or false on reset?
24132 setFromData : function(o){
24133 var dv = ''; // display value
24134 var vv = ''; // value value..
24136 if (this.displayField) {
24137 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
24139 // this is an error condition!!!
24140 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
24143 if(this.valueField){
24144 vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
24146 if(this.hiddenField){
24147 this.hiddenField.value = vv;
24149 this.lastSelectionText = dv;
24150 Roo.form.ComboBox.superclass.setValue.call(this, dv);
24154 // no hidden field.. - we store the value in 'value', but still display
24155 // display field!!!!
24156 this.lastSelectionText = dv;
24157 Roo.form.ComboBox.superclass.setValue.call(this, dv);
24163 reset : function(){
24164 // overridden so that last data is reset..
24165 this.setValue(this.originalValue);
24166 this.clearInvalid();
24167 this.lastData = false;
24169 this.view.clearSelections();
24173 findRecord : function(prop, value){
24175 if(this.store.getCount() > 0){
24176 this.store.each(function(r){
24177 if(r.data[prop] == value){
24187 getName: function()
24189 // returns hidden if it's set..
24190 if (!this.rendered) {return ''};
24191 return !this.hiddenName && this.el.dom.name ? this.el.dom.name : (this.hiddenName || '');
24195 onViewMove : function(e, t){
24196 this.inKeyMode = false;
24200 onViewOver : function(e, t){
24201 if(this.inKeyMode){ // prevent key nav and mouse over conflicts
24204 var item = this.view.findItemFromChild(t);
24206 var index = this.view.indexOf(item);
24207 this.select(index, false);
24212 onViewClick : function(doFocus)
24214 var index = this.view.getSelectedIndexes()[0];
24215 var r = this.store.getAt(index);
24217 this.onSelect(r, index);
24219 if(doFocus !== false && !this.blockFocus){
24225 restrictHeight : function(){
24226 this.innerList.dom.style.height = '';
24227 var inner = this.innerList.dom;
24228 var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
24229 this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
24230 this.list.beginUpdate();
24231 this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
24232 this.list.alignTo(this.el, this.listAlign);
24233 this.list.endUpdate();
24237 onEmptyResults : function(){
24242 * Returns true if the dropdown list is expanded, else false.
24244 isExpanded : function(){
24245 return this.list.isVisible();
24249 * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
24250 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
24251 * @param {String} value The data value of the item to select
24252 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
24253 * selected item if it is not currently in view (defaults to true)
24254 * @return {Boolean} True if the value matched an item in the list, else false
24256 selectByValue : function(v, scrollIntoView){
24257 if(v !== undefined && v !== null){
24258 var r = this.findRecord(this.valueField || this.displayField, v);
24260 this.select(this.store.indexOf(r), scrollIntoView);
24268 * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
24269 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
24270 * @param {Number} index The zero-based index of the list item to select
24271 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
24272 * selected item if it is not currently in view (defaults to true)
24274 select : function(index, scrollIntoView){
24275 this.selectedIndex = index;
24276 this.view.select(index);
24277 if(scrollIntoView !== false){
24278 var el = this.view.getNode(index);
24280 this.innerList.scrollChildIntoView(el, false);
24286 selectNext : function(){
24287 var ct = this.store.getCount();
24289 if(this.selectedIndex == -1){
24291 }else if(this.selectedIndex < ct-1){
24292 this.select(this.selectedIndex+1);
24298 selectPrev : function(){
24299 var ct = this.store.getCount();
24301 if(this.selectedIndex == -1){
24303 }else if(this.selectedIndex != 0){
24304 this.select(this.selectedIndex-1);
24310 onKeyUp : function(e){
24311 if(this.editable !== false && !e.isSpecialKey()){
24312 this.lastKey = e.getKey();
24313 this.dqTask.delay(this.queryDelay);
24318 validateBlur : function(){
24319 return !this.list || !this.list.isVisible();
24323 initQuery : function(){
24324 this.doQuery(this.getRawValue());
24328 doForce : function(){
24329 if(this.el.dom.value.length > 0){
24330 this.el.dom.value =
24331 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
24337 * Execute a query to filter the dropdown list. Fires the beforequery event prior to performing the
24338 * query allowing the query action to be canceled if needed.
24339 * @param {String} query The SQL query to execute
24340 * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
24341 * in the field than the minimum specified by the minChars config option. It also clears any filter previously
24342 * saved in the current store (defaults to false)
24344 doQuery : function(q, forceAll){
24345 if(q === undefined || q === null){
24350 forceAll: forceAll,
24354 if(this.fireEvent('beforequery', qe)===false || qe.cancel){
24358 forceAll = qe.forceAll;
24359 if(forceAll === true || (q.length >= this.minChars)){
24360 if(this.lastQuery != q || this.alwaysQuery){
24361 this.lastQuery = q;
24362 if(this.mode == 'local'){
24363 this.selectedIndex = -1;
24365 this.store.clearFilter();
24367 this.store.filter(this.displayField, q);
24371 this.store.baseParams[this.queryParam] = q;
24373 params: this.getParams(q)
24378 this.selectedIndex = -1;
24385 getParams : function(q){
24387 //p[this.queryParam] = q;
24390 p.limit = this.pageSize;
24396 * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
24398 collapse : function(){
24399 if(!this.isExpanded()){
24403 Roo.get(document).un('mousedown', this.collapseIf, this);
24404 Roo.get(document).un('mousewheel', this.collapseIf, this);
24405 if (!this.editable) {
24406 Roo.get(document).un('keydown', this.listKeyPress, this);
24408 this.fireEvent('collapse', this);
24412 collapseIf : function(e){
24413 if(!e.within(this.wrap) && !e.within(this.list)){
24419 * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
24421 expand : function(){
24422 if(this.isExpanded() || !this.hasFocus){
24425 this.list.alignTo(this.el, this.listAlign);
24427 Roo.get(document).on('mousedown', this.collapseIf, this);
24428 Roo.get(document).on('mousewheel', this.collapseIf, this);
24429 if (!this.editable) {
24430 Roo.get(document).on('keydown', this.listKeyPress, this);
24433 this.fireEvent('expand', this);
24437 // Implements the default empty TriggerField.onTriggerClick function
24438 onTriggerClick : function(){
24442 if(this.isExpanded()){
24444 if (!this.blockFocus) {
24449 this.hasFocus = true;
24450 if(this.triggerAction == 'all') {
24451 this.doQuery(this.allQuery, true);
24453 this.doQuery(this.getRawValue());
24455 if (!this.blockFocus) {
24460 listKeyPress : function(e)
24462 //Roo.log('listkeypress');
24463 // scroll to first matching element based on key pres..
24464 if (e.isSpecialKey()) {
24467 var k = String.fromCharCode(e.getKey()).toUpperCase();
24470 var csel = this.view.getSelectedNodes();
24471 var cselitem = false;
24473 var ix = this.view.indexOf(csel[0]);
24474 cselitem = this.store.getAt(ix);
24475 if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
24481 this.store.each(function(v) {
24483 // start at existing selection.
24484 if (cselitem.id == v.id) {
24490 if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
24491 match = this.store.indexOf(v);
24496 if (match === false) {
24497 return true; // no more action?
24500 this.view.select(match);
24501 var sn = Roo.get(this.view.getSelectedNodes()[0])
24502 sn.scrollIntoView(sn.dom.parentNode, false);
24506 * @cfg {Boolean} grow
24510 * @cfg {Number} growMin
24514 * @cfg {Number} growMax
24522 * Copyright(c) 2010-2012, Roo J Solutions Limited
24529 * @class Roo.form.ComboBoxArray
24530 * @extends Roo.form.TextField
24531 * A facebook style adder... for lists of email / people / countries etc...
24532 * pick multiple items from a combo box, and shows each one.
24534 * Fred [x] Brian [x] [Pick another |v]
24537 * For this to work: it needs various extra information
24538 * - normal combo problay has
24540 * + displayField, valueField
24542 * For our purpose...
24545 * If we change from 'extends' to wrapping...
24552 * Create a new ComboBoxArray.
24553 * @param {Object} config Configuration options
24557 Roo.form.ComboBoxArray = function(config)
24560 Roo.form.ComboBoxArray.superclass.constructor.call(this, config);
24562 this.items = new Roo.util.MixedCollection(false);
24564 // construct the child combo...
24574 Roo.extend(Roo.form.ComboBoxArray, Roo.form.TextField,
24577 * @cfg {Roo.form.Combo} combo The combo box that is wrapped
24582 // behavies liek a hiddne field
24583 inputType: 'hidden',
24585 * @cfg {Number} width The width of the box that displays the selected element
24592 * @cfg {String} name The name of the visable items on this form (eg. titles not ids)
24596 * @cfg {String} hiddenName The hidden name of the field, often contains an comma seperated list of names
24598 hiddenName : false,
24601 // private the array of items that are displayed..
24603 // private - the hidden field el.
24605 // private - the filed el..
24608 //validateValue : function() { return true; }, // all values are ok!
24609 //onAddClick: function() { },
24611 onRender : function(ct, position)
24614 // create the standard hidden element
24615 //Roo.form.ComboBoxArray.superclass.onRender.call(this, ct, position);
24618 // give fake names to child combo;
24619 this.combo.hiddenName = this.hiddenName ? (this.hiddenName+'-subcombo') : this.hiddenName;
24620 this.combo.name = this.name? (this.name+'-subcombo') : this.name;
24622 this.combo = Roo.factory(this.combo, Roo.form);
24623 this.combo.onRender(ct, position);
24624 if (typeof(this.combo.width) != 'undefined') {
24625 this.combo.onResize(this.combo.width,0);
24628 this.combo.initEvents();
24630 // assigned so form know we need to do this..
24631 this.store = this.combo.store;
24632 this.valueField = this.combo.valueField;
24633 this.displayField = this.combo.displayField ;
24636 this.combo.wrap.addClass('x-cbarray-grp');
24638 var cbwrap = this.combo.wrap.createChild(
24639 {tag: 'div', cls: 'x-cbarray-cb'},
24644 this.hiddenEl = this.combo.wrap.createChild({
24645 tag: 'input', type:'hidden' , name: this.hiddenName, value : ''
24647 this.el = this.combo.wrap.createChild({
24648 tag: 'input', type:'hidden' , name: this.name, value : ''
24650 // this.el.dom.removeAttribute("name");
24653 this.outerWrap = this.combo.wrap;
24654 this.wrap = cbwrap;
24656 this.outerWrap.setWidth(this.width);
24657 this.outerWrap.dom.removeChild(this.el.dom);
24659 this.wrap.dom.appendChild(this.el.dom);
24660 this.outerWrap.dom.removeChild(this.combo.trigger.dom);
24661 this.combo.wrap.dom.appendChild(this.combo.trigger.dom);
24663 this.combo.trigger.setStyle('position','relative');
24664 this.combo.trigger.setStyle('left', '0px');
24665 this.combo.trigger.setStyle('top', '2px');
24667 this.combo.el.setStyle('vertical-align', 'text-bottom');
24669 //this.trigger.setStyle('vertical-align', 'top');
24671 // this should use the code from combo really... on('add' ....)
24675 this.adder = this.outerWrap.createChild(
24676 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-adder', style: 'margin-left:2px'});
24678 this.adder.on('click', function(e) {
24679 _t.fireEvent('adderclick', this, e);
24683 //this.adder.on('click', this.onAddClick, _t);
24686 this.combo.on('select', function(cb, rec, ix) {
24687 this.addItem(rec.data);
24690 cb.el.dom.value = '';
24691 //cb.lastData = rec.data;
24700 getName: function()
24702 // returns hidden if it's set..
24703 if (!this.rendered) {return ''};
24704 return this.hiddenName ? this.hiddenName : this.name;
24709 onResize: function(w, h){
24712 // not sure if this is needed..
24713 //this.combo.onResize(w,h);
24715 if(typeof w != 'number'){
24716 // we do not handle it!?!?
24719 var tw = this.combo.trigger.getWidth();
24720 tw += this.addicon ? this.addicon.getWidth() : 0;
24721 tw += this.editicon ? this.editicon.getWidth() : 0;
24723 this.combo.el.setWidth( this.combo.adjustWidth('input', x));
24725 this.combo.trigger.setStyle('left', '0px');
24727 if(this.list && this.listWidth === undefined){
24728 var lw = Math.max(x + this.combo.trigger.getWidth(), this.combo.minListWidth);
24729 this.list.setWidth(lw);
24730 this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
24737 addItem: function(rec)
24739 var valueField = this.combo.valueField;
24740 var displayField = this.combo.displayField;
24741 if (this.items.indexOfKey(rec[valueField]) > -1) {
24742 //console.log("GOT " + rec.data.id);
24746 var x = new Roo.form.ComboBoxArray.Item({
24747 //id : rec[this.idField],
24749 displayField : displayField ,
24750 tipField : displayField ,
24754 this.items.add(rec[valueField],x);
24755 // add it before the element..
24756 this.updateHiddenEl();
24757 x.render(this.outerWrap, this.wrap.dom);
24758 // add the image handler..
24761 updateHiddenEl : function()
24764 if (!this.hiddenEl) {
24768 var idField = this.combo.valueField;
24770 this.items.each(function(f) {
24771 ar.push(f.data[idField]);
24774 this.hiddenEl.dom.value = ar.join(',');
24780 //Roo.form.ComboBoxArray.superclass.reset.call(this);
24781 this.items.each(function(f) {
24784 this.el.dom.value = '';
24785 if (this.hiddenEl) {
24786 this.hiddenEl.dom.value = '';
24790 getValue: function()
24792 return this.hiddenEl ? this.hiddenEl.dom.value : '';
24794 setValue: function(v) // not a valid action - must use addItems..
24801 if (this.store.isLocal && (typeof(v) == 'string')) {
24802 // then we can use the store to find the values..
24803 // comma seperated at present.. this needs to allow JSON based encoding..
24804 this.hiddenEl.value = v;
24806 Roo.each(v.split(','), function(k) {
24807 Roo.log("CHECK " + this.valueField + ',' + k);
24808 var li = this.store.query(this.valueField, k);
24813 add[this.valueField] = k;
24814 add[this.displayField] = li.item(0).data[this.displayField];
24820 if (typeof(v) == 'object') {
24821 // then let's assume it's an array of objects..
24822 Roo.each(v, function(l) {
24830 setFromData: function(v)
24832 // this recieves an object, if setValues is called.
24834 this.el.dom.value = v[this.displayField];
24835 this.hiddenEl.dom.value = v[this.valueField];
24836 if (typeof(v[this.valueField]) != 'string' || !v[this.valueField].length) {
24839 var kv = v[this.valueField];
24840 var dv = v[this.displayField];
24841 kv = typeof(kv) != 'string' ? '' : kv;
24842 dv = typeof(dv) != 'string' ? '' : dv;
24845 var keys = kv.split(',');
24846 var display = dv.split(',');
24847 for (var i = 0 ; i < keys.length; i++) {
24850 add[this.valueField] = keys[i];
24851 add[this.displayField] = display[i];
24859 validateValue : function(value){
24860 return Roo.form.ComboBoxArray.superclass.validateValue.call(this, this.getValue());
24869 * @class Roo.form.ComboBoxArray.Item
24870 * @extends Roo.BoxComponent
24871 * A selected item in the list
24872 * Fred [x] Brian [x] [Pick another |v]
24875 * Create a new item.
24876 * @param {Object} config Configuration options
24879 Roo.form.ComboBoxArray.Item = function(config) {
24880 config.id = Roo.id();
24881 Roo.form.ComboBoxArray.Item.superclass.constructor.call(this, config);
24884 Roo.extend(Roo.form.ComboBoxArray.Item, Roo.BoxComponent, {
24887 displayField : false,
24891 defaultAutoCreate : {
24893 cls: 'x-cbarray-item',
24900 src : Roo.BLANK_IMAGE_URL ,
24908 onRender : function(ct, position)
24910 Roo.form.Field.superclass.onRender.call(this, ct, position);
24913 var cfg = this.getAutoCreate();
24914 this.el = ct.createChild(cfg, position);
24917 this.el.child('img').dom.setAttribute('src', Roo.BLANK_IMAGE_URL);
24919 this.el.child('div').dom.innerHTML = this.cb.renderer ?
24920 this.cb.renderer(this.data) :
24921 String.format('{0}',this.data[this.displayField]);
24924 this.el.child('div').dom.setAttribute('qtip',
24925 String.format('{0}',this.data[this.tipField])
24928 this.el.child('img').on('click', this.remove, this);
24932 remove : function()
24935 this.cb.items.remove(this);
24936 this.el.child('img').un('click', this.remove, this);
24938 this.cb.updateHiddenEl();
24944 * Ext JS Library 1.1.1
24945 * Copyright(c) 2006-2007, Ext JS, LLC.
24947 * Originally Released Under LGPL - original licence link has changed is not relivant.
24950 * <script type="text/javascript">
24953 * @class Roo.form.Checkbox
24954 * @extends Roo.form.Field
24955 * Single checkbox field. Can be used as a direct replacement for traditional checkbox fields.
24957 * Creates a new Checkbox
24958 * @param {Object} config Configuration options
24960 Roo.form.Checkbox = function(config){
24961 Roo.form.Checkbox.superclass.constructor.call(this, config);
24965 * Fires when the checkbox is checked or unchecked.
24966 * @param {Roo.form.Checkbox} this This checkbox
24967 * @param {Boolean} checked The new checked value
24973 Roo.extend(Roo.form.Checkbox, Roo.form.Field, {
24975 * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
24977 focusClass : undefined,
24979 * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
24981 fieldClass: "x-form-field",
24983 * @cfg {Boolean} checked True if the the checkbox should render already checked (defaults to false)
24987 * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
24988 * {tag: "input", type: "checkbox", autocomplete: "off"})
24990 defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "off"},
24992 * @cfg {String} boxLabel The text that appears beside the checkbox
24996 * @cfg {String} inputValue The value that should go into the generated input element's value attribute
25000 * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
25002 valueOff: '0', // value when not checked..
25004 actionMode : 'viewEl',
25007 itemCls : 'x-menu-check-item x-form-item',
25008 groupClass : 'x-menu-group-item',
25009 inputType : 'hidden',
25012 inSetChecked: false, // check that we are not calling self...
25014 inputElement: false, // real input element?
25015 basedOn: false, // ????
25017 isFormField: true, // not sure where this is needed!!!!
25019 onResize : function(){
25020 Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
25021 if(!this.boxLabel){
25022 this.el.alignTo(this.wrap, 'c-c');
25026 initEvents : function(){
25027 Roo.form.Checkbox.superclass.initEvents.call(this);
25028 this.el.on("click", this.onClick, this);
25029 this.el.on("change", this.onClick, this);
25033 getResizeEl : function(){
25037 getPositionEl : function(){
25042 onRender : function(ct, position){
25043 Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
25045 if(this.inputValue !== undefined){
25046 this.el.dom.value = this.inputValue;
25049 //this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
25050 this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
25051 var viewEl = this.wrap.createChild({
25052 tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
25053 this.viewEl = viewEl;
25054 this.wrap.on('click', this.onClick, this);
25056 this.el.on('DOMAttrModified', this.setFromHidden, this); //ff
25057 this.el.on('propertychange', this.setFromHidden, this); //ie
25062 this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
25063 // viewEl.on('click', this.onClick, this);
25065 //if(this.checked){
25066 this.setChecked(this.checked);
25068 //this.checked = this.el.dom;
25074 initValue : Roo.emptyFn,
25077 * Returns the checked state of the checkbox.
25078 * @return {Boolean} True if checked, else false
25080 getValue : function(){
25082 return String(this.el.dom.value) == String(this.inputValue ) ? this.inputValue : this.valueOff;
25084 return this.valueOff;
25089 onClick : function(){
25090 this.setChecked(!this.checked);
25092 //if(this.el.dom.checked != this.checked){
25093 // this.setValue(this.el.dom.checked);
25098 * Sets the checked state of the checkbox.
25099 * On is always based on a string comparison between inputValue and the param.
25100 * @param {Boolean/String} value - the value to set
25101 * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
25103 setValue : function(v,suppressEvent){
25106 //this.checked = (v === true || v === 'true' || v == '1' || String(v).toLowerCase() == 'on');
25107 //if(this.el && this.el.dom){
25108 // this.el.dom.checked = this.checked;
25109 // this.el.dom.defaultChecked = this.checked;
25111 this.setChecked(String(v) === String(this.inputValue), suppressEvent);
25112 //this.fireEvent("check", this, this.checked);
25115 setChecked : function(state,suppressEvent)
25117 if (this.inSetChecked) {
25118 this.checked = state;
25124 this.wrap[state ? 'addClass' : 'removeClass']('x-menu-item-checked');
25126 this.checked = state;
25127 if(suppressEvent !== true){
25128 this.fireEvent('check', this, state);
25130 this.inSetChecked = true;
25131 this.el.dom.value = state ? this.inputValue : this.valueOff;
25132 this.inSetChecked = false;
25135 // handle setting of hidden value by some other method!!?!?
25136 setFromHidden: function()
25141 //console.log("SET FROM HIDDEN");
25142 //alert('setFrom hidden');
25143 this.setValue(this.el.dom.value);
25146 onDestroy : function()
25149 Roo.get(this.viewEl).remove();
25152 Roo.form.Checkbox.superclass.onDestroy.call(this);
25157 * Ext JS Library 1.1.1
25158 * Copyright(c) 2006-2007, Ext JS, LLC.
25160 * Originally Released Under LGPL - original licence link has changed is not relivant.
25163 * <script type="text/javascript">
25167 * @class Roo.form.Radio
25168 * @extends Roo.form.Checkbox
25169 * Single radio field. Same as Checkbox, but provided as a convenience for automatically setting the input type.
25170 * Radio grouping is handled automatically by the browser if you give each radio in a group the same name.
25172 * Creates a new Radio
25173 * @param {Object} config Configuration options
25175 Roo.form.Radio = function(){
25176 Roo.form.Radio.superclass.constructor.apply(this, arguments);
25178 Roo.extend(Roo.form.Radio, Roo.form.Checkbox, {
25179 inputType: 'radio',
25182 * If this radio is part of a group, it will return the selected value
25185 getGroupValue : function(){
25186 return this.el.up('form').child('input[name='+this.el.dom.name+']:checked', true).value;
25190 onRender : function(ct, position){
25191 Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
25193 if(this.inputValue !== undefined){
25194 this.el.dom.value = this.inputValue;
25197 this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
25198 //this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
25199 //var viewEl = this.wrap.createChild({
25200 // tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
25201 //this.viewEl = viewEl;
25202 //this.wrap.on('click', this.onClick, this);
25204 //this.el.on('DOMAttrModified', this.setFromHidden, this); //ff
25205 //this.el.on('propertychange', this.setFromHidden, this); //ie
25210 this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
25211 // viewEl.on('click', this.onClick, this);
25214 this.el.dom.checked = 'checked' ;
25220 });//<script type="text/javascript">
25223 * Ext JS Library 1.1.1
25224 * Copyright(c) 2006-2007, Ext JS, LLC.
25225 * licensing@extjs.com
25227 * http://www.extjs.com/license
25233 * Default CSS appears to render it as fixed text by default (should really be Sans-Serif)
25234 * - IE ? - no idea how much works there.
25242 * @class Ext.form.HtmlEditor
25243 * @extends Ext.form.Field
25244 * Provides a lightweight HTML Editor component.
25246 * This has been tested on Fireforx / Chrome.. IE may not be so great..
25248 * <br><br><b>Note: The focus/blur and validation marking functionality inherited from Ext.form.Field is NOT
25249 * supported by this editor.</b><br/><br/>
25250 * An Editor is a sensitive component that can't be used in all spots standard fields can be used. Putting an Editor within
25251 * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
25253 Roo.form.HtmlEditor = Roo.extend(Roo.form.Field, {
25255 * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
25259 * @cfg {String} createLinkText The default text for the create link prompt
25261 createLinkText : 'Please enter the URL for the link:',
25263 * @cfg {String} defaultLinkValue The default value for the create link prompt (defaults to http:/ /)
25265 defaultLinkValue : 'http:/'+'/',
25268 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
25273 * @cfg {Number} height (in pixels)
25277 * @cfg {Number} width (in pixels)
25282 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
25285 stylesheets: false,
25290 // private properties
25291 validationEvent : false,
25293 initialized : false,
25295 sourceEditMode : false,
25296 onFocus : Roo.emptyFn,
25298 hideMode:'offsets',
25300 defaultAutoCreate : { // modified by initCompnoent..
25302 style:"width:500px;height:300px;",
25303 autocomplete: "off"
25307 initComponent : function(){
25310 * @event initialize
25311 * Fires when the editor is fully initialized (including the iframe)
25312 * @param {HtmlEditor} this
25317 * Fires when the editor is first receives the focus. Any insertion must wait
25318 * until after this event.
25319 * @param {HtmlEditor} this
25323 * @event beforesync
25324 * Fires before the textarea is updated with content from the editor iframe. Return false
25325 * to cancel the sync.
25326 * @param {HtmlEditor} this
25327 * @param {String} html
25331 * @event beforepush
25332 * Fires before the iframe editor is updated with content from the textarea. Return false
25333 * to cancel the push.
25334 * @param {HtmlEditor} this
25335 * @param {String} html
25340 * Fires when the textarea is updated with content from the editor iframe.
25341 * @param {HtmlEditor} this
25342 * @param {String} html
25347 * Fires when the iframe editor is updated with content from the textarea.
25348 * @param {HtmlEditor} this
25349 * @param {String} html
25353 * @event editmodechange
25354 * Fires when the editor switches edit modes
25355 * @param {HtmlEditor} this
25356 * @param {Boolean} sourceEdit True if source edit, false if standard editing.
25358 editmodechange: true,
25360 * @event editorevent
25361 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
25362 * @param {HtmlEditor} this
25366 this.defaultAutoCreate = {
25368 style:'width: ' + this.width + 'px;height: ' + this.height + 'px;',
25369 autocomplete: "off"
25374 * Protected method that will not generally be called directly. It
25375 * is called when the editor creates its toolbar. Override this method if you need to
25376 * add custom toolbar buttons.
25377 * @param {HtmlEditor} editor
25379 createToolbar : function(editor){
25380 if (!editor.toolbars || !editor.toolbars.length) {
25381 editor.toolbars = [ new Roo.form.HtmlEditor.ToolbarStandard() ]; // can be empty?
25384 for (var i =0 ; i < editor.toolbars.length;i++) {
25385 editor.toolbars[i] = Roo.factory(
25386 typeof(editor.toolbars[i]) == 'string' ?
25387 { xtype: editor.toolbars[i]} : editor.toolbars[i],
25388 Roo.form.HtmlEditor);
25389 editor.toolbars[i].init(editor);
25396 * Protected method that will not generally be called directly. It
25397 * is called when the editor initializes the iframe with HTML contents. Override this method if you
25398 * want to change the initialization markup of the iframe (e.g. to add stylesheets).
25400 getDocMarkup : function(){
25403 if (this.stylesheets === false) {
25405 Roo.get(document.head).select('style').each(function(node) {
25406 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
25409 Roo.get(document.head).select('link').each(function(node) {
25410 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
25413 } else if (!this.stylesheets.length) {
25415 st = '<style type="text/css">' +
25416 'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
25419 Roo.each(this.stylesheets, function(s) {
25420 st += '<link rel="stylesheet" type="text/css" href="' + s +'" />'
25425 st += '<style type="text/css">' +
25426 'IMG { cursor: pointer } ' +
25430 return '<html><head>' + st +
25431 //<style type="text/css">' +
25432 //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
25434 ' </head><body class="roo-htmleditor-body"></body></html>';
25438 onRender : function(ct, position)
25441 Roo.form.HtmlEditor.superclass.onRender.call(this, ct, position);
25442 this.el.dom.style.border = '0 none';
25443 this.el.dom.setAttribute('tabIndex', -1);
25444 this.el.addClass('x-hidden');
25445 if(Roo.isIE){ // fix IE 1px bogus margin
25446 this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
25448 this.wrap = this.el.wrap({
25449 cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
25452 if (this.resizable) {
25453 this.resizeEl = new Roo.Resizable(this.wrap, {
25457 minHeight : this.height,
25458 height: this.height,
25459 handles : this.resizable,
25462 resize : function(r, w, h) {
25463 _t.onResize(w,h); // -something
25470 this.frameId = Roo.id();
25472 this.createToolbar(this);
25476 var iframe = this.wrap.createChild({
25479 name: this.frameId,
25480 frameBorder : 'no',
25481 'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL : "javascript:false"
25485 // console.log(iframe);
25486 //this.wrap.dom.appendChild(iframe);
25488 this.iframe = iframe.dom;
25490 this.assignDocWin();
25492 this.doc.designMode = 'on';
25495 this.doc.write(this.getDocMarkup());
25499 var task = { // must defer to wait for browser to be ready
25501 //console.log("run task?" + this.doc.readyState);
25502 this.assignDocWin();
25503 if(this.doc.body || this.doc.readyState == 'complete'){
25505 this.doc.designMode="on";
25509 Roo.TaskMgr.stop(task);
25510 this.initEditor.defer(10, this);
25517 Roo.TaskMgr.start(task);
25520 this.setSize(this.wrap.getSize());
25522 if (this.resizeEl) {
25523 this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
25524 // should trigger onReize..
25529 onResize : function(w, h)
25531 //Roo.log('resize: ' +w + ',' + h );
25532 Roo.form.HtmlEditor.superclass.onResize.apply(this, arguments);
25533 if(this.el && this.iframe){
25534 if(typeof w == 'number'){
25535 var aw = w - this.wrap.getFrameWidth('lr');
25536 this.el.setWidth(this.adjustWidth('textarea', aw));
25537 this.iframe.style.width = aw + 'px';
25539 if(typeof h == 'number'){
25541 for (var i =0; i < this.toolbars.length;i++) {
25542 // fixme - ask toolbars for heights?
25543 tbh += this.toolbars[i].tb.el.getHeight();
25544 if (this.toolbars[i].footer) {
25545 tbh += this.toolbars[i].footer.el.getHeight();
25552 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
25553 ah -= 5; // knock a few pixes off for look..
25554 this.el.setHeight(this.adjustWidth('textarea', ah));
25555 this.iframe.style.height = ah + 'px';
25557 (this.doc.body || this.doc.documentElement).style.height = (ah - (this.iframePad*2)) + 'px';
25564 * Toggles the editor between standard and source edit mode.
25565 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
25567 toggleSourceEdit : function(sourceEditMode){
25569 this.sourceEditMode = sourceEditMode === true;
25571 if(this.sourceEditMode){
25573 // Roo.log(this.syncValue());
25575 this.iframe.className = 'x-hidden';
25576 this.el.removeClass('x-hidden');
25577 this.el.dom.removeAttribute('tabIndex');
25581 // Roo.log(this.pushValue());
25583 this.iframe.className = '';
25584 this.el.addClass('x-hidden');
25585 this.el.dom.setAttribute('tabIndex', -1);
25588 this.setSize(this.wrap.getSize());
25589 this.fireEvent('editmodechange', this, this.sourceEditMode);
25592 // private used internally
25593 createLink : function(){
25594 var url = prompt(this.createLinkText, this.defaultLinkValue);
25595 if(url && url != 'http:/'+'/'){
25596 this.relayCmd('createlink', url);
25600 // private (for BoxComponent)
25601 adjustSize : Roo.BoxComponent.prototype.adjustSize,
25603 // private (for BoxComponent)
25604 getResizeEl : function(){
25608 // private (for BoxComponent)
25609 getPositionEl : function(){
25614 initEvents : function(){
25615 this.originalValue = this.getValue();
25619 * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
25622 markInvalid : Roo.emptyFn,
25624 * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
25627 clearInvalid : Roo.emptyFn,
25629 setValue : function(v){
25630 Roo.form.HtmlEditor.superclass.setValue.call(this, v);
25635 * Protected method that will not generally be called directly. If you need/want
25636 * custom HTML cleanup, this is the method you should override.
25637 * @param {String} html The HTML to be cleaned
25638 * return {String} The cleaned HTML
25640 cleanHtml : function(html){
25641 html = String(html);
25642 if(html.length > 5){
25643 if(Roo.isSafari){ // strip safari nonsense
25644 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
25647 if(html == ' '){
25654 * Protected method that will not generally be called directly. Syncs the contents
25655 * of the editor iframe with the textarea.
25657 syncValue : function(){
25658 if(this.initialized){
25659 var bd = (this.doc.body || this.doc.documentElement);
25660 //this.cleanUpPaste(); -- this is done else where and causes havoc..
25661 var html = bd.innerHTML;
25663 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
25664 var m = bs.match(/text-align:(.*?);/i);
25666 html = '<div style="'+m[0]+'">' + html + '</div>';
25669 html = this.cleanHtml(html);
25670 // fix up the special chars.. normaly like back quotes in word...
25671 // however we do not want to do this with chinese..
25672 html = html.replace(/([\x80-\uffff])/g, function (a, b) {
25673 var cc = b.charCodeAt();
25675 (cc >= 0x4E00 && cc < 0xA000 ) ||
25676 (cc >= 0x3400 && cc < 0x4E00 ) ||
25677 (cc >= 0xf900 && cc < 0xfb00 )
25683 if(this.fireEvent('beforesync', this, html) !== false){
25684 this.el.dom.value = html;
25685 this.fireEvent('sync', this, html);
25691 * Protected method that will not generally be called directly. Pushes the value of the textarea
25692 * into the iframe editor.
25694 pushValue : function(){
25695 if(this.initialized){
25696 var v = this.el.dom.value;
25702 if(this.fireEvent('beforepush', this, v) !== false){
25703 var d = (this.doc.body || this.doc.documentElement);
25705 this.cleanUpPaste();
25706 this.el.dom.value = d.innerHTML;
25707 this.fireEvent('push', this, v);
25713 deferFocus : function(){
25714 this.focus.defer(10, this);
25718 focus : function(){
25719 if(this.win && !this.sourceEditMode){
25726 assignDocWin: function()
25728 var iframe = this.iframe;
25731 this.doc = iframe.contentWindow.document;
25732 this.win = iframe.contentWindow;
25734 if (!Roo.get(this.frameId)) {
25737 this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
25738 this.win = Roo.get(this.frameId).dom.contentWindow;
25743 initEditor : function(){
25744 //console.log("INIT EDITOR");
25745 this.assignDocWin();
25749 this.doc.designMode="on";
25751 this.doc.write(this.getDocMarkup());
25754 var dbody = (this.doc.body || this.doc.documentElement);
25755 //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
25756 // this copies styles from the containing element into thsi one..
25757 // not sure why we need all of this..
25758 var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
25759 ss['background-attachment'] = 'fixed'; // w3c
25760 dbody.bgProperties = 'fixed'; // ie
25761 Roo.DomHelper.applyStyles(dbody, ss);
25762 Roo.EventManager.on(this.doc, {
25763 //'mousedown': this.onEditorEvent,
25764 'mouseup': this.onEditorEvent,
25765 'dblclick': this.onEditorEvent,
25766 'click': this.onEditorEvent,
25767 'keyup': this.onEditorEvent,
25772 Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
25774 if(Roo.isIE || Roo.isSafari || Roo.isOpera){
25775 Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
25777 this.initialized = true;
25779 this.fireEvent('initialize', this);
25784 onDestroy : function(){
25790 for (var i =0; i < this.toolbars.length;i++) {
25791 // fixme - ask toolbars for heights?
25792 this.toolbars[i].onDestroy();
25795 this.wrap.dom.innerHTML = '';
25796 this.wrap.remove();
25801 onFirstFocus : function(){
25803 this.assignDocWin();
25806 this.activated = true;
25807 for (var i =0; i < this.toolbars.length;i++) {
25808 this.toolbars[i].onFirstFocus();
25811 if(Roo.isGecko){ // prevent silly gecko errors
25813 var s = this.win.getSelection();
25814 if(!s.focusNode || s.focusNode.nodeType != 3){
25815 var r = s.getRangeAt(0);
25816 r.selectNodeContents((this.doc.body || this.doc.documentElement));
25821 this.execCmd('useCSS', true);
25822 this.execCmd('styleWithCSS', false);
25825 this.fireEvent('activate', this);
25829 adjustFont: function(btn){
25830 var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
25831 //if(Roo.isSafari){ // safari
25834 var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
25835 if(Roo.isSafari){ // safari
25836 var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
25837 v = (v < 10) ? 10 : v;
25838 v = (v > 48) ? 48 : v;
25839 v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
25844 v = Math.max(1, v+adjust);
25846 this.execCmd('FontSize', v );
25849 onEditorEvent : function(e){
25850 this.fireEvent('editorevent', this, e);
25851 // this.updateToolbar();
25852 this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
25855 insertTag : function(tg)
25857 // could be a bit smarter... -> wrap the current selected tRoo..
25858 if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
25860 range = this.createRange(this.getSelection());
25861 var wrappingNode = this.doc.createElement(tg.toLowerCase());
25862 wrappingNode.appendChild(range.extractContents());
25863 range.insertNode(wrappingNode);
25870 this.execCmd("formatblock", tg);
25874 insertText : function(txt)
25878 var range = this.createRange();
25879 range.deleteContents();
25880 //alert(Sender.getAttribute('label'));
25882 range.insertNode(this.doc.createTextNode(txt));
25886 relayBtnCmd : function(btn){
25887 this.relayCmd(btn.cmd);
25891 * Executes a Midas editor command on the editor document and performs necessary focus and
25892 * toolbar updates. <b>This should only be called after the editor is initialized.</b>
25893 * @param {String} cmd The Midas command
25894 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
25896 relayCmd : function(cmd, value){
25898 this.execCmd(cmd, value);
25899 this.fireEvent('editorevent', this);
25900 //this.updateToolbar();
25905 * Executes a Midas editor command directly on the editor document.
25906 * For visual commands, you should use {@link #relayCmd} instead.
25907 * <b>This should only be called after the editor is initialized.</b>
25908 * @param {String} cmd The Midas command
25909 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
25911 execCmd : function(cmd, value){
25912 this.doc.execCommand(cmd, false, value === undefined ? null : value);
25919 * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
25921 * @param {String} text | dom node..
25923 insertAtCursor : function(text)
25928 if(!this.activated){
25934 var r = this.doc.selection.createRange();
25945 if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
25949 // from jquery ui (MIT licenced)
25951 var win = this.win;
25953 if (win.getSelection && win.getSelection().getRangeAt) {
25954 range = win.getSelection().getRangeAt(0);
25955 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
25956 range.insertNode(node);
25957 } else if (win.document.selection && win.document.selection.createRange) {
25958 // no firefox support
25959 var txt = typeof(text) == 'string' ? text : text.outerHTML;
25960 win.document.selection.createRange().pasteHTML(txt);
25962 // no firefox support
25963 var txt = typeof(text) == 'string' ? text : text.outerHTML;
25964 this.execCmd('InsertHTML', txt);
25973 mozKeyPress : function(e){
25975 var c = e.getCharCode(), cmd;
25978 c = String.fromCharCode(c).toLowerCase();
25992 this.cleanUpPaste.defer(100, this);
26000 e.preventDefault();
26008 fixKeys : function(){ // load time branching for fastest keydown performance
26010 return function(e){
26011 var k = e.getKey(), r;
26014 r = this.doc.selection.createRange();
26017 r.pasteHTML('    ');
26024 r = this.doc.selection.createRange();
26026 var target = r.parentElement();
26027 if(!target || target.tagName.toLowerCase() != 'li'){
26029 r.pasteHTML('<br />');
26035 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
26036 this.cleanUpPaste.defer(100, this);
26042 }else if(Roo.isOpera){
26043 return function(e){
26044 var k = e.getKey();
26048 this.execCmd('InsertHTML','    ');
26051 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
26052 this.cleanUpPaste.defer(100, this);
26057 }else if(Roo.isSafari){
26058 return function(e){
26059 var k = e.getKey();
26063 this.execCmd('InsertText','\t');
26067 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
26068 this.cleanUpPaste.defer(100, this);
26076 getAllAncestors: function()
26078 var p = this.getSelectedNode();
26081 a.push(p); // push blank onto stack..
26082 p = this.getParentElement();
26086 while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
26090 a.push(this.doc.body);
26094 lastSelNode : false,
26097 getSelection : function()
26099 this.assignDocWin();
26100 return Roo.isIE ? this.doc.selection : this.win.getSelection();
26103 getSelectedNode: function()
26105 // this may only work on Gecko!!!
26107 // should we cache this!!!!
26112 var range = this.createRange(this.getSelection()).cloneRange();
26115 var parent = range.parentElement();
26117 var testRange = range.duplicate();
26118 testRange.moveToElementText(parent);
26119 if (testRange.inRange(range)) {
26122 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
26125 parent = parent.parentElement;
26130 // is ancestor a text element.
26131 var ac = range.commonAncestorContainer;
26132 if (ac.nodeType == 3) {
26133 ac = ac.parentNode;
26136 var ar = ac.childNodes;
26139 var other_nodes = [];
26140 var has_other_nodes = false;
26141 for (var i=0;i<ar.length;i++) {
26142 if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ?
26145 // fullly contained node.
26147 if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
26152 // probably selected..
26153 if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
26154 other_nodes.push(ar[i]);
26158 if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0)) {
26163 has_other_nodes = true;
26165 if (!nodes.length && other_nodes.length) {
26166 nodes= other_nodes;
26168 if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
26174 createRange: function(sel)
26176 // this has strange effects when using with
26177 // top toolbar - not sure if it's a great idea.
26178 //this.editor.contentWindow.focus();
26179 if (typeof sel != "undefined") {
26181 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
26183 return this.doc.createRange();
26186 return this.doc.createRange();
26189 getParentElement: function()
26192 this.assignDocWin();
26193 var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
26195 var range = this.createRange(sel);
26198 var p = range.commonAncestorContainer;
26199 while (p.nodeType == 3) { // text node
26210 * Range intersection.. the hard stuff...
26214 * [ -- selected range --- ]
26218 * if end is before start or hits it. fail.
26219 * if start is after end or hits it fail.
26221 * if either hits (but other is outside. - then it's not
26227 // @see http://www.thismuchiknow.co.uk/?p=64.
26228 rangeIntersectsNode : function(range, node)
26230 var nodeRange = node.ownerDocument.createRange();
26232 nodeRange.selectNode(node);
26234 nodeRange.selectNodeContents(node);
26237 var rangeStartRange = range.cloneRange();
26238 rangeStartRange.collapse(true);
26240 var rangeEndRange = range.cloneRange();
26241 rangeEndRange.collapse(false);
26243 var nodeStartRange = nodeRange.cloneRange();
26244 nodeStartRange.collapse(true);
26246 var nodeEndRange = nodeRange.cloneRange();
26247 nodeEndRange.collapse(false);
26249 return rangeStartRange.compareBoundaryPoints(
26250 Range.START_TO_START, nodeEndRange) == -1 &&
26251 rangeEndRange.compareBoundaryPoints(
26252 Range.START_TO_START, nodeStartRange) == 1;
26256 rangeCompareNode : function(range, node)
26258 var nodeRange = node.ownerDocument.createRange();
26260 nodeRange.selectNode(node);
26262 nodeRange.selectNodeContents(node);
26266 range.collapse(true);
26268 nodeRange.collapse(true);
26270 var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
26271 var ee = range.compareBoundaryPoints( Range.END_TO_END, nodeRange);
26273 //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
26275 var nodeIsBefore = ss == 1;
26276 var nodeIsAfter = ee == -1;
26278 if (nodeIsBefore && nodeIsAfter)
26280 if (!nodeIsBefore && nodeIsAfter)
26281 return 1; //right trailed.
26283 if (nodeIsBefore && !nodeIsAfter)
26284 return 2; // left trailed.
26289 // private? - in a new class?
26290 cleanUpPaste : function()
26292 // cleans up the whole document..
26293 Roo.log('cleanuppaste');
26294 this.cleanUpChildren(this.doc.body);
26295 var clean = this.cleanWordChars(this.doc.body.innerHTML);
26296 if (clean != this.doc.body.innerHTML) {
26297 this.doc.body.innerHTML = clean;
26302 cleanWordChars : function(input) {// change the chars to hex code
26303 var he = Roo.form.HtmlEditor;
26305 var output = input;
26306 Roo.each(he.swapCodes, function(sw) {
26307 var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
26309 output = output.replace(swapper, sw[1]);
26316 cleanUpChildren : function (n)
26318 if (!n.childNodes.length) {
26321 for (var i = n.childNodes.length-1; i > -1 ; i--) {
26322 this.cleanUpChild(n.childNodes[i]);
26329 cleanUpChild : function (node)
26332 //console.log(node);
26333 if (node.nodeName == "#text") {
26334 // clean up silly Windows -- stuff?
26337 if (node.nodeName == "#comment") {
26338 node.parentNode.removeChild(node);
26339 // clean up silly Windows -- stuff?
26343 if (Roo.form.HtmlEditor.black.indexOf(node.tagName.toLowerCase()) > -1) {
26345 node.parentNode.removeChild(node);
26350 var remove_keep_children= Roo.form.HtmlEditor.remove.indexOf(node.tagName.toLowerCase()) > -1;
26352 // remove <a name=....> as rendering on yahoo mailer is borked with this.
26353 // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
26355 //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
26356 // remove_keep_children = true;
26359 if (remove_keep_children) {
26360 this.cleanUpChildren(node);
26361 // inserts everything just before this node...
26362 while (node.childNodes.length) {
26363 var cn = node.childNodes[0];
26364 node.removeChild(cn);
26365 node.parentNode.insertBefore(cn, node);
26367 node.parentNode.removeChild(node);
26371 if (!node.attributes || !node.attributes.length) {
26372 this.cleanUpChildren(node);
26376 function cleanAttr(n,v)
26379 if (v.match(/^\./) || v.match(/^\//)) {
26382 if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
26385 if (v.match(/^#/)) {
26388 // Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
26389 node.removeAttribute(n);
26393 function cleanStyle(n,v)
26395 if (v.match(/expression/)) { //XSS?? should we even bother..
26396 node.removeAttribute(n);
26399 var cwhite = typeof(ed.cwhite) == 'undefined' ? Roo.form.HtmlEditor.cwhite : ed.cwhite;
26400 var cblack = typeof(ed.cblack) == 'undefined' ? Roo.form.HtmlEditor.cblack : ed.cblack;
26403 var parts = v.split(/;/);
26406 Roo.each(parts, function(p) {
26407 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
26411 var l = p.split(':').shift().replace(/\s+/g,'');
26412 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
26415 if ( cblack.indexOf(l) > -1) {
26416 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
26417 //node.removeAttribute(n);
26421 // only allow 'c whitelisted system attributes'
26422 if ( cwhite.length && cwhite.indexOf(l) < 0) {
26423 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
26424 //node.removeAttribute(n);
26434 if (clean.length) {
26435 node.setAttribute(n, clean.join(';'));
26437 node.removeAttribute(n);
26443 for (var i = node.attributes.length-1; i > -1 ; i--) {
26444 var a = node.attributes[i];
26447 if (a.name.toLowerCase().substr(0,2)=='on') {
26448 node.removeAttribute(a.name);
26451 if (Roo.form.HtmlEditor.ablack.indexOf(a.name.toLowerCase()) > -1) {
26452 node.removeAttribute(a.name);
26455 if (Roo.form.HtmlEditor.aclean.indexOf(a.name.toLowerCase()) > -1) {
26456 cleanAttr(a.name,a.value); // fixme..
26459 if (a.name == 'style') {
26460 cleanStyle(a.name,a.value);
26463 /// clean up MS crap..
26464 // tecnically this should be a list of valid class'es..
26467 if (a.name == 'class') {
26468 if (a.value.match(/^Mso/)) {
26469 node.className = '';
26472 if (a.value.match(/body/)) {
26473 node.className = '';
26484 this.cleanUpChildren(node);
26490 // hide stuff that is not compatible
26504 * @event specialkey
26508 * @cfg {String} fieldClass @hide
26511 * @cfg {String} focusClass @hide
26514 * @cfg {String} autoCreate @hide
26517 * @cfg {String} inputType @hide
26520 * @cfg {String} invalidClass @hide
26523 * @cfg {String} invalidText @hide
26526 * @cfg {String} msgFx @hide
26529 * @cfg {String} validateOnBlur @hide
26533 Roo.form.HtmlEditor.white = [
26534 'area', 'br', 'img', 'input', 'hr', 'wbr',
26536 'address', 'blockquote', 'center', 'dd', 'dir', 'div',
26537 'dl', 'dt', 'h1', 'h2', 'h3', 'h4',
26538 'h5', 'h6', 'hr', 'isindex', 'listing', 'marquee',
26539 'menu', 'multicol', 'ol', 'p', 'plaintext', 'pre',
26540 'table', 'ul', 'xmp',
26542 'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th',
26545 'dir', 'menu', 'ol', 'ul', 'dl',
26551 Roo.form.HtmlEditor.black = [
26552 // 'embed', 'object', // enable - backend responsiblity to clean thiese
26554 'base', 'basefont', 'bgsound', 'blink', 'body',
26555 'frame', 'frameset', 'head', 'html', 'ilayer',
26556 'iframe', 'layer', 'link', 'meta', 'object',
26557 'script', 'style' ,'title', 'xml' // clean later..
26559 Roo.form.HtmlEditor.clean = [
26560 'script', 'style', 'title', 'xml'
26562 Roo.form.HtmlEditor.remove = [
26567 Roo.form.HtmlEditor.ablack = [
26571 Roo.form.HtmlEditor.aclean = [
26572 'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc'
26576 Roo.form.HtmlEditor.pwhite= [
26577 'http', 'https', 'mailto'
26580 // white listed style attributes.
26581 Roo.form.HtmlEditor.cwhite= [
26582 // 'text-align', /// default is to allow most things..
26588 // black listed style attributes.
26589 Roo.form.HtmlEditor.cblack= [
26590 // 'font-size' -- this can be set by the project
26594 Roo.form.HtmlEditor.swapCodes =[
26605 // <script type="text/javascript">
26608 * Ext JS Library 1.1.1
26609 * Copyright(c) 2006-2007, Ext JS, LLC.
26615 * @class Roo.form.HtmlEditorToolbar1
26620 new Roo.form.HtmlEditor({
26623 new Roo.form.HtmlEditorToolbar1({
26624 disable : { fonts: 1 , format: 1, ..., ... , ...],
26630 * @cfg {Object} disable List of elements to disable..
26631 * @cfg {Array} btns List of additional buttons.
26635 * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
26638 Roo.form.HtmlEditor.ToolbarStandard = function(config)
26641 Roo.apply(this, config);
26643 // default disabled, based on 'good practice'..
26644 this.disable = this.disable || {};
26645 Roo.applyIf(this.disable, {
26648 specialElements : true
26652 //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
26653 // dont call parent... till later.
26656 Roo.apply(Roo.form.HtmlEditor.ToolbarStandard.prototype, {
26664 * @cfg {Object} disable List of toolbar elements to disable
26669 * @cfg {Array} fontFamilies An array of available font families
26687 // "á" , ?? a acute?
26692 "°" // , // degrees
26694 // "é" , // e ecute
26695 // "ú" , // u ecute?
26698 specialElements : [
26700 text: "Insert Table",
26703 ihtml : '<table><tr><td>Cell</td></tr></table>'
26707 text: "Insert Image",
26710 ihtml : '<img src="about:blank"/>'
26719 "form", "input:text", "input:hidden", "input:checkbox", "input:radio", "input:password",
26720 "input:submit", "input:button", "select", "textarea", "label" ],
26723 ["h1"],["h2"],["h3"],["h4"],["h5"],["h6"],
26725 ["abbr"],[ "acronym"],[ "address"],[ "cite"],[ "samp"],[ "var"],
26729 * @cfg {String} defaultFont default font to use.
26731 defaultFont: 'tahoma',
26733 fontSelect : false,
26736 formatCombo : false,
26738 init : function(editor)
26740 this.editor = editor;
26743 var fid = editor.frameId;
26745 function btn(id, toggle, handler){
26746 var xid = fid + '-'+ id ;
26750 cls : 'x-btn-icon x-edit-'+id,
26751 enableToggle:toggle !== false,
26752 scope: editor, // was editor...
26753 handler:handler||editor.relayBtnCmd,
26754 clickEvent:'mousedown',
26755 tooltip: etb.buttonTips[id] || undefined, ///tips ???
26762 var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
26764 // stop form submits
26765 tb.el.on('click', function(e){
26766 e.preventDefault(); // what does this do?
26769 if(!this.disable.font) { // && !Roo.isSafari){
26770 /* why no safari for fonts
26771 editor.fontSelect = tb.el.createChild({
26774 cls:'x-font-select',
26775 html: this.createFontOptions()
26778 editor.fontSelect.on('change', function(){
26779 var font = editor.fontSelect.dom.value;
26780 editor.relayCmd('fontname', font);
26781 editor.deferFocus();
26785 editor.fontSelect.dom,
26791 if(!this.disable.formats){
26792 this.formatCombo = new Roo.form.ComboBox({
26793 store: new Roo.data.SimpleStore({
26796 data : this.formats // from states.js
26800 //autoCreate : {tag: "div", size: "20"},
26801 displayField:'tag',
26805 triggerAction: 'all',
26806 emptyText:'Add tag',
26807 selectOnFocus:true,
26810 'select': function(c, r, i) {
26811 editor.insertTag(r.get('tag'));
26817 tb.addField(this.formatCombo);
26821 if(!this.disable.format){
26828 if(!this.disable.fontSize){
26833 btn('increasefontsize', false, editor.adjustFont),
26834 btn('decreasefontsize', false, editor.adjustFont)
26839 if(!this.disable.colors){
26842 id:editor.frameId +'-forecolor',
26843 cls:'x-btn-icon x-edit-forecolor',
26844 clickEvent:'mousedown',
26845 tooltip: this.buttonTips['forecolor'] || undefined,
26847 menu : new Roo.menu.ColorMenu({
26848 allowReselect: true,
26849 focus: Roo.emptyFn,
26852 selectHandler: function(cp, color){
26853 editor.execCmd('forecolor', Roo.isSafari || Roo.isIE ? '#'+color : color);
26854 editor.deferFocus();
26857 clickEvent:'mousedown'
26860 id:editor.frameId +'backcolor',
26861 cls:'x-btn-icon x-edit-backcolor',
26862 clickEvent:'mousedown',
26863 tooltip: this.buttonTips['backcolor'] || undefined,
26865 menu : new Roo.menu.ColorMenu({
26866 focus: Roo.emptyFn,
26869 allowReselect: true,
26870 selectHandler: function(cp, color){
26872 editor.execCmd('useCSS', false);
26873 editor.execCmd('hilitecolor', color);
26874 editor.execCmd('useCSS', true);
26875 editor.deferFocus();
26877 editor.execCmd(Roo.isOpera ? 'hilitecolor' : 'backcolor',
26878 Roo.isSafari || Roo.isIE ? '#'+color : color);
26879 editor.deferFocus();
26883 clickEvent:'mousedown'
26888 // now add all the items...
26891 if(!this.disable.alignments){
26894 btn('justifyleft'),
26895 btn('justifycenter'),
26896 btn('justifyright')
26900 //if(!Roo.isSafari){
26901 if(!this.disable.links){
26904 btn('createlink', false, editor.createLink) /// MOVE TO HERE?!!?!?!?!
26908 if(!this.disable.lists){
26911 btn('insertorderedlist'),
26912 btn('insertunorderedlist')
26915 if(!this.disable.sourceEdit){
26918 btn('sourceedit', true, function(btn){
26919 this.toggleSourceEdit(btn.pressed);
26926 // special menu.. - needs to be tidied up..
26927 if (!this.disable.special) {
26930 cls: 'x-edit-none',
26936 for (var i =0; i < this.specialChars.length; i++) {
26937 smenu.menu.items.push({
26939 html: this.specialChars[i],
26940 handler: function(a,b) {
26941 editor.insertAtCursor(String.fromCharCode(a.html.replace('&#','').replace(';', '')));
26942 //editor.insertAtCursor(a.html);
26955 if (!this.disable.specialElements) {
26958 cls: 'x-edit-none',
26963 for (var i =0; i < this.specialElements.length; i++) {
26964 semenu.menu.items.push(
26966 handler: function(a,b) {
26967 editor.insertAtCursor(this.ihtml);
26969 }, this.specialElements[i])
26981 for(var i =0; i< this.btns.length;i++) {
26982 var b = Roo.factory(this.btns[i],Roo.form);
26983 b.cls = 'x-edit-none';
26992 // disable everything...
26994 this.tb.items.each(function(item){
26995 if(item.id != editor.frameId+ '-sourceedit'){
26999 this.rendered = true;
27001 // the all the btns;
27002 editor.on('editorevent', this.updateToolbar, this);
27003 // other toolbars need to implement this..
27004 //editor.on('editmodechange', this.updateToolbar, this);
27010 * Protected method that will not generally be called directly. It triggers
27011 * a toolbar update by reading the markup state of the current selection in the editor.
27013 updateToolbar: function(){
27015 if(!this.editor.activated){
27016 this.editor.onFirstFocus();
27020 var btns = this.tb.items.map,
27021 doc = this.editor.doc,
27022 frameId = this.editor.frameId;
27024 if(!this.disable.font && !Roo.isSafari){
27026 var name = (doc.queryCommandValue('FontName')||this.editor.defaultFont).toLowerCase();
27027 if(name != this.fontSelect.dom.value){
27028 this.fontSelect.dom.value = name;
27032 if(!this.disable.format){
27033 btns[frameId + '-bold'].toggle(doc.queryCommandState('bold'));
27034 btns[frameId + '-italic'].toggle(doc.queryCommandState('italic'));
27035 btns[frameId + '-underline'].toggle(doc.queryCommandState('underline'));
27037 if(!this.disable.alignments){
27038 btns[frameId + '-justifyleft'].toggle(doc.queryCommandState('justifyleft'));
27039 btns[frameId + '-justifycenter'].toggle(doc.queryCommandState('justifycenter'));
27040 btns[frameId + '-justifyright'].toggle(doc.queryCommandState('justifyright'));
27042 if(!Roo.isSafari && !this.disable.lists){
27043 btns[frameId + '-insertorderedlist'].toggle(doc.queryCommandState('insertorderedlist'));
27044 btns[frameId + '-insertunorderedlist'].toggle(doc.queryCommandState('insertunorderedlist'));
27047 var ans = this.editor.getAllAncestors();
27048 if (this.formatCombo) {
27051 var store = this.formatCombo.store;
27052 this.formatCombo.setValue("");
27053 for (var i =0; i < ans.length;i++) {
27054 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
27056 this.formatCombo.setValue(ans[i].tagName.toLowerCase());
27064 // hides menus... - so this cant be on a menu...
27065 Roo.menu.MenuMgr.hideAll();
27067 //this.editorsyncValue();
27071 createFontOptions : function(){
27072 var buf = [], fs = this.fontFamilies, ff, lc;
27076 for(var i = 0, len = fs.length; i< len; i++){
27078 lc = ff.toLowerCase();
27080 '<option value="',lc,'" style="font-family:',ff,';"',
27081 (this.defaultFont == lc ? ' selected="true">' : '>'),
27086 return buf.join('');
27089 toggleSourceEdit : function(sourceEditMode){
27090 if(sourceEditMode === undefined){
27091 sourceEditMode = !this.sourceEditMode;
27093 this.sourceEditMode = sourceEditMode === true;
27094 var btn = this.tb.items.get(this.editor.frameId +'-sourceedit');
27095 // just toggle the button?
27096 if(btn.pressed !== this.editor.sourceEditMode){
27097 btn.toggle(this.editor.sourceEditMode);
27101 if(this.sourceEditMode){
27102 this.tb.items.each(function(item){
27103 if(item.cmd != 'sourceedit'){
27109 if(this.initialized){
27110 this.tb.items.each(function(item){
27116 // tell the editor that it's been pressed..
27117 this.editor.toggleSourceEdit(sourceEditMode);
27121 * Object collection of toolbar tooltips for the buttons in the editor. The key
27122 * is the command id associated with that button and the value is a valid QuickTips object.
27127 title: 'Bold (Ctrl+B)',
27128 text: 'Make the selected text bold.',
27129 cls: 'x-html-editor-tip'
27132 title: 'Italic (Ctrl+I)',
27133 text: 'Make the selected text italic.',
27134 cls: 'x-html-editor-tip'
27142 title: 'Bold (Ctrl+B)',
27143 text: 'Make the selected text bold.',
27144 cls: 'x-html-editor-tip'
27147 title: 'Italic (Ctrl+I)',
27148 text: 'Make the selected text italic.',
27149 cls: 'x-html-editor-tip'
27152 title: 'Underline (Ctrl+U)',
27153 text: 'Underline the selected text.',
27154 cls: 'x-html-editor-tip'
27156 increasefontsize : {
27157 title: 'Grow Text',
27158 text: 'Increase the font size.',
27159 cls: 'x-html-editor-tip'
27161 decreasefontsize : {
27162 title: 'Shrink Text',
27163 text: 'Decrease the font size.',
27164 cls: 'x-html-editor-tip'
27167 title: 'Text Highlight Color',
27168 text: 'Change the background color of the selected text.',
27169 cls: 'x-html-editor-tip'
27172 title: 'Font Color',
27173 text: 'Change the color of the selected text.',
27174 cls: 'x-html-editor-tip'
27177 title: 'Align Text Left',
27178 text: 'Align text to the left.',
27179 cls: 'x-html-editor-tip'
27182 title: 'Center Text',
27183 text: 'Center text in the editor.',
27184 cls: 'x-html-editor-tip'
27187 title: 'Align Text Right',
27188 text: 'Align text to the right.',
27189 cls: 'x-html-editor-tip'
27191 insertunorderedlist : {
27192 title: 'Bullet List',
27193 text: 'Start a bulleted list.',
27194 cls: 'x-html-editor-tip'
27196 insertorderedlist : {
27197 title: 'Numbered List',
27198 text: 'Start a numbered list.',
27199 cls: 'x-html-editor-tip'
27202 title: 'Hyperlink',
27203 text: 'Make the selected text a hyperlink.',
27204 cls: 'x-html-editor-tip'
27207 title: 'Source Edit',
27208 text: 'Switch to source editing mode.',
27209 cls: 'x-html-editor-tip'
27213 onDestroy : function(){
27216 this.tb.items.each(function(item){
27218 item.menu.removeAll();
27220 item.menu.el.destroy();
27228 onFirstFocus: function() {
27229 this.tb.items.each(function(item){
27238 // <script type="text/javascript">
27241 * Ext JS Library 1.1.1
27242 * Copyright(c) 2006-2007, Ext JS, LLC.
27249 * @class Roo.form.HtmlEditor.ToolbarContext
27254 new Roo.form.HtmlEditor({
27257 { xtype: 'ToolbarStandard', styles : {} }
27258 { xtype: 'ToolbarContext', disable : {} }
27264 * @config : {Object} disable List of elements to disable.. (not done yet.)
27265 * @config : {Object} styles Map of styles available.
27269 Roo.form.HtmlEditor.ToolbarContext = function(config)
27272 Roo.apply(this, config);
27273 //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
27274 // dont call parent... till later.
27275 this.styles = this.styles || {};
27280 Roo.form.HtmlEditor.ToolbarContext.types = {
27292 opts : [ [""],[ "left"],[ "right"],[ "center"],[ "top"]],
27354 opts : [[""],[ "left"],[ "center"],[ "right"],[ "justify"],[ "char"]],
27359 opts : [[""],[ "top"],[ "middle"],[ "bottom"],[ "baseline"]],
27369 style : 'fontFamily',
27370 displayField: 'display',
27371 optname : 'font-family',
27420 // should we really allow this??
27421 // should this just be
27432 style : 'fontFamily',
27433 displayField: 'display',
27434 optname : 'font-family',
27441 style : 'fontFamily',
27442 displayField: 'display',
27443 optname : 'font-family',
27450 style : 'fontFamily',
27451 displayField: 'display',
27452 optname : 'font-family',
27463 // this should be configurable.. - you can either set it up using stores, or modify options somehwere..
27464 Roo.form.HtmlEditor.ToolbarContext.stores = false;
27466 Roo.form.HtmlEditor.ToolbarContext.options = {
27468 [ 'Helvetica,Arial,sans-serif', 'Helvetica'],
27469 [ 'Courier New', 'Courier New'],
27470 [ 'Tahoma', 'Tahoma'],
27471 [ 'Times New Roman,serif', 'Times'],
27472 [ 'Verdana','Verdana' ]
27476 // fixme - these need to be configurable..
27479 Roo.form.HtmlEditor.ToolbarContext.types
27482 Roo.apply(Roo.form.HtmlEditor.ToolbarContext.prototype, {
27490 * @cfg {Object} disable List of toolbar elements to disable
27495 * @cfg {Object} styles List of styles
27496 * eg. { '*' : [ 'headline' ] , 'TD' : [ 'underline', 'double-underline' ] }
27498 * These must be defined in the page, so they get rendered correctly..
27509 init : function(editor)
27511 this.editor = editor;
27514 var fid = editor.frameId;
27516 function btn(id, toggle, handler){
27517 var xid = fid + '-'+ id ;
27521 cls : 'x-btn-icon x-edit-'+id,
27522 enableToggle:toggle !== false,
27523 scope: editor, // was editor...
27524 handler:handler||editor.relayBtnCmd,
27525 clickEvent:'mousedown',
27526 tooltip: etb.buttonTips[id] || undefined, ///tips ???
27530 // create a new element.
27531 var wdiv = editor.wrap.createChild({
27533 }, editor.wrap.dom.firstChild.nextSibling, true);
27535 // can we do this more than once??
27537 // stop form submits
27540 // disable everything...
27541 var ty= Roo.form.HtmlEditor.ToolbarContext.types;
27542 this.toolbars = {};
27544 for (var i in ty) {
27546 this.toolbars[i] = this.buildToolbar(ty[i],i);
27548 this.tb = this.toolbars.BODY;
27550 this.buildFooter();
27551 this.footer.show();
27552 editor.on('hide', function( ) { this.footer.hide() }, this);
27553 editor.on('show', function( ) { this.footer.show() }, this);
27556 this.rendered = true;
27558 // the all the btns;
27559 editor.on('editorevent', this.updateToolbar, this);
27560 // other toolbars need to implement this..
27561 //editor.on('editmodechange', this.updateToolbar, this);
27567 * Protected method that will not generally be called directly. It triggers
27568 * a toolbar update by reading the markup state of the current selection in the editor.
27570 updateToolbar: function(editor,ev,sel){
27573 // capture mouse up - this is handy for selecting images..
27574 // perhaps should go somewhere else...
27575 if(!this.editor.activated){
27576 this.editor.onFirstFocus();
27580 // http://developer.yahoo.com/yui/docs/simple-editor.js.html
27581 // selectNode - might want to handle IE?
27583 (ev.type == 'mouseup' || ev.type == 'click' ) &&
27584 ev.target && ev.target.tagName == 'IMG') {
27585 // they have click on an image...
27586 // let's see if we can change the selection...
27589 var nodeRange = sel.ownerDocument.createRange();
27591 nodeRange.selectNode(sel);
27593 nodeRange.selectNodeContents(sel);
27595 //nodeRange.collapse(true);
27596 var s = editor.win.getSelection();
27597 s.removeAllRanges();
27598 s.addRange(nodeRange);
27602 var updateFooter = sel ? false : true;
27605 var ans = this.editor.getAllAncestors();
27608 var ty= Roo.form.HtmlEditor.ToolbarContext.types;
27611 sel = ans.length ? (ans[0] ? ans[0] : ans[1]) : this.editor.doc.body;
27612 sel = sel ? sel : this.editor.doc.body;
27613 sel = sel.tagName.length ? sel : this.editor.doc.body;
27616 // pick a menu that exists..
27617 var tn = sel.tagName.toUpperCase();
27618 //sel = typeof(ty[tn]) != 'undefined' ? sel : this.editor.doc.body;
27620 tn = sel.tagName.toUpperCase();
27622 var lastSel = this.tb.selectedNode
27624 this.tb.selectedNode = sel;
27626 // if current menu does not match..
27627 if ((this.tb.name != tn) || (lastSel != this.tb.selectedNode)) {
27630 ///console.log("show: " + tn);
27631 this.tb = typeof(ty[tn]) != 'undefined' ? this.toolbars[tn] : this.toolbars['*'];
27634 this.tb.items.first().el.innerHTML = tn + ': ';
27637 // update attributes
27638 if (this.tb.fields) {
27639 this.tb.fields.each(function(e) {
27641 e.setValue(sel.style[e.stylename]);
27644 e.setValue(sel.getAttribute(e.attrname));
27648 var hasStyles = false;
27649 for(var i in this.styles) {
27656 var st = this.tb.fields.item(0);
27658 st.store.removeAll();
27661 var cn = sel.className.split(/\s+/);
27664 if (this.styles['*']) {
27666 Roo.each(this.styles['*'], function(v) {
27667 avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );
27670 if (this.styles[tn]) {
27671 Roo.each(this.styles[tn], function(v) {
27672 avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );
27676 st.store.loadData(avs);
27680 // flag our selected Node.
27681 this.tb.selectedNode = sel;
27684 Roo.menu.MenuMgr.hideAll();
27688 if (!updateFooter) {
27689 //this.footDisp.dom.innerHTML = '';
27692 // update the footer
27696 this.footerEls = ans.reverse();
27697 Roo.each(this.footerEls, function(a,i) {
27698 if (!a) { return; }
27699 html += html.length ? ' > ' : '';
27701 html += '<span class="x-ed-loc-' + i + '">' + a.tagName + '</span>';
27706 var sz = this.footDisp.up('td').getSize();
27707 this.footDisp.dom.style.width = (sz.width -10) + 'px';
27708 this.footDisp.dom.style.marginLeft = '5px';
27710 this.footDisp.dom.style.overflow = 'hidden';
27712 this.footDisp.dom.innerHTML = html;
27714 //this.editorsyncValue();
27721 onDestroy : function(){
27724 this.tb.items.each(function(item){
27726 item.menu.removeAll();
27728 item.menu.el.destroy();
27736 onFirstFocus: function() {
27737 // need to do this for all the toolbars..
27738 this.tb.items.each(function(item){
27742 buildToolbar: function(tlist, nm)
27744 var editor = this.editor;
27745 // create a new element.
27746 var wdiv = editor.wrap.createChild({
27748 }, editor.wrap.dom.firstChild.nextSibling, true);
27751 var tb = new Roo.Toolbar(wdiv);
27754 tb.add(nm+ ": ");
27757 for(var i in this.styles) {
27762 if (styles && styles.length) {
27764 // this needs a multi-select checkbox...
27765 tb.addField( new Roo.form.ComboBox({
27766 store: new Roo.data.SimpleStore({
27768 fields: ['val', 'selected'],
27771 name : '-roo-edit-className',
27772 attrname : 'className',
27773 displayField: 'val',
27777 triggerAction: 'all',
27778 emptyText:'Select Style',
27779 selectOnFocus:true,
27782 'select': function(c, r, i) {
27783 // initial support only for on class per el..
27784 tb.selectedNode.className = r ? r.get('val') : '';
27785 editor.syncValue();
27792 var tbc = Roo.form.HtmlEditor.ToolbarContext;
27793 var tbops = tbc.options;
27795 for (var i in tlist) {
27797 var item = tlist[i];
27798 tb.add(item.title + ": ");
27801 //optname == used so you can configure the options available..
27802 var opts = item.opts ? item.opts : false;
27803 if (item.optname) {
27804 opts = tbops[item.optname];
27809 // opts == pulldown..
27810 tb.addField( new Roo.form.ComboBox({
27811 store: typeof(tbc.stores[i]) != 'undefined' ? Roo.factory(tbc.stores[i],Roo.data) : new Roo.data.SimpleStore({
27813 fields: ['val', 'display'],
27816 name : '-roo-edit-' + i,
27818 stylename : item.style ? item.style : false,
27819 displayField: item.displayField ? item.displayField : 'val',
27820 valueField : 'val',
27822 mode: typeof(tbc.stores[i]) != 'undefined' ? 'remote' : 'local',
27824 triggerAction: 'all',
27825 emptyText:'Select',
27826 selectOnFocus:true,
27827 width: item.width ? item.width : 130,
27829 'select': function(c, r, i) {
27831 tb.selectedNode.style[c.stylename] = r.get('val');
27834 tb.selectedNode.setAttribute(c.attrname, r.get('val'));
27843 tb.addField( new Roo.form.TextField({
27846 //allowBlank:false,
27851 tb.addField( new Roo.form.TextField({
27852 name: '-roo-edit-' + i,
27859 'change' : function(f, nv, ov) {
27860 tb.selectedNode.setAttribute(f.attrname, nv);
27869 text: 'Remove Tag',
27872 click : function ()
27875 // undo does not work.
27877 var sn = tb.selectedNode;
27879 var pn = sn.parentNode;
27881 var stn = sn.childNodes[0];
27882 var en = sn.childNodes[sn.childNodes.length - 1 ];
27883 while (sn.childNodes.length) {
27884 var node = sn.childNodes[0];
27885 sn.removeChild(node);
27887 pn.insertBefore(node, sn);
27890 pn.removeChild(sn);
27891 var range = editor.createRange();
27893 range.setStart(stn,0);
27894 range.setEnd(en,0); //????
27895 //range.selectNode(sel);
27898 var selection = editor.getSelection();
27899 selection.removeAllRanges();
27900 selection.addRange(range);
27904 //_this.updateToolbar(null, null, pn);
27905 _this.updateToolbar(null, null, null);
27906 _this.footDisp.dom.innerHTML = '';
27916 tb.el.on('click', function(e){
27917 e.preventDefault(); // what does this do?
27919 tb.el.setVisibilityMode( Roo.Element.DISPLAY);
27922 // dont need to disable them... as they will get hidden
27927 buildFooter : function()
27930 var fel = this.editor.wrap.createChild();
27931 this.footer = new Roo.Toolbar(fel);
27932 // toolbar has scrolly on left / right?
27933 var footDisp= new Roo.Toolbar.Fill();
27939 handler : function() {
27940 _t.footDisp.scrollTo('left',0,true)
27944 this.footer.add( footDisp );
27949 handler : function() {
27951 _t.footDisp.select('span').last().scrollIntoView(_t.footDisp,true);
27955 var fel = Roo.get(footDisp.el);
27956 fel.addClass('x-editor-context');
27957 this.footDispWrap = fel;
27958 this.footDispWrap.overflow = 'hidden';
27960 this.footDisp = fel.createChild();
27961 this.footDispWrap.on('click', this.onContextClick, this)
27965 onContextClick : function (ev,dom)
27967 ev.preventDefault();
27968 var cn = dom.className;
27970 if (!cn.match(/x-ed-loc-/)) {
27973 var n = cn.split('-').pop();
27974 var ans = this.footerEls;
27978 var range = this.editor.createRange();
27980 range.selectNodeContents(sel);
27981 //range.selectNode(sel);
27984 var selection = this.editor.getSelection();
27985 selection.removeAllRanges();
27986 selection.addRange(range);
27990 this.updateToolbar(null, null, sel);
28007 * Ext JS Library 1.1.1
28008 * Copyright(c) 2006-2007, Ext JS, LLC.
28010 * Originally Released Under LGPL - original licence link has changed is not relivant.
28013 * <script type="text/javascript">
28017 * @class Roo.form.BasicForm
28018 * @extends Roo.util.Observable
28019 * Supplies the functionality to do "actions" on forms and initialize Roo.form.Field types on existing markup.
28021 * @param {String/HTMLElement/Roo.Element} el The form element or its id
28022 * @param {Object} config Configuration options
28024 Roo.form.BasicForm = function(el, config){
28025 this.allItems = [];
28026 this.childForms = [];
28027 Roo.apply(this, config);
28029 * The Roo.form.Field items in this form.
28030 * @type MixedCollection
28034 this.items = new Roo.util.MixedCollection(false, function(o){
28035 return o.id || (o.id = Roo.id());
28039 * @event beforeaction
28040 * Fires before any action is performed. Return false to cancel the action.
28041 * @param {Form} this
28042 * @param {Action} action The action to be performed
28044 beforeaction: true,
28046 * @event actionfailed
28047 * Fires when an action fails.
28048 * @param {Form} this
28049 * @param {Action} action The action that failed
28051 actionfailed : true,
28053 * @event actioncomplete
28054 * Fires when an action is completed.
28055 * @param {Form} this
28056 * @param {Action} action The action that completed
28058 actioncomplete : true
28063 Roo.form.BasicForm.superclass.constructor.call(this);
28066 Roo.extend(Roo.form.BasicForm, Roo.util.Observable, {
28068 * @cfg {String} method
28069 * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
28072 * @cfg {DataReader} reader
28073 * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when executing "load" actions.
28074 * This is optional as there is built-in support for processing JSON.
28077 * @cfg {DataReader} errorReader
28078 * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when reading validation errors on "submit" actions.
28079 * This is completely optional as there is built-in support for processing JSON.
28082 * @cfg {String} url
28083 * The URL to use for form actions if one isn't supplied in the action options.
28086 * @cfg {Boolean} fileUpload
28087 * Set to true if this form is a file upload.
28091 * @cfg {Object} baseParams
28092 * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
28097 * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
28102 activeAction : null,
28105 * @cfg {Boolean} trackResetOnLoad If set to true, form.reset() resets to the last loaded
28106 * or setValues() data instead of when the form was first created.
28108 trackResetOnLoad : false,
28112 * childForms - used for multi-tab forms
28115 childForms : false,
28118 * allItems - full list of fields.
28124 * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
28125 * element by passing it or its id or mask the form itself by passing in true.
28128 waitMsgTarget : false,
28131 initEl : function(el){
28132 this.el = Roo.get(el);
28133 this.id = this.el.id || Roo.id();
28134 this.el.on('submit', this.onSubmit, this);
28135 this.el.addClass('x-form');
28139 onSubmit : function(e){
28144 * Returns true if client-side validation on the form is successful.
28147 isValid : function(){
28149 this.items.each(function(f){
28158 * Returns true if any fields in this form have changed since their original load.
28161 isDirty : function(){
28163 this.items.each(function(f){
28173 * Performs a predefined action (submit or load) or custom actions you define on this form.
28174 * @param {String} actionName The name of the action type
28175 * @param {Object} options (optional) The options to pass to the action. All of the config options listed
28176 * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
28177 * accept other config options):
28179 Property Type Description
28180 ---------------- --------------- ----------------------------------------------------------------------------------
28181 url String The url for the action (defaults to the form's url)
28182 method String The form method to use (defaults to the form's method, or POST if not defined)
28183 params String/Object The params to pass (defaults to the form's baseParams, or none if not defined)
28184 clientValidation Boolean Applies to submit only. Pass true to call form.isValid() prior to posting to
28185 validate the form on the client (defaults to false)
28187 * @return {BasicForm} this
28189 doAction : function(action, options){
28190 if(typeof action == 'string'){
28191 action = new Roo.form.Action.ACTION_TYPES[action](this, options);
28193 if(this.fireEvent('beforeaction', this, action) !== false){
28194 this.beforeAction(action);
28195 action.run.defer(100, action);
28201 * Shortcut to do a submit action.
28202 * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
28203 * @return {BasicForm} this
28205 submit : function(options){
28206 this.doAction('submit', options);
28211 * Shortcut to do a load action.
28212 * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
28213 * @return {BasicForm} this
28215 load : function(options){
28216 this.doAction('load', options);
28221 * Persists the values in this form into the passed Roo.data.Record object in a beginEdit/endEdit block.
28222 * @param {Record} record The record to edit
28223 * @return {BasicForm} this
28225 updateRecord : function(record){
28226 record.beginEdit();
28227 var fs = record.fields;
28228 fs.each(function(f){
28229 var field = this.findField(f.name);
28231 record.set(f.name, field.getValue());
28239 * Loads an Roo.data.Record into this form.
28240 * @param {Record} record The record to load
28241 * @return {BasicForm} this
28243 loadRecord : function(record){
28244 this.setValues(record.data);
28249 beforeAction : function(action){
28250 var o = action.options;
28253 if(this.waitMsgTarget === true){
28254 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
28255 }else if(this.waitMsgTarget){
28256 this.waitMsgTarget = Roo.get(this.waitMsgTarget);
28257 this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
28259 Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
28265 afterAction : function(action, success){
28266 this.activeAction = null;
28267 var o = action.options;
28269 if(this.waitMsgTarget === true){
28271 }else if(this.waitMsgTarget){
28272 this.waitMsgTarget.unmask();
28274 Roo.MessageBox.updateProgress(1);
28275 Roo.MessageBox.hide();
28282 Roo.callback(o.success, o.scope, [this, action]);
28283 this.fireEvent('actioncomplete', this, action);
28287 // failure condition..
28288 // we have a scenario where updates need confirming.
28289 // eg. if a locking scenario exists..
28290 // we look for { errors : { needs_confirm : true }} in the response.
28292 (typeof(action.result) != 'undefined') &&
28293 (typeof(action.result.errors) != 'undefined') &&
28294 (typeof(action.result.errors.needs_confirm) != 'undefined')
28297 Roo.MessageBox.confirm(
28298 "Change requires confirmation",
28299 action.result.errorMsg,
28304 _t.doAction('submit', { params : { _submit_confirmed : 1 } } );
28314 Roo.callback(o.failure, o.scope, [this, action]);
28315 // show an error message if no failed handler is set..
28316 if (!this.hasListener('actionfailed')) {
28317 Roo.MessageBox.alert("Error",
28318 (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
28319 action.result.errorMsg :
28320 "Saving Failed, please check your entries or try again"
28324 this.fireEvent('actionfailed', this, action);
28330 * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
28331 * @param {String} id The value to search for
28334 findField : function(id){
28335 var field = this.items.get(id);
28337 this.items.each(function(f){
28338 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
28344 return field || null;
28348 * Add a secondary form to this one,
28349 * Used to provide tabbed forms. One form is primary, with hidden values
28350 * which mirror the elements from the other forms.
28352 * @param {Roo.form.Form} form to add.
28355 addForm : function(form)
28358 if (this.childForms.indexOf(form) > -1) {
28362 this.childForms.push(form);
28364 Roo.each(form.allItems, function (fe) {
28366 n = typeof(fe.getName) == 'undefined' ? fe.name : fe.getName();
28367 if (this.findField(n)) { // already added..
28370 var add = new Roo.form.Hidden({
28373 add.render(this.el);
28380 * Mark fields in this form invalid in bulk.
28381 * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
28382 * @return {BasicForm} this
28384 markInvalid : function(errors){
28385 if(errors instanceof Array){
28386 for(var i = 0, len = errors.length; i < len; i++){
28387 var fieldError = errors[i];
28388 var f = this.findField(fieldError.id);
28390 f.markInvalid(fieldError.msg);
28396 if(typeof errors[id] != 'function' && (field = this.findField(id))){
28397 field.markInvalid(errors[id]);
28401 Roo.each(this.childForms || [], function (f) {
28402 f.markInvalid(errors);
28409 * Set values for fields in this form in bulk.
28410 * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
28411 * @return {BasicForm} this
28413 setValues : function(values){
28414 if(values instanceof Array){ // array of objects
28415 for(var i = 0, len = values.length; i < len; i++){
28417 var f = this.findField(v.id);
28419 f.setValue(v.value);
28420 if(this.trackResetOnLoad){
28421 f.originalValue = f.getValue();
28425 }else{ // object hash
28428 if(typeof values[id] != 'function' && (field = this.findField(id))){
28430 if (field.setFromData &&
28431 field.valueField &&
28432 field.displayField &&
28433 // combos' with local stores can
28434 // be queried via setValue()
28435 // to set their value..
28436 (field.store && !field.store.isLocal)
28440 sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
28441 sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
28442 field.setFromData(sd);
28445 field.setValue(values[id]);
28449 if(this.trackResetOnLoad){
28450 field.originalValue = field.getValue();
28456 Roo.each(this.childForms || [], function (f) {
28457 f.setValues(values);
28464 * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
28465 * they are returned as an array.
28466 * @param {Boolean} asString
28469 getValues : function(asString){
28470 if (this.childForms) {
28471 // copy values from the child forms
28472 Roo.each(this.childForms, function (f) {
28473 this.setValues(f.getValues());
28479 var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
28480 if(asString === true){
28483 return Roo.urlDecode(fs);
28487 * Returns the fields in this form as an object with key/value pairs.
28488 * This differs from getValues as it calls getValue on each child item, rather than using dom data.
28491 getFieldValues : function(with_hidden)
28493 if (this.childForms) {
28494 // copy values from the child forms
28495 // should this call getFieldValues - probably not as we do not currently copy
28496 // hidden fields when we generate..
28497 Roo.each(this.childForms, function (f) {
28498 this.setValues(f.getValues());
28503 this.items.each(function(f){
28504 if (!f.getName()) {
28507 var v = f.getValue();
28508 if (f.inputType =='radio') {
28509 if (typeof(ret[f.getName()]) == 'undefined') {
28510 ret[f.getName()] = ''; // empty..
28513 if (!f.el.dom.checked) {
28517 v = f.el.dom.value;
28521 // not sure if this supported any more..
28522 if ((typeof(v) == 'object') && f.getRawValue) {
28523 v = f.getRawValue() ; // dates..
28525 // combo boxes where name != hiddenName...
28526 if (f.name != f.getName()) {
28527 ret[f.name] = f.getRawValue();
28529 ret[f.getName()] = v;
28536 * Clears all invalid messages in this form.
28537 * @return {BasicForm} this
28539 clearInvalid : function(){
28540 this.items.each(function(f){
28544 Roo.each(this.childForms || [], function (f) {
28553 * Resets this form.
28554 * @return {BasicForm} this
28556 reset : function(){
28557 this.items.each(function(f){
28561 Roo.each(this.childForms || [], function (f) {
28570 * Add Roo.form components to this form.
28571 * @param {Field} field1
28572 * @param {Field} field2 (optional)
28573 * @param {Field} etc (optional)
28574 * @return {BasicForm} this
28577 this.items.addAll(Array.prototype.slice.call(arguments, 0));
28583 * Removes a field from the items collection (does NOT remove its markup).
28584 * @param {Field} field
28585 * @return {BasicForm} this
28587 remove : function(field){
28588 this.items.remove(field);
28593 * Looks at the fields in this form, checks them for an id attribute,
28594 * and calls applyTo on the existing dom element with that id.
28595 * @return {BasicForm} this
28597 render : function(){
28598 this.items.each(function(f){
28599 if(f.isFormField && !f.rendered && document.getElementById(f.id)){ // if the element exists
28607 * Calls {@link Ext#apply} for all fields in this form with the passed object.
28608 * @param {Object} values
28609 * @return {BasicForm} this
28611 applyToFields : function(o){
28612 this.items.each(function(f){
28619 * Calls {@link Ext#applyIf} for all field in this form with the passed object.
28620 * @param {Object} values
28621 * @return {BasicForm} this
28623 applyIfToFields : function(o){
28624 this.items.each(function(f){
28632 Roo.BasicForm = Roo.form.BasicForm;/*
28634 * Ext JS Library 1.1.1
28635 * Copyright(c) 2006-2007, Ext JS, LLC.
28637 * Originally Released Under LGPL - original licence link has changed is not relivant.
28640 * <script type="text/javascript">
28644 * @class Roo.form.Form
28645 * @extends Roo.form.BasicForm
28646 * Adds the ability to dynamically render forms with JavaScript to {@link Roo.form.BasicForm}.
28648 * @param {Object} config Configuration options
28650 Roo.form.Form = function(config){
28652 if (config.items) {
28653 xitems = config.items;
28654 delete config.items;
28658 Roo.form.Form.superclass.constructor.call(this, null, config);
28659 this.url = this.url || this.action;
28661 this.root = new Roo.form.Layout(Roo.applyIf({
28665 this.active = this.root;
28667 * Array of all the buttons that have been added to this form via {@link addButton}
28671 this.allItems = [];
28674 * @event clientvalidation
28675 * If the monitorValid config option is true, this event fires repetitively to notify of valid state
28676 * @param {Form} this
28677 * @param {Boolean} valid true if the form has passed client-side validation
28679 clientvalidation: true,
28682 * Fires when the form is rendered
28683 * @param {Roo.form.Form} form
28688 if (this.progressUrl) {
28689 // push a hidden field onto the list of fields..
28693 name : 'UPLOAD_IDENTIFIER'
28698 Roo.each(xitems, this.addxtype, this);
28704 Roo.extend(Roo.form.Form, Roo.form.BasicForm, {
28706 * @cfg {Number} labelWidth The width of labels. This property cascades to child containers.
28709 * @cfg {String} itemCls A css class to apply to the x-form-item of fields. This property cascades to child containers.
28712 * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "center")
28714 buttonAlign:'center',
28717 * @cfg {Number} minButtonWidth Minimum width of all buttons in pixels (defaults to 75)
28722 * @cfg {String} labelAlign Valid values are "left," "top" and "right" (defaults to "left").
28723 * This property cascades to child containers if not set.
28728 * @cfg {Boolean} monitorValid If true the form monitors its valid state <b>client-side</b> and
28729 * fires a looping event with that state. This is required to bind buttons to the valid
28730 * state using the config value formBind:true on the button.
28732 monitorValid : false,
28735 * @cfg {Number} monitorPoll The milliseconds to poll valid state, ignored if monitorValid is not true (defaults to 200)
28740 * @cfg {String} progressUrl - Url to return progress data
28743 progressUrl : false,
28746 * Opens a new {@link Roo.form.Column} container in the layout stack. If fields are passed after the config, the
28747 * fields are added and the column is closed. If no fields are passed the column remains open
28748 * until end() is called.
28749 * @param {Object} config The config to pass to the column
28750 * @param {Field} field1 (optional)
28751 * @param {Field} field2 (optional)
28752 * @param {Field} etc (optional)
28753 * @return Column The column container object
28755 column : function(c){
28756 var col = new Roo.form.Column(c);
28758 if(arguments.length > 1){ // duplicate code required because of Opera
28759 this.add.apply(this, Array.prototype.slice.call(arguments, 1));
28766 * Opens a new {@link Roo.form.FieldSet} container in the layout stack. If fields are passed after the config, the
28767 * fields are added and the fieldset is closed. If no fields are passed the fieldset remains open
28768 * until end() is called.
28769 * @param {Object} config The config to pass to the fieldset
28770 * @param {Field} field1 (optional)
28771 * @param {Field} field2 (optional)
28772 * @param {Field} etc (optional)
28773 * @return FieldSet The fieldset container object
28775 fieldset : function(c){
28776 var fs = new Roo.form.FieldSet(c);
28778 if(arguments.length > 1){ // duplicate code required because of Opera
28779 this.add.apply(this, Array.prototype.slice.call(arguments, 1));
28786 * Opens a new {@link Roo.form.Layout} container in the layout stack. If fields are passed after the config, the
28787 * fields are added and the container is closed. If no fields are passed the container remains open
28788 * until end() is called.
28789 * @param {Object} config The config to pass to the Layout
28790 * @param {Field} field1 (optional)
28791 * @param {Field} field2 (optional)
28792 * @param {Field} etc (optional)
28793 * @return Layout The container object
28795 container : function(c){
28796 var l = new Roo.form.Layout(c);
28798 if(arguments.length > 1){ // duplicate code required because of Opera
28799 this.add.apply(this, Array.prototype.slice.call(arguments, 1));
28806 * Opens the passed container in the layout stack. The container can be any {@link Roo.form.Layout} or subclass.
28807 * @param {Object} container A Roo.form.Layout or subclass of Layout
28808 * @return {Form} this
28810 start : function(c){
28811 // cascade label info
28812 Roo.applyIf(c, {'labelAlign': this.active.labelAlign, 'labelWidth': this.active.labelWidth, 'itemCls': this.active.itemCls});
28813 this.active.stack.push(c);
28814 c.ownerCt = this.active;
28820 * Closes the current open container
28821 * @return {Form} this
28824 if(this.active == this.root){
28827 this.active = this.active.ownerCt;
28832 * Add Roo.form components to the current open container (e.g. column, fieldset, etc.). Fields added via this method
28833 * can also be passed with an additional property of fieldLabel, which if supplied, will provide the text to display
28834 * as the label of the field.
28835 * @param {Field} field1
28836 * @param {Field} field2 (optional)
28837 * @param {Field} etc. (optional)
28838 * @return {Form} this
28841 this.active.stack.push.apply(this.active.stack, arguments);
28842 this.allItems.push.apply(this.allItems,arguments);
28844 for(var i = 0, a = arguments, len = a.length; i < len; i++) {
28845 if(a[i].isFormField){
28850 Roo.form.Form.superclass.add.apply(this, r);
28860 * Find any element that has been added to a form, using it's ID or name
28861 * This can include framesets, columns etc. along with regular fields..
28862 * @param {String} id - id or name to find.
28864 * @return {Element} e - or false if nothing found.
28866 findbyId : function(id)
28872 Roo.each(this.allItems, function(f){
28873 if (f.id == id || f.name == id ){
28884 * Render this form into the passed container. This should only be called once!
28885 * @param {String/HTMLElement/Element} container The element this component should be rendered into
28886 * @return {Form} this
28888 render : function(ct)
28894 var o = this.autoCreate || {
28896 method : this.method || 'POST',
28897 id : this.id || Roo.id()
28899 this.initEl(ct.createChild(o));
28901 this.root.render(this.el);
28905 this.items.each(function(f){
28906 f.render('x-form-el-'+f.id);
28909 if(this.buttons.length > 0){
28910 // tables are required to maintain order and for correct IE layout
28911 var tb = this.el.createChild({cls:'x-form-btns-ct', cn: {
28912 cls:"x-form-btns x-form-btns-"+this.buttonAlign,
28913 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
28915 var tr = tb.getElementsByTagName('tr')[0];
28916 for(var i = 0, len = this.buttons.length; i < len; i++) {
28917 var b = this.buttons[i];
28918 var td = document.createElement('td');
28919 td.className = 'x-form-btn-td';
28920 b.render(tr.appendChild(td));
28923 if(this.monitorValid){ // initialize after render
28924 this.startMonitoring();
28926 this.fireEvent('rendered', this);
28931 * Adds a button to the footer of the form - this <b>must</b> be called before the form is rendered.
28932 * @param {String/Object} config A string becomes the button text, an object can either be a Button config
28933 * object or a valid Roo.DomHelper element config
28934 * @param {Function} handler The function called when the button is clicked
28935 * @param {Object} scope (optional) The scope of the handler function
28936 * @return {Roo.Button}
28938 addButton : function(config, handler, scope){
28942 minWidth: this.minButtonWidth,
28945 if(typeof config == "string"){
28948 Roo.apply(bc, config);
28950 var btn = new Roo.Button(null, bc);
28951 this.buttons.push(btn);
28956 * Adds a series of form elements (using the xtype property as the factory method.
28957 * Valid xtypes are: TextField, TextArea .... Button, Layout, FieldSet, Column, (and 'end' to close a block)
28958 * @param {Object} config
28961 addxtype : function()
28963 var ar = Array.prototype.slice.call(arguments, 0);
28965 for(var i = 0; i < ar.length; i++) {
28967 continue; // skip -- if this happends something invalid got sent, we
28968 // should ignore it, as basically that interface element will not show up
28969 // and that should be pretty obvious!!
28972 if (Roo.form[ar[i].xtype]) {
28974 var fe = Roo.factory(ar[i], Roo.form);
28980 fe.store.form = this;
28985 this.allItems.push(fe);
28986 if (fe.items && fe.addxtype) {
28987 fe.addxtype.apply(fe, fe.items);
28997 // console.log('adding ' + ar[i].xtype);
28999 if (ar[i].xtype == 'Button') {
29000 //console.log('adding button');
29001 //console.log(ar[i]);
29002 this.addButton(ar[i]);
29003 this.allItems.push(fe);
29007 if (ar[i].xtype == 'end') { // so we can add fieldsets... / layout etc.
29008 alert('end is not supported on xtype any more, use items');
29010 // //console.log('adding end');
29018 * Starts monitoring of the valid state of this form. Usually this is done by passing the config
29019 * option "monitorValid"
29021 startMonitoring : function(){
29024 Roo.TaskMgr.start({
29025 run : this.bindHandler,
29026 interval : this.monitorPoll || 200,
29033 * Stops monitoring of the valid state of this form
29035 stopMonitoring : function(){
29036 this.bound = false;
29040 bindHandler : function(){
29042 return false; // stops binding
29045 this.items.each(function(f){
29046 if(!f.isValid(true)){
29051 for(var i = 0, len = this.buttons.length; i < len; i++){
29052 var btn = this.buttons[i];
29053 if(btn.formBind === true && btn.disabled === valid){
29054 btn.setDisabled(!valid);
29057 this.fireEvent('clientvalidation', this, valid);
29071 Roo.Form = Roo.form.Form;
29074 * Ext JS Library 1.1.1
29075 * Copyright(c) 2006-2007, Ext JS, LLC.
29077 * Originally Released Under LGPL - original licence link has changed is not relivant.
29080 * <script type="text/javascript">
29084 * @class Roo.form.Action
29085 * Internal Class used to handle form actions
29087 * @param {Roo.form.BasicForm} el The form element or its id
29088 * @param {Object} config Configuration options
29092 // define the action interface
29093 Roo.form.Action = function(form, options){
29095 this.options = options || {};
29098 * Client Validation Failed
29101 Roo.form.Action.CLIENT_INVALID = 'client';
29103 * Server Validation Failed
29106 Roo.form.Action.SERVER_INVALID = 'server';
29108 * Connect to Server Failed
29111 Roo.form.Action.CONNECT_FAILURE = 'connect';
29113 * Reading Data from Server Failed
29116 Roo.form.Action.LOAD_FAILURE = 'load';
29118 Roo.form.Action.prototype = {
29120 failureType : undefined,
29121 response : undefined,
29122 result : undefined,
29124 // interface method
29125 run : function(options){
29129 // interface method
29130 success : function(response){
29134 // interface method
29135 handleResponse : function(response){
29139 // default connection failure
29140 failure : function(response){
29142 this.response = response;
29143 this.failureType = Roo.form.Action.CONNECT_FAILURE;
29144 this.form.afterAction(this, false);
29147 processResponse : function(response){
29148 this.response = response;
29149 if(!response.responseText){
29152 this.result = this.handleResponse(response);
29153 return this.result;
29156 // utility functions used internally
29157 getUrl : function(appendParams){
29158 var url = this.options.url || this.form.url || this.form.el.dom.action;
29160 var p = this.getParams();
29162 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
29168 getMethod : function(){
29169 return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
29172 getParams : function(){
29173 var bp = this.form.baseParams;
29174 var p = this.options.params;
29176 if(typeof p == "object"){
29177 p = Roo.urlEncode(Roo.applyIf(p, bp));
29178 }else if(typeof p == 'string' && bp){
29179 p += '&' + Roo.urlEncode(bp);
29182 p = Roo.urlEncode(bp);
29187 createCallback : function(){
29189 success: this.success,
29190 failure: this.failure,
29192 timeout: (this.form.timeout*1000),
29193 upload: this.form.fileUpload ? this.success : undefined
29198 Roo.form.Action.Submit = function(form, options){
29199 Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
29202 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
29205 haveProgress : false,
29206 uploadComplete : false,
29208 // uploadProgress indicator.
29209 uploadProgress : function()
29211 if (!this.form.progressUrl) {
29215 if (!this.haveProgress) {
29216 Roo.MessageBox.progress("Uploading", "Uploading");
29218 if (this.uploadComplete) {
29219 Roo.MessageBox.hide();
29223 this.haveProgress = true;
29225 var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
29227 var c = new Roo.data.Connection();
29229 url : this.form.progressUrl,
29234 success : function(req){
29235 //console.log(data);
29239 rdata = Roo.decode(req.responseText)
29241 Roo.log("Invalid data from server..");
29245 if (!rdata || !rdata.success) {
29247 Roo.MessageBox.alert(Roo.encode(rdata));
29250 var data = rdata.data;
29252 if (this.uploadComplete) {
29253 Roo.MessageBox.hide();
29258 Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
29259 Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
29262 this.uploadProgress.defer(2000,this);
29265 failure: function(data) {
29266 Roo.log('progress url failed ');
29277 // run get Values on the form, so it syncs any secondary forms.
29278 this.form.getValues();
29280 var o = this.options;
29281 var method = this.getMethod();
29282 var isPost = method == 'POST';
29283 if(o.clientValidation === false || this.form.isValid()){
29285 if (this.form.progressUrl) {
29286 this.form.findField('UPLOAD_IDENTIFIER').setValue(
29287 (new Date() * 1) + '' + Math.random());
29292 Roo.Ajax.request(Roo.apply(this.createCallback(), {
29293 form:this.form.el.dom,
29294 url:this.getUrl(!isPost),
29296 params:isPost ? this.getParams() : null,
29297 isUpload: this.form.fileUpload
29300 this.uploadProgress();
29302 }else if (o.clientValidation !== false){ // client validation failed
29303 this.failureType = Roo.form.Action.CLIENT_INVALID;
29304 this.form.afterAction(this, false);
29308 success : function(response)
29310 this.uploadComplete= true;
29311 if (this.haveProgress) {
29312 Roo.MessageBox.hide();
29316 var result = this.processResponse(response);
29317 if(result === true || result.success){
29318 this.form.afterAction(this, true);
29322 this.form.markInvalid(result.errors);
29323 this.failureType = Roo.form.Action.SERVER_INVALID;
29325 this.form.afterAction(this, false);
29327 failure : function(response)
29329 this.uploadComplete= true;
29330 if (this.haveProgress) {
29331 Roo.MessageBox.hide();
29334 this.response = response;
29335 this.failureType = Roo.form.Action.CONNECT_FAILURE;
29336 this.form.afterAction(this, false);
29339 handleResponse : function(response){
29340 if(this.form.errorReader){
29341 var rs = this.form.errorReader.read(response);
29344 for(var i = 0, len = rs.records.length; i < len; i++) {
29345 var r = rs.records[i];
29346 errors[i] = r.data;
29349 if(errors.length < 1){
29353 success : rs.success,
29359 ret = Roo.decode(response.responseText);
29363 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
29373 Roo.form.Action.Load = function(form, options){
29374 Roo.form.Action.Load.superclass.constructor.call(this, form, options);
29375 this.reader = this.form.reader;
29378 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
29383 Roo.Ajax.request(Roo.apply(
29384 this.createCallback(), {
29385 method:this.getMethod(),
29386 url:this.getUrl(false),
29387 params:this.getParams()
29391 success : function(response){
29393 var result = this.processResponse(response);
29394 if(result === true || !result.success || !result.data){
29395 this.failureType = Roo.form.Action.LOAD_FAILURE;
29396 this.form.afterAction(this, false);
29399 this.form.clearInvalid();
29400 this.form.setValues(result.data);
29401 this.form.afterAction(this, true);
29404 handleResponse : function(response){
29405 if(this.form.reader){
29406 var rs = this.form.reader.read(response);
29407 var data = rs.records && rs.records[0] ? rs.records[0].data : null;
29409 success : rs.success,
29413 return Roo.decode(response.responseText);
29417 Roo.form.Action.ACTION_TYPES = {
29418 'load' : Roo.form.Action.Load,
29419 'submit' : Roo.form.Action.Submit
29422 * Ext JS Library 1.1.1
29423 * Copyright(c) 2006-2007, Ext JS, LLC.
29425 * Originally Released Under LGPL - original licence link has changed is not relivant.
29428 * <script type="text/javascript">
29432 * @class Roo.form.Layout
29433 * @extends Roo.Component
29434 * Creates a container for layout and rendering of fields in an {@link Roo.form.Form}.
29436 * @param {Object} config Configuration options
29438 Roo.form.Layout = function(config){
29440 if (config.items) {
29441 xitems = config.items;
29442 delete config.items;
29444 Roo.form.Layout.superclass.constructor.call(this, config);
29446 Roo.each(xitems, this.addxtype, this);
29450 Roo.extend(Roo.form.Layout, Roo.Component, {
29452 * @cfg {String/Object} autoCreate
29453 * A DomHelper element spec used to autocreate the layout (defaults to {tag: 'div', cls: 'x-form-ct'})
29456 * @cfg {String/Object/Function} style
29457 * A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
29458 * a function which returns such a specification.
29461 * @cfg {String} labelAlign
29462 * Valid values are "left," "top" and "right" (defaults to "left")
29465 * @cfg {Number} labelWidth
29466 * Fixed width in pixels of all field labels (defaults to undefined)
29469 * @cfg {Boolean} clear
29470 * True to add a clearing element at the end of this layout, equivalent to CSS clear: both (defaults to true)
29474 * @cfg {String} labelSeparator
29475 * The separator to use after field labels (defaults to ':')
29477 labelSeparator : ':',
29479 * @cfg {Boolean} hideLabels
29480 * True to suppress the display of field labels in this layout (defaults to false)
29482 hideLabels : false,
29485 defaultAutoCreate : {tag: 'div', cls: 'x-form-ct'},
29490 onRender : function(ct, position){
29491 if(this.el){ // from markup
29492 this.el = Roo.get(this.el);
29493 }else { // generate
29494 var cfg = this.getAutoCreate();
29495 this.el = ct.createChild(cfg, position);
29498 this.el.applyStyles(this.style);
29500 if(this.labelAlign){
29501 this.el.addClass('x-form-label-'+this.labelAlign);
29503 if(this.hideLabels){
29504 this.labelStyle = "display:none";
29505 this.elementStyle = "padding-left:0;";
29507 if(typeof this.labelWidth == 'number'){
29508 this.labelStyle = "width:"+this.labelWidth+"px;";
29509 this.elementStyle = "padding-left:"+((this.labelWidth+(typeof this.labelPad == 'number' ? this.labelPad : 5))+'px')+";";
29511 if(this.labelAlign == 'top'){
29512 this.labelStyle = "width:auto;";
29513 this.elementStyle = "padding-left:0;";
29516 var stack = this.stack;
29517 var slen = stack.length;
29519 if(!this.fieldTpl){
29520 var t = new Roo.Template(
29521 '<div class="x-form-item {5}">',
29522 '<label for="{0}" style="{2}">{1}{4}</label>',
29523 '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
29525 '</div><div class="x-form-clear-left"></div>'
29527 t.disableFormats = true;
29529 Roo.form.Layout.prototype.fieldTpl = t;
29531 for(var i = 0; i < slen; i++) {
29532 if(stack[i].isFormField){
29533 this.renderField(stack[i]);
29535 this.renderComponent(stack[i]);
29540 this.el.createChild({cls:'x-form-clear'});
29545 renderField : function(f){
29546 f.fieldEl = Roo.get(this.fieldTpl.append(this.el, [
29549 f.labelStyle||this.labelStyle||'', //2
29550 this.elementStyle||'', //3
29551 typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator, //4
29552 f.itemCls||this.itemCls||'' //5
29553 ], true).getPrevSibling());
29557 renderComponent : function(c){
29558 c.render(c.isLayout ? this.el : this.el.createChild());
29561 * Adds a object form elements (using the xtype property as the factory method.)
29562 * Valid xtypes are: TextField, TextArea .... Button, Layout, FieldSet, Column
29563 * @param {Object} config
29565 addxtype : function(o)
29567 // create the lement.
29568 o.form = this.form;
29569 var fe = Roo.factory(o, Roo.form);
29570 this.form.allItems.push(fe);
29571 this.stack.push(fe);
29573 if (fe.isFormField) {
29574 this.form.items.add(fe);
29582 * @class Roo.form.Column
29583 * @extends Roo.form.Layout
29584 * Creates a column container for layout and rendering of fields in an {@link Roo.form.Form}.
29586 * @param {Object} config Configuration options
29588 Roo.form.Column = function(config){
29589 Roo.form.Column.superclass.constructor.call(this, config);
29592 Roo.extend(Roo.form.Column, Roo.form.Layout, {
29594 * @cfg {Number/String} width
29595 * The fixed width of the column in pixels or CSS value (defaults to "auto")
29598 * @cfg {String/Object} autoCreate
29599 * A DomHelper element spec used to autocreate the column (defaults to {tag: 'div', cls: 'x-form-ct x-form-column'})
29603 defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-column'},
29606 onRender : function(ct, position){
29607 Roo.form.Column.superclass.onRender.call(this, ct, position);
29609 this.el.setWidth(this.width);
29616 * @class Roo.form.Row
29617 * @extends Roo.form.Layout
29618 * Creates a row container for layout and rendering of fields in an {@link Roo.form.Form}.
29620 * @param {Object} config Configuration options
29624 Roo.form.Row = function(config){
29625 Roo.form.Row.superclass.constructor.call(this, config);
29628 Roo.extend(Roo.form.Row, Roo.form.Layout, {
29630 * @cfg {Number/String} width
29631 * The fixed width of the column in pixels or CSS value (defaults to "auto")
29634 * @cfg {Number/String} height
29635 * The fixed height of the column in pixels or CSS value (defaults to "auto")
29637 defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-row'},
29641 onRender : function(ct, position){
29642 //console.log('row render');
29644 var t = new Roo.Template(
29645 '<div class="x-form-item {5}" style="float:left;width:{6}px">',
29646 '<label for="{0}" style="{2}">{1}{4}</label>',
29647 '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
29651 t.disableFormats = true;
29653 Roo.form.Layout.prototype.rowTpl = t;
29655 this.fieldTpl = this.rowTpl;
29657 //console.log('lw' + this.labelWidth +', la:' + this.labelAlign);
29658 var labelWidth = 100;
29660 if ((this.labelAlign != 'top')) {
29661 if (typeof this.labelWidth == 'number') {
29662 labelWidth = this.labelWidth
29664 this.padWidth = 20 + labelWidth;
29668 Roo.form.Column.superclass.onRender.call(this, ct, position);
29670 this.el.setWidth(this.width);
29673 this.el.setHeight(this.height);
29678 renderField : function(f){
29679 f.fieldEl = this.fieldTpl.append(this.el, [
29680 f.id, f.fieldLabel,
29681 f.labelStyle||this.labelStyle||'',
29682 this.elementStyle||'',
29683 typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator,
29684 f.itemCls||this.itemCls||'',
29685 f.width ? f.width + this.padWidth : 160 + this.padWidth
29692 * @class Roo.form.FieldSet
29693 * @extends Roo.form.Layout
29694 * Creates a fieldset container for layout and rendering of fields in an {@link Roo.form.Form}.
29696 * @param {Object} config Configuration options
29698 Roo.form.FieldSet = function(config){
29699 Roo.form.FieldSet.superclass.constructor.call(this, config);
29702 Roo.extend(Roo.form.FieldSet, Roo.form.Layout, {
29704 * @cfg {String} legend
29705 * The text to display as the legend for the FieldSet (defaults to '')
29708 * @cfg {String/Object} autoCreate
29709 * A DomHelper element spec used to autocreate the fieldset (defaults to {tag: 'fieldset', cn: {tag:'legend'}})
29713 defaultAutoCreate : {tag: 'fieldset', cn: {tag:'legend'}},
29716 onRender : function(ct, position){
29717 Roo.form.FieldSet.superclass.onRender.call(this, ct, position);
29719 this.setLegend(this.legend);
29724 setLegend : function(text){
29726 this.el.child('legend').update(text);
29731 * Ext JS Library 1.1.1
29732 * Copyright(c) 2006-2007, Ext JS, LLC.
29734 * Originally Released Under LGPL - original licence link has changed is not relivant.
29737 * <script type="text/javascript">
29740 * @class Roo.form.VTypes
29741 * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
29744 Roo.form.VTypes = function(){
29745 // closure these in so they are only created once.
29746 var alpha = /^[a-zA-Z_]+$/;
29747 var alphanum = /^[a-zA-Z0-9_]+$/;
29748 var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,4}$/;
29749 var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
29751 // All these messages and functions are configurable
29754 * The function used to validate email addresses
29755 * @param {String} value The email address
29757 'email' : function(v){
29758 return email.test(v);
29761 * The error text to display when the email validation function returns false
29764 'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
29766 * The keystroke filter mask to be applied on email input
29769 'emailMask' : /[a-z0-9_\.\-@]/i,
29772 * The function used to validate URLs
29773 * @param {String} value The URL
29775 'url' : function(v){
29776 return url.test(v);
29779 * The error text to display when the url validation function returns false
29782 'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
29785 * The function used to validate alpha values
29786 * @param {String} value The value
29788 'alpha' : function(v){
29789 return alpha.test(v);
29792 * The error text to display when the alpha validation function returns false
29795 'alphaText' : 'This field should only contain letters and _',
29797 * The keystroke filter mask to be applied on alpha input
29800 'alphaMask' : /[a-z_]/i,
29803 * The function used to validate alphanumeric values
29804 * @param {String} value The value
29806 'alphanum' : function(v){
29807 return alphanum.test(v);
29810 * The error text to display when the alphanumeric validation function returns false
29813 'alphanumText' : 'This field should only contain letters, numbers and _',
29815 * The keystroke filter mask to be applied on alphanumeric input
29818 'alphanumMask' : /[a-z0-9_]/i
29820 }();//<script type="text/javascript">
29823 * @class Roo.form.FCKeditor
29824 * @extends Roo.form.TextArea
29825 * Wrapper around the FCKEditor http://www.fckeditor.net
29827 * Creates a new FCKeditor
29828 * @param {Object} config Configuration options
29830 Roo.form.FCKeditor = function(config){
29831 Roo.form.FCKeditor.superclass.constructor.call(this, config);
29834 * @event editorinit
29835 * Fired when the editor is initialized - you can add extra handlers here..
29836 * @param {FCKeditor} this
29837 * @param {Object} the FCK object.
29844 Roo.form.FCKeditor.editors = { };
29845 Roo.extend(Roo.form.FCKeditor, Roo.form.TextArea,
29847 //defaultAutoCreate : {
29848 // tag : "textarea",style : "width:100px;height:60px;" ,autocomplete : "off"
29852 * @cfg {Object} fck options - see fck manual for details.
29857 * @cfg {Object} fck toolbar set (Basic or Default)
29859 toolbarSet : 'Basic',
29861 * @cfg {Object} fck BasePath
29863 basePath : '/fckeditor/',
29871 onRender : function(ct, position)
29874 this.defaultAutoCreate = {
29876 style:"width:300px;height:60px;",
29877 autocomplete: "off"
29880 Roo.form.FCKeditor.superclass.onRender.call(this, ct, position);
29883 this.textSizeEl = Roo.DomHelper.append(document.body, {tag: "pre", cls: "x-form-grow-sizer"});
29884 if(this.preventScrollbars){
29885 this.el.setStyle("overflow", "hidden");
29887 this.el.setHeight(this.growMin);
29890 //console.log('onrender' + this.getId() );
29891 Roo.form.FCKeditor.editors[this.getId()] = this;
29894 this.replaceTextarea() ;
29898 getEditor : function() {
29899 return this.fckEditor;
29902 * Sets a data value into the field and validates it. To set the value directly without validation see {@link #setRawValue}.
29903 * @param {Mixed} value The value to set
29907 setValue : function(value)
29909 //console.log('setValue: ' + value);
29911 if(typeof(value) == 'undefined') { // not sure why this is happending...
29914 Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
29916 //if(!this.el || !this.getEditor()) {
29917 // this.value = value;
29918 //this.setValue.defer(100,this,[value]);
29922 if(!this.getEditor()) {
29926 this.getEditor().SetData(value);
29933 * Returns the normalized data value (undefined or emptyText will be returned as ''). To return the raw value see {@link #getRawValue}.
29934 * @return {Mixed} value The field value
29936 getValue : function()
29939 if (this.frame && this.frame.dom.style.display == 'none') {
29940 return Roo.form.FCKeditor.superclass.getValue.call(this);
29943 if(!this.el || !this.getEditor()) {
29945 // this.getValue.defer(100,this);
29950 var value=this.getEditor().GetData();
29951 Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
29952 return Roo.form.FCKeditor.superclass.getValue.call(this);
29958 * Returns the raw data value which may or may not be a valid, defined value. To return a normalized value see {@link #getValue}.
29959 * @return {Mixed} value The field value
29961 getRawValue : function()
29963 if (this.frame && this.frame.dom.style.display == 'none') {
29964 return Roo.form.FCKeditor.superclass.getRawValue.call(this);
29967 if(!this.el || !this.getEditor()) {
29968 //this.getRawValue.defer(100,this);
29975 var value=this.getEditor().GetData();
29976 Roo.form.FCKeditor.superclass.setRawValue.apply(this,[value]);
29977 return Roo.form.FCKeditor.superclass.getRawValue.call(this);
29981 setSize : function(w,h) {
29985 //if (this.frame && this.frame.dom.style.display == 'none') {
29986 // Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
29989 //if(!this.el || !this.getEditor()) {
29990 // this.setSize.defer(100,this, [w,h]);
29996 Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
29998 this.frame.dom.setAttribute('width', w);
29999 this.frame.dom.setAttribute('height', h);
30000 this.frame.setSize(w,h);
30004 toggleSourceEdit : function(value) {
30008 this.el.dom.style.display = value ? '' : 'none';
30009 this.frame.dom.style.display = value ? 'none' : '';
30014 focus: function(tag)
30016 if (this.frame.dom.style.display == 'none') {
30017 return Roo.form.FCKeditor.superclass.focus.call(this);
30019 if(!this.el || !this.getEditor()) {
30020 this.focus.defer(100,this, [tag]);
30027 var tgs = this.getEditor().EditorDocument.getElementsByTagName(tag);
30028 this.getEditor().Focus();
30030 if (!this.getEditor().Selection.GetSelection()) {
30031 this.focus.defer(100,this, [tag]);
30036 var r = this.getEditor().EditorDocument.createRange();
30037 r.setStart(tgs[0],0);
30038 r.setEnd(tgs[0],0);
30039 this.getEditor().Selection.GetSelection().removeAllRanges();
30040 this.getEditor().Selection.GetSelection().addRange(r);
30041 this.getEditor().Focus();
30048 replaceTextarea : function()
30050 if ( document.getElementById( this.getId() + '___Frame' ) )
30052 //if ( !this.checkBrowser || this._isCompatibleBrowser() )
30054 // We must check the elements firstly using the Id and then the name.
30055 var oTextarea = document.getElementById( this.getId() );
30057 var colElementsByName = document.getElementsByName( this.getId() ) ;
30059 oTextarea.style.display = 'none' ;
30061 if ( oTextarea.tabIndex ) {
30062 this.TabIndex = oTextarea.tabIndex ;
30065 this._insertHtmlBefore( this._getConfigHtml(), oTextarea ) ;
30066 this._insertHtmlBefore( this._getIFrameHtml(), oTextarea ) ;
30067 this.frame = Roo.get(this.getId() + '___Frame')
30070 _getConfigHtml : function()
30074 for ( var o in this.fckconfig ) {
30075 sConfig += sConfig.length > 0 ? '&' : '';
30076 sConfig += encodeURIComponent( o ) + '=' + encodeURIComponent( this.fckconfig[o] ) ;
30079 return '<input type="hidden" id="' + this.getId() + '___Config" value="' + sConfig + '" style="display:none" />' ;
30083 _getIFrameHtml : function()
30085 var sFile = 'fckeditor.html' ;
30086 /* no idea what this is about..
30089 if ( (/fcksource=true/i).test( window.top.location.search ) )
30090 sFile = 'fckeditor.original.html' ;
30095 var sLink = this.basePath + 'editor/' + sFile + '?InstanceName=' + encodeURIComponent( this.getId() ) ;
30096 sLink += this.toolbarSet ? ( '&Toolbar=' + this.toolbarSet) : '';
30099 var html = '<iframe id="' + this.getId() +
30100 '___Frame" src="' + sLink +
30101 '" width="' + this.width +
30102 '" height="' + this.height + '"' +
30103 (this.tabIndex ? ' tabindex="' + this.tabIndex + '"' :'' ) +
30104 ' frameborder="0" scrolling="no"></iframe>' ;
30109 _insertHtmlBefore : function( html, element )
30111 if ( element.insertAdjacentHTML ) {
30113 element.insertAdjacentHTML( 'beforeBegin', html ) ;
30115 var oRange = document.createRange() ;
30116 oRange.setStartBefore( element ) ;
30117 var oFragment = oRange.createContextualFragment( html );
30118 element.parentNode.insertBefore( oFragment, element ) ;
30131 //Roo.reg('fckeditor', Roo.form.FCKeditor);
30133 function FCKeditor_OnComplete(editorInstance){
30134 var f = Roo.form.FCKeditor.editors[editorInstance.Name];
30135 f.fckEditor = editorInstance;
30136 //console.log("loaded");
30137 f.fireEvent('editorinit', f, editorInstance);
30157 //<script type="text/javascript">
30159 * @class Roo.form.GridField
30160 * @extends Roo.form.Field
30161 * Embed a grid (or editable grid into a form)
30164 * This embeds a grid in a form, the value of the field should be the json encoded array of rows
30166 * xgrid.store = Roo.data.Store
30167 * xgrid.store.proxy = Roo.data.MemoryProxy (data = [] )
30168 * xgrid.store.reader = Roo.data.JsonReader
30172 * Creates a new GridField
30173 * @param {Object} config Configuration options
30175 Roo.form.GridField = function(config){
30176 Roo.form.GridField.superclass.constructor.call(this, config);
30180 Roo.extend(Roo.form.GridField, Roo.form.Field, {
30182 * @cfg {Number} width - used to restrict width of grid..
30186 * @cfg {Number} height - used to restrict height of grid..
30190 * @cfg {Object} xgrid (xtype'd description of grid) { xtype : 'Grid', dataSource: .... }
30196 * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
30197 * {tag: "input", type: "checkbox", autocomplete: "off"})
30199 // defaultAutoCreate : { tag: 'div' },
30200 defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'off'},
30202 * @cfg {String} addTitle Text to include for adding a title.
30206 onResize : function(){
30207 Roo.form.Field.superclass.onResize.apply(this, arguments);
30210 initEvents : function(){
30211 // Roo.form.Checkbox.superclass.initEvents.call(this);
30212 // has no events...
30217 getResizeEl : function(){
30221 getPositionEl : function(){
30226 onRender : function(ct, position){
30228 this.style = this.style || 'overflow: hidden; border:1px solid #c3daf9;';
30229 var style = this.style;
30232 Roo.form.GridField.superclass.onRender.call(this, ct, position);
30233 this.wrap = this.el.wrap({cls: ''}); // not sure why ive done thsi...
30234 this.viewEl = this.wrap.createChild({ tag: 'div' });
30236 this.viewEl.applyStyles(style);
30239 this.viewEl.setWidth(this.width);
30242 this.viewEl.setHeight(this.height);
30244 //if(this.inputValue !== undefined){
30245 //this.setValue(this.value);
30248 this.grid = new Roo.grid[this.xgrid.xtype](this.viewEl, this.xgrid);
30251 this.grid.render();
30252 this.grid.getDataSource().on('remove', this.refreshValue, this);
30253 this.grid.getDataSource().on('update', this.refreshValue, this);
30254 this.grid.on('afteredit', this.refreshValue, this);
30260 * Sets the value of the item.
30261 * @param {String} either an object or a string..
30263 setValue : function(v){
30265 v = v || []; // empty set..
30266 // this does not seem smart - it really only affects memoryproxy grids..
30267 if (this.grid && this.grid.getDataSource() && typeof(v) != 'undefined') {
30268 var ds = this.grid.getDataSource();
30269 // assumes a json reader..
30271 data[ds.reader.meta.root ] = typeof(v) == 'string' ? Roo.decode(v) : v;
30272 ds.loadData( data);
30274 // clear selection so it does not get stale.
30275 if (this.grid.sm) {
30276 this.grid.sm.clearSelections();
30279 Roo.form.GridField.superclass.setValue.call(this, v);
30280 this.refreshValue();
30281 // should load data in the grid really....
30285 refreshValue: function() {
30287 this.grid.getDataSource().each(function(r) {
30290 this.el.dom.value = Roo.encode(val);
30298 * Ext JS Library 1.1.1
30299 * Copyright(c) 2006-2007, Ext JS, LLC.
30301 * Originally Released Under LGPL - original licence link has changed is not relivant.
30304 * <script type="text/javascript">
30307 * @class Roo.form.DisplayField
30308 * @extends Roo.form.Field
30309 * A generic Field to display non-editable data.
30311 * Creates a new Display Field item.
30312 * @param {Object} config Configuration options
30314 Roo.form.DisplayField = function(config){
30315 Roo.form.DisplayField.superclass.constructor.call(this, config);
30319 Roo.extend(Roo.form.DisplayField, Roo.form.TextField, {
30320 inputType: 'hidden',
30326 * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
30328 focusClass : undefined,
30330 * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
30332 fieldClass: 'x-form-field',
30335 * @cfg {Function} valueRenderer The renderer for the field (so you can reformat output). should return raw HTML
30337 valueRenderer: undefined,
30341 * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
30342 * {tag: "input", type: "checkbox", autocomplete: "off"})
30345 // defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'off'},
30347 onResize : function(){
30348 Roo.form.DisplayField.superclass.onResize.apply(this, arguments);
30352 initEvents : function(){
30353 // Roo.form.Checkbox.superclass.initEvents.call(this);
30354 // has no events...
30359 getResizeEl : function(){
30363 getPositionEl : function(){
30368 onRender : function(ct, position){
30370 Roo.form.DisplayField.superclass.onRender.call(this, ct, position);
30371 //if(this.inputValue !== undefined){
30372 this.wrap = this.el.wrap();
30374 this.viewEl = this.wrap.createChild({ tag: 'div', cls: 'x-form-displayfield'});
30376 if (this.bodyStyle) {
30377 this.viewEl.applyStyles(this.bodyStyle);
30379 //this.viewEl.setStyle('padding', '2px');
30381 this.setValue(this.value);
30386 initValue : Roo.emptyFn,
30391 onClick : function(){
30396 * Sets the checked state of the checkbox.
30397 * @param {Boolean/String} checked True, 'true', '1', or 'on' to check the checkbox, any other value will uncheck it.
30399 setValue : function(v){
30401 var html = this.valueRenderer ? this.valueRenderer(v) : String.format('{0}', v);
30402 // this might be called before we have a dom element..
30403 if (!this.viewEl) {
30406 this.viewEl.dom.innerHTML = html;
30407 Roo.form.DisplayField.superclass.setValue.call(this, v);
30417 * @class Roo.form.DayPicker
30418 * @extends Roo.form.Field
30419 * A Day picker show [M] [T] [W] ....
30421 * Creates a new Day Picker
30422 * @param {Object} config Configuration options
30424 Roo.form.DayPicker= function(config){
30425 Roo.form.DayPicker.superclass.constructor.call(this, config);
30429 Roo.extend(Roo.form.DayPicker, Roo.form.Field, {
30431 * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
30433 focusClass : undefined,
30435 * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
30437 fieldClass: "x-form-field",
30440 * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
30441 * {tag: "input", type: "checkbox", autocomplete: "off"})
30443 defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "off"},
30446 actionMode : 'viewEl',
30450 inputType : 'hidden',
30453 inputElement: false, // real input element?
30454 basedOn: false, // ????
30456 isFormField: true, // not sure where this is needed!!!!
30458 onResize : function(){
30459 Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
30460 if(!this.boxLabel){
30461 this.el.alignTo(this.wrap, 'c-c');
30465 initEvents : function(){
30466 Roo.form.Checkbox.superclass.initEvents.call(this);
30467 this.el.on("click", this.onClick, this);
30468 this.el.on("change", this.onClick, this);
30472 getResizeEl : function(){
30476 getPositionEl : function(){
30482 onRender : function(ct, position){
30483 Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
30485 this.wrap = this.el.wrap({cls: 'x-form-daypick-item '});
30487 var r1 = '<table><tr>';
30488 var r2 = '<tr class="x-form-daypick-icons">';
30489 for (var i=0; i < 7; i++) {
30490 r1+= '<td><div>' + Date.dayNames[i].substring(0,3) + '</div></td>';
30491 r2+= '<td><img class="x-menu-item-icon" src="' + Roo.BLANK_IMAGE_URL +'"></td>';
30494 var viewEl = this.wrap.createChild( r1 + '</tr>' + r2 + '</tr></table>');
30495 viewEl.select('img').on('click', this.onClick, this);
30496 this.viewEl = viewEl;
30499 // this will not work on Chrome!!!
30500 this.el.on('DOMAttrModified', this.setFromHidden, this); //ff
30501 this.el.on('propertychange', this.setFromHidden, this); //ie
30509 initValue : Roo.emptyFn,
30512 * Returns the checked state of the checkbox.
30513 * @return {Boolean} True if checked, else false
30515 getValue : function(){
30516 return this.el.dom.value;
30521 onClick : function(e){
30522 //this.setChecked(!this.checked);
30523 Roo.get(e.target).toggleClass('x-menu-item-checked');
30524 this.refreshValue();
30525 //if(this.el.dom.checked != this.checked){
30526 // this.setValue(this.el.dom.checked);
30531 refreshValue : function()
30534 this.viewEl.select('img',true).each(function(e,i,n) {
30535 val += e.is(".x-menu-item-checked") ? String(n) : '';
30537 this.setValue(val, true);
30541 * Sets the checked state of the checkbox.
30542 * On is always based on a string comparison between inputValue and the param.
30543 * @param {Boolean/String} value - the value to set
30544 * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
30546 setValue : function(v,suppressEvent){
30547 if (!this.el.dom) {
30550 var old = this.el.dom.value ;
30551 this.el.dom.value = v;
30552 if (suppressEvent) {
30556 // update display..
30557 this.viewEl.select('img',true).each(function(e,i,n) {
30559 var on = e.is(".x-menu-item-checked");
30560 var newv = v.indexOf(String(n)) > -1;
30562 e.toggleClass('x-menu-item-checked');
30568 this.fireEvent('change', this, v, old);
30573 // handle setting of hidden value by some other method!!?!?
30574 setFromHidden: function()
30579 //console.log("SET FROM HIDDEN");
30580 //alert('setFrom hidden');
30581 this.setValue(this.el.dom.value);
30584 onDestroy : function()
30587 Roo.get(this.viewEl).remove();
30590 Roo.form.DayPicker.superclass.onDestroy.call(this);
30594 * RooJS Library 1.1.1
30595 * Copyright(c) 2008-2011 Alan Knowles
30602 * @class Roo.form.ComboCheck
30603 * @extends Roo.form.ComboBox
30604 * A combobox for multiple select items.
30606 * FIXME - could do with a reset button..
30609 * Create a new ComboCheck
30610 * @param {Object} config Configuration options
30612 Roo.form.ComboCheck = function(config){
30613 Roo.form.ComboCheck.superclass.constructor.call(this, config);
30614 // should verify some data...
30616 // hiddenName = required..
30617 // displayField = required
30618 // valudField == required
30619 var req= [ 'hiddenName', 'displayField', 'valueField' ];
30621 Roo.each(req, function(e) {
30622 if ((typeof(_t[e]) == 'undefined' ) || !_t[e].length) {
30623 throw "Roo.form.ComboCheck : missing value for: " + e;
30630 Roo.extend(Roo.form.ComboCheck, Roo.form.ComboBox, {
30635 selectedClass: 'x-menu-item-checked',
30638 onRender : function(ct, position){
30644 var cls = 'x-combo-list';
30647 this.tpl = new Roo.Template({
30648 html : '<div class="'+cls+'-item x-menu-check-item">' +
30649 '<img class="x-menu-item-icon" style="margin: 0px;" src="' + Roo.BLANK_IMAGE_URL + '">' +
30650 '<span>{' + this.displayField + '}</span>' +
30657 Roo.form.ComboCheck.superclass.onRender.call(this, ct, position);
30658 this.view.singleSelect = false;
30659 this.view.multiSelect = true;
30660 this.view.toggleSelect = true;
30661 this.pageTb.add(new Roo.Toolbar.Fill(), {
30664 handler: function()
30671 onViewOver : function(e, t){
30677 onViewClick : function(doFocus,index){
30681 select: function () {
30682 //Roo.log("SELECT CALLED");
30685 selectByValue : function(xv, scrollIntoView){
30686 var ar = this.getValueArray();
30689 Roo.each(ar, function(v) {
30690 if(v === undefined || v === null){
30693 var r = this.findRecord(this.valueField, v);
30695 sels.push(this.store.indexOf(r))
30699 this.view.select(sels);
30705 onSelect : function(record, index){
30706 // Roo.log("onselect Called");
30707 // this is only called by the clear button now..
30708 this.view.clearSelections();
30709 this.setValue('[]');
30710 if (this.value != this.valueBefore) {
30711 this.fireEvent('change', this, this.value, this.valueBefore);
30712 this.valueBefore = this.value;
30715 getValueArray : function()
30720 //Roo.log(this.value);
30721 if (typeof(this.value) == 'undefined') {
30724 var ar = Roo.decode(this.value);
30725 return ar instanceof Array ? ar : []; //?? valid?
30728 Roo.log(e + "\nRoo.form.ComboCheck:getValueArray invalid data:" + this.getValue());
30733 expand : function ()
30736 Roo.form.ComboCheck.superclass.expand.call(this);
30737 this.valueBefore = typeof(this.value) == 'undefined' ? '' : this.value;
30738 //this.valueBefore = typeof(this.valueBefore) == 'undefined' ? '' : this.valueBefore;
30743 collapse : function(){
30744 Roo.form.ComboCheck.superclass.collapse.call(this);
30745 var sl = this.view.getSelectedIndexes();
30746 var st = this.store;
30750 Roo.each(sl, function(i) {
30752 nv.push(r.get(this.valueField));
30754 this.setValue(Roo.encode(nv));
30755 if (this.value != this.valueBefore) {
30757 this.fireEvent('change', this, this.value, this.valueBefore);
30758 this.valueBefore = this.value;
30763 setValue : function(v){
30767 var vals = this.getValueArray();
30769 Roo.each(vals, function(k) {
30770 var r = this.findRecord(this.valueField, k);
30772 tv.push(r.data[this.displayField]);
30773 }else if(this.valueNotFoundText !== undefined){
30774 tv.push( this.valueNotFoundText );
30779 Roo.form.ComboBox.superclass.setValue.call(this, tv.join(', '));
30780 this.hiddenField.value = v;
30786 * Ext JS Library 1.1.1
30787 * Copyright(c) 2006-2007, Ext JS, LLC.
30789 * Originally Released Under LGPL - original licence link has changed is not relivant.
30792 * <script type="text/javascript">
30796 * @class Roo.form.Signature
30797 * @extends Roo.form.Field
30801 * @param {Object} config Configuration options
30804 Roo.form.Signature = function(config){
30805 Roo.form.Signature.superclass.constructor.call(this, config);
30807 this.addEvents({// not in used??
30810 * Fires when the 'confirm' icon is pressed (add a listener to enable add button)
30811 * @param {Roo.form.Signature} combo This combo box
30816 * Fires when the 'edit' icon is pressed (add a listener to enable add button)
30817 * @param {Roo.form.ComboBox} combo This combo box
30818 * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
30824 Roo.extend(Roo.form.Signature, Roo.form.Field, {
30826 * @cfg {Object} labels Label to use when rendering a form.
30830 * confirm : "Confirm"
30835 confirm : "Confirm"
30838 * @cfg {Number} width The signature panel width (defaults to 300)
30842 * @cfg {Number} height The signature panel height (defaults to 100)
30846 * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to false)
30848 allowBlank : false,
30851 // {Object} signPanel The signature SVG panel element (defaults to {})
30853 // {Boolean} isMouseDown False to validate that the mouse down event (defaults to false)
30854 isMouseDown : false,
30855 // {Boolean} isConfirmed validate the signature is confirmed or not for submitting form (defaults to false)
30856 isConfirmed : false,
30857 // {String} signatureTmp SVG mapping string (defaults to empty string)
30861 defaultAutoCreate : { // modified by initCompnoent..
30867 onRender : function(ct, position){
30869 Roo.form.Signature.superclass.onRender.call(this, ct, position);
30871 this.wrap = this.el.wrap({
30872 cls:'x-form-signature-wrap', style : 'width: ' + this.width + 'px', cn:{cls:'x-form-signature'}
30875 this.createToolbar(this);
30876 this.signPanel = this.wrap.createChild({
30878 style: 'width: ' + this.width + 'px; height: ' + this.height + 'px; border: 0;'
30882 this.svgID = Roo.id();
30883 this.svgEl = this.signPanel.createChild({
30884 xmlns : 'http://www.w3.org/2000/svg',
30886 id : this.svgID + "-svg",
30888 height: this.height,
30889 viewBox: '0 0 '+this.width+' '+this.height,
30893 id: this.svgID + "-svg-r",
30895 height: this.height,
30900 id: this.svgID + "-svg-l",
30902 y1: (this.height*0.8), // start set the line in 80% of height
30903 x2: this.width, // end
30904 y2: (this.height*0.8), // end set the line in 80% of height
30906 'stroke-width': "1",
30907 'stroke-dasharray': "3",
30908 'shape-rendering': "crispEdges",
30909 'pointer-events': "none"
30913 id: this.svgID + "-svg-p",
30915 'stroke-width': "3",
30917 'pointer-events': 'none'
30922 this.svgBox = this.svgEl.dom.getScreenCTM();
30924 createSVG : function(){
30925 var svg = this.signPanel;
30926 var r = svg.select('#'+ this.svgID + '-svg-r', true).first().dom;
30929 r.addEventListener('mousedown', function(e) { return t.down(e); }, false);
30930 r.addEventListener('mousemove', function(e) { return t.move(e); }, false);
30931 r.addEventListener('mouseup', function(e) { return t.up(e); }, false);
30932 r.addEventListener('mouseout', function(e) { return t.up(e); }, false);
30933 r.addEventListener('touchstart', function(e) { return t.down(e); }, false);
30934 r.addEventListener('touchmove', function(e) { return t.move(e); }, false);
30935 r.addEventListener('touchend', function(e) { return t.up(e); }, false);
30938 isTouchEvent : function(e){
30939 return e.type.match(/^touch/);
30941 getCoords : function (e) {
30942 var pt = this.svgEl.dom.createSVGPoint();
30945 if (this.isTouchEvent(e)) {
30946 pt.x = e.targetTouches[0].clientX
30947 pt.y = e.targetTouches[0].clientY;
30949 var a = this.svgEl.dom.getScreenCTM();
30950 var b = a.inverse();
30951 var mx = pt.matrixTransform(b);
30952 return mx.x + ',' + mx.y;
30954 //mouse event headler
30955 down : function (e) {
30956 this.signatureTmp += 'M' + this.getCoords(e) + ' ';
30957 this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr('d', this.signatureTmp);
30959 this.isMouseDown = true;
30961 e.preventDefault();
30963 move : function (e) {
30964 if (this.isMouseDown) {
30965 this.signatureTmp += 'L' + this.getCoords(e) + ' ';
30966 this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', this.signatureTmp);
30969 e.preventDefault();
30971 up : function (e) {
30972 this.isMouseDown = false;
30973 var sp = this.signatureTmp.split(' ');
30976 if(!sp[sp.length-2].match(/^L/)){
30980 this.signatureTmp = sp.join(" ");
30983 if(this.getValue() != this.signatureTmp){
30984 this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
30985 this.isConfirmed = false;
30987 e.preventDefault();
30991 * Protected method that will not generally be called directly. It
30992 * is called when the editor creates its toolbar. Override this method if you need to
30993 * add custom toolbar buttons.
30994 * @param {HtmlEditor} editor
30996 createToolbar : function(editor){
30997 function btn(id, toggle, handler){
30998 var xid = fid + '-'+ id ;
31002 cls : 'x-btn-icon x-edit-'+id,
31003 enableToggle:toggle !== false,
31004 scope: editor, // was editor...
31005 handler:handler||editor.relayBtnCmd,
31006 clickEvent:'mousedown',
31007 tooltip: etb.buttonTips[id] || undefined, ///tips ???
31013 var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
31017 cls : ' x-signature-btn x-signature-'+id,
31018 scope: editor, // was editor...
31019 handler: this.reset,
31020 clickEvent:'mousedown',
31021 text: this.labels.clear
31028 cls : ' x-signature-btn x-signature-'+id,
31029 scope: editor, // was editor...
31030 handler: this.confirmHandler,
31031 clickEvent:'mousedown',
31032 text: this.labels.confirm
31039 * when user is clicked confirm then show this image.....
31041 * @return {String} Image Data URI
31043 getImageDataURI : function(){
31044 var svg = this.svgEl.dom.parentNode.innerHTML;
31045 var src = 'data:image/svg+xml;base64,'+window.btoa(svg);
31050 * @return {Boolean} this.isConfirmed
31052 getConfirmed : function(){
31053 return this.isConfirmed;
31057 * @return {Number} this.width
31059 getWidth : function(){
31064 * @return {Number} this.height
31066 getHeight : function(){
31067 return this.height;
31070 getSignature : function(){
31071 return this.signatureTmp;
31074 reset : function(){
31075 this.signatureTmp = '';
31076 this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
31077 this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', '');
31078 this.isConfirmed = false;
31079 Roo.form.Signature.superclass.reset.call(this);
31081 setSignature : function(s){
31082 this.signatureTmp = s;
31083 this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
31084 this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', s);
31086 this.isConfirmed = false;
31087 Roo.form.Signature.superclass.reset.call(this);
31090 // Roo.log(this.signPanel.dom.contentWindow.up())
31093 setConfirmed : function(){
31097 // Roo.log(Roo.get(this.signPanel.dom.contentWindow.r).attr('fill', '#cfc'));
31100 confirmHandler : function(){
31101 if(!this.getSignature()){
31105 this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#cfc');
31106 this.setValue(this.getSignature());
31107 this.isConfirmed = true;
31109 this.fireEvent('confirm', this);
31112 // Subclasses should provide the validation implementation by overriding this
31113 validateValue : function(value){
31114 if(this.allowBlank){
31118 if(this.isConfirmed){
31125 * Ext JS Library 1.1.1
31126 * Copyright(c) 2006-2007, Ext JS, LLC.
31128 * Originally Released Under LGPL - original licence link has changed is not relivant.
31131 * <script type="text/javascript">
31136 * @class Roo.form.ComboBox
31137 * @extends Roo.form.TriggerField
31138 * A combobox control with support for autocomplete, remote-loading, paging and many other features.
31140 * Create a new ComboBox.
31141 * @param {Object} config Configuration options
31143 Roo.form.Select = function(config){
31144 Roo.form.Select.superclass.constructor.call(this, config);
31148 Roo.extend(Roo.form.Select , Roo.form.ComboBox, {
31150 * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
31153 * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
31154 * rendering into an Roo.Editor, defaults to false)
31157 * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
31158 * {tag: "input", type: "text", size: "24", autocomplete: "off"})
31161 * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
31164 * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
31165 * the dropdown list (defaults to undefined, with no header element)
31169 * @cfg {String/Roo.Template} tpl The template to use to render the output
31173 defaultAutoCreate : {tag: "select" },
31175 * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
31177 listWidth: undefined,
31179 * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
31180 * mode = 'remote' or 'text' if mode = 'local')
31182 displayField: undefined,
31184 * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
31185 * mode = 'remote' or 'value' if mode = 'local').
31186 * Note: use of a valueField requires the user make a selection
31187 * in order for a value to be mapped.
31189 valueField: undefined,
31193 * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
31194 * field's data value (defaults to the underlying DOM element's name)
31196 hiddenName: undefined,
31198 * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
31202 * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
31204 selectedClass: 'x-combo-selected',
31206 * @cfg {String} triggerClass An additional CSS class used to style the trigger button. The trigger will always get the
31207 * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
31208 * which displays a downward arrow icon).
31210 triggerClass : 'x-form-arrow-trigger',
31212 * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
31216 * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
31217 * anchor positions (defaults to 'tl-bl')
31219 listAlign: 'tl-bl?',
31221 * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
31225 * @cfg {String} triggerAction The action to execute when the trigger field is activated. Use 'all' to run the
31226 * query specified by the allQuery config option (defaults to 'query')
31228 triggerAction: 'query',
31230 * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
31231 * (defaults to 4, does not apply if editable = false)
31235 * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
31236 * delay (typeAheadDelay) if it matches a known value (defaults to false)
31240 * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
31241 * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
31245 * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
31246 * filter queries will execute with page start and limit parameters. Only applies when mode = 'remote' (defaults to 0)
31250 * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus. Only applies
31251 * when editable = true (defaults to false)
31253 selectOnFocus:false,
31255 * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
31257 queryParam: 'query',
31259 * @cfg {String} loadingText The text to display in the dropdown list while data is loading. Only applies
31260 * when mode = 'remote' (defaults to 'Loading...')
31262 loadingText: 'Loading...',
31264 * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
31268 * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
31272 * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
31273 * traditional select (defaults to true)
31277 * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
31281 * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
31285 * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
31286 * listWidth has a higher value)
31290 * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
31291 * allow the user to set arbitrary text into the field (defaults to false)
31293 forceSelection:false,
31295 * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
31296 * if typeAhead = true (defaults to 250)
31298 typeAheadDelay : 250,
31300 * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
31301 * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
31303 valueNotFoundText : undefined,
31306 * @cfg {String} defaultValue The value displayed after loading the store.
31311 * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
31313 blockFocus : false,
31316 * @cfg {Boolean} disableClear Disable showing of clear button.
31318 disableClear : false,
31320 * @cfg {Boolean} alwaysQuery Disable caching of results, and always send query
31322 alwaysQuery : false,
31328 // element that contains real text value.. (when hidden is used..)
31331 onRender : function(ct, position){
31332 Roo.form.Field.prototype.onRender.call(this, ct, position);
31335 this.store.on('beforeload', this.onBeforeLoad, this);
31336 this.store.on('load', this.onLoad, this);
31337 this.store.on('loadexception', this.onLoadException, this);
31338 this.store.load({});
31346 initEvents : function(){
31347 //Roo.form.ComboBox.superclass.initEvents.call(this);
31351 onDestroy : function(){
31354 this.store.un('beforeload', this.onBeforeLoad, this);
31355 this.store.un('load', this.onLoad, this);
31356 this.store.un('loadexception', this.onLoadException, this);
31358 //Roo.form.ComboBox.superclass.onDestroy.call(this);
31362 fireKey : function(e){
31363 if(e.isNavKeyPress() && !this.list.isVisible()){
31364 this.fireEvent("specialkey", this, e);
31369 onResize: function(w, h){
31377 * Allow or prevent the user from directly editing the field text. If false is passed,
31378 * the user will only be able to select from the items defined in the dropdown list. This method
31379 * is the runtime equivalent of setting the 'editable' config option at config time.
31380 * @param {Boolean} value True to allow the user to directly edit the field text
31382 setEditable : function(value){
31387 onBeforeLoad : function(){
31389 Roo.log("Select before load");
31392 this.innerList.update(this.loadingText ?
31393 '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
31394 //this.restrictHeight();
31395 this.selectedIndex = -1;
31399 onLoad : function(){
31402 var dom = this.el.dom;
31403 dom.innerHTML = '';
31404 var od = dom.ownerDocument;
31406 if (this.emptyText) {
31407 var op = od.createElement('option');
31408 op.setAttribute('value', '');
31409 op.innerHTML = String.format('{0}', this.emptyText);
31410 dom.appendChild(op);
31412 if(this.store.getCount() > 0){
31414 var vf = this.valueField;
31415 var df = this.displayField;
31416 this.store.data.each(function(r) {
31417 // which colmsn to use... testing - cdoe / title..
31418 var op = od.createElement('option');
31419 op.setAttribute('value', r.data[vf]);
31420 op.innerHTML = String.format('{0}', r.data[df]);
31421 dom.appendChild(op);
31423 if (typeof(this.defaultValue != 'undefined')) {
31424 this.setValue(this.defaultValue);
31429 //this.onEmptyResults();
31434 onLoadException : function()
31436 dom.innerHTML = '';
31438 Roo.log("Select on load exception");
31442 Roo.log(this.store.reader.jsonData);
31443 if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
31444 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
31450 onTypeAhead : function(){
31455 onSelect : function(record, index){
31456 Roo.log('on select?');
31458 if(this.fireEvent('beforeselect', this, record, index) !== false){
31459 this.setFromData(index > -1 ? record.data : false);
31461 this.fireEvent('select', this, record, index);
31466 * Returns the currently selected field value or empty string if no value is set.
31467 * @return {String} value The selected value
31469 getValue : function(){
31470 var dom = this.el.dom;
31471 this.value = dom.options[dom.selectedIndex].value;
31477 * Clears any text/value currently set in the field
31479 clearValue : function(){
31481 this.el.dom.selectedIndex = this.emptyText ? 0 : -1;
31486 * Sets the specified value into the field. If the value finds a match, the corresponding record text
31487 * will be displayed in the field. If the value does not match the data value of an existing item,
31488 * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
31489 * Otherwise the field will be blank (although the value will still be set).
31490 * @param {String} value The value to match
31492 setValue : function(v){
31493 var d = this.el.dom;
31494 for (var i =0; i < d.options.length;i++) {
31495 if (v == d.options[i].value) {
31496 d.selectedIndex = i;
31504 * @property {Object} the last set data for the element
31509 * Sets the value of the field based on a object which is related to the record format for the store.
31510 * @param {Object} value the value to set as. or false on reset?
31512 setFromData : function(o){
31513 Roo.log('setfrom data?');
31519 reset : function(){
31523 findRecord : function(prop, value){
31528 if(this.store.getCount() > 0){
31529 this.store.each(function(r){
31530 if(r.data[prop] == value){
31540 getName: function()
31542 // returns hidden if it's set..
31543 if (!this.rendered) {return ''};
31544 return !this.hiddenName && this.el.dom.name ? this.el.dom.name : (this.hiddenName || '');
31552 onEmptyResults : function(){
31553 Roo.log('empty results');
31558 * Returns true if the dropdown list is expanded, else false.
31560 isExpanded : function(){
31565 * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
31566 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
31567 * @param {String} value The data value of the item to select
31568 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
31569 * selected item if it is not currently in view (defaults to true)
31570 * @return {Boolean} True if the value matched an item in the list, else false
31572 selectByValue : function(v, scrollIntoView){
31573 Roo.log('select By Value');
31576 if(v !== undefined && v !== null){
31577 var r = this.findRecord(this.valueField || this.displayField, v);
31579 this.select(this.store.indexOf(r), scrollIntoView);
31587 * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
31588 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
31589 * @param {Number} index The zero-based index of the list item to select
31590 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
31591 * selected item if it is not currently in view (defaults to true)
31593 select : function(index, scrollIntoView){
31594 Roo.log('select ');
31597 this.selectedIndex = index;
31598 this.view.select(index);
31599 if(scrollIntoView !== false){
31600 var el = this.view.getNode(index);
31602 this.innerList.scrollChildIntoView(el, false);
31610 validateBlur : function(){
31617 initQuery : function(){
31618 this.doQuery(this.getRawValue());
31622 doForce : function(){
31623 if(this.el.dom.value.length > 0){
31624 this.el.dom.value =
31625 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
31631 * Execute a query to filter the dropdown list. Fires the beforequery event prior to performing the
31632 * query allowing the query action to be canceled if needed.
31633 * @param {String} query The SQL query to execute
31634 * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
31635 * in the field than the minimum specified by the minChars config option. It also clears any filter previously
31636 * saved in the current store (defaults to false)
31638 doQuery : function(q, forceAll){
31640 Roo.log('doQuery?');
31641 if(q === undefined || q === null){
31646 forceAll: forceAll,
31650 if(this.fireEvent('beforequery', qe)===false || qe.cancel){
31654 forceAll = qe.forceAll;
31655 if(forceAll === true || (q.length >= this.minChars)){
31656 if(this.lastQuery != q || this.alwaysQuery){
31657 this.lastQuery = q;
31658 if(this.mode == 'local'){
31659 this.selectedIndex = -1;
31661 this.store.clearFilter();
31663 this.store.filter(this.displayField, q);
31667 this.store.baseParams[this.queryParam] = q;
31669 params: this.getParams(q)
31674 this.selectedIndex = -1;
31681 getParams : function(q){
31683 //p[this.queryParam] = q;
31686 p.limit = this.pageSize;
31692 * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
31694 collapse : function(){
31699 collapseIf : function(e){
31704 * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
31706 expand : function(){
31714 * @cfg {Boolean} grow
31718 * @cfg {Number} growMin
31722 * @cfg {Number} growMax
31730 setWidth : function()
31734 getResizeEl : function(){
31737 });//<script type="text/javasscript">
31741 * @class Roo.DDView
31742 * A DnD enabled version of Roo.View.
31743 * @param {Element/String} container The Element in which to create the View.
31744 * @param {String} tpl The template string used to create the markup for each element of the View
31745 * @param {Object} config The configuration properties. These include all the config options of
31746 * {@link Roo.View} plus some specific to this class.<br>
31748 * Drag/drop is implemented by adding {@link Roo.data.Record}s to the target DDView. If copying is
31749 * not being performed, the original {@link Roo.data.Record} is removed from the source DDView.<br>
31751 * The following extra CSS rules are needed to provide insertion point highlighting:<pre><code>
31752 .x-view-drag-insert-above {
31753 border-top:1px dotted #3366cc;
31755 .x-view-drag-insert-below {
31756 border-bottom:1px dotted #3366cc;
31762 Roo.DDView = function(container, tpl, config) {
31763 Roo.DDView.superclass.constructor.apply(this, arguments);
31764 this.getEl().setStyle("outline", "0px none");
31765 this.getEl().unselectable();
31766 if (this.dragGroup) {
31767 this.setDraggable(this.dragGroup.split(","));
31769 if (this.dropGroup) {
31770 this.setDroppable(this.dropGroup.split(","));
31772 if (this.deletable) {
31773 this.setDeletable();
31775 this.isDirtyFlag = false;
31781 Roo.extend(Roo.DDView, Roo.View, {
31782 /** @cfg {String/Array} dragGroup The ddgroup name(s) for the View's DragZone. */
31783 /** @cfg {String/Array} dropGroup The ddgroup name(s) for the View's DropZone. */
31784 /** @cfg {Boolean} copy Causes drag operations to copy nodes rather than move. */
31785 /** @cfg {Boolean} allowCopy Causes ctrl/drag operations to copy nodes rather than move. */
31789 reset: Roo.emptyFn,
31791 clearInvalid: Roo.form.Field.prototype.clearInvalid,
31793 validate: function() {
31797 destroy: function() {
31798 this.purgeListeners();
31799 this.getEl.removeAllListeners();
31800 this.getEl().remove();
31801 if (this.dragZone) {
31802 if (this.dragZone.destroy) {
31803 this.dragZone.destroy();
31806 if (this.dropZone) {
31807 if (this.dropZone.destroy) {
31808 this.dropZone.destroy();
31813 /** Allows this class to be an Roo.form.Field so it can be found using {@link Roo.form.BasicForm#findField}. */
31814 getName: function() {
31818 /** Loads the View from a JSON string representing the Records to put into the Store. */
31819 setValue: function(v) {
31821 throw "DDView.setValue(). DDView must be constructed with a valid Store";
31824 data[this.store.reader.meta.root] = v ? [].concat(v) : [];
31825 this.store.proxy = new Roo.data.MemoryProxy(data);
31829 /** @return {String} a parenthesised list of the ids of the Records in the View. */
31830 getValue: function() {
31832 this.store.each(function(rec) {
31833 result += rec.id + ',';
31835 return result.substr(0, result.length - 1) + ')';
31838 getIds: function() {
31839 var i = 0, result = new Array(this.store.getCount());
31840 this.store.each(function(rec) {
31841 result[i++] = rec.id;
31846 isDirty: function() {
31847 return this.isDirtyFlag;
31851 * Part of the Roo.dd.DropZone interface. If no target node is found, the
31852 * whole Element becomes the target, and this causes the drop gesture to append.
31854 getTargetFromEvent : function(e) {
31855 var target = e.getTarget();
31856 while ((target !== null) && (target.parentNode != this.el.dom)) {
31857 target = target.parentNode;
31860 target = this.el.dom.lastChild || this.el.dom;
31866 * Create the drag data which consists of an object which has the property "ddel" as
31867 * the drag proxy element.
31869 getDragData : function(e) {
31870 var target = this.findItemFromChild(e.getTarget());
31872 this.handleSelection(e);
31873 var selNodes = this.getSelectedNodes();
31876 copy: this.copy || (this.allowCopy && e.ctrlKey),
31880 var selectedIndices = this.getSelectedIndexes();
31881 for (var i = 0; i < selectedIndices.length; i++) {
31882 dragData.records.push(this.store.getAt(selectedIndices[i]));
31884 if (selNodes.length == 1) {
31885 dragData.ddel = target.cloneNode(true); // the div element
31887 var div = document.createElement('div'); // create the multi element drag "ghost"
31888 div.className = 'multi-proxy';
31889 for (var i = 0, len = selNodes.length; i < len; i++) {
31890 div.appendChild(selNodes[i].cloneNode(true));
31892 dragData.ddel = div;
31894 //console.log(dragData)
31895 //console.log(dragData.ddel.innerHTML)
31898 //console.log('nodragData')
31902 /** Specify to which ddGroup items in this DDView may be dragged. */
31903 setDraggable: function(ddGroup) {
31904 if (ddGroup instanceof Array) {
31905 Roo.each(ddGroup, this.setDraggable, this);
31908 if (this.dragZone) {
31909 this.dragZone.addToGroup(ddGroup);
31911 this.dragZone = new Roo.dd.DragZone(this.getEl(), {
31912 containerScroll: true,
31916 // Draggability implies selection. DragZone's mousedown selects the element.
31917 if (!this.multiSelect) { this.singleSelect = true; }
31919 // Wire the DragZone's handlers up to methods in *this*
31920 this.dragZone.getDragData = this.getDragData.createDelegate(this);
31924 /** Specify from which ddGroup this DDView accepts drops. */
31925 setDroppable: function(ddGroup) {
31926 if (ddGroup instanceof Array) {
31927 Roo.each(ddGroup, this.setDroppable, this);
31930 if (this.dropZone) {
31931 this.dropZone.addToGroup(ddGroup);
31933 this.dropZone = new Roo.dd.DropZone(this.getEl(), {
31934 containerScroll: true,
31938 // Wire the DropZone's handlers up to methods in *this*
31939 this.dropZone.getTargetFromEvent = this.getTargetFromEvent.createDelegate(this);
31940 this.dropZone.onNodeEnter = this.onNodeEnter.createDelegate(this);
31941 this.dropZone.onNodeOver = this.onNodeOver.createDelegate(this);
31942 this.dropZone.onNodeOut = this.onNodeOut.createDelegate(this);
31943 this.dropZone.onNodeDrop = this.onNodeDrop.createDelegate(this);
31947 /** Decide whether to drop above or below a View node. */
31948 getDropPoint : function(e, n, dd){
31949 if (n == this.el.dom) { return "above"; }
31950 var t = Roo.lib.Dom.getY(n), b = t + n.offsetHeight;
31951 var c = t + (b - t) / 2;
31952 var y = Roo.lib.Event.getPageY(e);
31960 onNodeEnter : function(n, dd, e, data){
31964 onNodeOver : function(n, dd, e, data){
31965 var pt = this.getDropPoint(e, n, dd);
31966 // set the insert point style on the target node
31967 var dragElClass = this.dropNotAllowed;
31970 if (pt == "above"){
31971 dragElClass = n.previousSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-above";
31972 targetElClass = "x-view-drag-insert-above";
31974 dragElClass = n.nextSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-below";
31975 targetElClass = "x-view-drag-insert-below";
31977 if (this.lastInsertClass != targetElClass){
31978 Roo.fly(n).replaceClass(this.lastInsertClass, targetElClass);
31979 this.lastInsertClass = targetElClass;
31982 return dragElClass;
31985 onNodeOut : function(n, dd, e, data){
31986 this.removeDropIndicators(n);
31989 onNodeDrop : function(n, dd, e, data){
31990 if (this.fireEvent("drop", this, n, dd, e, data) === false) {
31993 var pt = this.getDropPoint(e, n, dd);
31994 var insertAt = (n == this.el.dom) ? this.nodes.length : n.nodeIndex;
31995 if (pt == "below") { insertAt++; }
31996 for (var i = 0; i < data.records.length; i++) {
31997 var r = data.records[i];
31998 var dup = this.store.getById(r.id);
31999 if (dup && (dd != this.dragZone)) {
32000 Roo.fly(this.getNode(this.store.indexOf(dup))).frame("red", 1);
32003 this.store.insert(insertAt++, r.copy());
32005 data.source.isDirtyFlag = true;
32007 this.store.insert(insertAt++, r);
32009 this.isDirtyFlag = true;
32012 this.dragZone.cachedTarget = null;
32016 removeDropIndicators : function(n){
32018 Roo.fly(n).removeClass([
32019 "x-view-drag-insert-above",
32020 "x-view-drag-insert-below"]);
32021 this.lastInsertClass = "_noclass";
32026 * Utility method. Add a delete option to the DDView's context menu.
32027 * @param {String} imageUrl The URL of the "delete" icon image.
32029 setDeletable: function(imageUrl) {
32030 if (!this.singleSelect && !this.multiSelect) {
32031 this.singleSelect = true;
32033 var c = this.getContextMenu();
32034 this.contextMenu.on("itemclick", function(item) {
32037 this.remove(this.getSelectedIndexes());
32041 this.contextMenu.add({
32048 /** Return the context menu for this DDView. */
32049 getContextMenu: function() {
32050 if (!this.contextMenu) {
32051 // Create the View's context menu
32052 this.contextMenu = new Roo.menu.Menu({
32053 id: this.id + "-contextmenu"
32055 this.el.on("contextmenu", this.showContextMenu, this);
32057 return this.contextMenu;
32060 disableContextMenu: function() {
32061 if (this.contextMenu) {
32062 this.el.un("contextmenu", this.showContextMenu, this);
32066 showContextMenu: function(e, item) {
32067 item = this.findItemFromChild(e.getTarget());
32070 this.select(this.getNode(item), this.multiSelect && e.ctrlKey, true);
32071 this.contextMenu.showAt(e.getXY());
32076 * Remove {@link Roo.data.Record}s at the specified indices.
32077 * @param {Array/Number} selectedIndices The index (or Array of indices) of Records to remove.
32079 remove: function(selectedIndices) {
32080 selectedIndices = [].concat(selectedIndices);
32081 for (var i = 0; i < selectedIndices.length; i++) {
32082 var rec = this.store.getAt(selectedIndices[i]);
32083 this.store.remove(rec);
32088 * Double click fires the event, but also, if this is draggable, and there is only one other
32089 * related DropZone, it transfers the selected node.
32091 onDblClick : function(e){
32092 var item = this.findItemFromChild(e.getTarget());
32094 if (this.fireEvent("dblclick", this, this.indexOf(item), item, e) === false) {
32097 if (this.dragGroup) {
32098 var targets = Roo.dd.DragDropMgr.getRelated(this.dragZone, true);
32099 while (targets.indexOf(this.dropZone) > -1) {
32100 targets.remove(this.dropZone);
32102 if (targets.length == 1) {
32103 this.dragZone.cachedTarget = null;
32104 var el = Roo.get(targets[0].getEl());
32105 var box = el.getBox(true);
32106 targets[0].onNodeDrop(el.dom, {
32108 xy: [box.x, box.y + box.height - 1]
32109 }, null, this.getDragData(e));
32115 handleSelection: function(e) {
32116 this.dragZone.cachedTarget = null;
32117 var item = this.findItemFromChild(e.getTarget());
32119 this.clearSelections(true);
32122 if (item && (this.multiSelect || this.singleSelect)){
32123 if(this.multiSelect && e.shiftKey && (!e.ctrlKey) && this.lastSelection){
32124 this.select(this.getNodes(this.indexOf(this.lastSelection), item.nodeIndex), false);
32125 }else if (this.isSelected(this.getNode(item)) && e.ctrlKey){
32126 this.unselect(item);
32128 this.select(item, this.multiSelect && e.ctrlKey);
32129 this.lastSelection = item;
32134 onItemClick : function(item, index, e){
32135 if(this.fireEvent("beforeclick", this, index, item, e) === false){
32141 unselect : function(nodeInfo, suppressEvent){
32142 var node = this.getNode(nodeInfo);
32143 if(node && this.isSelected(node)){
32144 if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
32145 Roo.fly(node).removeClass(this.selectedClass);
32146 this.selections.remove(node);
32147 if(!suppressEvent){
32148 this.fireEvent("selectionchange", this, this.selections);
32156 * Ext JS Library 1.1.1
32157 * Copyright(c) 2006-2007, Ext JS, LLC.
32159 * Originally Released Under LGPL - original licence link has changed is not relivant.
32162 * <script type="text/javascript">
32166 * @class Roo.LayoutManager
32167 * @extends Roo.util.Observable
32168 * Base class for layout managers.
32170 Roo.LayoutManager = function(container, config){
32171 Roo.LayoutManager.superclass.constructor.call(this);
32172 this.el = Roo.get(container);
32173 // ie scrollbar fix
32174 if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
32175 document.body.scroll = "no";
32176 }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
32177 this.el.position('relative');
32179 this.id = this.el.id;
32180 this.el.addClass("x-layout-container");
32181 /** false to disable window resize monitoring @type Boolean */
32182 this.monitorWindowResize = true;
32187 * Fires when a layout is performed.
32188 * @param {Roo.LayoutManager} this
32192 * @event regionresized
32193 * Fires when the user resizes a region.
32194 * @param {Roo.LayoutRegion} region The resized region
32195 * @param {Number} newSize The new size (width for east/west, height for north/south)
32197 "regionresized" : true,
32199 * @event regioncollapsed
32200 * Fires when a region is collapsed.
32201 * @param {Roo.LayoutRegion} region The collapsed region
32203 "regioncollapsed" : true,
32205 * @event regionexpanded
32206 * Fires when a region is expanded.
32207 * @param {Roo.LayoutRegion} region The expanded region
32209 "regionexpanded" : true
32211 this.updating = false;
32212 Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
32215 Roo.extend(Roo.LayoutManager, Roo.util.Observable, {
32217 * Returns true if this layout is currently being updated
32218 * @return {Boolean}
32220 isUpdating : function(){
32221 return this.updating;
32225 * Suspend the LayoutManager from doing auto-layouts while
32226 * making multiple add or remove calls
32228 beginUpdate : function(){
32229 this.updating = true;
32233 * Restore auto-layouts and optionally disable the manager from performing a layout
32234 * @param {Boolean} noLayout true to disable a layout update
32236 endUpdate : function(noLayout){
32237 this.updating = false;
32243 layout: function(){
32247 onRegionResized : function(region, newSize){
32248 this.fireEvent("regionresized", region, newSize);
32252 onRegionCollapsed : function(region){
32253 this.fireEvent("regioncollapsed", region);
32256 onRegionExpanded : function(region){
32257 this.fireEvent("regionexpanded", region);
32261 * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
32262 * performs box-model adjustments.
32263 * @return {Object} The size as an object {width: (the width), height: (the height)}
32265 getViewSize : function(){
32267 if(this.el.dom != document.body){
32268 size = this.el.getSize();
32270 size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
32272 size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
32273 size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
32278 * Returns the Element this layout is bound to.
32279 * @return {Roo.Element}
32281 getEl : function(){
32286 * Returns the specified region.
32287 * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
32288 * @return {Roo.LayoutRegion}
32290 getRegion : function(target){
32291 return this.regions[target.toLowerCase()];
32294 onWindowResize : function(){
32295 if(this.monitorWindowResize){
32301 * Ext JS Library 1.1.1
32302 * Copyright(c) 2006-2007, Ext JS, LLC.
32304 * Originally Released Under LGPL - original licence link has changed is not relivant.
32307 * <script type="text/javascript">
32310 * @class Roo.BorderLayout
32311 * @extends Roo.LayoutManager
32312 * This class represents a common layout manager used in desktop applications. For screenshots and more details,
32313 * please see: <br><br>
32314 * <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>
32315 * <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>
32318 var layout = new Roo.BorderLayout(document.body, {
32352 preferredTabWidth: 150
32357 var CP = Roo.ContentPanel;
32359 layout.beginUpdate();
32360 layout.add("north", new CP("north", "North"));
32361 layout.add("south", new CP("south", {title: "South", closable: true}));
32362 layout.add("west", new CP("west", {title: "West"}));
32363 layout.add("east", new CP("autoTabs", {title: "Auto Tabs", closable: true}));
32364 layout.add("center", new CP("center1", {title: "Close Me", closable: true}));
32365 layout.add("center", new CP("center2", {title: "Center Panel", closable: false}));
32366 layout.getRegion("center").showPanel("center1");
32367 layout.endUpdate();
32370 <b>The container the layout is rendered into can be either the body element or any other element.
32371 If it is not the body element, the container needs to either be an absolute positioned element,
32372 or you will need to add "position:relative" to the css of the container. You will also need to specify
32373 the container size if it is not the body element.</b>
32376 * Create a new BorderLayout
32377 * @param {String/HTMLElement/Element} container The container this layout is bound to
32378 * @param {Object} config Configuration options
32380 Roo.BorderLayout = function(container, config){
32381 config = config || {};
32382 Roo.BorderLayout.superclass.constructor.call(this, container, config);
32383 this.factory = config.factory || Roo.BorderLayout.RegionFactory;
32384 for(var i = 0, len = this.factory.validRegions.length; i < len; i++) {
32385 var target = this.factory.validRegions[i];
32386 if(config[target]){
32387 this.addRegion(target, config[target]);
32392 Roo.extend(Roo.BorderLayout, Roo.LayoutManager, {
32394 * Creates and adds a new region if it doesn't already exist.
32395 * @param {String} target The target region key (north, south, east, west or center).
32396 * @param {Object} config The regions config object
32397 * @return {BorderLayoutRegion} The new region
32399 addRegion : function(target, config){
32400 if(!this.regions[target]){
32401 var r = this.factory.create(target, this, config);
32402 this.bindRegion(target, r);
32404 return this.regions[target];
32408 bindRegion : function(name, r){
32409 this.regions[name] = r;
32410 r.on("visibilitychange", this.layout, this);
32411 r.on("paneladded", this.layout, this);
32412 r.on("panelremoved", this.layout, this);
32413 r.on("invalidated", this.layout, this);
32414 r.on("resized", this.onRegionResized, this);
32415 r.on("collapsed", this.onRegionCollapsed, this);
32416 r.on("expanded", this.onRegionExpanded, this);
32420 * Performs a layout update.
32422 layout : function(){
32423 if(this.updating) return;
32424 var size = this.getViewSize();
32425 var w = size.width;
32426 var h = size.height;
32431 //var x = 0, y = 0;
32433 var rs = this.regions;
32434 var north = rs["north"];
32435 var south = rs["south"];
32436 var west = rs["west"];
32437 var east = rs["east"];
32438 var center = rs["center"];
32439 //if(this.hideOnLayout){ // not supported anymore
32440 //c.el.setStyle("display", "none");
32442 if(north && north.isVisible()){
32443 var b = north.getBox();
32444 var m = north.getMargins();
32445 b.width = w - (m.left+m.right);
32448 centerY = b.height + b.y + m.bottom;
32449 centerH -= centerY;
32450 north.updateBox(this.safeBox(b));
32452 if(south && south.isVisible()){
32453 var b = south.getBox();
32454 var m = south.getMargins();
32455 b.width = w - (m.left+m.right);
32457 var totalHeight = (b.height + m.top + m.bottom);
32458 b.y = h - totalHeight + m.top;
32459 centerH -= totalHeight;
32460 south.updateBox(this.safeBox(b));
32462 if(west && west.isVisible()){
32463 var b = west.getBox();
32464 var m = west.getMargins();
32465 b.height = centerH - (m.top+m.bottom);
32467 b.y = centerY + m.top;
32468 var totalWidth = (b.width + m.left + m.right);
32469 centerX += totalWidth;
32470 centerW -= totalWidth;
32471 west.updateBox(this.safeBox(b));
32473 if(east && east.isVisible()){
32474 var b = east.getBox();
32475 var m = east.getMargins();
32476 b.height = centerH - (m.top+m.bottom);
32477 var totalWidth = (b.width + m.left + m.right);
32478 b.x = w - totalWidth + m.left;
32479 b.y = centerY + m.top;
32480 centerW -= totalWidth;
32481 east.updateBox(this.safeBox(b));
32484 var m = center.getMargins();
32486 x: centerX + m.left,
32487 y: centerY + m.top,
32488 width: centerW - (m.left+m.right),
32489 height: centerH - (m.top+m.bottom)
32491 //if(this.hideOnLayout){
32492 //center.el.setStyle("display", "block");
32494 center.updateBox(this.safeBox(centerBox));
32497 this.fireEvent("layout", this);
32501 safeBox : function(box){
32502 box.width = Math.max(0, box.width);
32503 box.height = Math.max(0, box.height);
32508 * Adds a ContentPanel (or subclass) to this layout.
32509 * @param {String} target The target region key (north, south, east, west or center).
32510 * @param {Roo.ContentPanel} panel The panel to add
32511 * @return {Roo.ContentPanel} The added panel
32513 add : function(target, panel){
32515 target = target.toLowerCase();
32516 return this.regions[target].add(panel);
32520 * Remove a ContentPanel (or subclass) to this layout.
32521 * @param {String} target The target region key (north, south, east, west or center).
32522 * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
32523 * @return {Roo.ContentPanel} The removed panel
32525 remove : function(target, panel){
32526 target = target.toLowerCase();
32527 return this.regions[target].remove(panel);
32531 * Searches all regions for a panel with the specified id
32532 * @param {String} panelId
32533 * @return {Roo.ContentPanel} The panel or null if it wasn't found
32535 findPanel : function(panelId){
32536 var rs = this.regions;
32537 for(var target in rs){
32538 if(typeof rs[target] != "function"){
32539 var p = rs[target].getPanel(panelId);
32549 * Searches all regions for a panel with the specified id and activates (shows) it.
32550 * @param {String/ContentPanel} panelId The panels id or the panel itself
32551 * @return {Roo.ContentPanel} The shown panel or null
32553 showPanel : function(panelId) {
32554 var rs = this.regions;
32555 for(var target in rs){
32556 var r = rs[target];
32557 if(typeof r != "function"){
32558 if(r.hasPanel(panelId)){
32559 return r.showPanel(panelId);
32567 * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
32568 * @param {Roo.state.Provider} provider (optional) An alternate state provider
32570 restoreState : function(provider){
32572 provider = Roo.state.Manager;
32574 var sm = new Roo.LayoutStateManager();
32575 sm.init(this, provider);
32579 * Adds a batch of multiple ContentPanels dynamically by passing a special regions config object. This config
32580 * object should contain properties for each region to add ContentPanels to, and each property's value should be
32581 * a valid ContentPanel config object. Example:
32583 // Create the main layout
32584 var layout = new Roo.BorderLayout('main-ct', {
32595 // Create and add multiple ContentPanels at once via configs
32598 id: 'source-files',
32600 title:'Ext Source Files',
32613 * @param {Object} regions An object containing ContentPanel configs by region name
32615 batchAdd : function(regions){
32616 this.beginUpdate();
32617 for(var rname in regions){
32618 var lr = this.regions[rname];
32620 this.addTypedPanels(lr, regions[rname]);
32627 addTypedPanels : function(lr, ps){
32628 if(typeof ps == 'string'){
32629 lr.add(new Roo.ContentPanel(ps));
32631 else if(ps instanceof Array){
32632 for(var i =0, len = ps.length; i < len; i++){
32633 this.addTypedPanels(lr, ps[i]);
32636 else if(!ps.events){ // raw config?
32638 delete ps.el; // prevent conflict
32639 lr.add(new Roo.ContentPanel(el || Roo.id(), ps));
32641 else { // panel object assumed!
32646 * Adds a xtype elements to the layout.
32650 xtype : 'ContentPanel',
32657 xtype : 'NestedLayoutPanel',
32663 items : [ ... list of content panels or nested layout panels.. ]
32667 * @param {Object} cfg Xtype definition of item to add.
32669 addxtype : function(cfg)
32671 // basically accepts a pannel...
32672 // can accept a layout region..!?!?
32673 //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
32675 if (!cfg.xtype.match(/Panel$/)) {
32680 if (typeof(cfg.region) == 'undefined') {
32681 Roo.log("Failed to add Panel, region was not set");
32685 var region = cfg.region;
32691 xitems = cfg.items;
32698 case 'ContentPanel': // ContentPanel (el, cfg)
32699 case 'ScrollPanel': // ContentPanel (el, cfg)
32701 if(cfg.autoCreate) {
32702 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
32704 var el = this.el.createChild();
32705 ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
32708 this.add(region, ret);
32712 case 'TreePanel': // our new panel!
32713 cfg.el = this.el.createChild();
32714 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
32715 this.add(region, ret);
32718 case 'NestedLayoutPanel':
32719 // create a new Layout (which is a Border Layout...
32720 var el = this.el.createChild();
32721 var clayout = cfg.layout;
32723 clayout.items = clayout.items || [];
32724 // replace this exitems with the clayout ones..
32725 xitems = clayout.items;
32728 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
32729 cfg.background = false;
32731 var layout = new Roo.BorderLayout(el, clayout);
32733 ret = new Roo[cfg.xtype](layout, cfg); // new panel!!!!!
32734 //console.log('adding nested layout panel ' + cfg.toSource());
32735 this.add(region, ret);
32736 nb = {}; /// find first...
32741 // needs grid and region
32743 //var el = this.getRegion(region).el.createChild();
32744 var el = this.el.createChild();
32745 // create the grid first...
32747 var grid = new Roo.grid[cfg.grid.xtype](el, cfg.grid);
32749 if (region == 'center' && this.active ) {
32750 cfg.background = false;
32752 ret = new Roo[cfg.xtype](grid, cfg); // new panel!!!!!
32754 this.add(region, ret);
32755 if (cfg.background) {
32756 ret.on('activate', function(gp) {
32757 if (!gp.grid.rendered) {
32772 alert("Can not add '" + cfg.xtype + "' to BorderLayout");
32774 // GridPanel (grid, cfg)
32777 this.beginUpdate();
32781 Roo.each(xitems, function(i) {
32782 region = nb && i.region ? i.region : false;
32784 var add = ret.addxtype(i);
32787 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
32788 if (!i.background) {
32789 abn[region] = nb[region] ;
32796 // make the last non-background panel active..
32797 //if (nb) { Roo.log(abn); }
32800 for(var r in abn) {
32801 region = this.getRegion(r);
32803 // tried using nb[r], but it does not work..
32805 region.showPanel(abn[r]);
32816 * Shortcut for creating a new BorderLayout object and adding one or more ContentPanels to it in a single step, handling
32817 * the beginUpdate and endUpdate calls internally. The key to this method is the <b>panels</b> property that can be
32818 * provided with each region config, which allows you to add ContentPanel configs in addition to the region configs
32819 * during creation. The following code is equivalent to the constructor-based example at the beginning of this class:
32822 var CP = Roo.ContentPanel;
32824 var layout = Roo.BorderLayout.create({
32828 panels: [new CP("north", "North")]
32837 panels: [new CP("west", {title: "West"})]
32846 panels: [new CP("autoTabs", {title: "Auto Tabs", closable: true})]
32855 panels: [new CP("south", {title: "South", closable: true})]
32862 preferredTabWidth: 150,
32864 new CP("center1", {title: "Close Me", closable: true}),
32865 new CP("center2", {title: "Center Panel", closable: false})
32870 layout.getRegion("center").showPanel("center1");
32875 Roo.BorderLayout.create = function(config, targetEl){
32876 var layout = new Roo.BorderLayout(targetEl || document.body, config);
32877 layout.beginUpdate();
32878 var regions = Roo.BorderLayout.RegionFactory.validRegions;
32879 for(var j = 0, jlen = regions.length; j < jlen; j++){
32880 var lr = regions[j];
32881 if(layout.regions[lr] && config[lr].panels){
32882 var r = layout.regions[lr];
32883 var ps = config[lr].panels;
32884 layout.addTypedPanels(r, ps);
32887 layout.endUpdate();
32892 Roo.BorderLayout.RegionFactory = {
32894 validRegions : ["north","south","east","west","center"],
32897 create : function(target, mgr, config){
32898 target = target.toLowerCase();
32899 if(config.lightweight || config.basic){
32900 return new Roo.BasicLayoutRegion(mgr, config, target);
32904 return new Roo.NorthLayoutRegion(mgr, config);
32906 return new Roo.SouthLayoutRegion(mgr, config);
32908 return new Roo.EastLayoutRegion(mgr, config);
32910 return new Roo.WestLayoutRegion(mgr, config);
32912 return new Roo.CenterLayoutRegion(mgr, config);
32914 throw 'Layout region "'+target+'" not supported.';
32918 * Ext JS Library 1.1.1
32919 * Copyright(c) 2006-2007, Ext JS, LLC.
32921 * Originally Released Under LGPL - original licence link has changed is not relivant.
32924 * <script type="text/javascript">
32928 * @class Roo.BasicLayoutRegion
32929 * @extends Roo.util.Observable
32930 * This class represents a lightweight region in a layout manager. This region does not move dom nodes
32931 * and does not have a titlebar, tabs or any other features. All it does is size and position
32932 * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
32934 Roo.BasicLayoutRegion = function(mgr, config, pos, skipConfig){
32936 this.position = pos;
32939 * @scope Roo.BasicLayoutRegion
32943 * @event beforeremove
32944 * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
32945 * @param {Roo.LayoutRegion} this
32946 * @param {Roo.ContentPanel} panel The panel
32947 * @param {Object} e The cancel event object
32949 "beforeremove" : true,
32951 * @event invalidated
32952 * Fires when the layout for this region is changed.
32953 * @param {Roo.LayoutRegion} this
32955 "invalidated" : true,
32957 * @event visibilitychange
32958 * Fires when this region is shown or hidden
32959 * @param {Roo.LayoutRegion} this
32960 * @param {Boolean} visibility true or false
32962 "visibilitychange" : true,
32964 * @event paneladded
32965 * Fires when a panel is added.
32966 * @param {Roo.LayoutRegion} this
32967 * @param {Roo.ContentPanel} panel The panel
32969 "paneladded" : true,
32971 * @event panelremoved
32972 * Fires when a panel is removed.
32973 * @param {Roo.LayoutRegion} this
32974 * @param {Roo.ContentPanel} panel The panel
32976 "panelremoved" : true,
32979 * Fires when this region is collapsed.
32980 * @param {Roo.LayoutRegion} this
32982 "collapsed" : true,
32985 * Fires when this region is expanded.
32986 * @param {Roo.LayoutRegion} this
32991 * Fires when this region is slid into view.
32992 * @param {Roo.LayoutRegion} this
32994 "slideshow" : true,
32997 * Fires when this region slides out of view.
32998 * @param {Roo.LayoutRegion} this
33000 "slidehide" : true,
33002 * @event panelactivated
33003 * Fires when a panel is activated.
33004 * @param {Roo.LayoutRegion} this
33005 * @param {Roo.ContentPanel} panel The activated panel
33007 "panelactivated" : true,
33010 * Fires when the user resizes this region.
33011 * @param {Roo.LayoutRegion} this
33012 * @param {Number} newSize The new size (width for east/west, height for north/south)
33016 /** A collection of panels in this region. @type Roo.util.MixedCollection */
33017 this.panels = new Roo.util.MixedCollection();
33018 this.panels.getKey = this.getPanelId.createDelegate(this);
33020 this.activePanel = null;
33021 // ensure listeners are added...
33023 if (config.listeners || config.events) {
33024 Roo.BasicLayoutRegion.superclass.constructor.call(this, {
33025 listeners : config.listeners || {},
33026 events : config.events || {}
33030 if(skipConfig !== true){
33031 this.applyConfig(config);
33035 Roo.extend(Roo.BasicLayoutRegion, Roo.util.Observable, {
33036 getPanelId : function(p){
33040 applyConfig : function(config){
33041 this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
33042 this.config = config;
33047 * Resizes the region to the specified size. For vertical regions (west, east) this adjusts
33048 * the width, for horizontal (north, south) the height.
33049 * @param {Number} newSize The new width or height
33051 resizeTo : function(newSize){
33052 var el = this.el ? this.el :
33053 (this.activePanel ? this.activePanel.getEl() : null);
33055 switch(this.position){
33058 el.setWidth(newSize);
33059 this.fireEvent("resized", this, newSize);
33063 el.setHeight(newSize);
33064 this.fireEvent("resized", this, newSize);
33070 getBox : function(){
33071 return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
33074 getMargins : function(){
33075 return this.margins;
33078 updateBox : function(box){
33080 var el = this.activePanel.getEl();
33081 el.dom.style.left = box.x + "px";
33082 el.dom.style.top = box.y + "px";
33083 this.activePanel.setSize(box.width, box.height);
33087 * Returns the container element for this region.
33088 * @return {Roo.Element}
33090 getEl : function(){
33091 return this.activePanel;
33095 * Returns true if this region is currently visible.
33096 * @return {Boolean}
33098 isVisible : function(){
33099 return this.activePanel ? true : false;
33102 setActivePanel : function(panel){
33103 panel = this.getPanel(panel);
33104 if(this.activePanel && this.activePanel != panel){
33105 this.activePanel.setActiveState(false);
33106 this.activePanel.getEl().setLeftTop(-10000,-10000);
33108 this.activePanel = panel;
33109 panel.setActiveState(true);
33111 panel.setSize(this.box.width, this.box.height);
33113 this.fireEvent("panelactivated", this, panel);
33114 this.fireEvent("invalidated");
33118 * Show the specified panel.
33119 * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
33120 * @return {Roo.ContentPanel} The shown panel or null
33122 showPanel : function(panel){
33123 if(panel = this.getPanel(panel)){
33124 this.setActivePanel(panel);
33130 * Get the active panel for this region.
33131 * @return {Roo.ContentPanel} The active panel or null
33133 getActivePanel : function(){
33134 return this.activePanel;
33138 * Add the passed ContentPanel(s)
33139 * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
33140 * @return {Roo.ContentPanel} The panel added (if only one was added)
33142 add : function(panel){
33143 if(arguments.length > 1){
33144 for(var i = 0, len = arguments.length; i < len; i++) {
33145 this.add(arguments[i]);
33149 if(this.hasPanel(panel)){
33150 this.showPanel(panel);
33153 var el = panel.getEl();
33154 if(el.dom.parentNode != this.mgr.el.dom){
33155 this.mgr.el.dom.appendChild(el.dom);
33157 if(panel.setRegion){
33158 panel.setRegion(this);
33160 this.panels.add(panel);
33161 el.setStyle("position", "absolute");
33162 if(!panel.background){
33163 this.setActivePanel(panel);
33164 if(this.config.initialSize && this.panels.getCount()==1){
33165 this.resizeTo(this.config.initialSize);
33168 this.fireEvent("paneladded", this, panel);
33173 * Returns true if the panel is in this region.
33174 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
33175 * @return {Boolean}
33177 hasPanel : function(panel){
33178 if(typeof panel == "object"){ // must be panel obj
33179 panel = panel.getId();
33181 return this.getPanel(panel) ? true : false;
33185 * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
33186 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
33187 * @param {Boolean} preservePanel Overrides the config preservePanel option
33188 * @return {Roo.ContentPanel} The panel that was removed
33190 remove : function(panel, preservePanel){
33191 panel = this.getPanel(panel);
33196 this.fireEvent("beforeremove", this, panel, e);
33197 if(e.cancel === true){
33200 var panelId = panel.getId();
33201 this.panels.removeKey(panelId);
33206 * Returns the panel specified or null if it's not in this region.
33207 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
33208 * @return {Roo.ContentPanel}
33210 getPanel : function(id){
33211 if(typeof id == "object"){ // must be panel obj
33214 return this.panels.get(id);
33218 * Returns this regions position (north/south/east/west/center).
33221 getPosition: function(){
33222 return this.position;
33226 * Ext JS Library 1.1.1
33227 * Copyright(c) 2006-2007, Ext JS, LLC.
33229 * Originally Released Under LGPL - original licence link has changed is not relivant.
33232 * <script type="text/javascript">
33236 * @class Roo.LayoutRegion
33237 * @extends Roo.BasicLayoutRegion
33238 * This class represents a region in a layout manager.
33239 * @cfg {Boolean} collapsible False to disable collapsing (defaults to true)
33240 * @cfg {Boolean} collapsed True to set the initial display to collapsed (defaults to false)
33241 * @cfg {Boolean} floatable False to disable floating (defaults to true)
33242 * @cfg {Object} margins Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
33243 * @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})
33244 * @cfg {String} tabPosition "top" or "bottom" (defaults to "bottom")
33245 * @cfg {String} collapsedTitle Optional string message to display in the collapsed block of a north or south region
33246 * @cfg {Boolean} alwaysShowTabs True to always display tabs even when there is only 1 panel (defaults to false)
33247 * @cfg {Boolean} autoScroll True to enable overflow scrolling (defaults to false)
33248 * @cfg {Boolean} titlebar True to display a title bar (defaults to true)
33249 * @cfg {String} title The title for the region (overrides panel titles)
33250 * @cfg {Boolean} animate True to animate expand/collapse (defaults to false)
33251 * @cfg {Boolean} autoHide False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
33252 * @cfg {Boolean} preservePanels True to preserve removed panels so they can be readded later (defaults to false)
33253 * @cfg {Boolean} closeOnTab True to place the close icon on the tabs instead of the region titlebar (defaults to false)
33254 * @cfg {Boolean} hideTabs True to hide the tab strip (defaults to false)
33255 * @cfg {Boolean} resizeTabs True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
33256 * the space available, similar to FireFox 1.5 tabs (defaults to false)
33257 * @cfg {Number} minTabWidth The minimum tab width (defaults to 40)
33258 * @cfg {Number} preferredTabWidth The preferred tab width (defaults to 150)
33259 * @cfg {Boolean} showPin True to show a pin button
33260 * @cfg {Boolean} hidden True to start the region hidden (defaults to false)
33261 * @cfg {Boolean} hideWhenEmpty True to hide the region when it has no panels
33262 * @cfg {Boolean} disableTabTips True to disable tab tooltips
33263 * @cfg {Number} width For East/West panels
33264 * @cfg {Number} height For North/South panels
33265 * @cfg {Boolean} split To show the splitter
33266 * @cfg {Boolean} toolbar xtype configuration for a toolbar - shows on right of tabbar
33268 Roo.LayoutRegion = function(mgr, config, pos){
33269 Roo.LayoutRegion.superclass.constructor.call(this, mgr, config, pos, true);
33270 var dh = Roo.DomHelper;
33271 /** This region's container element
33272 * @type Roo.Element */
33273 this.el = dh.append(mgr.el.dom, {tag: "div", cls: "x-layout-panel x-layout-panel-" + this.position}, true);
33274 /** This region's title element
33275 * @type Roo.Element */
33277 this.titleEl = dh.append(this.el.dom, {tag: "div", unselectable: "on", cls: "x-unselectable x-layout-panel-hd x-layout-title-"+this.position, children:[
33278 {tag: "span", cls: "x-unselectable x-layout-panel-hd-text", unselectable: "on", html: " "},
33279 {tag: "div", cls: "x-unselectable x-layout-panel-hd-tools", unselectable: "on"}
33281 this.titleEl.enableDisplayMode();
33282 /** This region's title text element
33283 * @type HTMLElement */
33284 this.titleTextEl = this.titleEl.dom.firstChild;
33285 this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
33286 this.closeBtn = this.createTool(this.tools.dom, "x-layout-close");
33287 this.closeBtn.enableDisplayMode();
33288 this.closeBtn.on("click", this.closeClicked, this);
33289 this.closeBtn.hide();
33291 this.createBody(config);
33292 this.visible = true;
33293 this.collapsed = false;
33295 if(config.hideWhenEmpty){
33297 this.on("paneladded", this.validateVisibility, this);
33298 this.on("panelremoved", this.validateVisibility, this);
33300 this.applyConfig(config);
33303 Roo.extend(Roo.LayoutRegion, Roo.BasicLayoutRegion, {
33305 createBody : function(){
33306 /** This region's body element
33307 * @type Roo.Element */
33308 this.bodyEl = this.el.createChild({tag: "div", cls: "x-layout-panel-body"});
33311 applyConfig : function(c){
33312 if(c.collapsible && this.position != "center" && !this.collapsedEl){
33313 var dh = Roo.DomHelper;
33314 if(c.titlebar !== false){
33315 this.collapseBtn = this.createTool(this.tools.dom, "x-layout-collapse-"+this.position);
33316 this.collapseBtn.on("click", this.collapse, this);
33317 this.collapseBtn.enableDisplayMode();
33319 if(c.showPin === true || this.showPin){
33320 this.stickBtn = this.createTool(this.tools.dom, "x-layout-stick");
33321 this.stickBtn.enableDisplayMode();
33322 this.stickBtn.on("click", this.expand, this);
33323 this.stickBtn.hide();
33326 /** This region's collapsed element
33327 * @type Roo.Element */
33328 this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
33329 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
33331 if(c.floatable !== false){
33332 this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
33333 this.collapsedEl.on("click", this.collapseClick, this);
33336 if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
33337 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
33338 id: "message", unselectable: "on", style:{"float":"left"}});
33339 this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
33341 this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
33342 this.expandBtn.on("click", this.expand, this);
33344 if(this.collapseBtn){
33345 this.collapseBtn.setVisible(c.collapsible == true);
33347 this.cmargins = c.cmargins || this.cmargins ||
33348 (this.position == "west" || this.position == "east" ?
33349 {top: 0, left: 2, right:2, bottom: 0} :
33350 {top: 2, left: 0, right:0, bottom: 2});
33351 this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
33352 this.bottomTabs = c.tabPosition != "top";
33353 this.autoScroll = c.autoScroll || false;
33354 if(this.autoScroll){
33355 this.bodyEl.setStyle("overflow", "auto");
33357 this.bodyEl.setStyle("overflow", "hidden");
33359 //if(c.titlebar !== false){
33360 if((!c.titlebar && !c.title) || c.titlebar === false){
33361 this.titleEl.hide();
33363 this.titleEl.show();
33365 this.titleTextEl.innerHTML = c.title;
33369 this.duration = c.duration || .30;
33370 this.slideDuration = c.slideDuration || .45;
33373 this.collapse(true);
33380 * Returns true if this region is currently visible.
33381 * @return {Boolean}
33383 isVisible : function(){
33384 return this.visible;
33388 * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
33389 * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&#160;")
33391 setCollapsedTitle : function(title){
33392 title = title || " ";
33393 if(this.collapsedTitleTextEl){
33394 this.collapsedTitleTextEl.innerHTML = title;
33398 getBox : function(){
33400 if(!this.collapsed){
33401 b = this.el.getBox(false, true);
33403 b = this.collapsedEl.getBox(false, true);
33408 getMargins : function(){
33409 return this.collapsed ? this.cmargins : this.margins;
33412 highlight : function(){
33413 this.el.addClass("x-layout-panel-dragover");
33416 unhighlight : function(){
33417 this.el.removeClass("x-layout-panel-dragover");
33420 updateBox : function(box){
33422 if(!this.collapsed){
33423 this.el.dom.style.left = box.x + "px";
33424 this.el.dom.style.top = box.y + "px";
33425 this.updateBody(box.width, box.height);
33427 this.collapsedEl.dom.style.left = box.x + "px";
33428 this.collapsedEl.dom.style.top = box.y + "px";
33429 this.collapsedEl.setSize(box.width, box.height);
33432 this.tabs.autoSizeTabs();
33436 updateBody : function(w, h){
33438 this.el.setWidth(w);
33439 w -= this.el.getBorderWidth("rl");
33440 if(this.config.adjustments){
33441 w += this.config.adjustments[0];
33445 this.el.setHeight(h);
33446 h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
33447 h -= this.el.getBorderWidth("tb");
33448 if(this.config.adjustments){
33449 h += this.config.adjustments[1];
33451 this.bodyEl.setHeight(h);
33453 h = this.tabs.syncHeight(h);
33456 if(this.panelSize){
33457 w = w !== null ? w : this.panelSize.width;
33458 h = h !== null ? h : this.panelSize.height;
33460 if(this.activePanel){
33461 var el = this.activePanel.getEl();
33462 w = w !== null ? w : el.getWidth();
33463 h = h !== null ? h : el.getHeight();
33464 this.panelSize = {width: w, height: h};
33465 this.activePanel.setSize(w, h);
33467 if(Roo.isIE && this.tabs){
33468 this.tabs.el.repaint();
33473 * Returns the container element for this region.
33474 * @return {Roo.Element}
33476 getEl : function(){
33481 * Hides this region.
33484 if(!this.collapsed){
33485 this.el.dom.style.left = "-2000px";
33488 this.collapsedEl.dom.style.left = "-2000px";
33489 this.collapsedEl.hide();
33491 this.visible = false;
33492 this.fireEvent("visibilitychange", this, false);
33496 * Shows this region if it was previously hidden.
33499 if(!this.collapsed){
33502 this.collapsedEl.show();
33504 this.visible = true;
33505 this.fireEvent("visibilitychange", this, true);
33508 closeClicked : function(){
33509 if(this.activePanel){
33510 this.remove(this.activePanel);
33514 collapseClick : function(e){
33516 e.stopPropagation();
33519 e.stopPropagation();
33525 * Collapses this region.
33526 * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
33528 collapse : function(skipAnim){
33529 if(this.collapsed) return;
33530 this.collapsed = true;
33532 this.split.el.hide();
33534 if(this.config.animate && skipAnim !== true){
33535 this.fireEvent("invalidated", this);
33536 this.animateCollapse();
33538 this.el.setLocation(-20000,-20000);
33540 this.collapsedEl.show();
33541 this.fireEvent("collapsed", this);
33542 this.fireEvent("invalidated", this);
33546 animateCollapse : function(){
33551 * Expands this region if it was previously collapsed.
33552 * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
33553 * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
33555 expand : function(e, skipAnim){
33556 if(e) e.stopPropagation();
33557 if(!this.collapsed || this.el.hasActiveFx()) return;
33559 this.afterSlideIn();
33562 this.collapsed = false;
33563 if(this.config.animate && skipAnim !== true){
33564 this.animateExpand();
33568 this.split.el.show();
33570 this.collapsedEl.setLocation(-2000,-2000);
33571 this.collapsedEl.hide();
33572 this.fireEvent("invalidated", this);
33573 this.fireEvent("expanded", this);
33577 animateExpand : function(){
33581 initTabs : function()
33583 this.bodyEl.setStyle("overflow", "hidden");
33584 var ts = new Roo.TabPanel(
33587 tabPosition: this.bottomTabs ? 'bottom' : 'top',
33588 disableTooltips: this.config.disableTabTips,
33589 toolbar : this.config.toolbar
33592 if(this.config.hideTabs){
33593 ts.stripWrap.setDisplayed(false);
33596 ts.resizeTabs = this.config.resizeTabs === true;
33597 ts.minTabWidth = this.config.minTabWidth || 40;
33598 ts.maxTabWidth = this.config.maxTabWidth || 250;
33599 ts.preferredTabWidth = this.config.preferredTabWidth || 150;
33600 ts.monitorResize = false;
33601 ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
33602 ts.bodyEl.addClass('x-layout-tabs-body');
33603 this.panels.each(this.initPanelAsTab, this);
33606 initPanelAsTab : function(panel){
33607 var ti = this.tabs.addTab(panel.getEl().id, panel.getTitle(), null,
33608 this.config.closeOnTab && panel.isClosable());
33609 if(panel.tabTip !== undefined){
33610 ti.setTooltip(panel.tabTip);
33612 ti.on("activate", function(){
33613 this.setActivePanel(panel);
33615 if(this.config.closeOnTab){
33616 ti.on("beforeclose", function(t, e){
33618 this.remove(panel);
33624 updatePanelTitle : function(panel, title){
33625 if(this.activePanel == panel){
33626 this.updateTitle(title);
33629 var ti = this.tabs.getTab(panel.getEl().id);
33631 if(panel.tabTip !== undefined){
33632 ti.setTooltip(panel.tabTip);
33637 updateTitle : function(title){
33638 if(this.titleTextEl && !this.config.title){
33639 this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : " ");
33643 setActivePanel : function(panel){
33644 panel = this.getPanel(panel);
33645 if(this.activePanel && this.activePanel != panel){
33646 this.activePanel.setActiveState(false);
33648 this.activePanel = panel;
33649 panel.setActiveState(true);
33650 if(this.panelSize){
33651 panel.setSize(this.panelSize.width, this.panelSize.height);
33654 this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
33656 this.updateTitle(panel.getTitle());
33658 this.fireEvent("invalidated", this);
33660 this.fireEvent("panelactivated", this, panel);
33664 * Shows the specified panel.
33665 * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
33666 * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
33668 showPanel : function(panel){
33669 if(panel = this.getPanel(panel)){
33671 var tab = this.tabs.getTab(panel.getEl().id);
33672 if(tab.isHidden()){
33673 this.tabs.unhideTab(tab.id);
33677 this.setActivePanel(panel);
33684 * Get the active panel for this region.
33685 * @return {Roo.ContentPanel} The active panel or null
33687 getActivePanel : function(){
33688 return this.activePanel;
33691 validateVisibility : function(){
33692 if(this.panels.getCount() < 1){
33693 this.updateTitle(" ");
33694 this.closeBtn.hide();
33697 if(!this.isVisible()){
33704 * Adds the passed ContentPanel(s) to this region.
33705 * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
33706 * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
33708 add : function(panel){
33709 if(arguments.length > 1){
33710 for(var i = 0, len = arguments.length; i < len; i++) {
33711 this.add(arguments[i]);
33715 if(this.hasPanel(panel)){
33716 this.showPanel(panel);
33719 panel.setRegion(this);
33720 this.panels.add(panel);
33721 if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
33722 this.bodyEl.dom.appendChild(panel.getEl().dom);
33723 if(panel.background !== true){
33724 this.setActivePanel(panel);
33726 this.fireEvent("paneladded", this, panel);
33732 this.initPanelAsTab(panel);
33734 if(panel.background !== true){
33735 this.tabs.activate(panel.getEl().id);
33737 this.fireEvent("paneladded", this, panel);
33742 * Hides the tab for the specified panel.
33743 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
33745 hidePanel : function(panel){
33746 if(this.tabs && (panel = this.getPanel(panel))){
33747 this.tabs.hideTab(panel.getEl().id);
33752 * Unhides the tab for a previously hidden panel.
33753 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
33755 unhidePanel : function(panel){
33756 if(this.tabs && (panel = this.getPanel(panel))){
33757 this.tabs.unhideTab(panel.getEl().id);
33761 clearPanels : function(){
33762 while(this.panels.getCount() > 0){
33763 this.remove(this.panels.first());
33768 * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
33769 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
33770 * @param {Boolean} preservePanel Overrides the config preservePanel option
33771 * @return {Roo.ContentPanel} The panel that was removed
33773 remove : function(panel, preservePanel){
33774 panel = this.getPanel(panel);
33779 this.fireEvent("beforeremove", this, panel, e);
33780 if(e.cancel === true){
33783 preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
33784 var panelId = panel.getId();
33785 this.panels.removeKey(panelId);
33787 document.body.appendChild(panel.getEl().dom);
33790 this.tabs.removeTab(panel.getEl().id);
33791 }else if (!preservePanel){
33792 this.bodyEl.dom.removeChild(panel.getEl().dom);
33794 if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
33795 var p = this.panels.first();
33796 var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
33797 tempEl.appendChild(p.getEl().dom);
33798 this.bodyEl.update("");
33799 this.bodyEl.dom.appendChild(p.getEl().dom);
33801 this.updateTitle(p.getTitle());
33803 this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
33804 this.setActivePanel(p);
33806 panel.setRegion(null);
33807 if(this.activePanel == panel){
33808 this.activePanel = null;
33810 if(this.config.autoDestroy !== false && preservePanel !== true){
33811 try{panel.destroy();}catch(e){}
33813 this.fireEvent("panelremoved", this, panel);
33818 * Returns the TabPanel component used by this region
33819 * @return {Roo.TabPanel}
33821 getTabs : function(){
33825 createTool : function(parentEl, className){
33826 var btn = Roo.DomHelper.append(parentEl, {tag: "div", cls: "x-layout-tools-button",
33827 children: [{tag: "div", cls: "x-layout-tools-button-inner " + className, html: " "}]}, true);
33828 btn.addClassOnOver("x-layout-tools-button-over");
33833 * Ext JS Library 1.1.1
33834 * Copyright(c) 2006-2007, Ext JS, LLC.
33836 * Originally Released Under LGPL - original licence link has changed is not relivant.
33839 * <script type="text/javascript">
33845 * @class Roo.SplitLayoutRegion
33846 * @extends Roo.LayoutRegion
33847 * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
33849 Roo.SplitLayoutRegion = function(mgr, config, pos, cursor){
33850 this.cursor = cursor;
33851 Roo.SplitLayoutRegion.superclass.constructor.call(this, mgr, config, pos);
33854 Roo.extend(Roo.SplitLayoutRegion, Roo.LayoutRegion, {
33855 splitTip : "Drag to resize.",
33856 collapsibleSplitTip : "Drag to resize. Double click to hide.",
33857 useSplitTips : false,
33859 applyConfig : function(config){
33860 Roo.SplitLayoutRegion.superclass.applyConfig.call(this, config);
33863 var splitEl = Roo.DomHelper.append(this.mgr.el.dom,
33864 {tag: "div", id: this.el.id + "-split", cls: "x-layout-split x-layout-split-"+this.position, html: " "});
33865 /** The SplitBar for this region
33866 * @type Roo.SplitBar */
33867 this.split = new Roo.SplitBar(splitEl, this.el, this.orientation);
33868 this.split.on("moved", this.onSplitMove, this);
33869 this.split.useShim = config.useShim === true;
33870 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
33871 if(this.useSplitTips){
33872 this.split.el.dom.title = config.collapsible ? this.collapsibleSplitTip : this.splitTip;
33874 if(config.collapsible){
33875 this.split.el.on("dblclick", this.collapse, this);
33878 if(typeof config.minSize != "undefined"){
33879 this.split.minSize = config.minSize;
33881 if(typeof config.maxSize != "undefined"){
33882 this.split.maxSize = config.maxSize;
33884 if(config.hideWhenEmpty || config.hidden || config.collapsed){
33885 this.hideSplitter();
33890 getHMaxSize : function(){
33891 var cmax = this.config.maxSize || 10000;
33892 var center = this.mgr.getRegion("center");
33893 return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
33896 getVMaxSize : function(){
33897 var cmax = this.config.maxSize || 10000;
33898 var center = this.mgr.getRegion("center");
33899 return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
33902 onSplitMove : function(split, newSize){
33903 this.fireEvent("resized", this, newSize);
33907 * Returns the {@link Roo.SplitBar} for this region.
33908 * @return {Roo.SplitBar}
33910 getSplitBar : function(){
33915 this.hideSplitter();
33916 Roo.SplitLayoutRegion.superclass.hide.call(this);
33919 hideSplitter : function(){
33921 this.split.el.setLocation(-2000,-2000);
33922 this.split.el.hide();
33928 this.split.el.show();
33930 Roo.SplitLayoutRegion.superclass.show.call(this);
33933 beforeSlide: function(){
33934 if(Roo.isGecko){// firefox overflow auto bug workaround
33935 this.bodyEl.clip();
33936 if(this.tabs) this.tabs.bodyEl.clip();
33937 if(this.activePanel){
33938 this.activePanel.getEl().clip();
33940 if(this.activePanel.beforeSlide){
33941 this.activePanel.beforeSlide();
33947 afterSlide : function(){
33948 if(Roo.isGecko){// firefox overflow auto bug workaround
33949 this.bodyEl.unclip();
33950 if(this.tabs) this.tabs.bodyEl.unclip();
33951 if(this.activePanel){
33952 this.activePanel.getEl().unclip();
33953 if(this.activePanel.afterSlide){
33954 this.activePanel.afterSlide();
33960 initAutoHide : function(){
33961 if(this.autoHide !== false){
33962 if(!this.autoHideHd){
33963 var st = new Roo.util.DelayedTask(this.slideIn, this);
33964 this.autoHideHd = {
33965 "mouseout": function(e){
33966 if(!e.within(this.el, true)){
33970 "mouseover" : function(e){
33976 this.el.on(this.autoHideHd);
33980 clearAutoHide : function(){
33981 if(this.autoHide !== false){
33982 this.el.un("mouseout", this.autoHideHd.mouseout);
33983 this.el.un("mouseover", this.autoHideHd.mouseover);
33987 clearMonitor : function(){
33988 Roo.get(document).un("click", this.slideInIf, this);
33991 // these names are backwards but not changed for compat
33992 slideOut : function(){
33993 if(this.isSlid || this.el.hasActiveFx()){
33996 this.isSlid = true;
33997 if(this.collapseBtn){
33998 this.collapseBtn.hide();
34000 this.closeBtnState = this.closeBtn.getStyle('display');
34001 this.closeBtn.hide();
34003 this.stickBtn.show();
34006 this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
34007 this.beforeSlide();
34008 this.el.setStyle("z-index", 10001);
34009 this.el.slideIn(this.getSlideAnchor(), {
34010 callback: function(){
34012 this.initAutoHide();
34013 Roo.get(document).on("click", this.slideInIf, this);
34014 this.fireEvent("slideshow", this);
34021 afterSlideIn : function(){
34022 this.clearAutoHide();
34023 this.isSlid = false;
34024 this.clearMonitor();
34025 this.el.setStyle("z-index", "");
34026 if(this.collapseBtn){
34027 this.collapseBtn.show();
34029 this.closeBtn.setStyle('display', this.closeBtnState);
34031 this.stickBtn.hide();
34033 this.fireEvent("slidehide", this);
34036 slideIn : function(cb){
34037 if(!this.isSlid || this.el.hasActiveFx()){
34041 this.isSlid = false;
34042 this.beforeSlide();
34043 this.el.slideOut(this.getSlideAnchor(), {
34044 callback: function(){
34045 this.el.setLeftTop(-10000, -10000);
34047 this.afterSlideIn();
34055 slideInIf : function(e){
34056 if(!e.within(this.el)){
34061 animateCollapse : function(){
34062 this.beforeSlide();
34063 this.el.setStyle("z-index", 20000);
34064 var anchor = this.getSlideAnchor();
34065 this.el.slideOut(anchor, {
34066 callback : function(){
34067 this.el.setStyle("z-index", "");
34068 this.collapsedEl.slideIn(anchor, {duration:.3});
34070 this.el.setLocation(-10000,-10000);
34072 this.fireEvent("collapsed", this);
34079 animateExpand : function(){
34080 this.beforeSlide();
34081 this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
34082 this.el.setStyle("z-index", 20000);
34083 this.collapsedEl.hide({
34086 this.el.slideIn(this.getSlideAnchor(), {
34087 callback : function(){
34088 this.el.setStyle("z-index", "");
34091 this.split.el.show();
34093 this.fireEvent("invalidated", this);
34094 this.fireEvent("expanded", this);
34122 getAnchor : function(){
34123 return this.anchors[this.position];
34126 getCollapseAnchor : function(){
34127 return this.canchors[this.position];
34130 getSlideAnchor : function(){
34131 return this.sanchors[this.position];
34134 getAlignAdj : function(){
34135 var cm = this.cmargins;
34136 switch(this.position){
34152 getExpandAdj : function(){
34153 var c = this.collapsedEl, cm = this.cmargins;
34154 switch(this.position){
34156 return [-(cm.right+c.getWidth()+cm.left), 0];
34159 return [cm.right+c.getWidth()+cm.left, 0];
34162 return [0, -(cm.top+cm.bottom+c.getHeight())];
34165 return [0, cm.top+cm.bottom+c.getHeight()];
34171 * Ext JS Library 1.1.1
34172 * Copyright(c) 2006-2007, Ext JS, LLC.
34174 * Originally Released Under LGPL - original licence link has changed is not relivant.
34177 * <script type="text/javascript">
34180 * These classes are private internal classes
34182 Roo.CenterLayoutRegion = function(mgr, config){
34183 Roo.LayoutRegion.call(this, mgr, config, "center");
34184 this.visible = true;
34185 this.minWidth = config.minWidth || 20;
34186 this.minHeight = config.minHeight || 20;
34189 Roo.extend(Roo.CenterLayoutRegion, Roo.LayoutRegion, {
34191 // center panel can't be hidden
34195 // center panel can't be hidden
34198 getMinWidth: function(){
34199 return this.minWidth;
34202 getMinHeight: function(){
34203 return this.minHeight;
34208 Roo.NorthLayoutRegion = function(mgr, config){
34209 Roo.LayoutRegion.call(this, mgr, config, "north", "n-resize");
34211 this.split.placement = Roo.SplitBar.TOP;
34212 this.split.orientation = Roo.SplitBar.VERTICAL;
34213 this.split.el.addClass("x-layout-split-v");
34215 var size = config.initialSize || config.height;
34216 if(typeof size != "undefined"){
34217 this.el.setHeight(size);
34220 Roo.extend(Roo.NorthLayoutRegion, Roo.SplitLayoutRegion, {
34221 orientation: Roo.SplitBar.VERTICAL,
34222 getBox : function(){
34223 if(this.collapsed){
34224 return this.collapsedEl.getBox();
34226 var box = this.el.getBox();
34228 box.height += this.split.el.getHeight();
34233 updateBox : function(box){
34234 if(this.split && !this.collapsed){
34235 box.height -= this.split.el.getHeight();
34236 this.split.el.setLeft(box.x);
34237 this.split.el.setTop(box.y+box.height);
34238 this.split.el.setWidth(box.width);
34240 if(this.collapsed){
34241 this.updateBody(box.width, null);
34243 Roo.LayoutRegion.prototype.updateBox.call(this, box);
34247 Roo.SouthLayoutRegion = function(mgr, config){
34248 Roo.SplitLayoutRegion.call(this, mgr, config, "south", "s-resize");
34250 this.split.placement = Roo.SplitBar.BOTTOM;
34251 this.split.orientation = Roo.SplitBar.VERTICAL;
34252 this.split.el.addClass("x-layout-split-v");
34254 var size = config.initialSize || config.height;
34255 if(typeof size != "undefined"){
34256 this.el.setHeight(size);
34259 Roo.extend(Roo.SouthLayoutRegion, Roo.SplitLayoutRegion, {
34260 orientation: Roo.SplitBar.VERTICAL,
34261 getBox : function(){
34262 if(this.collapsed){
34263 return this.collapsedEl.getBox();
34265 var box = this.el.getBox();
34267 var sh = this.split.el.getHeight();
34274 updateBox : function(box){
34275 if(this.split && !this.collapsed){
34276 var sh = this.split.el.getHeight();
34279 this.split.el.setLeft(box.x);
34280 this.split.el.setTop(box.y-sh);
34281 this.split.el.setWidth(box.width);
34283 if(this.collapsed){
34284 this.updateBody(box.width, null);
34286 Roo.LayoutRegion.prototype.updateBox.call(this, box);
34290 Roo.EastLayoutRegion = function(mgr, config){
34291 Roo.SplitLayoutRegion.call(this, mgr, config, "east", "e-resize");
34293 this.split.placement = Roo.SplitBar.RIGHT;
34294 this.split.orientation = Roo.SplitBar.HORIZONTAL;
34295 this.split.el.addClass("x-layout-split-h");
34297 var size = config.initialSize || config.width;
34298 if(typeof size != "undefined"){
34299 this.el.setWidth(size);
34302 Roo.extend(Roo.EastLayoutRegion, Roo.SplitLayoutRegion, {
34303 orientation: Roo.SplitBar.HORIZONTAL,
34304 getBox : function(){
34305 if(this.collapsed){
34306 return this.collapsedEl.getBox();
34308 var box = this.el.getBox();
34310 var sw = this.split.el.getWidth();
34317 updateBox : function(box){
34318 if(this.split && !this.collapsed){
34319 var sw = this.split.el.getWidth();
34321 this.split.el.setLeft(box.x);
34322 this.split.el.setTop(box.y);
34323 this.split.el.setHeight(box.height);
34326 if(this.collapsed){
34327 this.updateBody(null, box.height);
34329 Roo.LayoutRegion.prototype.updateBox.call(this, box);
34333 Roo.WestLayoutRegion = function(mgr, config){
34334 Roo.SplitLayoutRegion.call(this, mgr, config, "west", "w-resize");
34336 this.split.placement = Roo.SplitBar.LEFT;
34337 this.split.orientation = Roo.SplitBar.HORIZONTAL;
34338 this.split.el.addClass("x-layout-split-h");
34340 var size = config.initialSize || config.width;
34341 if(typeof size != "undefined"){
34342 this.el.setWidth(size);
34345 Roo.extend(Roo.WestLayoutRegion, Roo.SplitLayoutRegion, {
34346 orientation: Roo.SplitBar.HORIZONTAL,
34347 getBox : function(){
34348 if(this.collapsed){
34349 return this.collapsedEl.getBox();
34351 var box = this.el.getBox();
34353 box.width += this.split.el.getWidth();
34358 updateBox : function(box){
34359 if(this.split && !this.collapsed){
34360 var sw = this.split.el.getWidth();
34362 this.split.el.setLeft(box.x+box.width);
34363 this.split.el.setTop(box.y);
34364 this.split.el.setHeight(box.height);
34366 if(this.collapsed){
34367 this.updateBody(null, box.height);
34369 Roo.LayoutRegion.prototype.updateBox.call(this, box);
34374 * Ext JS Library 1.1.1
34375 * Copyright(c) 2006-2007, Ext JS, LLC.
34377 * Originally Released Under LGPL - original licence link has changed is not relivant.
34380 * <script type="text/javascript">
34385 * Private internal class for reading and applying state
34387 Roo.LayoutStateManager = function(layout){
34388 // default empty state
34397 Roo.LayoutStateManager.prototype = {
34398 init : function(layout, provider){
34399 this.provider = provider;
34400 var state = provider.get(layout.id+"-layout-state");
34402 var wasUpdating = layout.isUpdating();
34404 layout.beginUpdate();
34406 for(var key in state){
34407 if(typeof state[key] != "function"){
34408 var rstate = state[key];
34409 var r = layout.getRegion(key);
34412 r.resizeTo(rstate.size);
34414 if(rstate.collapsed == true){
34417 r.expand(null, true);
34423 layout.endUpdate();
34425 this.state = state;
34427 this.layout = layout;
34428 layout.on("regionresized", this.onRegionResized, this);
34429 layout.on("regioncollapsed", this.onRegionCollapsed, this);
34430 layout.on("regionexpanded", this.onRegionExpanded, this);
34433 storeState : function(){
34434 this.provider.set(this.layout.id+"-layout-state", this.state);
34437 onRegionResized : function(region, newSize){
34438 this.state[region.getPosition()].size = newSize;
34442 onRegionCollapsed : function(region){
34443 this.state[region.getPosition()].collapsed = true;
34447 onRegionExpanded : function(region){
34448 this.state[region.getPosition()].collapsed = false;
34453 * Ext JS Library 1.1.1
34454 * Copyright(c) 2006-2007, Ext JS, LLC.
34456 * Originally Released Under LGPL - original licence link has changed is not relivant.
34459 * <script type="text/javascript">
34462 * @class Roo.ContentPanel
34463 * @extends Roo.util.Observable
34464 * A basic ContentPanel element.
34465 * @cfg {Boolean} fitToFrame True for this panel to adjust its size to fit when the region resizes (defaults to false)
34466 * @cfg {Boolean} fitContainer When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container (defaults to false)
34467 * @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
34468 * @cfg {Boolean} closable True if the panel can be closed/removed
34469 * @cfg {Boolean} background True if the panel should not be activated when it is added (defaults to false)
34470 * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
34471 * @cfg {Toolbar} toolbar A toolbar for this panel
34472 * @cfg {Boolean} autoScroll True to scroll overflow in this panel (use with {@link #fitToFrame})
34473 * @cfg {String} title The title for this panel
34474 * @cfg {Array} adjustments Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
34475 * @cfg {String} url Calls {@link #setUrl} with this value
34476 * @cfg {String} region (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
34477 * @cfg {String/Object} params When used with {@link #url}, calls {@link #setUrl} with this value
34478 * @cfg {Boolean} loadOnce When used with {@link #url}, calls {@link #setUrl} with this value
34479 * @cfg {String} content Raw content to fill content panel with (uses setContent on construction.)
34482 * Create a new ContentPanel.
34483 * @param {String/HTMLElement/Roo.Element} el The container element for this panel
34484 * @param {String/Object} config A string to set only the title or a config object
34485 * @param {String} content (optional) Set the HTML content for this panel
34486 * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
34488 Roo.ContentPanel = function(el, config, content){
34492 if(el.autoCreate || el.xtype){ // xtype is available if this is called from factory
34496 if (config && config.parentLayout) {
34497 el = config.parentLayout.el.createChild();
34500 if(el.autoCreate){ // xtype is available if this is called from factory
34504 this.el = Roo.get(el);
34505 if(!this.el && config && config.autoCreate){
34506 if(typeof config.autoCreate == "object"){
34507 if(!config.autoCreate.id){
34508 config.autoCreate.id = config.id||el;
34510 this.el = Roo.DomHelper.append(document.body,
34511 config.autoCreate, true);
34513 this.el = Roo.DomHelper.append(document.body,
34514 {tag: "div", cls: "x-layout-inactive-content", id: config.id||el}, true);
34517 this.closable = false;
34518 this.loaded = false;
34519 this.active = false;
34520 if(typeof config == "string"){
34521 this.title = config;
34523 Roo.apply(this, config);
34526 if (this.toolbar && !this.toolbar.el && this.toolbar.xtype) {
34527 this.wrapEl = this.el.wrap();
34528 this.toolbar.container = this.el.insertSibling(false, 'before');
34529 this.toolbar = new Roo.Toolbar(this.toolbar);
34532 // xtype created footer. - not sure if will work as we normally have to render first..
34533 if (this.footer && !this.footer.el && this.footer.xtype) {
34534 if (!this.wrapEl) {
34535 this.wrapEl = this.el.wrap();
34538 this.footer.container = this.wrapEl.createChild();
34540 this.footer = Roo.factory(this.footer, Roo);
34545 this.resizeEl = Roo.get(this.resizeEl, true);
34547 this.resizeEl = this.el;
34549 // handle view.xtype
34557 * Fires when this panel is activated.
34558 * @param {Roo.ContentPanel} this
34562 * @event deactivate
34563 * Fires when this panel is activated.
34564 * @param {Roo.ContentPanel} this
34566 "deactivate" : true,
34570 * Fires when this panel is resized if fitToFrame is true.
34571 * @param {Roo.ContentPanel} this
34572 * @param {Number} width The width after any component adjustments
34573 * @param {Number} height The height after any component adjustments
34579 * Fires when this tab is created
34580 * @param {Roo.ContentPanel} this
34591 if(this.autoScroll){
34592 this.resizeEl.setStyle("overflow", "auto");
34594 // fix randome scrolling
34595 this.el.on('scroll', function() {
34596 Roo.log('fix random scolling');
34597 this.scrollTo('top',0);
34600 content = content || this.content;
34602 this.setContent(content);
34604 if(config && config.url){
34605 this.setUrl(this.url, this.params, this.loadOnce);
34610 Roo.ContentPanel.superclass.constructor.call(this);
34612 if (this.view && typeof(this.view.xtype) != 'undefined') {
34613 this.view.el = this.el.appendChild(document.createElement("div"));
34614 this.view = Roo.factory(this.view);
34615 this.view.render && this.view.render(false, '');
34619 this.fireEvent('render', this);
34622 Roo.extend(Roo.ContentPanel, Roo.util.Observable, {
34624 setRegion : function(region){
34625 this.region = region;
34627 this.el.replaceClass("x-layout-inactive-content", "x-layout-active-content");
34629 this.el.replaceClass("x-layout-active-content", "x-layout-inactive-content");
34634 * Returns the toolbar for this Panel if one was configured.
34635 * @return {Roo.Toolbar}
34637 getToolbar : function(){
34638 return this.toolbar;
34641 setActiveState : function(active){
34642 this.active = active;
34644 this.fireEvent("deactivate", this);
34646 this.fireEvent("activate", this);
34650 * Updates this panel's element
34651 * @param {String} content The new content
34652 * @param {Boolean} loadScripts (optional) true to look for and process scripts
34654 setContent : function(content, loadScripts){
34655 this.el.update(content, loadScripts);
34658 ignoreResize : function(w, h){
34659 if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
34662 this.lastSize = {width: w, height: h};
34667 * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
34668 * @return {Roo.UpdateManager} The UpdateManager
34670 getUpdateManager : function(){
34671 return this.el.getUpdateManager();
34674 * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
34675 * @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:
34678 url: "your-url.php",
34679 params: {param1: "foo", param2: "bar"}, // or a URL encoded string
34680 callback: yourFunction,
34681 scope: yourObject, //(optional scope)
34684 text: "Loading...",
34689 * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
34690 * 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.
34691 * @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}
34692 * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
34693 * @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.
34694 * @return {Roo.ContentPanel} this
34697 var um = this.el.getUpdateManager();
34698 um.update.apply(um, arguments);
34704 * 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.
34705 * @param {String/Function} url The URL to load the content from or a function to call to get the URL
34706 * @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)
34707 * @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)
34708 * @return {Roo.UpdateManager} The UpdateManager
34710 setUrl : function(url, params, loadOnce){
34711 if(this.refreshDelegate){
34712 this.removeListener("activate", this.refreshDelegate);
34714 this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
34715 this.on("activate", this.refreshDelegate);
34716 return this.el.getUpdateManager();
34719 _handleRefresh : function(url, params, loadOnce){
34720 if(!loadOnce || !this.loaded){
34721 var updater = this.el.getUpdateManager();
34722 updater.update(url, params, this._setLoaded.createDelegate(this));
34726 _setLoaded : function(){
34727 this.loaded = true;
34731 * Returns this panel's id
34734 getId : function(){
34739 * Returns this panel's element - used by regiosn to add.
34740 * @return {Roo.Element}
34742 getEl : function(){
34743 return this.wrapEl || this.el;
34746 adjustForComponents : function(width, height)
34748 //Roo.log('adjustForComponents ');
34749 if(this.resizeEl != this.el){
34750 width -= this.el.getFrameWidth('lr');
34751 height -= this.el.getFrameWidth('tb');
34754 var te = this.toolbar.getEl();
34755 height -= te.getHeight();
34756 te.setWidth(width);
34759 var te = this.footer.getEl();
34760 Roo.log("footer:" + te.getHeight());
34762 height -= te.getHeight();
34763 te.setWidth(width);
34767 if(this.adjustments){
34768 width += this.adjustments[0];
34769 height += this.adjustments[1];
34771 return {"width": width, "height": height};
34774 setSize : function(width, height){
34775 if(this.fitToFrame && !this.ignoreResize(width, height)){
34776 if(this.fitContainer && this.resizeEl != this.el){
34777 this.el.setSize(width, height);
34779 var size = this.adjustForComponents(width, height);
34780 this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
34781 this.fireEvent('resize', this, size.width, size.height);
34786 * Returns this panel's title
34789 getTitle : function(){
34794 * Set this panel's title
34795 * @param {String} title
34797 setTitle : function(title){
34798 this.title = title;
34800 this.region.updatePanelTitle(this, title);
34805 * Returns true is this panel was configured to be closable
34806 * @return {Boolean}
34808 isClosable : function(){
34809 return this.closable;
34812 beforeSlide : function(){
34814 this.resizeEl.clip();
34817 afterSlide : function(){
34819 this.resizeEl.unclip();
34823 * Force a content refresh from the URL specified in the {@link #setUrl} method.
34824 * Will fail silently if the {@link #setUrl} method has not been called.
34825 * This does not activate the panel, just updates its content.
34827 refresh : function(){
34828 if(this.refreshDelegate){
34829 this.loaded = false;
34830 this.refreshDelegate();
34835 * Destroys this panel
34837 destroy : function(){
34838 this.el.removeAllListeners();
34839 var tempEl = document.createElement("span");
34840 tempEl.appendChild(this.el.dom);
34841 tempEl.innerHTML = "";
34847 * form - if the content panel contains a form - this is a reference to it.
34848 * @type {Roo.form.Form}
34852 * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
34853 * This contains a reference to it.
34859 * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
34869 * @param {Object} cfg Xtype definition of item to add.
34872 addxtype : function(cfg) {
34874 if (cfg.xtype.match(/^Form$/)) {
34877 //if (this.footer) {
34878 // el = this.footer.container.insertSibling(false, 'before');
34880 el = this.el.createChild();
34883 this.form = new Roo.form.Form(cfg);
34886 if ( this.form.allItems.length) this.form.render(el.dom);
34889 // should only have one of theses..
34890 if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
34891 // views.. should not be just added - used named prop 'view''
34893 cfg.el = this.el.appendChild(document.createElement("div"));
34896 var ret = new Roo.factory(cfg);
34898 ret.render && ret.render(false, ''); // render blank..
34907 * @class Roo.GridPanel
34908 * @extends Roo.ContentPanel
34910 * Create a new GridPanel.
34911 * @param {Roo.grid.Grid} grid The grid for this panel
34912 * @param {String/Object} config A string to set only the panel's title, or a config object
34914 Roo.GridPanel = function(grid, config){
34917 this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
34918 {tag: "div", cls: "x-layout-grid-wrapper x-layout-inactive-content"}, true);
34920 this.wrapper.dom.appendChild(grid.getGridEl().dom);
34922 Roo.GridPanel.superclass.constructor.call(this, this.wrapper, config);
34925 this.toolbar.el.insertBefore(this.wrapper.dom.firstChild);
34927 // xtype created footer. - not sure if will work as we normally have to render first..
34928 if (this.footer && !this.footer.el && this.footer.xtype) {
34930 this.footer.container = this.grid.getView().getFooterPanel(true);
34931 this.footer.dataSource = this.grid.dataSource;
34932 this.footer = Roo.factory(this.footer, Roo);
34936 grid.monitorWindowResize = false; // turn off autosizing
34937 grid.autoHeight = false;
34938 grid.autoWidth = false;
34940 this.grid.getGridEl().replaceClass("x-layout-inactive-content", "x-layout-component-panel");
34943 Roo.extend(Roo.GridPanel, Roo.ContentPanel, {
34944 getId : function(){
34945 return this.grid.id;
34949 * Returns the grid for this panel
34950 * @return {Roo.grid.Grid}
34952 getGrid : function(){
34956 setSize : function(width, height){
34957 if(!this.ignoreResize(width, height)){
34958 var grid = this.grid;
34959 var size = this.adjustForComponents(width, height);
34960 grid.getGridEl().setSize(size.width, size.height);
34965 beforeSlide : function(){
34966 this.grid.getView().scroller.clip();
34969 afterSlide : function(){
34970 this.grid.getView().scroller.unclip();
34973 destroy : function(){
34974 this.grid.destroy();
34976 Roo.GridPanel.superclass.destroy.call(this);
34982 * @class Roo.NestedLayoutPanel
34983 * @extends Roo.ContentPanel
34985 * Create a new NestedLayoutPanel.
34988 * @param {Roo.BorderLayout} layout The layout for this panel
34989 * @param {String/Object} config A string to set only the title or a config object
34991 Roo.NestedLayoutPanel = function(layout, config)
34993 // construct with only one argument..
34994 /* FIXME - implement nicer consturctors
34995 if (layout.layout) {
34997 layout = config.layout;
34998 delete config.layout;
35000 if (layout.xtype && !layout.getEl) {
35001 // then layout needs constructing..
35002 layout = Roo.factory(layout, Roo);
35007 Roo.NestedLayoutPanel.superclass.constructor.call(this, layout.getEl(), config);
35009 layout.monitorWindowResize = false; // turn off autosizing
35010 this.layout = layout;
35011 this.layout.getEl().addClass("x-layout-nested-layout");
35018 Roo.extend(Roo.NestedLayoutPanel, Roo.ContentPanel, {
35020 setSize : function(width, height){
35021 if(!this.ignoreResize(width, height)){
35022 var size = this.adjustForComponents(width, height);
35023 var el = this.layout.getEl();
35024 el.setSize(size.width, size.height);
35025 var touch = el.dom.offsetWidth;
35026 this.layout.layout();
35027 // ie requires a double layout on the first pass
35028 if(Roo.isIE && !this.initialized){
35029 this.initialized = true;
35030 this.layout.layout();
35035 // activate all subpanels if not currently active..
35037 setActiveState : function(active){
35038 this.active = active;
35040 this.fireEvent("deactivate", this);
35044 this.fireEvent("activate", this);
35045 // not sure if this should happen before or after..
35046 if (!this.layout) {
35047 return; // should not happen..
35050 for (var r in this.layout.regions) {
35051 reg = this.layout.getRegion(r);
35052 if (reg.getActivePanel()) {
35053 //reg.showPanel(reg.getActivePanel()); // force it to activate..
35054 reg.setActivePanel(reg.getActivePanel());
35057 if (!reg.panels.length) {
35060 reg.showPanel(reg.getPanel(0));
35069 * Returns the nested BorderLayout for this panel
35070 * @return {Roo.BorderLayout}
35072 getLayout : function(){
35073 return this.layout;
35077 * Adds a xtype elements to the layout of the nested panel
35081 xtype : 'ContentPanel',
35088 xtype : 'NestedLayoutPanel',
35094 items : [ ... list of content panels or nested layout panels.. ]
35098 * @param {Object} cfg Xtype definition of item to add.
35100 addxtype : function(cfg) {
35101 return this.layout.addxtype(cfg);
35106 Roo.ScrollPanel = function(el, config, content){
35107 config = config || {};
35108 config.fitToFrame = true;
35109 Roo.ScrollPanel.superclass.constructor.call(this, el, config, content);
35111 this.el.dom.style.overflow = "hidden";
35112 var wrap = this.el.wrap({cls: "x-scroller x-layout-inactive-content"});
35113 this.el.removeClass("x-layout-inactive-content");
35114 this.el.on("mousewheel", this.onWheel, this);
35116 var up = wrap.createChild({cls: "x-scroller-up", html: " "}, this.el.dom);
35117 var down = wrap.createChild({cls: "x-scroller-down", html: " "});
35118 up.unselectable(); down.unselectable();
35119 up.on("click", this.scrollUp, this);
35120 down.on("click", this.scrollDown, this);
35121 up.addClassOnOver("x-scroller-btn-over");
35122 down.addClassOnOver("x-scroller-btn-over");
35123 up.addClassOnClick("x-scroller-btn-click");
35124 down.addClassOnClick("x-scroller-btn-click");
35125 this.adjustments = [0, -(up.getHeight() + down.getHeight())];
35127 this.resizeEl = this.el;
35128 this.el = wrap; this.up = up; this.down = down;
35131 Roo.extend(Roo.ScrollPanel, Roo.ContentPanel, {
35133 wheelIncrement : 5,
35134 scrollUp : function(){
35135 this.resizeEl.scroll("up", this.increment, {callback: this.afterScroll, scope: this});
35138 scrollDown : function(){
35139 this.resizeEl.scroll("down", this.increment, {callback: this.afterScroll, scope: this});
35142 afterScroll : function(){
35143 var el = this.resizeEl;
35144 var t = el.dom.scrollTop, h = el.dom.scrollHeight, ch = el.dom.clientHeight;
35145 this.up[t == 0 ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
35146 this.down[h - t <= ch ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
35149 setSize : function(){
35150 Roo.ScrollPanel.superclass.setSize.apply(this, arguments);
35151 this.afterScroll();
35154 onWheel : function(e){
35155 var d = e.getWheelDelta();
35156 this.resizeEl.dom.scrollTop -= (d*this.wheelIncrement);
35157 this.afterScroll();
35161 setContent : function(content, loadScripts){
35162 this.resizeEl.update(content, loadScripts);
35176 * @class Roo.TreePanel
35177 * @extends Roo.ContentPanel
35179 * Create a new TreePanel. - defaults to fit/scoll contents.
35180 * @param {String/Object} config A string to set only the panel's title, or a config object
35181 * @cfg {Roo.tree.TreePanel} tree The tree TreePanel, with config etc.
35183 Roo.TreePanel = function(config){
35184 var el = config.el;
35185 var tree = config.tree;
35186 delete config.tree;
35187 delete config.el; // hopefull!
35189 // wrapper for IE7 strict & safari scroll issue
35191 var treeEl = el.createChild();
35192 config.resizeEl = treeEl;
35196 Roo.TreePanel.superclass.constructor.call(this, el, config);
35199 this.tree = new Roo.tree.TreePanel(treeEl , tree);
35200 //console.log(tree);
35201 this.on('activate', function()
35203 if (this.tree.rendered) {
35206 //console.log('render tree');
35207 this.tree.render();
35209 // this should not be needed.. - it's actually the 'el' that resizes?
35210 // actuall it breaks the containerScroll - dragging nodes auto scroll at top
35212 //this.on('resize', function (cp, w, h) {
35213 // this.tree.innerCt.setWidth(w);
35214 // this.tree.innerCt.setHeight(h);
35215 // //this.tree.innerCt.setStyle('overflow-y', 'auto');
35222 Roo.extend(Roo.TreePanel, Roo.ContentPanel, {
35239 * Ext JS Library 1.1.1
35240 * Copyright(c) 2006-2007, Ext JS, LLC.
35242 * Originally Released Under LGPL - original licence link has changed is not relivant.
35245 * <script type="text/javascript">
35250 * @class Roo.ReaderLayout
35251 * @extends Roo.BorderLayout
35252 * This is a pre-built layout that represents a classic, 5-pane application. It consists of a header, a primary
35253 * center region containing two nested regions (a top one for a list view and one for item preview below),
35254 * and regions on either side that can be used for navigation, application commands, informational displays, etc.
35255 * The setup and configuration work exactly the same as it does for a {@link Roo.BorderLayout} - this class simply
35256 * expedites the setup of the overall layout and regions for this common application style.
35259 var reader = new Roo.ReaderLayout();
35260 var CP = Roo.ContentPanel; // shortcut for adding
35262 reader.beginUpdate();
35263 reader.add("north", new CP("north", "North"));
35264 reader.add("west", new CP("west", {title: "West"}));
35265 reader.add("east", new CP("east", {title: "East"}));
35267 reader.regions.listView.add(new CP("listView", "List"));
35268 reader.regions.preview.add(new CP("preview", "Preview"));
35269 reader.endUpdate();
35272 * Create a new ReaderLayout
35273 * @param {Object} config Configuration options
35274 * @param {String/HTMLElement/Element} container (optional) The container this layout is bound to (defaults to
35275 * document.body if omitted)
35277 Roo.ReaderLayout = function(config, renderTo){
35278 var c = config || {size:{}};
35279 Roo.ReaderLayout.superclass.constructor.call(this, renderTo || document.body, {
35280 north: c.north !== false ? Roo.apply({
35284 }, c.north) : false,
35285 west: c.west !== false ? Roo.apply({
35293 margins:{left:5,right:0,bottom:5,top:5},
35294 cmargins:{left:5,right:5,bottom:5,top:5}
35295 }, c.west) : false,
35296 east: c.east !== false ? Roo.apply({
35304 margins:{left:0,right:5,bottom:5,top:5},
35305 cmargins:{left:5,right:5,bottom:5,top:5}
35306 }, c.east) : false,
35307 center: Roo.apply({
35308 tabPosition: 'top',
35312 margins:{left:c.west!==false ? 0 : 5,right:c.east!==false ? 0 : 5,bottom:5,top:2}
35316 this.el.addClass('x-reader');
35318 this.beginUpdate();
35320 var inner = new Roo.BorderLayout(Roo.get(document.body).createChild(), {
35321 south: c.preview !== false ? Roo.apply({
35328 cmargins:{top:5,left:0, right:0, bottom:0}
35329 }, c.preview) : false,
35330 center: Roo.apply({
35336 this.add('center', new Roo.NestedLayoutPanel(inner,
35337 Roo.apply({title: c.mainTitle || '',tabTip:''},c.innerPanelCfg)));
35341 this.regions.preview = inner.getRegion('south');
35342 this.regions.listView = inner.getRegion('center');
35345 Roo.extend(Roo.ReaderLayout, Roo.BorderLayout);/*
35347 * Ext JS Library 1.1.1
35348 * Copyright(c) 2006-2007, Ext JS, LLC.
35350 * Originally Released Under LGPL - original licence link has changed is not relivant.
35353 * <script type="text/javascript">
35357 * @class Roo.grid.Grid
35358 * @extends Roo.util.Observable
35359 * This class represents the primary interface of a component based grid control.
35360 * <br><br>Usage:<pre><code>
35361 var grid = new Roo.grid.Grid("my-container-id", {
35364 selModel: mySelectionModel,
35365 autoSizeColumns: true,
35366 monitorWindowResize: false,
35367 trackMouseOver: true
35372 * <b>Common Problems:</b><br/>
35373 * - Grid does not resize properly when going smaller: Setting overflow hidden on the container
35374 * element will correct this<br/>
35375 * - If you get el.style[camel]= NaNpx or -2px or something related, be certain you have given your container element
35376 * dimensions. The grid adapts to your container's size, if your container has no size defined then the results
35377 * are unpredictable.<br/>
35378 * - Do not render the grid into an element with display:none. Try using visibility:hidden. Otherwise there is no way for the
35379 * grid to calculate dimensions/offsets.<br/>
35381 * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
35382 * The container MUST have some type of size defined for the grid to fill. The container will be
35383 * automatically set to position relative if it isn't already.
35384 * @param {Object} config A config object that sets properties on this grid.
35386 Roo.grid.Grid = function(container, config){
35387 // initialize the container
35388 this.container = Roo.get(container);
35389 this.container.update("");
35390 this.container.setStyle("overflow", "hidden");
35391 this.container.addClass('x-grid-container');
35393 this.id = this.container.id;
35395 Roo.apply(this, config);
35396 // check and correct shorthanded configs
35398 this.dataSource = this.ds;
35402 this.colModel = this.cm;
35406 this.selModel = this.sm;
35410 if (this.selModel) {
35411 this.selModel = Roo.factory(this.selModel, Roo.grid);
35412 this.sm = this.selModel;
35413 this.sm.xmodule = this.xmodule || false;
35415 if (typeof(this.colModel.config) == 'undefined') {
35416 this.colModel = new Roo.grid.ColumnModel(this.colModel);
35417 this.cm = this.colModel;
35418 this.cm.xmodule = this.xmodule || false;
35420 if (this.dataSource) {
35421 this.dataSource= Roo.factory(this.dataSource, Roo.data);
35422 this.ds = this.dataSource;
35423 this.ds.xmodule = this.xmodule || false;
35430 this.container.setWidth(this.width);
35434 this.container.setHeight(this.height);
35441 * The raw click event for the entire grid.
35442 * @param {Roo.EventObject} e
35447 * The raw dblclick event for the entire grid.
35448 * @param {Roo.EventObject} e
35452 * @event contextmenu
35453 * The raw contextmenu event for the entire grid.
35454 * @param {Roo.EventObject} e
35456 "contextmenu" : true,
35459 * The raw mousedown event for the entire grid.
35460 * @param {Roo.EventObject} e
35462 "mousedown" : true,
35465 * The raw mouseup event for the entire grid.
35466 * @param {Roo.EventObject} e
35471 * The raw mouseover event for the entire grid.
35472 * @param {Roo.EventObject} e
35474 "mouseover" : true,
35477 * The raw mouseout event for the entire grid.
35478 * @param {Roo.EventObject} e
35483 * The raw keypress event for the entire grid.
35484 * @param {Roo.EventObject} e
35489 * The raw keydown event for the entire grid.
35490 * @param {Roo.EventObject} e
35498 * Fires when a cell is clicked
35499 * @param {Grid} this
35500 * @param {Number} rowIndex
35501 * @param {Number} columnIndex
35502 * @param {Roo.EventObject} e
35504 "cellclick" : true,
35506 * @event celldblclick
35507 * Fires when a cell is double clicked
35508 * @param {Grid} this
35509 * @param {Number} rowIndex
35510 * @param {Number} columnIndex
35511 * @param {Roo.EventObject} e
35513 "celldblclick" : true,
35516 * Fires when a row is clicked
35517 * @param {Grid} this
35518 * @param {Number} rowIndex
35519 * @param {Roo.EventObject} e
35523 * @event rowdblclick
35524 * Fires when a row is double clicked
35525 * @param {Grid} this
35526 * @param {Number} rowIndex
35527 * @param {Roo.EventObject} e
35529 "rowdblclick" : true,
35531 * @event headerclick
35532 * Fires when a header is clicked
35533 * @param {Grid} this
35534 * @param {Number} columnIndex
35535 * @param {Roo.EventObject} e
35537 "headerclick" : true,
35539 * @event headerdblclick
35540 * Fires when a header cell is double clicked
35541 * @param {Grid} this
35542 * @param {Number} columnIndex
35543 * @param {Roo.EventObject} e
35545 "headerdblclick" : true,
35547 * @event rowcontextmenu
35548 * Fires when a row is right clicked
35549 * @param {Grid} this
35550 * @param {Number} rowIndex
35551 * @param {Roo.EventObject} e
35553 "rowcontextmenu" : true,
35555 * @event cellcontextmenu
35556 * Fires when a cell is right clicked
35557 * @param {Grid} this
35558 * @param {Number} rowIndex
35559 * @param {Number} cellIndex
35560 * @param {Roo.EventObject} e
35562 "cellcontextmenu" : true,
35564 * @event headercontextmenu
35565 * Fires when a header is right clicked
35566 * @param {Grid} this
35567 * @param {Number} columnIndex
35568 * @param {Roo.EventObject} e
35570 "headercontextmenu" : true,
35572 * @event bodyscroll
35573 * Fires when the body element is scrolled
35574 * @param {Number} scrollLeft
35575 * @param {Number} scrollTop
35577 "bodyscroll" : true,
35579 * @event columnresize
35580 * Fires when the user resizes a column
35581 * @param {Number} columnIndex
35582 * @param {Number} newSize
35584 "columnresize" : true,
35586 * @event columnmove
35587 * Fires when the user moves a column
35588 * @param {Number} oldIndex
35589 * @param {Number} newIndex
35591 "columnmove" : true,
35594 * Fires when row(s) start being dragged
35595 * @param {Grid} this
35596 * @param {Roo.GridDD} dd The drag drop object
35597 * @param {event} e The raw browser event
35599 "startdrag" : true,
35602 * Fires when a drag operation is complete
35603 * @param {Grid} this
35604 * @param {Roo.GridDD} dd The drag drop object
35605 * @param {event} e The raw browser event
35610 * Fires when dragged row(s) are dropped on a valid DD target
35611 * @param {Grid} this
35612 * @param {Roo.GridDD} dd The drag drop object
35613 * @param {String} targetId The target drag drop object
35614 * @param {event} e The raw browser event
35619 * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
35620 * @param {Grid} this
35621 * @param {Roo.GridDD} dd The drag drop object
35622 * @param {String} targetId The target drag drop object
35623 * @param {event} e The raw browser event
35628 * Fires when the dragged row(s) first cross another DD target while being dragged
35629 * @param {Grid} this
35630 * @param {Roo.GridDD} dd The drag drop object
35631 * @param {String} targetId The target drag drop object
35632 * @param {event} e The raw browser event
35634 "dragenter" : true,
35637 * Fires when the dragged row(s) leave another DD target while being dragged
35638 * @param {Grid} this
35639 * @param {Roo.GridDD} dd The drag drop object
35640 * @param {String} targetId The target drag drop object
35641 * @param {event} e The raw browser event
35646 * Fires when a row is rendered, so you can change add a style to it.
35647 * @param {GridView} gridview The grid view
35648 * @param {Object} rowcfg contains record rowIndex and rowClass - set rowClass to add a style.
35654 * Fires when the grid is rendered
35655 * @param {Grid} grid
35660 Roo.grid.Grid.superclass.constructor.call(this);
35662 Roo.extend(Roo.grid.Grid, Roo.util.Observable, {
35665 * @cfg {String} ddGroup - drag drop group.
35669 * @cfg {Number} minColumnWidth The minimum width a column can be resized to. Default is 25.
35671 minColumnWidth : 25,
35674 * @cfg {Boolean} autoSizeColumns True to automatically resize the columns to fit their content
35675 * <b>on initial render.</b> It is more efficient to explicitly size the columns
35676 * through the ColumnModel's {@link Roo.grid.ColumnModel#width} config option. Default is false.
35678 autoSizeColumns : false,
35681 * @cfg {Boolean} autoSizeHeaders True to measure headers with column data when auto sizing columns. Default is true.
35683 autoSizeHeaders : true,
35686 * @cfg {Boolean} monitorWindowResize True to autoSize the grid when the window resizes. Default is true.
35688 monitorWindowResize : true,
35691 * @cfg {Boolean} maxRowsToMeasure If autoSizeColumns is on, maxRowsToMeasure can be used to limit the number of
35692 * rows measured to get a columns size. Default is 0 (all rows).
35694 maxRowsToMeasure : 0,
35697 * @cfg {Boolean} trackMouseOver True to highlight rows when the mouse is over. Default is true.
35699 trackMouseOver : true,
35702 * @cfg {Boolean} enableDrag True to enable drag of rows. Default is false. (double check if this is needed?)
35706 * @cfg {Boolean} enableDragDrop True to enable drag and drop of rows. Default is false.
35708 enableDragDrop : false,
35711 * @cfg {Boolean} enableColumnMove True to enable drag and drop reorder of columns. Default is true.
35713 enableColumnMove : true,
35716 * @cfg {Boolean} enableColumnHide True to enable hiding of columns with the header context menu. Default is true.
35718 enableColumnHide : true,
35721 * @cfg {Boolean} enableRowHeightSync True to manually sync row heights across locked and not locked rows. Default is false.
35723 enableRowHeightSync : false,
35726 * @cfg {Boolean} stripeRows True to stripe the rows. Default is true.
35731 * @cfg {Boolean} autoHeight True to fit the height of the grid container to the height of the data. Default is false.
35733 autoHeight : false,
35736 * @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.
35738 autoExpandColumn : false,
35741 * @cfg {Number} autoExpandMin The minimum width the autoExpandColumn can have (if enabled).
35744 autoExpandMin : 50,
35747 * @cfg {Number} autoExpandMax The maximum width the autoExpandColumn can have (if enabled). Default is 1000.
35749 autoExpandMax : 1000,
35752 * @cfg {Object} view The {@link Roo.grid.GridView} used by the grid. This can be set before a call to render().
35757 * @cfg {Object} loadMask An {@link Roo.LoadMask} config or true to mask the grid while loading. Default is false.
35761 * @cfg {Roo.dd.DropTarget} dropTarget An {@link Roo.dd.DropTarget} config
35771 * @cfg {Boolean} autoWidth True to set the grid's width to the default total width of the grid's columns instead
35772 * of a fixed width. Default is false.
35775 * @cfg {Number} maxHeight Sets the maximum height of the grid - ignored if autoHeight is not on.
35778 * Called once after all setup has been completed and the grid is ready to be rendered.
35779 * @return {Roo.grid.Grid} this
35781 render : function()
35783 var c = this.container;
35784 // try to detect autoHeight/width mode
35785 if((!c.dom.offsetHeight || c.dom.offsetHeight < 20) || c.getStyle("height") == "auto"){
35786 this.autoHeight = true;
35788 var view = this.getView();
35791 c.on("click", this.onClick, this);
35792 c.on("dblclick", this.onDblClick, this);
35793 c.on("contextmenu", this.onContextMenu, this);
35794 c.on("keydown", this.onKeyDown, this);
35796 c.on("touchstart", this.onTouchStart, this);
35799 this.relayEvents(c, ["mousedown","mouseup","mouseover","mouseout","keypress"]);
35801 this.getSelectionModel().init(this);
35806 this.loadMask = new Roo.LoadMask(this.container,
35807 Roo.apply({store:this.dataSource}, this.loadMask));
35811 if (this.toolbar && this.toolbar.xtype) {
35812 this.toolbar.container = this.getView().getHeaderPanel(true);
35813 this.toolbar = new Roo.Toolbar(this.toolbar);
35815 if (this.footer && this.footer.xtype) {
35816 this.footer.dataSource = this.getDataSource();
35817 this.footer.container = this.getView().getFooterPanel(true);
35818 this.footer = Roo.factory(this.footer, Roo);
35820 if (this.dropTarget && this.dropTarget.xtype) {
35821 delete this.dropTarget.xtype;
35822 this.dropTarget = new Roo.dd.DropTarget(this.getView().mainBody, this.dropTarget);
35826 this.rendered = true;
35827 this.fireEvent('render', this);
35832 * Reconfigures the grid to use a different Store and Column Model.
35833 * The View will be bound to the new objects and refreshed.
35834 * @param {Roo.data.Store} dataSource The new {@link Roo.data.Store} object
35835 * @param {Roo.grid.ColumnModel} The new {@link Roo.grid.ColumnModel} object
35837 reconfigure : function(dataSource, colModel){
35839 this.loadMask.destroy();
35840 this.loadMask = new Roo.LoadMask(this.container,
35841 Roo.apply({store:dataSource}, this.loadMask));
35843 this.view.bind(dataSource, colModel);
35844 this.dataSource = dataSource;
35845 this.colModel = colModel;
35846 this.view.refresh(true);
35850 onKeyDown : function(e){
35851 this.fireEvent("keydown", e);
35855 * Destroy this grid.
35856 * @param {Boolean} removeEl True to remove the element
35858 destroy : function(removeEl, keepListeners){
35860 this.loadMask.destroy();
35862 var c = this.container;
35863 c.removeAllListeners();
35864 this.view.destroy();
35865 this.colModel.purgeListeners();
35866 if(!keepListeners){
35867 this.purgeListeners();
35870 if(removeEl === true){
35876 processEvent : function(name, e){
35877 // does this fire select???
35878 Roo.log('grid:processEvent ' + name);
35880 if (name != 'touchstart' ) {
35881 this.fireEvent(name, e);
35884 var t = e.getTarget();
35886 var header = v.findHeaderIndex(t);
35887 if(header !== false){
35888 this.fireEvent("header" + (name == 'touchstart' ? 'click' : name), this, header, e);
35890 var row = v.findRowIndex(t);
35891 var cell = v.findCellIndex(t);
35892 if (name == 'touchstart') {
35893 // first touch is always a click.
35894 // hopefull this happens after selection is updated.?
35897 if (typeof(this.selModel.getSelectedCell) != 'undefined') {
35898 var cs = this.selModel.getSelectedCell();
35899 if (row == cs[0] && cell == cs[1]){
35903 if (typeof(this.selModel.getSelections) != 'undefined') {
35904 var cs = this.selModel.getSelections();
35905 var ds = this.dataSource;
35906 if (cs.length == 1 && ds.getAt(row) == cs[0]){
35917 this.fireEvent("row" + name, this, row, e);
35918 if(cell !== false){
35919 this.fireEvent("cell" + name, this, row, cell, e);
35926 onClick : function(e){
35927 this.processEvent("click", e);
35930 onTouchStart : function(e){
35931 this.processEvent("touchstart", e);
35935 onContextMenu : function(e, t){
35936 this.processEvent("contextmenu", e);
35940 onDblClick : function(e){
35941 this.processEvent("dblclick", e);
35945 walkCells : function(row, col, step, fn, scope){
35946 var cm = this.colModel, clen = cm.getColumnCount();
35947 var ds = this.dataSource, rlen = ds.getCount(), first = true;
35959 if(fn.call(scope || this, row, col, cm) === true){
35977 if(fn.call(scope || this, row, col, cm) === true){
35989 getSelections : function(){
35990 return this.selModel.getSelections();
35994 * Causes the grid to manually recalculate its dimensions. Generally this is done automatically,
35995 * but if manual update is required this method will initiate it.
35997 autoSize : function(){
35999 this.view.layout();
36000 if(this.view.adjustForScroll){
36001 this.view.adjustForScroll();
36007 * Returns the grid's underlying element.
36008 * @return {Element} The element
36010 getGridEl : function(){
36011 return this.container;
36014 // private for compatibility, overridden by editor grid
36015 stopEditing : function(){},
36018 * Returns the grid's SelectionModel.
36019 * @return {SelectionModel}
36021 getSelectionModel : function(){
36022 if(!this.selModel){
36023 this.selModel = new Roo.grid.RowSelectionModel();
36025 return this.selModel;
36029 * Returns the grid's DataSource.
36030 * @return {DataSource}
36032 getDataSource : function(){
36033 return this.dataSource;
36037 * Returns the grid's ColumnModel.
36038 * @return {ColumnModel}
36040 getColumnModel : function(){
36041 return this.colModel;
36045 * Returns the grid's GridView object.
36046 * @return {GridView}
36048 getView : function(){
36050 this.view = new Roo.grid.GridView(this.viewConfig);
36055 * Called to get grid's drag proxy text, by default returns this.ddText.
36058 getDragDropText : function(){
36059 var count = this.selModel.getCount();
36060 return String.format(this.ddText, count, count == 1 ? '' : 's');
36064 * Configures the text is the drag proxy (defaults to "%0 selected row(s)").
36065 * %0 is replaced with the number of selected rows.
36068 Roo.grid.Grid.prototype.ddText = "{0} selected row{1}";/*
36070 * Ext JS Library 1.1.1
36071 * Copyright(c) 2006-2007, Ext JS, LLC.
36073 * Originally Released Under LGPL - original licence link has changed is not relivant.
36076 * <script type="text/javascript">
36079 Roo.grid.AbstractGridView = function(){
36083 "beforerowremoved" : true,
36084 "beforerowsinserted" : true,
36085 "beforerefresh" : true,
36086 "rowremoved" : true,
36087 "rowsinserted" : true,
36088 "rowupdated" : true,
36091 Roo.grid.AbstractGridView.superclass.constructor.call(this);
36094 Roo.extend(Roo.grid.AbstractGridView, Roo.util.Observable, {
36095 rowClass : "x-grid-row",
36096 cellClass : "x-grid-cell",
36097 tdClass : "x-grid-td",
36098 hdClass : "x-grid-hd",
36099 splitClass : "x-grid-hd-split",
36101 init: function(grid){
36103 var cid = this.grid.getGridEl().id;
36104 this.colSelector = "#" + cid + " ." + this.cellClass + "-";
36105 this.tdSelector = "#" + cid + " ." + this.tdClass + "-";
36106 this.hdSelector = "#" + cid + " ." + this.hdClass + "-";
36107 this.splitSelector = "#" + cid + " ." + this.splitClass + "-";
36110 getColumnRenderers : function(){
36111 var renderers = [];
36112 var cm = this.grid.colModel;
36113 var colCount = cm.getColumnCount();
36114 for(var i = 0; i < colCount; i++){
36115 renderers[i] = cm.getRenderer(i);
36120 getColumnIds : function(){
36122 var cm = this.grid.colModel;
36123 var colCount = cm.getColumnCount();
36124 for(var i = 0; i < colCount; i++){
36125 ids[i] = cm.getColumnId(i);
36130 getDataIndexes : function(){
36131 if(!this.indexMap){
36132 this.indexMap = this.buildIndexMap();
36134 return this.indexMap.colToData;
36137 getColumnIndexByDataIndex : function(dataIndex){
36138 if(!this.indexMap){
36139 this.indexMap = this.buildIndexMap();
36141 return this.indexMap.dataToCol[dataIndex];
36145 * Set a css style for a column dynamically.
36146 * @param {Number} colIndex The index of the column
36147 * @param {String} name The css property name
36148 * @param {String} value The css value
36150 setCSSStyle : function(colIndex, name, value){
36151 var selector = "#" + this.grid.id + " .x-grid-col-" + colIndex;
36152 Roo.util.CSS.updateRule(selector, name, value);
36155 generateRules : function(cm){
36156 var ruleBuf = [], rulesId = this.grid.id + '-cssrules';
36157 Roo.util.CSS.removeStyleSheet(rulesId);
36158 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
36159 var cid = cm.getColumnId(i);
36160 ruleBuf.push(this.colSelector, cid, " {\n", cm.config[i].css, "}\n",
36161 this.tdSelector, cid, " {\n}\n",
36162 this.hdSelector, cid, " {\n}\n",
36163 this.splitSelector, cid, " {\n}\n");
36165 return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
36169 * Ext JS Library 1.1.1
36170 * Copyright(c) 2006-2007, Ext JS, LLC.
36172 * Originally Released Under LGPL - original licence link has changed is not relivant.
36175 * <script type="text/javascript">
36179 // This is a support class used internally by the Grid components
36180 Roo.grid.HeaderDragZone = function(grid, hd, hd2){
36182 this.view = grid.getView();
36183 this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
36184 Roo.grid.HeaderDragZone.superclass.constructor.call(this, hd);
36186 this.setHandleElId(Roo.id(hd));
36187 this.setOuterHandleElId(Roo.id(hd2));
36189 this.scroll = false;
36191 Roo.extend(Roo.grid.HeaderDragZone, Roo.dd.DragZone, {
36193 getDragData : function(e){
36194 var t = Roo.lib.Event.getTarget(e);
36195 var h = this.view.findHeaderCell(t);
36197 return {ddel: h.firstChild, header:h};
36202 onInitDrag : function(e){
36203 this.view.headersDisabled = true;
36204 var clone = this.dragData.ddel.cloneNode(true);
36205 clone.id = Roo.id();
36206 clone.style.width = Math.min(this.dragData.header.offsetWidth,this.maxDragWidth) + "px";
36207 this.proxy.update(clone);
36211 afterValidDrop : function(){
36213 setTimeout(function(){
36214 v.headersDisabled = false;
36218 afterInvalidDrop : function(){
36220 setTimeout(function(){
36221 v.headersDisabled = false;
36227 * Ext JS Library 1.1.1
36228 * Copyright(c) 2006-2007, Ext JS, LLC.
36230 * Originally Released Under LGPL - original licence link has changed is not relivant.
36233 * <script type="text/javascript">
36236 // This is a support class used internally by the Grid components
36237 Roo.grid.HeaderDropZone = function(grid, hd, hd2){
36239 this.view = grid.getView();
36240 // split the proxies so they don't interfere with mouse events
36241 this.proxyTop = Roo.DomHelper.append(document.body, {
36242 cls:"col-move-top", html:" "
36244 this.proxyBottom = Roo.DomHelper.append(document.body, {
36245 cls:"col-move-bottom", html:" "
36247 this.proxyTop.hide = this.proxyBottom.hide = function(){
36248 this.setLeftTop(-100,-100);
36249 this.setStyle("visibility", "hidden");
36251 this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
36252 // temporarily disabled
36253 //Roo.dd.ScrollManager.register(this.view.scroller.dom);
36254 Roo.grid.HeaderDropZone.superclass.constructor.call(this, grid.getGridEl().dom);
36256 Roo.extend(Roo.grid.HeaderDropZone, Roo.dd.DropZone, {
36257 proxyOffsets : [-4, -9],
36258 fly: Roo.Element.fly,
36260 getTargetFromEvent : function(e){
36261 var t = Roo.lib.Event.getTarget(e);
36262 var cindex = this.view.findCellIndex(t);
36263 if(cindex !== false){
36264 return this.view.getHeaderCell(cindex);
36269 nextVisible : function(h){
36270 var v = this.view, cm = this.grid.colModel;
36273 if(!cm.isHidden(v.getCellIndex(h))){
36281 prevVisible : function(h){
36282 var v = this.view, cm = this.grid.colModel;
36285 if(!cm.isHidden(v.getCellIndex(h))){
36293 positionIndicator : function(h, n, e){
36294 var x = Roo.lib.Event.getPageX(e);
36295 var r = Roo.lib.Dom.getRegion(n.firstChild);
36296 var px, pt, py = r.top + this.proxyOffsets[1];
36297 if((r.right - x) <= (r.right-r.left)/2){
36298 px = r.right+this.view.borderWidth;
36304 var oldIndex = this.view.getCellIndex(h);
36305 var newIndex = this.view.getCellIndex(n);
36307 if(this.grid.colModel.isFixed(newIndex)){
36311 var locked = this.grid.colModel.isLocked(newIndex);
36316 if(oldIndex < newIndex){
36319 if(oldIndex == newIndex && (locked == this.grid.colModel.isLocked(oldIndex))){
36322 px += this.proxyOffsets[0];
36323 this.proxyTop.setLeftTop(px, py);
36324 this.proxyTop.show();
36325 if(!this.bottomOffset){
36326 this.bottomOffset = this.view.mainHd.getHeight();
36328 this.proxyBottom.setLeftTop(px, py+this.proxyTop.dom.offsetHeight+this.bottomOffset);
36329 this.proxyBottom.show();
36333 onNodeEnter : function(n, dd, e, data){
36334 if(data.header != n){
36335 this.positionIndicator(data.header, n, e);
36339 onNodeOver : function(n, dd, e, data){
36340 var result = false;
36341 if(data.header != n){
36342 result = this.positionIndicator(data.header, n, e);
36345 this.proxyTop.hide();
36346 this.proxyBottom.hide();
36348 return result ? this.dropAllowed : this.dropNotAllowed;
36351 onNodeOut : function(n, dd, e, data){
36352 this.proxyTop.hide();
36353 this.proxyBottom.hide();
36356 onNodeDrop : function(n, dd, e, data){
36357 var h = data.header;
36359 var cm = this.grid.colModel;
36360 var x = Roo.lib.Event.getPageX(e);
36361 var r = Roo.lib.Dom.getRegion(n.firstChild);
36362 var pt = (r.right - x) <= ((r.right-r.left)/2) ? "after" : "before";
36363 var oldIndex = this.view.getCellIndex(h);
36364 var newIndex = this.view.getCellIndex(n);
36365 var locked = cm.isLocked(newIndex);
36369 if(oldIndex < newIndex){
36372 if(oldIndex == newIndex && (locked == cm.isLocked(oldIndex))){
36375 cm.setLocked(oldIndex, locked, true);
36376 cm.moveColumn(oldIndex, newIndex);
36377 this.grid.fireEvent("columnmove", oldIndex, newIndex);
36385 * Ext JS Library 1.1.1
36386 * Copyright(c) 2006-2007, Ext JS, LLC.
36388 * Originally Released Under LGPL - original licence link has changed is not relivant.
36391 * <script type="text/javascript">
36395 * @class Roo.grid.GridView
36396 * @extends Roo.util.Observable
36399 * @param {Object} config
36401 Roo.grid.GridView = function(config){
36402 Roo.grid.GridView.superclass.constructor.call(this);
36405 Roo.apply(this, config);
36408 Roo.extend(Roo.grid.GridView, Roo.grid.AbstractGridView, {
36410 unselectable : 'unselectable="on"',
36411 unselectableCls : 'x-unselectable',
36414 rowClass : "x-grid-row",
36416 cellClass : "x-grid-col",
36418 tdClass : "x-grid-td",
36420 hdClass : "x-grid-hd",
36422 splitClass : "x-grid-split",
36424 sortClasses : ["sort-asc", "sort-desc"],
36426 enableMoveAnim : false,
36430 dh : Roo.DomHelper,
36432 fly : Roo.Element.fly,
36434 css : Roo.util.CSS,
36440 scrollIncrement : 22,
36442 cellRE: /(?:.*?)x-grid-(?:hd|cell|csplit)-(?:[\d]+)-([\d]+)(?:.*?)/,
36444 findRE: /\s?(?:x-grid-hd|x-grid-col|x-grid-csplit)\s/,
36446 bind : function(ds, cm){
36448 this.ds.un("load", this.onLoad, this);
36449 this.ds.un("datachanged", this.onDataChange, this);
36450 this.ds.un("add", this.onAdd, this);
36451 this.ds.un("remove", this.onRemove, this);
36452 this.ds.un("update", this.onUpdate, this);
36453 this.ds.un("clear", this.onClear, this);
36456 ds.on("load", this.onLoad, this);
36457 ds.on("datachanged", this.onDataChange, this);
36458 ds.on("add", this.onAdd, this);
36459 ds.on("remove", this.onRemove, this);
36460 ds.on("update", this.onUpdate, this);
36461 ds.on("clear", this.onClear, this);
36466 this.cm.un("widthchange", this.onColWidthChange, this);
36467 this.cm.un("headerchange", this.onHeaderChange, this);
36468 this.cm.un("hiddenchange", this.onHiddenChange, this);
36469 this.cm.un("columnmoved", this.onColumnMove, this);
36470 this.cm.un("columnlockchange", this.onColumnLock, this);
36473 this.generateRules(cm);
36474 cm.on("widthchange", this.onColWidthChange, this);
36475 cm.on("headerchange", this.onHeaderChange, this);
36476 cm.on("hiddenchange", this.onHiddenChange, this);
36477 cm.on("columnmoved", this.onColumnMove, this);
36478 cm.on("columnlockchange", this.onColumnLock, this);
36483 init: function(grid){
36484 Roo.grid.GridView.superclass.init.call(this, grid);
36486 this.bind(grid.dataSource, grid.colModel);
36488 grid.on("headerclick", this.handleHeaderClick, this);
36490 if(grid.trackMouseOver){
36491 grid.on("mouseover", this.onRowOver, this);
36492 grid.on("mouseout", this.onRowOut, this);
36494 grid.cancelTextSelection = function(){};
36495 this.gridId = grid.id;
36497 var tpls = this.templates || {};
36500 tpls.master = new Roo.Template(
36501 '<div class="x-grid" hidefocus="true">',
36502 '<a href="#" class="x-grid-focus" tabIndex="-1"></a>',
36503 '<div class="x-grid-topbar"></div>',
36504 '<div class="x-grid-scroller"><div></div></div>',
36505 '<div class="x-grid-locked">',
36506 '<div class="x-grid-header">{lockedHeader}</div>',
36507 '<div class="x-grid-body">{lockedBody}</div>',
36509 '<div class="x-grid-viewport">',
36510 '<div class="x-grid-header">{header}</div>',
36511 '<div class="x-grid-body">{body}</div>',
36513 '<div class="x-grid-bottombar"></div>',
36515 '<div class="x-grid-resize-proxy"> </div>',
36518 tpls.master.disableformats = true;
36522 tpls.header = new Roo.Template(
36523 '<table border="0" cellspacing="0" cellpadding="0">',
36524 '<tbody><tr class="x-grid-hd-row">{cells}</tr></tbody>',
36527 tpls.header.disableformats = true;
36529 tpls.header.compile();
36532 tpls.hcell = new Roo.Template(
36533 '<td class="x-grid-hd x-grid-td-{id} {cellId}"><div title="{title}" class="x-grid-hd-inner x-grid-hd-{id}">',
36534 '<div class="x-grid-hd-text ' + this.unselectableCls + '" ' + this.unselectable +'>{value}<img class="x-grid-sort-icon" src="', Roo.BLANK_IMAGE_URL, '" /></div>',
36537 tpls.hcell.disableFormats = true;
36539 tpls.hcell.compile();
36542 tpls.hsplit = new Roo.Template('<div class="x-grid-split {splitId} x-grid-split-{id}" style="{style} ' +
36543 this.unselectableCls + '" ' + this.unselectable +'> </div>');
36544 tpls.hsplit.disableFormats = true;
36546 tpls.hsplit.compile();
36549 tpls.body = new Roo.Template(
36550 '<table border="0" cellspacing="0" cellpadding="0">',
36551 "<tbody>{rows}</tbody>",
36554 tpls.body.disableFormats = true;
36556 tpls.body.compile();
36559 tpls.row = new Roo.Template('<tr class="x-grid-row {alt}">{cells}</tr>');
36560 tpls.row.disableFormats = true;
36562 tpls.row.compile();
36565 tpls.cell = new Roo.Template(
36566 '<td class="x-grid-col x-grid-td-{id} {cellId} {css}" tabIndex="0">',
36567 '<div class="x-grid-col-{id} x-grid-cell-inner"><div class="x-grid-cell-text ' +
36568 this.unselectableCls + '" ' + this.unselectable +'" {attr}>{value}</div></div>',
36571 tpls.cell.disableFormats = true;
36573 tpls.cell.compile();
36575 this.templates = tpls;
36578 // remap these for backwards compat
36579 onColWidthChange : function(){
36580 this.updateColumns.apply(this, arguments);
36582 onHeaderChange : function(){
36583 this.updateHeaders.apply(this, arguments);
36585 onHiddenChange : function(){
36586 this.handleHiddenChange.apply(this, arguments);
36588 onColumnMove : function(){
36589 this.handleColumnMove.apply(this, arguments);
36591 onColumnLock : function(){
36592 this.handleLockChange.apply(this, arguments);
36595 onDataChange : function(){
36597 this.updateHeaderSortState();
36600 onClear : function(){
36604 onUpdate : function(ds, record){
36605 this.refreshRow(record);
36608 refreshRow : function(record){
36609 var ds = this.ds, index;
36610 if(typeof record == 'number'){
36612 record = ds.getAt(index);
36614 index = ds.indexOf(record);
36616 this.insertRows(ds, index, index, true);
36617 this.onRemove(ds, record, index+1, true);
36618 this.syncRowHeights(index, index);
36620 this.fireEvent("rowupdated", this, index, record);
36623 onAdd : function(ds, records, index){
36624 this.insertRows(ds, index, index + (records.length-1));
36627 onRemove : function(ds, record, index, isUpdate){
36628 if(isUpdate !== true){
36629 this.fireEvent("beforerowremoved", this, index, record);
36631 var bt = this.getBodyTable(), lt = this.getLockedTable();
36632 if(bt.rows[index]){
36633 bt.firstChild.removeChild(bt.rows[index]);
36635 if(lt.rows[index]){
36636 lt.firstChild.removeChild(lt.rows[index]);
36638 if(isUpdate !== true){
36639 this.stripeRows(index);
36640 this.syncRowHeights(index, index);
36642 this.fireEvent("rowremoved", this, index, record);
36646 onLoad : function(){
36647 this.scrollToTop();
36651 * Scrolls the grid to the top
36653 scrollToTop : function(){
36655 this.scroller.dom.scrollTop = 0;
36661 * Gets a panel in the header of the grid that can be used for toolbars etc.
36662 * After modifying the contents of this panel a call to grid.autoSize() may be
36663 * required to register any changes in size.
36664 * @param {Boolean} doShow By default the header is hidden. Pass true to show the panel
36665 * @return Roo.Element
36667 getHeaderPanel : function(doShow){
36669 this.headerPanel.show();
36671 return this.headerPanel;
36675 * Gets a panel in the footer of the grid that can be used for toolbars etc.
36676 * After modifying the contents of this panel a call to grid.autoSize() may be
36677 * required to register any changes in size.
36678 * @param {Boolean} doShow By default the footer is hidden. Pass true to show the panel
36679 * @return Roo.Element
36681 getFooterPanel : function(doShow){
36683 this.footerPanel.show();
36685 return this.footerPanel;
36688 initElements : function(){
36689 var E = Roo.Element;
36690 var el = this.grid.getGridEl().dom.firstChild;
36691 var cs = el.childNodes;
36693 this.el = new E(el);
36695 this.focusEl = new E(el.firstChild);
36696 this.focusEl.swallowEvent("click", true);
36698 this.headerPanel = new E(cs[1]);
36699 this.headerPanel.enableDisplayMode("block");
36701 this.scroller = new E(cs[2]);
36702 this.scrollSizer = new E(this.scroller.dom.firstChild);
36704 this.lockedWrap = new E(cs[3]);
36705 this.lockedHd = new E(this.lockedWrap.dom.firstChild);
36706 this.lockedBody = new E(this.lockedWrap.dom.childNodes[1]);
36708 this.mainWrap = new E(cs[4]);
36709 this.mainHd = new E(this.mainWrap.dom.firstChild);
36710 this.mainBody = new E(this.mainWrap.dom.childNodes[1]);
36712 this.footerPanel = new E(cs[5]);
36713 this.footerPanel.enableDisplayMode("block");
36715 this.resizeProxy = new E(cs[6]);
36717 this.headerSelector = String.format(
36718 '#{0} td.x-grid-hd, #{1} td.x-grid-hd',
36719 this.lockedHd.id, this.mainHd.id
36722 this.splitterSelector = String.format(
36723 '#{0} div.x-grid-split, #{1} div.x-grid-split',
36724 this.idToCssName(this.lockedHd.id), this.idToCssName(this.mainHd.id)
36727 idToCssName : function(s)
36729 return s.replace(/[^a-z0-9]+/ig, '-');
36732 getHeaderCell : function(index){
36733 return Roo.DomQuery.select(this.headerSelector)[index];
36736 getHeaderCellMeasure : function(index){
36737 return this.getHeaderCell(index).firstChild;
36740 getHeaderCellText : function(index){
36741 return this.getHeaderCell(index).firstChild.firstChild;
36744 getLockedTable : function(){
36745 return this.lockedBody.dom.firstChild;
36748 getBodyTable : function(){
36749 return this.mainBody.dom.firstChild;
36752 getLockedRow : function(index){
36753 return this.getLockedTable().rows[index];
36756 getRow : function(index){
36757 return this.getBodyTable().rows[index];
36760 getRowComposite : function(index){
36762 this.rowEl = new Roo.CompositeElementLite();
36764 var els = [], lrow, mrow;
36765 if(lrow = this.getLockedRow(index)){
36768 if(mrow = this.getRow(index)){
36771 this.rowEl.elements = els;
36775 * Gets the 'td' of the cell
36777 * @param {Integer} rowIndex row to select
36778 * @param {Integer} colIndex column to select
36782 getCell : function(rowIndex, colIndex){
36783 var locked = this.cm.getLockedCount();
36785 if(colIndex < locked){
36786 source = this.lockedBody.dom.firstChild;
36788 source = this.mainBody.dom.firstChild;
36789 colIndex -= locked;
36791 return source.rows[rowIndex].childNodes[colIndex];
36794 getCellText : function(rowIndex, colIndex){
36795 return this.getCell(rowIndex, colIndex).firstChild.firstChild;
36798 getCellBox : function(cell){
36799 var b = this.fly(cell).getBox();
36800 if(Roo.isOpera){ // opera fails to report the Y
36801 b.y = cell.offsetTop + this.mainBody.getY();
36806 getCellIndex : function(cell){
36807 var id = String(cell.className).match(this.cellRE);
36809 return parseInt(id[1], 10);
36814 findHeaderIndex : function(n){
36815 var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
36816 return r ? this.getCellIndex(r) : false;
36819 findHeaderCell : function(n){
36820 var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
36821 return r ? r : false;
36824 findRowIndex : function(n){
36828 var r = Roo.fly(n).findParent("tr." + this.rowClass, 6);
36829 return r ? r.rowIndex : false;
36832 findCellIndex : function(node){
36833 var stop = this.el.dom;
36834 while(node && node != stop){
36835 if(this.findRE.test(node.className)){
36836 return this.getCellIndex(node);
36838 node = node.parentNode;
36843 getColumnId : function(index){
36844 return this.cm.getColumnId(index);
36847 getSplitters : function()
36849 if(this.splitterSelector){
36850 return Roo.DomQuery.select(this.splitterSelector);
36856 getSplitter : function(index){
36857 return this.getSplitters()[index];
36860 onRowOver : function(e, t){
36862 if((row = this.findRowIndex(t)) !== false){
36863 this.getRowComposite(row).addClass("x-grid-row-over");
36867 onRowOut : function(e, t){
36869 if((row = this.findRowIndex(t)) !== false && row !== this.findRowIndex(e.getRelatedTarget())){
36870 this.getRowComposite(row).removeClass("x-grid-row-over");
36874 renderHeaders : function(){
36876 var ct = this.templates.hcell, ht = this.templates.header, st = this.templates.hsplit;
36877 var cb = [], lb = [], sb = [], lsb = [], p = {};
36878 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
36879 p.cellId = "x-grid-hd-0-" + i;
36880 p.splitId = "x-grid-csplit-0-" + i;
36881 p.id = cm.getColumnId(i);
36882 p.title = cm.getColumnTooltip(i) || "";
36883 p.value = cm.getColumnHeader(i) || "";
36884 p.style = (this.grid.enableColumnResize === false || !cm.isResizable(i) || cm.isFixed(i)) ? 'cursor:default' : '';
36885 if(!cm.isLocked(i)){
36886 cb[cb.length] = ct.apply(p);
36887 sb[sb.length] = st.apply(p);
36889 lb[lb.length] = ct.apply(p);
36890 lsb[lsb.length] = st.apply(p);
36893 return [ht.apply({cells: lb.join(""), splits:lsb.join("")}),
36894 ht.apply({cells: cb.join(""), splits:sb.join("")})];
36897 updateHeaders : function(){
36898 var html = this.renderHeaders();
36899 this.lockedHd.update(html[0]);
36900 this.mainHd.update(html[1]);
36904 * Focuses the specified row.
36905 * @param {Number} row The row index
36907 focusRow : function(row)
36909 //Roo.log('GridView.focusRow');
36910 var x = this.scroller.dom.scrollLeft;
36911 this.focusCell(row, 0, false);
36912 this.scroller.dom.scrollLeft = x;
36916 * Focuses the specified cell.
36917 * @param {Number} row The row index
36918 * @param {Number} col The column index
36919 * @param {Boolean} hscroll false to disable horizontal scrolling
36921 focusCell : function(row, col, hscroll)
36923 //Roo.log('GridView.focusCell');
36924 var el = this.ensureVisible(row, col, hscroll);
36925 this.focusEl.alignTo(el, "tl-tl");
36927 this.focusEl.focus();
36929 this.focusEl.focus.defer(1, this.focusEl);
36934 * Scrolls the specified cell into view
36935 * @param {Number} row The row index
36936 * @param {Number} col The column index
36937 * @param {Boolean} hscroll false to disable horizontal scrolling
36939 ensureVisible : function(row, col, hscroll)
36941 //Roo.log('GridView.ensureVisible,' + row + ',' + col);
36942 //return null; //disable for testing.
36943 if(typeof row != "number"){
36944 row = row.rowIndex;
36946 if(row < 0 && row >= this.ds.getCount()){
36949 col = (col !== undefined ? col : 0);
36950 var cm = this.grid.colModel;
36951 while(cm.isHidden(col)){
36955 var el = this.getCell(row, col);
36959 var c = this.scroller.dom;
36961 var ctop = parseInt(el.offsetTop, 10);
36962 var cleft = parseInt(el.offsetLeft, 10);
36963 var cbot = ctop + el.offsetHeight;
36964 var cright = cleft + el.offsetWidth;
36966 var ch = c.clientHeight - this.mainHd.dom.offsetHeight;
36967 var stop = parseInt(c.scrollTop, 10);
36968 var sleft = parseInt(c.scrollLeft, 10);
36969 var sbot = stop + ch;
36970 var sright = sleft + c.clientWidth;
36972 Roo.log('GridView.ensureVisible:' +
36974 ' c.clientHeight:' + c.clientHeight +
36975 ' this.mainHd.dom.offsetHeight:' + this.mainHd.dom.offsetHeight +
36983 c.scrollTop = ctop;
36984 //Roo.log("set scrolltop to ctop DISABLE?");
36985 }else if(cbot > sbot){
36986 //Roo.log("set scrolltop to cbot-ch");
36987 c.scrollTop = cbot-ch;
36990 if(hscroll !== false){
36992 c.scrollLeft = cleft;
36993 }else if(cright > sright){
36994 c.scrollLeft = cright-c.clientWidth;
37001 updateColumns : function(){
37002 this.grid.stopEditing();
37003 var cm = this.grid.colModel, colIds = this.getColumnIds();
37004 //var totalWidth = cm.getTotalWidth();
37006 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
37007 //if(cm.isHidden(i)) continue;
37008 var w = cm.getColumnWidth(i);
37009 this.css.updateRule(this.colSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
37010 this.css.updateRule(this.hdSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
37012 this.updateSplitters();
37015 generateRules : function(cm){
37016 var ruleBuf = [], rulesId = this.idToCssName(this.grid.id)+ '-cssrules';
37017 Roo.util.CSS.removeStyleSheet(rulesId);
37018 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
37019 var cid = cm.getColumnId(i);
37021 if(cm.config[i].align){
37022 align = 'text-align:'+cm.config[i].align+';';
37025 if(cm.isHidden(i)){
37026 hidden = 'display:none;';
37028 var width = "width:" + (cm.getColumnWidth(i) - this.borderWidth) + "px;";
37030 this.colSelector, cid, " {\n", cm.config[i].css, align, width, "\n}\n",
37031 this.hdSelector, cid, " {\n", align, width, "}\n",
37032 this.tdSelector, cid, " {\n",hidden,"\n}\n",
37033 this.splitSelector, cid, " {\n", hidden , "\n}\n");
37035 return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
37038 updateSplitters : function(){
37039 var cm = this.cm, s = this.getSplitters();
37040 if(s){ // splitters not created yet
37041 var pos = 0, locked = true;
37042 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
37043 if(cm.isHidden(i)) continue;
37044 var w = cm.getColumnWidth(i); // make sure it's a number
37045 if(!cm.isLocked(i) && locked){
37050 s[i].style.left = (pos-this.splitOffset) + "px";
37055 handleHiddenChange : function(colModel, colIndex, hidden){
37057 this.hideColumn(colIndex);
37059 this.unhideColumn(colIndex);
37063 hideColumn : function(colIndex){
37064 var cid = this.getColumnId(colIndex);
37065 this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "none");
37066 this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "none");
37068 this.updateHeaders();
37070 this.updateSplitters();
37074 unhideColumn : function(colIndex){
37075 var cid = this.getColumnId(colIndex);
37076 this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "");
37077 this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "");
37080 this.updateHeaders();
37082 this.updateSplitters();
37086 insertRows : function(dm, firstRow, lastRow, isUpdate){
37087 if(firstRow == 0 && lastRow == dm.getCount()-1){
37091 this.fireEvent("beforerowsinserted", this, firstRow, lastRow);
37093 var s = this.getScrollState();
37094 var markup = this.renderRows(firstRow, lastRow);
37095 this.bufferRows(markup[0], this.getLockedTable(), firstRow);
37096 this.bufferRows(markup[1], this.getBodyTable(), firstRow);
37097 this.restoreScroll(s);
37099 this.fireEvent("rowsinserted", this, firstRow, lastRow);
37100 this.syncRowHeights(firstRow, lastRow);
37101 this.stripeRows(firstRow);
37107 bufferRows : function(markup, target, index){
37108 var before = null, trows = target.rows, tbody = target.tBodies[0];
37109 if(index < trows.length){
37110 before = trows[index];
37112 var b = document.createElement("div");
37113 b.innerHTML = "<table><tbody>"+markup+"</tbody></table>";
37114 var rows = b.firstChild.rows;
37115 for(var i = 0, len = rows.length; i < len; i++){
37117 tbody.insertBefore(rows[0], before);
37119 tbody.appendChild(rows[0]);
37126 deleteRows : function(dm, firstRow, lastRow){
37127 if(dm.getRowCount()<1){
37128 this.fireEvent("beforerefresh", this);
37129 this.mainBody.update("");
37130 this.lockedBody.update("");
37131 this.fireEvent("refresh", this);
37133 this.fireEvent("beforerowsdeleted", this, firstRow, lastRow);
37134 var bt = this.getBodyTable();
37135 var tbody = bt.firstChild;
37136 var rows = bt.rows;
37137 for(var rowIndex = firstRow; rowIndex <= lastRow; rowIndex++){
37138 tbody.removeChild(rows[firstRow]);
37140 this.stripeRows(firstRow);
37141 this.fireEvent("rowsdeleted", this, firstRow, lastRow);
37145 updateRows : function(dataSource, firstRow, lastRow){
37146 var s = this.getScrollState();
37148 this.restoreScroll(s);
37151 handleSort : function(dataSource, sortColumnIndex, sortDir, noRefresh){
37155 this.updateHeaderSortState();
37158 getScrollState : function(){
37160 var sb = this.scroller.dom;
37161 return {left: sb.scrollLeft, top: sb.scrollTop};
37164 stripeRows : function(startRow){
37165 if(!this.grid.stripeRows || this.ds.getCount() < 1){
37168 startRow = startRow || 0;
37169 var rows = this.getBodyTable().rows;
37170 var lrows = this.getLockedTable().rows;
37171 var cls = ' x-grid-row-alt ';
37172 for(var i = startRow, len = rows.length; i < len; i++){
37173 var row = rows[i], lrow = lrows[i];
37174 var isAlt = ((i+1) % 2 == 0);
37175 var hasAlt = (' '+row.className + ' ').indexOf(cls) != -1;
37176 if(isAlt == hasAlt){
37180 row.className += " x-grid-row-alt";
37182 row.className = row.className.replace("x-grid-row-alt", "");
37185 lrow.className = row.className;
37190 restoreScroll : function(state){
37191 //Roo.log('GridView.restoreScroll');
37192 var sb = this.scroller.dom;
37193 sb.scrollLeft = state.left;
37194 sb.scrollTop = state.top;
37198 syncScroll : function(){
37199 //Roo.log('GridView.syncScroll');
37200 var sb = this.scroller.dom;
37201 var sh = this.mainHd.dom;
37202 var bs = this.mainBody.dom;
37203 var lv = this.lockedBody.dom;
37204 sh.scrollLeft = bs.scrollLeft = sb.scrollLeft;
37205 lv.scrollTop = bs.scrollTop = sb.scrollTop;
37208 handleScroll : function(e){
37210 var sb = this.scroller.dom;
37211 this.grid.fireEvent("bodyscroll", sb.scrollLeft, sb.scrollTop);
37215 handleWheel : function(e){
37216 var d = e.getWheelDelta();
37217 this.scroller.dom.scrollTop -= d*22;
37218 // set this here to prevent jumpy scrolling on large tables
37219 this.lockedBody.dom.scrollTop = this.mainBody.dom.scrollTop = this.scroller.dom.scrollTop;
37223 renderRows : function(startRow, endRow){
37224 // pull in all the crap needed to render rows
37225 var g = this.grid, cm = g.colModel, ds = g.dataSource, stripe = g.stripeRows;
37226 var colCount = cm.getColumnCount();
37228 if(ds.getCount() < 1){
37232 // build a map for all the columns
37234 for(var i = 0; i < colCount; i++){
37235 var name = cm.getDataIndex(i);
37237 name : typeof name == 'undefined' ? ds.fields.get(i).name : name,
37238 renderer : cm.getRenderer(i),
37239 id : cm.getColumnId(i),
37240 locked : cm.isLocked(i)
37244 startRow = startRow || 0;
37245 endRow = typeof endRow == "undefined"? ds.getCount()-1 : endRow;
37247 // records to render
37248 var rs = ds.getRange(startRow, endRow);
37250 return this.doRender(cs, rs, ds, startRow, colCount, stripe);
37253 // As much as I hate to duplicate code, this was branched because FireFox really hates
37254 // [].join("") on strings. The performance difference was substantial enough to
37255 // branch this function
37256 doRender : Roo.isGecko ?
37257 function(cs, rs, ds, startRow, colCount, stripe){
37258 var ts = this.templates, ct = ts.cell, rt = ts.row;
37260 var buf = "", lbuf = "", cb, lcb, c, p = {}, rp = {}, r, rowIndex;
37262 var hasListener = this.grid.hasListener('rowclass');
37264 for(var j = 0, len = rs.length; j < len; j++){
37265 r = rs[j]; cb = ""; lcb = ""; rowIndex = (j+startRow);
37266 for(var i = 0; i < colCount; i++){
37268 p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
37270 p.css = p.attr = "";
37271 p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
37272 if(p.value == undefined || p.value === "") p.value = " ";
37273 if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
37274 p.css += p.css ? ' x-grid-dirty-cell' : 'x-grid-dirty-cell';
37276 var markup = ct.apply(p);
37284 if(stripe && ((rowIndex+1) % 2 == 0)){
37285 alt.push("x-grid-row-alt")
37288 alt.push( " x-grid-dirty-row");
37291 if(this.getRowClass){
37292 alt.push(this.getRowClass(r, rowIndex));
37298 rowIndex : rowIndex,
37301 this.grid.fireEvent('rowclass', this, rowcfg);
37302 alt.push(rowcfg.rowClass);
37304 rp.alt = alt.join(" ");
37305 lbuf+= rt.apply(rp);
37307 buf+= rt.apply(rp);
37309 return [lbuf, buf];
37311 function(cs, rs, ds, startRow, colCount, stripe){
37312 var ts = this.templates, ct = ts.cell, rt = ts.row;
37314 var buf = [], lbuf = [], cb, lcb, c, p = {}, rp = {}, r, rowIndex;
37315 var hasListener = this.grid.hasListener('rowclass');
37318 for(var j = 0, len = rs.length; j < len; j++){
37319 r = rs[j]; cb = []; lcb = []; rowIndex = (j+startRow);
37320 for(var i = 0; i < colCount; i++){
37322 p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
37324 p.css = p.attr = "";
37325 p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
37326 if(p.value == undefined || p.value === "") p.value = " ";
37327 if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
37328 p.css += p.css ? ' x-grid-dirty-cell' : 'x-grid-dirty-cell';
37331 var markup = ct.apply(p);
37333 cb[cb.length] = markup;
37335 lcb[lcb.length] = markup;
37339 if(stripe && ((rowIndex+1) % 2 == 0)){
37340 alt.push( "x-grid-row-alt");
37343 alt.push(" x-grid-dirty-row");
37346 if(this.getRowClass){
37347 alt.push( this.getRowClass(r, rowIndex));
37353 rowIndex : rowIndex,
37356 this.grid.fireEvent('rowclass', this, rowcfg);
37357 alt.push(rowcfg.rowClass);
37359 rp.alt = alt.join(" ");
37360 rp.cells = lcb.join("");
37361 lbuf[lbuf.length] = rt.apply(rp);
37362 rp.cells = cb.join("");
37363 buf[buf.length] = rt.apply(rp);
37365 return [lbuf.join(""), buf.join("")];
37368 renderBody : function(){
37369 var markup = this.renderRows();
37370 var bt = this.templates.body;
37371 return [bt.apply({rows: markup[0]}), bt.apply({rows: markup[1]})];
37375 * Refreshes the grid
37376 * @param {Boolean} headersToo
37378 refresh : function(headersToo){
37379 this.fireEvent("beforerefresh", this);
37380 this.grid.stopEditing();
37381 var result = this.renderBody();
37382 this.lockedBody.update(result[0]);
37383 this.mainBody.update(result[1]);
37384 if(headersToo === true){
37385 this.updateHeaders();
37386 this.updateColumns();
37387 this.updateSplitters();
37388 this.updateHeaderSortState();
37390 this.syncRowHeights();
37392 this.fireEvent("refresh", this);
37395 handleColumnMove : function(cm, oldIndex, newIndex){
37396 this.indexMap = null;
37397 var s = this.getScrollState();
37398 this.refresh(true);
37399 this.restoreScroll(s);
37400 this.afterMove(newIndex);
37403 afterMove : function(colIndex){
37404 if(this.enableMoveAnim && Roo.enableFx){
37405 this.fly(this.getHeaderCell(colIndex).firstChild).highlight(this.hlColor);
37407 // if multisort - fix sortOrder, and reload..
37408 if (this.grid.dataSource.multiSort) {
37409 // the we can call sort again..
37410 var dm = this.grid.dataSource;
37411 var cm = this.grid.colModel;
37413 for(var i = 0; i < cm.config.length; i++ ) {
37415 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined')) {
37416 continue; // dont' bother, it's not in sort list or being set.
37419 so.push(cm.config[i].dataIndex);
37422 dm.load(dm.lastOptions);
37429 updateCell : function(dm, rowIndex, dataIndex){
37430 var colIndex = this.getColumnIndexByDataIndex(dataIndex);
37431 if(typeof colIndex == "undefined"){ // not present in grid
37434 var cm = this.grid.colModel;
37435 var cell = this.getCell(rowIndex, colIndex);
37436 var cellText = this.getCellText(rowIndex, colIndex);
37439 cellId : "x-grid-cell-" + rowIndex + "-" + colIndex,
37440 id : cm.getColumnId(colIndex),
37441 css: colIndex == cm.getColumnCount()-1 ? "x-grid-col-last" : ""
37443 var renderer = cm.getRenderer(colIndex);
37444 var val = renderer(dm.getValueAt(rowIndex, dataIndex), p, rowIndex, colIndex, dm);
37445 if(typeof val == "undefined" || val === "") val = " ";
37446 cellText.innerHTML = val;
37447 cell.className = this.cellClass + " " + this.idToCssName(p.cellId) + " " + p.css;
37448 this.syncRowHeights(rowIndex, rowIndex);
37451 calcColumnWidth : function(colIndex, maxRowsToMeasure){
37453 if(this.grid.autoSizeHeaders){
37454 var h = this.getHeaderCellMeasure(colIndex);
37455 maxWidth = Math.max(maxWidth, h.scrollWidth);
37458 if(this.cm.isLocked(colIndex)){
37459 tb = this.getLockedTable();
37462 tb = this.getBodyTable();
37463 index = colIndex - this.cm.getLockedCount();
37466 var rows = tb.rows;
37467 var stopIndex = Math.min(maxRowsToMeasure || rows.length, rows.length);
37468 for(var i = 0; i < stopIndex; i++){
37469 var cell = rows[i].childNodes[index].firstChild;
37470 maxWidth = Math.max(maxWidth, cell.scrollWidth);
37473 return maxWidth + /*margin for error in IE*/ 5;
37476 * Autofit a column to its content.
37477 * @param {Number} colIndex
37478 * @param {Boolean} forceMinSize true to force the column to go smaller if possible
37480 autoSizeColumn : function(colIndex, forceMinSize, suppressEvent){
37481 if(this.cm.isHidden(colIndex)){
37482 return; // can't calc a hidden column
37485 var cid = this.cm.getColumnId(colIndex);
37486 this.css.updateRule(this.colSelector +this.idToCssName( cid), "width", this.grid.minColumnWidth + "px");
37487 if(this.grid.autoSizeHeaders){
37488 this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", this.grid.minColumnWidth + "px");
37491 var newWidth = this.calcColumnWidth(colIndex);
37492 this.cm.setColumnWidth(colIndex,
37493 Math.max(this.grid.minColumnWidth, newWidth), suppressEvent);
37494 if(!suppressEvent){
37495 this.grid.fireEvent("columnresize", colIndex, newWidth);
37500 * Autofits all columns to their content and then expands to fit any extra space in the grid
37502 autoSizeColumns : function(){
37503 var cm = this.grid.colModel;
37504 var colCount = cm.getColumnCount();
37505 for(var i = 0; i < colCount; i++){
37506 this.autoSizeColumn(i, true, true);
37508 if(cm.getTotalWidth() < this.scroller.dom.clientWidth){
37511 this.updateColumns();
37517 * Autofits all columns to the grid's width proportionate with their current size
37518 * @param {Boolean} reserveScrollSpace Reserve space for a scrollbar
37520 fitColumns : function(reserveScrollSpace){
37521 var cm = this.grid.colModel;
37522 var colCount = cm.getColumnCount();
37526 for (i = 0; i < colCount; i++){
37527 if(!cm.isHidden(i) && !cm.isFixed(i)){
37528 w = cm.getColumnWidth(i);
37534 var avail = Math.min(this.scroller.dom.clientWidth, this.el.getWidth());
37535 if(reserveScrollSpace){
37538 var frac = (avail - cm.getTotalWidth())/width;
37539 while (cols.length){
37542 cm.setColumnWidth(i, Math.floor(w + w*frac), true);
37544 this.updateColumns();
37548 onRowSelect : function(rowIndex){
37549 var row = this.getRowComposite(rowIndex);
37550 row.addClass("x-grid-row-selected");
37553 onRowDeselect : function(rowIndex){
37554 var row = this.getRowComposite(rowIndex);
37555 row.removeClass("x-grid-row-selected");
37558 onCellSelect : function(row, col){
37559 var cell = this.getCell(row, col);
37561 Roo.fly(cell).addClass("x-grid-cell-selected");
37565 onCellDeselect : function(row, col){
37566 var cell = this.getCell(row, col);
37568 Roo.fly(cell).removeClass("x-grid-cell-selected");
37572 updateHeaderSortState : function(){
37574 // sort state can be single { field: xxx, direction : yyy}
37575 // or { xxx=>ASC , yyy : DESC ..... }
37578 if (!this.ds.multiSort) {
37579 var state = this.ds.getSortState();
37583 mstate[state.field] = state.direction;
37584 // FIXME... - this is not used here.. but might be elsewhere..
37585 this.sortState = state;
37588 mstate = this.ds.sortToggle;
37590 //remove existing sort classes..
37592 var sc = this.sortClasses;
37593 var hds = this.el.select(this.headerSelector).removeClass(sc);
37595 for(var f in mstate) {
37597 var sortColumn = this.cm.findColumnIndex(f);
37599 if(sortColumn != -1){
37600 var sortDir = mstate[f];
37601 hds.item(sortColumn).addClass(sc[sortDir == "DESC" ? 1 : 0]);
37610 handleHeaderClick : function(g, index){
37611 if(this.headersDisabled){
37614 var dm = g.dataSource, cm = g.colModel;
37615 if(!cm.isSortable(index)){
37620 if (dm.multiSort) {
37621 // update the sortOrder
37623 for(var i = 0; i < cm.config.length; i++ ) {
37625 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined') && (index != i)) {
37626 continue; // dont' bother, it's not in sort list or being set.
37629 so.push(cm.config[i].dataIndex);
37635 dm.sort(cm.getDataIndex(index));
37639 destroy : function(){
37641 this.colMenu.removeAll();
37642 Roo.menu.MenuMgr.unregister(this.colMenu);
37643 this.colMenu.getEl().remove();
37644 delete this.colMenu;
37647 this.hmenu.removeAll();
37648 Roo.menu.MenuMgr.unregister(this.hmenu);
37649 this.hmenu.getEl().remove();
37652 if(this.grid.enableColumnMove){
37653 var dds = Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
37655 for(var dd in dds){
37656 if(!dds[dd].config.isTarget && dds[dd].dragElId){
37657 var elid = dds[dd].dragElId;
37659 Roo.get(elid).remove();
37660 } else if(dds[dd].config.isTarget){
37661 dds[dd].proxyTop.remove();
37662 dds[dd].proxyBottom.remove();
37665 if(Roo.dd.DDM.locationCache[dd]){
37666 delete Roo.dd.DDM.locationCache[dd];
37669 delete Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
37672 Roo.util.CSS.removeStyleSheet(this.idToCssName(this.grid.id) + '-cssrules');
37673 this.bind(null, null);
37674 Roo.EventManager.removeResizeListener(this.onWindowResize, this);
37677 handleLockChange : function(){
37678 this.refresh(true);
37681 onDenyColumnLock : function(){
37685 onDenyColumnHide : function(){
37689 handleHdMenuClick : function(item){
37690 var index = this.hdCtxIndex;
37691 var cm = this.cm, ds = this.ds;
37694 ds.sort(cm.getDataIndex(index), "ASC");
37697 ds.sort(cm.getDataIndex(index), "DESC");
37700 var lc = cm.getLockedCount();
37701 if(cm.getColumnCount(true) <= lc+1){
37702 this.onDenyColumnLock();
37706 cm.setLocked(index, true, true);
37707 cm.moveColumn(index, lc);
37708 this.grid.fireEvent("columnmove", index, lc);
37710 cm.setLocked(index, true);
37714 var lc = cm.getLockedCount();
37715 if((lc-1) != index){
37716 cm.setLocked(index, false, true);
37717 cm.moveColumn(index, lc-1);
37718 this.grid.fireEvent("columnmove", index, lc-1);
37720 cm.setLocked(index, false);
37724 index = cm.getIndexById(item.id.substr(4));
37726 if(item.checked && cm.getColumnCount(true) <= 1){
37727 this.onDenyColumnHide();
37730 cm.setHidden(index, item.checked);
37736 beforeColMenuShow : function(){
37737 var cm = this.cm, colCount = cm.getColumnCount();
37738 this.colMenu.removeAll();
37739 for(var i = 0; i < colCount; i++){
37740 this.colMenu.add(new Roo.menu.CheckItem({
37741 id: "col-"+cm.getColumnId(i),
37742 text: cm.getColumnHeader(i),
37743 checked: !cm.isHidden(i),
37749 handleHdCtx : function(g, index, e){
37751 var hd = this.getHeaderCell(index);
37752 this.hdCtxIndex = index;
37753 var ms = this.hmenu.items, cm = this.cm;
37754 ms.get("asc").setDisabled(!cm.isSortable(index));
37755 ms.get("desc").setDisabled(!cm.isSortable(index));
37756 if(this.grid.enableColLock !== false){
37757 ms.get("lock").setDisabled(cm.isLocked(index));
37758 ms.get("unlock").setDisabled(!cm.isLocked(index));
37760 this.hmenu.show(hd, "tl-bl");
37763 handleHdOver : function(e){
37764 var hd = this.findHeaderCell(e.getTarget());
37765 if(hd && !this.headersDisabled){
37766 if(this.grid.colModel.isSortable(this.getCellIndex(hd))){
37767 this.fly(hd).addClass("x-grid-hd-over");
37772 handleHdOut : function(e){
37773 var hd = this.findHeaderCell(e.getTarget());
37775 this.fly(hd).removeClass("x-grid-hd-over");
37779 handleSplitDblClick : function(e, t){
37780 var i = this.getCellIndex(t);
37781 if(this.grid.enableColumnResize !== false && this.cm.isResizable(i) && !this.cm.isFixed(i)){
37782 this.autoSizeColumn(i, true);
37787 render : function(){
37790 var colCount = cm.getColumnCount();
37792 if(this.grid.monitorWindowResize === true){
37793 Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
37795 var header = this.renderHeaders();
37796 var body = this.templates.body.apply({rows:""});
37797 var html = this.templates.master.apply({
37800 lockedHeader: header[0],
37804 //this.updateColumns();
37806 this.grid.getGridEl().dom.innerHTML = html;
37808 this.initElements();
37810 // a kludge to fix the random scolling effect in webkit
37811 this.el.on("scroll", function() {
37812 this.el.dom.scrollTop=0; // hopefully not recursive..
37815 this.scroller.on("scroll", this.handleScroll, this);
37816 this.lockedBody.on("mousewheel", this.handleWheel, this);
37817 this.mainBody.on("mousewheel", this.handleWheel, this);
37819 this.mainHd.on("mouseover", this.handleHdOver, this);
37820 this.mainHd.on("mouseout", this.handleHdOut, this);
37821 this.mainHd.on("dblclick", this.handleSplitDblClick, this,
37822 {delegate: "."+this.splitClass});
37824 this.lockedHd.on("mouseover", this.handleHdOver, this);
37825 this.lockedHd.on("mouseout", this.handleHdOut, this);
37826 this.lockedHd.on("dblclick", this.handleSplitDblClick, this,
37827 {delegate: "."+this.splitClass});
37829 if(this.grid.enableColumnResize !== false && Roo.grid.SplitDragZone){
37830 new Roo.grid.SplitDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
37833 this.updateSplitters();
37835 if(this.grid.enableColumnMove && Roo.grid.HeaderDragZone){
37836 new Roo.grid.HeaderDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
37837 new Roo.grid.HeaderDropZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
37840 if(this.grid.enableCtxMenu !== false && Roo.menu.Menu){
37841 this.hmenu = new Roo.menu.Menu({id: this.grid.id + "-hctx"});
37843 {id:"asc", text: this.sortAscText, cls: "xg-hmenu-sort-asc"},
37844 {id:"desc", text: this.sortDescText, cls: "xg-hmenu-sort-desc"}
37846 if(this.grid.enableColLock !== false){
37847 this.hmenu.add('-',
37848 {id:"lock", text: this.lockText, cls: "xg-hmenu-lock"},
37849 {id:"unlock", text: this.unlockText, cls: "xg-hmenu-unlock"}
37852 if(this.grid.enableColumnHide !== false){
37854 this.colMenu = new Roo.menu.Menu({id:this.grid.id + "-hcols-menu"});
37855 this.colMenu.on("beforeshow", this.beforeColMenuShow, this);
37856 this.colMenu.on("itemclick", this.handleHdMenuClick, this);
37858 this.hmenu.add('-',
37859 {id:"columns", text: this.columnsText, menu: this.colMenu}
37862 this.hmenu.on("itemclick", this.handleHdMenuClick, this);
37864 this.grid.on("headercontextmenu", this.handleHdCtx, this);
37867 if((this.grid.enableDragDrop || this.grid.enableDrag) && Roo.grid.GridDragZone){
37868 this.dd = new Roo.grid.GridDragZone(this.grid, {
37869 ddGroup : this.grid.ddGroup || 'GridDD'
37875 for(var i = 0; i < colCount; i++){
37876 if(cm.isHidden(i)){
37877 this.hideColumn(i);
37879 if(cm.config[i].align){
37880 this.css.updateRule(this.colSelector + i, "textAlign", cm.config[i].align);
37881 this.css.updateRule(this.hdSelector + i, "textAlign", cm.config[i].align);
37885 this.updateHeaderSortState();
37887 this.beforeInitialResize();
37890 // two part rendering gives faster view to the user
37891 this.renderPhase2.defer(1, this);
37894 renderPhase2 : function(){
37895 // render the rows now
37897 if(this.grid.autoSizeColumns){
37898 this.autoSizeColumns();
37902 beforeInitialResize : function(){
37906 onColumnSplitterMoved : function(i, w){
37907 this.userResized = true;
37908 var cm = this.grid.colModel;
37909 cm.setColumnWidth(i, w, true);
37910 var cid = cm.getColumnId(i);
37911 this.css.updateRule(this.colSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
37912 this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
37913 this.updateSplitters();
37915 this.grid.fireEvent("columnresize", i, w);
37918 syncRowHeights : function(startIndex, endIndex){
37919 if(this.grid.enableRowHeightSync === true && this.cm.getLockedCount() > 0){
37920 startIndex = startIndex || 0;
37921 var mrows = this.getBodyTable().rows;
37922 var lrows = this.getLockedTable().rows;
37923 var len = mrows.length-1;
37924 endIndex = Math.min(endIndex || len, len);
37925 for(var i = startIndex; i <= endIndex; i++){
37926 var m = mrows[i], l = lrows[i];
37927 var h = Math.max(m.offsetHeight, l.offsetHeight);
37928 m.style.height = l.style.height = h + "px";
37933 layout : function(initialRender, is2ndPass){
37935 var auto = g.autoHeight;
37936 var scrollOffset = 16;
37937 var c = g.getGridEl(), cm = this.cm,
37938 expandCol = g.autoExpandColumn,
37940 //c.beginMeasure();
37942 if(!c.dom.offsetWidth){ // display:none?
37944 this.lockedWrap.show();
37945 this.mainWrap.show();
37950 var hasLock = this.cm.isLocked(0);
37952 var tbh = this.headerPanel.getHeight();
37953 var bbh = this.footerPanel.getHeight();
37956 var ch = this.getBodyTable().offsetHeight + tbh + bbh + this.mainHd.getHeight();
37957 var newHeight = ch + c.getBorderWidth("tb");
37959 newHeight = Math.min(g.maxHeight, newHeight);
37961 c.setHeight(newHeight);
37965 c.setWidth(cm.getTotalWidth()+c.getBorderWidth('lr'));
37968 var s = this.scroller;
37970 var csize = c.getSize(true);
37972 this.el.setSize(csize.width, csize.height);
37974 this.headerPanel.setWidth(csize.width);
37975 this.footerPanel.setWidth(csize.width);
37977 var hdHeight = this.mainHd.getHeight();
37978 var vw = csize.width;
37979 var vh = csize.height - (tbh + bbh);
37983 var bt = this.getBodyTable();
37984 var ltWidth = hasLock ?
37985 Math.max(this.getLockedTable().offsetWidth, this.lockedHd.dom.firstChild.offsetWidth) : 0;
37987 var scrollHeight = bt.offsetHeight;
37988 var scrollWidth = ltWidth + bt.offsetWidth;
37989 var vscroll = false, hscroll = false;
37991 this.scrollSizer.setSize(scrollWidth, scrollHeight+hdHeight);
37993 var lw = this.lockedWrap, mw = this.mainWrap;
37994 var lb = this.lockedBody, mb = this.mainBody;
37996 setTimeout(function(){
37997 var t = s.dom.offsetTop;
37998 var w = s.dom.clientWidth,
37999 h = s.dom.clientHeight;
38002 lw.setSize(ltWidth, h);
38004 mw.setLeftTop(ltWidth, t);
38005 mw.setSize(w-ltWidth, h);
38007 lb.setHeight(h-hdHeight);
38008 mb.setHeight(h-hdHeight);
38010 if(is2ndPass !== true && !gv.userResized && expandCol){
38011 // high speed resize without full column calculation
38013 var ci = cm.getIndexById(expandCol);
38015 ci = cm.findColumnIndex(expandCol);
38017 ci = Math.max(0, ci); // make sure it's got at least the first col.
38018 var expandId = cm.getColumnId(ci);
38019 var tw = cm.getTotalWidth(false);
38020 var currentWidth = cm.getColumnWidth(ci);
38021 var cw = Math.min(Math.max(((w-tw)+currentWidth-2)-/*scrollbar*/(w <= s.dom.offsetWidth ? 0 : 18), g.autoExpandMin), g.autoExpandMax);
38022 if(currentWidth != cw){
38023 cm.setColumnWidth(ci, cw, true);
38024 gv.css.updateRule(gv.colSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
38025 gv.css.updateRule(gv.hdSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
38026 gv.updateSplitters();
38027 gv.layout(false, true);
38039 onWindowResize : function(){
38040 if(!this.grid.monitorWindowResize || this.grid.autoHeight){
38046 appendFooter : function(parentEl){
38050 sortAscText : "Sort Ascending",
38051 sortDescText : "Sort Descending",
38052 lockText : "Lock Column",
38053 unlockText : "Unlock Column",
38054 columnsText : "Columns"
38058 Roo.grid.GridView.ColumnDragZone = function(grid, hd){
38059 Roo.grid.GridView.ColumnDragZone.superclass.constructor.call(this, grid, hd, null);
38060 this.proxy.el.addClass('x-grid3-col-dd');
38063 Roo.extend(Roo.grid.GridView.ColumnDragZone, Roo.grid.HeaderDragZone, {
38064 handleMouseDown : function(e){
38068 callHandleMouseDown : function(e){
38069 Roo.grid.GridView.ColumnDragZone.superclass.handleMouseDown.call(this, e);
38074 * Ext JS Library 1.1.1
38075 * Copyright(c) 2006-2007, Ext JS, LLC.
38077 * Originally Released Under LGPL - original licence link has changed is not relivant.
38080 * <script type="text/javascript">
38084 // This is a support class used internally by the Grid components
38085 Roo.grid.SplitDragZone = function(grid, hd, hd2){
38087 this.view = grid.getView();
38088 this.proxy = this.view.resizeProxy;
38089 Roo.grid.SplitDragZone.superclass.constructor.call(this, hd,
38090 "gridSplitters" + this.grid.getGridEl().id, {
38091 dragElId : Roo.id(this.proxy.dom), resizeFrame:false
38093 this.setHandleElId(Roo.id(hd));
38094 this.setOuterHandleElId(Roo.id(hd2));
38095 this.scroll = false;
38097 Roo.extend(Roo.grid.SplitDragZone, Roo.dd.DDProxy, {
38098 fly: Roo.Element.fly,
38100 b4StartDrag : function(x, y){
38101 this.view.headersDisabled = true;
38102 this.proxy.setHeight(this.view.mainWrap.getHeight());
38103 var w = this.cm.getColumnWidth(this.cellIndex);
38104 var minw = Math.max(w-this.grid.minColumnWidth, 0);
38105 this.resetConstraints();
38106 this.setXConstraint(minw, 1000);
38107 this.setYConstraint(0, 0);
38108 this.minX = x - minw;
38109 this.maxX = x + 1000;
38111 Roo.dd.DDProxy.prototype.b4StartDrag.call(this, x, y);
38115 handleMouseDown : function(e){
38116 ev = Roo.EventObject.setEvent(e);
38117 var t = this.fly(ev.getTarget());
38118 if(t.hasClass("x-grid-split")){
38119 this.cellIndex = this.view.getCellIndex(t.dom);
38120 this.split = t.dom;
38121 this.cm = this.grid.colModel;
38122 if(this.cm.isResizable(this.cellIndex) && !this.cm.isFixed(this.cellIndex)){
38123 Roo.grid.SplitDragZone.superclass.handleMouseDown.apply(this, arguments);
38128 endDrag : function(e){
38129 this.view.headersDisabled = false;
38130 var endX = Math.max(this.minX, Roo.lib.Event.getPageX(e));
38131 var diff = endX - this.startPos;
38132 this.view.onColumnSplitterMoved(this.cellIndex, this.cm.getColumnWidth(this.cellIndex)+diff);
38135 autoOffset : function(){
38136 this.setDelta(0,0);
38140 * Ext JS Library 1.1.1
38141 * Copyright(c) 2006-2007, Ext JS, LLC.
38143 * Originally Released Under LGPL - original licence link has changed is not relivant.
38146 * <script type="text/javascript">
38150 // This is a support class used internally by the Grid components
38151 Roo.grid.GridDragZone = function(grid, config){
38152 this.view = grid.getView();
38153 Roo.grid.GridDragZone.superclass.constructor.call(this, this.view.mainBody.dom, config);
38154 if(this.view.lockedBody){
38155 this.setHandleElId(Roo.id(this.view.mainBody.dom));
38156 this.setOuterHandleElId(Roo.id(this.view.lockedBody.dom));
38158 this.scroll = false;
38160 this.ddel = document.createElement('div');
38161 this.ddel.className = 'x-grid-dd-wrap';
38164 Roo.extend(Roo.grid.GridDragZone, Roo.dd.DragZone, {
38165 ddGroup : "GridDD",
38167 getDragData : function(e){
38168 var t = Roo.lib.Event.getTarget(e);
38169 var rowIndex = this.view.findRowIndex(t);
38170 var sm = this.grid.selModel;
38172 //Roo.log(rowIndex);
38174 if (sm.getSelectedCell) {
38175 // cell selection..
38176 if (!sm.getSelectedCell()) {
38179 if (rowIndex != sm.getSelectedCell()[0]) {
38185 if(rowIndex !== false){
38190 //Roo.log([ sm.getSelectedCell() ? sm.getSelectedCell()[0] : 'NO' , rowIndex ]);
38192 //if(!sm.isSelected(rowIndex) || e.hasModifier()){
38195 if (e.hasModifier()){
38196 sm.handleMouseDown(e, t); // non modifier buttons are handled by row select.
38199 Roo.log("getDragData");
38204 rowIndex: rowIndex,
38205 selections:sm.getSelections ? sm.getSelections() : (
38206 sm.getSelectedCell() ? [ this.grid.ds.getAt(sm.getSelectedCell()[0]) ] : []
38213 onInitDrag : function(e){
38214 var data = this.dragData;
38215 this.ddel.innerHTML = this.grid.getDragDropText();
38216 this.proxy.update(this.ddel);
38217 // fire start drag?
38220 afterRepair : function(){
38221 this.dragging = false;
38224 getRepairXY : function(e, data){
38228 onEndDrag : function(data, e){
38232 onValidDrop : function(dd, e, id){
38237 beforeInvalidDrop : function(e, id){
38242 * Ext JS Library 1.1.1
38243 * Copyright(c) 2006-2007, Ext JS, LLC.
38245 * Originally Released Under LGPL - original licence link has changed is not relivant.
38248 * <script type="text/javascript">
38253 * @class Roo.grid.ColumnModel
38254 * @extends Roo.util.Observable
38255 * This is the default implementation of a ColumnModel used by the Grid. It defines
38256 * the columns in the grid.
38259 var colModel = new Roo.grid.ColumnModel([
38260 {header: "Ticker", width: 60, sortable: true, locked: true},
38261 {header: "Company Name", width: 150, sortable: true},
38262 {header: "Market Cap.", width: 100, sortable: true},
38263 {header: "$ Sales", width: 100, sortable: true, renderer: money},
38264 {header: "Employees", width: 100, sortable: true, resizable: false}
38269 * The config options listed for this class are options which may appear in each
38270 * individual column definition.
38271 * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
38273 * @param {Object} config An Array of column config objects. See this class's
38274 * config objects for details.
38276 Roo.grid.ColumnModel = function(config){
38278 * The config passed into the constructor
38280 this.config = config;
38283 // if no id, create one
38284 // if the column does not have a dataIndex mapping,
38285 // map it to the order it is in the config
38286 for(var i = 0, len = config.length; i < len; i++){
38288 if(typeof c.dataIndex == "undefined"){
38291 if(typeof c.renderer == "string"){
38292 c.renderer = Roo.util.Format[c.renderer];
38294 if(typeof c.id == "undefined"){
38297 if(c.editor && c.editor.xtype){
38298 c.editor = Roo.factory(c.editor, Roo.grid);
38300 if(c.editor && c.editor.isFormField){
38301 c.editor = new Roo.grid.GridEditor(c.editor);
38303 this.lookup[c.id] = c;
38307 * The width of columns which have no width specified (defaults to 100)
38310 this.defaultWidth = 100;
38313 * Default sortable of columns which have no sortable specified (defaults to false)
38316 this.defaultSortable = false;
38320 * @event widthchange
38321 * Fires when the width of a column changes.
38322 * @param {ColumnModel} this
38323 * @param {Number} columnIndex The column index
38324 * @param {Number} newWidth The new width
38326 "widthchange": true,
38328 * @event headerchange
38329 * Fires when the text of a header changes.
38330 * @param {ColumnModel} this
38331 * @param {Number} columnIndex The column index
38332 * @param {Number} newText The new header text
38334 "headerchange": true,
38336 * @event hiddenchange
38337 * Fires when a column is hidden or "unhidden".
38338 * @param {ColumnModel} this
38339 * @param {Number} columnIndex The column index
38340 * @param {Boolean} hidden true if hidden, false otherwise
38342 "hiddenchange": true,
38344 * @event columnmoved
38345 * Fires when a column is moved.
38346 * @param {ColumnModel} this
38347 * @param {Number} oldIndex
38348 * @param {Number} newIndex
38350 "columnmoved" : true,
38352 * @event columlockchange
38353 * Fires when a column's locked state is changed
38354 * @param {ColumnModel} this
38355 * @param {Number} colIndex
38356 * @param {Boolean} locked true if locked
38358 "columnlockchange" : true
38360 Roo.grid.ColumnModel.superclass.constructor.call(this);
38362 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
38364 * @cfg {String} header The header text to display in the Grid view.
38367 * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
38368 * {@link Roo.data.Record} definition from which to draw the column's value. If not
38369 * specified, the column's index is used as an index into the Record's data Array.
38372 * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
38373 * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
38376 * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
38377 * Defaults to the value of the {@link #defaultSortable} property.
38378 * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
38381 * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid. Defaults to false.
38384 * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed. Defaults to false.
38387 * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
38390 * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
38393 * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
38394 * given the cell's data value. See {@link #setRenderer}. If not specified, the
38395 * default renderer uses the raw data value.
38398 * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor
38401 * @cfg {String} align (Optional) Set the CSS text-align property of the column. Defaults to undefined.
38405 * Returns the id of the column at the specified index.
38406 * @param {Number} index The column index
38407 * @return {String} the id
38409 getColumnId : function(index){
38410 return this.config[index].id;
38414 * Returns the column for a specified id.
38415 * @param {String} id The column id
38416 * @return {Object} the column
38418 getColumnById : function(id){
38419 return this.lookup[id];
38424 * Returns the column for a specified dataIndex.
38425 * @param {String} dataIndex The column dataIndex
38426 * @return {Object|Boolean} the column or false if not found
38428 getColumnByDataIndex: function(dataIndex){
38429 var index = this.findColumnIndex(dataIndex);
38430 return index > -1 ? this.config[index] : false;
38434 * Returns the index for a specified column id.
38435 * @param {String} id The column id
38436 * @return {Number} the index, or -1 if not found
38438 getIndexById : function(id){
38439 for(var i = 0, len = this.config.length; i < len; i++){
38440 if(this.config[i].id == id){
38448 * Returns the index for a specified column dataIndex.
38449 * @param {String} dataIndex The column dataIndex
38450 * @return {Number} the index, or -1 if not found
38453 findColumnIndex : function(dataIndex){
38454 for(var i = 0, len = this.config.length; i < len; i++){
38455 if(this.config[i].dataIndex == dataIndex){
38463 moveColumn : function(oldIndex, newIndex){
38464 var c = this.config[oldIndex];
38465 this.config.splice(oldIndex, 1);
38466 this.config.splice(newIndex, 0, c);
38467 this.dataMap = null;
38468 this.fireEvent("columnmoved", this, oldIndex, newIndex);
38471 isLocked : function(colIndex){
38472 return this.config[colIndex].locked === true;
38475 setLocked : function(colIndex, value, suppressEvent){
38476 if(this.isLocked(colIndex) == value){
38479 this.config[colIndex].locked = value;
38480 if(!suppressEvent){
38481 this.fireEvent("columnlockchange", this, colIndex, value);
38485 getTotalLockedWidth : function(){
38486 var totalWidth = 0;
38487 for(var i = 0; i < this.config.length; i++){
38488 if(this.isLocked(i) && !this.isHidden(i)){
38489 this.totalWidth += this.getColumnWidth(i);
38495 getLockedCount : function(){
38496 for(var i = 0, len = this.config.length; i < len; i++){
38497 if(!this.isLocked(i)){
38504 * Returns the number of columns.
38507 getColumnCount : function(visibleOnly){
38508 if(visibleOnly === true){
38510 for(var i = 0, len = this.config.length; i < len; i++){
38511 if(!this.isHidden(i)){
38517 return this.config.length;
38521 * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
38522 * @param {Function} fn
38523 * @param {Object} scope (optional)
38524 * @return {Array} result
38526 getColumnsBy : function(fn, scope){
38528 for(var i = 0, len = this.config.length; i < len; i++){
38529 var c = this.config[i];
38530 if(fn.call(scope||this, c, i) === true){
38538 * Returns true if the specified column is sortable.
38539 * @param {Number} col The column index
38540 * @return {Boolean}
38542 isSortable : function(col){
38543 if(typeof this.config[col].sortable == "undefined"){
38544 return this.defaultSortable;
38546 return this.config[col].sortable;
38550 * Returns the rendering (formatting) function defined for the column.
38551 * @param {Number} col The column index.
38552 * @return {Function} The function used to render the cell. See {@link #setRenderer}.
38554 getRenderer : function(col){
38555 if(!this.config[col].renderer){
38556 return Roo.grid.ColumnModel.defaultRenderer;
38558 return this.config[col].renderer;
38562 * Sets the rendering (formatting) function for a column.
38563 * @param {Number} col The column index
38564 * @param {Function} fn The function to use to process the cell's raw data
38565 * to return HTML markup for the grid view. The render function is called with
38566 * the following parameters:<ul>
38567 * <li>Data value.</li>
38568 * <li>Cell metadata. An object in which you may set the following attributes:<ul>
38569 * <li>css A CSS style string to apply to the table cell.</li>
38570 * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
38571 * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
38572 * <li>Row index</li>
38573 * <li>Column index</li>
38574 * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
38576 setRenderer : function(col, fn){
38577 this.config[col].renderer = fn;
38581 * Returns the width for the specified column.
38582 * @param {Number} col The column index
38585 getColumnWidth : function(col){
38586 return this.config[col].width * 1 || this.defaultWidth;
38590 * Sets the width for a column.
38591 * @param {Number} col The column index
38592 * @param {Number} width The new width
38594 setColumnWidth : function(col, width, suppressEvent){
38595 this.config[col].width = width;
38596 this.totalWidth = null;
38597 if(!suppressEvent){
38598 this.fireEvent("widthchange", this, col, width);
38603 * Returns the total width of all columns.
38604 * @param {Boolean} includeHidden True to include hidden column widths
38607 getTotalWidth : function(includeHidden){
38608 if(!this.totalWidth){
38609 this.totalWidth = 0;
38610 for(var i = 0, len = this.config.length; i < len; i++){
38611 if(includeHidden || !this.isHidden(i)){
38612 this.totalWidth += this.getColumnWidth(i);
38616 return this.totalWidth;
38620 * Returns the header for the specified column.
38621 * @param {Number} col The column index
38624 getColumnHeader : function(col){
38625 return this.config[col].header;
38629 * Sets the header for a column.
38630 * @param {Number} col The column index
38631 * @param {String} header The new header
38633 setColumnHeader : function(col, header){
38634 this.config[col].header = header;
38635 this.fireEvent("headerchange", this, col, header);
38639 * Returns the tooltip for the specified column.
38640 * @param {Number} col The column index
38643 getColumnTooltip : function(col){
38644 return this.config[col].tooltip;
38647 * Sets the tooltip for a column.
38648 * @param {Number} col The column index
38649 * @param {String} tooltip The new tooltip
38651 setColumnTooltip : function(col, tooltip){
38652 this.config[col].tooltip = tooltip;
38656 * Returns the dataIndex for the specified column.
38657 * @param {Number} col The column index
38660 getDataIndex : function(col){
38661 return this.config[col].dataIndex;
38665 * Sets the dataIndex for a column.
38666 * @param {Number} col The column index
38667 * @param {Number} dataIndex The new dataIndex
38669 setDataIndex : function(col, dataIndex){
38670 this.config[col].dataIndex = dataIndex;
38676 * Returns true if the cell is editable.
38677 * @param {Number} colIndex The column index
38678 * @param {Number} rowIndex The row index
38679 * @return {Boolean}
38681 isCellEditable : function(colIndex, rowIndex){
38682 return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
38686 * Returns the editor defined for the cell/column.
38687 * return false or null to disable editing.
38688 * @param {Number} colIndex The column index
38689 * @param {Number} rowIndex The row index
38692 getCellEditor : function(colIndex, rowIndex){
38693 return this.config[colIndex].editor;
38697 * Sets if a column is editable.
38698 * @param {Number} col The column index
38699 * @param {Boolean} editable True if the column is editable
38701 setEditable : function(col, editable){
38702 this.config[col].editable = editable;
38707 * Returns true if the column is hidden.
38708 * @param {Number} colIndex The column index
38709 * @return {Boolean}
38711 isHidden : function(colIndex){
38712 return this.config[colIndex].hidden;
38717 * Returns true if the column width cannot be changed
38719 isFixed : function(colIndex){
38720 return this.config[colIndex].fixed;
38724 * Returns true if the column can be resized
38725 * @return {Boolean}
38727 isResizable : function(colIndex){
38728 return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
38731 * Sets if a column is hidden.
38732 * @param {Number} colIndex The column index
38733 * @param {Boolean} hidden True if the column is hidden
38735 setHidden : function(colIndex, hidden){
38736 this.config[colIndex].hidden = hidden;
38737 this.totalWidth = null;
38738 this.fireEvent("hiddenchange", this, colIndex, hidden);
38742 * Sets the editor for a column.
38743 * @param {Number} col The column index
38744 * @param {Object} editor The editor object
38746 setEditor : function(col, editor){
38747 this.config[col].editor = editor;
38751 Roo.grid.ColumnModel.defaultRenderer = function(value){
38752 if(typeof value == "string" && value.length < 1){
38758 // Alias for backwards compatibility
38759 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
38762 * Ext JS Library 1.1.1
38763 * Copyright(c) 2006-2007, Ext JS, LLC.
38765 * Originally Released Under LGPL - original licence link has changed is not relivant.
38768 * <script type="text/javascript">
38772 * @class Roo.grid.AbstractSelectionModel
38773 * @extends Roo.util.Observable
38774 * Abstract base class for grid SelectionModels. It provides the interface that should be
38775 * implemented by descendant classes. This class should not be directly instantiated.
38778 Roo.grid.AbstractSelectionModel = function(){
38779 this.locked = false;
38780 Roo.grid.AbstractSelectionModel.superclass.constructor.call(this);
38783 Roo.extend(Roo.grid.AbstractSelectionModel, Roo.util.Observable, {
38784 /** @ignore Called by the grid automatically. Do not call directly. */
38785 init : function(grid){
38791 * Locks the selections.
38794 this.locked = true;
38798 * Unlocks the selections.
38800 unlock : function(){
38801 this.locked = false;
38805 * Returns true if the selections are locked.
38806 * @return {Boolean}
38808 isLocked : function(){
38809 return this.locked;
38813 * Ext JS Library 1.1.1
38814 * Copyright(c) 2006-2007, Ext JS, LLC.
38816 * Originally Released Under LGPL - original licence link has changed is not relivant.
38819 * <script type="text/javascript">
38822 * @extends Roo.grid.AbstractSelectionModel
38823 * @class Roo.grid.RowSelectionModel
38824 * The default SelectionModel used by {@link Roo.grid.Grid}.
38825 * It supports multiple selections and keyboard selection/navigation.
38827 * @param {Object} config
38829 Roo.grid.RowSelectionModel = function(config){
38830 Roo.apply(this, config);
38831 this.selections = new Roo.util.MixedCollection(false, function(o){
38836 this.lastActive = false;
38840 * @event selectionchange
38841 * Fires when the selection changes
38842 * @param {SelectionModel} this
38844 "selectionchange" : true,
38846 * @event afterselectionchange
38847 * Fires after the selection changes (eg. by key press or clicking)
38848 * @param {SelectionModel} this
38850 "afterselectionchange" : true,
38852 * @event beforerowselect
38853 * Fires when a row is selected being selected, return false to cancel.
38854 * @param {SelectionModel} this
38855 * @param {Number} rowIndex The selected index
38856 * @param {Boolean} keepExisting False if other selections will be cleared
38858 "beforerowselect" : true,
38861 * Fires when a row is selected.
38862 * @param {SelectionModel} this
38863 * @param {Number} rowIndex The selected index
38864 * @param {Roo.data.Record} r The record
38866 "rowselect" : true,
38868 * @event rowdeselect
38869 * Fires when a row is deselected.
38870 * @param {SelectionModel} this
38871 * @param {Number} rowIndex The selected index
38873 "rowdeselect" : true
38875 Roo.grid.RowSelectionModel.superclass.constructor.call(this);
38876 this.locked = false;
38879 Roo.extend(Roo.grid.RowSelectionModel, Roo.grid.AbstractSelectionModel, {
38881 * @cfg {Boolean} singleSelect
38882 * True to allow selection of only one row at a time (defaults to false)
38884 singleSelect : false,
38887 initEvents : function(){
38889 if(!this.grid.enableDragDrop && !this.grid.enableDrag){
38890 this.grid.on("mousedown", this.handleMouseDown, this);
38891 }else{ // allow click to work like normal
38892 this.grid.on("rowclick", this.handleDragableRowClick, this);
38895 this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
38896 "up" : function(e){
38898 this.selectPrevious(e.shiftKey);
38899 }else if(this.last !== false && this.lastActive !== false){
38900 var last = this.last;
38901 this.selectRange(this.last, this.lastActive-1);
38902 this.grid.getView().focusRow(this.lastActive);
38903 if(last !== false){
38907 this.selectFirstRow();
38909 this.fireEvent("afterselectionchange", this);
38911 "down" : function(e){
38913 this.selectNext(e.shiftKey);
38914 }else if(this.last !== false && this.lastActive !== false){
38915 var last = this.last;
38916 this.selectRange(this.last, this.lastActive+1);
38917 this.grid.getView().focusRow(this.lastActive);
38918 if(last !== false){
38922 this.selectFirstRow();
38924 this.fireEvent("afterselectionchange", this);
38929 var view = this.grid.view;
38930 view.on("refresh", this.onRefresh, this);
38931 view.on("rowupdated", this.onRowUpdated, this);
38932 view.on("rowremoved", this.onRemove, this);
38936 onRefresh : function(){
38937 var ds = this.grid.dataSource, i, v = this.grid.view;
38938 var s = this.selections;
38939 s.each(function(r){
38940 if((i = ds.indexOfId(r.id)) != -1){
38949 onRemove : function(v, index, r){
38950 this.selections.remove(r);
38954 onRowUpdated : function(v, index, r){
38955 if(this.isSelected(r)){
38956 v.onRowSelect(index);
38962 * @param {Array} records The records to select
38963 * @param {Boolean} keepExisting (optional) True to keep existing selections
38965 selectRecords : function(records, keepExisting){
38967 this.clearSelections();
38969 var ds = this.grid.dataSource;
38970 for(var i = 0, len = records.length; i < len; i++){
38971 this.selectRow(ds.indexOf(records[i]), true);
38976 * Gets the number of selected rows.
38979 getCount : function(){
38980 return this.selections.length;
38984 * Selects the first row in the grid.
38986 selectFirstRow : function(){
38991 * Select the last row.
38992 * @param {Boolean} keepExisting (optional) True to keep existing selections
38994 selectLastRow : function(keepExisting){
38995 this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
38999 * Selects the row immediately following the last selected row.
39000 * @param {Boolean} keepExisting (optional) True to keep existing selections
39002 selectNext : function(keepExisting){
39003 if(this.last !== false && (this.last+1) < this.grid.dataSource.getCount()){
39004 this.selectRow(this.last+1, keepExisting);
39005 this.grid.getView().focusRow(this.last);
39010 * Selects the row that precedes the last selected row.
39011 * @param {Boolean} keepExisting (optional) True to keep existing selections
39013 selectPrevious : function(keepExisting){
39015 this.selectRow(this.last-1, keepExisting);
39016 this.grid.getView().focusRow(this.last);
39021 * Returns the selected records
39022 * @return {Array} Array of selected records
39024 getSelections : function(){
39025 return [].concat(this.selections.items);
39029 * Returns the first selected record.
39032 getSelected : function(){
39033 return this.selections.itemAt(0);
39038 * Clears all selections.
39040 clearSelections : function(fast){
39041 if(this.locked) return;
39043 var ds = this.grid.dataSource;
39044 var s = this.selections;
39045 s.each(function(r){
39046 this.deselectRow(ds.indexOfId(r.id));
39050 this.selections.clear();
39057 * Selects all rows.
39059 selectAll : function(){
39060 if(this.locked) return;
39061 this.selections.clear();
39062 for(var i = 0, len = this.grid.dataSource.getCount(); i < len; i++){
39063 this.selectRow(i, true);
39068 * Returns True if there is a selection.
39069 * @return {Boolean}
39071 hasSelection : function(){
39072 return this.selections.length > 0;
39076 * Returns True if the specified row is selected.
39077 * @param {Number/Record} record The record or index of the record to check
39078 * @return {Boolean}
39080 isSelected : function(index){
39081 var r = typeof index == "number" ? this.grid.dataSource.getAt(index) : index;
39082 return (r && this.selections.key(r.id) ? true : false);
39086 * Returns True if the specified record id is selected.
39087 * @param {String} id The id of record to check
39088 * @return {Boolean}
39090 isIdSelected : function(id){
39091 return (this.selections.key(id) ? true : false);
39095 handleMouseDown : function(e, t){
39096 var view = this.grid.getView(), rowIndex;
39097 if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
39100 if(e.shiftKey && this.last !== false){
39101 var last = this.last;
39102 this.selectRange(last, rowIndex, e.ctrlKey);
39103 this.last = last; // reset the last
39104 view.focusRow(rowIndex);
39106 var isSelected = this.isSelected(rowIndex);
39107 if(e.button !== 0 && isSelected){
39108 view.focusRow(rowIndex);
39109 }else if(e.ctrlKey && isSelected){
39110 this.deselectRow(rowIndex);
39111 }else if(!isSelected){
39112 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
39113 view.focusRow(rowIndex);
39116 this.fireEvent("afterselectionchange", this);
39119 handleDragableRowClick : function(grid, rowIndex, e)
39121 if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
39122 this.selectRow(rowIndex, false);
39123 grid.view.focusRow(rowIndex);
39124 this.fireEvent("afterselectionchange", this);
39129 * Selects multiple rows.
39130 * @param {Array} rows Array of the indexes of the row to select
39131 * @param {Boolean} keepExisting (optional) True to keep existing selections
39133 selectRows : function(rows, keepExisting){
39135 this.clearSelections();
39137 for(var i = 0, len = rows.length; i < len; i++){
39138 this.selectRow(rows[i], true);
39143 * Selects a range of rows. All rows in between startRow and endRow are also selected.
39144 * @param {Number} startRow The index of the first row in the range
39145 * @param {Number} endRow The index of the last row in the range
39146 * @param {Boolean} keepExisting (optional) True to retain existing selections
39148 selectRange : function(startRow, endRow, keepExisting){
39149 if(this.locked) return;
39151 this.clearSelections();
39153 if(startRow <= endRow){
39154 for(var i = startRow; i <= endRow; i++){
39155 this.selectRow(i, true);
39158 for(var i = startRow; i >= endRow; i--){
39159 this.selectRow(i, true);
39165 * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
39166 * @param {Number} startRow The index of the first row in the range
39167 * @param {Number} endRow The index of the last row in the range
39169 deselectRange : function(startRow, endRow, preventViewNotify){
39170 if(this.locked) return;
39171 for(var i = startRow; i <= endRow; i++){
39172 this.deselectRow(i, preventViewNotify);
39178 * @param {Number} row The index of the row to select
39179 * @param {Boolean} keepExisting (optional) True to keep existing selections
39181 selectRow : function(index, keepExisting, preventViewNotify){
39182 if(this.locked || (index < 0 || index >= this.grid.dataSource.getCount())) return;
39183 if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
39184 if(!keepExisting || this.singleSelect){
39185 this.clearSelections();
39187 var r = this.grid.dataSource.getAt(index);
39188 this.selections.add(r);
39189 this.last = this.lastActive = index;
39190 if(!preventViewNotify){
39191 this.grid.getView().onRowSelect(index);
39193 this.fireEvent("rowselect", this, index, r);
39194 this.fireEvent("selectionchange", this);
39200 * @param {Number} row The index of the row to deselect
39202 deselectRow : function(index, preventViewNotify){
39203 if(this.locked) return;
39204 if(this.last == index){
39207 if(this.lastActive == index){
39208 this.lastActive = false;
39210 var r = this.grid.dataSource.getAt(index);
39211 this.selections.remove(r);
39212 if(!preventViewNotify){
39213 this.grid.getView().onRowDeselect(index);
39215 this.fireEvent("rowdeselect", this, index);
39216 this.fireEvent("selectionchange", this);
39220 restoreLast : function(){
39222 this.last = this._last;
39227 acceptsNav : function(row, col, cm){
39228 return !cm.isHidden(col) && cm.isCellEditable(col, row);
39232 onEditorKey : function(field, e){
39233 var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
39238 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
39240 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
39242 }else if(k == e.ENTER && !e.ctrlKey){
39246 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
39248 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
39250 }else if(k == e.ESC){
39254 g.startEditing(newCell[0], newCell[1]);
39259 * Ext JS Library 1.1.1
39260 * Copyright(c) 2006-2007, Ext JS, LLC.
39262 * Originally Released Under LGPL - original licence link has changed is not relivant.
39265 * <script type="text/javascript">
39268 * @class Roo.grid.CellSelectionModel
39269 * @extends Roo.grid.AbstractSelectionModel
39270 * This class provides the basic implementation for cell selection in a grid.
39272 * @param {Object} config The object containing the configuration of this model.
39273 * @cfg {Boolean} enter_is_tab Enter behaves the same as tab. (eg. goes to next cell) default: false
39275 Roo.grid.CellSelectionModel = function(config){
39276 Roo.apply(this, config);
39278 this.selection = null;
39282 * @event beforerowselect
39283 * Fires before a cell is selected.
39284 * @param {SelectionModel} this
39285 * @param {Number} rowIndex The selected row index
39286 * @param {Number} colIndex The selected cell index
39288 "beforecellselect" : true,
39290 * @event cellselect
39291 * Fires when a cell is selected.
39292 * @param {SelectionModel} this
39293 * @param {Number} rowIndex The selected row index
39294 * @param {Number} colIndex The selected cell index
39296 "cellselect" : true,
39298 * @event selectionchange
39299 * Fires when the active selection changes.
39300 * @param {SelectionModel} this
39301 * @param {Object} selection null for no selection or an object (o) with two properties
39303 <li>o.record: the record object for the row the selection is in</li>
39304 <li>o.cell: An array of [rowIndex, columnIndex]</li>
39307 "selectionchange" : true,
39310 * Fires when the tab (or enter) was pressed on the last editable cell
39311 * You can use this to trigger add new row.
39312 * @param {SelectionModel} this
39316 * @event beforeeditnext
39317 * Fires before the next editable sell is made active
39318 * You can use this to skip to another cell or fire the tabend
39319 * if you set cell to false
39320 * @param {Object} eventdata object : { cell : [ row, col ] }
39322 "beforeeditnext" : true
39324 Roo.grid.CellSelectionModel.superclass.constructor.call(this);
39327 Roo.extend(Roo.grid.CellSelectionModel, Roo.grid.AbstractSelectionModel, {
39329 enter_is_tab: false,
39332 initEvents : function(){
39333 this.grid.on("mousedown", this.handleMouseDown, this);
39334 this.grid.getGridEl().on(Roo.isIE ? "keydown" : "keypress", this.handleKeyDown, this);
39335 var view = this.grid.view;
39336 view.on("refresh", this.onViewChange, this);
39337 view.on("rowupdated", this.onRowUpdated, this);
39338 view.on("beforerowremoved", this.clearSelections, this);
39339 view.on("beforerowsinserted", this.clearSelections, this);
39340 if(this.grid.isEditor){
39341 this.grid.on("beforeedit", this.beforeEdit, this);
39346 beforeEdit : function(e){
39347 this.select(e.row, e.column, false, true, e.record);
39351 onRowUpdated : function(v, index, r){
39352 if(this.selection && this.selection.record == r){
39353 v.onCellSelect(index, this.selection.cell[1]);
39358 onViewChange : function(){
39359 this.clearSelections(true);
39363 * Returns the currently selected cell,.
39364 * @return {Array} The selected cell (row, column) or null if none selected.
39366 getSelectedCell : function(){
39367 return this.selection ? this.selection.cell : null;
39371 * Clears all selections.
39372 * @param {Boolean} true to prevent the gridview from being notified about the change.
39374 clearSelections : function(preventNotify){
39375 var s = this.selection;
39377 if(preventNotify !== true){
39378 this.grid.view.onCellDeselect(s.cell[0], s.cell[1]);
39380 this.selection = null;
39381 this.fireEvent("selectionchange", this, null);
39386 * Returns true if there is a selection.
39387 * @return {Boolean}
39389 hasSelection : function(){
39390 return this.selection ? true : false;
39394 handleMouseDown : function(e, t){
39395 var v = this.grid.getView();
39396 if(this.isLocked()){
39399 var row = v.findRowIndex(t);
39400 var cell = v.findCellIndex(t);
39401 if(row !== false && cell !== false){
39402 this.select(row, cell);
39408 * @param {Number} rowIndex
39409 * @param {Number} collIndex
39411 select : function(rowIndex, colIndex, preventViewNotify, preventFocus, /*internal*/ r){
39412 if(this.fireEvent("beforecellselect", this, rowIndex, colIndex) !== false){
39413 this.clearSelections();
39414 r = r || this.grid.dataSource.getAt(rowIndex);
39417 cell : [rowIndex, colIndex]
39419 if(!preventViewNotify){
39420 var v = this.grid.getView();
39421 v.onCellSelect(rowIndex, colIndex);
39422 if(preventFocus !== true){
39423 v.focusCell(rowIndex, colIndex);
39426 this.fireEvent("cellselect", this, rowIndex, colIndex);
39427 this.fireEvent("selectionchange", this, this.selection);
39432 isSelectable : function(rowIndex, colIndex, cm){
39433 return !cm.isHidden(colIndex);
39437 handleKeyDown : function(e){
39438 //Roo.log('Cell Sel Model handleKeyDown');
39439 if(!e.isNavKeyPress()){
39442 var g = this.grid, s = this.selection;
39445 var cell = g.walkCells(0, 0, 1, this.isSelectable, this);
39447 this.select(cell[0], cell[1]);
39452 var walk = function(row, col, step){
39453 return g.walkCells(row, col, step, sm.isSelectable, sm);
39455 var k = e.getKey(), r = s.cell[0], c = s.cell[1];
39462 // handled by onEditorKey
39463 if (g.isEditor && g.editing) {
39467 newCell = walk(r, c-1, -1);
39469 newCell = walk(r, c+1, 1);
39474 newCell = walk(r+1, c, 1);
39478 newCell = walk(r-1, c, -1);
39482 newCell = walk(r, c+1, 1);
39486 newCell = walk(r, c-1, -1);
39491 if(g.isEditor && !g.editing){
39492 g.startEditing(r, c);
39501 this.select(newCell[0], newCell[1]);
39507 acceptsNav : function(row, col, cm){
39508 return !cm.isHidden(col) && cm.isCellEditable(col, row);
39512 * @param {Number} field (not used) - as it's normally used as a listener
39513 * @param {Number} e - event - fake it by using
39515 * var e = Roo.EventObjectImpl.prototype;
39516 * e.keyCode = e.TAB
39520 onEditorKey : function(field, e){
39522 var k = e.getKey(),
39525 ed = g.activeEditor,
39527 ///Roo.log('onEditorKey' + k);
39530 if (this.enter_is_tab && k == e.ENTER) {
39536 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
39538 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
39544 } else if(k == e.ENTER && !e.ctrlKey){
39547 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
39549 } else if(k == e.ESC){
39554 var ecall = { cell : newCell, forward : forward };
39555 this.fireEvent('beforeeditnext', ecall );
39556 newCell = ecall.cell;
39557 forward = ecall.forward;
39561 //Roo.log('next cell after edit');
39562 g.startEditing.defer(100, g, [newCell[0], newCell[1]]);
39563 } else if (forward) {
39564 // tabbed past last
39565 this.fireEvent.defer(100, this, ['tabend',this]);
39570 * Ext JS Library 1.1.1
39571 * Copyright(c) 2006-2007, Ext JS, LLC.
39573 * Originally Released Under LGPL - original licence link has changed is not relivant.
39576 * <script type="text/javascript">
39580 * @class Roo.grid.EditorGrid
39581 * @extends Roo.grid.Grid
39582 * Class for creating and editable grid.
39583 * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
39584 * The container MUST have some type of size defined for the grid to fill. The container will be
39585 * automatically set to position relative if it isn't already.
39586 * @param {Object} dataSource The data model to bind to
39587 * @param {Object} colModel The column model with info about this grid's columns
39589 Roo.grid.EditorGrid = function(container, config){
39590 Roo.grid.EditorGrid.superclass.constructor.call(this, container, config);
39591 this.getGridEl().addClass("xedit-grid");
39593 if(!this.selModel){
39594 this.selModel = new Roo.grid.CellSelectionModel();
39597 this.activeEditor = null;
39601 * @event beforeedit
39602 * Fires before cell editing is triggered. The edit event object has the following properties <br />
39603 * <ul style="padding:5px;padding-left:16px;">
39604 * <li>grid - This grid</li>
39605 * <li>record - The record being edited</li>
39606 * <li>field - The field name being edited</li>
39607 * <li>value - The value for the field being edited.</li>
39608 * <li>row - The grid row index</li>
39609 * <li>column - The grid column index</li>
39610 * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
39612 * @param {Object} e An edit event (see above for description)
39614 "beforeedit" : true,
39617 * Fires after a cell is edited. <br />
39618 * <ul style="padding:5px;padding-left:16px;">
39619 * <li>grid - This grid</li>
39620 * <li>record - The record being edited</li>
39621 * <li>field - The field name being edited</li>
39622 * <li>value - The value being set</li>
39623 * <li>originalValue - The original value for the field, before the edit.</li>
39624 * <li>row - The grid row index</li>
39625 * <li>column - The grid column index</li>
39627 * @param {Object} e An edit event (see above for description)
39629 "afteredit" : true,
39631 * @event validateedit
39632 * Fires after a cell is edited, but before the value is set in the record.
39633 * You can use this to modify the value being set in the field, Return false
39634 * to cancel the change. The edit event object has the following properties <br />
39635 * <ul style="padding:5px;padding-left:16px;">
39636 * <li>editor - This editor</li>
39637 * <li>grid - This grid</li>
39638 * <li>record - The record being edited</li>
39639 * <li>field - The field name being edited</li>
39640 * <li>value - The value being set</li>
39641 * <li>originalValue - The original value for the field, before the edit.</li>
39642 * <li>row - The grid row index</li>
39643 * <li>column - The grid column index</li>
39644 * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
39646 * @param {Object} e An edit event (see above for description)
39648 "validateedit" : true
39650 this.on("bodyscroll", this.stopEditing, this);
39651 this.on(this.clicksToEdit == 1 ? "cellclick" : "celldblclick", this.onCellDblClick, this);
39654 Roo.extend(Roo.grid.EditorGrid, Roo.grid.Grid, {
39656 * @cfg {Number} clicksToEdit
39657 * The number of clicks on a cell required to display the cell's editor (defaults to 2)
39664 trackMouseOver: false, // causes very odd FF errors
39666 onCellDblClick : function(g, row, col){
39667 this.startEditing(row, col);
39670 onEditComplete : function(ed, value, startValue){
39671 this.editing = false;
39672 this.activeEditor = null;
39673 ed.un("specialkey", this.selModel.onEditorKey, this.selModel);
39675 var field = this.colModel.getDataIndex(ed.col);
39680 originalValue: startValue,
39687 var cell = Roo.get(this.view.getCell(ed.row,ed.col))
39690 if(String(value) !== String(startValue)){
39692 if(this.fireEvent("validateedit", e) !== false && !e.cancel){
39693 r.set(field, e.value);
39694 // if we are dealing with a combo box..
39695 // then we also set the 'name' colum to be the displayField
39696 if (ed.field.displayField && ed.field.name) {
39697 r.set(ed.field.name, ed.field.el.dom.value);
39700 delete e.cancel; //?? why!!!
39701 this.fireEvent("afteredit", e);
39704 this.fireEvent("afteredit", e); // always fire it!
39706 this.view.focusCell(ed.row, ed.col);
39710 * Starts editing the specified for the specified row/column
39711 * @param {Number} rowIndex
39712 * @param {Number} colIndex
39714 startEditing : function(row, col){
39715 this.stopEditing();
39716 if(this.colModel.isCellEditable(col, row)){
39717 this.view.ensureVisible(row, col, true);
39719 var r = this.dataSource.getAt(row);
39720 var field = this.colModel.getDataIndex(col);
39721 var cell = Roo.get(this.view.getCell(row,col));
39726 value: r.data[field],
39731 if(this.fireEvent("beforeedit", e) !== false && !e.cancel){
39732 this.editing = true;
39733 var ed = this.colModel.getCellEditor(col, row);
39739 ed.render(ed.parentEl || document.body);
39745 (function(){ // complex but required for focus issues in safari, ie and opera
39749 ed.on("complete", this.onEditComplete, this, {single: true});
39750 ed.on("specialkey", this.selModel.onEditorKey, this.selModel);
39751 this.activeEditor = ed;
39752 var v = r.data[field];
39753 ed.startEdit(this.view.getCell(row, col), v);
39754 // combo's with 'displayField and name set
39755 if (ed.field.displayField && ed.field.name) {
39756 ed.field.el.dom.value = r.data[ed.field.name];
39760 }).defer(50, this);
39766 * Stops any active editing
39768 stopEditing : function(){
39769 if(this.activeEditor){
39770 this.activeEditor.completeEdit();
39772 this.activeEditor = null;
39776 * Called to get grid's drag proxy text, by default returns this.ddText.
39779 getDragDropText : function(){
39780 var count = this.selModel.getSelectedCell() ? 1 : 0;
39781 return String.format(this.ddText, count, count == 1 ? '' : 's');
39786 * Ext JS Library 1.1.1
39787 * Copyright(c) 2006-2007, Ext JS, LLC.
39789 * Originally Released Under LGPL - original licence link has changed is not relivant.
39792 * <script type="text/javascript">
39795 // private - not really -- you end up using it !
39796 // This is a support class used internally by the Grid components
39799 * @class Roo.grid.GridEditor
39800 * @extends Roo.Editor
39801 * Class for creating and editable grid elements.
39802 * @param {Object} config any settings (must include field)
39804 Roo.grid.GridEditor = function(field, config){
39805 if (!config && field.field) {
39807 field = Roo.factory(config.field, Roo.form);
39809 Roo.grid.GridEditor.superclass.constructor.call(this, field, config);
39810 field.monitorTab = false;
39813 Roo.extend(Roo.grid.GridEditor, Roo.Editor, {
39816 * @cfg {Roo.form.Field} field Field to wrap (or xtyped)
39819 alignment: "tl-tl",
39822 cls: "x-small-editor x-grid-editor",
39827 * Ext JS Library 1.1.1
39828 * Copyright(c) 2006-2007, Ext JS, LLC.
39830 * Originally Released Under LGPL - original licence link has changed is not relivant.
39833 * <script type="text/javascript">
39838 Roo.grid.PropertyRecord = Roo.data.Record.create([
39839 {name:'name',type:'string'}, 'value'
39843 Roo.grid.PropertyStore = function(grid, source){
39845 this.store = new Roo.data.Store({
39846 recordType : Roo.grid.PropertyRecord
39848 this.store.on('update', this.onUpdate, this);
39850 this.setSource(source);
39852 Roo.grid.PropertyStore.superclass.constructor.call(this);
39857 Roo.extend(Roo.grid.PropertyStore, Roo.util.Observable, {
39858 setSource : function(o){
39860 this.store.removeAll();
39863 if(this.isEditableValue(o[k])){
39864 data.push(new Roo.grid.PropertyRecord({name: k, value: o[k]}, k));
39867 this.store.loadRecords({records: data}, {}, true);
39870 onUpdate : function(ds, record, type){
39871 if(type == Roo.data.Record.EDIT){
39872 var v = record.data['value'];
39873 var oldValue = record.modified['value'];
39874 if(this.grid.fireEvent('beforepropertychange', this.source, record.id, v, oldValue) !== false){
39875 this.source[record.id] = v;
39877 this.grid.fireEvent('propertychange', this.source, record.id, v, oldValue);
39884 getProperty : function(row){
39885 return this.store.getAt(row);
39888 isEditableValue: function(val){
39889 if(val && val instanceof Date){
39891 }else if(typeof val == 'object' || typeof val == 'function'){
39897 setValue : function(prop, value){
39898 this.source[prop] = value;
39899 this.store.getById(prop).set('value', value);
39902 getSource : function(){
39903 return this.source;
39907 Roo.grid.PropertyColumnModel = function(grid, store){
39910 g.PropertyColumnModel.superclass.constructor.call(this, [
39911 {header: this.nameText, sortable: true, dataIndex:'name', id: 'name'},
39912 {header: this.valueText, resizable:false, dataIndex: 'value', id: 'value'}
39914 this.store = store;
39915 this.bselect = Roo.DomHelper.append(document.body, {
39916 tag: 'select', style:'display:none', cls: 'x-grid-editor', children: [
39917 {tag: 'option', value: 'true', html: 'true'},
39918 {tag: 'option', value: 'false', html: 'false'}
39921 Roo.id(this.bselect);
39924 'date' : new g.GridEditor(new f.DateField({selectOnFocus:true})),
39925 'string' : new g.GridEditor(new f.TextField({selectOnFocus:true})),
39926 'number' : new g.GridEditor(new f.NumberField({selectOnFocus:true, style:'text-align:left;'})),
39927 'int' : new g.GridEditor(new f.NumberField({selectOnFocus:true, allowDecimals:false, style:'text-align:left;'})),
39928 'boolean' : new g.GridEditor(new f.Field({el:this.bselect,selectOnFocus:true}))
39930 this.renderCellDelegate = this.renderCell.createDelegate(this);
39931 this.renderPropDelegate = this.renderProp.createDelegate(this);
39934 Roo.extend(Roo.grid.PropertyColumnModel, Roo.grid.ColumnModel, {
39938 valueText : 'Value',
39940 dateFormat : 'm/j/Y',
39943 renderDate : function(dateVal){
39944 return dateVal.dateFormat(this.dateFormat);
39947 renderBool : function(bVal){
39948 return bVal ? 'true' : 'false';
39951 isCellEditable : function(colIndex, rowIndex){
39952 return colIndex == 1;
39955 getRenderer : function(col){
39957 this.renderCellDelegate : this.renderPropDelegate;
39960 renderProp : function(v){
39961 return this.getPropertyName(v);
39964 renderCell : function(val){
39966 if(val instanceof Date){
39967 rv = this.renderDate(val);
39968 }else if(typeof val == 'boolean'){
39969 rv = this.renderBool(val);
39971 return Roo.util.Format.htmlEncode(rv);
39974 getPropertyName : function(name){
39975 var pn = this.grid.propertyNames;
39976 return pn && pn[name] ? pn[name] : name;
39979 getCellEditor : function(colIndex, rowIndex){
39980 var p = this.store.getProperty(rowIndex);
39981 var n = p.data['name'], val = p.data['value'];
39983 if(typeof(this.grid.customEditors[n]) == 'string'){
39984 return this.editors[this.grid.customEditors[n]];
39986 if(typeof(this.grid.customEditors[n]) != 'undefined'){
39987 return this.grid.customEditors[n];
39989 if(val instanceof Date){
39990 return this.editors['date'];
39991 }else if(typeof val == 'number'){
39992 return this.editors['number'];
39993 }else if(typeof val == 'boolean'){
39994 return this.editors['boolean'];
39996 return this.editors['string'];
40002 * @class Roo.grid.PropertyGrid
40003 * @extends Roo.grid.EditorGrid
40004 * This class represents the interface of a component based property grid control.
40005 * <br><br>Usage:<pre><code>
40006 var grid = new Roo.grid.PropertyGrid("my-container-id", {
40014 * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
40015 * The container MUST have some type of size defined for the grid to fill. The container will be
40016 * automatically set to position relative if it isn't already.
40017 * @param {Object} config A config object that sets properties on this grid.
40019 Roo.grid.PropertyGrid = function(container, config){
40020 config = config || {};
40021 var store = new Roo.grid.PropertyStore(this);
40022 this.store = store;
40023 var cm = new Roo.grid.PropertyColumnModel(this, store);
40024 store.store.sort('name', 'ASC');
40025 Roo.grid.PropertyGrid.superclass.constructor.call(this, container, Roo.apply({
40028 enableColLock:false,
40029 enableColumnMove:false,
40031 trackMouseOver: false,
40034 this.getGridEl().addClass('x-props-grid');
40035 this.lastEditRow = null;
40036 this.on('columnresize', this.onColumnResize, this);
40039 * @event beforepropertychange
40040 * Fires before a property changes (return false to stop?)
40041 * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
40042 * @param {String} id Record Id
40043 * @param {String} newval New Value
40044 * @param {String} oldval Old Value
40046 "beforepropertychange": true,
40048 * @event propertychange
40049 * Fires after a property changes
40050 * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
40051 * @param {String} id Record Id
40052 * @param {String} newval New Value
40053 * @param {String} oldval Old Value
40055 "propertychange": true
40057 this.customEditors = this.customEditors || {};
40059 Roo.extend(Roo.grid.PropertyGrid, Roo.grid.EditorGrid, {
40062 * @cfg {Object} customEditors map of colnames=> custom editors.
40063 * the custom editor can be one of the standard ones (date|string|number|int|boolean), or a
40064 * grid editor eg. Roo.grid.GridEditor(new Roo.form.TextArea({selectOnFocus:true})),
40065 * false disables editing of the field.
40069 * @cfg {Object} propertyNames map of property Names to their displayed value
40072 render : function(){
40073 Roo.grid.PropertyGrid.superclass.render.call(this);
40074 this.autoSize.defer(100, this);
40077 autoSize : function(){
40078 Roo.grid.PropertyGrid.superclass.autoSize.call(this);
40080 this.view.fitColumns();
40084 onColumnResize : function(){
40085 this.colModel.setColumnWidth(1, this.container.getWidth(true)-this.colModel.getColumnWidth(0));
40089 * Sets the data for the Grid
40090 * accepts a Key => Value object of all the elements avaiable.
40091 * @param {Object} data to appear in grid.
40093 setSource : function(source){
40094 this.store.setSource(source);
40098 * Gets all the data from the grid.
40099 * @return {Object} data data stored in grid
40101 getSource : function(){
40102 return this.store.getSource();
40106 * Ext JS Library 1.1.1
40107 * Copyright(c) 2006-2007, Ext JS, LLC.
40109 * Originally Released Under LGPL - original licence link has changed is not relivant.
40112 * <script type="text/javascript">
40116 * @class Roo.LoadMask
40117 * A simple utility class for generically masking elements while loading data. If the element being masked has
40118 * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
40119 * process and the mask element will be cached for reuse. For all other elements, this mask will replace the
40120 * element's UpdateManager load indicator and will be destroyed after the initial load.
40122 * Create a new LoadMask
40123 * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
40124 * @param {Object} config The config object
40126 Roo.LoadMask = function(el, config){
40127 this.el = Roo.get(el);
40128 Roo.apply(this, config);
40130 this.store.on('beforeload', this.onBeforeLoad, this);
40131 this.store.on('load', this.onLoad, this);
40132 this.store.on('loadexception', this.onLoadException, this);
40133 this.removeMask = false;
40135 var um = this.el.getUpdateManager();
40136 um.showLoadIndicator = false; // disable the default indicator
40137 um.on('beforeupdate', this.onBeforeLoad, this);
40138 um.on('update', this.onLoad, this);
40139 um.on('failure', this.onLoad, this);
40140 this.removeMask = true;
40144 Roo.LoadMask.prototype = {
40146 * @cfg {Boolean} removeMask
40147 * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
40148 * False to persist the mask element reference for multiple uses (e.g., for paged data widgets). Defaults to false.
40151 * @cfg {String} msg
40152 * The text to display in a centered loading message box (defaults to 'Loading...')
40154 msg : 'Loading...',
40156 * @cfg {String} msgCls
40157 * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
40159 msgCls : 'x-mask-loading',
40162 * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
40168 * Disables the mask to prevent it from being displayed
40170 disable : function(){
40171 this.disabled = true;
40175 * Enables the mask so that it can be displayed
40177 enable : function(){
40178 this.disabled = false;
40181 onLoadException : function()
40183 Roo.log(arguments);
40185 if (typeof(arguments[3]) != 'undefined') {
40186 Roo.MessageBox.alert("Error loading",arguments[3]);
40190 if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
40191 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
40200 this.el.unmask(this.removeMask);
40203 onLoad : function()
40205 this.el.unmask(this.removeMask);
40209 onBeforeLoad : function(){
40210 if(!this.disabled){
40211 this.el.mask(this.msg, this.msgCls);
40216 destroy : function(){
40218 this.store.un('beforeload', this.onBeforeLoad, this);
40219 this.store.un('load', this.onLoad, this);
40220 this.store.un('loadexception', this.onLoadException, this);
40222 var um = this.el.getUpdateManager();
40223 um.un('beforeupdate', this.onBeforeLoad, this);
40224 um.un('update', this.onLoad, this);
40225 um.un('failure', this.onLoad, this);
40230 * Ext JS Library 1.1.1
40231 * Copyright(c) 2006-2007, Ext JS, LLC.
40233 * Originally Released Under LGPL - original licence link has changed is not relivant.
40236 * <script type="text/javascript">
40241 * @class Roo.XTemplate
40242 * @extends Roo.Template
40243 * Provides a template that can have nested templates for loops or conditionals. The syntax is:
40245 var t = new Roo.XTemplate(
40246 '<select name="{name}">',
40247 '<tpl for="options"><option value="{value:trim}">{text:ellipsis(10)}</option></tpl>',
40251 // then append, applying the master template values
40254 * Supported features:
40259 {a_variable} - output encoded.
40260 {a_variable.format:("Y-m-d")} - call a method on the variable
40261 {a_variable:raw} - unencoded output
40262 {a_variable:toFixed(1,2)} - Roo.util.Format."toFixed"
40263 {a_variable:this.method_on_template(...)} - call a method on the template object.
40268 <tpl for="a_variable or condition.."></tpl>
40269 <tpl if="a_variable or condition"></tpl>
40270 <tpl exec="some javascript"></tpl>
40271 <tpl name="named_template"></tpl> (experimental)
40273 <tpl for="."></tpl> - just iterate the property..
40274 <tpl for=".."></tpl> - iterates with the parent (probably the template)
40278 Roo.XTemplate = function()
40280 Roo.XTemplate.superclass.constructor.apply(this, arguments);
40287 Roo.extend(Roo.XTemplate, Roo.Template, {
40290 * The various sub templates
40295 * basic tag replacing syntax
40298 * // you can fake an object call by doing this
40302 re : /\{([\w-\.]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
40305 * compile the template
40307 * This is not recursive, so I'm not sure how nested templates are really going to be handled..
40310 compile: function()
40314 s = ['<tpl>', s, '</tpl>'].join('');
40316 var re = /<tpl\b[^>]*>((?:(?=([^<]+))\2|<(?!tpl\b[^>]*>))*?)<\/tpl>/,
40317 nameRe = /^<tpl\b[^>]*?for="(.*?)"/,
40318 ifRe = /^<tpl\b[^>]*?if="(.*?)"/,
40319 execRe = /^<tpl\b[^>]*?exec="(.*?)"/,
40320 namedRe = /^<tpl\b[^>]*?name="(\w+)"/, // named templates..
40325 while(true == !!(m = s.match(re))){
40326 var forMatch = m[0].match(nameRe),
40327 ifMatch = m[0].match(ifRe),
40328 execMatch = m[0].match(execRe),
40329 namedMatch = m[0].match(namedRe),
40334 name = forMatch && forMatch[1] ? forMatch[1] : '';
40337 // if - puts fn into test..
40338 exp = ifMatch && ifMatch[1] ? ifMatch[1] : null;
40340 fn = new Function('values', 'parent', 'with(values){ return '+(Roo.util.Format.htmlDecode(exp))+'; }');
40345 // exec - calls a function... returns empty if true is returned.
40346 exp = execMatch && execMatch[1] ? execMatch[1] : null;
40348 exec = new Function('values', 'parent', 'with(values){ '+(Roo.util.Format.htmlDecode(exp))+'; }');
40356 case '.': name = new Function('values', 'parent', 'with(values){ return values; }'); break;
40357 case '..': name = new Function('values', 'parent', 'with(values){ return parent; }'); break;
40358 default: name = new Function('values', 'parent', 'with(values){ return '+name+'; }');
40361 var uid = namedMatch ? namedMatch[1] : id;
40365 id: namedMatch ? namedMatch[1] : id,
40372 s = s.replace(m[0], '');
40374 s = s.replace(m[0], '{xtpl'+ id + '}');
40379 for(var i = tpls.length-1; i >= 0; --i){
40380 this.compileTpl(tpls[i]);
40381 this.tpls[tpls[i].id] = tpls[i];
40383 this.master = tpls[tpls.length-1];
40387 * same as applyTemplate, except it's done to one of the subTemplates
40388 * when using named templates, you can do:
40390 * var str = pl.applySubTemplate('your-name', values);
40393 * @param {Number} id of the template
40394 * @param {Object} values to apply to template
40395 * @param {Object} parent (normaly the instance of this object)
40397 applySubTemplate : function(id, values, parent)
40401 var t = this.tpls[id];
40405 if(t.test && !t.test.call(this, values, parent)){
40409 Roo.log("Xtemplate.applySubTemplate 'test': Exception thrown");
40410 Roo.log(e.toString());
40416 if(t.exec && t.exec.call(this, values, parent)){
40420 Roo.log("Xtemplate.applySubTemplate 'exec': Exception thrown");
40421 Roo.log(e.toString());
40426 var vs = t.target ? t.target.call(this, values, parent) : values;
40427 parent = t.target ? values : parent;
40428 if(t.target && vs instanceof Array){
40430 for(var i = 0, len = vs.length; i < len; i++){
40431 buf[buf.length] = t.compiled.call(this, vs[i], parent);
40433 return buf.join('');
40435 return t.compiled.call(this, vs, parent);
40437 Roo.log("Xtemplate.applySubTemplate : Exception thrown");
40438 Roo.log(e.toString());
40439 Roo.log(t.compiled);
40444 compileTpl : function(tpl)
40446 var fm = Roo.util.Format;
40447 var useF = this.disableFormats !== true;
40448 var sep = Roo.isGecko ? "+" : ",";
40449 var undef = function(str) {
40450 Roo.log("Property not found :" + str);
40454 var fn = function(m, name, format, args)
40456 //Roo.log(arguments);
40457 args = args ? args.replace(/\\'/g,"'") : args;
40458 //["{TEST:(a,b,c)}", "TEST", "", "a,b,c", 0, "{TEST:(a,b,c)}"]
40459 if (typeof(format) == 'undefined') {
40460 format= 'htmlEncode';
40462 if (format == 'raw' ) {
40466 if(name.substr(0, 4) == 'xtpl'){
40467 return "'"+ sep +'this.applySubTemplate('+name.substr(4)+', values, parent)'+sep+"'";
40470 // build an array of options to determine if value is undefined..
40472 // basically get 'xxxx.yyyy' then do
40473 // (typeof(xxxx) == 'undefined' || typeof(xxx.yyyy) == 'undefined') ?
40474 // (function () { Roo.log("Property not found"); return ''; })() :
40479 Roo.each(name.split('.'), function(st) {
40480 lookfor += (lookfor.length ? '.': '') + st;
40481 udef_ar.push( "(typeof(" + lookfor + ") == 'undefined')" );
40484 var udef_st = '((' + udef_ar.join(" || ") +") ? undef('" + name + "') : "; // .. needs )
40487 if(format && useF){
40489 args = args ? ',' + args : "";
40491 if(format.substr(0, 5) != "this."){
40492 format = "fm." + format + '(';
40494 format = 'this.call("'+ format.substr(5) + '", ';
40498 return "'"+ sep + udef_st + format + name + args + "))"+sep+"'";
40502 // called with xxyx.yuu:(test,test)
40504 return "'"+ sep + udef_st + name + '(' + args + "))"+sep+"'";
40506 // raw.. - :raw modifier..
40507 return "'"+ sep + udef_st + name + ")"+sep+"'";
40511 // branched to use + in gecko and [].join() in others
40513 body = "tpl.compiled = function(values, parent){ with(values) { return '" +
40514 tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
40517 body = ["tpl.compiled = function(values, parent){ with (values) { return ['"];
40518 body.push(tpl.body.replace(/(\r\n|\n)/g,
40519 '\\n').replace(/'/g, "\\'").replace(this.re, fn));
40520 body.push("'].join('');};};");
40521 body = body.join('');
40524 Roo.debug && Roo.log(body.replace(/\\n/,'\n'));
40526 /** eval:var:tpl eval:var:fm eval:var:useF eval:var:undef */
40532 applyTemplate : function(values){
40533 return this.master.compiled.call(this, values, {});
40534 //var s = this.subs;
40537 apply : function(){
40538 return this.applyTemplate.apply(this, arguments);
40543 Roo.XTemplate.from = function(el){
40544 el = Roo.getDom(el);
40545 return new Roo.XTemplate(el.value || el.innerHTML);
40547 * Original code for Roojs - LGPL
40548 * <script type="text/javascript">
40552 * @class Roo.XComponent
40553 * A delayed Element creator...
40554 * Or a way to group chunks of interface together.
40556 * Mypart.xyx = new Roo.XComponent({
40558 parent : 'Mypart.xyz', // empty == document.element.!!
40562 disabled : function() {}
40564 tree : function() { // return an tree of xtype declared components
40568 xtype : 'NestedLayoutPanel',
40575 * It can be used to build a big heiracy, with parent etc.
40576 * or you can just use this to render a single compoent to a dom element
40577 * MYPART.render(Roo.Element | String(id) | dom_element )
40579 * @extends Roo.util.Observable
40581 * @param cfg {Object} configuration of component
40584 Roo.XComponent = function(cfg) {
40585 Roo.apply(this, cfg);
40589 * Fires when this the componnt is built
40590 * @param {Roo.XComponent} c the component
40595 this.region = this.region || 'center'; // default..
40596 Roo.XComponent.register(this);
40597 this.modules = false;
40598 this.el = false; // where the layout goes..
40602 Roo.extend(Roo.XComponent, Roo.util.Observable, {
40605 * The created element (with Roo.factory())
40606 * @type {Roo.Layout}
40612 * for BC - use el in new code
40613 * @type {Roo.Layout}
40619 * for BC - use el in new code
40620 * @type {Roo.Layout}
40625 * @cfg {Function|boolean} disabled
40626 * If this module is disabled by some rule, return true from the funtion
40631 * @cfg {String} parent
40632 * Name of parent element which it get xtype added to..
40637 * @cfg {String} order
40638 * Used to set the order in which elements are created (usefull for multiple tabs)
40643 * @cfg {String} name
40644 * String to display while loading.
40648 * @cfg {String} region
40649 * Region to render component to (defaults to center)
40654 * @cfg {Array} items
40655 * A single item array - the first element is the root of the tree..
40656 * It's done this way to stay compatible with the Xtype system...
40662 * The method that retuns the tree of parts that make up this compoennt
40669 * render element to dom or tree
40670 * @param {Roo.Element|String|DomElement} optional render to if parent is not set.
40673 render : function(el)
40677 var hp = this.parent ? 1 : 0;
40679 if (!el && typeof(this.parent) == 'string' && this.parent.substring(0,1) == '#') {
40680 // if parent is a '#.....' string, then let's use that..
40681 var ename = this.parent.substr(1)
40682 this.parent = false;
40683 el = Roo.get(ename);
40685 Roo.log("Warning - element can not be found :#" + ename );
40691 if (!this.parent) {
40693 el = el ? Roo.get(el) : false;
40695 // it's a top level one..
40697 el : new Roo.BorderLayout(el || document.body, {
40703 tabPosition: 'top',
40704 //resizeTabs: true,
40705 alwaysShowTabs: el && hp? false : true,
40706 hideTabs: el || !hp ? true : false,
40713 if (!this.parent.el) {
40714 // probably an old style ctor, which has been disabled.
40718 // The 'tree' method is '_tree now'
40720 var tree = this._tree ? this._tree() : this.tree();
40721 tree.region = tree.region || this.region;
40722 this.el = this.parent.el.addxtype(tree);
40723 this.fireEvent('built', this);
40725 this.panel = this.el;
40726 this.layout = this.panel.layout;
40727 this.parentLayout = this.parent.layout || false;
40733 Roo.apply(Roo.XComponent, {
40735 * @property hideProgress
40736 * true to disable the building progress bar.. usefull on single page renders.
40739 hideProgress : false,
40741 * @property buildCompleted
40742 * True when the builder has completed building the interface.
40745 buildCompleted : false,
40748 * @property topModule
40749 * the upper most module - uses document.element as it's constructor.
40756 * @property modules
40757 * array of modules to be created by registration system.
40758 * @type {Array} of Roo.XComponent
40763 * @property elmodules
40764 * array of modules to be created by which use #ID
40765 * @type {Array} of Roo.XComponent
40772 * Register components to be built later.
40774 * This solves the following issues
40775 * - Building is not done on page load, but after an authentication process has occured.
40776 * - Interface elements are registered on page load
40777 * - Parent Interface elements may not be loaded before child, so this handles that..
40784 module : 'Pman.Tab.projectMgr',
40786 parent : 'Pman.layout',
40787 disabled : false, // or use a function..
40790 * * @param {Object} details about module
40792 register : function(obj) {
40794 Roo.XComponent.event.fireEvent('register', obj);
40795 switch(typeof(obj.disabled) ) {
40801 if ( obj.disabled() ) {
40807 if (obj.disabled) {
40813 this.modules.push(obj);
40817 * convert a string to an object..
40818 * eg. 'AAA.BBB' -> finds AAA.BBB
40822 toObject : function(str)
40824 if (!str || typeof(str) == 'object') {
40827 if (str.substring(0,1) == '#') {
40831 var ar = str.split('.');
40836 eval('if (typeof ' + rt + ' == "undefined"){ o = false;} o = ' + rt + ';');
40838 throw "Module not found : " + str;
40842 throw "Module not found : " + str;
40844 Roo.each(ar, function(e) {
40845 if (typeof(o[e]) == 'undefined') {
40846 throw "Module not found : " + str;
40857 * move modules into their correct place in the tree..
40860 preBuild : function ()
40863 Roo.each(this.modules , function (obj)
40865 Roo.XComponent.event.fireEvent('beforebuild', obj);
40867 var opar = obj.parent;
40869 obj.parent = this.toObject(opar);
40871 Roo.log("parent:toObject failed: " + e.toString());
40876 Roo.debug && Roo.log("GOT top level module");
40877 Roo.debug && Roo.log(obj);
40878 obj.modules = new Roo.util.MixedCollection(false,
40879 function(o) { return o.order + '' }
40881 this.topModule = obj;
40884 // parent is a string (usually a dom element name..)
40885 if (typeof(obj.parent) == 'string') {
40886 this.elmodules.push(obj);
40889 if (obj.parent.constructor != Roo.XComponent) {
40890 Roo.log("Warning : Object Parent is not instance of XComponent:" + obj.name)
40892 if (!obj.parent.modules) {
40893 obj.parent.modules = new Roo.util.MixedCollection(false,
40894 function(o) { return o.order + '' }
40897 if (obj.parent.disabled) {
40898 obj.disabled = true;
40900 obj.parent.modules.add(obj);
40905 * make a list of modules to build.
40906 * @return {Array} list of modules.
40909 buildOrder : function()
40912 var cmp = function(a,b) {
40913 return String(a).toUpperCase() > String(b).toUpperCase() ? 1 : -1;
40915 if ((!this.topModule || !this.topModule.modules) && !this.elmodules.length) {
40916 throw "No top level modules to build";
40919 // make a flat list in order of modules to build.
40920 var mods = this.topModule ? [ this.topModule ] : [];
40923 // elmodules (is a list of DOM based modules )
40924 Roo.each(this.elmodules, function(e) {
40926 if (!this.topModule &&
40927 typeof(e.parent) == 'string' &&
40928 e.parent.substring(0,1) == '#' &&
40929 Roo.get(e.parent.substr(1))
40932 _this.topModule = e;
40938 // add modules to their parents..
40939 var addMod = function(m) {
40940 Roo.debug && Roo.log("build Order: add: " + m.name);
40943 if (m.modules && !m.disabled) {
40944 Roo.debug && Roo.log("build Order: " + m.modules.length + " child modules");
40945 m.modules.keySort('ASC', cmp );
40946 Roo.debug && Roo.log("build Order: " + m.modules.length + " child modules (after sort)");
40948 m.modules.each(addMod);
40950 Roo.debug && Roo.log("build Order: no child modules");
40952 // not sure if this is used any more..
40954 m.finalize.name = m.name + " (clean up) ";
40955 mods.push(m.finalize);
40959 if (this.topModule && this.topModule.modules) {
40960 this.topModule.modules.keySort('ASC', cmp );
40961 this.topModule.modules.each(addMod);
40967 * Build the registered modules.
40968 * @param {Object} parent element.
40969 * @param {Function} optional method to call after module has been added.
40977 var mods = this.buildOrder();
40979 //this.allmods = mods;
40980 //Roo.debug && Roo.log(mods);
40982 if (!mods.length) { // should not happen
40983 throw "NO modules!!!";
40987 var msg = "Building Interface...";
40988 // flash it up as modal - so we store the mask!?
40989 if (!this.hideProgress) {
40990 Roo.MessageBox.show({ title: 'loading' });
40991 Roo.MessageBox.show({
40992 title: "Please wait...",
41001 var total = mods.length;
41004 var progressRun = function() {
41005 if (!mods.length) {
41006 Roo.debug && Roo.log('hide?');
41007 if (!this.hideProgress) {
41008 Roo.MessageBox.hide();
41010 Roo.XComponent.event.fireEvent('buildcomplete', _this.topModule);
41016 var m = mods.shift();
41019 Roo.debug && Roo.log(m);
41020 // not sure if this is supported any more.. - modules that are are just function
41021 if (typeof(m) == 'function') {
41023 return progressRun.defer(10, _this);
41027 msg = "Building Interface " + (total - mods.length) +
41029 (m.name ? (' - ' + m.name) : '');
41030 Roo.debug && Roo.log(msg);
41031 if (!this.hideProgress) {
41032 Roo.MessageBox.updateProgress( (total - mods.length)/total, msg );
41036 // is the module disabled?
41037 var disabled = (typeof(m.disabled) == 'function') ?
41038 m.disabled.call(m.module.disabled) : m.disabled;
41042 return progressRun(); // we do not update the display!
41050 // it's 10 on top level, and 1 on others??? why...
41051 return progressRun.defer(10, _this);
41054 progressRun.defer(1, _this);
41068 * wrapper for event.on - aliased later..
41069 * Typically use to register a event handler for register:
41071 * eg. Roo.XComponent.on('register', function(comp) { comp.disable = true } );
41080 Roo.XComponent.event = new Roo.util.Observable({
41084 * Fires when an Component is registered,
41085 * set the disable property on the Component to stop registration.
41086 * @param {Roo.XComponent} c the component being registerd.
41091 * @event beforebuild
41092 * Fires before each Component is built
41093 * can be used to apply permissions.
41094 * @param {Roo.XComponent} c the component being registerd.
41097 'beforebuild' : true,
41099 * @event buildcomplete
41100 * Fires on the top level element when all elements have been built
41101 * @param {Roo.XComponent} the top level component.
41103 'buildcomplete' : true
41108 Roo.XComponent.on = Roo.XComponent.event.on.createDelegate(Roo.XComponent.event);