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){
827 if (!Roo.isTouch && this.primaryButtonOnly && e.button != 0) {
828 Roo.log('not touch/ button !=0');
832 if (this.isLocked()) {
837 this.DDM.refreshCache(this.groups);
839 var pt = new Roo.lib.Point(Roo.lib.Event.getPageX(e), Roo.lib.Event.getPageY(e));
840 if (!this.hasOuterHandles && !this.DDM.isOverTarget(pt, this) ) {
843 Roo.log('check validator');
844 if (this.clickValidator(e)) {
845 Roo.log('validate success');
846 // set the initial element position
847 this.setStartPosition();
853 this.DDM.handleMouseDown(e, this);
855 this.DDM.stopEvent(e);
863 clickValidator: function(e) {
864 var target = e.getTarget();
865 return ( this.isValidHandleChild(target) &&
866 (this.id == this.handleElId ||
867 this.DDM.handleWasClicked(target, this.id)) );
871 * Allows you to specify a tag name that should not start a drag operation
872 * when clicked. This is designed to facilitate embedding links within a
873 * drag handle that do something other than start the drag.
874 * @method addInvalidHandleType
875 * @param {string} tagName the type of element to exclude
877 addInvalidHandleType: function(tagName) {
878 var type = tagName.toUpperCase();
879 this.invalidHandleTypes[type] = type;
883 * Lets you to specify an element id for a child of a drag handle
884 * that should not initiate a drag
885 * @method addInvalidHandleId
886 * @param {string} id the element id of the element you wish to ignore
888 addInvalidHandleId: function(id) {
889 if (typeof id !== "string") {
892 this.invalidHandleIds[id] = id;
896 * Lets you specify a css class of elements that will not initiate a drag
897 * @method addInvalidHandleClass
898 * @param {string} cssClass the class of the elements you wish to ignore
900 addInvalidHandleClass: function(cssClass) {
901 this.invalidHandleClasses.push(cssClass);
905 * Unsets an excluded tag name set by addInvalidHandleType
906 * @method removeInvalidHandleType
907 * @param {string} tagName the type of element to unexclude
909 removeInvalidHandleType: function(tagName) {
910 var type = tagName.toUpperCase();
911 // this.invalidHandleTypes[type] = null;
912 delete this.invalidHandleTypes[type];
916 * Unsets an invalid handle id
917 * @method removeInvalidHandleId
918 * @param {string} id the id of the element to re-enable
920 removeInvalidHandleId: function(id) {
921 if (typeof id !== "string") {
924 delete this.invalidHandleIds[id];
928 * Unsets an invalid css class
929 * @method removeInvalidHandleClass
930 * @param {string} cssClass the class of the element(s) you wish to
933 removeInvalidHandleClass: function(cssClass) {
934 for (var i=0, len=this.invalidHandleClasses.length; i<len; ++i) {
935 if (this.invalidHandleClasses[i] == cssClass) {
936 delete this.invalidHandleClasses[i];
942 * Checks the tag exclusion list to see if this click should be ignored
943 * @method isValidHandleChild
944 * @param {HTMLElement} node the HTMLElement to evaluate
945 * @return {boolean} true if this is a valid tag type, false if not
947 isValidHandleChild: function(node) {
950 // var n = (node.nodeName == "#text") ? node.parentNode : node;
953 nodeName = node.nodeName.toUpperCase();
955 nodeName = node.nodeName;
957 valid = valid && !this.invalidHandleTypes[nodeName];
958 valid = valid && !this.invalidHandleIds[node.id];
960 for (var i=0, len=this.invalidHandleClasses.length; valid && i<len; ++i) {
961 valid = !Dom.hasClass(node, this.invalidHandleClasses[i]);
970 * Create the array of horizontal tick marks if an interval was specified
971 * in setXConstraint().
975 setXTicks: function(iStartX, iTickSize) {
977 this.xTickSize = iTickSize;
981 for (var i = this.initPageX; i >= this.minX; i = i - iTickSize) {
983 this.xTicks[this.xTicks.length] = i;
988 for (i = this.initPageX; i <= this.maxX; i = i + iTickSize) {
990 this.xTicks[this.xTicks.length] = i;
995 this.xTicks.sort(this.DDM.numericSort) ;
999 * Create the array of vertical tick marks if an interval was specified in
1004 setYTicks: function(iStartY, iTickSize) {
1006 this.yTickSize = iTickSize;
1010 for (var i = this.initPageY; i >= this.minY; i = i - iTickSize) {
1012 this.yTicks[this.yTicks.length] = i;
1017 for (i = this.initPageY; i <= this.maxY; i = i + iTickSize) {
1019 this.yTicks[this.yTicks.length] = i;
1024 this.yTicks.sort(this.DDM.numericSort) ;
1028 * By default, the element can be dragged any place on the screen. Use
1029 * this method to limit the horizontal travel of the element. Pass in
1030 * 0,0 for the parameters if you want to lock the drag to the y axis.
1031 * @method setXConstraint
1032 * @param {int} iLeft the number of pixels the element can move to the left
1033 * @param {int} iRight the number of pixels the element can move to the
1035 * @param {int} iTickSize optional parameter for specifying that the
1037 * should move iTickSize pixels at a time.
1039 setXConstraint: function(iLeft, iRight, iTickSize) {
1040 this.leftConstraint = iLeft;
1041 this.rightConstraint = iRight;
1043 this.minX = this.initPageX - iLeft;
1044 this.maxX = this.initPageX + iRight;
1045 if (iTickSize) { this.setXTicks(this.initPageX, iTickSize); }
1047 this.constrainX = true;
1051 * Clears any constraints applied to this instance. Also clears ticks
1052 * since they can't exist independent of a constraint at this time.
1053 * @method clearConstraints
1055 clearConstraints: function() {
1056 this.constrainX = false;
1057 this.constrainY = false;
1062 * Clears any tick interval defined for this instance
1063 * @method clearTicks
1065 clearTicks: function() {
1073 * By default, the element can be dragged any place on the screen. Set
1074 * this to limit the vertical travel of the element. Pass in 0,0 for the
1075 * parameters if you want to lock the drag to the x axis.
1076 * @method setYConstraint
1077 * @param {int} iUp the number of pixels the element can move up
1078 * @param {int} iDown the number of pixels the element can move down
1079 * @param {int} iTickSize optional parameter for specifying that the
1080 * element should move iTickSize pixels at a time.
1082 setYConstraint: function(iUp, iDown, iTickSize) {
1083 this.topConstraint = iUp;
1084 this.bottomConstraint = iDown;
1086 this.minY = this.initPageY - iUp;
1087 this.maxY = this.initPageY + iDown;
1088 if (iTickSize) { this.setYTicks(this.initPageY, iTickSize); }
1090 this.constrainY = true;
1095 * resetConstraints must be called if you manually reposition a dd element.
1096 * @method resetConstraints
1097 * @param {boolean} maintainOffset
1099 resetConstraints: function() {
1102 // Maintain offsets if necessary
1103 if (this.initPageX || this.initPageX === 0) {
1104 // figure out how much this thing has moved
1105 var dx = (this.maintainOffset) ? this.lastPageX - this.initPageX : 0;
1106 var dy = (this.maintainOffset) ? this.lastPageY - this.initPageY : 0;
1108 this.setInitPosition(dx, dy);
1110 // This is the first time we have detected the element's position
1112 this.setInitPosition();
1115 if (this.constrainX) {
1116 this.setXConstraint( this.leftConstraint,
1117 this.rightConstraint,
1121 if (this.constrainY) {
1122 this.setYConstraint( this.topConstraint,
1123 this.bottomConstraint,
1129 * Normally the drag element is moved pixel by pixel, but we can specify
1130 * that it move a number of pixels at a time. This method resolves the
1131 * location when we have it set up like this.
1133 * @param {int} val where we want to place the object
1134 * @param {int[]} tickArray sorted array of valid points
1135 * @return {int} the closest tick
1138 getTick: function(val, tickArray) {
1141 // If tick interval is not defined, it is effectively 1 pixel,
1142 // so we return the value passed to us.
1144 } else if (tickArray[0] >= val) {
1145 // The value is lower than the first tick, so we return the first
1147 return tickArray[0];
1149 for (var i=0, len=tickArray.length; i<len; ++i) {
1151 if (tickArray[next] && tickArray[next] >= val) {
1152 var diff1 = val - tickArray[i];
1153 var diff2 = tickArray[next] - val;
1154 return (diff2 > diff1) ? tickArray[i] : tickArray[next];
1158 // The value is larger than the last tick, so we return the last
1160 return tickArray[tickArray.length - 1];
1167 * @return {string} string representation of the dd obj
1169 toString: function() {
1170 return ("DragDrop " + this.id);
1178 * Ext JS Library 1.1.1
1179 * Copyright(c) 2006-2007, Ext JS, LLC.
1181 * Originally Released Under LGPL - original licence link has changed is not relivant.
1184 * <script type="text/javascript">
1189 * The drag and drop utility provides a framework for building drag and drop
1190 * applications. In addition to enabling drag and drop for specific elements,
1191 * the drag and drop elements are tracked by the manager class, and the
1192 * interactions between the various elements are tracked during the drag and
1193 * the implementing code is notified about these important moments.
1196 // Only load the library once. Rewriting the manager class would orphan
1197 // existing drag and drop instances.
1198 if (!Roo.dd.DragDropMgr) {
1201 * @class Roo.dd.DragDropMgr
1202 * DragDropMgr is a singleton that tracks the element interaction for
1203 * all DragDrop items in the window. Generally, you will not call
1204 * this class directly, but it does have helper methods that could
1205 * be useful in your DragDrop implementations.
1208 Roo.dd.DragDropMgr = function() {
1210 var Event = Roo.EventManager;
1215 * Two dimensional Array of registered DragDrop objects. The first
1216 * dimension is the DragDrop item group, the second the DragDrop
1219 * @type {string: string}
1226 * Array of element ids defined as drag handles. Used to determine
1227 * if the element that generated the mousedown event is actually the
1228 * handle and not the html element itself.
1229 * @property handleIds
1230 * @type {string: string}
1237 * the DragDrop object that is currently being dragged
1238 * @property dragCurrent
1246 * the DragDrop object(s) that are being hovered over
1247 * @property dragOvers
1255 * the X distance between the cursor and the object being dragged
1264 * the Y distance between the cursor and the object being dragged
1273 * Flag to determine if we should prevent the default behavior of the
1274 * events we define. By default this is true, but this can be set to
1275 * false if you need the default behavior (not recommended)
1276 * @property preventDefault
1280 preventDefault: true,
1283 * Flag to determine if we should stop the propagation of the events
1284 * we generate. This is true by default but you may want to set it to
1285 * false if the html element contains other features that require the
1287 * @property stopPropagation
1291 stopPropagation: true,
1294 * Internal flag that is set to true when drag and drop has been
1296 * @property initialized
1303 * All drag and drop can be disabled.
1311 * Called the first time an element is registered.
1317 this.initialized = true;
1321 * In point mode, drag and drop interaction is defined by the
1322 * location of the cursor during the drag/drop
1330 * In intersect mode, drag and drop interactio nis defined by the
1331 * overlap of two or more drag and drop objects.
1332 * @property INTERSECT
1339 * The current drag and drop mode. Default: POINT
1347 * Runs method on all drag and drop objects
1348 * @method _execOnAll
1352 _execOnAll: function(sMethod, args) {
1353 for (var i in this.ids) {
1354 for (var j in this.ids[i]) {
1355 var oDD = this.ids[i][j];
1356 if (! this.isTypeOfDD(oDD)) {
1359 oDD[sMethod].apply(oDD, args);
1365 * Drag and drop initialization. Sets up the global event handlers
1370 _onLoad: function() {
1375 Event.on(document, "mouseup", this.handleMouseUp, this, true);
1376 Event.on(document, "mousemove", this.handleMouseMove, this, true);
1378 Event.on(document, "touchend", this.handleMouseUp, this, true);
1379 Event.on(document, "touchmove", this.handleMouseMove, this, true);
1381 Event.on(window, "unload", this._onUnload, this, true);
1382 Event.on(window, "resize", this._onResize, this, true);
1383 // Event.on(window, "mouseout", this._test);
1388 * Reset constraints on all drag and drop objs
1393 _onResize: function(e) {
1394 this._execOnAll("resetConstraints", []);
1398 * Lock all drag and drop functionality
1402 lock: function() { this.locked = true; },
1405 * Unlock all drag and drop functionality
1409 unlock: function() { this.locked = false; },
1412 * Is drag and drop locked?
1414 * @return {boolean} True if drag and drop is locked, false otherwise.
1417 isLocked: function() { return this.locked; },
1420 * Location cache that is set for all drag drop objects when a drag is
1421 * initiated, cleared when the drag is finished.
1422 * @property locationCache
1429 * Set useCache to false if you want to force object the lookup of each
1430 * drag and drop linked element constantly during a drag.
1431 * @property useCache
1438 * The number of pixels that the mouse needs to move after the
1439 * mousedown before the drag is initiated. Default=3;
1440 * @property clickPixelThresh
1444 clickPixelThresh: 3,
1447 * The number of milliseconds after the mousedown event to initiate the
1448 * drag if we don't get a mouseup event. Default=1000
1449 * @property clickTimeThresh
1453 clickTimeThresh: 350,
1456 * Flag that indicates that either the drag pixel threshold or the
1457 * mousdown time threshold has been met
1458 * @property dragThreshMet
1463 dragThreshMet: false,
1466 * Timeout used for the click time threshold
1467 * @property clickTimeout
1475 * The X position of the mousedown event stored for later use when a
1476 * drag threshold is met.
1485 * The Y position of the mousedown event stored for later use when a
1486 * drag threshold is met.
1495 * Each DragDrop instance must be registered with the DragDropMgr.
1496 * This is executed in DragDrop.init()
1497 * @method regDragDrop
1498 * @param {DragDrop} oDD the DragDrop object to register
1499 * @param {String} sGroup the name of the group this element belongs to
1502 regDragDrop: function(oDD, sGroup) {
1503 if (!this.initialized) { this.init(); }
1505 if (!this.ids[sGroup]) {
1506 this.ids[sGroup] = {};
1508 this.ids[sGroup][oDD.id] = oDD;
1512 * Removes the supplied dd instance from the supplied group. Executed
1513 * by DragDrop.removeFromGroup, so don't call this function directly.
1514 * @method removeDDFromGroup
1518 removeDDFromGroup: function(oDD, sGroup) {
1519 if (!this.ids[sGroup]) {
1520 this.ids[sGroup] = {};
1523 var obj = this.ids[sGroup];
1524 if (obj && obj[oDD.id]) {
1530 * Unregisters a drag and drop item. This is executed in
1531 * DragDrop.unreg, use that method instead of calling this directly.
1536 _remove: function(oDD) {
1537 for (var g in oDD.groups) {
1538 if (g && this.ids[g][oDD.id]) {
1539 delete this.ids[g][oDD.id];
1542 delete this.handleIds[oDD.id];
1546 * Each DragDrop handle element must be registered. This is done
1547 * automatically when executing DragDrop.setHandleElId()
1549 * @param {String} sDDId the DragDrop id this element is a handle for
1550 * @param {String} sHandleId the id of the element that is the drag
1554 regHandle: function(sDDId, sHandleId) {
1555 if (!this.handleIds[sDDId]) {
1556 this.handleIds[sDDId] = {};
1558 this.handleIds[sDDId][sHandleId] = sHandleId;
1562 * Utility function to determine if a given element has been
1563 * registered as a drag drop item.
1564 * @method isDragDrop
1565 * @param {String} id the element id to check
1566 * @return {boolean} true if this element is a DragDrop item,
1570 isDragDrop: function(id) {
1571 return ( this.getDDById(id) ) ? true : false;
1575 * Returns the drag and drop instances that are in all groups the
1576 * passed in instance belongs to.
1577 * @method getRelated
1578 * @param {DragDrop} p_oDD the obj to get related data for
1579 * @param {boolean} bTargetsOnly if true, only return targetable objs
1580 * @return {DragDrop[]} the related instances
1583 getRelated: function(p_oDD, bTargetsOnly) {
1585 for (var i in p_oDD.groups) {
1586 for (j in this.ids[i]) {
1587 var dd = this.ids[i][j];
1588 if (! this.isTypeOfDD(dd)) {
1591 if (!bTargetsOnly || dd.isTarget) {
1592 oDDs[oDDs.length] = dd;
1601 * Returns true if the specified dd target is a legal target for
1602 * the specifice drag obj
1603 * @method isLegalTarget
1604 * @param {DragDrop} the drag obj
1605 * @param {DragDrop} the target
1606 * @return {boolean} true if the target is a legal target for the
1610 isLegalTarget: function (oDD, oTargetDD) {
1611 var targets = this.getRelated(oDD, true);
1612 for (var i=0, len=targets.length;i<len;++i) {
1613 if (targets[i].id == oTargetDD.id) {
1622 * My goal is to be able to transparently determine if an object is
1623 * typeof DragDrop, and the exact subclass of DragDrop. typeof
1624 * returns "object", oDD.constructor.toString() always returns
1625 * "DragDrop" and not the name of the subclass. So for now it just
1626 * evaluates a well-known variable in DragDrop.
1627 * @method isTypeOfDD
1628 * @param {Object} the object to evaluate
1629 * @return {boolean} true if typeof oDD = DragDrop
1632 isTypeOfDD: function (oDD) {
1633 return (oDD && oDD.__ygDragDrop);
1637 * Utility function to determine if a given element has been
1638 * registered as a drag drop handle for the given Drag Drop object.
1640 * @param {String} id the element id to check
1641 * @return {boolean} true if this element is a DragDrop handle, false
1645 isHandle: function(sDDId, sHandleId) {
1646 return ( this.handleIds[sDDId] &&
1647 this.handleIds[sDDId][sHandleId] );
1651 * Returns the DragDrop instance for a given id
1653 * @param {String} id the id of the DragDrop object
1654 * @return {DragDrop} the drag drop object, null if it is not found
1657 getDDById: function(id) {
1658 for (var i in this.ids) {
1659 if (this.ids[i][id]) {
1660 return this.ids[i][id];
1667 * Fired after a registered DragDrop object gets the mousedown event.
1668 * Sets up the events required to track the object being dragged
1669 * @method handleMouseDown
1670 * @param {Event} e the event
1671 * @param oDD the DragDrop object being dragged
1675 handleMouseDown: function(e, oDD) {
1677 Roo.QuickTips.disable();
1679 this.currentTarget = e.getTarget();
1681 this.dragCurrent = oDD;
1683 var el = oDD.getEl();
1685 // track start position
1686 this.startX = e.getPageX();
1687 this.startY = e.getPageY();
1689 this.deltaX = this.startX - el.offsetLeft;
1690 this.deltaY = this.startY - el.offsetTop;
1692 this.dragThreshMet = false;
1694 this.clickTimeout = setTimeout(
1696 var DDM = Roo.dd.DDM;
1697 DDM.startDrag(DDM.startX, DDM.startY);
1699 this.clickTimeThresh );
1703 * Fired when either the drag pixel threshol or the mousedown hold
1704 * time threshold has been met.
1706 * @param x {int} the X position of the original mousedown
1707 * @param y {int} the Y position of the original mousedown
1710 startDrag: function(x, y) {
1711 clearTimeout(this.clickTimeout);
1712 if (this.dragCurrent) {
1713 this.dragCurrent.b4StartDrag(x, y);
1714 this.dragCurrent.startDrag(x, y);
1716 this.dragThreshMet = true;
1720 * Internal function to handle the mouseup event. Will be invoked
1721 * from the context of the document.
1722 * @method handleMouseUp
1723 * @param {Event} e the event
1727 handleMouseUp: function(e) {
1730 Roo.QuickTips.enable();
1732 if (! this.dragCurrent) {
1736 clearTimeout(this.clickTimeout);
1738 if (this.dragThreshMet) {
1739 this.fireEvents(e, true);
1749 * Utility to stop event propagation and event default, if these
1750 * features are turned on.
1752 * @param {Event} e the event as returned by this.getEvent()
1755 stopEvent: function(e){
1756 if(this.stopPropagation) {
1757 e.stopPropagation();
1760 if (this.preventDefault) {
1766 * Internal function to clean up event handlers after the drag
1767 * operation is complete
1769 * @param {Event} e the event
1773 stopDrag: function(e) {
1774 // Fire the drag end event for the item that was dragged
1775 if (this.dragCurrent) {
1776 if (this.dragThreshMet) {
1777 this.dragCurrent.b4EndDrag(e);
1778 this.dragCurrent.endDrag(e);
1781 this.dragCurrent.onMouseUp(e);
1784 this.dragCurrent = null;
1785 this.dragOvers = {};
1789 * Internal function to handle the mousemove event. Will be invoked
1790 * from the context of the html element.
1792 * @TODO figure out what we can do about mouse events lost when the
1793 * user drags objects beyond the window boundary. Currently we can
1794 * detect this in internet explorer by verifying that the mouse is
1795 * down during the mousemove event. Firefox doesn't give us the
1796 * button state on the mousemove event.
1797 * @method handleMouseMove
1798 * @param {Event} e the event
1802 handleMouseMove: function(e) {
1803 if (! this.dragCurrent) {
1807 // var button = e.which || e.button;
1809 // check for IE mouseup outside of page boundary
1810 if (Roo.isIE && (e.button !== 0 && e.button !== 1 && e.button !== 2)) {
1812 return this.handleMouseUp(e);
1815 if (!this.dragThreshMet) {
1816 var diffX = Math.abs(this.startX - e.getPageX());
1817 var diffY = Math.abs(this.startY - e.getPageY());
1818 if (diffX > this.clickPixelThresh ||
1819 diffY > this.clickPixelThresh) {
1820 this.startDrag(this.startX, this.startY);
1824 if (this.dragThreshMet) {
1825 this.dragCurrent.b4Drag(e);
1826 this.dragCurrent.onDrag(e);
1827 if(!this.dragCurrent.moveOnly){
1828 this.fireEvents(e, false);
1838 * Iterates over all of the DragDrop elements to find ones we are
1839 * hovering over or dropping on
1840 * @method fireEvents
1841 * @param {Event} e the event
1842 * @param {boolean} isDrop is this a drop op or a mouseover op?
1846 fireEvents: function(e, isDrop) {
1847 var dc = this.dragCurrent;
1849 // If the user did the mouse up outside of the window, we could
1850 // get here even though we have ended the drag.
1851 if (!dc || dc.isLocked()) {
1855 var pt = e.getPoint();
1857 // cache the previous dragOver array
1865 // Check to see if the object(s) we were hovering over is no longer
1866 // being hovered over so we can fire the onDragOut event
1867 for (var i in this.dragOvers) {
1869 var ddo = this.dragOvers[i];
1871 if (! this.isTypeOfDD(ddo)) {
1875 if (! this.isOverTarget(pt, ddo, this.mode)) {
1876 outEvts.push( ddo );
1880 delete this.dragOvers[i];
1883 for (var sGroup in dc.groups) {
1885 if ("string" != typeof sGroup) {
1889 for (i in this.ids[sGroup]) {
1890 var oDD = this.ids[sGroup][i];
1891 if (! this.isTypeOfDD(oDD)) {
1895 if (oDD.isTarget && !oDD.isLocked() && oDD != dc) {
1896 if (this.isOverTarget(pt, oDD, this.mode)) {
1897 // look for drop interactions
1899 dropEvts.push( oDD );
1900 // look for drag enter and drag over interactions
1903 // initial drag over: dragEnter fires
1904 if (!oldOvers[oDD.id]) {
1905 enterEvts.push( oDD );
1906 // subsequent drag overs: dragOver fires
1908 overEvts.push( oDD );
1911 this.dragOvers[oDD.id] = oDD;
1919 if (outEvts.length) {
1920 dc.b4DragOut(e, outEvts);
1921 dc.onDragOut(e, outEvts);
1924 if (enterEvts.length) {
1925 dc.onDragEnter(e, enterEvts);
1928 if (overEvts.length) {
1929 dc.b4DragOver(e, overEvts);
1930 dc.onDragOver(e, overEvts);
1933 if (dropEvts.length) {
1934 dc.b4DragDrop(e, dropEvts);
1935 dc.onDragDrop(e, dropEvts);
1939 // fire dragout events
1941 for (i=0, len=outEvts.length; i<len; ++i) {
1942 dc.b4DragOut(e, outEvts[i].id);
1943 dc.onDragOut(e, outEvts[i].id);
1946 // fire enter events
1947 for (i=0,len=enterEvts.length; i<len; ++i) {
1948 // dc.b4DragEnter(e, oDD.id);
1949 dc.onDragEnter(e, enterEvts[i].id);
1953 for (i=0,len=overEvts.length; i<len; ++i) {
1954 dc.b4DragOver(e, overEvts[i].id);
1955 dc.onDragOver(e, overEvts[i].id);
1959 for (i=0, len=dropEvts.length; i<len; ++i) {
1960 dc.b4DragDrop(e, dropEvts[i].id);
1961 dc.onDragDrop(e, dropEvts[i].id);
1966 // notify about a drop that did not find a target
1967 if (isDrop && !dropEvts.length) {
1968 dc.onInvalidDrop(e);
1974 * Helper function for getting the best match from the list of drag
1975 * and drop objects returned by the drag and drop events when we are
1976 * in INTERSECT mode. It returns either the first object that the
1977 * cursor is over, or the object that has the greatest overlap with
1978 * the dragged element.
1979 * @method getBestMatch
1980 * @param {DragDrop[]} dds The array of drag and drop objects
1982 * @return {DragDrop} The best single match
1985 getBestMatch: function(dds) {
1987 // Return null if the input is not what we expect
1988 //if (!dds || !dds.length || dds.length == 0) {
1990 // If there is only one item, it wins
1991 //} else if (dds.length == 1) {
1993 var len = dds.length;
1998 // Loop through the targeted items
1999 for (var i=0; i<len; ++i) {
2001 // If the cursor is over the object, it wins. If the
2002 // cursor is over multiple matches, the first one we come
2004 if (dd.cursorIsOver) {
2007 // Otherwise the object with the most overlap wins
2010 winner.overlap.getArea() < dd.overlap.getArea()) {
2021 * Refreshes the cache of the top-left and bottom-right points of the
2022 * drag and drop objects in the specified group(s). This is in the
2023 * format that is stored in the drag and drop instance, so typical
2026 * Roo.dd.DragDropMgr.refreshCache(ddinstance.groups);
2030 * Roo.dd.DragDropMgr.refreshCache({group1:true, group2:true});
2032 * @TODO this really should be an indexed array. Alternatively this
2033 * method could accept both.
2034 * @method refreshCache
2035 * @param {Object} groups an associative array of groups to refresh
2038 refreshCache: function(groups) {
2039 for (var sGroup in groups) {
2040 if ("string" != typeof sGroup) {
2043 for (var i in this.ids[sGroup]) {
2044 var oDD = this.ids[sGroup][i];
2046 if (this.isTypeOfDD(oDD)) {
2047 // if (this.isTypeOfDD(oDD) && oDD.isTarget) {
2048 var loc = this.getLocation(oDD);
2050 this.locationCache[oDD.id] = loc;
2052 delete this.locationCache[oDD.id];
2053 // this will unregister the drag and drop object if
2054 // the element is not in a usable state
2063 * This checks to make sure an element exists and is in the DOM. The
2064 * main purpose is to handle cases where innerHTML is used to remove
2065 * drag and drop objects from the DOM. IE provides an 'unspecified
2066 * error' when trying to access the offsetParent of such an element
2068 * @param {HTMLElement} el the element to check
2069 * @return {boolean} true if the element looks usable
2072 verifyEl: function(el) {
2077 parent = el.offsetParent;
2080 parent = el.offsetParent;
2091 * Returns a Region object containing the drag and drop element's position
2092 * and size, including the padding configured for it
2093 * @method getLocation
2094 * @param {DragDrop} oDD the drag and drop object to get the
2096 * @return {Roo.lib.Region} a Region object representing the total area
2097 * the element occupies, including any padding
2098 * the instance is configured for.
2101 getLocation: function(oDD) {
2102 if (! this.isTypeOfDD(oDD)) {
2106 var el = oDD.getEl(), pos, x1, x2, y1, y2, t, r, b, l;
2109 pos= Roo.lib.Dom.getXY(el);
2117 x2 = x1 + el.offsetWidth;
2119 y2 = y1 + el.offsetHeight;
2121 t = y1 - oDD.padding[0];
2122 r = x2 + oDD.padding[1];
2123 b = y2 + oDD.padding[2];
2124 l = x1 - oDD.padding[3];
2126 return new Roo.lib.Region( t, r, b, l );
2130 * Checks the cursor location to see if it over the target
2131 * @method isOverTarget
2132 * @param {Roo.lib.Point} pt The point to evaluate
2133 * @param {DragDrop} oTarget the DragDrop object we are inspecting
2134 * @return {boolean} true if the mouse is over the target
2138 isOverTarget: function(pt, oTarget, intersect) {
2139 // use cache if available
2140 var loc = this.locationCache[oTarget.id];
2141 if (!loc || !this.useCache) {
2142 loc = this.getLocation(oTarget);
2143 this.locationCache[oTarget.id] = loc;
2151 oTarget.cursorIsOver = loc.contains( pt );
2153 // DragDrop is using this as a sanity check for the initial mousedown
2154 // in this case we are done. In POINT mode, if the drag obj has no
2155 // contraints, we are also done. Otherwise we need to evaluate the
2156 // location of the target as related to the actual location of the
2158 var dc = this.dragCurrent;
2159 if (!dc || !dc.getTargetCoord ||
2160 (!intersect && !dc.constrainX && !dc.constrainY)) {
2161 return oTarget.cursorIsOver;
2164 oTarget.overlap = null;
2166 // Get the current location of the drag element, this is the
2167 // location of the mouse event less the delta that represents
2168 // where the original mousedown happened on the element. We
2169 // need to consider constraints and ticks as well.
2170 var pos = dc.getTargetCoord(pt.x, pt.y);
2172 var el = dc.getDragEl();
2173 var curRegion = new Roo.lib.Region( pos.y,
2174 pos.x + el.offsetWidth,
2175 pos.y + el.offsetHeight,
2178 var overlap = curRegion.intersect(loc);
2181 oTarget.overlap = overlap;
2182 return (intersect) ? true : oTarget.cursorIsOver;
2189 * unload event handler
2194 _onUnload: function(e, me) {
2195 Roo.dd.DragDropMgr.unregAll();
2199 * Cleans up the drag and drop events and objects.
2204 unregAll: function() {
2206 if (this.dragCurrent) {
2208 this.dragCurrent = null;
2211 this._execOnAll("unreg", []);
2213 for (i in this.elementCache) {
2214 delete this.elementCache[i];
2217 this.elementCache = {};
2222 * A cache of DOM elements
2223 * @property elementCache
2230 * Get the wrapper for the DOM element specified
2231 * @method getElWrapper
2232 * @param {String} id the id of the element to get
2233 * @return {Roo.dd.DDM.ElementWrapper} the wrapped element
2235 * @deprecated This wrapper isn't that useful
2238 getElWrapper: function(id) {
2239 var oWrapper = this.elementCache[id];
2240 if (!oWrapper || !oWrapper.el) {
2241 oWrapper = this.elementCache[id] =
2242 new this.ElementWrapper(Roo.getDom(id));
2248 * Returns the actual DOM element
2249 * @method getElement
2250 * @param {String} id the id of the elment to get
2251 * @return {Object} The element
2252 * @deprecated use Roo.getDom instead
2255 getElement: function(id) {
2256 return Roo.getDom(id);
2260 * Returns the style property for the DOM element (i.e.,
2261 * document.getElById(id).style)
2263 * @param {String} id the id of the elment to get
2264 * @return {Object} The style property of the element
2265 * @deprecated use Roo.getDom instead
2268 getCss: function(id) {
2269 var el = Roo.getDom(id);
2270 return (el) ? el.style : null;
2274 * Inner class for cached elements
2275 * @class DragDropMgr.ElementWrapper
2280 ElementWrapper: function(el) {
2285 this.el = el || null;
2290 this.id = this.el && el.id;
2292 * A reference to the style property
2295 this.css = this.el && el.style;
2299 * Returns the X position of an html element
2301 * @param el the element for which to get the position
2302 * @return {int} the X coordinate
2304 * @deprecated use Roo.lib.Dom.getX instead
2307 getPosX: function(el) {
2308 return Roo.lib.Dom.getX(el);
2312 * Returns the Y position of an html element
2314 * @param el the element for which to get the position
2315 * @return {int} the Y coordinate
2316 * @deprecated use Roo.lib.Dom.getY instead
2319 getPosY: function(el) {
2320 return Roo.lib.Dom.getY(el);
2324 * Swap two nodes. In IE, we use the native method, for others we
2325 * emulate the IE behavior
2327 * @param n1 the first node to swap
2328 * @param n2 the other node to swap
2331 swapNode: function(n1, n2) {
2335 var p = n2.parentNode;
2336 var s = n2.nextSibling;
2339 p.insertBefore(n1, n2);
2340 } else if (n2 == n1.nextSibling) {
2341 p.insertBefore(n2, n1);
2343 n1.parentNode.replaceChild(n2, n1);
2344 p.insertBefore(n1, s);
2350 * Returns the current scroll position
2355 getScroll: function () {
2356 var t, l, dde=document.documentElement, db=document.body;
2357 if (dde && (dde.scrollTop || dde.scrollLeft)) {
2366 return { top: t, left: l };
2370 * Returns the specified element style property
2372 * @param {HTMLElement} el the element
2373 * @param {string} styleProp the style property
2374 * @return {string} The value of the style property
2375 * @deprecated use Roo.lib.Dom.getStyle
2378 getStyle: function(el, styleProp) {
2379 return Roo.fly(el).getStyle(styleProp);
2383 * Gets the scrollTop
2384 * @method getScrollTop
2385 * @return {int} the document's scrollTop
2388 getScrollTop: function () { return this.getScroll().top; },
2391 * Gets the scrollLeft
2392 * @method getScrollLeft
2393 * @return {int} the document's scrollTop
2396 getScrollLeft: function () { return this.getScroll().left; },
2399 * Sets the x/y position of an element to the location of the
2402 * @param {HTMLElement} moveEl The element to move
2403 * @param {HTMLElement} targetEl The position reference element
2406 moveToEl: function (moveEl, targetEl) {
2407 var aCoord = Roo.lib.Dom.getXY(targetEl);
2408 Roo.lib.Dom.setXY(moveEl, aCoord);
2412 * Numeric array sort function
2413 * @method numericSort
2416 numericSort: function(a, b) { return (a - b); },
2420 * @property _timeoutCount
2427 * Trying to make the load order less important. Without this we get
2428 * an error if this file is loaded before the Event Utility.
2429 * @method _addListeners
2433 _addListeners: function() {
2434 var DDM = Roo.dd.DDM;
2435 if ( Roo.lib.Event && document ) {
2438 if (DDM._timeoutCount > 2000) {
2440 setTimeout(DDM._addListeners, 10);
2441 if (document && document.body) {
2442 DDM._timeoutCount += 1;
2449 * Recursively searches the immediate parent and all child nodes for
2450 * the handle element in order to determine wheter or not it was
2452 * @method handleWasClicked
2453 * @param node the html element to inspect
2456 handleWasClicked: function(node, id) {
2457 if (this.isHandle(id, node.id)) {
2460 // check to see if this is a text node child of the one we want
2461 var p = node.parentNode;
2464 if (this.isHandle(id, p.id)) {
2479 // shorter alias, save a few bytes
2480 Roo.dd.DDM = Roo.dd.DragDropMgr;
2481 Roo.dd.DDM._addListeners();
2485 * Ext JS Library 1.1.1
2486 * Copyright(c) 2006-2007, Ext JS, LLC.
2488 * Originally Released Under LGPL - original licence link has changed is not relivant.
2491 * <script type="text/javascript">
2496 * A DragDrop implementation where the linked element follows the
2497 * mouse cursor during a drag.
2498 * @extends Roo.dd.DragDrop
2500 * @param {String} id the id of the linked element
2501 * @param {String} sGroup the group of related DragDrop items
2502 * @param {object} config an object containing configurable attributes
2503 * Valid properties for DD:
2506 Roo.dd.DD = function(id, sGroup, config) {
2508 this.init(id, sGroup, config);
2512 Roo.extend(Roo.dd.DD, Roo.dd.DragDrop, {
2515 * When set to true, the utility automatically tries to scroll the browser
2516 * window wehn a drag and drop element is dragged near the viewport boundary.
2524 * Sets the pointer offset to the distance between the linked element's top
2525 * left corner and the location the element was clicked
2526 * @method autoOffset
2527 * @param {int} iPageX the X coordinate of the click
2528 * @param {int} iPageY the Y coordinate of the click
2530 autoOffset: function(iPageX, iPageY) {
2531 var x = iPageX - this.startPageX;
2532 var y = iPageY - this.startPageY;
2533 this.setDelta(x, y);
2537 * Sets the pointer offset. You can call this directly to force the
2538 * offset to be in a particular location (e.g., pass in 0,0 to set it
2539 * to the center of the object)
2541 * @param {int} iDeltaX the distance from the left
2542 * @param {int} iDeltaY the distance from the top
2544 setDelta: function(iDeltaX, iDeltaY) {
2545 this.deltaX = iDeltaX;
2546 this.deltaY = iDeltaY;
2550 * Sets the drag element to the location of the mousedown or click event,
2551 * maintaining the cursor location relative to the location on the element
2552 * that was clicked. Override this if you want to place the element in a
2553 * location other than where the cursor is.
2554 * @method setDragElPos
2555 * @param {int} iPageX the X coordinate of the mousedown or drag event
2556 * @param {int} iPageY the Y coordinate of the mousedown or drag event
2558 setDragElPos: function(iPageX, iPageY) {
2559 // the first time we do this, we are going to check to make sure
2560 // the element has css positioning
2562 var el = this.getDragEl();
2563 this.alignElWithMouse(el, iPageX, iPageY);
2567 * Sets the element to the location of the mousedown or click event,
2568 * maintaining the cursor location relative to the location on the element
2569 * that was clicked. Override this if you want to place the element in a
2570 * location other than where the cursor is.
2571 * @method alignElWithMouse
2572 * @param {HTMLElement} el the element to move
2573 * @param {int} iPageX the X coordinate of the mousedown or drag event
2574 * @param {int} iPageY the Y coordinate of the mousedown or drag event
2576 alignElWithMouse: function(el, iPageX, iPageY) {
2577 var oCoord = this.getTargetCoord(iPageX, iPageY);
2578 var fly = el.dom ? el : Roo.fly(el);
2579 if (!this.deltaSetXY) {
2580 var aCoord = [oCoord.x, oCoord.y];
2582 var newLeft = fly.getLeft(true);
2583 var newTop = fly.getTop(true);
2584 this.deltaSetXY = [ newLeft - oCoord.x, newTop - oCoord.y ];
2586 fly.setLeftTop(oCoord.x + this.deltaSetXY[0], oCoord.y + this.deltaSetXY[1]);
2589 this.cachePosition(oCoord.x, oCoord.y);
2590 this.autoScroll(oCoord.x, oCoord.y, el.offsetHeight, el.offsetWidth);
2595 * Saves the most recent position so that we can reset the constraints and
2596 * tick marks on-demand. We need to know this so that we can calculate the
2597 * number of pixels the element is offset from its original position.
2598 * @method cachePosition
2599 * @param iPageX the current x position (optional, this just makes it so we
2600 * don't have to look it up again)
2601 * @param iPageY the current y position (optional, this just makes it so we
2602 * don't have to look it up again)
2604 cachePosition: function(iPageX, iPageY) {
2606 this.lastPageX = iPageX;
2607 this.lastPageY = iPageY;
2609 var aCoord = Roo.lib.Dom.getXY(this.getEl());
2610 this.lastPageX = aCoord[0];
2611 this.lastPageY = aCoord[1];
2616 * Auto-scroll the window if the dragged object has been moved beyond the
2617 * visible window boundary.
2618 * @method autoScroll
2619 * @param {int} x the drag element's x position
2620 * @param {int} y the drag element's y position
2621 * @param {int} h the height of the drag element
2622 * @param {int} w the width of the drag element
2625 autoScroll: function(x, y, h, w) {
2628 // The client height
2629 var clientH = Roo.lib.Dom.getViewWidth();
2632 var clientW = Roo.lib.Dom.getViewHeight();
2634 // The amt scrolled down
2635 var st = this.DDM.getScrollTop();
2637 // The amt scrolled right
2638 var sl = this.DDM.getScrollLeft();
2640 // Location of the bottom of the element
2643 // Location of the right of the element
2646 // The distance from the cursor to the bottom of the visible area,
2647 // adjusted so that we don't scroll if the cursor is beyond the
2648 // element drag constraints
2649 var toBot = (clientH + st - y - this.deltaY);
2651 // The distance from the cursor to the right of the visible area
2652 var toRight = (clientW + sl - x - this.deltaX);
2655 // How close to the edge the cursor must be before we scroll
2656 // var thresh = (document.all) ? 100 : 40;
2659 // How many pixels to scroll per autoscroll op. This helps to reduce
2660 // clunky scrolling. IE is more sensitive about this ... it needs this
2661 // value to be higher.
2662 var scrAmt = (document.all) ? 80 : 30;
2664 // Scroll down if we are near the bottom of the visible page and the
2665 // obj extends below the crease
2666 if ( bot > clientH && toBot < thresh ) {
2667 window.scrollTo(sl, st + scrAmt);
2670 // Scroll up if the window is scrolled down and the top of the object
2671 // goes above the top border
2672 if ( y < st && st > 0 && y - st < thresh ) {
2673 window.scrollTo(sl, st - scrAmt);
2676 // Scroll right if the obj is beyond the right border and the cursor is
2678 if ( right > clientW && toRight < thresh ) {
2679 window.scrollTo(sl + scrAmt, st);
2682 // Scroll left if the window has been scrolled to the right and the obj
2683 // extends past the left border
2684 if ( x < sl && sl > 0 && x - sl < thresh ) {
2685 window.scrollTo(sl - scrAmt, st);
2691 * Finds the location the element should be placed if we want to move
2692 * it to where the mouse location less the click offset would place us.
2693 * @method getTargetCoord
2694 * @param {int} iPageX the X coordinate of the click
2695 * @param {int} iPageY the Y coordinate of the click
2696 * @return an object that contains the coordinates (Object.x and Object.y)
2699 getTargetCoord: function(iPageX, iPageY) {
2702 var x = iPageX - this.deltaX;
2703 var y = iPageY - this.deltaY;
2705 if (this.constrainX) {
2706 if (x < this.minX) { x = this.minX; }
2707 if (x > this.maxX) { x = this.maxX; }
2710 if (this.constrainY) {
2711 if (y < this.minY) { y = this.minY; }
2712 if (y > this.maxY) { y = this.maxY; }
2715 x = this.getTick(x, this.xTicks);
2716 y = this.getTick(y, this.yTicks);
2723 * Sets up config options specific to this class. Overrides
2724 * Roo.dd.DragDrop, but all versions of this method through the
2725 * inheritance chain are called
2727 applyConfig: function() {
2728 Roo.dd.DD.superclass.applyConfig.call(this);
2729 this.scroll = (this.config.scroll !== false);
2733 * Event that fires prior to the onMouseDown event. Overrides
2736 b4MouseDown: function(e) {
2737 // this.resetConstraints();
2738 this.autoOffset(e.getPageX(),
2743 * Event that fires prior to the onDrag event. Overrides
2746 b4Drag: function(e) {
2747 this.setDragElPos(e.getPageX(),
2751 toString: function() {
2752 return ("DD " + this.id);
2755 //////////////////////////////////////////////////////////////////////////
2756 // Debugging ygDragDrop events that can be overridden
2757 //////////////////////////////////////////////////////////////////////////
2759 startDrag: function(x, y) {
2762 onDrag: function(e) {
2765 onDragEnter: function(e, id) {
2768 onDragOver: function(e, id) {
2771 onDragOut: function(e, id) {
2774 onDragDrop: function(e, id) {
2777 endDrag: function(e) {
2784 * Ext JS Library 1.1.1
2785 * Copyright(c) 2006-2007, Ext JS, LLC.
2787 * Originally Released Under LGPL - original licence link has changed is not relivant.
2790 * <script type="text/javascript">
2794 * @class Roo.dd.DDProxy
2795 * A DragDrop implementation that inserts an empty, bordered div into
2796 * the document that follows the cursor during drag operations. At the time of
2797 * the click, the frame div is resized to the dimensions of the linked html
2798 * element, and moved to the exact location of the linked element.
2800 * References to the "frame" element refer to the single proxy element that
2801 * was created to be dragged in place of all DDProxy elements on the
2804 * @extends Roo.dd.DD
2806 * @param {String} id the id of the linked html element
2807 * @param {String} sGroup the group of related DragDrop objects
2808 * @param {object} config an object containing configurable attributes
2809 * Valid properties for DDProxy in addition to those in DragDrop:
2810 * resizeFrame, centerFrame, dragElId
2812 Roo.dd.DDProxy = function(id, sGroup, config) {
2814 this.init(id, sGroup, config);
2820 * The default drag frame div id
2821 * @property Roo.dd.DDProxy.dragElId
2825 Roo.dd.DDProxy.dragElId = "ygddfdiv";
2827 Roo.extend(Roo.dd.DDProxy, Roo.dd.DD, {
2830 * By default we resize the drag frame to be the same size as the element
2831 * we want to drag (this is to get the frame effect). We can turn it off
2832 * if we want a different behavior.
2833 * @property resizeFrame
2839 * By default the frame is positioned exactly where the drag element is, so
2840 * we use the cursor offset provided by Roo.dd.DD. Another option that works only if
2841 * you do not have constraints on the obj is to have the drag frame centered
2842 * around the cursor. Set centerFrame to true for this effect.
2843 * @property centerFrame
2849 * Creates the proxy element if it does not yet exist
2850 * @method createFrame
2852 createFrame: function() {
2854 var body = document.body;
2856 if (!body || !body.firstChild) {
2857 setTimeout( function() { self.createFrame(); }, 50 );
2861 var div = this.getDragEl();
2864 div = document.createElement("div");
2865 div.id = this.dragElId;
2868 s.position = "absolute";
2869 s.visibility = "hidden";
2871 s.border = "2px solid #aaa";
2874 // appendChild can blow up IE if invoked prior to the window load event
2875 // while rendering a table. It is possible there are other scenarios
2876 // that would cause this to happen as well.
2877 body.insertBefore(div, body.firstChild);
2882 * Initialization for the drag frame element. Must be called in the
2883 * constructor of all subclasses
2886 initFrame: function() {
2890 applyConfig: function() {
2891 Roo.dd.DDProxy.superclass.applyConfig.call(this);
2893 this.resizeFrame = (this.config.resizeFrame !== false);
2894 this.centerFrame = (this.config.centerFrame);
2895 this.setDragElId(this.config.dragElId || Roo.dd.DDProxy.dragElId);
2899 * Resizes the drag frame to the dimensions of the clicked object, positions
2900 * it over the object, and finally displays it
2902 * @param {int} iPageX X click position
2903 * @param {int} iPageY Y click position
2906 showFrame: function(iPageX, iPageY) {
2907 var el = this.getEl();
2908 var dragEl = this.getDragEl();
2909 var s = dragEl.style;
2911 this._resizeProxy();
2913 if (this.centerFrame) {
2914 this.setDelta( Math.round(parseInt(s.width, 10)/2),
2915 Math.round(parseInt(s.height, 10)/2) );
2918 this.setDragElPos(iPageX, iPageY);
2920 Roo.fly(dragEl).show();
2924 * The proxy is automatically resized to the dimensions of the linked
2925 * element when a drag is initiated, unless resizeFrame is set to false
2926 * @method _resizeProxy
2929 _resizeProxy: function() {
2930 if (this.resizeFrame) {
2931 var el = this.getEl();
2932 Roo.fly(this.getDragEl()).setSize(el.offsetWidth, el.offsetHeight);
2936 // overrides Roo.dd.DragDrop
2937 b4MouseDown: function(e) {
2938 var x = e.getPageX();
2939 var y = e.getPageY();
2940 this.autoOffset(x, y);
2941 this.setDragElPos(x, y);
2944 // overrides Roo.dd.DragDrop
2945 b4StartDrag: function(x, y) {
2946 // show the drag frame
2947 this.showFrame(x, y);
2950 // overrides Roo.dd.DragDrop
2951 b4EndDrag: function(e) {
2952 Roo.fly(this.getDragEl()).hide();
2955 // overrides Roo.dd.DragDrop
2956 // By default we try to move the element to the last location of the frame.
2957 // This is so that the default behavior mirrors that of Roo.dd.DD.
2958 endDrag: function(e) {
2960 var lel = this.getEl();
2961 var del = this.getDragEl();
2963 // Show the drag frame briefly so we can get its position
2964 del.style.visibility = "";
2967 // Hide the linked element before the move to get around a Safari
2969 lel.style.visibility = "hidden";
2970 Roo.dd.DDM.moveToEl(lel, del);
2971 del.style.visibility = "hidden";
2972 lel.style.visibility = "";
2977 beforeMove : function(){
2981 afterDrag : function(){
2985 toString: function() {
2986 return ("DDProxy " + this.id);
2992 * Ext JS Library 1.1.1
2993 * Copyright(c) 2006-2007, Ext JS, LLC.
2995 * Originally Released Under LGPL - original licence link has changed is not relivant.
2998 * <script type="text/javascript">
3002 * @class Roo.dd.DDTarget
3003 * A DragDrop implementation that does not move, but can be a drop
3004 * target. You would get the same result by simply omitting implementation
3005 * for the event callbacks, but this way we reduce the processing cost of the
3006 * event listener and the callbacks.
3007 * @extends Roo.dd.DragDrop
3009 * @param {String} id the id of the element that is a drop target
3010 * @param {String} sGroup the group of related DragDrop objects
3011 * @param {object} config an object containing configurable attributes
3012 * Valid properties for DDTarget in addition to those in
3016 Roo.dd.DDTarget = function(id, sGroup, config) {
3018 this.initTarget(id, sGroup, config);
3020 if (config.listeners || config.events) {
3021 Roo.dd.DragDrop.superclass.constructor.call(this, {
3022 listeners : config.listeners || {},
3023 events : config.events || {}
3028 // Roo.dd.DDTarget.prototype = new Roo.dd.DragDrop();
3029 Roo.extend(Roo.dd.DDTarget, Roo.dd.DragDrop, {
3030 toString: function() {
3031 return ("DDTarget " + this.id);
3036 * Ext JS Library 1.1.1
3037 * Copyright(c) 2006-2007, Ext JS, LLC.
3039 * Originally Released Under LGPL - original licence link has changed is not relivant.
3042 * <script type="text/javascript">
3047 * @class Roo.dd.ScrollManager
3048 * Provides automatic scrolling of overflow regions in the page during drag operations.<br><br>
3049 * <b>Note: This class uses "Point Mode" and is untested in "Intersect Mode".</b>
3052 Roo.dd.ScrollManager = function(){
3053 var ddm = Roo.dd.DragDropMgr;
3060 var onStop = function(e){
3065 var triggerRefresh = function(){
3066 if(ddm.dragCurrent){
3067 ddm.refreshCache(ddm.dragCurrent.groups);
3071 var doScroll = function(){
3072 if(ddm.dragCurrent){
3073 var dds = Roo.dd.ScrollManager;
3075 if(proc.el.scroll(proc.dir, dds.increment)){
3079 proc.el.scroll(proc.dir, dds.increment, true, dds.animDuration, triggerRefresh);
3084 var clearProc = function(){
3086 clearInterval(proc.id);
3093 var startProc = function(el, dir){
3094 Roo.log('scroll startproc');
3098 proc.id = setInterval(doScroll, Roo.dd.ScrollManager.frequency);
3101 var onFire = function(e, isDrop){
3103 if(isDrop || !ddm.dragCurrent){ return; }
3104 var dds = Roo.dd.ScrollManager;
3105 if(!dragEl || dragEl != ddm.dragCurrent){
3106 dragEl = ddm.dragCurrent;
3107 // refresh regions on drag start
3111 var xy = Roo.lib.Event.getXY(e);
3112 var pt = new Roo.lib.Point(xy[0], xy[1]);
3114 var el = els[id], r = el._region;
3115 if(r && r.contains(pt) && el.isScrollable()){
3116 if(r.bottom - pt.y <= dds.thresh){
3118 startProc(el, "down");
3121 }else if(r.right - pt.x <= dds.thresh){
3123 startProc(el, "left");
3126 }else if(pt.y - r.top <= dds.thresh){
3128 startProc(el, "up");
3131 }else if(pt.x - r.left <= dds.thresh){
3133 startProc(el, "right");
3142 ddm.fireEvents = ddm.fireEvents.createSequence(onFire, ddm);
3143 ddm.stopDrag = ddm.stopDrag.createSequence(onStop, ddm);
3147 * Registers new overflow element(s) to auto scroll
3148 * @param {String/HTMLElement/Element/Array} el The id of or the element to be scrolled or an array of either
3150 register : function(el){
3151 if(el instanceof Array){
3152 for(var i = 0, len = el.length; i < len; i++) {
3153 this.register(el[i]);
3159 Roo.dd.ScrollManager.els = els;
3163 * Unregisters overflow element(s) so they are no longer scrolled
3164 * @param {String/HTMLElement/Element/Array} el The id of or the element to be removed or an array of either
3166 unregister : function(el){
3167 if(el instanceof Array){
3168 for(var i = 0, len = el.length; i < len; i++) {
3169 this.unregister(el[i]);
3178 * The number of pixels from the edge of a container the pointer needs to be to
3179 * trigger scrolling (defaults to 25)
3185 * The number of pixels to scroll in each scroll increment (defaults to 50)
3191 * The frequency of scrolls in milliseconds (defaults to 500)
3197 * True to animate the scroll (defaults to true)
3203 * The animation duration in seconds -
3204 * MUST BE less than Roo.dd.ScrollManager.frequency! (defaults to .4)
3210 * Manually trigger a cache refresh.
3212 refreshCache : function(){
3214 if(typeof els[id] == 'object'){ // for people extending the object prototype
3215 els[id]._region = els[id].getRegion();
3222 * Ext JS Library 1.1.1
3223 * Copyright(c) 2006-2007, Ext JS, LLC.
3225 * Originally Released Under LGPL - original licence link has changed is not relivant.
3228 * <script type="text/javascript">
3233 * @class Roo.dd.Registry
3234 * Provides easy access to all drag drop components that are registered on a page. Items can be retrieved either
3235 * directly by DOM node id, or by passing in the drag drop event that occurred and looking up the event target.
3238 Roo.dd.Registry = function(){
3243 var getId = function(el, autogen){
3244 if(typeof el == "string"){
3248 if(!id && autogen !== false){
3249 id = "roodd-" + (++autoIdSeed);
3257 * Register a drag drop element
3258 * @param {String|HTMLElement} element The id or DOM node to register
3259 * @param {Object} data (optional) A custom data object that will be passed between the elements that are involved
3260 * in drag drop operations. You can populate this object with any arbitrary properties that your own code
3261 * knows how to interpret, plus there are some specific properties known to the Registry that should be
3262 * populated in the data object (if applicable):
3264 Value Description<br />
3265 --------- ------------------------------------------<br />
3266 handles Array of DOM nodes that trigger dragging<br />
3267 for the element being registered<br />
3268 isHandle True if the element passed in triggers<br />
3269 dragging itself, else false
3272 register : function(el, data){
3274 if(typeof el == "string"){
3275 el = document.getElementById(el);
3278 elements[getId(el)] = data;
3279 if(data.isHandle !== false){
3280 handles[data.ddel.id] = data;
3283 var hs = data.handles;
3284 for(var i = 0, len = hs.length; i < len; i++){
3285 handles[getId(hs[i])] = data;
3291 * Unregister a drag drop element
3292 * @param {String|HTMLElement} element The id or DOM node to unregister
3294 unregister : function(el){
3295 var id = getId(el, false);
3296 var data = elements[id];
3298 delete elements[id];
3300 var hs = data.handles;
3301 for(var i = 0, len = hs.length; i < len; i++){
3302 delete handles[getId(hs[i], false)];
3309 * Returns the handle registered for a DOM Node by id
3310 * @param {String|HTMLElement} id The DOM node or id to look up
3311 * @return {Object} handle The custom handle data
3313 getHandle : function(id){
3314 if(typeof id != "string"){ // must be element?
3321 * Returns the handle that is registered for the DOM node that is the target of the event
3322 * @param {Event} e The event
3323 * @return {Object} handle The custom handle data
3325 getHandleFromEvent : function(e){
3326 var t = Roo.lib.Event.getTarget(e);
3327 return t ? handles[t.id] : null;
3331 * Returns a custom data object that is registered for a DOM node by id
3332 * @param {String|HTMLElement} id The DOM node or id to look up
3333 * @return {Object} data The custom data
3335 getTarget : function(id){
3336 if(typeof id != "string"){ // must be element?
3339 return elements[id];
3343 * Returns a custom data object that is registered for the DOM node that is the target of the event
3344 * @param {Event} e The event
3345 * @return {Object} data The custom data
3347 getTargetFromEvent : function(e){
3348 var t = Roo.lib.Event.getTarget(e);
3349 return t ? elements[t.id] || handles[t.id] : null;
3354 * Ext JS Library 1.1.1
3355 * Copyright(c) 2006-2007, Ext JS, LLC.
3357 * Originally Released Under LGPL - original licence link has changed is not relivant.
3360 * <script type="text/javascript">
3365 * @class Roo.dd.StatusProxy
3366 * A specialized drag proxy that supports a drop status icon, {@link Roo.Layer} styles and auto-repair. This is the
3367 * default drag proxy used by all Roo.dd components.
3369 * @param {Object} config
3371 Roo.dd.StatusProxy = function(config){
3372 Roo.apply(this, config);
3373 this.id = this.id || Roo.id();
3374 this.el = new Roo.Layer({
3376 id: this.id, tag: "div", cls: "x-dd-drag-proxy "+this.dropNotAllowed, children: [
3377 {tag: "div", cls: "x-dd-drop-icon"},
3378 {tag: "div", cls: "x-dd-drag-ghost"}
3381 shadow: !config || config.shadow !== false
3383 this.ghost = Roo.get(this.el.dom.childNodes[1]);
3384 this.dropStatus = this.dropNotAllowed;
3387 Roo.dd.StatusProxy.prototype = {
3389 * @cfg {String} dropAllowed
3390 * The CSS class to apply to the status element when drop is allowed (defaults to "x-dd-drop-ok").
3392 dropAllowed : "x-dd-drop-ok",
3394 * @cfg {String} dropNotAllowed
3395 * The CSS class to apply to the status element when drop is not allowed (defaults to "x-dd-drop-nodrop").
3397 dropNotAllowed : "x-dd-drop-nodrop",
3400 * Updates the proxy's visual element to indicate the status of whether or not drop is allowed
3401 * over the current target element.
3402 * @param {String} cssClass The css class for the new drop status indicator image
3404 setStatus : function(cssClass){
3405 cssClass = cssClass || this.dropNotAllowed;
3406 if(this.dropStatus != cssClass){
3407 this.el.replaceClass(this.dropStatus, cssClass);
3408 this.dropStatus = cssClass;
3413 * Resets the status indicator to the default dropNotAllowed value
3414 * @param {Boolean} clearGhost True to also remove all content from the ghost, false to preserve it
3416 reset : function(clearGhost){
3417 this.el.dom.className = "x-dd-drag-proxy " + this.dropNotAllowed;
3418 this.dropStatus = this.dropNotAllowed;
3420 this.ghost.update("");
3425 * Updates the contents of the ghost element
3426 * @param {String} html The html that will replace the current innerHTML of the ghost element
3428 update : function(html){
3429 if(typeof html == "string"){
3430 this.ghost.update(html);
3432 this.ghost.update("");
3433 html.style.margin = "0";
3434 this.ghost.dom.appendChild(html);
3436 // ensure float = none set?? cant remember why though.
3437 var el = this.ghost.dom.firstChild;
3439 Roo.fly(el).setStyle('float', 'none');
3444 * Returns the underlying proxy {@link Roo.Layer}
3445 * @return {Roo.Layer} el
3452 * Returns the ghost element
3453 * @return {Roo.Element} el
3455 getGhost : function(){
3461 * @param {Boolean} clear True to reset the status and clear the ghost contents, false to preserve them
3463 hide : function(clear){
3471 * Stops the repair animation if it's currently running
3474 if(this.anim && this.anim.isAnimated && this.anim.isAnimated()){
3480 * Displays this proxy
3487 * Force the Layer to sync its shadow and shim positions to the element
3494 * Causes the proxy to return to its position of origin via an animation. Should be called after an
3495 * invalid drop operation by the item being dragged.
3496 * @param {Array} xy The XY position of the element ([x, y])
3497 * @param {Function} callback The function to call after the repair is complete
3498 * @param {Object} scope The scope in which to execute the callback
3500 repair : function(xy, callback, scope){
3501 this.callback = callback;
3503 if(xy && this.animRepair !== false){
3504 this.el.addClass("x-dd-drag-repair");
3505 this.el.hideUnders(true);
3506 this.anim = this.el.shift({
3507 duration: this.repairDuration || .5,
3511 callback: this.afterRepair,
3520 afterRepair : function(){
3522 if(typeof this.callback == "function"){
3523 this.callback.call(this.scope || this);
3525 this.callback = null;
3530 * Ext JS Library 1.1.1
3531 * Copyright(c) 2006-2007, Ext JS, LLC.
3533 * Originally Released Under LGPL - original licence link has changed is not relivant.
3536 * <script type="text/javascript">
3540 * @class Roo.dd.DragSource
3541 * @extends Roo.dd.DDProxy
3542 * A simple class that provides the basic implementation needed to make any element draggable.
3544 * @param {String/HTMLElement/Element} el The container element
3545 * @param {Object} config
3547 Roo.dd.DragSource = function(el, config){
3548 this.el = Roo.get(el);
3551 Roo.apply(this, config);
3554 this.proxy = new Roo.dd.StatusProxy();
3557 Roo.dd.DragSource.superclass.constructor.call(this, this.el.dom, this.ddGroup || this.group,
3558 {dragElId : this.proxy.id, resizeFrame: false, isTarget: false, scroll: this.scroll === true});
3560 this.dragging = false;
3563 Roo.extend(Roo.dd.DragSource, Roo.dd.DDProxy, {
3565 * @cfg {String} dropAllowed
3566 * The CSS class returned to the drag source when drop is allowed (defaults to "x-dd-drop-ok").
3568 dropAllowed : "x-dd-drop-ok",
3570 * @cfg {String} dropNotAllowed
3571 * The CSS class returned to the drag source when drop is not allowed (defaults to "x-dd-drop-nodrop").
3573 dropNotAllowed : "x-dd-drop-nodrop",
3576 * Returns the data object associated with this drag source
3577 * @return {Object} data An object containing arbitrary data
3579 getDragData : function(e){
3580 return this.dragData;
3584 onDragEnter : function(e, id){
3585 var target = Roo.dd.DragDropMgr.getDDById(id);
3586 this.cachedTarget = target;
3587 if(this.beforeDragEnter(target, e, id) !== false){
3588 if(target.isNotifyTarget){
3589 var status = target.notifyEnter(this, e, this.dragData);
3590 this.proxy.setStatus(status);
3592 this.proxy.setStatus(this.dropAllowed);
3595 if(this.afterDragEnter){
3597 * An empty function by default, but provided so that you can perform a custom action
3598 * when the dragged item enters the drop target by providing an implementation.
3599 * @param {Roo.dd.DragDrop} target The drop target
3600 * @param {Event} e The event object
3601 * @param {String} id The id of the dragged element
3602 * @method afterDragEnter
3604 this.afterDragEnter(target, e, id);
3610 * An empty function by default, but provided so that you can perform a custom action
3611 * before the dragged item enters the drop target and optionally cancel the onDragEnter.
3612 * @param {Roo.dd.DragDrop} target The drop target
3613 * @param {Event} e The event object
3614 * @param {String} id The id of the dragged element
3615 * @return {Boolean} isValid True if the drag event is valid, else false to cancel
3617 beforeDragEnter : function(target, e, id){
3622 alignElWithMouse: function() {
3623 Roo.dd.DragSource.superclass.alignElWithMouse.apply(this, arguments);
3628 onDragOver : function(e, id){
3629 var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
3630 if(this.beforeDragOver(target, e, id) !== false){
3631 if(target.isNotifyTarget){
3632 var status = target.notifyOver(this, e, this.dragData);
3633 this.proxy.setStatus(status);
3636 if(this.afterDragOver){
3638 * An empty function by default, but provided so that you can perform a custom action
3639 * while the dragged item is over the drop target by providing an implementation.
3640 * @param {Roo.dd.DragDrop} target The drop target
3641 * @param {Event} e The event object
3642 * @param {String} id The id of the dragged element
3643 * @method afterDragOver
3645 this.afterDragOver(target, e, id);
3651 * An empty function by default, but provided so that you can perform a custom action
3652 * while the dragged item is over the drop target and optionally cancel the onDragOver.
3653 * @param {Roo.dd.DragDrop} target The drop target
3654 * @param {Event} e The event object
3655 * @param {String} id The id of the dragged element
3656 * @return {Boolean} isValid True if the drag event is valid, else false to cancel
3658 beforeDragOver : function(target, e, id){
3663 onDragOut : function(e, id){
3664 var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
3665 if(this.beforeDragOut(target, e, id) !== false){
3666 if(target.isNotifyTarget){
3667 target.notifyOut(this, e, this.dragData);
3670 if(this.afterDragOut){
3672 * An empty function by default, but provided so that you can perform a custom action
3673 * after the dragged item is dragged out of the target without dropping.
3674 * @param {Roo.dd.DragDrop} target The drop target
3675 * @param {Event} e The event object
3676 * @param {String} id The id of the dragged element
3677 * @method afterDragOut
3679 this.afterDragOut(target, e, id);
3682 this.cachedTarget = null;
3686 * An empty function by default, but provided so that you can perform a custom action before the dragged
3687 * item is dragged out of the target without dropping, and optionally cancel the onDragOut.
3688 * @param {Roo.dd.DragDrop} target The drop target
3689 * @param {Event} e The event object
3690 * @param {String} id The id of the dragged element
3691 * @return {Boolean} isValid True if the drag event is valid, else false to cancel
3693 beforeDragOut : function(target, e, id){
3698 onDragDrop : function(e, id){
3699 var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
3700 if(this.beforeDragDrop(target, e, id) !== false){
3701 if(target.isNotifyTarget){
3702 if(target.notifyDrop(this, e, this.dragData)){ // valid drop?
3703 this.onValidDrop(target, e, id);
3705 this.onInvalidDrop(target, e, id);
3708 this.onValidDrop(target, e, id);
3711 if(this.afterDragDrop){
3713 * An empty function by default, but provided so that you can perform a custom action
3714 * after a valid drag drop has occurred by providing an implementation.
3715 * @param {Roo.dd.DragDrop} target The drop target
3716 * @param {Event} e The event object
3717 * @param {String} id The id of the dropped element
3718 * @method afterDragDrop
3720 this.afterDragDrop(target, e, id);
3723 delete this.cachedTarget;
3727 * An empty function by default, but provided so that you can perform a custom action before the dragged
3728 * item is dropped onto the target and optionally cancel the onDragDrop.
3729 * @param {Roo.dd.DragDrop} target The drop target
3730 * @param {Event} e The event object
3731 * @param {String} id The id of the dragged element
3732 * @return {Boolean} isValid True if the drag drop event is valid, else false to cancel
3734 beforeDragDrop : function(target, e, id){
3739 onValidDrop : function(target, e, id){
3741 if(this.afterValidDrop){
3743 * An empty function by default, but provided so that you can perform a custom action
3744 * after a valid drop has occurred by providing an implementation.
3745 * @param {Object} target The target DD
3746 * @param {Event} e The event object
3747 * @param {String} id The id of the dropped element
3748 * @method afterInvalidDrop
3750 this.afterValidDrop(target, e, id);
3755 getRepairXY : function(e, data){
3756 return this.el.getXY();
3760 onInvalidDrop : function(target, e, id){
3761 this.beforeInvalidDrop(target, e, id);
3762 if(this.cachedTarget){
3763 if(this.cachedTarget.isNotifyTarget){
3764 this.cachedTarget.notifyOut(this, e, this.dragData);
3766 this.cacheTarget = null;
3768 this.proxy.repair(this.getRepairXY(e, this.dragData), this.afterRepair, this);
3770 if(this.afterInvalidDrop){
3772 * An empty function by default, but provided so that you can perform a custom action
3773 * after an invalid drop has occurred by providing an implementation.
3774 * @param {Event} e The event object
3775 * @param {String} id The id of the dropped element
3776 * @method afterInvalidDrop
3778 this.afterInvalidDrop(e, id);
3783 afterRepair : function(){
3785 this.el.highlight(this.hlColor || "c3daf9");
3787 this.dragging = false;
3791 * An empty function by default, but provided so that you can perform a custom action after an invalid
3792 * drop has occurred.
3793 * @param {Roo.dd.DragDrop} target The drop target
3794 * @param {Event} e The event object
3795 * @param {String} id The id of the dragged element
3796 * @return {Boolean} isValid True if the invalid drop should proceed, else false to cancel
3798 beforeInvalidDrop : function(target, e, id){
3803 handleMouseDown : function(e){
3807 var data = this.getDragData(e);
3808 if(data && this.onBeforeDrag(data, e) !== false){
3809 this.dragData = data;
3811 Roo.dd.DragSource.superclass.handleMouseDown.apply(this, arguments);
3816 * An empty function by default, but provided so that you can perform a custom action before the initial
3817 * drag event begins and optionally cancel it.
3818 * @param {Object} data An object containing arbitrary data to be shared with drop targets
3819 * @param {Event} e The event object
3820 * @return {Boolean} isValid True if the drag event is valid, else false to cancel
3822 onBeforeDrag : function(data, e){
3827 * An empty function by default, but provided so that you can perform a custom action once the initial
3828 * drag event has begun. The drag cannot be canceled from this function.
3829 * @param {Number} x The x position of the click on the dragged object
3830 * @param {Number} y The y position of the click on the dragged object
3832 onStartDrag : Roo.emptyFn,
3834 // private - YUI override
3835 startDrag : function(x, y){
3837 this.dragging = true;
3838 this.proxy.update("");
3839 this.onInitDrag(x, y);
3844 onInitDrag : function(x, y){
3845 var clone = this.el.dom.cloneNode(true);
3846 clone.id = Roo.id(); // prevent duplicate ids
3847 this.proxy.update(clone);
3848 this.onStartDrag(x, y);
3853 * Returns the drag source's underlying {@link Roo.dd.StatusProxy}
3854 * @return {Roo.dd.StatusProxy} proxy The StatusProxy
3856 getProxy : function(){
3861 * Hides the drag source's {@link Roo.dd.StatusProxy}
3863 hideProxy : function(){
3865 this.proxy.reset(true);
3866 this.dragging = false;
3870 triggerCacheRefresh : function(){
3871 Roo.dd.DDM.refreshCache(this.groups);
3874 // private - override to prevent hiding
3875 b4EndDrag: function(e) {
3878 // private - override to prevent moving
3879 endDrag : function(e){
3880 this.onEndDrag(this.dragData, e);
3884 onEndDrag : function(data, e){
3887 // private - pin to cursor
3888 autoOffset : function(x, y) {
3889 this.setDelta(-12, -20);
3893 * Ext JS Library 1.1.1
3894 * Copyright(c) 2006-2007, Ext JS, LLC.
3896 * Originally Released Under LGPL - original licence link has changed is not relivant.
3899 * <script type="text/javascript">
3904 * @class Roo.dd.DropTarget
3905 * @extends Roo.dd.DDTarget
3906 * A simple class that provides the basic implementation needed to make any element a drop target that can have
3907 * draggable items dropped onto it. The drop has no effect until an implementation of notifyDrop is provided.
3909 * @param {String/HTMLElement/Element} el The container element
3910 * @param {Object} config
3912 Roo.dd.DropTarget = function(el, config){
3913 this.el = Roo.get(el);
3915 var listeners = false; ;
3916 if (config && config.listeners) {
3917 listeners= config.listeners;
3918 delete config.listeners;
3920 Roo.apply(this, config);
3922 if(this.containerScroll){
3923 Roo.dd.ScrollManager.register(this.el);
3927 * @scope Roo.dd.DropTarget
3932 * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the source is now over the
3933 * target. This default implementation adds the CSS class specified by overClass (if any) to the drop element
3934 * and returns the dropAllowed config value. This method should be overridden if drop validation is required.
3936 * IMPORTANT : it should set this.overClass and this.dropAllowed
3938 * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
3939 * @param {Event} e The event
3940 * @param {Object} data An object containing arbitrary data supplied by the drag source
3946 * The function a {@link Roo.dd.DragSource} calls continuously while it is being dragged over the target.
3947 * This method will be called on every mouse movement while the drag source is over the drop target.
3948 * This default implementation simply returns the dropAllowed config value.
3950 * IMPORTANT : it should set this.dropAllowed
3952 * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
3953 * @param {Event} e The event
3954 * @param {Object} data An object containing arbitrary data supplied by the drag source
3960 * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the source has been dragged
3961 * out of the target without dropping. This default implementation simply removes the CSS class specified by
3962 * overClass (if any) from the drop element.
3964 * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
3965 * @param {Event} e The event
3966 * @param {Object} data An object containing arbitrary data supplied by the drag source
3972 * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the dragged item has
3973 * been dropped on it. This method has no default implementation and returns false, so you must provide an
3974 * implementation that does something to process the drop event and returns true so that the drag source's
3975 * repair action does not run.
3977 * IMPORTANT : it should set this.success
3979 * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
3980 * @param {Event} e The event
3981 * @param {Object} data An object containing arbitrary data supplied by the drag source
3987 Roo.dd.DropTarget.superclass.constructor.call( this,
3989 this.ddGroup || this.group,
3992 listeners : listeners || {}
4000 Roo.extend(Roo.dd.DropTarget, Roo.dd.DDTarget, {
4002 * @cfg {String} overClass
4003 * The CSS class applied to the drop target element while the drag source is over it (defaults to "").
4006 * @cfg {String} ddGroup
4007 * The drag drop group to handle drop events for
4011 * @cfg {String} dropAllowed
4012 * The CSS class returned to the drag source when drop is allowed (defaults to "x-dd-drop-ok").
4014 dropAllowed : "x-dd-drop-ok",
4016 * @cfg {String} dropNotAllowed
4017 * The CSS class returned to the drag source when drop is not allowed (defaults to "x-dd-drop-nodrop").
4019 dropNotAllowed : "x-dd-drop-nodrop",
4021 * @cfg {boolean} success
4022 * set this after drop listener..
4026 * @cfg {boolean|String} valid true/false or string (ok-add/ok-sub/ok/nodrop)
4027 * if the drop point is valid for over/enter..
4034 isNotifyTarget : true,
4039 notifyEnter : function(dd, e, data)
4042 this.fireEvent('enter', dd, e, data);
4044 this.el.addClass(this.overClass);
4046 return typeof(this.valid) == 'string' ? 'x-dd-drop-' + this.valid : (
4047 this.valid ? this.dropAllowed : this.dropNotAllowed
4054 notifyOver : function(dd, e, data)
4057 this.fireEvent('over', dd, e, data);
4058 return typeof(this.valid) == 'string' ? 'x-dd-drop-' + this.valid : (
4059 this.valid ? this.dropAllowed : this.dropNotAllowed
4066 notifyOut : function(dd, e, data)
4068 this.fireEvent('out', dd, e, data);
4070 this.el.removeClass(this.overClass);
4077 notifyDrop : function(dd, e, data)
4079 this.success = false;
4080 this.fireEvent('drop', dd, e, data);
4081 return this.success;
4085 * Ext JS Library 1.1.1
4086 * Copyright(c) 2006-2007, Ext JS, LLC.
4088 * Originally Released Under LGPL - original licence link has changed is not relivant.
4091 * <script type="text/javascript">
4096 * @class Roo.dd.DragZone
4097 * @extends Roo.dd.DragSource
4098 * This class provides a container DD instance that proxies for multiple child node sources.<br />
4099 * By default, this class requires that draggable child nodes are registered with {@link Roo.dd.Registry}.
4101 * @param {String/HTMLElement/Element} el The container element
4102 * @param {Object} config
4104 Roo.dd.DragZone = function(el, config){
4105 Roo.dd.DragZone.superclass.constructor.call(this, el, config);
4106 if(this.containerScroll){
4107 Roo.dd.ScrollManager.register(this.el);
4111 Roo.extend(Roo.dd.DragZone, Roo.dd.DragSource, {
4113 * @cfg {Boolean} containerScroll True to register this container with the Scrollmanager
4114 * for auto scrolling during drag operations.
4117 * @cfg {String} hlColor The color to use when visually highlighting the drag source in the afterRepair
4118 * method after a failed drop (defaults to "c3daf9" - light blue)
4122 * Called when a mousedown occurs in this container. Looks in {@link Roo.dd.Registry}
4123 * for a valid target to drag based on the mouse down. Override this method
4124 * to provide your own lookup logic (e.g. finding a child by class name). Make sure your returned
4125 * object has a "ddel" attribute (with an HTML Element) for other functions to work.
4126 * @param {EventObject} e The mouse down event
4127 * @return {Object} The dragData
4129 getDragData : function(e){
4130 return Roo.dd.Registry.getHandleFromEvent(e);
4134 * Called once drag threshold has been reached to initialize the proxy element. By default, it clones the
4135 * this.dragData.ddel
4136 * @param {Number} x The x position of the click on the dragged object
4137 * @param {Number} y The y position of the click on the dragged object
4138 * @return {Boolean} true to continue the drag, false to cancel
4140 onInitDrag : function(x, y){
4141 this.proxy.update(this.dragData.ddel.cloneNode(true));
4142 this.onStartDrag(x, y);
4147 * Called after a repair of an invalid drop. By default, highlights this.dragData.ddel
4149 afterRepair : function(){
4151 Roo.Element.fly(this.dragData.ddel).highlight(this.hlColor || "c3daf9");
4153 this.dragging = false;
4157 * Called before a repair of an invalid drop to get the XY to animate to. By default returns
4158 * the XY of this.dragData.ddel
4159 * @param {EventObject} e The mouse up event
4160 * @return {Array} The xy location (e.g. [100, 200])
4162 getRepairXY : function(e){
4163 return Roo.Element.fly(this.dragData.ddel).getXY();
4167 * Ext JS Library 1.1.1
4168 * Copyright(c) 2006-2007, Ext JS, LLC.
4170 * Originally Released Under LGPL - original licence link has changed is not relivant.
4173 * <script type="text/javascript">
4176 * @class Roo.dd.DropZone
4177 * @extends Roo.dd.DropTarget
4178 * This class provides a container DD instance that proxies for multiple child node targets.<br />
4179 * By default, this class requires that child nodes accepting drop are registered with {@link Roo.dd.Registry}.
4181 * @param {String/HTMLElement/Element} el The container element
4182 * @param {Object} config
4184 Roo.dd.DropZone = function(el, config){
4185 Roo.dd.DropZone.superclass.constructor.call(this, el, config);
4188 Roo.extend(Roo.dd.DropZone, Roo.dd.DropTarget, {
4190 * Returns a custom data object associated with the DOM node that is the target of the event. By default
4191 * this looks up the event target in the {@link Roo.dd.Registry}, although you can override this method to
4192 * provide your own custom lookup.
4193 * @param {Event} e The event
4194 * @return {Object} data The custom data
4196 getTargetFromEvent : function(e){
4197 return Roo.dd.Registry.getTargetFromEvent(e);
4201 * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has entered a drop node
4202 * that it has registered. This method has no default implementation and should be overridden to provide
4203 * node-specific processing if necessary.
4204 * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
4205 * {@link #getTargetFromEvent} for this node)
4206 * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4207 * @param {Event} e The event
4208 * @param {Object} data An object containing arbitrary data supplied by the drag source
4210 onNodeEnter : function(n, dd, e, data){
4215 * Called internally while the DropZone determines that a {@link Roo.dd.DragSource} is over a drop node
4216 * that it has registered. The default implementation returns this.dropNotAllowed, so it should be
4217 * overridden to provide the proper feedback.
4218 * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
4219 * {@link #getTargetFromEvent} for this node)
4220 * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4221 * @param {Event} e The event
4222 * @param {Object} data An object containing arbitrary data supplied by the drag source
4223 * @return {String} status The CSS class that communicates the drop status back to the source so that the
4224 * underlying {@link Roo.dd.StatusProxy} can be updated
4226 onNodeOver : function(n, dd, e, data){
4227 return this.dropAllowed;
4231 * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dragged out of
4232 * the drop node without dropping. This method has no default implementation and should be overridden to provide
4233 * node-specific processing if necessary.
4234 * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
4235 * {@link #getTargetFromEvent} for this node)
4236 * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4237 * @param {Event} e The event
4238 * @param {Object} data An object containing arbitrary data supplied by the drag source
4240 onNodeOut : function(n, dd, e, data){
4245 * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dropped onto
4246 * the drop node. The default implementation returns false, so it should be overridden to provide the
4247 * appropriate processing of the drop event and return true so that the drag source's repair action does not run.
4248 * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
4249 * {@link #getTargetFromEvent} for this node)
4250 * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4251 * @param {Event} e The event
4252 * @param {Object} data An object containing arbitrary data supplied by the drag source
4253 * @return {Boolean} True if the drop was valid, else false
4255 onNodeDrop : function(n, dd, e, data){
4260 * Called internally while the DropZone determines that a {@link Roo.dd.DragSource} is being dragged over it,
4261 * but not over any of its registered drop nodes. The default implementation returns this.dropNotAllowed, so
4262 * it should be overridden to provide the proper feedback if necessary.
4263 * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4264 * @param {Event} e The event
4265 * @param {Object} data An object containing arbitrary data supplied by the drag source
4266 * @return {String} status The CSS class that communicates the drop status back to the source so that the
4267 * underlying {@link Roo.dd.StatusProxy} can be updated
4269 onContainerOver : function(dd, e, data){
4270 return this.dropNotAllowed;
4274 * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dropped on it,
4275 * but not on any of its registered drop nodes. The default implementation returns false, so it should be
4276 * overridden to provide the appropriate processing of the drop event if you need the drop zone itself to
4277 * be able to accept drops. It should return true when valid so that the drag source's repair action does not run.
4278 * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4279 * @param {Event} e The event
4280 * @param {Object} data An object containing arbitrary data supplied by the drag source
4281 * @return {Boolean} True if the drop was valid, else false
4283 onContainerDrop : function(dd, e, data){
4288 * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the source is now over
4289 * the zone. The default implementation returns this.dropNotAllowed and expects that only registered drop
4290 * nodes can process drag drop operations, so if you need the drop zone itself to be able to process drops
4291 * you should override this method and provide a custom implementation.
4292 * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4293 * @param {Event} e The event
4294 * @param {Object} data An object containing arbitrary data supplied by the drag source
4295 * @return {String} status The CSS class that communicates the drop status back to the source so that the
4296 * underlying {@link Roo.dd.StatusProxy} can be updated
4298 notifyEnter : function(dd, e, data){
4299 return this.dropNotAllowed;
4303 * The function a {@link Roo.dd.DragSource} calls continuously while it is being dragged over the drop zone.
4304 * This method will be called on every mouse movement while the drag source is over the drop zone.
4305 * It will call {@link #onNodeOver} while the drag source is over a registered node, and will also automatically
4306 * delegate to the appropriate node-specific methods as necessary when the drag source enters and exits
4307 * registered nodes ({@link #onNodeEnter}, {@link #onNodeOut}). If the drag source is not currently over a
4308 * registered node, it will call {@link #onContainerOver}.
4309 * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4310 * @param {Event} e The event
4311 * @param {Object} data An object containing arbitrary data supplied by the drag source
4312 * @return {String} status The CSS class that communicates the drop status back to the source so that the
4313 * underlying {@link Roo.dd.StatusProxy} can be updated
4315 notifyOver : function(dd, e, data){
4316 var n = this.getTargetFromEvent(e);
4317 if(!n){ // not over valid drop target
4318 if(this.lastOverNode){
4319 this.onNodeOut(this.lastOverNode, dd, e, data);
4320 this.lastOverNode = null;
4322 return this.onContainerOver(dd, e, data);
4324 if(this.lastOverNode != n){
4325 if(this.lastOverNode){
4326 this.onNodeOut(this.lastOverNode, dd, e, data);
4328 this.onNodeEnter(n, dd, e, data);
4329 this.lastOverNode = n;
4331 return this.onNodeOver(n, dd, e, data);
4335 * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the source has been dragged
4336 * out of the zone without dropping. If the drag source is currently over a registered node, the notification
4337 * will be delegated to {@link #onNodeOut} for node-specific handling, otherwise it will be ignored.
4338 * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
4339 * @param {Event} e The event
4340 * @param {Object} data An object containing arbitrary data supplied by the drag zone
4342 notifyOut : function(dd, e, data){
4343 if(this.lastOverNode){
4344 this.onNodeOut(this.lastOverNode, dd, e, data);
4345 this.lastOverNode = null;
4350 * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the dragged item has
4351 * been dropped on it. The drag zone will look up the target node based on the event passed in, and if there
4352 * is a node registered for that event, it will delegate to {@link #onNodeDrop} for node-specific handling,
4353 * otherwise it will call {@link #onContainerDrop}.
4354 * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4355 * @param {Event} e The event
4356 * @param {Object} data An object containing arbitrary data supplied by the drag source
4357 * @return {Boolean} True if the drop was valid, else false
4359 notifyDrop : function(dd, e, data){
4360 if(this.lastOverNode){
4361 this.onNodeOut(this.lastOverNode, dd, e, data);
4362 this.lastOverNode = null;
4364 var n = this.getTargetFromEvent(e);
4366 this.onNodeDrop(n, dd, e, data) :
4367 this.onContainerDrop(dd, e, data);
4371 triggerCacheRefresh : function(){
4372 Roo.dd.DDM.refreshCache(this.groups);
4376 * Ext JS Library 1.1.1
4377 * Copyright(c) 2006-2007, Ext JS, LLC.
4379 * Originally Released Under LGPL - original licence link has changed is not relivant.
4382 * <script type="text/javascript">
4387 * @class Roo.data.SortTypes
4389 * Defines the default sorting (casting?) comparison functions used when sorting data.
4391 Roo.data.SortTypes = {
4393 * Default sort that does nothing
4394 * @param {Mixed} s The value being converted
4395 * @return {Mixed} The comparison value
4402 * The regular expression used to strip tags
4406 stripTagsRE : /<\/?[^>]+>/gi,
4409 * Strips all HTML tags to sort on text only
4410 * @param {Mixed} s The value being converted
4411 * @return {String} The comparison value
4413 asText : function(s){
4414 return String(s).replace(this.stripTagsRE, "");
4418 * Strips all HTML tags to sort on text only - Case insensitive
4419 * @param {Mixed} s The value being converted
4420 * @return {String} The comparison value
4422 asUCText : function(s){
4423 return String(s).toUpperCase().replace(this.stripTagsRE, "");
4427 * Case insensitive string
4428 * @param {Mixed} s The value being converted
4429 * @return {String} The comparison value
4431 asUCString : function(s) {
4432 return String(s).toUpperCase();
4437 * @param {Mixed} s The value being converted
4438 * @return {Number} The comparison value
4440 asDate : function(s) {
4444 if(s instanceof Date){
4447 return Date.parse(String(s));
4452 * @param {Mixed} s The value being converted
4453 * @return {Float} The comparison value
4455 asFloat : function(s) {
4456 var val = parseFloat(String(s).replace(/,/g, ""));
4457 if(isNaN(val)) val = 0;
4463 * @param {Mixed} s The value being converted
4464 * @return {Number} The comparison value
4466 asInt : function(s) {
4467 var val = parseInt(String(s).replace(/,/g, ""));
4468 if(isNaN(val)) val = 0;
4473 * Ext JS Library 1.1.1
4474 * Copyright(c) 2006-2007, Ext JS, LLC.
4476 * Originally Released Under LGPL - original licence link has changed is not relivant.
4479 * <script type="text/javascript">
4483 * @class Roo.data.Record
4484 * Instances of this class encapsulate both record <em>definition</em> information, and record
4485 * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
4486 * to access Records cached in an {@link Roo.data.Store} object.<br>
4488 * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
4489 * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
4492 * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
4494 * This constructor should not be used to create Record objects. Instead, use the constructor generated by
4495 * {@link #create}. The parameters are the same.
4496 * @param {Array} data An associative Array of data values keyed by the field name.
4497 * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
4498 * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
4499 * not specified an integer id is generated.
4501 Roo.data.Record = function(data, id){
4502 this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
4507 * Generate a constructor for a specific record layout.
4508 * @param {Array} o An Array of field definition objects which specify field names, and optionally,
4509 * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
4510 * Each field definition object may contain the following properties: <ul>
4511 * <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,
4512 * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
4513 * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
4514 * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
4515 * is being used, then this is a string containing the javascript expression to reference the data relative to
4516 * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
4517 * to the data item relative to the record element. If the mapping expression is the same as the field name,
4518 * this may be omitted.</p></li>
4519 * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
4520 * <ul><li>auto (Default, implies no conversion)</li>
4525 * <li>date</li></ul></p></li>
4526 * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
4527 * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
4528 * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
4529 * by the Reader into an object that will be stored in the Record. It is passed the
4530 * following parameters:<ul>
4531 * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
4533 * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
4535 * <br>usage:<br><pre><code>
4536 var TopicRecord = Roo.data.Record.create(
4537 {name: 'title', mapping: 'topic_title'},
4538 {name: 'author', mapping: 'username'},
4539 {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
4540 {name: 'lastPost', mapping: 'post_time', type: 'date'},
4541 {name: 'lastPoster', mapping: 'user2'},
4542 {name: 'excerpt', mapping: 'post_text'}
4545 var myNewRecord = new TopicRecord({
4546 title: 'Do my job please',
4549 lastPost: new Date(),
4550 lastPoster: 'Animal',
4551 excerpt: 'No way dude!'
4553 myStore.add(myNewRecord);
4558 Roo.data.Record.create = function(o){
4560 f.superclass.constructor.apply(this, arguments);
4562 Roo.extend(f, Roo.data.Record);
4563 var p = f.prototype;
4564 p.fields = new Roo.util.MixedCollection(false, function(field){
4567 for(var i = 0, len = o.length; i < len; i++){
4568 p.fields.add(new Roo.data.Field(o[i]));
4570 f.getField = function(name){
4571 return p.fields.get(name);
4576 Roo.data.Record.AUTO_ID = 1000;
4577 Roo.data.Record.EDIT = 'edit';
4578 Roo.data.Record.REJECT = 'reject';
4579 Roo.data.Record.COMMIT = 'commit';
4581 Roo.data.Record.prototype = {
4583 * Readonly flag - true if this record has been modified.
4592 join : function(store){
4597 * Set the named field to the specified value.
4598 * @param {String} name The name of the field to set.
4599 * @param {Object} value The value to set the field to.
4601 set : function(name, value){
4602 if(this.data[name] == value){
4609 if(typeof this.modified[name] == 'undefined'){
4610 this.modified[name] = this.data[name];
4612 this.data[name] = value;
4613 if(!this.editing && this.store){
4614 this.store.afterEdit(this);
4619 * Get the value of the named field.
4620 * @param {String} name The name of the field to get the value of.
4621 * @return {Object} The value of the field.
4623 get : function(name){
4624 return this.data[name];
4628 beginEdit : function(){
4629 this.editing = true;
4634 cancelEdit : function(){
4635 this.editing = false;
4636 delete this.modified;
4640 endEdit : function(){
4641 this.editing = false;
4642 if(this.dirty && this.store){
4643 this.store.afterEdit(this);
4648 * Usually called by the {@link Roo.data.Store} which owns the Record.
4649 * Rejects all changes made to the Record since either creation, or the last commit operation.
4650 * Modified fields are reverted to their original values.
4652 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
4653 * of reject operations.
4655 reject : function(){
4656 var m = this.modified;
4658 if(typeof m[n] != "function"){
4659 this.data[n] = m[n];
4663 delete this.modified;
4664 this.editing = false;
4666 this.store.afterReject(this);
4671 * Usually called by the {@link Roo.data.Store} which owns the Record.
4672 * Commits all changes made to the Record since either creation, or the last commit operation.
4674 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
4675 * of commit operations.
4677 commit : function(){
4679 delete this.modified;
4680 this.editing = false;
4682 this.store.afterCommit(this);
4687 hasError : function(){
4688 return this.error != null;
4692 clearError : function(){
4697 * Creates a copy of this record.
4698 * @param {String} id (optional) A new record id if you don't want to use this record's id
4701 copy : function(newId) {
4702 return new this.constructor(Roo.apply({}, this.data), newId || this.id);
4706 * Ext JS Library 1.1.1
4707 * Copyright(c) 2006-2007, Ext JS, LLC.
4709 * Originally Released Under LGPL - original licence link has changed is not relivant.
4712 * <script type="text/javascript">
4718 * @class Roo.data.Store
4719 * @extends Roo.util.Observable
4720 * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
4721 * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
4723 * 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
4724 * has no knowledge of the format of the data returned by the Proxy.<br>
4726 * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
4727 * instances from the data object. These records are cached and made available through accessor functions.
4729 * Creates a new Store.
4730 * @param {Object} config A config object containing the objects needed for the Store to access data,
4731 * and read the data into Records.
4733 Roo.data.Store = function(config){
4734 this.data = new Roo.util.MixedCollection(false);
4735 this.data.getKey = function(o){
4738 this.baseParams = {};
4745 "multisort" : "_multisort"
4748 if(config && config.data){
4749 this.inlineData = config.data;
4753 Roo.apply(this, config);
4755 if(this.reader){ // reader passed
4756 this.reader = Roo.factory(this.reader, Roo.data);
4757 this.reader.xmodule = this.xmodule || false;
4758 if(!this.recordType){
4759 this.recordType = this.reader.recordType;
4761 if(this.reader.onMetaChange){
4762 this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
4766 if(this.recordType){
4767 this.fields = this.recordType.prototype.fields;
4773 * @event datachanged
4774 * Fires when the data cache has changed, and a widget which is using this Store
4775 * as a Record cache should refresh its view.
4776 * @param {Store} this
4781 * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
4782 * @param {Store} this
4783 * @param {Object} meta The JSON metadata
4788 * Fires when Records have been added to the Store
4789 * @param {Store} this
4790 * @param {Roo.data.Record[]} records The array of Records added
4791 * @param {Number} index The index at which the record(s) were added
4796 * Fires when a Record has been removed from the Store
4797 * @param {Store} this
4798 * @param {Roo.data.Record} record The Record that was removed
4799 * @param {Number} index The index at which the record was removed
4804 * Fires when a Record has been updated
4805 * @param {Store} this
4806 * @param {Roo.data.Record} record The Record that was updated
4807 * @param {String} operation The update operation being performed. Value may be one of:
4809 Roo.data.Record.EDIT
4810 Roo.data.Record.REJECT
4811 Roo.data.Record.COMMIT
4817 * Fires when the data cache has been cleared.
4818 * @param {Store} this
4823 * Fires before a request is made for a new data object. If the beforeload handler returns false
4824 * the load action will be canceled.
4825 * @param {Store} this
4826 * @param {Object} options The loading options that were specified (see {@link #load} for details)
4830 * @event beforeloadadd
4831 * Fires after a new set of Records has been loaded.
4832 * @param {Store} this
4833 * @param {Roo.data.Record[]} records The Records that were loaded
4834 * @param {Object} options The loading options that were specified (see {@link #load} for details)
4836 beforeloadadd : true,
4839 * Fires after a new set of Records has been loaded, before they are added to the store.
4840 * @param {Store} this
4841 * @param {Roo.data.Record[]} records The Records that were loaded
4842 * @param {Object} options The loading options that were specified (see {@link #load} for details)
4843 * @params {Object} return from reader
4847 * @event loadexception
4848 * Fires if an exception occurs in the Proxy during loading.
4849 * Called with the signature of the Proxy's "loadexception" event.
4850 * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
4853 * @param {Object} return from JsonData.reader() - success, totalRecords, records
4854 * @param {Object} load options
4855 * @param {Object} jsonData from your request (normally this contains the Exception)
4857 loadexception : true
4861 this.proxy = Roo.factory(this.proxy, Roo.data);
4862 this.proxy.xmodule = this.xmodule || false;
4863 this.relayEvents(this.proxy, ["loadexception"]);
4865 this.sortToggle = {};
4866 this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
4868 Roo.data.Store.superclass.constructor.call(this);
4870 if(this.inlineData){
4871 this.loadData(this.inlineData);
4872 delete this.inlineData;
4876 Roo.extend(Roo.data.Store, Roo.util.Observable, {
4878 * @cfg {boolean} isLocal flag if data is locally available (and can be always looked up
4879 * without a remote query - used by combo/forms at present.
4883 * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
4886 * @cfg {Array} data Inline data to be loaded when the store is initialized.
4889 * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
4890 * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
4893 * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
4894 * on any HTTP request
4897 * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
4900 * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
4904 * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
4905 * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
4910 * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
4911 * loaded or when a record is removed. (defaults to false).
4913 pruneModifiedRecords : false,
4919 * Add Records to the Store and fires the add event.
4920 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
4922 add : function(records){
4923 records = [].concat(records);
4924 for(var i = 0, len = records.length; i < len; i++){
4925 records[i].join(this);
4927 var index = this.data.length;
4928 this.data.addAll(records);
4929 this.fireEvent("add", this, records, index);
4933 * Remove a Record from the Store and fires the remove event.
4934 * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
4936 remove : function(record){
4937 var index = this.data.indexOf(record);
4938 this.data.removeAt(index);
4939 if(this.pruneModifiedRecords){
4940 this.modified.remove(record);
4942 this.fireEvent("remove", this, record, index);
4946 * Remove all Records from the Store and fires the clear event.
4948 removeAll : function(){
4950 if(this.pruneModifiedRecords){
4953 this.fireEvent("clear", this);
4957 * Inserts Records to the Store at the given index and fires the add event.
4958 * @param {Number} index The start index at which to insert the passed Records.
4959 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
4961 insert : function(index, records){
4962 records = [].concat(records);
4963 for(var i = 0, len = records.length; i < len; i++){
4964 this.data.insert(index, records[i]);
4965 records[i].join(this);
4967 this.fireEvent("add", this, records, index);
4971 * Get the index within the cache of the passed Record.
4972 * @param {Roo.data.Record} record The Roo.data.Record object to to find.
4973 * @return {Number} The index of the passed Record. Returns -1 if not found.
4975 indexOf : function(record){
4976 return this.data.indexOf(record);
4980 * Get the index within the cache of the Record with the passed id.
4981 * @param {String} id The id of the Record to find.
4982 * @return {Number} The index of the Record. Returns -1 if not found.
4984 indexOfId : function(id){
4985 return this.data.indexOfKey(id);
4989 * Get the Record with the specified id.
4990 * @param {String} id The id of the Record to find.
4991 * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
4993 getById : function(id){
4994 return this.data.key(id);
4998 * Get the Record at the specified index.
4999 * @param {Number} index The index of the Record to find.
5000 * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
5002 getAt : function(index){
5003 return this.data.itemAt(index);
5007 * Returns a range of Records between specified indices.
5008 * @param {Number} startIndex (optional) The starting index (defaults to 0)
5009 * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
5010 * @return {Roo.data.Record[]} An array of Records
5012 getRange : function(start, end){
5013 return this.data.getRange(start, end);
5017 storeOptions : function(o){
5018 o = Roo.apply({}, o);
5021 this.lastOptions = o;
5025 * Loads the Record cache from the configured Proxy using the configured Reader.
5027 * If using remote paging, then the first load call must specify the <em>start</em>
5028 * and <em>limit</em> properties in the options.params property to establish the initial
5029 * position within the dataset, and the number of Records to cache on each read from the Proxy.
5031 * <strong>It is important to note that for remote data sources, loading is asynchronous,
5032 * and this call will return before the new data has been loaded. Perform any post-processing
5033 * in a callback function, or in a "load" event handler.</strong>
5035 * @param {Object} options An object containing properties which control loading options:<ul>
5036 * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
5037 * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
5038 * passed the following arguments:<ul>
5039 * <li>r : Roo.data.Record[]</li>
5040 * <li>options: Options object from the load call</li>
5041 * <li>success: Boolean success indicator</li></ul></li>
5042 * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
5043 * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
5046 load : function(options){
5047 options = options || {};
5048 if(this.fireEvent("beforeload", this, options) !== false){
5049 this.storeOptions(options);
5050 var p = Roo.apply(options.params || {}, this.baseParams);
5051 // if meta was not loaded from remote source.. try requesting it.
5052 if (!this.reader.metaFromRemote) {
5055 if(this.sortInfo && this.remoteSort){
5056 var pn = this.paramNames;
5057 p[pn["sort"]] = this.sortInfo.field;
5058 p[pn["dir"]] = this.sortInfo.direction;
5060 if (this.multiSort) {
5061 var pn = this.paramNames;
5062 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
5065 this.proxy.load(p, this.reader, this.loadRecords, this, options);
5070 * Reloads the Record cache from the configured Proxy using the configured Reader and
5071 * the options from the last load operation performed.
5072 * @param {Object} options (optional) An object containing properties which may override the options
5073 * used in the last load operation. See {@link #load} for details (defaults to null, in which case
5074 * the most recently used options are reused).
5076 reload : function(options){
5077 this.load(Roo.applyIf(options||{}, this.lastOptions));
5081 // Called as a callback by the Reader during a load operation.
5082 loadRecords : function(o, options, success){
5083 if(!o || success === false){
5084 if(success !== false){
5085 this.fireEvent("load", this, [], options, o);
5087 if(options.callback){
5088 options.callback.call(options.scope || this, [], options, false);
5092 // if data returned failure - throw an exception.
5093 if (o.success === false) {
5094 // show a message if no listener is registered.
5095 if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
5096 Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
5098 // loadmask wil be hooked into this..
5099 this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
5102 var r = o.records, t = o.totalRecords || r.length;
5104 this.fireEvent("beforeloadadd", this, r, options, o);
5106 if(!options || options.add !== true){
5107 if(this.pruneModifiedRecords){
5110 for(var i = 0, len = r.length; i < len; i++){
5114 this.data = this.snapshot;
5115 delete this.snapshot;
5118 this.data.addAll(r);
5119 this.totalLength = t;
5121 this.fireEvent("datachanged", this);
5123 this.totalLength = Math.max(t, this.data.length+r.length);
5126 this.fireEvent("load", this, r, options, o);
5127 if(options.callback){
5128 options.callback.call(options.scope || this, r, options, true);
5134 * Loads data from a passed data block. A Reader which understands the format of the data
5135 * must have been configured in the constructor.
5136 * @param {Object} data The data block from which to read the Records. The format of the data expected
5137 * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
5138 * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
5140 loadData : function(o, append){
5141 var r = this.reader.readRecords(o);
5142 this.loadRecords(r, {add: append}, true);
5146 * Gets the number of cached records.
5148 * <em>If using paging, this may not be the total size of the dataset. If the data object
5149 * used by the Reader contains the dataset size, then the getTotalCount() function returns
5150 * the data set size</em>
5152 getCount : function(){
5153 return this.data.length || 0;
5157 * Gets the total number of records in the dataset as returned by the server.
5159 * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
5160 * the dataset size</em>
5162 getTotalCount : function(){
5163 return this.totalLength || 0;
5167 * Returns the sort state of the Store as an object with two properties:
5169 field {String} The name of the field by which the Records are sorted
5170 direction {String} The sort order, "ASC" or "DESC"
5173 getSortState : function(){
5174 return this.sortInfo;
5178 applySort : function(){
5179 if(this.sortInfo && !this.remoteSort){
5180 var s = this.sortInfo, f = s.field;
5181 var st = this.fields.get(f).sortType;
5182 var fn = function(r1, r2){
5183 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
5184 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
5186 this.data.sort(s.direction, fn);
5187 if(this.snapshot && this.snapshot != this.data){
5188 this.snapshot.sort(s.direction, fn);
5194 * Sets the default sort column and order to be used by the next load operation.
5195 * @param {String} fieldName The name of the field to sort by.
5196 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
5198 setDefaultSort : function(field, dir){
5199 this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
5204 * If remote sorting is used, the sort is performed on the server, and the cache is
5205 * reloaded. If local sorting is used, the cache is sorted internally.
5206 * @param {String} fieldName The name of the field to sort by.
5207 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
5209 sort : function(fieldName, dir){
5210 var f = this.fields.get(fieldName);
5212 this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
5214 if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
5215 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
5220 this.sortToggle[f.name] = dir;
5221 this.sortInfo = {field: f.name, direction: dir};
5222 if(!this.remoteSort){
5224 this.fireEvent("datachanged", this);
5226 this.load(this.lastOptions);
5231 * Calls the specified function for each of the Records in the cache.
5232 * @param {Function} fn The function to call. The Record is passed as the first parameter.
5233 * Returning <em>false</em> aborts and exits the iteration.
5234 * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
5236 each : function(fn, scope){
5237 this.data.each(fn, scope);
5241 * Gets all records modified since the last commit. Modified records are persisted across load operations
5242 * (e.g., during paging).
5243 * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
5245 getModifiedRecords : function(){
5246 return this.modified;
5250 createFilterFn : function(property, value, anyMatch){
5251 if(!value.exec){ // not a regex
5252 value = String(value);
5253 if(value.length == 0){
5256 value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
5259 return value.test(r.data[property]);
5264 * Sums the value of <i>property</i> for each record between start and end and returns the result.
5265 * @param {String} property A field on your records
5266 * @param {Number} start The record index to start at (defaults to 0)
5267 * @param {Number} end The last record index to include (defaults to length - 1)
5268 * @return {Number} The sum
5270 sum : function(property, start, end){
5271 var rs = this.data.items, v = 0;
5273 end = (end || end === 0) ? end : rs.length-1;
5275 for(var i = start; i <= end; i++){
5276 v += (rs[i].data[property] || 0);
5282 * Filter the records by a specified property.
5283 * @param {String} field A field on your records
5284 * @param {String/RegExp} value Either a string that the field
5285 * should start with or a RegExp to test against the field
5286 * @param {Boolean} anyMatch True to match any part not just the beginning
5288 filter : function(property, value, anyMatch){
5289 var fn = this.createFilterFn(property, value, anyMatch);
5290 return fn ? this.filterBy(fn) : this.clearFilter();
5294 * Filter by a function. The specified function will be called with each
5295 * record in this data source. If the function returns true the record is included,
5296 * otherwise it is filtered.
5297 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
5298 * @param {Object} scope (optional) The scope of the function (defaults to this)
5300 filterBy : function(fn, scope){
5301 this.snapshot = this.snapshot || this.data;
5302 this.data = this.queryBy(fn, scope||this);
5303 this.fireEvent("datachanged", this);
5307 * Query the records by a specified property.
5308 * @param {String} field A field on your records
5309 * @param {String/RegExp} value Either a string that the field
5310 * should start with or a RegExp to test against the field
5311 * @param {Boolean} anyMatch True to match any part not just the beginning
5312 * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
5314 query : function(property, value, anyMatch){
5315 var fn = this.createFilterFn(property, value, anyMatch);
5316 return fn ? this.queryBy(fn) : this.data.clone();
5320 * Query by a function. The specified function will be called with each
5321 * record in this data source. If the function returns true the record is included
5323 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
5324 * @param {Object} scope (optional) The scope of the function (defaults to this)
5325 @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
5327 queryBy : function(fn, scope){
5328 var data = this.snapshot || this.data;
5329 return data.filterBy(fn, scope||this);
5333 * Collects unique values for a particular dataIndex from this store.
5334 * @param {String} dataIndex The property to collect
5335 * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
5336 * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
5337 * @return {Array} An array of the unique values
5339 collect : function(dataIndex, allowNull, bypassFilter){
5340 var d = (bypassFilter === true && this.snapshot) ?
5341 this.snapshot.items : this.data.items;
5342 var v, sv, r = [], l = {};
5343 for(var i = 0, len = d.length; i < len; i++){
5344 v = d[i].data[dataIndex];
5346 if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
5355 * Revert to a view of the Record cache with no filtering applied.
5356 * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
5358 clearFilter : function(suppressEvent){
5359 if(this.snapshot && this.snapshot != this.data){
5360 this.data = this.snapshot;
5361 delete this.snapshot;
5362 if(suppressEvent !== true){
5363 this.fireEvent("datachanged", this);
5369 afterEdit : function(record){
5370 if(this.modified.indexOf(record) == -1){
5371 this.modified.push(record);
5373 this.fireEvent("update", this, record, Roo.data.Record.EDIT);
5377 afterReject : function(record){
5378 this.modified.remove(record);
5379 this.fireEvent("update", this, record, Roo.data.Record.REJECT);
5383 afterCommit : function(record){
5384 this.modified.remove(record);
5385 this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
5389 * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
5390 * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
5392 commitChanges : function(){
5393 var m = this.modified.slice(0);
5395 for(var i = 0, len = m.length; i < len; i++){
5401 * Cancel outstanding changes on all changed records.
5403 rejectChanges : function(){
5404 var m = this.modified.slice(0);
5406 for(var i = 0, len = m.length; i < len; i++){
5411 onMetaChange : function(meta, rtype, o){
5412 this.recordType = rtype;
5413 this.fields = rtype.prototype.fields;
5414 delete this.snapshot;
5415 this.sortInfo = meta.sortInfo || this.sortInfo;
5417 this.fireEvent('metachange', this, this.reader.meta);
5421 * Ext JS Library 1.1.1
5422 * Copyright(c) 2006-2007, Ext JS, LLC.
5424 * Originally Released Under LGPL - original licence link has changed is not relivant.
5427 * <script type="text/javascript">
5431 * @class Roo.data.SimpleStore
5432 * @extends Roo.data.Store
5433 * Small helper class to make creating Stores from Array data easier.
5434 * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
5435 * @cfg {Array} fields An array of field definition objects, or field name strings.
5436 * @cfg {Array} data The multi-dimensional array of data
5438 * @param {Object} config
5440 Roo.data.SimpleStore = function(config){
5441 Roo.data.SimpleStore.superclass.constructor.call(this, {
5443 reader: new Roo.data.ArrayReader({
5446 Roo.data.Record.create(config.fields)
5448 proxy : new Roo.data.MemoryProxy(config.data)
5452 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
5454 * Ext JS Library 1.1.1
5455 * Copyright(c) 2006-2007, Ext JS, LLC.
5457 * Originally Released Under LGPL - original licence link has changed is not relivant.
5460 * <script type="text/javascript">
5465 * @extends Roo.data.Store
5466 * @class Roo.data.JsonStore
5467 * Small helper class to make creating Stores for JSON data easier. <br/>
5469 var store = new Roo.data.JsonStore({
5470 url: 'get-images.php',
5472 fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
5475 * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
5476 * JsonReader and HttpProxy (unless inline data is provided).</b>
5477 * @cfg {Array} fields An array of field definition objects, or field name strings.
5479 * @param {Object} config
5481 Roo.data.JsonStore = function(c){
5482 Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
5483 proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
5484 reader: new Roo.data.JsonReader(c, c.fields)
5487 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
5489 * Ext JS Library 1.1.1
5490 * Copyright(c) 2006-2007, Ext JS, LLC.
5492 * Originally Released Under LGPL - original licence link has changed is not relivant.
5495 * <script type="text/javascript">
5499 Roo.data.Field = function(config){
5500 if(typeof config == "string"){
5501 config = {name: config};
5503 Roo.apply(this, config);
5509 var st = Roo.data.SortTypes;
5510 // named sortTypes are supported, here we look them up
5511 if(typeof this.sortType == "string"){
5512 this.sortType = st[this.sortType];
5515 // set default sortType for strings and dates
5519 this.sortType = st.asUCString;
5522 this.sortType = st.asDate;
5525 this.sortType = st.none;
5530 var stripRe = /[\$,%]/g;
5532 // prebuilt conversion function for this field, instead of
5533 // switching every time we're reading a value
5535 var cv, dateFormat = this.dateFormat;
5540 cv = function(v){ return v; };
5543 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
5547 return v !== undefined && v !== null && v !== '' ?
5548 parseInt(String(v).replace(stripRe, ""), 10) : '';
5553 return v !== undefined && v !== null && v !== '' ?
5554 parseFloat(String(v).replace(stripRe, ""), 10) : '';
5559 cv = function(v){ return v === true || v === "true" || v == 1; };
5566 if(v instanceof Date){
5570 if(dateFormat == "timestamp"){
5571 return new Date(v*1000);
5573 return Date.parseDate(v, dateFormat);
5575 var parsed = Date.parse(v);
5576 return parsed ? new Date(parsed) : null;
5585 Roo.data.Field.prototype = {
5593 * Ext JS Library 1.1.1
5594 * Copyright(c) 2006-2007, Ext JS, LLC.
5596 * Originally Released Under LGPL - original licence link has changed is not relivant.
5599 * <script type="text/javascript">
5602 // Base class for reading structured data from a data source. This class is intended to be
5603 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
5606 * @class Roo.data.DataReader
5607 * Base class for reading structured data from a data source. This class is intended to be
5608 * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
5611 Roo.data.DataReader = function(meta, recordType){
5615 this.recordType = recordType instanceof Array ?
5616 Roo.data.Record.create(recordType) : recordType;
5619 Roo.data.DataReader.prototype = {
5621 * Create an empty record
5622 * @param {Object} data (optional) - overlay some values
5623 * @return {Roo.data.Record} record created.
5625 newRow : function(d) {
5627 this.recordType.prototype.fields.each(function(c) {
5629 case 'int' : da[c.name] = 0; break;
5630 case 'date' : da[c.name] = new Date(); break;
5631 case 'float' : da[c.name] = 0.0; break;
5632 case 'boolean' : da[c.name] = false; break;
5633 default : da[c.name] = ""; break;
5637 return new this.recordType(Roo.apply(da, d));
5642 * Ext JS Library 1.1.1
5643 * Copyright(c) 2006-2007, Ext JS, LLC.
5645 * Originally Released Under LGPL - original licence link has changed is not relivant.
5648 * <script type="text/javascript">
5652 * @class Roo.data.DataProxy
5653 * @extends Roo.data.Observable
5654 * This class is an abstract base class for implementations which provide retrieval of
5655 * unformatted data objects.<br>
5657 * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
5658 * (of the appropriate type which knows how to parse the data object) to provide a block of
5659 * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
5661 * Custom implementations must implement the load method as described in
5662 * {@link Roo.data.HttpProxy#load}.
5664 Roo.data.DataProxy = function(){
5668 * Fires before a network request is made to retrieve a data object.
5669 * @param {Object} This DataProxy object.
5670 * @param {Object} params The params parameter to the load function.
5675 * Fires before the load method's callback is called.
5676 * @param {Object} This DataProxy object.
5677 * @param {Object} o The data object.
5678 * @param {Object} arg The callback argument object passed to the load function.
5682 * @event loadexception
5683 * Fires if an Exception occurs during data retrieval.
5684 * @param {Object} This DataProxy object.
5685 * @param {Object} o The data object.
5686 * @param {Object} arg The callback argument object passed to the load function.
5687 * @param {Object} e The Exception.
5689 loadexception : true
5691 Roo.data.DataProxy.superclass.constructor.call(this);
5694 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
5697 * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
5701 * Ext JS Library 1.1.1
5702 * Copyright(c) 2006-2007, Ext JS, LLC.
5704 * Originally Released Under LGPL - original licence link has changed is not relivant.
5707 * <script type="text/javascript">
5710 * @class Roo.data.MemoryProxy
5711 * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
5712 * to the Reader when its load method is called.
5714 * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
5716 Roo.data.MemoryProxy = function(data){
5720 Roo.data.MemoryProxy.superclass.constructor.call(this);
5724 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
5726 * Load data from the requested source (in this case an in-memory
5727 * data object passed to the constructor), read the data object into
5728 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
5729 * process that block using the passed callback.
5730 * @param {Object} params This parameter is not used by the MemoryProxy class.
5731 * @param {Roo.data.DataReader} reader The Reader object which converts the data
5732 * object into a block of Roo.data.Records.
5733 * @param {Function} callback The function into which to pass the block of Roo.data.records.
5734 * The function must be passed <ul>
5735 * <li>The Record block object</li>
5736 * <li>The "arg" argument from the load function</li>
5737 * <li>A boolean success indicator</li>
5739 * @param {Object} scope The scope in which to call the callback
5740 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
5742 load : function(params, reader, callback, scope, arg){
5743 params = params || {};
5746 result = reader.readRecords(this.data);
5748 this.fireEvent("loadexception", this, arg, null, e);
5749 callback.call(scope, null, arg, false);
5752 callback.call(scope, result, arg, true);
5756 update : function(params, records){
5761 * Ext JS Library 1.1.1
5762 * Copyright(c) 2006-2007, Ext JS, LLC.
5764 * Originally Released Under LGPL - original licence link has changed is not relivant.
5767 * <script type="text/javascript">
5770 * @class Roo.data.HttpProxy
5771 * @extends Roo.data.DataProxy
5772 * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
5773 * configured to reference a certain URL.<br><br>
5775 * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
5776 * from which the running page was served.<br><br>
5778 * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
5780 * Be aware that to enable the browser to parse an XML document, the server must set
5781 * the Content-Type header in the HTTP response to "text/xml".
5783 * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
5784 * an {@link Roo.data.Connection} object. If a Connection config is passed, the singleton {@link Roo.Ajax} object
5785 * will be used to make the request.
5787 Roo.data.HttpProxy = function(conn){
5788 Roo.data.HttpProxy.superclass.constructor.call(this);
5789 // is conn a conn config or a real conn?
5791 this.useAjax = !conn || !conn.events;
5795 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
5796 // thse are take from connection...
5799 * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
5802 * @cfg {Object} extraParams (Optional) An object containing properties which are used as
5803 * extra parameters to each request made by this object. (defaults to undefined)
5806 * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
5807 * to each request made by this object. (defaults to undefined)
5810 * @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)
5813 * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
5816 * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
5822 * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
5826 * Return the {@link Roo.data.Connection} object being used by this Proxy.
5827 * @return {Connection} The Connection object. This object may be used to subscribe to events on
5828 * a finer-grained basis than the DataProxy events.
5830 getConnection : function(){
5831 return this.useAjax ? Roo.Ajax : this.conn;
5835 * Load data from the configured {@link Roo.data.Connection}, read the data object into
5836 * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
5837 * process that block using the passed callback.
5838 * @param {Object} params An object containing properties which are to be used as HTTP parameters
5839 * for the request to the remote server.
5840 * @param {Roo.data.DataReader} reader The Reader object which converts the data
5841 * object into a block of Roo.data.Records.
5842 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
5843 * The function must be passed <ul>
5844 * <li>The Record block object</li>
5845 * <li>The "arg" argument from the load function</li>
5846 * <li>A boolean success indicator</li>
5848 * @param {Object} scope The scope in which to call the callback
5849 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
5851 load : function(params, reader, callback, scope, arg){
5852 if(this.fireEvent("beforeload", this, params) !== false){
5854 params : params || {},
5856 callback : callback,
5861 callback : this.loadResponse,
5865 Roo.applyIf(o, this.conn);
5866 if(this.activeRequest){
5867 Roo.Ajax.abort(this.activeRequest);
5869 this.activeRequest = Roo.Ajax.request(o);
5871 this.conn.request(o);
5874 callback.call(scope||this, null, arg, false);
5879 loadResponse : function(o, success, response){
5880 delete this.activeRequest;
5882 this.fireEvent("loadexception", this, o, response);
5883 o.request.callback.call(o.request.scope, null, o.request.arg, false);
5888 result = o.reader.read(response);
5890 this.fireEvent("loadexception", this, o, response, e);
5891 o.request.callback.call(o.request.scope, null, o.request.arg, false);
5895 this.fireEvent("load", this, o, o.request.arg);
5896 o.request.callback.call(o.request.scope, result, o.request.arg, true);
5900 update : function(dataSet){
5905 updateResponse : function(dataSet){
5910 * Ext JS Library 1.1.1
5911 * Copyright(c) 2006-2007, Ext JS, LLC.
5913 * Originally Released Under LGPL - original licence link has changed is not relivant.
5916 * <script type="text/javascript">
5920 * @class Roo.data.ScriptTagProxy
5921 * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
5922 * other than the originating domain of the running page.<br><br>
5924 * <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
5925 * of the running page, you must use this class, rather than DataProxy.</em><br><br>
5927 * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
5928 * source code that is used as the source inside a <script> tag.<br><br>
5930 * In order for the browser to process the returned data, the server must wrap the data object
5931 * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
5932 * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
5933 * depending on whether the callback name was passed:
5936 boolean scriptTag = false;
5937 String cb = request.getParameter("callback");
5940 response.setContentType("text/javascript");
5942 response.setContentType("application/x-json");
5944 Writer out = response.getWriter();
5946 out.write(cb + "(");
5948 out.print(dataBlock.toJsonString());
5955 * @param {Object} config A configuration object.
5957 Roo.data.ScriptTagProxy = function(config){
5958 Roo.data.ScriptTagProxy.superclass.constructor.call(this);
5959 Roo.apply(this, config);
5960 this.head = document.getElementsByTagName("head")[0];
5963 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
5965 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
5967 * @cfg {String} url The URL from which to request the data object.
5970 * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
5974 * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
5975 * the server the name of the callback function set up by the load call to process the returned data object.
5976 * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
5977 * javascript output which calls this named function passing the data object as its only parameter.
5979 callbackParam : "callback",
5981 * @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
5982 * name to the request.
5987 * Load data from the configured URL, read the data object into
5988 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
5989 * process that block using the passed callback.
5990 * @param {Object} params An object containing properties which are to be used as HTTP parameters
5991 * for the request to the remote server.
5992 * @param {Roo.data.DataReader} reader The Reader object which converts the data
5993 * object into a block of Roo.data.Records.
5994 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
5995 * The function must be passed <ul>
5996 * <li>The Record block object</li>
5997 * <li>The "arg" argument from the load function</li>
5998 * <li>A boolean success indicator</li>
6000 * @param {Object} scope The scope in which to call the callback
6001 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
6003 load : function(params, reader, callback, scope, arg){
6004 if(this.fireEvent("beforeload", this, params) !== false){
6006 var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
6009 url += (url.indexOf("?") != -1 ? "&" : "?") + p;
6011 url += "&_dc=" + (new Date().getTime());
6013 var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
6016 cb : "stcCallback"+transId,
6017 scriptId : "stcScript"+transId,
6021 callback : callback,
6027 window[trans.cb] = function(o){
6028 conn.handleResponse(o, trans);
6031 url += String.format("&{0}={1}", this.callbackParam, trans.cb);
6033 if(this.autoAbort !== false){
6037 trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
6039 var script = document.createElement("script");
6040 script.setAttribute("src", url);
6041 script.setAttribute("type", "text/javascript");
6042 script.setAttribute("id", trans.scriptId);
6043 this.head.appendChild(script);
6047 callback.call(scope||this, null, arg, false);
6052 isLoading : function(){
6053 return this.trans ? true : false;
6057 * Abort the current server request.
6060 if(this.isLoading()){
6061 this.destroyTrans(this.trans);
6066 destroyTrans : function(trans, isLoaded){
6067 this.head.removeChild(document.getElementById(trans.scriptId));
6068 clearTimeout(trans.timeoutId);
6070 window[trans.cb] = undefined;
6072 delete window[trans.cb];
6075 // if hasn't been loaded, wait for load to remove it to prevent script error
6076 window[trans.cb] = function(){
6077 window[trans.cb] = undefined;
6079 delete window[trans.cb];
6086 handleResponse : function(o, trans){
6088 this.destroyTrans(trans, true);
6091 result = trans.reader.readRecords(o);
6093 this.fireEvent("loadexception", this, o, trans.arg, e);
6094 trans.callback.call(trans.scope||window, null, trans.arg, false);
6097 this.fireEvent("load", this, o, trans.arg);
6098 trans.callback.call(trans.scope||window, result, trans.arg, true);
6102 handleFailure : function(trans){
6104 this.destroyTrans(trans, false);
6105 this.fireEvent("loadexception", this, null, trans.arg);
6106 trans.callback.call(trans.scope||window, null, trans.arg, false);
6110 * Ext JS Library 1.1.1
6111 * Copyright(c) 2006-2007, Ext JS, LLC.
6113 * Originally Released Under LGPL - original licence link has changed is not relivant.
6116 * <script type="text/javascript">
6120 * @class Roo.data.JsonReader
6121 * @extends Roo.data.DataReader
6122 * Data reader class to create an Array of Roo.data.Record objects from a JSON response
6123 * based on mappings in a provided Roo.data.Record constructor.
6125 * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
6126 * in the reply previously.
6131 var RecordDef = Roo.data.Record.create([
6132 {name: 'name', mapping: 'name'}, // "mapping" property not needed if it's the same as "name"
6133 {name: 'occupation'} // This field will use "occupation" as the mapping.
6135 var myReader = new Roo.data.JsonReader({
6136 totalProperty: "results", // The property which contains the total dataset size (optional)
6137 root: "rows", // The property which contains an Array of row objects
6138 id: "id" // The property within each row object that provides an ID for the record (optional)
6142 * This would consume a JSON file like this:
6144 { 'results': 2, 'rows': [
6145 { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
6146 { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
6149 * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
6150 * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
6151 * paged from the remote server.
6152 * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
6153 * @cfg {String} root name of the property which contains the Array of row objects.
6154 * @cfg {String} id Name of the property within a row object that contains a record identifier value.
6156 * Create a new JsonReader
6157 * @param {Object} meta Metadata configuration options
6158 * @param {Object} recordType Either an Array of field definition objects,
6159 * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
6161 Roo.data.JsonReader = function(meta, recordType){
6164 // set some defaults:
6166 totalProperty: 'total',
6167 successProperty : 'success',
6172 Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
6174 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
6177 * @prop {Boolean} metaFromRemote - if the meta data was loaded from the remote source.
6178 * Used by Store query builder to append _requestMeta to params.
6181 metaFromRemote : false,
6183 * This method is only used by a DataProxy which has retrieved data from a remote server.
6184 * @param {Object} response The XHR object which contains the JSON data in its responseText.
6185 * @return {Object} data A data block which is used by an Roo.data.Store object as
6186 * a cache of Roo.data.Records.
6188 read : function(response){
6189 var json = response.responseText;
6191 var o = /* eval:var:o */ eval("("+json+")");
6193 throw {message: "JsonReader.read: Json object not found"};
6199 this.metaFromRemote = true;
6200 this.meta = o.metaData;
6201 this.recordType = Roo.data.Record.create(o.metaData.fields);
6202 this.onMetaChange(this.meta, this.recordType, o);
6204 return this.readRecords(o);
6207 // private function a store will implement
6208 onMetaChange : function(meta, recordType, o){
6215 simpleAccess: function(obj, subsc) {
6222 getJsonAccessor: function(){
6224 return function(expr) {
6226 return(re.test(expr))
6227 ? new Function("obj", "return obj." + expr)
6237 * Create a data block containing Roo.data.Records from an XML document.
6238 * @param {Object} o An object which contains an Array of row objects in the property specified
6239 * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
6240 * which contains the total size of the dataset.
6241 * @return {Object} data A data block which is used by an Roo.data.Store object as
6242 * a cache of Roo.data.Records.
6244 readRecords : function(o){
6246 * After any data loads, the raw JSON data is available for further custom processing.
6250 var s = this.meta, Record = this.recordType,
6251 f = Record.prototype.fields, fi = f.items, fl = f.length;
6253 // Generate extraction functions for the totalProperty, the root, the id, and for each field
6255 if(s.totalProperty) {
6256 this.getTotal = this.getJsonAccessor(s.totalProperty);
6258 if(s.successProperty) {
6259 this.getSuccess = this.getJsonAccessor(s.successProperty);
6261 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
6263 var g = this.getJsonAccessor(s.id);
6264 this.getId = function(rec) {
6266 return (r === undefined || r === "") ? null : r;
6269 this.getId = function(){return null;};
6272 for(var jj = 0; jj < fl; jj++){
6274 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
6275 this.ef[jj] = this.getJsonAccessor(map);
6279 var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
6280 if(s.totalProperty){
6281 var vt = parseInt(this.getTotal(o), 10);
6286 if(s.successProperty){
6287 var vs = this.getSuccess(o);
6288 if(vs === false || vs === 'false'){
6293 for(var i = 0; i < c; i++){
6296 var id = this.getId(n);
6297 for(var j = 0; j < fl; j++){
6299 var v = this.ef[j](n);
6301 Roo.log('missing convert for ' + f.name);
6305 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
6307 var record = new Record(values, id);
6309 records[i] = record;
6315 totalRecords : totalRecords
6320 * Ext JS Library 1.1.1
6321 * Copyright(c) 2006-2007, Ext JS, LLC.
6323 * Originally Released Under LGPL - original licence link has changed is not relivant.
6326 * <script type="text/javascript">
6330 * @class Roo.data.XmlReader
6331 * @extends Roo.data.DataReader
6332 * Data reader class to create an Array of {@link Roo.data.Record} objects from an XML document
6333 * based on mappings in a provided Roo.data.Record constructor.<br><br>
6335 * <em>Note that in order for the browser to parse a returned XML document, the Content-Type
6336 * header in the HTTP response must be set to "text/xml".</em>
6340 var RecordDef = Roo.data.Record.create([
6341 {name: 'name', mapping: 'name'}, // "mapping" property not needed if it's the same as "name"
6342 {name: 'occupation'} // This field will use "occupation" as the mapping.
6344 var myReader = new Roo.data.XmlReader({
6345 totalRecords: "results", // The element which contains the total dataset size (optional)
6346 record: "row", // The repeated element which contains row information
6347 id: "id" // The element within the row that provides an ID for the record (optional)
6351 * This would consume an XML file like this:
6355 <results>2</results>
6358 <name>Bill</name>
6359 <occupation>Gardener</occupation>
6363 <name>Ben</name>
6364 <occupation>Horticulturalist</occupation>
6368 * @cfg {String} totalRecords The DomQuery path from which to retrieve the total number of records
6369 * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
6370 * paged from the remote server.
6371 * @cfg {String} record The DomQuery path to the repeated element which contains record information.
6372 * @cfg {String} success The DomQuery path to the success attribute used by forms.
6373 * @cfg {String} id The DomQuery path relative from the record element to the element that contains
6374 * a record identifier value.
6376 * Create a new XmlReader
6377 * @param {Object} meta Metadata configuration options
6378 * @param {Mixed} recordType The definition of the data record type to produce. This can be either a valid
6379 * Record subclass created with {@link Roo.data.Record#create}, or an array of objects with which to call
6380 * Roo.data.Record.create. See the {@link Roo.data.Record} class for more details.
6382 Roo.data.XmlReader = function(meta, recordType){
6384 Roo.data.XmlReader.superclass.constructor.call(this, meta, recordType||meta.fields);
6386 Roo.extend(Roo.data.XmlReader, Roo.data.DataReader, {
6388 * This method is only used by a DataProxy which has retrieved data from a remote server.
6389 * @param {Object} response The XHR object which contains the parsed XML document. The response is expected
6390 * to contain a method called 'responseXML' that returns an XML document object.
6391 * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
6392 * a cache of Roo.data.Records.
6394 read : function(response){
6395 var doc = response.responseXML;
6397 throw {message: "XmlReader.read: XML Document not available"};
6399 return this.readRecords(doc);
6403 * Create a data block containing Roo.data.Records from an XML document.
6404 * @param {Object} doc A parsed XML document.
6405 * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
6406 * a cache of Roo.data.Records.
6408 readRecords : function(doc){
6410 * After any data loads/reads, the raw XML Document is available for further custom processing.
6414 var root = doc.documentElement || doc;
6415 var q = Roo.DomQuery;
6416 var recordType = this.recordType, fields = recordType.prototype.fields;
6417 var sid = this.meta.id;
6418 var totalRecords = 0, success = true;
6419 if(this.meta.totalRecords){
6420 totalRecords = q.selectNumber(this.meta.totalRecords, root, 0);
6423 if(this.meta.success){
6424 var sv = q.selectValue(this.meta.success, root, true);
6425 success = sv !== false && sv !== 'false';
6428 var ns = q.select(this.meta.record, root);
6429 for(var i = 0, len = ns.length; i < len; i++) {
6432 var id = sid ? q.selectValue(sid, n) : undefined;
6433 for(var j = 0, jlen = fields.length; j < jlen; j++){
6434 var f = fields.items[j];
6435 var v = q.selectValue(f.mapping || f.name, n, f.defaultValue);
6439 var record = new recordType(values, id);
6441 records[records.length] = record;
6447 totalRecords : totalRecords || records.length
6452 * Ext JS Library 1.1.1
6453 * Copyright(c) 2006-2007, Ext JS, LLC.
6455 * Originally Released Under LGPL - original licence link has changed is not relivant.
6458 * <script type="text/javascript">
6462 * @class Roo.data.ArrayReader
6463 * @extends Roo.data.DataReader
6464 * Data reader class to create an Array of Roo.data.Record objects from an Array.
6465 * Each element of that Array represents a row of data fields. The
6466 * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
6467 * of the field definition if it exists, or the field's ordinal position in the definition.<br>
6471 var RecordDef = Roo.data.Record.create([
6472 {name: 'name', mapping: 1}, // "mapping" only needed if an "id" field is present which
6473 {name: 'occupation', mapping: 2} // precludes using the ordinal position as the index.
6475 var myReader = new Roo.data.ArrayReader({
6476 id: 0 // The subscript within row Array that provides an ID for the Record (optional)
6480 * This would consume an Array like this:
6482 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
6484 * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
6486 * Create a new JsonReader
6487 * @param {Object} meta Metadata configuration options.
6488 * @param {Object} recordType Either an Array of field definition objects
6489 * as specified to {@link Roo.data.Record#create},
6490 * or an {@link Roo.data.Record} object
6491 * created using {@link Roo.data.Record#create}.
6493 Roo.data.ArrayReader = function(meta, recordType){
6494 Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
6497 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
6499 * Create a data block containing Roo.data.Records from an XML document.
6500 * @param {Object} o An Array of row objects which represents the dataset.
6501 * @return {Object} data A data block which is used by an Roo.data.Store object as
6502 * a cache of Roo.data.Records.
6504 readRecords : function(o){
6505 var sid = this.meta ? this.meta.id : null;
6506 var recordType = this.recordType, fields = recordType.prototype.fields;
6509 for(var i = 0; i < root.length; i++){
6512 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
6513 for(var j = 0, jlen = fields.length; j < jlen; j++){
6514 var f = fields.items[j];
6515 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
6516 var v = n[k] !== undefined ? n[k] : f.defaultValue;
6520 var record = new recordType(values, id);
6522 records[records.length] = record;
6526 totalRecords : records.length
6531 * Ext JS Library 1.1.1
6532 * Copyright(c) 2006-2007, Ext JS, LLC.
6534 * Originally Released Under LGPL - original licence link has changed is not relivant.
6537 * <script type="text/javascript">
6542 * @class Roo.data.Tree
6543 * @extends Roo.util.Observable
6544 * Represents a tree data structure and bubbles all the events for its nodes. The nodes
6545 * in the tree have most standard DOM functionality.
6547 * @param {Node} root (optional) The root node
6549 Roo.data.Tree = function(root){
6552 * The root node for this tree
6557 this.setRootNode(root);
6562 * Fires when a new child node is appended to a node in this tree.
6563 * @param {Tree} tree The owner tree
6564 * @param {Node} parent The parent node
6565 * @param {Node} node The newly appended node
6566 * @param {Number} index The index of the newly appended node
6571 * Fires when a child node is removed from a node in this tree.
6572 * @param {Tree} tree The owner tree
6573 * @param {Node} parent The parent node
6574 * @param {Node} node The child node removed
6579 * Fires when a node is moved to a new location in the tree
6580 * @param {Tree} tree The owner tree
6581 * @param {Node} node The node moved
6582 * @param {Node} oldParent The old parent of this node
6583 * @param {Node} newParent The new parent of this node
6584 * @param {Number} index The index it was moved to
6589 * Fires when a new child node is inserted in a node in this tree.
6590 * @param {Tree} tree The owner tree
6591 * @param {Node} parent The parent node
6592 * @param {Node} node The child node inserted
6593 * @param {Node} refNode The child node the node was inserted before
6597 * @event beforeappend
6598 * Fires before a new child is appended to a node in this tree, return false to cancel the append.
6599 * @param {Tree} tree The owner tree
6600 * @param {Node} parent The parent node
6601 * @param {Node} node The child node to be appended
6603 "beforeappend" : true,
6605 * @event beforeremove
6606 * Fires before a child is removed from a node in this tree, return false to cancel the remove.
6607 * @param {Tree} tree The owner tree
6608 * @param {Node} parent The parent node
6609 * @param {Node} node The child node to be removed
6611 "beforeremove" : true,
6614 * Fires before a node is moved to a new location in the tree. Return false to cancel the move.
6615 * @param {Tree} tree The owner tree
6616 * @param {Node} node The node being moved
6617 * @param {Node} oldParent The parent of the node
6618 * @param {Node} newParent The new parent the node is moving to
6619 * @param {Number} index The index it is being moved to
6621 "beforemove" : true,
6623 * @event beforeinsert
6624 * Fires before a new child is inserted in a node in this tree, return false to cancel the insert.
6625 * @param {Tree} tree The owner tree
6626 * @param {Node} parent The parent node
6627 * @param {Node} node The child node to be inserted
6628 * @param {Node} refNode The child node the node is being inserted before
6630 "beforeinsert" : true
6633 Roo.data.Tree.superclass.constructor.call(this);
6636 Roo.extend(Roo.data.Tree, Roo.util.Observable, {
6639 proxyNodeEvent : function(){
6640 return this.fireEvent.apply(this, arguments);
6644 * Returns the root node for this tree.
6647 getRootNode : function(){
6652 * Sets the root node for this tree.
6653 * @param {Node} node
6656 setRootNode : function(node){
6658 node.ownerTree = this;
6660 this.registerNode(node);
6665 * Gets a node in this tree by its id.
6666 * @param {String} id
6669 getNodeById : function(id){
6670 return this.nodeHash[id];
6673 registerNode : function(node){
6674 this.nodeHash[node.id] = node;
6677 unregisterNode : function(node){
6678 delete this.nodeHash[node.id];
6681 toString : function(){
6682 return "[Tree"+(this.id?" "+this.id:"")+"]";
6687 * @class Roo.data.Node
6688 * @extends Roo.util.Observable
6689 * @cfg {Boolean} leaf true if this node is a leaf and does not have children
6690 * @cfg {String} id The id for this node. If one is not specified, one is generated.
6692 * @param {Object} attributes The attributes/config for the node
6694 Roo.data.Node = function(attributes){
6696 * The attributes supplied for the node. You can use this property to access any custom attributes you supplied.
6699 this.attributes = attributes || {};
6700 this.leaf = this.attributes.leaf;
6702 * The node id. @type String
6704 this.id = this.attributes.id;
6706 this.id = Roo.id(null, "ynode-");
6707 this.attributes.id = this.id;
6712 * All child nodes of this node. @type Array
6714 this.childNodes = [];
6715 if(!this.childNodes.indexOf){ // indexOf is a must
6716 this.childNodes.indexOf = function(o){
6717 for(var i = 0, len = this.length; i < len; i++){
6726 * The parent node for this node. @type Node
6728 this.parentNode = null;
6730 * The first direct child node of this node, or null if this node has no child nodes. @type Node
6732 this.firstChild = null;
6734 * The last direct child node of this node, or null if this node has no child nodes. @type Node
6736 this.lastChild = null;
6738 * The node immediately preceding this node in the tree, or null if there is no sibling node. @type Node
6740 this.previousSibling = null;
6742 * The node immediately following this node in the tree, or null if there is no sibling node. @type Node
6744 this.nextSibling = null;
6749 * Fires when a new child node is appended
6750 * @param {Tree} tree The owner tree
6751 * @param {Node} this This node
6752 * @param {Node} node The newly appended node
6753 * @param {Number} index The index of the newly appended node
6758 * Fires when a child node is removed
6759 * @param {Tree} tree The owner tree
6760 * @param {Node} this This node
6761 * @param {Node} node The removed node
6766 * Fires when this node is moved to a new location in the tree
6767 * @param {Tree} tree The owner tree
6768 * @param {Node} this This node
6769 * @param {Node} oldParent The old parent of this node
6770 * @param {Node} newParent The new parent of this node
6771 * @param {Number} index The index it was moved to
6776 * Fires when a new child node is inserted.
6777 * @param {Tree} tree The owner tree
6778 * @param {Node} this This node
6779 * @param {Node} node The child node inserted
6780 * @param {Node} refNode The child node the node was inserted before
6784 * @event beforeappend
6785 * Fires before a new child is appended, return false to cancel the append.
6786 * @param {Tree} tree The owner tree
6787 * @param {Node} this This node
6788 * @param {Node} node The child node to be appended
6790 "beforeappend" : true,
6792 * @event beforeremove
6793 * Fires before a child is removed, return false to cancel the remove.
6794 * @param {Tree} tree The owner tree
6795 * @param {Node} this This node
6796 * @param {Node} node The child node to be removed
6798 "beforeremove" : true,
6801 * Fires before this node is moved to a new location in the tree. Return false to cancel the move.
6802 * @param {Tree} tree The owner tree
6803 * @param {Node} this This node
6804 * @param {Node} oldParent The parent of this node
6805 * @param {Node} newParent The new parent this node is moving to
6806 * @param {Number} index The index it is being moved to
6808 "beforemove" : true,
6810 * @event beforeinsert
6811 * Fires before a new child is inserted, return false to cancel the insert.
6812 * @param {Tree} tree The owner tree
6813 * @param {Node} this This node
6814 * @param {Node} node The child node to be inserted
6815 * @param {Node} refNode The child node the node is being inserted before
6817 "beforeinsert" : true
6819 this.listeners = this.attributes.listeners;
6820 Roo.data.Node.superclass.constructor.call(this);
6823 Roo.extend(Roo.data.Node, Roo.util.Observable, {
6824 fireEvent : function(evtName){
6825 // first do standard event for this node
6826 if(Roo.data.Node.superclass.fireEvent.apply(this, arguments) === false){
6829 // then bubble it up to the tree if the event wasn't cancelled
6830 var ot = this.getOwnerTree();
6832 if(ot.proxyNodeEvent.apply(ot, arguments) === false){
6840 * Returns true if this node is a leaf
6843 isLeaf : function(){
6844 return this.leaf === true;
6848 setFirstChild : function(node){
6849 this.firstChild = node;
6853 setLastChild : function(node){
6854 this.lastChild = node;
6859 * Returns true if this node is the last child of its parent
6862 isLast : function(){
6863 return (!this.parentNode ? true : this.parentNode.lastChild == this);
6867 * Returns true if this node is the first child of its parent
6870 isFirst : function(){
6871 return (!this.parentNode ? true : this.parentNode.firstChild == this);
6874 hasChildNodes : function(){
6875 return !this.isLeaf() && this.childNodes.length > 0;
6879 * Insert node(s) as the last child node of this node.
6880 * @param {Node/Array} node The node or Array of nodes to append
6881 * @return {Node} The appended node if single append, or null if an array was passed
6883 appendChild : function(node){
6885 if(node instanceof Array){
6887 }else if(arguments.length > 1){
6890 // if passed an array or multiple args do them one by one
6892 for(var i = 0, len = multi.length; i < len; i++) {
6893 this.appendChild(multi[i]);
6896 if(this.fireEvent("beforeappend", this.ownerTree, this, node) === false){
6899 var index = this.childNodes.length;
6900 var oldParent = node.parentNode;
6901 // it's a move, make sure we move it cleanly
6903 if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index) === false){
6906 oldParent.removeChild(node);
6908 index = this.childNodes.length;
6910 this.setFirstChild(node);
6912 this.childNodes.push(node);
6913 node.parentNode = this;
6914 var ps = this.childNodes[index-1];
6916 node.previousSibling = ps;
6917 ps.nextSibling = node;
6919 node.previousSibling = null;
6921 node.nextSibling = null;
6922 this.setLastChild(node);
6923 node.setOwnerTree(this.getOwnerTree());
6924 this.fireEvent("append", this.ownerTree, this, node, index);
6926 node.fireEvent("move", this.ownerTree, node, oldParent, this, index);
6933 * Removes a child node from this node.
6934 * @param {Node} node The node to remove
6935 * @return {Node} The removed node
6937 removeChild : function(node){
6938 var index = this.childNodes.indexOf(node);
6942 if(this.fireEvent("beforeremove", this.ownerTree, this, node) === false){
6946 // remove it from childNodes collection
6947 this.childNodes.splice(index, 1);
6950 if(node.previousSibling){
6951 node.previousSibling.nextSibling = node.nextSibling;
6953 if(node.nextSibling){
6954 node.nextSibling.previousSibling = node.previousSibling;
6957 // update child refs
6958 if(this.firstChild == node){
6959 this.setFirstChild(node.nextSibling);
6961 if(this.lastChild == node){
6962 this.setLastChild(node.previousSibling);
6965 node.setOwnerTree(null);
6966 // clear any references from the node
6967 node.parentNode = null;
6968 node.previousSibling = null;
6969 node.nextSibling = null;
6970 this.fireEvent("remove", this.ownerTree, this, node);
6975 * Inserts the first node before the second node in this nodes childNodes collection.
6976 * @param {Node} node The node to insert
6977 * @param {Node} refNode The node to insert before (if null the node is appended)
6978 * @return {Node} The inserted node
6980 insertBefore : function(node, refNode){
6981 if(!refNode){ // like standard Dom, refNode can be null for append
6982 return this.appendChild(node);
6985 if(node == refNode){
6989 if(this.fireEvent("beforeinsert", this.ownerTree, this, node, refNode) === false){
6992 var index = this.childNodes.indexOf(refNode);
6993 var oldParent = node.parentNode;
6994 var refIndex = index;
6996 // when moving internally, indexes will change after remove
6997 if(oldParent == this && this.childNodes.indexOf(node) < index){
7001 // it's a move, make sure we move it cleanly
7003 if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index, refNode) === false){
7006 oldParent.removeChild(node);
7009 this.setFirstChild(node);
7011 this.childNodes.splice(refIndex, 0, node);
7012 node.parentNode = this;
7013 var ps = this.childNodes[refIndex-1];
7015 node.previousSibling = ps;
7016 ps.nextSibling = node;
7018 node.previousSibling = null;
7020 node.nextSibling = refNode;
7021 refNode.previousSibling = node;
7022 node.setOwnerTree(this.getOwnerTree());
7023 this.fireEvent("insert", this.ownerTree, this, node, refNode);
7025 node.fireEvent("move", this.ownerTree, node, oldParent, this, refIndex, refNode);
7031 * Returns the child node at the specified index.
7032 * @param {Number} index
7035 item : function(index){
7036 return this.childNodes[index];
7040 * Replaces one child node in this node with another.
7041 * @param {Node} newChild The replacement node
7042 * @param {Node} oldChild The node to replace
7043 * @return {Node} The replaced node
7045 replaceChild : function(newChild, oldChild){
7046 this.insertBefore(newChild, oldChild);
7047 this.removeChild(oldChild);
7052 * Returns the index of a child node
7053 * @param {Node} node
7054 * @return {Number} The index of the node or -1 if it was not found
7056 indexOf : function(child){
7057 return this.childNodes.indexOf(child);
7061 * Returns the tree this node is in.
7064 getOwnerTree : function(){
7065 // if it doesn't have one, look for one
7066 if(!this.ownerTree){
7070 this.ownerTree = p.ownerTree;
7076 return this.ownerTree;
7080 * Returns depth of this node (the root node has a depth of 0)
7083 getDepth : function(){
7086 while(p.parentNode){
7094 setOwnerTree : function(tree){
7095 // if it's move, we need to update everyone
7096 if(tree != this.ownerTree){
7098 this.ownerTree.unregisterNode(this);
7100 this.ownerTree = tree;
7101 var cs = this.childNodes;
7102 for(var i = 0, len = cs.length; i < len; i++) {
7103 cs[i].setOwnerTree(tree);
7106 tree.registerNode(this);
7112 * Returns the path for this node. The path can be used to expand or select this node programmatically.
7113 * @param {String} attr (optional) The attr to use for the path (defaults to the node's id)
7114 * @return {String} The path
7116 getPath : function(attr){
7117 attr = attr || "id";
7118 var p = this.parentNode;
7119 var b = [this.attributes[attr]];
7121 b.unshift(p.attributes[attr]);
7124 var sep = this.getOwnerTree().pathSeparator;
7125 return sep + b.join(sep);
7129 * Bubbles up the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
7130 * function call will be the scope provided or the current node. The arguments to the function
7131 * will be the args provided or the current node. If the function returns false at any point,
7132 * the bubble is stopped.
7133 * @param {Function} fn The function to call
7134 * @param {Object} scope (optional) The scope of the function (defaults to current node)
7135 * @param {Array} args (optional) The args to call the function with (default to passing the current node)
7137 bubble : function(fn, scope, args){
7140 if(fn.call(scope || p, args || p) === false){
7148 * Cascades down the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
7149 * function call will be the scope provided or the current node. The arguments to the function
7150 * will be the args provided or the current node. If the function returns false at any point,
7151 * the cascade is stopped on that branch.
7152 * @param {Function} fn The function to call
7153 * @param {Object} scope (optional) The scope of the function (defaults to current node)
7154 * @param {Array} args (optional) The args to call the function with (default to passing the current node)
7156 cascade : function(fn, scope, args){
7157 if(fn.call(scope || this, args || this) !== false){
7158 var cs = this.childNodes;
7159 for(var i = 0, len = cs.length; i < len; i++) {
7160 cs[i].cascade(fn, scope, args);
7166 * Interates the child nodes of this node, calling the specified function with each node. The scope (<i>this</i>) of
7167 * function call will be the scope provided or the current node. The arguments to the function
7168 * will be the args provided or the current node. If the function returns false at any point,
7169 * the iteration stops.
7170 * @param {Function} fn The function to call
7171 * @param {Object} scope (optional) The scope of the function (defaults to current node)
7172 * @param {Array} args (optional) The args to call the function with (default to passing the current node)
7174 eachChild : function(fn, scope, args){
7175 var cs = this.childNodes;
7176 for(var i = 0, len = cs.length; i < len; i++) {
7177 if(fn.call(scope || this, args || cs[i]) === false){
7184 * Finds the first child that has the attribute with the specified value.
7185 * @param {String} attribute The attribute name
7186 * @param {Mixed} value The value to search for
7187 * @return {Node} The found child or null if none was found
7189 findChild : function(attribute, value){
7190 var cs = this.childNodes;
7191 for(var i = 0, len = cs.length; i < len; i++) {
7192 if(cs[i].attributes[attribute] == value){
7200 * Finds the first child by a custom function. The child matches if the function passed
7202 * @param {Function} fn
7203 * @param {Object} scope (optional)
7204 * @return {Node} The found child or null if none was found
7206 findChildBy : function(fn, scope){
7207 var cs = this.childNodes;
7208 for(var i = 0, len = cs.length; i < len; i++) {
7209 if(fn.call(scope||cs[i], cs[i]) === true){
7217 * Sorts this nodes children using the supplied sort function
7218 * @param {Function} fn
7219 * @param {Object} scope (optional)
7221 sort : function(fn, scope){
7222 var cs = this.childNodes;
7223 var len = cs.length;
7225 var sortFn = scope ? function(){fn.apply(scope, arguments);} : fn;
7227 for(var i = 0; i < len; i++){
7229 n.previousSibling = cs[i-1];
7230 n.nextSibling = cs[i+1];
7232 this.setFirstChild(n);
7235 this.setLastChild(n);
7242 * Returns true if this node is an ancestor (at any point) of the passed node.
7243 * @param {Node} node
7246 contains : function(node){
7247 return node.isAncestor(this);
7251 * Returns true if the passed node is an ancestor (at any point) of this node.
7252 * @param {Node} node
7255 isAncestor : function(node){
7256 var p = this.parentNode;
7266 toString : function(){
7267 return "[Node"+(this.id?" "+this.id:"")+"]";
7271 * Ext JS Library 1.1.1
7272 * Copyright(c) 2006-2007, Ext JS, LLC.
7274 * Originally Released Under LGPL - original licence link has changed is not relivant.
7277 * <script type="text/javascript">
7282 * @class Roo.ComponentMgr
7283 * Provides a common registry of all components on a page so that they can be easily accessed by component id (see {@link Roo.getCmp}).
7286 Roo.ComponentMgr = function(){
7287 var all = new Roo.util.MixedCollection();
7291 * Registers a component.
7292 * @param {Roo.Component} c The component
7294 register : function(c){
7299 * Unregisters a component.
7300 * @param {Roo.Component} c The component
7302 unregister : function(c){
7307 * Returns a component by id
7308 * @param {String} id The component id
7315 * Registers a function that will be called when a specified component is added to ComponentMgr
7316 * @param {String} id The component id
7317 * @param {Funtction} fn The callback function
7318 * @param {Object} scope The scope of the callback
7320 onAvailable : function(id, fn, scope){
7321 all.on("add", function(index, o){
7323 fn.call(scope || o, o);
7324 all.un("add", fn, scope);
7331 * Ext JS Library 1.1.1
7332 * Copyright(c) 2006-2007, Ext JS, LLC.
7334 * Originally Released Under LGPL - original licence link has changed is not relivant.
7337 * <script type="text/javascript">
7341 * @class Roo.Component
7342 * @extends Roo.util.Observable
7343 * Base class for all major Roo components. All subclasses of Component can automatically participate in the standard
7344 * Roo component lifecycle of creation, rendering and destruction. They also have automatic support for basic hide/show
7345 * and enable/disable behavior. Component allows any subclass to be lazy-rendered into any {@link Roo.Container} and
7346 * to be automatically registered with the {@link Roo.ComponentMgr} so that it can be referenced at any time via {@link Roo.getCmp}.
7347 * All visual components (widgets) that require rendering into a layout should subclass Component.
7349 * @param {Roo.Element/String/Object} config The configuration options. If an element is passed, it is set as the internal
7350 * 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
7351 * and is used as the component id. Otherwise, it is assumed to be a standard config object and is applied to the component.
7353 Roo.Component = function(config){
7354 config = config || {};
7355 if(config.tagName || config.dom || typeof config == "string"){ // element object
7356 config = {el: config, id: config.id || config};
7358 this.initialConfig = config;
7360 Roo.apply(this, config);
7364 * Fires after the component is disabled.
7365 * @param {Roo.Component} this
7370 * Fires after the component is enabled.
7371 * @param {Roo.Component} this
7376 * Fires before the component is shown. Return false to stop the show.
7377 * @param {Roo.Component} this
7382 * Fires after the component is shown.
7383 * @param {Roo.Component} this
7388 * Fires before the component is hidden. Return false to stop the hide.
7389 * @param {Roo.Component} this
7394 * Fires after the component is hidden.
7395 * @param {Roo.Component} this
7399 * @event beforerender
7400 * Fires before the component is rendered. Return false to stop the render.
7401 * @param {Roo.Component} this
7403 beforerender : true,
7406 * Fires after the component is rendered.
7407 * @param {Roo.Component} this
7411 * @event beforedestroy
7412 * Fires before the component is destroyed. Return false to stop the destroy.
7413 * @param {Roo.Component} this
7415 beforedestroy : true,
7418 * Fires after the component is destroyed.
7419 * @param {Roo.Component} this
7424 this.id = "ext-comp-" + (++Roo.Component.AUTO_ID);
7426 Roo.ComponentMgr.register(this);
7427 Roo.Component.superclass.constructor.call(this);
7428 this.initComponent();
7429 if(this.renderTo){ // not supported by all components yet. use at your own risk!
7430 this.render(this.renderTo);
7431 delete this.renderTo;
7436 Roo.Component.AUTO_ID = 1000;
7438 Roo.extend(Roo.Component, Roo.util.Observable, {
7440 * @scope Roo.Component.prototype
7442 * true if this component is hidden. Read-only.
7447 * true if this component is disabled. Read-only.
7452 * true if this component has been rendered. Read-only.
7456 /** @cfg {String} disableClass
7457 * CSS class added to the component when it is disabled (defaults to "x-item-disabled").
7459 disabledClass : "x-item-disabled",
7460 /** @cfg {Boolean} allowDomMove
7461 * Whether the component can move the Dom node when rendering (defaults to true).
7463 allowDomMove : true,
7464 /** @cfg {String} hideMode
7465 * How this component should hidden. Supported values are
7466 * "visibility" (css visibility), "offsets" (negative offset position) and
7467 * "display" (css display) - defaults to "display".
7469 hideMode: 'display',
7472 ctype : "Roo.Component",
7475 * @cfg {String} actionMode
7476 * which property holds the element that used for hide() / show() / disable() / enable()
7482 getActionEl : function(){
7483 return this[this.actionMode];
7486 initComponent : Roo.emptyFn,
7488 * If this is a lazy rendering component, render it to its container element.
7489 * @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.
7491 render : function(container, position){
7492 if(!this.rendered && this.fireEvent("beforerender", this) !== false){
7493 if(!container && this.el){
7494 this.el = Roo.get(this.el);
7495 container = this.el.dom.parentNode;
7496 this.allowDomMove = false;
7498 this.container = Roo.get(container);
7499 this.rendered = true;
7500 if(position !== undefined){
7501 if(typeof position == 'number'){
7502 position = this.container.dom.childNodes[position];
7504 position = Roo.getDom(position);
7507 this.onRender(this.container, position || null);
7509 this.el.addClass(this.cls);
7513 this.el.applyStyles(this.style);
7516 this.fireEvent("render", this);
7517 this.afterRender(this.container);
7529 // default function is not really useful
7530 onRender : function(ct, position){
7532 this.el = Roo.get(this.el);
7533 if(this.allowDomMove !== false){
7534 ct.dom.insertBefore(this.el.dom, position);
7540 getAutoCreate : function(){
7541 var cfg = typeof this.autoCreate == "object" ?
7542 this.autoCreate : Roo.apply({}, this.defaultAutoCreate);
7543 if(this.id && !cfg.id){
7550 afterRender : Roo.emptyFn,
7553 * Destroys this component by purging any event listeners, removing the component's element from the DOM,
7554 * removing the component from its {@link Roo.Container} (if applicable) and unregistering it from {@link Roo.ComponentMgr}.
7556 destroy : function(){
7557 if(this.fireEvent("beforedestroy", this) !== false){
7558 this.purgeListeners();
7559 this.beforeDestroy();
7561 this.el.removeAllListeners();
7563 if(this.actionMode == "container"){
7564 this.container.remove();
7568 Roo.ComponentMgr.unregister(this);
7569 this.fireEvent("destroy", this);
7574 beforeDestroy : function(){
7579 onDestroy : function(){
7584 * Returns the underlying {@link Roo.Element}.
7585 * @return {Roo.Element} The element
7592 * Returns the id of this component.
7600 * Try to focus this component.
7601 * @param {Boolean} selectText True to also select the text in this component (if applicable)
7602 * @return {Roo.Component} this
7604 focus : function(selectText){
7607 if(selectText === true){
7608 this.el.dom.select();
7623 * Disable this component.
7624 * @return {Roo.Component} this
7626 disable : function(){
7630 this.disabled = true;
7631 this.fireEvent("disable", this);
7636 onDisable : function(){
7637 this.getActionEl().addClass(this.disabledClass);
7638 this.el.dom.disabled = true;
7642 * Enable this component.
7643 * @return {Roo.Component} this
7645 enable : function(){
7649 this.disabled = false;
7650 this.fireEvent("enable", this);
7655 onEnable : function(){
7656 this.getActionEl().removeClass(this.disabledClass);
7657 this.el.dom.disabled = false;
7661 * Convenience function for setting disabled/enabled by boolean.
7662 * @param {Boolean} disabled
7664 setDisabled : function(disabled){
7665 this[disabled ? "disable" : "enable"]();
7669 * Show this component.
7670 * @return {Roo.Component} this
7673 if(this.fireEvent("beforeshow", this) !== false){
7674 this.hidden = false;
7678 this.fireEvent("show", this);
7684 onShow : function(){
7685 var ae = this.getActionEl();
7686 if(this.hideMode == 'visibility'){
7687 ae.dom.style.visibility = "visible";
7688 }else if(this.hideMode == 'offsets'){
7689 ae.removeClass('x-hidden');
7691 ae.dom.style.display = "";
7696 * Hide this component.
7697 * @return {Roo.Component} this
7700 if(this.fireEvent("beforehide", this) !== false){
7705 this.fireEvent("hide", this);
7711 onHide : function(){
7712 var ae = this.getActionEl();
7713 if(this.hideMode == 'visibility'){
7714 ae.dom.style.visibility = "hidden";
7715 }else if(this.hideMode == 'offsets'){
7716 ae.addClass('x-hidden');
7718 ae.dom.style.display = "none";
7723 * Convenience function to hide or show this component by boolean.
7724 * @param {Boolean} visible True to show, false to hide
7725 * @return {Roo.Component} this
7727 setVisible: function(visible){
7737 * Returns true if this component is visible.
7739 isVisible : function(){
7740 return this.getActionEl().isVisible();
7743 cloneConfig : function(overrides){
7744 overrides = overrides || {};
7745 var id = overrides.id || Roo.id();
7746 var cfg = Roo.applyIf(overrides, this.initialConfig);
7747 cfg.id = id; // prevent dup id
7748 return new this.constructor(cfg);
7752 * Ext JS Library 1.1.1
7753 * Copyright(c) 2006-2007, Ext JS, LLC.
7755 * Originally Released Under LGPL - original licence link has changed is not relivant.
7758 * <script type="text/javascript">
7763 * @extends Roo.Element
7764 * An extended {@link Roo.Element} object that supports a shadow and shim, constrain to viewport and
7765 * automatic maintaining of shadow/shim positions.
7766 * @cfg {Boolean} shim False to disable the iframe shim in browsers which need one (defaults to true)
7767 * @cfg {String/Boolean} shadow True to create a shadow element with default class "x-layer-shadow", or
7768 * you can pass a string with a CSS class name. False turns off the shadow.
7769 * @cfg {Object} dh DomHelper object config to create element with (defaults to {tag: "div", cls: "x-layer"}).
7770 * @cfg {Boolean} constrain False to disable constrain to viewport (defaults to true)
7771 * @cfg {String} cls CSS class to add to the element
7772 * @cfg {Number} zindex Starting z-index (defaults to 11000)
7773 * @cfg {Number} shadowOffset Number of pixels to offset the shadow (defaults to 3)
7775 * @param {Object} config An object with config options.
7776 * @param {String/HTMLElement} existingEl (optional) Uses an existing DOM element. If the element is not found it creates it.
7779 Roo.Layer = function(config, existingEl){
7780 config = config || {};
7781 var dh = Roo.DomHelper;
7782 var cp = config.parentEl, pel = cp ? Roo.getDom(cp) : document.body;
7784 this.dom = Roo.getDom(existingEl);
7787 var o = config.dh || {tag: "div", cls: "x-layer"};
7788 this.dom = dh.append(pel, o);
7791 this.addClass(config.cls);
7793 this.constrain = config.constrain !== false;
7794 this.visibilityMode = Roo.Element.VISIBILITY;
7796 this.id = this.dom.id = config.id;
7798 this.id = Roo.id(this.dom);
7800 this.zindex = config.zindex || this.getZIndex();
7801 this.position("absolute", this.zindex);
7803 this.shadowOffset = config.shadowOffset || 4;
7804 this.shadow = new Roo.Shadow({
7805 offset : this.shadowOffset,
7806 mode : config.shadow
7809 this.shadowOffset = 0;
7811 this.useShim = config.shim !== false && Roo.useShims;
7812 this.useDisplay = config.useDisplay;
7816 var supr = Roo.Element.prototype;
7818 // shims are shared among layer to keep from having 100 iframes
7821 Roo.extend(Roo.Layer, Roo.Element, {
7823 getZIndex : function(){
7824 return this.zindex || parseInt(this.getStyle("z-index"), 10) || 11000;
7827 getShim : function(){
7834 var shim = shims.shift();
7836 shim = this.createShim();
7837 shim.enableDisplayMode('block');
7838 shim.dom.style.display = 'none';
7839 shim.dom.style.visibility = 'visible';
7841 var pn = this.dom.parentNode;
7842 if(shim.dom.parentNode != pn){
7843 pn.insertBefore(shim.dom, this.dom);
7845 shim.setStyle('z-index', this.getZIndex()-2);
7850 hideShim : function(){
7852 this.shim.setDisplayed(false);
7853 shims.push(this.shim);
7858 disableShadow : function(){
7860 this.shadowDisabled = true;
7862 this.lastShadowOffset = this.shadowOffset;
7863 this.shadowOffset = 0;
7867 enableShadow : function(show){
7869 this.shadowDisabled = false;
7870 this.shadowOffset = this.lastShadowOffset;
7871 delete this.lastShadowOffset;
7879 // this code can execute repeatedly in milliseconds (i.e. during a drag) so
7880 // code size was sacrificed for effeciency (e.g. no getBox/setBox, no XY calls)
7881 sync : function(doShow){
7882 var sw = this.shadow;
7883 if(!this.updating && this.isVisible() && (sw || this.useShim)){
7884 var sh = this.getShim();
7886 var w = this.getWidth(),
7887 h = this.getHeight();
7889 var l = this.getLeft(true),
7890 t = this.getTop(true);
7892 if(sw && !this.shadowDisabled){
7893 if(doShow && !sw.isVisible()){
7896 sw.realign(l, t, w, h);
7902 // fit the shim behind the shadow, so it is shimmed too
7903 var a = sw.adjusts, s = sh.dom.style;
7904 s.left = (Math.min(l, l+a.l))+"px";
7905 s.top = (Math.min(t, t+a.t))+"px";
7906 s.width = (w+a.w)+"px";
7907 s.height = (h+a.h)+"px";
7914 sh.setLeftTop(l, t);
7921 destroy : function(){
7926 this.removeAllListeners();
7927 var pn = this.dom.parentNode;
7929 pn.removeChild(this.dom);
7931 Roo.Element.uncache(this.id);
7934 remove : function(){
7939 beginUpdate : function(){
7940 this.updating = true;
7944 endUpdate : function(){
7945 this.updating = false;
7950 hideUnders : function(negOffset){
7958 constrainXY : function(){
7960 var vw = Roo.lib.Dom.getViewWidth(),
7961 vh = Roo.lib.Dom.getViewHeight();
7962 var s = Roo.get(document).getScroll();
7964 var xy = this.getXY();
7965 var x = xy[0], y = xy[1];
7966 var w = this.dom.offsetWidth+this.shadowOffset, h = this.dom.offsetHeight+this.shadowOffset;
7967 // only move it if it needs it
7969 // first validate right/bottom
7970 if((x + w) > vw+s.left){
7971 x = vw - w - this.shadowOffset;
7974 if((y + h) > vh+s.top){
7975 y = vh - h - this.shadowOffset;
7978 // then make sure top/left isn't negative
7989 var ay = this.avoidY;
7990 if(y <= ay && (y+h) >= ay){
7996 supr.setXY.call(this, xy);
8002 isVisible : function(){
8003 return this.visible;
8007 showAction : function(){
8008 this.visible = true; // track visibility to prevent getStyle calls
8009 if(this.useDisplay === true){
8010 this.setDisplayed("");
8011 }else if(this.lastXY){
8012 supr.setXY.call(this, this.lastXY);
8013 }else if(this.lastLT){
8014 supr.setLeftTop.call(this, this.lastLT[0], this.lastLT[1]);
8019 hideAction : function(){
8020 this.visible = false;
8021 if(this.useDisplay === true){
8022 this.setDisplayed(false);
8024 this.setLeftTop(-10000,-10000);
8028 // overridden Element method
8029 setVisible : function(v, a, d, c, e){
8034 var cb = function(){
8039 }.createDelegate(this);
8040 supr.setVisible.call(this, true, true, d, cb, e);
8043 this.hideUnders(true);
8052 }.createDelegate(this);
8054 supr.setVisible.call(this, v, a, d, cb, e);
8063 storeXY : function(xy){
8068 storeLeftTop : function(left, top){
8070 this.lastLT = [left, top];
8074 beforeFx : function(){
8075 this.beforeAction();
8076 return Roo.Layer.superclass.beforeFx.apply(this, arguments);
8080 afterFx : function(){
8081 Roo.Layer.superclass.afterFx.apply(this, arguments);
8082 this.sync(this.isVisible());
8086 beforeAction : function(){
8087 if(!this.updating && this.shadow){
8092 // overridden Element method
8093 setLeft : function(left){
8094 this.storeLeftTop(left, this.getTop(true));
8095 supr.setLeft.apply(this, arguments);
8099 setTop : function(top){
8100 this.storeLeftTop(this.getLeft(true), top);
8101 supr.setTop.apply(this, arguments);
8105 setLeftTop : function(left, top){
8106 this.storeLeftTop(left, top);
8107 supr.setLeftTop.apply(this, arguments);
8111 setXY : function(xy, a, d, c, e){
8113 this.beforeAction();
8115 var cb = this.createCB(c);
8116 supr.setXY.call(this, xy, a, d, cb, e);
8123 createCB : function(c){
8134 // overridden Element method
8135 setX : function(x, a, d, c, e){
8136 this.setXY([x, this.getY()], a, d, c, e);
8139 // overridden Element method
8140 setY : function(y, a, d, c, e){
8141 this.setXY([this.getX(), y], a, d, c, e);
8144 // overridden Element method
8145 setSize : function(w, h, a, d, c, e){
8146 this.beforeAction();
8147 var cb = this.createCB(c);
8148 supr.setSize.call(this, w, h, a, d, cb, e);
8154 // overridden Element method
8155 setWidth : function(w, a, d, c, e){
8156 this.beforeAction();
8157 var cb = this.createCB(c);
8158 supr.setWidth.call(this, w, a, d, cb, e);
8164 // overridden Element method
8165 setHeight : function(h, a, d, c, e){
8166 this.beforeAction();
8167 var cb = this.createCB(c);
8168 supr.setHeight.call(this, h, a, d, cb, e);
8174 // overridden Element method
8175 setBounds : function(x, y, w, h, a, d, c, e){
8176 this.beforeAction();
8177 var cb = this.createCB(c);
8179 this.storeXY([x, y]);
8180 supr.setXY.call(this, [x, y]);
8181 supr.setSize.call(this, w, h, a, d, cb, e);
8184 supr.setBounds.call(this, x, y, w, h, a, d, cb, e);
8190 * Sets the z-index of this layer and adjusts any shadow and shim z-indexes. The layer z-index is automatically
8191 * incremented by two more than the value passed in so that it always shows above any shadow or shim (the shadow
8192 * element, if any, will be assigned z-index + 1, and the shim element, if any, will be assigned the unmodified z-index).
8193 * @param {Number} zindex The new z-index to set
8194 * @return {this} The Layer
8196 setZIndex : function(zindex){
8197 this.zindex = zindex;
8198 this.setStyle("z-index", zindex + 2);
8200 this.shadow.setZIndex(zindex + 1);
8203 this.shim.setStyle("z-index", zindex);
8209 * Ext JS Library 1.1.1
8210 * Copyright(c) 2006-2007, Ext JS, LLC.
8212 * Originally Released Under LGPL - original licence link has changed is not relivant.
8215 * <script type="text/javascript">
8221 * Simple class that can provide a shadow effect for any element. Note that the element MUST be absolutely positioned,
8222 * and the shadow does not provide any shimming. This should be used only in simple cases -- for more advanced
8223 * functionality that can also provide the same shadow effect, see the {@link Roo.Layer} class.
8225 * Create a new Shadow
8226 * @param {Object} config The config object
8228 Roo.Shadow = function(config){
8229 Roo.apply(this, config);
8230 if(typeof this.mode != "string"){
8231 this.mode = this.defaultMode;
8233 var o = this.offset, a = {h: 0};
8234 var rad = Math.floor(this.offset/2);
8235 switch(this.mode.toLowerCase()){ // all this hideous nonsense calculates the various offsets for shadows
8241 a.l -= this.offset + rad;
8242 a.t -= this.offset + rad;
8253 a.l -= (this.offset - rad);
8254 a.t -= this.offset + rad;
8256 a.w -= (this.offset - rad)*2;
8267 a.l -= (this.offset - rad);
8268 a.t -= (this.offset - rad);
8270 a.w -= (this.offset + rad + 1);
8271 a.h -= (this.offset + rad);
8280 Roo.Shadow.prototype = {
8282 * @cfg {String} mode
8283 * The shadow display mode. Supports the following options:<br />
8284 * sides: Shadow displays on both sides and bottom only<br />
8285 * frame: Shadow displays equally on all four sides<br />
8286 * drop: Traditional bottom-right drop shadow (default)
8289 * @cfg {String} offset
8290 * The number of pixels to offset the shadow from the element (defaults to 4)
8295 defaultMode: "drop",
8298 * Displays the shadow under the target element
8299 * @param {String/HTMLElement/Element} targetEl The id or element under which the shadow should display
8301 show : function(target){
8302 target = Roo.get(target);
8304 this.el = Roo.Shadow.Pool.pull();
8305 if(this.el.dom.nextSibling != target.dom){
8306 this.el.insertBefore(target);
8309 this.el.setStyle("z-index", this.zIndex || parseInt(target.getStyle("z-index"), 10)-1);
8311 this.el.dom.style.filter="progid:DXImageTransform.Microsoft.alpha(opacity=50) progid:DXImageTransform.Microsoft.Blur(pixelradius="+(this.offset)+")";
8314 target.getLeft(true),
8315 target.getTop(true),
8319 this.el.dom.style.display = "block";
8323 * Returns true if the shadow is visible, else false
8325 isVisible : function(){
8326 return this.el ? true : false;
8330 * Direct alignment when values are already available. Show must be called at least once before
8331 * calling this method to ensure it is initialized.
8332 * @param {Number} left The target element left position
8333 * @param {Number} top The target element top position
8334 * @param {Number} width The target element width
8335 * @param {Number} height The target element height
8337 realign : function(l, t, w, h){
8341 var a = this.adjusts, d = this.el.dom, s = d.style;
8343 s.left = (l+a.l)+"px";
8344 s.top = (t+a.t)+"px";
8345 var sw = (w+a.w), sh = (h+a.h), sws = sw +"px", shs = sh + "px";
8347 if(s.width != sws || s.height != shs){
8351 var cn = d.childNodes;
8352 var sww = Math.max(0, (sw-12))+"px";
8353 cn[0].childNodes[1].style.width = sww;
8354 cn[1].childNodes[1].style.width = sww;
8355 cn[2].childNodes[1].style.width = sww;
8356 cn[1].style.height = Math.max(0, (sh-12))+"px";
8366 this.el.dom.style.display = "none";
8367 Roo.Shadow.Pool.push(this.el);
8373 * Adjust the z-index of this shadow
8374 * @param {Number} zindex The new z-index
8376 setZIndex : function(z){
8379 this.el.setStyle("z-index", z);
8384 // Private utility class that manages the internal Shadow cache
8385 Roo.Shadow.Pool = function(){
8387 var markup = Roo.isIE ?
8388 '<div class="x-ie-shadow"></div>' :
8389 '<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>';
8394 sh = Roo.get(Roo.DomHelper.insertHtml("beforeBegin", document.body.firstChild, markup));
8395 sh.autoBoxAdjust = false;
8400 push : function(sh){
8406 * Ext JS Library 1.1.1
8407 * Copyright(c) 2006-2007, Ext JS, LLC.
8409 * Originally Released Under LGPL - original licence link has changed is not relivant.
8412 * <script type="text/javascript">
8416 * @class Roo.BoxComponent
8417 * @extends Roo.Component
8418 * Base class for any visual {@link Roo.Component} that uses a box container. BoxComponent provides automatic box
8419 * model adjustments for sizing and positioning and will work correctly withnin the Component rendering model. All
8420 * container classes should subclass BoxComponent so that they will work consistently when nested within other Ext
8421 * layout containers.
8423 * @param {Roo.Element/String/Object} config The configuration options.
8425 Roo.BoxComponent = function(config){
8426 Roo.Component.call(this, config);
8430 * Fires after the component is resized.
8431 * @param {Roo.Component} this
8432 * @param {Number} adjWidth The box-adjusted width that was set
8433 * @param {Number} adjHeight The box-adjusted height that was set
8434 * @param {Number} rawWidth The width that was originally specified
8435 * @param {Number} rawHeight The height that was originally specified
8440 * Fires after the component is moved.
8441 * @param {Roo.Component} this
8442 * @param {Number} x The new x position
8443 * @param {Number} y The new y position
8449 Roo.extend(Roo.BoxComponent, Roo.Component, {
8450 // private, set in afterRender to signify that the component has been rendered
8452 // private, used to defer height settings to subclasses
8454 /** @cfg {Number} width
8455 * width (optional) size of component
8457 /** @cfg {Number} height
8458 * height (optional) size of component
8462 * Sets the width and height of the component. This method fires the resize event. This method can accept
8463 * either width and height as separate numeric arguments, or you can pass a size object like {width:10, height:20}.
8464 * @param {Number/Object} width The new width to set, or a size object in the format {width, height}
8465 * @param {Number} height The new height to set (not required if a size object is passed as the first arg)
8466 * @return {Roo.BoxComponent} this
8468 setSize : function(w, h){
8469 // support for standard size objects
8470 if(typeof w == 'object'){
8481 // prevent recalcs when not needed
8482 if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
8485 this.lastSize = {width: w, height: h};
8487 var adj = this.adjustSize(w, h);
8488 var aw = adj.width, ah = adj.height;
8489 if(aw !== undefined || ah !== undefined){ // this code is nasty but performs better with floaters
8490 var rz = this.getResizeEl();
8491 if(!this.deferHeight && aw !== undefined && ah !== undefined){
8493 }else if(!this.deferHeight && ah !== undefined){
8495 }else if(aw !== undefined){
8498 this.onResize(aw, ah, w, h);
8499 this.fireEvent('resize', this, aw, ah, w, h);
8505 * Gets the current size of the component's underlying element.
8506 * @return {Object} An object containing the element's size {width: (element width), height: (element height)}
8508 getSize : function(){
8509 return this.el.getSize();
8513 * Gets the current XY position of the component's underlying element.
8514 * @param {Boolean} local (optional) If true the element's left and top are returned instead of page XY (defaults to false)
8515 * @return {Array} The XY position of the element (e.g., [100, 200])
8517 getPosition : function(local){
8519 return [this.el.getLeft(true), this.el.getTop(true)];
8521 return this.xy || this.el.getXY();
8525 * Gets the current box measurements of the component's underlying element.
8526 * @param {Boolean} local (optional) If true the element's left and top are returned instead of page XY (defaults to false)
8527 * @returns {Object} box An object in the format {x, y, width, height}
8529 getBox : function(local){
8530 var s = this.el.getSize();
8532 s.x = this.el.getLeft(true);
8533 s.y = this.el.getTop(true);
8535 var xy = this.xy || this.el.getXY();
8543 * Sets the current box measurements of the component's underlying element.
8544 * @param {Object} box An object in the format {x, y, width, height}
8545 * @returns {Roo.BoxComponent} this
8547 updateBox : function(box){
8548 this.setSize(box.width, box.height);
8549 this.setPagePosition(box.x, box.y);
8554 getResizeEl : function(){
8555 return this.resizeEl || this.el;
8559 getPositionEl : function(){
8560 return this.positionEl || this.el;
8564 * Sets the left and top of the component. To set the page XY position instead, use {@link #setPagePosition}.
8565 * This method fires the move event.
8566 * @param {Number} left The new left
8567 * @param {Number} top The new top
8568 * @returns {Roo.BoxComponent} this
8570 setPosition : function(x, y){
8576 var adj = this.adjustPosition(x, y);
8577 var ax = adj.x, ay = adj.y;
8579 var el = this.getPositionEl();
8580 if(ax !== undefined || ay !== undefined){
8581 if(ax !== undefined && ay !== undefined){
8582 el.setLeftTop(ax, ay);
8583 }else if(ax !== undefined){
8585 }else if(ay !== undefined){
8588 this.onPosition(ax, ay);
8589 this.fireEvent('move', this, ax, ay);
8595 * Sets the page XY position of the component. To set the left and top instead, use {@link #setPosition}.
8596 * This method fires the move event.
8597 * @param {Number} x The new x position
8598 * @param {Number} y The new y position
8599 * @returns {Roo.BoxComponent} this
8601 setPagePosition : function(x, y){
8607 if(x === undefined || y === undefined){ // cannot translate undefined points
8610 var p = this.el.translatePoints(x, y);
8611 this.setPosition(p.left, p.top);
8616 onRender : function(ct, position){
8617 Roo.BoxComponent.superclass.onRender.call(this, ct, position);
8619 this.resizeEl = Roo.get(this.resizeEl);
8621 if(this.positionEl){
8622 this.positionEl = Roo.get(this.positionEl);
8627 afterRender : function(){
8628 Roo.BoxComponent.superclass.afterRender.call(this);
8629 this.boxReady = true;
8630 this.setSize(this.width, this.height);
8631 if(this.x || this.y){
8632 this.setPosition(this.x, this.y);
8634 if(this.pageX || this.pageY){
8635 this.setPagePosition(this.pageX, this.pageY);
8640 * Force the component's size to recalculate based on the underlying element's current height and width.
8641 * @returns {Roo.BoxComponent} this
8643 syncSize : function(){
8644 delete this.lastSize;
8645 this.setSize(this.el.getWidth(), this.el.getHeight());
8650 * Called after the component is resized, this method is empty by default but can be implemented by any
8651 * subclass that needs to perform custom logic after a resize occurs.
8652 * @param {Number} adjWidth The box-adjusted width that was set
8653 * @param {Number} adjHeight The box-adjusted height that was set
8654 * @param {Number} rawWidth The width that was originally specified
8655 * @param {Number} rawHeight The height that was originally specified
8657 onResize : function(adjWidth, adjHeight, rawWidth, rawHeight){
8662 * Called after the component is moved, this method is empty by default but can be implemented by any
8663 * subclass that needs to perform custom logic after a move occurs.
8664 * @param {Number} x The new x position
8665 * @param {Number} y The new y position
8667 onPosition : function(x, y){
8672 adjustSize : function(w, h){
8676 if(this.autoHeight){
8679 return {width : w, height: h};
8683 adjustPosition : function(x, y){
8684 return {x : x, y: y};
8688 * Ext JS Library 1.1.1
8689 * Copyright(c) 2006-2007, Ext JS, LLC.
8691 * Originally Released Under LGPL - original licence link has changed is not relivant.
8694 * <script type="text/javascript">
8699 * @class Roo.SplitBar
8700 * @extends Roo.util.Observable
8701 * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
8705 var split = new Roo.SplitBar("elementToDrag", "elementToSize",
8706 Roo.SplitBar.HORIZONTAL, Roo.SplitBar.LEFT);
8707 split.setAdapter(new Roo.SplitBar.AbsoluteLayoutAdapter("container"));
8708 split.minSize = 100;
8709 split.maxSize = 600;
8710 split.animate = true;
8711 split.on('moved', splitterMoved);
8714 * Create a new SplitBar
8715 * @param {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar.
8716 * @param {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged
8717 * @param {Number} orientation (optional) Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
8718 * @param {Number} placement (optional) Either Roo.SplitBar.LEFT or Roo.SplitBar.RIGHT for horizontal or
8719 Roo.SplitBar.TOP or Roo.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
8720 position of the SplitBar).
8722 Roo.SplitBar = function(dragElement, resizingElement, orientation, placement, existingProxy){
8725 this.el = Roo.get(dragElement, true);
8726 this.el.dom.unselectable = "on";
8728 this.resizingEl = Roo.get(resizingElement, true);
8732 * The orientation of the split. Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
8733 * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
8736 this.orientation = orientation || Roo.SplitBar.HORIZONTAL;
8739 * The minimum size of the resizing element. (Defaults to 0)
8745 * The maximum size of the resizing element. (Defaults to 2000)
8748 this.maxSize = 2000;
8751 * Whether to animate the transition to the new size
8754 this.animate = false;
8757 * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
8760 this.useShim = false;
8767 this.proxy = Roo.SplitBar.createProxy(this.orientation);
8769 this.proxy = Roo.get(existingProxy).dom;
8772 this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
8775 this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
8778 this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
8781 this.dragSpecs = {};
8784 * @private The adapter to use to positon and resize elements
8786 this.adapter = new Roo.SplitBar.BasicLayoutAdapter();
8787 this.adapter.init(this);
8789 if(this.orientation == Roo.SplitBar.HORIZONTAL){
8791 this.placement = placement || (this.el.getX() > this.resizingEl.getX() ? Roo.SplitBar.LEFT : Roo.SplitBar.RIGHT);
8792 this.el.addClass("x-splitbar-h");
8795 this.placement = placement || (this.el.getY() > this.resizingEl.getY() ? Roo.SplitBar.TOP : Roo.SplitBar.BOTTOM);
8796 this.el.addClass("x-splitbar-v");
8802 * Fires when the splitter is moved (alias for {@link #event-moved})
8803 * @param {Roo.SplitBar} this
8804 * @param {Number} newSize the new width or height
8809 * Fires when the splitter is moved
8810 * @param {Roo.SplitBar} this
8811 * @param {Number} newSize the new width or height
8815 * @event beforeresize
8816 * Fires before the splitter is dragged
8817 * @param {Roo.SplitBar} this
8819 "beforeresize" : true,
8821 "beforeapply" : true
8824 Roo.util.Observable.call(this);
8827 Roo.extend(Roo.SplitBar, Roo.util.Observable, {
8828 onStartProxyDrag : function(x, y){
8829 this.fireEvent("beforeresize", this);
8831 var o = Roo.DomHelper.insertFirst(document.body, {cls: "x-drag-overlay", html: " "}, true);
8833 o.enableDisplayMode("block");
8834 // all splitbars share the same overlay
8835 Roo.SplitBar.prototype.overlay = o;
8837 this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
8838 this.overlay.show();
8839 Roo.get(this.proxy).setDisplayed("block");
8840 var size = this.adapter.getElementSize(this);
8841 this.activeMinSize = this.getMinimumSize();;
8842 this.activeMaxSize = this.getMaximumSize();;
8843 var c1 = size - this.activeMinSize;
8844 var c2 = Math.max(this.activeMaxSize - size, 0);
8845 if(this.orientation == Roo.SplitBar.HORIZONTAL){
8846 this.dd.resetConstraints();
8847 this.dd.setXConstraint(
8848 this.placement == Roo.SplitBar.LEFT ? c1 : c2,
8849 this.placement == Roo.SplitBar.LEFT ? c2 : c1
8851 this.dd.setYConstraint(0, 0);
8853 this.dd.resetConstraints();
8854 this.dd.setXConstraint(0, 0);
8855 this.dd.setYConstraint(
8856 this.placement == Roo.SplitBar.TOP ? c1 : c2,
8857 this.placement == Roo.SplitBar.TOP ? c2 : c1
8860 this.dragSpecs.startSize = size;
8861 this.dragSpecs.startPoint = [x, y];
8862 Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
8866 * @private Called after the drag operation by the DDProxy
8868 onEndProxyDrag : function(e){
8869 Roo.get(this.proxy).setDisplayed(false);
8870 var endPoint = Roo.lib.Event.getXY(e);
8872 this.overlay.hide();
8875 if(this.orientation == Roo.SplitBar.HORIZONTAL){
8876 newSize = this.dragSpecs.startSize +
8877 (this.placement == Roo.SplitBar.LEFT ?
8878 endPoint[0] - this.dragSpecs.startPoint[0] :
8879 this.dragSpecs.startPoint[0] - endPoint[0]
8882 newSize = this.dragSpecs.startSize +
8883 (this.placement == Roo.SplitBar.TOP ?
8884 endPoint[1] - this.dragSpecs.startPoint[1] :
8885 this.dragSpecs.startPoint[1] - endPoint[1]
8888 newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
8889 if(newSize != this.dragSpecs.startSize){
8890 if(this.fireEvent('beforeapply', this, newSize) !== false){
8891 this.adapter.setElementSize(this, newSize);
8892 this.fireEvent("moved", this, newSize);
8893 this.fireEvent("resize", this, newSize);
8899 * Get the adapter this SplitBar uses
8900 * @return The adapter object
8902 getAdapter : function(){
8903 return this.adapter;
8907 * Set the adapter this SplitBar uses
8908 * @param {Object} adapter A SplitBar adapter object
8910 setAdapter : function(adapter){
8911 this.adapter = adapter;
8912 this.adapter.init(this);
8916 * Gets the minimum size for the resizing element
8917 * @return {Number} The minimum size
8919 getMinimumSize : function(){
8920 return this.minSize;
8924 * Sets the minimum size for the resizing element
8925 * @param {Number} minSize The minimum size
8927 setMinimumSize : function(minSize){
8928 this.minSize = minSize;
8932 * Gets the maximum size for the resizing element
8933 * @return {Number} The maximum size
8935 getMaximumSize : function(){
8936 return this.maxSize;
8940 * Sets the maximum size for the resizing element
8941 * @param {Number} maxSize The maximum size
8943 setMaximumSize : function(maxSize){
8944 this.maxSize = maxSize;
8948 * Sets the initialize size for the resizing element
8949 * @param {Number} size The initial size
8951 setCurrentSize : function(size){
8952 var oldAnimate = this.animate;
8953 this.animate = false;
8954 this.adapter.setElementSize(this, size);
8955 this.animate = oldAnimate;
8959 * Destroy this splitbar.
8960 * @param {Boolean} removeEl True to remove the element
8962 destroy : function(removeEl){
8967 this.proxy.parentNode.removeChild(this.proxy);
8975 * @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.
8977 Roo.SplitBar.createProxy = function(dir){
8978 var proxy = new Roo.Element(document.createElement("div"));
8979 proxy.unselectable();
8980 var cls = 'x-splitbar-proxy';
8981 proxy.addClass(cls + ' ' + (dir == Roo.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
8982 document.body.appendChild(proxy.dom);
8987 * @class Roo.SplitBar.BasicLayoutAdapter
8988 * Default Adapter. It assumes the splitter and resizing element are not positioned
8989 * elements and only gets/sets the width of the element. Generally used for table based layouts.
8991 Roo.SplitBar.BasicLayoutAdapter = function(){
8994 Roo.SplitBar.BasicLayoutAdapter.prototype = {
8995 // do nothing for now
9000 * Called before drag operations to get the current size of the resizing element.
9001 * @param {Roo.SplitBar} s The SplitBar using this adapter
9003 getElementSize : function(s){
9004 if(s.orientation == Roo.SplitBar.HORIZONTAL){
9005 return s.resizingEl.getWidth();
9007 return s.resizingEl.getHeight();
9012 * Called after drag operations to set the size of the resizing element.
9013 * @param {Roo.SplitBar} s The SplitBar using this adapter
9014 * @param {Number} newSize The new size to set
9015 * @param {Function} onComplete A function to be invoked when resizing is complete
9017 setElementSize : function(s, newSize, onComplete){
9018 if(s.orientation == Roo.SplitBar.HORIZONTAL){
9020 s.resizingEl.setWidth(newSize);
9022 onComplete(s, newSize);
9025 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
9030 s.resizingEl.setHeight(newSize);
9032 onComplete(s, newSize);
9035 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
9042 *@class Roo.SplitBar.AbsoluteLayoutAdapter
9043 * @extends Roo.SplitBar.BasicLayoutAdapter
9044 * Adapter that moves the splitter element to align with the resized sizing element.
9045 * Used with an absolute positioned SplitBar.
9046 * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
9047 * document.body, make sure you assign an id to the body element.
9049 Roo.SplitBar.AbsoluteLayoutAdapter = function(container){
9050 this.basic = new Roo.SplitBar.BasicLayoutAdapter();
9051 this.container = Roo.get(container);
9054 Roo.SplitBar.AbsoluteLayoutAdapter.prototype = {
9059 getElementSize : function(s){
9060 return this.basic.getElementSize(s);
9063 setElementSize : function(s, newSize, onComplete){
9064 this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
9067 moveSplitter : function(s){
9068 var yes = Roo.SplitBar;
9069 switch(s.placement){
9071 s.el.setX(s.resizingEl.getRight());
9074 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
9077 s.el.setY(s.resizingEl.getBottom());
9080 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
9087 * Orientation constant - Create a vertical SplitBar
9091 Roo.SplitBar.VERTICAL = 1;
9094 * Orientation constant - Create a horizontal SplitBar
9098 Roo.SplitBar.HORIZONTAL = 2;
9101 * Placement constant - The resizing element is to the left of the splitter element
9105 Roo.SplitBar.LEFT = 1;
9108 * Placement constant - The resizing element is to the right of the splitter element
9112 Roo.SplitBar.RIGHT = 2;
9115 * Placement constant - The resizing element is positioned above the splitter element
9119 Roo.SplitBar.TOP = 3;
9122 * Placement constant - The resizing element is positioned under splitter element
9126 Roo.SplitBar.BOTTOM = 4;
9129 * Ext JS Library 1.1.1
9130 * Copyright(c) 2006-2007, Ext JS, LLC.
9132 * Originally Released Under LGPL - original licence link has changed is not relivant.
9135 * <script type="text/javascript">
9140 * @extends Roo.util.Observable
9141 * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template.
9142 * This class also supports single and multi selection modes. <br>
9143 * Create a data model bound view:
9145 var store = new Roo.data.Store(...);
9147 var view = new Roo.View({
9149 tpl : '<div id="{0}">{2} - {1}</div>', // auto create template
9152 selectedClass: "ydataview-selected",
9156 // listen for node click?
9157 view.on("click", function(vw, index, node, e){
9158 alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
9162 dataModel.load("foobar.xml");
9164 For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
9166 * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
9167 * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
9169 * Note: old style constructor is still suported (container, template, config)
9173 * @param {Object} config The config object
9176 Roo.View = function(config, depreciated_tpl, depreciated_config){
9178 if (typeof(depreciated_tpl) == 'undefined') {
9179 // new way.. - universal constructor.
9180 Roo.apply(this, config);
9181 this.el = Roo.get(this.el);
9184 this.el = Roo.get(config);
9185 this.tpl = depreciated_tpl;
9186 Roo.apply(this, depreciated_config);
9188 this.wrapEl = this.el.wrap().wrap();
9189 ///this.el = this.wrapEla.appendChild(document.createElement("div"));
9192 if(typeof(this.tpl) == "string"){
9193 this.tpl = new Roo.Template(this.tpl);
9195 // support xtype ctors..
9196 this.tpl = new Roo.factory(this.tpl, Roo);
9208 * @event beforeclick
9209 * Fires before a click is processed. Returns false to cancel the default action.
9210 * @param {Roo.View} this
9211 * @param {Number} index The index of the target node
9212 * @param {HTMLElement} node The target node
9213 * @param {Roo.EventObject} e The raw event object
9215 "beforeclick" : true,
9218 * Fires when a template node is clicked.
9219 * @param {Roo.View} this
9220 * @param {Number} index The index of the target node
9221 * @param {HTMLElement} node The target node
9222 * @param {Roo.EventObject} e The raw event object
9227 * Fires when a template node is double clicked.
9228 * @param {Roo.View} this
9229 * @param {Number} index The index of the target node
9230 * @param {HTMLElement} node The target node
9231 * @param {Roo.EventObject} e The raw event object
9235 * @event contextmenu
9236 * Fires when a template node is right clicked.
9237 * @param {Roo.View} this
9238 * @param {Number} index The index of the target node
9239 * @param {HTMLElement} node The target node
9240 * @param {Roo.EventObject} e The raw event object
9242 "contextmenu" : true,
9244 * @event selectionchange
9245 * Fires when the selected nodes change.
9246 * @param {Roo.View} this
9247 * @param {Array} selections Array of the selected nodes
9249 "selectionchange" : true,
9252 * @event beforeselect
9253 * Fires before a selection is made. If any handlers return false, the selection is cancelled.
9254 * @param {Roo.View} this
9255 * @param {HTMLElement} node The node to be selected
9256 * @param {Array} selections Array of currently selected nodes
9258 "beforeselect" : true,
9260 * @event preparedata
9261 * Fires on every row to render, to allow you to change the data.
9262 * @param {Roo.View} this
9263 * @param {Object} data to be rendered (change this)
9265 "preparedata" : true
9273 "click": this.onClick,
9274 "dblclick": this.onDblClick,
9275 "contextmenu": this.onContextMenu,
9279 this.selections = [];
9281 this.cmp = new Roo.CompositeElementLite([]);
9283 this.store = Roo.factory(this.store, Roo.data);
9284 this.setStore(this.store, true);
9287 if ( this.footer && this.footer.xtype) {
9289 var fctr = this.wrapEl.appendChild(document.createElement("div"));
9291 this.footer.dataSource = this.store
9292 this.footer.container = fctr;
9293 this.footer = Roo.factory(this.footer, Roo);
9294 fctr.insertFirst(this.el);
9296 // this is a bit insane - as the paging toolbar seems to detach the el..
9297 // dom.parentNode.parentNode.parentNode
9298 // they get detached?
9302 Roo.View.superclass.constructor.call(this);
9307 Roo.extend(Roo.View, Roo.util.Observable, {
9310 * @cfg {Roo.data.Store} store Data store to load data from.
9315 * @cfg {String|Roo.Element} el The container element.
9320 * @cfg {String|Roo.Template} tpl The template used by this View
9324 * @cfg {String} dataName the named area of the template to use as the data area
9325 * Works with domtemplates roo-name="name"
9329 * @cfg {String} selectedClass The css class to add to selected nodes
9331 selectedClass : "x-view-selected",
9333 * @cfg {String} emptyText The empty text to show when nothing is loaded.
9338 * @cfg {String} text to display on mask (default Loading)
9342 * @cfg {Boolean} multiSelect Allow multiple selection
9344 multiSelect : false,
9346 * @cfg {Boolean} singleSelect Allow single selection
9348 singleSelect: false,
9351 * @cfg {Boolean} toggleSelect - selecting
9353 toggleSelect : false,
9356 * Returns the element this view is bound to.
9357 * @return {Roo.Element}
9366 * Refreshes the view. - called by datachanged on the store. - do not call directly.
9368 refresh : function(){
9371 // if we are using something like 'domtemplate', then
9372 // the what gets used is:
9373 // t.applySubtemplate(NAME, data, wrapping data..)
9374 // the outer template then get' applied with
9375 // the store 'extra data'
9376 // and the body get's added to the
9377 // roo-name="data" node?
9378 // <span class='roo-tpl-{name}'></span> ?????
9382 this.clearSelections();
9385 var records = this.store.getRange();
9386 if(records.length < 1) {
9388 // is this valid?? = should it render a template??
9390 this.el.update(this.emptyText);
9394 if (this.dataName) {
9395 this.el.update(t.apply(this.store.meta)); //????
9396 el = this.el.child('.roo-tpl-' + this.dataName);
9399 for(var i = 0, len = records.length; i < len; i++){
9400 var data = this.prepareData(records[i].data, i, records[i]);
9401 this.fireEvent("preparedata", this, data, i, records[i]);
9402 html[html.length] = Roo.util.Format.trim(
9404 t.applySubtemplate(this.dataName, data, this.store.meta) :
9411 el.update(html.join(""));
9412 this.nodes = el.dom.childNodes;
9413 this.updateIndexes(0);
9417 * Function to override to reformat the data that is sent to
9418 * the template for each node.
9419 * DEPRICATED - use the preparedata event handler.
9420 * @param {Array/Object} data The raw data (array of colData for a data model bound view or
9421 * a JSON object for an UpdateManager bound view).
9423 prepareData : function(data, index, record)
9425 this.fireEvent("preparedata", this, data, index, record);
9429 onUpdate : function(ds, record){
9430 this.clearSelections();
9431 var index = this.store.indexOf(record);
9432 var n = this.nodes[index];
9433 this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
9434 n.parentNode.removeChild(n);
9435 this.updateIndexes(index, index);
9441 onAdd : function(ds, records, index)
9443 this.clearSelections();
9444 if(this.nodes.length == 0){
9448 var n = this.nodes[index];
9449 for(var i = 0, len = records.length; i < len; i++){
9450 var d = this.prepareData(records[i].data, i, records[i]);
9452 this.tpl.insertBefore(n, d);
9455 this.tpl.append(this.el, d);
9458 this.updateIndexes(index);
9461 onRemove : function(ds, record, index){
9462 this.clearSelections();
9463 var el = this.dataName ?
9464 this.el.child('.roo-tpl-' + this.dataName) :
9466 el.dom.removeChild(this.nodes[index]);
9467 this.updateIndexes(index);
9471 * Refresh an individual node.
9472 * @param {Number} index
9474 refreshNode : function(index){
9475 this.onUpdate(this.store, this.store.getAt(index));
9478 updateIndexes : function(startIndex, endIndex){
9479 var ns = this.nodes;
9480 startIndex = startIndex || 0;
9481 endIndex = endIndex || ns.length - 1;
9482 for(var i = startIndex; i <= endIndex; i++){
9483 ns[i].nodeIndex = i;
9488 * Changes the data store this view uses and refresh the view.
9489 * @param {Store} store
9491 setStore : function(store, initial){
9492 if(!initial && this.store){
9493 this.store.un("datachanged", this.refresh);
9494 this.store.un("add", this.onAdd);
9495 this.store.un("remove", this.onRemove);
9496 this.store.un("update", this.onUpdate);
9497 this.store.un("clear", this.refresh);
9498 this.store.un("beforeload", this.onBeforeLoad);
9499 this.store.un("load", this.onLoad);
9500 this.store.un("loadexception", this.onLoad);
9504 store.on("datachanged", this.refresh, this);
9505 store.on("add", this.onAdd, this);
9506 store.on("remove", this.onRemove, this);
9507 store.on("update", this.onUpdate, this);
9508 store.on("clear", this.refresh, this);
9509 store.on("beforeload", this.onBeforeLoad, this);
9510 store.on("load", this.onLoad, this);
9511 store.on("loadexception", this.onLoad, this);
9519 * onbeforeLoad - masks the loading area.
9522 onBeforeLoad : function()
9525 this.el.mask(this.mask ? this.mask : "Loading" );
9527 onLoad : function ()
9534 * Returns the template node the passed child belongs to or null if it doesn't belong to one.
9535 * @param {HTMLElement} node
9536 * @return {HTMLElement} The template node
9538 findItemFromChild : function(node){
9539 var el = this.dataName ?
9540 this.el.child('.roo-tpl-' + this.dataName,true) :
9543 if(!node || node.parentNode == el){
9546 var p = node.parentNode;
9547 while(p && p != el){
9548 if(p.parentNode == el){
9557 onClick : function(e){
9558 var item = this.findItemFromChild(e.getTarget());
9560 var index = this.indexOf(item);
9561 if(this.onItemClick(item, index, e) !== false){
9562 this.fireEvent("click", this, index, item, e);
9565 this.clearSelections();
9570 onContextMenu : function(e){
9571 var item = this.findItemFromChild(e.getTarget());
9573 this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
9578 onDblClick : function(e){
9579 var item = this.findItemFromChild(e.getTarget());
9581 this.fireEvent("dblclick", this, this.indexOf(item), item, e);
9585 onItemClick : function(item, index, e)
9587 if(this.fireEvent("beforeclick", this, index, item, e) === false){
9590 if (this.toggleSelect) {
9591 var m = this.isSelected(item) ? 'unselect' : 'select';
9594 _t[m](item, true, false);
9597 if(this.multiSelect || this.singleSelect){
9598 if(this.multiSelect && e.shiftKey && this.lastSelection){
9599 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
9601 this.select(item, this.multiSelect && e.ctrlKey);
9602 this.lastSelection = item;
9610 * Get the number of selected nodes.
9613 getSelectionCount : function(){
9614 return this.selections.length;
9618 * Get the currently selected nodes.
9619 * @return {Array} An array of HTMLElements
9621 getSelectedNodes : function(){
9622 return this.selections;
9626 * Get the indexes of the selected nodes.
9629 getSelectedIndexes : function(){
9630 var indexes = [], s = this.selections;
9631 for(var i = 0, len = s.length; i < len; i++){
9632 indexes.push(s[i].nodeIndex);
9638 * Clear all selections
9639 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
9641 clearSelections : function(suppressEvent){
9642 if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
9643 this.cmp.elements = this.selections;
9644 this.cmp.removeClass(this.selectedClass);
9645 this.selections = [];
9647 this.fireEvent("selectionchange", this, this.selections);
9653 * Returns true if the passed node is selected
9654 * @param {HTMLElement/Number} node The node or node index
9657 isSelected : function(node){
9658 var s = this.selections;
9662 node = this.getNode(node);
9663 return s.indexOf(node) !== -1;
9668 * @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
9669 * @param {Boolean} keepExisting (optional) true to keep existing selections
9670 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
9672 select : function(nodeInfo, keepExisting, suppressEvent){
9673 if(nodeInfo instanceof Array){
9675 this.clearSelections(true);
9677 for(var i = 0, len = nodeInfo.length; i < len; i++){
9678 this.select(nodeInfo[i], true, true);
9682 var node = this.getNode(nodeInfo);
9683 if(!node || this.isSelected(node)){
9684 return; // already selected.
9687 this.clearSelections(true);
9689 if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
9690 Roo.fly(node).addClass(this.selectedClass);
9691 this.selections.push(node);
9693 this.fireEvent("selectionchange", this, this.selections);
9701 * @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
9702 * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
9703 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
9705 unselect : function(nodeInfo, keepExisting, suppressEvent)
9707 if(nodeInfo instanceof Array){
9708 Roo.each(this.selections, function(s) {
9709 this.unselect(s, nodeInfo);
9713 var node = this.getNode(nodeInfo);
9714 if(!node || !this.isSelected(node)){
9715 Roo.log("not selected");
9716 return; // not selected.
9720 Roo.each(this.selections, function(s) {
9722 Roo.fly(node).removeClass(this.selectedClass);
9729 this.selections= ns;
9730 this.fireEvent("selectionchange", this, this.selections);
9734 * Gets a template node.
9735 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
9736 * @return {HTMLElement} The node or null if it wasn't found
9738 getNode : function(nodeInfo){
9739 if(typeof nodeInfo == "string"){
9740 return document.getElementById(nodeInfo);
9741 }else if(typeof nodeInfo == "number"){
9742 return this.nodes[nodeInfo];
9748 * Gets a range template nodes.
9749 * @param {Number} startIndex
9750 * @param {Number} endIndex
9751 * @return {Array} An array of nodes
9753 getNodes : function(start, end){
9754 var ns = this.nodes;
9756 end = typeof end == "undefined" ? ns.length - 1 : end;
9759 for(var i = start; i <= end; i++){
9763 for(var i = start; i >= end; i--){
9771 * Finds the index of the passed node
9772 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
9773 * @return {Number} The index of the node or -1
9775 indexOf : function(node){
9776 node = this.getNode(node);
9777 if(typeof node.nodeIndex == "number"){
9778 return node.nodeIndex;
9780 var ns = this.nodes;
9781 for(var i = 0, len = ns.length; i < len; i++){
9791 * Ext JS Library 1.1.1
9792 * Copyright(c) 2006-2007, Ext JS, LLC.
9794 * Originally Released Under LGPL - original licence link has changed is not relivant.
9797 * <script type="text/javascript">
9801 * @class Roo.JsonView
9803 * Shortcut class to create a JSON + {@link Roo.UpdateManager} template view. Usage:
9805 var view = new Roo.JsonView({
9806 container: "my-element",
9807 tpl: '<div id="{id}">{foo} - {bar}</div>', // auto create template
9812 // listen for node click?
9813 view.on("click", function(vw, index, node, e){
9814 alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
9817 // direct load of JSON data
9818 view.load("foobar.php");
9820 // Example from my blog list
9821 var tpl = new Roo.Template(
9822 '<div class="entry">' +
9823 '<a class="entry-title" href="{link}">{title}</a>' +
9824 "<h4>{date} by {author} | {comments} Comments</h4>{description}" +
9825 "</div><hr />"
9828 var moreView = new Roo.JsonView({
9829 container : "entry-list",
9833 moreView.on("beforerender", this.sortEntries, this);
9835 url: "/blog/get-posts.php",
9836 params: "allposts=true",
9837 text: "Loading Blog Entries..."
9841 * Note: old code is supported with arguments : (container, template, config)
9845 * Create a new JsonView
9847 * @param {Object} config The config object
9850 Roo.JsonView = function(config, depreciated_tpl, depreciated_config){
9853 Roo.JsonView.superclass.constructor.call(this, config, depreciated_tpl, depreciated_config);
9855 var um = this.el.getUpdateManager();
9856 um.setRenderer(this);
9857 um.on("update", this.onLoad, this);
9858 um.on("failure", this.onLoadException, this);
9861 * @event beforerender
9862 * Fires before rendering of the downloaded JSON data.
9863 * @param {Roo.JsonView} this
9864 * @param {Object} data The JSON data loaded
9868 * Fires when data is loaded.
9869 * @param {Roo.JsonView} this
9870 * @param {Object} data The JSON data loaded
9871 * @param {Object} response The raw Connect response object
9874 * @event loadexception
9875 * Fires when loading fails.
9876 * @param {Roo.JsonView} this
9877 * @param {Object} response The raw Connect response object
9880 'beforerender' : true,
9882 'loadexception' : true
9885 Roo.extend(Roo.JsonView, Roo.View, {
9887 * @type {String} The root property in the loaded JSON object that contains the data
9892 * Refreshes the view.
9894 refresh : function(){
9895 this.clearSelections();
9898 var o = this.jsonData;
9899 if(o && o.length > 0){
9900 for(var i = 0, len = o.length; i < len; i++){
9901 var data = this.prepareData(o[i], i, o);
9902 html[html.length] = this.tpl.apply(data);
9905 html.push(this.emptyText);
9907 this.el.update(html.join(""));
9908 this.nodes = this.el.dom.childNodes;
9909 this.updateIndexes(0);
9913 * 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.
9914 * @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:
9917 url: "your-url.php",
9918 params: {param1: "foo", param2: "bar"}, // or a URL encoded string
9919 callback: yourFunction,
9920 scope: yourObject, //(optional scope)
9928 * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
9929 * 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.
9930 * @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}
9931 * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
9932 * @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.
9935 var um = this.el.getUpdateManager();
9936 um.update.apply(um, arguments);
9939 render : function(el, response){
9940 this.clearSelections();
9944 o = Roo.util.JSON.decode(response.responseText);
9947 o = o[this.jsonRoot];
9952 * The current JSON data or null
9955 this.beforeRender();
9960 * Get the number of records in the current JSON dataset
9963 getCount : function(){
9964 return this.jsonData ? this.jsonData.length : 0;
9968 * Returns the JSON object for the specified node(s)
9969 * @param {HTMLElement/Array} node The node or an array of nodes
9970 * @return {Object/Array} If you pass in an array, you get an array back, otherwise
9971 * you get the JSON object for the node
9973 getNodeData : function(node){
9974 if(node instanceof Array){
9976 for(var i = 0, len = node.length; i < len; i++){
9977 data.push(this.getNodeData(node[i]));
9981 return this.jsonData[this.indexOf(node)] || null;
9984 beforeRender : function(){
9985 this.snapshot = this.jsonData;
9987 this.sort.apply(this, this.sortInfo);
9989 this.fireEvent("beforerender", this, this.jsonData);
9992 onLoad : function(el, o){
9993 this.fireEvent("load", this, this.jsonData, o);
9996 onLoadException : function(el, o){
9997 this.fireEvent("loadexception", this, o);
10001 * Filter the data by a specific property.
10002 * @param {String} property A property on your JSON objects
10003 * @param {String/RegExp} value Either string that the property values
10004 * should start with, or a RegExp to test against the property
10006 filter : function(property, value){
10009 var ss = this.snapshot;
10010 if(typeof value == "string"){
10011 var vlen = value.length;
10013 this.clearFilter();
10016 value = value.toLowerCase();
10017 for(var i = 0, len = ss.length; i < len; i++){
10019 if(o[property].substr(0, vlen).toLowerCase() == value){
10023 } else if(value.exec){ // regex?
10024 for(var i = 0, len = ss.length; i < len; i++){
10026 if(value.test(o[property])){
10033 this.jsonData = data;
10039 * Filter by a function. The passed function will be called with each
10040 * object in the current dataset. If the function returns true the value is kept,
10041 * otherwise it is filtered.
10042 * @param {Function} fn
10043 * @param {Object} scope (optional) The scope of the function (defaults to this JsonView)
10045 filterBy : function(fn, scope){
10048 var ss = this.snapshot;
10049 for(var i = 0, len = ss.length; i < len; i++){
10051 if(fn.call(scope || this, o)){
10055 this.jsonData = data;
10061 * Clears the current filter.
10063 clearFilter : function(){
10064 if(this.snapshot && this.jsonData != this.snapshot){
10065 this.jsonData = this.snapshot;
10072 * Sorts the data for this view and refreshes it.
10073 * @param {String} property A property on your JSON objects to sort on
10074 * @param {String} direction (optional) "desc" or "asc" (defaults to "asc")
10075 * @param {Function} sortType (optional) A function to call to convert the data to a sortable value.
10077 sort : function(property, dir, sortType){
10078 this.sortInfo = Array.prototype.slice.call(arguments, 0);
10081 var dsc = dir && dir.toLowerCase() == "desc";
10082 var f = function(o1, o2){
10083 var v1 = sortType ? sortType(o1[p]) : o1[p];
10084 var v2 = sortType ? sortType(o2[p]) : o2[p];
10087 return dsc ? +1 : -1;
10088 } else if(v1 > v2){
10089 return dsc ? -1 : +1;
10094 this.jsonData.sort(f);
10096 if(this.jsonData != this.snapshot){
10097 this.snapshot.sort(f);
10103 * Ext JS Library 1.1.1
10104 * Copyright(c) 2006-2007, Ext JS, LLC.
10106 * Originally Released Under LGPL - original licence link has changed is not relivant.
10109 * <script type="text/javascript">
10114 * @class Roo.ColorPalette
10115 * @extends Roo.Component
10116 * Simple color palette class for choosing colors. The palette can be rendered to any container.<br />
10117 * Here's an example of typical usage:
10119 var cp = new Roo.ColorPalette({value:'993300'}); // initial selected color
10120 cp.render('my-div');
10122 cp.on('select', function(palette, selColor){
10123 // do something with selColor
10127 * Create a new ColorPalette
10128 * @param {Object} config The config object
10130 Roo.ColorPalette = function(config){
10131 Roo.ColorPalette.superclass.constructor.call(this, config);
10135 * Fires when a color is selected
10136 * @param {ColorPalette} this
10137 * @param {String} color The 6-digit color hex code (without the # symbol)
10143 this.on("select", this.handler, this.scope, true);
10146 Roo.extend(Roo.ColorPalette, Roo.Component, {
10148 * @cfg {String} itemCls
10149 * The CSS class to apply to the containing element (defaults to "x-color-palette")
10151 itemCls : "x-color-palette",
10153 * @cfg {String} value
10154 * The initial color to highlight (should be a valid 6-digit color hex code without the # symbol). Note that
10155 * the hex codes are case-sensitive.
10158 clickEvent:'click',
10160 ctype: "Roo.ColorPalette",
10163 * @cfg {Boolean} allowReselect If set to true then reselecting a color that is already selected fires the selection event
10165 allowReselect : false,
10168 * <p>An array of 6-digit color hex code strings (without the # symbol). This array can contain any number
10169 * of colors, and each hex code should be unique. The width of the palette is controlled via CSS by adjusting
10170 * the width property of the 'x-color-palette' class (or assigning a custom class), so you can balance the number
10171 * of colors with the width setting until the box is symmetrical.</p>
10172 * <p>You can override individual colors if needed:</p>
10174 var cp = new Roo.ColorPalette();
10175 cp.colors[0] = "FF0000"; // change the first box to red
10178 Or you can provide a custom array of your own for complete control:
10180 var cp = new Roo.ColorPalette();
10181 cp.colors = ["000000", "993300", "333300"];
10186 "000000", "993300", "333300", "003300", "003366", "000080", "333399", "333333",
10187 "800000", "FF6600", "808000", "008000", "008080", "0000FF", "666699", "808080",
10188 "FF0000", "FF9900", "99CC00", "339966", "33CCCC", "3366FF", "800080", "969696",
10189 "FF00FF", "FFCC00", "FFFF00", "00FF00", "00FFFF", "00CCFF", "993366", "C0C0C0",
10190 "FF99CC", "FFCC99", "FFFF99", "CCFFCC", "CCFFFF", "99CCFF", "CC99FF", "FFFFFF"
10194 onRender : function(container, position){
10195 var t = new Roo.MasterTemplate(
10196 '<tpl><a href="#" class="color-{0}" hidefocus="on"><em><span style="background:#{0}" unselectable="on"> </span></em></a></tpl>'
10198 var c = this.colors;
10199 for(var i = 0, len = c.length; i < len; i++){
10202 var el = document.createElement("div");
10203 el.className = this.itemCls;
10205 container.dom.insertBefore(el, position);
10206 this.el = Roo.get(el);
10207 this.el.on(this.clickEvent, this.handleClick, this, {delegate: "a"});
10208 if(this.clickEvent != 'click'){
10209 this.el.on('click', Roo.emptyFn, this, {delegate: "a", preventDefault:true});
10214 afterRender : function(){
10215 Roo.ColorPalette.superclass.afterRender.call(this);
10217 var s = this.value;
10224 handleClick : function(e, t){
10225 e.preventDefault();
10226 if(!this.disabled){
10227 var c = t.className.match(/(?:^|\s)color-(.{6})(?:\s|$)/)[1];
10228 this.select(c.toUpperCase());
10233 * Selects the specified color in the palette (fires the select event)
10234 * @param {String} color A valid 6-digit color hex code (# will be stripped if included)
10236 select : function(color){
10237 color = color.replace("#", "");
10238 if(color != this.value || this.allowReselect){
10241 el.child("a.color-"+this.value).removeClass("x-color-palette-sel");
10243 el.child("a.color-"+color).addClass("x-color-palette-sel");
10244 this.value = color;
10245 this.fireEvent("select", this, color);
10250 * Ext JS Library 1.1.1
10251 * Copyright(c) 2006-2007, Ext JS, LLC.
10253 * Originally Released Under LGPL - original licence link has changed is not relivant.
10256 * <script type="text/javascript">
10260 * @class Roo.DatePicker
10261 * @extends Roo.Component
10262 * Simple date picker class.
10264 * Create a new DatePicker
10265 * @param {Object} config The config object
10267 Roo.DatePicker = function(config){
10268 Roo.DatePicker.superclass.constructor.call(this, config);
10270 this.value = config && config.value ?
10271 config.value.clearTime() : new Date().clearTime();
10276 * Fires when a date is selected
10277 * @param {DatePicker} this
10278 * @param {Date} date The selected date
10282 * @event monthchange
10283 * Fires when the displayed month changes
10284 * @param {DatePicker} this
10285 * @param {Date} date The selected month
10287 'monthchange': true
10291 this.on("select", this.handler, this.scope || this);
10293 // build the disabledDatesRE
10294 if(!this.disabledDatesRE && this.disabledDates){
10295 var dd = this.disabledDates;
10297 for(var i = 0; i < dd.length; i++){
10299 if(i != dd.length-1) re += "|";
10301 this.disabledDatesRE = new RegExp(re + ")");
10305 Roo.extend(Roo.DatePicker, Roo.Component, {
10307 * @cfg {String} todayText
10308 * The text to display on the button that selects the current date (defaults to "Today")
10310 todayText : "Today",
10312 * @cfg {String} okText
10313 * The text to display on the ok button
10315 okText : " OK ", //   to give the user extra clicking room
10317 * @cfg {String} cancelText
10318 * The text to display on the cancel button
10320 cancelText : "Cancel",
10322 * @cfg {String} todayTip
10323 * The tooltip to display for the button that selects the current date (defaults to "{current date} (Spacebar)")
10325 todayTip : "{0} (Spacebar)",
10327 * @cfg {Date} minDate
10328 * Minimum allowable date (JavaScript date object, defaults to null)
10332 * @cfg {Date} maxDate
10333 * Maximum allowable date (JavaScript date object, defaults to null)
10337 * @cfg {String} minText
10338 * The error text to display if the minDate validation fails (defaults to "This date is before the minimum date")
10340 minText : "This date is before the minimum date",
10342 * @cfg {String} maxText
10343 * The error text to display if the maxDate validation fails (defaults to "This date is after the maximum date")
10345 maxText : "This date is after the maximum date",
10347 * @cfg {String} format
10348 * The default date format string which can be overriden for localization support. The format must be
10349 * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
10353 * @cfg {Array} disabledDays
10354 * An array of days to disable, 0-based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
10356 disabledDays : null,
10358 * @cfg {String} disabledDaysText
10359 * The tooltip to display when the date falls on a disabled day (defaults to "")
10361 disabledDaysText : "",
10363 * @cfg {RegExp} disabledDatesRE
10364 * JavaScript regular expression used to disable a pattern of dates (defaults to null)
10366 disabledDatesRE : null,
10368 * @cfg {String} disabledDatesText
10369 * The tooltip text to display when the date falls on a disabled date (defaults to "")
10371 disabledDatesText : "",
10373 * @cfg {Boolean} constrainToViewport
10374 * True to constrain the date picker to the viewport (defaults to true)
10376 constrainToViewport : true,
10378 * @cfg {Array} monthNames
10379 * An array of textual month names which can be overriden for localization support (defaults to Date.monthNames)
10381 monthNames : Date.monthNames,
10383 * @cfg {Array} dayNames
10384 * An array of textual day names which can be overriden for localization support (defaults to Date.dayNames)
10386 dayNames : Date.dayNames,
10388 * @cfg {String} nextText
10389 * The next month navigation button tooltip (defaults to 'Next Month (Control+Right)')
10391 nextText: 'Next Month (Control+Right)',
10393 * @cfg {String} prevText
10394 * The previous month navigation button tooltip (defaults to 'Previous Month (Control+Left)')
10396 prevText: 'Previous Month (Control+Left)',
10398 * @cfg {String} monthYearText
10399 * The header month selector tooltip (defaults to 'Choose a month (Control+Up/Down to move years)')
10401 monthYearText: 'Choose a month (Control+Up/Down to move years)',
10403 * @cfg {Number} startDay
10404 * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
10408 * @cfg {Bool} showClear
10409 * Show a clear button (usefull for date form elements that can be blank.)
10415 * Sets the value of the date field
10416 * @param {Date} value The date to set
10418 setValue : function(value){
10419 var old = this.value;
10421 if (typeof(value) == 'string') {
10423 value = Date.parseDate(value, this.format);
10426 value = new Date();
10429 this.value = value.clearTime(true);
10431 this.update(this.value);
10436 * Gets the current selected value of the date field
10437 * @return {Date} The selected date
10439 getValue : function(){
10444 focus : function(){
10446 this.update(this.activeDate);
10451 onRender : function(container, position){
10454 '<table cellspacing="0">',
10455 '<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>',
10456 '<tr><td colspan="3"><table class="x-date-inner" cellspacing="0"><thead><tr>'];
10457 var dn = this.dayNames;
10458 for(var i = 0; i < 7; i++){
10459 var d = this.startDay+i;
10463 m.push("<th><span>", dn[d].substr(0,1), "</span></th>");
10465 m[m.length] = "</tr></thead><tbody><tr>";
10466 for(var i = 0; i < 42; i++) {
10467 if(i % 7 == 0 && i != 0){
10468 m[m.length] = "</tr><tr>";
10470 m[m.length] = '<td><a href="#" hidefocus="on" class="x-date-date" tabIndex="1"><em><span></span></em></a></td>';
10472 m[m.length] = '</tr></tbody></table></td></tr><tr>'+
10473 '<td colspan="3" class="x-date-bottom" align="center"></td></tr></table><div class="x-date-mp"></div>';
10475 var el = document.createElement("div");
10476 el.className = "x-date-picker";
10477 el.innerHTML = m.join("");
10479 container.dom.insertBefore(el, position);
10481 this.el = Roo.get(el);
10482 this.eventEl = Roo.get(el.firstChild);
10484 new Roo.util.ClickRepeater(this.el.child("td.x-date-left a"), {
10485 handler: this.showPrevMonth,
10487 preventDefault:true,
10491 new Roo.util.ClickRepeater(this.el.child("td.x-date-right a"), {
10492 handler: this.showNextMonth,
10494 preventDefault:true,
10498 this.eventEl.on("mousewheel", this.handleMouseWheel, this);
10500 this.monthPicker = this.el.down('div.x-date-mp');
10501 this.monthPicker.enableDisplayMode('block');
10503 var kn = new Roo.KeyNav(this.eventEl, {
10504 "left" : function(e){
10506 this.showPrevMonth() :
10507 this.update(this.activeDate.add("d", -1));
10510 "right" : function(e){
10512 this.showNextMonth() :
10513 this.update(this.activeDate.add("d", 1));
10516 "up" : function(e){
10518 this.showNextYear() :
10519 this.update(this.activeDate.add("d", -7));
10522 "down" : function(e){
10524 this.showPrevYear() :
10525 this.update(this.activeDate.add("d", 7));
10528 "pageUp" : function(e){
10529 this.showNextMonth();
10532 "pageDown" : function(e){
10533 this.showPrevMonth();
10536 "enter" : function(e){
10537 e.stopPropagation();
10544 this.eventEl.on("click", this.handleDateClick, this, {delegate: "a.x-date-date"});
10546 this.eventEl.addKeyListener(Roo.EventObject.SPACE, this.selectToday, this);
10548 this.el.unselectable();
10550 this.cells = this.el.select("table.x-date-inner tbody td");
10551 this.textNodes = this.el.query("table.x-date-inner tbody span");
10553 this.mbtn = new Roo.Button(this.el.child("td.x-date-middle", true), {
10555 tooltip: this.monthYearText
10558 this.mbtn.on('click', this.showMonthPicker, this);
10559 this.mbtn.el.child(this.mbtn.menuClassTarget).addClass("x-btn-with-menu");
10562 var today = (new Date()).dateFormat(this.format);
10564 var baseTb = new Roo.Toolbar(this.el.child("td.x-date-bottom", true));
10565 if (this.showClear) {
10566 baseTb.add( new Roo.Toolbar.Fill());
10569 text: String.format(this.todayText, today),
10570 tooltip: String.format(this.todayTip, today),
10571 handler: this.selectToday,
10575 //var todayBtn = new Roo.Button(this.el.child("td.x-date-bottom", true), {
10578 if (this.showClear) {
10580 baseTb.add( new Roo.Toolbar.Fill());
10583 cls: 'x-btn-icon x-btn-clear',
10584 handler: function() {
10586 this.fireEvent("select", this, '');
10596 this.update(this.value);
10599 createMonthPicker : function(){
10600 if(!this.monthPicker.dom.firstChild){
10601 var buf = ['<table border="0" cellspacing="0">'];
10602 for(var i = 0; i < 6; i++){
10604 '<tr><td class="x-date-mp-month"><a href="#">', this.monthNames[i].substr(0, 3), '</a></td>',
10605 '<td class="x-date-mp-month x-date-mp-sep"><a href="#">', this.monthNames[i+6].substr(0, 3), '</a></td>',
10607 '<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>' :
10608 '<td class="x-date-mp-year"><a href="#"></a></td><td class="x-date-mp-year"><a href="#"></a></td></tr>'
10612 '<tr class="x-date-mp-btns"><td colspan="4"><button type="button" class="x-date-mp-ok">',
10614 '</button><button type="button" class="x-date-mp-cancel">',
10616 '</button></td></tr>',
10619 this.monthPicker.update(buf.join(''));
10620 this.monthPicker.on('click', this.onMonthClick, this);
10621 this.monthPicker.on('dblclick', this.onMonthDblClick, this);
10623 this.mpMonths = this.monthPicker.select('td.x-date-mp-month');
10624 this.mpYears = this.monthPicker.select('td.x-date-mp-year');
10626 this.mpMonths.each(function(m, a, i){
10629 m.dom.xmonth = 5 + Math.round(i * .5);
10631 m.dom.xmonth = Math.round((i-1) * .5);
10637 showMonthPicker : function(){
10638 this.createMonthPicker();
10639 var size = this.el.getSize();
10640 this.monthPicker.setSize(size);
10641 this.monthPicker.child('table').setSize(size);
10643 this.mpSelMonth = (this.activeDate || this.value).getMonth();
10644 this.updateMPMonth(this.mpSelMonth);
10645 this.mpSelYear = (this.activeDate || this.value).getFullYear();
10646 this.updateMPYear(this.mpSelYear);
10648 this.monthPicker.slideIn('t', {duration:.2});
10651 updateMPYear : function(y){
10653 var ys = this.mpYears.elements;
10654 for(var i = 1; i <= 10; i++){
10655 var td = ys[i-1], y2;
10657 y2 = y + Math.round(i * .5);
10658 td.firstChild.innerHTML = y2;
10661 y2 = y - (5-Math.round(i * .5));
10662 td.firstChild.innerHTML = y2;
10665 this.mpYears.item(i-1)[y2 == this.mpSelYear ? 'addClass' : 'removeClass']('x-date-mp-sel');
10669 updateMPMonth : function(sm){
10670 this.mpMonths.each(function(m, a, i){
10671 m[m.dom.xmonth == sm ? 'addClass' : 'removeClass']('x-date-mp-sel');
10675 selectMPMonth: function(m){
10679 onMonthClick : function(e, t){
10681 var el = new Roo.Element(t), pn;
10682 if(el.is('button.x-date-mp-cancel')){
10683 this.hideMonthPicker();
10685 else if(el.is('button.x-date-mp-ok')){
10686 this.update(new Date(this.mpSelYear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
10687 this.hideMonthPicker();
10689 else if(pn = el.up('td.x-date-mp-month', 2)){
10690 this.mpMonths.removeClass('x-date-mp-sel');
10691 pn.addClass('x-date-mp-sel');
10692 this.mpSelMonth = pn.dom.xmonth;
10694 else if(pn = el.up('td.x-date-mp-year', 2)){
10695 this.mpYears.removeClass('x-date-mp-sel');
10696 pn.addClass('x-date-mp-sel');
10697 this.mpSelYear = pn.dom.xyear;
10699 else if(el.is('a.x-date-mp-prev')){
10700 this.updateMPYear(this.mpyear-10);
10702 else if(el.is('a.x-date-mp-next')){
10703 this.updateMPYear(this.mpyear+10);
10707 onMonthDblClick : function(e, t){
10709 var el = new Roo.Element(t), pn;
10710 if(pn = el.up('td.x-date-mp-month', 2)){
10711 this.update(new Date(this.mpSelYear, pn.dom.xmonth, (this.activeDate || this.value).getDate()));
10712 this.hideMonthPicker();
10714 else if(pn = el.up('td.x-date-mp-year', 2)){
10715 this.update(new Date(pn.dom.xyear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
10716 this.hideMonthPicker();
10720 hideMonthPicker : function(disableAnim){
10721 if(this.monthPicker){
10722 if(disableAnim === true){
10723 this.monthPicker.hide();
10725 this.monthPicker.slideOut('t', {duration:.2});
10731 showPrevMonth : function(e){
10732 this.update(this.activeDate.add("mo", -1));
10736 showNextMonth : function(e){
10737 this.update(this.activeDate.add("mo", 1));
10741 showPrevYear : function(){
10742 this.update(this.activeDate.add("y", -1));
10746 showNextYear : function(){
10747 this.update(this.activeDate.add("y", 1));
10751 handleMouseWheel : function(e){
10752 var delta = e.getWheelDelta();
10754 this.showPrevMonth();
10756 } else if(delta < 0){
10757 this.showNextMonth();
10763 handleDateClick : function(e, t){
10765 if(t.dateValue && !Roo.fly(t.parentNode).hasClass("x-date-disabled")){
10766 this.setValue(new Date(t.dateValue));
10767 this.fireEvent("select", this, this.value);
10772 selectToday : function(){
10773 this.setValue(new Date().clearTime());
10774 this.fireEvent("select", this, this.value);
10778 update : function(date)
10780 var vd = this.activeDate;
10781 this.activeDate = date;
10783 var t = date.getTime();
10784 if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
10785 this.cells.removeClass("x-date-selected");
10786 this.cells.each(function(c){
10787 if(c.dom.firstChild.dateValue == t){
10788 c.addClass("x-date-selected");
10789 setTimeout(function(){
10790 try{c.dom.firstChild.focus();}catch(e){}
10799 var days = date.getDaysInMonth();
10800 var firstOfMonth = date.getFirstDateOfMonth();
10801 var startingPos = firstOfMonth.getDay()-this.startDay;
10803 if(startingPos <= this.startDay){
10807 var pm = date.add("mo", -1);
10808 var prevStart = pm.getDaysInMonth()-startingPos;
10810 var cells = this.cells.elements;
10811 var textEls = this.textNodes;
10812 days += startingPos;
10814 // convert everything to numbers so it's fast
10815 var day = 86400000;
10816 var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
10817 var today = new Date().clearTime().getTime();
10818 var sel = date.clearTime().getTime();
10819 var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
10820 var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
10821 var ddMatch = this.disabledDatesRE;
10822 var ddText = this.disabledDatesText;
10823 var ddays = this.disabledDays ? this.disabledDays.join("") : false;
10824 var ddaysText = this.disabledDaysText;
10825 var format = this.format;
10827 var setCellClass = function(cal, cell){
10829 var t = d.getTime();
10830 cell.firstChild.dateValue = t;
10832 cell.className += " x-date-today";
10833 cell.title = cal.todayText;
10836 cell.className += " x-date-selected";
10837 setTimeout(function(){
10838 try{cell.firstChild.focus();}catch(e){}
10843 cell.className = " x-date-disabled";
10844 cell.title = cal.minText;
10848 cell.className = " x-date-disabled";
10849 cell.title = cal.maxText;
10853 if(ddays.indexOf(d.getDay()) != -1){
10854 cell.title = ddaysText;
10855 cell.className = " x-date-disabled";
10858 if(ddMatch && format){
10859 var fvalue = d.dateFormat(format);
10860 if(ddMatch.test(fvalue)){
10861 cell.title = ddText.replace("%0", fvalue);
10862 cell.className = " x-date-disabled";
10868 for(; i < startingPos; i++) {
10869 textEls[i].innerHTML = (++prevStart);
10870 d.setDate(d.getDate()+1);
10871 cells[i].className = "x-date-prevday";
10872 setCellClass(this, cells[i]);
10874 for(; i < days; i++){
10875 intDay = i - startingPos + 1;
10876 textEls[i].innerHTML = (intDay);
10877 d.setDate(d.getDate()+1);
10878 cells[i].className = "x-date-active";
10879 setCellClass(this, cells[i]);
10882 for(; i < 42; i++) {
10883 textEls[i].innerHTML = (++extraDays);
10884 d.setDate(d.getDate()+1);
10885 cells[i].className = "x-date-nextday";
10886 setCellClass(this, cells[i]);
10889 this.mbtn.setText(this.monthNames[date.getMonth()] + " " + date.getFullYear());
10890 this.fireEvent('monthchange', this, date);
10892 if(!this.internalRender){
10893 var main = this.el.dom.firstChild;
10894 var w = main.offsetWidth;
10895 this.el.setWidth(w + this.el.getBorderWidth("lr"));
10896 Roo.fly(main).setWidth(w);
10897 this.internalRender = true;
10898 // opera does not respect the auto grow header center column
10899 // then, after it gets a width opera refuses to recalculate
10900 // without a second pass
10901 if(Roo.isOpera && !this.secondPass){
10902 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
10903 this.secondPass = true;
10904 this.update.defer(10, this, [date]);
10912 * Ext JS Library 1.1.1
10913 * Copyright(c) 2006-2007, Ext JS, LLC.
10915 * Originally Released Under LGPL - original licence link has changed is not relivant.
10918 * <script type="text/javascript">
10921 * @class Roo.TabPanel
10922 * @extends Roo.util.Observable
10923 * A lightweight tab container.
10927 // basic tabs 1, built from existing content
10928 var tabs = new Roo.TabPanel("tabs1");
10929 tabs.addTab("script", "View Script");
10930 tabs.addTab("markup", "View Markup");
10931 tabs.activate("script");
10933 // more advanced tabs, built from javascript
10934 var jtabs = new Roo.TabPanel("jtabs");
10935 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
10937 // set up the UpdateManager
10938 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
10939 var updater = tab2.getUpdateManager();
10940 updater.setDefaultUrl("ajax1.htm");
10941 tab2.on('activate', updater.refresh, updater, true);
10943 // Use setUrl for Ajax loading
10944 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
10945 tab3.setUrl("ajax2.htm", null, true);
10948 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
10951 jtabs.activate("jtabs-1");
10954 * Create a new TabPanel.
10955 * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
10956 * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
10958 Roo.TabPanel = function(container, config){
10960 * The container element for this TabPanel.
10961 * @type Roo.Element
10963 this.el = Roo.get(container, true);
10965 if(typeof config == "boolean"){
10966 this.tabPosition = config ? "bottom" : "top";
10968 Roo.apply(this, config);
10971 if(this.tabPosition == "bottom"){
10972 this.bodyEl = Roo.get(this.createBody(this.el.dom));
10973 this.el.addClass("x-tabs-bottom");
10975 this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
10976 this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
10977 this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
10979 Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
10981 if(this.tabPosition != "bottom"){
10982 /** The body element that contains {@link Roo.TabPanelItem} bodies. +
10983 * @type Roo.Element
10985 this.bodyEl = Roo.get(this.createBody(this.el.dom));
10986 this.el.addClass("x-tabs-top");
10990 this.bodyEl.setStyle("position", "relative");
10992 this.active = null;
10993 this.activateDelegate = this.activate.createDelegate(this);
10998 * Fires when the active tab changes
10999 * @param {Roo.TabPanel} this
11000 * @param {Roo.TabPanelItem} activePanel The new active tab
11004 * @event beforetabchange
11005 * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
11006 * @param {Roo.TabPanel} this
11007 * @param {Object} e Set cancel to true on this object to cancel the tab change
11008 * @param {Roo.TabPanelItem} tab The tab being changed to
11010 "beforetabchange" : true
11013 Roo.EventManager.onWindowResize(this.onResize, this);
11014 this.cpad = this.el.getPadding("lr");
11015 this.hiddenCount = 0;
11018 // toolbar on the tabbar support...
11019 if (this.toolbar) {
11020 var tcfg = this.toolbar;
11021 tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');
11022 this.toolbar = new Roo.Toolbar(tcfg);
11023 if (Roo.isSafari) {
11024 var tbl = tcfg.container.child('table', true);
11025 tbl.setAttribute('width', '100%');
11032 Roo.TabPanel.superclass.constructor.call(this);
11035 Roo.extend(Roo.TabPanel, Roo.util.Observable, {
11037 *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
11039 tabPosition : "top",
11041 *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
11043 currentTabWidth : 0,
11045 *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
11049 *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
11053 *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
11055 preferredTabWidth : 175,
11057 *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
11059 resizeTabs : false,
11061 *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
11063 monitorResize : true,
11065 *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar.
11070 * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
11071 * @param {String} id The id of the div to use <b>or create</b>
11072 * @param {String} text The text for the tab
11073 * @param {String} content (optional) Content to put in the TabPanelItem body
11074 * @param {Boolean} closable (optional) True to create a close icon on the tab
11075 * @return {Roo.TabPanelItem} The created TabPanelItem
11077 addTab : function(id, text, content, closable){
11078 var item = new Roo.TabPanelItem(this, id, text, closable);
11079 this.addTabItem(item);
11081 item.setContent(content);
11087 * Returns the {@link Roo.TabPanelItem} with the specified id/index
11088 * @param {String/Number} id The id or index of the TabPanelItem to fetch.
11089 * @return {Roo.TabPanelItem}
11091 getTab : function(id){
11092 return this.items[id];
11096 * Hides the {@link Roo.TabPanelItem} with the specified id/index
11097 * @param {String/Number} id The id or index of the TabPanelItem to hide.
11099 hideTab : function(id){
11100 var t = this.items[id];
11103 this.hiddenCount++;
11104 this.autoSizeTabs();
11109 * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
11110 * @param {String/Number} id The id or index of the TabPanelItem to unhide.
11112 unhideTab : function(id){
11113 var t = this.items[id];
11115 t.setHidden(false);
11116 this.hiddenCount--;
11117 this.autoSizeTabs();
11122 * Adds an existing {@link Roo.TabPanelItem}.
11123 * @param {Roo.TabPanelItem} item The TabPanelItem to add
11125 addTabItem : function(item){
11126 this.items[item.id] = item;
11127 this.items.push(item);
11128 if(this.resizeTabs){
11129 item.setWidth(this.currentTabWidth || this.preferredTabWidth);
11130 this.autoSizeTabs();
11137 * Removes a {@link Roo.TabPanelItem}.
11138 * @param {String/Number} id The id or index of the TabPanelItem to remove.
11140 removeTab : function(id){
11141 var items = this.items;
11142 var tab = items[id];
11143 if(!tab) { return; }
11144 var index = items.indexOf(tab);
11145 if(this.active == tab && items.length > 1){
11146 var newTab = this.getNextAvailable(index);
11151 this.stripEl.dom.removeChild(tab.pnode.dom);
11152 if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
11153 this.bodyEl.dom.removeChild(tab.bodyEl.dom);
11155 items.splice(index, 1);
11156 delete this.items[tab.id];
11157 tab.fireEvent("close", tab);
11158 tab.purgeListeners();
11159 this.autoSizeTabs();
11162 getNextAvailable : function(start){
11163 var items = this.items;
11165 // look for a next tab that will slide over to
11166 // replace the one being removed
11167 while(index < items.length){
11168 var item = items[++index];
11169 if(item && !item.isHidden()){
11173 // if one isn't found select the previous tab (on the left)
11176 var item = items[--index];
11177 if(item && !item.isHidden()){
11185 * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
11186 * @param {String/Number} id The id or index of the TabPanelItem to disable.
11188 disableTab : function(id){
11189 var tab = this.items[id];
11190 if(tab && this.active != tab){
11196 * Enables a {@link Roo.TabPanelItem} that is disabled.
11197 * @param {String/Number} id The id or index of the TabPanelItem to enable.
11199 enableTab : function(id){
11200 var tab = this.items[id];
11205 * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
11206 * @param {String/Number} id The id or index of the TabPanelItem to activate.
11207 * @return {Roo.TabPanelItem} The TabPanelItem.
11209 activate : function(id){
11210 var tab = this.items[id];
11214 if(tab == this.active || tab.disabled){
11218 this.fireEvent("beforetabchange", this, e, tab);
11219 if(e.cancel !== true && !tab.disabled){
11221 this.active.hide();
11223 this.active = this.items[id];
11224 this.active.show();
11225 this.fireEvent("tabchange", this, this.active);
11231 * Gets the active {@link Roo.TabPanelItem}.
11232 * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
11234 getActiveTab : function(){
11235 return this.active;
11239 * Updates the tab body element to fit the height of the container element
11240 * for overflow scrolling
11241 * @param {Number} targetHeight (optional) Override the starting height from the elements height
11243 syncHeight : function(targetHeight){
11244 var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
11245 var bm = this.bodyEl.getMargins();
11246 var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
11247 this.bodyEl.setHeight(newHeight);
11251 onResize : function(){
11252 if(this.monitorResize){
11253 this.autoSizeTabs();
11258 * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
11260 beginUpdate : function(){
11261 this.updating = true;
11265 * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
11267 endUpdate : function(){
11268 this.updating = false;
11269 this.autoSizeTabs();
11273 * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
11275 autoSizeTabs : function(){
11276 var count = this.items.length;
11277 var vcount = count - this.hiddenCount;
11278 if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) return;
11279 var w = Math.max(this.el.getWidth() - this.cpad, 10);
11280 var availWidth = Math.floor(w / vcount);
11281 var b = this.stripBody;
11282 if(b.getWidth() > w){
11283 var tabs = this.items;
11284 this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
11285 if(availWidth < this.minTabWidth){
11286 /*if(!this.sleft){ // incomplete scrolling code
11287 this.createScrollButtons();
11290 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
11293 if(this.currentTabWidth < this.preferredTabWidth){
11294 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
11300 * Returns the number of tabs in this TabPanel.
11303 getCount : function(){
11304 return this.items.length;
11308 * Resizes all the tabs to the passed width
11309 * @param {Number} The new width
11311 setTabWidth : function(width){
11312 this.currentTabWidth = width;
11313 for(var i = 0, len = this.items.length; i < len; i++) {
11314 if(!this.items[i].isHidden())this.items[i].setWidth(width);
11319 * Destroys this TabPanel
11320 * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
11322 destroy : function(removeEl){
11323 Roo.EventManager.removeResizeListener(this.onResize, this);
11324 for(var i = 0, len = this.items.length; i < len; i++){
11325 this.items[i].purgeListeners();
11327 if(removeEl === true){
11328 this.el.update("");
11335 * @class Roo.TabPanelItem
11336 * @extends Roo.util.Observable
11337 * Represents an individual item (tab plus body) in a TabPanel.
11338 * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
11339 * @param {String} id The id of this TabPanelItem
11340 * @param {String} text The text for the tab of this TabPanelItem
11341 * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
11343 Roo.TabPanelItem = function(tabPanel, id, text, closable){
11345 * The {@link Roo.TabPanel} this TabPanelItem belongs to
11346 * @type Roo.TabPanel
11348 this.tabPanel = tabPanel;
11350 * The id for this TabPanelItem
11355 this.disabled = false;
11359 this.loaded = false;
11360 this.closable = closable;
11363 * The body element for this TabPanelItem.
11364 * @type Roo.Element
11366 this.bodyEl = Roo.get(tabPanel.createItemBody(tabPanel.bodyEl.dom, id));
11367 this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
11368 this.bodyEl.setStyle("display", "block");
11369 this.bodyEl.setStyle("zoom", "1");
11372 var els = tabPanel.createStripElements(tabPanel.stripEl.dom, text, closable);
11374 this.el = Roo.get(els.el, true);
11375 this.inner = Roo.get(els.inner, true);
11376 this.textEl = Roo.get(this.el.dom.firstChild.firstChild.firstChild, true);
11377 this.pnode = Roo.get(els.el.parentNode, true);
11378 this.el.on("mousedown", this.onTabMouseDown, this);
11379 this.el.on("click", this.onTabClick, this);
11382 var c = Roo.get(els.close, true);
11383 c.dom.title = this.closeText;
11384 c.addClassOnOver("close-over");
11385 c.on("click", this.closeClick, this);
11391 * Fires when this tab becomes the active tab.
11392 * @param {Roo.TabPanel} tabPanel The parent TabPanel
11393 * @param {Roo.TabPanelItem} this
11397 * @event beforeclose
11398 * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
11399 * @param {Roo.TabPanelItem} this
11400 * @param {Object} e Set cancel to true on this object to cancel the close.
11402 "beforeclose": true,
11405 * Fires when this tab is closed.
11406 * @param {Roo.TabPanelItem} this
11410 * @event deactivate
11411 * Fires when this tab is no longer the active tab.
11412 * @param {Roo.TabPanel} tabPanel The parent TabPanel
11413 * @param {Roo.TabPanelItem} this
11415 "deactivate" : true
11417 this.hidden = false;
11419 Roo.TabPanelItem.superclass.constructor.call(this);
11422 Roo.extend(Roo.TabPanelItem, Roo.util.Observable, {
11423 purgeListeners : function(){
11424 Roo.util.Observable.prototype.purgeListeners.call(this);
11425 this.el.removeAllListeners();
11428 * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
11431 this.pnode.addClass("on");
11434 this.tabPanel.stripWrap.repaint();
11436 this.fireEvent("activate", this.tabPanel, this);
11440 * Returns true if this tab is the active tab.
11441 * @return {Boolean}
11443 isActive : function(){
11444 return this.tabPanel.getActiveTab() == this;
11448 * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
11451 this.pnode.removeClass("on");
11453 this.fireEvent("deactivate", this.tabPanel, this);
11456 hideAction : function(){
11457 this.bodyEl.hide();
11458 this.bodyEl.setStyle("position", "absolute");
11459 this.bodyEl.setLeft("-20000px");
11460 this.bodyEl.setTop("-20000px");
11463 showAction : function(){
11464 this.bodyEl.setStyle("position", "relative");
11465 this.bodyEl.setTop("");
11466 this.bodyEl.setLeft("");
11467 this.bodyEl.show();
11471 * Set the tooltip for the tab.
11472 * @param {String} tooltip The tab's tooltip
11474 setTooltip : function(text){
11475 if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
11476 this.textEl.dom.qtip = text;
11477 this.textEl.dom.removeAttribute('title');
11479 this.textEl.dom.title = text;
11483 onTabClick : function(e){
11484 e.preventDefault();
11485 this.tabPanel.activate(this.id);
11488 onTabMouseDown : function(e){
11489 e.preventDefault();
11490 this.tabPanel.activate(this.id);
11493 getWidth : function(){
11494 return this.inner.getWidth();
11497 setWidth : function(width){
11498 var iwidth = width - this.pnode.getPadding("lr");
11499 this.inner.setWidth(iwidth);
11500 this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
11501 this.pnode.setWidth(width);
11505 * Show or hide the tab
11506 * @param {Boolean} hidden True to hide or false to show.
11508 setHidden : function(hidden){
11509 this.hidden = hidden;
11510 this.pnode.setStyle("display", hidden ? "none" : "");
11514 * Returns true if this tab is "hidden"
11515 * @return {Boolean}
11517 isHidden : function(){
11518 return this.hidden;
11522 * Returns the text for this tab
11525 getText : function(){
11529 autoSize : function(){
11530 //this.el.beginMeasure();
11531 this.textEl.setWidth(1);
11532 this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr"));
11533 //this.el.endMeasure();
11537 * Sets the text for the tab (Note: this also sets the tooltip text)
11538 * @param {String} text The tab's text and tooltip
11540 setText : function(text){
11542 this.textEl.update(text);
11543 this.setTooltip(text);
11544 if(!this.tabPanel.resizeTabs){
11549 * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
11551 activate : function(){
11552 this.tabPanel.activate(this.id);
11556 * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
11558 disable : function(){
11559 if(this.tabPanel.active != this){
11560 this.disabled = true;
11561 this.pnode.addClass("disabled");
11566 * Enables this TabPanelItem if it was previously disabled.
11568 enable : function(){
11569 this.disabled = false;
11570 this.pnode.removeClass("disabled");
11574 * Sets the content for this TabPanelItem.
11575 * @param {String} content The content
11576 * @param {Boolean} loadScripts true to look for and load scripts
11578 setContent : function(content, loadScripts){
11579 this.bodyEl.update(content, loadScripts);
11583 * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
11584 * @return {Roo.UpdateManager} The UpdateManager
11586 getUpdateManager : function(){
11587 return this.bodyEl.getUpdateManager();
11591 * Set a URL to be used to load the content for this TabPanelItem.
11592 * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
11593 * @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)
11594 * @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)
11595 * @return {Roo.UpdateManager} The UpdateManager
11597 setUrl : function(url, params, loadOnce){
11598 if(this.refreshDelegate){
11599 this.un('activate', this.refreshDelegate);
11601 this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
11602 this.on("activate", this.refreshDelegate);
11603 return this.bodyEl.getUpdateManager();
11607 _handleRefresh : function(url, params, loadOnce){
11608 if(!loadOnce || !this.loaded){
11609 var updater = this.bodyEl.getUpdateManager();
11610 updater.update(url, params, this._setLoaded.createDelegate(this));
11615 * Forces a content refresh from the URL specified in the {@link #setUrl} method.
11616 * Will fail silently if the setUrl method has not been called.
11617 * This does not activate the panel, just updates its content.
11619 refresh : function(){
11620 if(this.refreshDelegate){
11621 this.loaded = false;
11622 this.refreshDelegate();
11627 _setLoaded : function(){
11628 this.loaded = true;
11632 closeClick : function(e){
11635 this.fireEvent("beforeclose", this, o);
11636 if(o.cancel !== true){
11637 this.tabPanel.removeTab(this.id);
11641 * The text displayed in the tooltip for the close icon.
11644 closeText : "Close this tab"
11648 Roo.TabPanel.prototype.createStrip = function(container){
11649 var strip = document.createElement("div");
11650 strip.className = "x-tabs-wrap";
11651 container.appendChild(strip);
11655 Roo.TabPanel.prototype.createStripList = function(strip){
11656 // div wrapper for retard IE
11657 // returns the "tr" element.
11658 strip.innerHTML = '<div class="x-tabs-strip-wrap">'+
11659 '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
11660 '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
11661 return strip.firstChild.firstChild.firstChild.firstChild;
11664 Roo.TabPanel.prototype.createBody = function(container){
11665 var body = document.createElement("div");
11666 Roo.id(body, "tab-body");
11667 Roo.fly(body).addClass("x-tabs-body");
11668 container.appendChild(body);
11672 Roo.TabPanel.prototype.createItemBody = function(bodyEl, id){
11673 var body = Roo.getDom(id);
11675 body = document.createElement("div");
11678 Roo.fly(body).addClass("x-tabs-item-body");
11679 bodyEl.insertBefore(body, bodyEl.firstChild);
11683 Roo.TabPanel.prototype.createStripElements = function(stripEl, text, closable){
11684 var td = document.createElement("td");
11685 stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
11686 //stripEl.appendChild(td);
11688 td.className = "x-tabs-closable";
11689 if(!this.closeTpl){
11690 this.closeTpl = new Roo.Template(
11691 '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
11692 '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
11693 '<div unselectable="on" class="close-icon"> </div></em></span></a>'
11696 var el = this.closeTpl.overwrite(td, {"text": text});
11697 var close = el.getElementsByTagName("div")[0];
11698 var inner = el.getElementsByTagName("em")[0];
11699 return {"el": el, "close": close, "inner": inner};
11702 this.tabTpl = new Roo.Template(
11703 '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
11704 '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
11707 var el = this.tabTpl.overwrite(td, {"text": text});
11708 var inner = el.getElementsByTagName("em")[0];
11709 return {"el": el, "inner": inner};
11713 * Ext JS Library 1.1.1
11714 * Copyright(c) 2006-2007, Ext JS, LLC.
11716 * Originally Released Under LGPL - original licence link has changed is not relivant.
11719 * <script type="text/javascript">
11723 * @class Roo.Button
11724 * @extends Roo.util.Observable
11725 * Simple Button class
11726 * @cfg {String} text The button text
11727 * @cfg {String} icon The path to an image to display in the button (the image will be set as the background-image
11728 * CSS property of the button by default, so if you want a mixed icon/text button, set cls:"x-btn-text-icon")
11729 * @cfg {Function} handler A function called when the button is clicked (can be used instead of click event)
11730 * @cfg {Object} scope The scope of the handler
11731 * @cfg {Number} minWidth The minimum width for this button (used to give a set of buttons a common width)
11732 * @cfg {String/Object} tooltip The tooltip for the button - can be a string or QuickTips config object
11733 * @cfg {Boolean} hidden True to start hidden (defaults to false)
11734 * @cfg {Boolean} disabled True to start disabled (defaults to false)
11735 * @cfg {Boolean} pressed True to start pressed (only if enableToggle = true)
11736 * @cfg {String} toggleGroup The group this toggle button is a member of (only 1 per group can be pressed, only
11737 applies if enableToggle = true)
11738 * @cfg {String/HTMLElement/Element} renderTo The element to append the button to
11739 * @cfg {Boolean/Object} repeat True to repeat fire the click event while the mouse is down. This can also be
11740 an {@link Roo.util.ClickRepeater} config object (defaults to false).
11742 * Create a new button
11743 * @param {Object} config The config object
11745 Roo.Button = function(renderTo, config)
11749 renderTo = config.renderTo || false;
11752 Roo.apply(this, config);
11756 * Fires when this button is clicked
11757 * @param {Button} this
11758 * @param {EventObject} e The click event
11763 * Fires when the "pressed" state of this button changes (only if enableToggle = true)
11764 * @param {Button} this
11765 * @param {Boolean} pressed
11770 * Fires when the mouse hovers over the button
11771 * @param {Button} this
11772 * @param {Event} e The event object
11774 'mouseover' : true,
11777 * Fires when the mouse exits the button
11778 * @param {Button} this
11779 * @param {Event} e The event object
11784 * Fires when the button is rendered
11785 * @param {Button} this
11790 this.menu = Roo.menu.MenuMgr.get(this.menu);
11792 // register listeners first!! - so render can be captured..
11793 Roo.util.Observable.call(this);
11795 this.render(renderTo);
11801 Roo.extend(Roo.Button, Roo.util.Observable, {
11807 * Read-only. True if this button is hidden
11812 * Read-only. True if this button is disabled
11817 * Read-only. True if this button is pressed (only if enableToggle = true)
11823 * @cfg {Number} tabIndex
11824 * The DOM tabIndex for this button (defaults to undefined)
11826 tabIndex : undefined,
11829 * @cfg {Boolean} enableToggle
11830 * True to enable pressed/not pressed toggling (defaults to false)
11832 enableToggle: false,
11834 * @cfg {Mixed} menu
11835 * Standard menu attribute consisting of a reference to a menu object, a menu id or a menu config blob (defaults to undefined).
11839 * @cfg {String} menuAlign
11840 * The position to align the menu to (see {@link Roo.Element#alignTo} for more details, defaults to 'tl-bl?').
11842 menuAlign : "tl-bl?",
11845 * @cfg {String} iconCls
11846 * A css class which sets a background image to be used as the icon for this button (defaults to undefined).
11848 iconCls : undefined,
11850 * @cfg {String} type
11851 * The button's type, corresponding to the DOM input element type attribute. Either "submit," "reset" or "button" (default).
11856 menuClassTarget: 'tr',
11859 * @cfg {String} clickEvent
11860 * The type of event to map to the button's event handler (defaults to 'click')
11862 clickEvent : 'click',
11865 * @cfg {Boolean} handleMouseEvents
11866 * False to disable visual cues on mouseover, mouseout and mousedown (defaults to true)
11868 handleMouseEvents : true,
11871 * @cfg {String} tooltipType
11872 * The type of tooltip to use. Either "qtip" (default) for QuickTips or "title" for title attribute.
11874 tooltipType : 'qtip',
11877 * @cfg {String} cls
11878 * A CSS class to apply to the button's main element.
11882 * @cfg {Roo.Template} template (Optional)
11883 * An {@link Roo.Template} with which to create the Button's main element. This Template must
11884 * contain numeric substitution parameter 0 if it is to display the tRoo property. Changing the template could
11885 * require code modifications if required elements (e.g. a button) aren't present.
11889 render : function(renderTo){
11891 if(this.hideParent){
11892 this.parentEl = Roo.get(renderTo);
11894 if(!this.dhconfig){
11895 if(!this.template){
11896 if(!Roo.Button.buttonTemplate){
11897 // hideous table template
11898 Roo.Button.buttonTemplate = new Roo.Template(
11899 '<table border="0" cellpadding="0" cellspacing="0" class="x-btn-wrap"><tbody><tr>',
11900 '<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>',
11901 "</tr></tbody></table>");
11903 this.template = Roo.Button.buttonTemplate;
11905 btn = this.template.append(renderTo, [this.text || ' ', this.type], true);
11906 var btnEl = btn.child("button:first");
11907 btnEl.on('focus', this.onFocus, this);
11908 btnEl.on('blur', this.onBlur, this);
11910 btn.addClass(this.cls);
11913 btnEl.setStyle('background-image', 'url(' +this.icon +')');
11916 btnEl.addClass(this.iconCls);
11918 btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
11921 if(this.tabIndex !== undefined){
11922 btnEl.dom.tabIndex = this.tabIndex;
11925 if(typeof this.tooltip == 'object'){
11926 Roo.QuickTips.tips(Roo.apply({
11930 btnEl.dom[this.tooltipType] = this.tooltip;
11934 btn = Roo.DomHelper.append(Roo.get(renderTo).dom, this.dhconfig, true);
11938 this.el.dom.id = this.el.id = this.id;
11941 this.el.child(this.menuClassTarget).addClass("x-btn-with-menu");
11942 this.menu.on("show", this.onMenuShow, this);
11943 this.menu.on("hide", this.onMenuHide, this);
11945 btn.addClass("x-btn");
11946 if(Roo.isIE && !Roo.isIE7){
11947 this.autoWidth.defer(1, this);
11951 if(this.handleMouseEvents){
11952 btn.on("mouseover", this.onMouseOver, this);
11953 btn.on("mouseout", this.onMouseOut, this);
11954 btn.on("mousedown", this.onMouseDown, this);
11956 btn.on(this.clickEvent, this.onClick, this);
11957 //btn.on("mouseup", this.onMouseUp, this);
11964 Roo.ButtonToggleMgr.register(this);
11966 this.el.addClass("x-btn-pressed");
11969 var repeater = new Roo.util.ClickRepeater(btn,
11970 typeof this.repeat == "object" ? this.repeat : {}
11972 repeater.on("click", this.onClick, this);
11975 this.fireEvent('render', this);
11979 * Returns the button's underlying element
11980 * @return {Roo.Element} The element
11982 getEl : function(){
11987 * Destroys this Button and removes any listeners.
11989 destroy : function(){
11990 Roo.ButtonToggleMgr.unregister(this);
11991 this.el.removeAllListeners();
11992 this.purgeListeners();
11997 autoWidth : function(){
11999 this.el.setWidth("auto");
12000 if(Roo.isIE7 && Roo.isStrict){
12001 var ib = this.el.child('button');
12002 if(ib && ib.getWidth() > 20){
12004 ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
12009 this.el.beginMeasure();
12011 if(this.el.getWidth() < this.minWidth){
12012 this.el.setWidth(this.minWidth);
12015 this.el.endMeasure();
12022 * Assigns this button's click handler
12023 * @param {Function} handler The function to call when the button is clicked
12024 * @param {Object} scope (optional) Scope for the function passed in
12026 setHandler : function(handler, scope){
12027 this.handler = handler;
12028 this.scope = scope;
12032 * Sets this button's text
12033 * @param {String} text The button text
12035 setText : function(text){
12038 this.el.child("td.x-btn-center button.x-btn-text").update(text);
12044 * Gets the text for this button
12045 * @return {String} The button text
12047 getText : function(){
12055 this.hidden = false;
12057 this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "");
12065 this.hidden = true;
12067 this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "none");
12072 * Convenience function for boolean show/hide
12073 * @param {Boolean} visible True to show, false to hide
12075 setVisible: function(visible){
12084 * If a state it passed, it becomes the pressed state otherwise the current state is toggled.
12085 * @param {Boolean} state (optional) Force a particular state
12087 toggle : function(state){
12088 state = state === undefined ? !this.pressed : state;
12089 if(state != this.pressed){
12091 this.el.addClass("x-btn-pressed");
12092 this.pressed = true;
12093 this.fireEvent("toggle", this, true);
12095 this.el.removeClass("x-btn-pressed");
12096 this.pressed = false;
12097 this.fireEvent("toggle", this, false);
12099 if(this.toggleHandler){
12100 this.toggleHandler.call(this.scope || this, this, state);
12108 focus : function(){
12109 this.el.child('button:first').focus();
12113 * Disable this button
12115 disable : function(){
12117 this.el.addClass("x-btn-disabled");
12119 this.disabled = true;
12123 * Enable this button
12125 enable : function(){
12127 this.el.removeClass("x-btn-disabled");
12129 this.disabled = false;
12133 * Convenience function for boolean enable/disable
12134 * @param {Boolean} enabled True to enable, false to disable
12136 setDisabled : function(v){
12137 this[v !== true ? "enable" : "disable"]();
12141 onClick : function(e){
12143 e.preventDefault();
12148 if(!this.disabled){
12149 if(this.enableToggle){
12152 if(this.menu && !this.menu.isVisible()){
12153 this.menu.show(this.el, this.menuAlign);
12155 this.fireEvent("click", this, e);
12157 this.el.removeClass("x-btn-over");
12158 this.handler.call(this.scope || this, this, e);
12163 onMouseOver : function(e){
12164 if(!this.disabled){
12165 this.el.addClass("x-btn-over");
12166 this.fireEvent('mouseover', this, e);
12170 onMouseOut : function(e){
12171 if(!e.within(this.el, true)){
12172 this.el.removeClass("x-btn-over");
12173 this.fireEvent('mouseout', this, e);
12177 onFocus : function(e){
12178 if(!this.disabled){
12179 this.el.addClass("x-btn-focus");
12183 onBlur : function(e){
12184 this.el.removeClass("x-btn-focus");
12187 onMouseDown : function(e){
12188 if(!this.disabled && e.button == 0){
12189 this.el.addClass("x-btn-click");
12190 Roo.get(document).on('mouseup', this.onMouseUp, this);
12194 onMouseUp : function(e){
12196 this.el.removeClass("x-btn-click");
12197 Roo.get(document).un('mouseup', this.onMouseUp, this);
12201 onMenuShow : function(e){
12202 this.el.addClass("x-btn-menu-active");
12205 onMenuHide : function(e){
12206 this.el.removeClass("x-btn-menu-active");
12210 // Private utility class used by Button
12211 Roo.ButtonToggleMgr = function(){
12214 function toggleGroup(btn, state){
12216 var g = groups[btn.toggleGroup];
12217 for(var i = 0, l = g.length; i < l; i++){
12219 g[i].toggle(false);
12226 register : function(btn){
12227 if(!btn.toggleGroup){
12230 var g = groups[btn.toggleGroup];
12232 g = groups[btn.toggleGroup] = [];
12235 btn.on("toggle", toggleGroup);
12238 unregister : function(btn){
12239 if(!btn.toggleGroup){
12242 var g = groups[btn.toggleGroup];
12245 btn.un("toggle", toggleGroup);
12251 * Ext JS Library 1.1.1
12252 * Copyright(c) 2006-2007, Ext JS, LLC.
12254 * Originally Released Under LGPL - original licence link has changed is not relivant.
12257 * <script type="text/javascript">
12261 * @class Roo.SplitButton
12262 * @extends Roo.Button
12263 * A split button that provides a built-in dropdown arrow that can fire an event separately from the default
12264 * click event of the button. Typically this would be used to display a dropdown menu that provides additional
12265 * options to the primary button action, but any custom handler can provide the arrowclick implementation.
12266 * @cfg {Function} arrowHandler A function called when the arrow button is clicked (can be used instead of click event)
12267 * @cfg {String} arrowTooltip The title attribute of the arrow
12269 * Create a new menu button
12270 * @param {String/HTMLElement/Element} renderTo The element to append the button to
12271 * @param {Object} config The config object
12273 Roo.SplitButton = function(renderTo, config){
12274 Roo.SplitButton.superclass.constructor.call(this, renderTo, config);
12276 * @event arrowclick
12277 * Fires when this button's arrow is clicked
12278 * @param {SplitButton} this
12279 * @param {EventObject} e The click event
12281 this.addEvents({"arrowclick":true});
12284 Roo.extend(Roo.SplitButton, Roo.Button, {
12285 render : function(renderTo){
12286 // this is one sweet looking template!
12287 var tpl = new Roo.Template(
12288 '<table cellspacing="0" class="x-btn-menu-wrap x-btn"><tr><td>',
12289 '<table cellspacing="0" class="x-btn-wrap x-btn-menu-text-wrap"><tbody>',
12290 '<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>',
12291 "</tbody></table></td><td>",
12292 '<table cellspacing="0" class="x-btn-wrap x-btn-menu-arrow-wrap"><tbody>',
12293 '<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>',
12294 "</tbody></table></td></tr></table>"
12296 var btn = tpl.append(renderTo, [this.text, this.type], true);
12297 var btnEl = btn.child("button");
12299 btn.addClass(this.cls);
12302 btnEl.setStyle('background-image', 'url(' +this.icon +')');
12305 btnEl.addClass(this.iconCls);
12307 btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
12311 if(this.handleMouseEvents){
12312 btn.on("mouseover", this.onMouseOver, this);
12313 btn.on("mouseout", this.onMouseOut, this);
12314 btn.on("mousedown", this.onMouseDown, this);
12315 btn.on("mouseup", this.onMouseUp, this);
12317 btn.on(this.clickEvent, this.onClick, this);
12319 if(typeof this.tooltip == 'object'){
12320 Roo.QuickTips.tips(Roo.apply({
12324 btnEl.dom[this.tooltipType] = this.tooltip;
12327 if(this.arrowTooltip){
12328 btn.child("button:nth(2)").dom[this.tooltipType] = this.arrowTooltip;
12337 this.el.addClass("x-btn-pressed");
12339 if(Roo.isIE && !Roo.isIE7){
12340 this.autoWidth.defer(1, this);
12345 this.menu.on("show", this.onMenuShow, this);
12346 this.menu.on("hide", this.onMenuHide, this);
12348 this.fireEvent('render', this);
12352 autoWidth : function(){
12354 var tbl = this.el.child("table:first");
12355 var tbl2 = this.el.child("table:last");
12356 this.el.setWidth("auto");
12357 tbl.setWidth("auto");
12358 if(Roo.isIE7 && Roo.isStrict){
12359 var ib = this.el.child('button:first');
12360 if(ib && ib.getWidth() > 20){
12362 ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
12367 this.el.beginMeasure();
12369 if((tbl.getWidth()+tbl2.getWidth()) < this.minWidth){
12370 tbl.setWidth(this.minWidth-tbl2.getWidth());
12373 this.el.endMeasure();
12376 this.el.setWidth(tbl.getWidth()+tbl2.getWidth());
12380 * Sets this button's click handler
12381 * @param {Function} handler The function to call when the button is clicked
12382 * @param {Object} scope (optional) Scope for the function passed above
12384 setHandler : function(handler, scope){
12385 this.handler = handler;
12386 this.scope = scope;
12390 * Sets this button's arrow click handler
12391 * @param {Function} handler The function to call when the arrow is clicked
12392 * @param {Object} scope (optional) Scope for the function passed above
12394 setArrowHandler : function(handler, scope){
12395 this.arrowHandler = handler;
12396 this.scope = scope;
12402 focus : function(){
12404 this.el.child("button:first").focus();
12409 onClick : function(e){
12410 e.preventDefault();
12411 if(!this.disabled){
12412 if(e.getTarget(".x-btn-menu-arrow-wrap")){
12413 if(this.menu && !this.menu.isVisible()){
12414 this.menu.show(this.el, this.menuAlign);
12416 this.fireEvent("arrowclick", this, e);
12417 if(this.arrowHandler){
12418 this.arrowHandler.call(this.scope || this, this, e);
12421 this.fireEvent("click", this, e);
12423 this.handler.call(this.scope || this, this, e);
12429 onMouseDown : function(e){
12430 if(!this.disabled){
12431 Roo.fly(e.getTarget("table")).addClass("x-btn-click");
12435 onMouseUp : function(e){
12436 Roo.fly(e.getTarget("table")).removeClass("x-btn-click");
12441 // backwards compat
12442 Roo.MenuButton = Roo.SplitButton;/*
12444 * Ext JS Library 1.1.1
12445 * Copyright(c) 2006-2007, Ext JS, LLC.
12447 * Originally Released Under LGPL - original licence link has changed is not relivant.
12450 * <script type="text/javascript">
12454 * @class Roo.Toolbar
12455 * Basic Toolbar class.
12457 * Creates a new Toolbar
12458 * @param {Object} container The config object
12460 Roo.Toolbar = function(container, buttons, config)
12462 /// old consturctor format still supported..
12463 if(container instanceof Array){ // omit the container for later rendering
12464 buttons = container;
12468 if (typeof(container) == 'object' && container.xtype) {
12469 config = container;
12470 container = config.container;
12471 buttons = config.buttons || []; // not really - use items!!
12474 if (config && config.items) {
12475 xitems = config.items;
12476 delete config.items;
12478 Roo.apply(this, config);
12479 this.buttons = buttons;
12482 this.render(container);
12484 this.xitems = xitems;
12485 Roo.each(xitems, function(b) {
12491 Roo.Toolbar.prototype = {
12493 * @cfg {Array} items
12494 * array of button configs or elements to add (will be converted to a MixedCollection)
12498 * @cfg {String/HTMLElement/Element} container
12499 * The id or element that will contain the toolbar
12502 render : function(ct){
12503 this.el = Roo.get(ct);
12505 this.el.addClass(this.cls);
12507 // using a table allows for vertical alignment
12508 // 100% width is needed by Safari...
12509 this.el.update('<div class="x-toolbar x-small-editor"><table cellspacing="0"><tr></tr></table></div>');
12510 this.tr = this.el.child("tr", true);
12512 this.items = new Roo.util.MixedCollection(false, function(o){
12513 return o.id || ("item" + (++autoId));
12516 this.add.apply(this, this.buttons);
12517 delete this.buttons;
12522 * Adds element(s) to the toolbar -- this function takes a variable number of
12523 * arguments of mixed type and adds them to the toolbar.
12524 * @param {Mixed} arg1 The following types of arguments are all valid:<br />
12526 * <li>{@link Roo.Toolbar.Button} config: A valid button config object (equivalent to {@link #addButton})</li>
12527 * <li>HtmlElement: Any standard HTML element (equivalent to {@link #addElement})</li>
12528 * <li>Field: Any form field (equivalent to {@link #addField})</li>
12529 * <li>Item: Any subclass of {@link Roo.Toolbar.Item} (equivalent to {@link #addItem})</li>
12530 * <li>String: Any generic string (gets wrapped in a {@link Roo.Toolbar.TextItem}, equivalent to {@link #addText}).
12531 * Note that there are a few special strings that are treated differently as explained nRoo.</li>
12532 * <li>'separator' or '-': Creates a separator element (equivalent to {@link #addSeparator})</li>
12533 * <li>' ': Creates a spacer element (equivalent to {@link #addSpacer})</li>
12534 * <li>'->': Creates a fill element (equivalent to {@link #addFill})</li>
12536 * @param {Mixed} arg2
12537 * @param {Mixed} etc.
12540 var a = arguments, l = a.length;
12541 for(var i = 0; i < l; i++){
12546 _add : function(el) {
12549 el = Roo.factory(el, typeof(Roo.Toolbar[el.xtype]) == 'undefined' ? Roo.form : Roo.Toolbar);
12552 if (el.applyTo){ // some kind of form field
12553 return this.addField(el);
12555 if (el.render){ // some kind of Toolbar.Item
12556 return this.addItem(el);
12558 if (typeof el == "string"){ // string
12559 if(el == "separator" || el == "-"){
12560 return this.addSeparator();
12563 return this.addSpacer();
12566 return this.addFill();
12568 return this.addText(el);
12571 if(el.tagName){ // element
12572 return this.addElement(el);
12574 if(typeof el == "object"){ // must be button config?
12575 return this.addButton(el);
12577 // and now what?!?!
12583 * Add an Xtype element
12584 * @param {Object} xtype Xtype Object
12585 * @return {Object} created Object
12587 addxtype : function(e){
12588 return this.add(e);
12592 * Returns the Element for this toolbar.
12593 * @return {Roo.Element}
12595 getEl : function(){
12601 * @return {Roo.Toolbar.Item} The separator item
12603 addSeparator : function(){
12604 return this.addItem(new Roo.Toolbar.Separator());
12608 * Adds a spacer element
12609 * @return {Roo.Toolbar.Spacer} The spacer item
12611 addSpacer : function(){
12612 return this.addItem(new Roo.Toolbar.Spacer());
12616 * Adds a fill element that forces subsequent additions to the right side of the toolbar
12617 * @return {Roo.Toolbar.Fill} The fill item
12619 addFill : function(){
12620 return this.addItem(new Roo.Toolbar.Fill());
12624 * Adds any standard HTML element to the toolbar
12625 * @param {String/HTMLElement/Element} el The element or id of the element to add
12626 * @return {Roo.Toolbar.Item} The element's item
12628 addElement : function(el){
12629 return this.addItem(new Roo.Toolbar.Item(el));
12632 * Collection of items on the toolbar.. (only Toolbar Items, so use fields to retrieve fields)
12633 * @type Roo.util.MixedCollection
12638 * Adds any Toolbar.Item or subclass
12639 * @param {Roo.Toolbar.Item} item
12640 * @return {Roo.Toolbar.Item} The item
12642 addItem : function(item){
12643 var td = this.nextBlock();
12645 this.items.add(item);
12650 * Adds a button (or buttons). See {@link Roo.Toolbar.Button} for more info on the config.
12651 * @param {Object/Array} config A button config or array of configs
12652 * @return {Roo.Toolbar.Button/Array}
12654 addButton : function(config){
12655 if(config instanceof Array){
12657 for(var i = 0, len = config.length; i < len; i++) {
12658 buttons.push(this.addButton(config[i]));
12663 if(!(config instanceof Roo.Toolbar.Button)){
12665 new Roo.Toolbar.SplitButton(config) :
12666 new Roo.Toolbar.Button(config);
12668 var td = this.nextBlock();
12675 * Adds text to the toolbar
12676 * @param {String} text The text to add
12677 * @return {Roo.Toolbar.Item} The element's item
12679 addText : function(text){
12680 return this.addItem(new Roo.Toolbar.TextItem(text));
12684 * Inserts any {@link Roo.Toolbar.Item}/{@link Roo.Toolbar.Button} at the specified index.
12685 * @param {Number} index The index where the item is to be inserted
12686 * @param {Object/Roo.Toolbar.Item/Roo.Toolbar.Button (may be Array)} item The button, or button config object to be inserted.
12687 * @return {Roo.Toolbar.Button/Item}
12689 insertButton : function(index, item){
12690 if(item instanceof Array){
12692 for(var i = 0, len = item.length; i < len; i++) {
12693 buttons.push(this.insertButton(index + i, item[i]));
12697 if (!(item instanceof Roo.Toolbar.Button)){
12698 item = new Roo.Toolbar.Button(item);
12700 var td = document.createElement("td");
12701 this.tr.insertBefore(td, this.tr.childNodes[index]);
12703 this.items.insert(index, item);
12708 * Adds a new element to the toolbar from the passed {@link Roo.DomHelper} config.
12709 * @param {Object} config
12710 * @return {Roo.Toolbar.Item} The element's item
12712 addDom : function(config, returnEl){
12713 var td = this.nextBlock();
12714 Roo.DomHelper.overwrite(td, config);
12715 var ti = new Roo.Toolbar.Item(td.firstChild);
12717 this.items.add(ti);
12722 * Collection of fields on the toolbar.. usefull for quering (value is false if there are no fields)
12723 * @type Roo.util.MixedCollection
12728 * Adds a dynamically rendered Roo.form field (TextField, ComboBox, etc).
12729 * Note: the field should not have been rendered yet. For a field that has already been
12730 * rendered, use {@link #addElement}.
12731 * @param {Roo.form.Field} field
12732 * @return {Roo.ToolbarItem}
12736 addField : function(field) {
12737 if (!this.fields) {
12739 this.fields = new Roo.util.MixedCollection(false, function(o){
12740 return o.id || ("item" + (++autoId));
12745 var td = this.nextBlock();
12747 var ti = new Roo.Toolbar.Item(td.firstChild);
12749 this.items.add(ti);
12750 this.fields.add(field);
12761 this.el.child('div').setVisibilityMode(Roo.Element.DISPLAY);
12762 this.el.child('div').hide();
12770 this.el.child('div').show();
12774 nextBlock : function(){
12775 var td = document.createElement("td");
12776 this.tr.appendChild(td);
12781 destroy : function(){
12782 if(this.items){ // rendered?
12783 Roo.destroy.apply(Roo, this.items.items);
12785 if(this.fields){ // rendered?
12786 Roo.destroy.apply(Roo, this.fields.items);
12788 Roo.Element.uncache(this.el, this.tr);
12793 * @class Roo.Toolbar.Item
12794 * The base class that other classes should extend in order to get some basic common toolbar item functionality.
12796 * Creates a new Item
12797 * @param {HTMLElement} el
12799 Roo.Toolbar.Item = function(el){
12800 this.el = Roo.getDom(el);
12801 this.id = Roo.id(this.el);
12802 this.hidden = false;
12805 Roo.Toolbar.Item.prototype = {
12808 * Get this item's HTML Element
12809 * @return {HTMLElement}
12811 getEl : function(){
12816 render : function(td){
12818 td.appendChild(this.el);
12822 * Removes and destroys this item.
12824 destroy : function(){
12825 this.td.parentNode.removeChild(this.td);
12832 this.hidden = false;
12833 this.td.style.display = "";
12840 this.hidden = true;
12841 this.td.style.display = "none";
12845 * Convenience function for boolean show/hide.
12846 * @param {Boolean} visible true to show/false to hide
12848 setVisible: function(visible){
12857 * Try to focus this item.
12859 focus : function(){
12860 Roo.fly(this.el).focus();
12864 * Disables this item.
12866 disable : function(){
12867 Roo.fly(this.td).addClass("x-item-disabled");
12868 this.disabled = true;
12869 this.el.disabled = true;
12873 * Enables this item.
12875 enable : function(){
12876 Roo.fly(this.td).removeClass("x-item-disabled");
12877 this.disabled = false;
12878 this.el.disabled = false;
12884 * @class Roo.Toolbar.Separator
12885 * @extends Roo.Toolbar.Item
12886 * A simple toolbar separator class
12888 * Creates a new Separator
12890 Roo.Toolbar.Separator = function(){
12891 var s = document.createElement("span");
12892 s.className = "ytb-sep";
12893 Roo.Toolbar.Separator.superclass.constructor.call(this, s);
12895 Roo.extend(Roo.Toolbar.Separator, Roo.Toolbar.Item, {
12896 enable:Roo.emptyFn,
12897 disable:Roo.emptyFn,
12902 * @class Roo.Toolbar.Spacer
12903 * @extends Roo.Toolbar.Item
12904 * A simple element that adds extra horizontal space to a toolbar.
12906 * Creates a new Spacer
12908 Roo.Toolbar.Spacer = function(){
12909 var s = document.createElement("div");
12910 s.className = "ytb-spacer";
12911 Roo.Toolbar.Spacer.superclass.constructor.call(this, s);
12913 Roo.extend(Roo.Toolbar.Spacer, Roo.Toolbar.Item, {
12914 enable:Roo.emptyFn,
12915 disable:Roo.emptyFn,
12920 * @class Roo.Toolbar.Fill
12921 * @extends Roo.Toolbar.Spacer
12922 * A simple element that adds a greedy (100% width) horizontal space to a toolbar.
12924 * Creates a new Spacer
12926 Roo.Toolbar.Fill = Roo.extend(Roo.Toolbar.Spacer, {
12928 render : function(td){
12929 td.style.width = '100%';
12930 Roo.Toolbar.Fill.superclass.render.call(this, td);
12935 * @class Roo.Toolbar.TextItem
12936 * @extends Roo.Toolbar.Item
12937 * A simple class that renders text directly into a toolbar.
12939 * Creates a new TextItem
12940 * @param {String} text
12942 Roo.Toolbar.TextItem = function(text){
12943 if (typeof(text) == 'object') {
12946 var s = document.createElement("span");
12947 s.className = "ytb-text";
12948 s.innerHTML = text;
12949 Roo.Toolbar.TextItem.superclass.constructor.call(this, s);
12951 Roo.extend(Roo.Toolbar.TextItem, Roo.Toolbar.Item, {
12952 enable:Roo.emptyFn,
12953 disable:Roo.emptyFn,
12958 * @class Roo.Toolbar.Button
12959 * @extends Roo.Button
12960 * A button that renders into a toolbar.
12962 * Creates a new Button
12963 * @param {Object} config A standard {@link Roo.Button} config object
12965 Roo.Toolbar.Button = function(config){
12966 Roo.Toolbar.Button.superclass.constructor.call(this, null, config);
12968 Roo.extend(Roo.Toolbar.Button, Roo.Button, {
12969 render : function(td){
12971 Roo.Toolbar.Button.superclass.render.call(this, td);
12975 * Removes and destroys this button
12977 destroy : function(){
12978 Roo.Toolbar.Button.superclass.destroy.call(this);
12979 this.td.parentNode.removeChild(this.td);
12983 * Shows this button
12986 this.hidden = false;
12987 this.td.style.display = "";
12991 * Hides this button
12994 this.hidden = true;
12995 this.td.style.display = "none";
12999 * Disables this item
13001 disable : function(){
13002 Roo.fly(this.td).addClass("x-item-disabled");
13003 this.disabled = true;
13007 * Enables this item
13009 enable : function(){
13010 Roo.fly(this.td).removeClass("x-item-disabled");
13011 this.disabled = false;
13014 // backwards compat
13015 Roo.ToolbarButton = Roo.Toolbar.Button;
13018 * @class Roo.Toolbar.SplitButton
13019 * @extends Roo.SplitButton
13020 * A menu button that renders into a toolbar.
13022 * Creates a new SplitButton
13023 * @param {Object} config A standard {@link Roo.SplitButton} config object
13025 Roo.Toolbar.SplitButton = function(config){
13026 Roo.Toolbar.SplitButton.superclass.constructor.call(this, null, config);
13028 Roo.extend(Roo.Toolbar.SplitButton, Roo.SplitButton, {
13029 render : function(td){
13031 Roo.Toolbar.SplitButton.superclass.render.call(this, td);
13035 * Removes and destroys this button
13037 destroy : function(){
13038 Roo.Toolbar.SplitButton.superclass.destroy.call(this);
13039 this.td.parentNode.removeChild(this.td);
13043 * Shows this button
13046 this.hidden = false;
13047 this.td.style.display = "";
13051 * Hides this button
13054 this.hidden = true;
13055 this.td.style.display = "none";
13059 // backwards compat
13060 Roo.Toolbar.MenuButton = Roo.Toolbar.SplitButton;/*
13062 * Ext JS Library 1.1.1
13063 * Copyright(c) 2006-2007, Ext JS, LLC.
13065 * Originally Released Under LGPL - original licence link has changed is not relivant.
13068 * <script type="text/javascript">
13072 * @class Roo.PagingToolbar
13073 * @extends Roo.Toolbar
13074 * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
13076 * Create a new PagingToolbar
13077 * @param {Object} config The config object
13079 Roo.PagingToolbar = function(el, ds, config)
13081 // old args format still supported... - xtype is prefered..
13082 if (typeof(el) == 'object' && el.xtype) {
13083 // created from xtype...
13085 ds = el.dataSource;
13086 el = config.container;
13089 if (config.items) {
13090 items = config.items;
13094 Roo.PagingToolbar.superclass.constructor.call(this, el, null, config);
13097 this.renderButtons(this.el);
13100 // supprot items array.
13102 Roo.each(items, function(e) {
13103 this.add(Roo.factory(e));
13108 Roo.extend(Roo.PagingToolbar, Roo.Toolbar, {
13110 * @cfg {Roo.data.Store} dataSource
13111 * The underlying data store providing the paged data
13114 * @cfg {String/HTMLElement/Element} container
13115 * container The id or element that will contain the toolbar
13118 * @cfg {Boolean} displayInfo
13119 * True to display the displayMsg (defaults to false)
13122 * @cfg {Number} pageSize
13123 * The number of records to display per page (defaults to 20)
13127 * @cfg {String} displayMsg
13128 * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
13130 displayMsg : 'Displaying {0} - {1} of {2}',
13132 * @cfg {String} emptyMsg
13133 * The message to display when no records are found (defaults to "No data to display")
13135 emptyMsg : 'No data to display',
13137 * Customizable piece of the default paging text (defaults to "Page")
13140 beforePageText : "Page",
13142 * Customizable piece of the default paging text (defaults to "of %0")
13145 afterPageText : "of {0}",
13147 * Customizable piece of the default paging text (defaults to "First Page")
13150 firstText : "First Page",
13152 * Customizable piece of the default paging text (defaults to "Previous Page")
13155 prevText : "Previous Page",
13157 * Customizable piece of the default paging text (defaults to "Next Page")
13160 nextText : "Next Page",
13162 * Customizable piece of the default paging text (defaults to "Last Page")
13165 lastText : "Last Page",
13167 * Customizable piece of the default paging text (defaults to "Refresh")
13170 refreshText : "Refresh",
13173 renderButtons : function(el){
13174 Roo.PagingToolbar.superclass.render.call(this, el);
13175 this.first = this.addButton({
13176 tooltip: this.firstText,
13177 cls: "x-btn-icon x-grid-page-first",
13179 handler: this.onClick.createDelegate(this, ["first"])
13181 this.prev = this.addButton({
13182 tooltip: this.prevText,
13183 cls: "x-btn-icon x-grid-page-prev",
13185 handler: this.onClick.createDelegate(this, ["prev"])
13187 //this.addSeparator();
13188 this.add(this.beforePageText);
13189 this.field = Roo.get(this.addDom({
13194 cls: "x-grid-page-number"
13196 this.field.on("keydown", this.onPagingKeydown, this);
13197 this.field.on("focus", function(){this.dom.select();});
13198 this.afterTextEl = this.addText(String.format(this.afterPageText, 1));
13199 this.field.setHeight(18);
13200 //this.addSeparator();
13201 this.next = this.addButton({
13202 tooltip: this.nextText,
13203 cls: "x-btn-icon x-grid-page-next",
13205 handler: this.onClick.createDelegate(this, ["next"])
13207 this.last = this.addButton({
13208 tooltip: this.lastText,
13209 cls: "x-btn-icon x-grid-page-last",
13211 handler: this.onClick.createDelegate(this, ["last"])
13213 //this.addSeparator();
13214 this.loading = this.addButton({
13215 tooltip: this.refreshText,
13216 cls: "x-btn-icon x-grid-loading",
13217 handler: this.onClick.createDelegate(this, ["refresh"])
13220 if(this.displayInfo){
13221 this.displayEl = Roo.fly(this.el.dom.firstChild).createChild({cls:'x-paging-info'});
13226 updateInfo : function(){
13227 if(this.displayEl){
13228 var count = this.ds.getCount();
13229 var msg = count == 0 ?
13233 this.cursor+1, this.cursor+count, this.ds.getTotalCount()
13235 this.displayEl.update(msg);
13240 onLoad : function(ds, r, o){
13241 this.cursor = o.params ? o.params.start : 0;
13242 var d = this.getPageData(), ap = d.activePage, ps = d.pages;
13244 this.afterTextEl.el.innerHTML = String.format(this.afterPageText, d.pages);
13245 this.field.dom.value = ap;
13246 this.first.setDisabled(ap == 1);
13247 this.prev.setDisabled(ap == 1);
13248 this.next.setDisabled(ap == ps);
13249 this.last.setDisabled(ap == ps);
13250 this.loading.enable();
13255 getPageData : function(){
13256 var total = this.ds.getTotalCount();
13259 activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
13260 pages : total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
13265 onLoadError : function(){
13266 this.loading.enable();
13270 onPagingKeydown : function(e){
13271 var k = e.getKey();
13272 var d = this.getPageData();
13274 var v = this.field.dom.value, pageNum;
13275 if(!v || isNaN(pageNum = parseInt(v, 10))){
13276 this.field.dom.value = d.activePage;
13279 pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
13280 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
13283 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))
13285 var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
13286 this.field.dom.value = pageNum;
13287 this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
13290 else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
13292 var v = this.field.dom.value, pageNum;
13293 var increment = (e.shiftKey) ? 10 : 1;
13294 if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
13296 if(!v || isNaN(pageNum = parseInt(v, 10))) {
13297 this.field.dom.value = d.activePage;
13300 else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
13302 this.field.dom.value = parseInt(v, 10) + increment;
13303 pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
13304 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
13311 beforeLoad : function(){
13313 this.loading.disable();
13318 onClick : function(which){
13322 ds.load({params:{start: 0, limit: this.pageSize}});
13325 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
13328 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
13331 var total = ds.getTotalCount();
13332 var extra = total % this.pageSize;
13333 var lastStart = extra ? (total - extra) : total-this.pageSize;
13334 ds.load({params:{start: lastStart, limit: this.pageSize}});
13337 ds.load({params:{start: this.cursor, limit: this.pageSize}});
13343 * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
13344 * @param {Roo.data.Store} store The data store to unbind
13346 unbind : function(ds){
13347 ds.un("beforeload", this.beforeLoad, this);
13348 ds.un("load", this.onLoad, this);
13349 ds.un("loadexception", this.onLoadError, this);
13350 ds.un("remove", this.updateInfo, this);
13351 ds.un("add", this.updateInfo, this);
13352 this.ds = undefined;
13356 * Binds the paging toolbar to the specified {@link Roo.data.Store}
13357 * @param {Roo.data.Store} store The data store to bind
13359 bind : function(ds){
13360 ds.on("beforeload", this.beforeLoad, this);
13361 ds.on("load", this.onLoad, this);
13362 ds.on("loadexception", this.onLoadError, this);
13363 ds.on("remove", this.updateInfo, this);
13364 ds.on("add", this.updateInfo, this);
13369 * Ext JS Library 1.1.1
13370 * Copyright(c) 2006-2007, Ext JS, LLC.
13372 * Originally Released Under LGPL - original licence link has changed is not relivant.
13375 * <script type="text/javascript">
13379 * @class Roo.Resizable
13380 * @extends Roo.util.Observable
13381 * <p>Applies drag handles to an element to make it resizable. The drag handles are inserted into the element
13382 * and positioned absolute. Some elements, such as a textarea or image, don't support this. To overcome that, you can wrap
13383 * 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
13384 * the element will be wrapped for you automatically.</p>
13385 * <p>Here is the list of valid resize handles:</p>
13388 ------ -------------------
13397 'hd' horizontal drag
13400 * <p>Here's an example showing the creation of a typical Resizable:</p>
13402 var resizer = new Roo.Resizable("element-id", {
13410 resizer.on("resize", myHandler);
13412 * <p>To hide a particular handle, set its display to none in CSS, or through script:<br>
13413 * resizer.east.setDisplayed(false);</p>
13414 * @cfg {Boolean/String/Element} resizeChild True to resize the first child, or id/element to resize (defaults to false)
13415 * @cfg {Array/String} adjustments String "auto" or an array [width, height] with values to be <b>added</b> to the
13416 * resize operation's new size (defaults to [0, 0])
13417 * @cfg {Number} minWidth The minimum width for the element (defaults to 5)
13418 * @cfg {Number} minHeight The minimum height for the element (defaults to 5)
13419 * @cfg {Number} maxWidth The maximum width for the element (defaults to 10000)
13420 * @cfg {Number} maxHeight The maximum height for the element (defaults to 10000)
13421 * @cfg {Boolean} enabled False to disable resizing (defaults to true)
13422 * @cfg {Boolean} wrap True to wrap an element with a div if needed (required for textareas and images, defaults to false)
13423 * @cfg {Number} width The width of the element in pixels (defaults to null)
13424 * @cfg {Number} height The height of the element in pixels (defaults to null)
13425 * @cfg {Boolean} animate True to animate the resize (not compatible with dynamic sizing, defaults to false)
13426 * @cfg {Number} duration Animation duration if animate = true (defaults to .35)
13427 * @cfg {Boolean} dynamic True to resize the element while dragging instead of using a proxy (defaults to false)
13428 * @cfg {String} handles String consisting of the resize handles to display (defaults to undefined)
13429 * @cfg {Boolean} multiDirectional <b>Deprecated</b>. The old style of adding multi-direction resize handles, deprecated
13430 * in favor of the handles config option (defaults to false)
13431 * @cfg {Boolean} disableTrackOver True to disable mouse tracking. This is only applied at config time. (defaults to false)
13432 * @cfg {String} easing Animation easing if animate = true (defaults to 'easingOutStrong')
13433 * @cfg {Number} widthIncrement The increment to snap the width resize in pixels (dynamic must be true, defaults to 0)
13434 * @cfg {Number} heightIncrement The increment to snap the height resize in pixels (dynamic must be true, defaults to 0)
13435 * @cfg {Boolean} pinned True to ensure that the resize handles are always visible, false to display them only when the
13436 * user mouses over the resizable borders. This is only applied at config time. (defaults to false)
13437 * @cfg {Boolean} preserveRatio True to preserve the original ratio between height and width during resize (defaults to false)
13438 * @cfg {Boolean} transparent True for transparent handles. This is only applied at config time. (defaults to false)
13439 * @cfg {Number} minX The minimum allowed page X for the element (only used for west resizing, defaults to 0)
13440 * @cfg {Number} minY The minimum allowed page Y for the element (only used for north resizing, defaults to 0)
13441 * @cfg {Boolean} draggable Convenience to initialize drag drop (defaults to false)
13443 * Create a new resizable component
13444 * @param {String/HTMLElement/Roo.Element} el The id or element to resize
13445 * @param {Object} config configuration options
13447 Roo.Resizable = function(el, config)
13449 this.el = Roo.get(el);
13451 if(config && config.wrap){
13452 config.resizeChild = this.el;
13453 this.el = this.el.wrap(typeof config.wrap == "object" ? config.wrap : {cls:"xresizable-wrap"});
13454 this.el.id = this.el.dom.id = config.resizeChild.id + "-rzwrap";
13455 this.el.setStyle("overflow", "hidden");
13456 this.el.setPositioning(config.resizeChild.getPositioning());
13457 config.resizeChild.clearPositioning();
13458 if(!config.width || !config.height){
13459 var csize = config.resizeChild.getSize();
13460 this.el.setSize(csize.width, csize.height);
13462 if(config.pinned && !config.adjustments){
13463 config.adjustments = "auto";
13467 this.proxy = this.el.createProxy({tag: "div", cls: "x-resizable-proxy", id: this.el.id + "-rzproxy"});
13468 this.proxy.unselectable();
13469 this.proxy.enableDisplayMode('block');
13471 Roo.apply(this, config);
13474 this.disableTrackOver = true;
13475 this.el.addClass("x-resizable-pinned");
13477 // if the element isn't positioned, make it relative
13478 var position = this.el.getStyle("position");
13479 if(position != "absolute" && position != "fixed"){
13480 this.el.setStyle("position", "relative");
13482 if(!this.handles){ // no handles passed, must be legacy style
13483 this.handles = 's,e,se';
13484 if(this.multiDirectional){
13485 this.handles += ',n,w';
13488 if(this.handles == "all"){
13489 this.handles = "n s e w ne nw se sw";
13491 var hs = this.handles.split(/\s*?[,;]\s*?| /);
13492 var ps = Roo.Resizable.positions;
13493 for(var i = 0, len = hs.length; i < len; i++){
13494 if(hs[i] && ps[hs[i]]){
13495 var pos = ps[hs[i]];
13496 this[pos] = new Roo.Resizable.Handle(this, pos, this.disableTrackOver, this.transparent);
13500 this.corner = this.southeast;
13502 // updateBox = the box can move..
13503 if(this.handles.indexOf("n") != -1 || this.handles.indexOf("w") != -1 || this.handles.indexOf("hd") != -1) {
13504 this.updateBox = true;
13507 this.activeHandle = null;
13509 if(this.resizeChild){
13510 if(typeof this.resizeChild == "boolean"){
13511 this.resizeChild = Roo.get(this.el.dom.firstChild, true);
13513 this.resizeChild = Roo.get(this.resizeChild, true);
13517 if(this.adjustments == "auto"){
13518 var rc = this.resizeChild;
13519 var hw = this.west, he = this.east, hn = this.north, hs = this.south;
13520 if(rc && (hw || hn)){
13521 rc.position("relative");
13522 rc.setLeft(hw ? hw.el.getWidth() : 0);
13523 rc.setTop(hn ? hn.el.getHeight() : 0);
13525 this.adjustments = [
13526 (he ? -he.el.getWidth() : 0) + (hw ? -hw.el.getWidth() : 0),
13527 (hn ? -hn.el.getHeight() : 0) + (hs ? -hs.el.getHeight() : 0) -1
13531 if(this.draggable){
13532 this.dd = this.dynamic ?
13533 this.el.initDD(null) : this.el.initDDProxy(null, {dragElId: this.proxy.id});
13534 this.dd.setHandleElId(this.resizeChild ? this.resizeChild.id : this.el.id);
13540 * @event beforeresize
13541 * Fired before resize is allowed. Set enabled to false to cancel resize.
13542 * @param {Roo.Resizable} this
13543 * @param {Roo.EventObject} e The mousedown event
13545 "beforeresize" : true,
13548 * Fired a resizing.
13549 * @param {Roo.Resizable} this
13550 * @param {Number} x The new x position
13551 * @param {Number} y The new y position
13552 * @param {Number} w The new w width
13553 * @param {Number} h The new h hight
13554 * @param {Roo.EventObject} e The mouseup event
13559 * Fired after a resize.
13560 * @param {Roo.Resizable} this
13561 * @param {Number} width The new width
13562 * @param {Number} height The new height
13563 * @param {Roo.EventObject} e The mouseup event
13568 if(this.width !== null && this.height !== null){
13569 this.resizeTo(this.width, this.height);
13571 this.updateChildSize();
13574 this.el.dom.style.zoom = 1;
13576 Roo.Resizable.superclass.constructor.call(this);
13579 Roo.extend(Roo.Resizable, Roo.util.Observable, {
13580 resizeChild : false,
13581 adjustments : [0, 0],
13591 multiDirectional : false,
13592 disableTrackOver : false,
13593 easing : 'easeOutStrong',
13594 widthIncrement : 0,
13595 heightIncrement : 0,
13599 preserveRatio : false,
13600 transparent: false,
13606 * @cfg {String/HTMLElement/Element} constrainTo Constrain the resize to a particular element
13608 constrainTo: undefined,
13610 * @cfg {Roo.lib.Region} resizeRegion Constrain the resize to a particular region
13612 resizeRegion: undefined,
13616 * Perform a manual resize
13617 * @param {Number} width
13618 * @param {Number} height
13620 resizeTo : function(width, height){
13621 this.el.setSize(width, height);
13622 this.updateChildSize();
13623 this.fireEvent("resize", this, width, height, null);
13627 startSizing : function(e, handle){
13628 this.fireEvent("beforeresize", this, e);
13629 if(this.enabled){ // 2nd enabled check in case disabled before beforeresize handler
13632 this.overlay = this.el.createProxy({tag: "div", cls: "x-resizable-overlay", html: " "});
13633 this.overlay.unselectable();
13634 this.overlay.enableDisplayMode("block");
13635 this.overlay.on("mousemove", this.onMouseMove, this);
13636 this.overlay.on("mouseup", this.onMouseUp, this);
13638 this.overlay.setStyle("cursor", handle.el.getStyle("cursor"));
13640 this.resizing = true;
13641 this.startBox = this.el.getBox();
13642 this.startPoint = e.getXY();
13643 this.offsets = [(this.startBox.x + this.startBox.width) - this.startPoint[0],
13644 (this.startBox.y + this.startBox.height) - this.startPoint[1]];
13646 this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
13647 this.overlay.show();
13649 if(this.constrainTo) {
13650 var ct = Roo.get(this.constrainTo);
13651 this.resizeRegion = ct.getRegion().adjust(
13652 ct.getFrameWidth('t'),
13653 ct.getFrameWidth('l'),
13654 -ct.getFrameWidth('b'),
13655 -ct.getFrameWidth('r')
13659 this.proxy.setStyle('visibility', 'hidden'); // workaround display none
13661 this.proxy.setBox(this.startBox);
13663 this.proxy.setStyle('visibility', 'visible');
13669 onMouseDown : function(handle, e){
13672 this.activeHandle = handle;
13673 this.startSizing(e, handle);
13678 onMouseUp : function(e){
13679 var size = this.resizeElement();
13680 this.resizing = false;
13682 this.overlay.hide();
13684 this.fireEvent("resize", this, size.width, size.height, e);
13688 updateChildSize : function(){
13690 if(this.resizeChild){
13692 var child = this.resizeChild;
13693 var adj = this.adjustments;
13694 if(el.dom.offsetWidth){
13695 var b = el.getSize(true);
13696 child.setSize(b.width+adj[0], b.height+adj[1]);
13698 // Second call here for IE
13699 // The first call enables instant resizing and
13700 // the second call corrects scroll bars if they
13703 setTimeout(function(){
13704 if(el.dom.offsetWidth){
13705 var b = el.getSize(true);
13706 child.setSize(b.width+adj[0], b.height+adj[1]);
13714 snap : function(value, inc, min){
13715 if(!inc || !value) return value;
13716 var newValue = value;
13717 var m = value % inc;
13720 newValue = value + (inc-m);
13722 newValue = value - m;
13725 return Math.max(min, newValue);
13729 resizeElement : function(){
13730 var box = this.proxy.getBox();
13731 if(this.updateBox){
13732 this.el.setBox(box, false, this.animate, this.duration, null, this.easing);
13734 this.el.setSize(box.width, box.height, this.animate, this.duration, null, this.easing);
13736 this.updateChildSize();
13744 constrain : function(v, diff, m, mx){
13747 }else if(v - diff > mx){
13754 onMouseMove : function(e){
13757 try{// try catch so if something goes wrong the user doesn't get hung
13759 if(this.resizeRegion && !this.resizeRegion.contains(e.getPoint())) {
13763 //var curXY = this.startPoint;
13764 var curSize = this.curSize || this.startBox;
13765 var x = this.startBox.x, y = this.startBox.y;
13766 var ox = x, oy = y;
13767 var w = curSize.width, h = curSize.height;
13768 var ow = w, oh = h;
13769 var mw = this.minWidth, mh = this.minHeight;
13770 var mxw = this.maxWidth, mxh = this.maxHeight;
13771 var wi = this.widthIncrement;
13772 var hi = this.heightIncrement;
13774 var eventXY = e.getXY();
13775 var diffX = -(this.startPoint[0] - Math.max(this.minX, eventXY[0]));
13776 var diffY = -(this.startPoint[1] - Math.max(this.minY, eventXY[1]));
13778 var pos = this.activeHandle.position;
13783 w = Math.min(Math.max(mw, w), mxw);
13788 h = Math.min(Math.max(mh, h), mxh);
13793 w = Math.min(Math.max(mw, w), mxw);
13794 h = Math.min(Math.max(mh, h), mxh);
13797 diffY = this.constrain(h, diffY, mh, mxh);
13804 var adiffX = Math.abs(diffX);
13805 var sub = (adiffX % wi); // how much
13806 if (sub > (wi/2)) { // far enough to snap
13807 diffX = (diffX > 0) ? diffX-sub + wi : diffX+sub - wi;
13809 // remove difference..
13810 diffX = (diffX > 0) ? diffX-sub : diffX+sub;
13814 x = Math.max(this.minX, x);
13817 diffX = this.constrain(w, diffX, mw, mxw);
13823 w = Math.min(Math.max(mw, w), mxw);
13824 diffY = this.constrain(h, diffY, mh, mxh);
13829 diffX = this.constrain(w, diffX, mw, mxw);
13830 diffY = this.constrain(h, diffY, mh, mxh);
13837 diffX = this.constrain(w, diffX, mw, mxw);
13839 h = Math.min(Math.max(mh, h), mxh);
13845 var sw = this.snap(w, wi, mw);
13846 var sh = this.snap(h, hi, mh);
13847 if(sw != w || sh != h){
13870 if(this.preserveRatio){
13875 h = Math.min(Math.max(mh, h), mxh);
13880 w = Math.min(Math.max(mw, w), mxw);
13885 w = Math.min(Math.max(mw, w), mxw);
13891 w = Math.min(Math.max(mw, w), mxw);
13897 h = Math.min(Math.max(mh, h), mxh);
13905 h = Math.min(Math.max(mh, h), mxh);
13915 h = Math.min(Math.max(mh, h), mxh);
13923 if (pos == 'hdrag') {
13926 this.proxy.setBounds(x, y, w, h);
13928 this.resizeElement();
13932 this.fireEvent("resizing", this, x, y, w, h, e);
13936 handleOver : function(){
13938 this.el.addClass("x-resizable-over");
13943 handleOut : function(){
13944 if(!this.resizing){
13945 this.el.removeClass("x-resizable-over");
13950 * Returns the element this component is bound to.
13951 * @return {Roo.Element}
13953 getEl : function(){
13958 * Returns the resizeChild element (or null).
13959 * @return {Roo.Element}
13961 getResizeChild : function(){
13962 return this.resizeChild;
13964 groupHandler : function()
13969 * Destroys this resizable. If the element was wrapped and
13970 * removeEl is not true then the element remains.
13971 * @param {Boolean} removeEl (optional) true to remove the element from the DOM
13973 destroy : function(removeEl){
13974 this.proxy.remove();
13976 this.overlay.removeAllListeners();
13977 this.overlay.remove();
13979 var ps = Roo.Resizable.positions;
13981 if(typeof ps[k] != "function" && this[ps[k]]){
13982 var h = this[ps[k]];
13983 h.el.removeAllListeners();
13988 this.el.update("");
13995 // hash to map config positions to true positions
13996 Roo.Resizable.positions = {
13997 n: "north", s: "south", e: "east", w: "west", se: "southeast", sw: "southwest", nw: "northwest", ne: "northeast",
14002 Roo.Resizable.Handle = function(rz, pos, disableTrackOver, transparent){
14004 // only initialize the template if resizable is used
14005 var tpl = Roo.DomHelper.createTemplate(
14006 {tag: "div", cls: "x-resizable-handle x-resizable-handle-{0}"}
14009 Roo.Resizable.Handle.prototype.tpl = tpl;
14011 this.position = pos;
14013 // show north drag fro topdra
14014 var handlepos = pos == 'hdrag' ? 'north' : pos;
14016 this.el = this.tpl.append(rz.el.dom, [handlepos], true);
14017 if (pos == 'hdrag') {
14018 this.el.setStyle('cursor', 'pointer');
14020 this.el.unselectable();
14022 this.el.setOpacity(0);
14024 this.el.on("mousedown", this.onMouseDown, this);
14025 if(!disableTrackOver){
14026 this.el.on("mouseover", this.onMouseOver, this);
14027 this.el.on("mouseout", this.onMouseOut, this);
14032 Roo.Resizable.Handle.prototype = {
14033 afterResize : function(rz){
14037 onMouseDown : function(e){
14038 this.rz.onMouseDown(this, e);
14041 onMouseOver : function(e){
14042 this.rz.handleOver(this, e);
14045 onMouseOut : function(e){
14046 this.rz.handleOut(this, e);
14050 * Ext JS Library 1.1.1
14051 * Copyright(c) 2006-2007, Ext JS, LLC.
14053 * Originally Released Under LGPL - original licence link has changed is not relivant.
14056 * <script type="text/javascript">
14060 * @class Roo.Editor
14061 * @extends Roo.Component
14062 * A base editor field that handles displaying/hiding on demand and has some built-in sizing and event handling logic.
14064 * Create a new Editor
14065 * @param {Roo.form.Field} field The Field object (or descendant)
14066 * @param {Object} config The config object
14068 Roo.Editor = function(field, config){
14069 Roo.Editor.superclass.constructor.call(this, config);
14070 this.field = field;
14073 * @event beforestartedit
14074 * Fires when editing is initiated, but before the value changes. Editing can be canceled by returning
14075 * false from the handler of this event.
14076 * @param {Editor} this
14077 * @param {Roo.Element} boundEl The underlying element bound to this editor
14078 * @param {Mixed} value The field value being set
14080 "beforestartedit" : true,
14083 * Fires when this editor is displayed
14084 * @param {Roo.Element} boundEl The underlying element bound to this editor
14085 * @param {Mixed} value The starting field value
14087 "startedit" : true,
14089 * @event beforecomplete
14090 * Fires after a change has been made to the field, but before the change is reflected in the underlying
14091 * field. Saving the change to the field can be canceled by returning false from the handler of this event.
14092 * Note that if the value has not changed and ignoreNoChange = true, the editing will still end but this
14093 * event will not fire since no edit actually occurred.
14094 * @param {Editor} this
14095 * @param {Mixed} value The current field value
14096 * @param {Mixed} startValue The original field value
14098 "beforecomplete" : true,
14101 * Fires after editing is complete and any changed value has been written to the underlying field.
14102 * @param {Editor} this
14103 * @param {Mixed} value The current field value
14104 * @param {Mixed} startValue The original field value
14108 * @event specialkey
14109 * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed. You can check
14110 * {@link Roo.EventObject#getKey} to determine which key was pressed.
14111 * @param {Roo.form.Field} this
14112 * @param {Roo.EventObject} e The event object
14114 "specialkey" : true
14118 Roo.extend(Roo.Editor, Roo.Component, {
14120 * @cfg {Boolean/String} autosize
14121 * True for the editor to automatically adopt the size of the underlying field, "width" to adopt the width only,
14122 * or "height" to adopt the height only (defaults to false)
14125 * @cfg {Boolean} revertInvalid
14126 * True to automatically revert the field value and cancel the edit when the user completes an edit and the field
14127 * validation fails (defaults to true)
14130 * @cfg {Boolean} ignoreNoChange
14131 * True to skip the the edit completion process (no save, no events fired) if the user completes an edit and
14132 * the value has not changed (defaults to false). Applies only to string values - edits for other data types
14133 * will never be ignored.
14136 * @cfg {Boolean} hideEl
14137 * False to keep the bound element visible while the editor is displayed (defaults to true)
14140 * @cfg {Mixed} value
14141 * The data value of the underlying field (defaults to "")
14145 * @cfg {String} alignment
14146 * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "c-c?").
14150 * @cfg {Boolean/String} shadow "sides" for sides/bottom only, "frame" for 4-way shadow, and "drop"
14151 * for bottom-right shadow (defaults to "frame")
14155 * @cfg {Boolean} constrain True to constrain the editor to the viewport
14159 * @cfg {Boolean} completeOnEnter True to complete the edit when the enter key is pressed (defaults to false)
14161 completeOnEnter : false,
14163 * @cfg {Boolean} cancelOnEsc True to cancel the edit when the escape key is pressed (defaults to false)
14165 cancelOnEsc : false,
14167 * @cfg {Boolean} updateEl True to update the innerHTML of the bound element when the update completes (defaults to false)
14172 onRender : function(ct, position){
14173 this.el = new Roo.Layer({
14174 shadow: this.shadow,
14180 constrain: this.constrain
14182 this.el.setStyle("overflow", Roo.isGecko ? "auto" : "hidden");
14183 if(this.field.msgTarget != 'title'){
14184 this.field.msgTarget = 'qtip';
14186 this.field.render(this.el);
14188 this.field.el.dom.setAttribute('autocomplete', 'off');
14190 this.field.on("specialkey", this.onSpecialKey, this);
14191 if(this.swallowKeys){
14192 this.field.el.swallowEvent(['keydown','keypress']);
14195 this.field.on("blur", this.onBlur, this);
14196 if(this.field.grow){
14197 this.field.on("autosize", this.el.sync, this.el, {delay:1});
14201 onSpecialKey : function(field, e)
14203 //Roo.log('editor onSpecialKey');
14204 if(this.completeOnEnter && e.getKey() == e.ENTER){
14206 this.completeEdit();
14209 // do not fire special key otherwise it might hide close the editor...
14210 if(e.getKey() == e.ENTER){
14213 if(this.cancelOnEsc && e.getKey() == e.ESC){
14217 this.fireEvent('specialkey', field, e);
14222 * Starts the editing process and shows the editor.
14223 * @param {String/HTMLElement/Element} el The element to edit
14224 * @param {String} value (optional) A value to initialize the editor with. If a value is not provided, it defaults
14225 * to the innerHTML of el.
14227 startEdit : function(el, value){
14229 this.completeEdit();
14231 this.boundEl = Roo.get(el);
14232 var v = value !== undefined ? value : this.boundEl.dom.innerHTML;
14233 if(!this.rendered){
14234 this.render(this.parentEl || document.body);
14236 if(this.fireEvent("beforestartedit", this, this.boundEl, v) === false){
14239 this.startValue = v;
14240 this.field.setValue(v);
14242 var sz = this.boundEl.getSize();
14243 switch(this.autoSize){
14245 this.setSize(sz.width, "");
14248 this.setSize("", sz.height);
14251 this.setSize(sz.width, sz.height);
14254 this.el.alignTo(this.boundEl, this.alignment);
14255 this.editing = true;
14257 Roo.QuickTips.disable();
14263 * Sets the height and width of this editor.
14264 * @param {Number} width The new width
14265 * @param {Number} height The new height
14267 setSize : function(w, h){
14268 this.field.setSize(w, h);
14275 * Realigns the editor to the bound field based on the current alignment config value.
14277 realign : function(){
14278 this.el.alignTo(this.boundEl, this.alignment);
14282 * Ends the editing process, persists the changed value to the underlying field, and hides the editor.
14283 * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after edit (defaults to false)
14285 completeEdit : function(remainVisible){
14289 var v = this.getValue();
14290 if(this.revertInvalid !== false && !this.field.isValid()){
14291 v = this.startValue;
14292 this.cancelEdit(true);
14294 if(String(v) === String(this.startValue) && this.ignoreNoChange){
14295 this.editing = false;
14299 if(this.fireEvent("beforecomplete", this, v, this.startValue) !== false){
14300 this.editing = false;
14301 if(this.updateEl && this.boundEl){
14302 this.boundEl.update(v);
14304 if(remainVisible !== true){
14307 this.fireEvent("complete", this, v, this.startValue);
14312 onShow : function(){
14314 if(this.hideEl !== false){
14315 this.boundEl.hide();
14318 if(Roo.isIE && !this.fixIEFocus){ // IE has problems with focusing the first time
14319 this.fixIEFocus = true;
14320 this.deferredFocus.defer(50, this);
14322 this.field.focus();
14324 this.fireEvent("startedit", this.boundEl, this.startValue);
14327 deferredFocus : function(){
14329 this.field.focus();
14334 * Cancels the editing process and hides the editor without persisting any changes. The field value will be
14335 * reverted to the original starting value.
14336 * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after
14337 * cancel (defaults to false)
14339 cancelEdit : function(remainVisible){
14341 this.setValue(this.startValue);
14342 if(remainVisible !== true){
14349 onBlur : function(){
14350 if(this.allowBlur !== true && this.editing){
14351 this.completeEdit();
14356 onHide : function(){
14358 this.completeEdit();
14362 if(this.field.collapse){
14363 this.field.collapse();
14366 if(this.hideEl !== false){
14367 this.boundEl.show();
14370 Roo.QuickTips.enable();
14375 * Sets the data value of the editor
14376 * @param {Mixed} value Any valid value supported by the underlying field
14378 setValue : function(v){
14379 this.field.setValue(v);
14383 * Gets the data value of the editor
14384 * @return {Mixed} The data value
14386 getValue : function(){
14387 return this.field.getValue();
14391 * Ext JS Library 1.1.1
14392 * Copyright(c) 2006-2007, Ext JS, LLC.
14394 * Originally Released Under LGPL - original licence link has changed is not relivant.
14397 * <script type="text/javascript">
14401 * @class Roo.BasicDialog
14402 * @extends Roo.util.Observable
14403 * Lightweight Dialog Class. The code below shows the creation of a typical dialog using existing HTML markup:
14405 var dlg = new Roo.BasicDialog("my-dlg", {
14414 dlg.addKeyListener(27, dlg.hide, dlg); // ESC can also close the dialog
14415 dlg.addButton('OK', dlg.hide, dlg); // Could call a save function instead of hiding
14416 dlg.addButton('Cancel', dlg.hide, dlg);
14419 <b>A Dialog should always be a direct child of the body element.</b>
14420 * @cfg {Boolean/DomHelper} autoCreate True to auto create from scratch, or using a DomHelper Object (defaults to false)
14421 * @cfg {String} title Default text to display in the title bar (defaults to null)
14422 * @cfg {Number} width Width of the dialog in pixels (can also be set via CSS). Determined by browser if unspecified.
14423 * @cfg {Number} height Height of the dialog in pixels (can also be set via CSS). Determined by browser if unspecified.
14424 * @cfg {Number} x The default left page coordinate of the dialog (defaults to center screen)
14425 * @cfg {Number} y The default top page coordinate of the dialog (defaults to center screen)
14426 * @cfg {String/Element} animateTarget Id or element from which the dialog should animate while opening
14427 * (defaults to null with no animation)
14428 * @cfg {Boolean} resizable False to disable manual dialog resizing (defaults to true)
14429 * @cfg {String} resizeHandles Which resize handles to display - see the {@link Roo.Resizable} handles config
14430 * property for valid values (defaults to 'all')
14431 * @cfg {Number} minHeight The minimum allowable height for a resizable dialog (defaults to 80)
14432 * @cfg {Number} minWidth The minimum allowable width for a resizable dialog (defaults to 200)
14433 * @cfg {Boolean} modal True to show the dialog modally, preventing user interaction with the rest of the page (defaults to false)
14434 * @cfg {Boolean} autoScroll True to allow the dialog body contents to overflow and display scrollbars (defaults to false)
14435 * @cfg {Boolean} closable False to remove the built-in top-right corner close button (defaults to true)
14436 * @cfg {Boolean} collapsible False to remove the built-in top-right corner collapse button (defaults to true)
14437 * @cfg {Boolean} constraintoviewport True to keep the dialog constrained within the visible viewport boundaries (defaults to true)
14438 * @cfg {Boolean} syncHeightBeforeShow True to cause the dimensions to be recalculated before the dialog is shown (defaults to false)
14439 * @cfg {Boolean} draggable False to disable dragging of the dialog within the viewport (defaults to true)
14440 * @cfg {Boolean} autoTabs If true, all elements with class 'x-dlg-tab' will get automatically converted to tabs (defaults to false)
14441 * @cfg {String} tabTag The tag name of tab elements, used when autoTabs = true (defaults to 'div')
14442 * @cfg {Boolean} proxyDrag True to drag a lightweight proxy element rather than the dialog itself, used when
14443 * draggable = true (defaults to false)
14444 * @cfg {Boolean} fixedcenter True to ensure that anytime the dialog is shown or resized it gets centered (defaults to false)
14445 * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
14446 * shadow (defaults to false)
14447 * @cfg {Number} shadowOffset The number of pixels to offset the shadow if displayed (defaults to 5)
14448 * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "right")
14449 * @cfg {Number} minButtonWidth Minimum width of all dialog buttons (defaults to 75)
14450 * @cfg {Array} buttons Array of buttons
14451 * @cfg {Boolean} shim True to create an iframe shim that prevents selects from showing through (defaults to false)
14453 * Create a new BasicDialog.
14454 * @param {String/HTMLElement/Roo.Element} el The container element or DOM node, or its id
14455 * @param {Object} config Configuration options
14457 Roo.BasicDialog = function(el, config){
14458 this.el = Roo.get(el);
14459 var dh = Roo.DomHelper;
14460 if(!this.el && config && config.autoCreate){
14461 if(typeof config.autoCreate == "object"){
14462 if(!config.autoCreate.id){
14463 config.autoCreate.id = el;
14465 this.el = dh.append(document.body,
14466 config.autoCreate, true);
14468 this.el = dh.append(document.body,
14469 {tag: "div", id: el, style:'visibility:hidden;'}, true);
14473 el.setDisplayed(true);
14474 el.hide = this.hideAction;
14476 el.addClass("x-dlg");
14478 Roo.apply(this, config);
14480 this.proxy = el.createProxy("x-dlg-proxy");
14481 this.proxy.hide = this.hideAction;
14482 this.proxy.setOpacity(.5);
14486 el.setWidth(config.width);
14489 el.setHeight(config.height);
14491 this.size = el.getSize();
14492 if(typeof config.x != "undefined" && typeof config.y != "undefined"){
14493 this.xy = [config.x,config.y];
14495 this.xy = el.getCenterXY(true);
14497 /** The header element @type Roo.Element */
14498 this.header = el.child("> .x-dlg-hd");
14499 /** The body element @type Roo.Element */
14500 this.body = el.child("> .x-dlg-bd");
14501 /** The footer element @type Roo.Element */
14502 this.footer = el.child("> .x-dlg-ft");
14505 this.header = el.createChild({tag: "div", cls:"x-dlg-hd", html: " "}, this.body ? this.body.dom : null);
14508 this.body = el.createChild({tag: "div", cls:"x-dlg-bd"});
14511 this.header.unselectable();
14513 this.header.update(this.title);
14515 // this element allows the dialog to be focused for keyboard event
14516 this.focusEl = el.createChild({tag: "a", href:"#", cls:"x-dlg-focus", tabIndex:"-1"});
14517 this.focusEl.swallowEvent("click", true);
14519 this.header.wrap({cls:"x-dlg-hd-right"}).wrap({cls:"x-dlg-hd-left"}, true);
14521 // wrap the body and footer for special rendering
14522 this.bwrap = this.body.wrap({tag: "div", cls:"x-dlg-dlg-body"});
14524 this.bwrap.dom.appendChild(this.footer.dom);
14527 this.bg = this.el.createChild({
14528 tag: "div", cls:"x-dlg-bg",
14529 html: '<div class="x-dlg-bg-left"><div class="x-dlg-bg-right"><div class="x-dlg-bg-center"> </div></div></div>'
14531 this.centerBg = this.bg.child("div.x-dlg-bg-center");
14534 if(this.autoScroll !== false && !this.autoTabs){
14535 this.body.setStyle("overflow", "auto");
14538 this.toolbox = this.el.createChild({cls: "x-dlg-toolbox"});
14540 if(this.closable !== false){
14541 this.el.addClass("x-dlg-closable");
14542 this.close = this.toolbox.createChild({cls:"x-dlg-close"});
14543 this.close.on("click", this.closeClick, this);
14544 this.close.addClassOnOver("x-dlg-close-over");
14546 if(this.collapsible !== false){
14547 this.collapseBtn = this.toolbox.createChild({cls:"x-dlg-collapse"});
14548 this.collapseBtn.on("click", this.collapseClick, this);
14549 this.collapseBtn.addClassOnOver("x-dlg-collapse-over");
14550 this.header.on("dblclick", this.collapseClick, this);
14552 if(this.resizable !== false){
14553 this.el.addClass("x-dlg-resizable");
14554 this.resizer = new Roo.Resizable(el, {
14555 minWidth: this.minWidth || 80,
14556 minHeight:this.minHeight || 80,
14557 handles: this.resizeHandles || "all",
14560 this.resizer.on("beforeresize", this.beforeResize, this);
14561 this.resizer.on("resize", this.onResize, this);
14563 if(this.draggable !== false){
14564 el.addClass("x-dlg-draggable");
14565 if (!this.proxyDrag) {
14566 var dd = new Roo.dd.DD(el.dom.id, "WindowDrag");
14569 var dd = new Roo.dd.DDProxy(el.dom.id, "WindowDrag", {dragElId: this.proxy.id});
14571 dd.setHandleElId(this.header.id);
14572 dd.endDrag = this.endMove.createDelegate(this);
14573 dd.startDrag = this.startMove.createDelegate(this);
14574 dd.onDrag = this.onDrag.createDelegate(this);
14579 this.mask = dh.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
14580 this.mask.enableDisplayMode("block");
14582 this.el.addClass("x-dlg-modal");
14585 this.shadow = new Roo.Shadow({
14586 mode : typeof this.shadow == "string" ? this.shadow : "sides",
14587 offset : this.shadowOffset
14590 this.shadowOffset = 0;
14592 if(Roo.useShims && this.shim !== false){
14593 this.shim = this.el.createShim();
14594 this.shim.hide = this.hideAction;
14602 if (this.buttons) {
14603 var bts= this.buttons;
14605 Roo.each(bts, function(b) {
14614 * Fires when a key is pressed
14615 * @param {Roo.BasicDialog} this
14616 * @param {Roo.EventObject} e
14621 * Fires when this dialog is moved by the user.
14622 * @param {Roo.BasicDialog} this
14623 * @param {Number} x The new page X
14624 * @param {Number} y The new page Y
14629 * Fires when this dialog is resized by the user.
14630 * @param {Roo.BasicDialog} this
14631 * @param {Number} width The new width
14632 * @param {Number} height The new height
14636 * @event beforehide
14637 * Fires before this dialog is hidden.
14638 * @param {Roo.BasicDialog} this
14640 "beforehide" : true,
14643 * Fires when this dialog is hidden.
14644 * @param {Roo.BasicDialog} this
14648 * @event beforeshow
14649 * Fires before this dialog is shown.
14650 * @param {Roo.BasicDialog} this
14652 "beforeshow" : true,
14655 * Fires when this dialog is shown.
14656 * @param {Roo.BasicDialog} this
14660 el.on("keydown", this.onKeyDown, this);
14661 el.on("mousedown", this.toFront, this);
14662 Roo.EventManager.onWindowResize(this.adjustViewport, this, true);
14664 Roo.DialogManager.register(this);
14665 Roo.BasicDialog.superclass.constructor.call(this);
14668 Roo.extend(Roo.BasicDialog, Roo.util.Observable, {
14669 shadowOffset: Roo.isIE ? 6 : 5,
14672 minButtonWidth: 75,
14673 defaultButton: null,
14674 buttonAlign: "right",
14679 * Sets the dialog title text
14680 * @param {String} text The title text to display
14681 * @return {Roo.BasicDialog} this
14683 setTitle : function(text){
14684 this.header.update(text);
14689 closeClick : function(){
14694 collapseClick : function(){
14695 this[this.collapsed ? "expand" : "collapse"]();
14699 * Collapses the dialog to its minimized state (only the title bar is visible).
14700 * Equivalent to the user clicking the collapse dialog button.
14702 collapse : function(){
14703 if(!this.collapsed){
14704 this.collapsed = true;
14705 this.el.addClass("x-dlg-collapsed");
14706 this.restoreHeight = this.el.getHeight();
14707 this.resizeTo(this.el.getWidth(), this.header.getHeight());
14712 * Expands a collapsed dialog back to its normal state. Equivalent to the user
14713 * clicking the expand dialog button.
14715 expand : function(){
14716 if(this.collapsed){
14717 this.collapsed = false;
14718 this.el.removeClass("x-dlg-collapsed");
14719 this.resizeTo(this.el.getWidth(), this.restoreHeight);
14724 * Reinitializes the tabs component, clearing out old tabs and finding new ones.
14725 * @return {Roo.TabPanel} The tabs component
14727 initTabs : function(){
14728 var tabs = this.getTabs();
14729 while(tabs.getTab(0)){
14732 this.el.select(this.tabTag+'.x-dlg-tab').each(function(el){
14734 tabs.addTab(Roo.id(dom), dom.title);
14742 beforeResize : function(){
14743 this.resizer.minHeight = Math.max(this.minHeight, this.getHeaderFooterHeight(true)+40);
14747 onResize : function(){
14748 this.refreshSize();
14749 this.syncBodyHeight();
14750 this.adjustAssets();
14752 this.fireEvent("resize", this, this.size.width, this.size.height);
14756 onKeyDown : function(e){
14757 if(this.isVisible()){
14758 this.fireEvent("keydown", this, e);
14763 * Resizes the dialog.
14764 * @param {Number} width
14765 * @param {Number} height
14766 * @return {Roo.BasicDialog} this
14768 resizeTo : function(width, height){
14769 this.el.setSize(width, height);
14770 this.size = {width: width, height: height};
14771 this.syncBodyHeight();
14772 if(this.fixedcenter){
14775 if(this.isVisible()){
14776 this.constrainXY();
14777 this.adjustAssets();
14779 this.fireEvent("resize", this, width, height);
14785 * Resizes the dialog to fit the specified content size.
14786 * @param {Number} width
14787 * @param {Number} height
14788 * @return {Roo.BasicDialog} this
14790 setContentSize : function(w, h){
14791 h += this.getHeaderFooterHeight() + this.body.getMargins("tb");
14792 w += this.body.getMargins("lr") + this.bwrap.getMargins("lr") + this.centerBg.getPadding("lr");
14793 //if(!this.el.isBorderBox()){
14794 h += this.body.getPadding("tb") + this.bwrap.getBorderWidth("tb") + this.body.getBorderWidth("tb") + this.el.getBorderWidth("tb");
14795 w += this.body.getPadding("lr") + this.bwrap.getBorderWidth("lr") + this.body.getBorderWidth("lr") + this.bwrap.getPadding("lr") + this.el.getBorderWidth("lr");
14798 h += this.tabs.stripWrap.getHeight() + this.tabs.bodyEl.getMargins("tb") + this.tabs.bodyEl.getPadding("tb");
14799 w += this.tabs.bodyEl.getMargins("lr") + this.tabs.bodyEl.getPadding("lr");
14801 this.resizeTo(w, h);
14806 * Adds a key listener for when this dialog is displayed. This allows you to hook in a function that will be
14807 * executed in response to a particular key being pressed while the dialog is active.
14808 * @param {Number/Array/Object} key Either the numeric key code, array of key codes or an object with the following options:
14809 * {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
14810 * @param {Function} fn The function to call
14811 * @param {Object} scope (optional) The scope of the function
14812 * @return {Roo.BasicDialog} this
14814 addKeyListener : function(key, fn, scope){
14815 var keyCode, shift, ctrl, alt;
14816 if(typeof key == "object" && !(key instanceof Array)){
14817 keyCode = key["key"];
14818 shift = key["shift"];
14819 ctrl = key["ctrl"];
14824 var handler = function(dlg, e){
14825 if((!shift || e.shiftKey) && (!ctrl || e.ctrlKey) && (!alt || e.altKey)){
14826 var k = e.getKey();
14827 if(keyCode instanceof Array){
14828 for(var i = 0, len = keyCode.length; i < len; i++){
14829 if(keyCode[i] == k){
14830 fn.call(scope || window, dlg, k, e);
14836 fn.call(scope || window, dlg, k, e);
14841 this.on("keydown", handler);
14846 * Returns the TabPanel component (creates it if it doesn't exist).
14847 * Note: If you wish to simply check for the existence of tabs without creating them,
14848 * check for a null 'tabs' property.
14849 * @return {Roo.TabPanel} The tabs component
14851 getTabs : function(){
14853 this.el.addClass("x-dlg-auto-tabs");
14854 this.body.addClass(this.tabPosition == "bottom" ? "x-tabs-bottom" : "x-tabs-top");
14855 this.tabs = new Roo.TabPanel(this.body.dom, this.tabPosition == "bottom");
14861 * Adds a button to the footer section of the dialog.
14862 * @param {String/Object} config A string becomes the button text, an object can either be a Button config
14863 * object or a valid Roo.DomHelper element config
14864 * @param {Function} handler The function called when the button is clicked
14865 * @param {Object} scope (optional) The scope of the handler function (accepts position as a property)
14866 * @return {Roo.Button} The new button
14868 addButton : function(config, handler, scope){
14869 var dh = Roo.DomHelper;
14871 this.footer = dh.append(this.bwrap, {tag: "div", cls:"x-dlg-ft"}, true);
14873 if(!this.btnContainer){
14874 var tb = this.footer.createChild({
14876 cls:"x-dlg-btns x-dlg-btns-"+this.buttonAlign,
14877 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
14879 this.btnContainer = tb.firstChild.firstChild.firstChild;
14884 minWidth: this.minButtonWidth,
14887 if(typeof config == "string"){
14888 bconfig.text = config;
14891 bconfig.dhconfig = config;
14893 Roo.apply(bconfig, config);
14897 if ((typeof(bconfig.position) != 'undefined') && bconfig.position < this.btnContainer.childNodes.length-1) {
14898 bconfig.position = Math.max(0, bconfig.position);
14899 fc = this.btnContainer.childNodes[bconfig.position];
14902 var btn = new Roo.Button(
14904 this.btnContainer.insertBefore(document.createElement("td"),fc)
14905 : this.btnContainer.appendChild(document.createElement("td")),
14906 //Roo.get(this.btnContainer).createChild( { tag: 'td'}, fc ),
14909 this.syncBodyHeight();
14912 * Array of all the buttons that have been added to this dialog via addButton
14917 this.buttons.push(btn);
14922 * Sets the default button to be focused when the dialog is displayed.
14923 * @param {Roo.BasicDialog.Button} btn The button object returned by {@link #addButton}
14924 * @return {Roo.BasicDialog} this
14926 setDefaultButton : function(btn){
14927 this.defaultButton = btn;
14932 getHeaderFooterHeight : function(safe){
14935 height += this.header.getHeight();
14938 var fm = this.footer.getMargins();
14939 height += (this.footer.getHeight()+fm.top+fm.bottom);
14941 height += this.bwrap.getPadding("tb")+this.bwrap.getBorderWidth("tb");
14942 height += this.centerBg.getPadding("tb");
14947 syncBodyHeight : function()
14949 var bd = this.body, // the text
14950 cb = this.centerBg, // wrapper around bottom.. but does not seem to be used..
14952 var height = this.size.height - this.getHeaderFooterHeight(false);
14953 bd.setHeight(height-bd.getMargins("tb"));
14954 var hh = this.header.getHeight();
14955 var h = this.size.height-hh;
14958 bw.setLeftTop(cb.getPadding("l"), hh+cb.getPadding("t"));
14959 bw.setHeight(h-cb.getPadding("tb"));
14961 bw.setWidth(this.el.getWidth(true)-cb.getPadding("lr"));
14962 bd.setWidth(bw.getWidth(true));
14964 this.tabs.syncHeight();
14966 this.tabs.el.repaint();
14972 * Restores the previous state of the dialog if Roo.state is configured.
14973 * @return {Roo.BasicDialog} this
14975 restoreState : function(){
14976 var box = Roo.state.Manager.get(this.stateId || (this.el.id + "-state"));
14977 if(box && box.width){
14978 this.xy = [box.x, box.y];
14979 this.resizeTo(box.width, box.height);
14985 beforeShow : function(){
14987 if(this.fixedcenter){
14988 this.xy = this.el.getCenterXY(true);
14991 Roo.get(document.body).addClass("x-body-masked");
14992 this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
14995 this.constrainXY();
14999 animShow : function(){
15000 var b = Roo.get(this.animateTarget).getBox();
15001 this.proxy.setSize(b.width, b.height);
15002 this.proxy.setLocation(b.x, b.y);
15004 this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height,
15005 true, .35, this.showEl.createDelegate(this));
15009 * Shows the dialog.
15010 * @param {String/HTMLElement/Roo.Element} animateTarget (optional) Reset the animation target
15011 * @return {Roo.BasicDialog} this
15013 show : function(animateTarget){
15014 if (this.fireEvent("beforeshow", this) === false){
15017 if(this.syncHeightBeforeShow){
15018 this.syncBodyHeight();
15019 }else if(this.firstShow){
15020 this.firstShow = false;
15021 this.syncBodyHeight(); // sync the height on the first show instead of in the constructor
15023 this.animateTarget = animateTarget || this.animateTarget;
15024 if(!this.el.isVisible()){
15026 if(this.animateTarget && Roo.get(this.animateTarget)){
15036 showEl : function(){
15038 this.el.setXY(this.xy);
15040 this.adjustAssets(true);
15043 // IE peekaboo bug - fix found by Dave Fenwick
15047 this.fireEvent("show", this);
15051 * Focuses the dialog. If a defaultButton is set, it will receive focus, otherwise the
15052 * dialog itself will receive focus.
15054 focus : function(){
15055 if(this.defaultButton){
15056 this.defaultButton.focus();
15058 this.focusEl.focus();
15063 constrainXY : function(){
15064 if(this.constraintoviewport !== false){
15065 if(!this.viewSize){
15066 if(this.container){
15067 var s = this.container.getSize();
15068 this.viewSize = [s.width, s.height];
15070 this.viewSize = [Roo.lib.Dom.getViewWidth(),Roo.lib.Dom.getViewHeight()];
15073 var s = Roo.get(this.container||document).getScroll();
15075 var x = this.xy[0], y = this.xy[1];
15076 var w = this.size.width, h = this.size.height;
15077 var vw = this.viewSize[0], vh = this.viewSize[1];
15078 // only move it if it needs it
15080 // first validate right/bottom
15081 if(x + w > vw+s.left){
15085 if(y + h > vh+s.top){
15089 // then make sure top/left isn't negative
15101 if(this.isVisible()){
15102 this.el.setLocation(x, y);
15103 this.adjustAssets();
15110 onDrag : function(){
15111 if(!this.proxyDrag){
15112 this.xy = this.el.getXY();
15113 this.adjustAssets();
15118 adjustAssets : function(doShow){
15119 var x = this.xy[0], y = this.xy[1];
15120 var w = this.size.width, h = this.size.height;
15121 if(doShow === true){
15123 this.shadow.show(this.el);
15129 if(this.shadow && this.shadow.isVisible()){
15130 this.shadow.show(this.el);
15132 if(this.shim && this.shim.isVisible()){
15133 this.shim.setBounds(x, y, w, h);
15138 adjustViewport : function(w, h){
15140 w = Roo.lib.Dom.getViewWidth();
15141 h = Roo.lib.Dom.getViewHeight();
15144 this.viewSize = [w, h];
15145 if(this.modal && this.mask.isVisible()){
15146 this.mask.setSize(w, h); // first make sure the mask isn't causing overflow
15147 this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
15149 if(this.isVisible()){
15150 this.constrainXY();
15155 * Destroys this dialog and all its supporting elements (including any tabs, shim,
15156 * shadow, proxy, mask, etc.) Also removes all event listeners.
15157 * @param {Boolean} removeEl (optional) true to remove the element from the DOM
15159 destroy : function(removeEl){
15160 if(this.isVisible()){
15161 this.animateTarget = null;
15164 Roo.EventManager.removeResizeListener(this.adjustViewport, this);
15166 this.tabs.destroy(removeEl);
15179 for(var i = 0, len = this.buttons.length; i < len; i++){
15180 this.buttons[i].destroy();
15183 this.el.removeAllListeners();
15184 if(removeEl === true){
15185 this.el.update("");
15188 Roo.DialogManager.unregister(this);
15192 startMove : function(){
15193 if(this.proxyDrag){
15196 if(this.constraintoviewport !== false){
15197 this.dd.constrainTo(document.body, {right: this.shadowOffset, bottom: this.shadowOffset});
15202 endMove : function(){
15203 if(!this.proxyDrag){
15204 Roo.dd.DD.prototype.endDrag.apply(this.dd, arguments);
15206 Roo.dd.DDProxy.prototype.endDrag.apply(this.dd, arguments);
15209 this.refreshSize();
15210 this.adjustAssets();
15212 this.fireEvent("move", this, this.xy[0], this.xy[1]);
15216 * Brings this dialog to the front of any other visible dialogs
15217 * @return {Roo.BasicDialog} this
15219 toFront : function(){
15220 Roo.DialogManager.bringToFront(this);
15225 * Sends this dialog to the back (under) of any other visible dialogs
15226 * @return {Roo.BasicDialog} this
15228 toBack : function(){
15229 Roo.DialogManager.sendToBack(this);
15234 * Centers this dialog in the viewport
15235 * @return {Roo.BasicDialog} this
15237 center : function(){
15238 var xy = this.el.getCenterXY(true);
15239 this.moveTo(xy[0], xy[1]);
15244 * Moves the dialog's top-left corner to the specified point
15245 * @param {Number} x
15246 * @param {Number} y
15247 * @return {Roo.BasicDialog} this
15249 moveTo : function(x, y){
15251 if(this.isVisible()){
15252 this.el.setXY(this.xy);
15253 this.adjustAssets();
15259 * Aligns the dialog to the specified element
15260 * @param {String/HTMLElement/Roo.Element} element The element to align to.
15261 * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details).
15262 * @param {Array} offsets (optional) Offset the positioning by [x, y]
15263 * @return {Roo.BasicDialog} this
15265 alignTo : function(element, position, offsets){
15266 this.xy = this.el.getAlignToXY(element, position, offsets);
15267 if(this.isVisible()){
15268 this.el.setXY(this.xy);
15269 this.adjustAssets();
15275 * Anchors an element to another element and realigns it when the window is resized.
15276 * @param {String/HTMLElement/Roo.Element} element The element to align to.
15277 * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details)
15278 * @param {Array} offsets (optional) Offset the positioning by [x, y]
15279 * @param {Boolean/Number} monitorScroll (optional) true to monitor body scroll and reposition. If this parameter
15280 * is a number, it is used as the buffer delay (defaults to 50ms).
15281 * @return {Roo.BasicDialog} this
15283 anchorTo : function(el, alignment, offsets, monitorScroll){
15284 var action = function(){
15285 this.alignTo(el, alignment, offsets);
15287 Roo.EventManager.onWindowResize(action, this);
15288 var tm = typeof monitorScroll;
15289 if(tm != 'undefined'){
15290 Roo.EventManager.on(window, 'scroll', action, this,
15291 {buffer: tm == 'number' ? monitorScroll : 50});
15298 * Returns true if the dialog is visible
15299 * @return {Boolean}
15301 isVisible : function(){
15302 return this.el.isVisible();
15306 animHide : function(callback){
15307 var b = Roo.get(this.animateTarget).getBox();
15309 this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height);
15311 this.proxy.setBounds(b.x, b.y, b.width, b.height, true, .35,
15312 this.hideEl.createDelegate(this, [callback]));
15316 * Hides the dialog.
15317 * @param {Function} callback (optional) Function to call when the dialog is hidden
15318 * @return {Roo.BasicDialog} this
15320 hide : function(callback){
15321 if (this.fireEvent("beforehide", this) === false){
15325 this.shadow.hide();
15330 // sometimes animateTarget seems to get set.. causing problems...
15331 // this just double checks..
15332 if(this.animateTarget && Roo.get(this.animateTarget)) {
15333 this.animHide(callback);
15336 this.hideEl(callback);
15342 hideEl : function(callback){
15346 Roo.get(document.body).removeClass("x-body-masked");
15348 this.fireEvent("hide", this);
15349 if(typeof callback == "function"){
15355 hideAction : function(){
15356 this.setLeft("-10000px");
15357 this.setTop("-10000px");
15358 this.setStyle("visibility", "hidden");
15362 refreshSize : function(){
15363 this.size = this.el.getSize();
15364 this.xy = this.el.getXY();
15365 Roo.state.Manager.set(this.stateId || this.el.id + "-state", this.el.getBox());
15369 // z-index is managed by the DialogManager and may be overwritten at any time
15370 setZIndex : function(index){
15372 this.mask.setStyle("z-index", index);
15375 this.shim.setStyle("z-index", ++index);
15378 this.shadow.setZIndex(++index);
15380 this.el.setStyle("z-index", ++index);
15382 this.proxy.setStyle("z-index", ++index);
15385 this.resizer.proxy.setStyle("z-index", ++index);
15388 this.lastZIndex = index;
15392 * Returns the element for this dialog
15393 * @return {Roo.Element} The underlying dialog Element
15395 getEl : function(){
15401 * @class Roo.DialogManager
15402 * Provides global access to BasicDialogs that have been created and
15403 * support for z-indexing (layering) multiple open dialogs.
15405 Roo.DialogManager = function(){
15407 var accessList = [];
15411 var sortDialogs = function(d1, d2){
15412 return (!d1._lastAccess || d1._lastAccess < d2._lastAccess) ? -1 : 1;
15416 var orderDialogs = function(){
15417 accessList.sort(sortDialogs);
15418 var seed = Roo.DialogManager.zseed;
15419 for(var i = 0, len = accessList.length; i < len; i++){
15420 var dlg = accessList[i];
15422 dlg.setZIndex(seed + (i*10));
15429 * The starting z-index for BasicDialogs (defaults to 9000)
15430 * @type Number The z-index value
15435 register : function(dlg){
15436 list[dlg.id] = dlg;
15437 accessList.push(dlg);
15441 unregister : function(dlg){
15442 delete list[dlg.id];
15445 if(!accessList.indexOf){
15446 for( i = 0, len = accessList.length; i < len; i++){
15447 if(accessList[i] == dlg){
15448 accessList.splice(i, 1);
15453 i = accessList.indexOf(dlg);
15455 accessList.splice(i, 1);
15461 * Gets a registered dialog by id
15462 * @param {String/Object} id The id of the dialog or a dialog
15463 * @return {Roo.BasicDialog} this
15465 get : function(id){
15466 return typeof id == "object" ? id : list[id];
15470 * Brings the specified dialog to the front
15471 * @param {String/Object} dlg The id of the dialog or a dialog
15472 * @return {Roo.BasicDialog} this
15474 bringToFront : function(dlg){
15475 dlg = this.get(dlg);
15478 dlg._lastAccess = new Date().getTime();
15485 * Sends the specified dialog to the back
15486 * @param {String/Object} dlg The id of the dialog or a dialog
15487 * @return {Roo.BasicDialog} this
15489 sendToBack : function(dlg){
15490 dlg = this.get(dlg);
15491 dlg._lastAccess = -(new Date().getTime());
15497 * Hides all dialogs
15499 hideAll : function(){
15500 for(var id in list){
15501 if(list[id] && typeof list[id] != "function" && list[id].isVisible()){
15510 * @class Roo.LayoutDialog
15511 * @extends Roo.BasicDialog
15512 * Dialog which provides adjustments for working with a layout in a Dialog.
15513 * Add your necessary layout config options to the dialog's config.<br>
15514 * Example usage (including a nested layout):
15517 dialog = new Roo.LayoutDialog("download-dlg", {
15526 // layout config merges with the dialog config
15528 tabPosition: "top",
15529 alwaysShowTabs: true
15532 dialog.addKeyListener(27, dialog.hide, dialog);
15533 dialog.setDefaultButton(dialog.addButton("Close", dialog.hide, dialog));
15534 dialog.addButton("Build It!", this.getDownload, this);
15536 // we can even add nested layouts
15537 var innerLayout = new Roo.BorderLayout("dl-inner", {
15547 innerLayout.beginUpdate();
15548 innerLayout.add("east", new Roo.ContentPanel("dl-details"));
15549 innerLayout.add("center", new Roo.ContentPanel("selection-panel"));
15550 innerLayout.endUpdate(true);
15552 var layout = dialog.getLayout();
15553 layout.beginUpdate();
15554 layout.add("center", new Roo.ContentPanel("standard-panel",
15555 {title: "Download the Source", fitToFrame:true}));
15556 layout.add("center", new Roo.NestedLayoutPanel(innerLayout,
15557 {title: "Build your own roo.js"}));
15558 layout.getRegion("center").showPanel(sp);
15559 layout.endUpdate();
15563 * @param {String/HTMLElement/Roo.Element} el The id of or container element, or config
15564 * @param {Object} config configuration options
15566 Roo.LayoutDialog = function(el, cfg){
15569 if (typeof(cfg) == 'undefined') {
15570 config = Roo.apply({}, el);
15571 // not sure why we use documentElement here.. - it should always be body.
15572 // IE7 borks horribly if we use documentElement.
15573 // webkit also does not like documentElement - it creates a body element...
15574 el = Roo.get( document.body || document.documentElement ).createChild();
15575 //config.autoCreate = true;
15579 config.autoTabs = false;
15580 Roo.LayoutDialog.superclass.constructor.call(this, el, config);
15581 this.body.setStyle({overflow:"hidden", position:"relative"});
15582 this.layout = new Roo.BorderLayout(this.body.dom, config);
15583 this.layout.monitorWindowResize = false;
15584 this.el.addClass("x-dlg-auto-layout");
15585 // fix case when center region overwrites center function
15586 this.center = Roo.BasicDialog.prototype.center;
15587 this.on("show", this.layout.layout, this.layout, true);
15588 if (config.items) {
15589 var xitems = config.items;
15590 delete config.items;
15591 Roo.each(xitems, this.addxtype, this);
15596 Roo.extend(Roo.LayoutDialog, Roo.BasicDialog, {
15598 * Ends update of the layout <strike>and resets display to none</strike>. Use standard beginUpdate/endUpdate on the layout.
15601 endUpdate : function(){
15602 this.layout.endUpdate();
15606 * Begins an update of the layout <strike>and sets display to block and visibility to hidden</strike>. Use standard beginUpdate/endUpdate on the layout.
15609 beginUpdate : function(){
15610 this.layout.beginUpdate();
15614 * Get the BorderLayout for this dialog
15615 * @return {Roo.BorderLayout}
15617 getLayout : function(){
15618 return this.layout;
15621 showEl : function(){
15622 Roo.LayoutDialog.superclass.showEl.apply(this, arguments);
15624 this.layout.layout();
15629 // Use the syncHeightBeforeShow config option to control this automatically
15630 syncBodyHeight : function(){
15631 Roo.LayoutDialog.superclass.syncBodyHeight.call(this);
15632 if(this.layout){this.layout.layout();}
15636 * Add an xtype element (actually adds to the layout.)
15637 * @return {Object} xdata xtype object data.
15640 addxtype : function(c) {
15641 return this.layout.addxtype(c);
15645 * Ext JS Library 1.1.1
15646 * Copyright(c) 2006-2007, Ext JS, LLC.
15648 * Originally Released Under LGPL - original licence link has changed is not relivant.
15651 * <script type="text/javascript">
15655 * @class Roo.MessageBox
15656 * Utility class for generating different styles of message boxes. The alias Roo.Msg can also be used.
15660 Roo.Msg.alert('Status', 'Changes saved successfully.');
15662 // Prompt for user data:
15663 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
15665 // process text value...
15669 // Show a dialog using config options:
15671 title:'Save Changes?',
15672 msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
15673 buttons: Roo.Msg.YESNOCANCEL,
15680 Roo.MessageBox = function(){
15681 var dlg, opt, mask, waitTimer;
15682 var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
15683 var buttons, activeTextEl, bwidth;
15686 var handleButton = function(button){
15688 Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
15692 var handleHide = function(){
15693 if(opt && opt.cls){
15694 dlg.el.removeClass(opt.cls);
15697 Roo.TaskMgr.stop(waitTimer);
15703 var updateButtons = function(b){
15706 buttons["ok"].hide();
15707 buttons["cancel"].hide();
15708 buttons["yes"].hide();
15709 buttons["no"].hide();
15710 dlg.footer.dom.style.display = 'none';
15713 dlg.footer.dom.style.display = '';
15714 for(var k in buttons){
15715 if(typeof buttons[k] != "function"){
15718 buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.MessageBox.buttonText[k]);
15719 width += buttons[k].el.getWidth()+15;
15729 var handleEsc = function(d, k, e){
15730 if(opt && opt.closable !== false){
15740 * Returns a reference to the underlying {@link Roo.BasicDialog} element
15741 * @return {Roo.BasicDialog} The BasicDialog element
15743 getDialog : function(){
15745 dlg = new Roo.BasicDialog("x-msg-box", {
15750 constraintoviewport:false,
15752 collapsible : false,
15755 width:400, height:100,
15756 buttonAlign:"center",
15757 closeClick : function(){
15758 if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
15759 handleButton("no");
15761 handleButton("cancel");
15765 dlg.on("hide", handleHide);
15767 dlg.addKeyListener(27, handleEsc);
15769 var bt = this.buttonText;
15770 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
15771 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
15772 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
15773 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
15774 bodyEl = dlg.body.createChild({
15776 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>'
15778 msgEl = bodyEl.dom.firstChild;
15779 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
15780 textboxEl.enableDisplayMode();
15781 textboxEl.addKeyListener([10,13], function(){
15782 if(dlg.isVisible() && opt && opt.buttons){
15783 if(opt.buttons.ok){
15784 handleButton("ok");
15785 }else if(opt.buttons.yes){
15786 handleButton("yes");
15790 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
15791 textareaEl.enableDisplayMode();
15792 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
15793 progressEl.enableDisplayMode();
15794 var pf = progressEl.dom.firstChild;
15796 pp = Roo.get(pf.firstChild);
15797 pp.setHeight(pf.offsetHeight);
15805 * Updates the message box body text
15806 * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
15807 * the XHTML-compliant non-breaking space character '&#160;')
15808 * @return {Roo.MessageBox} This message box
15810 updateText : function(text){
15811 if(!dlg.isVisible() && !opt.width){
15812 dlg.resizeTo(this.maxWidth, 100); // resize first so content is never clipped from previous shows
15814 msgEl.innerHTML = text || ' ';
15816 var cw = Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
15817 //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
15819 Math.min(opt.width || cw , this.maxWidth),
15820 Math.max(opt.minWidth || this.minWidth, bwidth)
15823 activeTextEl.setWidth(w);
15825 if(dlg.isVisible()){
15826 dlg.fixedcenter = false;
15828 // to big, make it scroll. = But as usual stupid IE does not support
15831 if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
15832 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
15833 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
15835 bodyEl.dom.style.height = '';
15836 bodyEl.dom.style.overflowY = '';
15839 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
15841 bodyEl.dom.style.overflowX = '';
15844 dlg.setContentSize(w, bodyEl.getHeight());
15845 if(dlg.isVisible()){
15846 dlg.fixedcenter = true;
15852 * Updates a progress-style message box's text and progress bar. Only relevant on message boxes
15853 * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
15854 * @param {Number} value Any number between 0 and 1 (e.g., .5)
15855 * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
15856 * @return {Roo.MessageBox} This message box
15858 updateProgress : function(value, text){
15860 this.updateText(text);
15862 if (pp) { // weird bug on my firefox - for some reason this is not defined
15863 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
15869 * Returns true if the message box is currently displayed
15870 * @return {Boolean} True if the message box is visible, else false
15872 isVisible : function(){
15873 return dlg && dlg.isVisible();
15877 * Hides the message box if it is displayed
15880 if(this.isVisible()){
15886 * Displays a new message box, or reinitializes an existing message box, based on the config options
15887 * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
15888 * The following config object properties are supported:
15890 Property Type Description
15891 ---------- --------------- ------------------------------------------------------------------------------------
15892 animEl String/Element An id or Element from which the message box should animate as it opens and
15893 closes (defaults to undefined)
15894 buttons Object/Boolean A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
15895 cancel:'Bar'}), or false to not show any buttons (defaults to false)
15896 closable Boolean False to hide the top-right close button (defaults to true). Note that
15897 progress and wait dialogs will ignore this property and always hide the
15898 close button as they can only be closed programmatically.
15899 cls String A custom CSS class to apply to the message box element
15900 defaultTextHeight Number The default height in pixels of the message box's multiline textarea if
15901 displayed (defaults to 75)
15902 fn Function A callback function to execute after closing the dialog. The arguments to the
15903 function will be btn (the name of the button that was clicked, if applicable,
15904 e.g. "ok"), and text (the value of the active text field, if applicable).
15905 Progress and wait dialogs will ignore this option since they do not respond to
15906 user actions and can only be closed programmatically, so any required function
15907 should be called by the same code after it closes the dialog.
15908 icon String A CSS class that provides a background image to be used as an icon for
15909 the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
15910 maxWidth Number The maximum width in pixels of the message box (defaults to 600)
15911 minWidth Number The minimum width in pixels of the message box (defaults to 100)
15912 modal Boolean False to allow user interaction with the page while the message box is
15913 displayed (defaults to true)
15914 msg String A string that will replace the existing message box body text (defaults
15915 to the XHTML-compliant non-breaking space character ' ')
15916 multiline Boolean True to prompt the user to enter multi-line text (defaults to false)
15917 progress Boolean True to display a progress bar (defaults to false)
15918 progressText String The text to display inside the progress bar if progress = true (defaults to '')
15919 prompt Boolean True to prompt the user to enter single-line text (defaults to false)
15920 proxyDrag Boolean True to display a lightweight proxy while dragging (defaults to false)
15921 title String The title text
15922 value String The string value to set into the active textbox element if displayed
15923 wait Boolean True to display a progress bar (defaults to false)
15924 width Number The width of the dialog in pixels
15931 msg: 'Please enter your address:',
15933 buttons: Roo.MessageBox.OKCANCEL,
15936 animEl: 'addAddressBtn'
15939 * @param {Object} config Configuration options
15940 * @return {Roo.MessageBox} This message box
15942 show : function(options)
15945 // this causes nightmares if you show one dialog after another
15946 // especially on callbacks..
15948 if(this.isVisible()){
15951 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
15952 Roo.log("Old Dialog Message:" + msgEl.innerHTML );
15953 Roo.log("New Dialog Message:" + options.msg )
15954 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
15955 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
15958 var d = this.getDialog();
15960 d.setTitle(opt.title || " ");
15961 d.close.setDisplayed(opt.closable !== false);
15962 activeTextEl = textboxEl;
15963 opt.prompt = opt.prompt || (opt.multiline ? true : false);
15968 textareaEl.setHeight(typeof opt.multiline == "number" ?
15969 opt.multiline : this.defaultTextHeight);
15970 activeTextEl = textareaEl;
15979 progressEl.setDisplayed(opt.progress === true);
15980 this.updateProgress(0);
15981 activeTextEl.dom.value = opt.value || "";
15983 dlg.setDefaultButton(activeTextEl);
15985 var bs = opt.buttons;
15988 db = buttons["ok"];
15989 }else if(bs && bs.yes){
15990 db = buttons["yes"];
15992 dlg.setDefaultButton(db);
15994 bwidth = updateButtons(opt.buttons);
15995 this.updateText(opt.msg);
15997 d.el.addClass(opt.cls);
15999 d.proxyDrag = opt.proxyDrag === true;
16000 d.modal = opt.modal !== false;
16001 d.mask = opt.modal !== false ? mask : false;
16002 if(!d.isVisible()){
16003 // force it to the end of the z-index stack so it gets a cursor in FF
16004 document.body.appendChild(dlg.el.dom);
16005 d.animateTarget = null;
16006 d.show(options.animEl);
16012 * Displays a message box with a progress bar. This message box has no buttons and is not closeable by
16013 * the user. You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
16014 * and closing the message box when the process is complete.
16015 * @param {String} title The title bar text
16016 * @param {String} msg The message box body text
16017 * @return {Roo.MessageBox} This message box
16019 progress : function(title, msg){
16026 minWidth: this.minProgressWidth,
16033 * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
16034 * If a callback function is passed it will be called after the user clicks the button, and the
16035 * id of the button that was clicked will be passed as the only parameter to the callback
16036 * (could also be the top-right close button).
16037 * @param {String} title The title bar text
16038 * @param {String} msg The message box body text
16039 * @param {Function} fn (optional) The callback function invoked after the message box is closed
16040 * @param {Object} scope (optional) The scope of the callback function
16041 * @return {Roo.MessageBox} This message box
16043 alert : function(title, msg, fn, scope){
16056 * Displays a message box with an infinitely auto-updating progress bar. This can be used to block user
16057 * interaction while waiting for a long-running process to complete that does not have defined intervals.
16058 * You are responsible for closing the message box when the process is complete.
16059 * @param {String} msg The message box body text
16060 * @param {String} title (optional) The title bar text
16061 * @return {Roo.MessageBox} This message box
16063 wait : function(msg, title){
16074 waitTimer = Roo.TaskMgr.start({
16076 Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
16084 * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
16085 * If a callback function is passed it will be called after the user clicks either button, and the id of the
16086 * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
16087 * @param {String} title The title bar text
16088 * @param {String} msg The message box body text
16089 * @param {Function} fn (optional) The callback function invoked after the message box is closed
16090 * @param {Object} scope (optional) The scope of the callback function
16091 * @return {Roo.MessageBox} This message box
16093 confirm : function(title, msg, fn, scope){
16097 buttons: this.YESNO,
16106 * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
16107 * JavaScript's Window.prompt). The prompt can be a single-line or multi-line textbox. If a callback function
16108 * is passed it will be called after the user clicks either button, and the id of the button that was clicked
16109 * (could also be the top-right close button) and the text that was entered will be passed as the two
16110 * parameters to the callback.
16111 * @param {String} title The title bar text
16112 * @param {String} msg The message box body text
16113 * @param {Function} fn (optional) The callback function invoked after the message box is closed
16114 * @param {Object} scope (optional) The scope of the callback function
16115 * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
16116 * property, or the height in pixels to create the textbox (defaults to false / single-line)
16117 * @return {Roo.MessageBox} This message box
16119 prompt : function(title, msg, fn, scope, multiline){
16123 buttons: this.OKCANCEL,
16128 multiline: multiline,
16135 * Button config that displays a single OK button
16140 * Button config that displays Yes and No buttons
16143 YESNO : {yes:true, no:true},
16145 * Button config that displays OK and Cancel buttons
16148 OKCANCEL : {ok:true, cancel:true},
16150 * Button config that displays Yes, No and Cancel buttons
16153 YESNOCANCEL : {yes:true, no:true, cancel:true},
16156 * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
16159 defaultTextHeight : 75,
16161 * The maximum width in pixels of the message box (defaults to 600)
16166 * The minimum width in pixels of the message box (defaults to 100)
16171 * The minimum width in pixels of the message box if it is a progress-style dialog. This is useful
16172 * for setting a different minimum width than text-only dialogs may need (defaults to 250)
16175 minProgressWidth : 250,
16177 * An object containing the default button text strings that can be overriden for localized language support.
16178 * Supported properties are: ok, cancel, yes and no.
16179 * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
16192 * Shorthand for {@link Roo.MessageBox}
16194 Roo.Msg = Roo.MessageBox;/*
16196 * Ext JS Library 1.1.1
16197 * Copyright(c) 2006-2007, Ext JS, LLC.
16199 * Originally Released Under LGPL - original licence link has changed is not relivant.
16202 * <script type="text/javascript">
16205 * @class Roo.QuickTips
16206 * Provides attractive and customizable tooltips for any element.
16209 Roo.QuickTips = function(){
16210 var el, tipBody, tipBodyText, tipTitle, tm, cfg, close, tagEls = {}, esc, removeCls = null, bdLeft, bdRight;
16211 var ce, bd, xy, dd;
16212 var visible = false, disabled = true, inited = false;
16213 var showProc = 1, hideProc = 1, dismissProc = 1, locks = [];
16215 var onOver = function(e){
16219 var t = e.getTarget();
16220 if(!t || t.nodeType !== 1 || t == document || t == document.body){
16223 if(ce && t == ce.el){
16224 clearTimeout(hideProc);
16227 if(t && tagEls[t.id]){
16228 tagEls[t.id].el = t;
16229 showProc = show.defer(tm.showDelay, tm, [tagEls[t.id]]);
16232 var ttp, et = Roo.fly(t);
16233 var ns = cfg.namespace;
16234 if(tm.interceptTitles && t.title){
16237 t.removeAttribute("title");
16238 e.preventDefault();
16240 ttp = t.qtip || et.getAttributeNS(ns, cfg.attribute);
16243 showProc = show.defer(tm.showDelay, tm, [{
16246 width: et.getAttributeNS(ns, cfg.width),
16247 autoHide: et.getAttributeNS(ns, cfg.hide) != "user",
16248 title: et.getAttributeNS(ns, cfg.title),
16249 cls: et.getAttributeNS(ns, cfg.cls)
16254 var onOut = function(e){
16255 clearTimeout(showProc);
16256 var t = e.getTarget();
16257 if(t && ce && ce.el == t && (tm.autoHide && ce.autoHide !== false)){
16258 hideProc = setTimeout(hide, tm.hideDelay);
16262 var onMove = function(e){
16268 if(tm.trackMouse && ce){
16273 var onDown = function(e){
16274 clearTimeout(showProc);
16275 clearTimeout(hideProc);
16277 if(tm.hideOnClick){
16280 tm.enable.defer(100, tm);
16285 var getPad = function(){
16286 return 2;//bdLeft.getPadding('l')+bdRight.getPadding('r');
16289 var show = function(o){
16293 clearTimeout(dismissProc);
16295 if(removeCls){ // in case manually hidden
16296 el.removeClass(removeCls);
16300 el.addClass(ce.cls);
16301 removeCls = ce.cls;
16304 tipTitle.update(ce.title);
16307 tipTitle.update('');
16310 el.dom.style.width = tm.maxWidth+'px';
16311 //tipBody.dom.style.width = '';
16312 tipBodyText.update(o.text);
16313 var p = getPad(), w = ce.width;
16315 var td = tipBodyText.dom;
16316 var aw = Math.max(td.offsetWidth, td.clientWidth, td.scrollWidth);
16317 if(aw > tm.maxWidth){
16319 }else if(aw < tm.minWidth){
16325 //tipBody.setWidth(w);
16326 el.setWidth(parseInt(w, 10) + p);
16327 if(ce.autoHide === false){
16328 close.setDisplayed(true);
16333 close.setDisplayed(false);
16339 el.avoidY = xy[1]-18;
16344 el.setStyle("visibility", "visible");
16345 el.fadeIn({callback: afterShow});
16351 var afterShow = function(){
16355 if(tm.autoDismiss && ce.autoHide !== false){
16356 dismissProc = setTimeout(hide, tm.autoDismissDelay);
16361 var hide = function(noanim){
16362 clearTimeout(dismissProc);
16363 clearTimeout(hideProc);
16365 if(el.isVisible()){
16367 if(noanim !== true && tm.animate){
16368 el.fadeOut({callback: afterHide});
16375 var afterHide = function(){
16378 el.removeClass(removeCls);
16385 * @cfg {Number} minWidth
16386 * The minimum width of the quick tip (defaults to 40)
16390 * @cfg {Number} maxWidth
16391 * The maximum width of the quick tip (defaults to 300)
16395 * @cfg {Boolean} interceptTitles
16396 * True to automatically use the element's DOM title value if available (defaults to false)
16398 interceptTitles : false,
16400 * @cfg {Boolean} trackMouse
16401 * True to have the quick tip follow the mouse as it moves over the target element (defaults to false)
16403 trackMouse : false,
16405 * @cfg {Boolean} hideOnClick
16406 * True to hide the quick tip if the user clicks anywhere in the document (defaults to true)
16408 hideOnClick : true,
16410 * @cfg {Number} showDelay
16411 * Delay in milliseconds before the quick tip displays after the mouse enters the target element (defaults to 500)
16415 * @cfg {Number} hideDelay
16416 * Delay in milliseconds before the quick tip hides when autoHide = true (defaults to 200)
16420 * @cfg {Boolean} autoHide
16421 * True to automatically hide the quick tip after the mouse exits the target element (defaults to true).
16422 * Used in conjunction with hideDelay.
16427 * True to automatically hide the quick tip after a set period of time, regardless of the user's actions
16428 * (defaults to true). Used in conjunction with autoDismissDelay.
16430 autoDismiss : true,
16433 * Delay in milliseconds before the quick tip hides when autoDismiss = true (defaults to 5000)
16435 autoDismissDelay : 5000,
16437 * @cfg {Boolean} animate
16438 * True to turn on fade animation. Defaults to false (ClearType/scrollbar flicker issues in IE7).
16443 * @cfg {String} title
16444 * Title text to display (defaults to ''). This can be any valid HTML markup.
16448 * @cfg {String} text
16449 * Body text to display (defaults to ''). This can be any valid HTML markup.
16453 * @cfg {String} cls
16454 * A CSS class to apply to the base quick tip element (defaults to '').
16458 * @cfg {Number} width
16459 * Width in pixels of the quick tip (defaults to auto). Width will be ignored if it exceeds the bounds of
16460 * minWidth or maxWidth.
16465 * Initialize and enable QuickTips for first use. This should be called once before the first attempt to access
16466 * or display QuickTips in a page.
16469 tm = Roo.QuickTips;
16470 cfg = tm.tagConfig;
16472 if(!Roo.isReady){ // allow calling of init() before onReady
16473 Roo.onReady(Roo.QuickTips.init, Roo.QuickTips);
16476 el = new Roo.Layer({cls:"x-tip", shadow:"drop", shim: true, constrain:true, shadowOffset:4});
16477 el.fxDefaults = {stopFx: true};
16478 // maximum custom styling
16479 //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>');
16480 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>');
16481 tipTitle = el.child('h3');
16482 tipTitle.enableDisplayMode("block");
16483 tipBody = el.child('div.x-tip-bd');
16484 tipBodyText = el.child('div.x-tip-bd-inner');
16485 //bdLeft = el.child('div.x-tip-bd-left');
16486 //bdRight = el.child('div.x-tip-bd-right');
16487 close = el.child('div.x-tip-close');
16488 close.enableDisplayMode("block");
16489 close.on("click", hide);
16490 var d = Roo.get(document);
16491 d.on("mousedown", onDown);
16492 d.on("mouseover", onOver);
16493 d.on("mouseout", onOut);
16494 d.on("mousemove", onMove);
16495 esc = d.addKeyListener(27, hide);
16498 dd = el.initDD("default", null, {
16499 onDrag : function(){
16503 dd.setHandleElId(tipTitle.id);
16512 * Configures a new quick tip instance and assigns it to a target element. The following config options
16515 Property Type Description
16516 ---------- --------------------- ------------------------------------------------------------------------
16517 target Element/String/Array An Element, id or array of ids that this quick tip should be tied to
16519 * @param {Object} config The config object
16521 register : function(config){
16522 var cs = config instanceof Array ? config : arguments;
16523 for(var i = 0, len = cs.length; i < len; i++) {
16525 var target = c.target;
16527 if(target instanceof Array){
16528 for(var j = 0, jlen = target.length; j < jlen; j++){
16529 tagEls[target[j]] = c;
16532 tagEls[typeof target == 'string' ? target : Roo.id(target)] = c;
16539 * Removes this quick tip from its element and destroys it.
16540 * @param {String/HTMLElement/Element} el The element from which the quick tip is to be removed.
16542 unregister : function(el){
16543 delete tagEls[Roo.id(el)];
16547 * Enable this quick tip.
16549 enable : function(){
16550 if(inited && disabled){
16552 if(locks.length < 1){
16559 * Disable this quick tip.
16561 disable : function(){
16563 clearTimeout(showProc);
16564 clearTimeout(hideProc);
16565 clearTimeout(dismissProc);
16573 * Returns true if the quick tip is enabled, else false.
16575 isEnabled : function(){
16582 attribute : "qtip",
16592 // backwards compat
16593 Roo.QuickTips.tips = Roo.QuickTips.register;/*
16595 * Ext JS Library 1.1.1
16596 * Copyright(c) 2006-2007, Ext JS, LLC.
16598 * Originally Released Under LGPL - original licence link has changed is not relivant.
16601 * <script type="text/javascript">
16606 * @class Roo.tree.TreePanel
16607 * @extends Roo.data.Tree
16609 * @cfg {Boolean} rootVisible false to hide the root node (defaults to true)
16610 * @cfg {Boolean} lines false to disable tree lines (defaults to true)
16611 * @cfg {Boolean} enableDD true to enable drag and drop
16612 * @cfg {Boolean} enableDrag true to enable just drag
16613 * @cfg {Boolean} enableDrop true to enable just drop
16614 * @cfg {Object} dragConfig Custom config to pass to the {@link Roo.tree.TreeDragZone} instance
16615 * @cfg {Object} dropConfig Custom config to pass to the {@link Roo.tree.TreeDropZone} instance
16616 * @cfg {String} ddGroup The DD group this TreePanel belongs to
16617 * @cfg {String} ddAppendOnly True if the tree should only allow append drops (use for trees which are sorted)
16618 * @cfg {Boolean} ddScroll true to enable YUI body scrolling
16619 * @cfg {Boolean} containerScroll true to register this container with ScrollManager
16620 * @cfg {Boolean} hlDrop false to disable node highlight on drop (defaults to the value of Roo.enableFx)
16621 * @cfg {String} hlColor The color of the node highlight (defaults to C3DAF9)
16622 * @cfg {Boolean} animate true to enable animated expand/collapse (defaults to the value of Roo.enableFx)
16623 * @cfg {Boolean} singleExpand true if only 1 node per branch may be expanded
16624 * @cfg {Boolean} selModel A tree selection model to use with this TreePanel (defaults to a {@link Roo.tree.DefaultSelectionModel})
16625 * @cfg {Boolean} loader A TreeLoader for use with this TreePanel
16626 * @cfg {Object|Roo.tree.TreeEditor} editor The TreeEditor or xtype data to display when clicked.
16627 * @cfg {String} pathSeparator The token used to separate sub-paths in path strings (defaults to '/')
16628 * @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>
16629 * @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>
16632 * @param {String/HTMLElement/Element} el The container element
16633 * @param {Object} config
16635 Roo.tree.TreePanel = function(el, config){
16637 var loader = false;
16639 root = config.root;
16640 delete config.root;
16642 if (config.loader) {
16643 loader = config.loader;
16644 delete config.loader;
16647 Roo.apply(this, config);
16648 Roo.tree.TreePanel.superclass.constructor.call(this);
16649 this.el = Roo.get(el);
16650 this.el.addClass('x-tree');
16651 //console.log(root);
16653 this.setRootNode( Roo.factory(root, Roo.tree));
16656 this.loader = Roo.factory(loader, Roo.tree);
16659 * Read-only. The id of the container element becomes this TreePanel's id.
16661 this.id = this.el.id;
16664 * @event beforeload
16665 * Fires before a node is loaded, return false to cancel
16666 * @param {Node} node The node being loaded
16668 "beforeload" : true,
16671 * Fires when a node is loaded
16672 * @param {Node} node The node that was loaded
16676 * @event textchange
16677 * Fires when the text for a node is changed
16678 * @param {Node} node The node
16679 * @param {String} text The new text
16680 * @param {String} oldText The old text
16682 "textchange" : true,
16684 * @event beforeexpand
16685 * Fires before a node is expanded, return false to cancel.
16686 * @param {Node} node The node
16687 * @param {Boolean} deep
16688 * @param {Boolean} anim
16690 "beforeexpand" : true,
16692 * @event beforecollapse
16693 * Fires before a node is collapsed, return false to cancel.
16694 * @param {Node} node The node
16695 * @param {Boolean} deep
16696 * @param {Boolean} anim
16698 "beforecollapse" : true,
16701 * Fires when a node is expanded
16702 * @param {Node} node The node
16706 * @event disabledchange
16707 * Fires when the disabled status of a node changes
16708 * @param {Node} node The node
16709 * @param {Boolean} disabled
16711 "disabledchange" : true,
16714 * Fires when a node is collapsed
16715 * @param {Node} node The node
16719 * @event beforeclick
16720 * Fires before click processing on a node. Return false to cancel the default action.
16721 * @param {Node} node The node
16722 * @param {Roo.EventObject} e The event object
16724 "beforeclick":true,
16726 * @event checkchange
16727 * Fires when a node with a checkbox's checked property changes
16728 * @param {Node} this This node
16729 * @param {Boolean} checked
16731 "checkchange":true,
16734 * Fires when a node is clicked
16735 * @param {Node} node The node
16736 * @param {Roo.EventObject} e The event object
16741 * Fires when a node is double clicked
16742 * @param {Node} node The node
16743 * @param {Roo.EventObject} e The event object
16747 * @event contextmenu
16748 * Fires when a node is right clicked
16749 * @param {Node} node The node
16750 * @param {Roo.EventObject} e The event object
16752 "contextmenu":true,
16754 * @event beforechildrenrendered
16755 * Fires right before the child nodes for a node are rendered
16756 * @param {Node} node The node
16758 "beforechildrenrendered":true,
16761 * Fires when a node starts being dragged
16762 * @param {Roo.tree.TreePanel} this
16763 * @param {Roo.tree.TreeNode} node
16764 * @param {event} e The raw browser event
16766 "startdrag" : true,
16769 * Fires when a drag operation is complete
16770 * @param {Roo.tree.TreePanel} this
16771 * @param {Roo.tree.TreeNode} node
16772 * @param {event} e The raw browser event
16777 * Fires when a dragged node is dropped on a valid DD target
16778 * @param {Roo.tree.TreePanel} this
16779 * @param {Roo.tree.TreeNode} node
16780 * @param {DD} dd The dd it was dropped on
16781 * @param {event} e The raw browser event
16785 * @event beforenodedrop
16786 * Fires when a DD object is dropped on a node in this tree for preprocessing. Return false to cancel the drop. The dropEvent
16787 * passed to handlers has the following properties:<br />
16788 * <ul style="padding:5px;padding-left:16px;">
16789 * <li>tree - The TreePanel</li>
16790 * <li>target - The node being targeted for the drop</li>
16791 * <li>data - The drag data from the drag source</li>
16792 * <li>point - The point of the drop - append, above or below</li>
16793 * <li>source - The drag source</li>
16794 * <li>rawEvent - Raw mouse event</li>
16795 * <li>dropNode - Drop node(s) provided by the source <b>OR</b> you can supply node(s)
16796 * to be inserted by setting them on this object.</li>
16797 * <li>cancel - Set this to true to cancel the drop.</li>
16799 * @param {Object} dropEvent
16801 "beforenodedrop" : true,
16804 * Fires after a DD object is dropped on a node in this tree. The dropEvent
16805 * passed to handlers has the following properties:<br />
16806 * <ul style="padding:5px;padding-left:16px;">
16807 * <li>tree - The TreePanel</li>
16808 * <li>target - The node being targeted for the drop</li>
16809 * <li>data - The drag data from the drag source</li>
16810 * <li>point - The point of the drop - append, above or below</li>
16811 * <li>source - The drag source</li>
16812 * <li>rawEvent - Raw mouse event</li>
16813 * <li>dropNode - Dropped node(s).</li>
16815 * @param {Object} dropEvent
16819 * @event nodedragover
16820 * Fires when a tree node is being targeted for a drag drop, return false to signal drop not allowed. The dragOverEvent
16821 * passed to handlers has the following properties:<br />
16822 * <ul style="padding:5px;padding-left:16px;">
16823 * <li>tree - The TreePanel</li>
16824 * <li>target - The node being targeted for the drop</li>
16825 * <li>data - The drag data from the drag source</li>
16826 * <li>point - The point of the drop - append, above or below</li>
16827 * <li>source - The drag source</li>
16828 * <li>rawEvent - Raw mouse event</li>
16829 * <li>dropNode - Drop node(s) provided by the source.</li>
16830 * <li>cancel - Set this to true to signal drop not allowed.</li>
16832 * @param {Object} dragOverEvent
16834 "nodedragover" : true
16837 if(this.singleExpand){
16838 this.on("beforeexpand", this.restrictExpand, this);
16841 this.editor.tree = this;
16842 this.editor = Roo.factory(this.editor, Roo.tree);
16845 if (this.selModel) {
16846 this.selModel = Roo.factory(this.selModel, Roo.tree);
16850 Roo.extend(Roo.tree.TreePanel, Roo.data.Tree, {
16851 rootVisible : true,
16852 animate: Roo.enableFx,
16855 hlDrop : Roo.enableFx,
16859 rendererTip: false,
16861 restrictExpand : function(node){
16862 var p = node.parentNode;
16864 if(p.expandedChild && p.expandedChild.parentNode == p){
16865 p.expandedChild.collapse();
16867 p.expandedChild = node;
16871 // private override
16872 setRootNode : function(node){
16873 Roo.tree.TreePanel.superclass.setRootNode.call(this, node);
16874 if(!this.rootVisible){
16875 node.ui = new Roo.tree.RootTreeNodeUI(node);
16881 * Returns the container element for this TreePanel
16883 getEl : function(){
16888 * Returns the default TreeLoader for this TreePanel
16890 getLoader : function(){
16891 return this.loader;
16897 expandAll : function(){
16898 this.root.expand(true);
16902 * Collapse all nodes
16904 collapseAll : function(){
16905 this.root.collapse(true);
16909 * Returns the selection model used by this TreePanel
16911 getSelectionModel : function(){
16912 if(!this.selModel){
16913 this.selModel = new Roo.tree.DefaultSelectionModel();
16915 return this.selModel;
16919 * Retrieve an array of checked nodes, or an array of a specific attribute of checked nodes (e.g. "id")
16920 * @param {String} attribute (optional) Defaults to null (return the actual nodes)
16921 * @param {TreeNode} startNode (optional) The node to start from, defaults to the root
16924 getChecked : function(a, startNode){
16925 startNode = startNode || this.root;
16927 var f = function(){
16928 if(this.attributes.checked){
16929 r.push(!a ? this : (a == 'id' ? this.id : this.attributes[a]));
16932 startNode.cascade(f);
16937 * Expands a specified path in this TreePanel. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
16938 * @param {String} path
16939 * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
16940 * @param {Function} callback (optional) The callback to call when the expand is complete. The callback will be called with
16941 * (bSuccess, oLastNode) where bSuccess is if the expand was successful and oLastNode is the last node that was expanded.
16943 expandPath : function(path, attr, callback){
16944 attr = attr || "id";
16945 var keys = path.split(this.pathSeparator);
16946 var curNode = this.root;
16947 if(curNode.attributes[attr] != keys[1]){ // invalid root
16949 callback(false, null);
16954 var f = function(){
16955 if(++index == keys.length){
16957 callback(true, curNode);
16961 var c = curNode.findChild(attr, keys[index]);
16964 callback(false, curNode);
16969 c.expand(false, false, f);
16971 curNode.expand(false, false, f);
16975 * Selects the node in this tree at the specified path. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
16976 * @param {String} path
16977 * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
16978 * @param {Function} callback (optional) The callback to call when the selection is complete. The callback will be called with
16979 * (bSuccess, oSelNode) where bSuccess is if the selection was successful and oSelNode is the selected node.
16981 selectPath : function(path, attr, callback){
16982 attr = attr || "id";
16983 var keys = path.split(this.pathSeparator);
16984 var v = keys.pop();
16985 if(keys.length > 0){
16986 var f = function(success, node){
16987 if(success && node){
16988 var n = node.findChild(attr, v);
16994 }else if(callback){
16995 callback(false, n);
16999 callback(false, n);
17003 this.expandPath(keys.join(this.pathSeparator), attr, f);
17005 this.root.select();
17007 callback(true, this.root);
17012 getTreeEl : function(){
17017 * Trigger rendering of this TreePanel
17019 render : function(){
17020 if (this.innerCt) {
17021 return this; // stop it rendering more than once!!
17024 this.innerCt = this.el.createChild({tag:"ul",
17025 cls:"x-tree-root-ct " +
17026 (this.lines ? "x-tree-lines" : "x-tree-no-lines")});
17028 if(this.containerScroll){
17029 Roo.dd.ScrollManager.register(this.el);
17031 if((this.enableDD || this.enableDrop) && !this.dropZone){
17033 * The dropZone used by this tree if drop is enabled
17034 * @type Roo.tree.TreeDropZone
17036 this.dropZone = new Roo.tree.TreeDropZone(this, this.dropConfig || {
17037 ddGroup: this.ddGroup || "TreeDD", appendOnly: this.ddAppendOnly === true
17040 if((this.enableDD || this.enableDrag) && !this.dragZone){
17042 * The dragZone used by this tree if drag is enabled
17043 * @type Roo.tree.TreeDragZone
17045 this.dragZone = new Roo.tree.TreeDragZone(this, this.dragConfig || {
17046 ddGroup: this.ddGroup || "TreeDD",
17047 scroll: this.ddScroll
17050 this.getSelectionModel().init(this);
17052 Roo.log("ROOT not set in tree");
17055 this.root.render();
17056 if(!this.rootVisible){
17057 this.root.renderChildren();
17063 * Ext JS Library 1.1.1
17064 * Copyright(c) 2006-2007, Ext JS, LLC.
17066 * Originally Released Under LGPL - original licence link has changed is not relivant.
17069 * <script type="text/javascript">
17074 * @class Roo.tree.DefaultSelectionModel
17075 * @extends Roo.util.Observable
17076 * The default single selection for a TreePanel.
17077 * @param {Object} cfg Configuration
17079 Roo.tree.DefaultSelectionModel = function(cfg){
17080 this.selNode = null;
17086 * @event selectionchange
17087 * Fires when the selected node changes
17088 * @param {DefaultSelectionModel} this
17089 * @param {TreeNode} node the new selection
17091 "selectionchange" : true,
17094 * @event beforeselect
17095 * Fires before the selected node changes, return false to cancel the change
17096 * @param {DefaultSelectionModel} this
17097 * @param {TreeNode} node the new selection
17098 * @param {TreeNode} node the old selection
17100 "beforeselect" : true
17103 Roo.tree.DefaultSelectionModel.superclass.constructor.call(this,cfg);
17106 Roo.extend(Roo.tree.DefaultSelectionModel, Roo.util.Observable, {
17107 init : function(tree){
17109 tree.getTreeEl().on("keydown", this.onKeyDown, this);
17110 tree.on("click", this.onNodeClick, this);
17113 onNodeClick : function(node, e){
17114 if (e.ctrlKey && this.selNode == node) {
17115 this.unselect(node);
17123 * @param {TreeNode} node The node to select
17124 * @return {TreeNode} The selected node
17126 select : function(node){
17127 var last = this.selNode;
17128 if(last != node && this.fireEvent('beforeselect', this, node, last) !== false){
17130 last.ui.onSelectedChange(false);
17132 this.selNode = node;
17133 node.ui.onSelectedChange(true);
17134 this.fireEvent("selectionchange", this, node, last);
17141 * @param {TreeNode} node The node to unselect
17143 unselect : function(node){
17144 if(this.selNode == node){
17145 this.clearSelections();
17150 * Clear all selections
17152 clearSelections : function(){
17153 var n = this.selNode;
17155 n.ui.onSelectedChange(false);
17156 this.selNode = null;
17157 this.fireEvent("selectionchange", this, null);
17163 * Get the selected node
17164 * @return {TreeNode} The selected node
17166 getSelectedNode : function(){
17167 return this.selNode;
17171 * Returns true if the node is selected
17172 * @param {TreeNode} node The node to check
17173 * @return {Boolean}
17175 isSelected : function(node){
17176 return this.selNode == node;
17180 * Selects the node above the selected node in the tree, intelligently walking the nodes
17181 * @return TreeNode The new selection
17183 selectPrevious : function(){
17184 var s = this.selNode || this.lastSelNode;
17188 var ps = s.previousSibling;
17190 if(!ps.isExpanded() || ps.childNodes.length < 1){
17191 return this.select(ps);
17193 var lc = ps.lastChild;
17194 while(lc && lc.isExpanded() && lc.childNodes.length > 0){
17197 return this.select(lc);
17199 } else if(s.parentNode && (this.tree.rootVisible || !s.parentNode.isRoot)){
17200 return this.select(s.parentNode);
17206 * Selects the node above the selected node in the tree, intelligently walking the nodes
17207 * @return TreeNode The new selection
17209 selectNext : function(){
17210 var s = this.selNode || this.lastSelNode;
17214 if(s.firstChild && s.isExpanded()){
17215 return this.select(s.firstChild);
17216 }else if(s.nextSibling){
17217 return this.select(s.nextSibling);
17218 }else if(s.parentNode){
17220 s.parentNode.bubble(function(){
17221 if(this.nextSibling){
17222 newS = this.getOwnerTree().selModel.select(this.nextSibling);
17231 onKeyDown : function(e){
17232 var s = this.selNode || this.lastSelNode;
17233 // undesirable, but required
17238 var k = e.getKey();
17246 this.selectPrevious();
17249 e.preventDefault();
17250 if(s.hasChildNodes()){
17251 if(!s.isExpanded()){
17253 }else if(s.firstChild){
17254 this.select(s.firstChild, e);
17259 e.preventDefault();
17260 if(s.hasChildNodes() && s.isExpanded()){
17262 }else if(s.parentNode && (this.tree.rootVisible || s.parentNode != this.tree.getRootNode())){
17263 this.select(s.parentNode, e);
17271 * @class Roo.tree.MultiSelectionModel
17272 * @extends Roo.util.Observable
17273 * Multi selection for a TreePanel.
17274 * @param {Object} cfg Configuration
17276 Roo.tree.MultiSelectionModel = function(){
17277 this.selNodes = [];
17281 * @event selectionchange
17282 * Fires when the selected nodes change
17283 * @param {MultiSelectionModel} this
17284 * @param {Array} nodes Array of the selected nodes
17286 "selectionchange" : true
17288 Roo.tree.MultiSelectionModel.superclass.constructor.call(this,cfg);
17292 Roo.extend(Roo.tree.MultiSelectionModel, Roo.util.Observable, {
17293 init : function(tree){
17295 tree.getTreeEl().on("keydown", this.onKeyDown, this);
17296 tree.on("click", this.onNodeClick, this);
17299 onNodeClick : function(node, e){
17300 this.select(node, e, e.ctrlKey);
17305 * @param {TreeNode} node The node to select
17306 * @param {EventObject} e (optional) An event associated with the selection
17307 * @param {Boolean} keepExisting True to retain existing selections
17308 * @return {TreeNode} The selected node
17310 select : function(node, e, keepExisting){
17311 if(keepExisting !== true){
17312 this.clearSelections(true);
17314 if(this.isSelected(node)){
17315 this.lastSelNode = node;
17318 this.selNodes.push(node);
17319 this.selMap[node.id] = node;
17320 this.lastSelNode = node;
17321 node.ui.onSelectedChange(true);
17322 this.fireEvent("selectionchange", this, this.selNodes);
17328 * @param {TreeNode} node The node to unselect
17330 unselect : function(node){
17331 if(this.selMap[node.id]){
17332 node.ui.onSelectedChange(false);
17333 var sn = this.selNodes;
17336 index = sn.indexOf(node);
17338 for(var i = 0, len = sn.length; i < len; i++){
17346 this.selNodes.splice(index, 1);
17348 delete this.selMap[node.id];
17349 this.fireEvent("selectionchange", this, this.selNodes);
17354 * Clear all selections
17356 clearSelections : function(suppressEvent){
17357 var sn = this.selNodes;
17359 for(var i = 0, len = sn.length; i < len; i++){
17360 sn[i].ui.onSelectedChange(false);
17362 this.selNodes = [];
17364 if(suppressEvent !== true){
17365 this.fireEvent("selectionchange", this, this.selNodes);
17371 * Returns true if the node is selected
17372 * @param {TreeNode} node The node to check
17373 * @return {Boolean}
17375 isSelected : function(node){
17376 return this.selMap[node.id] ? true : false;
17380 * Returns an array of the selected nodes
17383 getSelectedNodes : function(){
17384 return this.selNodes;
17387 onKeyDown : Roo.tree.DefaultSelectionModel.prototype.onKeyDown,
17389 selectNext : Roo.tree.DefaultSelectionModel.prototype.selectNext,
17391 selectPrevious : Roo.tree.DefaultSelectionModel.prototype.selectPrevious
17394 * Ext JS Library 1.1.1
17395 * Copyright(c) 2006-2007, Ext JS, LLC.
17397 * Originally Released Under LGPL - original licence link has changed is not relivant.
17400 * <script type="text/javascript">
17404 * @class Roo.tree.TreeNode
17405 * @extends Roo.data.Node
17406 * @cfg {String} text The text for this node
17407 * @cfg {Boolean} expanded true to start the node expanded
17408 * @cfg {Boolean} allowDrag false to make this node undraggable if DD is on (defaults to true)
17409 * @cfg {Boolean} allowDrop false if this node cannot be drop on
17410 * @cfg {Boolean} disabled true to start the node disabled
17411 * @cfg {String} icon The path to an icon for the node. The preferred way to do this
17412 * is to use the cls or iconCls attributes and add the icon via a CSS background image.
17413 * @cfg {String} cls A css class to be added to the node
17414 * @cfg {String} iconCls A css class to be added to the nodes icon element for applying css background images
17415 * @cfg {String} href URL of the link used for the node (defaults to #)
17416 * @cfg {String} hrefTarget target frame for the link
17417 * @cfg {String} qtip An Ext QuickTip for the node
17418 * @cfg {String} qtipCfg An Ext QuickTip config for the node (used instead of qtip)
17419 * @cfg {Boolean} singleClickExpand True for single click expand on this node
17420 * @cfg {Function} uiProvider A UI <b>class</b> to use for this node (defaults to Roo.tree.TreeNodeUI)
17421 * @cfg {Boolean} checked True to render a checked checkbox for this node, false to render an unchecked checkbox
17422 * (defaults to undefined with no checkbox rendered)
17424 * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node
17426 Roo.tree.TreeNode = function(attributes){
17427 attributes = attributes || {};
17428 if(typeof attributes == "string"){
17429 attributes = {text: attributes};
17431 this.childrenRendered = false;
17432 this.rendered = false;
17433 Roo.tree.TreeNode.superclass.constructor.call(this, attributes);
17434 this.expanded = attributes.expanded === true;
17435 this.isTarget = attributes.isTarget !== false;
17436 this.draggable = attributes.draggable !== false && attributes.allowDrag !== false;
17437 this.allowChildren = attributes.allowChildren !== false && attributes.allowDrop !== false;
17440 * Read-only. The text for this node. To change it use setText().
17443 this.text = attributes.text;
17445 * True if this node is disabled.
17448 this.disabled = attributes.disabled === true;
17452 * @event textchange
17453 * Fires when the text for this node is changed
17454 * @param {Node} this This node
17455 * @param {String} text The new text
17456 * @param {String} oldText The old text
17458 "textchange" : true,
17460 * @event beforeexpand
17461 * Fires before this node is expanded, return false to cancel.
17462 * @param {Node} this This node
17463 * @param {Boolean} deep
17464 * @param {Boolean} anim
17466 "beforeexpand" : true,
17468 * @event beforecollapse
17469 * Fires before this node is collapsed, return false to cancel.
17470 * @param {Node} this This node
17471 * @param {Boolean} deep
17472 * @param {Boolean} anim
17474 "beforecollapse" : true,
17477 * Fires when this node is expanded
17478 * @param {Node} this This node
17482 * @event disabledchange
17483 * Fires when the disabled status of this node changes
17484 * @param {Node} this This node
17485 * @param {Boolean} disabled
17487 "disabledchange" : true,
17490 * Fires when this node is collapsed
17491 * @param {Node} this This node
17495 * @event beforeclick
17496 * Fires before click processing. Return false to cancel the default action.
17497 * @param {Node} this This node
17498 * @param {Roo.EventObject} e The event object
17500 "beforeclick":true,
17502 * @event checkchange
17503 * Fires when a node with a checkbox's checked property changes
17504 * @param {Node} this This node
17505 * @param {Boolean} checked
17507 "checkchange":true,
17510 * Fires when this node is clicked
17511 * @param {Node} this This node
17512 * @param {Roo.EventObject} e The event object
17517 * Fires when this node is double clicked
17518 * @param {Node} this This node
17519 * @param {Roo.EventObject} e The event object
17523 * @event contextmenu
17524 * Fires when this node is right clicked
17525 * @param {Node} this This node
17526 * @param {Roo.EventObject} e The event object
17528 "contextmenu":true,
17530 * @event beforechildrenrendered
17531 * Fires right before the child nodes for this node are rendered
17532 * @param {Node} this This node
17534 "beforechildrenrendered":true
17537 var uiClass = this.attributes.uiProvider || Roo.tree.TreeNodeUI;
17540 * Read-only. The UI for this node
17543 this.ui = new uiClass(this);
17545 // finally support items[]
17546 if (typeof(this.attributes.items) == 'undefined' || !this.attributes.items) {
17551 Roo.each(this.attributes.items, function(c) {
17552 this.appendChild(Roo.factory(c,Roo.Tree));
17554 delete this.attributes.items;
17559 Roo.extend(Roo.tree.TreeNode, Roo.data.Node, {
17560 preventHScroll: true,
17562 * Returns true if this node is expanded
17563 * @return {Boolean}
17565 isExpanded : function(){
17566 return this.expanded;
17570 * Returns the UI object for this node
17571 * @return {TreeNodeUI}
17573 getUI : function(){
17577 // private override
17578 setFirstChild : function(node){
17579 var of = this.firstChild;
17580 Roo.tree.TreeNode.superclass.setFirstChild.call(this, node);
17581 if(this.childrenRendered && of && node != of){
17582 of.renderIndent(true, true);
17585 this.renderIndent(true, true);
17589 // private override
17590 setLastChild : function(node){
17591 var ol = this.lastChild;
17592 Roo.tree.TreeNode.superclass.setLastChild.call(this, node);
17593 if(this.childrenRendered && ol && node != ol){
17594 ol.renderIndent(true, true);
17597 this.renderIndent(true, true);
17601 // these methods are overridden to provide lazy rendering support
17602 // private override
17603 appendChild : function()
17605 var node = Roo.tree.TreeNode.superclass.appendChild.apply(this, arguments);
17606 if(node && this.childrenRendered){
17609 this.ui.updateExpandIcon();
17613 // private override
17614 removeChild : function(node){
17615 this.ownerTree.getSelectionModel().unselect(node);
17616 Roo.tree.TreeNode.superclass.removeChild.apply(this, arguments);
17617 // if it's been rendered remove dom node
17618 if(this.childrenRendered){
17621 if(this.childNodes.length < 1){
17622 this.collapse(false, false);
17624 this.ui.updateExpandIcon();
17626 if(!this.firstChild) {
17627 this.childrenRendered = false;
17632 // private override
17633 insertBefore : function(node, refNode){
17634 var newNode = Roo.tree.TreeNode.superclass.insertBefore.apply(this, arguments);
17635 if(newNode && refNode && this.childrenRendered){
17638 this.ui.updateExpandIcon();
17643 * Sets the text for this node
17644 * @param {String} text
17646 setText : function(text){
17647 var oldText = this.text;
17649 this.attributes.text = text;
17650 if(this.rendered){ // event without subscribing
17651 this.ui.onTextChange(this, text, oldText);
17653 this.fireEvent("textchange", this, text, oldText);
17657 * Triggers selection of this node
17659 select : function(){
17660 this.getOwnerTree().getSelectionModel().select(this);
17664 * Triggers deselection of this node
17666 unselect : function(){
17667 this.getOwnerTree().getSelectionModel().unselect(this);
17671 * Returns true if this node is selected
17672 * @return {Boolean}
17674 isSelected : function(){
17675 return this.getOwnerTree().getSelectionModel().isSelected(this);
17679 * Expand this node.
17680 * @param {Boolean} deep (optional) True to expand all children as well
17681 * @param {Boolean} anim (optional) false to cancel the default animation
17682 * @param {Function} callback (optional) A callback to be called when
17683 * expanding this node completes (does not wait for deep expand to complete).
17684 * Called with 1 parameter, this node.
17686 expand : function(deep, anim, callback){
17687 if(!this.expanded){
17688 if(this.fireEvent("beforeexpand", this, deep, anim) === false){
17691 if(!this.childrenRendered){
17692 this.renderChildren();
17694 this.expanded = true;
17695 if(!this.isHiddenRoot() && (this.getOwnerTree().animate && anim !== false) || anim){
17696 this.ui.animExpand(function(){
17697 this.fireEvent("expand", this);
17698 if(typeof callback == "function"){
17702 this.expandChildNodes(true);
17704 }.createDelegate(this));
17708 this.fireEvent("expand", this);
17709 if(typeof callback == "function"){
17714 if(typeof callback == "function"){
17719 this.expandChildNodes(true);
17723 isHiddenRoot : function(){
17724 return this.isRoot && !this.getOwnerTree().rootVisible;
17728 * Collapse this node.
17729 * @param {Boolean} deep (optional) True to collapse all children as well
17730 * @param {Boolean} anim (optional) false to cancel the default animation
17732 collapse : function(deep, anim){
17733 if(this.expanded && !this.isHiddenRoot()){
17734 if(this.fireEvent("beforecollapse", this, deep, anim) === false){
17737 this.expanded = false;
17738 if((this.getOwnerTree().animate && anim !== false) || anim){
17739 this.ui.animCollapse(function(){
17740 this.fireEvent("collapse", this);
17742 this.collapseChildNodes(true);
17744 }.createDelegate(this));
17747 this.ui.collapse();
17748 this.fireEvent("collapse", this);
17752 var cs = this.childNodes;
17753 for(var i = 0, len = cs.length; i < len; i++) {
17754 cs[i].collapse(true, false);
17760 delayedExpand : function(delay){
17761 if(!this.expandProcId){
17762 this.expandProcId = this.expand.defer(delay, this);
17767 cancelExpand : function(){
17768 if(this.expandProcId){
17769 clearTimeout(this.expandProcId);
17771 this.expandProcId = false;
17775 * Toggles expanded/collapsed state of the node
17777 toggle : function(){
17786 * Ensures all parent nodes are expanded
17788 ensureVisible : function(callback){
17789 var tree = this.getOwnerTree();
17790 tree.expandPath(this.parentNode.getPath(), false, function(){
17791 tree.getTreeEl().scrollChildIntoView(this.ui.anchor);
17792 Roo.callback(callback);
17793 }.createDelegate(this));
17797 * Expand all child nodes
17798 * @param {Boolean} deep (optional) true if the child nodes should also expand their child nodes
17800 expandChildNodes : function(deep){
17801 var cs = this.childNodes;
17802 for(var i = 0, len = cs.length; i < len; i++) {
17803 cs[i].expand(deep);
17808 * Collapse all child nodes
17809 * @param {Boolean} deep (optional) true if the child nodes should also collapse their child nodes
17811 collapseChildNodes : function(deep){
17812 var cs = this.childNodes;
17813 for(var i = 0, len = cs.length; i < len; i++) {
17814 cs[i].collapse(deep);
17819 * Disables this node
17821 disable : function(){
17822 this.disabled = true;
17824 if(this.rendered && this.ui.onDisableChange){ // event without subscribing
17825 this.ui.onDisableChange(this, true);
17827 this.fireEvent("disabledchange", this, true);
17831 * Enables this node
17833 enable : function(){
17834 this.disabled = false;
17835 if(this.rendered && this.ui.onDisableChange){ // event without subscribing
17836 this.ui.onDisableChange(this, false);
17838 this.fireEvent("disabledchange", this, false);
17842 renderChildren : function(suppressEvent){
17843 if(suppressEvent !== false){
17844 this.fireEvent("beforechildrenrendered", this);
17846 var cs = this.childNodes;
17847 for(var i = 0, len = cs.length; i < len; i++){
17848 cs[i].render(true);
17850 this.childrenRendered = true;
17854 sort : function(fn, scope){
17855 Roo.tree.TreeNode.superclass.sort.apply(this, arguments);
17856 if(this.childrenRendered){
17857 var cs = this.childNodes;
17858 for(var i = 0, len = cs.length; i < len; i++){
17859 cs[i].render(true);
17865 render : function(bulkRender){
17866 this.ui.render(bulkRender);
17867 if(!this.rendered){
17868 this.rendered = true;
17870 this.expanded = false;
17871 this.expand(false, false);
17877 renderIndent : function(deep, refresh){
17879 this.ui.childIndent = null;
17881 this.ui.renderIndent();
17882 if(deep === true && this.childrenRendered){
17883 var cs = this.childNodes;
17884 for(var i = 0, len = cs.length; i < len; i++){
17885 cs[i].renderIndent(true, refresh);
17891 * Ext JS Library 1.1.1
17892 * Copyright(c) 2006-2007, Ext JS, LLC.
17894 * Originally Released Under LGPL - original licence link has changed is not relivant.
17897 * <script type="text/javascript">
17901 * @class Roo.tree.AsyncTreeNode
17902 * @extends Roo.tree.TreeNode
17903 * @cfg {TreeLoader} loader A TreeLoader to be used by this node (defaults to the loader defined on the tree)
17905 * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node
17907 Roo.tree.AsyncTreeNode = function(config){
17908 this.loaded = false;
17909 this.loading = false;
17910 Roo.tree.AsyncTreeNode.superclass.constructor.apply(this, arguments);
17912 * @event beforeload
17913 * Fires before this node is loaded, return false to cancel
17914 * @param {Node} this This node
17916 this.addEvents({'beforeload':true, 'load': true});
17919 * Fires when this node is loaded
17920 * @param {Node} this This node
17923 * The loader used by this node (defaults to using the tree's defined loader)
17928 Roo.extend(Roo.tree.AsyncTreeNode, Roo.tree.TreeNode, {
17929 expand : function(deep, anim, callback){
17930 if(this.loading){ // if an async load is already running, waiting til it's done
17932 var f = function(){
17933 if(!this.loading){ // done loading
17934 clearInterval(timer);
17935 this.expand(deep, anim, callback);
17937 }.createDelegate(this);
17938 timer = setInterval(f, 200);
17942 if(this.fireEvent("beforeload", this) === false){
17945 this.loading = true;
17946 this.ui.beforeLoad(this);
17947 var loader = this.loader || this.attributes.loader || this.getOwnerTree().getLoader();
17949 loader.load(this, this.loadComplete.createDelegate(this, [deep, anim, callback]));
17953 Roo.tree.AsyncTreeNode.superclass.expand.call(this, deep, anim, callback);
17957 * Returns true if this node is currently loading
17958 * @return {Boolean}
17960 isLoading : function(){
17961 return this.loading;
17964 loadComplete : function(deep, anim, callback){
17965 this.loading = false;
17966 this.loaded = true;
17967 this.ui.afterLoad(this);
17968 this.fireEvent("load", this);
17969 this.expand(deep, anim, callback);
17973 * Returns true if this node has been loaded
17974 * @return {Boolean}
17976 isLoaded : function(){
17977 return this.loaded;
17980 hasChildNodes : function(){
17981 if(!this.isLeaf() && !this.loaded){
17984 return Roo.tree.AsyncTreeNode.superclass.hasChildNodes.call(this);
17989 * Trigger a reload for this node
17990 * @param {Function} callback
17992 reload : function(callback){
17993 this.collapse(false, false);
17994 while(this.firstChild){
17995 this.removeChild(this.firstChild);
17997 this.childrenRendered = false;
17998 this.loaded = false;
17999 if(this.isHiddenRoot()){
18000 this.expanded = false;
18002 this.expand(false, false, callback);
18006 * Ext JS Library 1.1.1
18007 * Copyright(c) 2006-2007, Ext JS, LLC.
18009 * Originally Released Under LGPL - original licence link has changed is not relivant.
18012 * <script type="text/javascript">
18016 * @class Roo.tree.TreeNodeUI
18018 * @param {Object} node The node to render
18019 * The TreeNode UI implementation is separate from the
18020 * tree implementation. Unless you are customizing the tree UI,
18021 * you should never have to use this directly.
18023 Roo.tree.TreeNodeUI = function(node){
18025 this.rendered = false;
18026 this.animating = false;
18027 this.emptyIcon = Roo.BLANK_IMAGE_URL;
18030 Roo.tree.TreeNodeUI.prototype = {
18031 removeChild : function(node){
18033 this.ctNode.removeChild(node.ui.getEl());
18037 beforeLoad : function(){
18038 this.addClass("x-tree-node-loading");
18041 afterLoad : function(){
18042 this.removeClass("x-tree-node-loading");
18045 onTextChange : function(node, text, oldText){
18047 this.textNode.innerHTML = text;
18051 onDisableChange : function(node, state){
18052 this.disabled = state;
18054 this.addClass("x-tree-node-disabled");
18056 this.removeClass("x-tree-node-disabled");
18060 onSelectedChange : function(state){
18063 this.addClass("x-tree-selected");
18066 this.removeClass("x-tree-selected");
18070 onMove : function(tree, node, oldParent, newParent, index, refNode){
18071 this.childIndent = null;
18073 var targetNode = newParent.ui.getContainer();
18074 if(!targetNode){//target not rendered
18075 this.holder = document.createElement("div");
18076 this.holder.appendChild(this.wrap);
18079 var insertBefore = refNode ? refNode.ui.getEl() : null;
18081 targetNode.insertBefore(this.wrap, insertBefore);
18083 targetNode.appendChild(this.wrap);
18085 this.node.renderIndent(true);
18089 addClass : function(cls){
18091 Roo.fly(this.elNode).addClass(cls);
18095 removeClass : function(cls){
18097 Roo.fly(this.elNode).removeClass(cls);
18101 remove : function(){
18103 this.holder = document.createElement("div");
18104 this.holder.appendChild(this.wrap);
18108 fireEvent : function(){
18109 return this.node.fireEvent.apply(this.node, arguments);
18112 initEvents : function(){
18113 this.node.on("move", this.onMove, this);
18114 var E = Roo.EventManager;
18115 var a = this.anchor;
18117 var el = Roo.fly(a, '_treeui');
18119 if(Roo.isOpera){ // opera render bug ignores the CSS
18120 el.setStyle("text-decoration", "none");
18123 el.on("click", this.onClick, this);
18124 el.on("dblclick", this.onDblClick, this);
18127 Roo.EventManager.on(this.checkbox,
18128 Roo.isIE ? 'click' : 'change', this.onCheckChange, this);
18131 el.on("contextmenu", this.onContextMenu, this);
18133 var icon = Roo.fly(this.iconNode);
18134 icon.on("click", this.onClick, this);
18135 icon.on("dblclick", this.onDblClick, this);
18136 icon.on("contextmenu", this.onContextMenu, this);
18137 E.on(this.ecNode, "click", this.ecClick, this, true);
18139 if(this.node.disabled){
18140 this.addClass("x-tree-node-disabled");
18142 if(this.node.hidden){
18143 this.addClass("x-tree-node-disabled");
18145 var ot = this.node.getOwnerTree();
18146 var dd = ot.enableDD || ot.enableDrag || ot.enableDrop;
18147 if(dd && (!this.node.isRoot || ot.rootVisible)){
18148 Roo.dd.Registry.register(this.elNode, {
18150 handles: this.getDDHandles(),
18156 getDDHandles : function(){
18157 return [this.iconNode, this.textNode];
18162 this.wrap.style.display = "none";
18168 this.wrap.style.display = "";
18172 onContextMenu : function(e){
18173 if (this.node.hasListener("contextmenu") || this.node.getOwnerTree().hasListener("contextmenu")) {
18174 e.preventDefault();
18176 this.fireEvent("contextmenu", this.node, e);
18180 onClick : function(e){
18185 if(this.fireEvent("beforeclick", this.node, e) !== false){
18186 if(!this.disabled && this.node.attributes.href){
18187 this.fireEvent("click", this.node, e);
18190 e.preventDefault();
18195 if(this.node.attributes.singleClickExpand && !this.animating && this.node.hasChildNodes()){
18196 this.node.toggle();
18199 this.fireEvent("click", this.node, e);
18205 onDblClick : function(e){
18206 e.preventDefault();
18211 this.toggleCheck();
18213 if(!this.animating && this.node.hasChildNodes()){
18214 this.node.toggle();
18216 this.fireEvent("dblclick", this.node, e);
18219 onCheckChange : function(){
18220 var checked = this.checkbox.checked;
18221 this.node.attributes.checked = checked;
18222 this.fireEvent('checkchange', this.node, checked);
18225 ecClick : function(e){
18226 if(!this.animating && this.node.hasChildNodes()){
18227 this.node.toggle();
18231 startDrop : function(){
18232 this.dropping = true;
18235 // delayed drop so the click event doesn't get fired on a drop
18236 endDrop : function(){
18237 setTimeout(function(){
18238 this.dropping = false;
18239 }.createDelegate(this), 50);
18242 expand : function(){
18243 this.updateExpandIcon();
18244 this.ctNode.style.display = "";
18247 focus : function(){
18248 if(!this.node.preventHScroll){
18249 try{this.anchor.focus();
18251 }else if(!Roo.isIE){
18253 var noscroll = this.node.getOwnerTree().getTreeEl().dom;
18254 var l = noscroll.scrollLeft;
18255 this.anchor.focus();
18256 noscroll.scrollLeft = l;
18261 toggleCheck : function(value){
18262 var cb = this.checkbox;
18264 cb.checked = (value === undefined ? !cb.checked : value);
18270 this.anchor.blur();
18274 animExpand : function(callback){
18275 var ct = Roo.get(this.ctNode);
18277 if(!this.node.hasChildNodes()){
18278 this.updateExpandIcon();
18279 this.ctNode.style.display = "";
18280 Roo.callback(callback);
18283 this.animating = true;
18284 this.updateExpandIcon();
18287 callback : function(){
18288 this.animating = false;
18289 Roo.callback(callback);
18292 duration: this.node.ownerTree.duration || .25
18296 highlight : function(){
18297 var tree = this.node.getOwnerTree();
18298 Roo.fly(this.wrap).highlight(
18299 tree.hlColor || "C3DAF9",
18300 {endColor: tree.hlBaseColor}
18304 collapse : function(){
18305 this.updateExpandIcon();
18306 this.ctNode.style.display = "none";
18309 animCollapse : function(callback){
18310 var ct = Roo.get(this.ctNode);
18311 ct.enableDisplayMode('block');
18314 this.animating = true;
18315 this.updateExpandIcon();
18318 callback : function(){
18319 this.animating = false;
18320 Roo.callback(callback);
18323 duration: this.node.ownerTree.duration || .25
18327 getContainer : function(){
18328 return this.ctNode;
18331 getEl : function(){
18335 appendDDGhost : function(ghostNode){
18336 ghostNode.appendChild(this.elNode.cloneNode(true));
18339 getDDRepairXY : function(){
18340 return Roo.lib.Dom.getXY(this.iconNode);
18343 onRender : function(){
18347 render : function(bulkRender){
18348 var n = this.node, a = n.attributes;
18349 var targetNode = n.parentNode ?
18350 n.parentNode.ui.getContainer() : n.ownerTree.innerCt.dom;
18352 if(!this.rendered){
18353 this.rendered = true;
18355 this.renderElements(n, a, targetNode, bulkRender);
18358 if(this.textNode.setAttributeNS){
18359 this.textNode.setAttributeNS("ext", "qtip", a.qtip);
18361 this.textNode.setAttributeNS("ext", "qtitle", a.qtipTitle);
18364 this.textNode.setAttribute("ext:qtip", a.qtip);
18366 this.textNode.setAttribute("ext:qtitle", a.qtipTitle);
18369 }else if(a.qtipCfg){
18370 a.qtipCfg.target = Roo.id(this.textNode);
18371 Roo.QuickTips.register(a.qtipCfg);
18374 if(!this.node.expanded){
18375 this.updateExpandIcon();
18378 if(bulkRender === true) {
18379 targetNode.appendChild(this.wrap);
18384 renderElements : function(n, a, targetNode, bulkRender)
18386 // add some indent caching, this helps performance when rendering a large tree
18387 this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
18388 var t = n.getOwnerTree();
18389 var txt = t.renderer ? t.renderer(n.attributes) : Roo.util.Format.htmlEncode(n.text);
18390 if (typeof(n.attributes.html) != 'undefined') {
18391 txt = n.attributes.html;
18393 var tip = t.rendererTip ? t.rendererTip(n.attributes) : txt;
18394 var cb = typeof a.checked == 'boolean';
18395 var href = a.href ? a.href : Roo.isGecko ? "" : "#";
18396 var buf = ['<li class="x-tree-node"><div class="x-tree-node-el ', a.cls,'">',
18397 '<span class="x-tree-node-indent">',this.indentMarkup,"</span>",
18398 '<img src="', this.emptyIcon, '" class="x-tree-ec-icon" />',
18399 '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',(a.icon ? " x-tree-node-inline-icon" : ""),(a.iconCls ? " "+a.iconCls : ""),'" unselectable="on" />',
18400 cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + (a.checked ? 'checked="checked" />' : ' />')) : '',
18401 '<a hidefocus="on" href="',href,'" tabIndex="1" ',
18402 a.hrefTarget ? ' target="'+a.hrefTarget+'"' : "",
18403 '><span unselectable="on" qtip="' , tip ,'">',txt,"</span></a></div>",
18404 '<ul class="x-tree-node-ct" style="display:none;"></ul>',
18407 if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
18408 this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
18409 n.nextSibling.ui.getEl(), buf.join(""));
18411 this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
18414 this.elNode = this.wrap.childNodes[0];
18415 this.ctNode = this.wrap.childNodes[1];
18416 var cs = this.elNode.childNodes;
18417 this.indentNode = cs[0];
18418 this.ecNode = cs[1];
18419 this.iconNode = cs[2];
18422 this.checkbox = cs[3];
18425 this.anchor = cs[index];
18426 this.textNode = cs[index].firstChild;
18429 getAnchor : function(){
18430 return this.anchor;
18433 getTextEl : function(){
18434 return this.textNode;
18437 getIconEl : function(){
18438 return this.iconNode;
18441 isChecked : function(){
18442 return this.checkbox ? this.checkbox.checked : false;
18445 updateExpandIcon : function(){
18447 var n = this.node, c1, c2;
18448 var cls = n.isLast() ? "x-tree-elbow-end" : "x-tree-elbow";
18449 var hasChild = n.hasChildNodes();
18453 c1 = "x-tree-node-collapsed";
18454 c2 = "x-tree-node-expanded";
18457 c1 = "x-tree-node-expanded";
18458 c2 = "x-tree-node-collapsed";
18461 this.removeClass("x-tree-node-leaf");
18462 this.wasLeaf = false;
18464 if(this.c1 != c1 || this.c2 != c2){
18465 Roo.fly(this.elNode).replaceClass(c1, c2);
18466 this.c1 = c1; this.c2 = c2;
18469 // this changes non-leafs into leafs if they have no children.
18470 // it's not very rational behaviour..
18472 if(!this.wasLeaf && this.node.leaf){
18473 Roo.fly(this.elNode).replaceClass("x-tree-node-expanded", "x-tree-node-leaf");
18476 this.wasLeaf = true;
18479 var ecc = "x-tree-ec-icon "+cls;
18480 if(this.ecc != ecc){
18481 this.ecNode.className = ecc;
18487 getChildIndent : function(){
18488 if(!this.childIndent){
18492 if(!p.isRoot || (p.isRoot && p.ownerTree.rootVisible)){
18494 buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-elbow-line" />');
18496 buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-icon" />');
18501 this.childIndent = buf.join("");
18503 return this.childIndent;
18506 renderIndent : function(){
18509 var p = this.node.parentNode;
18511 indent = p.ui.getChildIndent();
18513 if(this.indentMarkup != indent){ // don't rerender if not required
18514 this.indentNode.innerHTML = indent;
18515 this.indentMarkup = indent;
18517 this.updateExpandIcon();
18522 Roo.tree.RootTreeNodeUI = function(){
18523 Roo.tree.RootTreeNodeUI.superclass.constructor.apply(this, arguments);
18525 Roo.extend(Roo.tree.RootTreeNodeUI, Roo.tree.TreeNodeUI, {
18526 render : function(){
18527 if(!this.rendered){
18528 var targetNode = this.node.ownerTree.innerCt.dom;
18529 this.node.expanded = true;
18530 targetNode.innerHTML = '<div class="x-tree-root-node"></div>';
18531 this.wrap = this.ctNode = targetNode.firstChild;
18534 collapse : function(){
18536 expand : function(){
18540 * Ext JS Library 1.1.1
18541 * Copyright(c) 2006-2007, Ext JS, LLC.
18543 * Originally Released Under LGPL - original licence link has changed is not relivant.
18546 * <script type="text/javascript">
18549 * @class Roo.tree.TreeLoader
18550 * @extends Roo.util.Observable
18551 * A TreeLoader provides for lazy loading of an {@link Roo.tree.TreeNode}'s child
18552 * nodes from a specified URL. The response must be a javascript Array definition
18553 * who's elements are node definition objects. eg:
18558 { 'id': 1, 'text': 'A folder Node', 'leaf': false },
18559 { 'id': 2, 'text': 'A leaf Node', 'leaf': true }
18566 * The old style respose with just an array is still supported, but not recommended.
18569 * A server request is sent, and child nodes are loaded only when a node is expanded.
18570 * The loading node's id is passed to the server under the parameter name "node" to
18571 * enable the server to produce the correct child nodes.
18573 * To pass extra parameters, an event handler may be attached to the "beforeload"
18574 * event, and the parameters specified in the TreeLoader's baseParams property:
18576 myTreeLoader.on("beforeload", function(treeLoader, node) {
18577 this.baseParams.category = node.attributes.category;
18580 * This would pass an HTTP parameter called "category" to the server containing
18581 * the value of the Node's "category" attribute.
18583 * Creates a new Treeloader.
18584 * @param {Object} config A config object containing config properties.
18586 Roo.tree.TreeLoader = function(config){
18587 this.baseParams = {};
18588 this.requestMethod = "POST";
18589 Roo.apply(this, config);
18594 * @event beforeload
18595 * Fires before a network request is made to retrieve the Json text which specifies a node's children.
18596 * @param {Object} This TreeLoader object.
18597 * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
18598 * @param {Object} callback The callback function specified in the {@link #load} call.
18603 * Fires when the node has been successfuly loaded.
18604 * @param {Object} This TreeLoader object.
18605 * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
18606 * @param {Object} response The response object containing the data from the server.
18610 * @event loadexception
18611 * Fires if the network request failed.
18612 * @param {Object} This TreeLoader object.
18613 * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
18614 * @param {Object} response The response object containing the data from the server.
18616 loadexception : true,
18619 * Fires before a node is created, enabling you to return custom Node types
18620 * @param {Object} This TreeLoader object.
18621 * @param {Object} attr - the data returned from the AJAX call (modify it to suit)
18626 Roo.tree.TreeLoader.superclass.constructor.call(this);
18629 Roo.extend(Roo.tree.TreeLoader, Roo.util.Observable, {
18631 * @cfg {String} dataUrl The URL from which to request a Json string which
18632 * specifies an array of node definition object representing the child nodes
18636 * @cfg {String} requestMethod either GET or POST
18637 * defaults to POST (due to BC)
18641 * @cfg {Object} baseParams (optional) An object containing properties which
18642 * specify HTTP parameters to be passed to each request for child nodes.
18645 * @cfg {Object} baseAttrs (optional) An object containing attributes to be added to all nodes
18646 * created by this loader. If the attributes sent by the server have an attribute in this object,
18647 * they take priority.
18650 * @cfg {Object} uiProviders (optional) An object containing properties which
18652 * DEPRECATED - use 'create' event handler to modify attributes - which affect creation.
18653 * specify custom {@link Roo.tree.TreeNodeUI} implementations. If the optional
18654 * <i>uiProvider</i> attribute of a returned child node is a string rather
18655 * than a reference to a TreeNodeUI implementation, this that string value
18656 * is used as a property name in the uiProviders object. You can define the provider named
18657 * 'default' , and this will be used for all nodes (if no uiProvider is delivered by the node data)
18662 * @cfg {Boolean} clearOnLoad (optional) Default to true. Remove previously existing
18663 * child nodes before loading.
18665 clearOnLoad : true,
18668 * @cfg {String} root (optional) Default to false. Use this to read data from an object
18669 * property on loading, rather than expecting an array. (eg. more compatible to a standard
18670 * Grid query { data : [ .....] }
18675 * @cfg {String} queryParam (optional)
18676 * Name of the query as it will be passed on the querystring (defaults to 'node')
18677 * eg. the request will be ?node=[id]
18684 * Load an {@link Roo.tree.TreeNode} from the URL specified in the constructor.
18685 * This is called automatically when a node is expanded, but may be used to reload
18686 * a node (or append new children if the {@link #clearOnLoad} option is false.)
18687 * @param {Roo.tree.TreeNode} node
18688 * @param {Function} callback
18690 load : function(node, callback){
18691 if(this.clearOnLoad){
18692 while(node.firstChild){
18693 node.removeChild(node.firstChild);
18696 if(node.attributes.children){ // preloaded json children
18697 var cs = node.attributes.children;
18698 for(var i = 0, len = cs.length; i < len; i++){
18699 node.appendChild(this.createNode(cs[i]));
18701 if(typeof callback == "function"){
18704 }else if(this.dataUrl){
18705 this.requestData(node, callback);
18709 getParams: function(node){
18710 var buf = [], bp = this.baseParams;
18711 for(var key in bp){
18712 if(typeof bp[key] != "function"){
18713 buf.push(encodeURIComponent(key), "=", encodeURIComponent(bp[key]), "&");
18716 var n = this.queryParam === false ? 'node' : this.queryParam;
18717 buf.push(n + "=", encodeURIComponent(node.id));
18718 return buf.join("");
18721 requestData : function(node, callback){
18722 if(this.fireEvent("beforeload", this, node, callback) !== false){
18723 this.transId = Roo.Ajax.request({
18724 method:this.requestMethod,
18725 url: this.dataUrl||this.url,
18726 success: this.handleResponse,
18727 failure: this.handleFailure,
18729 argument: {callback: callback, node: node},
18730 params: this.getParams(node)
18733 // if the load is cancelled, make sure we notify
18734 // the node that we are done
18735 if(typeof callback == "function"){
18741 isLoading : function(){
18742 return this.transId ? true : false;
18745 abort : function(){
18746 if(this.isLoading()){
18747 Roo.Ajax.abort(this.transId);
18752 createNode : function(attr)
18754 // apply baseAttrs, nice idea Corey!
18755 if(this.baseAttrs){
18756 Roo.applyIf(attr, this.baseAttrs);
18758 if(this.applyLoader !== false){
18759 attr.loader = this;
18761 // uiProvider = depreciated..
18763 if(typeof(attr.uiProvider) == 'string'){
18764 attr.uiProvider = this.uiProviders[attr.uiProvider] ||
18765 /** eval:var:attr */ eval(attr.uiProvider);
18767 if(typeof(this.uiProviders['default']) != 'undefined') {
18768 attr.uiProvider = this.uiProviders['default'];
18771 this.fireEvent('create', this, attr);
18773 attr.leaf = typeof(attr.leaf) == 'string' ? attr.leaf * 1 : attr.leaf;
18775 new Roo.tree.TreeNode(attr) :
18776 new Roo.tree.AsyncTreeNode(attr));
18779 processResponse : function(response, node, callback)
18781 var json = response.responseText;
18784 var o = Roo.decode(json);
18786 if (this.root === false && typeof(o.success) != undefined) {
18787 this.root = 'data'; // the default behaviour for list like data..
18790 if (this.root !== false && !o.success) {
18791 // it's a failure condition.
18792 var a = response.argument;
18793 this.fireEvent("loadexception", this, a.node, response);
18794 Roo.log("Load failed - should have a handler really");
18800 if (this.root !== false) {
18804 for(var i = 0, len = o.length; i < len; i++){
18805 var n = this.createNode(o[i]);
18807 node.appendChild(n);
18810 if(typeof callback == "function"){
18811 callback(this, node);
18814 this.handleFailure(response);
18818 handleResponse : function(response){
18819 this.transId = false;
18820 var a = response.argument;
18821 this.processResponse(response, a.node, a.callback);
18822 this.fireEvent("load", this, a.node, response);
18825 handleFailure : function(response)
18827 // should handle failure better..
18828 this.transId = false;
18829 var a = response.argument;
18830 this.fireEvent("loadexception", this, a.node, response);
18831 if(typeof a.callback == "function"){
18832 a.callback(this, a.node);
18837 * Ext JS Library 1.1.1
18838 * Copyright(c) 2006-2007, Ext JS, LLC.
18840 * Originally Released Under LGPL - original licence link has changed is not relivant.
18843 * <script type="text/javascript">
18847 * @class Roo.tree.TreeFilter
18848 * Note this class is experimental and doesn't update the indent (lines) or expand collapse icons of the nodes
18849 * @param {TreePanel} tree
18850 * @param {Object} config (optional)
18852 Roo.tree.TreeFilter = function(tree, config){
18854 this.filtered = {};
18855 Roo.apply(this, config);
18858 Roo.tree.TreeFilter.prototype = {
18865 * Filter the data by a specific attribute.
18866 * @param {String/RegExp} value Either string that the attribute value
18867 * should start with or a RegExp to test against the attribute
18868 * @param {String} attr (optional) The attribute passed in your node's attributes collection. Defaults to "text".
18869 * @param {TreeNode} startNode (optional) The node to start the filter at.
18871 filter : function(value, attr, startNode){
18872 attr = attr || "text";
18874 if(typeof value == "string"){
18875 var vlen = value.length;
18876 // auto clear empty filter
18877 if(vlen == 0 && this.clearBlank){
18881 value = value.toLowerCase();
18883 return n.attributes[attr].substr(0, vlen).toLowerCase() == value;
18885 }else if(value.exec){ // regex?
18887 return value.test(n.attributes[attr]);
18890 throw 'Illegal filter type, must be string or regex';
18892 this.filterBy(f, null, startNode);
18896 * Filter by a function. The passed function will be called with each
18897 * node in the tree (or from the startNode). If the function returns true, the node is kept
18898 * otherwise it is filtered. If a node is filtered, its children are also filtered.
18899 * @param {Function} fn The filter function
18900 * @param {Object} scope (optional) The scope of the function (defaults to the current node)
18902 filterBy : function(fn, scope, startNode){
18903 startNode = startNode || this.tree.root;
18904 if(this.autoClear){
18907 var af = this.filtered, rv = this.reverse;
18908 var f = function(n){
18909 if(n == startNode){
18915 var m = fn.call(scope || n, n);
18923 startNode.cascade(f);
18926 if(typeof id != "function"){
18928 if(n && n.parentNode){
18929 n.parentNode.removeChild(n);
18937 * Clears the current filter. Note: with the "remove" option
18938 * set a filter cannot be cleared.
18940 clear : function(){
18942 var af = this.filtered;
18944 if(typeof id != "function"){
18951 this.filtered = {};
18956 * Ext JS Library 1.1.1
18957 * Copyright(c) 2006-2007, Ext JS, LLC.
18959 * Originally Released Under LGPL - original licence link has changed is not relivant.
18962 * <script type="text/javascript">
18967 * @class Roo.tree.TreeSorter
18968 * Provides sorting of nodes in a TreePanel
18970 * @cfg {Boolean} folderSort True to sort leaf nodes under non leaf nodes
18971 * @cfg {String} property The named attribute on the node to sort by (defaults to text)
18972 * @cfg {String} dir The direction to sort (asc or desc) (defaults to asc)
18973 * @cfg {String} leafAttr The attribute used to determine leaf nodes in folder sort (defaults to "leaf")
18974 * @cfg {Boolean} caseSensitive true for case sensitive sort (defaults to false)
18975 * @cfg {Function} sortType A custom "casting" function used to convert node values before sorting
18977 * @param {TreePanel} tree
18978 * @param {Object} config
18980 Roo.tree.TreeSorter = function(tree, config){
18981 Roo.apply(this, config);
18982 tree.on("beforechildrenrendered", this.doSort, this);
18983 tree.on("append", this.updateSort, this);
18984 tree.on("insert", this.updateSort, this);
18986 var dsc = this.dir && this.dir.toLowerCase() == "desc";
18987 var p = this.property || "text";
18988 var sortType = this.sortType;
18989 var fs = this.folderSort;
18990 var cs = this.caseSensitive === true;
18991 var leafAttr = this.leafAttr || 'leaf';
18993 this.sortFn = function(n1, n2){
18995 if(n1.attributes[leafAttr] && !n2.attributes[leafAttr]){
18998 if(!n1.attributes[leafAttr] && n2.attributes[leafAttr]){
19002 var v1 = sortType ? sortType(n1) : (cs ? n1.attributes[p] : n1.attributes[p].toUpperCase());
19003 var v2 = sortType ? sortType(n2) : (cs ? n2.attributes[p] : n2.attributes[p].toUpperCase());
19005 return dsc ? +1 : -1;
19007 return dsc ? -1 : +1;
19014 Roo.tree.TreeSorter.prototype = {
19015 doSort : function(node){
19016 node.sort(this.sortFn);
19019 compareNodes : function(n1, n2){
19020 return (n1.text.toUpperCase() > n2.text.toUpperCase() ? 1 : -1);
19023 updateSort : function(tree, node){
19024 if(node.childrenRendered){
19025 this.doSort.defer(1, this, [node]);
19030 * Ext JS Library 1.1.1
19031 * Copyright(c) 2006-2007, Ext JS, LLC.
19033 * Originally Released Under LGPL - original licence link has changed is not relivant.
19036 * <script type="text/javascript">
19039 if(Roo.dd.DropZone){
19041 Roo.tree.TreeDropZone = function(tree, config){
19042 this.allowParentInsert = false;
19043 this.allowContainerDrop = false;
19044 this.appendOnly = false;
19045 Roo.tree.TreeDropZone.superclass.constructor.call(this, tree.innerCt, config);
19047 this.lastInsertClass = "x-tree-no-status";
19048 this.dragOverData = {};
19051 Roo.extend(Roo.tree.TreeDropZone, Roo.dd.DropZone, {
19052 ddGroup : "TreeDD",
19055 expandDelay : 1000,
19057 expandNode : function(node){
19058 if(node.hasChildNodes() && !node.isExpanded()){
19059 node.expand(false, null, this.triggerCacheRefresh.createDelegate(this));
19063 queueExpand : function(node){
19064 this.expandProcId = this.expandNode.defer(this.expandDelay, this, [node]);
19067 cancelExpand : function(){
19068 if(this.expandProcId){
19069 clearTimeout(this.expandProcId);
19070 this.expandProcId = false;
19074 isValidDropPoint : function(n, pt, dd, e, data){
19075 if(!n || !data){ return false; }
19076 var targetNode = n.node;
19077 var dropNode = data.node;
19078 // default drop rules
19079 if(!(targetNode && targetNode.isTarget && pt)){
19082 if(pt == "append" && targetNode.allowChildren === false){
19085 if((pt == "above" || pt == "below") && (targetNode.parentNode && targetNode.parentNode.allowChildren === false)){
19088 if(dropNode && (targetNode == dropNode || dropNode.contains(targetNode))){
19091 // reuse the object
19092 var overEvent = this.dragOverData;
19093 overEvent.tree = this.tree;
19094 overEvent.target = targetNode;
19095 overEvent.data = data;
19096 overEvent.point = pt;
19097 overEvent.source = dd;
19098 overEvent.rawEvent = e;
19099 overEvent.dropNode = dropNode;
19100 overEvent.cancel = false;
19101 var result = this.tree.fireEvent("nodedragover", overEvent);
19102 return overEvent.cancel === false && result !== false;
19105 getDropPoint : function(e, n, dd)
19109 return tn.allowChildren !== false ? "append" : false; // always append for root
19111 var dragEl = n.ddel;
19112 var t = Roo.lib.Dom.getY(dragEl), b = t + dragEl.offsetHeight;
19113 var y = Roo.lib.Event.getPageY(e);
19114 //var noAppend = tn.allowChildren === false || tn.isLeaf();
19116 // we may drop nodes anywhere, as long as allowChildren has not been set to false..
19117 var noAppend = tn.allowChildren === false;
19118 if(this.appendOnly || tn.parentNode.allowChildren === false){
19119 return noAppend ? false : "append";
19121 var noBelow = false;
19122 if(!this.allowParentInsert){
19123 noBelow = tn.hasChildNodes() && tn.isExpanded();
19125 var q = (b - t) / (noAppend ? 2 : 3);
19126 if(y >= t && y < (t + q)){
19128 }else if(!noBelow && (noAppend || y >= b-q && y <= b)){
19135 onNodeEnter : function(n, dd, e, data)
19137 this.cancelExpand();
19140 onNodeOver : function(n, dd, e, data)
19143 var pt = this.getDropPoint(e, n, dd);
19146 // auto node expand check
19147 if(!this.expandProcId && pt == "append" && node.hasChildNodes() && !n.node.isExpanded()){
19148 this.queueExpand(node);
19149 }else if(pt != "append"){
19150 this.cancelExpand();
19153 // set the insert point style on the target node
19154 var returnCls = this.dropNotAllowed;
19155 if(this.isValidDropPoint(n, pt, dd, e, data)){
19160 returnCls = n.node.isFirst() ? "x-tree-drop-ok-above" : "x-tree-drop-ok-between";
19161 cls = "x-tree-drag-insert-above";
19162 }else if(pt == "below"){
19163 returnCls = n.node.isLast() ? "x-tree-drop-ok-below" : "x-tree-drop-ok-between";
19164 cls = "x-tree-drag-insert-below";
19166 returnCls = "x-tree-drop-ok-append";
19167 cls = "x-tree-drag-append";
19169 if(this.lastInsertClass != cls){
19170 Roo.fly(el).replaceClass(this.lastInsertClass, cls);
19171 this.lastInsertClass = cls;
19178 onNodeOut : function(n, dd, e, data){
19180 this.cancelExpand();
19181 this.removeDropIndicators(n);
19184 onNodeDrop : function(n, dd, e, data){
19185 var point = this.getDropPoint(e, n, dd);
19186 var targetNode = n.node;
19187 targetNode.ui.startDrop();
19188 if(!this.isValidDropPoint(n, point, dd, e, data)){
19189 targetNode.ui.endDrop();
19192 // first try to find the drop node
19193 var dropNode = data.node || (dd.getTreeNode ? dd.getTreeNode(data, targetNode, point, e) : null);
19196 target: targetNode,
19201 dropNode: dropNode,
19204 var retval = this.tree.fireEvent("beforenodedrop", dropEvent);
19205 if(retval === false || dropEvent.cancel === true || !dropEvent.dropNode){
19206 targetNode.ui.endDrop();
19209 // allow target changing
19210 targetNode = dropEvent.target;
19211 if(point == "append" && !targetNode.isExpanded()){
19212 targetNode.expand(false, null, function(){
19213 this.completeDrop(dropEvent);
19214 }.createDelegate(this));
19216 this.completeDrop(dropEvent);
19221 completeDrop : function(de){
19222 var ns = de.dropNode, p = de.point, t = de.target;
19223 if(!(ns instanceof Array)){
19227 for(var i = 0, len = ns.length; i < len; i++){
19230 t.parentNode.insertBefore(n, t);
19231 }else if(p == "below"){
19232 t.parentNode.insertBefore(n, t.nextSibling);
19238 if(this.tree.hlDrop){
19242 this.tree.fireEvent("nodedrop", de);
19245 afterNodeMoved : function(dd, data, e, targetNode, dropNode){
19246 if(this.tree.hlDrop){
19247 dropNode.ui.focus();
19248 dropNode.ui.highlight();
19250 this.tree.fireEvent("nodedrop", this.tree, targetNode, data, dd, e);
19253 getTree : function(){
19257 removeDropIndicators : function(n){
19260 Roo.fly(el).removeClass([
19261 "x-tree-drag-insert-above",
19262 "x-tree-drag-insert-below",
19263 "x-tree-drag-append"]);
19264 this.lastInsertClass = "_noclass";
19268 beforeDragDrop : function(target, e, id){
19269 this.cancelExpand();
19273 afterRepair : function(data){
19274 if(data && Roo.enableFx){
19275 data.node.ui.highlight();
19285 * Ext JS Library 1.1.1
19286 * Copyright(c) 2006-2007, Ext JS, LLC.
19288 * Originally Released Under LGPL - original licence link has changed is not relivant.
19291 * <script type="text/javascript">
19295 if(Roo.dd.DragZone){
19296 Roo.tree.TreeDragZone = function(tree, config){
19297 Roo.tree.TreeDragZone.superclass.constructor.call(this, tree.getTreeEl(), config);
19301 Roo.extend(Roo.tree.TreeDragZone, Roo.dd.DragZone, {
19302 ddGroup : "TreeDD",
19304 onBeforeDrag : function(data, e){
19306 return n && n.draggable && !n.disabled;
19310 onInitDrag : function(e){
19311 var data = this.dragData;
19312 this.tree.getSelectionModel().select(data.node);
19313 this.proxy.update("");
19314 data.node.ui.appendDDGhost(this.proxy.ghost.dom);
19315 this.tree.fireEvent("startdrag", this.tree, data.node, e);
19318 getRepairXY : function(e, data){
19319 return data.node.ui.getDDRepairXY();
19322 onEndDrag : function(data, e){
19323 this.tree.fireEvent("enddrag", this.tree, data.node, e);
19328 onValidDrop : function(dd, e, id){
19329 this.tree.fireEvent("dragdrop", this.tree, this.dragData.node, dd, e);
19333 beforeInvalidDrop : function(e, id){
19334 // this scrolls the original position back into view
19335 var sm = this.tree.getSelectionModel();
19336 sm.clearSelections();
19337 sm.select(this.dragData.node);
19342 * Ext JS Library 1.1.1
19343 * Copyright(c) 2006-2007, Ext JS, LLC.
19345 * Originally Released Under LGPL - original licence link has changed is not relivant.
19348 * <script type="text/javascript">
19351 * @class Roo.tree.TreeEditor
19352 * @extends Roo.Editor
19353 * Provides editor functionality for inline tree node editing. Any valid {@link Roo.form.Field} can be used
19354 * as the editor field.
19356 * @param {Object} config (used to be the tree panel.)
19357 * @param {Object} oldconfig DEPRECIATED Either a prebuilt {@link Roo.form.Field} instance or a Field config object
19359 * @cfg {Roo.tree.TreePanel} tree The tree to bind to.
19360 * @cfg {Roo.form.TextField|Object} field The field configuration
19364 Roo.tree.TreeEditor = function(config, oldconfig) { // was -- (tree, config){
19367 if (oldconfig) { // old style..
19368 field = oldconfig.events ? oldconfig : new Roo.form.TextField(oldconfig);
19371 tree = config.tree;
19372 config.field = config.field || {};
19373 config.field.xtype = 'TextField';
19374 field = Roo.factory(config.field, Roo.form);
19376 config = config || {};
19381 * @event beforenodeedit
19382 * Fires when editing is initiated, but before the value changes. Editing can be canceled by returning
19383 * false from the handler of this event.
19384 * @param {Editor} this
19385 * @param {Roo.tree.Node} node
19387 "beforenodeedit" : true
19391 Roo.tree.TreeEditor.superclass.constructor.call(this, field, config);
19395 tree.on('beforeclick', this.beforeNodeClick, this);
19396 tree.getTreeEl().on('mousedown', this.hide, this);
19397 this.on('complete', this.updateNode, this);
19398 this.on('beforestartedit', this.fitToTree, this);
19399 this.on('startedit', this.bindScroll, this, {delay:10});
19400 this.on('specialkey', this.onSpecialKey, this);
19403 Roo.extend(Roo.tree.TreeEditor, Roo.Editor, {
19405 * @cfg {String} alignment
19406 * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "l-l").
19412 * @cfg {Boolean} hideEl
19413 * True to hide the bound element while the editor is displayed (defaults to false)
19417 * @cfg {String} cls
19418 * CSS class to apply to the editor (defaults to "x-small-editor x-tree-editor")
19420 cls: "x-small-editor x-tree-editor",
19422 * @cfg {Boolean} shim
19423 * True to shim the editor if selects/iframes could be displayed beneath it (defaults to false)
19429 * @cfg {Number} maxWidth
19430 * The maximum width in pixels of the editor field (defaults to 250). Note that if the maxWidth would exceed
19431 * the containing tree element's size, it will be automatically limited for you to the container width, taking
19432 * scroll and client offsets into account prior to each edit.
19439 fitToTree : function(ed, el){
19440 var td = this.tree.getTreeEl().dom, nd = el.dom;
19441 if(td.scrollLeft > nd.offsetLeft){ // ensure the node left point is visible
19442 td.scrollLeft = nd.offsetLeft;
19446 (td.clientWidth > 20 ? td.clientWidth : td.offsetWidth) - Math.max(0, nd.offsetLeft-td.scrollLeft) - /*cushion*/5);
19447 this.setSize(w, '');
19449 return this.fireEvent('beforenodeedit', this, this.editNode);
19454 triggerEdit : function(node){
19455 this.completeEdit();
19456 this.editNode = node;
19457 this.startEdit(node.ui.textNode, node.text);
19461 bindScroll : function(){
19462 this.tree.getTreeEl().on('scroll', this.cancelEdit, this);
19466 beforeNodeClick : function(node, e){
19467 var sinceLast = (this.lastClick ? this.lastClick.getElapsed() : 0);
19468 this.lastClick = new Date();
19469 if(sinceLast > this.editDelay && this.tree.getSelectionModel().isSelected(node)){
19471 this.triggerEdit(node);
19478 updateNode : function(ed, value){
19479 this.tree.getTreeEl().un('scroll', this.cancelEdit, this);
19480 this.editNode.setText(value);
19484 onHide : function(){
19485 Roo.tree.TreeEditor.superclass.onHide.call(this);
19487 this.editNode.ui.focus();
19492 onSpecialKey : function(field, e){
19493 var k = e.getKey();
19497 }else if(k == e.ENTER && !e.hasModifier()){
19499 this.completeEdit();
19502 });//<Script type="text/javascript">
19505 * Ext JS Library 1.1.1
19506 * Copyright(c) 2006-2007, Ext JS, LLC.
19508 * Originally Released Under LGPL - original licence link has changed is not relivant.
19511 * <script type="text/javascript">
19515 * Not documented??? - probably should be...
19518 Roo.tree.ColumnNodeUI = Roo.extend(Roo.tree.TreeNodeUI, {
19519 //focus: Roo.emptyFn, // prevent odd scrolling behavior
19521 renderElements : function(n, a, targetNode, bulkRender){
19522 //consel.log("renderElements?");
19523 this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
19525 var t = n.getOwnerTree();
19526 var tid = Pman.Tab.Document_TypesTree.tree.el.id;
19528 var cols = t.columns;
19529 var bw = t.borderWidth;
19531 var href = a.href ? a.href : Roo.isGecko ? "" : "#";
19532 var cb = typeof a.checked == "boolean";
19533 var tx = String.format('{0}',n.text || (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
19534 var colcls = 'x-t-' + tid + '-c0';
19536 '<li class="x-tree-node">',
19539 '<div class="x-tree-node-el ', a.cls,'">',
19541 '<div class="x-tree-col ', colcls, '" style="width:', c.width-bw, 'px;">',
19544 '<span class="x-tree-node-indent">',this.indentMarkup,'</span>',
19545 '<img src="', this.emptyIcon, '" class="x-tree-ec-icon " />',
19546 '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',
19547 (a.icon ? ' x-tree-node-inline-icon' : ''),
19548 (a.iconCls ? ' '+a.iconCls : ''),
19549 '" unselectable="on" />',
19550 (cb ? ('<input class="x-tree-node-cb" type="checkbox" ' +
19551 (a.checked ? 'checked="checked" />' : ' />')) : ''),
19553 '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
19554 (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>',
19555 '<span unselectable="on" qtip="' + tx + '">',
19559 '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
19560 (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>'
19562 for(var i = 1, len = cols.length; i < len; i++){
19564 colcls = 'x-t-' + tid + '-c' +i;
19565 tx = String.format('{0}', (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
19566 buf.push('<div class="x-tree-col ', colcls, ' ' ,(c.cls?c.cls:''),'" style="width:',c.width-bw,'px;">',
19567 '<div class="x-tree-col-text" qtip="' + tx +'">',tx,"</div>",
19573 '<div class="x-clear"></div></div>',
19574 '<ul class="x-tree-node-ct" style="display:none;"></ul>',
19577 if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
19578 this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
19579 n.nextSibling.ui.getEl(), buf.join(""));
19581 this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
19583 var el = this.wrap.firstChild;
19585 this.elNode = el.firstChild;
19586 this.ranchor = el.childNodes[1];
19587 this.ctNode = this.wrap.childNodes[1];
19588 var cs = el.firstChild.childNodes;
19589 this.indentNode = cs[0];
19590 this.ecNode = cs[1];
19591 this.iconNode = cs[2];
19594 this.checkbox = cs[3];
19597 this.anchor = cs[index];
19599 this.textNode = cs[index].firstChild;
19601 //el.on("click", this.onClick, this);
19602 //el.on("dblclick", this.onDblClick, this);
19605 // console.log(this);
19607 initEvents : function(){
19608 Roo.tree.ColumnNodeUI.superclass.initEvents.call(this);
19611 var a = this.ranchor;
19613 var el = Roo.get(a);
19615 if(Roo.isOpera){ // opera render bug ignores the CSS
19616 el.setStyle("text-decoration", "none");
19619 el.on("click", this.onClick, this);
19620 el.on("dblclick", this.onDblClick, this);
19621 el.on("contextmenu", this.onContextMenu, this);
19625 /*onSelectedChange : function(state){
19628 this.addClass("x-tree-selected");
19631 this.removeClass("x-tree-selected");
19634 addClass : function(cls){
19636 Roo.fly(this.elRow).addClass(cls);
19642 removeClass : function(cls){
19644 Roo.fly(this.elRow).removeClass(cls);
19650 });//<Script type="text/javascript">
19654 * Ext JS Library 1.1.1
19655 * Copyright(c) 2006-2007, Ext JS, LLC.
19657 * Originally Released Under LGPL - original licence link has changed is not relivant.
19660 * <script type="text/javascript">
19665 * @class Roo.tree.ColumnTree
19666 * @extends Roo.data.TreePanel
19667 * @cfg {Object} columns Including width, header, renderer, cls, dataIndex
19668 * @cfg {int} borderWidth compined right/left border allowance
19670 * @param {String/HTMLElement/Element} el The container element
19671 * @param {Object} config
19673 Roo.tree.ColumnTree = function(el, config)
19675 Roo.tree.ColumnTree.superclass.constructor.call(this, el , config);
19679 * Fire this event on a container when it resizes
19680 * @param {int} w Width
19681 * @param {int} h Height
19685 this.on('resize', this.onResize, this);
19688 Roo.extend(Roo.tree.ColumnTree, Roo.tree.TreePanel, {
19692 borderWidth: Roo.isBorderBox ? 0 : 2,
19695 render : function(){
19696 // add the header.....
19698 Roo.tree.ColumnTree.superclass.render.apply(this);
19700 this.el.addClass('x-column-tree');
19702 this.headers = this.el.createChild(
19703 {cls:'x-tree-headers'},this.innerCt.dom);
19705 var cols = this.columns, c;
19706 var totalWidth = 0;
19708 var len = cols.length;
19709 for(var i = 0; i < len; i++){
19711 totalWidth += c.width;
19712 this.headEls.push(this.headers.createChild({
19713 cls:'x-tree-hd ' + (c.cls?c.cls+'-hd':''),
19715 cls:'x-tree-hd-text',
19718 style:'width:'+(c.width-this.borderWidth)+'px;'
19721 this.headers.createChild({cls:'x-clear'});
19722 // prevent floats from wrapping when clipped
19723 this.headers.setWidth(totalWidth);
19724 //this.innerCt.setWidth(totalWidth);
19725 this.innerCt.setStyle({ overflow: 'auto' });
19726 this.onResize(this.width, this.height);
19730 onResize : function(w,h)
19735 this.innerCt.setWidth(this.width);
19736 this.innerCt.setHeight(this.height-20);
19739 var cols = this.columns, c;
19740 var totalWidth = 0;
19742 var len = cols.length;
19743 for(var i = 0; i < len; i++){
19745 if (this.autoExpandColumn !== false && c.dataIndex == this.autoExpandColumn) {
19746 // it's the expander..
19747 expEl = this.headEls[i];
19750 totalWidth += c.width;
19754 expEl.setWidth( ((w - totalWidth)-this.borderWidth - 20));
19756 this.headers.setWidth(w-20);
19765 * Ext JS Library 1.1.1
19766 * Copyright(c) 2006-2007, Ext JS, LLC.
19768 * Originally Released Under LGPL - original licence link has changed is not relivant.
19771 * <script type="text/javascript">
19775 * @class Roo.menu.Menu
19776 * @extends Roo.util.Observable
19777 * A menu object. This is the container to which you add all other menu items. Menu can also serve a as a base class
19778 * when you want a specialzed menu based off of another component (like {@link Roo.menu.DateMenu} for example).
19780 * Creates a new Menu
19781 * @param {Object} config Configuration options
19783 Roo.menu.Menu = function(config){
19784 Roo.apply(this, config);
19785 this.id = this.id || Roo.id();
19788 * @event beforeshow
19789 * Fires before this menu is displayed
19790 * @param {Roo.menu.Menu} this
19794 * @event beforehide
19795 * Fires before this menu is hidden
19796 * @param {Roo.menu.Menu} this
19801 * Fires after this menu is displayed
19802 * @param {Roo.menu.Menu} this
19807 * Fires after this menu is hidden
19808 * @param {Roo.menu.Menu} this
19813 * Fires when this menu is clicked (or when the enter key is pressed while it is active)
19814 * @param {Roo.menu.Menu} this
19815 * @param {Roo.menu.Item} menuItem The menu item that was clicked
19816 * @param {Roo.EventObject} e
19821 * Fires when the mouse is hovering over this menu
19822 * @param {Roo.menu.Menu} this
19823 * @param {Roo.EventObject} e
19824 * @param {Roo.menu.Item} menuItem The menu item that was clicked
19829 * Fires when the mouse exits this menu
19830 * @param {Roo.menu.Menu} this
19831 * @param {Roo.EventObject} e
19832 * @param {Roo.menu.Item} menuItem The menu item that was clicked
19837 * Fires when a menu item contained in this menu is clicked
19838 * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
19839 * @param {Roo.EventObject} e
19843 if (this.registerMenu) {
19844 Roo.menu.MenuMgr.register(this);
19847 var mis = this.items;
19848 this.items = new Roo.util.MixedCollection();
19850 this.add.apply(this, mis);
19854 Roo.extend(Roo.menu.Menu, Roo.util.Observable, {
19856 * @cfg {Number} minWidth The minimum width of the menu in pixels (defaults to 120)
19860 * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop"
19861 * for bottom-right shadow (defaults to "sides")
19865 * @cfg {String} subMenuAlign The {@link Roo.Element#alignTo} anchor position value to use for submenus of
19866 * this menu (defaults to "tl-tr?")
19868 subMenuAlign : "tl-tr?",
19870 * @cfg {String} defaultAlign The default {@link Roo.Element#alignTo) anchor position value for this menu
19871 * relative to its element of origin (defaults to "tl-bl?")
19873 defaultAlign : "tl-bl?",
19875 * @cfg {Boolean} allowOtherMenus True to allow multiple menus to be displayed at the same time (defaults to false)
19877 allowOtherMenus : false,
19879 * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
19881 registerMenu : true,
19886 render : function(){
19890 var el = this.el = new Roo.Layer({
19892 shadow:this.shadow,
19894 parentEl: this.parentEl || document.body,
19898 this.keyNav = new Roo.menu.MenuNav(this);
19901 el.addClass("x-menu-plain");
19904 el.addClass(this.cls);
19906 // generic focus element
19907 this.focusEl = el.createChild({
19908 tag: "a", cls: "x-menu-focus", href: "#", onclick: "return false;", tabIndex:"-1"
19910 var ul = el.createChild({tag: "ul", cls: "x-menu-list"});
19911 ul.on("click", this.onClick, this);
19912 ul.on("mouseover", this.onMouseOver, this);
19913 ul.on("mouseout", this.onMouseOut, this);
19914 this.items.each(function(item){
19919 var li = document.createElement("li");
19920 li.className = "x-menu-list-item";
19921 ul.dom.appendChild(li);
19922 item.render(li, this);
19929 autoWidth : function(){
19930 var el = this.el, ul = this.ul;
19934 var w = this.width;
19937 }else if(Roo.isIE){
19938 el.setWidth(this.minWidth);
19939 var t = el.dom.offsetWidth; // force recalc
19940 el.setWidth(ul.getWidth()+el.getFrameWidth("lr"));
19945 delayAutoWidth : function(){
19948 this.awTask = new Roo.util.DelayedTask(this.autoWidth, this);
19950 this.awTask.delay(20);
19955 findTargetItem : function(e){
19956 var t = e.getTarget(".x-menu-list-item", this.ul, true);
19957 if(t && t.menuItemId){
19958 return this.items.get(t.menuItemId);
19963 onClick : function(e){
19965 if(t = this.findTargetItem(e)){
19967 this.fireEvent("click", this, t, e);
19972 setActiveItem : function(item, autoExpand){
19973 if(item != this.activeItem){
19974 if(this.activeItem){
19975 this.activeItem.deactivate();
19977 this.activeItem = item;
19978 item.activate(autoExpand);
19979 }else if(autoExpand){
19985 tryActivate : function(start, step){
19986 var items = this.items;
19987 for(var i = start, len = items.length; i >= 0 && i < len; i+= step){
19988 var item = items.get(i);
19989 if(!item.disabled && item.canActivate){
19990 this.setActiveItem(item, false);
19998 onMouseOver : function(e){
20000 if(t = this.findTargetItem(e)){
20001 if(t.canActivate && !t.disabled){
20002 this.setActiveItem(t, true);
20005 this.fireEvent("mouseover", this, e, t);
20009 onMouseOut : function(e){
20011 if(t = this.findTargetItem(e)){
20012 if(t == this.activeItem && t.shouldDeactivate(e)){
20013 this.activeItem.deactivate();
20014 delete this.activeItem;
20017 this.fireEvent("mouseout", this, e, t);
20021 * Read-only. Returns true if the menu is currently displayed, else false.
20024 isVisible : function(){
20025 return this.el && !this.hidden;
20029 * Displays this menu relative to another element
20030 * @param {String/HTMLElement/Roo.Element} element The element to align to
20031 * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
20032 * the element (defaults to this.defaultAlign)
20033 * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
20035 show : function(el, pos, parentMenu){
20036 this.parentMenu = parentMenu;
20040 this.fireEvent("beforeshow", this);
20041 this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
20045 * Displays this menu at a specific xy position
20046 * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
20047 * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
20049 showAt : function(xy, parentMenu, /* private: */_e){
20050 this.parentMenu = parentMenu;
20055 this.fireEvent("beforeshow", this);
20056 xy = this.el.adjustForConstraints(xy);
20060 this.hidden = false;
20062 this.fireEvent("show", this);
20065 focus : function(){
20067 this.doFocus.defer(50, this);
20071 doFocus : function(){
20073 this.focusEl.focus();
20078 * Hides this menu and optionally all parent menus
20079 * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
20081 hide : function(deep){
20082 if(this.el && this.isVisible()){
20083 this.fireEvent("beforehide", this);
20084 if(this.activeItem){
20085 this.activeItem.deactivate();
20086 this.activeItem = null;
20089 this.hidden = true;
20090 this.fireEvent("hide", this);
20092 if(deep === true && this.parentMenu){
20093 this.parentMenu.hide(true);
20098 * Addds one or more items of any type supported by the Menu class, or that can be converted into menu items.
20099 * Any of the following are valid:
20101 * <li>Any menu item object based on {@link Roo.menu.Item}</li>
20102 * <li>An HTMLElement object which will be converted to a menu item</li>
20103 * <li>A menu item config object that will be created as a new menu item</li>
20104 * <li>A string, which can either be '-' or 'separator' to add a menu separator, otherwise
20105 * it will be converted into a {@link Roo.menu.TextItem} and added</li>
20110 var menu = new Roo.menu.Menu();
20112 // Create a menu item to add by reference
20113 var menuItem = new Roo.menu.Item({ text: 'New Item!' });
20115 // Add a bunch of items at once using different methods.
20116 // Only the last item added will be returned.
20117 var item = menu.add(
20118 menuItem, // add existing item by ref
20119 'Dynamic Item', // new TextItem
20120 '-', // new separator
20121 { text: 'Config Item' } // new item by config
20124 * @param {Mixed} args One or more menu items, menu item configs or other objects that can be converted to menu items
20125 * @return {Roo.menu.Item} The menu item that was added, or the last one if multiple items were added
20128 var a = arguments, l = a.length, item;
20129 for(var i = 0; i < l; i++){
20131 if ((typeof(el) == "object") && el.xtype && el.xns) {
20132 el = Roo.factory(el, Roo.menu);
20135 if(el.render){ // some kind of Item
20136 item = this.addItem(el);
20137 }else if(typeof el == "string"){ // string
20138 if(el == "separator" || el == "-"){
20139 item = this.addSeparator();
20141 item = this.addText(el);
20143 }else if(el.tagName || el.el){ // element
20144 item = this.addElement(el);
20145 }else if(typeof el == "object"){ // must be menu item config?
20146 item = this.addMenuItem(el);
20153 * Returns this menu's underlying {@link Roo.Element} object
20154 * @return {Roo.Element} The element
20156 getEl : function(){
20164 * Adds a separator bar to the menu
20165 * @return {Roo.menu.Item} The menu item that was added
20167 addSeparator : function(){
20168 return this.addItem(new Roo.menu.Separator());
20172 * Adds an {@link Roo.Element} object to the menu
20173 * @param {String/HTMLElement/Roo.Element} el The element or DOM node to add, or its id
20174 * @return {Roo.menu.Item} The menu item that was added
20176 addElement : function(el){
20177 return this.addItem(new Roo.menu.BaseItem(el));
20181 * Adds an existing object based on {@link Roo.menu.Item} to the menu
20182 * @param {Roo.menu.Item} item The menu item to add
20183 * @return {Roo.menu.Item} The menu item that was added
20185 addItem : function(item){
20186 this.items.add(item);
20188 var li = document.createElement("li");
20189 li.className = "x-menu-list-item";
20190 this.ul.dom.appendChild(li);
20191 item.render(li, this);
20192 this.delayAutoWidth();
20198 * Creates a new {@link Roo.menu.Item} based an the supplied config object and adds it to the menu
20199 * @param {Object} config A MenuItem config object
20200 * @return {Roo.menu.Item} The menu item that was added
20202 addMenuItem : function(config){
20203 if(!(config instanceof Roo.menu.Item)){
20204 if(typeof config.checked == "boolean"){ // must be check menu item config?
20205 config = new Roo.menu.CheckItem(config);
20207 config = new Roo.menu.Item(config);
20210 return this.addItem(config);
20214 * Creates a new {@link Roo.menu.TextItem} with the supplied text and adds it to the menu
20215 * @param {String} text The text to display in the menu item
20216 * @return {Roo.menu.Item} The menu item that was added
20218 addText : function(text){
20219 return this.addItem(new Roo.menu.TextItem({ text : text }));
20223 * Inserts an existing object based on {@link Roo.menu.Item} to the menu at a specified index
20224 * @param {Number} index The index in the menu's list of current items where the new item should be inserted
20225 * @param {Roo.menu.Item} item The menu item to add
20226 * @return {Roo.menu.Item} The menu item that was added
20228 insert : function(index, item){
20229 this.items.insert(index, item);
20231 var li = document.createElement("li");
20232 li.className = "x-menu-list-item";
20233 this.ul.dom.insertBefore(li, this.ul.dom.childNodes[index]);
20234 item.render(li, this);
20235 this.delayAutoWidth();
20241 * Removes an {@link Roo.menu.Item} from the menu and destroys the object
20242 * @param {Roo.menu.Item} item The menu item to remove
20244 remove : function(item){
20245 this.items.removeKey(item.id);
20250 * Removes and destroys all items in the menu
20252 removeAll : function(){
20254 while(f = this.items.first()){
20260 // MenuNav is a private utility class used internally by the Menu
20261 Roo.menu.MenuNav = function(menu){
20262 Roo.menu.MenuNav.superclass.constructor.call(this, menu.el);
20263 this.scope = this.menu = menu;
20266 Roo.extend(Roo.menu.MenuNav, Roo.KeyNav, {
20267 doRelay : function(e, h){
20268 var k = e.getKey();
20269 if(!this.menu.activeItem && e.isNavKeyPress() && k != e.SPACE && k != e.RETURN){
20270 this.menu.tryActivate(0, 1);
20273 return h.call(this.scope || this, e, this.menu);
20276 up : function(e, m){
20277 if(!m.tryActivate(m.items.indexOf(m.activeItem)-1, -1)){
20278 m.tryActivate(m.items.length-1, -1);
20282 down : function(e, m){
20283 if(!m.tryActivate(m.items.indexOf(m.activeItem)+1, 1)){
20284 m.tryActivate(0, 1);
20288 right : function(e, m){
20290 m.activeItem.expandMenu(true);
20294 left : function(e, m){
20296 if(m.parentMenu && m.parentMenu.activeItem){
20297 m.parentMenu.activeItem.activate();
20301 enter : function(e, m){
20303 e.stopPropagation();
20304 m.activeItem.onClick(e);
20305 m.fireEvent("click", this, m.activeItem);
20311 * Ext JS Library 1.1.1
20312 * Copyright(c) 2006-2007, Ext JS, LLC.
20314 * Originally Released Under LGPL - original licence link has changed is not relivant.
20317 * <script type="text/javascript">
20321 * @class Roo.menu.MenuMgr
20322 * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
20325 Roo.menu.MenuMgr = function(){
20326 var menus, active, groups = {}, attached = false, lastShow = new Date();
20328 // private - called when first menu is created
20331 active = new Roo.util.MixedCollection();
20332 Roo.get(document).addKeyListener(27, function(){
20333 if(active.length > 0){
20340 function hideAll(){
20341 if(active && active.length > 0){
20342 var c = active.clone();
20343 c.each(function(m){
20350 function onHide(m){
20352 if(active.length < 1){
20353 Roo.get(document).un("mousedown", onMouseDown);
20359 function onShow(m){
20360 var last = active.last();
20361 lastShow = new Date();
20364 Roo.get(document).on("mousedown", onMouseDown);
20368 m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
20369 m.parentMenu.activeChild = m;
20370 }else if(last && last.isVisible()){
20371 m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
20376 function onBeforeHide(m){
20378 m.activeChild.hide();
20380 if(m.autoHideTimer){
20381 clearTimeout(m.autoHideTimer);
20382 delete m.autoHideTimer;
20387 function onBeforeShow(m){
20388 var pm = m.parentMenu;
20389 if(!pm && !m.allowOtherMenus){
20391 }else if(pm && pm.activeChild && active != m){
20392 pm.activeChild.hide();
20397 function onMouseDown(e){
20398 if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".x-menu")){
20404 function onBeforeCheck(mi, state){
20406 var g = groups[mi.group];
20407 for(var i = 0, l = g.length; i < l; i++){
20409 g[i].setChecked(false);
20418 * Hides all menus that are currently visible
20420 hideAll : function(){
20425 register : function(menu){
20429 menus[menu.id] = menu;
20430 menu.on("beforehide", onBeforeHide);
20431 menu.on("hide", onHide);
20432 menu.on("beforeshow", onBeforeShow);
20433 menu.on("show", onShow);
20434 var g = menu.group;
20435 if(g && menu.events["checkchange"]){
20439 groups[g].push(menu);
20440 menu.on("checkchange", onCheck);
20445 * Returns a {@link Roo.menu.Menu} object
20446 * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
20447 * be used to generate and return a new Menu instance.
20449 get : function(menu){
20450 if(typeof menu == "string"){ // menu id
20451 return menus[menu];
20452 }else if(menu.events){ // menu instance
20454 }else if(typeof menu.length == 'number'){ // array of menu items?
20455 return new Roo.menu.Menu({items:menu});
20456 }else{ // otherwise, must be a config
20457 return new Roo.menu.Menu(menu);
20462 unregister : function(menu){
20463 delete menus[menu.id];
20464 menu.un("beforehide", onBeforeHide);
20465 menu.un("hide", onHide);
20466 menu.un("beforeshow", onBeforeShow);
20467 menu.un("show", onShow);
20468 var g = menu.group;
20469 if(g && menu.events["checkchange"]){
20470 groups[g].remove(menu);
20471 menu.un("checkchange", onCheck);
20476 registerCheckable : function(menuItem){
20477 var g = menuItem.group;
20482 groups[g].push(menuItem);
20483 menuItem.on("beforecheckchange", onBeforeCheck);
20488 unregisterCheckable : function(menuItem){
20489 var g = menuItem.group;
20491 groups[g].remove(menuItem);
20492 menuItem.un("beforecheckchange", onBeforeCheck);
20498 * Ext JS Library 1.1.1
20499 * Copyright(c) 2006-2007, Ext JS, LLC.
20501 * Originally Released Under LGPL - original licence link has changed is not relivant.
20504 * <script type="text/javascript">
20509 * @class Roo.menu.BaseItem
20510 * @extends Roo.Component
20511 * The base class for all items that render into menus. BaseItem provides default rendering, activated state
20512 * management and base configuration options shared by all menu components.
20514 * Creates a new BaseItem
20515 * @param {Object} config Configuration options
20517 Roo.menu.BaseItem = function(config){
20518 Roo.menu.BaseItem.superclass.constructor.call(this, config);
20523 * Fires when this item is clicked
20524 * @param {Roo.menu.BaseItem} this
20525 * @param {Roo.EventObject} e
20530 * Fires when this item is activated
20531 * @param {Roo.menu.BaseItem} this
20535 * @event deactivate
20536 * Fires when this item is deactivated
20537 * @param {Roo.menu.BaseItem} this
20543 this.on("click", this.handler, this.scope, true);
20547 Roo.extend(Roo.menu.BaseItem, Roo.Component, {
20549 * @cfg {Function} handler
20550 * A function that will handle the click event of this menu item (defaults to undefined)
20553 * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to false)
20555 canActivate : false,
20558 * @cfg {Boolean} hidden True to prevent creation of this menu item (defaults to false)
20563 * @cfg {String} activeClass The CSS class to use when the item becomes activated (defaults to "x-menu-item-active")
20565 activeClass : "x-menu-item-active",
20567 * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to true)
20569 hideOnClick : true,
20571 * @cfg {Number} hideDelay Length of time in milliseconds to wait before hiding after a click (defaults to 100)
20576 ctype: "Roo.menu.BaseItem",
20579 actionMode : "container",
20582 render : function(container, parentMenu){
20583 this.parentMenu = parentMenu;
20584 Roo.menu.BaseItem.superclass.render.call(this, container);
20585 this.container.menuItemId = this.id;
20589 onRender : function(container, position){
20590 this.el = Roo.get(this.el);
20591 container.dom.appendChild(this.el.dom);
20595 onClick : function(e){
20596 if(!this.disabled && this.fireEvent("click", this, e) !== false
20597 && this.parentMenu.fireEvent("itemclick", this, e) !== false){
20598 this.handleClick(e);
20605 activate : function(){
20609 var li = this.container;
20610 li.addClass(this.activeClass);
20611 this.region = li.getRegion().adjust(2, 2, -2, -2);
20612 this.fireEvent("activate", this);
20617 deactivate : function(){
20618 this.container.removeClass(this.activeClass);
20619 this.fireEvent("deactivate", this);
20623 shouldDeactivate : function(e){
20624 return !this.region || !this.region.contains(e.getPoint());
20628 handleClick : function(e){
20629 if(this.hideOnClick){
20630 this.parentMenu.hide.defer(this.hideDelay, this.parentMenu, [true]);
20635 expandMenu : function(autoActivate){
20640 hideMenu : function(){
20645 * Ext JS Library 1.1.1
20646 * Copyright(c) 2006-2007, Ext JS, LLC.
20648 * Originally Released Under LGPL - original licence link has changed is not relivant.
20651 * <script type="text/javascript">
20655 * @class Roo.menu.Adapter
20656 * @extends Roo.menu.BaseItem
20657 * 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.
20658 * It provides basic rendering, activation management and enable/disable logic required to work in menus.
20660 * Creates a new Adapter
20661 * @param {Object} config Configuration options
20663 Roo.menu.Adapter = function(component, config){
20664 Roo.menu.Adapter.superclass.constructor.call(this, config);
20665 this.component = component;
20667 Roo.extend(Roo.menu.Adapter, Roo.menu.BaseItem, {
20669 canActivate : true,
20672 onRender : function(container, position){
20673 this.component.render(container);
20674 this.el = this.component.getEl();
20678 activate : function(){
20682 this.component.focus();
20683 this.fireEvent("activate", this);
20688 deactivate : function(){
20689 this.fireEvent("deactivate", this);
20693 disable : function(){
20694 this.component.disable();
20695 Roo.menu.Adapter.superclass.disable.call(this);
20699 enable : function(){
20700 this.component.enable();
20701 Roo.menu.Adapter.superclass.enable.call(this);
20705 * Ext JS Library 1.1.1
20706 * Copyright(c) 2006-2007, Ext JS, LLC.
20708 * Originally Released Under LGPL - original licence link has changed is not relivant.
20711 * <script type="text/javascript">
20715 * @class Roo.menu.TextItem
20716 * @extends Roo.menu.BaseItem
20717 * Adds a static text string to a menu, usually used as either a heading or group separator.
20718 * Note: old style constructor with text is still supported.
20721 * Creates a new TextItem
20722 * @param {Object} cfg Configuration
20724 Roo.menu.TextItem = function(cfg){
20725 if (typeof(cfg) == 'string') {
20728 Roo.apply(this,cfg);
20731 Roo.menu.TextItem.superclass.constructor.call(this);
20734 Roo.extend(Roo.menu.TextItem, Roo.menu.BaseItem, {
20736 * @cfg {Boolean} text Text to show on item.
20741 * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
20743 hideOnClick : false,
20745 * @cfg {String} itemCls The default CSS class to use for text items (defaults to "x-menu-text")
20747 itemCls : "x-menu-text",
20750 onRender : function(){
20751 var s = document.createElement("span");
20752 s.className = this.itemCls;
20753 s.innerHTML = this.text;
20755 Roo.menu.TextItem.superclass.onRender.apply(this, arguments);
20759 * Ext JS Library 1.1.1
20760 * Copyright(c) 2006-2007, Ext JS, LLC.
20762 * Originally Released Under LGPL - original licence link has changed is not relivant.
20765 * <script type="text/javascript">
20769 * @class Roo.menu.Separator
20770 * @extends Roo.menu.BaseItem
20771 * Adds a separator bar to a menu, used to divide logical groups of menu items. Generally you will
20772 * add one of these by using "-" in you call to add() or in your items config rather than creating one directly.
20774 * @param {Object} config Configuration options
20776 Roo.menu.Separator = function(config){
20777 Roo.menu.Separator.superclass.constructor.call(this, config);
20780 Roo.extend(Roo.menu.Separator, Roo.menu.BaseItem, {
20782 * @cfg {String} itemCls The default CSS class to use for separators (defaults to "x-menu-sep")
20784 itemCls : "x-menu-sep",
20786 * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
20788 hideOnClick : false,
20791 onRender : function(li){
20792 var s = document.createElement("span");
20793 s.className = this.itemCls;
20794 s.innerHTML = " ";
20796 li.addClass("x-menu-sep-li");
20797 Roo.menu.Separator.superclass.onRender.apply(this, arguments);
20801 * Ext JS Library 1.1.1
20802 * Copyright(c) 2006-2007, Ext JS, LLC.
20804 * Originally Released Under LGPL - original licence link has changed is not relivant.
20807 * <script type="text/javascript">
20810 * @class Roo.menu.Item
20811 * @extends Roo.menu.BaseItem
20812 * A base class for all menu items that require menu-related functionality (like sub-menus) and are not static
20813 * display items. Item extends the base functionality of {@link Roo.menu.BaseItem} by adding menu-specific
20814 * activation and click handling.
20816 * Creates a new Item
20817 * @param {Object} config Configuration options
20819 Roo.menu.Item = function(config){
20820 Roo.menu.Item.superclass.constructor.call(this, config);
20822 this.menu = Roo.menu.MenuMgr.get(this.menu);
20825 Roo.extend(Roo.menu.Item, Roo.menu.BaseItem, {
20828 * @cfg {String} text
20829 * The text to show on the menu item.
20833 * @cfg {String} HTML to render in menu
20834 * The text to show on the menu item (HTML version).
20838 * @cfg {String} icon
20839 * The path to an icon to display in this menu item (defaults to Roo.BLANK_IMAGE_URL)
20843 * @cfg {String} itemCls The default CSS class to use for menu items (defaults to "x-menu-item")
20845 itemCls : "x-menu-item",
20847 * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to true)
20849 canActivate : true,
20851 * @cfg {Number} showDelay Length of time in milliseconds to wait before showing this item (defaults to 200)
20854 // doc'd in BaseItem
20858 ctype: "Roo.menu.Item",
20861 onRender : function(container, position){
20862 var el = document.createElement("a");
20863 el.hideFocus = true;
20864 el.unselectable = "on";
20865 el.href = this.href || "#";
20866 if(this.hrefTarget){
20867 el.target = this.hrefTarget;
20869 el.className = this.itemCls + (this.menu ? " x-menu-item-arrow" : "") + (this.cls ? " " + this.cls : "");
20871 var html = this.html.length ? this.html : String.format('{0}',this.text);
20873 el.innerHTML = String.format(
20874 '<img src="{0}" class="x-menu-item-icon {1}" />' + html,
20875 this.icon || Roo.BLANK_IMAGE_URL, this.iconCls || '');
20877 Roo.menu.Item.superclass.onRender.call(this, container, position);
20881 * Sets the text to display in this menu item
20882 * @param {String} text The text to display
20883 * @param {Boolean} isHTML true to indicate text is pure html.
20885 setText : function(text, isHTML){
20893 var html = this.html.length ? this.html : String.format('{0}',this.text);
20895 this.el.update(String.format(
20896 '<img src="{0}" class="x-menu-item-icon {2}">' + html,
20897 this.icon || Roo.BLANK_IMAGE_URL, this.text, this.iconCls || ''));
20898 this.parentMenu.autoWidth();
20903 handleClick : function(e){
20904 if(!this.href){ // if no link defined, stop the event automatically
20907 Roo.menu.Item.superclass.handleClick.apply(this, arguments);
20911 activate : function(autoExpand){
20912 if(Roo.menu.Item.superclass.activate.apply(this, arguments)){
20922 shouldDeactivate : function(e){
20923 if(Roo.menu.Item.superclass.shouldDeactivate.call(this, e)){
20924 if(this.menu && this.menu.isVisible()){
20925 return !this.menu.getEl().getRegion().contains(e.getPoint());
20933 deactivate : function(){
20934 Roo.menu.Item.superclass.deactivate.apply(this, arguments);
20939 expandMenu : function(autoActivate){
20940 if(!this.disabled && this.menu){
20941 clearTimeout(this.hideTimer);
20942 delete this.hideTimer;
20943 if(!this.menu.isVisible() && !this.showTimer){
20944 this.showTimer = this.deferExpand.defer(this.showDelay, this, [autoActivate]);
20945 }else if (this.menu.isVisible() && autoActivate){
20946 this.menu.tryActivate(0, 1);
20952 deferExpand : function(autoActivate){
20953 delete this.showTimer;
20954 this.menu.show(this.container, this.parentMenu.subMenuAlign || "tl-tr?", this.parentMenu);
20956 this.menu.tryActivate(0, 1);
20961 hideMenu : function(){
20962 clearTimeout(this.showTimer);
20963 delete this.showTimer;
20964 if(!this.hideTimer && this.menu && this.menu.isVisible()){
20965 this.hideTimer = this.deferHide.defer(this.hideDelay, this);
20970 deferHide : function(){
20971 delete this.hideTimer;
20976 * Ext JS Library 1.1.1
20977 * Copyright(c) 2006-2007, Ext JS, LLC.
20979 * Originally Released Under LGPL - original licence link has changed is not relivant.
20982 * <script type="text/javascript">
20986 * @class Roo.menu.CheckItem
20987 * @extends Roo.menu.Item
20988 * Adds a menu item that contains a checkbox by default, but can also be part of a radio group.
20990 * Creates a new CheckItem
20991 * @param {Object} config Configuration options
20993 Roo.menu.CheckItem = function(config){
20994 Roo.menu.CheckItem.superclass.constructor.call(this, config);
20997 * @event beforecheckchange
20998 * Fires before the checked value is set, providing an opportunity to cancel if needed
20999 * @param {Roo.menu.CheckItem} this
21000 * @param {Boolean} checked The new checked value that will be set
21002 "beforecheckchange" : true,
21004 * @event checkchange
21005 * Fires after the checked value has been set
21006 * @param {Roo.menu.CheckItem} this
21007 * @param {Boolean} checked The checked value that was set
21009 "checkchange" : true
21011 if(this.checkHandler){
21012 this.on('checkchange', this.checkHandler, this.scope);
21015 Roo.extend(Roo.menu.CheckItem, Roo.menu.Item, {
21017 * @cfg {String} group
21018 * All check items with the same group name will automatically be grouped into a single-select
21019 * radio button group (defaults to '')
21022 * @cfg {String} itemCls The default CSS class to use for check items (defaults to "x-menu-item x-menu-check-item")
21024 itemCls : "x-menu-item x-menu-check-item",
21026 * @cfg {String} groupClass The default CSS class to use for radio group check items (defaults to "x-menu-group-item")
21028 groupClass : "x-menu-group-item",
21031 * @cfg {Boolean} checked True to initialize this checkbox as checked (defaults to false). Note that
21032 * if this checkbox is part of a radio group (group = true) only the last item in the group that is
21033 * initialized with checked = true will be rendered as checked.
21038 ctype: "Roo.menu.CheckItem",
21041 onRender : function(c){
21042 Roo.menu.CheckItem.superclass.onRender.apply(this, arguments);
21044 this.el.addClass(this.groupClass);
21046 Roo.menu.MenuMgr.registerCheckable(this);
21048 this.checked = false;
21049 this.setChecked(true, true);
21054 destroy : function(){
21056 Roo.menu.MenuMgr.unregisterCheckable(this);
21058 Roo.menu.CheckItem.superclass.destroy.apply(this, arguments);
21062 * Set the checked state of this item
21063 * @param {Boolean} checked The new checked value
21064 * @param {Boolean} suppressEvent (optional) True to prevent the checkchange event from firing (defaults to false)
21066 setChecked : function(state, suppressEvent){
21067 if(this.checked != state && this.fireEvent("beforecheckchange", this, state) !== false){
21068 if(this.container){
21069 this.container[state ? "addClass" : "removeClass"]("x-menu-item-checked");
21071 this.checked = state;
21072 if(suppressEvent !== true){
21073 this.fireEvent("checkchange", this, state);
21079 handleClick : function(e){
21080 if(!this.disabled && !(this.checked && this.group)){// disable unselect on radio item
21081 this.setChecked(!this.checked);
21083 Roo.menu.CheckItem.superclass.handleClick.apply(this, arguments);
21087 * Ext JS Library 1.1.1
21088 * Copyright(c) 2006-2007, Ext JS, LLC.
21090 * Originally Released Under LGPL - original licence link has changed is not relivant.
21093 * <script type="text/javascript">
21097 * @class Roo.menu.DateItem
21098 * @extends Roo.menu.Adapter
21099 * A menu item that wraps the {@link Roo.DatPicker} component.
21101 * Creates a new DateItem
21102 * @param {Object} config Configuration options
21104 Roo.menu.DateItem = function(config){
21105 Roo.menu.DateItem.superclass.constructor.call(this, new Roo.DatePicker(config), config);
21106 /** The Roo.DatePicker object @type Roo.DatePicker */
21107 this.picker = this.component;
21108 this.addEvents({select: true});
21110 this.picker.on("render", function(picker){
21111 picker.getEl().swallowEvent("click");
21112 picker.container.addClass("x-menu-date-item");
21115 this.picker.on("select", this.onSelect, this);
21118 Roo.extend(Roo.menu.DateItem, Roo.menu.Adapter, {
21120 onSelect : function(picker, date){
21121 this.fireEvent("select", this, date, picker);
21122 Roo.menu.DateItem.superclass.handleClick.call(this);
21126 * Ext JS Library 1.1.1
21127 * Copyright(c) 2006-2007, Ext JS, LLC.
21129 * Originally Released Under LGPL - original licence link has changed is not relivant.
21132 * <script type="text/javascript">
21136 * @class Roo.menu.ColorItem
21137 * @extends Roo.menu.Adapter
21138 * A menu item that wraps the {@link Roo.ColorPalette} component.
21140 * Creates a new ColorItem
21141 * @param {Object} config Configuration options
21143 Roo.menu.ColorItem = function(config){
21144 Roo.menu.ColorItem.superclass.constructor.call(this, new Roo.ColorPalette(config), config);
21145 /** The Roo.ColorPalette object @type Roo.ColorPalette */
21146 this.palette = this.component;
21147 this.relayEvents(this.palette, ["select"]);
21148 if(this.selectHandler){
21149 this.on('select', this.selectHandler, this.scope);
21152 Roo.extend(Roo.menu.ColorItem, Roo.menu.Adapter);/*
21154 * Ext JS Library 1.1.1
21155 * Copyright(c) 2006-2007, Ext JS, LLC.
21157 * Originally Released Under LGPL - original licence link has changed is not relivant.
21160 * <script type="text/javascript">
21165 * @class Roo.menu.DateMenu
21166 * @extends Roo.menu.Menu
21167 * A menu containing a {@link Roo.menu.DateItem} component (which provides a date picker).
21169 * Creates a new DateMenu
21170 * @param {Object} config Configuration options
21172 Roo.menu.DateMenu = function(config){
21173 Roo.menu.DateMenu.superclass.constructor.call(this, config);
21175 var di = new Roo.menu.DateItem(config);
21178 * The {@link Roo.DatePicker} instance for this DateMenu
21181 this.picker = di.picker;
21184 * @param {DatePicker} picker
21185 * @param {Date} date
21187 this.relayEvents(di, ["select"]);
21188 this.on('beforeshow', function(){
21190 this.picker.hideMonthPicker(false);
21194 Roo.extend(Roo.menu.DateMenu, Roo.menu.Menu, {
21198 * Ext JS Library 1.1.1
21199 * Copyright(c) 2006-2007, Ext JS, LLC.
21201 * Originally Released Under LGPL - original licence link has changed is not relivant.
21204 * <script type="text/javascript">
21209 * @class Roo.menu.ColorMenu
21210 * @extends Roo.menu.Menu
21211 * A menu containing a {@link Roo.menu.ColorItem} component (which provides a basic color picker).
21213 * Creates a new ColorMenu
21214 * @param {Object} config Configuration options
21216 Roo.menu.ColorMenu = function(config){
21217 Roo.menu.ColorMenu.superclass.constructor.call(this, config);
21219 var ci = new Roo.menu.ColorItem(config);
21222 * The {@link Roo.ColorPalette} instance for this ColorMenu
21223 * @type ColorPalette
21225 this.palette = ci.palette;
21228 * @param {ColorPalette} palette
21229 * @param {String} color
21231 this.relayEvents(ci, ["select"]);
21233 Roo.extend(Roo.menu.ColorMenu, Roo.menu.Menu);/*
21235 * Ext JS Library 1.1.1
21236 * Copyright(c) 2006-2007, Ext JS, LLC.
21238 * Originally Released Under LGPL - original licence link has changed is not relivant.
21241 * <script type="text/javascript">
21245 * @class Roo.form.Field
21246 * @extends Roo.BoxComponent
21247 * Base class for form fields that provides default event handling, sizing, value handling and other functionality.
21249 * Creates a new Field
21250 * @param {Object} config Configuration options
21252 Roo.form.Field = function(config){
21253 Roo.form.Field.superclass.constructor.call(this, config);
21256 Roo.extend(Roo.form.Field, Roo.BoxComponent, {
21258 * @cfg {String} fieldLabel Label to use when rendering a form.
21261 * @cfg {String} qtip Mouse over tip
21265 * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
21267 invalidClass : "x-form-invalid",
21269 * @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")
21271 invalidText : "The value in this field is invalid",
21273 * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
21275 focusClass : "x-form-focus",
21277 * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
21278 automatic validation (defaults to "keyup").
21280 validationEvent : "keyup",
21282 * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
21284 validateOnBlur : true,
21286 * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
21288 validationDelay : 250,
21290 * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
21291 * {tag: "input", type: "text", size: "20", autocomplete: "off"})
21293 defaultAutoCreate : {tag: "input", type: "text", size: "20", autocomplete: "off"},
21295 * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field")
21297 fieldClass : "x-form-field",
21299 * @cfg {String} msgTarget The location where error text should display. Should be one of the following values (defaults to 'qtip'):
21302 ----------- ----------------------------------------------------------------------
21303 qtip Display a quick tip when the user hovers over the field
21304 title Display a default browser title attribute popup
21305 under Add a block div beneath the field containing the error text
21306 side Add an error icon to the right of the field with a popup on hover
21307 [element id] Add the error text directly to the innerHTML of the specified element
21310 msgTarget : 'qtip',
21312 * @cfg {String} msgFx <b>Experimental</b> The effect used when displaying a validation message under the field (defaults to 'normal').
21317 * @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.
21322 * @cfg {Boolean} disabled True to disable the field (defaults to false).
21327 * @cfg {String} inputType The type attribute for input fields -- e.g. radio, text, password (defaults to "text").
21329 inputType : undefined,
21332 * @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).
21334 tabIndex : undefined,
21337 isFormField : true,
21342 * @property {Roo.Element} fieldEl
21343 * Element Containing the rendered Field (with label etc.)
21346 * @cfg {Mixed} value A value to initialize this field with.
21351 * @cfg {String} name The field's HTML name attribute.
21354 * @cfg {String} cls A CSS class to apply to the field's underlying element.
21358 initComponent : function(){
21359 Roo.form.Field.superclass.initComponent.call(this);
21363 * Fires when this field receives input focus.
21364 * @param {Roo.form.Field} this
21369 * Fires when this field loses input focus.
21370 * @param {Roo.form.Field} this
21374 * @event specialkey
21375 * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed. You can check
21376 * {@link Roo.EventObject#getKey} to determine which key was pressed.
21377 * @param {Roo.form.Field} this
21378 * @param {Roo.EventObject} e The event object
21383 * Fires just before the field blurs if the field value has changed.
21384 * @param {Roo.form.Field} this
21385 * @param {Mixed} newValue The new value
21386 * @param {Mixed} oldValue The original value
21391 * Fires after the field has been marked as invalid.
21392 * @param {Roo.form.Field} this
21393 * @param {String} msg The validation message
21398 * Fires after the field has been validated with no errors.
21399 * @param {Roo.form.Field} this
21404 * Fires after the key up
21405 * @param {Roo.form.Field} this
21406 * @param {Roo.EventObject} e The event Object
21413 * Returns the name attribute of the field if available
21414 * @return {String} name The field name
21416 getName: function(){
21417 return this.rendered && this.el.dom.name ? this.el.dom.name : (this.hiddenName || '');
21421 onRender : function(ct, position){
21422 Roo.form.Field.superclass.onRender.call(this, ct, position);
21424 var cfg = this.getAutoCreate();
21426 cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
21428 if (!cfg.name.length) {
21431 if(this.inputType){
21432 cfg.type = this.inputType;
21434 this.el = ct.createChild(cfg, position);
21436 var type = this.el.dom.type;
21438 if(type == 'password'){
21441 this.el.addClass('x-form-'+type);
21444 this.el.dom.readOnly = true;
21446 if(this.tabIndex !== undefined){
21447 this.el.dom.setAttribute('tabIndex', this.tabIndex);
21450 this.el.addClass([this.fieldClass, this.cls]);
21455 * Apply the behaviors of this component to an existing element. <b>This is used instead of render().</b>
21456 * @param {String/HTMLElement/Element} el The id of the node, a DOM node or an existing Element
21457 * @return {Roo.form.Field} this
21459 applyTo : function(target){
21460 this.allowDomMove = false;
21461 this.el = Roo.get(target);
21462 this.render(this.el.dom.parentNode);
21467 initValue : function(){
21468 if(this.value !== undefined){
21469 this.setValue(this.value);
21470 }else if(this.el.dom.value.length > 0){
21471 this.setValue(this.el.dom.value);
21476 * Returns true if this field has been changed since it was originally loaded and is not disabled.
21478 isDirty : function() {
21479 if(this.disabled) {
21482 return String(this.getValue()) !== String(this.originalValue);
21486 afterRender : function(){
21487 Roo.form.Field.superclass.afterRender.call(this);
21492 fireKey : function(e){
21493 //Roo.log('field ' + e.getKey());
21494 if(e.isNavKeyPress()){
21495 this.fireEvent("specialkey", this, e);
21500 * Resets the current field value to the originally loaded value and clears any validation messages
21502 reset : function(){
21503 this.setValue(this.originalValue);
21504 this.clearInvalid();
21508 initEvents : function(){
21509 // safari killled keypress - so keydown is now used..
21510 this.el.on("keydown" , this.fireKey, this);
21511 this.el.on("focus", this.onFocus, this);
21512 this.el.on("blur", this.onBlur, this);
21513 this.el.relayEvent('keyup', this);
21515 // reference to original value for reset
21516 this.originalValue = this.getValue();
21520 onFocus : function(){
21521 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
21522 this.el.addClass(this.focusClass);
21524 if(!this.hasFocus){
21525 this.hasFocus = true;
21526 this.startValue = this.getValue();
21527 this.fireEvent("focus", this);
21531 beforeBlur : Roo.emptyFn,
21534 onBlur : function(){
21536 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
21537 this.el.removeClass(this.focusClass);
21539 this.hasFocus = false;
21540 if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
21543 var v = this.getValue();
21544 if(String(v) !== String(this.startValue)){
21545 this.fireEvent('change', this, v, this.startValue);
21547 this.fireEvent("blur", this);
21551 * Returns whether or not the field value is currently valid
21552 * @param {Boolean} preventMark True to disable marking the field invalid
21553 * @return {Boolean} True if the value is valid, else false
21555 isValid : function(preventMark){
21559 var restore = this.preventMark;
21560 this.preventMark = preventMark === true;
21561 var v = this.validateValue(this.processValue(this.getRawValue()));
21562 this.preventMark = restore;
21567 * Validates the field value
21568 * @return {Boolean} True if the value is valid, else false
21570 validate : function(){
21571 if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
21572 this.clearInvalid();
21578 processValue : function(value){
21583 // Subclasses should provide the validation implementation by overriding this
21584 validateValue : function(value){
21589 * Mark this field as invalid
21590 * @param {String} msg The validation message
21592 markInvalid : function(msg){
21593 if(!this.rendered || this.preventMark){ // not rendered
21596 this.el.addClass(this.invalidClass);
21597 msg = msg || this.invalidText;
21598 switch(this.msgTarget){
21600 this.el.dom.qtip = msg;
21601 this.el.dom.qclass = 'x-form-invalid-tip';
21602 if(Roo.QuickTips){ // fix for floating editors interacting with DND
21603 Roo.QuickTips.enable();
21607 this.el.dom.title = msg;
21611 var elp = this.el.findParent('.x-form-element', 5, true);
21612 this.errorEl = elp.createChild({cls:'x-form-invalid-msg'});
21613 this.errorEl.setWidth(elp.getWidth(true)-20);
21615 this.errorEl.update(msg);
21616 Roo.form.Field.msgFx[this.msgFx].show(this.errorEl, this);
21619 if(!this.errorIcon){
21620 var elp = this.el.findParent('.x-form-element', 5, true);
21621 this.errorIcon = elp.createChild({cls:'x-form-invalid-icon'});
21623 this.alignErrorIcon();
21624 this.errorIcon.dom.qtip = msg;
21625 this.errorIcon.dom.qclass = 'x-form-invalid-tip';
21626 this.errorIcon.show();
21627 this.on('resize', this.alignErrorIcon, this);
21630 var t = Roo.getDom(this.msgTarget);
21632 t.style.display = this.msgDisplay;
21635 this.fireEvent('invalid', this, msg);
21639 alignErrorIcon : function(){
21640 this.errorIcon.alignTo(this.el, 'tl-tr', [2, 0]);
21644 * Clear any invalid styles/messages for this field
21646 clearInvalid : function(){
21647 if(!this.rendered || this.preventMark){ // not rendered
21650 this.el.removeClass(this.invalidClass);
21651 switch(this.msgTarget){
21653 this.el.dom.qtip = '';
21656 this.el.dom.title = '';
21660 Roo.form.Field.msgFx[this.msgFx].hide(this.errorEl, this);
21664 if(this.errorIcon){
21665 this.errorIcon.dom.qtip = '';
21666 this.errorIcon.hide();
21667 this.un('resize', this.alignErrorIcon, this);
21671 var t = Roo.getDom(this.msgTarget);
21673 t.style.display = 'none';
21676 this.fireEvent('valid', this);
21680 * Returns the raw data value which may or may not be a valid, defined value. To return a normalized value see {@link #getValue}.
21681 * @return {Mixed} value The field value
21683 getRawValue : function(){
21684 var v = this.el.getValue();
21690 * Returns the normalized data value (undefined or emptyText will be returned as ''). To return the raw value see {@link #getRawValue}.
21691 * @return {Mixed} value The field value
21693 getValue : function(){
21694 var v = this.el.getValue();
21700 * Sets the underlying DOM field's value directly, bypassing validation. To set the value with validation see {@link #setValue}.
21701 * @param {Mixed} value The value to set
21703 setRawValue : function(v){
21704 return this.el.dom.value = (v === null || v === undefined ? '' : v);
21708 * Sets a data value into the field and validates it. To set the value directly without validation see {@link #setRawValue}.
21709 * @param {Mixed} value The value to set
21711 setValue : function(v){
21714 this.el.dom.value = (v === null || v === undefined ? '' : v);
21719 adjustSize : function(w, h){
21720 var s = Roo.form.Field.superclass.adjustSize.call(this, w, h);
21721 s.width = this.adjustWidth(this.el.dom.tagName, s.width);
21725 adjustWidth : function(tag, w){
21726 tag = tag.toLowerCase();
21727 if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
21728 if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
21729 if(tag == 'input'){
21732 if(tag == 'textarea'){
21735 }else if(Roo.isOpera){
21736 if(tag == 'input'){
21739 if(tag == 'textarea'){
21749 // anything other than normal should be considered experimental
21750 Roo.form.Field.msgFx = {
21752 show: function(msgEl, f){
21753 msgEl.setDisplayed('block');
21756 hide : function(msgEl, f){
21757 msgEl.setDisplayed(false).update('');
21762 show: function(msgEl, f){
21763 msgEl.slideIn('t', {stopFx:true});
21766 hide : function(msgEl, f){
21767 msgEl.slideOut('t', {stopFx:true,useDisplay:true});
21772 show: function(msgEl, f){
21773 msgEl.fixDisplay();
21774 msgEl.alignTo(f.el, 'tl-tr');
21775 msgEl.slideIn('l', {stopFx:true});
21778 hide : function(msgEl, f){
21779 msgEl.slideOut('l', {stopFx:true,useDisplay:true});
21784 * Ext JS Library 1.1.1
21785 * Copyright(c) 2006-2007, Ext JS, LLC.
21787 * Originally Released Under LGPL - original licence link has changed is not relivant.
21790 * <script type="text/javascript">
21795 * @class Roo.form.TextField
21796 * @extends Roo.form.Field
21797 * Basic text field. Can be used as a direct replacement for traditional text inputs, or as the base
21798 * class for more sophisticated input controls (like {@link Roo.form.TextArea} and {@link Roo.form.ComboBox}).
21800 * Creates a new TextField
21801 * @param {Object} config Configuration options
21803 Roo.form.TextField = function(config){
21804 Roo.form.TextField.superclass.constructor.call(this, config);
21808 * Fires when the autosize function is triggered. The field may or may not have actually changed size
21809 * according to the default logic, but this event provides a hook for the developer to apply additional
21810 * logic at runtime to resize the field if needed.
21811 * @param {Roo.form.Field} this This text field
21812 * @param {Number} width The new field width
21818 Roo.extend(Roo.form.TextField, Roo.form.Field, {
21820 * @cfg {Boolean} grow True if this field should automatically grow and shrink to its content
21824 * @cfg {Number} growMin The minimum width to allow when grow = true (defaults to 30)
21828 * @cfg {Number} growMax The maximum width to allow when grow = true (defaults to 800)
21832 * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
21836 * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
21840 * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
21842 disableKeyFilter : false,
21844 * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
21848 * @cfg {Number} minLength Minimum input field length required (defaults to 0)
21852 * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
21854 maxLength : Number.MAX_VALUE,
21856 * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
21858 minLengthText : "The minimum length for this field is {0}",
21860 * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
21862 maxLengthText : "The maximum length for this field is {0}",
21864 * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
21866 selectOnFocus : false,
21868 * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
21870 blankText : "This field is required",
21872 * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
21873 * If available, this function will be called only after the basic validators all return true, and will be passed the
21874 * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
21878 * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
21879 * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
21880 * current field value. If the test fails, the field will be marked invalid using {@link #regexText}.
21884 * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
21888 * @cfg {String} emptyText The default text to display in an empty field - placeholder... (defaults to null).
21894 initEvents : function()
21896 if (this.emptyText) {
21897 this.el.attr('placeholder', this.emptyText);
21900 Roo.form.TextField.superclass.initEvents.call(this);
21901 if(this.validationEvent == 'keyup'){
21902 this.validationTask = new Roo.util.DelayedTask(this.validate, this);
21903 this.el.on('keyup', this.filterValidation, this);
21905 else if(this.validationEvent !== false){
21906 this.el.on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
21909 if(this.selectOnFocus){
21910 this.on("focus", this.preFocus, this);
21913 if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
21914 this.el.on("keypress", this.filterKeys, this);
21917 this.el.on("keyup", this.onKeyUp, this, {buffer:50});
21918 this.el.on("click", this.autoSize, this);
21920 if(this.el.is('input[type=password]') && Roo.isSafari){
21921 this.el.on('keydown', this.SafariOnKeyDown, this);
21925 processValue : function(value){
21926 if(this.stripCharsRe){
21927 var newValue = value.replace(this.stripCharsRe, '');
21928 if(newValue !== value){
21929 this.setRawValue(newValue);
21936 filterValidation : function(e){
21937 if(!e.isNavKeyPress()){
21938 this.validationTask.delay(this.validationDelay);
21943 onKeyUp : function(e){
21944 if(!e.isNavKeyPress()){
21950 * Resets the current field value to the originally-loaded value and clears any validation messages.
21953 reset : function(){
21954 Roo.form.TextField.superclass.reset.call(this);
21960 preFocus : function(){
21962 if(this.selectOnFocus){
21963 this.el.dom.select();
21969 filterKeys : function(e){
21970 var k = e.getKey();
21971 if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
21974 var c = e.getCharCode(), cc = String.fromCharCode(c);
21975 if(Roo.isIE && (e.isSpecialKey() || !cc)){
21978 if(!this.maskRe.test(cc)){
21983 setValue : function(v){
21985 Roo.form.TextField.superclass.setValue.apply(this, arguments);
21991 * Validates a value according to the field's validation rules and marks the field as invalid
21992 * if the validation fails
21993 * @param {Mixed} value The value to validate
21994 * @return {Boolean} True if the value is valid, else false
21996 validateValue : function(value){
21997 if(value.length < 1) { // if it's blank
21998 if(this.allowBlank){
21999 this.clearInvalid();
22002 this.markInvalid(this.blankText);
22006 if(value.length < this.minLength){
22007 this.markInvalid(String.format(this.minLengthText, this.minLength));
22010 if(value.length > this.maxLength){
22011 this.markInvalid(String.format(this.maxLengthText, this.maxLength));
22015 var vt = Roo.form.VTypes;
22016 if(!vt[this.vtype](value, this)){
22017 this.markInvalid(this.vtypeText || vt[this.vtype +'Text']);
22021 if(typeof this.validator == "function"){
22022 var msg = this.validator(value);
22024 this.markInvalid(msg);
22028 if(this.regex && !this.regex.test(value)){
22029 this.markInvalid(this.regexText);
22036 * Selects text in this field
22037 * @param {Number} start (optional) The index where the selection should start (defaults to 0)
22038 * @param {Number} end (optional) The index where the selection should end (defaults to the text length)
22040 selectText : function(start, end){
22041 var v = this.getRawValue();
22043 start = start === undefined ? 0 : start;
22044 end = end === undefined ? v.length : end;
22045 var d = this.el.dom;
22046 if(d.setSelectionRange){
22047 d.setSelectionRange(start, end);
22048 }else if(d.createTextRange){
22049 var range = d.createTextRange();
22050 range.moveStart("character", start);
22051 range.moveEnd("character", v.length-end);
22058 * Automatically grows the field to accomodate the width of the text up to the maximum field width allowed.
22059 * This only takes effect if grow = true, and fires the autosize event.
22061 autoSize : function(){
22062 if(!this.grow || !this.rendered){
22066 this.metrics = Roo.util.TextMetrics.createInstance(this.el);
22069 var v = el.dom.value;
22070 var d = document.createElement('div');
22071 d.appendChild(document.createTextNode(v));
22075 var w = Math.min(this.growMax, Math.max(this.metrics.getWidth(v) + /* add extra padding */ 10, this.growMin));
22076 this.el.setWidth(w);
22077 this.fireEvent("autosize", this, w);
22081 SafariOnKeyDown : function(event)
22083 // this is a workaround for a password hang bug on chrome/ webkit.
22085 var isSelectAll = false;
22087 if(this.el.dom.selectionEnd > 0){
22088 isSelectAll = (this.el.dom.selectionEnd - this.el.dom.selectionStart - this.getValue().length == 0) ? true : false;
22090 if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
22091 event.preventDefault();
22096 if(isSelectAll){ // backspace and delete key
22098 event.preventDefault();
22099 // this is very hacky as keydown always get's upper case.
22101 var cc = String.fromCharCode(event.getCharCode());
22102 this.setValue( event.shiftKey ? cc : cc.toLowerCase());
22110 * Ext JS Library 1.1.1
22111 * Copyright(c) 2006-2007, Ext JS, LLC.
22113 * Originally Released Under LGPL - original licence link has changed is not relivant.
22116 * <script type="text/javascript">
22120 * @class Roo.form.Hidden
22121 * @extends Roo.form.TextField
22122 * Simple Hidden element used on forms
22124 * usage: form.add(new Roo.form.HiddenField({ 'name' : 'test1' }));
22127 * Creates a new Hidden form element.
22128 * @param {Object} config Configuration options
22133 // easy hidden field...
22134 Roo.form.Hidden = function(config){
22135 Roo.form.Hidden.superclass.constructor.call(this, config);
22138 Roo.extend(Roo.form.Hidden, Roo.form.TextField, {
22140 inputType: 'hidden',
22143 labelSeparator: '',
22145 itemCls : 'x-form-item-display-none'
22153 * Ext JS Library 1.1.1
22154 * Copyright(c) 2006-2007, Ext JS, LLC.
22156 * Originally Released Under LGPL - original licence link has changed is not relivant.
22159 * <script type="text/javascript">
22163 * @class Roo.form.TriggerField
22164 * @extends Roo.form.TextField
22165 * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
22166 * The trigger has no default action, so you must assign a function to implement the trigger click handler by
22167 * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
22168 * for which you can provide a custom implementation. For example:
22170 var trigger = new Roo.form.TriggerField();
22171 trigger.onTriggerClick = myTriggerFn;
22172 trigger.applyTo('my-field');
22175 * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
22176 * {@link Roo.form.DateField} and {@link Roo.form.ComboBox} are perfect examples of this.
22177 * @cfg {String} triggerClass An additional CSS class used to style the trigger button. The trigger will always get the
22178 * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
22180 * Create a new TriggerField.
22181 * @param {Object} config Configuration options (valid {@Roo.form.TextField} config options will also be applied
22182 * to the base TextField)
22184 Roo.form.TriggerField = function(config){
22185 this.mimicing = false;
22186 Roo.form.TriggerField.superclass.constructor.call(this, config);
22189 Roo.extend(Roo.form.TriggerField, Roo.form.TextField, {
22191 * @cfg {String} triggerClass A CSS class to apply to the trigger
22194 * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
22195 * {tag: "input", type: "text", size: "16", autocomplete: "off"})
22197 defaultAutoCreate : {tag: "input", type: "text", size: "16", autocomplete: "off"},
22199 * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
22203 /** @cfg {Boolean} grow @hide */
22204 /** @cfg {Number} growMin @hide */
22205 /** @cfg {Number} growMax @hide */
22211 autoSize: Roo.emptyFn,
22215 deferHeight : true,
22218 actionMode : 'wrap',
22220 onResize : function(w, h){
22221 Roo.form.TriggerField.superclass.onResize.apply(this, arguments);
22222 if(typeof w == 'number'){
22223 var x = w - this.trigger.getWidth();
22224 this.el.setWidth(this.adjustWidth('input', x));
22225 this.trigger.setStyle('left', x+'px');
22230 adjustSize : Roo.BoxComponent.prototype.adjustSize,
22233 getResizeEl : function(){
22238 getPositionEl : function(){
22243 alignErrorIcon : function(){
22244 this.errorIcon.alignTo(this.wrap, 'tl-tr', [2, 0]);
22248 onRender : function(ct, position){
22249 Roo.form.TriggerField.superclass.onRender.call(this, ct, position);
22250 this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
22251 this.trigger = this.wrap.createChild(this.triggerConfig ||
22252 {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.triggerClass});
22253 if(this.hideTrigger){
22254 this.trigger.setDisplayed(false);
22256 this.initTrigger();
22258 this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
22263 initTrigger : function(){
22264 this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
22265 this.trigger.addClassOnOver('x-form-trigger-over');
22266 this.trigger.addClassOnClick('x-form-trigger-click');
22270 onDestroy : function(){
22272 this.trigger.removeAllListeners();
22273 this.trigger.remove();
22276 this.wrap.remove();
22278 Roo.form.TriggerField.superclass.onDestroy.call(this);
22282 onFocus : function(){
22283 Roo.form.TriggerField.superclass.onFocus.call(this);
22284 if(!this.mimicing){
22285 this.wrap.addClass('x-trigger-wrap-focus');
22286 this.mimicing = true;
22287 Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
22288 if(this.monitorTab){
22289 this.el.on("keydown", this.checkTab, this);
22295 checkTab : function(e){
22296 if(e.getKey() == e.TAB){
22297 this.triggerBlur();
22302 onBlur : function(){
22307 mimicBlur : function(e, t){
22308 if(!this.wrap.contains(t) && this.validateBlur()){
22309 this.triggerBlur();
22314 triggerBlur : function(){
22315 this.mimicing = false;
22316 Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
22317 if(this.monitorTab){
22318 this.el.un("keydown", this.checkTab, this);
22320 this.wrap.removeClass('x-trigger-wrap-focus');
22321 Roo.form.TriggerField.superclass.onBlur.call(this);
22325 // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
22326 validateBlur : function(e, t){
22331 onDisable : function(){
22332 Roo.form.TriggerField.superclass.onDisable.call(this);
22334 this.wrap.addClass('x-item-disabled');
22339 onEnable : function(){
22340 Roo.form.TriggerField.superclass.onEnable.call(this);
22342 this.wrap.removeClass('x-item-disabled');
22347 onShow : function(){
22348 var ae = this.getActionEl();
22351 ae.dom.style.display = '';
22352 ae.dom.style.visibility = 'visible';
22358 onHide : function(){
22359 var ae = this.getActionEl();
22360 ae.dom.style.display = 'none';
22364 * The function that should handle the trigger's click event. This method does nothing by default until overridden
22365 * by an implementing function.
22367 * @param {EventObject} e
22369 onTriggerClick : Roo.emptyFn
22372 // TwinTriggerField is not a public class to be used directly. It is meant as an abstract base class
22373 // to be extended by an implementing class. For an example of implementing this class, see the custom
22374 // SearchField implementation here: http://extjs.com/deploy/ext/examples/form/custom.html
22375 Roo.form.TwinTriggerField = Roo.extend(Roo.form.TriggerField, {
22376 initComponent : function(){
22377 Roo.form.TwinTriggerField.superclass.initComponent.call(this);
22379 this.triggerConfig = {
22380 tag:'span', cls:'x-form-twin-triggers', cn:[
22381 {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger1Class},
22382 {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger2Class}
22386 getTrigger : function(index){
22387 return this.triggers[index];
22390 initTrigger : function(){
22391 var ts = this.trigger.select('.x-form-trigger', true);
22392 this.wrap.setStyle('overflow', 'hidden');
22393 var triggerField = this;
22394 ts.each(function(t, all, index){
22395 t.hide = function(){
22396 var w = triggerField.wrap.getWidth();
22397 this.dom.style.display = 'none';
22398 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
22400 t.show = function(){
22401 var w = triggerField.wrap.getWidth();
22402 this.dom.style.display = '';
22403 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
22405 var triggerIndex = 'Trigger'+(index+1);
22407 if(this['hide'+triggerIndex]){
22408 t.dom.style.display = 'none';
22410 t.on("click", this['on'+triggerIndex+'Click'], this, {preventDefault:true});
22411 t.addClassOnOver('x-form-trigger-over');
22412 t.addClassOnClick('x-form-trigger-click');
22414 this.triggers = ts.elements;
22417 onTrigger1Click : Roo.emptyFn,
22418 onTrigger2Click : Roo.emptyFn
22421 * Ext JS Library 1.1.1
22422 * Copyright(c) 2006-2007, Ext JS, LLC.
22424 * Originally Released Under LGPL - original licence link has changed is not relivant.
22427 * <script type="text/javascript">
22431 * @class Roo.form.TextArea
22432 * @extends Roo.form.TextField
22433 * Multiline text field. Can be used as a direct replacement for traditional textarea fields, plus adds
22434 * support for auto-sizing.
22436 * Creates a new TextArea
22437 * @param {Object} config Configuration options
22439 Roo.form.TextArea = function(config){
22440 Roo.form.TextArea.superclass.constructor.call(this, config);
22441 // these are provided exchanges for backwards compat
22442 // minHeight/maxHeight were replaced by growMin/growMax to be
22443 // compatible with TextField growing config values
22444 if(this.minHeight !== undefined){
22445 this.growMin = this.minHeight;
22447 if(this.maxHeight !== undefined){
22448 this.growMax = this.maxHeight;
22452 Roo.extend(Roo.form.TextArea, Roo.form.TextField, {
22454 * @cfg {Number} growMin The minimum height to allow when grow = true (defaults to 60)
22458 * @cfg {Number} growMax The maximum height to allow when grow = true (defaults to 1000)
22462 * @cfg {Boolean} preventScrollbars True to prevent scrollbars from appearing regardless of how much text is
22463 * in the field (equivalent to setting overflow: hidden, defaults to false)
22465 preventScrollbars: false,
22467 * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
22468 * {tag: "textarea", style: "width:300px;height:60px;", autocomplete: "off"})
22472 onRender : function(ct, position){
22474 this.defaultAutoCreate = {
22476 style:"width:300px;height:60px;",
22477 autocomplete: "off"
22480 Roo.form.TextArea.superclass.onRender.call(this, ct, position);
22482 this.textSizeEl = Roo.DomHelper.append(document.body, {
22483 tag: "pre", cls: "x-form-grow-sizer"
22485 if(this.preventScrollbars){
22486 this.el.setStyle("overflow", "hidden");
22488 this.el.setHeight(this.growMin);
22492 onDestroy : function(){
22493 if(this.textSizeEl){
22494 this.textSizeEl.parentNode.removeChild(this.textSizeEl);
22496 Roo.form.TextArea.superclass.onDestroy.call(this);
22500 onKeyUp : function(e){
22501 if(!e.isNavKeyPress() || e.getKey() == e.ENTER){
22507 * Automatically grows the field to accomodate the height of the text up to the maximum field height allowed.
22508 * This only takes effect if grow = true, and fires the autosize event if the height changes.
22510 autoSize : function(){
22511 if(!this.grow || !this.textSizeEl){
22515 var v = el.dom.value;
22516 var ts = this.textSizeEl;
22519 ts.appendChild(document.createTextNode(v));
22522 Roo.fly(ts).setWidth(this.el.getWidth());
22524 v = "  ";
22527 v = v.replace(/\n/g, '<p> </p>');
22529 v += " \n ";
22532 var h = Math.min(this.growMax, Math.max(ts.offsetHeight, this.growMin));
22533 if(h != this.lastHeight){
22534 this.lastHeight = h;
22535 this.el.setHeight(h);
22536 this.fireEvent("autosize", this, h);
22541 * Ext JS Library 1.1.1
22542 * Copyright(c) 2006-2007, Ext JS, LLC.
22544 * Originally Released Under LGPL - original licence link has changed is not relivant.
22547 * <script type="text/javascript">
22552 * @class Roo.form.NumberField
22553 * @extends Roo.form.TextField
22554 * Numeric text field that provides automatic keystroke filtering and numeric validation.
22556 * Creates a new NumberField
22557 * @param {Object} config Configuration options
22559 Roo.form.NumberField = function(config){
22560 Roo.form.NumberField.superclass.constructor.call(this, config);
22563 Roo.extend(Roo.form.NumberField, Roo.form.TextField, {
22565 * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field x-form-num-field")
22567 fieldClass: "x-form-field x-form-num-field",
22569 * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
22571 allowDecimals : true,
22573 * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
22575 decimalSeparator : ".",
22577 * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
22579 decimalPrecision : 2,
22581 * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
22583 allowNegative : true,
22585 * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
22587 minValue : Number.NEGATIVE_INFINITY,
22589 * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
22591 maxValue : Number.MAX_VALUE,
22593 * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
22595 minText : "The minimum value for this field is {0}",
22597 * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
22599 maxText : "The maximum value for this field is {0}",
22601 * @cfg {String} nanText Error text to display if the value is not a valid number. For example, this can happen
22602 * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
22604 nanText : "{0} is not a valid number",
22607 initEvents : function(){
22608 Roo.form.NumberField.superclass.initEvents.call(this);
22609 var allowed = "0123456789";
22610 if(this.allowDecimals){
22611 allowed += this.decimalSeparator;
22613 if(this.allowNegative){
22616 this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
22617 var keyPress = function(e){
22618 var k = e.getKey();
22619 if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
22622 var c = e.getCharCode();
22623 if(allowed.indexOf(String.fromCharCode(c)) === -1){
22627 this.el.on("keypress", keyPress, this);
22631 validateValue : function(value){
22632 if(!Roo.form.NumberField.superclass.validateValue.call(this, value)){
22635 if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
22638 var num = this.parseValue(value);
22640 this.markInvalid(String.format(this.nanText, value));
22643 if(num < this.minValue){
22644 this.markInvalid(String.format(this.minText, this.minValue));
22647 if(num > this.maxValue){
22648 this.markInvalid(String.format(this.maxText, this.maxValue));
22654 getValue : function(){
22655 return this.fixPrecision(this.parseValue(Roo.form.NumberField.superclass.getValue.call(this)));
22659 parseValue : function(value){
22660 value = parseFloat(String(value).replace(this.decimalSeparator, "."));
22661 return isNaN(value) ? '' : value;
22665 fixPrecision : function(value){
22666 var nan = isNaN(value);
22667 if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
22668 return nan ? '' : value;
22670 return parseFloat(value).toFixed(this.decimalPrecision);
22673 setValue : function(v){
22674 v = this.fixPrecision(v);
22675 Roo.form.NumberField.superclass.setValue.call(this, String(v).replace(".", this.decimalSeparator));
22679 decimalPrecisionFcn : function(v){
22680 return Math.floor(v);
22683 beforeBlur : function(){
22684 var v = this.parseValue(this.getRawValue());
22691 * Ext JS Library 1.1.1
22692 * Copyright(c) 2006-2007, Ext JS, LLC.
22694 * Originally Released Under LGPL - original licence link has changed is not relivant.
22697 * <script type="text/javascript">
22701 * @class Roo.form.DateField
22702 * @extends Roo.form.TriggerField
22703 * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
22705 * Create a new DateField
22706 * @param {Object} config
22708 Roo.form.DateField = function(config){
22709 Roo.form.DateField.superclass.constructor.call(this, config);
22715 * Fires when a date is selected
22716 * @param {Roo.form.DateField} combo This combo box
22717 * @param {Date} date The date selected
22724 if(typeof this.minValue == "string") this.minValue = this.parseDate(this.minValue);
22725 if(typeof this.maxValue == "string") this.maxValue = this.parseDate(this.maxValue);
22726 this.ddMatch = null;
22727 if(this.disabledDates){
22728 var dd = this.disabledDates;
22730 for(var i = 0; i < dd.length; i++){
22732 if(i != dd.length-1) re += "|";
22734 this.ddMatch = new RegExp(re + ")");
22738 Roo.extend(Roo.form.DateField, Roo.form.TriggerField, {
22740 * @cfg {String} format
22741 * The default date format string which can be overriden for localization support. The format must be
22742 * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
22746 * @cfg {String} altFormats
22747 * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
22748 * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
22750 altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
22752 * @cfg {Array} disabledDays
22753 * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
22755 disabledDays : null,
22757 * @cfg {String} disabledDaysText
22758 * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
22760 disabledDaysText : "Disabled",
22762 * @cfg {Array} disabledDates
22763 * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
22764 * expression so they are very powerful. Some examples:
22766 * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
22767 * <li>["03/08", "09/16"] would disable those days for every year</li>
22768 * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
22769 * <li>["03/../2006"] would disable every day in March 2006</li>
22770 * <li>["^03"] would disable every day in every March</li>
22772 * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
22773 * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
22775 disabledDates : null,
22777 * @cfg {String} disabledDatesText
22778 * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
22780 disabledDatesText : "Disabled",
22782 * @cfg {Date/String} minValue
22783 * The minimum allowed date. Can be either a Javascript date object or a string date in a
22784 * valid format (defaults to null).
22788 * @cfg {Date/String} maxValue
22789 * The maximum allowed date. Can be either a Javascript date object or a string date in a
22790 * valid format (defaults to null).
22794 * @cfg {String} minText
22795 * The error text to display when the date in the cell is before minValue (defaults to
22796 * 'The date in this field must be after {minValue}').
22798 minText : "The date in this field must be equal to or after {0}",
22800 * @cfg {String} maxText
22801 * The error text to display when the date in the cell is after maxValue (defaults to
22802 * 'The date in this field must be before {maxValue}').
22804 maxText : "The date in this field must be equal to or before {0}",
22806 * @cfg {String} invalidText
22807 * The error text to display when the date in the field is invalid (defaults to
22808 * '{value} is not a valid date - it must be in the format {format}').
22810 invalidText : "{0} is not a valid date - it must be in the format {1}",
22812 * @cfg {String} triggerClass
22813 * An additional CSS class used to style the trigger button. The trigger will always get the
22814 * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
22815 * which displays a calendar icon).
22817 triggerClass : 'x-form-date-trigger',
22821 * @cfg {Boolean} useIso
22822 * if enabled, then the date field will use a hidden field to store the
22823 * real value as iso formated date. default (false)
22827 * @cfg {String/Object} autoCreate
22828 * A DomHelper element spec, or true for a default element spec (defaults to
22829 * {tag: "input", type: "text", size: "10", autocomplete: "off"})
22832 defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "off"},
22835 hiddenField: false,
22837 onRender : function(ct, position)
22839 Roo.form.DateField.superclass.onRender.call(this, ct, position);
22841 //this.el.dom.removeAttribute('name');
22842 Roo.log("Changing name?");
22843 this.el.dom.setAttribute('name', this.name + '____hidden___' );
22844 this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
22846 this.hiddenField.value = this.value ? this.formatDate(this.value, 'Y-m-d') : '';
22847 // prevent input submission
22848 this.hiddenName = this.name;
22855 validateValue : function(value)
22857 value = this.formatDate(value);
22858 if(!Roo.form.DateField.superclass.validateValue.call(this, value)){
22859 Roo.log('super failed');
22862 if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
22865 var svalue = value;
22866 value = this.parseDate(value);
22868 Roo.log('parse date failed' + svalue);
22869 this.markInvalid(String.format(this.invalidText, svalue, this.format));
22872 var time = value.getTime();
22873 if(this.minValue && time < this.minValue.getTime()){
22874 this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
22877 if(this.maxValue && time > this.maxValue.getTime()){
22878 this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
22881 if(this.disabledDays){
22882 var day = value.getDay();
22883 for(var i = 0; i < this.disabledDays.length; i++) {
22884 if(day === this.disabledDays[i]){
22885 this.markInvalid(this.disabledDaysText);
22890 var fvalue = this.formatDate(value);
22891 if(this.ddMatch && this.ddMatch.test(fvalue)){
22892 this.markInvalid(String.format(this.disabledDatesText, fvalue));
22899 // Provides logic to override the default TriggerField.validateBlur which just returns true
22900 validateBlur : function(){
22901 return !this.menu || !this.menu.isVisible();
22904 getName: function()
22906 // returns hidden if it's set..
22907 if (!this.rendered) {return ''};
22908 return !this.hiddenName && this.el.dom.name ? this.el.dom.name : (this.hiddenName || '');
22913 * Returns the current date value of the date field.
22914 * @return {Date} The date value
22916 getValue : function(){
22918 return this.hiddenField ?
22919 this.hiddenField.value :
22920 this.parseDate(Roo.form.DateField.superclass.getValue.call(this)) || "";
22924 * Sets the value of the date field. You can pass a date object or any string that can be parsed into a valid
22925 * date, using DateField.format as the date format, according to the same rules as {@link Date#parseDate}
22926 * (the default format used is "m/d/y").
22929 //All of these calls set the same date value (May 4, 2006)
22931 //Pass a date object:
22932 var dt = new Date('5/4/06');
22933 dateField.setValue(dt);
22935 //Pass a date string (default format):
22936 dateField.setValue('5/4/06');
22938 //Pass a date string (custom format):
22939 dateField.format = 'Y-m-d';
22940 dateField.setValue('2006-5-4');
22942 * @param {String/Date} date The date or valid date string
22944 setValue : function(date){
22945 if (this.hiddenField) {
22946 this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
22948 Roo.form.DateField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
22949 // make sure the value field is always stored as a date..
22950 this.value = this.parseDate(date);
22956 parseDate : function(value){
22957 if(!value || value instanceof Date){
22960 var v = Date.parseDate(value, this.format);
22961 if (!v && this.useIso) {
22962 v = Date.parseDate(value, 'Y-m-d');
22964 if(!v && this.altFormats){
22965 if(!this.altFormatsArray){
22966 this.altFormatsArray = this.altFormats.split("|");
22968 for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
22969 v = Date.parseDate(value, this.altFormatsArray[i]);
22976 formatDate : function(date, fmt){
22977 return (!date || !(date instanceof Date)) ?
22978 date : date.dateFormat(fmt || this.format);
22983 select: function(m, d){
22986 this.fireEvent('select', this, d);
22988 show : function(){ // retain focus styling
22992 this.focus.defer(10, this);
22993 var ml = this.menuListeners;
22994 this.menu.un("select", ml.select, this);
22995 this.menu.un("show", ml.show, this);
22996 this.menu.un("hide", ml.hide, this);
23001 // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
23002 onTriggerClick : function(){
23006 if(this.menu == null){
23007 this.menu = new Roo.menu.DateMenu();
23009 Roo.apply(this.menu.picker, {
23010 showClear: this.allowBlank,
23011 minDate : this.minValue,
23012 maxDate : this.maxValue,
23013 disabledDatesRE : this.ddMatch,
23014 disabledDatesText : this.disabledDatesText,
23015 disabledDays : this.disabledDays,
23016 disabledDaysText : this.disabledDaysText,
23017 format : this.useIso ? 'Y-m-d' : this.format,
23018 minText : String.format(this.minText, this.formatDate(this.minValue)),
23019 maxText : String.format(this.maxText, this.formatDate(this.maxValue))
23021 this.menu.on(Roo.apply({}, this.menuListeners, {
23024 this.menu.picker.setValue(this.getValue() || new Date());
23025 this.menu.show(this.el, "tl-bl?");
23028 beforeBlur : function(){
23029 var v = this.parseDate(this.getRawValue());
23035 /** @cfg {Boolean} grow @hide */
23036 /** @cfg {Number} growMin @hide */
23037 /** @cfg {Number} growMax @hide */
23044 * Ext JS Library 1.1.1
23045 * Copyright(c) 2006-2007, Ext JS, LLC.
23047 * Originally Released Under LGPL - original licence link has changed is not relivant.
23050 * <script type="text/javascript">
23054 * @class Roo.form.MonthField
23055 * @extends Roo.form.TriggerField
23056 * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
23058 * Create a new MonthField
23059 * @param {Object} config
23061 Roo.form.MonthField = function(config){
23063 Roo.form.MonthField.superclass.constructor.call(this, config);
23069 * Fires when a date is selected
23070 * @param {Roo.form.MonthFieeld} combo This combo box
23071 * @param {Date} date The date selected
23078 if(typeof this.minValue == "string") this.minValue = this.parseDate(this.minValue);
23079 if(typeof this.maxValue == "string") this.maxValue = this.parseDate(this.maxValue);
23080 this.ddMatch = null;
23081 if(this.disabledDates){
23082 var dd = this.disabledDates;
23084 for(var i = 0; i < dd.length; i++){
23086 if(i != dd.length-1) re += "|";
23088 this.ddMatch = new RegExp(re + ")");
23092 Roo.extend(Roo.form.MonthField, Roo.form.TriggerField, {
23094 * @cfg {String} format
23095 * The default date format string which can be overriden for localization support. The format must be
23096 * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
23100 * @cfg {String} altFormats
23101 * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
23102 * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
23104 altFormats : "M Y|m/Y|m-y|m-Y|my|mY",
23106 * @cfg {Array} disabledDays
23107 * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
23109 disabledDays : [0,1,2,3,4,5,6],
23111 * @cfg {String} disabledDaysText
23112 * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
23114 disabledDaysText : "Disabled",
23116 * @cfg {Array} disabledDates
23117 * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
23118 * expression so they are very powerful. Some examples:
23120 * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
23121 * <li>["03/08", "09/16"] would disable those days for every year</li>
23122 * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
23123 * <li>["03/../2006"] would disable every day in March 2006</li>
23124 * <li>["^03"] would disable every day in every March</li>
23126 * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
23127 * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
23129 disabledDates : null,
23131 * @cfg {String} disabledDatesText
23132 * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
23134 disabledDatesText : "Disabled",
23136 * @cfg {Date/String} minValue
23137 * The minimum allowed date. Can be either a Javascript date object or a string date in a
23138 * valid format (defaults to null).
23142 * @cfg {Date/String} maxValue
23143 * The maximum allowed date. Can be either a Javascript date object or a string date in a
23144 * valid format (defaults to null).
23148 * @cfg {String} minText
23149 * The error text to display when the date in the cell is before minValue (defaults to
23150 * 'The date in this field must be after {minValue}').
23152 minText : "The date in this field must be equal to or after {0}",
23154 * @cfg {String} maxTextf
23155 * The error text to display when the date in the cell is after maxValue (defaults to
23156 * 'The date in this field must be before {maxValue}').
23158 maxText : "The date in this field must be equal to or before {0}",
23160 * @cfg {String} invalidText
23161 * The error text to display when the date in the field is invalid (defaults to
23162 * '{value} is not a valid date - it must be in the format {format}').
23164 invalidText : "{0} is not a valid date - it must be in the format {1}",
23166 * @cfg {String} triggerClass
23167 * An additional CSS class used to style the trigger button. The trigger will always get the
23168 * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
23169 * which displays a calendar icon).
23171 triggerClass : 'x-form-date-trigger',
23175 * @cfg {Boolean} useIso
23176 * if enabled, then the date field will use a hidden field to store the
23177 * real value as iso formated date. default (true)
23181 * @cfg {String/Object} autoCreate
23182 * A DomHelper element spec, or true for a default element spec (defaults to
23183 * {tag: "input", type: "text", size: "10", autocomplete: "off"})
23186 defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "off"},
23189 hiddenField: false,
23191 hideMonthPicker : false,
23193 onRender : function(ct, position)
23195 Roo.form.MonthField.superclass.onRender.call(this, ct, position);
23197 this.el.dom.removeAttribute('name');
23198 this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
23200 this.hiddenField.value = this.value ? this.formatDate(this.value, 'Y-m-d') : '';
23201 // prevent input submission
23202 this.hiddenName = this.name;
23209 validateValue : function(value)
23211 value = this.formatDate(value);
23212 if(!Roo.form.MonthField.superclass.validateValue.call(this, value)){
23215 if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
23218 var svalue = value;
23219 value = this.parseDate(value);
23221 this.markInvalid(String.format(this.invalidText, svalue, this.format));
23224 var time = value.getTime();
23225 if(this.minValue && time < this.minValue.getTime()){
23226 this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
23229 if(this.maxValue && time > this.maxValue.getTime()){
23230 this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
23233 /*if(this.disabledDays){
23234 var day = value.getDay();
23235 for(var i = 0; i < this.disabledDays.length; i++) {
23236 if(day === this.disabledDays[i]){
23237 this.markInvalid(this.disabledDaysText);
23243 var fvalue = this.formatDate(value);
23244 /*if(this.ddMatch && this.ddMatch.test(fvalue)){
23245 this.markInvalid(String.format(this.disabledDatesText, fvalue));
23253 // Provides logic to override the default TriggerField.validateBlur which just returns true
23254 validateBlur : function(){
23255 return !this.menu || !this.menu.isVisible();
23259 * Returns the current date value of the date field.
23260 * @return {Date} The date value
23262 getValue : function(){
23266 return this.hiddenField ?
23267 this.hiddenField.value :
23268 this.parseDate(Roo.form.MonthField.superclass.getValue.call(this)) || "";
23272 * Sets the value of the date field. You can pass a date object or any string that can be parsed into a valid
23273 * date, using MonthField.format as the date format, according to the same rules as {@link Date#parseDate}
23274 * (the default format used is "m/d/y").
23277 //All of these calls set the same date value (May 4, 2006)
23279 //Pass a date object:
23280 var dt = new Date('5/4/06');
23281 monthField.setValue(dt);
23283 //Pass a date string (default format):
23284 monthField.setValue('5/4/06');
23286 //Pass a date string (custom format):
23287 monthField.format = 'Y-m-d';
23288 monthField.setValue('2006-5-4');
23290 * @param {String/Date} date The date or valid date string
23292 setValue : function(date){
23293 Roo.log('month setValue' + date);
23294 // can only be first of month..
23296 var val = this.parseDate(date);
23298 if (this.hiddenField) {
23299 this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
23301 Roo.form.MonthField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
23302 this.value = this.parseDate(date);
23306 parseDate : function(value){
23307 if(!value || value instanceof Date){
23308 value = value ? Date.parseDate(value.format('Y-m') + '-01', 'Y-m-d') : null;
23311 var v = Date.parseDate(value, this.format);
23312 if (!v && this.useIso) {
23313 v = Date.parseDate(value, 'Y-m-d');
23317 v = Date.parseDate(v.format('Y-m') +'-01', 'Y-m-d');
23321 if(!v && this.altFormats){
23322 if(!this.altFormatsArray){
23323 this.altFormatsArray = this.altFormats.split("|");
23325 for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
23326 v = Date.parseDate(value, this.altFormatsArray[i]);
23333 formatDate : function(date, fmt){
23334 return (!date || !(date instanceof Date)) ?
23335 date : date.dateFormat(fmt || this.format);
23340 select: function(m, d){
23342 this.fireEvent('select', this, d);
23344 show : function(){ // retain focus styling
23348 this.focus.defer(10, this);
23349 var ml = this.menuListeners;
23350 this.menu.un("select", ml.select, this);
23351 this.menu.un("show", ml.show, this);
23352 this.menu.un("hide", ml.hide, this);
23356 // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
23357 onTriggerClick : function(){
23361 if(this.menu == null){
23362 this.menu = new Roo.menu.DateMenu();
23366 Roo.apply(this.menu.picker, {
23368 showClear: this.allowBlank,
23369 minDate : this.minValue,
23370 maxDate : this.maxValue,
23371 disabledDatesRE : this.ddMatch,
23372 disabledDatesText : this.disabledDatesText,
23374 format : this.useIso ? 'Y-m-d' : this.format,
23375 minText : String.format(this.minText, this.formatDate(this.minValue)),
23376 maxText : String.format(this.maxText, this.formatDate(this.maxValue))
23379 this.menu.on(Roo.apply({}, this.menuListeners, {
23387 // hide month picker get's called when we called by 'before hide';
23389 var ignorehide = true;
23390 p.hideMonthPicker = function(disableAnim){
23394 if(this.monthPicker){
23395 Roo.log("hideMonthPicker called");
23396 if(disableAnim === true){
23397 this.monthPicker.hide();
23399 this.monthPicker.slideOut('t', {duration:.2});
23400 p.setValue(new Date(m.picker.mpSelYear, m.picker.mpSelMonth, 1));
23401 p.fireEvent("select", this, this.value);
23407 Roo.log('picker set value');
23408 Roo.log(this.getValue());
23409 p.setValue(this.getValue() ? this.parseDate(this.getValue()) : new Date());
23410 m.show(this.el, 'tl-bl?');
23411 ignorehide = false;
23412 // this will trigger hideMonthPicker..
23415 // hidden the day picker
23416 Roo.select('.x-date-picker table', true).first().dom.style.visibility = "hidden";
23422 p.showMonthPicker.defer(100, p);
23428 beforeBlur : function(){
23429 var v = this.parseDate(this.getRawValue());
23435 /** @cfg {Boolean} grow @hide */
23436 /** @cfg {Number} growMin @hide */
23437 /** @cfg {Number} growMax @hide */
23444 * Ext JS Library 1.1.1
23445 * Copyright(c) 2006-2007, Ext JS, LLC.
23447 * Originally Released Under LGPL - original licence link has changed is not relivant.
23450 * <script type="text/javascript">
23455 * @class Roo.form.ComboBox
23456 * @extends Roo.form.TriggerField
23457 * A combobox control with support for autocomplete, remote-loading, paging and many other features.
23459 * Create a new ComboBox.
23460 * @param {Object} config Configuration options
23462 Roo.form.ComboBox = function(config){
23463 Roo.form.ComboBox.superclass.constructor.call(this, config);
23467 * Fires when the dropdown list is expanded
23468 * @param {Roo.form.ComboBox} combo This combo box
23473 * Fires when the dropdown list is collapsed
23474 * @param {Roo.form.ComboBox} combo This combo box
23478 * @event beforeselect
23479 * Fires before a list item is selected. Return false to cancel the selection.
23480 * @param {Roo.form.ComboBox} combo This combo box
23481 * @param {Roo.data.Record} record The data record returned from the underlying store
23482 * @param {Number} index The index of the selected item in the dropdown list
23484 'beforeselect' : true,
23487 * Fires when a list item is selected
23488 * @param {Roo.form.ComboBox} combo This combo box
23489 * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
23490 * @param {Number} index The index of the selected item in the dropdown list
23494 * @event beforequery
23495 * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
23496 * The event object passed has these properties:
23497 * @param {Roo.form.ComboBox} combo This combo box
23498 * @param {String} query The query
23499 * @param {Boolean} forceAll true to force "all" query
23500 * @param {Boolean} cancel true to cancel the query
23501 * @param {Object} e The query event object
23503 'beforequery': true,
23506 * Fires when the 'add' icon is pressed (add a listener to enable add button)
23507 * @param {Roo.form.ComboBox} combo This combo box
23512 * Fires when the 'edit' icon is pressed (add a listener to enable add button)
23513 * @param {Roo.form.ComboBox} combo This combo box
23514 * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
23520 if(this.transform){
23521 this.allowDomMove = false;
23522 var s = Roo.getDom(this.transform);
23523 if(!this.hiddenName){
23524 this.hiddenName = s.name;
23527 this.mode = 'local';
23528 var d = [], opts = s.options;
23529 for(var i = 0, len = opts.length;i < len; i++){
23531 var value = (Roo.isIE ? o.getAttributeNode('value').specified : o.hasAttribute('value')) ? o.value : o.text;
23533 this.value = value;
23535 d.push([value, o.text]);
23537 this.store = new Roo.data.SimpleStore({
23539 fields: ['value', 'text'],
23542 this.valueField = 'value';
23543 this.displayField = 'text';
23545 s.name = Roo.id(); // wipe out the name in case somewhere else they have a reference
23546 if(!this.lazyRender){
23547 this.target = true;
23548 this.el = Roo.DomHelper.insertBefore(s, this.autoCreate || this.defaultAutoCreate);
23549 s.parentNode.removeChild(s); // remove it
23550 this.render(this.el.parentNode);
23552 s.parentNode.removeChild(s); // remove it
23557 this.store = Roo.factory(this.store, Roo.data);
23560 this.selectedIndex = -1;
23561 if(this.mode == 'local'){
23562 if(config.queryDelay === undefined){
23563 this.queryDelay = 10;
23565 if(config.minChars === undefined){
23571 Roo.extend(Roo.form.ComboBox, Roo.form.TriggerField, {
23573 * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
23576 * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
23577 * rendering into an Roo.Editor, defaults to false)
23580 * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
23581 * {tag: "input", type: "text", size: "24", autocomplete: "off"})
23584 * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
23587 * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
23588 * the dropdown list (defaults to undefined, with no header element)
23592 * @cfg {String/Roo.Template} tpl The template to use to render the output
23596 defaultAutoCreate : {tag: "input", type: "text", size: "24", autocomplete: "off"},
23598 * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
23600 listWidth: undefined,
23602 * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
23603 * mode = 'remote' or 'text' if mode = 'local')
23605 displayField: undefined,
23607 * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
23608 * mode = 'remote' or 'value' if mode = 'local').
23609 * Note: use of a valueField requires the user make a selection
23610 * in order for a value to be mapped.
23612 valueField: undefined,
23616 * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
23617 * field's data value (defaults to the underlying DOM element's name)
23619 hiddenName: undefined,
23621 * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
23625 * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
23627 selectedClass: 'x-combo-selected',
23629 * @cfg {String} triggerClass An additional CSS class used to style the trigger button. The trigger will always get the
23630 * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
23631 * which displays a downward arrow icon).
23633 triggerClass : 'x-form-arrow-trigger',
23635 * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
23639 * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
23640 * anchor positions (defaults to 'tl-bl')
23642 listAlign: 'tl-bl?',
23644 * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
23648 * @cfg {String} triggerAction The action to execute when the trigger field is activated. Use 'all' to run the
23649 * query specified by the allQuery config option (defaults to 'query')
23651 triggerAction: 'query',
23653 * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
23654 * (defaults to 4, does not apply if editable = false)
23658 * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
23659 * delay (typeAheadDelay) if it matches a known value (defaults to false)
23663 * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
23664 * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
23668 * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
23669 * filter queries will execute with page start and limit parameters. Only applies when mode = 'remote' (defaults to 0)
23673 * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus. Only applies
23674 * when editable = true (defaults to false)
23676 selectOnFocus:false,
23678 * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
23680 queryParam: 'query',
23682 * @cfg {String} loadingText The text to display in the dropdown list while data is loading. Only applies
23683 * when mode = 'remote' (defaults to 'Loading...')
23685 loadingText: 'Loading...',
23687 * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
23691 * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
23695 * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
23696 * traditional select (defaults to true)
23700 * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
23704 * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
23708 * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
23709 * listWidth has a higher value)
23713 * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
23714 * allow the user to set arbitrary text into the field (defaults to false)
23716 forceSelection:false,
23718 * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
23719 * if typeAhead = true (defaults to 250)
23721 typeAheadDelay : 250,
23723 * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
23724 * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
23726 valueNotFoundText : undefined,
23728 * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
23730 blockFocus : false,
23733 * @cfg {Boolean} disableClear Disable showing of clear button.
23735 disableClear : false,
23737 * @cfg {Boolean} alwaysQuery Disable caching of results, and always send query
23739 alwaysQuery : false,
23745 // element that contains real text value.. (when hidden is used..)
23748 onRender : function(ct, position){
23749 Roo.form.ComboBox.superclass.onRender.call(this, ct, position);
23750 if(this.hiddenName){
23751 this.hiddenField = this.el.insertSibling({tag:'input', type:'hidden', name: this.hiddenName, id: (this.hiddenId||this.hiddenName)},
23753 this.hiddenField.value =
23754 this.hiddenValue !== undefined ? this.hiddenValue :
23755 this.value !== undefined ? this.value : '';
23757 // prevent input submission
23758 this.el.dom.removeAttribute('name');
23763 this.el.dom.setAttribute('autocomplete', 'off');
23766 var cls = 'x-combo-list';
23768 this.list = new Roo.Layer({
23769 shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
23772 var lw = this.listWidth || Math.max(this.wrap.getWidth(), this.minListWidth);
23773 this.list.setWidth(lw);
23774 this.list.swallowEvent('mousewheel');
23775 this.assetHeight = 0;
23778 this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
23779 this.assetHeight += this.header.getHeight();
23782 this.innerList = this.list.createChild({cls:cls+'-inner'});
23783 this.innerList.on('mouseover', this.onViewOver, this);
23784 this.innerList.on('mousemove', this.onViewMove, this);
23785 this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
23787 if(this.allowBlank && !this.pageSize && !this.disableClear){
23788 this.footer = this.list.createChild({cls:cls+'-ft'});
23789 this.pageTb = new Roo.Toolbar(this.footer);
23793 this.footer = this.list.createChild({cls:cls+'-ft'});
23794 this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
23795 {pageSize: this.pageSize});
23799 if (this.pageTb && this.allowBlank && !this.disableClear) {
23801 this.pageTb.add(new Roo.Toolbar.Fill(), {
23802 cls: 'x-btn-icon x-btn-clear',
23804 handler: function()
23807 _this.clearValue();
23808 _this.onSelect(false, -1);
23813 this.assetHeight += this.footer.getHeight();
23818 this.tpl = '<div class="'+cls+'-item">{' + this.displayField + '}</div>';
23821 this.view = new Roo.View(this.innerList, this.tpl, {
23822 singleSelect:true, store: this.store, selectedClass: this.selectedClass
23825 this.view.on('click', this.onViewClick, this);
23827 this.store.on('beforeload', this.onBeforeLoad, this);
23828 this.store.on('load', this.onLoad, this);
23829 this.store.on('loadexception', this.onLoadException, this);
23831 if(this.resizable){
23832 this.resizer = new Roo.Resizable(this.list, {
23833 pinned:true, handles:'se'
23835 this.resizer.on('resize', function(r, w, h){
23836 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
23837 this.listWidth = w;
23838 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
23839 this.restrictHeight();
23841 this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
23843 if(!this.editable){
23844 this.editable = true;
23845 this.setEditable(false);
23849 if (typeof(this.events.add.listeners) != 'undefined') {
23851 this.addicon = this.wrap.createChild(
23852 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });
23854 this.addicon.on('click', function(e) {
23855 this.fireEvent('add', this);
23858 if (typeof(this.events.edit.listeners) != 'undefined') {
23860 this.editicon = this.wrap.createChild(
23861 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });
23862 if (this.addicon) {
23863 this.editicon.setStyle('margin-left', '40px');
23865 this.editicon.on('click', function(e) {
23867 // we fire even if inothing is selected..
23868 this.fireEvent('edit', this, this.lastData );
23878 initEvents : function(){
23879 Roo.form.ComboBox.superclass.initEvents.call(this);
23881 this.keyNav = new Roo.KeyNav(this.el, {
23882 "up" : function(e){
23883 this.inKeyMode = true;
23887 "down" : function(e){
23888 if(!this.isExpanded()){
23889 this.onTriggerClick();
23891 this.inKeyMode = true;
23896 "enter" : function(e){
23897 this.onViewClick();
23901 "esc" : function(e){
23905 "tab" : function(e){
23906 this.onViewClick(false);
23907 this.fireEvent("specialkey", this, e);
23913 doRelay : function(foo, bar, hname){
23914 if(hname == 'down' || this.scope.isExpanded()){
23915 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
23922 this.queryDelay = Math.max(this.queryDelay || 10,
23923 this.mode == 'local' ? 10 : 250);
23924 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
23925 if(this.typeAhead){
23926 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
23928 if(this.editable !== false){
23929 this.el.on("keyup", this.onKeyUp, this);
23931 if(this.forceSelection){
23932 this.on('blur', this.doForce, this);
23936 onDestroy : function(){
23938 this.view.setStore(null);
23939 this.view.el.removeAllListeners();
23940 this.view.el.remove();
23941 this.view.purgeListeners();
23944 this.list.destroy();
23947 this.store.un('beforeload', this.onBeforeLoad, this);
23948 this.store.un('load', this.onLoad, this);
23949 this.store.un('loadexception', this.onLoadException, this);
23951 Roo.form.ComboBox.superclass.onDestroy.call(this);
23955 fireKey : function(e){
23956 if(e.isNavKeyPress() && !this.list.isVisible()){
23957 this.fireEvent("specialkey", this, e);
23962 onResize: function(w, h){
23963 Roo.form.ComboBox.superclass.onResize.apply(this, arguments);
23965 if(typeof w != 'number'){
23966 // we do not handle it!?!?
23969 var tw = this.trigger.getWidth();
23970 tw += this.addicon ? this.addicon.getWidth() : 0;
23971 tw += this.editicon ? this.editicon.getWidth() : 0;
23973 this.el.setWidth( this.adjustWidth('input', x));
23975 this.trigger.setStyle('left', x+'px');
23977 if(this.list && this.listWidth === undefined){
23978 var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
23979 this.list.setWidth(lw);
23980 this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
23988 * Allow or prevent the user from directly editing the field text. If false is passed,
23989 * the user will only be able to select from the items defined in the dropdown list. This method
23990 * is the runtime equivalent of setting the 'editable' config option at config time.
23991 * @param {Boolean} value True to allow the user to directly edit the field text
23993 setEditable : function(value){
23994 if(value == this.editable){
23997 this.editable = value;
23999 this.el.dom.setAttribute('readOnly', true);
24000 this.el.on('mousedown', this.onTriggerClick, this);
24001 this.el.addClass('x-combo-noedit');
24003 this.el.dom.setAttribute('readOnly', false);
24004 this.el.un('mousedown', this.onTriggerClick, this);
24005 this.el.removeClass('x-combo-noedit');
24010 onBeforeLoad : function(){
24011 if(!this.hasFocus){
24014 this.innerList.update(this.loadingText ?
24015 '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
24016 this.restrictHeight();
24017 this.selectedIndex = -1;
24021 onLoad : function(){
24022 if(!this.hasFocus){
24025 if(this.store.getCount() > 0){
24027 this.restrictHeight();
24028 if(this.lastQuery == this.allQuery){
24030 this.el.dom.select();
24032 if(!this.selectByValue(this.value, true)){
24033 this.select(0, true);
24037 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
24038 this.taTask.delay(this.typeAheadDelay);
24042 this.onEmptyResults();
24047 onLoadException : function()
24050 Roo.log(this.store.reader.jsonData);
24051 if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
24052 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
24058 onTypeAhead : function(){
24059 if(this.store.getCount() > 0){
24060 var r = this.store.getAt(0);
24061 var newValue = r.data[this.displayField];
24062 var len = newValue.length;
24063 var selStart = this.getRawValue().length;
24064 if(selStart != len){
24065 this.setRawValue(newValue);
24066 this.selectText(selStart, newValue.length);
24072 onSelect : function(record, index){
24073 if(this.fireEvent('beforeselect', this, record, index) !== false){
24074 this.setFromData(index > -1 ? record.data : false);
24076 this.fireEvent('select', this, record, index);
24081 * Returns the currently selected field value or empty string if no value is set.
24082 * @return {String} value The selected value
24084 getValue : function(){
24085 if(this.valueField){
24086 return typeof this.value != 'undefined' ? this.value : '';
24088 return Roo.form.ComboBox.superclass.getValue.call(this);
24093 * Clears any text/value currently set in the field
24095 clearValue : function(){
24096 if(this.hiddenField){
24097 this.hiddenField.value = '';
24100 this.setRawValue('');
24101 this.lastSelectionText = '';
24106 * Sets the specified value into the field. If the value finds a match, the corresponding record text
24107 * will be displayed in the field. If the value does not match the data value of an existing item,
24108 * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
24109 * Otherwise the field will be blank (although the value will still be set).
24110 * @param {String} value The value to match
24112 setValue : function(v){
24114 if(this.valueField){
24115 var r = this.findRecord(this.valueField, v);
24117 text = r.data[this.displayField];
24118 }else if(this.valueNotFoundText !== undefined){
24119 text = this.valueNotFoundText;
24122 this.lastSelectionText = text;
24123 if(this.hiddenField){
24124 this.hiddenField.value = v;
24126 Roo.form.ComboBox.superclass.setValue.call(this, text);
24130 * @property {Object} the last set data for the element
24135 * Sets the value of the field based on a object which is related to the record format for the store.
24136 * @param {Object} value the value to set as. or false on reset?
24138 setFromData : function(o){
24139 var dv = ''; // display value
24140 var vv = ''; // value value..
24142 if (this.displayField) {
24143 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
24145 // this is an error condition!!!
24146 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
24149 if(this.valueField){
24150 vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
24152 if(this.hiddenField){
24153 this.hiddenField.value = vv;
24155 this.lastSelectionText = dv;
24156 Roo.form.ComboBox.superclass.setValue.call(this, dv);
24160 // no hidden field.. - we store the value in 'value', but still display
24161 // display field!!!!
24162 this.lastSelectionText = dv;
24163 Roo.form.ComboBox.superclass.setValue.call(this, dv);
24169 reset : function(){
24170 // overridden so that last data is reset..
24171 this.setValue(this.originalValue);
24172 this.clearInvalid();
24173 this.lastData = false;
24175 this.view.clearSelections();
24179 findRecord : function(prop, value){
24181 if(this.store.getCount() > 0){
24182 this.store.each(function(r){
24183 if(r.data[prop] == value){
24193 getName: function()
24195 // returns hidden if it's set..
24196 if (!this.rendered) {return ''};
24197 return !this.hiddenName && this.el.dom.name ? this.el.dom.name : (this.hiddenName || '');
24201 onViewMove : function(e, t){
24202 this.inKeyMode = false;
24206 onViewOver : function(e, t){
24207 if(this.inKeyMode){ // prevent key nav and mouse over conflicts
24210 var item = this.view.findItemFromChild(t);
24212 var index = this.view.indexOf(item);
24213 this.select(index, false);
24218 onViewClick : function(doFocus)
24220 var index = this.view.getSelectedIndexes()[0];
24221 var r = this.store.getAt(index);
24223 this.onSelect(r, index);
24225 if(doFocus !== false && !this.blockFocus){
24231 restrictHeight : function(){
24232 this.innerList.dom.style.height = '';
24233 var inner = this.innerList.dom;
24234 var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
24235 this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
24236 this.list.beginUpdate();
24237 this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
24238 this.list.alignTo(this.el, this.listAlign);
24239 this.list.endUpdate();
24243 onEmptyResults : function(){
24248 * Returns true if the dropdown list is expanded, else false.
24250 isExpanded : function(){
24251 return this.list.isVisible();
24255 * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
24256 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
24257 * @param {String} value The data value of the item to select
24258 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
24259 * selected item if it is not currently in view (defaults to true)
24260 * @return {Boolean} True if the value matched an item in the list, else false
24262 selectByValue : function(v, scrollIntoView){
24263 if(v !== undefined && v !== null){
24264 var r = this.findRecord(this.valueField || this.displayField, v);
24266 this.select(this.store.indexOf(r), scrollIntoView);
24274 * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
24275 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
24276 * @param {Number} index The zero-based index of the list item to select
24277 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
24278 * selected item if it is not currently in view (defaults to true)
24280 select : function(index, scrollIntoView){
24281 this.selectedIndex = index;
24282 this.view.select(index);
24283 if(scrollIntoView !== false){
24284 var el = this.view.getNode(index);
24286 this.innerList.scrollChildIntoView(el, false);
24292 selectNext : function(){
24293 var ct = this.store.getCount();
24295 if(this.selectedIndex == -1){
24297 }else if(this.selectedIndex < ct-1){
24298 this.select(this.selectedIndex+1);
24304 selectPrev : function(){
24305 var ct = this.store.getCount();
24307 if(this.selectedIndex == -1){
24309 }else if(this.selectedIndex != 0){
24310 this.select(this.selectedIndex-1);
24316 onKeyUp : function(e){
24317 if(this.editable !== false && !e.isSpecialKey()){
24318 this.lastKey = e.getKey();
24319 this.dqTask.delay(this.queryDelay);
24324 validateBlur : function(){
24325 return !this.list || !this.list.isVisible();
24329 initQuery : function(){
24330 this.doQuery(this.getRawValue());
24334 doForce : function(){
24335 if(this.el.dom.value.length > 0){
24336 this.el.dom.value =
24337 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
24343 * Execute a query to filter the dropdown list. Fires the beforequery event prior to performing the
24344 * query allowing the query action to be canceled if needed.
24345 * @param {String} query The SQL query to execute
24346 * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
24347 * in the field than the minimum specified by the minChars config option. It also clears any filter previously
24348 * saved in the current store (defaults to false)
24350 doQuery : function(q, forceAll){
24351 if(q === undefined || q === null){
24356 forceAll: forceAll,
24360 if(this.fireEvent('beforequery', qe)===false || qe.cancel){
24364 forceAll = qe.forceAll;
24365 if(forceAll === true || (q.length >= this.minChars)){
24366 if(this.lastQuery != q || this.alwaysQuery){
24367 this.lastQuery = q;
24368 if(this.mode == 'local'){
24369 this.selectedIndex = -1;
24371 this.store.clearFilter();
24373 this.store.filter(this.displayField, q);
24377 this.store.baseParams[this.queryParam] = q;
24379 params: this.getParams(q)
24384 this.selectedIndex = -1;
24391 getParams : function(q){
24393 //p[this.queryParam] = q;
24396 p.limit = this.pageSize;
24402 * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
24404 collapse : function(){
24405 if(!this.isExpanded()){
24409 Roo.get(document).un('mousedown', this.collapseIf, this);
24410 Roo.get(document).un('mousewheel', this.collapseIf, this);
24411 if (!this.editable) {
24412 Roo.get(document).un('keydown', this.listKeyPress, this);
24414 this.fireEvent('collapse', this);
24418 collapseIf : function(e){
24419 if(!e.within(this.wrap) && !e.within(this.list)){
24425 * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
24427 expand : function(){
24428 if(this.isExpanded() || !this.hasFocus){
24431 this.list.alignTo(this.el, this.listAlign);
24433 Roo.get(document).on('mousedown', this.collapseIf, this);
24434 Roo.get(document).on('mousewheel', this.collapseIf, this);
24435 if (!this.editable) {
24436 Roo.get(document).on('keydown', this.listKeyPress, this);
24439 this.fireEvent('expand', this);
24443 // Implements the default empty TriggerField.onTriggerClick function
24444 onTriggerClick : function(){
24448 if(this.isExpanded()){
24450 if (!this.blockFocus) {
24455 this.hasFocus = true;
24456 if(this.triggerAction == 'all') {
24457 this.doQuery(this.allQuery, true);
24459 this.doQuery(this.getRawValue());
24461 if (!this.blockFocus) {
24466 listKeyPress : function(e)
24468 //Roo.log('listkeypress');
24469 // scroll to first matching element based on key pres..
24470 if (e.isSpecialKey()) {
24473 var k = String.fromCharCode(e.getKey()).toUpperCase();
24476 var csel = this.view.getSelectedNodes();
24477 var cselitem = false;
24479 var ix = this.view.indexOf(csel[0]);
24480 cselitem = this.store.getAt(ix);
24481 if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
24487 this.store.each(function(v) {
24489 // start at existing selection.
24490 if (cselitem.id == v.id) {
24496 if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
24497 match = this.store.indexOf(v);
24502 if (match === false) {
24503 return true; // no more action?
24506 this.view.select(match);
24507 var sn = Roo.get(this.view.getSelectedNodes()[0])
24508 sn.scrollIntoView(sn.dom.parentNode, false);
24512 * @cfg {Boolean} grow
24516 * @cfg {Number} growMin
24520 * @cfg {Number} growMax
24528 * Copyright(c) 2010-2012, Roo J Solutions Limited
24535 * @class Roo.form.ComboBoxArray
24536 * @extends Roo.form.TextField
24537 * A facebook style adder... for lists of email / people / countries etc...
24538 * pick multiple items from a combo box, and shows each one.
24540 * Fred [x] Brian [x] [Pick another |v]
24543 * For this to work: it needs various extra information
24544 * - normal combo problay has
24546 * + displayField, valueField
24548 * For our purpose...
24551 * If we change from 'extends' to wrapping...
24558 * Create a new ComboBoxArray.
24559 * @param {Object} config Configuration options
24563 Roo.form.ComboBoxArray = function(config)
24566 Roo.form.ComboBoxArray.superclass.constructor.call(this, config);
24568 this.items = new Roo.util.MixedCollection(false);
24570 // construct the child combo...
24580 Roo.extend(Roo.form.ComboBoxArray, Roo.form.TextField,
24583 * @cfg {Roo.form.Combo} combo The combo box that is wrapped
24588 // behavies liek a hiddne field
24589 inputType: 'hidden',
24591 * @cfg {Number} width The width of the box that displays the selected element
24598 * @cfg {String} name The name of the visable items on this form (eg. titles not ids)
24602 * @cfg {String} hiddenName The hidden name of the field, often contains an comma seperated list of names
24604 hiddenName : false,
24607 // private the array of items that are displayed..
24609 // private - the hidden field el.
24611 // private - the filed el..
24614 //validateValue : function() { return true; }, // all values are ok!
24615 //onAddClick: function() { },
24617 onRender : function(ct, position)
24620 // create the standard hidden element
24621 //Roo.form.ComboBoxArray.superclass.onRender.call(this, ct, position);
24624 // give fake names to child combo;
24625 this.combo.hiddenName = this.hiddenName ? (this.hiddenName+'-subcombo') : this.hiddenName;
24626 this.combo.name = this.name? (this.name+'-subcombo') : this.name;
24628 this.combo = Roo.factory(this.combo, Roo.form);
24629 this.combo.onRender(ct, position);
24630 if (typeof(this.combo.width) != 'undefined') {
24631 this.combo.onResize(this.combo.width,0);
24634 this.combo.initEvents();
24636 // assigned so form know we need to do this..
24637 this.store = this.combo.store;
24638 this.valueField = this.combo.valueField;
24639 this.displayField = this.combo.displayField ;
24642 this.combo.wrap.addClass('x-cbarray-grp');
24644 var cbwrap = this.combo.wrap.createChild(
24645 {tag: 'div', cls: 'x-cbarray-cb'},
24650 this.hiddenEl = this.combo.wrap.createChild({
24651 tag: 'input', type:'hidden' , name: this.hiddenName, value : ''
24653 this.el = this.combo.wrap.createChild({
24654 tag: 'input', type:'hidden' , name: this.name, value : ''
24656 // this.el.dom.removeAttribute("name");
24659 this.outerWrap = this.combo.wrap;
24660 this.wrap = cbwrap;
24662 this.outerWrap.setWidth(this.width);
24663 this.outerWrap.dom.removeChild(this.el.dom);
24665 this.wrap.dom.appendChild(this.el.dom);
24666 this.outerWrap.dom.removeChild(this.combo.trigger.dom);
24667 this.combo.wrap.dom.appendChild(this.combo.trigger.dom);
24669 this.combo.trigger.setStyle('position','relative');
24670 this.combo.trigger.setStyle('left', '0px');
24671 this.combo.trigger.setStyle('top', '2px');
24673 this.combo.el.setStyle('vertical-align', 'text-bottom');
24675 //this.trigger.setStyle('vertical-align', 'top');
24677 // this should use the code from combo really... on('add' ....)
24681 this.adder = this.outerWrap.createChild(
24682 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-adder', style: 'margin-left:2px'});
24684 this.adder.on('click', function(e) {
24685 _t.fireEvent('adderclick', this, e);
24689 //this.adder.on('click', this.onAddClick, _t);
24692 this.combo.on('select', function(cb, rec, ix) {
24693 this.addItem(rec.data);
24696 cb.el.dom.value = '';
24697 //cb.lastData = rec.data;
24706 getName: function()
24708 // returns hidden if it's set..
24709 if (!this.rendered) {return ''};
24710 return this.hiddenName ? this.hiddenName : this.name;
24715 onResize: function(w, h){
24718 // not sure if this is needed..
24719 //this.combo.onResize(w,h);
24721 if(typeof w != 'number'){
24722 // we do not handle it!?!?
24725 var tw = this.combo.trigger.getWidth();
24726 tw += this.addicon ? this.addicon.getWidth() : 0;
24727 tw += this.editicon ? this.editicon.getWidth() : 0;
24729 this.combo.el.setWidth( this.combo.adjustWidth('input', x));
24731 this.combo.trigger.setStyle('left', '0px');
24733 if(this.list && this.listWidth === undefined){
24734 var lw = Math.max(x + this.combo.trigger.getWidth(), this.combo.minListWidth);
24735 this.list.setWidth(lw);
24736 this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
24743 addItem: function(rec)
24745 var valueField = this.combo.valueField;
24746 var displayField = this.combo.displayField;
24747 if (this.items.indexOfKey(rec[valueField]) > -1) {
24748 //console.log("GOT " + rec.data.id);
24752 var x = new Roo.form.ComboBoxArray.Item({
24753 //id : rec[this.idField],
24755 displayField : displayField ,
24756 tipField : displayField ,
24760 this.items.add(rec[valueField],x);
24761 // add it before the element..
24762 this.updateHiddenEl();
24763 x.render(this.outerWrap, this.wrap.dom);
24764 // add the image handler..
24767 updateHiddenEl : function()
24770 if (!this.hiddenEl) {
24774 var idField = this.combo.valueField;
24776 this.items.each(function(f) {
24777 ar.push(f.data[idField]);
24780 this.hiddenEl.dom.value = ar.join(',');
24786 //Roo.form.ComboBoxArray.superclass.reset.call(this);
24787 this.items.each(function(f) {
24790 this.el.dom.value = '';
24791 if (this.hiddenEl) {
24792 this.hiddenEl.dom.value = '';
24796 getValue: function()
24798 return this.hiddenEl ? this.hiddenEl.dom.value : '';
24800 setValue: function(v) // not a valid action - must use addItems..
24807 if (this.store.isLocal && (typeof(v) == 'string')) {
24808 // then we can use the store to find the values..
24809 // comma seperated at present.. this needs to allow JSON based encoding..
24810 this.hiddenEl.value = v;
24812 Roo.each(v.split(','), function(k) {
24813 Roo.log("CHECK " + this.valueField + ',' + k);
24814 var li = this.store.query(this.valueField, k);
24819 add[this.valueField] = k;
24820 add[this.displayField] = li.item(0).data[this.displayField];
24826 if (typeof(v) == 'object') {
24827 // then let's assume it's an array of objects..
24828 Roo.each(v, function(l) {
24836 setFromData: function(v)
24838 // this recieves an object, if setValues is called.
24840 this.el.dom.value = v[this.displayField];
24841 this.hiddenEl.dom.value = v[this.valueField];
24842 if (typeof(v[this.valueField]) != 'string' || !v[this.valueField].length) {
24845 var kv = v[this.valueField];
24846 var dv = v[this.displayField];
24847 kv = typeof(kv) != 'string' ? '' : kv;
24848 dv = typeof(dv) != 'string' ? '' : dv;
24851 var keys = kv.split(',');
24852 var display = dv.split(',');
24853 for (var i = 0 ; i < keys.length; i++) {
24856 add[this.valueField] = keys[i];
24857 add[this.displayField] = display[i];
24865 validateValue : function(value){
24866 return Roo.form.ComboBoxArray.superclass.validateValue.call(this, this.getValue());
24875 * @class Roo.form.ComboBoxArray.Item
24876 * @extends Roo.BoxComponent
24877 * A selected item in the list
24878 * Fred [x] Brian [x] [Pick another |v]
24881 * Create a new item.
24882 * @param {Object} config Configuration options
24885 Roo.form.ComboBoxArray.Item = function(config) {
24886 config.id = Roo.id();
24887 Roo.form.ComboBoxArray.Item.superclass.constructor.call(this, config);
24890 Roo.extend(Roo.form.ComboBoxArray.Item, Roo.BoxComponent, {
24893 displayField : false,
24897 defaultAutoCreate : {
24899 cls: 'x-cbarray-item',
24906 src : Roo.BLANK_IMAGE_URL ,
24914 onRender : function(ct, position)
24916 Roo.form.Field.superclass.onRender.call(this, ct, position);
24919 var cfg = this.getAutoCreate();
24920 this.el = ct.createChild(cfg, position);
24923 this.el.child('img').dom.setAttribute('src', Roo.BLANK_IMAGE_URL);
24925 this.el.child('div').dom.innerHTML = this.cb.renderer ?
24926 this.cb.renderer(this.data) :
24927 String.format('{0}',this.data[this.displayField]);
24930 this.el.child('div').dom.setAttribute('qtip',
24931 String.format('{0}',this.data[this.tipField])
24934 this.el.child('img').on('click', this.remove, this);
24938 remove : function()
24941 this.cb.items.remove(this);
24942 this.el.child('img').un('click', this.remove, this);
24944 this.cb.updateHiddenEl();
24950 * Ext JS Library 1.1.1
24951 * Copyright(c) 2006-2007, Ext JS, LLC.
24953 * Originally Released Under LGPL - original licence link has changed is not relivant.
24956 * <script type="text/javascript">
24959 * @class Roo.form.Checkbox
24960 * @extends Roo.form.Field
24961 * Single checkbox field. Can be used as a direct replacement for traditional checkbox fields.
24963 * Creates a new Checkbox
24964 * @param {Object} config Configuration options
24966 Roo.form.Checkbox = function(config){
24967 Roo.form.Checkbox.superclass.constructor.call(this, config);
24971 * Fires when the checkbox is checked or unchecked.
24972 * @param {Roo.form.Checkbox} this This checkbox
24973 * @param {Boolean} checked The new checked value
24979 Roo.extend(Roo.form.Checkbox, Roo.form.Field, {
24981 * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
24983 focusClass : undefined,
24985 * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
24987 fieldClass: "x-form-field",
24989 * @cfg {Boolean} checked True if the the checkbox should render already checked (defaults to false)
24993 * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
24994 * {tag: "input", type: "checkbox", autocomplete: "off"})
24996 defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "off"},
24998 * @cfg {String} boxLabel The text that appears beside the checkbox
25002 * @cfg {String} inputValue The value that should go into the generated input element's value attribute
25006 * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
25008 valueOff: '0', // value when not checked..
25010 actionMode : 'viewEl',
25013 itemCls : 'x-menu-check-item x-form-item',
25014 groupClass : 'x-menu-group-item',
25015 inputType : 'hidden',
25018 inSetChecked: false, // check that we are not calling self...
25020 inputElement: false, // real input element?
25021 basedOn: false, // ????
25023 isFormField: true, // not sure where this is needed!!!!
25025 onResize : function(){
25026 Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
25027 if(!this.boxLabel){
25028 this.el.alignTo(this.wrap, 'c-c');
25032 initEvents : function(){
25033 Roo.form.Checkbox.superclass.initEvents.call(this);
25034 this.el.on("click", this.onClick, this);
25035 this.el.on("change", this.onClick, this);
25039 getResizeEl : function(){
25043 getPositionEl : function(){
25048 onRender : function(ct, position){
25049 Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
25051 if(this.inputValue !== undefined){
25052 this.el.dom.value = this.inputValue;
25055 //this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
25056 this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
25057 var viewEl = this.wrap.createChild({
25058 tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
25059 this.viewEl = viewEl;
25060 this.wrap.on('click', this.onClick, this);
25062 this.el.on('DOMAttrModified', this.setFromHidden, this); //ff
25063 this.el.on('propertychange', this.setFromHidden, this); //ie
25068 this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
25069 // viewEl.on('click', this.onClick, this);
25071 //if(this.checked){
25072 this.setChecked(this.checked);
25074 //this.checked = this.el.dom;
25080 initValue : Roo.emptyFn,
25083 * Returns the checked state of the checkbox.
25084 * @return {Boolean} True if checked, else false
25086 getValue : function(){
25088 return String(this.el.dom.value) == String(this.inputValue ) ? this.inputValue : this.valueOff;
25090 return this.valueOff;
25095 onClick : function(){
25096 this.setChecked(!this.checked);
25098 //if(this.el.dom.checked != this.checked){
25099 // this.setValue(this.el.dom.checked);
25104 * Sets the checked state of the checkbox.
25105 * On is always based on a string comparison between inputValue and the param.
25106 * @param {Boolean/String} value - the value to set
25107 * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
25109 setValue : function(v,suppressEvent){
25112 //this.checked = (v === true || v === 'true' || v == '1' || String(v).toLowerCase() == 'on');
25113 //if(this.el && this.el.dom){
25114 // this.el.dom.checked = this.checked;
25115 // this.el.dom.defaultChecked = this.checked;
25117 this.setChecked(String(v) === String(this.inputValue), suppressEvent);
25118 //this.fireEvent("check", this, this.checked);
25121 setChecked : function(state,suppressEvent)
25123 if (this.inSetChecked) {
25124 this.checked = state;
25130 this.wrap[state ? 'addClass' : 'removeClass']('x-menu-item-checked');
25132 this.checked = state;
25133 if(suppressEvent !== true){
25134 this.fireEvent('check', this, state);
25136 this.inSetChecked = true;
25137 this.el.dom.value = state ? this.inputValue : this.valueOff;
25138 this.inSetChecked = false;
25141 // handle setting of hidden value by some other method!!?!?
25142 setFromHidden: function()
25147 //console.log("SET FROM HIDDEN");
25148 //alert('setFrom hidden');
25149 this.setValue(this.el.dom.value);
25152 onDestroy : function()
25155 Roo.get(this.viewEl).remove();
25158 Roo.form.Checkbox.superclass.onDestroy.call(this);
25163 * Ext JS Library 1.1.1
25164 * Copyright(c) 2006-2007, Ext JS, LLC.
25166 * Originally Released Under LGPL - original licence link has changed is not relivant.
25169 * <script type="text/javascript">
25173 * @class Roo.form.Radio
25174 * @extends Roo.form.Checkbox
25175 * Single radio field. Same as Checkbox, but provided as a convenience for automatically setting the input type.
25176 * Radio grouping is handled automatically by the browser if you give each radio in a group the same name.
25178 * Creates a new Radio
25179 * @param {Object} config Configuration options
25181 Roo.form.Radio = function(){
25182 Roo.form.Radio.superclass.constructor.apply(this, arguments);
25184 Roo.extend(Roo.form.Radio, Roo.form.Checkbox, {
25185 inputType: 'radio',
25188 * If this radio is part of a group, it will return the selected value
25191 getGroupValue : function(){
25192 return this.el.up('form').child('input[name='+this.el.dom.name+']:checked', true).value;
25196 onRender : function(ct, position){
25197 Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
25199 if(this.inputValue !== undefined){
25200 this.el.dom.value = this.inputValue;
25203 this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
25204 //this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
25205 //var viewEl = this.wrap.createChild({
25206 // tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
25207 //this.viewEl = viewEl;
25208 //this.wrap.on('click', this.onClick, this);
25210 //this.el.on('DOMAttrModified', this.setFromHidden, this); //ff
25211 //this.el.on('propertychange', this.setFromHidden, this); //ie
25216 this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
25217 // viewEl.on('click', this.onClick, this);
25220 this.el.dom.checked = 'checked' ;
25226 });//<script type="text/javascript">
25229 * Ext JS Library 1.1.1
25230 * Copyright(c) 2006-2007, Ext JS, LLC.
25231 * licensing@extjs.com
25233 * http://www.extjs.com/license
25239 * Default CSS appears to render it as fixed text by default (should really be Sans-Serif)
25240 * - IE ? - no idea how much works there.
25248 * @class Ext.form.HtmlEditor
25249 * @extends Ext.form.Field
25250 * Provides a lightweight HTML Editor component.
25252 * This has been tested on Fireforx / Chrome.. IE may not be so great..
25254 * <br><br><b>Note: The focus/blur and validation marking functionality inherited from Ext.form.Field is NOT
25255 * supported by this editor.</b><br/><br/>
25256 * An Editor is a sensitive component that can't be used in all spots standard fields can be used. Putting an Editor within
25257 * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
25259 Roo.form.HtmlEditor = Roo.extend(Roo.form.Field, {
25261 * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
25265 * @cfg {String} createLinkText The default text for the create link prompt
25267 createLinkText : 'Please enter the URL for the link:',
25269 * @cfg {String} defaultLinkValue The default value for the create link prompt (defaults to http:/ /)
25271 defaultLinkValue : 'http:/'+'/',
25274 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
25279 * @cfg {Number} height (in pixels)
25283 * @cfg {Number} width (in pixels)
25288 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
25291 stylesheets: false,
25296 // private properties
25297 validationEvent : false,
25299 initialized : false,
25301 sourceEditMode : false,
25302 onFocus : Roo.emptyFn,
25304 hideMode:'offsets',
25306 defaultAutoCreate : { // modified by initCompnoent..
25308 style:"width:500px;height:300px;",
25309 autocomplete: "off"
25313 initComponent : function(){
25316 * @event initialize
25317 * Fires when the editor is fully initialized (including the iframe)
25318 * @param {HtmlEditor} this
25323 * Fires when the editor is first receives the focus. Any insertion must wait
25324 * until after this event.
25325 * @param {HtmlEditor} this
25329 * @event beforesync
25330 * Fires before the textarea is updated with content from the editor iframe. Return false
25331 * to cancel the sync.
25332 * @param {HtmlEditor} this
25333 * @param {String} html
25337 * @event beforepush
25338 * Fires before the iframe editor is updated with content from the textarea. Return false
25339 * to cancel the push.
25340 * @param {HtmlEditor} this
25341 * @param {String} html
25346 * Fires when the textarea is updated with content from the editor iframe.
25347 * @param {HtmlEditor} this
25348 * @param {String} html
25353 * Fires when the iframe editor is updated with content from the textarea.
25354 * @param {HtmlEditor} this
25355 * @param {String} html
25359 * @event editmodechange
25360 * Fires when the editor switches edit modes
25361 * @param {HtmlEditor} this
25362 * @param {Boolean} sourceEdit True if source edit, false if standard editing.
25364 editmodechange: true,
25366 * @event editorevent
25367 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
25368 * @param {HtmlEditor} this
25372 this.defaultAutoCreate = {
25374 style:'width: ' + this.width + 'px;height: ' + this.height + 'px;',
25375 autocomplete: "off"
25380 * Protected method that will not generally be called directly. It
25381 * is called when the editor creates its toolbar. Override this method if you need to
25382 * add custom toolbar buttons.
25383 * @param {HtmlEditor} editor
25385 createToolbar : function(editor){
25386 if (!editor.toolbars || !editor.toolbars.length) {
25387 editor.toolbars = [ new Roo.form.HtmlEditor.ToolbarStandard() ]; // can be empty?
25390 for (var i =0 ; i < editor.toolbars.length;i++) {
25391 editor.toolbars[i] = Roo.factory(
25392 typeof(editor.toolbars[i]) == 'string' ?
25393 { xtype: editor.toolbars[i]} : editor.toolbars[i],
25394 Roo.form.HtmlEditor);
25395 editor.toolbars[i].init(editor);
25402 * Protected method that will not generally be called directly. It
25403 * is called when the editor initializes the iframe with HTML contents. Override this method if you
25404 * want to change the initialization markup of the iframe (e.g. to add stylesheets).
25406 getDocMarkup : function(){
25409 if (this.stylesheets === false) {
25411 Roo.get(document.head).select('style').each(function(node) {
25412 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
25415 Roo.get(document.head).select('link').each(function(node) {
25416 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
25419 } else if (!this.stylesheets.length) {
25421 st = '<style type="text/css">' +
25422 'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
25425 Roo.each(this.stylesheets, function(s) {
25426 st += '<link rel="stylesheet" type="text/css" href="' + s +'" />'
25431 st += '<style type="text/css">' +
25432 'IMG { cursor: pointer } ' +
25436 return '<html><head>' + st +
25437 //<style type="text/css">' +
25438 //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
25440 ' </head><body class="roo-htmleditor-body"></body></html>';
25444 onRender : function(ct, position)
25447 Roo.form.HtmlEditor.superclass.onRender.call(this, ct, position);
25448 this.el.dom.style.border = '0 none';
25449 this.el.dom.setAttribute('tabIndex', -1);
25450 this.el.addClass('x-hidden');
25451 if(Roo.isIE){ // fix IE 1px bogus margin
25452 this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
25454 this.wrap = this.el.wrap({
25455 cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
25458 if (this.resizable) {
25459 this.resizeEl = new Roo.Resizable(this.wrap, {
25463 minHeight : this.height,
25464 height: this.height,
25465 handles : this.resizable,
25468 resize : function(r, w, h) {
25469 _t.onResize(w,h); // -something
25476 this.frameId = Roo.id();
25478 this.createToolbar(this);
25482 var iframe = this.wrap.createChild({
25485 name: this.frameId,
25486 frameBorder : 'no',
25487 'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL : "javascript:false"
25491 // console.log(iframe);
25492 //this.wrap.dom.appendChild(iframe);
25494 this.iframe = iframe.dom;
25496 this.assignDocWin();
25498 this.doc.designMode = 'on';
25501 this.doc.write(this.getDocMarkup());
25505 var task = { // must defer to wait for browser to be ready
25507 //console.log("run task?" + this.doc.readyState);
25508 this.assignDocWin();
25509 if(this.doc.body || this.doc.readyState == 'complete'){
25511 this.doc.designMode="on";
25515 Roo.TaskMgr.stop(task);
25516 this.initEditor.defer(10, this);
25523 Roo.TaskMgr.start(task);
25526 this.setSize(this.wrap.getSize());
25528 if (this.resizeEl) {
25529 this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
25530 // should trigger onReize..
25535 onResize : function(w, h)
25537 //Roo.log('resize: ' +w + ',' + h );
25538 Roo.form.HtmlEditor.superclass.onResize.apply(this, arguments);
25539 if(this.el && this.iframe){
25540 if(typeof w == 'number'){
25541 var aw = w - this.wrap.getFrameWidth('lr');
25542 this.el.setWidth(this.adjustWidth('textarea', aw));
25543 this.iframe.style.width = aw + 'px';
25545 if(typeof h == 'number'){
25547 for (var i =0; i < this.toolbars.length;i++) {
25548 // fixme - ask toolbars for heights?
25549 tbh += this.toolbars[i].tb.el.getHeight();
25550 if (this.toolbars[i].footer) {
25551 tbh += this.toolbars[i].footer.el.getHeight();
25558 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
25559 ah -= 5; // knock a few pixes off for look..
25560 this.el.setHeight(this.adjustWidth('textarea', ah));
25561 this.iframe.style.height = ah + 'px';
25563 (this.doc.body || this.doc.documentElement).style.height = (ah - (this.iframePad*2)) + 'px';
25570 * Toggles the editor between standard and source edit mode.
25571 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
25573 toggleSourceEdit : function(sourceEditMode){
25575 this.sourceEditMode = sourceEditMode === true;
25577 if(this.sourceEditMode){
25579 // Roo.log(this.syncValue());
25581 this.iframe.className = 'x-hidden';
25582 this.el.removeClass('x-hidden');
25583 this.el.dom.removeAttribute('tabIndex');
25587 // Roo.log(this.pushValue());
25589 this.iframe.className = '';
25590 this.el.addClass('x-hidden');
25591 this.el.dom.setAttribute('tabIndex', -1);
25594 this.setSize(this.wrap.getSize());
25595 this.fireEvent('editmodechange', this, this.sourceEditMode);
25598 // private used internally
25599 createLink : function(){
25600 var url = prompt(this.createLinkText, this.defaultLinkValue);
25601 if(url && url != 'http:/'+'/'){
25602 this.relayCmd('createlink', url);
25606 // private (for BoxComponent)
25607 adjustSize : Roo.BoxComponent.prototype.adjustSize,
25609 // private (for BoxComponent)
25610 getResizeEl : function(){
25614 // private (for BoxComponent)
25615 getPositionEl : function(){
25620 initEvents : function(){
25621 this.originalValue = this.getValue();
25625 * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
25628 markInvalid : Roo.emptyFn,
25630 * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
25633 clearInvalid : Roo.emptyFn,
25635 setValue : function(v){
25636 Roo.form.HtmlEditor.superclass.setValue.call(this, v);
25641 * Protected method that will not generally be called directly. If you need/want
25642 * custom HTML cleanup, this is the method you should override.
25643 * @param {String} html The HTML to be cleaned
25644 * return {String} The cleaned HTML
25646 cleanHtml : function(html){
25647 html = String(html);
25648 if(html.length > 5){
25649 if(Roo.isSafari){ // strip safari nonsense
25650 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
25653 if(html == ' '){
25660 * Protected method that will not generally be called directly. Syncs the contents
25661 * of the editor iframe with the textarea.
25663 syncValue : function(){
25664 if(this.initialized){
25665 var bd = (this.doc.body || this.doc.documentElement);
25666 //this.cleanUpPaste(); -- this is done else where and causes havoc..
25667 var html = bd.innerHTML;
25669 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
25670 var m = bs.match(/text-align:(.*?);/i);
25672 html = '<div style="'+m[0]+'">' + html + '</div>';
25675 html = this.cleanHtml(html);
25676 // fix up the special chars.. normaly like back quotes in word...
25677 // however we do not want to do this with chinese..
25678 html = html.replace(/([\x80-\uffff])/g, function (a, b) {
25679 var cc = b.charCodeAt();
25681 (cc >= 0x4E00 && cc < 0xA000 ) ||
25682 (cc >= 0x3400 && cc < 0x4E00 ) ||
25683 (cc >= 0xf900 && cc < 0xfb00 )
25689 if(this.fireEvent('beforesync', this, html) !== false){
25690 this.el.dom.value = html;
25691 this.fireEvent('sync', this, html);
25697 * Protected method that will not generally be called directly. Pushes the value of the textarea
25698 * into the iframe editor.
25700 pushValue : function(){
25701 if(this.initialized){
25702 var v = this.el.dom.value;
25708 if(this.fireEvent('beforepush', this, v) !== false){
25709 var d = (this.doc.body || this.doc.documentElement);
25711 this.cleanUpPaste();
25712 this.el.dom.value = d.innerHTML;
25713 this.fireEvent('push', this, v);
25719 deferFocus : function(){
25720 this.focus.defer(10, this);
25724 focus : function(){
25725 if(this.win && !this.sourceEditMode){
25732 assignDocWin: function()
25734 var iframe = this.iframe;
25737 this.doc = iframe.contentWindow.document;
25738 this.win = iframe.contentWindow;
25740 if (!Roo.get(this.frameId)) {
25743 this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
25744 this.win = Roo.get(this.frameId).dom.contentWindow;
25749 initEditor : function(){
25750 //console.log("INIT EDITOR");
25751 this.assignDocWin();
25755 this.doc.designMode="on";
25757 this.doc.write(this.getDocMarkup());
25760 var dbody = (this.doc.body || this.doc.documentElement);
25761 //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
25762 // this copies styles from the containing element into thsi one..
25763 // not sure why we need all of this..
25764 var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
25765 ss['background-attachment'] = 'fixed'; // w3c
25766 dbody.bgProperties = 'fixed'; // ie
25767 Roo.DomHelper.applyStyles(dbody, ss);
25768 Roo.EventManager.on(this.doc, {
25769 //'mousedown': this.onEditorEvent,
25770 'mouseup': this.onEditorEvent,
25771 'dblclick': this.onEditorEvent,
25772 'click': this.onEditorEvent,
25773 'keyup': this.onEditorEvent,
25778 Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
25780 if(Roo.isIE || Roo.isSafari || Roo.isOpera){
25781 Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
25783 this.initialized = true;
25785 this.fireEvent('initialize', this);
25790 onDestroy : function(){
25796 for (var i =0; i < this.toolbars.length;i++) {
25797 // fixme - ask toolbars for heights?
25798 this.toolbars[i].onDestroy();
25801 this.wrap.dom.innerHTML = '';
25802 this.wrap.remove();
25807 onFirstFocus : function(){
25809 this.assignDocWin();
25812 this.activated = true;
25813 for (var i =0; i < this.toolbars.length;i++) {
25814 this.toolbars[i].onFirstFocus();
25817 if(Roo.isGecko){ // prevent silly gecko errors
25819 var s = this.win.getSelection();
25820 if(!s.focusNode || s.focusNode.nodeType != 3){
25821 var r = s.getRangeAt(0);
25822 r.selectNodeContents((this.doc.body || this.doc.documentElement));
25827 this.execCmd('useCSS', true);
25828 this.execCmd('styleWithCSS', false);
25831 this.fireEvent('activate', this);
25835 adjustFont: function(btn){
25836 var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
25837 //if(Roo.isSafari){ // safari
25840 var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
25841 if(Roo.isSafari){ // safari
25842 var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
25843 v = (v < 10) ? 10 : v;
25844 v = (v > 48) ? 48 : v;
25845 v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
25850 v = Math.max(1, v+adjust);
25852 this.execCmd('FontSize', v );
25855 onEditorEvent : function(e){
25856 this.fireEvent('editorevent', this, e);
25857 // this.updateToolbar();
25858 this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
25861 insertTag : function(tg)
25863 // could be a bit smarter... -> wrap the current selected tRoo..
25864 if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
25866 range = this.createRange(this.getSelection());
25867 var wrappingNode = this.doc.createElement(tg.toLowerCase());
25868 wrappingNode.appendChild(range.extractContents());
25869 range.insertNode(wrappingNode);
25876 this.execCmd("formatblock", tg);
25880 insertText : function(txt)
25884 var range = this.createRange();
25885 range.deleteContents();
25886 //alert(Sender.getAttribute('label'));
25888 range.insertNode(this.doc.createTextNode(txt));
25892 relayBtnCmd : function(btn){
25893 this.relayCmd(btn.cmd);
25897 * Executes a Midas editor command on the editor document and performs necessary focus and
25898 * toolbar updates. <b>This should only be called after the editor is initialized.</b>
25899 * @param {String} cmd The Midas command
25900 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
25902 relayCmd : function(cmd, value){
25904 this.execCmd(cmd, value);
25905 this.fireEvent('editorevent', this);
25906 //this.updateToolbar();
25911 * Executes a Midas editor command directly on the editor document.
25912 * For visual commands, you should use {@link #relayCmd} instead.
25913 * <b>This should only be called after the editor is initialized.</b>
25914 * @param {String} cmd The Midas command
25915 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
25917 execCmd : function(cmd, value){
25918 this.doc.execCommand(cmd, false, value === undefined ? null : value);
25925 * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
25927 * @param {String} text | dom node..
25929 insertAtCursor : function(text)
25934 if(!this.activated){
25940 var r = this.doc.selection.createRange();
25951 if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
25955 // from jquery ui (MIT licenced)
25957 var win = this.win;
25959 if (win.getSelection && win.getSelection().getRangeAt) {
25960 range = win.getSelection().getRangeAt(0);
25961 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
25962 range.insertNode(node);
25963 } else if (win.document.selection && win.document.selection.createRange) {
25964 // no firefox support
25965 var txt = typeof(text) == 'string' ? text : text.outerHTML;
25966 win.document.selection.createRange().pasteHTML(txt);
25968 // no firefox support
25969 var txt = typeof(text) == 'string' ? text : text.outerHTML;
25970 this.execCmd('InsertHTML', txt);
25979 mozKeyPress : function(e){
25981 var c = e.getCharCode(), cmd;
25984 c = String.fromCharCode(c).toLowerCase();
25998 this.cleanUpPaste.defer(100, this);
26006 e.preventDefault();
26014 fixKeys : function(){ // load time branching for fastest keydown performance
26016 return function(e){
26017 var k = e.getKey(), r;
26020 r = this.doc.selection.createRange();
26023 r.pasteHTML('    ');
26030 r = this.doc.selection.createRange();
26032 var target = r.parentElement();
26033 if(!target || target.tagName.toLowerCase() != 'li'){
26035 r.pasteHTML('<br />');
26041 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
26042 this.cleanUpPaste.defer(100, this);
26048 }else if(Roo.isOpera){
26049 return function(e){
26050 var k = e.getKey();
26054 this.execCmd('InsertHTML','    ');
26057 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
26058 this.cleanUpPaste.defer(100, this);
26063 }else if(Roo.isSafari){
26064 return function(e){
26065 var k = e.getKey();
26069 this.execCmd('InsertText','\t');
26073 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
26074 this.cleanUpPaste.defer(100, this);
26082 getAllAncestors: function()
26084 var p = this.getSelectedNode();
26087 a.push(p); // push blank onto stack..
26088 p = this.getParentElement();
26092 while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
26096 a.push(this.doc.body);
26100 lastSelNode : false,
26103 getSelection : function()
26105 this.assignDocWin();
26106 return Roo.isIE ? this.doc.selection : this.win.getSelection();
26109 getSelectedNode: function()
26111 // this may only work on Gecko!!!
26113 // should we cache this!!!!
26118 var range = this.createRange(this.getSelection()).cloneRange();
26121 var parent = range.parentElement();
26123 var testRange = range.duplicate();
26124 testRange.moveToElementText(parent);
26125 if (testRange.inRange(range)) {
26128 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
26131 parent = parent.parentElement;
26136 // is ancestor a text element.
26137 var ac = range.commonAncestorContainer;
26138 if (ac.nodeType == 3) {
26139 ac = ac.parentNode;
26142 var ar = ac.childNodes;
26145 var other_nodes = [];
26146 var has_other_nodes = false;
26147 for (var i=0;i<ar.length;i++) {
26148 if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ?
26151 // fullly contained node.
26153 if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
26158 // probably selected..
26159 if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
26160 other_nodes.push(ar[i]);
26164 if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0)) {
26169 has_other_nodes = true;
26171 if (!nodes.length && other_nodes.length) {
26172 nodes= other_nodes;
26174 if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
26180 createRange: function(sel)
26182 // this has strange effects when using with
26183 // top toolbar - not sure if it's a great idea.
26184 //this.editor.contentWindow.focus();
26185 if (typeof sel != "undefined") {
26187 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
26189 return this.doc.createRange();
26192 return this.doc.createRange();
26195 getParentElement: function()
26198 this.assignDocWin();
26199 var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
26201 var range = this.createRange(sel);
26204 var p = range.commonAncestorContainer;
26205 while (p.nodeType == 3) { // text node
26216 * Range intersection.. the hard stuff...
26220 * [ -- selected range --- ]
26224 * if end is before start or hits it. fail.
26225 * if start is after end or hits it fail.
26227 * if either hits (but other is outside. - then it's not
26233 // @see http://www.thismuchiknow.co.uk/?p=64.
26234 rangeIntersectsNode : function(range, node)
26236 var nodeRange = node.ownerDocument.createRange();
26238 nodeRange.selectNode(node);
26240 nodeRange.selectNodeContents(node);
26243 var rangeStartRange = range.cloneRange();
26244 rangeStartRange.collapse(true);
26246 var rangeEndRange = range.cloneRange();
26247 rangeEndRange.collapse(false);
26249 var nodeStartRange = nodeRange.cloneRange();
26250 nodeStartRange.collapse(true);
26252 var nodeEndRange = nodeRange.cloneRange();
26253 nodeEndRange.collapse(false);
26255 return rangeStartRange.compareBoundaryPoints(
26256 Range.START_TO_START, nodeEndRange) == -1 &&
26257 rangeEndRange.compareBoundaryPoints(
26258 Range.START_TO_START, nodeStartRange) == 1;
26262 rangeCompareNode : function(range, node)
26264 var nodeRange = node.ownerDocument.createRange();
26266 nodeRange.selectNode(node);
26268 nodeRange.selectNodeContents(node);
26272 range.collapse(true);
26274 nodeRange.collapse(true);
26276 var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
26277 var ee = range.compareBoundaryPoints( Range.END_TO_END, nodeRange);
26279 //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
26281 var nodeIsBefore = ss == 1;
26282 var nodeIsAfter = ee == -1;
26284 if (nodeIsBefore && nodeIsAfter)
26286 if (!nodeIsBefore && nodeIsAfter)
26287 return 1; //right trailed.
26289 if (nodeIsBefore && !nodeIsAfter)
26290 return 2; // left trailed.
26295 // private? - in a new class?
26296 cleanUpPaste : function()
26298 // cleans up the whole document..
26299 Roo.log('cleanuppaste');
26300 this.cleanUpChildren(this.doc.body);
26301 var clean = this.cleanWordChars(this.doc.body.innerHTML);
26302 if (clean != this.doc.body.innerHTML) {
26303 this.doc.body.innerHTML = clean;
26308 cleanWordChars : function(input) {// change the chars to hex code
26309 var he = Roo.form.HtmlEditor;
26311 var output = input;
26312 Roo.each(he.swapCodes, function(sw) {
26313 var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
26315 output = output.replace(swapper, sw[1]);
26322 cleanUpChildren : function (n)
26324 if (!n.childNodes.length) {
26327 for (var i = n.childNodes.length-1; i > -1 ; i--) {
26328 this.cleanUpChild(n.childNodes[i]);
26335 cleanUpChild : function (node)
26338 //console.log(node);
26339 if (node.nodeName == "#text") {
26340 // clean up silly Windows -- stuff?
26343 if (node.nodeName == "#comment") {
26344 node.parentNode.removeChild(node);
26345 // clean up silly Windows -- stuff?
26349 if (Roo.form.HtmlEditor.black.indexOf(node.tagName.toLowerCase()) > -1) {
26351 node.parentNode.removeChild(node);
26356 var remove_keep_children= Roo.form.HtmlEditor.remove.indexOf(node.tagName.toLowerCase()) > -1;
26358 // remove <a name=....> as rendering on yahoo mailer is borked with this.
26359 // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
26361 //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
26362 // remove_keep_children = true;
26365 if (remove_keep_children) {
26366 this.cleanUpChildren(node);
26367 // inserts everything just before this node...
26368 while (node.childNodes.length) {
26369 var cn = node.childNodes[0];
26370 node.removeChild(cn);
26371 node.parentNode.insertBefore(cn, node);
26373 node.parentNode.removeChild(node);
26377 if (!node.attributes || !node.attributes.length) {
26378 this.cleanUpChildren(node);
26382 function cleanAttr(n,v)
26385 if (v.match(/^\./) || v.match(/^\//)) {
26388 if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
26391 if (v.match(/^#/)) {
26394 // Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
26395 node.removeAttribute(n);
26399 function cleanStyle(n,v)
26401 if (v.match(/expression/)) { //XSS?? should we even bother..
26402 node.removeAttribute(n);
26405 var cwhite = typeof(ed.cwhite) == 'undefined' ? Roo.form.HtmlEditor.cwhite : ed.cwhite;
26406 var cblack = typeof(ed.cblack) == 'undefined' ? Roo.form.HtmlEditor.cblack : ed.cblack;
26409 var parts = v.split(/;/);
26412 Roo.each(parts, function(p) {
26413 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
26417 var l = p.split(':').shift().replace(/\s+/g,'');
26418 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
26421 if ( cblack.indexOf(l) > -1) {
26422 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
26423 //node.removeAttribute(n);
26427 // only allow 'c whitelisted system attributes'
26428 if ( cwhite.length && cwhite.indexOf(l) < 0) {
26429 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
26430 //node.removeAttribute(n);
26440 if (clean.length) {
26441 node.setAttribute(n, clean.join(';'));
26443 node.removeAttribute(n);
26449 for (var i = node.attributes.length-1; i > -1 ; i--) {
26450 var a = node.attributes[i];
26453 if (a.name.toLowerCase().substr(0,2)=='on') {
26454 node.removeAttribute(a.name);
26457 if (Roo.form.HtmlEditor.ablack.indexOf(a.name.toLowerCase()) > -1) {
26458 node.removeAttribute(a.name);
26461 if (Roo.form.HtmlEditor.aclean.indexOf(a.name.toLowerCase()) > -1) {
26462 cleanAttr(a.name,a.value); // fixme..
26465 if (a.name == 'style') {
26466 cleanStyle(a.name,a.value);
26469 /// clean up MS crap..
26470 // tecnically this should be a list of valid class'es..
26473 if (a.name == 'class') {
26474 if (a.value.match(/^Mso/)) {
26475 node.className = '';
26478 if (a.value.match(/body/)) {
26479 node.className = '';
26490 this.cleanUpChildren(node);
26496 // hide stuff that is not compatible
26510 * @event specialkey
26514 * @cfg {String} fieldClass @hide
26517 * @cfg {String} focusClass @hide
26520 * @cfg {String} autoCreate @hide
26523 * @cfg {String} inputType @hide
26526 * @cfg {String} invalidClass @hide
26529 * @cfg {String} invalidText @hide
26532 * @cfg {String} msgFx @hide
26535 * @cfg {String} validateOnBlur @hide
26539 Roo.form.HtmlEditor.white = [
26540 'area', 'br', 'img', 'input', 'hr', 'wbr',
26542 'address', 'blockquote', 'center', 'dd', 'dir', 'div',
26543 'dl', 'dt', 'h1', 'h2', 'h3', 'h4',
26544 'h5', 'h6', 'hr', 'isindex', 'listing', 'marquee',
26545 'menu', 'multicol', 'ol', 'p', 'plaintext', 'pre',
26546 'table', 'ul', 'xmp',
26548 'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th',
26551 'dir', 'menu', 'ol', 'ul', 'dl',
26557 Roo.form.HtmlEditor.black = [
26558 // 'embed', 'object', // enable - backend responsiblity to clean thiese
26560 'base', 'basefont', 'bgsound', 'blink', 'body',
26561 'frame', 'frameset', 'head', 'html', 'ilayer',
26562 'iframe', 'layer', 'link', 'meta', 'object',
26563 'script', 'style' ,'title', 'xml' // clean later..
26565 Roo.form.HtmlEditor.clean = [
26566 'script', 'style', 'title', 'xml'
26568 Roo.form.HtmlEditor.remove = [
26573 Roo.form.HtmlEditor.ablack = [
26577 Roo.form.HtmlEditor.aclean = [
26578 'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc'
26582 Roo.form.HtmlEditor.pwhite= [
26583 'http', 'https', 'mailto'
26586 // white listed style attributes.
26587 Roo.form.HtmlEditor.cwhite= [
26588 // 'text-align', /// default is to allow most things..
26594 // black listed style attributes.
26595 Roo.form.HtmlEditor.cblack= [
26596 // 'font-size' -- this can be set by the project
26600 Roo.form.HtmlEditor.swapCodes =[
26611 // <script type="text/javascript">
26614 * Ext JS Library 1.1.1
26615 * Copyright(c) 2006-2007, Ext JS, LLC.
26621 * @class Roo.form.HtmlEditorToolbar1
26626 new Roo.form.HtmlEditor({
26629 new Roo.form.HtmlEditorToolbar1({
26630 disable : { fonts: 1 , format: 1, ..., ... , ...],
26636 * @cfg {Object} disable List of elements to disable..
26637 * @cfg {Array} btns List of additional buttons.
26641 * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
26644 Roo.form.HtmlEditor.ToolbarStandard = function(config)
26647 Roo.apply(this, config);
26649 // default disabled, based on 'good practice'..
26650 this.disable = this.disable || {};
26651 Roo.applyIf(this.disable, {
26654 specialElements : true
26658 //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
26659 // dont call parent... till later.
26662 Roo.apply(Roo.form.HtmlEditor.ToolbarStandard.prototype, {
26670 * @cfg {Object} disable List of toolbar elements to disable
26675 * @cfg {Array} fontFamilies An array of available font families
26693 // "á" , ?? a acute?
26698 "°" // , // degrees
26700 // "é" , // e ecute
26701 // "ú" , // u ecute?
26704 specialElements : [
26706 text: "Insert Table",
26709 ihtml : '<table><tr><td>Cell</td></tr></table>'
26713 text: "Insert Image",
26716 ihtml : '<img src="about:blank"/>'
26725 "form", "input:text", "input:hidden", "input:checkbox", "input:radio", "input:password",
26726 "input:submit", "input:button", "select", "textarea", "label" ],
26729 ["h1"],["h2"],["h3"],["h4"],["h5"],["h6"],
26731 ["abbr"],[ "acronym"],[ "address"],[ "cite"],[ "samp"],[ "var"],
26735 * @cfg {String} defaultFont default font to use.
26737 defaultFont: 'tahoma',
26739 fontSelect : false,
26742 formatCombo : false,
26744 init : function(editor)
26746 this.editor = editor;
26749 var fid = editor.frameId;
26751 function btn(id, toggle, handler){
26752 var xid = fid + '-'+ id ;
26756 cls : 'x-btn-icon x-edit-'+id,
26757 enableToggle:toggle !== false,
26758 scope: editor, // was editor...
26759 handler:handler||editor.relayBtnCmd,
26760 clickEvent:'mousedown',
26761 tooltip: etb.buttonTips[id] || undefined, ///tips ???
26768 var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
26770 // stop form submits
26771 tb.el.on('click', function(e){
26772 e.preventDefault(); // what does this do?
26775 if(!this.disable.font) { // && !Roo.isSafari){
26776 /* why no safari for fonts
26777 editor.fontSelect = tb.el.createChild({
26780 cls:'x-font-select',
26781 html: this.createFontOptions()
26784 editor.fontSelect.on('change', function(){
26785 var font = editor.fontSelect.dom.value;
26786 editor.relayCmd('fontname', font);
26787 editor.deferFocus();
26791 editor.fontSelect.dom,
26797 if(!this.disable.formats){
26798 this.formatCombo = new Roo.form.ComboBox({
26799 store: new Roo.data.SimpleStore({
26802 data : this.formats // from states.js
26806 //autoCreate : {tag: "div", size: "20"},
26807 displayField:'tag',
26811 triggerAction: 'all',
26812 emptyText:'Add tag',
26813 selectOnFocus:true,
26816 'select': function(c, r, i) {
26817 editor.insertTag(r.get('tag'));
26823 tb.addField(this.formatCombo);
26827 if(!this.disable.format){
26834 if(!this.disable.fontSize){
26839 btn('increasefontsize', false, editor.adjustFont),
26840 btn('decreasefontsize', false, editor.adjustFont)
26845 if(!this.disable.colors){
26848 id:editor.frameId +'-forecolor',
26849 cls:'x-btn-icon x-edit-forecolor',
26850 clickEvent:'mousedown',
26851 tooltip: this.buttonTips['forecolor'] || undefined,
26853 menu : new Roo.menu.ColorMenu({
26854 allowReselect: true,
26855 focus: Roo.emptyFn,
26858 selectHandler: function(cp, color){
26859 editor.execCmd('forecolor', Roo.isSafari || Roo.isIE ? '#'+color : color);
26860 editor.deferFocus();
26863 clickEvent:'mousedown'
26866 id:editor.frameId +'backcolor',
26867 cls:'x-btn-icon x-edit-backcolor',
26868 clickEvent:'mousedown',
26869 tooltip: this.buttonTips['backcolor'] || undefined,
26871 menu : new Roo.menu.ColorMenu({
26872 focus: Roo.emptyFn,
26875 allowReselect: true,
26876 selectHandler: function(cp, color){
26878 editor.execCmd('useCSS', false);
26879 editor.execCmd('hilitecolor', color);
26880 editor.execCmd('useCSS', true);
26881 editor.deferFocus();
26883 editor.execCmd(Roo.isOpera ? 'hilitecolor' : 'backcolor',
26884 Roo.isSafari || Roo.isIE ? '#'+color : color);
26885 editor.deferFocus();
26889 clickEvent:'mousedown'
26894 // now add all the items...
26897 if(!this.disable.alignments){
26900 btn('justifyleft'),
26901 btn('justifycenter'),
26902 btn('justifyright')
26906 //if(!Roo.isSafari){
26907 if(!this.disable.links){
26910 btn('createlink', false, editor.createLink) /// MOVE TO HERE?!!?!?!?!
26914 if(!this.disable.lists){
26917 btn('insertorderedlist'),
26918 btn('insertunorderedlist')
26921 if(!this.disable.sourceEdit){
26924 btn('sourceedit', true, function(btn){
26925 this.toggleSourceEdit(btn.pressed);
26932 // special menu.. - needs to be tidied up..
26933 if (!this.disable.special) {
26936 cls: 'x-edit-none',
26942 for (var i =0; i < this.specialChars.length; i++) {
26943 smenu.menu.items.push({
26945 html: this.specialChars[i],
26946 handler: function(a,b) {
26947 editor.insertAtCursor(String.fromCharCode(a.html.replace('&#','').replace(';', '')));
26948 //editor.insertAtCursor(a.html);
26961 if (!this.disable.specialElements) {
26964 cls: 'x-edit-none',
26969 for (var i =0; i < this.specialElements.length; i++) {
26970 semenu.menu.items.push(
26972 handler: function(a,b) {
26973 editor.insertAtCursor(this.ihtml);
26975 }, this.specialElements[i])
26987 for(var i =0; i< this.btns.length;i++) {
26988 var b = Roo.factory(this.btns[i],Roo.form);
26989 b.cls = 'x-edit-none';
26998 // disable everything...
27000 this.tb.items.each(function(item){
27001 if(item.id != editor.frameId+ '-sourceedit'){
27005 this.rendered = true;
27007 // the all the btns;
27008 editor.on('editorevent', this.updateToolbar, this);
27009 // other toolbars need to implement this..
27010 //editor.on('editmodechange', this.updateToolbar, this);
27016 * Protected method that will not generally be called directly. It triggers
27017 * a toolbar update by reading the markup state of the current selection in the editor.
27019 updateToolbar: function(){
27021 if(!this.editor.activated){
27022 this.editor.onFirstFocus();
27026 var btns = this.tb.items.map,
27027 doc = this.editor.doc,
27028 frameId = this.editor.frameId;
27030 if(!this.disable.font && !Roo.isSafari){
27032 var name = (doc.queryCommandValue('FontName')||this.editor.defaultFont).toLowerCase();
27033 if(name != this.fontSelect.dom.value){
27034 this.fontSelect.dom.value = name;
27038 if(!this.disable.format){
27039 btns[frameId + '-bold'].toggle(doc.queryCommandState('bold'));
27040 btns[frameId + '-italic'].toggle(doc.queryCommandState('italic'));
27041 btns[frameId + '-underline'].toggle(doc.queryCommandState('underline'));
27043 if(!this.disable.alignments){
27044 btns[frameId + '-justifyleft'].toggle(doc.queryCommandState('justifyleft'));
27045 btns[frameId + '-justifycenter'].toggle(doc.queryCommandState('justifycenter'));
27046 btns[frameId + '-justifyright'].toggle(doc.queryCommandState('justifyright'));
27048 if(!Roo.isSafari && !this.disable.lists){
27049 btns[frameId + '-insertorderedlist'].toggle(doc.queryCommandState('insertorderedlist'));
27050 btns[frameId + '-insertunorderedlist'].toggle(doc.queryCommandState('insertunorderedlist'));
27053 var ans = this.editor.getAllAncestors();
27054 if (this.formatCombo) {
27057 var store = this.formatCombo.store;
27058 this.formatCombo.setValue("");
27059 for (var i =0; i < ans.length;i++) {
27060 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
27062 this.formatCombo.setValue(ans[i].tagName.toLowerCase());
27070 // hides menus... - so this cant be on a menu...
27071 Roo.menu.MenuMgr.hideAll();
27073 //this.editorsyncValue();
27077 createFontOptions : function(){
27078 var buf = [], fs = this.fontFamilies, ff, lc;
27082 for(var i = 0, len = fs.length; i< len; i++){
27084 lc = ff.toLowerCase();
27086 '<option value="',lc,'" style="font-family:',ff,';"',
27087 (this.defaultFont == lc ? ' selected="true">' : '>'),
27092 return buf.join('');
27095 toggleSourceEdit : function(sourceEditMode){
27096 if(sourceEditMode === undefined){
27097 sourceEditMode = !this.sourceEditMode;
27099 this.sourceEditMode = sourceEditMode === true;
27100 var btn = this.tb.items.get(this.editor.frameId +'-sourceedit');
27101 // just toggle the button?
27102 if(btn.pressed !== this.editor.sourceEditMode){
27103 btn.toggle(this.editor.sourceEditMode);
27107 if(this.sourceEditMode){
27108 this.tb.items.each(function(item){
27109 if(item.cmd != 'sourceedit'){
27115 if(this.initialized){
27116 this.tb.items.each(function(item){
27122 // tell the editor that it's been pressed..
27123 this.editor.toggleSourceEdit(sourceEditMode);
27127 * Object collection of toolbar tooltips for the buttons in the editor. The key
27128 * is the command id associated with that button and the value is a valid QuickTips object.
27133 title: 'Bold (Ctrl+B)',
27134 text: 'Make the selected text bold.',
27135 cls: 'x-html-editor-tip'
27138 title: 'Italic (Ctrl+I)',
27139 text: 'Make the selected text italic.',
27140 cls: 'x-html-editor-tip'
27148 title: 'Bold (Ctrl+B)',
27149 text: 'Make the selected text bold.',
27150 cls: 'x-html-editor-tip'
27153 title: 'Italic (Ctrl+I)',
27154 text: 'Make the selected text italic.',
27155 cls: 'x-html-editor-tip'
27158 title: 'Underline (Ctrl+U)',
27159 text: 'Underline the selected text.',
27160 cls: 'x-html-editor-tip'
27162 increasefontsize : {
27163 title: 'Grow Text',
27164 text: 'Increase the font size.',
27165 cls: 'x-html-editor-tip'
27167 decreasefontsize : {
27168 title: 'Shrink Text',
27169 text: 'Decrease the font size.',
27170 cls: 'x-html-editor-tip'
27173 title: 'Text Highlight Color',
27174 text: 'Change the background color of the selected text.',
27175 cls: 'x-html-editor-tip'
27178 title: 'Font Color',
27179 text: 'Change the color of the selected text.',
27180 cls: 'x-html-editor-tip'
27183 title: 'Align Text Left',
27184 text: 'Align text to the left.',
27185 cls: 'x-html-editor-tip'
27188 title: 'Center Text',
27189 text: 'Center text in the editor.',
27190 cls: 'x-html-editor-tip'
27193 title: 'Align Text Right',
27194 text: 'Align text to the right.',
27195 cls: 'x-html-editor-tip'
27197 insertunorderedlist : {
27198 title: 'Bullet List',
27199 text: 'Start a bulleted list.',
27200 cls: 'x-html-editor-tip'
27202 insertorderedlist : {
27203 title: 'Numbered List',
27204 text: 'Start a numbered list.',
27205 cls: 'x-html-editor-tip'
27208 title: 'Hyperlink',
27209 text: 'Make the selected text a hyperlink.',
27210 cls: 'x-html-editor-tip'
27213 title: 'Source Edit',
27214 text: 'Switch to source editing mode.',
27215 cls: 'x-html-editor-tip'
27219 onDestroy : function(){
27222 this.tb.items.each(function(item){
27224 item.menu.removeAll();
27226 item.menu.el.destroy();
27234 onFirstFocus: function() {
27235 this.tb.items.each(function(item){
27244 // <script type="text/javascript">
27247 * Ext JS Library 1.1.1
27248 * Copyright(c) 2006-2007, Ext JS, LLC.
27255 * @class Roo.form.HtmlEditor.ToolbarContext
27260 new Roo.form.HtmlEditor({
27263 { xtype: 'ToolbarStandard', styles : {} }
27264 { xtype: 'ToolbarContext', disable : {} }
27270 * @config : {Object} disable List of elements to disable.. (not done yet.)
27271 * @config : {Object} styles Map of styles available.
27275 Roo.form.HtmlEditor.ToolbarContext = function(config)
27278 Roo.apply(this, config);
27279 //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
27280 // dont call parent... till later.
27281 this.styles = this.styles || {};
27286 Roo.form.HtmlEditor.ToolbarContext.types = {
27298 opts : [ [""],[ "left"],[ "right"],[ "center"],[ "top"]],
27360 opts : [[""],[ "left"],[ "center"],[ "right"],[ "justify"],[ "char"]],
27365 opts : [[""],[ "top"],[ "middle"],[ "bottom"],[ "baseline"]],
27375 style : 'fontFamily',
27376 displayField: 'display',
27377 optname : 'font-family',
27426 // should we really allow this??
27427 // should this just be
27438 style : 'fontFamily',
27439 displayField: 'display',
27440 optname : 'font-family',
27447 style : 'fontFamily',
27448 displayField: 'display',
27449 optname : 'font-family',
27456 style : 'fontFamily',
27457 displayField: 'display',
27458 optname : 'font-family',
27469 // this should be configurable.. - you can either set it up using stores, or modify options somehwere..
27470 Roo.form.HtmlEditor.ToolbarContext.stores = false;
27472 Roo.form.HtmlEditor.ToolbarContext.options = {
27474 [ 'Helvetica,Arial,sans-serif', 'Helvetica'],
27475 [ 'Courier New', 'Courier New'],
27476 [ 'Tahoma', 'Tahoma'],
27477 [ 'Times New Roman,serif', 'Times'],
27478 [ 'Verdana','Verdana' ]
27482 // fixme - these need to be configurable..
27485 Roo.form.HtmlEditor.ToolbarContext.types
27488 Roo.apply(Roo.form.HtmlEditor.ToolbarContext.prototype, {
27496 * @cfg {Object} disable List of toolbar elements to disable
27501 * @cfg {Object} styles List of styles
27502 * eg. { '*' : [ 'headline' ] , 'TD' : [ 'underline', 'double-underline' ] }
27504 * These must be defined in the page, so they get rendered correctly..
27515 init : function(editor)
27517 this.editor = editor;
27520 var fid = editor.frameId;
27522 function btn(id, toggle, handler){
27523 var xid = fid + '-'+ id ;
27527 cls : 'x-btn-icon x-edit-'+id,
27528 enableToggle:toggle !== false,
27529 scope: editor, // was editor...
27530 handler:handler||editor.relayBtnCmd,
27531 clickEvent:'mousedown',
27532 tooltip: etb.buttonTips[id] || undefined, ///tips ???
27536 // create a new element.
27537 var wdiv = editor.wrap.createChild({
27539 }, editor.wrap.dom.firstChild.nextSibling, true);
27541 // can we do this more than once??
27543 // stop form submits
27546 // disable everything...
27547 var ty= Roo.form.HtmlEditor.ToolbarContext.types;
27548 this.toolbars = {};
27550 for (var i in ty) {
27552 this.toolbars[i] = this.buildToolbar(ty[i],i);
27554 this.tb = this.toolbars.BODY;
27556 this.buildFooter();
27557 this.footer.show();
27558 editor.on('hide', function( ) { this.footer.hide() }, this);
27559 editor.on('show', function( ) { this.footer.show() }, this);
27562 this.rendered = true;
27564 // the all the btns;
27565 editor.on('editorevent', this.updateToolbar, this);
27566 // other toolbars need to implement this..
27567 //editor.on('editmodechange', this.updateToolbar, this);
27573 * Protected method that will not generally be called directly. It triggers
27574 * a toolbar update by reading the markup state of the current selection in the editor.
27576 updateToolbar: function(editor,ev,sel){
27579 // capture mouse up - this is handy for selecting images..
27580 // perhaps should go somewhere else...
27581 if(!this.editor.activated){
27582 this.editor.onFirstFocus();
27586 // http://developer.yahoo.com/yui/docs/simple-editor.js.html
27587 // selectNode - might want to handle IE?
27589 (ev.type == 'mouseup' || ev.type == 'click' ) &&
27590 ev.target && ev.target.tagName == 'IMG') {
27591 // they have click on an image...
27592 // let's see if we can change the selection...
27595 var nodeRange = sel.ownerDocument.createRange();
27597 nodeRange.selectNode(sel);
27599 nodeRange.selectNodeContents(sel);
27601 //nodeRange.collapse(true);
27602 var s = editor.win.getSelection();
27603 s.removeAllRanges();
27604 s.addRange(nodeRange);
27608 var updateFooter = sel ? false : true;
27611 var ans = this.editor.getAllAncestors();
27614 var ty= Roo.form.HtmlEditor.ToolbarContext.types;
27617 sel = ans.length ? (ans[0] ? ans[0] : ans[1]) : this.editor.doc.body;
27618 sel = sel ? sel : this.editor.doc.body;
27619 sel = sel.tagName.length ? sel : this.editor.doc.body;
27622 // pick a menu that exists..
27623 var tn = sel.tagName.toUpperCase();
27624 //sel = typeof(ty[tn]) != 'undefined' ? sel : this.editor.doc.body;
27626 tn = sel.tagName.toUpperCase();
27628 var lastSel = this.tb.selectedNode
27630 this.tb.selectedNode = sel;
27632 // if current menu does not match..
27633 if ((this.tb.name != tn) || (lastSel != this.tb.selectedNode)) {
27636 ///console.log("show: " + tn);
27637 this.tb = typeof(ty[tn]) != 'undefined' ? this.toolbars[tn] : this.toolbars['*'];
27640 this.tb.items.first().el.innerHTML = tn + ': ';
27643 // update attributes
27644 if (this.tb.fields) {
27645 this.tb.fields.each(function(e) {
27647 e.setValue(sel.style[e.stylename]);
27650 e.setValue(sel.getAttribute(e.attrname));
27654 var hasStyles = false;
27655 for(var i in this.styles) {
27662 var st = this.tb.fields.item(0);
27664 st.store.removeAll();
27667 var cn = sel.className.split(/\s+/);
27670 if (this.styles['*']) {
27672 Roo.each(this.styles['*'], function(v) {
27673 avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );
27676 if (this.styles[tn]) {
27677 Roo.each(this.styles[tn], function(v) {
27678 avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );
27682 st.store.loadData(avs);
27686 // flag our selected Node.
27687 this.tb.selectedNode = sel;
27690 Roo.menu.MenuMgr.hideAll();
27694 if (!updateFooter) {
27695 //this.footDisp.dom.innerHTML = '';
27698 // update the footer
27702 this.footerEls = ans.reverse();
27703 Roo.each(this.footerEls, function(a,i) {
27704 if (!a) { return; }
27705 html += html.length ? ' > ' : '';
27707 html += '<span class="x-ed-loc-' + i + '">' + a.tagName + '</span>';
27712 var sz = this.footDisp.up('td').getSize();
27713 this.footDisp.dom.style.width = (sz.width -10) + 'px';
27714 this.footDisp.dom.style.marginLeft = '5px';
27716 this.footDisp.dom.style.overflow = 'hidden';
27718 this.footDisp.dom.innerHTML = html;
27720 //this.editorsyncValue();
27727 onDestroy : function(){
27730 this.tb.items.each(function(item){
27732 item.menu.removeAll();
27734 item.menu.el.destroy();
27742 onFirstFocus: function() {
27743 // need to do this for all the toolbars..
27744 this.tb.items.each(function(item){
27748 buildToolbar: function(tlist, nm)
27750 var editor = this.editor;
27751 // create a new element.
27752 var wdiv = editor.wrap.createChild({
27754 }, editor.wrap.dom.firstChild.nextSibling, true);
27757 var tb = new Roo.Toolbar(wdiv);
27760 tb.add(nm+ ": ");
27763 for(var i in this.styles) {
27768 if (styles && styles.length) {
27770 // this needs a multi-select checkbox...
27771 tb.addField( new Roo.form.ComboBox({
27772 store: new Roo.data.SimpleStore({
27774 fields: ['val', 'selected'],
27777 name : '-roo-edit-className',
27778 attrname : 'className',
27779 displayField: 'val',
27783 triggerAction: 'all',
27784 emptyText:'Select Style',
27785 selectOnFocus:true,
27788 'select': function(c, r, i) {
27789 // initial support only for on class per el..
27790 tb.selectedNode.className = r ? r.get('val') : '';
27791 editor.syncValue();
27798 var tbc = Roo.form.HtmlEditor.ToolbarContext;
27799 var tbops = tbc.options;
27801 for (var i in tlist) {
27803 var item = tlist[i];
27804 tb.add(item.title + ": ");
27807 //optname == used so you can configure the options available..
27808 var opts = item.opts ? item.opts : false;
27809 if (item.optname) {
27810 opts = tbops[item.optname];
27815 // opts == pulldown..
27816 tb.addField( new Roo.form.ComboBox({
27817 store: typeof(tbc.stores[i]) != 'undefined' ? Roo.factory(tbc.stores[i],Roo.data) : new Roo.data.SimpleStore({
27819 fields: ['val', 'display'],
27822 name : '-roo-edit-' + i,
27824 stylename : item.style ? item.style : false,
27825 displayField: item.displayField ? item.displayField : 'val',
27826 valueField : 'val',
27828 mode: typeof(tbc.stores[i]) != 'undefined' ? 'remote' : 'local',
27830 triggerAction: 'all',
27831 emptyText:'Select',
27832 selectOnFocus:true,
27833 width: item.width ? item.width : 130,
27835 'select': function(c, r, i) {
27837 tb.selectedNode.style[c.stylename] = r.get('val');
27840 tb.selectedNode.setAttribute(c.attrname, r.get('val'));
27849 tb.addField( new Roo.form.TextField({
27852 //allowBlank:false,
27857 tb.addField( new Roo.form.TextField({
27858 name: '-roo-edit-' + i,
27865 'change' : function(f, nv, ov) {
27866 tb.selectedNode.setAttribute(f.attrname, nv);
27875 text: 'Remove Tag',
27878 click : function ()
27881 // undo does not work.
27883 var sn = tb.selectedNode;
27885 var pn = sn.parentNode;
27887 var stn = sn.childNodes[0];
27888 var en = sn.childNodes[sn.childNodes.length - 1 ];
27889 while (sn.childNodes.length) {
27890 var node = sn.childNodes[0];
27891 sn.removeChild(node);
27893 pn.insertBefore(node, sn);
27896 pn.removeChild(sn);
27897 var range = editor.createRange();
27899 range.setStart(stn,0);
27900 range.setEnd(en,0); //????
27901 //range.selectNode(sel);
27904 var selection = editor.getSelection();
27905 selection.removeAllRanges();
27906 selection.addRange(range);
27910 //_this.updateToolbar(null, null, pn);
27911 _this.updateToolbar(null, null, null);
27912 _this.footDisp.dom.innerHTML = '';
27922 tb.el.on('click', function(e){
27923 e.preventDefault(); // what does this do?
27925 tb.el.setVisibilityMode( Roo.Element.DISPLAY);
27928 // dont need to disable them... as they will get hidden
27933 buildFooter : function()
27936 var fel = this.editor.wrap.createChild();
27937 this.footer = new Roo.Toolbar(fel);
27938 // toolbar has scrolly on left / right?
27939 var footDisp= new Roo.Toolbar.Fill();
27945 handler : function() {
27946 _t.footDisp.scrollTo('left',0,true)
27950 this.footer.add( footDisp );
27955 handler : function() {
27957 _t.footDisp.select('span').last().scrollIntoView(_t.footDisp,true);
27961 var fel = Roo.get(footDisp.el);
27962 fel.addClass('x-editor-context');
27963 this.footDispWrap = fel;
27964 this.footDispWrap.overflow = 'hidden';
27966 this.footDisp = fel.createChild();
27967 this.footDispWrap.on('click', this.onContextClick, this)
27971 onContextClick : function (ev,dom)
27973 ev.preventDefault();
27974 var cn = dom.className;
27976 if (!cn.match(/x-ed-loc-/)) {
27979 var n = cn.split('-').pop();
27980 var ans = this.footerEls;
27984 var range = this.editor.createRange();
27986 range.selectNodeContents(sel);
27987 //range.selectNode(sel);
27990 var selection = this.editor.getSelection();
27991 selection.removeAllRanges();
27992 selection.addRange(range);
27996 this.updateToolbar(null, null, sel);
28013 * Ext JS Library 1.1.1
28014 * Copyright(c) 2006-2007, Ext JS, LLC.
28016 * Originally Released Under LGPL - original licence link has changed is not relivant.
28019 * <script type="text/javascript">
28023 * @class Roo.form.BasicForm
28024 * @extends Roo.util.Observable
28025 * Supplies the functionality to do "actions" on forms and initialize Roo.form.Field types on existing markup.
28027 * @param {String/HTMLElement/Roo.Element} el The form element or its id
28028 * @param {Object} config Configuration options
28030 Roo.form.BasicForm = function(el, config){
28031 this.allItems = [];
28032 this.childForms = [];
28033 Roo.apply(this, config);
28035 * The Roo.form.Field items in this form.
28036 * @type MixedCollection
28040 this.items = new Roo.util.MixedCollection(false, function(o){
28041 return o.id || (o.id = Roo.id());
28045 * @event beforeaction
28046 * Fires before any action is performed. Return false to cancel the action.
28047 * @param {Form} this
28048 * @param {Action} action The action to be performed
28050 beforeaction: true,
28052 * @event actionfailed
28053 * Fires when an action fails.
28054 * @param {Form} this
28055 * @param {Action} action The action that failed
28057 actionfailed : true,
28059 * @event actioncomplete
28060 * Fires when an action is completed.
28061 * @param {Form} this
28062 * @param {Action} action The action that completed
28064 actioncomplete : true
28069 Roo.form.BasicForm.superclass.constructor.call(this);
28072 Roo.extend(Roo.form.BasicForm, Roo.util.Observable, {
28074 * @cfg {String} method
28075 * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
28078 * @cfg {DataReader} reader
28079 * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when executing "load" actions.
28080 * This is optional as there is built-in support for processing JSON.
28083 * @cfg {DataReader} errorReader
28084 * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when reading validation errors on "submit" actions.
28085 * This is completely optional as there is built-in support for processing JSON.
28088 * @cfg {String} url
28089 * The URL to use for form actions if one isn't supplied in the action options.
28092 * @cfg {Boolean} fileUpload
28093 * Set to true if this form is a file upload.
28097 * @cfg {Object} baseParams
28098 * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
28103 * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
28108 activeAction : null,
28111 * @cfg {Boolean} trackResetOnLoad If set to true, form.reset() resets to the last loaded
28112 * or setValues() data instead of when the form was first created.
28114 trackResetOnLoad : false,
28118 * childForms - used for multi-tab forms
28121 childForms : false,
28124 * allItems - full list of fields.
28130 * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
28131 * element by passing it or its id or mask the form itself by passing in true.
28134 waitMsgTarget : false,
28137 initEl : function(el){
28138 this.el = Roo.get(el);
28139 this.id = this.el.id || Roo.id();
28140 this.el.on('submit', this.onSubmit, this);
28141 this.el.addClass('x-form');
28145 onSubmit : function(e){
28150 * Returns true if client-side validation on the form is successful.
28153 isValid : function(){
28155 this.items.each(function(f){
28164 * Returns true if any fields in this form have changed since their original load.
28167 isDirty : function(){
28169 this.items.each(function(f){
28179 * Performs a predefined action (submit or load) or custom actions you define on this form.
28180 * @param {String} actionName The name of the action type
28181 * @param {Object} options (optional) The options to pass to the action. All of the config options listed
28182 * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
28183 * accept other config options):
28185 Property Type Description
28186 ---------------- --------------- ----------------------------------------------------------------------------------
28187 url String The url for the action (defaults to the form's url)
28188 method String The form method to use (defaults to the form's method, or POST if not defined)
28189 params String/Object The params to pass (defaults to the form's baseParams, or none if not defined)
28190 clientValidation Boolean Applies to submit only. Pass true to call form.isValid() prior to posting to
28191 validate the form on the client (defaults to false)
28193 * @return {BasicForm} this
28195 doAction : function(action, options){
28196 if(typeof action == 'string'){
28197 action = new Roo.form.Action.ACTION_TYPES[action](this, options);
28199 if(this.fireEvent('beforeaction', this, action) !== false){
28200 this.beforeAction(action);
28201 action.run.defer(100, action);
28207 * Shortcut to do a submit action.
28208 * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
28209 * @return {BasicForm} this
28211 submit : function(options){
28212 this.doAction('submit', options);
28217 * Shortcut to do a load action.
28218 * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
28219 * @return {BasicForm} this
28221 load : function(options){
28222 this.doAction('load', options);
28227 * Persists the values in this form into the passed Roo.data.Record object in a beginEdit/endEdit block.
28228 * @param {Record} record The record to edit
28229 * @return {BasicForm} this
28231 updateRecord : function(record){
28232 record.beginEdit();
28233 var fs = record.fields;
28234 fs.each(function(f){
28235 var field = this.findField(f.name);
28237 record.set(f.name, field.getValue());
28245 * Loads an Roo.data.Record into this form.
28246 * @param {Record} record The record to load
28247 * @return {BasicForm} this
28249 loadRecord : function(record){
28250 this.setValues(record.data);
28255 beforeAction : function(action){
28256 var o = action.options;
28259 if(this.waitMsgTarget === true){
28260 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
28261 }else if(this.waitMsgTarget){
28262 this.waitMsgTarget = Roo.get(this.waitMsgTarget);
28263 this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
28265 Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
28271 afterAction : function(action, success){
28272 this.activeAction = null;
28273 var o = action.options;
28275 if(this.waitMsgTarget === true){
28277 }else if(this.waitMsgTarget){
28278 this.waitMsgTarget.unmask();
28280 Roo.MessageBox.updateProgress(1);
28281 Roo.MessageBox.hide();
28288 Roo.callback(o.success, o.scope, [this, action]);
28289 this.fireEvent('actioncomplete', this, action);
28293 // failure condition..
28294 // we have a scenario where updates need confirming.
28295 // eg. if a locking scenario exists..
28296 // we look for { errors : { needs_confirm : true }} in the response.
28298 (typeof(action.result) != 'undefined') &&
28299 (typeof(action.result.errors) != 'undefined') &&
28300 (typeof(action.result.errors.needs_confirm) != 'undefined')
28303 Roo.MessageBox.confirm(
28304 "Change requires confirmation",
28305 action.result.errorMsg,
28310 _t.doAction('submit', { params : { _submit_confirmed : 1 } } );
28320 Roo.callback(o.failure, o.scope, [this, action]);
28321 // show an error message if no failed handler is set..
28322 if (!this.hasListener('actionfailed')) {
28323 Roo.MessageBox.alert("Error",
28324 (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
28325 action.result.errorMsg :
28326 "Saving Failed, please check your entries or try again"
28330 this.fireEvent('actionfailed', this, action);
28336 * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
28337 * @param {String} id The value to search for
28340 findField : function(id){
28341 var field = this.items.get(id);
28343 this.items.each(function(f){
28344 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
28350 return field || null;
28354 * Add a secondary form to this one,
28355 * Used to provide tabbed forms. One form is primary, with hidden values
28356 * which mirror the elements from the other forms.
28358 * @param {Roo.form.Form} form to add.
28361 addForm : function(form)
28364 if (this.childForms.indexOf(form) > -1) {
28368 this.childForms.push(form);
28370 Roo.each(form.allItems, function (fe) {
28372 n = typeof(fe.getName) == 'undefined' ? fe.name : fe.getName();
28373 if (this.findField(n)) { // already added..
28376 var add = new Roo.form.Hidden({
28379 add.render(this.el);
28386 * Mark fields in this form invalid in bulk.
28387 * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
28388 * @return {BasicForm} this
28390 markInvalid : function(errors){
28391 if(errors instanceof Array){
28392 for(var i = 0, len = errors.length; i < len; i++){
28393 var fieldError = errors[i];
28394 var f = this.findField(fieldError.id);
28396 f.markInvalid(fieldError.msg);
28402 if(typeof errors[id] != 'function' && (field = this.findField(id))){
28403 field.markInvalid(errors[id]);
28407 Roo.each(this.childForms || [], function (f) {
28408 f.markInvalid(errors);
28415 * Set values for fields in this form in bulk.
28416 * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
28417 * @return {BasicForm} this
28419 setValues : function(values){
28420 if(values instanceof Array){ // array of objects
28421 for(var i = 0, len = values.length; i < len; i++){
28423 var f = this.findField(v.id);
28425 f.setValue(v.value);
28426 if(this.trackResetOnLoad){
28427 f.originalValue = f.getValue();
28431 }else{ // object hash
28434 if(typeof values[id] != 'function' && (field = this.findField(id))){
28436 if (field.setFromData &&
28437 field.valueField &&
28438 field.displayField &&
28439 // combos' with local stores can
28440 // be queried via setValue()
28441 // to set their value..
28442 (field.store && !field.store.isLocal)
28446 sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
28447 sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
28448 field.setFromData(sd);
28451 field.setValue(values[id]);
28455 if(this.trackResetOnLoad){
28456 field.originalValue = field.getValue();
28462 Roo.each(this.childForms || [], function (f) {
28463 f.setValues(values);
28470 * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
28471 * they are returned as an array.
28472 * @param {Boolean} asString
28475 getValues : function(asString){
28476 if (this.childForms) {
28477 // copy values from the child forms
28478 Roo.each(this.childForms, function (f) {
28479 this.setValues(f.getValues());
28485 var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
28486 if(asString === true){
28489 return Roo.urlDecode(fs);
28493 * Returns the fields in this form as an object with key/value pairs.
28494 * This differs from getValues as it calls getValue on each child item, rather than using dom data.
28497 getFieldValues : function(with_hidden)
28499 if (this.childForms) {
28500 // copy values from the child forms
28501 // should this call getFieldValues - probably not as we do not currently copy
28502 // hidden fields when we generate..
28503 Roo.each(this.childForms, function (f) {
28504 this.setValues(f.getValues());
28509 this.items.each(function(f){
28510 if (!f.getName()) {
28513 var v = f.getValue();
28514 if (f.inputType =='radio') {
28515 if (typeof(ret[f.getName()]) == 'undefined') {
28516 ret[f.getName()] = ''; // empty..
28519 if (!f.el.dom.checked) {
28523 v = f.el.dom.value;
28527 // not sure if this supported any more..
28528 if ((typeof(v) == 'object') && f.getRawValue) {
28529 v = f.getRawValue() ; // dates..
28531 // combo boxes where name != hiddenName...
28532 if (f.name != f.getName()) {
28533 ret[f.name] = f.getRawValue();
28535 ret[f.getName()] = v;
28542 * Clears all invalid messages in this form.
28543 * @return {BasicForm} this
28545 clearInvalid : function(){
28546 this.items.each(function(f){
28550 Roo.each(this.childForms || [], function (f) {
28559 * Resets this form.
28560 * @return {BasicForm} this
28562 reset : function(){
28563 this.items.each(function(f){
28567 Roo.each(this.childForms || [], function (f) {
28576 * Add Roo.form components to this form.
28577 * @param {Field} field1
28578 * @param {Field} field2 (optional)
28579 * @param {Field} etc (optional)
28580 * @return {BasicForm} this
28583 this.items.addAll(Array.prototype.slice.call(arguments, 0));
28589 * Removes a field from the items collection (does NOT remove its markup).
28590 * @param {Field} field
28591 * @return {BasicForm} this
28593 remove : function(field){
28594 this.items.remove(field);
28599 * Looks at the fields in this form, checks them for an id attribute,
28600 * and calls applyTo on the existing dom element with that id.
28601 * @return {BasicForm} this
28603 render : function(){
28604 this.items.each(function(f){
28605 if(f.isFormField && !f.rendered && document.getElementById(f.id)){ // if the element exists
28613 * Calls {@link Ext#apply} for all fields in this form with the passed object.
28614 * @param {Object} values
28615 * @return {BasicForm} this
28617 applyToFields : function(o){
28618 this.items.each(function(f){
28625 * Calls {@link Ext#applyIf} for all field in this form with the passed object.
28626 * @param {Object} values
28627 * @return {BasicForm} this
28629 applyIfToFields : function(o){
28630 this.items.each(function(f){
28638 Roo.BasicForm = Roo.form.BasicForm;/*
28640 * Ext JS Library 1.1.1
28641 * Copyright(c) 2006-2007, Ext JS, LLC.
28643 * Originally Released Under LGPL - original licence link has changed is not relivant.
28646 * <script type="text/javascript">
28650 * @class Roo.form.Form
28651 * @extends Roo.form.BasicForm
28652 * Adds the ability to dynamically render forms with JavaScript to {@link Roo.form.BasicForm}.
28654 * @param {Object} config Configuration options
28656 Roo.form.Form = function(config){
28658 if (config.items) {
28659 xitems = config.items;
28660 delete config.items;
28664 Roo.form.Form.superclass.constructor.call(this, null, config);
28665 this.url = this.url || this.action;
28667 this.root = new Roo.form.Layout(Roo.applyIf({
28671 this.active = this.root;
28673 * Array of all the buttons that have been added to this form via {@link addButton}
28677 this.allItems = [];
28680 * @event clientvalidation
28681 * If the monitorValid config option is true, this event fires repetitively to notify of valid state
28682 * @param {Form} this
28683 * @param {Boolean} valid true if the form has passed client-side validation
28685 clientvalidation: true,
28688 * Fires when the form is rendered
28689 * @param {Roo.form.Form} form
28694 if (this.progressUrl) {
28695 // push a hidden field onto the list of fields..
28699 name : 'UPLOAD_IDENTIFIER'
28704 Roo.each(xitems, this.addxtype, this);
28710 Roo.extend(Roo.form.Form, Roo.form.BasicForm, {
28712 * @cfg {Number} labelWidth The width of labels. This property cascades to child containers.
28715 * @cfg {String} itemCls A css class to apply to the x-form-item of fields. This property cascades to child containers.
28718 * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "center")
28720 buttonAlign:'center',
28723 * @cfg {Number} minButtonWidth Minimum width of all buttons in pixels (defaults to 75)
28728 * @cfg {String} labelAlign Valid values are "left," "top" and "right" (defaults to "left").
28729 * This property cascades to child containers if not set.
28734 * @cfg {Boolean} monitorValid If true the form monitors its valid state <b>client-side</b> and
28735 * fires a looping event with that state. This is required to bind buttons to the valid
28736 * state using the config value formBind:true on the button.
28738 monitorValid : false,
28741 * @cfg {Number} monitorPoll The milliseconds to poll valid state, ignored if monitorValid is not true (defaults to 200)
28746 * @cfg {String} progressUrl - Url to return progress data
28749 progressUrl : false,
28752 * Opens a new {@link Roo.form.Column} container in the layout stack. If fields are passed after the config, the
28753 * fields are added and the column is closed. If no fields are passed the column remains open
28754 * until end() is called.
28755 * @param {Object} config The config to pass to the column
28756 * @param {Field} field1 (optional)
28757 * @param {Field} field2 (optional)
28758 * @param {Field} etc (optional)
28759 * @return Column The column container object
28761 column : function(c){
28762 var col = new Roo.form.Column(c);
28764 if(arguments.length > 1){ // duplicate code required because of Opera
28765 this.add.apply(this, Array.prototype.slice.call(arguments, 1));
28772 * Opens a new {@link Roo.form.FieldSet} container in the layout stack. If fields are passed after the config, the
28773 * fields are added and the fieldset is closed. If no fields are passed the fieldset remains open
28774 * until end() is called.
28775 * @param {Object} config The config to pass to the fieldset
28776 * @param {Field} field1 (optional)
28777 * @param {Field} field2 (optional)
28778 * @param {Field} etc (optional)
28779 * @return FieldSet The fieldset container object
28781 fieldset : function(c){
28782 var fs = new Roo.form.FieldSet(c);
28784 if(arguments.length > 1){ // duplicate code required because of Opera
28785 this.add.apply(this, Array.prototype.slice.call(arguments, 1));
28792 * Opens a new {@link Roo.form.Layout} container in the layout stack. If fields are passed after the config, the
28793 * fields are added and the container is closed. If no fields are passed the container remains open
28794 * until end() is called.
28795 * @param {Object} config The config to pass to the Layout
28796 * @param {Field} field1 (optional)
28797 * @param {Field} field2 (optional)
28798 * @param {Field} etc (optional)
28799 * @return Layout The container object
28801 container : function(c){
28802 var l = new Roo.form.Layout(c);
28804 if(arguments.length > 1){ // duplicate code required because of Opera
28805 this.add.apply(this, Array.prototype.slice.call(arguments, 1));
28812 * Opens the passed container in the layout stack. The container can be any {@link Roo.form.Layout} or subclass.
28813 * @param {Object} container A Roo.form.Layout or subclass of Layout
28814 * @return {Form} this
28816 start : function(c){
28817 // cascade label info
28818 Roo.applyIf(c, {'labelAlign': this.active.labelAlign, 'labelWidth': this.active.labelWidth, 'itemCls': this.active.itemCls});
28819 this.active.stack.push(c);
28820 c.ownerCt = this.active;
28826 * Closes the current open container
28827 * @return {Form} this
28830 if(this.active == this.root){
28833 this.active = this.active.ownerCt;
28838 * Add Roo.form components to the current open container (e.g. column, fieldset, etc.). Fields added via this method
28839 * can also be passed with an additional property of fieldLabel, which if supplied, will provide the text to display
28840 * as the label of the field.
28841 * @param {Field} field1
28842 * @param {Field} field2 (optional)
28843 * @param {Field} etc. (optional)
28844 * @return {Form} this
28847 this.active.stack.push.apply(this.active.stack, arguments);
28848 this.allItems.push.apply(this.allItems,arguments);
28850 for(var i = 0, a = arguments, len = a.length; i < len; i++) {
28851 if(a[i].isFormField){
28856 Roo.form.Form.superclass.add.apply(this, r);
28866 * Find any element that has been added to a form, using it's ID or name
28867 * This can include framesets, columns etc. along with regular fields..
28868 * @param {String} id - id or name to find.
28870 * @return {Element} e - or false if nothing found.
28872 findbyId : function(id)
28878 Roo.each(this.allItems, function(f){
28879 if (f.id == id || f.name == id ){
28890 * Render this form into the passed container. This should only be called once!
28891 * @param {String/HTMLElement/Element} container The element this component should be rendered into
28892 * @return {Form} this
28894 render : function(ct)
28900 var o = this.autoCreate || {
28902 method : this.method || 'POST',
28903 id : this.id || Roo.id()
28905 this.initEl(ct.createChild(o));
28907 this.root.render(this.el);
28911 this.items.each(function(f){
28912 f.render('x-form-el-'+f.id);
28915 if(this.buttons.length > 0){
28916 // tables are required to maintain order and for correct IE layout
28917 var tb = this.el.createChild({cls:'x-form-btns-ct', cn: {
28918 cls:"x-form-btns x-form-btns-"+this.buttonAlign,
28919 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
28921 var tr = tb.getElementsByTagName('tr')[0];
28922 for(var i = 0, len = this.buttons.length; i < len; i++) {
28923 var b = this.buttons[i];
28924 var td = document.createElement('td');
28925 td.className = 'x-form-btn-td';
28926 b.render(tr.appendChild(td));
28929 if(this.monitorValid){ // initialize after render
28930 this.startMonitoring();
28932 this.fireEvent('rendered', this);
28937 * Adds a button to the footer of the form - this <b>must</b> be called before the form is rendered.
28938 * @param {String/Object} config A string becomes the button text, an object can either be a Button config
28939 * object or a valid Roo.DomHelper element config
28940 * @param {Function} handler The function called when the button is clicked
28941 * @param {Object} scope (optional) The scope of the handler function
28942 * @return {Roo.Button}
28944 addButton : function(config, handler, scope){
28948 minWidth: this.minButtonWidth,
28951 if(typeof config == "string"){
28954 Roo.apply(bc, config);
28956 var btn = new Roo.Button(null, bc);
28957 this.buttons.push(btn);
28962 * Adds a series of form elements (using the xtype property as the factory method.
28963 * Valid xtypes are: TextField, TextArea .... Button, Layout, FieldSet, Column, (and 'end' to close a block)
28964 * @param {Object} config
28967 addxtype : function()
28969 var ar = Array.prototype.slice.call(arguments, 0);
28971 for(var i = 0; i < ar.length; i++) {
28973 continue; // skip -- if this happends something invalid got sent, we
28974 // should ignore it, as basically that interface element will not show up
28975 // and that should be pretty obvious!!
28978 if (Roo.form[ar[i].xtype]) {
28980 var fe = Roo.factory(ar[i], Roo.form);
28986 fe.store.form = this;
28991 this.allItems.push(fe);
28992 if (fe.items && fe.addxtype) {
28993 fe.addxtype.apply(fe, fe.items);
29003 // console.log('adding ' + ar[i].xtype);
29005 if (ar[i].xtype == 'Button') {
29006 //console.log('adding button');
29007 //console.log(ar[i]);
29008 this.addButton(ar[i]);
29009 this.allItems.push(fe);
29013 if (ar[i].xtype == 'end') { // so we can add fieldsets... / layout etc.
29014 alert('end is not supported on xtype any more, use items');
29016 // //console.log('adding end');
29024 * Starts monitoring of the valid state of this form. Usually this is done by passing the config
29025 * option "monitorValid"
29027 startMonitoring : function(){
29030 Roo.TaskMgr.start({
29031 run : this.bindHandler,
29032 interval : this.monitorPoll || 200,
29039 * Stops monitoring of the valid state of this form
29041 stopMonitoring : function(){
29042 this.bound = false;
29046 bindHandler : function(){
29048 return false; // stops binding
29051 this.items.each(function(f){
29052 if(!f.isValid(true)){
29057 for(var i = 0, len = this.buttons.length; i < len; i++){
29058 var btn = this.buttons[i];
29059 if(btn.formBind === true && btn.disabled === valid){
29060 btn.setDisabled(!valid);
29063 this.fireEvent('clientvalidation', this, valid);
29077 Roo.Form = Roo.form.Form;
29080 * Ext JS Library 1.1.1
29081 * Copyright(c) 2006-2007, Ext JS, LLC.
29083 * Originally Released Under LGPL - original licence link has changed is not relivant.
29086 * <script type="text/javascript">
29090 * @class Roo.form.Action
29091 * Internal Class used to handle form actions
29093 * @param {Roo.form.BasicForm} el The form element or its id
29094 * @param {Object} config Configuration options
29098 // define the action interface
29099 Roo.form.Action = function(form, options){
29101 this.options = options || {};
29104 * Client Validation Failed
29107 Roo.form.Action.CLIENT_INVALID = 'client';
29109 * Server Validation Failed
29112 Roo.form.Action.SERVER_INVALID = 'server';
29114 * Connect to Server Failed
29117 Roo.form.Action.CONNECT_FAILURE = 'connect';
29119 * Reading Data from Server Failed
29122 Roo.form.Action.LOAD_FAILURE = 'load';
29124 Roo.form.Action.prototype = {
29126 failureType : undefined,
29127 response : undefined,
29128 result : undefined,
29130 // interface method
29131 run : function(options){
29135 // interface method
29136 success : function(response){
29140 // interface method
29141 handleResponse : function(response){
29145 // default connection failure
29146 failure : function(response){
29148 this.response = response;
29149 this.failureType = Roo.form.Action.CONNECT_FAILURE;
29150 this.form.afterAction(this, false);
29153 processResponse : function(response){
29154 this.response = response;
29155 if(!response.responseText){
29158 this.result = this.handleResponse(response);
29159 return this.result;
29162 // utility functions used internally
29163 getUrl : function(appendParams){
29164 var url = this.options.url || this.form.url || this.form.el.dom.action;
29166 var p = this.getParams();
29168 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
29174 getMethod : function(){
29175 return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
29178 getParams : function(){
29179 var bp = this.form.baseParams;
29180 var p = this.options.params;
29182 if(typeof p == "object"){
29183 p = Roo.urlEncode(Roo.applyIf(p, bp));
29184 }else if(typeof p == 'string' && bp){
29185 p += '&' + Roo.urlEncode(bp);
29188 p = Roo.urlEncode(bp);
29193 createCallback : function(){
29195 success: this.success,
29196 failure: this.failure,
29198 timeout: (this.form.timeout*1000),
29199 upload: this.form.fileUpload ? this.success : undefined
29204 Roo.form.Action.Submit = function(form, options){
29205 Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
29208 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
29211 haveProgress : false,
29212 uploadComplete : false,
29214 // uploadProgress indicator.
29215 uploadProgress : function()
29217 if (!this.form.progressUrl) {
29221 if (!this.haveProgress) {
29222 Roo.MessageBox.progress("Uploading", "Uploading");
29224 if (this.uploadComplete) {
29225 Roo.MessageBox.hide();
29229 this.haveProgress = true;
29231 var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
29233 var c = new Roo.data.Connection();
29235 url : this.form.progressUrl,
29240 success : function(req){
29241 //console.log(data);
29245 rdata = Roo.decode(req.responseText)
29247 Roo.log("Invalid data from server..");
29251 if (!rdata || !rdata.success) {
29253 Roo.MessageBox.alert(Roo.encode(rdata));
29256 var data = rdata.data;
29258 if (this.uploadComplete) {
29259 Roo.MessageBox.hide();
29264 Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
29265 Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
29268 this.uploadProgress.defer(2000,this);
29271 failure: function(data) {
29272 Roo.log('progress url failed ');
29283 // run get Values on the form, so it syncs any secondary forms.
29284 this.form.getValues();
29286 var o = this.options;
29287 var method = this.getMethod();
29288 var isPost = method == 'POST';
29289 if(o.clientValidation === false || this.form.isValid()){
29291 if (this.form.progressUrl) {
29292 this.form.findField('UPLOAD_IDENTIFIER').setValue(
29293 (new Date() * 1) + '' + Math.random());
29298 Roo.Ajax.request(Roo.apply(this.createCallback(), {
29299 form:this.form.el.dom,
29300 url:this.getUrl(!isPost),
29302 params:isPost ? this.getParams() : null,
29303 isUpload: this.form.fileUpload
29306 this.uploadProgress();
29308 }else if (o.clientValidation !== false){ // client validation failed
29309 this.failureType = Roo.form.Action.CLIENT_INVALID;
29310 this.form.afterAction(this, false);
29314 success : function(response)
29316 this.uploadComplete= true;
29317 if (this.haveProgress) {
29318 Roo.MessageBox.hide();
29322 var result = this.processResponse(response);
29323 if(result === true || result.success){
29324 this.form.afterAction(this, true);
29328 this.form.markInvalid(result.errors);
29329 this.failureType = Roo.form.Action.SERVER_INVALID;
29331 this.form.afterAction(this, false);
29333 failure : function(response)
29335 this.uploadComplete= true;
29336 if (this.haveProgress) {
29337 Roo.MessageBox.hide();
29340 this.response = response;
29341 this.failureType = Roo.form.Action.CONNECT_FAILURE;
29342 this.form.afterAction(this, false);
29345 handleResponse : function(response){
29346 if(this.form.errorReader){
29347 var rs = this.form.errorReader.read(response);
29350 for(var i = 0, len = rs.records.length; i < len; i++) {
29351 var r = rs.records[i];
29352 errors[i] = r.data;
29355 if(errors.length < 1){
29359 success : rs.success,
29365 ret = Roo.decode(response.responseText);
29369 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
29379 Roo.form.Action.Load = function(form, options){
29380 Roo.form.Action.Load.superclass.constructor.call(this, form, options);
29381 this.reader = this.form.reader;
29384 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
29389 Roo.Ajax.request(Roo.apply(
29390 this.createCallback(), {
29391 method:this.getMethod(),
29392 url:this.getUrl(false),
29393 params:this.getParams()
29397 success : function(response){
29399 var result = this.processResponse(response);
29400 if(result === true || !result.success || !result.data){
29401 this.failureType = Roo.form.Action.LOAD_FAILURE;
29402 this.form.afterAction(this, false);
29405 this.form.clearInvalid();
29406 this.form.setValues(result.data);
29407 this.form.afterAction(this, true);
29410 handleResponse : function(response){
29411 if(this.form.reader){
29412 var rs = this.form.reader.read(response);
29413 var data = rs.records && rs.records[0] ? rs.records[0].data : null;
29415 success : rs.success,
29419 return Roo.decode(response.responseText);
29423 Roo.form.Action.ACTION_TYPES = {
29424 'load' : Roo.form.Action.Load,
29425 'submit' : Roo.form.Action.Submit
29428 * Ext JS Library 1.1.1
29429 * Copyright(c) 2006-2007, Ext JS, LLC.
29431 * Originally Released Under LGPL - original licence link has changed is not relivant.
29434 * <script type="text/javascript">
29438 * @class Roo.form.Layout
29439 * @extends Roo.Component
29440 * Creates a container for layout and rendering of fields in an {@link Roo.form.Form}.
29442 * @param {Object} config Configuration options
29444 Roo.form.Layout = function(config){
29446 if (config.items) {
29447 xitems = config.items;
29448 delete config.items;
29450 Roo.form.Layout.superclass.constructor.call(this, config);
29452 Roo.each(xitems, this.addxtype, this);
29456 Roo.extend(Roo.form.Layout, Roo.Component, {
29458 * @cfg {String/Object} autoCreate
29459 * A DomHelper element spec used to autocreate the layout (defaults to {tag: 'div', cls: 'x-form-ct'})
29462 * @cfg {String/Object/Function} style
29463 * A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
29464 * a function which returns such a specification.
29467 * @cfg {String} labelAlign
29468 * Valid values are "left," "top" and "right" (defaults to "left")
29471 * @cfg {Number} labelWidth
29472 * Fixed width in pixels of all field labels (defaults to undefined)
29475 * @cfg {Boolean} clear
29476 * True to add a clearing element at the end of this layout, equivalent to CSS clear: both (defaults to true)
29480 * @cfg {String} labelSeparator
29481 * The separator to use after field labels (defaults to ':')
29483 labelSeparator : ':',
29485 * @cfg {Boolean} hideLabels
29486 * True to suppress the display of field labels in this layout (defaults to false)
29488 hideLabels : false,
29491 defaultAutoCreate : {tag: 'div', cls: 'x-form-ct'},
29496 onRender : function(ct, position){
29497 if(this.el){ // from markup
29498 this.el = Roo.get(this.el);
29499 }else { // generate
29500 var cfg = this.getAutoCreate();
29501 this.el = ct.createChild(cfg, position);
29504 this.el.applyStyles(this.style);
29506 if(this.labelAlign){
29507 this.el.addClass('x-form-label-'+this.labelAlign);
29509 if(this.hideLabels){
29510 this.labelStyle = "display:none";
29511 this.elementStyle = "padding-left:0;";
29513 if(typeof this.labelWidth == 'number'){
29514 this.labelStyle = "width:"+this.labelWidth+"px;";
29515 this.elementStyle = "padding-left:"+((this.labelWidth+(typeof this.labelPad == 'number' ? this.labelPad : 5))+'px')+";";
29517 if(this.labelAlign == 'top'){
29518 this.labelStyle = "width:auto;";
29519 this.elementStyle = "padding-left:0;";
29522 var stack = this.stack;
29523 var slen = stack.length;
29525 if(!this.fieldTpl){
29526 var t = new Roo.Template(
29527 '<div class="x-form-item {5}">',
29528 '<label for="{0}" style="{2}">{1}{4}</label>',
29529 '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
29531 '</div><div class="x-form-clear-left"></div>'
29533 t.disableFormats = true;
29535 Roo.form.Layout.prototype.fieldTpl = t;
29537 for(var i = 0; i < slen; i++) {
29538 if(stack[i].isFormField){
29539 this.renderField(stack[i]);
29541 this.renderComponent(stack[i]);
29546 this.el.createChild({cls:'x-form-clear'});
29551 renderField : function(f){
29552 f.fieldEl = Roo.get(this.fieldTpl.append(this.el, [
29555 f.labelStyle||this.labelStyle||'', //2
29556 this.elementStyle||'', //3
29557 typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator, //4
29558 f.itemCls||this.itemCls||'' //5
29559 ], true).getPrevSibling());
29563 renderComponent : function(c){
29564 c.render(c.isLayout ? this.el : this.el.createChild());
29567 * Adds a object form elements (using the xtype property as the factory method.)
29568 * Valid xtypes are: TextField, TextArea .... Button, Layout, FieldSet, Column
29569 * @param {Object} config
29571 addxtype : function(o)
29573 // create the lement.
29574 o.form = this.form;
29575 var fe = Roo.factory(o, Roo.form);
29576 this.form.allItems.push(fe);
29577 this.stack.push(fe);
29579 if (fe.isFormField) {
29580 this.form.items.add(fe);
29588 * @class Roo.form.Column
29589 * @extends Roo.form.Layout
29590 * Creates a column container for layout and rendering of fields in an {@link Roo.form.Form}.
29592 * @param {Object} config Configuration options
29594 Roo.form.Column = function(config){
29595 Roo.form.Column.superclass.constructor.call(this, config);
29598 Roo.extend(Roo.form.Column, Roo.form.Layout, {
29600 * @cfg {Number/String} width
29601 * The fixed width of the column in pixels or CSS value (defaults to "auto")
29604 * @cfg {String/Object} autoCreate
29605 * A DomHelper element spec used to autocreate the column (defaults to {tag: 'div', cls: 'x-form-ct x-form-column'})
29609 defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-column'},
29612 onRender : function(ct, position){
29613 Roo.form.Column.superclass.onRender.call(this, ct, position);
29615 this.el.setWidth(this.width);
29622 * @class Roo.form.Row
29623 * @extends Roo.form.Layout
29624 * Creates a row container for layout and rendering of fields in an {@link Roo.form.Form}.
29626 * @param {Object} config Configuration options
29630 Roo.form.Row = function(config){
29631 Roo.form.Row.superclass.constructor.call(this, config);
29634 Roo.extend(Roo.form.Row, Roo.form.Layout, {
29636 * @cfg {Number/String} width
29637 * The fixed width of the column in pixels or CSS value (defaults to "auto")
29640 * @cfg {Number/String} height
29641 * The fixed height of the column in pixels or CSS value (defaults to "auto")
29643 defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-row'},
29647 onRender : function(ct, position){
29648 //console.log('row render');
29650 var t = new Roo.Template(
29651 '<div class="x-form-item {5}" style="float:left;width:{6}px">',
29652 '<label for="{0}" style="{2}">{1}{4}</label>',
29653 '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
29657 t.disableFormats = true;
29659 Roo.form.Layout.prototype.rowTpl = t;
29661 this.fieldTpl = this.rowTpl;
29663 //console.log('lw' + this.labelWidth +', la:' + this.labelAlign);
29664 var labelWidth = 100;
29666 if ((this.labelAlign != 'top')) {
29667 if (typeof this.labelWidth == 'number') {
29668 labelWidth = this.labelWidth
29670 this.padWidth = 20 + labelWidth;
29674 Roo.form.Column.superclass.onRender.call(this, ct, position);
29676 this.el.setWidth(this.width);
29679 this.el.setHeight(this.height);
29684 renderField : function(f){
29685 f.fieldEl = this.fieldTpl.append(this.el, [
29686 f.id, f.fieldLabel,
29687 f.labelStyle||this.labelStyle||'',
29688 this.elementStyle||'',
29689 typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator,
29690 f.itemCls||this.itemCls||'',
29691 f.width ? f.width + this.padWidth : 160 + this.padWidth
29698 * @class Roo.form.FieldSet
29699 * @extends Roo.form.Layout
29700 * Creates a fieldset container for layout and rendering of fields in an {@link Roo.form.Form}.
29702 * @param {Object} config Configuration options
29704 Roo.form.FieldSet = function(config){
29705 Roo.form.FieldSet.superclass.constructor.call(this, config);
29708 Roo.extend(Roo.form.FieldSet, Roo.form.Layout, {
29710 * @cfg {String} legend
29711 * The text to display as the legend for the FieldSet (defaults to '')
29714 * @cfg {String/Object} autoCreate
29715 * A DomHelper element spec used to autocreate the fieldset (defaults to {tag: 'fieldset', cn: {tag:'legend'}})
29719 defaultAutoCreate : {tag: 'fieldset', cn: {tag:'legend'}},
29722 onRender : function(ct, position){
29723 Roo.form.FieldSet.superclass.onRender.call(this, ct, position);
29725 this.setLegend(this.legend);
29730 setLegend : function(text){
29732 this.el.child('legend').update(text);
29737 * Ext JS Library 1.1.1
29738 * Copyright(c) 2006-2007, Ext JS, LLC.
29740 * Originally Released Under LGPL - original licence link has changed is not relivant.
29743 * <script type="text/javascript">
29746 * @class Roo.form.VTypes
29747 * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
29750 Roo.form.VTypes = function(){
29751 // closure these in so they are only created once.
29752 var alpha = /^[a-zA-Z_]+$/;
29753 var alphanum = /^[a-zA-Z0-9_]+$/;
29754 var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,4}$/;
29755 var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
29757 // All these messages and functions are configurable
29760 * The function used to validate email addresses
29761 * @param {String} value The email address
29763 'email' : function(v){
29764 return email.test(v);
29767 * The error text to display when the email validation function returns false
29770 'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
29772 * The keystroke filter mask to be applied on email input
29775 'emailMask' : /[a-z0-9_\.\-@]/i,
29778 * The function used to validate URLs
29779 * @param {String} value The URL
29781 'url' : function(v){
29782 return url.test(v);
29785 * The error text to display when the url validation function returns false
29788 'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
29791 * The function used to validate alpha values
29792 * @param {String} value The value
29794 'alpha' : function(v){
29795 return alpha.test(v);
29798 * The error text to display when the alpha validation function returns false
29801 'alphaText' : 'This field should only contain letters and _',
29803 * The keystroke filter mask to be applied on alpha input
29806 'alphaMask' : /[a-z_]/i,
29809 * The function used to validate alphanumeric values
29810 * @param {String} value The value
29812 'alphanum' : function(v){
29813 return alphanum.test(v);
29816 * The error text to display when the alphanumeric validation function returns false
29819 'alphanumText' : 'This field should only contain letters, numbers and _',
29821 * The keystroke filter mask to be applied on alphanumeric input
29824 'alphanumMask' : /[a-z0-9_]/i
29826 }();//<script type="text/javascript">
29829 * @class Roo.form.FCKeditor
29830 * @extends Roo.form.TextArea
29831 * Wrapper around the FCKEditor http://www.fckeditor.net
29833 * Creates a new FCKeditor
29834 * @param {Object} config Configuration options
29836 Roo.form.FCKeditor = function(config){
29837 Roo.form.FCKeditor.superclass.constructor.call(this, config);
29840 * @event editorinit
29841 * Fired when the editor is initialized - you can add extra handlers here..
29842 * @param {FCKeditor} this
29843 * @param {Object} the FCK object.
29850 Roo.form.FCKeditor.editors = { };
29851 Roo.extend(Roo.form.FCKeditor, Roo.form.TextArea,
29853 //defaultAutoCreate : {
29854 // tag : "textarea",style : "width:100px;height:60px;" ,autocomplete : "off"
29858 * @cfg {Object} fck options - see fck manual for details.
29863 * @cfg {Object} fck toolbar set (Basic or Default)
29865 toolbarSet : 'Basic',
29867 * @cfg {Object} fck BasePath
29869 basePath : '/fckeditor/',
29877 onRender : function(ct, position)
29880 this.defaultAutoCreate = {
29882 style:"width:300px;height:60px;",
29883 autocomplete: "off"
29886 Roo.form.FCKeditor.superclass.onRender.call(this, ct, position);
29889 this.textSizeEl = Roo.DomHelper.append(document.body, {tag: "pre", cls: "x-form-grow-sizer"});
29890 if(this.preventScrollbars){
29891 this.el.setStyle("overflow", "hidden");
29893 this.el.setHeight(this.growMin);
29896 //console.log('onrender' + this.getId() );
29897 Roo.form.FCKeditor.editors[this.getId()] = this;
29900 this.replaceTextarea() ;
29904 getEditor : function() {
29905 return this.fckEditor;
29908 * Sets a data value into the field and validates it. To set the value directly without validation see {@link #setRawValue}.
29909 * @param {Mixed} value The value to set
29913 setValue : function(value)
29915 //console.log('setValue: ' + value);
29917 if(typeof(value) == 'undefined') { // not sure why this is happending...
29920 Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
29922 //if(!this.el || !this.getEditor()) {
29923 // this.value = value;
29924 //this.setValue.defer(100,this,[value]);
29928 if(!this.getEditor()) {
29932 this.getEditor().SetData(value);
29939 * Returns the normalized data value (undefined or emptyText will be returned as ''). To return the raw value see {@link #getRawValue}.
29940 * @return {Mixed} value The field value
29942 getValue : function()
29945 if (this.frame && this.frame.dom.style.display == 'none') {
29946 return Roo.form.FCKeditor.superclass.getValue.call(this);
29949 if(!this.el || !this.getEditor()) {
29951 // this.getValue.defer(100,this);
29956 var value=this.getEditor().GetData();
29957 Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
29958 return Roo.form.FCKeditor.superclass.getValue.call(this);
29964 * Returns the raw data value which may or may not be a valid, defined value. To return a normalized value see {@link #getValue}.
29965 * @return {Mixed} value The field value
29967 getRawValue : function()
29969 if (this.frame && this.frame.dom.style.display == 'none') {
29970 return Roo.form.FCKeditor.superclass.getRawValue.call(this);
29973 if(!this.el || !this.getEditor()) {
29974 //this.getRawValue.defer(100,this);
29981 var value=this.getEditor().GetData();
29982 Roo.form.FCKeditor.superclass.setRawValue.apply(this,[value]);
29983 return Roo.form.FCKeditor.superclass.getRawValue.call(this);
29987 setSize : function(w,h) {
29991 //if (this.frame && this.frame.dom.style.display == 'none') {
29992 // Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
29995 //if(!this.el || !this.getEditor()) {
29996 // this.setSize.defer(100,this, [w,h]);
30002 Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
30004 this.frame.dom.setAttribute('width', w);
30005 this.frame.dom.setAttribute('height', h);
30006 this.frame.setSize(w,h);
30010 toggleSourceEdit : function(value) {
30014 this.el.dom.style.display = value ? '' : 'none';
30015 this.frame.dom.style.display = value ? 'none' : '';
30020 focus: function(tag)
30022 if (this.frame.dom.style.display == 'none') {
30023 return Roo.form.FCKeditor.superclass.focus.call(this);
30025 if(!this.el || !this.getEditor()) {
30026 this.focus.defer(100,this, [tag]);
30033 var tgs = this.getEditor().EditorDocument.getElementsByTagName(tag);
30034 this.getEditor().Focus();
30036 if (!this.getEditor().Selection.GetSelection()) {
30037 this.focus.defer(100,this, [tag]);
30042 var r = this.getEditor().EditorDocument.createRange();
30043 r.setStart(tgs[0],0);
30044 r.setEnd(tgs[0],0);
30045 this.getEditor().Selection.GetSelection().removeAllRanges();
30046 this.getEditor().Selection.GetSelection().addRange(r);
30047 this.getEditor().Focus();
30054 replaceTextarea : function()
30056 if ( document.getElementById( this.getId() + '___Frame' ) )
30058 //if ( !this.checkBrowser || this._isCompatibleBrowser() )
30060 // We must check the elements firstly using the Id and then the name.
30061 var oTextarea = document.getElementById( this.getId() );
30063 var colElementsByName = document.getElementsByName( this.getId() ) ;
30065 oTextarea.style.display = 'none' ;
30067 if ( oTextarea.tabIndex ) {
30068 this.TabIndex = oTextarea.tabIndex ;
30071 this._insertHtmlBefore( this._getConfigHtml(), oTextarea ) ;
30072 this._insertHtmlBefore( this._getIFrameHtml(), oTextarea ) ;
30073 this.frame = Roo.get(this.getId() + '___Frame')
30076 _getConfigHtml : function()
30080 for ( var o in this.fckconfig ) {
30081 sConfig += sConfig.length > 0 ? '&' : '';
30082 sConfig += encodeURIComponent( o ) + '=' + encodeURIComponent( this.fckconfig[o] ) ;
30085 return '<input type="hidden" id="' + this.getId() + '___Config" value="' + sConfig + '" style="display:none" />' ;
30089 _getIFrameHtml : function()
30091 var sFile = 'fckeditor.html' ;
30092 /* no idea what this is about..
30095 if ( (/fcksource=true/i).test( window.top.location.search ) )
30096 sFile = 'fckeditor.original.html' ;
30101 var sLink = this.basePath + 'editor/' + sFile + '?InstanceName=' + encodeURIComponent( this.getId() ) ;
30102 sLink += this.toolbarSet ? ( '&Toolbar=' + this.toolbarSet) : '';
30105 var html = '<iframe id="' + this.getId() +
30106 '___Frame" src="' + sLink +
30107 '" width="' + this.width +
30108 '" height="' + this.height + '"' +
30109 (this.tabIndex ? ' tabindex="' + this.tabIndex + '"' :'' ) +
30110 ' frameborder="0" scrolling="no"></iframe>' ;
30115 _insertHtmlBefore : function( html, element )
30117 if ( element.insertAdjacentHTML ) {
30119 element.insertAdjacentHTML( 'beforeBegin', html ) ;
30121 var oRange = document.createRange() ;
30122 oRange.setStartBefore( element ) ;
30123 var oFragment = oRange.createContextualFragment( html );
30124 element.parentNode.insertBefore( oFragment, element ) ;
30137 //Roo.reg('fckeditor', Roo.form.FCKeditor);
30139 function FCKeditor_OnComplete(editorInstance){
30140 var f = Roo.form.FCKeditor.editors[editorInstance.Name];
30141 f.fckEditor = editorInstance;
30142 //console.log("loaded");
30143 f.fireEvent('editorinit', f, editorInstance);
30163 //<script type="text/javascript">
30165 * @class Roo.form.GridField
30166 * @extends Roo.form.Field
30167 * Embed a grid (or editable grid into a form)
30170 * This embeds a grid in a form, the value of the field should be the json encoded array of rows
30172 * xgrid.store = Roo.data.Store
30173 * xgrid.store.proxy = Roo.data.MemoryProxy (data = [] )
30174 * xgrid.store.reader = Roo.data.JsonReader
30178 * Creates a new GridField
30179 * @param {Object} config Configuration options
30181 Roo.form.GridField = function(config){
30182 Roo.form.GridField.superclass.constructor.call(this, config);
30186 Roo.extend(Roo.form.GridField, Roo.form.Field, {
30188 * @cfg {Number} width - used to restrict width of grid..
30192 * @cfg {Number} height - used to restrict height of grid..
30196 * @cfg {Object} xgrid (xtype'd description of grid) { xtype : 'Grid', dataSource: .... }
30202 * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
30203 * {tag: "input", type: "checkbox", autocomplete: "off"})
30205 // defaultAutoCreate : { tag: 'div' },
30206 defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'off'},
30208 * @cfg {String} addTitle Text to include for adding a title.
30212 onResize : function(){
30213 Roo.form.Field.superclass.onResize.apply(this, arguments);
30216 initEvents : function(){
30217 // Roo.form.Checkbox.superclass.initEvents.call(this);
30218 // has no events...
30223 getResizeEl : function(){
30227 getPositionEl : function(){
30232 onRender : function(ct, position){
30234 this.style = this.style || 'overflow: hidden; border:1px solid #c3daf9;';
30235 var style = this.style;
30238 Roo.form.GridField.superclass.onRender.call(this, ct, position);
30239 this.wrap = this.el.wrap({cls: ''}); // not sure why ive done thsi...
30240 this.viewEl = this.wrap.createChild({ tag: 'div' });
30242 this.viewEl.applyStyles(style);
30245 this.viewEl.setWidth(this.width);
30248 this.viewEl.setHeight(this.height);
30250 //if(this.inputValue !== undefined){
30251 //this.setValue(this.value);
30254 this.grid = new Roo.grid[this.xgrid.xtype](this.viewEl, this.xgrid);
30257 this.grid.render();
30258 this.grid.getDataSource().on('remove', this.refreshValue, this);
30259 this.grid.getDataSource().on('update', this.refreshValue, this);
30260 this.grid.on('afteredit', this.refreshValue, this);
30266 * Sets the value of the item.
30267 * @param {String} either an object or a string..
30269 setValue : function(v){
30271 v = v || []; // empty set..
30272 // this does not seem smart - it really only affects memoryproxy grids..
30273 if (this.grid && this.grid.getDataSource() && typeof(v) != 'undefined') {
30274 var ds = this.grid.getDataSource();
30275 // assumes a json reader..
30277 data[ds.reader.meta.root ] = typeof(v) == 'string' ? Roo.decode(v) : v;
30278 ds.loadData( data);
30280 // clear selection so it does not get stale.
30281 if (this.grid.sm) {
30282 this.grid.sm.clearSelections();
30285 Roo.form.GridField.superclass.setValue.call(this, v);
30286 this.refreshValue();
30287 // should load data in the grid really....
30291 refreshValue: function() {
30293 this.grid.getDataSource().each(function(r) {
30296 this.el.dom.value = Roo.encode(val);
30304 * Ext JS Library 1.1.1
30305 * Copyright(c) 2006-2007, Ext JS, LLC.
30307 * Originally Released Under LGPL - original licence link has changed is not relivant.
30310 * <script type="text/javascript">
30313 * @class Roo.form.DisplayField
30314 * @extends Roo.form.Field
30315 * A generic Field to display non-editable data.
30317 * Creates a new Display Field item.
30318 * @param {Object} config Configuration options
30320 Roo.form.DisplayField = function(config){
30321 Roo.form.DisplayField.superclass.constructor.call(this, config);
30325 Roo.extend(Roo.form.DisplayField, Roo.form.TextField, {
30326 inputType: 'hidden',
30332 * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
30334 focusClass : undefined,
30336 * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
30338 fieldClass: 'x-form-field',
30341 * @cfg {Function} valueRenderer The renderer for the field (so you can reformat output). should return raw HTML
30343 valueRenderer: undefined,
30347 * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
30348 * {tag: "input", type: "checkbox", autocomplete: "off"})
30351 // defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'off'},
30353 onResize : function(){
30354 Roo.form.DisplayField.superclass.onResize.apply(this, arguments);
30358 initEvents : function(){
30359 // Roo.form.Checkbox.superclass.initEvents.call(this);
30360 // has no events...
30365 getResizeEl : function(){
30369 getPositionEl : function(){
30374 onRender : function(ct, position){
30376 Roo.form.DisplayField.superclass.onRender.call(this, ct, position);
30377 //if(this.inputValue !== undefined){
30378 this.wrap = this.el.wrap();
30380 this.viewEl = this.wrap.createChild({ tag: 'div', cls: 'x-form-displayfield'});
30382 if (this.bodyStyle) {
30383 this.viewEl.applyStyles(this.bodyStyle);
30385 //this.viewEl.setStyle('padding', '2px');
30387 this.setValue(this.value);
30392 initValue : Roo.emptyFn,
30397 onClick : function(){
30402 * Sets the checked state of the checkbox.
30403 * @param {Boolean/String} checked True, 'true', '1', or 'on' to check the checkbox, any other value will uncheck it.
30405 setValue : function(v){
30407 var html = this.valueRenderer ? this.valueRenderer(v) : String.format('{0}', v);
30408 // this might be called before we have a dom element..
30409 if (!this.viewEl) {
30412 this.viewEl.dom.innerHTML = html;
30413 Roo.form.DisplayField.superclass.setValue.call(this, v);
30423 * @class Roo.form.DayPicker
30424 * @extends Roo.form.Field
30425 * A Day picker show [M] [T] [W] ....
30427 * Creates a new Day Picker
30428 * @param {Object} config Configuration options
30430 Roo.form.DayPicker= function(config){
30431 Roo.form.DayPicker.superclass.constructor.call(this, config);
30435 Roo.extend(Roo.form.DayPicker, Roo.form.Field, {
30437 * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
30439 focusClass : undefined,
30441 * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
30443 fieldClass: "x-form-field",
30446 * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
30447 * {tag: "input", type: "checkbox", autocomplete: "off"})
30449 defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "off"},
30452 actionMode : 'viewEl',
30456 inputType : 'hidden',
30459 inputElement: false, // real input element?
30460 basedOn: false, // ????
30462 isFormField: true, // not sure where this is needed!!!!
30464 onResize : function(){
30465 Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
30466 if(!this.boxLabel){
30467 this.el.alignTo(this.wrap, 'c-c');
30471 initEvents : function(){
30472 Roo.form.Checkbox.superclass.initEvents.call(this);
30473 this.el.on("click", this.onClick, this);
30474 this.el.on("change", this.onClick, this);
30478 getResizeEl : function(){
30482 getPositionEl : function(){
30488 onRender : function(ct, position){
30489 Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
30491 this.wrap = this.el.wrap({cls: 'x-form-daypick-item '});
30493 var r1 = '<table><tr>';
30494 var r2 = '<tr class="x-form-daypick-icons">';
30495 for (var i=0; i < 7; i++) {
30496 r1+= '<td><div>' + Date.dayNames[i].substring(0,3) + '</div></td>';
30497 r2+= '<td><img class="x-menu-item-icon" src="' + Roo.BLANK_IMAGE_URL +'"></td>';
30500 var viewEl = this.wrap.createChild( r1 + '</tr>' + r2 + '</tr></table>');
30501 viewEl.select('img').on('click', this.onClick, this);
30502 this.viewEl = viewEl;
30505 // this will not work on Chrome!!!
30506 this.el.on('DOMAttrModified', this.setFromHidden, this); //ff
30507 this.el.on('propertychange', this.setFromHidden, this); //ie
30515 initValue : Roo.emptyFn,
30518 * Returns the checked state of the checkbox.
30519 * @return {Boolean} True if checked, else false
30521 getValue : function(){
30522 return this.el.dom.value;
30527 onClick : function(e){
30528 //this.setChecked(!this.checked);
30529 Roo.get(e.target).toggleClass('x-menu-item-checked');
30530 this.refreshValue();
30531 //if(this.el.dom.checked != this.checked){
30532 // this.setValue(this.el.dom.checked);
30537 refreshValue : function()
30540 this.viewEl.select('img',true).each(function(e,i,n) {
30541 val += e.is(".x-menu-item-checked") ? String(n) : '';
30543 this.setValue(val, true);
30547 * Sets the checked state of the checkbox.
30548 * On is always based on a string comparison between inputValue and the param.
30549 * @param {Boolean/String} value - the value to set
30550 * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
30552 setValue : function(v,suppressEvent){
30553 if (!this.el.dom) {
30556 var old = this.el.dom.value ;
30557 this.el.dom.value = v;
30558 if (suppressEvent) {
30562 // update display..
30563 this.viewEl.select('img',true).each(function(e,i,n) {
30565 var on = e.is(".x-menu-item-checked");
30566 var newv = v.indexOf(String(n)) > -1;
30568 e.toggleClass('x-menu-item-checked');
30574 this.fireEvent('change', this, v, old);
30579 // handle setting of hidden value by some other method!!?!?
30580 setFromHidden: function()
30585 //console.log("SET FROM HIDDEN");
30586 //alert('setFrom hidden');
30587 this.setValue(this.el.dom.value);
30590 onDestroy : function()
30593 Roo.get(this.viewEl).remove();
30596 Roo.form.DayPicker.superclass.onDestroy.call(this);
30600 * RooJS Library 1.1.1
30601 * Copyright(c) 2008-2011 Alan Knowles
30608 * @class Roo.form.ComboCheck
30609 * @extends Roo.form.ComboBox
30610 * A combobox for multiple select items.
30612 * FIXME - could do with a reset button..
30615 * Create a new ComboCheck
30616 * @param {Object} config Configuration options
30618 Roo.form.ComboCheck = function(config){
30619 Roo.form.ComboCheck.superclass.constructor.call(this, config);
30620 // should verify some data...
30622 // hiddenName = required..
30623 // displayField = required
30624 // valudField == required
30625 var req= [ 'hiddenName', 'displayField', 'valueField' ];
30627 Roo.each(req, function(e) {
30628 if ((typeof(_t[e]) == 'undefined' ) || !_t[e].length) {
30629 throw "Roo.form.ComboCheck : missing value for: " + e;
30636 Roo.extend(Roo.form.ComboCheck, Roo.form.ComboBox, {
30641 selectedClass: 'x-menu-item-checked',
30644 onRender : function(ct, position){
30650 var cls = 'x-combo-list';
30653 this.tpl = new Roo.Template({
30654 html : '<div class="'+cls+'-item x-menu-check-item">' +
30655 '<img class="x-menu-item-icon" style="margin: 0px;" src="' + Roo.BLANK_IMAGE_URL + '">' +
30656 '<span>{' + this.displayField + '}</span>' +
30663 Roo.form.ComboCheck.superclass.onRender.call(this, ct, position);
30664 this.view.singleSelect = false;
30665 this.view.multiSelect = true;
30666 this.view.toggleSelect = true;
30667 this.pageTb.add(new Roo.Toolbar.Fill(), {
30670 handler: function()
30677 onViewOver : function(e, t){
30683 onViewClick : function(doFocus,index){
30687 select: function () {
30688 //Roo.log("SELECT CALLED");
30691 selectByValue : function(xv, scrollIntoView){
30692 var ar = this.getValueArray();
30695 Roo.each(ar, function(v) {
30696 if(v === undefined || v === null){
30699 var r = this.findRecord(this.valueField, v);
30701 sels.push(this.store.indexOf(r))
30705 this.view.select(sels);
30711 onSelect : function(record, index){
30712 // Roo.log("onselect Called");
30713 // this is only called by the clear button now..
30714 this.view.clearSelections();
30715 this.setValue('[]');
30716 if (this.value != this.valueBefore) {
30717 this.fireEvent('change', this, this.value, this.valueBefore);
30718 this.valueBefore = this.value;
30721 getValueArray : function()
30726 //Roo.log(this.value);
30727 if (typeof(this.value) == 'undefined') {
30730 var ar = Roo.decode(this.value);
30731 return ar instanceof Array ? ar : []; //?? valid?
30734 Roo.log(e + "\nRoo.form.ComboCheck:getValueArray invalid data:" + this.getValue());
30739 expand : function ()
30742 Roo.form.ComboCheck.superclass.expand.call(this);
30743 this.valueBefore = typeof(this.value) == 'undefined' ? '' : this.value;
30744 //this.valueBefore = typeof(this.valueBefore) == 'undefined' ? '' : this.valueBefore;
30749 collapse : function(){
30750 Roo.form.ComboCheck.superclass.collapse.call(this);
30751 var sl = this.view.getSelectedIndexes();
30752 var st = this.store;
30756 Roo.each(sl, function(i) {
30758 nv.push(r.get(this.valueField));
30760 this.setValue(Roo.encode(nv));
30761 if (this.value != this.valueBefore) {
30763 this.fireEvent('change', this, this.value, this.valueBefore);
30764 this.valueBefore = this.value;
30769 setValue : function(v){
30773 var vals = this.getValueArray();
30775 Roo.each(vals, function(k) {
30776 var r = this.findRecord(this.valueField, k);
30778 tv.push(r.data[this.displayField]);
30779 }else if(this.valueNotFoundText !== undefined){
30780 tv.push( this.valueNotFoundText );
30785 Roo.form.ComboBox.superclass.setValue.call(this, tv.join(', '));
30786 this.hiddenField.value = v;
30792 * Ext JS Library 1.1.1
30793 * Copyright(c) 2006-2007, Ext JS, LLC.
30795 * Originally Released Under LGPL - original licence link has changed is not relivant.
30798 * <script type="text/javascript">
30802 * @class Roo.form.Signature
30803 * @extends Roo.form.Field
30807 * @param {Object} config Configuration options
30810 Roo.form.Signature = function(config){
30811 Roo.form.Signature.superclass.constructor.call(this, config);
30813 this.addEvents({// not in used??
30816 * Fires when the 'confirm' icon is pressed (add a listener to enable add button)
30817 * @param {Roo.form.Signature} combo This combo box
30822 * Fires when the 'edit' icon is pressed (add a listener to enable add button)
30823 * @param {Roo.form.ComboBox} combo This combo box
30824 * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
30830 Roo.extend(Roo.form.Signature, Roo.form.Field, {
30832 * @cfg {Object} labels Label to use when rendering a form.
30836 * confirm : "Confirm"
30841 confirm : "Confirm"
30844 * @cfg {Number} width The signature panel width (defaults to 300)
30848 * @cfg {Number} height The signature panel height (defaults to 100)
30852 * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to false)
30854 allowBlank : false,
30857 // {Object} signPanel The signature SVG panel element (defaults to {})
30859 // {Boolean} isMouseDown False to validate that the mouse down event (defaults to false)
30860 isMouseDown : false,
30861 // {Boolean} isConfirmed validate the signature is confirmed or not for submitting form (defaults to false)
30862 isConfirmed : false,
30863 // {String} signatureTmp SVG mapping string (defaults to empty string)
30867 defaultAutoCreate : { // modified by initCompnoent..
30873 onRender : function(ct, position){
30875 Roo.form.Signature.superclass.onRender.call(this, ct, position);
30877 this.wrap = this.el.wrap({
30878 cls:'x-form-signature-wrap', style : 'width: ' + this.width + 'px', cn:{cls:'x-form-signature'}
30881 this.createToolbar(this);
30882 this.signPanel = this.wrap.createChild({
30884 style: 'width: ' + this.width + 'px; height: ' + this.height + 'px; border: 0;'
30888 this.svgID = Roo.id();
30889 this.svgEl = this.signPanel.createChild({
30890 xmlns : 'http://www.w3.org/2000/svg',
30892 id : this.svgID + "-svg",
30894 height: this.height,
30895 viewBox: '0 0 '+this.width+' '+this.height,
30899 id: this.svgID + "-svg-r",
30901 height: this.height,
30906 id: this.svgID + "-svg-l",
30908 y1: (this.height*0.8), // start set the line in 80% of height
30909 x2: this.width, // end
30910 y2: (this.height*0.8), // end set the line in 80% of height
30912 'stroke-width': "1",
30913 'stroke-dasharray': "3",
30914 'shape-rendering': "crispEdges",
30915 'pointer-events': "none"
30919 id: this.svgID + "-svg-p",
30921 'stroke-width': "3",
30923 'pointer-events': 'none'
30928 this.svgBox = this.svgEl.dom.getScreenCTM();
30930 createSVG : function(){
30931 var svg = this.signPanel;
30932 var r = svg.select('#'+ this.svgID + '-svg-r', true).first().dom;
30935 r.addEventListener('mousedown', function(e) { return t.down(e); }, false);
30936 r.addEventListener('mousemove', function(e) { return t.move(e); }, false);
30937 r.addEventListener('mouseup', function(e) { return t.up(e); }, false);
30938 r.addEventListener('mouseout', function(e) { return t.up(e); }, false);
30939 r.addEventListener('touchstart', function(e) { return t.down(e); }, false);
30940 r.addEventListener('touchmove', function(e) { return t.move(e); }, false);
30941 r.addEventListener('touchend', function(e) { return t.up(e); }, false);
30944 isTouchEvent : function(e){
30945 return e.type.match(/^touch/);
30947 getCoords : function (e) {
30948 var pt = this.svgEl.dom.createSVGPoint();
30951 if (this.isTouchEvent(e)) {
30952 pt.x = e.targetTouches[0].clientX
30953 pt.y = e.targetTouches[0].clientY;
30955 var a = this.svgEl.dom.getScreenCTM();
30956 var b = a.inverse();
30957 var mx = pt.matrixTransform(b);
30958 return mx.x + ',' + mx.y;
30960 //mouse event headler
30961 down : function (e) {
30962 this.signatureTmp += 'M' + this.getCoords(e) + ' ';
30963 this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr('d', this.signatureTmp);
30965 this.isMouseDown = true;
30967 e.preventDefault();
30969 move : function (e) {
30970 if (this.isMouseDown) {
30971 this.signatureTmp += 'L' + this.getCoords(e) + ' ';
30972 this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', this.signatureTmp);
30975 e.preventDefault();
30977 up : function (e) {
30978 this.isMouseDown = false;
30979 var sp = this.signatureTmp.split(' ');
30982 if(!sp[sp.length-2].match(/^L/)){
30986 this.signatureTmp = sp.join(" ");
30989 if(this.getValue() != this.signatureTmp){
30990 this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
30991 this.isConfirmed = false;
30993 e.preventDefault();
30997 * Protected method that will not generally be called directly. It
30998 * is called when the editor creates its toolbar. Override this method if you need to
30999 * add custom toolbar buttons.
31000 * @param {HtmlEditor} editor
31002 createToolbar : function(editor){
31003 function btn(id, toggle, handler){
31004 var xid = fid + '-'+ id ;
31008 cls : 'x-btn-icon x-edit-'+id,
31009 enableToggle:toggle !== false,
31010 scope: editor, // was editor...
31011 handler:handler||editor.relayBtnCmd,
31012 clickEvent:'mousedown',
31013 tooltip: etb.buttonTips[id] || undefined, ///tips ???
31019 var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
31023 cls : ' x-signature-btn x-signature-'+id,
31024 scope: editor, // was editor...
31025 handler: this.reset,
31026 clickEvent:'mousedown',
31027 text: this.labels.clear
31034 cls : ' x-signature-btn x-signature-'+id,
31035 scope: editor, // was editor...
31036 handler: this.confirmHandler,
31037 clickEvent:'mousedown',
31038 text: this.labels.confirm
31045 * when user is clicked confirm then show this image.....
31047 * @return {String} Image Data URI
31049 getImageDataURI : function(){
31050 var svg = this.svgEl.dom.parentNode.innerHTML;
31051 var src = 'data:image/svg+xml;base64,'+window.btoa(svg);
31056 * @return {Boolean} this.isConfirmed
31058 getConfirmed : function(){
31059 return this.isConfirmed;
31063 * @return {Number} this.width
31065 getWidth : function(){
31070 * @return {Number} this.height
31072 getHeight : function(){
31073 return this.height;
31076 getSignature : function(){
31077 return this.signatureTmp;
31080 reset : function(){
31081 this.signatureTmp = '';
31082 this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
31083 this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', '');
31084 this.isConfirmed = false;
31085 Roo.form.Signature.superclass.reset.call(this);
31087 setSignature : function(s){
31088 this.signatureTmp = s;
31089 this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
31090 this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', s);
31092 this.isConfirmed = false;
31093 Roo.form.Signature.superclass.reset.call(this);
31096 // Roo.log(this.signPanel.dom.contentWindow.up())
31099 setConfirmed : function(){
31103 // Roo.log(Roo.get(this.signPanel.dom.contentWindow.r).attr('fill', '#cfc'));
31106 confirmHandler : function(){
31107 if(!this.getSignature()){
31111 this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#cfc');
31112 this.setValue(this.getSignature());
31113 this.isConfirmed = true;
31115 this.fireEvent('confirm', this);
31118 // Subclasses should provide the validation implementation by overriding this
31119 validateValue : function(value){
31120 if(this.allowBlank){
31124 if(this.isConfirmed){
31131 * Ext JS Library 1.1.1
31132 * Copyright(c) 2006-2007, Ext JS, LLC.
31134 * Originally Released Under LGPL - original licence link has changed is not relivant.
31137 * <script type="text/javascript">
31142 * @class Roo.form.ComboBox
31143 * @extends Roo.form.TriggerField
31144 * A combobox control with support for autocomplete, remote-loading, paging and many other features.
31146 * Create a new ComboBox.
31147 * @param {Object} config Configuration options
31149 Roo.form.Select = function(config){
31150 Roo.form.Select.superclass.constructor.call(this, config);
31154 Roo.extend(Roo.form.Select , Roo.form.ComboBox, {
31156 * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
31159 * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
31160 * rendering into an Roo.Editor, defaults to false)
31163 * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
31164 * {tag: "input", type: "text", size: "24", autocomplete: "off"})
31167 * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
31170 * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
31171 * the dropdown list (defaults to undefined, with no header element)
31175 * @cfg {String/Roo.Template} tpl The template to use to render the output
31179 defaultAutoCreate : {tag: "select" },
31181 * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
31183 listWidth: undefined,
31185 * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
31186 * mode = 'remote' or 'text' if mode = 'local')
31188 displayField: undefined,
31190 * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
31191 * mode = 'remote' or 'value' if mode = 'local').
31192 * Note: use of a valueField requires the user make a selection
31193 * in order for a value to be mapped.
31195 valueField: undefined,
31199 * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
31200 * field's data value (defaults to the underlying DOM element's name)
31202 hiddenName: undefined,
31204 * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
31208 * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
31210 selectedClass: 'x-combo-selected',
31212 * @cfg {String} triggerClass An additional CSS class used to style the trigger button. The trigger will always get the
31213 * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
31214 * which displays a downward arrow icon).
31216 triggerClass : 'x-form-arrow-trigger',
31218 * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
31222 * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
31223 * anchor positions (defaults to 'tl-bl')
31225 listAlign: 'tl-bl?',
31227 * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
31231 * @cfg {String} triggerAction The action to execute when the trigger field is activated. Use 'all' to run the
31232 * query specified by the allQuery config option (defaults to 'query')
31234 triggerAction: 'query',
31236 * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
31237 * (defaults to 4, does not apply if editable = false)
31241 * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
31242 * delay (typeAheadDelay) if it matches a known value (defaults to false)
31246 * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
31247 * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
31251 * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
31252 * filter queries will execute with page start and limit parameters. Only applies when mode = 'remote' (defaults to 0)
31256 * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus. Only applies
31257 * when editable = true (defaults to false)
31259 selectOnFocus:false,
31261 * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
31263 queryParam: 'query',
31265 * @cfg {String} loadingText The text to display in the dropdown list while data is loading. Only applies
31266 * when mode = 'remote' (defaults to 'Loading...')
31268 loadingText: 'Loading...',
31270 * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
31274 * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
31278 * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
31279 * traditional select (defaults to true)
31283 * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
31287 * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
31291 * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
31292 * listWidth has a higher value)
31296 * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
31297 * allow the user to set arbitrary text into the field (defaults to false)
31299 forceSelection:false,
31301 * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
31302 * if typeAhead = true (defaults to 250)
31304 typeAheadDelay : 250,
31306 * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
31307 * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
31309 valueNotFoundText : undefined,
31312 * @cfg {String} defaultValue The value displayed after loading the store.
31317 * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
31319 blockFocus : false,
31322 * @cfg {Boolean} disableClear Disable showing of clear button.
31324 disableClear : false,
31326 * @cfg {Boolean} alwaysQuery Disable caching of results, and always send query
31328 alwaysQuery : false,
31334 // element that contains real text value.. (when hidden is used..)
31337 onRender : function(ct, position){
31338 Roo.form.Field.prototype.onRender.call(this, ct, position);
31341 this.store.on('beforeload', this.onBeforeLoad, this);
31342 this.store.on('load', this.onLoad, this);
31343 this.store.on('loadexception', this.onLoadException, this);
31344 this.store.load({});
31352 initEvents : function(){
31353 //Roo.form.ComboBox.superclass.initEvents.call(this);
31357 onDestroy : function(){
31360 this.store.un('beforeload', this.onBeforeLoad, this);
31361 this.store.un('load', this.onLoad, this);
31362 this.store.un('loadexception', this.onLoadException, this);
31364 //Roo.form.ComboBox.superclass.onDestroy.call(this);
31368 fireKey : function(e){
31369 if(e.isNavKeyPress() && !this.list.isVisible()){
31370 this.fireEvent("specialkey", this, e);
31375 onResize: function(w, h){
31383 * Allow or prevent the user from directly editing the field text. If false is passed,
31384 * the user will only be able to select from the items defined in the dropdown list. This method
31385 * is the runtime equivalent of setting the 'editable' config option at config time.
31386 * @param {Boolean} value True to allow the user to directly edit the field text
31388 setEditable : function(value){
31393 onBeforeLoad : function(){
31395 Roo.log("Select before load");
31398 this.innerList.update(this.loadingText ?
31399 '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
31400 //this.restrictHeight();
31401 this.selectedIndex = -1;
31405 onLoad : function(){
31408 var dom = this.el.dom;
31409 dom.innerHTML = '';
31410 var od = dom.ownerDocument;
31412 if (this.emptyText) {
31413 var op = od.createElement('option');
31414 op.setAttribute('value', '');
31415 op.innerHTML = String.format('{0}', this.emptyText);
31416 dom.appendChild(op);
31418 if(this.store.getCount() > 0){
31420 var vf = this.valueField;
31421 var df = this.displayField;
31422 this.store.data.each(function(r) {
31423 // which colmsn to use... testing - cdoe / title..
31424 var op = od.createElement('option');
31425 op.setAttribute('value', r.data[vf]);
31426 op.innerHTML = String.format('{0}', r.data[df]);
31427 dom.appendChild(op);
31429 if (typeof(this.defaultValue != 'undefined')) {
31430 this.setValue(this.defaultValue);
31435 //this.onEmptyResults();
31440 onLoadException : function()
31442 dom.innerHTML = '';
31444 Roo.log("Select on load exception");
31448 Roo.log(this.store.reader.jsonData);
31449 if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
31450 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
31456 onTypeAhead : function(){
31461 onSelect : function(record, index){
31462 Roo.log('on select?');
31464 if(this.fireEvent('beforeselect', this, record, index) !== false){
31465 this.setFromData(index > -1 ? record.data : false);
31467 this.fireEvent('select', this, record, index);
31472 * Returns the currently selected field value or empty string if no value is set.
31473 * @return {String} value The selected value
31475 getValue : function(){
31476 var dom = this.el.dom;
31477 this.value = dom.options[dom.selectedIndex].value;
31483 * Clears any text/value currently set in the field
31485 clearValue : function(){
31487 this.el.dom.selectedIndex = this.emptyText ? 0 : -1;
31492 * Sets the specified value into the field. If the value finds a match, the corresponding record text
31493 * will be displayed in the field. If the value does not match the data value of an existing item,
31494 * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
31495 * Otherwise the field will be blank (although the value will still be set).
31496 * @param {String} value The value to match
31498 setValue : function(v){
31499 var d = this.el.dom;
31500 for (var i =0; i < d.options.length;i++) {
31501 if (v == d.options[i].value) {
31502 d.selectedIndex = i;
31510 * @property {Object} the last set data for the element
31515 * Sets the value of the field based on a object which is related to the record format for the store.
31516 * @param {Object} value the value to set as. or false on reset?
31518 setFromData : function(o){
31519 Roo.log('setfrom data?');
31525 reset : function(){
31529 findRecord : function(prop, value){
31534 if(this.store.getCount() > 0){
31535 this.store.each(function(r){
31536 if(r.data[prop] == value){
31546 getName: function()
31548 // returns hidden if it's set..
31549 if (!this.rendered) {return ''};
31550 return !this.hiddenName && this.el.dom.name ? this.el.dom.name : (this.hiddenName || '');
31558 onEmptyResults : function(){
31559 Roo.log('empty results');
31564 * Returns true if the dropdown list is expanded, else false.
31566 isExpanded : function(){
31571 * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
31572 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
31573 * @param {String} value The data value of the item to select
31574 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
31575 * selected item if it is not currently in view (defaults to true)
31576 * @return {Boolean} True if the value matched an item in the list, else false
31578 selectByValue : function(v, scrollIntoView){
31579 Roo.log('select By Value');
31582 if(v !== undefined && v !== null){
31583 var r = this.findRecord(this.valueField || this.displayField, v);
31585 this.select(this.store.indexOf(r), scrollIntoView);
31593 * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
31594 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
31595 * @param {Number} index The zero-based index of the list item to select
31596 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
31597 * selected item if it is not currently in view (defaults to true)
31599 select : function(index, scrollIntoView){
31600 Roo.log('select ');
31603 this.selectedIndex = index;
31604 this.view.select(index);
31605 if(scrollIntoView !== false){
31606 var el = this.view.getNode(index);
31608 this.innerList.scrollChildIntoView(el, false);
31616 validateBlur : function(){
31623 initQuery : function(){
31624 this.doQuery(this.getRawValue());
31628 doForce : function(){
31629 if(this.el.dom.value.length > 0){
31630 this.el.dom.value =
31631 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
31637 * Execute a query to filter the dropdown list. Fires the beforequery event prior to performing the
31638 * query allowing the query action to be canceled if needed.
31639 * @param {String} query The SQL query to execute
31640 * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
31641 * in the field than the minimum specified by the minChars config option. It also clears any filter previously
31642 * saved in the current store (defaults to false)
31644 doQuery : function(q, forceAll){
31646 Roo.log('doQuery?');
31647 if(q === undefined || q === null){
31652 forceAll: forceAll,
31656 if(this.fireEvent('beforequery', qe)===false || qe.cancel){
31660 forceAll = qe.forceAll;
31661 if(forceAll === true || (q.length >= this.minChars)){
31662 if(this.lastQuery != q || this.alwaysQuery){
31663 this.lastQuery = q;
31664 if(this.mode == 'local'){
31665 this.selectedIndex = -1;
31667 this.store.clearFilter();
31669 this.store.filter(this.displayField, q);
31673 this.store.baseParams[this.queryParam] = q;
31675 params: this.getParams(q)
31680 this.selectedIndex = -1;
31687 getParams : function(q){
31689 //p[this.queryParam] = q;
31692 p.limit = this.pageSize;
31698 * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
31700 collapse : function(){
31705 collapseIf : function(e){
31710 * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
31712 expand : function(){
31720 * @cfg {Boolean} grow
31724 * @cfg {Number} growMin
31728 * @cfg {Number} growMax
31736 setWidth : function()
31740 getResizeEl : function(){
31743 });//<script type="text/javasscript">
31747 * @class Roo.DDView
31748 * A DnD enabled version of Roo.View.
31749 * @param {Element/String} container The Element in which to create the View.
31750 * @param {String} tpl The template string used to create the markup for each element of the View
31751 * @param {Object} config The configuration properties. These include all the config options of
31752 * {@link Roo.View} plus some specific to this class.<br>
31754 * Drag/drop is implemented by adding {@link Roo.data.Record}s to the target DDView. If copying is
31755 * not being performed, the original {@link Roo.data.Record} is removed from the source DDView.<br>
31757 * The following extra CSS rules are needed to provide insertion point highlighting:<pre><code>
31758 .x-view-drag-insert-above {
31759 border-top:1px dotted #3366cc;
31761 .x-view-drag-insert-below {
31762 border-bottom:1px dotted #3366cc;
31768 Roo.DDView = function(container, tpl, config) {
31769 Roo.DDView.superclass.constructor.apply(this, arguments);
31770 this.getEl().setStyle("outline", "0px none");
31771 this.getEl().unselectable();
31772 if (this.dragGroup) {
31773 this.setDraggable(this.dragGroup.split(","));
31775 if (this.dropGroup) {
31776 this.setDroppable(this.dropGroup.split(","));
31778 if (this.deletable) {
31779 this.setDeletable();
31781 this.isDirtyFlag = false;
31787 Roo.extend(Roo.DDView, Roo.View, {
31788 /** @cfg {String/Array} dragGroup The ddgroup name(s) for the View's DragZone. */
31789 /** @cfg {String/Array} dropGroup The ddgroup name(s) for the View's DropZone. */
31790 /** @cfg {Boolean} copy Causes drag operations to copy nodes rather than move. */
31791 /** @cfg {Boolean} allowCopy Causes ctrl/drag operations to copy nodes rather than move. */
31795 reset: Roo.emptyFn,
31797 clearInvalid: Roo.form.Field.prototype.clearInvalid,
31799 validate: function() {
31803 destroy: function() {
31804 this.purgeListeners();
31805 this.getEl.removeAllListeners();
31806 this.getEl().remove();
31807 if (this.dragZone) {
31808 if (this.dragZone.destroy) {
31809 this.dragZone.destroy();
31812 if (this.dropZone) {
31813 if (this.dropZone.destroy) {
31814 this.dropZone.destroy();
31819 /** Allows this class to be an Roo.form.Field so it can be found using {@link Roo.form.BasicForm#findField}. */
31820 getName: function() {
31824 /** Loads the View from a JSON string representing the Records to put into the Store. */
31825 setValue: function(v) {
31827 throw "DDView.setValue(). DDView must be constructed with a valid Store";
31830 data[this.store.reader.meta.root] = v ? [].concat(v) : [];
31831 this.store.proxy = new Roo.data.MemoryProxy(data);
31835 /** @return {String} a parenthesised list of the ids of the Records in the View. */
31836 getValue: function() {
31838 this.store.each(function(rec) {
31839 result += rec.id + ',';
31841 return result.substr(0, result.length - 1) + ')';
31844 getIds: function() {
31845 var i = 0, result = new Array(this.store.getCount());
31846 this.store.each(function(rec) {
31847 result[i++] = rec.id;
31852 isDirty: function() {
31853 return this.isDirtyFlag;
31857 * Part of the Roo.dd.DropZone interface. If no target node is found, the
31858 * whole Element becomes the target, and this causes the drop gesture to append.
31860 getTargetFromEvent : function(e) {
31861 var target = e.getTarget();
31862 while ((target !== null) && (target.parentNode != this.el.dom)) {
31863 target = target.parentNode;
31866 target = this.el.dom.lastChild || this.el.dom;
31872 * Create the drag data which consists of an object which has the property "ddel" as
31873 * the drag proxy element.
31875 getDragData : function(e) {
31876 var target = this.findItemFromChild(e.getTarget());
31878 this.handleSelection(e);
31879 var selNodes = this.getSelectedNodes();
31882 copy: this.copy || (this.allowCopy && e.ctrlKey),
31886 var selectedIndices = this.getSelectedIndexes();
31887 for (var i = 0; i < selectedIndices.length; i++) {
31888 dragData.records.push(this.store.getAt(selectedIndices[i]));
31890 if (selNodes.length == 1) {
31891 dragData.ddel = target.cloneNode(true); // the div element
31893 var div = document.createElement('div'); // create the multi element drag "ghost"
31894 div.className = 'multi-proxy';
31895 for (var i = 0, len = selNodes.length; i < len; i++) {
31896 div.appendChild(selNodes[i].cloneNode(true));
31898 dragData.ddel = div;
31900 //console.log(dragData)
31901 //console.log(dragData.ddel.innerHTML)
31904 //console.log('nodragData')
31908 /** Specify to which ddGroup items in this DDView may be dragged. */
31909 setDraggable: function(ddGroup) {
31910 if (ddGroup instanceof Array) {
31911 Roo.each(ddGroup, this.setDraggable, this);
31914 if (this.dragZone) {
31915 this.dragZone.addToGroup(ddGroup);
31917 this.dragZone = new Roo.dd.DragZone(this.getEl(), {
31918 containerScroll: true,
31922 // Draggability implies selection. DragZone's mousedown selects the element.
31923 if (!this.multiSelect) { this.singleSelect = true; }
31925 // Wire the DragZone's handlers up to methods in *this*
31926 this.dragZone.getDragData = this.getDragData.createDelegate(this);
31930 /** Specify from which ddGroup this DDView accepts drops. */
31931 setDroppable: function(ddGroup) {
31932 if (ddGroup instanceof Array) {
31933 Roo.each(ddGroup, this.setDroppable, this);
31936 if (this.dropZone) {
31937 this.dropZone.addToGroup(ddGroup);
31939 this.dropZone = new Roo.dd.DropZone(this.getEl(), {
31940 containerScroll: true,
31944 // Wire the DropZone's handlers up to methods in *this*
31945 this.dropZone.getTargetFromEvent = this.getTargetFromEvent.createDelegate(this);
31946 this.dropZone.onNodeEnter = this.onNodeEnter.createDelegate(this);
31947 this.dropZone.onNodeOver = this.onNodeOver.createDelegate(this);
31948 this.dropZone.onNodeOut = this.onNodeOut.createDelegate(this);
31949 this.dropZone.onNodeDrop = this.onNodeDrop.createDelegate(this);
31953 /** Decide whether to drop above or below a View node. */
31954 getDropPoint : function(e, n, dd){
31955 if (n == this.el.dom) { return "above"; }
31956 var t = Roo.lib.Dom.getY(n), b = t + n.offsetHeight;
31957 var c = t + (b - t) / 2;
31958 var y = Roo.lib.Event.getPageY(e);
31966 onNodeEnter : function(n, dd, e, data){
31970 onNodeOver : function(n, dd, e, data){
31971 var pt = this.getDropPoint(e, n, dd);
31972 // set the insert point style on the target node
31973 var dragElClass = this.dropNotAllowed;
31976 if (pt == "above"){
31977 dragElClass = n.previousSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-above";
31978 targetElClass = "x-view-drag-insert-above";
31980 dragElClass = n.nextSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-below";
31981 targetElClass = "x-view-drag-insert-below";
31983 if (this.lastInsertClass != targetElClass){
31984 Roo.fly(n).replaceClass(this.lastInsertClass, targetElClass);
31985 this.lastInsertClass = targetElClass;
31988 return dragElClass;
31991 onNodeOut : function(n, dd, e, data){
31992 this.removeDropIndicators(n);
31995 onNodeDrop : function(n, dd, e, data){
31996 if (this.fireEvent("drop", this, n, dd, e, data) === false) {
31999 var pt = this.getDropPoint(e, n, dd);
32000 var insertAt = (n == this.el.dom) ? this.nodes.length : n.nodeIndex;
32001 if (pt == "below") { insertAt++; }
32002 for (var i = 0; i < data.records.length; i++) {
32003 var r = data.records[i];
32004 var dup = this.store.getById(r.id);
32005 if (dup && (dd != this.dragZone)) {
32006 Roo.fly(this.getNode(this.store.indexOf(dup))).frame("red", 1);
32009 this.store.insert(insertAt++, r.copy());
32011 data.source.isDirtyFlag = true;
32013 this.store.insert(insertAt++, r);
32015 this.isDirtyFlag = true;
32018 this.dragZone.cachedTarget = null;
32022 removeDropIndicators : function(n){
32024 Roo.fly(n).removeClass([
32025 "x-view-drag-insert-above",
32026 "x-view-drag-insert-below"]);
32027 this.lastInsertClass = "_noclass";
32032 * Utility method. Add a delete option to the DDView's context menu.
32033 * @param {String} imageUrl The URL of the "delete" icon image.
32035 setDeletable: function(imageUrl) {
32036 if (!this.singleSelect && !this.multiSelect) {
32037 this.singleSelect = true;
32039 var c = this.getContextMenu();
32040 this.contextMenu.on("itemclick", function(item) {
32043 this.remove(this.getSelectedIndexes());
32047 this.contextMenu.add({
32054 /** Return the context menu for this DDView. */
32055 getContextMenu: function() {
32056 if (!this.contextMenu) {
32057 // Create the View's context menu
32058 this.contextMenu = new Roo.menu.Menu({
32059 id: this.id + "-contextmenu"
32061 this.el.on("contextmenu", this.showContextMenu, this);
32063 return this.contextMenu;
32066 disableContextMenu: function() {
32067 if (this.contextMenu) {
32068 this.el.un("contextmenu", this.showContextMenu, this);
32072 showContextMenu: function(e, item) {
32073 item = this.findItemFromChild(e.getTarget());
32076 this.select(this.getNode(item), this.multiSelect && e.ctrlKey, true);
32077 this.contextMenu.showAt(e.getXY());
32082 * Remove {@link Roo.data.Record}s at the specified indices.
32083 * @param {Array/Number} selectedIndices The index (or Array of indices) of Records to remove.
32085 remove: function(selectedIndices) {
32086 selectedIndices = [].concat(selectedIndices);
32087 for (var i = 0; i < selectedIndices.length; i++) {
32088 var rec = this.store.getAt(selectedIndices[i]);
32089 this.store.remove(rec);
32094 * Double click fires the event, but also, if this is draggable, and there is only one other
32095 * related DropZone, it transfers the selected node.
32097 onDblClick : function(e){
32098 var item = this.findItemFromChild(e.getTarget());
32100 if (this.fireEvent("dblclick", this, this.indexOf(item), item, e) === false) {
32103 if (this.dragGroup) {
32104 var targets = Roo.dd.DragDropMgr.getRelated(this.dragZone, true);
32105 while (targets.indexOf(this.dropZone) > -1) {
32106 targets.remove(this.dropZone);
32108 if (targets.length == 1) {
32109 this.dragZone.cachedTarget = null;
32110 var el = Roo.get(targets[0].getEl());
32111 var box = el.getBox(true);
32112 targets[0].onNodeDrop(el.dom, {
32114 xy: [box.x, box.y + box.height - 1]
32115 }, null, this.getDragData(e));
32121 handleSelection: function(e) {
32122 this.dragZone.cachedTarget = null;
32123 var item = this.findItemFromChild(e.getTarget());
32125 this.clearSelections(true);
32128 if (item && (this.multiSelect || this.singleSelect)){
32129 if(this.multiSelect && e.shiftKey && (!e.ctrlKey) && this.lastSelection){
32130 this.select(this.getNodes(this.indexOf(this.lastSelection), item.nodeIndex), false);
32131 }else if (this.isSelected(this.getNode(item)) && e.ctrlKey){
32132 this.unselect(item);
32134 this.select(item, this.multiSelect && e.ctrlKey);
32135 this.lastSelection = item;
32140 onItemClick : function(item, index, e){
32141 if(this.fireEvent("beforeclick", this, index, item, e) === false){
32147 unselect : function(nodeInfo, suppressEvent){
32148 var node = this.getNode(nodeInfo);
32149 if(node && this.isSelected(node)){
32150 if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
32151 Roo.fly(node).removeClass(this.selectedClass);
32152 this.selections.remove(node);
32153 if(!suppressEvent){
32154 this.fireEvent("selectionchange", this, this.selections);
32162 * Ext JS Library 1.1.1
32163 * Copyright(c) 2006-2007, Ext JS, LLC.
32165 * Originally Released Under LGPL - original licence link has changed is not relivant.
32168 * <script type="text/javascript">
32172 * @class Roo.LayoutManager
32173 * @extends Roo.util.Observable
32174 * Base class for layout managers.
32176 Roo.LayoutManager = function(container, config){
32177 Roo.LayoutManager.superclass.constructor.call(this);
32178 this.el = Roo.get(container);
32179 // ie scrollbar fix
32180 if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
32181 document.body.scroll = "no";
32182 }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
32183 this.el.position('relative');
32185 this.id = this.el.id;
32186 this.el.addClass("x-layout-container");
32187 /** false to disable window resize monitoring @type Boolean */
32188 this.monitorWindowResize = true;
32193 * Fires when a layout is performed.
32194 * @param {Roo.LayoutManager} this
32198 * @event regionresized
32199 * Fires when the user resizes a region.
32200 * @param {Roo.LayoutRegion} region The resized region
32201 * @param {Number} newSize The new size (width for east/west, height for north/south)
32203 "regionresized" : true,
32205 * @event regioncollapsed
32206 * Fires when a region is collapsed.
32207 * @param {Roo.LayoutRegion} region The collapsed region
32209 "regioncollapsed" : true,
32211 * @event regionexpanded
32212 * Fires when a region is expanded.
32213 * @param {Roo.LayoutRegion} region The expanded region
32215 "regionexpanded" : true
32217 this.updating = false;
32218 Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
32221 Roo.extend(Roo.LayoutManager, Roo.util.Observable, {
32223 * Returns true if this layout is currently being updated
32224 * @return {Boolean}
32226 isUpdating : function(){
32227 return this.updating;
32231 * Suspend the LayoutManager from doing auto-layouts while
32232 * making multiple add or remove calls
32234 beginUpdate : function(){
32235 this.updating = true;
32239 * Restore auto-layouts and optionally disable the manager from performing a layout
32240 * @param {Boolean} noLayout true to disable a layout update
32242 endUpdate : function(noLayout){
32243 this.updating = false;
32249 layout: function(){
32253 onRegionResized : function(region, newSize){
32254 this.fireEvent("regionresized", region, newSize);
32258 onRegionCollapsed : function(region){
32259 this.fireEvent("regioncollapsed", region);
32262 onRegionExpanded : function(region){
32263 this.fireEvent("regionexpanded", region);
32267 * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
32268 * performs box-model adjustments.
32269 * @return {Object} The size as an object {width: (the width), height: (the height)}
32271 getViewSize : function(){
32273 if(this.el.dom != document.body){
32274 size = this.el.getSize();
32276 size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
32278 size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
32279 size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
32284 * Returns the Element this layout is bound to.
32285 * @return {Roo.Element}
32287 getEl : function(){
32292 * Returns the specified region.
32293 * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
32294 * @return {Roo.LayoutRegion}
32296 getRegion : function(target){
32297 return this.regions[target.toLowerCase()];
32300 onWindowResize : function(){
32301 if(this.monitorWindowResize){
32307 * Ext JS Library 1.1.1
32308 * Copyright(c) 2006-2007, Ext JS, LLC.
32310 * Originally Released Under LGPL - original licence link has changed is not relivant.
32313 * <script type="text/javascript">
32316 * @class Roo.BorderLayout
32317 * @extends Roo.LayoutManager
32318 * This class represents a common layout manager used in desktop applications. For screenshots and more details,
32319 * please see: <br><br>
32320 * <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>
32321 * <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>
32324 var layout = new Roo.BorderLayout(document.body, {
32358 preferredTabWidth: 150
32363 var CP = Roo.ContentPanel;
32365 layout.beginUpdate();
32366 layout.add("north", new CP("north", "North"));
32367 layout.add("south", new CP("south", {title: "South", closable: true}));
32368 layout.add("west", new CP("west", {title: "West"}));
32369 layout.add("east", new CP("autoTabs", {title: "Auto Tabs", closable: true}));
32370 layout.add("center", new CP("center1", {title: "Close Me", closable: true}));
32371 layout.add("center", new CP("center2", {title: "Center Panel", closable: false}));
32372 layout.getRegion("center").showPanel("center1");
32373 layout.endUpdate();
32376 <b>The container the layout is rendered into can be either the body element or any other element.
32377 If it is not the body element, the container needs to either be an absolute positioned element,
32378 or you will need to add "position:relative" to the css of the container. You will also need to specify
32379 the container size if it is not the body element.</b>
32382 * Create a new BorderLayout
32383 * @param {String/HTMLElement/Element} container The container this layout is bound to
32384 * @param {Object} config Configuration options
32386 Roo.BorderLayout = function(container, config){
32387 config = config || {};
32388 Roo.BorderLayout.superclass.constructor.call(this, container, config);
32389 this.factory = config.factory || Roo.BorderLayout.RegionFactory;
32390 for(var i = 0, len = this.factory.validRegions.length; i < len; i++) {
32391 var target = this.factory.validRegions[i];
32392 if(config[target]){
32393 this.addRegion(target, config[target]);
32398 Roo.extend(Roo.BorderLayout, Roo.LayoutManager, {
32400 * Creates and adds a new region if it doesn't already exist.
32401 * @param {String} target The target region key (north, south, east, west or center).
32402 * @param {Object} config The regions config object
32403 * @return {BorderLayoutRegion} The new region
32405 addRegion : function(target, config){
32406 if(!this.regions[target]){
32407 var r = this.factory.create(target, this, config);
32408 this.bindRegion(target, r);
32410 return this.regions[target];
32414 bindRegion : function(name, r){
32415 this.regions[name] = r;
32416 r.on("visibilitychange", this.layout, this);
32417 r.on("paneladded", this.layout, this);
32418 r.on("panelremoved", this.layout, this);
32419 r.on("invalidated", this.layout, this);
32420 r.on("resized", this.onRegionResized, this);
32421 r.on("collapsed", this.onRegionCollapsed, this);
32422 r.on("expanded", this.onRegionExpanded, this);
32426 * Performs a layout update.
32428 layout : function(){
32429 if(this.updating) return;
32430 var size = this.getViewSize();
32431 var w = size.width;
32432 var h = size.height;
32437 //var x = 0, y = 0;
32439 var rs = this.regions;
32440 var north = rs["north"];
32441 var south = rs["south"];
32442 var west = rs["west"];
32443 var east = rs["east"];
32444 var center = rs["center"];
32445 //if(this.hideOnLayout){ // not supported anymore
32446 //c.el.setStyle("display", "none");
32448 if(north && north.isVisible()){
32449 var b = north.getBox();
32450 var m = north.getMargins();
32451 b.width = w - (m.left+m.right);
32454 centerY = b.height + b.y + m.bottom;
32455 centerH -= centerY;
32456 north.updateBox(this.safeBox(b));
32458 if(south && south.isVisible()){
32459 var b = south.getBox();
32460 var m = south.getMargins();
32461 b.width = w - (m.left+m.right);
32463 var totalHeight = (b.height + m.top + m.bottom);
32464 b.y = h - totalHeight + m.top;
32465 centerH -= totalHeight;
32466 south.updateBox(this.safeBox(b));
32468 if(west && west.isVisible()){
32469 var b = west.getBox();
32470 var m = west.getMargins();
32471 b.height = centerH - (m.top+m.bottom);
32473 b.y = centerY + m.top;
32474 var totalWidth = (b.width + m.left + m.right);
32475 centerX += totalWidth;
32476 centerW -= totalWidth;
32477 west.updateBox(this.safeBox(b));
32479 if(east && east.isVisible()){
32480 var b = east.getBox();
32481 var m = east.getMargins();
32482 b.height = centerH - (m.top+m.bottom);
32483 var totalWidth = (b.width + m.left + m.right);
32484 b.x = w - totalWidth + m.left;
32485 b.y = centerY + m.top;
32486 centerW -= totalWidth;
32487 east.updateBox(this.safeBox(b));
32490 var m = center.getMargins();
32492 x: centerX + m.left,
32493 y: centerY + m.top,
32494 width: centerW - (m.left+m.right),
32495 height: centerH - (m.top+m.bottom)
32497 //if(this.hideOnLayout){
32498 //center.el.setStyle("display", "block");
32500 center.updateBox(this.safeBox(centerBox));
32503 this.fireEvent("layout", this);
32507 safeBox : function(box){
32508 box.width = Math.max(0, box.width);
32509 box.height = Math.max(0, box.height);
32514 * Adds a ContentPanel (or subclass) to this layout.
32515 * @param {String} target The target region key (north, south, east, west or center).
32516 * @param {Roo.ContentPanel} panel The panel to add
32517 * @return {Roo.ContentPanel} The added panel
32519 add : function(target, panel){
32521 target = target.toLowerCase();
32522 return this.regions[target].add(panel);
32526 * Remove a ContentPanel (or subclass) to this layout.
32527 * @param {String} target The target region key (north, south, east, west or center).
32528 * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
32529 * @return {Roo.ContentPanel} The removed panel
32531 remove : function(target, panel){
32532 target = target.toLowerCase();
32533 return this.regions[target].remove(panel);
32537 * Searches all regions for a panel with the specified id
32538 * @param {String} panelId
32539 * @return {Roo.ContentPanel} The panel or null if it wasn't found
32541 findPanel : function(panelId){
32542 var rs = this.regions;
32543 for(var target in rs){
32544 if(typeof rs[target] != "function"){
32545 var p = rs[target].getPanel(panelId);
32555 * Searches all regions for a panel with the specified id and activates (shows) it.
32556 * @param {String/ContentPanel} panelId The panels id or the panel itself
32557 * @return {Roo.ContentPanel} The shown panel or null
32559 showPanel : function(panelId) {
32560 var rs = this.regions;
32561 for(var target in rs){
32562 var r = rs[target];
32563 if(typeof r != "function"){
32564 if(r.hasPanel(panelId)){
32565 return r.showPanel(panelId);
32573 * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
32574 * @param {Roo.state.Provider} provider (optional) An alternate state provider
32576 restoreState : function(provider){
32578 provider = Roo.state.Manager;
32580 var sm = new Roo.LayoutStateManager();
32581 sm.init(this, provider);
32585 * Adds a batch of multiple ContentPanels dynamically by passing a special regions config object. This config
32586 * object should contain properties for each region to add ContentPanels to, and each property's value should be
32587 * a valid ContentPanel config object. Example:
32589 // Create the main layout
32590 var layout = new Roo.BorderLayout('main-ct', {
32601 // Create and add multiple ContentPanels at once via configs
32604 id: 'source-files',
32606 title:'Ext Source Files',
32619 * @param {Object} regions An object containing ContentPanel configs by region name
32621 batchAdd : function(regions){
32622 this.beginUpdate();
32623 for(var rname in regions){
32624 var lr = this.regions[rname];
32626 this.addTypedPanels(lr, regions[rname]);
32633 addTypedPanels : function(lr, ps){
32634 if(typeof ps == 'string'){
32635 lr.add(new Roo.ContentPanel(ps));
32637 else if(ps instanceof Array){
32638 for(var i =0, len = ps.length; i < len; i++){
32639 this.addTypedPanels(lr, ps[i]);
32642 else if(!ps.events){ // raw config?
32644 delete ps.el; // prevent conflict
32645 lr.add(new Roo.ContentPanel(el || Roo.id(), ps));
32647 else { // panel object assumed!
32652 * Adds a xtype elements to the layout.
32656 xtype : 'ContentPanel',
32663 xtype : 'NestedLayoutPanel',
32669 items : [ ... list of content panels or nested layout panels.. ]
32673 * @param {Object} cfg Xtype definition of item to add.
32675 addxtype : function(cfg)
32677 // basically accepts a pannel...
32678 // can accept a layout region..!?!?
32679 //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
32681 if (!cfg.xtype.match(/Panel$/)) {
32686 if (typeof(cfg.region) == 'undefined') {
32687 Roo.log("Failed to add Panel, region was not set");
32691 var region = cfg.region;
32697 xitems = cfg.items;
32704 case 'ContentPanel': // ContentPanel (el, cfg)
32705 case 'ScrollPanel': // ContentPanel (el, cfg)
32707 if(cfg.autoCreate) {
32708 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
32710 var el = this.el.createChild();
32711 ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
32714 this.add(region, ret);
32718 case 'TreePanel': // our new panel!
32719 cfg.el = this.el.createChild();
32720 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
32721 this.add(region, ret);
32724 case 'NestedLayoutPanel':
32725 // create a new Layout (which is a Border Layout...
32726 var el = this.el.createChild();
32727 var clayout = cfg.layout;
32729 clayout.items = clayout.items || [];
32730 // replace this exitems with the clayout ones..
32731 xitems = clayout.items;
32734 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
32735 cfg.background = false;
32737 var layout = new Roo.BorderLayout(el, clayout);
32739 ret = new Roo[cfg.xtype](layout, cfg); // new panel!!!!!
32740 //console.log('adding nested layout panel ' + cfg.toSource());
32741 this.add(region, ret);
32742 nb = {}; /// find first...
32747 // needs grid and region
32749 //var el = this.getRegion(region).el.createChild();
32750 var el = this.el.createChild();
32751 // create the grid first...
32753 var grid = new Roo.grid[cfg.grid.xtype](el, cfg.grid);
32755 if (region == 'center' && this.active ) {
32756 cfg.background = false;
32758 ret = new Roo[cfg.xtype](grid, cfg); // new panel!!!!!
32760 this.add(region, ret);
32761 if (cfg.background) {
32762 ret.on('activate', function(gp) {
32763 if (!gp.grid.rendered) {
32778 alert("Can not add '" + cfg.xtype + "' to BorderLayout");
32780 // GridPanel (grid, cfg)
32783 this.beginUpdate();
32787 Roo.each(xitems, function(i) {
32788 region = nb && i.region ? i.region : false;
32790 var add = ret.addxtype(i);
32793 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
32794 if (!i.background) {
32795 abn[region] = nb[region] ;
32802 // make the last non-background panel active..
32803 //if (nb) { Roo.log(abn); }
32806 for(var r in abn) {
32807 region = this.getRegion(r);
32809 // tried using nb[r], but it does not work..
32811 region.showPanel(abn[r]);
32822 * Shortcut for creating a new BorderLayout object and adding one or more ContentPanels to it in a single step, handling
32823 * the beginUpdate and endUpdate calls internally. The key to this method is the <b>panels</b> property that can be
32824 * provided with each region config, which allows you to add ContentPanel configs in addition to the region configs
32825 * during creation. The following code is equivalent to the constructor-based example at the beginning of this class:
32828 var CP = Roo.ContentPanel;
32830 var layout = Roo.BorderLayout.create({
32834 panels: [new CP("north", "North")]
32843 panels: [new CP("west", {title: "West"})]
32852 panels: [new CP("autoTabs", {title: "Auto Tabs", closable: true})]
32861 panels: [new CP("south", {title: "South", closable: true})]
32868 preferredTabWidth: 150,
32870 new CP("center1", {title: "Close Me", closable: true}),
32871 new CP("center2", {title: "Center Panel", closable: false})
32876 layout.getRegion("center").showPanel("center1");
32881 Roo.BorderLayout.create = function(config, targetEl){
32882 var layout = new Roo.BorderLayout(targetEl || document.body, config);
32883 layout.beginUpdate();
32884 var regions = Roo.BorderLayout.RegionFactory.validRegions;
32885 for(var j = 0, jlen = regions.length; j < jlen; j++){
32886 var lr = regions[j];
32887 if(layout.regions[lr] && config[lr].panels){
32888 var r = layout.regions[lr];
32889 var ps = config[lr].panels;
32890 layout.addTypedPanels(r, ps);
32893 layout.endUpdate();
32898 Roo.BorderLayout.RegionFactory = {
32900 validRegions : ["north","south","east","west","center"],
32903 create : function(target, mgr, config){
32904 target = target.toLowerCase();
32905 if(config.lightweight || config.basic){
32906 return new Roo.BasicLayoutRegion(mgr, config, target);
32910 return new Roo.NorthLayoutRegion(mgr, config);
32912 return new Roo.SouthLayoutRegion(mgr, config);
32914 return new Roo.EastLayoutRegion(mgr, config);
32916 return new Roo.WestLayoutRegion(mgr, config);
32918 return new Roo.CenterLayoutRegion(mgr, config);
32920 throw 'Layout region "'+target+'" not supported.';
32924 * Ext JS Library 1.1.1
32925 * Copyright(c) 2006-2007, Ext JS, LLC.
32927 * Originally Released Under LGPL - original licence link has changed is not relivant.
32930 * <script type="text/javascript">
32934 * @class Roo.BasicLayoutRegion
32935 * @extends Roo.util.Observable
32936 * This class represents a lightweight region in a layout manager. This region does not move dom nodes
32937 * and does not have a titlebar, tabs or any other features. All it does is size and position
32938 * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
32940 Roo.BasicLayoutRegion = function(mgr, config, pos, skipConfig){
32942 this.position = pos;
32945 * @scope Roo.BasicLayoutRegion
32949 * @event beforeremove
32950 * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
32951 * @param {Roo.LayoutRegion} this
32952 * @param {Roo.ContentPanel} panel The panel
32953 * @param {Object} e The cancel event object
32955 "beforeremove" : true,
32957 * @event invalidated
32958 * Fires when the layout for this region is changed.
32959 * @param {Roo.LayoutRegion} this
32961 "invalidated" : true,
32963 * @event visibilitychange
32964 * Fires when this region is shown or hidden
32965 * @param {Roo.LayoutRegion} this
32966 * @param {Boolean} visibility true or false
32968 "visibilitychange" : true,
32970 * @event paneladded
32971 * Fires when a panel is added.
32972 * @param {Roo.LayoutRegion} this
32973 * @param {Roo.ContentPanel} panel The panel
32975 "paneladded" : true,
32977 * @event panelremoved
32978 * Fires when a panel is removed.
32979 * @param {Roo.LayoutRegion} this
32980 * @param {Roo.ContentPanel} panel The panel
32982 "panelremoved" : true,
32985 * Fires when this region is collapsed.
32986 * @param {Roo.LayoutRegion} this
32988 "collapsed" : true,
32991 * Fires when this region is expanded.
32992 * @param {Roo.LayoutRegion} this
32997 * Fires when this region is slid into view.
32998 * @param {Roo.LayoutRegion} this
33000 "slideshow" : true,
33003 * Fires when this region slides out of view.
33004 * @param {Roo.LayoutRegion} this
33006 "slidehide" : true,
33008 * @event panelactivated
33009 * Fires when a panel is activated.
33010 * @param {Roo.LayoutRegion} this
33011 * @param {Roo.ContentPanel} panel The activated panel
33013 "panelactivated" : true,
33016 * Fires when the user resizes this region.
33017 * @param {Roo.LayoutRegion} this
33018 * @param {Number} newSize The new size (width for east/west, height for north/south)
33022 /** A collection of panels in this region. @type Roo.util.MixedCollection */
33023 this.panels = new Roo.util.MixedCollection();
33024 this.panels.getKey = this.getPanelId.createDelegate(this);
33026 this.activePanel = null;
33027 // ensure listeners are added...
33029 if (config.listeners || config.events) {
33030 Roo.BasicLayoutRegion.superclass.constructor.call(this, {
33031 listeners : config.listeners || {},
33032 events : config.events || {}
33036 if(skipConfig !== true){
33037 this.applyConfig(config);
33041 Roo.extend(Roo.BasicLayoutRegion, Roo.util.Observable, {
33042 getPanelId : function(p){
33046 applyConfig : function(config){
33047 this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
33048 this.config = config;
33053 * Resizes the region to the specified size. For vertical regions (west, east) this adjusts
33054 * the width, for horizontal (north, south) the height.
33055 * @param {Number} newSize The new width or height
33057 resizeTo : function(newSize){
33058 var el = this.el ? this.el :
33059 (this.activePanel ? this.activePanel.getEl() : null);
33061 switch(this.position){
33064 el.setWidth(newSize);
33065 this.fireEvent("resized", this, newSize);
33069 el.setHeight(newSize);
33070 this.fireEvent("resized", this, newSize);
33076 getBox : function(){
33077 return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
33080 getMargins : function(){
33081 return this.margins;
33084 updateBox : function(box){
33086 var el = this.activePanel.getEl();
33087 el.dom.style.left = box.x + "px";
33088 el.dom.style.top = box.y + "px";
33089 this.activePanel.setSize(box.width, box.height);
33093 * Returns the container element for this region.
33094 * @return {Roo.Element}
33096 getEl : function(){
33097 return this.activePanel;
33101 * Returns true if this region is currently visible.
33102 * @return {Boolean}
33104 isVisible : function(){
33105 return this.activePanel ? true : false;
33108 setActivePanel : function(panel){
33109 panel = this.getPanel(panel);
33110 if(this.activePanel && this.activePanel != panel){
33111 this.activePanel.setActiveState(false);
33112 this.activePanel.getEl().setLeftTop(-10000,-10000);
33114 this.activePanel = panel;
33115 panel.setActiveState(true);
33117 panel.setSize(this.box.width, this.box.height);
33119 this.fireEvent("panelactivated", this, panel);
33120 this.fireEvent("invalidated");
33124 * Show the specified panel.
33125 * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
33126 * @return {Roo.ContentPanel} The shown panel or null
33128 showPanel : function(panel){
33129 if(panel = this.getPanel(panel)){
33130 this.setActivePanel(panel);
33136 * Get the active panel for this region.
33137 * @return {Roo.ContentPanel} The active panel or null
33139 getActivePanel : function(){
33140 return this.activePanel;
33144 * Add the passed ContentPanel(s)
33145 * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
33146 * @return {Roo.ContentPanel} The panel added (if only one was added)
33148 add : function(panel){
33149 if(arguments.length > 1){
33150 for(var i = 0, len = arguments.length; i < len; i++) {
33151 this.add(arguments[i]);
33155 if(this.hasPanel(panel)){
33156 this.showPanel(panel);
33159 var el = panel.getEl();
33160 if(el.dom.parentNode != this.mgr.el.dom){
33161 this.mgr.el.dom.appendChild(el.dom);
33163 if(panel.setRegion){
33164 panel.setRegion(this);
33166 this.panels.add(panel);
33167 el.setStyle("position", "absolute");
33168 if(!panel.background){
33169 this.setActivePanel(panel);
33170 if(this.config.initialSize && this.panels.getCount()==1){
33171 this.resizeTo(this.config.initialSize);
33174 this.fireEvent("paneladded", this, panel);
33179 * Returns true if the panel is in this region.
33180 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
33181 * @return {Boolean}
33183 hasPanel : function(panel){
33184 if(typeof panel == "object"){ // must be panel obj
33185 panel = panel.getId();
33187 return this.getPanel(panel) ? true : false;
33191 * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
33192 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
33193 * @param {Boolean} preservePanel Overrides the config preservePanel option
33194 * @return {Roo.ContentPanel} The panel that was removed
33196 remove : function(panel, preservePanel){
33197 panel = this.getPanel(panel);
33202 this.fireEvent("beforeremove", this, panel, e);
33203 if(e.cancel === true){
33206 var panelId = panel.getId();
33207 this.panels.removeKey(panelId);
33212 * Returns the panel specified or null if it's not in this region.
33213 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
33214 * @return {Roo.ContentPanel}
33216 getPanel : function(id){
33217 if(typeof id == "object"){ // must be panel obj
33220 return this.panels.get(id);
33224 * Returns this regions position (north/south/east/west/center).
33227 getPosition: function(){
33228 return this.position;
33232 * Ext JS Library 1.1.1
33233 * Copyright(c) 2006-2007, Ext JS, LLC.
33235 * Originally Released Under LGPL - original licence link has changed is not relivant.
33238 * <script type="text/javascript">
33242 * @class Roo.LayoutRegion
33243 * @extends Roo.BasicLayoutRegion
33244 * This class represents a region in a layout manager.
33245 * @cfg {Boolean} collapsible False to disable collapsing (defaults to true)
33246 * @cfg {Boolean} collapsed True to set the initial display to collapsed (defaults to false)
33247 * @cfg {Boolean} floatable False to disable floating (defaults to true)
33248 * @cfg {Object} margins Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
33249 * @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})
33250 * @cfg {String} tabPosition "top" or "bottom" (defaults to "bottom")
33251 * @cfg {String} collapsedTitle Optional string message to display in the collapsed block of a north or south region
33252 * @cfg {Boolean} alwaysShowTabs True to always display tabs even when there is only 1 panel (defaults to false)
33253 * @cfg {Boolean} autoScroll True to enable overflow scrolling (defaults to false)
33254 * @cfg {Boolean} titlebar True to display a title bar (defaults to true)
33255 * @cfg {String} title The title for the region (overrides panel titles)
33256 * @cfg {Boolean} animate True to animate expand/collapse (defaults to false)
33257 * @cfg {Boolean} autoHide False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
33258 * @cfg {Boolean} preservePanels True to preserve removed panels so they can be readded later (defaults to false)
33259 * @cfg {Boolean} closeOnTab True to place the close icon on the tabs instead of the region titlebar (defaults to false)
33260 * @cfg {Boolean} hideTabs True to hide the tab strip (defaults to false)
33261 * @cfg {Boolean} resizeTabs True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
33262 * the space available, similar to FireFox 1.5 tabs (defaults to false)
33263 * @cfg {Number} minTabWidth The minimum tab width (defaults to 40)
33264 * @cfg {Number} preferredTabWidth The preferred tab width (defaults to 150)
33265 * @cfg {Boolean} showPin True to show a pin button
33266 * @cfg {Boolean} hidden True to start the region hidden (defaults to false)
33267 * @cfg {Boolean} hideWhenEmpty True to hide the region when it has no panels
33268 * @cfg {Boolean} disableTabTips True to disable tab tooltips
33269 * @cfg {Number} width For East/West panels
33270 * @cfg {Number} height For North/South panels
33271 * @cfg {Boolean} split To show the splitter
33272 * @cfg {Boolean} toolbar xtype configuration for a toolbar - shows on right of tabbar
33274 Roo.LayoutRegion = function(mgr, config, pos){
33275 Roo.LayoutRegion.superclass.constructor.call(this, mgr, config, pos, true);
33276 var dh = Roo.DomHelper;
33277 /** This region's container element
33278 * @type Roo.Element */
33279 this.el = dh.append(mgr.el.dom, {tag: "div", cls: "x-layout-panel x-layout-panel-" + this.position}, true);
33280 /** This region's title element
33281 * @type Roo.Element */
33283 this.titleEl = dh.append(this.el.dom, {tag: "div", unselectable: "on", cls: "x-unselectable x-layout-panel-hd x-layout-title-"+this.position, children:[
33284 {tag: "span", cls: "x-unselectable x-layout-panel-hd-text", unselectable: "on", html: " "},
33285 {tag: "div", cls: "x-unselectable x-layout-panel-hd-tools", unselectable: "on"}
33287 this.titleEl.enableDisplayMode();
33288 /** This region's title text element
33289 * @type HTMLElement */
33290 this.titleTextEl = this.titleEl.dom.firstChild;
33291 this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
33292 this.closeBtn = this.createTool(this.tools.dom, "x-layout-close");
33293 this.closeBtn.enableDisplayMode();
33294 this.closeBtn.on("click", this.closeClicked, this);
33295 this.closeBtn.hide();
33297 this.createBody(config);
33298 this.visible = true;
33299 this.collapsed = false;
33301 if(config.hideWhenEmpty){
33303 this.on("paneladded", this.validateVisibility, this);
33304 this.on("panelremoved", this.validateVisibility, this);
33306 this.applyConfig(config);
33309 Roo.extend(Roo.LayoutRegion, Roo.BasicLayoutRegion, {
33311 createBody : function(){
33312 /** This region's body element
33313 * @type Roo.Element */
33314 this.bodyEl = this.el.createChild({tag: "div", cls: "x-layout-panel-body"});
33317 applyConfig : function(c){
33318 if(c.collapsible && this.position != "center" && !this.collapsedEl){
33319 var dh = Roo.DomHelper;
33320 if(c.titlebar !== false){
33321 this.collapseBtn = this.createTool(this.tools.dom, "x-layout-collapse-"+this.position);
33322 this.collapseBtn.on("click", this.collapse, this);
33323 this.collapseBtn.enableDisplayMode();
33325 if(c.showPin === true || this.showPin){
33326 this.stickBtn = this.createTool(this.tools.dom, "x-layout-stick");
33327 this.stickBtn.enableDisplayMode();
33328 this.stickBtn.on("click", this.expand, this);
33329 this.stickBtn.hide();
33332 /** This region's collapsed element
33333 * @type Roo.Element */
33334 this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
33335 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
33337 if(c.floatable !== false){
33338 this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
33339 this.collapsedEl.on("click", this.collapseClick, this);
33342 if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
33343 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
33344 id: "message", unselectable: "on", style:{"float":"left"}});
33345 this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
33347 this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
33348 this.expandBtn.on("click", this.expand, this);
33350 if(this.collapseBtn){
33351 this.collapseBtn.setVisible(c.collapsible == true);
33353 this.cmargins = c.cmargins || this.cmargins ||
33354 (this.position == "west" || this.position == "east" ?
33355 {top: 0, left: 2, right:2, bottom: 0} :
33356 {top: 2, left: 0, right:0, bottom: 2});
33357 this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
33358 this.bottomTabs = c.tabPosition != "top";
33359 this.autoScroll = c.autoScroll || false;
33360 if(this.autoScroll){
33361 this.bodyEl.setStyle("overflow", "auto");
33363 this.bodyEl.setStyle("overflow", "hidden");
33365 //if(c.titlebar !== false){
33366 if((!c.titlebar && !c.title) || c.titlebar === false){
33367 this.titleEl.hide();
33369 this.titleEl.show();
33371 this.titleTextEl.innerHTML = c.title;
33375 this.duration = c.duration || .30;
33376 this.slideDuration = c.slideDuration || .45;
33379 this.collapse(true);
33386 * Returns true if this region is currently visible.
33387 * @return {Boolean}
33389 isVisible : function(){
33390 return this.visible;
33394 * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
33395 * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&#160;")
33397 setCollapsedTitle : function(title){
33398 title = title || " ";
33399 if(this.collapsedTitleTextEl){
33400 this.collapsedTitleTextEl.innerHTML = title;
33404 getBox : function(){
33406 if(!this.collapsed){
33407 b = this.el.getBox(false, true);
33409 b = this.collapsedEl.getBox(false, true);
33414 getMargins : function(){
33415 return this.collapsed ? this.cmargins : this.margins;
33418 highlight : function(){
33419 this.el.addClass("x-layout-panel-dragover");
33422 unhighlight : function(){
33423 this.el.removeClass("x-layout-panel-dragover");
33426 updateBox : function(box){
33428 if(!this.collapsed){
33429 this.el.dom.style.left = box.x + "px";
33430 this.el.dom.style.top = box.y + "px";
33431 this.updateBody(box.width, box.height);
33433 this.collapsedEl.dom.style.left = box.x + "px";
33434 this.collapsedEl.dom.style.top = box.y + "px";
33435 this.collapsedEl.setSize(box.width, box.height);
33438 this.tabs.autoSizeTabs();
33442 updateBody : function(w, h){
33444 this.el.setWidth(w);
33445 w -= this.el.getBorderWidth("rl");
33446 if(this.config.adjustments){
33447 w += this.config.adjustments[0];
33451 this.el.setHeight(h);
33452 h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
33453 h -= this.el.getBorderWidth("tb");
33454 if(this.config.adjustments){
33455 h += this.config.adjustments[1];
33457 this.bodyEl.setHeight(h);
33459 h = this.tabs.syncHeight(h);
33462 if(this.panelSize){
33463 w = w !== null ? w : this.panelSize.width;
33464 h = h !== null ? h : this.panelSize.height;
33466 if(this.activePanel){
33467 var el = this.activePanel.getEl();
33468 w = w !== null ? w : el.getWidth();
33469 h = h !== null ? h : el.getHeight();
33470 this.panelSize = {width: w, height: h};
33471 this.activePanel.setSize(w, h);
33473 if(Roo.isIE && this.tabs){
33474 this.tabs.el.repaint();
33479 * Returns the container element for this region.
33480 * @return {Roo.Element}
33482 getEl : function(){
33487 * Hides this region.
33490 if(!this.collapsed){
33491 this.el.dom.style.left = "-2000px";
33494 this.collapsedEl.dom.style.left = "-2000px";
33495 this.collapsedEl.hide();
33497 this.visible = false;
33498 this.fireEvent("visibilitychange", this, false);
33502 * Shows this region if it was previously hidden.
33505 if(!this.collapsed){
33508 this.collapsedEl.show();
33510 this.visible = true;
33511 this.fireEvent("visibilitychange", this, true);
33514 closeClicked : function(){
33515 if(this.activePanel){
33516 this.remove(this.activePanel);
33520 collapseClick : function(e){
33522 e.stopPropagation();
33525 e.stopPropagation();
33531 * Collapses this region.
33532 * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
33534 collapse : function(skipAnim){
33535 if(this.collapsed) return;
33536 this.collapsed = true;
33538 this.split.el.hide();
33540 if(this.config.animate && skipAnim !== true){
33541 this.fireEvent("invalidated", this);
33542 this.animateCollapse();
33544 this.el.setLocation(-20000,-20000);
33546 this.collapsedEl.show();
33547 this.fireEvent("collapsed", this);
33548 this.fireEvent("invalidated", this);
33552 animateCollapse : function(){
33557 * Expands this region if it was previously collapsed.
33558 * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
33559 * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
33561 expand : function(e, skipAnim){
33562 if(e) e.stopPropagation();
33563 if(!this.collapsed || this.el.hasActiveFx()) return;
33565 this.afterSlideIn();
33568 this.collapsed = false;
33569 if(this.config.animate && skipAnim !== true){
33570 this.animateExpand();
33574 this.split.el.show();
33576 this.collapsedEl.setLocation(-2000,-2000);
33577 this.collapsedEl.hide();
33578 this.fireEvent("invalidated", this);
33579 this.fireEvent("expanded", this);
33583 animateExpand : function(){
33587 initTabs : function()
33589 this.bodyEl.setStyle("overflow", "hidden");
33590 var ts = new Roo.TabPanel(
33593 tabPosition: this.bottomTabs ? 'bottom' : 'top',
33594 disableTooltips: this.config.disableTabTips,
33595 toolbar : this.config.toolbar
33598 if(this.config.hideTabs){
33599 ts.stripWrap.setDisplayed(false);
33602 ts.resizeTabs = this.config.resizeTabs === true;
33603 ts.minTabWidth = this.config.minTabWidth || 40;
33604 ts.maxTabWidth = this.config.maxTabWidth || 250;
33605 ts.preferredTabWidth = this.config.preferredTabWidth || 150;
33606 ts.monitorResize = false;
33607 ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
33608 ts.bodyEl.addClass('x-layout-tabs-body');
33609 this.panels.each(this.initPanelAsTab, this);
33612 initPanelAsTab : function(panel){
33613 var ti = this.tabs.addTab(panel.getEl().id, panel.getTitle(), null,
33614 this.config.closeOnTab && panel.isClosable());
33615 if(panel.tabTip !== undefined){
33616 ti.setTooltip(panel.tabTip);
33618 ti.on("activate", function(){
33619 this.setActivePanel(panel);
33621 if(this.config.closeOnTab){
33622 ti.on("beforeclose", function(t, e){
33624 this.remove(panel);
33630 updatePanelTitle : function(panel, title){
33631 if(this.activePanel == panel){
33632 this.updateTitle(title);
33635 var ti = this.tabs.getTab(panel.getEl().id);
33637 if(panel.tabTip !== undefined){
33638 ti.setTooltip(panel.tabTip);
33643 updateTitle : function(title){
33644 if(this.titleTextEl && !this.config.title){
33645 this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : " ");
33649 setActivePanel : function(panel){
33650 panel = this.getPanel(panel);
33651 if(this.activePanel && this.activePanel != panel){
33652 this.activePanel.setActiveState(false);
33654 this.activePanel = panel;
33655 panel.setActiveState(true);
33656 if(this.panelSize){
33657 panel.setSize(this.panelSize.width, this.panelSize.height);
33660 this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
33662 this.updateTitle(panel.getTitle());
33664 this.fireEvent("invalidated", this);
33666 this.fireEvent("panelactivated", this, panel);
33670 * Shows the specified panel.
33671 * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
33672 * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
33674 showPanel : function(panel){
33675 if(panel = this.getPanel(panel)){
33677 var tab = this.tabs.getTab(panel.getEl().id);
33678 if(tab.isHidden()){
33679 this.tabs.unhideTab(tab.id);
33683 this.setActivePanel(panel);
33690 * Get the active panel for this region.
33691 * @return {Roo.ContentPanel} The active panel or null
33693 getActivePanel : function(){
33694 return this.activePanel;
33697 validateVisibility : function(){
33698 if(this.panels.getCount() < 1){
33699 this.updateTitle(" ");
33700 this.closeBtn.hide();
33703 if(!this.isVisible()){
33710 * Adds the passed ContentPanel(s) to this region.
33711 * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
33712 * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
33714 add : function(panel){
33715 if(arguments.length > 1){
33716 for(var i = 0, len = arguments.length; i < len; i++) {
33717 this.add(arguments[i]);
33721 if(this.hasPanel(panel)){
33722 this.showPanel(panel);
33725 panel.setRegion(this);
33726 this.panels.add(panel);
33727 if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
33728 this.bodyEl.dom.appendChild(panel.getEl().dom);
33729 if(panel.background !== true){
33730 this.setActivePanel(panel);
33732 this.fireEvent("paneladded", this, panel);
33738 this.initPanelAsTab(panel);
33740 if(panel.background !== true){
33741 this.tabs.activate(panel.getEl().id);
33743 this.fireEvent("paneladded", this, panel);
33748 * Hides the tab for the specified panel.
33749 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
33751 hidePanel : function(panel){
33752 if(this.tabs && (panel = this.getPanel(panel))){
33753 this.tabs.hideTab(panel.getEl().id);
33758 * Unhides the tab for a previously hidden panel.
33759 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
33761 unhidePanel : function(panel){
33762 if(this.tabs && (panel = this.getPanel(panel))){
33763 this.tabs.unhideTab(panel.getEl().id);
33767 clearPanels : function(){
33768 while(this.panels.getCount() > 0){
33769 this.remove(this.panels.first());
33774 * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
33775 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
33776 * @param {Boolean} preservePanel Overrides the config preservePanel option
33777 * @return {Roo.ContentPanel} The panel that was removed
33779 remove : function(panel, preservePanel){
33780 panel = this.getPanel(panel);
33785 this.fireEvent("beforeremove", this, panel, e);
33786 if(e.cancel === true){
33789 preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
33790 var panelId = panel.getId();
33791 this.panels.removeKey(panelId);
33793 document.body.appendChild(panel.getEl().dom);
33796 this.tabs.removeTab(panel.getEl().id);
33797 }else if (!preservePanel){
33798 this.bodyEl.dom.removeChild(panel.getEl().dom);
33800 if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
33801 var p = this.panels.first();
33802 var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
33803 tempEl.appendChild(p.getEl().dom);
33804 this.bodyEl.update("");
33805 this.bodyEl.dom.appendChild(p.getEl().dom);
33807 this.updateTitle(p.getTitle());
33809 this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
33810 this.setActivePanel(p);
33812 panel.setRegion(null);
33813 if(this.activePanel == panel){
33814 this.activePanel = null;
33816 if(this.config.autoDestroy !== false && preservePanel !== true){
33817 try{panel.destroy();}catch(e){}
33819 this.fireEvent("panelremoved", this, panel);
33824 * Returns the TabPanel component used by this region
33825 * @return {Roo.TabPanel}
33827 getTabs : function(){
33831 createTool : function(parentEl, className){
33832 var btn = Roo.DomHelper.append(parentEl, {tag: "div", cls: "x-layout-tools-button",
33833 children: [{tag: "div", cls: "x-layout-tools-button-inner " + className, html: " "}]}, true);
33834 btn.addClassOnOver("x-layout-tools-button-over");
33839 * Ext JS Library 1.1.1
33840 * Copyright(c) 2006-2007, Ext JS, LLC.
33842 * Originally Released Under LGPL - original licence link has changed is not relivant.
33845 * <script type="text/javascript">
33851 * @class Roo.SplitLayoutRegion
33852 * @extends Roo.LayoutRegion
33853 * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
33855 Roo.SplitLayoutRegion = function(mgr, config, pos, cursor){
33856 this.cursor = cursor;
33857 Roo.SplitLayoutRegion.superclass.constructor.call(this, mgr, config, pos);
33860 Roo.extend(Roo.SplitLayoutRegion, Roo.LayoutRegion, {
33861 splitTip : "Drag to resize.",
33862 collapsibleSplitTip : "Drag to resize. Double click to hide.",
33863 useSplitTips : false,
33865 applyConfig : function(config){
33866 Roo.SplitLayoutRegion.superclass.applyConfig.call(this, config);
33869 var splitEl = Roo.DomHelper.append(this.mgr.el.dom,
33870 {tag: "div", id: this.el.id + "-split", cls: "x-layout-split x-layout-split-"+this.position, html: " "});
33871 /** The SplitBar for this region
33872 * @type Roo.SplitBar */
33873 this.split = new Roo.SplitBar(splitEl, this.el, this.orientation);
33874 this.split.on("moved", this.onSplitMove, this);
33875 this.split.useShim = config.useShim === true;
33876 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
33877 if(this.useSplitTips){
33878 this.split.el.dom.title = config.collapsible ? this.collapsibleSplitTip : this.splitTip;
33880 if(config.collapsible){
33881 this.split.el.on("dblclick", this.collapse, this);
33884 if(typeof config.minSize != "undefined"){
33885 this.split.minSize = config.minSize;
33887 if(typeof config.maxSize != "undefined"){
33888 this.split.maxSize = config.maxSize;
33890 if(config.hideWhenEmpty || config.hidden || config.collapsed){
33891 this.hideSplitter();
33896 getHMaxSize : function(){
33897 var cmax = this.config.maxSize || 10000;
33898 var center = this.mgr.getRegion("center");
33899 return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
33902 getVMaxSize : function(){
33903 var cmax = this.config.maxSize || 10000;
33904 var center = this.mgr.getRegion("center");
33905 return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
33908 onSplitMove : function(split, newSize){
33909 this.fireEvent("resized", this, newSize);
33913 * Returns the {@link Roo.SplitBar} for this region.
33914 * @return {Roo.SplitBar}
33916 getSplitBar : function(){
33921 this.hideSplitter();
33922 Roo.SplitLayoutRegion.superclass.hide.call(this);
33925 hideSplitter : function(){
33927 this.split.el.setLocation(-2000,-2000);
33928 this.split.el.hide();
33934 this.split.el.show();
33936 Roo.SplitLayoutRegion.superclass.show.call(this);
33939 beforeSlide: function(){
33940 if(Roo.isGecko){// firefox overflow auto bug workaround
33941 this.bodyEl.clip();
33942 if(this.tabs) this.tabs.bodyEl.clip();
33943 if(this.activePanel){
33944 this.activePanel.getEl().clip();
33946 if(this.activePanel.beforeSlide){
33947 this.activePanel.beforeSlide();
33953 afterSlide : function(){
33954 if(Roo.isGecko){// firefox overflow auto bug workaround
33955 this.bodyEl.unclip();
33956 if(this.tabs) this.tabs.bodyEl.unclip();
33957 if(this.activePanel){
33958 this.activePanel.getEl().unclip();
33959 if(this.activePanel.afterSlide){
33960 this.activePanel.afterSlide();
33966 initAutoHide : function(){
33967 if(this.autoHide !== false){
33968 if(!this.autoHideHd){
33969 var st = new Roo.util.DelayedTask(this.slideIn, this);
33970 this.autoHideHd = {
33971 "mouseout": function(e){
33972 if(!e.within(this.el, true)){
33976 "mouseover" : function(e){
33982 this.el.on(this.autoHideHd);
33986 clearAutoHide : function(){
33987 if(this.autoHide !== false){
33988 this.el.un("mouseout", this.autoHideHd.mouseout);
33989 this.el.un("mouseover", this.autoHideHd.mouseover);
33993 clearMonitor : function(){
33994 Roo.get(document).un("click", this.slideInIf, this);
33997 // these names are backwards but not changed for compat
33998 slideOut : function(){
33999 if(this.isSlid || this.el.hasActiveFx()){
34002 this.isSlid = true;
34003 if(this.collapseBtn){
34004 this.collapseBtn.hide();
34006 this.closeBtnState = this.closeBtn.getStyle('display');
34007 this.closeBtn.hide();
34009 this.stickBtn.show();
34012 this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
34013 this.beforeSlide();
34014 this.el.setStyle("z-index", 10001);
34015 this.el.slideIn(this.getSlideAnchor(), {
34016 callback: function(){
34018 this.initAutoHide();
34019 Roo.get(document).on("click", this.slideInIf, this);
34020 this.fireEvent("slideshow", this);
34027 afterSlideIn : function(){
34028 this.clearAutoHide();
34029 this.isSlid = false;
34030 this.clearMonitor();
34031 this.el.setStyle("z-index", "");
34032 if(this.collapseBtn){
34033 this.collapseBtn.show();
34035 this.closeBtn.setStyle('display', this.closeBtnState);
34037 this.stickBtn.hide();
34039 this.fireEvent("slidehide", this);
34042 slideIn : function(cb){
34043 if(!this.isSlid || this.el.hasActiveFx()){
34047 this.isSlid = false;
34048 this.beforeSlide();
34049 this.el.slideOut(this.getSlideAnchor(), {
34050 callback: function(){
34051 this.el.setLeftTop(-10000, -10000);
34053 this.afterSlideIn();
34061 slideInIf : function(e){
34062 if(!e.within(this.el)){
34067 animateCollapse : function(){
34068 this.beforeSlide();
34069 this.el.setStyle("z-index", 20000);
34070 var anchor = this.getSlideAnchor();
34071 this.el.slideOut(anchor, {
34072 callback : function(){
34073 this.el.setStyle("z-index", "");
34074 this.collapsedEl.slideIn(anchor, {duration:.3});
34076 this.el.setLocation(-10000,-10000);
34078 this.fireEvent("collapsed", this);
34085 animateExpand : function(){
34086 this.beforeSlide();
34087 this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
34088 this.el.setStyle("z-index", 20000);
34089 this.collapsedEl.hide({
34092 this.el.slideIn(this.getSlideAnchor(), {
34093 callback : function(){
34094 this.el.setStyle("z-index", "");
34097 this.split.el.show();
34099 this.fireEvent("invalidated", this);
34100 this.fireEvent("expanded", this);
34128 getAnchor : function(){
34129 return this.anchors[this.position];
34132 getCollapseAnchor : function(){
34133 return this.canchors[this.position];
34136 getSlideAnchor : function(){
34137 return this.sanchors[this.position];
34140 getAlignAdj : function(){
34141 var cm = this.cmargins;
34142 switch(this.position){
34158 getExpandAdj : function(){
34159 var c = this.collapsedEl, cm = this.cmargins;
34160 switch(this.position){
34162 return [-(cm.right+c.getWidth()+cm.left), 0];
34165 return [cm.right+c.getWidth()+cm.left, 0];
34168 return [0, -(cm.top+cm.bottom+c.getHeight())];
34171 return [0, cm.top+cm.bottom+c.getHeight()];
34177 * Ext JS Library 1.1.1
34178 * Copyright(c) 2006-2007, Ext JS, LLC.
34180 * Originally Released Under LGPL - original licence link has changed is not relivant.
34183 * <script type="text/javascript">
34186 * These classes are private internal classes
34188 Roo.CenterLayoutRegion = function(mgr, config){
34189 Roo.LayoutRegion.call(this, mgr, config, "center");
34190 this.visible = true;
34191 this.minWidth = config.minWidth || 20;
34192 this.minHeight = config.minHeight || 20;
34195 Roo.extend(Roo.CenterLayoutRegion, Roo.LayoutRegion, {
34197 // center panel can't be hidden
34201 // center panel can't be hidden
34204 getMinWidth: function(){
34205 return this.minWidth;
34208 getMinHeight: function(){
34209 return this.minHeight;
34214 Roo.NorthLayoutRegion = function(mgr, config){
34215 Roo.LayoutRegion.call(this, mgr, config, "north", "n-resize");
34217 this.split.placement = Roo.SplitBar.TOP;
34218 this.split.orientation = Roo.SplitBar.VERTICAL;
34219 this.split.el.addClass("x-layout-split-v");
34221 var size = config.initialSize || config.height;
34222 if(typeof size != "undefined"){
34223 this.el.setHeight(size);
34226 Roo.extend(Roo.NorthLayoutRegion, Roo.SplitLayoutRegion, {
34227 orientation: Roo.SplitBar.VERTICAL,
34228 getBox : function(){
34229 if(this.collapsed){
34230 return this.collapsedEl.getBox();
34232 var box = this.el.getBox();
34234 box.height += this.split.el.getHeight();
34239 updateBox : function(box){
34240 if(this.split && !this.collapsed){
34241 box.height -= this.split.el.getHeight();
34242 this.split.el.setLeft(box.x);
34243 this.split.el.setTop(box.y+box.height);
34244 this.split.el.setWidth(box.width);
34246 if(this.collapsed){
34247 this.updateBody(box.width, null);
34249 Roo.LayoutRegion.prototype.updateBox.call(this, box);
34253 Roo.SouthLayoutRegion = function(mgr, config){
34254 Roo.SplitLayoutRegion.call(this, mgr, config, "south", "s-resize");
34256 this.split.placement = Roo.SplitBar.BOTTOM;
34257 this.split.orientation = Roo.SplitBar.VERTICAL;
34258 this.split.el.addClass("x-layout-split-v");
34260 var size = config.initialSize || config.height;
34261 if(typeof size != "undefined"){
34262 this.el.setHeight(size);
34265 Roo.extend(Roo.SouthLayoutRegion, Roo.SplitLayoutRegion, {
34266 orientation: Roo.SplitBar.VERTICAL,
34267 getBox : function(){
34268 if(this.collapsed){
34269 return this.collapsedEl.getBox();
34271 var box = this.el.getBox();
34273 var sh = this.split.el.getHeight();
34280 updateBox : function(box){
34281 if(this.split && !this.collapsed){
34282 var sh = this.split.el.getHeight();
34285 this.split.el.setLeft(box.x);
34286 this.split.el.setTop(box.y-sh);
34287 this.split.el.setWidth(box.width);
34289 if(this.collapsed){
34290 this.updateBody(box.width, null);
34292 Roo.LayoutRegion.prototype.updateBox.call(this, box);
34296 Roo.EastLayoutRegion = function(mgr, config){
34297 Roo.SplitLayoutRegion.call(this, mgr, config, "east", "e-resize");
34299 this.split.placement = Roo.SplitBar.RIGHT;
34300 this.split.orientation = Roo.SplitBar.HORIZONTAL;
34301 this.split.el.addClass("x-layout-split-h");
34303 var size = config.initialSize || config.width;
34304 if(typeof size != "undefined"){
34305 this.el.setWidth(size);
34308 Roo.extend(Roo.EastLayoutRegion, Roo.SplitLayoutRegion, {
34309 orientation: Roo.SplitBar.HORIZONTAL,
34310 getBox : function(){
34311 if(this.collapsed){
34312 return this.collapsedEl.getBox();
34314 var box = this.el.getBox();
34316 var sw = this.split.el.getWidth();
34323 updateBox : function(box){
34324 if(this.split && !this.collapsed){
34325 var sw = this.split.el.getWidth();
34327 this.split.el.setLeft(box.x);
34328 this.split.el.setTop(box.y);
34329 this.split.el.setHeight(box.height);
34332 if(this.collapsed){
34333 this.updateBody(null, box.height);
34335 Roo.LayoutRegion.prototype.updateBox.call(this, box);
34339 Roo.WestLayoutRegion = function(mgr, config){
34340 Roo.SplitLayoutRegion.call(this, mgr, config, "west", "w-resize");
34342 this.split.placement = Roo.SplitBar.LEFT;
34343 this.split.orientation = Roo.SplitBar.HORIZONTAL;
34344 this.split.el.addClass("x-layout-split-h");
34346 var size = config.initialSize || config.width;
34347 if(typeof size != "undefined"){
34348 this.el.setWidth(size);
34351 Roo.extend(Roo.WestLayoutRegion, Roo.SplitLayoutRegion, {
34352 orientation: Roo.SplitBar.HORIZONTAL,
34353 getBox : function(){
34354 if(this.collapsed){
34355 return this.collapsedEl.getBox();
34357 var box = this.el.getBox();
34359 box.width += this.split.el.getWidth();
34364 updateBox : function(box){
34365 if(this.split && !this.collapsed){
34366 var sw = this.split.el.getWidth();
34368 this.split.el.setLeft(box.x+box.width);
34369 this.split.el.setTop(box.y);
34370 this.split.el.setHeight(box.height);
34372 if(this.collapsed){
34373 this.updateBody(null, box.height);
34375 Roo.LayoutRegion.prototype.updateBox.call(this, box);
34380 * Ext JS Library 1.1.1
34381 * Copyright(c) 2006-2007, Ext JS, LLC.
34383 * Originally Released Under LGPL - original licence link has changed is not relivant.
34386 * <script type="text/javascript">
34391 * Private internal class for reading and applying state
34393 Roo.LayoutStateManager = function(layout){
34394 // default empty state
34403 Roo.LayoutStateManager.prototype = {
34404 init : function(layout, provider){
34405 this.provider = provider;
34406 var state = provider.get(layout.id+"-layout-state");
34408 var wasUpdating = layout.isUpdating();
34410 layout.beginUpdate();
34412 for(var key in state){
34413 if(typeof state[key] != "function"){
34414 var rstate = state[key];
34415 var r = layout.getRegion(key);
34418 r.resizeTo(rstate.size);
34420 if(rstate.collapsed == true){
34423 r.expand(null, true);
34429 layout.endUpdate();
34431 this.state = state;
34433 this.layout = layout;
34434 layout.on("regionresized", this.onRegionResized, this);
34435 layout.on("regioncollapsed", this.onRegionCollapsed, this);
34436 layout.on("regionexpanded", this.onRegionExpanded, this);
34439 storeState : function(){
34440 this.provider.set(this.layout.id+"-layout-state", this.state);
34443 onRegionResized : function(region, newSize){
34444 this.state[region.getPosition()].size = newSize;
34448 onRegionCollapsed : function(region){
34449 this.state[region.getPosition()].collapsed = true;
34453 onRegionExpanded : function(region){
34454 this.state[region.getPosition()].collapsed = false;
34459 * Ext JS Library 1.1.1
34460 * Copyright(c) 2006-2007, Ext JS, LLC.
34462 * Originally Released Under LGPL - original licence link has changed is not relivant.
34465 * <script type="text/javascript">
34468 * @class Roo.ContentPanel
34469 * @extends Roo.util.Observable
34470 * A basic ContentPanel element.
34471 * @cfg {Boolean} fitToFrame True for this panel to adjust its size to fit when the region resizes (defaults to false)
34472 * @cfg {Boolean} fitContainer When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container (defaults to false)
34473 * @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
34474 * @cfg {Boolean} closable True if the panel can be closed/removed
34475 * @cfg {Boolean} background True if the panel should not be activated when it is added (defaults to false)
34476 * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
34477 * @cfg {Toolbar} toolbar A toolbar for this panel
34478 * @cfg {Boolean} autoScroll True to scroll overflow in this panel (use with {@link #fitToFrame})
34479 * @cfg {String} title The title for this panel
34480 * @cfg {Array} adjustments Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
34481 * @cfg {String} url Calls {@link #setUrl} with this value
34482 * @cfg {String} region (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
34483 * @cfg {String/Object} params When used with {@link #url}, calls {@link #setUrl} with this value
34484 * @cfg {Boolean} loadOnce When used with {@link #url}, calls {@link #setUrl} with this value
34485 * @cfg {String} content Raw content to fill content panel with (uses setContent on construction.)
34488 * Create a new ContentPanel.
34489 * @param {String/HTMLElement/Roo.Element} el The container element for this panel
34490 * @param {String/Object} config A string to set only the title or a config object
34491 * @param {String} content (optional) Set the HTML content for this panel
34492 * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
34494 Roo.ContentPanel = function(el, config, content){
34498 if(el.autoCreate || el.xtype){ // xtype is available if this is called from factory
34502 if (config && config.parentLayout) {
34503 el = config.parentLayout.el.createChild();
34506 if(el.autoCreate){ // xtype is available if this is called from factory
34510 this.el = Roo.get(el);
34511 if(!this.el && config && config.autoCreate){
34512 if(typeof config.autoCreate == "object"){
34513 if(!config.autoCreate.id){
34514 config.autoCreate.id = config.id||el;
34516 this.el = Roo.DomHelper.append(document.body,
34517 config.autoCreate, true);
34519 this.el = Roo.DomHelper.append(document.body,
34520 {tag: "div", cls: "x-layout-inactive-content", id: config.id||el}, true);
34523 this.closable = false;
34524 this.loaded = false;
34525 this.active = false;
34526 if(typeof config == "string"){
34527 this.title = config;
34529 Roo.apply(this, config);
34532 if (this.toolbar && !this.toolbar.el && this.toolbar.xtype) {
34533 this.wrapEl = this.el.wrap();
34534 this.toolbar.container = this.el.insertSibling(false, 'before');
34535 this.toolbar = new Roo.Toolbar(this.toolbar);
34538 // xtype created footer. - not sure if will work as we normally have to render first..
34539 if (this.footer && !this.footer.el && this.footer.xtype) {
34540 if (!this.wrapEl) {
34541 this.wrapEl = this.el.wrap();
34544 this.footer.container = this.wrapEl.createChild();
34546 this.footer = Roo.factory(this.footer, Roo);
34551 this.resizeEl = Roo.get(this.resizeEl, true);
34553 this.resizeEl = this.el;
34555 // handle view.xtype
34563 * Fires when this panel is activated.
34564 * @param {Roo.ContentPanel} this
34568 * @event deactivate
34569 * Fires when this panel is activated.
34570 * @param {Roo.ContentPanel} this
34572 "deactivate" : true,
34576 * Fires when this panel is resized if fitToFrame is true.
34577 * @param {Roo.ContentPanel} this
34578 * @param {Number} width The width after any component adjustments
34579 * @param {Number} height The height after any component adjustments
34585 * Fires when this tab is created
34586 * @param {Roo.ContentPanel} this
34597 if(this.autoScroll){
34598 this.resizeEl.setStyle("overflow", "auto");
34600 // fix randome scrolling
34601 this.el.on('scroll', function() {
34602 Roo.log('fix random scolling');
34603 this.scrollTo('top',0);
34606 content = content || this.content;
34608 this.setContent(content);
34610 if(config && config.url){
34611 this.setUrl(this.url, this.params, this.loadOnce);
34616 Roo.ContentPanel.superclass.constructor.call(this);
34618 if (this.view && typeof(this.view.xtype) != 'undefined') {
34619 this.view.el = this.el.appendChild(document.createElement("div"));
34620 this.view = Roo.factory(this.view);
34621 this.view.render && this.view.render(false, '');
34625 this.fireEvent('render', this);
34628 Roo.extend(Roo.ContentPanel, Roo.util.Observable, {
34630 setRegion : function(region){
34631 this.region = region;
34633 this.el.replaceClass("x-layout-inactive-content", "x-layout-active-content");
34635 this.el.replaceClass("x-layout-active-content", "x-layout-inactive-content");
34640 * Returns the toolbar for this Panel if one was configured.
34641 * @return {Roo.Toolbar}
34643 getToolbar : function(){
34644 return this.toolbar;
34647 setActiveState : function(active){
34648 this.active = active;
34650 this.fireEvent("deactivate", this);
34652 this.fireEvent("activate", this);
34656 * Updates this panel's element
34657 * @param {String} content The new content
34658 * @param {Boolean} loadScripts (optional) true to look for and process scripts
34660 setContent : function(content, loadScripts){
34661 this.el.update(content, loadScripts);
34664 ignoreResize : function(w, h){
34665 if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
34668 this.lastSize = {width: w, height: h};
34673 * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
34674 * @return {Roo.UpdateManager} The UpdateManager
34676 getUpdateManager : function(){
34677 return this.el.getUpdateManager();
34680 * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
34681 * @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:
34684 url: "your-url.php",
34685 params: {param1: "foo", param2: "bar"}, // or a URL encoded string
34686 callback: yourFunction,
34687 scope: yourObject, //(optional scope)
34690 text: "Loading...",
34695 * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
34696 * 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.
34697 * @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}
34698 * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
34699 * @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.
34700 * @return {Roo.ContentPanel} this
34703 var um = this.el.getUpdateManager();
34704 um.update.apply(um, arguments);
34710 * 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.
34711 * @param {String/Function} url The URL to load the content from or a function to call to get the URL
34712 * @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)
34713 * @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)
34714 * @return {Roo.UpdateManager} The UpdateManager
34716 setUrl : function(url, params, loadOnce){
34717 if(this.refreshDelegate){
34718 this.removeListener("activate", this.refreshDelegate);
34720 this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
34721 this.on("activate", this.refreshDelegate);
34722 return this.el.getUpdateManager();
34725 _handleRefresh : function(url, params, loadOnce){
34726 if(!loadOnce || !this.loaded){
34727 var updater = this.el.getUpdateManager();
34728 updater.update(url, params, this._setLoaded.createDelegate(this));
34732 _setLoaded : function(){
34733 this.loaded = true;
34737 * Returns this panel's id
34740 getId : function(){
34745 * Returns this panel's element - used by regiosn to add.
34746 * @return {Roo.Element}
34748 getEl : function(){
34749 return this.wrapEl || this.el;
34752 adjustForComponents : function(width, height)
34754 //Roo.log('adjustForComponents ');
34755 if(this.resizeEl != this.el){
34756 width -= this.el.getFrameWidth('lr');
34757 height -= this.el.getFrameWidth('tb');
34760 var te = this.toolbar.getEl();
34761 height -= te.getHeight();
34762 te.setWidth(width);
34765 var te = this.footer.getEl();
34766 Roo.log("footer:" + te.getHeight());
34768 height -= te.getHeight();
34769 te.setWidth(width);
34773 if(this.adjustments){
34774 width += this.adjustments[0];
34775 height += this.adjustments[1];
34777 return {"width": width, "height": height};
34780 setSize : function(width, height){
34781 if(this.fitToFrame && !this.ignoreResize(width, height)){
34782 if(this.fitContainer && this.resizeEl != this.el){
34783 this.el.setSize(width, height);
34785 var size = this.adjustForComponents(width, height);
34786 this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
34787 this.fireEvent('resize', this, size.width, size.height);
34792 * Returns this panel's title
34795 getTitle : function(){
34800 * Set this panel's title
34801 * @param {String} title
34803 setTitle : function(title){
34804 this.title = title;
34806 this.region.updatePanelTitle(this, title);
34811 * Returns true is this panel was configured to be closable
34812 * @return {Boolean}
34814 isClosable : function(){
34815 return this.closable;
34818 beforeSlide : function(){
34820 this.resizeEl.clip();
34823 afterSlide : function(){
34825 this.resizeEl.unclip();
34829 * Force a content refresh from the URL specified in the {@link #setUrl} method.
34830 * Will fail silently if the {@link #setUrl} method has not been called.
34831 * This does not activate the panel, just updates its content.
34833 refresh : function(){
34834 if(this.refreshDelegate){
34835 this.loaded = false;
34836 this.refreshDelegate();
34841 * Destroys this panel
34843 destroy : function(){
34844 this.el.removeAllListeners();
34845 var tempEl = document.createElement("span");
34846 tempEl.appendChild(this.el.dom);
34847 tempEl.innerHTML = "";
34853 * form - if the content panel contains a form - this is a reference to it.
34854 * @type {Roo.form.Form}
34858 * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
34859 * This contains a reference to it.
34865 * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
34875 * @param {Object} cfg Xtype definition of item to add.
34878 addxtype : function(cfg) {
34880 if (cfg.xtype.match(/^Form$/)) {
34883 //if (this.footer) {
34884 // el = this.footer.container.insertSibling(false, 'before');
34886 el = this.el.createChild();
34889 this.form = new Roo.form.Form(cfg);
34892 if ( this.form.allItems.length) this.form.render(el.dom);
34895 // should only have one of theses..
34896 if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
34897 // views.. should not be just added - used named prop 'view''
34899 cfg.el = this.el.appendChild(document.createElement("div"));
34902 var ret = new Roo.factory(cfg);
34904 ret.render && ret.render(false, ''); // render blank..
34913 * @class Roo.GridPanel
34914 * @extends Roo.ContentPanel
34916 * Create a new GridPanel.
34917 * @param {Roo.grid.Grid} grid The grid for this panel
34918 * @param {String/Object} config A string to set only the panel's title, or a config object
34920 Roo.GridPanel = function(grid, config){
34923 this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
34924 {tag: "div", cls: "x-layout-grid-wrapper x-layout-inactive-content"}, true);
34926 this.wrapper.dom.appendChild(grid.getGridEl().dom);
34928 Roo.GridPanel.superclass.constructor.call(this, this.wrapper, config);
34931 this.toolbar.el.insertBefore(this.wrapper.dom.firstChild);
34933 // xtype created footer. - not sure if will work as we normally have to render first..
34934 if (this.footer && !this.footer.el && this.footer.xtype) {
34936 this.footer.container = this.grid.getView().getFooterPanel(true);
34937 this.footer.dataSource = this.grid.dataSource;
34938 this.footer = Roo.factory(this.footer, Roo);
34942 grid.monitorWindowResize = false; // turn off autosizing
34943 grid.autoHeight = false;
34944 grid.autoWidth = false;
34946 this.grid.getGridEl().replaceClass("x-layout-inactive-content", "x-layout-component-panel");
34949 Roo.extend(Roo.GridPanel, Roo.ContentPanel, {
34950 getId : function(){
34951 return this.grid.id;
34955 * Returns the grid for this panel
34956 * @return {Roo.grid.Grid}
34958 getGrid : function(){
34962 setSize : function(width, height){
34963 if(!this.ignoreResize(width, height)){
34964 var grid = this.grid;
34965 var size = this.adjustForComponents(width, height);
34966 grid.getGridEl().setSize(size.width, size.height);
34971 beforeSlide : function(){
34972 this.grid.getView().scroller.clip();
34975 afterSlide : function(){
34976 this.grid.getView().scroller.unclip();
34979 destroy : function(){
34980 this.grid.destroy();
34982 Roo.GridPanel.superclass.destroy.call(this);
34988 * @class Roo.NestedLayoutPanel
34989 * @extends Roo.ContentPanel
34991 * Create a new NestedLayoutPanel.
34994 * @param {Roo.BorderLayout} layout The layout for this panel
34995 * @param {String/Object} config A string to set only the title or a config object
34997 Roo.NestedLayoutPanel = function(layout, config)
34999 // construct with only one argument..
35000 /* FIXME - implement nicer consturctors
35001 if (layout.layout) {
35003 layout = config.layout;
35004 delete config.layout;
35006 if (layout.xtype && !layout.getEl) {
35007 // then layout needs constructing..
35008 layout = Roo.factory(layout, Roo);
35013 Roo.NestedLayoutPanel.superclass.constructor.call(this, layout.getEl(), config);
35015 layout.monitorWindowResize = false; // turn off autosizing
35016 this.layout = layout;
35017 this.layout.getEl().addClass("x-layout-nested-layout");
35024 Roo.extend(Roo.NestedLayoutPanel, Roo.ContentPanel, {
35026 setSize : function(width, height){
35027 if(!this.ignoreResize(width, height)){
35028 var size = this.adjustForComponents(width, height);
35029 var el = this.layout.getEl();
35030 el.setSize(size.width, size.height);
35031 var touch = el.dom.offsetWidth;
35032 this.layout.layout();
35033 // ie requires a double layout on the first pass
35034 if(Roo.isIE && !this.initialized){
35035 this.initialized = true;
35036 this.layout.layout();
35041 // activate all subpanels if not currently active..
35043 setActiveState : function(active){
35044 this.active = active;
35046 this.fireEvent("deactivate", this);
35050 this.fireEvent("activate", this);
35051 // not sure if this should happen before or after..
35052 if (!this.layout) {
35053 return; // should not happen..
35056 for (var r in this.layout.regions) {
35057 reg = this.layout.getRegion(r);
35058 if (reg.getActivePanel()) {
35059 //reg.showPanel(reg.getActivePanel()); // force it to activate..
35060 reg.setActivePanel(reg.getActivePanel());
35063 if (!reg.panels.length) {
35066 reg.showPanel(reg.getPanel(0));
35075 * Returns the nested BorderLayout for this panel
35076 * @return {Roo.BorderLayout}
35078 getLayout : function(){
35079 return this.layout;
35083 * Adds a xtype elements to the layout of the nested panel
35087 xtype : 'ContentPanel',
35094 xtype : 'NestedLayoutPanel',
35100 items : [ ... list of content panels or nested layout panels.. ]
35104 * @param {Object} cfg Xtype definition of item to add.
35106 addxtype : function(cfg) {
35107 return this.layout.addxtype(cfg);
35112 Roo.ScrollPanel = function(el, config, content){
35113 config = config || {};
35114 config.fitToFrame = true;
35115 Roo.ScrollPanel.superclass.constructor.call(this, el, config, content);
35117 this.el.dom.style.overflow = "hidden";
35118 var wrap = this.el.wrap({cls: "x-scroller x-layout-inactive-content"});
35119 this.el.removeClass("x-layout-inactive-content");
35120 this.el.on("mousewheel", this.onWheel, this);
35122 var up = wrap.createChild({cls: "x-scroller-up", html: " "}, this.el.dom);
35123 var down = wrap.createChild({cls: "x-scroller-down", html: " "});
35124 up.unselectable(); down.unselectable();
35125 up.on("click", this.scrollUp, this);
35126 down.on("click", this.scrollDown, this);
35127 up.addClassOnOver("x-scroller-btn-over");
35128 down.addClassOnOver("x-scroller-btn-over");
35129 up.addClassOnClick("x-scroller-btn-click");
35130 down.addClassOnClick("x-scroller-btn-click");
35131 this.adjustments = [0, -(up.getHeight() + down.getHeight())];
35133 this.resizeEl = this.el;
35134 this.el = wrap; this.up = up; this.down = down;
35137 Roo.extend(Roo.ScrollPanel, Roo.ContentPanel, {
35139 wheelIncrement : 5,
35140 scrollUp : function(){
35141 this.resizeEl.scroll("up", this.increment, {callback: this.afterScroll, scope: this});
35144 scrollDown : function(){
35145 this.resizeEl.scroll("down", this.increment, {callback: this.afterScroll, scope: this});
35148 afterScroll : function(){
35149 var el = this.resizeEl;
35150 var t = el.dom.scrollTop, h = el.dom.scrollHeight, ch = el.dom.clientHeight;
35151 this.up[t == 0 ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
35152 this.down[h - t <= ch ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
35155 setSize : function(){
35156 Roo.ScrollPanel.superclass.setSize.apply(this, arguments);
35157 this.afterScroll();
35160 onWheel : function(e){
35161 var d = e.getWheelDelta();
35162 this.resizeEl.dom.scrollTop -= (d*this.wheelIncrement);
35163 this.afterScroll();
35167 setContent : function(content, loadScripts){
35168 this.resizeEl.update(content, loadScripts);
35182 * @class Roo.TreePanel
35183 * @extends Roo.ContentPanel
35185 * Create a new TreePanel. - defaults to fit/scoll contents.
35186 * @param {String/Object} config A string to set only the panel's title, or a config object
35187 * @cfg {Roo.tree.TreePanel} tree The tree TreePanel, with config etc.
35189 Roo.TreePanel = function(config){
35190 var el = config.el;
35191 var tree = config.tree;
35192 delete config.tree;
35193 delete config.el; // hopefull!
35195 // wrapper for IE7 strict & safari scroll issue
35197 var treeEl = el.createChild();
35198 config.resizeEl = treeEl;
35202 Roo.TreePanel.superclass.constructor.call(this, el, config);
35205 this.tree = new Roo.tree.TreePanel(treeEl , tree);
35206 //console.log(tree);
35207 this.on('activate', function()
35209 if (this.tree.rendered) {
35212 //console.log('render tree');
35213 this.tree.render();
35215 // this should not be needed.. - it's actually the 'el' that resizes?
35216 // actuall it breaks the containerScroll - dragging nodes auto scroll at top
35218 //this.on('resize', function (cp, w, h) {
35219 // this.tree.innerCt.setWidth(w);
35220 // this.tree.innerCt.setHeight(h);
35221 // //this.tree.innerCt.setStyle('overflow-y', 'auto');
35228 Roo.extend(Roo.TreePanel, Roo.ContentPanel, {
35245 * Ext JS Library 1.1.1
35246 * Copyright(c) 2006-2007, Ext JS, LLC.
35248 * Originally Released Under LGPL - original licence link has changed is not relivant.
35251 * <script type="text/javascript">
35256 * @class Roo.ReaderLayout
35257 * @extends Roo.BorderLayout
35258 * This is a pre-built layout that represents a classic, 5-pane application. It consists of a header, a primary
35259 * center region containing two nested regions (a top one for a list view and one for item preview below),
35260 * and regions on either side that can be used for navigation, application commands, informational displays, etc.
35261 * The setup and configuration work exactly the same as it does for a {@link Roo.BorderLayout} - this class simply
35262 * expedites the setup of the overall layout and regions for this common application style.
35265 var reader = new Roo.ReaderLayout();
35266 var CP = Roo.ContentPanel; // shortcut for adding
35268 reader.beginUpdate();
35269 reader.add("north", new CP("north", "North"));
35270 reader.add("west", new CP("west", {title: "West"}));
35271 reader.add("east", new CP("east", {title: "East"}));
35273 reader.regions.listView.add(new CP("listView", "List"));
35274 reader.regions.preview.add(new CP("preview", "Preview"));
35275 reader.endUpdate();
35278 * Create a new ReaderLayout
35279 * @param {Object} config Configuration options
35280 * @param {String/HTMLElement/Element} container (optional) The container this layout is bound to (defaults to
35281 * document.body if omitted)
35283 Roo.ReaderLayout = function(config, renderTo){
35284 var c = config || {size:{}};
35285 Roo.ReaderLayout.superclass.constructor.call(this, renderTo || document.body, {
35286 north: c.north !== false ? Roo.apply({
35290 }, c.north) : false,
35291 west: c.west !== false ? Roo.apply({
35299 margins:{left:5,right:0,bottom:5,top:5},
35300 cmargins:{left:5,right:5,bottom:5,top:5}
35301 }, c.west) : false,
35302 east: c.east !== false ? Roo.apply({
35310 margins:{left:0,right:5,bottom:5,top:5},
35311 cmargins:{left:5,right:5,bottom:5,top:5}
35312 }, c.east) : false,
35313 center: Roo.apply({
35314 tabPosition: 'top',
35318 margins:{left:c.west!==false ? 0 : 5,right:c.east!==false ? 0 : 5,bottom:5,top:2}
35322 this.el.addClass('x-reader');
35324 this.beginUpdate();
35326 var inner = new Roo.BorderLayout(Roo.get(document.body).createChild(), {
35327 south: c.preview !== false ? Roo.apply({
35334 cmargins:{top:5,left:0, right:0, bottom:0}
35335 }, c.preview) : false,
35336 center: Roo.apply({
35342 this.add('center', new Roo.NestedLayoutPanel(inner,
35343 Roo.apply({title: c.mainTitle || '',tabTip:''},c.innerPanelCfg)));
35347 this.regions.preview = inner.getRegion('south');
35348 this.regions.listView = inner.getRegion('center');
35351 Roo.extend(Roo.ReaderLayout, Roo.BorderLayout);/*
35353 * Ext JS Library 1.1.1
35354 * Copyright(c) 2006-2007, Ext JS, LLC.
35356 * Originally Released Under LGPL - original licence link has changed is not relivant.
35359 * <script type="text/javascript">
35363 * @class Roo.grid.Grid
35364 * @extends Roo.util.Observable
35365 * This class represents the primary interface of a component based grid control.
35366 * <br><br>Usage:<pre><code>
35367 var grid = new Roo.grid.Grid("my-container-id", {
35370 selModel: mySelectionModel,
35371 autoSizeColumns: true,
35372 monitorWindowResize: false,
35373 trackMouseOver: true
35378 * <b>Common Problems:</b><br/>
35379 * - Grid does not resize properly when going smaller: Setting overflow hidden on the container
35380 * element will correct this<br/>
35381 * - If you get el.style[camel]= NaNpx or -2px or something related, be certain you have given your container element
35382 * dimensions. The grid adapts to your container's size, if your container has no size defined then the results
35383 * are unpredictable.<br/>
35384 * - Do not render the grid into an element with display:none. Try using visibility:hidden. Otherwise there is no way for the
35385 * grid to calculate dimensions/offsets.<br/>
35387 * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
35388 * The container MUST have some type of size defined for the grid to fill. The container will be
35389 * automatically set to position relative if it isn't already.
35390 * @param {Object} config A config object that sets properties on this grid.
35392 Roo.grid.Grid = function(container, config){
35393 // initialize the container
35394 this.container = Roo.get(container);
35395 this.container.update("");
35396 this.container.setStyle("overflow", "hidden");
35397 this.container.addClass('x-grid-container');
35399 this.id = this.container.id;
35401 Roo.apply(this, config);
35402 // check and correct shorthanded configs
35404 this.dataSource = this.ds;
35408 this.colModel = this.cm;
35412 this.selModel = this.sm;
35416 if (this.selModel) {
35417 this.selModel = Roo.factory(this.selModel, Roo.grid);
35418 this.sm = this.selModel;
35419 this.sm.xmodule = this.xmodule || false;
35421 if (typeof(this.colModel.config) == 'undefined') {
35422 this.colModel = new Roo.grid.ColumnModel(this.colModel);
35423 this.cm = this.colModel;
35424 this.cm.xmodule = this.xmodule || false;
35426 if (this.dataSource) {
35427 this.dataSource= Roo.factory(this.dataSource, Roo.data);
35428 this.ds = this.dataSource;
35429 this.ds.xmodule = this.xmodule || false;
35436 this.container.setWidth(this.width);
35440 this.container.setHeight(this.height);
35447 * The raw click event for the entire grid.
35448 * @param {Roo.EventObject} e
35453 * The raw dblclick event for the entire grid.
35454 * @param {Roo.EventObject} e
35458 * @event contextmenu
35459 * The raw contextmenu event for the entire grid.
35460 * @param {Roo.EventObject} e
35462 "contextmenu" : true,
35465 * The raw mousedown event for the entire grid.
35466 * @param {Roo.EventObject} e
35468 "mousedown" : true,
35471 * The raw mouseup event for the entire grid.
35472 * @param {Roo.EventObject} e
35477 * The raw mouseover event for the entire grid.
35478 * @param {Roo.EventObject} e
35480 "mouseover" : true,
35483 * The raw mouseout event for the entire grid.
35484 * @param {Roo.EventObject} e
35489 * The raw keypress event for the entire grid.
35490 * @param {Roo.EventObject} e
35495 * The raw keydown event for the entire grid.
35496 * @param {Roo.EventObject} e
35504 * Fires when a cell is clicked
35505 * @param {Grid} this
35506 * @param {Number} rowIndex
35507 * @param {Number} columnIndex
35508 * @param {Roo.EventObject} e
35510 "cellclick" : true,
35512 * @event celldblclick
35513 * Fires when a cell is double clicked
35514 * @param {Grid} this
35515 * @param {Number} rowIndex
35516 * @param {Number} columnIndex
35517 * @param {Roo.EventObject} e
35519 "celldblclick" : true,
35522 * Fires when a row is clicked
35523 * @param {Grid} this
35524 * @param {Number} rowIndex
35525 * @param {Roo.EventObject} e
35529 * @event rowdblclick
35530 * Fires when a row is double clicked
35531 * @param {Grid} this
35532 * @param {Number} rowIndex
35533 * @param {Roo.EventObject} e
35535 "rowdblclick" : true,
35537 * @event headerclick
35538 * Fires when a header is clicked
35539 * @param {Grid} this
35540 * @param {Number} columnIndex
35541 * @param {Roo.EventObject} e
35543 "headerclick" : true,
35545 * @event headerdblclick
35546 * Fires when a header cell is double clicked
35547 * @param {Grid} this
35548 * @param {Number} columnIndex
35549 * @param {Roo.EventObject} e
35551 "headerdblclick" : true,
35553 * @event rowcontextmenu
35554 * Fires when a row is right clicked
35555 * @param {Grid} this
35556 * @param {Number} rowIndex
35557 * @param {Roo.EventObject} e
35559 "rowcontextmenu" : true,
35561 * @event cellcontextmenu
35562 * Fires when a cell is right clicked
35563 * @param {Grid} this
35564 * @param {Number} rowIndex
35565 * @param {Number} cellIndex
35566 * @param {Roo.EventObject} e
35568 "cellcontextmenu" : true,
35570 * @event headercontextmenu
35571 * Fires when a header is right clicked
35572 * @param {Grid} this
35573 * @param {Number} columnIndex
35574 * @param {Roo.EventObject} e
35576 "headercontextmenu" : true,
35578 * @event bodyscroll
35579 * Fires when the body element is scrolled
35580 * @param {Number} scrollLeft
35581 * @param {Number} scrollTop
35583 "bodyscroll" : true,
35585 * @event columnresize
35586 * Fires when the user resizes a column
35587 * @param {Number} columnIndex
35588 * @param {Number} newSize
35590 "columnresize" : true,
35592 * @event columnmove
35593 * Fires when the user moves a column
35594 * @param {Number} oldIndex
35595 * @param {Number} newIndex
35597 "columnmove" : true,
35600 * Fires when row(s) start being dragged
35601 * @param {Grid} this
35602 * @param {Roo.GridDD} dd The drag drop object
35603 * @param {event} e The raw browser event
35605 "startdrag" : true,
35608 * Fires when a drag operation is complete
35609 * @param {Grid} this
35610 * @param {Roo.GridDD} dd The drag drop object
35611 * @param {event} e The raw browser event
35616 * Fires when dragged row(s) are dropped on a valid DD target
35617 * @param {Grid} this
35618 * @param {Roo.GridDD} dd The drag drop object
35619 * @param {String} targetId The target drag drop object
35620 * @param {event} e The raw browser event
35625 * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
35626 * @param {Grid} this
35627 * @param {Roo.GridDD} dd The drag drop object
35628 * @param {String} targetId The target drag drop object
35629 * @param {event} e The raw browser event
35634 * Fires when the dragged row(s) first cross another DD target while being dragged
35635 * @param {Grid} this
35636 * @param {Roo.GridDD} dd The drag drop object
35637 * @param {String} targetId The target drag drop object
35638 * @param {event} e The raw browser event
35640 "dragenter" : true,
35643 * Fires when the dragged row(s) leave another DD target while being dragged
35644 * @param {Grid} this
35645 * @param {Roo.GridDD} dd The drag drop object
35646 * @param {String} targetId The target drag drop object
35647 * @param {event} e The raw browser event
35652 * Fires when a row is rendered, so you can change add a style to it.
35653 * @param {GridView} gridview The grid view
35654 * @param {Object} rowcfg contains record rowIndex and rowClass - set rowClass to add a style.
35660 * Fires when the grid is rendered
35661 * @param {Grid} grid
35666 Roo.grid.Grid.superclass.constructor.call(this);
35668 Roo.extend(Roo.grid.Grid, Roo.util.Observable, {
35671 * @cfg {String} ddGroup - drag drop group.
35675 * @cfg {Number} minColumnWidth The minimum width a column can be resized to. Default is 25.
35677 minColumnWidth : 25,
35680 * @cfg {Boolean} autoSizeColumns True to automatically resize the columns to fit their content
35681 * <b>on initial render.</b> It is more efficient to explicitly size the columns
35682 * through the ColumnModel's {@link Roo.grid.ColumnModel#width} config option. Default is false.
35684 autoSizeColumns : false,
35687 * @cfg {Boolean} autoSizeHeaders True to measure headers with column data when auto sizing columns. Default is true.
35689 autoSizeHeaders : true,
35692 * @cfg {Boolean} monitorWindowResize True to autoSize the grid when the window resizes. Default is true.
35694 monitorWindowResize : true,
35697 * @cfg {Boolean} maxRowsToMeasure If autoSizeColumns is on, maxRowsToMeasure can be used to limit the number of
35698 * rows measured to get a columns size. Default is 0 (all rows).
35700 maxRowsToMeasure : 0,
35703 * @cfg {Boolean} trackMouseOver True to highlight rows when the mouse is over. Default is true.
35705 trackMouseOver : true,
35708 * @cfg {Boolean} enableDrag True to enable drag of rows. Default is false. (double check if this is needed?)
35712 * @cfg {Boolean} enableDragDrop True to enable drag and drop of rows. Default is false.
35714 enableDragDrop : false,
35717 * @cfg {Boolean} enableColumnMove True to enable drag and drop reorder of columns. Default is true.
35719 enableColumnMove : true,
35722 * @cfg {Boolean} enableColumnHide True to enable hiding of columns with the header context menu. Default is true.
35724 enableColumnHide : true,
35727 * @cfg {Boolean} enableRowHeightSync True to manually sync row heights across locked and not locked rows. Default is false.
35729 enableRowHeightSync : false,
35732 * @cfg {Boolean} stripeRows True to stripe the rows. Default is true.
35737 * @cfg {Boolean} autoHeight True to fit the height of the grid container to the height of the data. Default is false.
35739 autoHeight : false,
35742 * @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.
35744 autoExpandColumn : false,
35747 * @cfg {Number} autoExpandMin The minimum width the autoExpandColumn can have (if enabled).
35750 autoExpandMin : 50,
35753 * @cfg {Number} autoExpandMax The maximum width the autoExpandColumn can have (if enabled). Default is 1000.
35755 autoExpandMax : 1000,
35758 * @cfg {Object} view The {@link Roo.grid.GridView} used by the grid. This can be set before a call to render().
35763 * @cfg {Object} loadMask An {@link Roo.LoadMask} config or true to mask the grid while loading. Default is false.
35767 * @cfg {Roo.dd.DropTarget} dropTarget An {@link Roo.dd.DropTarget} config
35777 * @cfg {Boolean} autoWidth True to set the grid's width to the default total width of the grid's columns instead
35778 * of a fixed width. Default is false.
35781 * @cfg {Number} maxHeight Sets the maximum height of the grid - ignored if autoHeight is not on.
35784 * Called once after all setup has been completed and the grid is ready to be rendered.
35785 * @return {Roo.grid.Grid} this
35787 render : function()
35789 var c = this.container;
35790 // try to detect autoHeight/width mode
35791 if((!c.dom.offsetHeight || c.dom.offsetHeight < 20) || c.getStyle("height") == "auto"){
35792 this.autoHeight = true;
35794 var view = this.getView();
35797 c.on("click", this.onClick, this);
35798 c.on("dblclick", this.onDblClick, this);
35799 c.on("contextmenu", this.onContextMenu, this);
35800 c.on("keydown", this.onKeyDown, this);
35802 c.on("touchstart", this.onTouchStart, this);
35805 this.relayEvents(c, ["mousedown","mouseup","mouseover","mouseout","keypress"]);
35807 this.getSelectionModel().init(this);
35812 this.loadMask = new Roo.LoadMask(this.container,
35813 Roo.apply({store:this.dataSource}, this.loadMask));
35817 if (this.toolbar && this.toolbar.xtype) {
35818 this.toolbar.container = this.getView().getHeaderPanel(true);
35819 this.toolbar = new Roo.Toolbar(this.toolbar);
35821 if (this.footer && this.footer.xtype) {
35822 this.footer.dataSource = this.getDataSource();
35823 this.footer.container = this.getView().getFooterPanel(true);
35824 this.footer = Roo.factory(this.footer, Roo);
35826 if (this.dropTarget && this.dropTarget.xtype) {
35827 delete this.dropTarget.xtype;
35828 this.dropTarget = new Roo.dd.DropTarget(this.getView().mainBody, this.dropTarget);
35832 this.rendered = true;
35833 this.fireEvent('render', this);
35838 * Reconfigures the grid to use a different Store and Column Model.
35839 * The View will be bound to the new objects and refreshed.
35840 * @param {Roo.data.Store} dataSource The new {@link Roo.data.Store} object
35841 * @param {Roo.grid.ColumnModel} The new {@link Roo.grid.ColumnModel} object
35843 reconfigure : function(dataSource, colModel){
35845 this.loadMask.destroy();
35846 this.loadMask = new Roo.LoadMask(this.container,
35847 Roo.apply({store:dataSource}, this.loadMask));
35849 this.view.bind(dataSource, colModel);
35850 this.dataSource = dataSource;
35851 this.colModel = colModel;
35852 this.view.refresh(true);
35856 onKeyDown : function(e){
35857 this.fireEvent("keydown", e);
35861 * Destroy this grid.
35862 * @param {Boolean} removeEl True to remove the element
35864 destroy : function(removeEl, keepListeners){
35866 this.loadMask.destroy();
35868 var c = this.container;
35869 c.removeAllListeners();
35870 this.view.destroy();
35871 this.colModel.purgeListeners();
35872 if(!keepListeners){
35873 this.purgeListeners();
35876 if(removeEl === true){
35882 processEvent : function(name, e){
35883 // does this fire select???
35884 Roo.log('grid:processEvent ' + name);
35886 if (name != 'touchstart' ) {
35887 this.fireEvent(name, e);
35890 var t = e.getTarget();
35892 var header = v.findHeaderIndex(t);
35893 if(header !== false){
35894 this.fireEvent("header" + (name == 'touchstart' ? 'click' : name), this, header, e);
35896 var row = v.findRowIndex(t);
35897 var cell = v.findCellIndex(t);
35898 if (name == 'touchstart') {
35899 // first touch is always a click.
35900 // hopefull this happens after selection is updated.?
35903 if (typeof(this.selModel.getSelectedCell) != 'undefined') {
35904 var cs = this.selModel.getSelectedCell();
35905 if (row == cs[0] && cell == cs[1]){
35909 if (typeof(this.selModel.getSelections) != 'undefined') {
35910 var cs = this.selModel.getSelections();
35911 var ds = this.dataSource;
35912 if (cs.length == 1 && ds.getAt(row) == cs[0]){
35923 this.fireEvent("row" + name, this, row, e);
35924 if(cell !== false){
35925 this.fireEvent("cell" + name, this, row, cell, e);
35932 onClick : function(e){
35933 this.processEvent("click", e);
35936 onTouchStart : function(e){
35937 this.processEvent("touchstart", e);
35941 onContextMenu : function(e, t){
35942 this.processEvent("contextmenu", e);
35946 onDblClick : function(e){
35947 this.processEvent("dblclick", e);
35951 walkCells : function(row, col, step, fn, scope){
35952 var cm = this.colModel, clen = cm.getColumnCount();
35953 var ds = this.dataSource, rlen = ds.getCount(), first = true;
35965 if(fn.call(scope || this, row, col, cm) === true){
35983 if(fn.call(scope || this, row, col, cm) === true){
35995 getSelections : function(){
35996 return this.selModel.getSelections();
36000 * Causes the grid to manually recalculate its dimensions. Generally this is done automatically,
36001 * but if manual update is required this method will initiate it.
36003 autoSize : function(){
36005 this.view.layout();
36006 if(this.view.adjustForScroll){
36007 this.view.adjustForScroll();
36013 * Returns the grid's underlying element.
36014 * @return {Element} The element
36016 getGridEl : function(){
36017 return this.container;
36020 // private for compatibility, overridden by editor grid
36021 stopEditing : function(){},
36024 * Returns the grid's SelectionModel.
36025 * @return {SelectionModel}
36027 getSelectionModel : function(){
36028 if(!this.selModel){
36029 this.selModel = new Roo.grid.RowSelectionModel();
36031 return this.selModel;
36035 * Returns the grid's DataSource.
36036 * @return {DataSource}
36038 getDataSource : function(){
36039 return this.dataSource;
36043 * Returns the grid's ColumnModel.
36044 * @return {ColumnModel}
36046 getColumnModel : function(){
36047 return this.colModel;
36051 * Returns the grid's GridView object.
36052 * @return {GridView}
36054 getView : function(){
36056 this.view = new Roo.grid.GridView(this.viewConfig);
36061 * Called to get grid's drag proxy text, by default returns this.ddText.
36064 getDragDropText : function(){
36065 var count = this.selModel.getCount();
36066 return String.format(this.ddText, count, count == 1 ? '' : 's');
36070 * Configures the text is the drag proxy (defaults to "%0 selected row(s)").
36071 * %0 is replaced with the number of selected rows.
36074 Roo.grid.Grid.prototype.ddText = "{0} selected row{1}";/*
36076 * Ext JS Library 1.1.1
36077 * Copyright(c) 2006-2007, Ext JS, LLC.
36079 * Originally Released Under LGPL - original licence link has changed is not relivant.
36082 * <script type="text/javascript">
36085 Roo.grid.AbstractGridView = function(){
36089 "beforerowremoved" : true,
36090 "beforerowsinserted" : true,
36091 "beforerefresh" : true,
36092 "rowremoved" : true,
36093 "rowsinserted" : true,
36094 "rowupdated" : true,
36097 Roo.grid.AbstractGridView.superclass.constructor.call(this);
36100 Roo.extend(Roo.grid.AbstractGridView, Roo.util.Observable, {
36101 rowClass : "x-grid-row",
36102 cellClass : "x-grid-cell",
36103 tdClass : "x-grid-td",
36104 hdClass : "x-grid-hd",
36105 splitClass : "x-grid-hd-split",
36107 init: function(grid){
36109 var cid = this.grid.getGridEl().id;
36110 this.colSelector = "#" + cid + " ." + this.cellClass + "-";
36111 this.tdSelector = "#" + cid + " ." + this.tdClass + "-";
36112 this.hdSelector = "#" + cid + " ." + this.hdClass + "-";
36113 this.splitSelector = "#" + cid + " ." + this.splitClass + "-";
36116 getColumnRenderers : function(){
36117 var renderers = [];
36118 var cm = this.grid.colModel;
36119 var colCount = cm.getColumnCount();
36120 for(var i = 0; i < colCount; i++){
36121 renderers[i] = cm.getRenderer(i);
36126 getColumnIds : function(){
36128 var cm = this.grid.colModel;
36129 var colCount = cm.getColumnCount();
36130 for(var i = 0; i < colCount; i++){
36131 ids[i] = cm.getColumnId(i);
36136 getDataIndexes : function(){
36137 if(!this.indexMap){
36138 this.indexMap = this.buildIndexMap();
36140 return this.indexMap.colToData;
36143 getColumnIndexByDataIndex : function(dataIndex){
36144 if(!this.indexMap){
36145 this.indexMap = this.buildIndexMap();
36147 return this.indexMap.dataToCol[dataIndex];
36151 * Set a css style for a column dynamically.
36152 * @param {Number} colIndex The index of the column
36153 * @param {String} name The css property name
36154 * @param {String} value The css value
36156 setCSSStyle : function(colIndex, name, value){
36157 var selector = "#" + this.grid.id + " .x-grid-col-" + colIndex;
36158 Roo.util.CSS.updateRule(selector, name, value);
36161 generateRules : function(cm){
36162 var ruleBuf = [], rulesId = this.grid.id + '-cssrules';
36163 Roo.util.CSS.removeStyleSheet(rulesId);
36164 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
36165 var cid = cm.getColumnId(i);
36166 ruleBuf.push(this.colSelector, cid, " {\n", cm.config[i].css, "}\n",
36167 this.tdSelector, cid, " {\n}\n",
36168 this.hdSelector, cid, " {\n}\n",
36169 this.splitSelector, cid, " {\n}\n");
36171 return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
36175 * Ext JS Library 1.1.1
36176 * Copyright(c) 2006-2007, Ext JS, LLC.
36178 * Originally Released Under LGPL - original licence link has changed is not relivant.
36181 * <script type="text/javascript">
36185 // This is a support class used internally by the Grid components
36186 Roo.grid.HeaderDragZone = function(grid, hd, hd2){
36188 this.view = grid.getView();
36189 this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
36190 Roo.grid.HeaderDragZone.superclass.constructor.call(this, hd);
36192 this.setHandleElId(Roo.id(hd));
36193 this.setOuterHandleElId(Roo.id(hd2));
36195 this.scroll = false;
36197 Roo.extend(Roo.grid.HeaderDragZone, Roo.dd.DragZone, {
36199 getDragData : function(e){
36200 var t = Roo.lib.Event.getTarget(e);
36201 var h = this.view.findHeaderCell(t);
36203 return {ddel: h.firstChild, header:h};
36208 onInitDrag : function(e){
36209 this.view.headersDisabled = true;
36210 var clone = this.dragData.ddel.cloneNode(true);
36211 clone.id = Roo.id();
36212 clone.style.width = Math.min(this.dragData.header.offsetWidth,this.maxDragWidth) + "px";
36213 this.proxy.update(clone);
36217 afterValidDrop : function(){
36219 setTimeout(function(){
36220 v.headersDisabled = false;
36224 afterInvalidDrop : function(){
36226 setTimeout(function(){
36227 v.headersDisabled = false;
36233 * Ext JS Library 1.1.1
36234 * Copyright(c) 2006-2007, Ext JS, LLC.
36236 * Originally Released Under LGPL - original licence link has changed is not relivant.
36239 * <script type="text/javascript">
36242 // This is a support class used internally by the Grid components
36243 Roo.grid.HeaderDropZone = function(grid, hd, hd2){
36245 this.view = grid.getView();
36246 // split the proxies so they don't interfere with mouse events
36247 this.proxyTop = Roo.DomHelper.append(document.body, {
36248 cls:"col-move-top", html:" "
36250 this.proxyBottom = Roo.DomHelper.append(document.body, {
36251 cls:"col-move-bottom", html:" "
36253 this.proxyTop.hide = this.proxyBottom.hide = function(){
36254 this.setLeftTop(-100,-100);
36255 this.setStyle("visibility", "hidden");
36257 this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
36258 // temporarily disabled
36259 //Roo.dd.ScrollManager.register(this.view.scroller.dom);
36260 Roo.grid.HeaderDropZone.superclass.constructor.call(this, grid.getGridEl().dom);
36262 Roo.extend(Roo.grid.HeaderDropZone, Roo.dd.DropZone, {
36263 proxyOffsets : [-4, -9],
36264 fly: Roo.Element.fly,
36266 getTargetFromEvent : function(e){
36267 var t = Roo.lib.Event.getTarget(e);
36268 var cindex = this.view.findCellIndex(t);
36269 if(cindex !== false){
36270 return this.view.getHeaderCell(cindex);
36275 nextVisible : function(h){
36276 var v = this.view, cm = this.grid.colModel;
36279 if(!cm.isHidden(v.getCellIndex(h))){
36287 prevVisible : function(h){
36288 var v = this.view, cm = this.grid.colModel;
36291 if(!cm.isHidden(v.getCellIndex(h))){
36299 positionIndicator : function(h, n, e){
36300 var x = Roo.lib.Event.getPageX(e);
36301 var r = Roo.lib.Dom.getRegion(n.firstChild);
36302 var px, pt, py = r.top + this.proxyOffsets[1];
36303 if((r.right - x) <= (r.right-r.left)/2){
36304 px = r.right+this.view.borderWidth;
36310 var oldIndex = this.view.getCellIndex(h);
36311 var newIndex = this.view.getCellIndex(n);
36313 if(this.grid.colModel.isFixed(newIndex)){
36317 var locked = this.grid.colModel.isLocked(newIndex);
36322 if(oldIndex < newIndex){
36325 if(oldIndex == newIndex && (locked == this.grid.colModel.isLocked(oldIndex))){
36328 px += this.proxyOffsets[0];
36329 this.proxyTop.setLeftTop(px, py);
36330 this.proxyTop.show();
36331 if(!this.bottomOffset){
36332 this.bottomOffset = this.view.mainHd.getHeight();
36334 this.proxyBottom.setLeftTop(px, py+this.proxyTop.dom.offsetHeight+this.bottomOffset);
36335 this.proxyBottom.show();
36339 onNodeEnter : function(n, dd, e, data){
36340 if(data.header != n){
36341 this.positionIndicator(data.header, n, e);
36345 onNodeOver : function(n, dd, e, data){
36346 var result = false;
36347 if(data.header != n){
36348 result = this.positionIndicator(data.header, n, e);
36351 this.proxyTop.hide();
36352 this.proxyBottom.hide();
36354 return result ? this.dropAllowed : this.dropNotAllowed;
36357 onNodeOut : function(n, dd, e, data){
36358 this.proxyTop.hide();
36359 this.proxyBottom.hide();
36362 onNodeDrop : function(n, dd, e, data){
36363 var h = data.header;
36365 var cm = this.grid.colModel;
36366 var x = Roo.lib.Event.getPageX(e);
36367 var r = Roo.lib.Dom.getRegion(n.firstChild);
36368 var pt = (r.right - x) <= ((r.right-r.left)/2) ? "after" : "before";
36369 var oldIndex = this.view.getCellIndex(h);
36370 var newIndex = this.view.getCellIndex(n);
36371 var locked = cm.isLocked(newIndex);
36375 if(oldIndex < newIndex){
36378 if(oldIndex == newIndex && (locked == cm.isLocked(oldIndex))){
36381 cm.setLocked(oldIndex, locked, true);
36382 cm.moveColumn(oldIndex, newIndex);
36383 this.grid.fireEvent("columnmove", oldIndex, newIndex);
36391 * Ext JS Library 1.1.1
36392 * Copyright(c) 2006-2007, Ext JS, LLC.
36394 * Originally Released Under LGPL - original licence link has changed is not relivant.
36397 * <script type="text/javascript">
36401 * @class Roo.grid.GridView
36402 * @extends Roo.util.Observable
36405 * @param {Object} config
36407 Roo.grid.GridView = function(config){
36408 Roo.grid.GridView.superclass.constructor.call(this);
36411 Roo.apply(this, config);
36414 Roo.extend(Roo.grid.GridView, Roo.grid.AbstractGridView, {
36416 unselectable : 'unselectable="on"',
36417 unselectableCls : 'x-unselectable',
36420 rowClass : "x-grid-row",
36422 cellClass : "x-grid-col",
36424 tdClass : "x-grid-td",
36426 hdClass : "x-grid-hd",
36428 splitClass : "x-grid-split",
36430 sortClasses : ["sort-asc", "sort-desc"],
36432 enableMoveAnim : false,
36436 dh : Roo.DomHelper,
36438 fly : Roo.Element.fly,
36440 css : Roo.util.CSS,
36446 scrollIncrement : 22,
36448 cellRE: /(?:.*?)x-grid-(?:hd|cell|csplit)-(?:[\d]+)-([\d]+)(?:.*?)/,
36450 findRE: /\s?(?:x-grid-hd|x-grid-col|x-grid-csplit)\s/,
36452 bind : function(ds, cm){
36454 this.ds.un("load", this.onLoad, this);
36455 this.ds.un("datachanged", this.onDataChange, this);
36456 this.ds.un("add", this.onAdd, this);
36457 this.ds.un("remove", this.onRemove, this);
36458 this.ds.un("update", this.onUpdate, this);
36459 this.ds.un("clear", this.onClear, this);
36462 ds.on("load", this.onLoad, this);
36463 ds.on("datachanged", this.onDataChange, this);
36464 ds.on("add", this.onAdd, this);
36465 ds.on("remove", this.onRemove, this);
36466 ds.on("update", this.onUpdate, this);
36467 ds.on("clear", this.onClear, this);
36472 this.cm.un("widthchange", this.onColWidthChange, this);
36473 this.cm.un("headerchange", this.onHeaderChange, this);
36474 this.cm.un("hiddenchange", this.onHiddenChange, this);
36475 this.cm.un("columnmoved", this.onColumnMove, this);
36476 this.cm.un("columnlockchange", this.onColumnLock, this);
36479 this.generateRules(cm);
36480 cm.on("widthchange", this.onColWidthChange, this);
36481 cm.on("headerchange", this.onHeaderChange, this);
36482 cm.on("hiddenchange", this.onHiddenChange, this);
36483 cm.on("columnmoved", this.onColumnMove, this);
36484 cm.on("columnlockchange", this.onColumnLock, this);
36489 init: function(grid){
36490 Roo.grid.GridView.superclass.init.call(this, grid);
36492 this.bind(grid.dataSource, grid.colModel);
36494 grid.on("headerclick", this.handleHeaderClick, this);
36496 if(grid.trackMouseOver){
36497 grid.on("mouseover", this.onRowOver, this);
36498 grid.on("mouseout", this.onRowOut, this);
36500 grid.cancelTextSelection = function(){};
36501 this.gridId = grid.id;
36503 var tpls = this.templates || {};
36506 tpls.master = new Roo.Template(
36507 '<div class="x-grid" hidefocus="true">',
36508 '<a href="#" class="x-grid-focus" tabIndex="-1"></a>',
36509 '<div class="x-grid-topbar"></div>',
36510 '<div class="x-grid-scroller"><div></div></div>',
36511 '<div class="x-grid-locked">',
36512 '<div class="x-grid-header">{lockedHeader}</div>',
36513 '<div class="x-grid-body">{lockedBody}</div>',
36515 '<div class="x-grid-viewport">',
36516 '<div class="x-grid-header">{header}</div>',
36517 '<div class="x-grid-body">{body}</div>',
36519 '<div class="x-grid-bottombar"></div>',
36521 '<div class="x-grid-resize-proxy"> </div>',
36524 tpls.master.disableformats = true;
36528 tpls.header = new Roo.Template(
36529 '<table border="0" cellspacing="0" cellpadding="0">',
36530 '<tbody><tr class="x-grid-hd-row">{cells}</tr></tbody>',
36533 tpls.header.disableformats = true;
36535 tpls.header.compile();
36538 tpls.hcell = new Roo.Template(
36539 '<td class="x-grid-hd x-grid-td-{id} {cellId}"><div title="{title}" class="x-grid-hd-inner x-grid-hd-{id}">',
36540 '<div class="x-grid-hd-text ' + this.unselectableCls + '" ' + this.unselectable +'>{value}<img class="x-grid-sort-icon" src="', Roo.BLANK_IMAGE_URL, '" /></div>',
36543 tpls.hcell.disableFormats = true;
36545 tpls.hcell.compile();
36548 tpls.hsplit = new Roo.Template('<div class="x-grid-split {splitId} x-grid-split-{id}" style="{style} ' +
36549 this.unselectableCls + '" ' + this.unselectable +'> </div>');
36550 tpls.hsplit.disableFormats = true;
36552 tpls.hsplit.compile();
36555 tpls.body = new Roo.Template(
36556 '<table border="0" cellspacing="0" cellpadding="0">',
36557 "<tbody>{rows}</tbody>",
36560 tpls.body.disableFormats = true;
36562 tpls.body.compile();
36565 tpls.row = new Roo.Template('<tr class="x-grid-row {alt}">{cells}</tr>');
36566 tpls.row.disableFormats = true;
36568 tpls.row.compile();
36571 tpls.cell = new Roo.Template(
36572 '<td class="x-grid-col x-grid-td-{id} {cellId} {css}" tabIndex="0">',
36573 '<div class="x-grid-col-{id} x-grid-cell-inner"><div class="x-grid-cell-text ' +
36574 this.unselectableCls + '" ' + this.unselectable +'" {attr}>{value}</div></div>',
36577 tpls.cell.disableFormats = true;
36579 tpls.cell.compile();
36581 this.templates = tpls;
36584 // remap these for backwards compat
36585 onColWidthChange : function(){
36586 this.updateColumns.apply(this, arguments);
36588 onHeaderChange : function(){
36589 this.updateHeaders.apply(this, arguments);
36591 onHiddenChange : function(){
36592 this.handleHiddenChange.apply(this, arguments);
36594 onColumnMove : function(){
36595 this.handleColumnMove.apply(this, arguments);
36597 onColumnLock : function(){
36598 this.handleLockChange.apply(this, arguments);
36601 onDataChange : function(){
36603 this.updateHeaderSortState();
36606 onClear : function(){
36610 onUpdate : function(ds, record){
36611 this.refreshRow(record);
36614 refreshRow : function(record){
36615 var ds = this.ds, index;
36616 if(typeof record == 'number'){
36618 record = ds.getAt(index);
36620 index = ds.indexOf(record);
36622 this.insertRows(ds, index, index, true);
36623 this.onRemove(ds, record, index+1, true);
36624 this.syncRowHeights(index, index);
36626 this.fireEvent("rowupdated", this, index, record);
36629 onAdd : function(ds, records, index){
36630 this.insertRows(ds, index, index + (records.length-1));
36633 onRemove : function(ds, record, index, isUpdate){
36634 if(isUpdate !== true){
36635 this.fireEvent("beforerowremoved", this, index, record);
36637 var bt = this.getBodyTable(), lt = this.getLockedTable();
36638 if(bt.rows[index]){
36639 bt.firstChild.removeChild(bt.rows[index]);
36641 if(lt.rows[index]){
36642 lt.firstChild.removeChild(lt.rows[index]);
36644 if(isUpdate !== true){
36645 this.stripeRows(index);
36646 this.syncRowHeights(index, index);
36648 this.fireEvent("rowremoved", this, index, record);
36652 onLoad : function(){
36653 this.scrollToTop();
36657 * Scrolls the grid to the top
36659 scrollToTop : function(){
36661 this.scroller.dom.scrollTop = 0;
36667 * Gets a panel in the header of the grid that can be used for toolbars etc.
36668 * After modifying the contents of this panel a call to grid.autoSize() may be
36669 * required to register any changes in size.
36670 * @param {Boolean} doShow By default the header is hidden. Pass true to show the panel
36671 * @return Roo.Element
36673 getHeaderPanel : function(doShow){
36675 this.headerPanel.show();
36677 return this.headerPanel;
36681 * Gets a panel in the footer of the grid that can be used for toolbars etc.
36682 * After modifying the contents of this panel a call to grid.autoSize() may be
36683 * required to register any changes in size.
36684 * @param {Boolean} doShow By default the footer is hidden. Pass true to show the panel
36685 * @return Roo.Element
36687 getFooterPanel : function(doShow){
36689 this.footerPanel.show();
36691 return this.footerPanel;
36694 initElements : function(){
36695 var E = Roo.Element;
36696 var el = this.grid.getGridEl().dom.firstChild;
36697 var cs = el.childNodes;
36699 this.el = new E(el);
36701 this.focusEl = new E(el.firstChild);
36702 this.focusEl.swallowEvent("click", true);
36704 this.headerPanel = new E(cs[1]);
36705 this.headerPanel.enableDisplayMode("block");
36707 this.scroller = new E(cs[2]);
36708 this.scrollSizer = new E(this.scroller.dom.firstChild);
36710 this.lockedWrap = new E(cs[3]);
36711 this.lockedHd = new E(this.lockedWrap.dom.firstChild);
36712 this.lockedBody = new E(this.lockedWrap.dom.childNodes[1]);
36714 this.mainWrap = new E(cs[4]);
36715 this.mainHd = new E(this.mainWrap.dom.firstChild);
36716 this.mainBody = new E(this.mainWrap.dom.childNodes[1]);
36718 this.footerPanel = new E(cs[5]);
36719 this.footerPanel.enableDisplayMode("block");
36721 this.resizeProxy = new E(cs[6]);
36723 this.headerSelector = String.format(
36724 '#{0} td.x-grid-hd, #{1} td.x-grid-hd',
36725 this.lockedHd.id, this.mainHd.id
36728 this.splitterSelector = String.format(
36729 '#{0} div.x-grid-split, #{1} div.x-grid-split',
36730 this.idToCssName(this.lockedHd.id), this.idToCssName(this.mainHd.id)
36733 idToCssName : function(s)
36735 return s.replace(/[^a-z0-9]+/ig, '-');
36738 getHeaderCell : function(index){
36739 return Roo.DomQuery.select(this.headerSelector)[index];
36742 getHeaderCellMeasure : function(index){
36743 return this.getHeaderCell(index).firstChild;
36746 getHeaderCellText : function(index){
36747 return this.getHeaderCell(index).firstChild.firstChild;
36750 getLockedTable : function(){
36751 return this.lockedBody.dom.firstChild;
36754 getBodyTable : function(){
36755 return this.mainBody.dom.firstChild;
36758 getLockedRow : function(index){
36759 return this.getLockedTable().rows[index];
36762 getRow : function(index){
36763 return this.getBodyTable().rows[index];
36766 getRowComposite : function(index){
36768 this.rowEl = new Roo.CompositeElementLite();
36770 var els = [], lrow, mrow;
36771 if(lrow = this.getLockedRow(index)){
36774 if(mrow = this.getRow(index)){
36777 this.rowEl.elements = els;
36781 * Gets the 'td' of the cell
36783 * @param {Integer} rowIndex row to select
36784 * @param {Integer} colIndex column to select
36788 getCell : function(rowIndex, colIndex){
36789 var locked = this.cm.getLockedCount();
36791 if(colIndex < locked){
36792 source = this.lockedBody.dom.firstChild;
36794 source = this.mainBody.dom.firstChild;
36795 colIndex -= locked;
36797 return source.rows[rowIndex].childNodes[colIndex];
36800 getCellText : function(rowIndex, colIndex){
36801 return this.getCell(rowIndex, colIndex).firstChild.firstChild;
36804 getCellBox : function(cell){
36805 var b = this.fly(cell).getBox();
36806 if(Roo.isOpera){ // opera fails to report the Y
36807 b.y = cell.offsetTop + this.mainBody.getY();
36812 getCellIndex : function(cell){
36813 var id = String(cell.className).match(this.cellRE);
36815 return parseInt(id[1], 10);
36820 findHeaderIndex : function(n){
36821 var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
36822 return r ? this.getCellIndex(r) : false;
36825 findHeaderCell : function(n){
36826 var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
36827 return r ? r : false;
36830 findRowIndex : function(n){
36834 var r = Roo.fly(n).findParent("tr." + this.rowClass, 6);
36835 return r ? r.rowIndex : false;
36838 findCellIndex : function(node){
36839 var stop = this.el.dom;
36840 while(node && node != stop){
36841 if(this.findRE.test(node.className)){
36842 return this.getCellIndex(node);
36844 node = node.parentNode;
36849 getColumnId : function(index){
36850 return this.cm.getColumnId(index);
36853 getSplitters : function()
36855 if(this.splitterSelector){
36856 return Roo.DomQuery.select(this.splitterSelector);
36862 getSplitter : function(index){
36863 return this.getSplitters()[index];
36866 onRowOver : function(e, t){
36868 if((row = this.findRowIndex(t)) !== false){
36869 this.getRowComposite(row).addClass("x-grid-row-over");
36873 onRowOut : function(e, t){
36875 if((row = this.findRowIndex(t)) !== false && row !== this.findRowIndex(e.getRelatedTarget())){
36876 this.getRowComposite(row).removeClass("x-grid-row-over");
36880 renderHeaders : function(){
36882 var ct = this.templates.hcell, ht = this.templates.header, st = this.templates.hsplit;
36883 var cb = [], lb = [], sb = [], lsb = [], p = {};
36884 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
36885 p.cellId = "x-grid-hd-0-" + i;
36886 p.splitId = "x-grid-csplit-0-" + i;
36887 p.id = cm.getColumnId(i);
36888 p.title = cm.getColumnTooltip(i) || "";
36889 p.value = cm.getColumnHeader(i) || "";
36890 p.style = (this.grid.enableColumnResize === false || !cm.isResizable(i) || cm.isFixed(i)) ? 'cursor:default' : '';
36891 if(!cm.isLocked(i)){
36892 cb[cb.length] = ct.apply(p);
36893 sb[sb.length] = st.apply(p);
36895 lb[lb.length] = ct.apply(p);
36896 lsb[lsb.length] = st.apply(p);
36899 return [ht.apply({cells: lb.join(""), splits:lsb.join("")}),
36900 ht.apply({cells: cb.join(""), splits:sb.join("")})];
36903 updateHeaders : function(){
36904 var html = this.renderHeaders();
36905 this.lockedHd.update(html[0]);
36906 this.mainHd.update(html[1]);
36910 * Focuses the specified row.
36911 * @param {Number} row The row index
36913 focusRow : function(row)
36915 //Roo.log('GridView.focusRow');
36916 var x = this.scroller.dom.scrollLeft;
36917 this.focusCell(row, 0, false);
36918 this.scroller.dom.scrollLeft = x;
36922 * Focuses the specified cell.
36923 * @param {Number} row The row index
36924 * @param {Number} col The column index
36925 * @param {Boolean} hscroll false to disable horizontal scrolling
36927 focusCell : function(row, col, hscroll)
36929 //Roo.log('GridView.focusCell');
36930 var el = this.ensureVisible(row, col, hscroll);
36931 this.focusEl.alignTo(el, "tl-tl");
36933 this.focusEl.focus();
36935 this.focusEl.focus.defer(1, this.focusEl);
36940 * Scrolls the specified cell into view
36941 * @param {Number} row The row index
36942 * @param {Number} col The column index
36943 * @param {Boolean} hscroll false to disable horizontal scrolling
36945 ensureVisible : function(row, col, hscroll)
36947 //Roo.log('GridView.ensureVisible,' + row + ',' + col);
36948 //return null; //disable for testing.
36949 if(typeof row != "number"){
36950 row = row.rowIndex;
36952 if(row < 0 && row >= this.ds.getCount()){
36955 col = (col !== undefined ? col : 0);
36956 var cm = this.grid.colModel;
36957 while(cm.isHidden(col)){
36961 var el = this.getCell(row, col);
36965 var c = this.scroller.dom;
36967 var ctop = parseInt(el.offsetTop, 10);
36968 var cleft = parseInt(el.offsetLeft, 10);
36969 var cbot = ctop + el.offsetHeight;
36970 var cright = cleft + el.offsetWidth;
36972 var ch = c.clientHeight - this.mainHd.dom.offsetHeight;
36973 var stop = parseInt(c.scrollTop, 10);
36974 var sleft = parseInt(c.scrollLeft, 10);
36975 var sbot = stop + ch;
36976 var sright = sleft + c.clientWidth;
36978 Roo.log('GridView.ensureVisible:' +
36980 ' c.clientHeight:' + c.clientHeight +
36981 ' this.mainHd.dom.offsetHeight:' + this.mainHd.dom.offsetHeight +
36989 c.scrollTop = ctop;
36990 //Roo.log("set scrolltop to ctop DISABLE?");
36991 }else if(cbot > sbot){
36992 //Roo.log("set scrolltop to cbot-ch");
36993 c.scrollTop = cbot-ch;
36996 if(hscroll !== false){
36998 c.scrollLeft = cleft;
36999 }else if(cright > sright){
37000 c.scrollLeft = cright-c.clientWidth;
37007 updateColumns : function(){
37008 this.grid.stopEditing();
37009 var cm = this.grid.colModel, colIds = this.getColumnIds();
37010 //var totalWidth = cm.getTotalWidth();
37012 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
37013 //if(cm.isHidden(i)) continue;
37014 var w = cm.getColumnWidth(i);
37015 this.css.updateRule(this.colSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
37016 this.css.updateRule(this.hdSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
37018 this.updateSplitters();
37021 generateRules : function(cm){
37022 var ruleBuf = [], rulesId = this.idToCssName(this.grid.id)+ '-cssrules';
37023 Roo.util.CSS.removeStyleSheet(rulesId);
37024 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
37025 var cid = cm.getColumnId(i);
37027 if(cm.config[i].align){
37028 align = 'text-align:'+cm.config[i].align+';';
37031 if(cm.isHidden(i)){
37032 hidden = 'display:none;';
37034 var width = "width:" + (cm.getColumnWidth(i) - this.borderWidth) + "px;";
37036 this.colSelector, cid, " {\n", cm.config[i].css, align, width, "\n}\n",
37037 this.hdSelector, cid, " {\n", align, width, "}\n",
37038 this.tdSelector, cid, " {\n",hidden,"\n}\n",
37039 this.splitSelector, cid, " {\n", hidden , "\n}\n");
37041 return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
37044 updateSplitters : function(){
37045 var cm = this.cm, s = this.getSplitters();
37046 if(s){ // splitters not created yet
37047 var pos = 0, locked = true;
37048 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
37049 if(cm.isHidden(i)) continue;
37050 var w = cm.getColumnWidth(i); // make sure it's a number
37051 if(!cm.isLocked(i) && locked){
37056 s[i].style.left = (pos-this.splitOffset) + "px";
37061 handleHiddenChange : function(colModel, colIndex, hidden){
37063 this.hideColumn(colIndex);
37065 this.unhideColumn(colIndex);
37069 hideColumn : function(colIndex){
37070 var cid = this.getColumnId(colIndex);
37071 this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "none");
37072 this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "none");
37074 this.updateHeaders();
37076 this.updateSplitters();
37080 unhideColumn : function(colIndex){
37081 var cid = this.getColumnId(colIndex);
37082 this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "");
37083 this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "");
37086 this.updateHeaders();
37088 this.updateSplitters();
37092 insertRows : function(dm, firstRow, lastRow, isUpdate){
37093 if(firstRow == 0 && lastRow == dm.getCount()-1){
37097 this.fireEvent("beforerowsinserted", this, firstRow, lastRow);
37099 var s = this.getScrollState();
37100 var markup = this.renderRows(firstRow, lastRow);
37101 this.bufferRows(markup[0], this.getLockedTable(), firstRow);
37102 this.bufferRows(markup[1], this.getBodyTable(), firstRow);
37103 this.restoreScroll(s);
37105 this.fireEvent("rowsinserted", this, firstRow, lastRow);
37106 this.syncRowHeights(firstRow, lastRow);
37107 this.stripeRows(firstRow);
37113 bufferRows : function(markup, target, index){
37114 var before = null, trows = target.rows, tbody = target.tBodies[0];
37115 if(index < trows.length){
37116 before = trows[index];
37118 var b = document.createElement("div");
37119 b.innerHTML = "<table><tbody>"+markup+"</tbody></table>";
37120 var rows = b.firstChild.rows;
37121 for(var i = 0, len = rows.length; i < len; i++){
37123 tbody.insertBefore(rows[0], before);
37125 tbody.appendChild(rows[0]);
37132 deleteRows : function(dm, firstRow, lastRow){
37133 if(dm.getRowCount()<1){
37134 this.fireEvent("beforerefresh", this);
37135 this.mainBody.update("");
37136 this.lockedBody.update("");
37137 this.fireEvent("refresh", this);
37139 this.fireEvent("beforerowsdeleted", this, firstRow, lastRow);
37140 var bt = this.getBodyTable();
37141 var tbody = bt.firstChild;
37142 var rows = bt.rows;
37143 for(var rowIndex = firstRow; rowIndex <= lastRow; rowIndex++){
37144 tbody.removeChild(rows[firstRow]);
37146 this.stripeRows(firstRow);
37147 this.fireEvent("rowsdeleted", this, firstRow, lastRow);
37151 updateRows : function(dataSource, firstRow, lastRow){
37152 var s = this.getScrollState();
37154 this.restoreScroll(s);
37157 handleSort : function(dataSource, sortColumnIndex, sortDir, noRefresh){
37161 this.updateHeaderSortState();
37164 getScrollState : function(){
37166 var sb = this.scroller.dom;
37167 return {left: sb.scrollLeft, top: sb.scrollTop};
37170 stripeRows : function(startRow){
37171 if(!this.grid.stripeRows || this.ds.getCount() < 1){
37174 startRow = startRow || 0;
37175 var rows = this.getBodyTable().rows;
37176 var lrows = this.getLockedTable().rows;
37177 var cls = ' x-grid-row-alt ';
37178 for(var i = startRow, len = rows.length; i < len; i++){
37179 var row = rows[i], lrow = lrows[i];
37180 var isAlt = ((i+1) % 2 == 0);
37181 var hasAlt = (' '+row.className + ' ').indexOf(cls) != -1;
37182 if(isAlt == hasAlt){
37186 row.className += " x-grid-row-alt";
37188 row.className = row.className.replace("x-grid-row-alt", "");
37191 lrow.className = row.className;
37196 restoreScroll : function(state){
37197 //Roo.log('GridView.restoreScroll');
37198 var sb = this.scroller.dom;
37199 sb.scrollLeft = state.left;
37200 sb.scrollTop = state.top;
37204 syncScroll : function(){
37205 //Roo.log('GridView.syncScroll');
37206 var sb = this.scroller.dom;
37207 var sh = this.mainHd.dom;
37208 var bs = this.mainBody.dom;
37209 var lv = this.lockedBody.dom;
37210 sh.scrollLeft = bs.scrollLeft = sb.scrollLeft;
37211 lv.scrollTop = bs.scrollTop = sb.scrollTop;
37214 handleScroll : function(e){
37216 var sb = this.scroller.dom;
37217 this.grid.fireEvent("bodyscroll", sb.scrollLeft, sb.scrollTop);
37221 handleWheel : function(e){
37222 var d = e.getWheelDelta();
37223 this.scroller.dom.scrollTop -= d*22;
37224 // set this here to prevent jumpy scrolling on large tables
37225 this.lockedBody.dom.scrollTop = this.mainBody.dom.scrollTop = this.scroller.dom.scrollTop;
37229 renderRows : function(startRow, endRow){
37230 // pull in all the crap needed to render rows
37231 var g = this.grid, cm = g.colModel, ds = g.dataSource, stripe = g.stripeRows;
37232 var colCount = cm.getColumnCount();
37234 if(ds.getCount() < 1){
37238 // build a map for all the columns
37240 for(var i = 0; i < colCount; i++){
37241 var name = cm.getDataIndex(i);
37243 name : typeof name == 'undefined' ? ds.fields.get(i).name : name,
37244 renderer : cm.getRenderer(i),
37245 id : cm.getColumnId(i),
37246 locked : cm.isLocked(i)
37250 startRow = startRow || 0;
37251 endRow = typeof endRow == "undefined"? ds.getCount()-1 : endRow;
37253 // records to render
37254 var rs = ds.getRange(startRow, endRow);
37256 return this.doRender(cs, rs, ds, startRow, colCount, stripe);
37259 // As much as I hate to duplicate code, this was branched because FireFox really hates
37260 // [].join("") on strings. The performance difference was substantial enough to
37261 // branch this function
37262 doRender : Roo.isGecko ?
37263 function(cs, rs, ds, startRow, colCount, stripe){
37264 var ts = this.templates, ct = ts.cell, rt = ts.row;
37266 var buf = "", lbuf = "", cb, lcb, c, p = {}, rp = {}, r, rowIndex;
37268 var hasListener = this.grid.hasListener('rowclass');
37270 for(var j = 0, len = rs.length; j < len; j++){
37271 r = rs[j]; cb = ""; lcb = ""; rowIndex = (j+startRow);
37272 for(var i = 0; i < colCount; i++){
37274 p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
37276 p.css = p.attr = "";
37277 p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
37278 if(p.value == undefined || p.value === "") p.value = " ";
37279 if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
37280 p.css += p.css ? ' x-grid-dirty-cell' : 'x-grid-dirty-cell';
37282 var markup = ct.apply(p);
37290 if(stripe && ((rowIndex+1) % 2 == 0)){
37291 alt.push("x-grid-row-alt")
37294 alt.push( " x-grid-dirty-row");
37297 if(this.getRowClass){
37298 alt.push(this.getRowClass(r, rowIndex));
37304 rowIndex : rowIndex,
37307 this.grid.fireEvent('rowclass', this, rowcfg);
37308 alt.push(rowcfg.rowClass);
37310 rp.alt = alt.join(" ");
37311 lbuf+= rt.apply(rp);
37313 buf+= rt.apply(rp);
37315 return [lbuf, buf];
37317 function(cs, rs, ds, startRow, colCount, stripe){
37318 var ts = this.templates, ct = ts.cell, rt = ts.row;
37320 var buf = [], lbuf = [], cb, lcb, c, p = {}, rp = {}, r, rowIndex;
37321 var hasListener = this.grid.hasListener('rowclass');
37324 for(var j = 0, len = rs.length; j < len; j++){
37325 r = rs[j]; cb = []; lcb = []; rowIndex = (j+startRow);
37326 for(var i = 0; i < colCount; i++){
37328 p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
37330 p.css = p.attr = "";
37331 p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
37332 if(p.value == undefined || p.value === "") p.value = " ";
37333 if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
37334 p.css += p.css ? ' x-grid-dirty-cell' : 'x-grid-dirty-cell';
37337 var markup = ct.apply(p);
37339 cb[cb.length] = markup;
37341 lcb[lcb.length] = markup;
37345 if(stripe && ((rowIndex+1) % 2 == 0)){
37346 alt.push( "x-grid-row-alt");
37349 alt.push(" x-grid-dirty-row");
37352 if(this.getRowClass){
37353 alt.push( this.getRowClass(r, rowIndex));
37359 rowIndex : rowIndex,
37362 this.grid.fireEvent('rowclass', this, rowcfg);
37363 alt.push(rowcfg.rowClass);
37365 rp.alt = alt.join(" ");
37366 rp.cells = lcb.join("");
37367 lbuf[lbuf.length] = rt.apply(rp);
37368 rp.cells = cb.join("");
37369 buf[buf.length] = rt.apply(rp);
37371 return [lbuf.join(""), buf.join("")];
37374 renderBody : function(){
37375 var markup = this.renderRows();
37376 var bt = this.templates.body;
37377 return [bt.apply({rows: markup[0]}), bt.apply({rows: markup[1]})];
37381 * Refreshes the grid
37382 * @param {Boolean} headersToo
37384 refresh : function(headersToo){
37385 this.fireEvent("beforerefresh", this);
37386 this.grid.stopEditing();
37387 var result = this.renderBody();
37388 this.lockedBody.update(result[0]);
37389 this.mainBody.update(result[1]);
37390 if(headersToo === true){
37391 this.updateHeaders();
37392 this.updateColumns();
37393 this.updateSplitters();
37394 this.updateHeaderSortState();
37396 this.syncRowHeights();
37398 this.fireEvent("refresh", this);
37401 handleColumnMove : function(cm, oldIndex, newIndex){
37402 this.indexMap = null;
37403 var s = this.getScrollState();
37404 this.refresh(true);
37405 this.restoreScroll(s);
37406 this.afterMove(newIndex);
37409 afterMove : function(colIndex){
37410 if(this.enableMoveAnim && Roo.enableFx){
37411 this.fly(this.getHeaderCell(colIndex).firstChild).highlight(this.hlColor);
37413 // if multisort - fix sortOrder, and reload..
37414 if (this.grid.dataSource.multiSort) {
37415 // the we can call sort again..
37416 var dm = this.grid.dataSource;
37417 var cm = this.grid.colModel;
37419 for(var i = 0; i < cm.config.length; i++ ) {
37421 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined')) {
37422 continue; // dont' bother, it's not in sort list or being set.
37425 so.push(cm.config[i].dataIndex);
37428 dm.load(dm.lastOptions);
37435 updateCell : function(dm, rowIndex, dataIndex){
37436 var colIndex = this.getColumnIndexByDataIndex(dataIndex);
37437 if(typeof colIndex == "undefined"){ // not present in grid
37440 var cm = this.grid.colModel;
37441 var cell = this.getCell(rowIndex, colIndex);
37442 var cellText = this.getCellText(rowIndex, colIndex);
37445 cellId : "x-grid-cell-" + rowIndex + "-" + colIndex,
37446 id : cm.getColumnId(colIndex),
37447 css: colIndex == cm.getColumnCount()-1 ? "x-grid-col-last" : ""
37449 var renderer = cm.getRenderer(colIndex);
37450 var val = renderer(dm.getValueAt(rowIndex, dataIndex), p, rowIndex, colIndex, dm);
37451 if(typeof val == "undefined" || val === "") val = " ";
37452 cellText.innerHTML = val;
37453 cell.className = this.cellClass + " " + this.idToCssName(p.cellId) + " " + p.css;
37454 this.syncRowHeights(rowIndex, rowIndex);
37457 calcColumnWidth : function(colIndex, maxRowsToMeasure){
37459 if(this.grid.autoSizeHeaders){
37460 var h = this.getHeaderCellMeasure(colIndex);
37461 maxWidth = Math.max(maxWidth, h.scrollWidth);
37464 if(this.cm.isLocked(colIndex)){
37465 tb = this.getLockedTable();
37468 tb = this.getBodyTable();
37469 index = colIndex - this.cm.getLockedCount();
37472 var rows = tb.rows;
37473 var stopIndex = Math.min(maxRowsToMeasure || rows.length, rows.length);
37474 for(var i = 0; i < stopIndex; i++){
37475 var cell = rows[i].childNodes[index].firstChild;
37476 maxWidth = Math.max(maxWidth, cell.scrollWidth);
37479 return maxWidth + /*margin for error in IE*/ 5;
37482 * Autofit a column to its content.
37483 * @param {Number} colIndex
37484 * @param {Boolean} forceMinSize true to force the column to go smaller if possible
37486 autoSizeColumn : function(colIndex, forceMinSize, suppressEvent){
37487 if(this.cm.isHidden(colIndex)){
37488 return; // can't calc a hidden column
37491 var cid = this.cm.getColumnId(colIndex);
37492 this.css.updateRule(this.colSelector +this.idToCssName( cid), "width", this.grid.minColumnWidth + "px");
37493 if(this.grid.autoSizeHeaders){
37494 this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", this.grid.minColumnWidth + "px");
37497 var newWidth = this.calcColumnWidth(colIndex);
37498 this.cm.setColumnWidth(colIndex,
37499 Math.max(this.grid.minColumnWidth, newWidth), suppressEvent);
37500 if(!suppressEvent){
37501 this.grid.fireEvent("columnresize", colIndex, newWidth);
37506 * Autofits all columns to their content and then expands to fit any extra space in the grid
37508 autoSizeColumns : function(){
37509 var cm = this.grid.colModel;
37510 var colCount = cm.getColumnCount();
37511 for(var i = 0; i < colCount; i++){
37512 this.autoSizeColumn(i, true, true);
37514 if(cm.getTotalWidth() < this.scroller.dom.clientWidth){
37517 this.updateColumns();
37523 * Autofits all columns to the grid's width proportionate with their current size
37524 * @param {Boolean} reserveScrollSpace Reserve space for a scrollbar
37526 fitColumns : function(reserveScrollSpace){
37527 var cm = this.grid.colModel;
37528 var colCount = cm.getColumnCount();
37532 for (i = 0; i < colCount; i++){
37533 if(!cm.isHidden(i) && !cm.isFixed(i)){
37534 w = cm.getColumnWidth(i);
37540 var avail = Math.min(this.scroller.dom.clientWidth, this.el.getWidth());
37541 if(reserveScrollSpace){
37544 var frac = (avail - cm.getTotalWidth())/width;
37545 while (cols.length){
37548 cm.setColumnWidth(i, Math.floor(w + w*frac), true);
37550 this.updateColumns();
37554 onRowSelect : function(rowIndex){
37555 var row = this.getRowComposite(rowIndex);
37556 row.addClass("x-grid-row-selected");
37559 onRowDeselect : function(rowIndex){
37560 var row = this.getRowComposite(rowIndex);
37561 row.removeClass("x-grid-row-selected");
37564 onCellSelect : function(row, col){
37565 var cell = this.getCell(row, col);
37567 Roo.fly(cell).addClass("x-grid-cell-selected");
37571 onCellDeselect : function(row, col){
37572 var cell = this.getCell(row, col);
37574 Roo.fly(cell).removeClass("x-grid-cell-selected");
37578 updateHeaderSortState : function(){
37580 // sort state can be single { field: xxx, direction : yyy}
37581 // or { xxx=>ASC , yyy : DESC ..... }
37584 if (!this.ds.multiSort) {
37585 var state = this.ds.getSortState();
37589 mstate[state.field] = state.direction;
37590 // FIXME... - this is not used here.. but might be elsewhere..
37591 this.sortState = state;
37594 mstate = this.ds.sortToggle;
37596 //remove existing sort classes..
37598 var sc = this.sortClasses;
37599 var hds = this.el.select(this.headerSelector).removeClass(sc);
37601 for(var f in mstate) {
37603 var sortColumn = this.cm.findColumnIndex(f);
37605 if(sortColumn != -1){
37606 var sortDir = mstate[f];
37607 hds.item(sortColumn).addClass(sc[sortDir == "DESC" ? 1 : 0]);
37616 handleHeaderClick : function(g, index){
37617 if(this.headersDisabled){
37620 var dm = g.dataSource, cm = g.colModel;
37621 if(!cm.isSortable(index)){
37626 if (dm.multiSort) {
37627 // update the sortOrder
37629 for(var i = 0; i < cm.config.length; i++ ) {
37631 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined') && (index != i)) {
37632 continue; // dont' bother, it's not in sort list or being set.
37635 so.push(cm.config[i].dataIndex);
37641 dm.sort(cm.getDataIndex(index));
37645 destroy : function(){
37647 this.colMenu.removeAll();
37648 Roo.menu.MenuMgr.unregister(this.colMenu);
37649 this.colMenu.getEl().remove();
37650 delete this.colMenu;
37653 this.hmenu.removeAll();
37654 Roo.menu.MenuMgr.unregister(this.hmenu);
37655 this.hmenu.getEl().remove();
37658 if(this.grid.enableColumnMove){
37659 var dds = Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
37661 for(var dd in dds){
37662 if(!dds[dd].config.isTarget && dds[dd].dragElId){
37663 var elid = dds[dd].dragElId;
37665 Roo.get(elid).remove();
37666 } else if(dds[dd].config.isTarget){
37667 dds[dd].proxyTop.remove();
37668 dds[dd].proxyBottom.remove();
37671 if(Roo.dd.DDM.locationCache[dd]){
37672 delete Roo.dd.DDM.locationCache[dd];
37675 delete Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
37678 Roo.util.CSS.removeStyleSheet(this.idToCssName(this.grid.id) + '-cssrules');
37679 this.bind(null, null);
37680 Roo.EventManager.removeResizeListener(this.onWindowResize, this);
37683 handleLockChange : function(){
37684 this.refresh(true);
37687 onDenyColumnLock : function(){
37691 onDenyColumnHide : function(){
37695 handleHdMenuClick : function(item){
37696 var index = this.hdCtxIndex;
37697 var cm = this.cm, ds = this.ds;
37700 ds.sort(cm.getDataIndex(index), "ASC");
37703 ds.sort(cm.getDataIndex(index), "DESC");
37706 var lc = cm.getLockedCount();
37707 if(cm.getColumnCount(true) <= lc+1){
37708 this.onDenyColumnLock();
37712 cm.setLocked(index, true, true);
37713 cm.moveColumn(index, lc);
37714 this.grid.fireEvent("columnmove", index, lc);
37716 cm.setLocked(index, true);
37720 var lc = cm.getLockedCount();
37721 if((lc-1) != index){
37722 cm.setLocked(index, false, true);
37723 cm.moveColumn(index, lc-1);
37724 this.grid.fireEvent("columnmove", index, lc-1);
37726 cm.setLocked(index, false);
37730 index = cm.getIndexById(item.id.substr(4));
37732 if(item.checked && cm.getColumnCount(true) <= 1){
37733 this.onDenyColumnHide();
37736 cm.setHidden(index, item.checked);
37742 beforeColMenuShow : function(){
37743 var cm = this.cm, colCount = cm.getColumnCount();
37744 this.colMenu.removeAll();
37745 for(var i = 0; i < colCount; i++){
37746 this.colMenu.add(new Roo.menu.CheckItem({
37747 id: "col-"+cm.getColumnId(i),
37748 text: cm.getColumnHeader(i),
37749 checked: !cm.isHidden(i),
37755 handleHdCtx : function(g, index, e){
37757 var hd = this.getHeaderCell(index);
37758 this.hdCtxIndex = index;
37759 var ms = this.hmenu.items, cm = this.cm;
37760 ms.get("asc").setDisabled(!cm.isSortable(index));
37761 ms.get("desc").setDisabled(!cm.isSortable(index));
37762 if(this.grid.enableColLock !== false){
37763 ms.get("lock").setDisabled(cm.isLocked(index));
37764 ms.get("unlock").setDisabled(!cm.isLocked(index));
37766 this.hmenu.show(hd, "tl-bl");
37769 handleHdOver : function(e){
37770 var hd = this.findHeaderCell(e.getTarget());
37771 if(hd && !this.headersDisabled){
37772 if(this.grid.colModel.isSortable(this.getCellIndex(hd))){
37773 this.fly(hd).addClass("x-grid-hd-over");
37778 handleHdOut : function(e){
37779 var hd = this.findHeaderCell(e.getTarget());
37781 this.fly(hd).removeClass("x-grid-hd-over");
37785 handleSplitDblClick : function(e, t){
37786 var i = this.getCellIndex(t);
37787 if(this.grid.enableColumnResize !== false && this.cm.isResizable(i) && !this.cm.isFixed(i)){
37788 this.autoSizeColumn(i, true);
37793 render : function(){
37796 var colCount = cm.getColumnCount();
37798 if(this.grid.monitorWindowResize === true){
37799 Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
37801 var header = this.renderHeaders();
37802 var body = this.templates.body.apply({rows:""});
37803 var html = this.templates.master.apply({
37806 lockedHeader: header[0],
37810 //this.updateColumns();
37812 this.grid.getGridEl().dom.innerHTML = html;
37814 this.initElements();
37816 // a kludge to fix the random scolling effect in webkit
37817 this.el.on("scroll", function() {
37818 this.el.dom.scrollTop=0; // hopefully not recursive..
37821 this.scroller.on("scroll", this.handleScroll, this);
37822 this.lockedBody.on("mousewheel", this.handleWheel, this);
37823 this.mainBody.on("mousewheel", this.handleWheel, this);
37825 this.mainHd.on("mouseover", this.handleHdOver, this);
37826 this.mainHd.on("mouseout", this.handleHdOut, this);
37827 this.mainHd.on("dblclick", this.handleSplitDblClick, this,
37828 {delegate: "."+this.splitClass});
37830 this.lockedHd.on("mouseover", this.handleHdOver, this);
37831 this.lockedHd.on("mouseout", this.handleHdOut, this);
37832 this.lockedHd.on("dblclick", this.handleSplitDblClick, this,
37833 {delegate: "."+this.splitClass});
37835 if(this.grid.enableColumnResize !== false && Roo.grid.SplitDragZone){
37836 new Roo.grid.SplitDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
37839 this.updateSplitters();
37841 if(this.grid.enableColumnMove && Roo.grid.HeaderDragZone){
37842 new Roo.grid.HeaderDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
37843 new Roo.grid.HeaderDropZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
37846 if(this.grid.enableCtxMenu !== false && Roo.menu.Menu){
37847 this.hmenu = new Roo.menu.Menu({id: this.grid.id + "-hctx"});
37849 {id:"asc", text: this.sortAscText, cls: "xg-hmenu-sort-asc"},
37850 {id:"desc", text: this.sortDescText, cls: "xg-hmenu-sort-desc"}
37852 if(this.grid.enableColLock !== false){
37853 this.hmenu.add('-',
37854 {id:"lock", text: this.lockText, cls: "xg-hmenu-lock"},
37855 {id:"unlock", text: this.unlockText, cls: "xg-hmenu-unlock"}
37858 if(this.grid.enableColumnHide !== false){
37860 this.colMenu = new Roo.menu.Menu({id:this.grid.id + "-hcols-menu"});
37861 this.colMenu.on("beforeshow", this.beforeColMenuShow, this);
37862 this.colMenu.on("itemclick", this.handleHdMenuClick, this);
37864 this.hmenu.add('-',
37865 {id:"columns", text: this.columnsText, menu: this.colMenu}
37868 this.hmenu.on("itemclick", this.handleHdMenuClick, this);
37870 this.grid.on("headercontextmenu", this.handleHdCtx, this);
37873 if((this.grid.enableDragDrop || this.grid.enableDrag) && Roo.grid.GridDragZone){
37874 this.dd = new Roo.grid.GridDragZone(this.grid, {
37875 ddGroup : this.grid.ddGroup || 'GridDD'
37881 for(var i = 0; i < colCount; i++){
37882 if(cm.isHidden(i)){
37883 this.hideColumn(i);
37885 if(cm.config[i].align){
37886 this.css.updateRule(this.colSelector + i, "textAlign", cm.config[i].align);
37887 this.css.updateRule(this.hdSelector + i, "textAlign", cm.config[i].align);
37891 this.updateHeaderSortState();
37893 this.beforeInitialResize();
37896 // two part rendering gives faster view to the user
37897 this.renderPhase2.defer(1, this);
37900 renderPhase2 : function(){
37901 // render the rows now
37903 if(this.grid.autoSizeColumns){
37904 this.autoSizeColumns();
37908 beforeInitialResize : function(){
37912 onColumnSplitterMoved : function(i, w){
37913 this.userResized = true;
37914 var cm = this.grid.colModel;
37915 cm.setColumnWidth(i, w, true);
37916 var cid = cm.getColumnId(i);
37917 this.css.updateRule(this.colSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
37918 this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
37919 this.updateSplitters();
37921 this.grid.fireEvent("columnresize", i, w);
37924 syncRowHeights : function(startIndex, endIndex){
37925 if(this.grid.enableRowHeightSync === true && this.cm.getLockedCount() > 0){
37926 startIndex = startIndex || 0;
37927 var mrows = this.getBodyTable().rows;
37928 var lrows = this.getLockedTable().rows;
37929 var len = mrows.length-1;
37930 endIndex = Math.min(endIndex || len, len);
37931 for(var i = startIndex; i <= endIndex; i++){
37932 var m = mrows[i], l = lrows[i];
37933 var h = Math.max(m.offsetHeight, l.offsetHeight);
37934 m.style.height = l.style.height = h + "px";
37939 layout : function(initialRender, is2ndPass){
37941 var auto = g.autoHeight;
37942 var scrollOffset = 16;
37943 var c = g.getGridEl(), cm = this.cm,
37944 expandCol = g.autoExpandColumn,
37946 //c.beginMeasure();
37948 if(!c.dom.offsetWidth){ // display:none?
37950 this.lockedWrap.show();
37951 this.mainWrap.show();
37956 var hasLock = this.cm.isLocked(0);
37958 var tbh = this.headerPanel.getHeight();
37959 var bbh = this.footerPanel.getHeight();
37962 var ch = this.getBodyTable().offsetHeight + tbh + bbh + this.mainHd.getHeight();
37963 var newHeight = ch + c.getBorderWidth("tb");
37965 newHeight = Math.min(g.maxHeight, newHeight);
37967 c.setHeight(newHeight);
37971 c.setWidth(cm.getTotalWidth()+c.getBorderWidth('lr'));
37974 var s = this.scroller;
37976 var csize = c.getSize(true);
37978 this.el.setSize(csize.width, csize.height);
37980 this.headerPanel.setWidth(csize.width);
37981 this.footerPanel.setWidth(csize.width);
37983 var hdHeight = this.mainHd.getHeight();
37984 var vw = csize.width;
37985 var vh = csize.height - (tbh + bbh);
37989 var bt = this.getBodyTable();
37990 var ltWidth = hasLock ?
37991 Math.max(this.getLockedTable().offsetWidth, this.lockedHd.dom.firstChild.offsetWidth) : 0;
37993 var scrollHeight = bt.offsetHeight;
37994 var scrollWidth = ltWidth + bt.offsetWidth;
37995 var vscroll = false, hscroll = false;
37997 this.scrollSizer.setSize(scrollWidth, scrollHeight+hdHeight);
37999 var lw = this.lockedWrap, mw = this.mainWrap;
38000 var lb = this.lockedBody, mb = this.mainBody;
38002 setTimeout(function(){
38003 var t = s.dom.offsetTop;
38004 var w = s.dom.clientWidth,
38005 h = s.dom.clientHeight;
38008 lw.setSize(ltWidth, h);
38010 mw.setLeftTop(ltWidth, t);
38011 mw.setSize(w-ltWidth, h);
38013 lb.setHeight(h-hdHeight);
38014 mb.setHeight(h-hdHeight);
38016 if(is2ndPass !== true && !gv.userResized && expandCol){
38017 // high speed resize without full column calculation
38019 var ci = cm.getIndexById(expandCol);
38021 ci = cm.findColumnIndex(expandCol);
38023 ci = Math.max(0, ci); // make sure it's got at least the first col.
38024 var expandId = cm.getColumnId(ci);
38025 var tw = cm.getTotalWidth(false);
38026 var currentWidth = cm.getColumnWidth(ci);
38027 var cw = Math.min(Math.max(((w-tw)+currentWidth-2)-/*scrollbar*/(w <= s.dom.offsetWidth ? 0 : 18), g.autoExpandMin), g.autoExpandMax);
38028 if(currentWidth != cw){
38029 cm.setColumnWidth(ci, cw, true);
38030 gv.css.updateRule(gv.colSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
38031 gv.css.updateRule(gv.hdSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
38032 gv.updateSplitters();
38033 gv.layout(false, true);
38045 onWindowResize : function(){
38046 if(!this.grid.monitorWindowResize || this.grid.autoHeight){
38052 appendFooter : function(parentEl){
38056 sortAscText : "Sort Ascending",
38057 sortDescText : "Sort Descending",
38058 lockText : "Lock Column",
38059 unlockText : "Unlock Column",
38060 columnsText : "Columns"
38064 Roo.grid.GridView.ColumnDragZone = function(grid, hd){
38065 Roo.grid.GridView.ColumnDragZone.superclass.constructor.call(this, grid, hd, null);
38066 this.proxy.el.addClass('x-grid3-col-dd');
38069 Roo.extend(Roo.grid.GridView.ColumnDragZone, Roo.grid.HeaderDragZone, {
38070 handleMouseDown : function(e){
38074 callHandleMouseDown : function(e){
38075 Roo.grid.GridView.ColumnDragZone.superclass.handleMouseDown.call(this, e);
38080 * Ext JS Library 1.1.1
38081 * Copyright(c) 2006-2007, Ext JS, LLC.
38083 * Originally Released Under LGPL - original licence link has changed is not relivant.
38086 * <script type="text/javascript">
38090 // This is a support class used internally by the Grid components
38091 Roo.grid.SplitDragZone = function(grid, hd, hd2){
38093 this.view = grid.getView();
38094 this.proxy = this.view.resizeProxy;
38095 Roo.grid.SplitDragZone.superclass.constructor.call(this, hd,
38096 "gridSplitters" + this.grid.getGridEl().id, {
38097 dragElId : Roo.id(this.proxy.dom), resizeFrame:false
38099 this.setHandleElId(Roo.id(hd));
38100 this.setOuterHandleElId(Roo.id(hd2));
38101 this.scroll = false;
38103 Roo.extend(Roo.grid.SplitDragZone, Roo.dd.DDProxy, {
38104 fly: Roo.Element.fly,
38106 b4StartDrag : function(x, y){
38107 this.view.headersDisabled = true;
38108 this.proxy.setHeight(this.view.mainWrap.getHeight());
38109 var w = this.cm.getColumnWidth(this.cellIndex);
38110 var minw = Math.max(w-this.grid.minColumnWidth, 0);
38111 this.resetConstraints();
38112 this.setXConstraint(minw, 1000);
38113 this.setYConstraint(0, 0);
38114 this.minX = x - minw;
38115 this.maxX = x + 1000;
38117 Roo.dd.DDProxy.prototype.b4StartDrag.call(this, x, y);
38121 handleMouseDown : function(e){
38122 ev = Roo.EventObject.setEvent(e);
38123 var t = this.fly(ev.getTarget());
38124 if(t.hasClass("x-grid-split")){
38125 this.cellIndex = this.view.getCellIndex(t.dom);
38126 this.split = t.dom;
38127 this.cm = this.grid.colModel;
38128 if(this.cm.isResizable(this.cellIndex) && !this.cm.isFixed(this.cellIndex)){
38129 Roo.grid.SplitDragZone.superclass.handleMouseDown.apply(this, arguments);
38134 endDrag : function(e){
38135 this.view.headersDisabled = false;
38136 var endX = Math.max(this.minX, Roo.lib.Event.getPageX(e));
38137 var diff = endX - this.startPos;
38138 this.view.onColumnSplitterMoved(this.cellIndex, this.cm.getColumnWidth(this.cellIndex)+diff);
38141 autoOffset : function(){
38142 this.setDelta(0,0);
38146 * Ext JS Library 1.1.1
38147 * Copyright(c) 2006-2007, Ext JS, LLC.
38149 * Originally Released Under LGPL - original licence link has changed is not relivant.
38152 * <script type="text/javascript">
38156 // This is a support class used internally by the Grid components
38157 Roo.grid.GridDragZone = function(grid, config){
38158 this.view = grid.getView();
38159 Roo.grid.GridDragZone.superclass.constructor.call(this, this.view.mainBody.dom, config);
38160 if(this.view.lockedBody){
38161 this.setHandleElId(Roo.id(this.view.mainBody.dom));
38162 this.setOuterHandleElId(Roo.id(this.view.lockedBody.dom));
38164 this.scroll = false;
38166 this.ddel = document.createElement('div');
38167 this.ddel.className = 'x-grid-dd-wrap';
38170 Roo.extend(Roo.grid.GridDragZone, Roo.dd.DragZone, {
38171 ddGroup : "GridDD",
38173 getDragData : function(e){
38174 var t = Roo.lib.Event.getTarget(e);
38175 var rowIndex = this.view.findRowIndex(t);
38176 var sm = this.grid.selModel;
38178 //Roo.log(rowIndex);
38180 if (sm.getSelectedCell) {
38181 // cell selection..
38182 if (!sm.getSelectedCell()) {
38185 if (rowIndex != sm.getSelectedCell()[0]) {
38191 if(rowIndex !== false){
38196 //Roo.log([ sm.getSelectedCell() ? sm.getSelectedCell()[0] : 'NO' , rowIndex ]);
38198 //if(!sm.isSelected(rowIndex) || e.hasModifier()){
38201 if (e.hasModifier()){
38202 sm.handleMouseDown(e, t); // non modifier buttons are handled by row select.
38205 Roo.log("getDragData");
38210 rowIndex: rowIndex,
38211 selections:sm.getSelections ? sm.getSelections() : (
38212 sm.getSelectedCell() ? [ this.grid.ds.getAt(sm.getSelectedCell()[0]) ] : []
38219 onInitDrag : function(e){
38220 var data = this.dragData;
38221 this.ddel.innerHTML = this.grid.getDragDropText();
38222 this.proxy.update(this.ddel);
38223 // fire start drag?
38226 afterRepair : function(){
38227 this.dragging = false;
38230 getRepairXY : function(e, data){
38234 onEndDrag : function(data, e){
38238 onValidDrop : function(dd, e, id){
38243 beforeInvalidDrop : function(e, id){
38248 * Ext JS Library 1.1.1
38249 * Copyright(c) 2006-2007, Ext JS, LLC.
38251 * Originally Released Under LGPL - original licence link has changed is not relivant.
38254 * <script type="text/javascript">
38259 * @class Roo.grid.ColumnModel
38260 * @extends Roo.util.Observable
38261 * This is the default implementation of a ColumnModel used by the Grid. It defines
38262 * the columns in the grid.
38265 var colModel = new Roo.grid.ColumnModel([
38266 {header: "Ticker", width: 60, sortable: true, locked: true},
38267 {header: "Company Name", width: 150, sortable: true},
38268 {header: "Market Cap.", width: 100, sortable: true},
38269 {header: "$ Sales", width: 100, sortable: true, renderer: money},
38270 {header: "Employees", width: 100, sortable: true, resizable: false}
38275 * The config options listed for this class are options which may appear in each
38276 * individual column definition.
38277 * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
38279 * @param {Object} config An Array of column config objects. See this class's
38280 * config objects for details.
38282 Roo.grid.ColumnModel = function(config){
38284 * The config passed into the constructor
38286 this.config = config;
38289 // if no id, create one
38290 // if the column does not have a dataIndex mapping,
38291 // map it to the order it is in the config
38292 for(var i = 0, len = config.length; i < len; i++){
38294 if(typeof c.dataIndex == "undefined"){
38297 if(typeof c.renderer == "string"){
38298 c.renderer = Roo.util.Format[c.renderer];
38300 if(typeof c.id == "undefined"){
38303 if(c.editor && c.editor.xtype){
38304 c.editor = Roo.factory(c.editor, Roo.grid);
38306 if(c.editor && c.editor.isFormField){
38307 c.editor = new Roo.grid.GridEditor(c.editor);
38309 this.lookup[c.id] = c;
38313 * The width of columns which have no width specified (defaults to 100)
38316 this.defaultWidth = 100;
38319 * Default sortable of columns which have no sortable specified (defaults to false)
38322 this.defaultSortable = false;
38326 * @event widthchange
38327 * Fires when the width of a column changes.
38328 * @param {ColumnModel} this
38329 * @param {Number} columnIndex The column index
38330 * @param {Number} newWidth The new width
38332 "widthchange": true,
38334 * @event headerchange
38335 * Fires when the text of a header changes.
38336 * @param {ColumnModel} this
38337 * @param {Number} columnIndex The column index
38338 * @param {Number} newText The new header text
38340 "headerchange": true,
38342 * @event hiddenchange
38343 * Fires when a column is hidden or "unhidden".
38344 * @param {ColumnModel} this
38345 * @param {Number} columnIndex The column index
38346 * @param {Boolean} hidden true if hidden, false otherwise
38348 "hiddenchange": true,
38350 * @event columnmoved
38351 * Fires when a column is moved.
38352 * @param {ColumnModel} this
38353 * @param {Number} oldIndex
38354 * @param {Number} newIndex
38356 "columnmoved" : true,
38358 * @event columlockchange
38359 * Fires when a column's locked state is changed
38360 * @param {ColumnModel} this
38361 * @param {Number} colIndex
38362 * @param {Boolean} locked true if locked
38364 "columnlockchange" : true
38366 Roo.grid.ColumnModel.superclass.constructor.call(this);
38368 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
38370 * @cfg {String} header The header text to display in the Grid view.
38373 * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
38374 * {@link Roo.data.Record} definition from which to draw the column's value. If not
38375 * specified, the column's index is used as an index into the Record's data Array.
38378 * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
38379 * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
38382 * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
38383 * Defaults to the value of the {@link #defaultSortable} property.
38384 * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
38387 * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid. Defaults to false.
38390 * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed. Defaults to false.
38393 * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
38396 * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
38399 * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
38400 * given the cell's data value. See {@link #setRenderer}. If not specified, the
38401 * default renderer uses the raw data value.
38404 * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor
38407 * @cfg {String} align (Optional) Set the CSS text-align property of the column. Defaults to undefined.
38411 * Returns the id of the column at the specified index.
38412 * @param {Number} index The column index
38413 * @return {String} the id
38415 getColumnId : function(index){
38416 return this.config[index].id;
38420 * Returns the column for a specified id.
38421 * @param {String} id The column id
38422 * @return {Object} the column
38424 getColumnById : function(id){
38425 return this.lookup[id];
38430 * Returns the column for a specified dataIndex.
38431 * @param {String} dataIndex The column dataIndex
38432 * @return {Object|Boolean} the column or false if not found
38434 getColumnByDataIndex: function(dataIndex){
38435 var index = this.findColumnIndex(dataIndex);
38436 return index > -1 ? this.config[index] : false;
38440 * Returns the index for a specified column id.
38441 * @param {String} id The column id
38442 * @return {Number} the index, or -1 if not found
38444 getIndexById : function(id){
38445 for(var i = 0, len = this.config.length; i < len; i++){
38446 if(this.config[i].id == id){
38454 * Returns the index for a specified column dataIndex.
38455 * @param {String} dataIndex The column dataIndex
38456 * @return {Number} the index, or -1 if not found
38459 findColumnIndex : function(dataIndex){
38460 for(var i = 0, len = this.config.length; i < len; i++){
38461 if(this.config[i].dataIndex == dataIndex){
38469 moveColumn : function(oldIndex, newIndex){
38470 var c = this.config[oldIndex];
38471 this.config.splice(oldIndex, 1);
38472 this.config.splice(newIndex, 0, c);
38473 this.dataMap = null;
38474 this.fireEvent("columnmoved", this, oldIndex, newIndex);
38477 isLocked : function(colIndex){
38478 return this.config[colIndex].locked === true;
38481 setLocked : function(colIndex, value, suppressEvent){
38482 if(this.isLocked(colIndex) == value){
38485 this.config[colIndex].locked = value;
38486 if(!suppressEvent){
38487 this.fireEvent("columnlockchange", this, colIndex, value);
38491 getTotalLockedWidth : function(){
38492 var totalWidth = 0;
38493 for(var i = 0; i < this.config.length; i++){
38494 if(this.isLocked(i) && !this.isHidden(i)){
38495 this.totalWidth += this.getColumnWidth(i);
38501 getLockedCount : function(){
38502 for(var i = 0, len = this.config.length; i < len; i++){
38503 if(!this.isLocked(i)){
38510 * Returns the number of columns.
38513 getColumnCount : function(visibleOnly){
38514 if(visibleOnly === true){
38516 for(var i = 0, len = this.config.length; i < len; i++){
38517 if(!this.isHidden(i)){
38523 return this.config.length;
38527 * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
38528 * @param {Function} fn
38529 * @param {Object} scope (optional)
38530 * @return {Array} result
38532 getColumnsBy : function(fn, scope){
38534 for(var i = 0, len = this.config.length; i < len; i++){
38535 var c = this.config[i];
38536 if(fn.call(scope||this, c, i) === true){
38544 * Returns true if the specified column is sortable.
38545 * @param {Number} col The column index
38546 * @return {Boolean}
38548 isSortable : function(col){
38549 if(typeof this.config[col].sortable == "undefined"){
38550 return this.defaultSortable;
38552 return this.config[col].sortable;
38556 * Returns the rendering (formatting) function defined for the column.
38557 * @param {Number} col The column index.
38558 * @return {Function} The function used to render the cell. See {@link #setRenderer}.
38560 getRenderer : function(col){
38561 if(!this.config[col].renderer){
38562 return Roo.grid.ColumnModel.defaultRenderer;
38564 return this.config[col].renderer;
38568 * Sets the rendering (formatting) function for a column.
38569 * @param {Number} col The column index
38570 * @param {Function} fn The function to use to process the cell's raw data
38571 * to return HTML markup for the grid view. The render function is called with
38572 * the following parameters:<ul>
38573 * <li>Data value.</li>
38574 * <li>Cell metadata. An object in which you may set the following attributes:<ul>
38575 * <li>css A CSS style string to apply to the table cell.</li>
38576 * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
38577 * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
38578 * <li>Row index</li>
38579 * <li>Column index</li>
38580 * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
38582 setRenderer : function(col, fn){
38583 this.config[col].renderer = fn;
38587 * Returns the width for the specified column.
38588 * @param {Number} col The column index
38591 getColumnWidth : function(col){
38592 return this.config[col].width * 1 || this.defaultWidth;
38596 * Sets the width for a column.
38597 * @param {Number} col The column index
38598 * @param {Number} width The new width
38600 setColumnWidth : function(col, width, suppressEvent){
38601 this.config[col].width = width;
38602 this.totalWidth = null;
38603 if(!suppressEvent){
38604 this.fireEvent("widthchange", this, col, width);
38609 * Returns the total width of all columns.
38610 * @param {Boolean} includeHidden True to include hidden column widths
38613 getTotalWidth : function(includeHidden){
38614 if(!this.totalWidth){
38615 this.totalWidth = 0;
38616 for(var i = 0, len = this.config.length; i < len; i++){
38617 if(includeHidden || !this.isHidden(i)){
38618 this.totalWidth += this.getColumnWidth(i);
38622 return this.totalWidth;
38626 * Returns the header for the specified column.
38627 * @param {Number} col The column index
38630 getColumnHeader : function(col){
38631 return this.config[col].header;
38635 * Sets the header for a column.
38636 * @param {Number} col The column index
38637 * @param {String} header The new header
38639 setColumnHeader : function(col, header){
38640 this.config[col].header = header;
38641 this.fireEvent("headerchange", this, col, header);
38645 * Returns the tooltip for the specified column.
38646 * @param {Number} col The column index
38649 getColumnTooltip : function(col){
38650 return this.config[col].tooltip;
38653 * Sets the tooltip for a column.
38654 * @param {Number} col The column index
38655 * @param {String} tooltip The new tooltip
38657 setColumnTooltip : function(col, tooltip){
38658 this.config[col].tooltip = tooltip;
38662 * Returns the dataIndex for the specified column.
38663 * @param {Number} col The column index
38666 getDataIndex : function(col){
38667 return this.config[col].dataIndex;
38671 * Sets the dataIndex for a column.
38672 * @param {Number} col The column index
38673 * @param {Number} dataIndex The new dataIndex
38675 setDataIndex : function(col, dataIndex){
38676 this.config[col].dataIndex = dataIndex;
38682 * Returns true if the cell is editable.
38683 * @param {Number} colIndex The column index
38684 * @param {Number} rowIndex The row index
38685 * @return {Boolean}
38687 isCellEditable : function(colIndex, rowIndex){
38688 return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
38692 * Returns the editor defined for the cell/column.
38693 * return false or null to disable editing.
38694 * @param {Number} colIndex The column index
38695 * @param {Number} rowIndex The row index
38698 getCellEditor : function(colIndex, rowIndex){
38699 return this.config[colIndex].editor;
38703 * Sets if a column is editable.
38704 * @param {Number} col The column index
38705 * @param {Boolean} editable True if the column is editable
38707 setEditable : function(col, editable){
38708 this.config[col].editable = editable;
38713 * Returns true if the column is hidden.
38714 * @param {Number} colIndex The column index
38715 * @return {Boolean}
38717 isHidden : function(colIndex){
38718 return this.config[colIndex].hidden;
38723 * Returns true if the column width cannot be changed
38725 isFixed : function(colIndex){
38726 return this.config[colIndex].fixed;
38730 * Returns true if the column can be resized
38731 * @return {Boolean}
38733 isResizable : function(colIndex){
38734 return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
38737 * Sets if a column is hidden.
38738 * @param {Number} colIndex The column index
38739 * @param {Boolean} hidden True if the column is hidden
38741 setHidden : function(colIndex, hidden){
38742 this.config[colIndex].hidden = hidden;
38743 this.totalWidth = null;
38744 this.fireEvent("hiddenchange", this, colIndex, hidden);
38748 * Sets the editor for a column.
38749 * @param {Number} col The column index
38750 * @param {Object} editor The editor object
38752 setEditor : function(col, editor){
38753 this.config[col].editor = editor;
38757 Roo.grid.ColumnModel.defaultRenderer = function(value){
38758 if(typeof value == "string" && value.length < 1){
38764 // Alias for backwards compatibility
38765 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
38768 * Ext JS Library 1.1.1
38769 * Copyright(c) 2006-2007, Ext JS, LLC.
38771 * Originally Released Under LGPL - original licence link has changed is not relivant.
38774 * <script type="text/javascript">
38778 * @class Roo.grid.AbstractSelectionModel
38779 * @extends Roo.util.Observable
38780 * Abstract base class for grid SelectionModels. It provides the interface that should be
38781 * implemented by descendant classes. This class should not be directly instantiated.
38784 Roo.grid.AbstractSelectionModel = function(){
38785 this.locked = false;
38786 Roo.grid.AbstractSelectionModel.superclass.constructor.call(this);
38789 Roo.extend(Roo.grid.AbstractSelectionModel, Roo.util.Observable, {
38790 /** @ignore Called by the grid automatically. Do not call directly. */
38791 init : function(grid){
38797 * Locks the selections.
38800 this.locked = true;
38804 * Unlocks the selections.
38806 unlock : function(){
38807 this.locked = false;
38811 * Returns true if the selections are locked.
38812 * @return {Boolean}
38814 isLocked : function(){
38815 return this.locked;
38819 * Ext JS Library 1.1.1
38820 * Copyright(c) 2006-2007, Ext JS, LLC.
38822 * Originally Released Under LGPL - original licence link has changed is not relivant.
38825 * <script type="text/javascript">
38828 * @extends Roo.grid.AbstractSelectionModel
38829 * @class Roo.grid.RowSelectionModel
38830 * The default SelectionModel used by {@link Roo.grid.Grid}.
38831 * It supports multiple selections and keyboard selection/navigation.
38833 * @param {Object} config
38835 Roo.grid.RowSelectionModel = function(config){
38836 Roo.apply(this, config);
38837 this.selections = new Roo.util.MixedCollection(false, function(o){
38842 this.lastActive = false;
38846 * @event selectionchange
38847 * Fires when the selection changes
38848 * @param {SelectionModel} this
38850 "selectionchange" : true,
38852 * @event afterselectionchange
38853 * Fires after the selection changes (eg. by key press or clicking)
38854 * @param {SelectionModel} this
38856 "afterselectionchange" : true,
38858 * @event beforerowselect
38859 * Fires when a row is selected being selected, return false to cancel.
38860 * @param {SelectionModel} this
38861 * @param {Number} rowIndex The selected index
38862 * @param {Boolean} keepExisting False if other selections will be cleared
38864 "beforerowselect" : true,
38867 * Fires when a row is selected.
38868 * @param {SelectionModel} this
38869 * @param {Number} rowIndex The selected index
38870 * @param {Roo.data.Record} r The record
38872 "rowselect" : true,
38874 * @event rowdeselect
38875 * Fires when a row is deselected.
38876 * @param {SelectionModel} this
38877 * @param {Number} rowIndex The selected index
38879 "rowdeselect" : true
38881 Roo.grid.RowSelectionModel.superclass.constructor.call(this);
38882 this.locked = false;
38885 Roo.extend(Roo.grid.RowSelectionModel, Roo.grid.AbstractSelectionModel, {
38887 * @cfg {Boolean} singleSelect
38888 * True to allow selection of only one row at a time (defaults to false)
38890 singleSelect : false,
38893 initEvents : function(){
38895 if(!this.grid.enableDragDrop && !this.grid.enableDrag){
38896 this.grid.on("mousedown", this.handleMouseDown, this);
38897 }else{ // allow click to work like normal
38898 this.grid.on("rowclick", this.handleDragableRowClick, this);
38901 this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
38902 "up" : function(e){
38904 this.selectPrevious(e.shiftKey);
38905 }else if(this.last !== false && this.lastActive !== false){
38906 var last = this.last;
38907 this.selectRange(this.last, this.lastActive-1);
38908 this.grid.getView().focusRow(this.lastActive);
38909 if(last !== false){
38913 this.selectFirstRow();
38915 this.fireEvent("afterselectionchange", this);
38917 "down" : function(e){
38919 this.selectNext(e.shiftKey);
38920 }else if(this.last !== false && this.lastActive !== false){
38921 var last = this.last;
38922 this.selectRange(this.last, this.lastActive+1);
38923 this.grid.getView().focusRow(this.lastActive);
38924 if(last !== false){
38928 this.selectFirstRow();
38930 this.fireEvent("afterselectionchange", this);
38935 var view = this.grid.view;
38936 view.on("refresh", this.onRefresh, this);
38937 view.on("rowupdated", this.onRowUpdated, this);
38938 view.on("rowremoved", this.onRemove, this);
38942 onRefresh : function(){
38943 var ds = this.grid.dataSource, i, v = this.grid.view;
38944 var s = this.selections;
38945 s.each(function(r){
38946 if((i = ds.indexOfId(r.id)) != -1){
38955 onRemove : function(v, index, r){
38956 this.selections.remove(r);
38960 onRowUpdated : function(v, index, r){
38961 if(this.isSelected(r)){
38962 v.onRowSelect(index);
38968 * @param {Array} records The records to select
38969 * @param {Boolean} keepExisting (optional) True to keep existing selections
38971 selectRecords : function(records, keepExisting){
38973 this.clearSelections();
38975 var ds = this.grid.dataSource;
38976 for(var i = 0, len = records.length; i < len; i++){
38977 this.selectRow(ds.indexOf(records[i]), true);
38982 * Gets the number of selected rows.
38985 getCount : function(){
38986 return this.selections.length;
38990 * Selects the first row in the grid.
38992 selectFirstRow : function(){
38997 * Select the last row.
38998 * @param {Boolean} keepExisting (optional) True to keep existing selections
39000 selectLastRow : function(keepExisting){
39001 this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
39005 * Selects the row immediately following the last selected row.
39006 * @param {Boolean} keepExisting (optional) True to keep existing selections
39008 selectNext : function(keepExisting){
39009 if(this.last !== false && (this.last+1) < this.grid.dataSource.getCount()){
39010 this.selectRow(this.last+1, keepExisting);
39011 this.grid.getView().focusRow(this.last);
39016 * Selects the row that precedes the last selected row.
39017 * @param {Boolean} keepExisting (optional) True to keep existing selections
39019 selectPrevious : function(keepExisting){
39021 this.selectRow(this.last-1, keepExisting);
39022 this.grid.getView().focusRow(this.last);
39027 * Returns the selected records
39028 * @return {Array} Array of selected records
39030 getSelections : function(){
39031 return [].concat(this.selections.items);
39035 * Returns the first selected record.
39038 getSelected : function(){
39039 return this.selections.itemAt(0);
39044 * Clears all selections.
39046 clearSelections : function(fast){
39047 if(this.locked) return;
39049 var ds = this.grid.dataSource;
39050 var s = this.selections;
39051 s.each(function(r){
39052 this.deselectRow(ds.indexOfId(r.id));
39056 this.selections.clear();
39063 * Selects all rows.
39065 selectAll : function(){
39066 if(this.locked) return;
39067 this.selections.clear();
39068 for(var i = 0, len = this.grid.dataSource.getCount(); i < len; i++){
39069 this.selectRow(i, true);
39074 * Returns True if there is a selection.
39075 * @return {Boolean}
39077 hasSelection : function(){
39078 return this.selections.length > 0;
39082 * Returns True if the specified row is selected.
39083 * @param {Number/Record} record The record or index of the record to check
39084 * @return {Boolean}
39086 isSelected : function(index){
39087 var r = typeof index == "number" ? this.grid.dataSource.getAt(index) : index;
39088 return (r && this.selections.key(r.id) ? true : false);
39092 * Returns True if the specified record id is selected.
39093 * @param {String} id The id of record to check
39094 * @return {Boolean}
39096 isIdSelected : function(id){
39097 return (this.selections.key(id) ? true : false);
39101 handleMouseDown : function(e, t){
39102 var view = this.grid.getView(), rowIndex;
39103 if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
39106 if(e.shiftKey && this.last !== false){
39107 var last = this.last;
39108 this.selectRange(last, rowIndex, e.ctrlKey);
39109 this.last = last; // reset the last
39110 view.focusRow(rowIndex);
39112 var isSelected = this.isSelected(rowIndex);
39113 if(e.button !== 0 && isSelected){
39114 view.focusRow(rowIndex);
39115 }else if(e.ctrlKey && isSelected){
39116 this.deselectRow(rowIndex);
39117 }else if(!isSelected){
39118 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
39119 view.focusRow(rowIndex);
39122 this.fireEvent("afterselectionchange", this);
39125 handleDragableRowClick : function(grid, rowIndex, e)
39127 if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
39128 this.selectRow(rowIndex, false);
39129 grid.view.focusRow(rowIndex);
39130 this.fireEvent("afterselectionchange", this);
39135 * Selects multiple rows.
39136 * @param {Array} rows Array of the indexes of the row to select
39137 * @param {Boolean} keepExisting (optional) True to keep existing selections
39139 selectRows : function(rows, keepExisting){
39141 this.clearSelections();
39143 for(var i = 0, len = rows.length; i < len; i++){
39144 this.selectRow(rows[i], true);
39149 * Selects a range of rows. All rows in between startRow and endRow are also selected.
39150 * @param {Number} startRow The index of the first row in the range
39151 * @param {Number} endRow The index of the last row in the range
39152 * @param {Boolean} keepExisting (optional) True to retain existing selections
39154 selectRange : function(startRow, endRow, keepExisting){
39155 if(this.locked) return;
39157 this.clearSelections();
39159 if(startRow <= endRow){
39160 for(var i = startRow; i <= endRow; i++){
39161 this.selectRow(i, true);
39164 for(var i = startRow; i >= endRow; i--){
39165 this.selectRow(i, true);
39171 * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
39172 * @param {Number} startRow The index of the first row in the range
39173 * @param {Number} endRow The index of the last row in the range
39175 deselectRange : function(startRow, endRow, preventViewNotify){
39176 if(this.locked) return;
39177 for(var i = startRow; i <= endRow; i++){
39178 this.deselectRow(i, preventViewNotify);
39184 * @param {Number} row The index of the row to select
39185 * @param {Boolean} keepExisting (optional) True to keep existing selections
39187 selectRow : function(index, keepExisting, preventViewNotify){
39188 if(this.locked || (index < 0 || index >= this.grid.dataSource.getCount())) return;
39189 if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
39190 if(!keepExisting || this.singleSelect){
39191 this.clearSelections();
39193 var r = this.grid.dataSource.getAt(index);
39194 this.selections.add(r);
39195 this.last = this.lastActive = index;
39196 if(!preventViewNotify){
39197 this.grid.getView().onRowSelect(index);
39199 this.fireEvent("rowselect", this, index, r);
39200 this.fireEvent("selectionchange", this);
39206 * @param {Number} row The index of the row to deselect
39208 deselectRow : function(index, preventViewNotify){
39209 if(this.locked) return;
39210 if(this.last == index){
39213 if(this.lastActive == index){
39214 this.lastActive = false;
39216 var r = this.grid.dataSource.getAt(index);
39217 this.selections.remove(r);
39218 if(!preventViewNotify){
39219 this.grid.getView().onRowDeselect(index);
39221 this.fireEvent("rowdeselect", this, index);
39222 this.fireEvent("selectionchange", this);
39226 restoreLast : function(){
39228 this.last = this._last;
39233 acceptsNav : function(row, col, cm){
39234 return !cm.isHidden(col) && cm.isCellEditable(col, row);
39238 onEditorKey : function(field, e){
39239 var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
39244 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
39246 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
39248 }else if(k == e.ENTER && !e.ctrlKey){
39252 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
39254 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
39256 }else if(k == e.ESC){
39260 g.startEditing(newCell[0], newCell[1]);
39265 * Ext JS Library 1.1.1
39266 * Copyright(c) 2006-2007, Ext JS, LLC.
39268 * Originally Released Under LGPL - original licence link has changed is not relivant.
39271 * <script type="text/javascript">
39274 * @class Roo.grid.CellSelectionModel
39275 * @extends Roo.grid.AbstractSelectionModel
39276 * This class provides the basic implementation for cell selection in a grid.
39278 * @param {Object} config The object containing the configuration of this model.
39279 * @cfg {Boolean} enter_is_tab Enter behaves the same as tab. (eg. goes to next cell) default: false
39281 Roo.grid.CellSelectionModel = function(config){
39282 Roo.apply(this, config);
39284 this.selection = null;
39288 * @event beforerowselect
39289 * Fires before a cell is selected.
39290 * @param {SelectionModel} this
39291 * @param {Number} rowIndex The selected row index
39292 * @param {Number} colIndex The selected cell index
39294 "beforecellselect" : true,
39296 * @event cellselect
39297 * Fires when a cell is selected.
39298 * @param {SelectionModel} this
39299 * @param {Number} rowIndex The selected row index
39300 * @param {Number} colIndex The selected cell index
39302 "cellselect" : true,
39304 * @event selectionchange
39305 * Fires when the active selection changes.
39306 * @param {SelectionModel} this
39307 * @param {Object} selection null for no selection or an object (o) with two properties
39309 <li>o.record: the record object for the row the selection is in</li>
39310 <li>o.cell: An array of [rowIndex, columnIndex]</li>
39313 "selectionchange" : true,
39316 * Fires when the tab (or enter) was pressed on the last editable cell
39317 * You can use this to trigger add new row.
39318 * @param {SelectionModel} this
39322 * @event beforeeditnext
39323 * Fires before the next editable sell is made active
39324 * You can use this to skip to another cell or fire the tabend
39325 * if you set cell to false
39326 * @param {Object} eventdata object : { cell : [ row, col ] }
39328 "beforeeditnext" : true
39330 Roo.grid.CellSelectionModel.superclass.constructor.call(this);
39333 Roo.extend(Roo.grid.CellSelectionModel, Roo.grid.AbstractSelectionModel, {
39335 enter_is_tab: false,
39338 initEvents : function(){
39339 this.grid.on("mousedown", this.handleMouseDown, this);
39340 this.grid.getGridEl().on(Roo.isIE ? "keydown" : "keypress", this.handleKeyDown, this);
39341 var view = this.grid.view;
39342 view.on("refresh", this.onViewChange, this);
39343 view.on("rowupdated", this.onRowUpdated, this);
39344 view.on("beforerowremoved", this.clearSelections, this);
39345 view.on("beforerowsinserted", this.clearSelections, this);
39346 if(this.grid.isEditor){
39347 this.grid.on("beforeedit", this.beforeEdit, this);
39352 beforeEdit : function(e){
39353 this.select(e.row, e.column, false, true, e.record);
39357 onRowUpdated : function(v, index, r){
39358 if(this.selection && this.selection.record == r){
39359 v.onCellSelect(index, this.selection.cell[1]);
39364 onViewChange : function(){
39365 this.clearSelections(true);
39369 * Returns the currently selected cell,.
39370 * @return {Array} The selected cell (row, column) or null if none selected.
39372 getSelectedCell : function(){
39373 return this.selection ? this.selection.cell : null;
39377 * Clears all selections.
39378 * @param {Boolean} true to prevent the gridview from being notified about the change.
39380 clearSelections : function(preventNotify){
39381 var s = this.selection;
39383 if(preventNotify !== true){
39384 this.grid.view.onCellDeselect(s.cell[0], s.cell[1]);
39386 this.selection = null;
39387 this.fireEvent("selectionchange", this, null);
39392 * Returns true if there is a selection.
39393 * @return {Boolean}
39395 hasSelection : function(){
39396 return this.selection ? true : false;
39400 handleMouseDown : function(e, t){
39401 var v = this.grid.getView();
39402 if(this.isLocked()){
39405 var row = v.findRowIndex(t);
39406 var cell = v.findCellIndex(t);
39407 if(row !== false && cell !== false){
39408 this.select(row, cell);
39414 * @param {Number} rowIndex
39415 * @param {Number} collIndex
39417 select : function(rowIndex, colIndex, preventViewNotify, preventFocus, /*internal*/ r){
39418 if(this.fireEvent("beforecellselect", this, rowIndex, colIndex) !== false){
39419 this.clearSelections();
39420 r = r || this.grid.dataSource.getAt(rowIndex);
39423 cell : [rowIndex, colIndex]
39425 if(!preventViewNotify){
39426 var v = this.grid.getView();
39427 v.onCellSelect(rowIndex, colIndex);
39428 if(preventFocus !== true){
39429 v.focusCell(rowIndex, colIndex);
39432 this.fireEvent("cellselect", this, rowIndex, colIndex);
39433 this.fireEvent("selectionchange", this, this.selection);
39438 isSelectable : function(rowIndex, colIndex, cm){
39439 return !cm.isHidden(colIndex);
39443 handleKeyDown : function(e){
39444 //Roo.log('Cell Sel Model handleKeyDown');
39445 if(!e.isNavKeyPress()){
39448 var g = this.grid, s = this.selection;
39451 var cell = g.walkCells(0, 0, 1, this.isSelectable, this);
39453 this.select(cell[0], cell[1]);
39458 var walk = function(row, col, step){
39459 return g.walkCells(row, col, step, sm.isSelectable, sm);
39461 var k = e.getKey(), r = s.cell[0], c = s.cell[1];
39468 // handled by onEditorKey
39469 if (g.isEditor && g.editing) {
39473 newCell = walk(r, c-1, -1);
39475 newCell = walk(r, c+1, 1);
39480 newCell = walk(r+1, c, 1);
39484 newCell = walk(r-1, c, -1);
39488 newCell = walk(r, c+1, 1);
39492 newCell = walk(r, c-1, -1);
39497 if(g.isEditor && !g.editing){
39498 g.startEditing(r, c);
39507 this.select(newCell[0], newCell[1]);
39513 acceptsNav : function(row, col, cm){
39514 return !cm.isHidden(col) && cm.isCellEditable(col, row);
39518 * @param {Number} field (not used) - as it's normally used as a listener
39519 * @param {Number} e - event - fake it by using
39521 * var e = Roo.EventObjectImpl.prototype;
39522 * e.keyCode = e.TAB
39526 onEditorKey : function(field, e){
39528 var k = e.getKey(),
39531 ed = g.activeEditor,
39533 ///Roo.log('onEditorKey' + k);
39536 if (this.enter_is_tab && k == e.ENTER) {
39542 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
39544 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
39550 } else if(k == e.ENTER && !e.ctrlKey){
39553 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
39555 } else if(k == e.ESC){
39560 var ecall = { cell : newCell, forward : forward };
39561 this.fireEvent('beforeeditnext', ecall );
39562 newCell = ecall.cell;
39563 forward = ecall.forward;
39567 //Roo.log('next cell after edit');
39568 g.startEditing.defer(100, g, [newCell[0], newCell[1]]);
39569 } else if (forward) {
39570 // tabbed past last
39571 this.fireEvent.defer(100, this, ['tabend',this]);
39576 * Ext JS Library 1.1.1
39577 * Copyright(c) 2006-2007, Ext JS, LLC.
39579 * Originally Released Under LGPL - original licence link has changed is not relivant.
39582 * <script type="text/javascript">
39586 * @class Roo.grid.EditorGrid
39587 * @extends Roo.grid.Grid
39588 * Class for creating and editable grid.
39589 * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
39590 * The container MUST have some type of size defined for the grid to fill. The container will be
39591 * automatically set to position relative if it isn't already.
39592 * @param {Object} dataSource The data model to bind to
39593 * @param {Object} colModel The column model with info about this grid's columns
39595 Roo.grid.EditorGrid = function(container, config){
39596 Roo.grid.EditorGrid.superclass.constructor.call(this, container, config);
39597 this.getGridEl().addClass("xedit-grid");
39599 if(!this.selModel){
39600 this.selModel = new Roo.grid.CellSelectionModel();
39603 this.activeEditor = null;
39607 * @event beforeedit
39608 * Fires before cell editing is triggered. The edit event object has the following properties <br />
39609 * <ul style="padding:5px;padding-left:16px;">
39610 * <li>grid - This grid</li>
39611 * <li>record - The record being edited</li>
39612 * <li>field - The field name being edited</li>
39613 * <li>value - The value for the field being edited.</li>
39614 * <li>row - The grid row index</li>
39615 * <li>column - The grid column index</li>
39616 * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
39618 * @param {Object} e An edit event (see above for description)
39620 "beforeedit" : true,
39623 * Fires after a cell is edited. <br />
39624 * <ul style="padding:5px;padding-left:16px;">
39625 * <li>grid - This grid</li>
39626 * <li>record - The record being edited</li>
39627 * <li>field - The field name being edited</li>
39628 * <li>value - The value being set</li>
39629 * <li>originalValue - The original value for the field, before the edit.</li>
39630 * <li>row - The grid row index</li>
39631 * <li>column - The grid column index</li>
39633 * @param {Object} e An edit event (see above for description)
39635 "afteredit" : true,
39637 * @event validateedit
39638 * Fires after a cell is edited, but before the value is set in the record.
39639 * You can use this to modify the value being set in the field, Return false
39640 * to cancel the change. The edit event object has the following properties <br />
39641 * <ul style="padding:5px;padding-left:16px;">
39642 * <li>editor - This editor</li>
39643 * <li>grid - This grid</li>
39644 * <li>record - The record being edited</li>
39645 * <li>field - The field name being edited</li>
39646 * <li>value - The value being set</li>
39647 * <li>originalValue - The original value for the field, before the edit.</li>
39648 * <li>row - The grid row index</li>
39649 * <li>column - The grid column index</li>
39650 * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
39652 * @param {Object} e An edit event (see above for description)
39654 "validateedit" : true
39656 this.on("bodyscroll", this.stopEditing, this);
39657 this.on(this.clicksToEdit == 1 ? "cellclick" : "celldblclick", this.onCellDblClick, this);
39660 Roo.extend(Roo.grid.EditorGrid, Roo.grid.Grid, {
39662 * @cfg {Number} clicksToEdit
39663 * The number of clicks on a cell required to display the cell's editor (defaults to 2)
39670 trackMouseOver: false, // causes very odd FF errors
39672 onCellDblClick : function(g, row, col){
39673 this.startEditing(row, col);
39676 onEditComplete : function(ed, value, startValue){
39677 this.editing = false;
39678 this.activeEditor = null;
39679 ed.un("specialkey", this.selModel.onEditorKey, this.selModel);
39681 var field = this.colModel.getDataIndex(ed.col);
39686 originalValue: startValue,
39693 var cell = Roo.get(this.view.getCell(ed.row,ed.col))
39696 if(String(value) !== String(startValue)){
39698 if(this.fireEvent("validateedit", e) !== false && !e.cancel){
39699 r.set(field, e.value);
39700 // if we are dealing with a combo box..
39701 // then we also set the 'name' colum to be the displayField
39702 if (ed.field.displayField && ed.field.name) {
39703 r.set(ed.field.name, ed.field.el.dom.value);
39706 delete e.cancel; //?? why!!!
39707 this.fireEvent("afteredit", e);
39710 this.fireEvent("afteredit", e); // always fire it!
39712 this.view.focusCell(ed.row, ed.col);
39716 * Starts editing the specified for the specified row/column
39717 * @param {Number} rowIndex
39718 * @param {Number} colIndex
39720 startEditing : function(row, col){
39721 this.stopEditing();
39722 if(this.colModel.isCellEditable(col, row)){
39723 this.view.ensureVisible(row, col, true);
39725 var r = this.dataSource.getAt(row);
39726 var field = this.colModel.getDataIndex(col);
39727 var cell = Roo.get(this.view.getCell(row,col));
39732 value: r.data[field],
39737 if(this.fireEvent("beforeedit", e) !== false && !e.cancel){
39738 this.editing = true;
39739 var ed = this.colModel.getCellEditor(col, row);
39745 ed.render(ed.parentEl || document.body);
39751 (function(){ // complex but required for focus issues in safari, ie and opera
39755 ed.on("complete", this.onEditComplete, this, {single: true});
39756 ed.on("specialkey", this.selModel.onEditorKey, this.selModel);
39757 this.activeEditor = ed;
39758 var v = r.data[field];
39759 ed.startEdit(this.view.getCell(row, col), v);
39760 // combo's with 'displayField and name set
39761 if (ed.field.displayField && ed.field.name) {
39762 ed.field.el.dom.value = r.data[ed.field.name];
39766 }).defer(50, this);
39772 * Stops any active editing
39774 stopEditing : function(){
39775 if(this.activeEditor){
39776 this.activeEditor.completeEdit();
39778 this.activeEditor = null;
39782 * Called to get grid's drag proxy text, by default returns this.ddText.
39785 getDragDropText : function(){
39786 var count = this.selModel.getSelectedCell() ? 1 : 0;
39787 return String.format(this.ddText, count, count == 1 ? '' : 's');
39792 * Ext JS Library 1.1.1
39793 * Copyright(c) 2006-2007, Ext JS, LLC.
39795 * Originally Released Under LGPL - original licence link has changed is not relivant.
39798 * <script type="text/javascript">
39801 // private - not really -- you end up using it !
39802 // This is a support class used internally by the Grid components
39805 * @class Roo.grid.GridEditor
39806 * @extends Roo.Editor
39807 * Class for creating and editable grid elements.
39808 * @param {Object} config any settings (must include field)
39810 Roo.grid.GridEditor = function(field, config){
39811 if (!config && field.field) {
39813 field = Roo.factory(config.field, Roo.form);
39815 Roo.grid.GridEditor.superclass.constructor.call(this, field, config);
39816 field.monitorTab = false;
39819 Roo.extend(Roo.grid.GridEditor, Roo.Editor, {
39822 * @cfg {Roo.form.Field} field Field to wrap (or xtyped)
39825 alignment: "tl-tl",
39828 cls: "x-small-editor x-grid-editor",
39833 * Ext JS Library 1.1.1
39834 * Copyright(c) 2006-2007, Ext JS, LLC.
39836 * Originally Released Under LGPL - original licence link has changed is not relivant.
39839 * <script type="text/javascript">
39844 Roo.grid.PropertyRecord = Roo.data.Record.create([
39845 {name:'name',type:'string'}, 'value'
39849 Roo.grid.PropertyStore = function(grid, source){
39851 this.store = new Roo.data.Store({
39852 recordType : Roo.grid.PropertyRecord
39854 this.store.on('update', this.onUpdate, this);
39856 this.setSource(source);
39858 Roo.grid.PropertyStore.superclass.constructor.call(this);
39863 Roo.extend(Roo.grid.PropertyStore, Roo.util.Observable, {
39864 setSource : function(o){
39866 this.store.removeAll();
39869 if(this.isEditableValue(o[k])){
39870 data.push(new Roo.grid.PropertyRecord({name: k, value: o[k]}, k));
39873 this.store.loadRecords({records: data}, {}, true);
39876 onUpdate : function(ds, record, type){
39877 if(type == Roo.data.Record.EDIT){
39878 var v = record.data['value'];
39879 var oldValue = record.modified['value'];
39880 if(this.grid.fireEvent('beforepropertychange', this.source, record.id, v, oldValue) !== false){
39881 this.source[record.id] = v;
39883 this.grid.fireEvent('propertychange', this.source, record.id, v, oldValue);
39890 getProperty : function(row){
39891 return this.store.getAt(row);
39894 isEditableValue: function(val){
39895 if(val && val instanceof Date){
39897 }else if(typeof val == 'object' || typeof val == 'function'){
39903 setValue : function(prop, value){
39904 this.source[prop] = value;
39905 this.store.getById(prop).set('value', value);
39908 getSource : function(){
39909 return this.source;
39913 Roo.grid.PropertyColumnModel = function(grid, store){
39916 g.PropertyColumnModel.superclass.constructor.call(this, [
39917 {header: this.nameText, sortable: true, dataIndex:'name', id: 'name'},
39918 {header: this.valueText, resizable:false, dataIndex: 'value', id: 'value'}
39920 this.store = store;
39921 this.bselect = Roo.DomHelper.append(document.body, {
39922 tag: 'select', style:'display:none', cls: 'x-grid-editor', children: [
39923 {tag: 'option', value: 'true', html: 'true'},
39924 {tag: 'option', value: 'false', html: 'false'}
39927 Roo.id(this.bselect);
39930 'date' : new g.GridEditor(new f.DateField({selectOnFocus:true})),
39931 'string' : new g.GridEditor(new f.TextField({selectOnFocus:true})),
39932 'number' : new g.GridEditor(new f.NumberField({selectOnFocus:true, style:'text-align:left;'})),
39933 'int' : new g.GridEditor(new f.NumberField({selectOnFocus:true, allowDecimals:false, style:'text-align:left;'})),
39934 'boolean' : new g.GridEditor(new f.Field({el:this.bselect,selectOnFocus:true}))
39936 this.renderCellDelegate = this.renderCell.createDelegate(this);
39937 this.renderPropDelegate = this.renderProp.createDelegate(this);
39940 Roo.extend(Roo.grid.PropertyColumnModel, Roo.grid.ColumnModel, {
39944 valueText : 'Value',
39946 dateFormat : 'm/j/Y',
39949 renderDate : function(dateVal){
39950 return dateVal.dateFormat(this.dateFormat);
39953 renderBool : function(bVal){
39954 return bVal ? 'true' : 'false';
39957 isCellEditable : function(colIndex, rowIndex){
39958 return colIndex == 1;
39961 getRenderer : function(col){
39963 this.renderCellDelegate : this.renderPropDelegate;
39966 renderProp : function(v){
39967 return this.getPropertyName(v);
39970 renderCell : function(val){
39972 if(val instanceof Date){
39973 rv = this.renderDate(val);
39974 }else if(typeof val == 'boolean'){
39975 rv = this.renderBool(val);
39977 return Roo.util.Format.htmlEncode(rv);
39980 getPropertyName : function(name){
39981 var pn = this.grid.propertyNames;
39982 return pn && pn[name] ? pn[name] : name;
39985 getCellEditor : function(colIndex, rowIndex){
39986 var p = this.store.getProperty(rowIndex);
39987 var n = p.data['name'], val = p.data['value'];
39989 if(typeof(this.grid.customEditors[n]) == 'string'){
39990 return this.editors[this.grid.customEditors[n]];
39992 if(typeof(this.grid.customEditors[n]) != 'undefined'){
39993 return this.grid.customEditors[n];
39995 if(val instanceof Date){
39996 return this.editors['date'];
39997 }else if(typeof val == 'number'){
39998 return this.editors['number'];
39999 }else if(typeof val == 'boolean'){
40000 return this.editors['boolean'];
40002 return this.editors['string'];
40008 * @class Roo.grid.PropertyGrid
40009 * @extends Roo.grid.EditorGrid
40010 * This class represents the interface of a component based property grid control.
40011 * <br><br>Usage:<pre><code>
40012 var grid = new Roo.grid.PropertyGrid("my-container-id", {
40020 * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
40021 * The container MUST have some type of size defined for the grid to fill. The container will be
40022 * automatically set to position relative if it isn't already.
40023 * @param {Object} config A config object that sets properties on this grid.
40025 Roo.grid.PropertyGrid = function(container, config){
40026 config = config || {};
40027 var store = new Roo.grid.PropertyStore(this);
40028 this.store = store;
40029 var cm = new Roo.grid.PropertyColumnModel(this, store);
40030 store.store.sort('name', 'ASC');
40031 Roo.grid.PropertyGrid.superclass.constructor.call(this, container, Roo.apply({
40034 enableColLock:false,
40035 enableColumnMove:false,
40037 trackMouseOver: false,
40040 this.getGridEl().addClass('x-props-grid');
40041 this.lastEditRow = null;
40042 this.on('columnresize', this.onColumnResize, this);
40045 * @event beforepropertychange
40046 * Fires before a property changes (return false to stop?)
40047 * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
40048 * @param {String} id Record Id
40049 * @param {String} newval New Value
40050 * @param {String} oldval Old Value
40052 "beforepropertychange": true,
40054 * @event propertychange
40055 * Fires after a property changes
40056 * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
40057 * @param {String} id Record Id
40058 * @param {String} newval New Value
40059 * @param {String} oldval Old Value
40061 "propertychange": true
40063 this.customEditors = this.customEditors || {};
40065 Roo.extend(Roo.grid.PropertyGrid, Roo.grid.EditorGrid, {
40068 * @cfg {Object} customEditors map of colnames=> custom editors.
40069 * the custom editor can be one of the standard ones (date|string|number|int|boolean), or a
40070 * grid editor eg. Roo.grid.GridEditor(new Roo.form.TextArea({selectOnFocus:true})),
40071 * false disables editing of the field.
40075 * @cfg {Object} propertyNames map of property Names to their displayed value
40078 render : function(){
40079 Roo.grid.PropertyGrid.superclass.render.call(this);
40080 this.autoSize.defer(100, this);
40083 autoSize : function(){
40084 Roo.grid.PropertyGrid.superclass.autoSize.call(this);
40086 this.view.fitColumns();
40090 onColumnResize : function(){
40091 this.colModel.setColumnWidth(1, this.container.getWidth(true)-this.colModel.getColumnWidth(0));
40095 * Sets the data for the Grid
40096 * accepts a Key => Value object of all the elements avaiable.
40097 * @param {Object} data to appear in grid.
40099 setSource : function(source){
40100 this.store.setSource(source);
40104 * Gets all the data from the grid.
40105 * @return {Object} data data stored in grid
40107 getSource : function(){
40108 return this.store.getSource();
40112 * Ext JS Library 1.1.1
40113 * Copyright(c) 2006-2007, Ext JS, LLC.
40115 * Originally Released Under LGPL - original licence link has changed is not relivant.
40118 * <script type="text/javascript">
40122 * @class Roo.LoadMask
40123 * A simple utility class for generically masking elements while loading data. If the element being masked has
40124 * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
40125 * process and the mask element will be cached for reuse. For all other elements, this mask will replace the
40126 * element's UpdateManager load indicator and will be destroyed after the initial load.
40128 * Create a new LoadMask
40129 * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
40130 * @param {Object} config The config object
40132 Roo.LoadMask = function(el, config){
40133 this.el = Roo.get(el);
40134 Roo.apply(this, config);
40136 this.store.on('beforeload', this.onBeforeLoad, this);
40137 this.store.on('load', this.onLoad, this);
40138 this.store.on('loadexception', this.onLoadException, this);
40139 this.removeMask = false;
40141 var um = this.el.getUpdateManager();
40142 um.showLoadIndicator = false; // disable the default indicator
40143 um.on('beforeupdate', this.onBeforeLoad, this);
40144 um.on('update', this.onLoad, this);
40145 um.on('failure', this.onLoad, this);
40146 this.removeMask = true;
40150 Roo.LoadMask.prototype = {
40152 * @cfg {Boolean} removeMask
40153 * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
40154 * False to persist the mask element reference for multiple uses (e.g., for paged data widgets). Defaults to false.
40157 * @cfg {String} msg
40158 * The text to display in a centered loading message box (defaults to 'Loading...')
40160 msg : 'Loading...',
40162 * @cfg {String} msgCls
40163 * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
40165 msgCls : 'x-mask-loading',
40168 * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
40174 * Disables the mask to prevent it from being displayed
40176 disable : function(){
40177 this.disabled = true;
40181 * Enables the mask so that it can be displayed
40183 enable : function(){
40184 this.disabled = false;
40187 onLoadException : function()
40189 Roo.log(arguments);
40191 if (typeof(arguments[3]) != 'undefined') {
40192 Roo.MessageBox.alert("Error loading",arguments[3]);
40196 if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
40197 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
40206 this.el.unmask(this.removeMask);
40209 onLoad : function()
40211 this.el.unmask(this.removeMask);
40215 onBeforeLoad : function(){
40216 if(!this.disabled){
40217 this.el.mask(this.msg, this.msgCls);
40222 destroy : function(){
40224 this.store.un('beforeload', this.onBeforeLoad, this);
40225 this.store.un('load', this.onLoad, this);
40226 this.store.un('loadexception', this.onLoadException, this);
40228 var um = this.el.getUpdateManager();
40229 um.un('beforeupdate', this.onBeforeLoad, this);
40230 um.un('update', this.onLoad, this);
40231 um.un('failure', this.onLoad, this);
40236 * Ext JS Library 1.1.1
40237 * Copyright(c) 2006-2007, Ext JS, LLC.
40239 * Originally Released Under LGPL - original licence link has changed is not relivant.
40242 * <script type="text/javascript">
40247 * @class Roo.XTemplate
40248 * @extends Roo.Template
40249 * Provides a template that can have nested templates for loops or conditionals. The syntax is:
40251 var t = new Roo.XTemplate(
40252 '<select name="{name}">',
40253 '<tpl for="options"><option value="{value:trim}">{text:ellipsis(10)}</option></tpl>',
40257 // then append, applying the master template values
40260 * Supported features:
40265 {a_variable} - output encoded.
40266 {a_variable.format:("Y-m-d")} - call a method on the variable
40267 {a_variable:raw} - unencoded output
40268 {a_variable:toFixed(1,2)} - Roo.util.Format."toFixed"
40269 {a_variable:this.method_on_template(...)} - call a method on the template object.
40274 <tpl for="a_variable or condition.."></tpl>
40275 <tpl if="a_variable or condition"></tpl>
40276 <tpl exec="some javascript"></tpl>
40277 <tpl name="named_template"></tpl> (experimental)
40279 <tpl for="."></tpl> - just iterate the property..
40280 <tpl for=".."></tpl> - iterates with the parent (probably the template)
40284 Roo.XTemplate = function()
40286 Roo.XTemplate.superclass.constructor.apply(this, arguments);
40293 Roo.extend(Roo.XTemplate, Roo.Template, {
40296 * The various sub templates
40301 * basic tag replacing syntax
40304 * // you can fake an object call by doing this
40308 re : /\{([\w-\.]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
40311 * compile the template
40313 * This is not recursive, so I'm not sure how nested templates are really going to be handled..
40316 compile: function()
40320 s = ['<tpl>', s, '</tpl>'].join('');
40322 var re = /<tpl\b[^>]*>((?:(?=([^<]+))\2|<(?!tpl\b[^>]*>))*?)<\/tpl>/,
40323 nameRe = /^<tpl\b[^>]*?for="(.*?)"/,
40324 ifRe = /^<tpl\b[^>]*?if="(.*?)"/,
40325 execRe = /^<tpl\b[^>]*?exec="(.*?)"/,
40326 namedRe = /^<tpl\b[^>]*?name="(\w+)"/, // named templates..
40331 while(true == !!(m = s.match(re))){
40332 var forMatch = m[0].match(nameRe),
40333 ifMatch = m[0].match(ifRe),
40334 execMatch = m[0].match(execRe),
40335 namedMatch = m[0].match(namedRe),
40340 name = forMatch && forMatch[1] ? forMatch[1] : '';
40343 // if - puts fn into test..
40344 exp = ifMatch && ifMatch[1] ? ifMatch[1] : null;
40346 fn = new Function('values', 'parent', 'with(values){ return '+(Roo.util.Format.htmlDecode(exp))+'; }');
40351 // exec - calls a function... returns empty if true is returned.
40352 exp = execMatch && execMatch[1] ? execMatch[1] : null;
40354 exec = new Function('values', 'parent', 'with(values){ '+(Roo.util.Format.htmlDecode(exp))+'; }');
40362 case '.': name = new Function('values', 'parent', 'with(values){ return values; }'); break;
40363 case '..': name = new Function('values', 'parent', 'with(values){ return parent; }'); break;
40364 default: name = new Function('values', 'parent', 'with(values){ return '+name+'; }');
40367 var uid = namedMatch ? namedMatch[1] : id;
40371 id: namedMatch ? namedMatch[1] : id,
40378 s = s.replace(m[0], '');
40380 s = s.replace(m[0], '{xtpl'+ id + '}');
40385 for(var i = tpls.length-1; i >= 0; --i){
40386 this.compileTpl(tpls[i]);
40387 this.tpls[tpls[i].id] = tpls[i];
40389 this.master = tpls[tpls.length-1];
40393 * same as applyTemplate, except it's done to one of the subTemplates
40394 * when using named templates, you can do:
40396 * var str = pl.applySubTemplate('your-name', values);
40399 * @param {Number} id of the template
40400 * @param {Object} values to apply to template
40401 * @param {Object} parent (normaly the instance of this object)
40403 applySubTemplate : function(id, values, parent)
40407 var t = this.tpls[id];
40411 if(t.test && !t.test.call(this, values, parent)){
40415 Roo.log("Xtemplate.applySubTemplate 'test': Exception thrown");
40416 Roo.log(e.toString());
40422 if(t.exec && t.exec.call(this, values, parent)){
40426 Roo.log("Xtemplate.applySubTemplate 'exec': Exception thrown");
40427 Roo.log(e.toString());
40432 var vs = t.target ? t.target.call(this, values, parent) : values;
40433 parent = t.target ? values : parent;
40434 if(t.target && vs instanceof Array){
40436 for(var i = 0, len = vs.length; i < len; i++){
40437 buf[buf.length] = t.compiled.call(this, vs[i], parent);
40439 return buf.join('');
40441 return t.compiled.call(this, vs, parent);
40443 Roo.log("Xtemplate.applySubTemplate : Exception thrown");
40444 Roo.log(e.toString());
40445 Roo.log(t.compiled);
40450 compileTpl : function(tpl)
40452 var fm = Roo.util.Format;
40453 var useF = this.disableFormats !== true;
40454 var sep = Roo.isGecko ? "+" : ",";
40455 var undef = function(str) {
40456 Roo.log("Property not found :" + str);
40460 var fn = function(m, name, format, args)
40462 //Roo.log(arguments);
40463 args = args ? args.replace(/\\'/g,"'") : args;
40464 //["{TEST:(a,b,c)}", "TEST", "", "a,b,c", 0, "{TEST:(a,b,c)}"]
40465 if (typeof(format) == 'undefined') {
40466 format= 'htmlEncode';
40468 if (format == 'raw' ) {
40472 if(name.substr(0, 4) == 'xtpl'){
40473 return "'"+ sep +'this.applySubTemplate('+name.substr(4)+', values, parent)'+sep+"'";
40476 // build an array of options to determine if value is undefined..
40478 // basically get 'xxxx.yyyy' then do
40479 // (typeof(xxxx) == 'undefined' || typeof(xxx.yyyy) == 'undefined') ?
40480 // (function () { Roo.log("Property not found"); return ''; })() :
40485 Roo.each(name.split('.'), function(st) {
40486 lookfor += (lookfor.length ? '.': '') + st;
40487 udef_ar.push( "(typeof(" + lookfor + ") == 'undefined')" );
40490 var udef_st = '((' + udef_ar.join(" || ") +") ? undef('" + name + "') : "; // .. needs )
40493 if(format && useF){
40495 args = args ? ',' + args : "";
40497 if(format.substr(0, 5) != "this."){
40498 format = "fm." + format + '(';
40500 format = 'this.call("'+ format.substr(5) + '", ';
40504 return "'"+ sep + udef_st + format + name + args + "))"+sep+"'";
40508 // called with xxyx.yuu:(test,test)
40510 return "'"+ sep + udef_st + name + '(' + args + "))"+sep+"'";
40512 // raw.. - :raw modifier..
40513 return "'"+ sep + udef_st + name + ")"+sep+"'";
40517 // branched to use + in gecko and [].join() in others
40519 body = "tpl.compiled = function(values, parent){ with(values) { return '" +
40520 tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
40523 body = ["tpl.compiled = function(values, parent){ with (values) { return ['"];
40524 body.push(tpl.body.replace(/(\r\n|\n)/g,
40525 '\\n').replace(/'/g, "\\'").replace(this.re, fn));
40526 body.push("'].join('');};};");
40527 body = body.join('');
40530 Roo.debug && Roo.log(body.replace(/\\n/,'\n'));
40532 /** eval:var:tpl eval:var:fm eval:var:useF eval:var:undef */
40538 applyTemplate : function(values){
40539 return this.master.compiled.call(this, values, {});
40540 //var s = this.subs;
40543 apply : function(){
40544 return this.applyTemplate.apply(this, arguments);
40549 Roo.XTemplate.from = function(el){
40550 el = Roo.getDom(el);
40551 return new Roo.XTemplate(el.value || el.innerHTML);
40553 * Original code for Roojs - LGPL
40554 * <script type="text/javascript">
40558 * @class Roo.XComponent
40559 * A delayed Element creator...
40560 * Or a way to group chunks of interface together.
40562 * Mypart.xyx = new Roo.XComponent({
40564 parent : 'Mypart.xyz', // empty == document.element.!!
40568 disabled : function() {}
40570 tree : function() { // return an tree of xtype declared components
40574 xtype : 'NestedLayoutPanel',
40581 * It can be used to build a big heiracy, with parent etc.
40582 * or you can just use this to render a single compoent to a dom element
40583 * MYPART.render(Roo.Element | String(id) | dom_element )
40585 * @extends Roo.util.Observable
40587 * @param cfg {Object} configuration of component
40590 Roo.XComponent = function(cfg) {
40591 Roo.apply(this, cfg);
40595 * Fires when this the componnt is built
40596 * @param {Roo.XComponent} c the component
40601 this.region = this.region || 'center'; // default..
40602 Roo.XComponent.register(this);
40603 this.modules = false;
40604 this.el = false; // where the layout goes..
40608 Roo.extend(Roo.XComponent, Roo.util.Observable, {
40611 * The created element (with Roo.factory())
40612 * @type {Roo.Layout}
40618 * for BC - use el in new code
40619 * @type {Roo.Layout}
40625 * for BC - use el in new code
40626 * @type {Roo.Layout}
40631 * @cfg {Function|boolean} disabled
40632 * If this module is disabled by some rule, return true from the funtion
40637 * @cfg {String} parent
40638 * Name of parent element which it get xtype added to..
40643 * @cfg {String} order
40644 * Used to set the order in which elements are created (usefull for multiple tabs)
40649 * @cfg {String} name
40650 * String to display while loading.
40654 * @cfg {String} region
40655 * Region to render component to (defaults to center)
40660 * @cfg {Array} items
40661 * A single item array - the first element is the root of the tree..
40662 * It's done this way to stay compatible with the Xtype system...
40668 * The method that retuns the tree of parts that make up this compoennt
40675 * render element to dom or tree
40676 * @param {Roo.Element|String|DomElement} optional render to if parent is not set.
40679 render : function(el)
40683 var hp = this.parent ? 1 : 0;
40685 if (!el && typeof(this.parent) == 'string' && this.parent.substring(0,1) == '#') {
40686 // if parent is a '#.....' string, then let's use that..
40687 var ename = this.parent.substr(1)
40688 this.parent = false;
40689 el = Roo.get(ename);
40691 Roo.log("Warning - element can not be found :#" + ename );
40697 if (!this.parent) {
40699 el = el ? Roo.get(el) : false;
40701 // it's a top level one..
40703 el : new Roo.BorderLayout(el || document.body, {
40709 tabPosition: 'top',
40710 //resizeTabs: true,
40711 alwaysShowTabs: el && hp? false : true,
40712 hideTabs: el || !hp ? true : false,
40719 if (!this.parent.el) {
40720 // probably an old style ctor, which has been disabled.
40724 // The 'tree' method is '_tree now'
40726 var tree = this._tree ? this._tree() : this.tree();
40727 tree.region = tree.region || this.region;
40728 this.el = this.parent.el.addxtype(tree);
40729 this.fireEvent('built', this);
40731 this.panel = this.el;
40732 this.layout = this.panel.layout;
40733 this.parentLayout = this.parent.layout || false;
40739 Roo.apply(Roo.XComponent, {
40741 * @property hideProgress
40742 * true to disable the building progress bar.. usefull on single page renders.
40745 hideProgress : false,
40747 * @property buildCompleted
40748 * True when the builder has completed building the interface.
40751 buildCompleted : false,
40754 * @property topModule
40755 * the upper most module - uses document.element as it's constructor.
40762 * @property modules
40763 * array of modules to be created by registration system.
40764 * @type {Array} of Roo.XComponent
40769 * @property elmodules
40770 * array of modules to be created by which use #ID
40771 * @type {Array} of Roo.XComponent
40778 * Register components to be built later.
40780 * This solves the following issues
40781 * - Building is not done on page load, but after an authentication process has occured.
40782 * - Interface elements are registered on page load
40783 * - Parent Interface elements may not be loaded before child, so this handles that..
40790 module : 'Pman.Tab.projectMgr',
40792 parent : 'Pman.layout',
40793 disabled : false, // or use a function..
40796 * * @param {Object} details about module
40798 register : function(obj) {
40800 Roo.XComponent.event.fireEvent('register', obj);
40801 switch(typeof(obj.disabled) ) {
40807 if ( obj.disabled() ) {
40813 if (obj.disabled) {
40819 this.modules.push(obj);
40823 * convert a string to an object..
40824 * eg. 'AAA.BBB' -> finds AAA.BBB
40828 toObject : function(str)
40830 if (!str || typeof(str) == 'object') {
40833 if (str.substring(0,1) == '#') {
40837 var ar = str.split('.');
40842 eval('if (typeof ' + rt + ' == "undefined"){ o = false;} o = ' + rt + ';');
40844 throw "Module not found : " + str;
40848 throw "Module not found : " + str;
40850 Roo.each(ar, function(e) {
40851 if (typeof(o[e]) == 'undefined') {
40852 throw "Module not found : " + str;
40863 * move modules into their correct place in the tree..
40866 preBuild : function ()
40869 Roo.each(this.modules , function (obj)
40871 Roo.XComponent.event.fireEvent('beforebuild', obj);
40873 var opar = obj.parent;
40875 obj.parent = this.toObject(opar);
40877 Roo.log("parent:toObject failed: " + e.toString());
40882 Roo.debug && Roo.log("GOT top level module");
40883 Roo.debug && Roo.log(obj);
40884 obj.modules = new Roo.util.MixedCollection(false,
40885 function(o) { return o.order + '' }
40887 this.topModule = obj;
40890 // parent is a string (usually a dom element name..)
40891 if (typeof(obj.parent) == 'string') {
40892 this.elmodules.push(obj);
40895 if (obj.parent.constructor != Roo.XComponent) {
40896 Roo.log("Warning : Object Parent is not instance of XComponent:" + obj.name)
40898 if (!obj.parent.modules) {
40899 obj.parent.modules = new Roo.util.MixedCollection(false,
40900 function(o) { return o.order + '' }
40903 if (obj.parent.disabled) {
40904 obj.disabled = true;
40906 obj.parent.modules.add(obj);
40911 * make a list of modules to build.
40912 * @return {Array} list of modules.
40915 buildOrder : function()
40918 var cmp = function(a,b) {
40919 return String(a).toUpperCase() > String(b).toUpperCase() ? 1 : -1;
40921 if ((!this.topModule || !this.topModule.modules) && !this.elmodules.length) {
40922 throw "No top level modules to build";
40925 // make a flat list in order of modules to build.
40926 var mods = this.topModule ? [ this.topModule ] : [];
40929 // elmodules (is a list of DOM based modules )
40930 Roo.each(this.elmodules, function(e) {
40932 if (!this.topModule &&
40933 typeof(e.parent) == 'string' &&
40934 e.parent.substring(0,1) == '#' &&
40935 Roo.get(e.parent.substr(1))
40938 _this.topModule = e;
40944 // add modules to their parents..
40945 var addMod = function(m) {
40946 Roo.debug && Roo.log("build Order: add: " + m.name);
40949 if (m.modules && !m.disabled) {
40950 Roo.debug && Roo.log("build Order: " + m.modules.length + " child modules");
40951 m.modules.keySort('ASC', cmp );
40952 Roo.debug && Roo.log("build Order: " + m.modules.length + " child modules (after sort)");
40954 m.modules.each(addMod);
40956 Roo.debug && Roo.log("build Order: no child modules");
40958 // not sure if this is used any more..
40960 m.finalize.name = m.name + " (clean up) ";
40961 mods.push(m.finalize);
40965 if (this.topModule && this.topModule.modules) {
40966 this.topModule.modules.keySort('ASC', cmp );
40967 this.topModule.modules.each(addMod);
40973 * Build the registered modules.
40974 * @param {Object} parent element.
40975 * @param {Function} optional method to call after module has been added.
40983 var mods = this.buildOrder();
40985 //this.allmods = mods;
40986 //Roo.debug && Roo.log(mods);
40988 if (!mods.length) { // should not happen
40989 throw "NO modules!!!";
40993 var msg = "Building Interface...";
40994 // flash it up as modal - so we store the mask!?
40995 if (!this.hideProgress) {
40996 Roo.MessageBox.show({ title: 'loading' });
40997 Roo.MessageBox.show({
40998 title: "Please wait...",
41007 var total = mods.length;
41010 var progressRun = function() {
41011 if (!mods.length) {
41012 Roo.debug && Roo.log('hide?');
41013 if (!this.hideProgress) {
41014 Roo.MessageBox.hide();
41016 Roo.XComponent.event.fireEvent('buildcomplete', _this.topModule);
41022 var m = mods.shift();
41025 Roo.debug && Roo.log(m);
41026 // not sure if this is supported any more.. - modules that are are just function
41027 if (typeof(m) == 'function') {
41029 return progressRun.defer(10, _this);
41033 msg = "Building Interface " + (total - mods.length) +
41035 (m.name ? (' - ' + m.name) : '');
41036 Roo.debug && Roo.log(msg);
41037 if (!this.hideProgress) {
41038 Roo.MessageBox.updateProgress( (total - mods.length)/total, msg );
41042 // is the module disabled?
41043 var disabled = (typeof(m.disabled) == 'function') ?
41044 m.disabled.call(m.module.disabled) : m.disabled;
41048 return progressRun(); // we do not update the display!
41056 // it's 10 on top level, and 1 on others??? why...
41057 return progressRun.defer(10, _this);
41060 progressRun.defer(1, _this);
41074 * wrapper for event.on - aliased later..
41075 * Typically use to register a event handler for register:
41077 * eg. Roo.XComponent.on('register', function(comp) { comp.disable = true } );
41086 Roo.XComponent.event = new Roo.util.Observable({
41090 * Fires when an Component is registered,
41091 * set the disable property on the Component to stop registration.
41092 * @param {Roo.XComponent} c the component being registerd.
41097 * @event beforebuild
41098 * Fires before each Component is built
41099 * can be used to apply permissions.
41100 * @param {Roo.XComponent} c the component being registerd.
41103 'beforebuild' : true,
41105 * @event buildcomplete
41106 * Fires on the top level element when all elements have been built
41107 * @param {Roo.XComponent} the top level component.
41109 'buildcomplete' : true
41114 Roo.XComponent.on = Roo.XComponent.event.on.createDelegate(Roo.XComponent.event);