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){
826 if (!Roo.isTouch && this.primaryButtonOnly && e.button != 0) {
827 //Roo.log('not touch/ button !=0');
830 if (e.browserEvent.touches && e.browserEvent.touches.length != 1) {
831 return; // double touch..
835 if (this.isLocked()) {
840 this.DDM.refreshCache(this.groups);
841 Roo.log([Roo.lib.Event.getPageX(e), Roo.lib.Event.getPageY(e)]);
842 var pt = new Roo.lib.Point(Roo.lib.Event.getPageX(e), Roo.lib.Event.getPageY(e));
843 if (!this.hasOuterHandles && !this.DDM.isOverTarget(pt, this) ) {
844 //Roo.log('no outer handes or not over target');
847 Roo.log('check validator');
848 if (this.clickValidator(e)) {
849 Roo.log('validate success');
850 // set the initial element position
851 this.setStartPosition();
857 this.DDM.handleMouseDown(e, this);
859 this.DDM.stopEvent(e);
867 clickValidator: function(e) {
868 var target = e.getTarget();
869 return ( this.isValidHandleChild(target) &&
870 (this.id == this.handleElId ||
871 this.DDM.handleWasClicked(target, this.id)) );
875 * Allows you to specify a tag name that should not start a drag operation
876 * when clicked. This is designed to facilitate embedding links within a
877 * drag handle that do something other than start the drag.
878 * @method addInvalidHandleType
879 * @param {string} tagName the type of element to exclude
881 addInvalidHandleType: function(tagName) {
882 var type = tagName.toUpperCase();
883 this.invalidHandleTypes[type] = type;
887 * Lets you to specify an element id for a child of a drag handle
888 * that should not initiate a drag
889 * @method addInvalidHandleId
890 * @param {string} id the element id of the element you wish to ignore
892 addInvalidHandleId: function(id) {
893 if (typeof id !== "string") {
896 this.invalidHandleIds[id] = id;
900 * Lets you specify a css class of elements that will not initiate a drag
901 * @method addInvalidHandleClass
902 * @param {string} cssClass the class of the elements you wish to ignore
904 addInvalidHandleClass: function(cssClass) {
905 this.invalidHandleClasses.push(cssClass);
909 * Unsets an excluded tag name set by addInvalidHandleType
910 * @method removeInvalidHandleType
911 * @param {string} tagName the type of element to unexclude
913 removeInvalidHandleType: function(tagName) {
914 var type = tagName.toUpperCase();
915 // this.invalidHandleTypes[type] = null;
916 delete this.invalidHandleTypes[type];
920 * Unsets an invalid handle id
921 * @method removeInvalidHandleId
922 * @param {string} id the id of the element to re-enable
924 removeInvalidHandleId: function(id) {
925 if (typeof id !== "string") {
928 delete this.invalidHandleIds[id];
932 * Unsets an invalid css class
933 * @method removeInvalidHandleClass
934 * @param {string} cssClass the class of the element(s) you wish to
937 removeInvalidHandleClass: function(cssClass) {
938 for (var i=0, len=this.invalidHandleClasses.length; i<len; ++i) {
939 if (this.invalidHandleClasses[i] == cssClass) {
940 delete this.invalidHandleClasses[i];
946 * Checks the tag exclusion list to see if this click should be ignored
947 * @method isValidHandleChild
948 * @param {HTMLElement} node the HTMLElement to evaluate
949 * @return {boolean} true if this is a valid tag type, false if not
951 isValidHandleChild: function(node) {
954 // var n = (node.nodeName == "#text") ? node.parentNode : node;
957 nodeName = node.nodeName.toUpperCase();
959 nodeName = node.nodeName;
961 valid = valid && !this.invalidHandleTypes[nodeName];
962 valid = valid && !this.invalidHandleIds[node.id];
964 for (var i=0, len=this.invalidHandleClasses.length; valid && i<len; ++i) {
965 valid = !Dom.hasClass(node, this.invalidHandleClasses[i]);
974 * Create the array of horizontal tick marks if an interval was specified
975 * in setXConstraint().
979 setXTicks: function(iStartX, iTickSize) {
981 this.xTickSize = iTickSize;
985 for (var i = this.initPageX; i >= this.minX; i = i - iTickSize) {
987 this.xTicks[this.xTicks.length] = i;
992 for (i = this.initPageX; i <= this.maxX; i = i + iTickSize) {
994 this.xTicks[this.xTicks.length] = i;
999 this.xTicks.sort(this.DDM.numericSort) ;
1003 * Create the array of vertical tick marks if an interval was specified in
1008 setYTicks: function(iStartY, iTickSize) {
1010 this.yTickSize = iTickSize;
1014 for (var i = this.initPageY; i >= this.minY; i = i - iTickSize) {
1016 this.yTicks[this.yTicks.length] = i;
1021 for (i = this.initPageY; i <= this.maxY; i = i + iTickSize) {
1023 this.yTicks[this.yTicks.length] = i;
1028 this.yTicks.sort(this.DDM.numericSort) ;
1032 * By default, the element can be dragged any place on the screen. Use
1033 * this method to limit the horizontal travel of the element. Pass in
1034 * 0,0 for the parameters if you want to lock the drag to the y axis.
1035 * @method setXConstraint
1036 * @param {int} iLeft the number of pixels the element can move to the left
1037 * @param {int} iRight the number of pixels the element can move to the
1039 * @param {int} iTickSize optional parameter for specifying that the
1041 * should move iTickSize pixels at a time.
1043 setXConstraint: function(iLeft, iRight, iTickSize) {
1044 this.leftConstraint = iLeft;
1045 this.rightConstraint = iRight;
1047 this.minX = this.initPageX - iLeft;
1048 this.maxX = this.initPageX + iRight;
1049 if (iTickSize) { this.setXTicks(this.initPageX, iTickSize); }
1051 this.constrainX = true;
1055 * Clears any constraints applied to this instance. Also clears ticks
1056 * since they can't exist independent of a constraint at this time.
1057 * @method clearConstraints
1059 clearConstraints: function() {
1060 this.constrainX = false;
1061 this.constrainY = false;
1066 * Clears any tick interval defined for this instance
1067 * @method clearTicks
1069 clearTicks: function() {
1077 * By default, the element can be dragged any place on the screen. Set
1078 * this to limit the vertical travel of the element. Pass in 0,0 for the
1079 * parameters if you want to lock the drag to the x axis.
1080 * @method setYConstraint
1081 * @param {int} iUp the number of pixels the element can move up
1082 * @param {int} iDown the number of pixels the element can move down
1083 * @param {int} iTickSize optional parameter for specifying that the
1084 * element should move iTickSize pixels at a time.
1086 setYConstraint: function(iUp, iDown, iTickSize) {
1087 this.topConstraint = iUp;
1088 this.bottomConstraint = iDown;
1090 this.minY = this.initPageY - iUp;
1091 this.maxY = this.initPageY + iDown;
1092 if (iTickSize) { this.setYTicks(this.initPageY, iTickSize); }
1094 this.constrainY = true;
1099 * resetConstraints must be called if you manually reposition a dd element.
1100 * @method resetConstraints
1101 * @param {boolean} maintainOffset
1103 resetConstraints: function() {
1106 // Maintain offsets if necessary
1107 if (this.initPageX || this.initPageX === 0) {
1108 // figure out how much this thing has moved
1109 var dx = (this.maintainOffset) ? this.lastPageX - this.initPageX : 0;
1110 var dy = (this.maintainOffset) ? this.lastPageY - this.initPageY : 0;
1112 this.setInitPosition(dx, dy);
1114 // This is the first time we have detected the element's position
1116 this.setInitPosition();
1119 if (this.constrainX) {
1120 this.setXConstraint( this.leftConstraint,
1121 this.rightConstraint,
1125 if (this.constrainY) {
1126 this.setYConstraint( this.topConstraint,
1127 this.bottomConstraint,
1133 * Normally the drag element is moved pixel by pixel, but we can specify
1134 * that it move a number of pixels at a time. This method resolves the
1135 * location when we have it set up like this.
1137 * @param {int} val where we want to place the object
1138 * @param {int[]} tickArray sorted array of valid points
1139 * @return {int} the closest tick
1142 getTick: function(val, tickArray) {
1145 // If tick interval is not defined, it is effectively 1 pixel,
1146 // so we return the value passed to us.
1148 } else if (tickArray[0] >= val) {
1149 // The value is lower than the first tick, so we return the first
1151 return tickArray[0];
1153 for (var i=0, len=tickArray.length; i<len; ++i) {
1155 if (tickArray[next] && tickArray[next] >= val) {
1156 var diff1 = val - tickArray[i];
1157 var diff2 = tickArray[next] - val;
1158 return (diff2 > diff1) ? tickArray[i] : tickArray[next];
1162 // The value is larger than the last tick, so we return the last
1164 return tickArray[tickArray.length - 1];
1171 * @return {string} string representation of the dd obj
1173 toString: function() {
1174 return ("DragDrop " + this.id);
1182 * Ext JS Library 1.1.1
1183 * Copyright(c) 2006-2007, Ext JS, LLC.
1185 * Originally Released Under LGPL - original licence link has changed is not relivant.
1188 * <script type="text/javascript">
1193 * The drag and drop utility provides a framework for building drag and drop
1194 * applications. In addition to enabling drag and drop for specific elements,
1195 * the drag and drop elements are tracked by the manager class, and the
1196 * interactions between the various elements are tracked during the drag and
1197 * the implementing code is notified about these important moments.
1200 // Only load the library once. Rewriting the manager class would orphan
1201 // existing drag and drop instances.
1202 if (!Roo.dd.DragDropMgr) {
1205 * @class Roo.dd.DragDropMgr
1206 * DragDropMgr is a singleton that tracks the element interaction for
1207 * all DragDrop items in the window. Generally, you will not call
1208 * this class directly, but it does have helper methods that could
1209 * be useful in your DragDrop implementations.
1212 Roo.dd.DragDropMgr = function() {
1214 var Event = Roo.EventManager;
1219 * Two dimensional Array of registered DragDrop objects. The first
1220 * dimension is the DragDrop item group, the second the DragDrop
1223 * @type {string: string}
1230 * Array of element ids defined as drag handles. Used to determine
1231 * if the element that generated the mousedown event is actually the
1232 * handle and not the html element itself.
1233 * @property handleIds
1234 * @type {string: string}
1241 * the DragDrop object that is currently being dragged
1242 * @property dragCurrent
1250 * the DragDrop object(s) that are being hovered over
1251 * @property dragOvers
1259 * the X distance between the cursor and the object being dragged
1268 * the Y distance between the cursor and the object being dragged
1277 * Flag to determine if we should prevent the default behavior of the
1278 * events we define. By default this is true, but this can be set to
1279 * false if you need the default behavior (not recommended)
1280 * @property preventDefault
1284 preventDefault: true,
1287 * Flag to determine if we should stop the propagation of the events
1288 * we generate. This is true by default but you may want to set it to
1289 * false if the html element contains other features that require the
1291 * @property stopPropagation
1295 stopPropagation: true,
1298 * Internal flag that is set to true when drag and drop has been
1300 * @property initialized
1307 * All drag and drop can be disabled.
1315 * Called the first time an element is registered.
1321 this.initialized = true;
1325 * In point mode, drag and drop interaction is defined by the
1326 * location of the cursor during the drag/drop
1334 * In intersect mode, drag and drop interactio nis defined by the
1335 * overlap of two or more drag and drop objects.
1336 * @property INTERSECT
1343 * The current drag and drop mode. Default: POINT
1351 * Runs method on all drag and drop objects
1352 * @method _execOnAll
1356 _execOnAll: function(sMethod, args) {
1357 for (var i in this.ids) {
1358 for (var j in this.ids[i]) {
1359 var oDD = this.ids[i][j];
1360 if (! this.isTypeOfDD(oDD)) {
1363 oDD[sMethod].apply(oDD, args);
1369 * Drag and drop initialization. Sets up the global event handlers
1374 _onLoad: function() {
1379 Event.on(document, "mouseup", this.handleMouseUp, this, true);
1380 Event.on(document, "mousemove", this.handleMouseMove, this, true);
1382 Event.on(document, "touchend", this.handleMouseUp, this, true);
1383 Event.on(document, "touchmove", this.handleMouseMove, this, true);
1385 Event.on(window, "unload", this._onUnload, this, true);
1386 Event.on(window, "resize", this._onResize, this, true);
1387 // Event.on(window, "mouseout", this._test);
1392 * Reset constraints on all drag and drop objs
1397 _onResize: function(e) {
1398 this._execOnAll("resetConstraints", []);
1402 * Lock all drag and drop functionality
1406 lock: function() { this.locked = true; },
1409 * Unlock all drag and drop functionality
1413 unlock: function() { this.locked = false; },
1416 * Is drag and drop locked?
1418 * @return {boolean} True if drag and drop is locked, false otherwise.
1421 isLocked: function() { return this.locked; },
1424 * Location cache that is set for all drag drop objects when a drag is
1425 * initiated, cleared when the drag is finished.
1426 * @property locationCache
1433 * Set useCache to false if you want to force object the lookup of each
1434 * drag and drop linked element constantly during a drag.
1435 * @property useCache
1442 * The number of pixels that the mouse needs to move after the
1443 * mousedown before the drag is initiated. Default=3;
1444 * @property clickPixelThresh
1448 clickPixelThresh: 3,
1451 * The number of milliseconds after the mousedown event to initiate the
1452 * drag if we don't get a mouseup event. Default=1000
1453 * @property clickTimeThresh
1457 clickTimeThresh: 350,
1460 * Flag that indicates that either the drag pixel threshold or the
1461 * mousdown time threshold has been met
1462 * @property dragThreshMet
1467 dragThreshMet: false,
1470 * Timeout used for the click time threshold
1471 * @property clickTimeout
1479 * The X position of the mousedown event stored for later use when a
1480 * drag threshold is met.
1489 * The Y position of the mousedown event stored for later use when a
1490 * drag threshold is met.
1499 * Each DragDrop instance must be registered with the DragDropMgr.
1500 * This is executed in DragDrop.init()
1501 * @method regDragDrop
1502 * @param {DragDrop} oDD the DragDrop object to register
1503 * @param {String} sGroup the name of the group this element belongs to
1506 regDragDrop: function(oDD, sGroup) {
1507 if (!this.initialized) { this.init(); }
1509 if (!this.ids[sGroup]) {
1510 this.ids[sGroup] = {};
1512 this.ids[sGroup][oDD.id] = oDD;
1516 * Removes the supplied dd instance from the supplied group. Executed
1517 * by DragDrop.removeFromGroup, so don't call this function directly.
1518 * @method removeDDFromGroup
1522 removeDDFromGroup: function(oDD, sGroup) {
1523 if (!this.ids[sGroup]) {
1524 this.ids[sGroup] = {};
1527 var obj = this.ids[sGroup];
1528 if (obj && obj[oDD.id]) {
1534 * Unregisters a drag and drop item. This is executed in
1535 * DragDrop.unreg, use that method instead of calling this directly.
1540 _remove: function(oDD) {
1541 for (var g in oDD.groups) {
1542 if (g && this.ids[g][oDD.id]) {
1543 delete this.ids[g][oDD.id];
1546 delete this.handleIds[oDD.id];
1550 * Each DragDrop handle element must be registered. This is done
1551 * automatically when executing DragDrop.setHandleElId()
1553 * @param {String} sDDId the DragDrop id this element is a handle for
1554 * @param {String} sHandleId the id of the element that is the drag
1558 regHandle: function(sDDId, sHandleId) {
1559 if (!this.handleIds[sDDId]) {
1560 this.handleIds[sDDId] = {};
1562 this.handleIds[sDDId][sHandleId] = sHandleId;
1566 * Utility function to determine if a given element has been
1567 * registered as a drag drop item.
1568 * @method isDragDrop
1569 * @param {String} id the element id to check
1570 * @return {boolean} true if this element is a DragDrop item,
1574 isDragDrop: function(id) {
1575 return ( this.getDDById(id) ) ? true : false;
1579 * Returns the drag and drop instances that are in all groups the
1580 * passed in instance belongs to.
1581 * @method getRelated
1582 * @param {DragDrop} p_oDD the obj to get related data for
1583 * @param {boolean} bTargetsOnly if true, only return targetable objs
1584 * @return {DragDrop[]} the related instances
1587 getRelated: function(p_oDD, bTargetsOnly) {
1589 for (var i in p_oDD.groups) {
1590 for (j in this.ids[i]) {
1591 var dd = this.ids[i][j];
1592 if (! this.isTypeOfDD(dd)) {
1595 if (!bTargetsOnly || dd.isTarget) {
1596 oDDs[oDDs.length] = dd;
1605 * Returns true if the specified dd target is a legal target for
1606 * the specifice drag obj
1607 * @method isLegalTarget
1608 * @param {DragDrop} the drag obj
1609 * @param {DragDrop} the target
1610 * @return {boolean} true if the target is a legal target for the
1614 isLegalTarget: function (oDD, oTargetDD) {
1615 var targets = this.getRelated(oDD, true);
1616 for (var i=0, len=targets.length;i<len;++i) {
1617 if (targets[i].id == oTargetDD.id) {
1626 * My goal is to be able to transparently determine if an object is
1627 * typeof DragDrop, and the exact subclass of DragDrop. typeof
1628 * returns "object", oDD.constructor.toString() always returns
1629 * "DragDrop" and not the name of the subclass. So for now it just
1630 * evaluates a well-known variable in DragDrop.
1631 * @method isTypeOfDD
1632 * @param {Object} the object to evaluate
1633 * @return {boolean} true if typeof oDD = DragDrop
1636 isTypeOfDD: function (oDD) {
1637 return (oDD && oDD.__ygDragDrop);
1641 * Utility function to determine if a given element has been
1642 * registered as a drag drop handle for the given Drag Drop object.
1644 * @param {String} id the element id to check
1645 * @return {boolean} true if this element is a DragDrop handle, false
1649 isHandle: function(sDDId, sHandleId) {
1650 return ( this.handleIds[sDDId] &&
1651 this.handleIds[sDDId][sHandleId] );
1655 * Returns the DragDrop instance for a given id
1657 * @param {String} id the id of the DragDrop object
1658 * @return {DragDrop} the drag drop object, null if it is not found
1661 getDDById: function(id) {
1662 for (var i in this.ids) {
1663 if (this.ids[i][id]) {
1664 return this.ids[i][id];
1671 * Fired after a registered DragDrop object gets the mousedown event.
1672 * Sets up the events required to track the object being dragged
1673 * @method handleMouseDown
1674 * @param {Event} e the event
1675 * @param oDD the DragDrop object being dragged
1679 handleMouseDown: function(e, oDD) {
1681 Roo.QuickTips.disable();
1683 this.currentTarget = e.getTarget();
1685 this.dragCurrent = oDD;
1687 var el = oDD.getEl();
1689 // track start position
1690 this.startX = e.getPageX();
1691 this.startY = e.getPageY();
1693 this.deltaX = this.startX - el.offsetLeft;
1694 this.deltaY = this.startY - el.offsetTop;
1696 this.dragThreshMet = false;
1698 this.clickTimeout = setTimeout(
1700 var DDM = Roo.dd.DDM;
1701 DDM.startDrag(DDM.startX, DDM.startY);
1703 this.clickTimeThresh );
1707 * Fired when either the drag pixel threshol or the mousedown hold
1708 * time threshold has been met.
1710 * @param x {int} the X position of the original mousedown
1711 * @param y {int} the Y position of the original mousedown
1714 startDrag: function(x, y) {
1715 clearTimeout(this.clickTimeout);
1716 if (this.dragCurrent) {
1717 this.dragCurrent.b4StartDrag(x, y);
1718 this.dragCurrent.startDrag(x, y);
1720 this.dragThreshMet = true;
1724 * Internal function to handle the mouseup event. Will be invoked
1725 * from the context of the document.
1726 * @method handleMouseUp
1727 * @param {Event} e the event
1731 handleMouseUp: function(e) {
1734 Roo.QuickTips.enable();
1736 if (! this.dragCurrent) {
1740 clearTimeout(this.clickTimeout);
1742 if (this.dragThreshMet) {
1743 this.fireEvents(e, true);
1753 * Utility to stop event propagation and event default, if these
1754 * features are turned on.
1756 * @param {Event} e the event as returned by this.getEvent()
1759 stopEvent: function(e){
1760 if(this.stopPropagation) {
1761 e.stopPropagation();
1764 if (this.preventDefault) {
1770 * Internal function to clean up event handlers after the drag
1771 * operation is complete
1773 * @param {Event} e the event
1777 stopDrag: function(e) {
1778 // Fire the drag end event for the item that was dragged
1779 if (this.dragCurrent) {
1780 if (this.dragThreshMet) {
1781 this.dragCurrent.b4EndDrag(e);
1782 this.dragCurrent.endDrag(e);
1785 this.dragCurrent.onMouseUp(e);
1788 this.dragCurrent = null;
1789 this.dragOvers = {};
1793 * Internal function to handle the mousemove event. Will be invoked
1794 * from the context of the html element.
1796 * @TODO figure out what we can do about mouse events lost when the
1797 * user drags objects beyond the window boundary. Currently we can
1798 * detect this in internet explorer by verifying that the mouse is
1799 * down during the mousemove event. Firefox doesn't give us the
1800 * button state on the mousemove event.
1801 * @method handleMouseMove
1802 * @param {Event} e the event
1806 handleMouseMove: function(e) {
1807 if (! this.dragCurrent) {
1811 // var button = e.which || e.button;
1813 // check for IE mouseup outside of page boundary
1814 if (Roo.isIE && (e.button !== 0 && e.button !== 1 && e.button !== 2)) {
1816 return this.handleMouseUp(e);
1819 if (!this.dragThreshMet) {
1820 var diffX = Math.abs(this.startX - e.getPageX());
1821 var diffY = Math.abs(this.startY - e.getPageY());
1822 if (diffX > this.clickPixelThresh ||
1823 diffY > this.clickPixelThresh) {
1824 this.startDrag(this.startX, this.startY);
1828 if (this.dragThreshMet) {
1829 this.dragCurrent.b4Drag(e);
1830 this.dragCurrent.onDrag(e);
1831 if(!this.dragCurrent.moveOnly){
1832 this.fireEvents(e, false);
1842 * Iterates over all of the DragDrop elements to find ones we are
1843 * hovering over or dropping on
1844 * @method fireEvents
1845 * @param {Event} e the event
1846 * @param {boolean} isDrop is this a drop op or a mouseover op?
1850 fireEvents: function(e, isDrop) {
1851 var dc = this.dragCurrent;
1853 // If the user did the mouse up outside of the window, we could
1854 // get here even though we have ended the drag.
1855 if (!dc || dc.isLocked()) {
1859 var pt = e.getPoint();
1861 // cache the previous dragOver array
1869 // Check to see if the object(s) we were hovering over is no longer
1870 // being hovered over so we can fire the onDragOut event
1871 for (var i in this.dragOvers) {
1873 var ddo = this.dragOvers[i];
1875 if (! this.isTypeOfDD(ddo)) {
1879 if (! this.isOverTarget(pt, ddo, this.mode)) {
1880 outEvts.push( ddo );
1884 delete this.dragOvers[i];
1887 for (var sGroup in dc.groups) {
1889 if ("string" != typeof sGroup) {
1893 for (i in this.ids[sGroup]) {
1894 var oDD = this.ids[sGroup][i];
1895 if (! this.isTypeOfDD(oDD)) {
1899 if (oDD.isTarget && !oDD.isLocked() && oDD != dc) {
1900 if (this.isOverTarget(pt, oDD, this.mode)) {
1901 // look for drop interactions
1903 dropEvts.push( oDD );
1904 // look for drag enter and drag over interactions
1907 // initial drag over: dragEnter fires
1908 if (!oldOvers[oDD.id]) {
1909 enterEvts.push( oDD );
1910 // subsequent drag overs: dragOver fires
1912 overEvts.push( oDD );
1915 this.dragOvers[oDD.id] = oDD;
1923 if (outEvts.length) {
1924 dc.b4DragOut(e, outEvts);
1925 dc.onDragOut(e, outEvts);
1928 if (enterEvts.length) {
1929 dc.onDragEnter(e, enterEvts);
1932 if (overEvts.length) {
1933 dc.b4DragOver(e, overEvts);
1934 dc.onDragOver(e, overEvts);
1937 if (dropEvts.length) {
1938 dc.b4DragDrop(e, dropEvts);
1939 dc.onDragDrop(e, dropEvts);
1943 // fire dragout events
1945 for (i=0, len=outEvts.length; i<len; ++i) {
1946 dc.b4DragOut(e, outEvts[i].id);
1947 dc.onDragOut(e, outEvts[i].id);
1950 // fire enter events
1951 for (i=0,len=enterEvts.length; i<len; ++i) {
1952 // dc.b4DragEnter(e, oDD.id);
1953 dc.onDragEnter(e, enterEvts[i].id);
1957 for (i=0,len=overEvts.length; i<len; ++i) {
1958 dc.b4DragOver(e, overEvts[i].id);
1959 dc.onDragOver(e, overEvts[i].id);
1963 for (i=0, len=dropEvts.length; i<len; ++i) {
1964 dc.b4DragDrop(e, dropEvts[i].id);
1965 dc.onDragDrop(e, dropEvts[i].id);
1970 // notify about a drop that did not find a target
1971 if (isDrop && !dropEvts.length) {
1972 dc.onInvalidDrop(e);
1978 * Helper function for getting the best match from the list of drag
1979 * and drop objects returned by the drag and drop events when we are
1980 * in INTERSECT mode. It returns either the first object that the
1981 * cursor is over, or the object that has the greatest overlap with
1982 * the dragged element.
1983 * @method getBestMatch
1984 * @param {DragDrop[]} dds The array of drag and drop objects
1986 * @return {DragDrop} The best single match
1989 getBestMatch: function(dds) {
1991 // Return null if the input is not what we expect
1992 //if (!dds || !dds.length || dds.length == 0) {
1994 // If there is only one item, it wins
1995 //} else if (dds.length == 1) {
1997 var len = dds.length;
2002 // Loop through the targeted items
2003 for (var i=0; i<len; ++i) {
2005 // If the cursor is over the object, it wins. If the
2006 // cursor is over multiple matches, the first one we come
2008 if (dd.cursorIsOver) {
2011 // Otherwise the object with the most overlap wins
2014 winner.overlap.getArea() < dd.overlap.getArea()) {
2025 * Refreshes the cache of the top-left and bottom-right points of the
2026 * drag and drop objects in the specified group(s). This is in the
2027 * format that is stored in the drag and drop instance, so typical
2030 * Roo.dd.DragDropMgr.refreshCache(ddinstance.groups);
2034 * Roo.dd.DragDropMgr.refreshCache({group1:true, group2:true});
2036 * @TODO this really should be an indexed array. Alternatively this
2037 * method could accept both.
2038 * @method refreshCache
2039 * @param {Object} groups an associative array of groups to refresh
2042 refreshCache: function(groups) {
2043 for (var sGroup in groups) {
2044 if ("string" != typeof sGroup) {
2047 for (var i in this.ids[sGroup]) {
2048 var oDD = this.ids[sGroup][i];
2050 if (this.isTypeOfDD(oDD)) {
2051 // if (this.isTypeOfDD(oDD) && oDD.isTarget) {
2052 var loc = this.getLocation(oDD);
2054 this.locationCache[oDD.id] = loc;
2056 delete this.locationCache[oDD.id];
2057 // this will unregister the drag and drop object if
2058 // the element is not in a usable state
2067 * This checks to make sure an element exists and is in the DOM. The
2068 * main purpose is to handle cases where innerHTML is used to remove
2069 * drag and drop objects from the DOM. IE provides an 'unspecified
2070 * error' when trying to access the offsetParent of such an element
2072 * @param {HTMLElement} el the element to check
2073 * @return {boolean} true if the element looks usable
2076 verifyEl: function(el) {
2081 parent = el.offsetParent;
2084 parent = el.offsetParent;
2095 * Returns a Region object containing the drag and drop element's position
2096 * and size, including the padding configured for it
2097 * @method getLocation
2098 * @param {DragDrop} oDD the drag and drop object to get the
2100 * @return {Roo.lib.Region} a Region object representing the total area
2101 * the element occupies, including any padding
2102 * the instance is configured for.
2105 getLocation: function(oDD) {
2106 if (! this.isTypeOfDD(oDD)) {
2110 var el = oDD.getEl(), pos, x1, x2, y1, y2, t, r, b, l;
2113 pos= Roo.lib.Dom.getXY(el);
2121 x2 = x1 + el.offsetWidth;
2123 y2 = y1 + el.offsetHeight;
2125 t = y1 - oDD.padding[0];
2126 r = x2 + oDD.padding[1];
2127 b = y2 + oDD.padding[2];
2128 l = x1 - oDD.padding[3];
2130 return new Roo.lib.Region( t, r, b, l );
2134 * Checks the cursor location to see if it over the target
2135 * @method isOverTarget
2136 * @param {Roo.lib.Point} pt The point to evaluate
2137 * @param {DragDrop} oTarget the DragDrop object we are inspecting
2138 * @return {boolean} true if the mouse is over the target
2142 isOverTarget: function(pt, oTarget, intersect) {
2143 // use cache if available
2144 var loc = this.locationCache[oTarget.id];
2145 if (!loc || !this.useCache) {
2146 loc = this.getLocation(oTarget);
2147 this.locationCache[oTarget.id] = loc;
2155 oTarget.cursorIsOver = loc.contains( pt );
2157 // DragDrop is using this as a sanity check for the initial mousedown
2158 // in this case we are done. In POINT mode, if the drag obj has no
2159 // contraints, we are also done. Otherwise we need to evaluate the
2160 // location of the target as related to the actual location of the
2162 var dc = this.dragCurrent;
2163 if (!dc || !dc.getTargetCoord ||
2164 (!intersect && !dc.constrainX && !dc.constrainY)) {
2165 return oTarget.cursorIsOver;
2168 oTarget.overlap = null;
2170 // Get the current location of the drag element, this is the
2171 // location of the mouse event less the delta that represents
2172 // where the original mousedown happened on the element. We
2173 // need to consider constraints and ticks as well.
2174 var pos = dc.getTargetCoord(pt.x, pt.y);
2176 var el = dc.getDragEl();
2177 var curRegion = new Roo.lib.Region( pos.y,
2178 pos.x + el.offsetWidth,
2179 pos.y + el.offsetHeight,
2182 var overlap = curRegion.intersect(loc);
2185 oTarget.overlap = overlap;
2186 return (intersect) ? true : oTarget.cursorIsOver;
2193 * unload event handler
2198 _onUnload: function(e, me) {
2199 Roo.dd.DragDropMgr.unregAll();
2203 * Cleans up the drag and drop events and objects.
2208 unregAll: function() {
2210 if (this.dragCurrent) {
2212 this.dragCurrent = null;
2215 this._execOnAll("unreg", []);
2217 for (i in this.elementCache) {
2218 delete this.elementCache[i];
2221 this.elementCache = {};
2226 * A cache of DOM elements
2227 * @property elementCache
2234 * Get the wrapper for the DOM element specified
2235 * @method getElWrapper
2236 * @param {String} id the id of the element to get
2237 * @return {Roo.dd.DDM.ElementWrapper} the wrapped element
2239 * @deprecated This wrapper isn't that useful
2242 getElWrapper: function(id) {
2243 var oWrapper = this.elementCache[id];
2244 if (!oWrapper || !oWrapper.el) {
2245 oWrapper = this.elementCache[id] =
2246 new this.ElementWrapper(Roo.getDom(id));
2252 * Returns the actual DOM element
2253 * @method getElement
2254 * @param {String} id the id of the elment to get
2255 * @return {Object} The element
2256 * @deprecated use Roo.getDom instead
2259 getElement: function(id) {
2260 return Roo.getDom(id);
2264 * Returns the style property for the DOM element (i.e.,
2265 * document.getElById(id).style)
2267 * @param {String} id the id of the elment to get
2268 * @return {Object} The style property of the element
2269 * @deprecated use Roo.getDom instead
2272 getCss: function(id) {
2273 var el = Roo.getDom(id);
2274 return (el) ? el.style : null;
2278 * Inner class for cached elements
2279 * @class DragDropMgr.ElementWrapper
2284 ElementWrapper: function(el) {
2289 this.el = el || null;
2294 this.id = this.el && el.id;
2296 * A reference to the style property
2299 this.css = this.el && el.style;
2303 * Returns the X position of an html element
2305 * @param el the element for which to get the position
2306 * @return {int} the X coordinate
2308 * @deprecated use Roo.lib.Dom.getX instead
2311 getPosX: function(el) {
2312 return Roo.lib.Dom.getX(el);
2316 * Returns the Y position of an html element
2318 * @param el the element for which to get the position
2319 * @return {int} the Y coordinate
2320 * @deprecated use Roo.lib.Dom.getY instead
2323 getPosY: function(el) {
2324 return Roo.lib.Dom.getY(el);
2328 * Swap two nodes. In IE, we use the native method, for others we
2329 * emulate the IE behavior
2331 * @param n1 the first node to swap
2332 * @param n2 the other node to swap
2335 swapNode: function(n1, n2) {
2339 var p = n2.parentNode;
2340 var s = n2.nextSibling;
2343 p.insertBefore(n1, n2);
2344 } else if (n2 == n1.nextSibling) {
2345 p.insertBefore(n2, n1);
2347 n1.parentNode.replaceChild(n2, n1);
2348 p.insertBefore(n1, s);
2354 * Returns the current scroll position
2359 getScroll: function () {
2360 var t, l, dde=document.documentElement, db=document.body;
2361 if (dde && (dde.scrollTop || dde.scrollLeft)) {
2370 return { top: t, left: l };
2374 * Returns the specified element style property
2376 * @param {HTMLElement} el the element
2377 * @param {string} styleProp the style property
2378 * @return {string} The value of the style property
2379 * @deprecated use Roo.lib.Dom.getStyle
2382 getStyle: function(el, styleProp) {
2383 return Roo.fly(el).getStyle(styleProp);
2387 * Gets the scrollTop
2388 * @method getScrollTop
2389 * @return {int} the document's scrollTop
2392 getScrollTop: function () { return this.getScroll().top; },
2395 * Gets the scrollLeft
2396 * @method getScrollLeft
2397 * @return {int} the document's scrollTop
2400 getScrollLeft: function () { return this.getScroll().left; },
2403 * Sets the x/y position of an element to the location of the
2406 * @param {HTMLElement} moveEl The element to move
2407 * @param {HTMLElement} targetEl The position reference element
2410 moveToEl: function (moveEl, targetEl) {
2411 var aCoord = Roo.lib.Dom.getXY(targetEl);
2412 Roo.lib.Dom.setXY(moveEl, aCoord);
2416 * Numeric array sort function
2417 * @method numericSort
2420 numericSort: function(a, b) { return (a - b); },
2424 * @property _timeoutCount
2431 * Trying to make the load order less important. Without this we get
2432 * an error if this file is loaded before the Event Utility.
2433 * @method _addListeners
2437 _addListeners: function() {
2438 var DDM = Roo.dd.DDM;
2439 if ( Roo.lib.Event && document ) {
2442 if (DDM._timeoutCount > 2000) {
2444 setTimeout(DDM._addListeners, 10);
2445 if (document && document.body) {
2446 DDM._timeoutCount += 1;
2453 * Recursively searches the immediate parent and all child nodes for
2454 * the handle element in order to determine wheter or not it was
2456 * @method handleWasClicked
2457 * @param node the html element to inspect
2460 handleWasClicked: function(node, id) {
2461 if (this.isHandle(id, node.id)) {
2464 // check to see if this is a text node child of the one we want
2465 var p = node.parentNode;
2468 if (this.isHandle(id, p.id)) {
2483 // shorter alias, save a few bytes
2484 Roo.dd.DDM = Roo.dd.DragDropMgr;
2485 Roo.dd.DDM._addListeners();
2489 * Ext JS Library 1.1.1
2490 * Copyright(c) 2006-2007, Ext JS, LLC.
2492 * Originally Released Under LGPL - original licence link has changed is not relivant.
2495 * <script type="text/javascript">
2500 * A DragDrop implementation where the linked element follows the
2501 * mouse cursor during a drag.
2502 * @extends Roo.dd.DragDrop
2504 * @param {String} id the id of the linked element
2505 * @param {String} sGroup the group of related DragDrop items
2506 * @param {object} config an object containing configurable attributes
2507 * Valid properties for DD:
2510 Roo.dd.DD = function(id, sGroup, config) {
2512 this.init(id, sGroup, config);
2516 Roo.extend(Roo.dd.DD, Roo.dd.DragDrop, {
2519 * When set to true, the utility automatically tries to scroll the browser
2520 * window wehn a drag and drop element is dragged near the viewport boundary.
2528 * Sets the pointer offset to the distance between the linked element's top
2529 * left corner and the location the element was clicked
2530 * @method autoOffset
2531 * @param {int} iPageX the X coordinate of the click
2532 * @param {int} iPageY the Y coordinate of the click
2534 autoOffset: function(iPageX, iPageY) {
2535 var x = iPageX - this.startPageX;
2536 var y = iPageY - this.startPageY;
2537 this.setDelta(x, y);
2541 * Sets the pointer offset. You can call this directly to force the
2542 * offset to be in a particular location (e.g., pass in 0,0 to set it
2543 * to the center of the object)
2545 * @param {int} iDeltaX the distance from the left
2546 * @param {int} iDeltaY the distance from the top
2548 setDelta: function(iDeltaX, iDeltaY) {
2549 this.deltaX = iDeltaX;
2550 this.deltaY = iDeltaY;
2554 * Sets the drag element to the location of the mousedown or click event,
2555 * maintaining the cursor location relative to the location on the element
2556 * that was clicked. Override this if you want to place the element in a
2557 * location other than where the cursor is.
2558 * @method setDragElPos
2559 * @param {int} iPageX the X coordinate of the mousedown or drag event
2560 * @param {int} iPageY the Y coordinate of the mousedown or drag event
2562 setDragElPos: function(iPageX, iPageY) {
2563 // the first time we do this, we are going to check to make sure
2564 // the element has css positioning
2566 var el = this.getDragEl();
2567 this.alignElWithMouse(el, iPageX, iPageY);
2571 * Sets the element to the location of the mousedown or click event,
2572 * maintaining the cursor location relative to the location on the element
2573 * that was clicked. Override this if you want to place the element in a
2574 * location other than where the cursor is.
2575 * @method alignElWithMouse
2576 * @param {HTMLElement} el the element to move
2577 * @param {int} iPageX the X coordinate of the mousedown or drag event
2578 * @param {int} iPageY the Y coordinate of the mousedown or drag event
2580 alignElWithMouse: function(el, iPageX, iPageY) {
2581 var oCoord = this.getTargetCoord(iPageX, iPageY);
2582 var fly = el.dom ? el : Roo.fly(el);
2583 if (!this.deltaSetXY) {
2584 var aCoord = [oCoord.x, oCoord.y];
2586 var newLeft = fly.getLeft(true);
2587 var newTop = fly.getTop(true);
2588 this.deltaSetXY = [ newLeft - oCoord.x, newTop - oCoord.y ];
2590 fly.setLeftTop(oCoord.x + this.deltaSetXY[0], oCoord.y + this.deltaSetXY[1]);
2593 this.cachePosition(oCoord.x, oCoord.y);
2594 this.autoScroll(oCoord.x, oCoord.y, el.offsetHeight, el.offsetWidth);
2599 * Saves the most recent position so that we can reset the constraints and
2600 * tick marks on-demand. We need to know this so that we can calculate the
2601 * number of pixels the element is offset from its original position.
2602 * @method cachePosition
2603 * @param iPageX the current x position (optional, this just makes it so we
2604 * don't have to look it up again)
2605 * @param iPageY the current y position (optional, this just makes it so we
2606 * don't have to look it up again)
2608 cachePosition: function(iPageX, iPageY) {
2610 this.lastPageX = iPageX;
2611 this.lastPageY = iPageY;
2613 var aCoord = Roo.lib.Dom.getXY(this.getEl());
2614 this.lastPageX = aCoord[0];
2615 this.lastPageY = aCoord[1];
2620 * Auto-scroll the window if the dragged object has been moved beyond the
2621 * visible window boundary.
2622 * @method autoScroll
2623 * @param {int} x the drag element's x position
2624 * @param {int} y the drag element's y position
2625 * @param {int} h the height of the drag element
2626 * @param {int} w the width of the drag element
2629 autoScroll: function(x, y, h, w) {
2632 // The client height
2633 var clientH = Roo.lib.Dom.getViewWidth();
2636 var clientW = Roo.lib.Dom.getViewHeight();
2638 // The amt scrolled down
2639 var st = this.DDM.getScrollTop();
2641 // The amt scrolled right
2642 var sl = this.DDM.getScrollLeft();
2644 // Location of the bottom of the element
2647 // Location of the right of the element
2650 // The distance from the cursor to the bottom of the visible area,
2651 // adjusted so that we don't scroll if the cursor is beyond the
2652 // element drag constraints
2653 var toBot = (clientH + st - y - this.deltaY);
2655 // The distance from the cursor to the right of the visible area
2656 var toRight = (clientW + sl - x - this.deltaX);
2659 // How close to the edge the cursor must be before we scroll
2660 // var thresh = (document.all) ? 100 : 40;
2663 // How many pixels to scroll per autoscroll op. This helps to reduce
2664 // clunky scrolling. IE is more sensitive about this ... it needs this
2665 // value to be higher.
2666 var scrAmt = (document.all) ? 80 : 30;
2668 // Scroll down if we are near the bottom of the visible page and the
2669 // obj extends below the crease
2670 if ( bot > clientH && toBot < thresh ) {
2671 window.scrollTo(sl, st + scrAmt);
2674 // Scroll up if the window is scrolled down and the top of the object
2675 // goes above the top border
2676 if ( y < st && st > 0 && y - st < thresh ) {
2677 window.scrollTo(sl, st - scrAmt);
2680 // Scroll right if the obj is beyond the right border and the cursor is
2682 if ( right > clientW && toRight < thresh ) {
2683 window.scrollTo(sl + scrAmt, st);
2686 // Scroll left if the window has been scrolled to the right and the obj
2687 // extends past the left border
2688 if ( x < sl && sl > 0 && x - sl < thresh ) {
2689 window.scrollTo(sl - scrAmt, st);
2695 * Finds the location the element should be placed if we want to move
2696 * it to where the mouse location less the click offset would place us.
2697 * @method getTargetCoord
2698 * @param {int} iPageX the X coordinate of the click
2699 * @param {int} iPageY the Y coordinate of the click
2700 * @return an object that contains the coordinates (Object.x and Object.y)
2703 getTargetCoord: function(iPageX, iPageY) {
2706 var x = iPageX - this.deltaX;
2707 var y = iPageY - this.deltaY;
2709 if (this.constrainX) {
2710 if (x < this.minX) { x = this.minX; }
2711 if (x > this.maxX) { x = this.maxX; }
2714 if (this.constrainY) {
2715 if (y < this.minY) { y = this.minY; }
2716 if (y > this.maxY) { y = this.maxY; }
2719 x = this.getTick(x, this.xTicks);
2720 y = this.getTick(y, this.yTicks);
2727 * Sets up config options specific to this class. Overrides
2728 * Roo.dd.DragDrop, but all versions of this method through the
2729 * inheritance chain are called
2731 applyConfig: function() {
2732 Roo.dd.DD.superclass.applyConfig.call(this);
2733 this.scroll = (this.config.scroll !== false);
2737 * Event that fires prior to the onMouseDown event. Overrides
2740 b4MouseDown: function(e) {
2741 // this.resetConstraints();
2742 this.autoOffset(e.getPageX(),
2747 * Event that fires prior to the onDrag event. Overrides
2750 b4Drag: function(e) {
2751 this.setDragElPos(e.getPageX(),
2755 toString: function() {
2756 return ("DD " + this.id);
2759 //////////////////////////////////////////////////////////////////////////
2760 // Debugging ygDragDrop events that can be overridden
2761 //////////////////////////////////////////////////////////////////////////
2763 startDrag: function(x, y) {
2766 onDrag: function(e) {
2769 onDragEnter: function(e, id) {
2772 onDragOver: function(e, id) {
2775 onDragOut: function(e, id) {
2778 onDragDrop: function(e, id) {
2781 endDrag: function(e) {
2788 * Ext JS Library 1.1.1
2789 * Copyright(c) 2006-2007, Ext JS, LLC.
2791 * Originally Released Under LGPL - original licence link has changed is not relivant.
2794 * <script type="text/javascript">
2798 * @class Roo.dd.DDProxy
2799 * A DragDrop implementation that inserts an empty, bordered div into
2800 * the document that follows the cursor during drag operations. At the time of
2801 * the click, the frame div is resized to the dimensions of the linked html
2802 * element, and moved to the exact location of the linked element.
2804 * References to the "frame" element refer to the single proxy element that
2805 * was created to be dragged in place of all DDProxy elements on the
2808 * @extends Roo.dd.DD
2810 * @param {String} id the id of the linked html element
2811 * @param {String} sGroup the group of related DragDrop objects
2812 * @param {object} config an object containing configurable attributes
2813 * Valid properties for DDProxy in addition to those in DragDrop:
2814 * resizeFrame, centerFrame, dragElId
2816 Roo.dd.DDProxy = function(id, sGroup, config) {
2818 this.init(id, sGroup, config);
2824 * The default drag frame div id
2825 * @property Roo.dd.DDProxy.dragElId
2829 Roo.dd.DDProxy.dragElId = "ygddfdiv";
2831 Roo.extend(Roo.dd.DDProxy, Roo.dd.DD, {
2834 * By default we resize the drag frame to be the same size as the element
2835 * we want to drag (this is to get the frame effect). We can turn it off
2836 * if we want a different behavior.
2837 * @property resizeFrame
2843 * By default the frame is positioned exactly where the drag element is, so
2844 * we use the cursor offset provided by Roo.dd.DD. Another option that works only if
2845 * you do not have constraints on the obj is to have the drag frame centered
2846 * around the cursor. Set centerFrame to true for this effect.
2847 * @property centerFrame
2853 * Creates the proxy element if it does not yet exist
2854 * @method createFrame
2856 createFrame: function() {
2858 var body = document.body;
2860 if (!body || !body.firstChild) {
2861 setTimeout( function() { self.createFrame(); }, 50 );
2865 var div = this.getDragEl();
2868 div = document.createElement("div");
2869 div.id = this.dragElId;
2872 s.position = "absolute";
2873 s.visibility = "hidden";
2875 s.border = "2px solid #aaa";
2878 // appendChild can blow up IE if invoked prior to the window load event
2879 // while rendering a table. It is possible there are other scenarios
2880 // that would cause this to happen as well.
2881 body.insertBefore(div, body.firstChild);
2886 * Initialization for the drag frame element. Must be called in the
2887 * constructor of all subclasses
2890 initFrame: function() {
2894 applyConfig: function() {
2895 Roo.dd.DDProxy.superclass.applyConfig.call(this);
2897 this.resizeFrame = (this.config.resizeFrame !== false);
2898 this.centerFrame = (this.config.centerFrame);
2899 this.setDragElId(this.config.dragElId || Roo.dd.DDProxy.dragElId);
2903 * Resizes the drag frame to the dimensions of the clicked object, positions
2904 * it over the object, and finally displays it
2906 * @param {int} iPageX X click position
2907 * @param {int} iPageY Y click position
2910 showFrame: function(iPageX, iPageY) {
2911 var el = this.getEl();
2912 var dragEl = this.getDragEl();
2913 var s = dragEl.style;
2915 this._resizeProxy();
2917 if (this.centerFrame) {
2918 this.setDelta( Math.round(parseInt(s.width, 10)/2),
2919 Math.round(parseInt(s.height, 10)/2) );
2922 this.setDragElPos(iPageX, iPageY);
2924 Roo.fly(dragEl).show();
2928 * The proxy is automatically resized to the dimensions of the linked
2929 * element when a drag is initiated, unless resizeFrame is set to false
2930 * @method _resizeProxy
2933 _resizeProxy: function() {
2934 if (this.resizeFrame) {
2935 var el = this.getEl();
2936 Roo.fly(this.getDragEl()).setSize(el.offsetWidth, el.offsetHeight);
2940 // overrides Roo.dd.DragDrop
2941 b4MouseDown: function(e) {
2942 var x = e.getPageX();
2943 var y = e.getPageY();
2944 this.autoOffset(x, y);
2945 this.setDragElPos(x, y);
2948 // overrides Roo.dd.DragDrop
2949 b4StartDrag: function(x, y) {
2950 // show the drag frame
2951 this.showFrame(x, y);
2954 // overrides Roo.dd.DragDrop
2955 b4EndDrag: function(e) {
2956 Roo.fly(this.getDragEl()).hide();
2959 // overrides Roo.dd.DragDrop
2960 // By default we try to move the element to the last location of the frame.
2961 // This is so that the default behavior mirrors that of Roo.dd.DD.
2962 endDrag: function(e) {
2964 var lel = this.getEl();
2965 var del = this.getDragEl();
2967 // Show the drag frame briefly so we can get its position
2968 del.style.visibility = "";
2971 // Hide the linked element before the move to get around a Safari
2973 lel.style.visibility = "hidden";
2974 Roo.dd.DDM.moveToEl(lel, del);
2975 del.style.visibility = "hidden";
2976 lel.style.visibility = "";
2981 beforeMove : function(){
2985 afterDrag : function(){
2989 toString: function() {
2990 return ("DDProxy " + this.id);
2996 * Ext JS Library 1.1.1
2997 * Copyright(c) 2006-2007, Ext JS, LLC.
2999 * Originally Released Under LGPL - original licence link has changed is not relivant.
3002 * <script type="text/javascript">
3006 * @class Roo.dd.DDTarget
3007 * A DragDrop implementation that does not move, but can be a drop
3008 * target. You would get the same result by simply omitting implementation
3009 * for the event callbacks, but this way we reduce the processing cost of the
3010 * event listener and the callbacks.
3011 * @extends Roo.dd.DragDrop
3013 * @param {String} id the id of the element that is a drop target
3014 * @param {String} sGroup the group of related DragDrop objects
3015 * @param {object} config an object containing configurable attributes
3016 * Valid properties for DDTarget in addition to those in
3020 Roo.dd.DDTarget = function(id, sGroup, config) {
3022 this.initTarget(id, sGroup, config);
3024 if (config.listeners || config.events) {
3025 Roo.dd.DragDrop.superclass.constructor.call(this, {
3026 listeners : config.listeners || {},
3027 events : config.events || {}
3032 // Roo.dd.DDTarget.prototype = new Roo.dd.DragDrop();
3033 Roo.extend(Roo.dd.DDTarget, Roo.dd.DragDrop, {
3034 toString: function() {
3035 return ("DDTarget " + this.id);
3040 * Ext JS Library 1.1.1
3041 * Copyright(c) 2006-2007, Ext JS, LLC.
3043 * Originally Released Under LGPL - original licence link has changed is not relivant.
3046 * <script type="text/javascript">
3051 * @class Roo.dd.ScrollManager
3052 * Provides automatic scrolling of overflow regions in the page during drag operations.<br><br>
3053 * <b>Note: This class uses "Point Mode" and is untested in "Intersect Mode".</b>
3056 Roo.dd.ScrollManager = function(){
3057 var ddm = Roo.dd.DragDropMgr;
3064 var onStop = function(e){
3069 var triggerRefresh = function(){
3070 if(ddm.dragCurrent){
3071 ddm.refreshCache(ddm.dragCurrent.groups);
3075 var doScroll = function(){
3076 if(ddm.dragCurrent){
3077 var dds = Roo.dd.ScrollManager;
3079 if(proc.el.scroll(proc.dir, dds.increment)){
3083 proc.el.scroll(proc.dir, dds.increment, true, dds.animDuration, triggerRefresh);
3088 var clearProc = function(){
3090 clearInterval(proc.id);
3097 var startProc = function(el, dir){
3098 Roo.log('scroll startproc');
3102 proc.id = setInterval(doScroll, Roo.dd.ScrollManager.frequency);
3105 var onFire = function(e, isDrop){
3107 if(isDrop || !ddm.dragCurrent){ return; }
3108 var dds = Roo.dd.ScrollManager;
3109 if(!dragEl || dragEl != ddm.dragCurrent){
3110 dragEl = ddm.dragCurrent;
3111 // refresh regions on drag start
3115 var xy = Roo.lib.Event.getXY(e);
3116 var pt = new Roo.lib.Point(xy[0], xy[1]);
3118 var el = els[id], r = el._region;
3119 if(r && r.contains(pt) && el.isScrollable()){
3120 if(r.bottom - pt.y <= dds.thresh){
3122 startProc(el, "down");
3125 }else if(r.right - pt.x <= dds.thresh){
3127 startProc(el, "left");
3130 }else if(pt.y - r.top <= dds.thresh){
3132 startProc(el, "up");
3135 }else if(pt.x - r.left <= dds.thresh){
3137 startProc(el, "right");
3146 ddm.fireEvents = ddm.fireEvents.createSequence(onFire, ddm);
3147 ddm.stopDrag = ddm.stopDrag.createSequence(onStop, ddm);
3151 * Registers new overflow element(s) to auto scroll
3152 * @param {String/HTMLElement/Element/Array} el The id of or the element to be scrolled or an array of either
3154 register : function(el){
3155 if(el instanceof Array){
3156 for(var i = 0, len = el.length; i < len; i++) {
3157 this.register(el[i]);
3163 Roo.dd.ScrollManager.els = els;
3167 * Unregisters overflow element(s) so they are no longer scrolled
3168 * @param {String/HTMLElement/Element/Array} el The id of or the element to be removed or an array of either
3170 unregister : function(el){
3171 if(el instanceof Array){
3172 for(var i = 0, len = el.length; i < len; i++) {
3173 this.unregister(el[i]);
3182 * The number of pixels from the edge of a container the pointer needs to be to
3183 * trigger scrolling (defaults to 25)
3189 * The number of pixels to scroll in each scroll increment (defaults to 50)
3195 * The frequency of scrolls in milliseconds (defaults to 500)
3201 * True to animate the scroll (defaults to true)
3207 * The animation duration in seconds -
3208 * MUST BE less than Roo.dd.ScrollManager.frequency! (defaults to .4)
3214 * Manually trigger a cache refresh.
3216 refreshCache : function(){
3218 if(typeof els[id] == 'object'){ // for people extending the object prototype
3219 els[id]._region = els[id].getRegion();
3226 * Ext JS Library 1.1.1
3227 * Copyright(c) 2006-2007, Ext JS, LLC.
3229 * Originally Released Under LGPL - original licence link has changed is not relivant.
3232 * <script type="text/javascript">
3237 * @class Roo.dd.Registry
3238 * Provides easy access to all drag drop components that are registered on a page. Items can be retrieved either
3239 * directly by DOM node id, or by passing in the drag drop event that occurred and looking up the event target.
3242 Roo.dd.Registry = function(){
3247 var getId = function(el, autogen){
3248 if(typeof el == "string"){
3252 if(!id && autogen !== false){
3253 id = "roodd-" + (++autoIdSeed);
3261 * Register a drag drop element
3262 * @param {String|HTMLElement} element The id or DOM node to register
3263 * @param {Object} data (optional) A custom data object that will be passed between the elements that are involved
3264 * in drag drop operations. You can populate this object with any arbitrary properties that your own code
3265 * knows how to interpret, plus there are some specific properties known to the Registry that should be
3266 * populated in the data object (if applicable):
3268 Value Description<br />
3269 --------- ------------------------------------------<br />
3270 handles Array of DOM nodes that trigger dragging<br />
3271 for the element being registered<br />
3272 isHandle True if the element passed in triggers<br />
3273 dragging itself, else false
3276 register : function(el, data){
3278 if(typeof el == "string"){
3279 el = document.getElementById(el);
3282 elements[getId(el)] = data;
3283 if(data.isHandle !== false){
3284 handles[data.ddel.id] = data;
3287 var hs = data.handles;
3288 for(var i = 0, len = hs.length; i < len; i++){
3289 handles[getId(hs[i])] = data;
3295 * Unregister a drag drop element
3296 * @param {String|HTMLElement} element The id or DOM node to unregister
3298 unregister : function(el){
3299 var id = getId(el, false);
3300 var data = elements[id];
3302 delete elements[id];
3304 var hs = data.handles;
3305 for(var i = 0, len = hs.length; i < len; i++){
3306 delete handles[getId(hs[i], false)];
3313 * Returns the handle registered for a DOM Node by id
3314 * @param {String|HTMLElement} id The DOM node or id to look up
3315 * @return {Object} handle The custom handle data
3317 getHandle : function(id){
3318 if(typeof id != "string"){ // must be element?
3325 * Returns the handle that is registered for the DOM node that is the target of the event
3326 * @param {Event} e The event
3327 * @return {Object} handle The custom handle data
3329 getHandleFromEvent : function(e){
3330 var t = Roo.lib.Event.getTarget(e);
3331 return t ? handles[t.id] : null;
3335 * Returns a custom data object that is registered for a DOM node by id
3336 * @param {String|HTMLElement} id The DOM node or id to look up
3337 * @return {Object} data The custom data
3339 getTarget : function(id){
3340 if(typeof id != "string"){ // must be element?
3343 return elements[id];
3347 * Returns a custom data object that is registered for the DOM node that is the target of the event
3348 * @param {Event} e The event
3349 * @return {Object} data The custom data
3351 getTargetFromEvent : function(e){
3352 var t = Roo.lib.Event.getTarget(e);
3353 return t ? elements[t.id] || handles[t.id] : null;
3358 * Ext JS Library 1.1.1
3359 * Copyright(c) 2006-2007, Ext JS, LLC.
3361 * Originally Released Under LGPL - original licence link has changed is not relivant.
3364 * <script type="text/javascript">
3369 * @class Roo.dd.StatusProxy
3370 * A specialized drag proxy that supports a drop status icon, {@link Roo.Layer} styles and auto-repair. This is the
3371 * default drag proxy used by all Roo.dd components.
3373 * @param {Object} config
3375 Roo.dd.StatusProxy = function(config){
3376 Roo.apply(this, config);
3377 this.id = this.id || Roo.id();
3378 this.el = new Roo.Layer({
3380 id: this.id, tag: "div", cls: "x-dd-drag-proxy "+this.dropNotAllowed, children: [
3381 {tag: "div", cls: "x-dd-drop-icon"},
3382 {tag: "div", cls: "x-dd-drag-ghost"}
3385 shadow: !config || config.shadow !== false
3387 this.ghost = Roo.get(this.el.dom.childNodes[1]);
3388 this.dropStatus = this.dropNotAllowed;
3391 Roo.dd.StatusProxy.prototype = {
3393 * @cfg {String} dropAllowed
3394 * The CSS class to apply to the status element when drop is allowed (defaults to "x-dd-drop-ok").
3396 dropAllowed : "x-dd-drop-ok",
3398 * @cfg {String} dropNotAllowed
3399 * The CSS class to apply to the status element when drop is not allowed (defaults to "x-dd-drop-nodrop").
3401 dropNotAllowed : "x-dd-drop-nodrop",
3404 * Updates the proxy's visual element to indicate the status of whether or not drop is allowed
3405 * over the current target element.
3406 * @param {String} cssClass The css class for the new drop status indicator image
3408 setStatus : function(cssClass){
3409 cssClass = cssClass || this.dropNotAllowed;
3410 if(this.dropStatus != cssClass){
3411 this.el.replaceClass(this.dropStatus, cssClass);
3412 this.dropStatus = cssClass;
3417 * Resets the status indicator to the default dropNotAllowed value
3418 * @param {Boolean} clearGhost True to also remove all content from the ghost, false to preserve it
3420 reset : function(clearGhost){
3421 this.el.dom.className = "x-dd-drag-proxy " + this.dropNotAllowed;
3422 this.dropStatus = this.dropNotAllowed;
3424 this.ghost.update("");
3429 * Updates the contents of the ghost element
3430 * @param {String} html The html that will replace the current innerHTML of the ghost element
3432 update : function(html){
3433 if(typeof html == "string"){
3434 this.ghost.update(html);
3436 this.ghost.update("");
3437 html.style.margin = "0";
3438 this.ghost.dom.appendChild(html);
3440 // ensure float = none set?? cant remember why though.
3441 var el = this.ghost.dom.firstChild;
3443 Roo.fly(el).setStyle('float', 'none');
3448 * Returns the underlying proxy {@link Roo.Layer}
3449 * @return {Roo.Layer} el
3456 * Returns the ghost element
3457 * @return {Roo.Element} el
3459 getGhost : function(){
3465 * @param {Boolean} clear True to reset the status and clear the ghost contents, false to preserve them
3467 hide : function(clear){
3475 * Stops the repair animation if it's currently running
3478 if(this.anim && this.anim.isAnimated && this.anim.isAnimated()){
3484 * Displays this proxy
3491 * Force the Layer to sync its shadow and shim positions to the element
3498 * Causes the proxy to return to its position of origin via an animation. Should be called after an
3499 * invalid drop operation by the item being dragged.
3500 * @param {Array} xy The XY position of the element ([x, y])
3501 * @param {Function} callback The function to call after the repair is complete
3502 * @param {Object} scope The scope in which to execute the callback
3504 repair : function(xy, callback, scope){
3505 this.callback = callback;
3507 if(xy && this.animRepair !== false){
3508 this.el.addClass("x-dd-drag-repair");
3509 this.el.hideUnders(true);
3510 this.anim = this.el.shift({
3511 duration: this.repairDuration || .5,
3515 callback: this.afterRepair,
3524 afterRepair : function(){
3526 if(typeof this.callback == "function"){
3527 this.callback.call(this.scope || this);
3529 this.callback = null;
3534 * Ext JS Library 1.1.1
3535 * Copyright(c) 2006-2007, Ext JS, LLC.
3537 * Originally Released Under LGPL - original licence link has changed is not relivant.
3540 * <script type="text/javascript">
3544 * @class Roo.dd.DragSource
3545 * @extends Roo.dd.DDProxy
3546 * A simple class that provides the basic implementation needed to make any element draggable.
3548 * @param {String/HTMLElement/Element} el The container element
3549 * @param {Object} config
3551 Roo.dd.DragSource = function(el, config){
3552 this.el = Roo.get(el);
3555 Roo.apply(this, config);
3558 this.proxy = new Roo.dd.StatusProxy();
3561 Roo.dd.DragSource.superclass.constructor.call(this, this.el.dom, this.ddGroup || this.group,
3562 {dragElId : this.proxy.id, resizeFrame: false, isTarget: false, scroll: this.scroll === true});
3564 this.dragging = false;
3567 Roo.extend(Roo.dd.DragSource, Roo.dd.DDProxy, {
3569 * @cfg {String} dropAllowed
3570 * The CSS class returned to the drag source when drop is allowed (defaults to "x-dd-drop-ok").
3572 dropAllowed : "x-dd-drop-ok",
3574 * @cfg {String} dropNotAllowed
3575 * The CSS class returned to the drag source when drop is not allowed (defaults to "x-dd-drop-nodrop").
3577 dropNotAllowed : "x-dd-drop-nodrop",
3580 * Returns the data object associated with this drag source
3581 * @return {Object} data An object containing arbitrary data
3583 getDragData : function(e){
3584 return this.dragData;
3588 onDragEnter : function(e, id){
3589 var target = Roo.dd.DragDropMgr.getDDById(id);
3590 this.cachedTarget = target;
3591 if(this.beforeDragEnter(target, e, id) !== false){
3592 if(target.isNotifyTarget){
3593 var status = target.notifyEnter(this, e, this.dragData);
3594 this.proxy.setStatus(status);
3596 this.proxy.setStatus(this.dropAllowed);
3599 if(this.afterDragEnter){
3601 * An empty function by default, but provided so that you can perform a custom action
3602 * when the dragged item enters the drop target by providing an implementation.
3603 * @param {Roo.dd.DragDrop} target The drop target
3604 * @param {Event} e The event object
3605 * @param {String} id The id of the dragged element
3606 * @method afterDragEnter
3608 this.afterDragEnter(target, e, id);
3614 * An empty function by default, but provided so that you can perform a custom action
3615 * before the dragged item enters the drop target and optionally cancel the onDragEnter.
3616 * @param {Roo.dd.DragDrop} target The drop target
3617 * @param {Event} e The event object
3618 * @param {String} id The id of the dragged element
3619 * @return {Boolean} isValid True if the drag event is valid, else false to cancel
3621 beforeDragEnter : function(target, e, id){
3626 alignElWithMouse: function() {
3627 Roo.dd.DragSource.superclass.alignElWithMouse.apply(this, arguments);
3632 onDragOver : function(e, id){
3633 var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
3634 if(this.beforeDragOver(target, e, id) !== false){
3635 if(target.isNotifyTarget){
3636 var status = target.notifyOver(this, e, this.dragData);
3637 this.proxy.setStatus(status);
3640 if(this.afterDragOver){
3642 * An empty function by default, but provided so that you can perform a custom action
3643 * while the dragged item is over the drop target by providing an implementation.
3644 * @param {Roo.dd.DragDrop} target The drop target
3645 * @param {Event} e The event object
3646 * @param {String} id The id of the dragged element
3647 * @method afterDragOver
3649 this.afterDragOver(target, e, id);
3655 * An empty function by default, but provided so that you can perform a custom action
3656 * while the dragged item is over the drop target and optionally cancel the onDragOver.
3657 * @param {Roo.dd.DragDrop} target The drop target
3658 * @param {Event} e The event object
3659 * @param {String} id The id of the dragged element
3660 * @return {Boolean} isValid True if the drag event is valid, else false to cancel
3662 beforeDragOver : function(target, e, id){
3667 onDragOut : function(e, id){
3668 var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
3669 if(this.beforeDragOut(target, e, id) !== false){
3670 if(target.isNotifyTarget){
3671 target.notifyOut(this, e, this.dragData);
3674 if(this.afterDragOut){
3676 * An empty function by default, but provided so that you can perform a custom action
3677 * after the dragged item is dragged out of the target without dropping.
3678 * @param {Roo.dd.DragDrop} target The drop target
3679 * @param {Event} e The event object
3680 * @param {String} id The id of the dragged element
3681 * @method afterDragOut
3683 this.afterDragOut(target, e, id);
3686 this.cachedTarget = null;
3690 * An empty function by default, but provided so that you can perform a custom action before the dragged
3691 * item is dragged out of the target without dropping, and optionally cancel the onDragOut.
3692 * @param {Roo.dd.DragDrop} target The drop target
3693 * @param {Event} e The event object
3694 * @param {String} id The id of the dragged element
3695 * @return {Boolean} isValid True if the drag event is valid, else false to cancel
3697 beforeDragOut : function(target, e, id){
3702 onDragDrop : function(e, id){
3703 var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
3704 if(this.beforeDragDrop(target, e, id) !== false){
3705 if(target.isNotifyTarget){
3706 if(target.notifyDrop(this, e, this.dragData)){ // valid drop?
3707 this.onValidDrop(target, e, id);
3709 this.onInvalidDrop(target, e, id);
3712 this.onValidDrop(target, e, id);
3715 if(this.afterDragDrop){
3717 * An empty function by default, but provided so that you can perform a custom action
3718 * after a valid drag drop has occurred by providing an implementation.
3719 * @param {Roo.dd.DragDrop} target The drop target
3720 * @param {Event} e The event object
3721 * @param {String} id The id of the dropped element
3722 * @method afterDragDrop
3724 this.afterDragDrop(target, e, id);
3727 delete this.cachedTarget;
3731 * An empty function by default, but provided so that you can perform a custom action before the dragged
3732 * item is dropped onto the target and optionally cancel the onDragDrop.
3733 * @param {Roo.dd.DragDrop} target The drop target
3734 * @param {Event} e The event object
3735 * @param {String} id The id of the dragged element
3736 * @return {Boolean} isValid True if the drag drop event is valid, else false to cancel
3738 beforeDragDrop : function(target, e, id){
3743 onValidDrop : function(target, e, id){
3745 if(this.afterValidDrop){
3747 * An empty function by default, but provided so that you can perform a custom action
3748 * after a valid drop has occurred by providing an implementation.
3749 * @param {Object} target The target DD
3750 * @param {Event} e The event object
3751 * @param {String} id The id of the dropped element
3752 * @method afterInvalidDrop
3754 this.afterValidDrop(target, e, id);
3759 getRepairXY : function(e, data){
3760 return this.el.getXY();
3764 onInvalidDrop : function(target, e, id){
3765 this.beforeInvalidDrop(target, e, id);
3766 if(this.cachedTarget){
3767 if(this.cachedTarget.isNotifyTarget){
3768 this.cachedTarget.notifyOut(this, e, this.dragData);
3770 this.cacheTarget = null;
3772 this.proxy.repair(this.getRepairXY(e, this.dragData), this.afterRepair, this);
3774 if(this.afterInvalidDrop){
3776 * An empty function by default, but provided so that you can perform a custom action
3777 * after an invalid drop has occurred by providing an implementation.
3778 * @param {Event} e The event object
3779 * @param {String} id The id of the dropped element
3780 * @method afterInvalidDrop
3782 this.afterInvalidDrop(e, id);
3787 afterRepair : function(){
3789 this.el.highlight(this.hlColor || "c3daf9");
3791 this.dragging = false;
3795 * An empty function by default, but provided so that you can perform a custom action after an invalid
3796 * drop has occurred.
3797 * @param {Roo.dd.DragDrop} target The drop target
3798 * @param {Event} e The event object
3799 * @param {String} id The id of the dragged element
3800 * @return {Boolean} isValid True if the invalid drop should proceed, else false to cancel
3802 beforeInvalidDrop : function(target, e, id){
3807 handleMouseDown : function(e){
3811 var data = this.getDragData(e);
3812 if(data && this.onBeforeDrag(data, e) !== false){
3813 this.dragData = data;
3815 Roo.dd.DragSource.superclass.handleMouseDown.apply(this, arguments);
3820 * An empty function by default, but provided so that you can perform a custom action before the initial
3821 * drag event begins and optionally cancel it.
3822 * @param {Object} data An object containing arbitrary data to be shared with drop targets
3823 * @param {Event} e The event object
3824 * @return {Boolean} isValid True if the drag event is valid, else false to cancel
3826 onBeforeDrag : function(data, e){
3831 * An empty function by default, but provided so that you can perform a custom action once the initial
3832 * drag event has begun. The drag cannot be canceled from this function.
3833 * @param {Number} x The x position of the click on the dragged object
3834 * @param {Number} y The y position of the click on the dragged object
3836 onStartDrag : Roo.emptyFn,
3838 // private - YUI override
3839 startDrag : function(x, y){
3841 this.dragging = true;
3842 this.proxy.update("");
3843 this.onInitDrag(x, y);
3848 onInitDrag : function(x, y){
3849 var clone = this.el.dom.cloneNode(true);
3850 clone.id = Roo.id(); // prevent duplicate ids
3851 this.proxy.update(clone);
3852 this.onStartDrag(x, y);
3857 * Returns the drag source's underlying {@link Roo.dd.StatusProxy}
3858 * @return {Roo.dd.StatusProxy} proxy The StatusProxy
3860 getProxy : function(){
3865 * Hides the drag source's {@link Roo.dd.StatusProxy}
3867 hideProxy : function(){
3869 this.proxy.reset(true);
3870 this.dragging = false;
3874 triggerCacheRefresh : function(){
3875 Roo.dd.DDM.refreshCache(this.groups);
3878 // private - override to prevent hiding
3879 b4EndDrag: function(e) {
3882 // private - override to prevent moving
3883 endDrag : function(e){
3884 this.onEndDrag(this.dragData, e);
3888 onEndDrag : function(data, e){
3891 // private - pin to cursor
3892 autoOffset : function(x, y) {
3893 this.setDelta(-12, -20);
3897 * Ext JS Library 1.1.1
3898 * Copyright(c) 2006-2007, Ext JS, LLC.
3900 * Originally Released Under LGPL - original licence link has changed is not relivant.
3903 * <script type="text/javascript">
3908 * @class Roo.dd.DropTarget
3909 * @extends Roo.dd.DDTarget
3910 * A simple class that provides the basic implementation needed to make any element a drop target that can have
3911 * draggable items dropped onto it. The drop has no effect until an implementation of notifyDrop is provided.
3913 * @param {String/HTMLElement/Element} el The container element
3914 * @param {Object} config
3916 Roo.dd.DropTarget = function(el, config){
3917 this.el = Roo.get(el);
3919 var listeners = false; ;
3920 if (config && config.listeners) {
3921 listeners= config.listeners;
3922 delete config.listeners;
3924 Roo.apply(this, config);
3926 if(this.containerScroll){
3927 Roo.dd.ScrollManager.register(this.el);
3931 * @scope Roo.dd.DropTarget
3936 * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the source is now over the
3937 * target. This default implementation adds the CSS class specified by overClass (if any) to the drop element
3938 * and returns the dropAllowed config value. This method should be overridden if drop validation is required.
3940 * IMPORTANT : it should set this.overClass and this.dropAllowed
3942 * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
3943 * @param {Event} e The event
3944 * @param {Object} data An object containing arbitrary data supplied by the drag source
3950 * The function a {@link Roo.dd.DragSource} calls continuously while it is being dragged over the target.
3951 * This method will be called on every mouse movement while the drag source is over the drop target.
3952 * This default implementation simply returns the dropAllowed config value.
3954 * IMPORTANT : it should set this.dropAllowed
3956 * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
3957 * @param {Event} e The event
3958 * @param {Object} data An object containing arbitrary data supplied by the drag source
3964 * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the source has been dragged
3965 * out of the target without dropping. This default implementation simply removes the CSS class specified by
3966 * overClass (if any) from the drop element.
3968 * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
3969 * @param {Event} e The event
3970 * @param {Object} data An object containing arbitrary data supplied by the drag source
3976 * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the dragged item has
3977 * been dropped on it. This method has no default implementation and returns false, so you must provide an
3978 * implementation that does something to process the drop event and returns true so that the drag source's
3979 * repair action does not run.
3981 * IMPORTANT : it should set this.success
3983 * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
3984 * @param {Event} e The event
3985 * @param {Object} data An object containing arbitrary data supplied by the drag source
3991 Roo.dd.DropTarget.superclass.constructor.call( this,
3993 this.ddGroup || this.group,
3996 listeners : listeners || {}
4004 Roo.extend(Roo.dd.DropTarget, Roo.dd.DDTarget, {
4006 * @cfg {String} overClass
4007 * The CSS class applied to the drop target element while the drag source is over it (defaults to "").
4010 * @cfg {String} ddGroup
4011 * The drag drop group to handle drop events for
4015 * @cfg {String} dropAllowed
4016 * The CSS class returned to the drag source when drop is allowed (defaults to "x-dd-drop-ok").
4018 dropAllowed : "x-dd-drop-ok",
4020 * @cfg {String} dropNotAllowed
4021 * The CSS class returned to the drag source when drop is not allowed (defaults to "x-dd-drop-nodrop").
4023 dropNotAllowed : "x-dd-drop-nodrop",
4025 * @cfg {boolean} success
4026 * set this after drop listener..
4030 * @cfg {boolean|String} valid true/false or string (ok-add/ok-sub/ok/nodrop)
4031 * if the drop point is valid for over/enter..
4038 isNotifyTarget : true,
4043 notifyEnter : function(dd, e, data)
4046 this.fireEvent('enter', dd, e, data);
4048 this.el.addClass(this.overClass);
4050 return typeof(this.valid) == 'string' ? 'x-dd-drop-' + this.valid : (
4051 this.valid ? this.dropAllowed : this.dropNotAllowed
4058 notifyOver : function(dd, e, data)
4061 this.fireEvent('over', dd, e, data);
4062 return typeof(this.valid) == 'string' ? 'x-dd-drop-' + this.valid : (
4063 this.valid ? this.dropAllowed : this.dropNotAllowed
4070 notifyOut : function(dd, e, data)
4072 this.fireEvent('out', dd, e, data);
4074 this.el.removeClass(this.overClass);
4081 notifyDrop : function(dd, e, data)
4083 this.success = false;
4084 this.fireEvent('drop', dd, e, data);
4085 return this.success;
4089 * Ext JS Library 1.1.1
4090 * Copyright(c) 2006-2007, Ext JS, LLC.
4092 * Originally Released Under LGPL - original licence link has changed is not relivant.
4095 * <script type="text/javascript">
4100 * @class Roo.dd.DragZone
4101 * @extends Roo.dd.DragSource
4102 * This class provides a container DD instance that proxies for multiple child node sources.<br />
4103 * By default, this class requires that draggable child nodes are registered with {@link Roo.dd.Registry}.
4105 * @param {String/HTMLElement/Element} el The container element
4106 * @param {Object} config
4108 Roo.dd.DragZone = function(el, config){
4109 Roo.dd.DragZone.superclass.constructor.call(this, el, config);
4110 if(this.containerScroll){
4111 Roo.dd.ScrollManager.register(this.el);
4115 Roo.extend(Roo.dd.DragZone, Roo.dd.DragSource, {
4117 * @cfg {Boolean} containerScroll True to register this container with the Scrollmanager
4118 * for auto scrolling during drag operations.
4121 * @cfg {String} hlColor The color to use when visually highlighting the drag source in the afterRepair
4122 * method after a failed drop (defaults to "c3daf9" - light blue)
4126 * Called when a mousedown occurs in this container. Looks in {@link Roo.dd.Registry}
4127 * for a valid target to drag based on the mouse down. Override this method
4128 * to provide your own lookup logic (e.g. finding a child by class name). Make sure your returned
4129 * object has a "ddel" attribute (with an HTML Element) for other functions to work.
4130 * @param {EventObject} e The mouse down event
4131 * @return {Object} The dragData
4133 getDragData : function(e){
4134 return Roo.dd.Registry.getHandleFromEvent(e);
4138 * Called once drag threshold has been reached to initialize the proxy element. By default, it clones the
4139 * this.dragData.ddel
4140 * @param {Number} x The x position of the click on the dragged object
4141 * @param {Number} y The y position of the click on the dragged object
4142 * @return {Boolean} true to continue the drag, false to cancel
4144 onInitDrag : function(x, y){
4145 this.proxy.update(this.dragData.ddel.cloneNode(true));
4146 this.onStartDrag(x, y);
4151 * Called after a repair of an invalid drop. By default, highlights this.dragData.ddel
4153 afterRepair : function(){
4155 Roo.Element.fly(this.dragData.ddel).highlight(this.hlColor || "c3daf9");
4157 this.dragging = false;
4161 * Called before a repair of an invalid drop to get the XY to animate to. By default returns
4162 * the XY of this.dragData.ddel
4163 * @param {EventObject} e The mouse up event
4164 * @return {Array} The xy location (e.g. [100, 200])
4166 getRepairXY : function(e){
4167 return Roo.Element.fly(this.dragData.ddel).getXY();
4171 * Ext JS Library 1.1.1
4172 * Copyright(c) 2006-2007, Ext JS, LLC.
4174 * Originally Released Under LGPL - original licence link has changed is not relivant.
4177 * <script type="text/javascript">
4180 * @class Roo.dd.DropZone
4181 * @extends Roo.dd.DropTarget
4182 * This class provides a container DD instance that proxies for multiple child node targets.<br />
4183 * By default, this class requires that child nodes accepting drop are registered with {@link Roo.dd.Registry}.
4185 * @param {String/HTMLElement/Element} el The container element
4186 * @param {Object} config
4188 Roo.dd.DropZone = function(el, config){
4189 Roo.dd.DropZone.superclass.constructor.call(this, el, config);
4192 Roo.extend(Roo.dd.DropZone, Roo.dd.DropTarget, {
4194 * Returns a custom data object associated with the DOM node that is the target of the event. By default
4195 * this looks up the event target in the {@link Roo.dd.Registry}, although you can override this method to
4196 * provide your own custom lookup.
4197 * @param {Event} e The event
4198 * @return {Object} data The custom data
4200 getTargetFromEvent : function(e){
4201 return Roo.dd.Registry.getTargetFromEvent(e);
4205 * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has entered a drop node
4206 * that it has registered. This method has no default implementation and should be overridden to provide
4207 * node-specific processing if necessary.
4208 * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
4209 * {@link #getTargetFromEvent} for this node)
4210 * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4211 * @param {Event} e The event
4212 * @param {Object} data An object containing arbitrary data supplied by the drag source
4214 onNodeEnter : function(n, dd, e, data){
4219 * Called internally while the DropZone determines that a {@link Roo.dd.DragSource} is over a drop node
4220 * that it has registered. The default implementation returns this.dropNotAllowed, so it should be
4221 * overridden to provide the proper feedback.
4222 * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
4223 * {@link #getTargetFromEvent} for this node)
4224 * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4225 * @param {Event} e The event
4226 * @param {Object} data An object containing arbitrary data supplied by the drag source
4227 * @return {String} status The CSS class that communicates the drop status back to the source so that the
4228 * underlying {@link Roo.dd.StatusProxy} can be updated
4230 onNodeOver : function(n, dd, e, data){
4231 return this.dropAllowed;
4235 * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dragged out of
4236 * the drop node without dropping. This method has no default implementation and should be overridden to provide
4237 * node-specific processing if necessary.
4238 * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
4239 * {@link #getTargetFromEvent} for this node)
4240 * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4241 * @param {Event} e The event
4242 * @param {Object} data An object containing arbitrary data supplied by the drag source
4244 onNodeOut : function(n, dd, e, data){
4249 * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dropped onto
4250 * the drop node. The default implementation returns false, so it should be overridden to provide the
4251 * appropriate processing of the drop event and return true so that the drag source's repair action does not run.
4252 * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
4253 * {@link #getTargetFromEvent} for this node)
4254 * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4255 * @param {Event} e The event
4256 * @param {Object} data An object containing arbitrary data supplied by the drag source
4257 * @return {Boolean} True if the drop was valid, else false
4259 onNodeDrop : function(n, dd, e, data){
4264 * Called internally while the DropZone determines that a {@link Roo.dd.DragSource} is being dragged over it,
4265 * but not over any of its registered drop nodes. The default implementation returns this.dropNotAllowed, so
4266 * it should be overridden to provide the proper feedback if necessary.
4267 * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4268 * @param {Event} e The event
4269 * @param {Object} data An object containing arbitrary data supplied by the drag source
4270 * @return {String} status The CSS class that communicates the drop status back to the source so that the
4271 * underlying {@link Roo.dd.StatusProxy} can be updated
4273 onContainerOver : function(dd, e, data){
4274 return this.dropNotAllowed;
4278 * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dropped on it,
4279 * but not on any of its registered drop nodes. The default implementation returns false, so it should be
4280 * overridden to provide the appropriate processing of the drop event if you need the drop zone itself to
4281 * be able to accept drops. It should return true when valid so that the drag source's repair action does not run.
4282 * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4283 * @param {Event} e The event
4284 * @param {Object} data An object containing arbitrary data supplied by the drag source
4285 * @return {Boolean} True if the drop was valid, else false
4287 onContainerDrop : function(dd, e, data){
4292 * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the source is now over
4293 * the zone. The default implementation returns this.dropNotAllowed and expects that only registered drop
4294 * nodes can process drag drop operations, so if you need the drop zone itself to be able to process drops
4295 * you should override this method and provide a custom implementation.
4296 * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4297 * @param {Event} e The event
4298 * @param {Object} data An object containing arbitrary data supplied by the drag source
4299 * @return {String} status The CSS class that communicates the drop status back to the source so that the
4300 * underlying {@link Roo.dd.StatusProxy} can be updated
4302 notifyEnter : function(dd, e, data){
4303 return this.dropNotAllowed;
4307 * The function a {@link Roo.dd.DragSource} calls continuously while it is being dragged over the drop zone.
4308 * This method will be called on every mouse movement while the drag source is over the drop zone.
4309 * It will call {@link #onNodeOver} while the drag source is over a registered node, and will also automatically
4310 * delegate to the appropriate node-specific methods as necessary when the drag source enters and exits
4311 * registered nodes ({@link #onNodeEnter}, {@link #onNodeOut}). If the drag source is not currently over a
4312 * registered node, it will call {@link #onContainerOver}.
4313 * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4314 * @param {Event} e The event
4315 * @param {Object} data An object containing arbitrary data supplied by the drag source
4316 * @return {String} status The CSS class that communicates the drop status back to the source so that the
4317 * underlying {@link Roo.dd.StatusProxy} can be updated
4319 notifyOver : function(dd, e, data){
4320 var n = this.getTargetFromEvent(e);
4321 if(!n){ // not over valid drop target
4322 if(this.lastOverNode){
4323 this.onNodeOut(this.lastOverNode, dd, e, data);
4324 this.lastOverNode = null;
4326 return this.onContainerOver(dd, e, data);
4328 if(this.lastOverNode != n){
4329 if(this.lastOverNode){
4330 this.onNodeOut(this.lastOverNode, dd, e, data);
4332 this.onNodeEnter(n, dd, e, data);
4333 this.lastOverNode = n;
4335 return this.onNodeOver(n, dd, e, data);
4339 * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the source has been dragged
4340 * out of the zone without dropping. If the drag source is currently over a registered node, the notification
4341 * will be delegated to {@link #onNodeOut} for node-specific handling, otherwise it will be ignored.
4342 * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
4343 * @param {Event} e The event
4344 * @param {Object} data An object containing arbitrary data supplied by the drag zone
4346 notifyOut : function(dd, e, data){
4347 if(this.lastOverNode){
4348 this.onNodeOut(this.lastOverNode, dd, e, data);
4349 this.lastOverNode = null;
4354 * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the dragged item has
4355 * been dropped on it. The drag zone will look up the target node based on the event passed in, and if there
4356 * is a node registered for that event, it will delegate to {@link #onNodeDrop} for node-specific handling,
4357 * otherwise it will call {@link #onContainerDrop}.
4358 * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4359 * @param {Event} e The event
4360 * @param {Object} data An object containing arbitrary data supplied by the drag source
4361 * @return {Boolean} True if the drop was valid, else false
4363 notifyDrop : function(dd, e, data){
4364 if(this.lastOverNode){
4365 this.onNodeOut(this.lastOverNode, dd, e, data);
4366 this.lastOverNode = null;
4368 var n = this.getTargetFromEvent(e);
4370 this.onNodeDrop(n, dd, e, data) :
4371 this.onContainerDrop(dd, e, data);
4375 triggerCacheRefresh : function(){
4376 Roo.dd.DDM.refreshCache(this.groups);
4380 * Ext JS Library 1.1.1
4381 * Copyright(c) 2006-2007, Ext JS, LLC.
4383 * Originally Released Under LGPL - original licence link has changed is not relivant.
4386 * <script type="text/javascript">
4391 * @class Roo.data.SortTypes
4393 * Defines the default sorting (casting?) comparison functions used when sorting data.
4395 Roo.data.SortTypes = {
4397 * Default sort that does nothing
4398 * @param {Mixed} s The value being converted
4399 * @return {Mixed} The comparison value
4406 * The regular expression used to strip tags
4410 stripTagsRE : /<\/?[^>]+>/gi,
4413 * Strips all HTML tags to sort on text only
4414 * @param {Mixed} s The value being converted
4415 * @return {String} The comparison value
4417 asText : function(s){
4418 return String(s).replace(this.stripTagsRE, "");
4422 * Strips all HTML tags to sort on text only - Case insensitive
4423 * @param {Mixed} s The value being converted
4424 * @return {String} The comparison value
4426 asUCText : function(s){
4427 return String(s).toUpperCase().replace(this.stripTagsRE, "");
4431 * Case insensitive string
4432 * @param {Mixed} s The value being converted
4433 * @return {String} The comparison value
4435 asUCString : function(s) {
4436 return String(s).toUpperCase();
4441 * @param {Mixed} s The value being converted
4442 * @return {Number} The comparison value
4444 asDate : function(s) {
4448 if(s instanceof Date){
4451 return Date.parse(String(s));
4456 * @param {Mixed} s The value being converted
4457 * @return {Float} The comparison value
4459 asFloat : function(s) {
4460 var val = parseFloat(String(s).replace(/,/g, ""));
4461 if(isNaN(val)) val = 0;
4467 * @param {Mixed} s The value being converted
4468 * @return {Number} The comparison value
4470 asInt : function(s) {
4471 var val = parseInt(String(s).replace(/,/g, ""));
4472 if(isNaN(val)) val = 0;
4477 * Ext JS Library 1.1.1
4478 * Copyright(c) 2006-2007, Ext JS, LLC.
4480 * Originally Released Under LGPL - original licence link has changed is not relivant.
4483 * <script type="text/javascript">
4487 * @class Roo.data.Record
4488 * Instances of this class encapsulate both record <em>definition</em> information, and record
4489 * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
4490 * to access Records cached in an {@link Roo.data.Store} object.<br>
4492 * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
4493 * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
4496 * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
4498 * This constructor should not be used to create Record objects. Instead, use the constructor generated by
4499 * {@link #create}. The parameters are the same.
4500 * @param {Array} data An associative Array of data values keyed by the field name.
4501 * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
4502 * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
4503 * not specified an integer id is generated.
4505 Roo.data.Record = function(data, id){
4506 this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
4511 * Generate a constructor for a specific record layout.
4512 * @param {Array} o An Array of field definition objects which specify field names, and optionally,
4513 * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
4514 * Each field definition object may contain the following properties: <ul>
4515 * <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,
4516 * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
4517 * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
4518 * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
4519 * is being used, then this is a string containing the javascript expression to reference the data relative to
4520 * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
4521 * to the data item relative to the record element. If the mapping expression is the same as the field name,
4522 * this may be omitted.</p></li>
4523 * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
4524 * <ul><li>auto (Default, implies no conversion)</li>
4529 * <li>date</li></ul></p></li>
4530 * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
4531 * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
4532 * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
4533 * by the Reader into an object that will be stored in the Record. It is passed the
4534 * following parameters:<ul>
4535 * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
4537 * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
4539 * <br>usage:<br><pre><code>
4540 var TopicRecord = Roo.data.Record.create(
4541 {name: 'title', mapping: 'topic_title'},
4542 {name: 'author', mapping: 'username'},
4543 {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
4544 {name: 'lastPost', mapping: 'post_time', type: 'date'},
4545 {name: 'lastPoster', mapping: 'user2'},
4546 {name: 'excerpt', mapping: 'post_text'}
4549 var myNewRecord = new TopicRecord({
4550 title: 'Do my job please',
4553 lastPost: new Date(),
4554 lastPoster: 'Animal',
4555 excerpt: 'No way dude!'
4557 myStore.add(myNewRecord);
4562 Roo.data.Record.create = function(o){
4564 f.superclass.constructor.apply(this, arguments);
4566 Roo.extend(f, Roo.data.Record);
4567 var p = f.prototype;
4568 p.fields = new Roo.util.MixedCollection(false, function(field){
4571 for(var i = 0, len = o.length; i < len; i++){
4572 p.fields.add(new Roo.data.Field(o[i]));
4574 f.getField = function(name){
4575 return p.fields.get(name);
4580 Roo.data.Record.AUTO_ID = 1000;
4581 Roo.data.Record.EDIT = 'edit';
4582 Roo.data.Record.REJECT = 'reject';
4583 Roo.data.Record.COMMIT = 'commit';
4585 Roo.data.Record.prototype = {
4587 * Readonly flag - true if this record has been modified.
4596 join : function(store){
4601 * Set the named field to the specified value.
4602 * @param {String} name The name of the field to set.
4603 * @param {Object} value The value to set the field to.
4605 set : function(name, value){
4606 if(this.data[name] == value){
4613 if(typeof this.modified[name] == 'undefined'){
4614 this.modified[name] = this.data[name];
4616 this.data[name] = value;
4617 if(!this.editing && this.store){
4618 this.store.afterEdit(this);
4623 * Get the value of the named field.
4624 * @param {String} name The name of the field to get the value of.
4625 * @return {Object} The value of the field.
4627 get : function(name){
4628 return this.data[name];
4632 beginEdit : function(){
4633 this.editing = true;
4638 cancelEdit : function(){
4639 this.editing = false;
4640 delete this.modified;
4644 endEdit : function(){
4645 this.editing = false;
4646 if(this.dirty && this.store){
4647 this.store.afterEdit(this);
4652 * Usually called by the {@link Roo.data.Store} which owns the Record.
4653 * Rejects all changes made to the Record since either creation, or the last commit operation.
4654 * Modified fields are reverted to their original values.
4656 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
4657 * of reject operations.
4659 reject : function(){
4660 var m = this.modified;
4662 if(typeof m[n] != "function"){
4663 this.data[n] = m[n];
4667 delete this.modified;
4668 this.editing = false;
4670 this.store.afterReject(this);
4675 * Usually called by the {@link Roo.data.Store} which owns the Record.
4676 * Commits all changes made to the Record since either creation, or the last commit operation.
4678 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
4679 * of commit operations.
4681 commit : function(){
4683 delete this.modified;
4684 this.editing = false;
4686 this.store.afterCommit(this);
4691 hasError : function(){
4692 return this.error != null;
4696 clearError : function(){
4701 * Creates a copy of this record.
4702 * @param {String} id (optional) A new record id if you don't want to use this record's id
4705 copy : function(newId) {
4706 return new this.constructor(Roo.apply({}, this.data), newId || this.id);
4710 * Ext JS Library 1.1.1
4711 * Copyright(c) 2006-2007, Ext JS, LLC.
4713 * Originally Released Under LGPL - original licence link has changed is not relivant.
4716 * <script type="text/javascript">
4722 * @class Roo.data.Store
4723 * @extends Roo.util.Observable
4724 * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
4725 * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
4727 * 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
4728 * has no knowledge of the format of the data returned by the Proxy.<br>
4730 * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
4731 * instances from the data object. These records are cached and made available through accessor functions.
4733 * Creates a new Store.
4734 * @param {Object} config A config object containing the objects needed for the Store to access data,
4735 * and read the data into Records.
4737 Roo.data.Store = function(config){
4738 this.data = new Roo.util.MixedCollection(false);
4739 this.data.getKey = function(o){
4742 this.baseParams = {};
4749 "multisort" : "_multisort"
4752 if(config && config.data){
4753 this.inlineData = config.data;
4757 Roo.apply(this, config);
4759 if(this.reader){ // reader passed
4760 this.reader = Roo.factory(this.reader, Roo.data);
4761 this.reader.xmodule = this.xmodule || false;
4762 if(!this.recordType){
4763 this.recordType = this.reader.recordType;
4765 if(this.reader.onMetaChange){
4766 this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
4770 if(this.recordType){
4771 this.fields = this.recordType.prototype.fields;
4777 * @event datachanged
4778 * Fires when the data cache has changed, and a widget which is using this Store
4779 * as a Record cache should refresh its view.
4780 * @param {Store} this
4785 * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
4786 * @param {Store} this
4787 * @param {Object} meta The JSON metadata
4792 * Fires when Records have been added to the Store
4793 * @param {Store} this
4794 * @param {Roo.data.Record[]} records The array of Records added
4795 * @param {Number} index The index at which the record(s) were added
4800 * Fires when a Record has been removed from the Store
4801 * @param {Store} this
4802 * @param {Roo.data.Record} record The Record that was removed
4803 * @param {Number} index The index at which the record was removed
4808 * Fires when a Record has been updated
4809 * @param {Store} this
4810 * @param {Roo.data.Record} record The Record that was updated
4811 * @param {String} operation The update operation being performed. Value may be one of:
4813 Roo.data.Record.EDIT
4814 Roo.data.Record.REJECT
4815 Roo.data.Record.COMMIT
4821 * Fires when the data cache has been cleared.
4822 * @param {Store} this
4827 * Fires before a request is made for a new data object. If the beforeload handler returns false
4828 * the load action will be canceled.
4829 * @param {Store} this
4830 * @param {Object} options The loading options that were specified (see {@link #load} for details)
4834 * @event beforeloadadd
4835 * Fires after a new set of Records has been loaded.
4836 * @param {Store} this
4837 * @param {Roo.data.Record[]} records The Records that were loaded
4838 * @param {Object} options The loading options that were specified (see {@link #load} for details)
4840 beforeloadadd : true,
4843 * Fires after a new set of Records has been loaded, before they are added to the store.
4844 * @param {Store} this
4845 * @param {Roo.data.Record[]} records The Records that were loaded
4846 * @param {Object} options The loading options that were specified (see {@link #load} for details)
4847 * @params {Object} return from reader
4851 * @event loadexception
4852 * Fires if an exception occurs in the Proxy during loading.
4853 * Called with the signature of the Proxy's "loadexception" event.
4854 * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
4857 * @param {Object} return from JsonData.reader() - success, totalRecords, records
4858 * @param {Object} load options
4859 * @param {Object} jsonData from your request (normally this contains the Exception)
4861 loadexception : true
4865 this.proxy = Roo.factory(this.proxy, Roo.data);
4866 this.proxy.xmodule = this.xmodule || false;
4867 this.relayEvents(this.proxy, ["loadexception"]);
4869 this.sortToggle = {};
4870 this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
4872 Roo.data.Store.superclass.constructor.call(this);
4874 if(this.inlineData){
4875 this.loadData(this.inlineData);
4876 delete this.inlineData;
4880 Roo.extend(Roo.data.Store, Roo.util.Observable, {
4882 * @cfg {boolean} isLocal flag if data is locally available (and can be always looked up
4883 * without a remote query - used by combo/forms at present.
4887 * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
4890 * @cfg {Array} data Inline data to be loaded when the store is initialized.
4893 * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
4894 * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
4897 * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
4898 * on any HTTP request
4901 * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
4904 * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
4908 * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
4909 * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
4914 * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
4915 * loaded or when a record is removed. (defaults to false).
4917 pruneModifiedRecords : false,
4923 * Add Records to the Store and fires the add event.
4924 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
4926 add : function(records){
4927 records = [].concat(records);
4928 for(var i = 0, len = records.length; i < len; i++){
4929 records[i].join(this);
4931 var index = this.data.length;
4932 this.data.addAll(records);
4933 this.fireEvent("add", this, records, index);
4937 * Remove a Record from the Store and fires the remove event.
4938 * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
4940 remove : function(record){
4941 var index = this.data.indexOf(record);
4942 this.data.removeAt(index);
4943 if(this.pruneModifiedRecords){
4944 this.modified.remove(record);
4946 this.fireEvent("remove", this, record, index);
4950 * Remove all Records from the Store and fires the clear event.
4952 removeAll : function(){
4954 if(this.pruneModifiedRecords){
4957 this.fireEvent("clear", this);
4961 * Inserts Records to the Store at the given index and fires the add event.
4962 * @param {Number} index The start index at which to insert the passed Records.
4963 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
4965 insert : function(index, records){
4966 records = [].concat(records);
4967 for(var i = 0, len = records.length; i < len; i++){
4968 this.data.insert(index, records[i]);
4969 records[i].join(this);
4971 this.fireEvent("add", this, records, index);
4975 * Get the index within the cache of the passed Record.
4976 * @param {Roo.data.Record} record The Roo.data.Record object to to find.
4977 * @return {Number} The index of the passed Record. Returns -1 if not found.
4979 indexOf : function(record){
4980 return this.data.indexOf(record);
4984 * Get the index within the cache of the Record with the passed id.
4985 * @param {String} id The id of the Record to find.
4986 * @return {Number} The index of the Record. Returns -1 if not found.
4988 indexOfId : function(id){
4989 return this.data.indexOfKey(id);
4993 * Get the Record with the specified id.
4994 * @param {String} id The id of the Record to find.
4995 * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
4997 getById : function(id){
4998 return this.data.key(id);
5002 * Get the Record at the specified index.
5003 * @param {Number} index The index of the Record to find.
5004 * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
5006 getAt : function(index){
5007 return this.data.itemAt(index);
5011 * Returns a range of Records between specified indices.
5012 * @param {Number} startIndex (optional) The starting index (defaults to 0)
5013 * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
5014 * @return {Roo.data.Record[]} An array of Records
5016 getRange : function(start, end){
5017 return this.data.getRange(start, end);
5021 storeOptions : function(o){
5022 o = Roo.apply({}, o);
5025 this.lastOptions = o;
5029 * Loads the Record cache from the configured Proxy using the configured Reader.
5031 * If using remote paging, then the first load call must specify the <em>start</em>
5032 * and <em>limit</em> properties in the options.params property to establish the initial
5033 * position within the dataset, and the number of Records to cache on each read from the Proxy.
5035 * <strong>It is important to note that for remote data sources, loading is asynchronous,
5036 * and this call will return before the new data has been loaded. Perform any post-processing
5037 * in a callback function, or in a "load" event handler.</strong>
5039 * @param {Object} options An object containing properties which control loading options:<ul>
5040 * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
5041 * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
5042 * passed the following arguments:<ul>
5043 * <li>r : Roo.data.Record[]</li>
5044 * <li>options: Options object from the load call</li>
5045 * <li>success: Boolean success indicator</li></ul></li>
5046 * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
5047 * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
5050 load : function(options){
5051 options = options || {};
5052 if(this.fireEvent("beforeload", this, options) !== false){
5053 this.storeOptions(options);
5054 var p = Roo.apply(options.params || {}, this.baseParams);
5055 // if meta was not loaded from remote source.. try requesting it.
5056 if (!this.reader.metaFromRemote) {
5059 if(this.sortInfo && this.remoteSort){
5060 var pn = this.paramNames;
5061 p[pn["sort"]] = this.sortInfo.field;
5062 p[pn["dir"]] = this.sortInfo.direction;
5064 if (this.multiSort) {
5065 var pn = this.paramNames;
5066 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
5069 this.proxy.load(p, this.reader, this.loadRecords, this, options);
5074 * Reloads the Record cache from the configured Proxy using the configured Reader and
5075 * the options from the last load operation performed.
5076 * @param {Object} options (optional) An object containing properties which may override the options
5077 * used in the last load operation. See {@link #load} for details (defaults to null, in which case
5078 * the most recently used options are reused).
5080 reload : function(options){
5081 this.load(Roo.applyIf(options||{}, this.lastOptions));
5085 // Called as a callback by the Reader during a load operation.
5086 loadRecords : function(o, options, success){
5087 if(!o || success === false){
5088 if(success !== false){
5089 this.fireEvent("load", this, [], options, o);
5091 if(options.callback){
5092 options.callback.call(options.scope || this, [], options, false);
5096 // if data returned failure - throw an exception.
5097 if (o.success === false) {
5098 // show a message if no listener is registered.
5099 if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
5100 Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
5102 // loadmask wil be hooked into this..
5103 this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
5106 var r = o.records, t = o.totalRecords || r.length;
5108 this.fireEvent("beforeloadadd", this, r, options, o);
5110 if(!options || options.add !== true){
5111 if(this.pruneModifiedRecords){
5114 for(var i = 0, len = r.length; i < len; i++){
5118 this.data = this.snapshot;
5119 delete this.snapshot;
5122 this.data.addAll(r);
5123 this.totalLength = t;
5125 this.fireEvent("datachanged", this);
5127 this.totalLength = Math.max(t, this.data.length+r.length);
5130 this.fireEvent("load", this, r, options, o);
5131 if(options.callback){
5132 options.callback.call(options.scope || this, r, options, true);
5138 * Loads data from a passed data block. A Reader which understands the format of the data
5139 * must have been configured in the constructor.
5140 * @param {Object} data The data block from which to read the Records. The format of the data expected
5141 * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
5142 * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
5144 loadData : function(o, append){
5145 var r = this.reader.readRecords(o);
5146 this.loadRecords(r, {add: append}, true);
5150 * Gets the number of cached records.
5152 * <em>If using paging, this may not be the total size of the dataset. If the data object
5153 * used by the Reader contains the dataset size, then the getTotalCount() function returns
5154 * the data set size</em>
5156 getCount : function(){
5157 return this.data.length || 0;
5161 * Gets the total number of records in the dataset as returned by the server.
5163 * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
5164 * the dataset size</em>
5166 getTotalCount : function(){
5167 return this.totalLength || 0;
5171 * Returns the sort state of the Store as an object with two properties:
5173 field {String} The name of the field by which the Records are sorted
5174 direction {String} The sort order, "ASC" or "DESC"
5177 getSortState : function(){
5178 return this.sortInfo;
5182 applySort : function(){
5183 if(this.sortInfo && !this.remoteSort){
5184 var s = this.sortInfo, f = s.field;
5185 var st = this.fields.get(f).sortType;
5186 var fn = function(r1, r2){
5187 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
5188 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
5190 this.data.sort(s.direction, fn);
5191 if(this.snapshot && this.snapshot != this.data){
5192 this.snapshot.sort(s.direction, fn);
5198 * Sets the default sort column and order to be used by the next load operation.
5199 * @param {String} fieldName The name of the field to sort by.
5200 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
5202 setDefaultSort : function(field, dir){
5203 this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
5208 * If remote sorting is used, the sort is performed on the server, and the cache is
5209 * reloaded. If local sorting is used, the cache is sorted internally.
5210 * @param {String} fieldName The name of the field to sort by.
5211 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
5213 sort : function(fieldName, dir){
5214 var f = this.fields.get(fieldName);
5216 this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
5218 if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
5219 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
5224 this.sortToggle[f.name] = dir;
5225 this.sortInfo = {field: f.name, direction: dir};
5226 if(!this.remoteSort){
5228 this.fireEvent("datachanged", this);
5230 this.load(this.lastOptions);
5235 * Calls the specified function for each of the Records in the cache.
5236 * @param {Function} fn The function to call. The Record is passed as the first parameter.
5237 * Returning <em>false</em> aborts and exits the iteration.
5238 * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
5240 each : function(fn, scope){
5241 this.data.each(fn, scope);
5245 * Gets all records modified since the last commit. Modified records are persisted across load operations
5246 * (e.g., during paging).
5247 * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
5249 getModifiedRecords : function(){
5250 return this.modified;
5254 createFilterFn : function(property, value, anyMatch){
5255 if(!value.exec){ // not a regex
5256 value = String(value);
5257 if(value.length == 0){
5260 value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
5263 return value.test(r.data[property]);
5268 * Sums the value of <i>property</i> for each record between start and end and returns the result.
5269 * @param {String} property A field on your records
5270 * @param {Number} start The record index to start at (defaults to 0)
5271 * @param {Number} end The last record index to include (defaults to length - 1)
5272 * @return {Number} The sum
5274 sum : function(property, start, end){
5275 var rs = this.data.items, v = 0;
5277 end = (end || end === 0) ? end : rs.length-1;
5279 for(var i = start; i <= end; i++){
5280 v += (rs[i].data[property] || 0);
5286 * Filter the records by a specified property.
5287 * @param {String} field A field on your records
5288 * @param {String/RegExp} value Either a string that the field
5289 * should start with or a RegExp to test against the field
5290 * @param {Boolean} anyMatch True to match any part not just the beginning
5292 filter : function(property, value, anyMatch){
5293 var fn = this.createFilterFn(property, value, anyMatch);
5294 return fn ? this.filterBy(fn) : this.clearFilter();
5298 * Filter by a function. The specified function will be called with each
5299 * record in this data source. If the function returns true the record is included,
5300 * otherwise it is filtered.
5301 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
5302 * @param {Object} scope (optional) The scope of the function (defaults to this)
5304 filterBy : function(fn, scope){
5305 this.snapshot = this.snapshot || this.data;
5306 this.data = this.queryBy(fn, scope||this);
5307 this.fireEvent("datachanged", this);
5311 * Query the records by a specified property.
5312 * @param {String} field A field on your records
5313 * @param {String/RegExp} value Either a string that the field
5314 * should start with or a RegExp to test against the field
5315 * @param {Boolean} anyMatch True to match any part not just the beginning
5316 * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
5318 query : function(property, value, anyMatch){
5319 var fn = this.createFilterFn(property, value, anyMatch);
5320 return fn ? this.queryBy(fn) : this.data.clone();
5324 * Query by a function. The specified function will be called with each
5325 * record in this data source. If the function returns true the record is included
5327 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
5328 * @param {Object} scope (optional) The scope of the function (defaults to this)
5329 @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
5331 queryBy : function(fn, scope){
5332 var data = this.snapshot || this.data;
5333 return data.filterBy(fn, scope||this);
5337 * Collects unique values for a particular dataIndex from this store.
5338 * @param {String} dataIndex The property to collect
5339 * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
5340 * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
5341 * @return {Array} An array of the unique values
5343 collect : function(dataIndex, allowNull, bypassFilter){
5344 var d = (bypassFilter === true && this.snapshot) ?
5345 this.snapshot.items : this.data.items;
5346 var v, sv, r = [], l = {};
5347 for(var i = 0, len = d.length; i < len; i++){
5348 v = d[i].data[dataIndex];
5350 if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
5359 * Revert to a view of the Record cache with no filtering applied.
5360 * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
5362 clearFilter : function(suppressEvent){
5363 if(this.snapshot && this.snapshot != this.data){
5364 this.data = this.snapshot;
5365 delete this.snapshot;
5366 if(suppressEvent !== true){
5367 this.fireEvent("datachanged", this);
5373 afterEdit : function(record){
5374 if(this.modified.indexOf(record) == -1){
5375 this.modified.push(record);
5377 this.fireEvent("update", this, record, Roo.data.Record.EDIT);
5381 afterReject : function(record){
5382 this.modified.remove(record);
5383 this.fireEvent("update", this, record, Roo.data.Record.REJECT);
5387 afterCommit : function(record){
5388 this.modified.remove(record);
5389 this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
5393 * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
5394 * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
5396 commitChanges : function(){
5397 var m = this.modified.slice(0);
5399 for(var i = 0, len = m.length; i < len; i++){
5405 * Cancel outstanding changes on all changed records.
5407 rejectChanges : function(){
5408 var m = this.modified.slice(0);
5410 for(var i = 0, len = m.length; i < len; i++){
5415 onMetaChange : function(meta, rtype, o){
5416 this.recordType = rtype;
5417 this.fields = rtype.prototype.fields;
5418 delete this.snapshot;
5419 this.sortInfo = meta.sortInfo || this.sortInfo;
5421 this.fireEvent('metachange', this, this.reader.meta);
5425 * Ext JS Library 1.1.1
5426 * Copyright(c) 2006-2007, Ext JS, LLC.
5428 * Originally Released Under LGPL - original licence link has changed is not relivant.
5431 * <script type="text/javascript">
5435 * @class Roo.data.SimpleStore
5436 * @extends Roo.data.Store
5437 * Small helper class to make creating Stores from Array data easier.
5438 * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
5439 * @cfg {Array} fields An array of field definition objects, or field name strings.
5440 * @cfg {Array} data The multi-dimensional array of data
5442 * @param {Object} config
5444 Roo.data.SimpleStore = function(config){
5445 Roo.data.SimpleStore.superclass.constructor.call(this, {
5447 reader: new Roo.data.ArrayReader({
5450 Roo.data.Record.create(config.fields)
5452 proxy : new Roo.data.MemoryProxy(config.data)
5456 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
5458 * Ext JS Library 1.1.1
5459 * Copyright(c) 2006-2007, Ext JS, LLC.
5461 * Originally Released Under LGPL - original licence link has changed is not relivant.
5464 * <script type="text/javascript">
5469 * @extends Roo.data.Store
5470 * @class Roo.data.JsonStore
5471 * Small helper class to make creating Stores for JSON data easier. <br/>
5473 var store = new Roo.data.JsonStore({
5474 url: 'get-images.php',
5476 fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
5479 * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
5480 * JsonReader and HttpProxy (unless inline data is provided).</b>
5481 * @cfg {Array} fields An array of field definition objects, or field name strings.
5483 * @param {Object} config
5485 Roo.data.JsonStore = function(c){
5486 Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
5487 proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
5488 reader: new Roo.data.JsonReader(c, c.fields)
5491 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
5493 * Ext JS Library 1.1.1
5494 * Copyright(c) 2006-2007, Ext JS, LLC.
5496 * Originally Released Under LGPL - original licence link has changed is not relivant.
5499 * <script type="text/javascript">
5503 Roo.data.Field = function(config){
5504 if(typeof config == "string"){
5505 config = {name: config};
5507 Roo.apply(this, config);
5513 var st = Roo.data.SortTypes;
5514 // named sortTypes are supported, here we look them up
5515 if(typeof this.sortType == "string"){
5516 this.sortType = st[this.sortType];
5519 // set default sortType for strings and dates
5523 this.sortType = st.asUCString;
5526 this.sortType = st.asDate;
5529 this.sortType = st.none;
5534 var stripRe = /[\$,%]/g;
5536 // prebuilt conversion function for this field, instead of
5537 // switching every time we're reading a value
5539 var cv, dateFormat = this.dateFormat;
5544 cv = function(v){ return v; };
5547 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
5551 return v !== undefined && v !== null && v !== '' ?
5552 parseInt(String(v).replace(stripRe, ""), 10) : '';
5557 return v !== undefined && v !== null && v !== '' ?
5558 parseFloat(String(v).replace(stripRe, ""), 10) : '';
5563 cv = function(v){ return v === true || v === "true" || v == 1; };
5570 if(v instanceof Date){
5574 if(dateFormat == "timestamp"){
5575 return new Date(v*1000);
5577 return Date.parseDate(v, dateFormat);
5579 var parsed = Date.parse(v);
5580 return parsed ? new Date(parsed) : null;
5589 Roo.data.Field.prototype = {
5597 * Ext JS Library 1.1.1
5598 * Copyright(c) 2006-2007, Ext JS, LLC.
5600 * Originally Released Under LGPL - original licence link has changed is not relivant.
5603 * <script type="text/javascript">
5606 // Base class for reading structured data from a data source. This class is intended to be
5607 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
5610 * @class Roo.data.DataReader
5611 * Base class for reading structured data from a data source. This class is intended to be
5612 * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
5615 Roo.data.DataReader = function(meta, recordType){
5619 this.recordType = recordType instanceof Array ?
5620 Roo.data.Record.create(recordType) : recordType;
5623 Roo.data.DataReader.prototype = {
5625 * Create an empty record
5626 * @param {Object} data (optional) - overlay some values
5627 * @return {Roo.data.Record} record created.
5629 newRow : function(d) {
5631 this.recordType.prototype.fields.each(function(c) {
5633 case 'int' : da[c.name] = 0; break;
5634 case 'date' : da[c.name] = new Date(); break;
5635 case 'float' : da[c.name] = 0.0; break;
5636 case 'boolean' : da[c.name] = false; break;
5637 default : da[c.name] = ""; break;
5641 return new this.recordType(Roo.apply(da, d));
5646 * Ext JS Library 1.1.1
5647 * Copyright(c) 2006-2007, Ext JS, LLC.
5649 * Originally Released Under LGPL - original licence link has changed is not relivant.
5652 * <script type="text/javascript">
5656 * @class Roo.data.DataProxy
5657 * @extends Roo.data.Observable
5658 * This class is an abstract base class for implementations which provide retrieval of
5659 * unformatted data objects.<br>
5661 * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
5662 * (of the appropriate type which knows how to parse the data object) to provide a block of
5663 * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
5665 * Custom implementations must implement the load method as described in
5666 * {@link Roo.data.HttpProxy#load}.
5668 Roo.data.DataProxy = function(){
5672 * Fires before a network request is made to retrieve a data object.
5673 * @param {Object} This DataProxy object.
5674 * @param {Object} params The params parameter to the load function.
5679 * Fires before the load method's callback is called.
5680 * @param {Object} This DataProxy object.
5681 * @param {Object} o The data object.
5682 * @param {Object} arg The callback argument object passed to the load function.
5686 * @event loadexception
5687 * Fires if an Exception occurs during data retrieval.
5688 * @param {Object} This DataProxy object.
5689 * @param {Object} o The data object.
5690 * @param {Object} arg The callback argument object passed to the load function.
5691 * @param {Object} e The Exception.
5693 loadexception : true
5695 Roo.data.DataProxy.superclass.constructor.call(this);
5698 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
5701 * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
5705 * Ext JS Library 1.1.1
5706 * Copyright(c) 2006-2007, Ext JS, LLC.
5708 * Originally Released Under LGPL - original licence link has changed is not relivant.
5711 * <script type="text/javascript">
5714 * @class Roo.data.MemoryProxy
5715 * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
5716 * to the Reader when its load method is called.
5718 * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
5720 Roo.data.MemoryProxy = function(data){
5724 Roo.data.MemoryProxy.superclass.constructor.call(this);
5728 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
5730 * Load data from the requested source (in this case an in-memory
5731 * data object passed to the constructor), read the data object into
5732 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
5733 * process that block using the passed callback.
5734 * @param {Object} params This parameter is not used by the MemoryProxy class.
5735 * @param {Roo.data.DataReader} reader The Reader object which converts the data
5736 * object into a block of Roo.data.Records.
5737 * @param {Function} callback The function into which to pass the block of Roo.data.records.
5738 * The function must be passed <ul>
5739 * <li>The Record block object</li>
5740 * <li>The "arg" argument from the load function</li>
5741 * <li>A boolean success indicator</li>
5743 * @param {Object} scope The scope in which to call the callback
5744 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
5746 load : function(params, reader, callback, scope, arg){
5747 params = params || {};
5750 result = reader.readRecords(this.data);
5752 this.fireEvent("loadexception", this, arg, null, e);
5753 callback.call(scope, null, arg, false);
5756 callback.call(scope, result, arg, true);
5760 update : function(params, records){
5765 * Ext JS Library 1.1.1
5766 * Copyright(c) 2006-2007, Ext JS, LLC.
5768 * Originally Released Under LGPL - original licence link has changed is not relivant.
5771 * <script type="text/javascript">
5774 * @class Roo.data.HttpProxy
5775 * @extends Roo.data.DataProxy
5776 * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
5777 * configured to reference a certain URL.<br><br>
5779 * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
5780 * from which the running page was served.<br><br>
5782 * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
5784 * Be aware that to enable the browser to parse an XML document, the server must set
5785 * the Content-Type header in the HTTP response to "text/xml".
5787 * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
5788 * an {@link Roo.data.Connection} object. If a Connection config is passed, the singleton {@link Roo.Ajax} object
5789 * will be used to make the request.
5791 Roo.data.HttpProxy = function(conn){
5792 Roo.data.HttpProxy.superclass.constructor.call(this);
5793 // is conn a conn config or a real conn?
5795 this.useAjax = !conn || !conn.events;
5799 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
5800 // thse are take from connection...
5803 * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
5806 * @cfg {Object} extraParams (Optional) An object containing properties which are used as
5807 * extra parameters to each request made by this object. (defaults to undefined)
5810 * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
5811 * to each request made by this object. (defaults to undefined)
5814 * @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)
5817 * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
5820 * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
5826 * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
5830 * Return the {@link Roo.data.Connection} object being used by this Proxy.
5831 * @return {Connection} The Connection object. This object may be used to subscribe to events on
5832 * a finer-grained basis than the DataProxy events.
5834 getConnection : function(){
5835 return this.useAjax ? Roo.Ajax : this.conn;
5839 * Load data from the configured {@link Roo.data.Connection}, read the data object into
5840 * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
5841 * process that block using the passed callback.
5842 * @param {Object} params An object containing properties which are to be used as HTTP parameters
5843 * for the request to the remote server.
5844 * @param {Roo.data.DataReader} reader The Reader object which converts the data
5845 * object into a block of Roo.data.Records.
5846 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
5847 * The function must be passed <ul>
5848 * <li>The Record block object</li>
5849 * <li>The "arg" argument from the load function</li>
5850 * <li>A boolean success indicator</li>
5852 * @param {Object} scope The scope in which to call the callback
5853 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
5855 load : function(params, reader, callback, scope, arg){
5856 if(this.fireEvent("beforeload", this, params) !== false){
5858 params : params || {},
5860 callback : callback,
5865 callback : this.loadResponse,
5869 Roo.applyIf(o, this.conn);
5870 if(this.activeRequest){
5871 Roo.Ajax.abort(this.activeRequest);
5873 this.activeRequest = Roo.Ajax.request(o);
5875 this.conn.request(o);
5878 callback.call(scope||this, null, arg, false);
5883 loadResponse : function(o, success, response){
5884 delete this.activeRequest;
5886 this.fireEvent("loadexception", this, o, response);
5887 o.request.callback.call(o.request.scope, null, o.request.arg, false);
5892 result = o.reader.read(response);
5894 this.fireEvent("loadexception", this, o, response, e);
5895 o.request.callback.call(o.request.scope, null, o.request.arg, false);
5899 this.fireEvent("load", this, o, o.request.arg);
5900 o.request.callback.call(o.request.scope, result, o.request.arg, true);
5904 update : function(dataSet){
5909 updateResponse : function(dataSet){
5914 * Ext JS Library 1.1.1
5915 * Copyright(c) 2006-2007, Ext JS, LLC.
5917 * Originally Released Under LGPL - original licence link has changed is not relivant.
5920 * <script type="text/javascript">
5924 * @class Roo.data.ScriptTagProxy
5925 * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
5926 * other than the originating domain of the running page.<br><br>
5928 * <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
5929 * of the running page, you must use this class, rather than DataProxy.</em><br><br>
5931 * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
5932 * source code that is used as the source inside a <script> tag.<br><br>
5934 * In order for the browser to process the returned data, the server must wrap the data object
5935 * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
5936 * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
5937 * depending on whether the callback name was passed:
5940 boolean scriptTag = false;
5941 String cb = request.getParameter("callback");
5944 response.setContentType("text/javascript");
5946 response.setContentType("application/x-json");
5948 Writer out = response.getWriter();
5950 out.write(cb + "(");
5952 out.print(dataBlock.toJsonString());
5959 * @param {Object} config A configuration object.
5961 Roo.data.ScriptTagProxy = function(config){
5962 Roo.data.ScriptTagProxy.superclass.constructor.call(this);
5963 Roo.apply(this, config);
5964 this.head = document.getElementsByTagName("head")[0];
5967 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
5969 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
5971 * @cfg {String} url The URL from which to request the data object.
5974 * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
5978 * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
5979 * the server the name of the callback function set up by the load call to process the returned data object.
5980 * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
5981 * javascript output which calls this named function passing the data object as its only parameter.
5983 callbackParam : "callback",
5985 * @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
5986 * name to the request.
5991 * Load data from the configured URL, read the data object into
5992 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
5993 * process that block using the passed callback.
5994 * @param {Object} params An object containing properties which are to be used as HTTP parameters
5995 * for the request to the remote server.
5996 * @param {Roo.data.DataReader} reader The Reader object which converts the data
5997 * object into a block of Roo.data.Records.
5998 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
5999 * The function must be passed <ul>
6000 * <li>The Record block object</li>
6001 * <li>The "arg" argument from the load function</li>
6002 * <li>A boolean success indicator</li>
6004 * @param {Object} scope The scope in which to call the callback
6005 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
6007 load : function(params, reader, callback, scope, arg){
6008 if(this.fireEvent("beforeload", this, params) !== false){
6010 var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
6013 url += (url.indexOf("?") != -1 ? "&" : "?") + p;
6015 url += "&_dc=" + (new Date().getTime());
6017 var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
6020 cb : "stcCallback"+transId,
6021 scriptId : "stcScript"+transId,
6025 callback : callback,
6031 window[trans.cb] = function(o){
6032 conn.handleResponse(o, trans);
6035 url += String.format("&{0}={1}", this.callbackParam, trans.cb);
6037 if(this.autoAbort !== false){
6041 trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
6043 var script = document.createElement("script");
6044 script.setAttribute("src", url);
6045 script.setAttribute("type", "text/javascript");
6046 script.setAttribute("id", trans.scriptId);
6047 this.head.appendChild(script);
6051 callback.call(scope||this, null, arg, false);
6056 isLoading : function(){
6057 return this.trans ? true : false;
6061 * Abort the current server request.
6064 if(this.isLoading()){
6065 this.destroyTrans(this.trans);
6070 destroyTrans : function(trans, isLoaded){
6071 this.head.removeChild(document.getElementById(trans.scriptId));
6072 clearTimeout(trans.timeoutId);
6074 window[trans.cb] = undefined;
6076 delete window[trans.cb];
6079 // if hasn't been loaded, wait for load to remove it to prevent script error
6080 window[trans.cb] = function(){
6081 window[trans.cb] = undefined;
6083 delete window[trans.cb];
6090 handleResponse : function(o, trans){
6092 this.destroyTrans(trans, true);
6095 result = trans.reader.readRecords(o);
6097 this.fireEvent("loadexception", this, o, trans.arg, e);
6098 trans.callback.call(trans.scope||window, null, trans.arg, false);
6101 this.fireEvent("load", this, o, trans.arg);
6102 trans.callback.call(trans.scope||window, result, trans.arg, true);
6106 handleFailure : function(trans){
6108 this.destroyTrans(trans, false);
6109 this.fireEvent("loadexception", this, null, trans.arg);
6110 trans.callback.call(trans.scope||window, null, trans.arg, false);
6114 * Ext JS Library 1.1.1
6115 * Copyright(c) 2006-2007, Ext JS, LLC.
6117 * Originally Released Under LGPL - original licence link has changed is not relivant.
6120 * <script type="text/javascript">
6124 * @class Roo.data.JsonReader
6125 * @extends Roo.data.DataReader
6126 * Data reader class to create an Array of Roo.data.Record objects from a JSON response
6127 * based on mappings in a provided Roo.data.Record constructor.
6129 * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
6130 * in the reply previously.
6135 var RecordDef = Roo.data.Record.create([
6136 {name: 'name', mapping: 'name'}, // "mapping" property not needed if it's the same as "name"
6137 {name: 'occupation'} // This field will use "occupation" as the mapping.
6139 var myReader = new Roo.data.JsonReader({
6140 totalProperty: "results", // The property which contains the total dataset size (optional)
6141 root: "rows", // The property which contains an Array of row objects
6142 id: "id" // The property within each row object that provides an ID for the record (optional)
6146 * This would consume a JSON file like this:
6148 { 'results': 2, 'rows': [
6149 { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
6150 { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
6153 * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
6154 * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
6155 * paged from the remote server.
6156 * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
6157 * @cfg {String} root name of the property which contains the Array of row objects.
6158 * @cfg {String} id Name of the property within a row object that contains a record identifier value.
6160 * Create a new JsonReader
6161 * @param {Object} meta Metadata configuration options
6162 * @param {Object} recordType Either an Array of field definition objects,
6163 * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
6165 Roo.data.JsonReader = function(meta, recordType){
6168 // set some defaults:
6170 totalProperty: 'total',
6171 successProperty : 'success',
6176 Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
6178 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
6181 * @prop {Boolean} metaFromRemote - if the meta data was loaded from the remote source.
6182 * Used by Store query builder to append _requestMeta to params.
6185 metaFromRemote : false,
6187 * This method is only used by a DataProxy which has retrieved data from a remote server.
6188 * @param {Object} response The XHR object which contains the JSON data in its responseText.
6189 * @return {Object} data A data block which is used by an Roo.data.Store object as
6190 * a cache of Roo.data.Records.
6192 read : function(response){
6193 var json = response.responseText;
6195 var o = /* eval:var:o */ eval("("+json+")");
6197 throw {message: "JsonReader.read: Json object not found"};
6203 this.metaFromRemote = true;
6204 this.meta = o.metaData;
6205 this.recordType = Roo.data.Record.create(o.metaData.fields);
6206 this.onMetaChange(this.meta, this.recordType, o);
6208 return this.readRecords(o);
6211 // private function a store will implement
6212 onMetaChange : function(meta, recordType, o){
6219 simpleAccess: function(obj, subsc) {
6226 getJsonAccessor: function(){
6228 return function(expr) {
6230 return(re.test(expr))
6231 ? new Function("obj", "return obj." + expr)
6241 * Create a data block containing Roo.data.Records from an XML document.
6242 * @param {Object} o An object which contains an Array of row objects in the property specified
6243 * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
6244 * which contains the total size of the dataset.
6245 * @return {Object} data A data block which is used by an Roo.data.Store object as
6246 * a cache of Roo.data.Records.
6248 readRecords : function(o){
6250 * After any data loads, the raw JSON data is available for further custom processing.
6254 var s = this.meta, Record = this.recordType,
6255 f = Record.prototype.fields, fi = f.items, fl = f.length;
6257 // Generate extraction functions for the totalProperty, the root, the id, and for each field
6259 if(s.totalProperty) {
6260 this.getTotal = this.getJsonAccessor(s.totalProperty);
6262 if(s.successProperty) {
6263 this.getSuccess = this.getJsonAccessor(s.successProperty);
6265 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
6267 var g = this.getJsonAccessor(s.id);
6268 this.getId = function(rec) {
6270 return (r === undefined || r === "") ? null : r;
6273 this.getId = function(){return null;};
6276 for(var jj = 0; jj < fl; jj++){
6278 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
6279 this.ef[jj] = this.getJsonAccessor(map);
6283 var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
6284 if(s.totalProperty){
6285 var vt = parseInt(this.getTotal(o), 10);
6290 if(s.successProperty){
6291 var vs = this.getSuccess(o);
6292 if(vs === false || vs === 'false'){
6297 for(var i = 0; i < c; i++){
6300 var id = this.getId(n);
6301 for(var j = 0; j < fl; j++){
6303 var v = this.ef[j](n);
6305 Roo.log('missing convert for ' + f.name);
6309 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
6311 var record = new Record(values, id);
6313 records[i] = record;
6319 totalRecords : totalRecords
6324 * Ext JS Library 1.1.1
6325 * Copyright(c) 2006-2007, Ext JS, LLC.
6327 * Originally Released Under LGPL - original licence link has changed is not relivant.
6330 * <script type="text/javascript">
6334 * @class Roo.data.XmlReader
6335 * @extends Roo.data.DataReader
6336 * Data reader class to create an Array of {@link Roo.data.Record} objects from an XML document
6337 * based on mappings in a provided Roo.data.Record constructor.<br><br>
6339 * <em>Note that in order for the browser to parse a returned XML document, the Content-Type
6340 * header in the HTTP response must be set to "text/xml".</em>
6344 var RecordDef = Roo.data.Record.create([
6345 {name: 'name', mapping: 'name'}, // "mapping" property not needed if it's the same as "name"
6346 {name: 'occupation'} // This field will use "occupation" as the mapping.
6348 var myReader = new Roo.data.XmlReader({
6349 totalRecords: "results", // The element which contains the total dataset size (optional)
6350 record: "row", // The repeated element which contains row information
6351 id: "id" // The element within the row that provides an ID for the record (optional)
6355 * This would consume an XML file like this:
6359 <results>2</results>
6362 <name>Bill</name>
6363 <occupation>Gardener</occupation>
6367 <name>Ben</name>
6368 <occupation>Horticulturalist</occupation>
6372 * @cfg {String} totalRecords The DomQuery path from which to retrieve the total number of records
6373 * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
6374 * paged from the remote server.
6375 * @cfg {String} record The DomQuery path to the repeated element which contains record information.
6376 * @cfg {String} success The DomQuery path to the success attribute used by forms.
6377 * @cfg {String} id The DomQuery path relative from the record element to the element that contains
6378 * a record identifier value.
6380 * Create a new XmlReader
6381 * @param {Object} meta Metadata configuration options
6382 * @param {Mixed} recordType The definition of the data record type to produce. This can be either a valid
6383 * Record subclass created with {@link Roo.data.Record#create}, or an array of objects with which to call
6384 * Roo.data.Record.create. See the {@link Roo.data.Record} class for more details.
6386 Roo.data.XmlReader = function(meta, recordType){
6388 Roo.data.XmlReader.superclass.constructor.call(this, meta, recordType||meta.fields);
6390 Roo.extend(Roo.data.XmlReader, Roo.data.DataReader, {
6392 * This method is only used by a DataProxy which has retrieved data from a remote server.
6393 * @param {Object} response The XHR object which contains the parsed XML document. The response is expected
6394 * to contain a method called 'responseXML' that returns an XML document object.
6395 * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
6396 * a cache of Roo.data.Records.
6398 read : function(response){
6399 var doc = response.responseXML;
6401 throw {message: "XmlReader.read: XML Document not available"};
6403 return this.readRecords(doc);
6407 * Create a data block containing Roo.data.Records from an XML document.
6408 * @param {Object} doc A parsed XML document.
6409 * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
6410 * a cache of Roo.data.Records.
6412 readRecords : function(doc){
6414 * After any data loads/reads, the raw XML Document is available for further custom processing.
6418 var root = doc.documentElement || doc;
6419 var q = Roo.DomQuery;
6420 var recordType = this.recordType, fields = recordType.prototype.fields;
6421 var sid = this.meta.id;
6422 var totalRecords = 0, success = true;
6423 if(this.meta.totalRecords){
6424 totalRecords = q.selectNumber(this.meta.totalRecords, root, 0);
6427 if(this.meta.success){
6428 var sv = q.selectValue(this.meta.success, root, true);
6429 success = sv !== false && sv !== 'false';
6432 var ns = q.select(this.meta.record, root);
6433 for(var i = 0, len = ns.length; i < len; i++) {
6436 var id = sid ? q.selectValue(sid, n) : undefined;
6437 for(var j = 0, jlen = fields.length; j < jlen; j++){
6438 var f = fields.items[j];
6439 var v = q.selectValue(f.mapping || f.name, n, f.defaultValue);
6443 var record = new recordType(values, id);
6445 records[records.length] = record;
6451 totalRecords : totalRecords || records.length
6456 * Ext JS Library 1.1.1
6457 * Copyright(c) 2006-2007, Ext JS, LLC.
6459 * Originally Released Under LGPL - original licence link has changed is not relivant.
6462 * <script type="text/javascript">
6466 * @class Roo.data.ArrayReader
6467 * @extends Roo.data.DataReader
6468 * Data reader class to create an Array of Roo.data.Record objects from an Array.
6469 * Each element of that Array represents a row of data fields. The
6470 * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
6471 * of the field definition if it exists, or the field's ordinal position in the definition.<br>
6475 var RecordDef = Roo.data.Record.create([
6476 {name: 'name', mapping: 1}, // "mapping" only needed if an "id" field is present which
6477 {name: 'occupation', mapping: 2} // precludes using the ordinal position as the index.
6479 var myReader = new Roo.data.ArrayReader({
6480 id: 0 // The subscript within row Array that provides an ID for the Record (optional)
6484 * This would consume an Array like this:
6486 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
6488 * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
6490 * Create a new JsonReader
6491 * @param {Object} meta Metadata configuration options.
6492 * @param {Object} recordType Either an Array of field definition objects
6493 * as specified to {@link Roo.data.Record#create},
6494 * or an {@link Roo.data.Record} object
6495 * created using {@link Roo.data.Record#create}.
6497 Roo.data.ArrayReader = function(meta, recordType){
6498 Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
6501 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
6503 * Create a data block containing Roo.data.Records from an XML document.
6504 * @param {Object} o An Array of row objects which represents the dataset.
6505 * @return {Object} data A data block which is used by an Roo.data.Store object as
6506 * a cache of Roo.data.Records.
6508 readRecords : function(o){
6509 var sid = this.meta ? this.meta.id : null;
6510 var recordType = this.recordType, fields = recordType.prototype.fields;
6513 for(var i = 0; i < root.length; i++){
6516 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
6517 for(var j = 0, jlen = fields.length; j < jlen; j++){
6518 var f = fields.items[j];
6519 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
6520 var v = n[k] !== undefined ? n[k] : f.defaultValue;
6524 var record = new recordType(values, id);
6526 records[records.length] = record;
6530 totalRecords : records.length
6535 * Ext JS Library 1.1.1
6536 * Copyright(c) 2006-2007, Ext JS, LLC.
6538 * Originally Released Under LGPL - original licence link has changed is not relivant.
6541 * <script type="text/javascript">
6546 * @class Roo.data.Tree
6547 * @extends Roo.util.Observable
6548 * Represents a tree data structure and bubbles all the events for its nodes. The nodes
6549 * in the tree have most standard DOM functionality.
6551 * @param {Node} root (optional) The root node
6553 Roo.data.Tree = function(root){
6556 * The root node for this tree
6561 this.setRootNode(root);
6566 * Fires when a new child node is appended to a node in this tree.
6567 * @param {Tree} tree The owner tree
6568 * @param {Node} parent The parent node
6569 * @param {Node} node The newly appended node
6570 * @param {Number} index The index of the newly appended node
6575 * Fires when a child node is removed from a node in this tree.
6576 * @param {Tree} tree The owner tree
6577 * @param {Node} parent The parent node
6578 * @param {Node} node The child node removed
6583 * Fires when a node is moved to a new location in the tree
6584 * @param {Tree} tree The owner tree
6585 * @param {Node} node The node moved
6586 * @param {Node} oldParent The old parent of this node
6587 * @param {Node} newParent The new parent of this node
6588 * @param {Number} index The index it was moved to
6593 * Fires when a new child node is inserted in a node in this tree.
6594 * @param {Tree} tree The owner tree
6595 * @param {Node} parent The parent node
6596 * @param {Node} node The child node inserted
6597 * @param {Node} refNode The child node the node was inserted before
6601 * @event beforeappend
6602 * Fires before a new child is appended to a node in this tree, return false to cancel the append.
6603 * @param {Tree} tree The owner tree
6604 * @param {Node} parent The parent node
6605 * @param {Node} node The child node to be appended
6607 "beforeappend" : true,
6609 * @event beforeremove
6610 * Fires before a child is removed from a node in this tree, return false to cancel the remove.
6611 * @param {Tree} tree The owner tree
6612 * @param {Node} parent The parent node
6613 * @param {Node} node The child node to be removed
6615 "beforeremove" : true,
6618 * Fires before a node is moved to a new location in the tree. Return false to cancel the move.
6619 * @param {Tree} tree The owner tree
6620 * @param {Node} node The node being moved
6621 * @param {Node} oldParent The parent of the node
6622 * @param {Node} newParent The new parent the node is moving to
6623 * @param {Number} index The index it is being moved to
6625 "beforemove" : true,
6627 * @event beforeinsert
6628 * Fires before a new child is inserted in a node in this tree, return false to cancel the insert.
6629 * @param {Tree} tree The owner tree
6630 * @param {Node} parent The parent node
6631 * @param {Node} node The child node to be inserted
6632 * @param {Node} refNode The child node the node is being inserted before
6634 "beforeinsert" : true
6637 Roo.data.Tree.superclass.constructor.call(this);
6640 Roo.extend(Roo.data.Tree, Roo.util.Observable, {
6643 proxyNodeEvent : function(){
6644 return this.fireEvent.apply(this, arguments);
6648 * Returns the root node for this tree.
6651 getRootNode : function(){
6656 * Sets the root node for this tree.
6657 * @param {Node} node
6660 setRootNode : function(node){
6662 node.ownerTree = this;
6664 this.registerNode(node);
6669 * Gets a node in this tree by its id.
6670 * @param {String} id
6673 getNodeById : function(id){
6674 return this.nodeHash[id];
6677 registerNode : function(node){
6678 this.nodeHash[node.id] = node;
6681 unregisterNode : function(node){
6682 delete this.nodeHash[node.id];
6685 toString : function(){
6686 return "[Tree"+(this.id?" "+this.id:"")+"]";
6691 * @class Roo.data.Node
6692 * @extends Roo.util.Observable
6693 * @cfg {Boolean} leaf true if this node is a leaf and does not have children
6694 * @cfg {String} id The id for this node. If one is not specified, one is generated.
6696 * @param {Object} attributes The attributes/config for the node
6698 Roo.data.Node = function(attributes){
6700 * The attributes supplied for the node. You can use this property to access any custom attributes you supplied.
6703 this.attributes = attributes || {};
6704 this.leaf = this.attributes.leaf;
6706 * The node id. @type String
6708 this.id = this.attributes.id;
6710 this.id = Roo.id(null, "ynode-");
6711 this.attributes.id = this.id;
6716 * All child nodes of this node. @type Array
6718 this.childNodes = [];
6719 if(!this.childNodes.indexOf){ // indexOf is a must
6720 this.childNodes.indexOf = function(o){
6721 for(var i = 0, len = this.length; i < len; i++){
6730 * The parent node for this node. @type Node
6732 this.parentNode = null;
6734 * The first direct child node of this node, or null if this node has no child nodes. @type Node
6736 this.firstChild = null;
6738 * The last direct child node of this node, or null if this node has no child nodes. @type Node
6740 this.lastChild = null;
6742 * The node immediately preceding this node in the tree, or null if there is no sibling node. @type Node
6744 this.previousSibling = null;
6746 * The node immediately following this node in the tree, or null if there is no sibling node. @type Node
6748 this.nextSibling = null;
6753 * Fires when a new child node is appended
6754 * @param {Tree} tree The owner tree
6755 * @param {Node} this This node
6756 * @param {Node} node The newly appended node
6757 * @param {Number} index The index of the newly appended node
6762 * Fires when a child node is removed
6763 * @param {Tree} tree The owner tree
6764 * @param {Node} this This node
6765 * @param {Node} node The removed node
6770 * Fires when this node is moved to a new location in the tree
6771 * @param {Tree} tree The owner tree
6772 * @param {Node} this This node
6773 * @param {Node} oldParent The old parent of this node
6774 * @param {Node} newParent The new parent of this node
6775 * @param {Number} index The index it was moved to
6780 * Fires when a new child node is inserted.
6781 * @param {Tree} tree The owner tree
6782 * @param {Node} this This node
6783 * @param {Node} node The child node inserted
6784 * @param {Node} refNode The child node the node was inserted before
6788 * @event beforeappend
6789 * Fires before a new child is appended, return false to cancel the append.
6790 * @param {Tree} tree The owner tree
6791 * @param {Node} this This node
6792 * @param {Node} node The child node to be appended
6794 "beforeappend" : true,
6796 * @event beforeremove
6797 * Fires before a child is removed, return false to cancel the remove.
6798 * @param {Tree} tree The owner tree
6799 * @param {Node} this This node
6800 * @param {Node} node The child node to be removed
6802 "beforeremove" : true,
6805 * Fires before this node is moved to a new location in the tree. Return false to cancel the move.
6806 * @param {Tree} tree The owner tree
6807 * @param {Node} this This node
6808 * @param {Node} oldParent The parent of this node
6809 * @param {Node} newParent The new parent this node is moving to
6810 * @param {Number} index The index it is being moved to
6812 "beforemove" : true,
6814 * @event beforeinsert
6815 * Fires before a new child is inserted, return false to cancel the insert.
6816 * @param {Tree} tree The owner tree
6817 * @param {Node} this This node
6818 * @param {Node} node The child node to be inserted
6819 * @param {Node} refNode The child node the node is being inserted before
6821 "beforeinsert" : true
6823 this.listeners = this.attributes.listeners;
6824 Roo.data.Node.superclass.constructor.call(this);
6827 Roo.extend(Roo.data.Node, Roo.util.Observable, {
6828 fireEvent : function(evtName){
6829 // first do standard event for this node
6830 if(Roo.data.Node.superclass.fireEvent.apply(this, arguments) === false){
6833 // then bubble it up to the tree if the event wasn't cancelled
6834 var ot = this.getOwnerTree();
6836 if(ot.proxyNodeEvent.apply(ot, arguments) === false){
6844 * Returns true if this node is a leaf
6847 isLeaf : function(){
6848 return this.leaf === true;
6852 setFirstChild : function(node){
6853 this.firstChild = node;
6857 setLastChild : function(node){
6858 this.lastChild = node;
6863 * Returns true if this node is the last child of its parent
6866 isLast : function(){
6867 return (!this.parentNode ? true : this.parentNode.lastChild == this);
6871 * Returns true if this node is the first child of its parent
6874 isFirst : function(){
6875 return (!this.parentNode ? true : this.parentNode.firstChild == this);
6878 hasChildNodes : function(){
6879 return !this.isLeaf() && this.childNodes.length > 0;
6883 * Insert node(s) as the last child node of this node.
6884 * @param {Node/Array} node The node or Array of nodes to append
6885 * @return {Node} The appended node if single append, or null if an array was passed
6887 appendChild : function(node){
6889 if(node instanceof Array){
6891 }else if(arguments.length > 1){
6894 // if passed an array or multiple args do them one by one
6896 for(var i = 0, len = multi.length; i < len; i++) {
6897 this.appendChild(multi[i]);
6900 if(this.fireEvent("beforeappend", this.ownerTree, this, node) === false){
6903 var index = this.childNodes.length;
6904 var oldParent = node.parentNode;
6905 // it's a move, make sure we move it cleanly
6907 if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index) === false){
6910 oldParent.removeChild(node);
6912 index = this.childNodes.length;
6914 this.setFirstChild(node);
6916 this.childNodes.push(node);
6917 node.parentNode = this;
6918 var ps = this.childNodes[index-1];
6920 node.previousSibling = ps;
6921 ps.nextSibling = node;
6923 node.previousSibling = null;
6925 node.nextSibling = null;
6926 this.setLastChild(node);
6927 node.setOwnerTree(this.getOwnerTree());
6928 this.fireEvent("append", this.ownerTree, this, node, index);
6930 node.fireEvent("move", this.ownerTree, node, oldParent, this, index);
6937 * Removes a child node from this node.
6938 * @param {Node} node The node to remove
6939 * @return {Node} The removed node
6941 removeChild : function(node){
6942 var index = this.childNodes.indexOf(node);
6946 if(this.fireEvent("beforeremove", this.ownerTree, this, node) === false){
6950 // remove it from childNodes collection
6951 this.childNodes.splice(index, 1);
6954 if(node.previousSibling){
6955 node.previousSibling.nextSibling = node.nextSibling;
6957 if(node.nextSibling){
6958 node.nextSibling.previousSibling = node.previousSibling;
6961 // update child refs
6962 if(this.firstChild == node){
6963 this.setFirstChild(node.nextSibling);
6965 if(this.lastChild == node){
6966 this.setLastChild(node.previousSibling);
6969 node.setOwnerTree(null);
6970 // clear any references from the node
6971 node.parentNode = null;
6972 node.previousSibling = null;
6973 node.nextSibling = null;
6974 this.fireEvent("remove", this.ownerTree, this, node);
6979 * Inserts the first node before the second node in this nodes childNodes collection.
6980 * @param {Node} node The node to insert
6981 * @param {Node} refNode The node to insert before (if null the node is appended)
6982 * @return {Node} The inserted node
6984 insertBefore : function(node, refNode){
6985 if(!refNode){ // like standard Dom, refNode can be null for append
6986 return this.appendChild(node);
6989 if(node == refNode){
6993 if(this.fireEvent("beforeinsert", this.ownerTree, this, node, refNode) === false){
6996 var index = this.childNodes.indexOf(refNode);
6997 var oldParent = node.parentNode;
6998 var refIndex = index;
7000 // when moving internally, indexes will change after remove
7001 if(oldParent == this && this.childNodes.indexOf(node) < index){
7005 // it's a move, make sure we move it cleanly
7007 if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index, refNode) === false){
7010 oldParent.removeChild(node);
7013 this.setFirstChild(node);
7015 this.childNodes.splice(refIndex, 0, node);
7016 node.parentNode = this;
7017 var ps = this.childNodes[refIndex-1];
7019 node.previousSibling = ps;
7020 ps.nextSibling = node;
7022 node.previousSibling = null;
7024 node.nextSibling = refNode;
7025 refNode.previousSibling = node;
7026 node.setOwnerTree(this.getOwnerTree());
7027 this.fireEvent("insert", this.ownerTree, this, node, refNode);
7029 node.fireEvent("move", this.ownerTree, node, oldParent, this, refIndex, refNode);
7035 * Returns the child node at the specified index.
7036 * @param {Number} index
7039 item : function(index){
7040 return this.childNodes[index];
7044 * Replaces one child node in this node with another.
7045 * @param {Node} newChild The replacement node
7046 * @param {Node} oldChild The node to replace
7047 * @return {Node} The replaced node
7049 replaceChild : function(newChild, oldChild){
7050 this.insertBefore(newChild, oldChild);
7051 this.removeChild(oldChild);
7056 * Returns the index of a child node
7057 * @param {Node} node
7058 * @return {Number} The index of the node or -1 if it was not found
7060 indexOf : function(child){
7061 return this.childNodes.indexOf(child);
7065 * Returns the tree this node is in.
7068 getOwnerTree : function(){
7069 // if it doesn't have one, look for one
7070 if(!this.ownerTree){
7074 this.ownerTree = p.ownerTree;
7080 return this.ownerTree;
7084 * Returns depth of this node (the root node has a depth of 0)
7087 getDepth : function(){
7090 while(p.parentNode){
7098 setOwnerTree : function(tree){
7099 // if it's move, we need to update everyone
7100 if(tree != this.ownerTree){
7102 this.ownerTree.unregisterNode(this);
7104 this.ownerTree = tree;
7105 var cs = this.childNodes;
7106 for(var i = 0, len = cs.length; i < len; i++) {
7107 cs[i].setOwnerTree(tree);
7110 tree.registerNode(this);
7116 * Returns the path for this node. The path can be used to expand or select this node programmatically.
7117 * @param {String} attr (optional) The attr to use for the path (defaults to the node's id)
7118 * @return {String} The path
7120 getPath : function(attr){
7121 attr = attr || "id";
7122 var p = this.parentNode;
7123 var b = [this.attributes[attr]];
7125 b.unshift(p.attributes[attr]);
7128 var sep = this.getOwnerTree().pathSeparator;
7129 return sep + b.join(sep);
7133 * Bubbles up the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
7134 * function call will be the scope provided or the current node. The arguments to the function
7135 * will be the args provided or the current node. If the function returns false at any point,
7136 * the bubble is stopped.
7137 * @param {Function} fn The function to call
7138 * @param {Object} scope (optional) The scope of the function (defaults to current node)
7139 * @param {Array} args (optional) The args to call the function with (default to passing the current node)
7141 bubble : function(fn, scope, args){
7144 if(fn.call(scope || p, args || p) === false){
7152 * Cascades down the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
7153 * function call will be the scope provided or the current node. The arguments to the function
7154 * will be the args provided or the current node. If the function returns false at any point,
7155 * the cascade is stopped on that branch.
7156 * @param {Function} fn The function to call
7157 * @param {Object} scope (optional) The scope of the function (defaults to current node)
7158 * @param {Array} args (optional) The args to call the function with (default to passing the current node)
7160 cascade : function(fn, scope, args){
7161 if(fn.call(scope || this, args || this) !== false){
7162 var cs = this.childNodes;
7163 for(var i = 0, len = cs.length; i < len; i++) {
7164 cs[i].cascade(fn, scope, args);
7170 * Interates the child nodes of this node, calling the specified function with each node. The scope (<i>this</i>) of
7171 * function call will be the scope provided or the current node. The arguments to the function
7172 * will be the args provided or the current node. If the function returns false at any point,
7173 * the iteration stops.
7174 * @param {Function} fn The function to call
7175 * @param {Object} scope (optional) The scope of the function (defaults to current node)
7176 * @param {Array} args (optional) The args to call the function with (default to passing the current node)
7178 eachChild : function(fn, scope, args){
7179 var cs = this.childNodes;
7180 for(var i = 0, len = cs.length; i < len; i++) {
7181 if(fn.call(scope || this, args || cs[i]) === false){
7188 * Finds the first child that has the attribute with the specified value.
7189 * @param {String} attribute The attribute name
7190 * @param {Mixed} value The value to search for
7191 * @return {Node} The found child or null if none was found
7193 findChild : function(attribute, value){
7194 var cs = this.childNodes;
7195 for(var i = 0, len = cs.length; i < len; i++) {
7196 if(cs[i].attributes[attribute] == value){
7204 * Finds the first child by a custom function. The child matches if the function passed
7206 * @param {Function} fn
7207 * @param {Object} scope (optional)
7208 * @return {Node} The found child or null if none was found
7210 findChildBy : function(fn, scope){
7211 var cs = this.childNodes;
7212 for(var i = 0, len = cs.length; i < len; i++) {
7213 if(fn.call(scope||cs[i], cs[i]) === true){
7221 * Sorts this nodes children using the supplied sort function
7222 * @param {Function} fn
7223 * @param {Object} scope (optional)
7225 sort : function(fn, scope){
7226 var cs = this.childNodes;
7227 var len = cs.length;
7229 var sortFn = scope ? function(){fn.apply(scope, arguments);} : fn;
7231 for(var i = 0; i < len; i++){
7233 n.previousSibling = cs[i-1];
7234 n.nextSibling = cs[i+1];
7236 this.setFirstChild(n);
7239 this.setLastChild(n);
7246 * Returns true if this node is an ancestor (at any point) of the passed node.
7247 * @param {Node} node
7250 contains : function(node){
7251 return node.isAncestor(this);
7255 * Returns true if the passed node is an ancestor (at any point) of this node.
7256 * @param {Node} node
7259 isAncestor : function(node){
7260 var p = this.parentNode;
7270 toString : function(){
7271 return "[Node"+(this.id?" "+this.id:"")+"]";
7275 * Ext JS Library 1.1.1
7276 * Copyright(c) 2006-2007, Ext JS, LLC.
7278 * Originally Released Under LGPL - original licence link has changed is not relivant.
7281 * <script type="text/javascript">
7286 * @class Roo.ComponentMgr
7287 * Provides a common registry of all components on a page so that they can be easily accessed by component id (see {@link Roo.getCmp}).
7290 Roo.ComponentMgr = function(){
7291 var all = new Roo.util.MixedCollection();
7295 * Registers a component.
7296 * @param {Roo.Component} c The component
7298 register : function(c){
7303 * Unregisters a component.
7304 * @param {Roo.Component} c The component
7306 unregister : function(c){
7311 * Returns a component by id
7312 * @param {String} id The component id
7319 * Registers a function that will be called when a specified component is added to ComponentMgr
7320 * @param {String} id The component id
7321 * @param {Funtction} fn The callback function
7322 * @param {Object} scope The scope of the callback
7324 onAvailable : function(id, fn, scope){
7325 all.on("add", function(index, o){
7327 fn.call(scope || o, o);
7328 all.un("add", fn, scope);
7335 * Ext JS Library 1.1.1
7336 * Copyright(c) 2006-2007, Ext JS, LLC.
7338 * Originally Released Under LGPL - original licence link has changed is not relivant.
7341 * <script type="text/javascript">
7345 * @class Roo.Component
7346 * @extends Roo.util.Observable
7347 * Base class for all major Roo components. All subclasses of Component can automatically participate in the standard
7348 * Roo component lifecycle of creation, rendering and destruction. They also have automatic support for basic hide/show
7349 * and enable/disable behavior. Component allows any subclass to be lazy-rendered into any {@link Roo.Container} and
7350 * to be automatically registered with the {@link Roo.ComponentMgr} so that it can be referenced at any time via {@link Roo.getCmp}.
7351 * All visual components (widgets) that require rendering into a layout should subclass Component.
7353 * @param {Roo.Element/String/Object} config The configuration options. If an element is passed, it is set as the internal
7354 * 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
7355 * and is used as the component id. Otherwise, it is assumed to be a standard config object and is applied to the component.
7357 Roo.Component = function(config){
7358 config = config || {};
7359 if(config.tagName || config.dom || typeof config == "string"){ // element object
7360 config = {el: config, id: config.id || config};
7362 this.initialConfig = config;
7364 Roo.apply(this, config);
7368 * Fires after the component is disabled.
7369 * @param {Roo.Component} this
7374 * Fires after the component is enabled.
7375 * @param {Roo.Component} this
7380 * Fires before the component is shown. Return false to stop the show.
7381 * @param {Roo.Component} this
7386 * Fires after the component is shown.
7387 * @param {Roo.Component} this
7392 * Fires before the component is hidden. Return false to stop the hide.
7393 * @param {Roo.Component} this
7398 * Fires after the component is hidden.
7399 * @param {Roo.Component} this
7403 * @event beforerender
7404 * Fires before the component is rendered. Return false to stop the render.
7405 * @param {Roo.Component} this
7407 beforerender : true,
7410 * Fires after the component is rendered.
7411 * @param {Roo.Component} this
7415 * @event beforedestroy
7416 * Fires before the component is destroyed. Return false to stop the destroy.
7417 * @param {Roo.Component} this
7419 beforedestroy : true,
7422 * Fires after the component is destroyed.
7423 * @param {Roo.Component} this
7428 this.id = "ext-comp-" + (++Roo.Component.AUTO_ID);
7430 Roo.ComponentMgr.register(this);
7431 Roo.Component.superclass.constructor.call(this);
7432 this.initComponent();
7433 if(this.renderTo){ // not supported by all components yet. use at your own risk!
7434 this.render(this.renderTo);
7435 delete this.renderTo;
7440 Roo.Component.AUTO_ID = 1000;
7442 Roo.extend(Roo.Component, Roo.util.Observable, {
7444 * @scope Roo.Component.prototype
7446 * true if this component is hidden. Read-only.
7451 * true if this component is disabled. Read-only.
7456 * true if this component has been rendered. Read-only.
7460 /** @cfg {String} disableClass
7461 * CSS class added to the component when it is disabled (defaults to "x-item-disabled").
7463 disabledClass : "x-item-disabled",
7464 /** @cfg {Boolean} allowDomMove
7465 * Whether the component can move the Dom node when rendering (defaults to true).
7467 allowDomMove : true,
7468 /** @cfg {String} hideMode
7469 * How this component should hidden. Supported values are
7470 * "visibility" (css visibility), "offsets" (negative offset position) and
7471 * "display" (css display) - defaults to "display".
7473 hideMode: 'display',
7476 ctype : "Roo.Component",
7479 * @cfg {String} actionMode
7480 * which property holds the element that used for hide() / show() / disable() / enable()
7486 getActionEl : function(){
7487 return this[this.actionMode];
7490 initComponent : Roo.emptyFn,
7492 * If this is a lazy rendering component, render it to its container element.
7493 * @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.
7495 render : function(container, position){
7496 if(!this.rendered && this.fireEvent("beforerender", this) !== false){
7497 if(!container && this.el){
7498 this.el = Roo.get(this.el);
7499 container = this.el.dom.parentNode;
7500 this.allowDomMove = false;
7502 this.container = Roo.get(container);
7503 this.rendered = true;
7504 if(position !== undefined){
7505 if(typeof position == 'number'){
7506 position = this.container.dom.childNodes[position];
7508 position = Roo.getDom(position);
7511 this.onRender(this.container, position || null);
7513 this.el.addClass(this.cls);
7517 this.el.applyStyles(this.style);
7520 this.fireEvent("render", this);
7521 this.afterRender(this.container);
7533 // default function is not really useful
7534 onRender : function(ct, position){
7536 this.el = Roo.get(this.el);
7537 if(this.allowDomMove !== false){
7538 ct.dom.insertBefore(this.el.dom, position);
7544 getAutoCreate : function(){
7545 var cfg = typeof this.autoCreate == "object" ?
7546 this.autoCreate : Roo.apply({}, this.defaultAutoCreate);
7547 if(this.id && !cfg.id){
7554 afterRender : Roo.emptyFn,
7557 * Destroys this component by purging any event listeners, removing the component's element from the DOM,
7558 * removing the component from its {@link Roo.Container} (if applicable) and unregistering it from {@link Roo.ComponentMgr}.
7560 destroy : function(){
7561 if(this.fireEvent("beforedestroy", this) !== false){
7562 this.purgeListeners();
7563 this.beforeDestroy();
7565 this.el.removeAllListeners();
7567 if(this.actionMode == "container"){
7568 this.container.remove();
7572 Roo.ComponentMgr.unregister(this);
7573 this.fireEvent("destroy", this);
7578 beforeDestroy : function(){
7583 onDestroy : function(){
7588 * Returns the underlying {@link Roo.Element}.
7589 * @return {Roo.Element} The element
7596 * Returns the id of this component.
7604 * Try to focus this component.
7605 * @param {Boolean} selectText True to also select the text in this component (if applicable)
7606 * @return {Roo.Component} this
7608 focus : function(selectText){
7611 if(selectText === true){
7612 this.el.dom.select();
7627 * Disable this component.
7628 * @return {Roo.Component} this
7630 disable : function(){
7634 this.disabled = true;
7635 this.fireEvent("disable", this);
7640 onDisable : function(){
7641 this.getActionEl().addClass(this.disabledClass);
7642 this.el.dom.disabled = true;
7646 * Enable this component.
7647 * @return {Roo.Component} this
7649 enable : function(){
7653 this.disabled = false;
7654 this.fireEvent("enable", this);
7659 onEnable : function(){
7660 this.getActionEl().removeClass(this.disabledClass);
7661 this.el.dom.disabled = false;
7665 * Convenience function for setting disabled/enabled by boolean.
7666 * @param {Boolean} disabled
7668 setDisabled : function(disabled){
7669 this[disabled ? "disable" : "enable"]();
7673 * Show this component.
7674 * @return {Roo.Component} this
7677 if(this.fireEvent("beforeshow", this) !== false){
7678 this.hidden = false;
7682 this.fireEvent("show", this);
7688 onShow : function(){
7689 var ae = this.getActionEl();
7690 if(this.hideMode == 'visibility'){
7691 ae.dom.style.visibility = "visible";
7692 }else if(this.hideMode == 'offsets'){
7693 ae.removeClass('x-hidden');
7695 ae.dom.style.display = "";
7700 * Hide this component.
7701 * @return {Roo.Component} this
7704 if(this.fireEvent("beforehide", this) !== false){
7709 this.fireEvent("hide", this);
7715 onHide : function(){
7716 var ae = this.getActionEl();
7717 if(this.hideMode == 'visibility'){
7718 ae.dom.style.visibility = "hidden";
7719 }else if(this.hideMode == 'offsets'){
7720 ae.addClass('x-hidden');
7722 ae.dom.style.display = "none";
7727 * Convenience function to hide or show this component by boolean.
7728 * @param {Boolean} visible True to show, false to hide
7729 * @return {Roo.Component} this
7731 setVisible: function(visible){
7741 * Returns true if this component is visible.
7743 isVisible : function(){
7744 return this.getActionEl().isVisible();
7747 cloneConfig : function(overrides){
7748 overrides = overrides || {};
7749 var id = overrides.id || Roo.id();
7750 var cfg = Roo.applyIf(overrides, this.initialConfig);
7751 cfg.id = id; // prevent dup id
7752 return new this.constructor(cfg);
7756 * Ext JS Library 1.1.1
7757 * Copyright(c) 2006-2007, Ext JS, LLC.
7759 * Originally Released Under LGPL - original licence link has changed is not relivant.
7762 * <script type="text/javascript">
7767 * @extends Roo.Element
7768 * An extended {@link Roo.Element} object that supports a shadow and shim, constrain to viewport and
7769 * automatic maintaining of shadow/shim positions.
7770 * @cfg {Boolean} shim False to disable the iframe shim in browsers which need one (defaults to true)
7771 * @cfg {String/Boolean} shadow True to create a shadow element with default class "x-layer-shadow", or
7772 * you can pass a string with a CSS class name. False turns off the shadow.
7773 * @cfg {Object} dh DomHelper object config to create element with (defaults to {tag: "div", cls: "x-layer"}).
7774 * @cfg {Boolean} constrain False to disable constrain to viewport (defaults to true)
7775 * @cfg {String} cls CSS class to add to the element
7776 * @cfg {Number} zindex Starting z-index (defaults to 11000)
7777 * @cfg {Number} shadowOffset Number of pixels to offset the shadow (defaults to 3)
7779 * @param {Object} config An object with config options.
7780 * @param {String/HTMLElement} existingEl (optional) Uses an existing DOM element. If the element is not found it creates it.
7783 Roo.Layer = function(config, existingEl){
7784 config = config || {};
7785 var dh = Roo.DomHelper;
7786 var cp = config.parentEl, pel = cp ? Roo.getDom(cp) : document.body;
7788 this.dom = Roo.getDom(existingEl);
7791 var o = config.dh || {tag: "div", cls: "x-layer"};
7792 this.dom = dh.append(pel, o);
7795 this.addClass(config.cls);
7797 this.constrain = config.constrain !== false;
7798 this.visibilityMode = Roo.Element.VISIBILITY;
7800 this.id = this.dom.id = config.id;
7802 this.id = Roo.id(this.dom);
7804 this.zindex = config.zindex || this.getZIndex();
7805 this.position("absolute", this.zindex);
7807 this.shadowOffset = config.shadowOffset || 4;
7808 this.shadow = new Roo.Shadow({
7809 offset : this.shadowOffset,
7810 mode : config.shadow
7813 this.shadowOffset = 0;
7815 this.useShim = config.shim !== false && Roo.useShims;
7816 this.useDisplay = config.useDisplay;
7820 var supr = Roo.Element.prototype;
7822 // shims are shared among layer to keep from having 100 iframes
7825 Roo.extend(Roo.Layer, Roo.Element, {
7827 getZIndex : function(){
7828 return this.zindex || parseInt(this.getStyle("z-index"), 10) || 11000;
7831 getShim : function(){
7838 var shim = shims.shift();
7840 shim = this.createShim();
7841 shim.enableDisplayMode('block');
7842 shim.dom.style.display = 'none';
7843 shim.dom.style.visibility = 'visible';
7845 var pn = this.dom.parentNode;
7846 if(shim.dom.parentNode != pn){
7847 pn.insertBefore(shim.dom, this.dom);
7849 shim.setStyle('z-index', this.getZIndex()-2);
7854 hideShim : function(){
7856 this.shim.setDisplayed(false);
7857 shims.push(this.shim);
7862 disableShadow : function(){
7864 this.shadowDisabled = true;
7866 this.lastShadowOffset = this.shadowOffset;
7867 this.shadowOffset = 0;
7871 enableShadow : function(show){
7873 this.shadowDisabled = false;
7874 this.shadowOffset = this.lastShadowOffset;
7875 delete this.lastShadowOffset;
7883 // this code can execute repeatedly in milliseconds (i.e. during a drag) so
7884 // code size was sacrificed for effeciency (e.g. no getBox/setBox, no XY calls)
7885 sync : function(doShow){
7886 var sw = this.shadow;
7887 if(!this.updating && this.isVisible() && (sw || this.useShim)){
7888 var sh = this.getShim();
7890 var w = this.getWidth(),
7891 h = this.getHeight();
7893 var l = this.getLeft(true),
7894 t = this.getTop(true);
7896 if(sw && !this.shadowDisabled){
7897 if(doShow && !sw.isVisible()){
7900 sw.realign(l, t, w, h);
7906 // fit the shim behind the shadow, so it is shimmed too
7907 var a = sw.adjusts, s = sh.dom.style;
7908 s.left = (Math.min(l, l+a.l))+"px";
7909 s.top = (Math.min(t, t+a.t))+"px";
7910 s.width = (w+a.w)+"px";
7911 s.height = (h+a.h)+"px";
7918 sh.setLeftTop(l, t);
7925 destroy : function(){
7930 this.removeAllListeners();
7931 var pn = this.dom.parentNode;
7933 pn.removeChild(this.dom);
7935 Roo.Element.uncache(this.id);
7938 remove : function(){
7943 beginUpdate : function(){
7944 this.updating = true;
7948 endUpdate : function(){
7949 this.updating = false;
7954 hideUnders : function(negOffset){
7962 constrainXY : function(){
7964 var vw = Roo.lib.Dom.getViewWidth(),
7965 vh = Roo.lib.Dom.getViewHeight();
7966 var s = Roo.get(document).getScroll();
7968 var xy = this.getXY();
7969 var x = xy[0], y = xy[1];
7970 var w = this.dom.offsetWidth+this.shadowOffset, h = this.dom.offsetHeight+this.shadowOffset;
7971 // only move it if it needs it
7973 // first validate right/bottom
7974 if((x + w) > vw+s.left){
7975 x = vw - w - this.shadowOffset;
7978 if((y + h) > vh+s.top){
7979 y = vh - h - this.shadowOffset;
7982 // then make sure top/left isn't negative
7993 var ay = this.avoidY;
7994 if(y <= ay && (y+h) >= ay){
8000 supr.setXY.call(this, xy);
8006 isVisible : function(){
8007 return this.visible;
8011 showAction : function(){
8012 this.visible = true; // track visibility to prevent getStyle calls
8013 if(this.useDisplay === true){
8014 this.setDisplayed("");
8015 }else if(this.lastXY){
8016 supr.setXY.call(this, this.lastXY);
8017 }else if(this.lastLT){
8018 supr.setLeftTop.call(this, this.lastLT[0], this.lastLT[1]);
8023 hideAction : function(){
8024 this.visible = false;
8025 if(this.useDisplay === true){
8026 this.setDisplayed(false);
8028 this.setLeftTop(-10000,-10000);
8032 // overridden Element method
8033 setVisible : function(v, a, d, c, e){
8038 var cb = function(){
8043 }.createDelegate(this);
8044 supr.setVisible.call(this, true, true, d, cb, e);
8047 this.hideUnders(true);
8056 }.createDelegate(this);
8058 supr.setVisible.call(this, v, a, d, cb, e);
8067 storeXY : function(xy){
8072 storeLeftTop : function(left, top){
8074 this.lastLT = [left, top];
8078 beforeFx : function(){
8079 this.beforeAction();
8080 return Roo.Layer.superclass.beforeFx.apply(this, arguments);
8084 afterFx : function(){
8085 Roo.Layer.superclass.afterFx.apply(this, arguments);
8086 this.sync(this.isVisible());
8090 beforeAction : function(){
8091 if(!this.updating && this.shadow){
8096 // overridden Element method
8097 setLeft : function(left){
8098 this.storeLeftTop(left, this.getTop(true));
8099 supr.setLeft.apply(this, arguments);
8103 setTop : function(top){
8104 this.storeLeftTop(this.getLeft(true), top);
8105 supr.setTop.apply(this, arguments);
8109 setLeftTop : function(left, top){
8110 this.storeLeftTop(left, top);
8111 supr.setLeftTop.apply(this, arguments);
8115 setXY : function(xy, a, d, c, e){
8117 this.beforeAction();
8119 var cb = this.createCB(c);
8120 supr.setXY.call(this, xy, a, d, cb, e);
8127 createCB : function(c){
8138 // overridden Element method
8139 setX : function(x, a, d, c, e){
8140 this.setXY([x, this.getY()], a, d, c, e);
8143 // overridden Element method
8144 setY : function(y, a, d, c, e){
8145 this.setXY([this.getX(), y], a, d, c, e);
8148 // overridden Element method
8149 setSize : function(w, h, a, d, c, e){
8150 this.beforeAction();
8151 var cb = this.createCB(c);
8152 supr.setSize.call(this, w, h, a, d, cb, e);
8158 // overridden Element method
8159 setWidth : function(w, a, d, c, e){
8160 this.beforeAction();
8161 var cb = this.createCB(c);
8162 supr.setWidth.call(this, w, a, d, cb, e);
8168 // overridden Element method
8169 setHeight : function(h, a, d, c, e){
8170 this.beforeAction();
8171 var cb = this.createCB(c);
8172 supr.setHeight.call(this, h, a, d, cb, e);
8178 // overridden Element method
8179 setBounds : function(x, y, w, h, a, d, c, e){
8180 this.beforeAction();
8181 var cb = this.createCB(c);
8183 this.storeXY([x, y]);
8184 supr.setXY.call(this, [x, y]);
8185 supr.setSize.call(this, w, h, a, d, cb, e);
8188 supr.setBounds.call(this, x, y, w, h, a, d, cb, e);
8194 * Sets the z-index of this layer and adjusts any shadow and shim z-indexes. The layer z-index is automatically
8195 * incremented by two more than the value passed in so that it always shows above any shadow or shim (the shadow
8196 * element, if any, will be assigned z-index + 1, and the shim element, if any, will be assigned the unmodified z-index).
8197 * @param {Number} zindex The new z-index to set
8198 * @return {this} The Layer
8200 setZIndex : function(zindex){
8201 this.zindex = zindex;
8202 this.setStyle("z-index", zindex + 2);
8204 this.shadow.setZIndex(zindex + 1);
8207 this.shim.setStyle("z-index", zindex);
8213 * Ext JS Library 1.1.1
8214 * Copyright(c) 2006-2007, Ext JS, LLC.
8216 * Originally Released Under LGPL - original licence link has changed is not relivant.
8219 * <script type="text/javascript">
8225 * Simple class that can provide a shadow effect for any element. Note that the element MUST be absolutely positioned,
8226 * and the shadow does not provide any shimming. This should be used only in simple cases -- for more advanced
8227 * functionality that can also provide the same shadow effect, see the {@link Roo.Layer} class.
8229 * Create a new Shadow
8230 * @param {Object} config The config object
8232 Roo.Shadow = function(config){
8233 Roo.apply(this, config);
8234 if(typeof this.mode != "string"){
8235 this.mode = this.defaultMode;
8237 var o = this.offset, a = {h: 0};
8238 var rad = Math.floor(this.offset/2);
8239 switch(this.mode.toLowerCase()){ // all this hideous nonsense calculates the various offsets for shadows
8245 a.l -= this.offset + rad;
8246 a.t -= this.offset + rad;
8257 a.l -= (this.offset - rad);
8258 a.t -= this.offset + rad;
8260 a.w -= (this.offset - rad)*2;
8271 a.l -= (this.offset - rad);
8272 a.t -= (this.offset - rad);
8274 a.w -= (this.offset + rad + 1);
8275 a.h -= (this.offset + rad);
8284 Roo.Shadow.prototype = {
8286 * @cfg {String} mode
8287 * The shadow display mode. Supports the following options:<br />
8288 * sides: Shadow displays on both sides and bottom only<br />
8289 * frame: Shadow displays equally on all four sides<br />
8290 * drop: Traditional bottom-right drop shadow (default)
8293 * @cfg {String} offset
8294 * The number of pixels to offset the shadow from the element (defaults to 4)
8299 defaultMode: "drop",
8302 * Displays the shadow under the target element
8303 * @param {String/HTMLElement/Element} targetEl The id or element under which the shadow should display
8305 show : function(target){
8306 target = Roo.get(target);
8308 this.el = Roo.Shadow.Pool.pull();
8309 if(this.el.dom.nextSibling != target.dom){
8310 this.el.insertBefore(target);
8313 this.el.setStyle("z-index", this.zIndex || parseInt(target.getStyle("z-index"), 10)-1);
8315 this.el.dom.style.filter="progid:DXImageTransform.Microsoft.alpha(opacity=50) progid:DXImageTransform.Microsoft.Blur(pixelradius="+(this.offset)+")";
8318 target.getLeft(true),
8319 target.getTop(true),
8323 this.el.dom.style.display = "block";
8327 * Returns true if the shadow is visible, else false
8329 isVisible : function(){
8330 return this.el ? true : false;
8334 * Direct alignment when values are already available. Show must be called at least once before
8335 * calling this method to ensure it is initialized.
8336 * @param {Number} left The target element left position
8337 * @param {Number} top The target element top position
8338 * @param {Number} width The target element width
8339 * @param {Number} height The target element height
8341 realign : function(l, t, w, h){
8345 var a = this.adjusts, d = this.el.dom, s = d.style;
8347 s.left = (l+a.l)+"px";
8348 s.top = (t+a.t)+"px";
8349 var sw = (w+a.w), sh = (h+a.h), sws = sw +"px", shs = sh + "px";
8351 if(s.width != sws || s.height != shs){
8355 var cn = d.childNodes;
8356 var sww = Math.max(0, (sw-12))+"px";
8357 cn[0].childNodes[1].style.width = sww;
8358 cn[1].childNodes[1].style.width = sww;
8359 cn[2].childNodes[1].style.width = sww;
8360 cn[1].style.height = Math.max(0, (sh-12))+"px";
8370 this.el.dom.style.display = "none";
8371 Roo.Shadow.Pool.push(this.el);
8377 * Adjust the z-index of this shadow
8378 * @param {Number} zindex The new z-index
8380 setZIndex : function(z){
8383 this.el.setStyle("z-index", z);
8388 // Private utility class that manages the internal Shadow cache
8389 Roo.Shadow.Pool = function(){
8391 var markup = Roo.isIE ?
8392 '<div class="x-ie-shadow"></div>' :
8393 '<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>';
8398 sh = Roo.get(Roo.DomHelper.insertHtml("beforeBegin", document.body.firstChild, markup));
8399 sh.autoBoxAdjust = false;
8404 push : function(sh){
8410 * Ext JS Library 1.1.1
8411 * Copyright(c) 2006-2007, Ext JS, LLC.
8413 * Originally Released Under LGPL - original licence link has changed is not relivant.
8416 * <script type="text/javascript">
8420 * @class Roo.BoxComponent
8421 * @extends Roo.Component
8422 * Base class for any visual {@link Roo.Component} that uses a box container. BoxComponent provides automatic box
8423 * model adjustments for sizing and positioning and will work correctly withnin the Component rendering model. All
8424 * container classes should subclass BoxComponent so that they will work consistently when nested within other Ext
8425 * layout containers.
8427 * @param {Roo.Element/String/Object} config The configuration options.
8429 Roo.BoxComponent = function(config){
8430 Roo.Component.call(this, config);
8434 * Fires after the component is resized.
8435 * @param {Roo.Component} this
8436 * @param {Number} adjWidth The box-adjusted width that was set
8437 * @param {Number} adjHeight The box-adjusted height that was set
8438 * @param {Number} rawWidth The width that was originally specified
8439 * @param {Number} rawHeight The height that was originally specified
8444 * Fires after the component is moved.
8445 * @param {Roo.Component} this
8446 * @param {Number} x The new x position
8447 * @param {Number} y The new y position
8453 Roo.extend(Roo.BoxComponent, Roo.Component, {
8454 // private, set in afterRender to signify that the component has been rendered
8456 // private, used to defer height settings to subclasses
8458 /** @cfg {Number} width
8459 * width (optional) size of component
8461 /** @cfg {Number} height
8462 * height (optional) size of component
8466 * Sets the width and height of the component. This method fires the resize event. This method can accept
8467 * either width and height as separate numeric arguments, or you can pass a size object like {width:10, height:20}.
8468 * @param {Number/Object} width The new width to set, or a size object in the format {width, height}
8469 * @param {Number} height The new height to set (not required if a size object is passed as the first arg)
8470 * @return {Roo.BoxComponent} this
8472 setSize : function(w, h){
8473 // support for standard size objects
8474 if(typeof w == 'object'){
8485 // prevent recalcs when not needed
8486 if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
8489 this.lastSize = {width: w, height: h};
8491 var adj = this.adjustSize(w, h);
8492 var aw = adj.width, ah = adj.height;
8493 if(aw !== undefined || ah !== undefined){ // this code is nasty but performs better with floaters
8494 var rz = this.getResizeEl();
8495 if(!this.deferHeight && aw !== undefined && ah !== undefined){
8497 }else if(!this.deferHeight && ah !== undefined){
8499 }else if(aw !== undefined){
8502 this.onResize(aw, ah, w, h);
8503 this.fireEvent('resize', this, aw, ah, w, h);
8509 * Gets the current size of the component's underlying element.
8510 * @return {Object} An object containing the element's size {width: (element width), height: (element height)}
8512 getSize : function(){
8513 return this.el.getSize();
8517 * Gets the current XY position of the component's underlying element.
8518 * @param {Boolean} local (optional) If true the element's left and top are returned instead of page XY (defaults to false)
8519 * @return {Array} The XY position of the element (e.g., [100, 200])
8521 getPosition : function(local){
8523 return [this.el.getLeft(true), this.el.getTop(true)];
8525 return this.xy || this.el.getXY();
8529 * Gets the current box measurements of the component's underlying element.
8530 * @param {Boolean} local (optional) If true the element's left and top are returned instead of page XY (defaults to false)
8531 * @returns {Object} box An object in the format {x, y, width, height}
8533 getBox : function(local){
8534 var s = this.el.getSize();
8536 s.x = this.el.getLeft(true);
8537 s.y = this.el.getTop(true);
8539 var xy = this.xy || this.el.getXY();
8547 * Sets the current box measurements of the component's underlying element.
8548 * @param {Object} box An object in the format {x, y, width, height}
8549 * @returns {Roo.BoxComponent} this
8551 updateBox : function(box){
8552 this.setSize(box.width, box.height);
8553 this.setPagePosition(box.x, box.y);
8558 getResizeEl : function(){
8559 return this.resizeEl || this.el;
8563 getPositionEl : function(){
8564 return this.positionEl || this.el;
8568 * Sets the left and top of the component. To set the page XY position instead, use {@link #setPagePosition}.
8569 * This method fires the move event.
8570 * @param {Number} left The new left
8571 * @param {Number} top The new top
8572 * @returns {Roo.BoxComponent} this
8574 setPosition : function(x, y){
8580 var adj = this.adjustPosition(x, y);
8581 var ax = adj.x, ay = adj.y;
8583 var el = this.getPositionEl();
8584 if(ax !== undefined || ay !== undefined){
8585 if(ax !== undefined && ay !== undefined){
8586 el.setLeftTop(ax, ay);
8587 }else if(ax !== undefined){
8589 }else if(ay !== undefined){
8592 this.onPosition(ax, ay);
8593 this.fireEvent('move', this, ax, ay);
8599 * Sets the page XY position of the component. To set the left and top instead, use {@link #setPosition}.
8600 * This method fires the move event.
8601 * @param {Number} x The new x position
8602 * @param {Number} y The new y position
8603 * @returns {Roo.BoxComponent} this
8605 setPagePosition : function(x, y){
8611 if(x === undefined || y === undefined){ // cannot translate undefined points
8614 var p = this.el.translatePoints(x, y);
8615 this.setPosition(p.left, p.top);
8620 onRender : function(ct, position){
8621 Roo.BoxComponent.superclass.onRender.call(this, ct, position);
8623 this.resizeEl = Roo.get(this.resizeEl);
8625 if(this.positionEl){
8626 this.positionEl = Roo.get(this.positionEl);
8631 afterRender : function(){
8632 Roo.BoxComponent.superclass.afterRender.call(this);
8633 this.boxReady = true;
8634 this.setSize(this.width, this.height);
8635 if(this.x || this.y){
8636 this.setPosition(this.x, this.y);
8638 if(this.pageX || this.pageY){
8639 this.setPagePosition(this.pageX, this.pageY);
8644 * Force the component's size to recalculate based on the underlying element's current height and width.
8645 * @returns {Roo.BoxComponent} this
8647 syncSize : function(){
8648 delete this.lastSize;
8649 this.setSize(this.el.getWidth(), this.el.getHeight());
8654 * Called after the component is resized, this method is empty by default but can be implemented by any
8655 * subclass that needs to perform custom logic after a resize occurs.
8656 * @param {Number} adjWidth The box-adjusted width that was set
8657 * @param {Number} adjHeight The box-adjusted height that was set
8658 * @param {Number} rawWidth The width that was originally specified
8659 * @param {Number} rawHeight The height that was originally specified
8661 onResize : function(adjWidth, adjHeight, rawWidth, rawHeight){
8666 * Called after the component is moved, this method is empty by default but can be implemented by any
8667 * subclass that needs to perform custom logic after a move occurs.
8668 * @param {Number} x The new x position
8669 * @param {Number} y The new y position
8671 onPosition : function(x, y){
8676 adjustSize : function(w, h){
8680 if(this.autoHeight){
8683 return {width : w, height: h};
8687 adjustPosition : function(x, y){
8688 return {x : x, y: y};
8692 * Ext JS Library 1.1.1
8693 * Copyright(c) 2006-2007, Ext JS, LLC.
8695 * Originally Released Under LGPL - original licence link has changed is not relivant.
8698 * <script type="text/javascript">
8703 * @class Roo.SplitBar
8704 * @extends Roo.util.Observable
8705 * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
8709 var split = new Roo.SplitBar("elementToDrag", "elementToSize",
8710 Roo.SplitBar.HORIZONTAL, Roo.SplitBar.LEFT);
8711 split.setAdapter(new Roo.SplitBar.AbsoluteLayoutAdapter("container"));
8712 split.minSize = 100;
8713 split.maxSize = 600;
8714 split.animate = true;
8715 split.on('moved', splitterMoved);
8718 * Create a new SplitBar
8719 * @param {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar.
8720 * @param {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged
8721 * @param {Number} orientation (optional) Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
8722 * @param {Number} placement (optional) Either Roo.SplitBar.LEFT or Roo.SplitBar.RIGHT for horizontal or
8723 Roo.SplitBar.TOP or Roo.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
8724 position of the SplitBar).
8726 Roo.SplitBar = function(dragElement, resizingElement, orientation, placement, existingProxy){
8729 this.el = Roo.get(dragElement, true);
8730 this.el.dom.unselectable = "on";
8732 this.resizingEl = Roo.get(resizingElement, true);
8736 * The orientation of the split. Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
8737 * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
8740 this.orientation = orientation || Roo.SplitBar.HORIZONTAL;
8743 * The minimum size of the resizing element. (Defaults to 0)
8749 * The maximum size of the resizing element. (Defaults to 2000)
8752 this.maxSize = 2000;
8755 * Whether to animate the transition to the new size
8758 this.animate = false;
8761 * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
8764 this.useShim = false;
8771 this.proxy = Roo.SplitBar.createProxy(this.orientation);
8773 this.proxy = Roo.get(existingProxy).dom;
8776 this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
8779 this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
8782 this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
8785 this.dragSpecs = {};
8788 * @private The adapter to use to positon and resize elements
8790 this.adapter = new Roo.SplitBar.BasicLayoutAdapter();
8791 this.adapter.init(this);
8793 if(this.orientation == Roo.SplitBar.HORIZONTAL){
8795 this.placement = placement || (this.el.getX() > this.resizingEl.getX() ? Roo.SplitBar.LEFT : Roo.SplitBar.RIGHT);
8796 this.el.addClass("x-splitbar-h");
8799 this.placement = placement || (this.el.getY() > this.resizingEl.getY() ? Roo.SplitBar.TOP : Roo.SplitBar.BOTTOM);
8800 this.el.addClass("x-splitbar-v");
8806 * Fires when the splitter is moved (alias for {@link #event-moved})
8807 * @param {Roo.SplitBar} this
8808 * @param {Number} newSize the new width or height
8813 * Fires when the splitter is moved
8814 * @param {Roo.SplitBar} this
8815 * @param {Number} newSize the new width or height
8819 * @event beforeresize
8820 * Fires before the splitter is dragged
8821 * @param {Roo.SplitBar} this
8823 "beforeresize" : true,
8825 "beforeapply" : true
8828 Roo.util.Observable.call(this);
8831 Roo.extend(Roo.SplitBar, Roo.util.Observable, {
8832 onStartProxyDrag : function(x, y){
8833 this.fireEvent("beforeresize", this);
8835 var o = Roo.DomHelper.insertFirst(document.body, {cls: "x-drag-overlay", html: " "}, true);
8837 o.enableDisplayMode("block");
8838 // all splitbars share the same overlay
8839 Roo.SplitBar.prototype.overlay = o;
8841 this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
8842 this.overlay.show();
8843 Roo.get(this.proxy).setDisplayed("block");
8844 var size = this.adapter.getElementSize(this);
8845 this.activeMinSize = this.getMinimumSize();;
8846 this.activeMaxSize = this.getMaximumSize();;
8847 var c1 = size - this.activeMinSize;
8848 var c2 = Math.max(this.activeMaxSize - size, 0);
8849 if(this.orientation == Roo.SplitBar.HORIZONTAL){
8850 this.dd.resetConstraints();
8851 this.dd.setXConstraint(
8852 this.placement == Roo.SplitBar.LEFT ? c1 : c2,
8853 this.placement == Roo.SplitBar.LEFT ? c2 : c1
8855 this.dd.setYConstraint(0, 0);
8857 this.dd.resetConstraints();
8858 this.dd.setXConstraint(0, 0);
8859 this.dd.setYConstraint(
8860 this.placement == Roo.SplitBar.TOP ? c1 : c2,
8861 this.placement == Roo.SplitBar.TOP ? c2 : c1
8864 this.dragSpecs.startSize = size;
8865 this.dragSpecs.startPoint = [x, y];
8866 Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
8870 * @private Called after the drag operation by the DDProxy
8872 onEndProxyDrag : function(e){
8873 Roo.get(this.proxy).setDisplayed(false);
8874 var endPoint = Roo.lib.Event.getXY(e);
8876 this.overlay.hide();
8879 if(this.orientation == Roo.SplitBar.HORIZONTAL){
8880 newSize = this.dragSpecs.startSize +
8881 (this.placement == Roo.SplitBar.LEFT ?
8882 endPoint[0] - this.dragSpecs.startPoint[0] :
8883 this.dragSpecs.startPoint[0] - endPoint[0]
8886 newSize = this.dragSpecs.startSize +
8887 (this.placement == Roo.SplitBar.TOP ?
8888 endPoint[1] - this.dragSpecs.startPoint[1] :
8889 this.dragSpecs.startPoint[1] - endPoint[1]
8892 newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
8893 if(newSize != this.dragSpecs.startSize){
8894 if(this.fireEvent('beforeapply', this, newSize) !== false){
8895 this.adapter.setElementSize(this, newSize);
8896 this.fireEvent("moved", this, newSize);
8897 this.fireEvent("resize", this, newSize);
8903 * Get the adapter this SplitBar uses
8904 * @return The adapter object
8906 getAdapter : function(){
8907 return this.adapter;
8911 * Set the adapter this SplitBar uses
8912 * @param {Object} adapter A SplitBar adapter object
8914 setAdapter : function(adapter){
8915 this.adapter = adapter;
8916 this.adapter.init(this);
8920 * Gets the minimum size for the resizing element
8921 * @return {Number} The minimum size
8923 getMinimumSize : function(){
8924 return this.minSize;
8928 * Sets the minimum size for the resizing element
8929 * @param {Number} minSize The minimum size
8931 setMinimumSize : function(minSize){
8932 this.minSize = minSize;
8936 * Gets the maximum size for the resizing element
8937 * @return {Number} The maximum size
8939 getMaximumSize : function(){
8940 return this.maxSize;
8944 * Sets the maximum size for the resizing element
8945 * @param {Number} maxSize The maximum size
8947 setMaximumSize : function(maxSize){
8948 this.maxSize = maxSize;
8952 * Sets the initialize size for the resizing element
8953 * @param {Number} size The initial size
8955 setCurrentSize : function(size){
8956 var oldAnimate = this.animate;
8957 this.animate = false;
8958 this.adapter.setElementSize(this, size);
8959 this.animate = oldAnimate;
8963 * Destroy this splitbar.
8964 * @param {Boolean} removeEl True to remove the element
8966 destroy : function(removeEl){
8971 this.proxy.parentNode.removeChild(this.proxy);
8979 * @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.
8981 Roo.SplitBar.createProxy = function(dir){
8982 var proxy = new Roo.Element(document.createElement("div"));
8983 proxy.unselectable();
8984 var cls = 'x-splitbar-proxy';
8985 proxy.addClass(cls + ' ' + (dir == Roo.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
8986 document.body.appendChild(proxy.dom);
8991 * @class Roo.SplitBar.BasicLayoutAdapter
8992 * Default Adapter. It assumes the splitter and resizing element are not positioned
8993 * elements and only gets/sets the width of the element. Generally used for table based layouts.
8995 Roo.SplitBar.BasicLayoutAdapter = function(){
8998 Roo.SplitBar.BasicLayoutAdapter.prototype = {
8999 // do nothing for now
9004 * Called before drag operations to get the current size of the resizing element.
9005 * @param {Roo.SplitBar} s The SplitBar using this adapter
9007 getElementSize : function(s){
9008 if(s.orientation == Roo.SplitBar.HORIZONTAL){
9009 return s.resizingEl.getWidth();
9011 return s.resizingEl.getHeight();
9016 * Called after drag operations to set the size of the resizing element.
9017 * @param {Roo.SplitBar} s The SplitBar using this adapter
9018 * @param {Number} newSize The new size to set
9019 * @param {Function} onComplete A function to be invoked when resizing is complete
9021 setElementSize : function(s, newSize, onComplete){
9022 if(s.orientation == Roo.SplitBar.HORIZONTAL){
9024 s.resizingEl.setWidth(newSize);
9026 onComplete(s, newSize);
9029 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
9034 s.resizingEl.setHeight(newSize);
9036 onComplete(s, newSize);
9039 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
9046 *@class Roo.SplitBar.AbsoluteLayoutAdapter
9047 * @extends Roo.SplitBar.BasicLayoutAdapter
9048 * Adapter that moves the splitter element to align with the resized sizing element.
9049 * Used with an absolute positioned SplitBar.
9050 * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
9051 * document.body, make sure you assign an id to the body element.
9053 Roo.SplitBar.AbsoluteLayoutAdapter = function(container){
9054 this.basic = new Roo.SplitBar.BasicLayoutAdapter();
9055 this.container = Roo.get(container);
9058 Roo.SplitBar.AbsoluteLayoutAdapter.prototype = {
9063 getElementSize : function(s){
9064 return this.basic.getElementSize(s);
9067 setElementSize : function(s, newSize, onComplete){
9068 this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
9071 moveSplitter : function(s){
9072 var yes = Roo.SplitBar;
9073 switch(s.placement){
9075 s.el.setX(s.resizingEl.getRight());
9078 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
9081 s.el.setY(s.resizingEl.getBottom());
9084 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
9091 * Orientation constant - Create a vertical SplitBar
9095 Roo.SplitBar.VERTICAL = 1;
9098 * Orientation constant - Create a horizontal SplitBar
9102 Roo.SplitBar.HORIZONTAL = 2;
9105 * Placement constant - The resizing element is to the left of the splitter element
9109 Roo.SplitBar.LEFT = 1;
9112 * Placement constant - The resizing element is to the right of the splitter element
9116 Roo.SplitBar.RIGHT = 2;
9119 * Placement constant - The resizing element is positioned above the splitter element
9123 Roo.SplitBar.TOP = 3;
9126 * Placement constant - The resizing element is positioned under splitter element
9130 Roo.SplitBar.BOTTOM = 4;
9133 * Ext JS Library 1.1.1
9134 * Copyright(c) 2006-2007, Ext JS, LLC.
9136 * Originally Released Under LGPL - original licence link has changed is not relivant.
9139 * <script type="text/javascript">
9144 * @extends Roo.util.Observable
9145 * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template.
9146 * This class also supports single and multi selection modes. <br>
9147 * Create a data model bound view:
9149 var store = new Roo.data.Store(...);
9151 var view = new Roo.View({
9153 tpl : '<div id="{0}">{2} - {1}</div>', // auto create template
9156 selectedClass: "ydataview-selected",
9160 // listen for node click?
9161 view.on("click", function(vw, index, node, e){
9162 alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
9166 dataModel.load("foobar.xml");
9168 For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
9170 * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
9171 * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
9173 * Note: old style constructor is still suported (container, template, config)
9177 * @param {Object} config The config object
9180 Roo.View = function(config, depreciated_tpl, depreciated_config){
9182 if (typeof(depreciated_tpl) == 'undefined') {
9183 // new way.. - universal constructor.
9184 Roo.apply(this, config);
9185 this.el = Roo.get(this.el);
9188 this.el = Roo.get(config);
9189 this.tpl = depreciated_tpl;
9190 Roo.apply(this, depreciated_config);
9192 this.wrapEl = this.el.wrap().wrap();
9193 ///this.el = this.wrapEla.appendChild(document.createElement("div"));
9196 if(typeof(this.tpl) == "string"){
9197 this.tpl = new Roo.Template(this.tpl);
9199 // support xtype ctors..
9200 this.tpl = new Roo.factory(this.tpl, Roo);
9212 * @event beforeclick
9213 * Fires before a click is processed. Returns false to cancel the default action.
9214 * @param {Roo.View} this
9215 * @param {Number} index The index of the target node
9216 * @param {HTMLElement} node The target node
9217 * @param {Roo.EventObject} e The raw event object
9219 "beforeclick" : true,
9222 * Fires when a template node is clicked.
9223 * @param {Roo.View} this
9224 * @param {Number} index The index of the target node
9225 * @param {HTMLElement} node The target node
9226 * @param {Roo.EventObject} e The raw event object
9231 * Fires when a template node is double clicked.
9232 * @param {Roo.View} this
9233 * @param {Number} index The index of the target node
9234 * @param {HTMLElement} node The target node
9235 * @param {Roo.EventObject} e The raw event object
9239 * @event contextmenu
9240 * Fires when a template node is right clicked.
9241 * @param {Roo.View} this
9242 * @param {Number} index The index of the target node
9243 * @param {HTMLElement} node The target node
9244 * @param {Roo.EventObject} e The raw event object
9246 "contextmenu" : true,
9248 * @event selectionchange
9249 * Fires when the selected nodes change.
9250 * @param {Roo.View} this
9251 * @param {Array} selections Array of the selected nodes
9253 "selectionchange" : true,
9256 * @event beforeselect
9257 * Fires before a selection is made. If any handlers return false, the selection is cancelled.
9258 * @param {Roo.View} this
9259 * @param {HTMLElement} node The node to be selected
9260 * @param {Array} selections Array of currently selected nodes
9262 "beforeselect" : true,
9264 * @event preparedata
9265 * Fires on every row to render, to allow you to change the data.
9266 * @param {Roo.View} this
9267 * @param {Object} data to be rendered (change this)
9269 "preparedata" : true
9277 "click": this.onClick,
9278 "dblclick": this.onDblClick,
9279 "contextmenu": this.onContextMenu,
9283 this.selections = [];
9285 this.cmp = new Roo.CompositeElementLite([]);
9287 this.store = Roo.factory(this.store, Roo.data);
9288 this.setStore(this.store, true);
9291 if ( this.footer && this.footer.xtype) {
9293 var fctr = this.wrapEl.appendChild(document.createElement("div"));
9295 this.footer.dataSource = this.store
9296 this.footer.container = fctr;
9297 this.footer = Roo.factory(this.footer, Roo);
9298 fctr.insertFirst(this.el);
9300 // this is a bit insane - as the paging toolbar seems to detach the el..
9301 // dom.parentNode.parentNode.parentNode
9302 // they get detached?
9306 Roo.View.superclass.constructor.call(this);
9311 Roo.extend(Roo.View, Roo.util.Observable, {
9314 * @cfg {Roo.data.Store} store Data store to load data from.
9319 * @cfg {String|Roo.Element} el The container element.
9324 * @cfg {String|Roo.Template} tpl The template used by this View
9328 * @cfg {String} dataName the named area of the template to use as the data area
9329 * Works with domtemplates roo-name="name"
9333 * @cfg {String} selectedClass The css class to add to selected nodes
9335 selectedClass : "x-view-selected",
9337 * @cfg {String} emptyText The empty text to show when nothing is loaded.
9342 * @cfg {String} text to display on mask (default Loading)
9346 * @cfg {Boolean} multiSelect Allow multiple selection
9348 multiSelect : false,
9350 * @cfg {Boolean} singleSelect Allow single selection
9352 singleSelect: false,
9355 * @cfg {Boolean} toggleSelect - selecting
9357 toggleSelect : false,
9360 * Returns the element this view is bound to.
9361 * @return {Roo.Element}
9370 * Refreshes the view. - called by datachanged on the store. - do not call directly.
9372 refresh : function(){
9375 // if we are using something like 'domtemplate', then
9376 // the what gets used is:
9377 // t.applySubtemplate(NAME, data, wrapping data..)
9378 // the outer template then get' applied with
9379 // the store 'extra data'
9380 // and the body get's added to the
9381 // roo-name="data" node?
9382 // <span class='roo-tpl-{name}'></span> ?????
9386 this.clearSelections();
9389 var records = this.store.getRange();
9390 if(records.length < 1) {
9392 // is this valid?? = should it render a template??
9394 this.el.update(this.emptyText);
9398 if (this.dataName) {
9399 this.el.update(t.apply(this.store.meta)); //????
9400 el = this.el.child('.roo-tpl-' + this.dataName);
9403 for(var i = 0, len = records.length; i < len; i++){
9404 var data = this.prepareData(records[i].data, i, records[i]);
9405 this.fireEvent("preparedata", this, data, i, records[i]);
9406 html[html.length] = Roo.util.Format.trim(
9408 t.applySubtemplate(this.dataName, data, this.store.meta) :
9415 el.update(html.join(""));
9416 this.nodes = el.dom.childNodes;
9417 this.updateIndexes(0);
9421 * Function to override to reformat the data that is sent to
9422 * the template for each node.
9423 * DEPRICATED - use the preparedata event handler.
9424 * @param {Array/Object} data The raw data (array of colData for a data model bound view or
9425 * a JSON object for an UpdateManager bound view).
9427 prepareData : function(data, index, record)
9429 this.fireEvent("preparedata", this, data, index, record);
9433 onUpdate : function(ds, record){
9434 this.clearSelections();
9435 var index = this.store.indexOf(record);
9436 var n = this.nodes[index];
9437 this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
9438 n.parentNode.removeChild(n);
9439 this.updateIndexes(index, index);
9445 onAdd : function(ds, records, index)
9447 this.clearSelections();
9448 if(this.nodes.length == 0){
9452 var n = this.nodes[index];
9453 for(var i = 0, len = records.length; i < len; i++){
9454 var d = this.prepareData(records[i].data, i, records[i]);
9456 this.tpl.insertBefore(n, d);
9459 this.tpl.append(this.el, d);
9462 this.updateIndexes(index);
9465 onRemove : function(ds, record, index){
9466 this.clearSelections();
9467 var el = this.dataName ?
9468 this.el.child('.roo-tpl-' + this.dataName) :
9470 el.dom.removeChild(this.nodes[index]);
9471 this.updateIndexes(index);
9475 * Refresh an individual node.
9476 * @param {Number} index
9478 refreshNode : function(index){
9479 this.onUpdate(this.store, this.store.getAt(index));
9482 updateIndexes : function(startIndex, endIndex){
9483 var ns = this.nodes;
9484 startIndex = startIndex || 0;
9485 endIndex = endIndex || ns.length - 1;
9486 for(var i = startIndex; i <= endIndex; i++){
9487 ns[i].nodeIndex = i;
9492 * Changes the data store this view uses and refresh the view.
9493 * @param {Store} store
9495 setStore : function(store, initial){
9496 if(!initial && this.store){
9497 this.store.un("datachanged", this.refresh);
9498 this.store.un("add", this.onAdd);
9499 this.store.un("remove", this.onRemove);
9500 this.store.un("update", this.onUpdate);
9501 this.store.un("clear", this.refresh);
9502 this.store.un("beforeload", this.onBeforeLoad);
9503 this.store.un("load", this.onLoad);
9504 this.store.un("loadexception", this.onLoad);
9508 store.on("datachanged", this.refresh, this);
9509 store.on("add", this.onAdd, this);
9510 store.on("remove", this.onRemove, this);
9511 store.on("update", this.onUpdate, this);
9512 store.on("clear", this.refresh, this);
9513 store.on("beforeload", this.onBeforeLoad, this);
9514 store.on("load", this.onLoad, this);
9515 store.on("loadexception", this.onLoad, this);
9523 * onbeforeLoad - masks the loading area.
9526 onBeforeLoad : function()
9529 this.el.mask(this.mask ? this.mask : "Loading" );
9531 onLoad : function ()
9538 * Returns the template node the passed child belongs to or null if it doesn't belong to one.
9539 * @param {HTMLElement} node
9540 * @return {HTMLElement} The template node
9542 findItemFromChild : function(node){
9543 var el = this.dataName ?
9544 this.el.child('.roo-tpl-' + this.dataName,true) :
9547 if(!node || node.parentNode == el){
9550 var p = node.parentNode;
9551 while(p && p != el){
9552 if(p.parentNode == el){
9561 onClick : function(e){
9562 var item = this.findItemFromChild(e.getTarget());
9564 var index = this.indexOf(item);
9565 if(this.onItemClick(item, index, e) !== false){
9566 this.fireEvent("click", this, index, item, e);
9569 this.clearSelections();
9574 onContextMenu : function(e){
9575 var item = this.findItemFromChild(e.getTarget());
9577 this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
9582 onDblClick : function(e){
9583 var item = this.findItemFromChild(e.getTarget());
9585 this.fireEvent("dblclick", this, this.indexOf(item), item, e);
9589 onItemClick : function(item, index, e)
9591 if(this.fireEvent("beforeclick", this, index, item, e) === false){
9594 if (this.toggleSelect) {
9595 var m = this.isSelected(item) ? 'unselect' : 'select';
9598 _t[m](item, true, false);
9601 if(this.multiSelect || this.singleSelect){
9602 if(this.multiSelect && e.shiftKey && this.lastSelection){
9603 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
9605 this.select(item, this.multiSelect && e.ctrlKey);
9606 this.lastSelection = item;
9614 * Get the number of selected nodes.
9617 getSelectionCount : function(){
9618 return this.selections.length;
9622 * Get the currently selected nodes.
9623 * @return {Array} An array of HTMLElements
9625 getSelectedNodes : function(){
9626 return this.selections;
9630 * Get the indexes of the selected nodes.
9633 getSelectedIndexes : function(){
9634 var indexes = [], s = this.selections;
9635 for(var i = 0, len = s.length; i < len; i++){
9636 indexes.push(s[i].nodeIndex);
9642 * Clear all selections
9643 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
9645 clearSelections : function(suppressEvent){
9646 if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
9647 this.cmp.elements = this.selections;
9648 this.cmp.removeClass(this.selectedClass);
9649 this.selections = [];
9651 this.fireEvent("selectionchange", this, this.selections);
9657 * Returns true if the passed node is selected
9658 * @param {HTMLElement/Number} node The node or node index
9661 isSelected : function(node){
9662 var s = this.selections;
9666 node = this.getNode(node);
9667 return s.indexOf(node) !== -1;
9672 * @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
9673 * @param {Boolean} keepExisting (optional) true to keep existing selections
9674 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
9676 select : function(nodeInfo, keepExisting, suppressEvent){
9677 if(nodeInfo instanceof Array){
9679 this.clearSelections(true);
9681 for(var i = 0, len = nodeInfo.length; i < len; i++){
9682 this.select(nodeInfo[i], true, true);
9686 var node = this.getNode(nodeInfo);
9687 if(!node || this.isSelected(node)){
9688 return; // already selected.
9691 this.clearSelections(true);
9693 if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
9694 Roo.fly(node).addClass(this.selectedClass);
9695 this.selections.push(node);
9697 this.fireEvent("selectionchange", this, this.selections);
9705 * @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
9706 * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
9707 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
9709 unselect : function(nodeInfo, keepExisting, suppressEvent)
9711 if(nodeInfo instanceof Array){
9712 Roo.each(this.selections, function(s) {
9713 this.unselect(s, nodeInfo);
9717 var node = this.getNode(nodeInfo);
9718 if(!node || !this.isSelected(node)){
9719 Roo.log("not selected");
9720 return; // not selected.
9724 Roo.each(this.selections, function(s) {
9726 Roo.fly(node).removeClass(this.selectedClass);
9733 this.selections= ns;
9734 this.fireEvent("selectionchange", this, this.selections);
9738 * Gets a template node.
9739 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
9740 * @return {HTMLElement} The node or null if it wasn't found
9742 getNode : function(nodeInfo){
9743 if(typeof nodeInfo == "string"){
9744 return document.getElementById(nodeInfo);
9745 }else if(typeof nodeInfo == "number"){
9746 return this.nodes[nodeInfo];
9752 * Gets a range template nodes.
9753 * @param {Number} startIndex
9754 * @param {Number} endIndex
9755 * @return {Array} An array of nodes
9757 getNodes : function(start, end){
9758 var ns = this.nodes;
9760 end = typeof end == "undefined" ? ns.length - 1 : end;
9763 for(var i = start; i <= end; i++){
9767 for(var i = start; i >= end; i--){
9775 * Finds the index of the passed node
9776 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
9777 * @return {Number} The index of the node or -1
9779 indexOf : function(node){
9780 node = this.getNode(node);
9781 if(typeof node.nodeIndex == "number"){
9782 return node.nodeIndex;
9784 var ns = this.nodes;
9785 for(var i = 0, len = ns.length; i < len; i++){
9795 * Ext JS Library 1.1.1
9796 * Copyright(c) 2006-2007, Ext JS, LLC.
9798 * Originally Released Under LGPL - original licence link has changed is not relivant.
9801 * <script type="text/javascript">
9805 * @class Roo.JsonView
9807 * Shortcut class to create a JSON + {@link Roo.UpdateManager} template view. Usage:
9809 var view = new Roo.JsonView({
9810 container: "my-element",
9811 tpl: '<div id="{id}">{foo} - {bar}</div>', // auto create template
9816 // listen for node click?
9817 view.on("click", function(vw, index, node, e){
9818 alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
9821 // direct load of JSON data
9822 view.load("foobar.php");
9824 // Example from my blog list
9825 var tpl = new Roo.Template(
9826 '<div class="entry">' +
9827 '<a class="entry-title" href="{link}">{title}</a>' +
9828 "<h4>{date} by {author} | {comments} Comments</h4>{description}" +
9829 "</div><hr />"
9832 var moreView = new Roo.JsonView({
9833 container : "entry-list",
9837 moreView.on("beforerender", this.sortEntries, this);
9839 url: "/blog/get-posts.php",
9840 params: "allposts=true",
9841 text: "Loading Blog Entries..."
9845 * Note: old code is supported with arguments : (container, template, config)
9849 * Create a new JsonView
9851 * @param {Object} config The config object
9854 Roo.JsonView = function(config, depreciated_tpl, depreciated_config){
9857 Roo.JsonView.superclass.constructor.call(this, config, depreciated_tpl, depreciated_config);
9859 var um = this.el.getUpdateManager();
9860 um.setRenderer(this);
9861 um.on("update", this.onLoad, this);
9862 um.on("failure", this.onLoadException, this);
9865 * @event beforerender
9866 * Fires before rendering of the downloaded JSON data.
9867 * @param {Roo.JsonView} this
9868 * @param {Object} data The JSON data loaded
9872 * Fires when data is loaded.
9873 * @param {Roo.JsonView} this
9874 * @param {Object} data The JSON data loaded
9875 * @param {Object} response The raw Connect response object
9878 * @event loadexception
9879 * Fires when loading fails.
9880 * @param {Roo.JsonView} this
9881 * @param {Object} response The raw Connect response object
9884 'beforerender' : true,
9886 'loadexception' : true
9889 Roo.extend(Roo.JsonView, Roo.View, {
9891 * @type {String} The root property in the loaded JSON object that contains the data
9896 * Refreshes the view.
9898 refresh : function(){
9899 this.clearSelections();
9902 var o = this.jsonData;
9903 if(o && o.length > 0){
9904 for(var i = 0, len = o.length; i < len; i++){
9905 var data = this.prepareData(o[i], i, o);
9906 html[html.length] = this.tpl.apply(data);
9909 html.push(this.emptyText);
9911 this.el.update(html.join(""));
9912 this.nodes = this.el.dom.childNodes;
9913 this.updateIndexes(0);
9917 * 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.
9918 * @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:
9921 url: "your-url.php",
9922 params: {param1: "foo", param2: "bar"}, // or a URL encoded string
9923 callback: yourFunction,
9924 scope: yourObject, //(optional scope)
9932 * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
9933 * 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.
9934 * @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}
9935 * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
9936 * @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.
9939 var um = this.el.getUpdateManager();
9940 um.update.apply(um, arguments);
9943 render : function(el, response){
9944 this.clearSelections();
9948 o = Roo.util.JSON.decode(response.responseText);
9951 o = o[this.jsonRoot];
9956 * The current JSON data or null
9959 this.beforeRender();
9964 * Get the number of records in the current JSON dataset
9967 getCount : function(){
9968 return this.jsonData ? this.jsonData.length : 0;
9972 * Returns the JSON object for the specified node(s)
9973 * @param {HTMLElement/Array} node The node or an array of nodes
9974 * @return {Object/Array} If you pass in an array, you get an array back, otherwise
9975 * you get the JSON object for the node
9977 getNodeData : function(node){
9978 if(node instanceof Array){
9980 for(var i = 0, len = node.length; i < len; i++){
9981 data.push(this.getNodeData(node[i]));
9985 return this.jsonData[this.indexOf(node)] || null;
9988 beforeRender : function(){
9989 this.snapshot = this.jsonData;
9991 this.sort.apply(this, this.sortInfo);
9993 this.fireEvent("beforerender", this, this.jsonData);
9996 onLoad : function(el, o){
9997 this.fireEvent("load", this, this.jsonData, o);
10000 onLoadException : function(el, o){
10001 this.fireEvent("loadexception", this, o);
10005 * Filter the data by a specific property.
10006 * @param {String} property A property on your JSON objects
10007 * @param {String/RegExp} value Either string that the property values
10008 * should start with, or a RegExp to test against the property
10010 filter : function(property, value){
10013 var ss = this.snapshot;
10014 if(typeof value == "string"){
10015 var vlen = value.length;
10017 this.clearFilter();
10020 value = value.toLowerCase();
10021 for(var i = 0, len = ss.length; i < len; i++){
10023 if(o[property].substr(0, vlen).toLowerCase() == value){
10027 } else if(value.exec){ // regex?
10028 for(var i = 0, len = ss.length; i < len; i++){
10030 if(value.test(o[property])){
10037 this.jsonData = data;
10043 * Filter by a function. The passed function will be called with each
10044 * object in the current dataset. If the function returns true the value is kept,
10045 * otherwise it is filtered.
10046 * @param {Function} fn
10047 * @param {Object} scope (optional) The scope of the function (defaults to this JsonView)
10049 filterBy : function(fn, scope){
10052 var ss = this.snapshot;
10053 for(var i = 0, len = ss.length; i < len; i++){
10055 if(fn.call(scope || this, o)){
10059 this.jsonData = data;
10065 * Clears the current filter.
10067 clearFilter : function(){
10068 if(this.snapshot && this.jsonData != this.snapshot){
10069 this.jsonData = this.snapshot;
10076 * Sorts the data for this view and refreshes it.
10077 * @param {String} property A property on your JSON objects to sort on
10078 * @param {String} direction (optional) "desc" or "asc" (defaults to "asc")
10079 * @param {Function} sortType (optional) A function to call to convert the data to a sortable value.
10081 sort : function(property, dir, sortType){
10082 this.sortInfo = Array.prototype.slice.call(arguments, 0);
10085 var dsc = dir && dir.toLowerCase() == "desc";
10086 var f = function(o1, o2){
10087 var v1 = sortType ? sortType(o1[p]) : o1[p];
10088 var v2 = sortType ? sortType(o2[p]) : o2[p];
10091 return dsc ? +1 : -1;
10092 } else if(v1 > v2){
10093 return dsc ? -1 : +1;
10098 this.jsonData.sort(f);
10100 if(this.jsonData != this.snapshot){
10101 this.snapshot.sort(f);
10107 * Ext JS Library 1.1.1
10108 * Copyright(c) 2006-2007, Ext JS, LLC.
10110 * Originally Released Under LGPL - original licence link has changed is not relivant.
10113 * <script type="text/javascript">
10118 * @class Roo.ColorPalette
10119 * @extends Roo.Component
10120 * Simple color palette class for choosing colors. The palette can be rendered to any container.<br />
10121 * Here's an example of typical usage:
10123 var cp = new Roo.ColorPalette({value:'993300'}); // initial selected color
10124 cp.render('my-div');
10126 cp.on('select', function(palette, selColor){
10127 // do something with selColor
10131 * Create a new ColorPalette
10132 * @param {Object} config The config object
10134 Roo.ColorPalette = function(config){
10135 Roo.ColorPalette.superclass.constructor.call(this, config);
10139 * Fires when a color is selected
10140 * @param {ColorPalette} this
10141 * @param {String} color The 6-digit color hex code (without the # symbol)
10147 this.on("select", this.handler, this.scope, true);
10150 Roo.extend(Roo.ColorPalette, Roo.Component, {
10152 * @cfg {String} itemCls
10153 * The CSS class to apply to the containing element (defaults to "x-color-palette")
10155 itemCls : "x-color-palette",
10157 * @cfg {String} value
10158 * The initial color to highlight (should be a valid 6-digit color hex code without the # symbol). Note that
10159 * the hex codes are case-sensitive.
10162 clickEvent:'click',
10164 ctype: "Roo.ColorPalette",
10167 * @cfg {Boolean} allowReselect If set to true then reselecting a color that is already selected fires the selection event
10169 allowReselect : false,
10172 * <p>An array of 6-digit color hex code strings (without the # symbol). This array can contain any number
10173 * of colors, and each hex code should be unique. The width of the palette is controlled via CSS by adjusting
10174 * the width property of the 'x-color-palette' class (or assigning a custom class), so you can balance the number
10175 * of colors with the width setting until the box is symmetrical.</p>
10176 * <p>You can override individual colors if needed:</p>
10178 var cp = new Roo.ColorPalette();
10179 cp.colors[0] = "FF0000"; // change the first box to red
10182 Or you can provide a custom array of your own for complete control:
10184 var cp = new Roo.ColorPalette();
10185 cp.colors = ["000000", "993300", "333300"];
10190 "000000", "993300", "333300", "003300", "003366", "000080", "333399", "333333",
10191 "800000", "FF6600", "808000", "008000", "008080", "0000FF", "666699", "808080",
10192 "FF0000", "FF9900", "99CC00", "339966", "33CCCC", "3366FF", "800080", "969696",
10193 "FF00FF", "FFCC00", "FFFF00", "00FF00", "00FFFF", "00CCFF", "993366", "C0C0C0",
10194 "FF99CC", "FFCC99", "FFFF99", "CCFFCC", "CCFFFF", "99CCFF", "CC99FF", "FFFFFF"
10198 onRender : function(container, position){
10199 var t = new Roo.MasterTemplate(
10200 '<tpl><a href="#" class="color-{0}" hidefocus="on"><em><span style="background:#{0}" unselectable="on"> </span></em></a></tpl>'
10202 var c = this.colors;
10203 for(var i = 0, len = c.length; i < len; i++){
10206 var el = document.createElement("div");
10207 el.className = this.itemCls;
10209 container.dom.insertBefore(el, position);
10210 this.el = Roo.get(el);
10211 this.el.on(this.clickEvent, this.handleClick, this, {delegate: "a"});
10212 if(this.clickEvent != 'click'){
10213 this.el.on('click', Roo.emptyFn, this, {delegate: "a", preventDefault:true});
10218 afterRender : function(){
10219 Roo.ColorPalette.superclass.afterRender.call(this);
10221 var s = this.value;
10228 handleClick : function(e, t){
10229 e.preventDefault();
10230 if(!this.disabled){
10231 var c = t.className.match(/(?:^|\s)color-(.{6})(?:\s|$)/)[1];
10232 this.select(c.toUpperCase());
10237 * Selects the specified color in the palette (fires the select event)
10238 * @param {String} color A valid 6-digit color hex code (# will be stripped if included)
10240 select : function(color){
10241 color = color.replace("#", "");
10242 if(color != this.value || this.allowReselect){
10245 el.child("a.color-"+this.value).removeClass("x-color-palette-sel");
10247 el.child("a.color-"+color).addClass("x-color-palette-sel");
10248 this.value = color;
10249 this.fireEvent("select", this, color);
10254 * Ext JS Library 1.1.1
10255 * Copyright(c) 2006-2007, Ext JS, LLC.
10257 * Originally Released Under LGPL - original licence link has changed is not relivant.
10260 * <script type="text/javascript">
10264 * @class Roo.DatePicker
10265 * @extends Roo.Component
10266 * Simple date picker class.
10268 * Create a new DatePicker
10269 * @param {Object} config The config object
10271 Roo.DatePicker = function(config){
10272 Roo.DatePicker.superclass.constructor.call(this, config);
10274 this.value = config && config.value ?
10275 config.value.clearTime() : new Date().clearTime();
10280 * Fires when a date is selected
10281 * @param {DatePicker} this
10282 * @param {Date} date The selected date
10286 * @event monthchange
10287 * Fires when the displayed month changes
10288 * @param {DatePicker} this
10289 * @param {Date} date The selected month
10291 'monthchange': true
10295 this.on("select", this.handler, this.scope || this);
10297 // build the disabledDatesRE
10298 if(!this.disabledDatesRE && this.disabledDates){
10299 var dd = this.disabledDates;
10301 for(var i = 0; i < dd.length; i++){
10303 if(i != dd.length-1) re += "|";
10305 this.disabledDatesRE = new RegExp(re + ")");
10309 Roo.extend(Roo.DatePicker, Roo.Component, {
10311 * @cfg {String} todayText
10312 * The text to display on the button that selects the current date (defaults to "Today")
10314 todayText : "Today",
10316 * @cfg {String} okText
10317 * The text to display on the ok button
10319 okText : " OK ", //   to give the user extra clicking room
10321 * @cfg {String} cancelText
10322 * The text to display on the cancel button
10324 cancelText : "Cancel",
10326 * @cfg {String} todayTip
10327 * The tooltip to display for the button that selects the current date (defaults to "{current date} (Spacebar)")
10329 todayTip : "{0} (Spacebar)",
10331 * @cfg {Date} minDate
10332 * Minimum allowable date (JavaScript date object, defaults to null)
10336 * @cfg {Date} maxDate
10337 * Maximum allowable date (JavaScript date object, defaults to null)
10341 * @cfg {String} minText
10342 * The error text to display if the minDate validation fails (defaults to "This date is before the minimum date")
10344 minText : "This date is before the minimum date",
10346 * @cfg {String} maxText
10347 * The error text to display if the maxDate validation fails (defaults to "This date is after the maximum date")
10349 maxText : "This date is after the maximum date",
10351 * @cfg {String} format
10352 * The default date format string which can be overriden for localization support. The format must be
10353 * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
10357 * @cfg {Array} disabledDays
10358 * An array of days to disable, 0-based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
10360 disabledDays : null,
10362 * @cfg {String} disabledDaysText
10363 * The tooltip to display when the date falls on a disabled day (defaults to "")
10365 disabledDaysText : "",
10367 * @cfg {RegExp} disabledDatesRE
10368 * JavaScript regular expression used to disable a pattern of dates (defaults to null)
10370 disabledDatesRE : null,
10372 * @cfg {String} disabledDatesText
10373 * The tooltip text to display when the date falls on a disabled date (defaults to "")
10375 disabledDatesText : "",
10377 * @cfg {Boolean} constrainToViewport
10378 * True to constrain the date picker to the viewport (defaults to true)
10380 constrainToViewport : true,
10382 * @cfg {Array} monthNames
10383 * An array of textual month names which can be overriden for localization support (defaults to Date.monthNames)
10385 monthNames : Date.monthNames,
10387 * @cfg {Array} dayNames
10388 * An array of textual day names which can be overriden for localization support (defaults to Date.dayNames)
10390 dayNames : Date.dayNames,
10392 * @cfg {String} nextText
10393 * The next month navigation button tooltip (defaults to 'Next Month (Control+Right)')
10395 nextText: 'Next Month (Control+Right)',
10397 * @cfg {String} prevText
10398 * The previous month navigation button tooltip (defaults to 'Previous Month (Control+Left)')
10400 prevText: 'Previous Month (Control+Left)',
10402 * @cfg {String} monthYearText
10403 * The header month selector tooltip (defaults to 'Choose a month (Control+Up/Down to move years)')
10405 monthYearText: 'Choose a month (Control+Up/Down to move years)',
10407 * @cfg {Number} startDay
10408 * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
10412 * @cfg {Bool} showClear
10413 * Show a clear button (usefull for date form elements that can be blank.)
10419 * Sets the value of the date field
10420 * @param {Date} value The date to set
10422 setValue : function(value){
10423 var old = this.value;
10425 if (typeof(value) == 'string') {
10427 value = Date.parseDate(value, this.format);
10430 value = new Date();
10433 this.value = value.clearTime(true);
10435 this.update(this.value);
10440 * Gets the current selected value of the date field
10441 * @return {Date} The selected date
10443 getValue : function(){
10448 focus : function(){
10450 this.update(this.activeDate);
10455 onRender : function(container, position){
10458 '<table cellspacing="0">',
10459 '<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>',
10460 '<tr><td colspan="3"><table class="x-date-inner" cellspacing="0"><thead><tr>'];
10461 var dn = this.dayNames;
10462 for(var i = 0; i < 7; i++){
10463 var d = this.startDay+i;
10467 m.push("<th><span>", dn[d].substr(0,1), "</span></th>");
10469 m[m.length] = "</tr></thead><tbody><tr>";
10470 for(var i = 0; i < 42; i++) {
10471 if(i % 7 == 0 && i != 0){
10472 m[m.length] = "</tr><tr>";
10474 m[m.length] = '<td><a href="#" hidefocus="on" class="x-date-date" tabIndex="1"><em><span></span></em></a></td>';
10476 m[m.length] = '</tr></tbody></table></td></tr><tr>'+
10477 '<td colspan="3" class="x-date-bottom" align="center"></td></tr></table><div class="x-date-mp"></div>';
10479 var el = document.createElement("div");
10480 el.className = "x-date-picker";
10481 el.innerHTML = m.join("");
10483 container.dom.insertBefore(el, position);
10485 this.el = Roo.get(el);
10486 this.eventEl = Roo.get(el.firstChild);
10488 new Roo.util.ClickRepeater(this.el.child("td.x-date-left a"), {
10489 handler: this.showPrevMonth,
10491 preventDefault:true,
10495 new Roo.util.ClickRepeater(this.el.child("td.x-date-right a"), {
10496 handler: this.showNextMonth,
10498 preventDefault:true,
10502 this.eventEl.on("mousewheel", this.handleMouseWheel, this);
10504 this.monthPicker = this.el.down('div.x-date-mp');
10505 this.monthPicker.enableDisplayMode('block');
10507 var kn = new Roo.KeyNav(this.eventEl, {
10508 "left" : function(e){
10510 this.showPrevMonth() :
10511 this.update(this.activeDate.add("d", -1));
10514 "right" : function(e){
10516 this.showNextMonth() :
10517 this.update(this.activeDate.add("d", 1));
10520 "up" : function(e){
10522 this.showNextYear() :
10523 this.update(this.activeDate.add("d", -7));
10526 "down" : function(e){
10528 this.showPrevYear() :
10529 this.update(this.activeDate.add("d", 7));
10532 "pageUp" : function(e){
10533 this.showNextMonth();
10536 "pageDown" : function(e){
10537 this.showPrevMonth();
10540 "enter" : function(e){
10541 e.stopPropagation();
10548 this.eventEl.on("click", this.handleDateClick, this, {delegate: "a.x-date-date"});
10550 this.eventEl.addKeyListener(Roo.EventObject.SPACE, this.selectToday, this);
10552 this.el.unselectable();
10554 this.cells = this.el.select("table.x-date-inner tbody td");
10555 this.textNodes = this.el.query("table.x-date-inner tbody span");
10557 this.mbtn = new Roo.Button(this.el.child("td.x-date-middle", true), {
10559 tooltip: this.monthYearText
10562 this.mbtn.on('click', this.showMonthPicker, this);
10563 this.mbtn.el.child(this.mbtn.menuClassTarget).addClass("x-btn-with-menu");
10566 var today = (new Date()).dateFormat(this.format);
10568 var baseTb = new Roo.Toolbar(this.el.child("td.x-date-bottom", true));
10569 if (this.showClear) {
10570 baseTb.add( new Roo.Toolbar.Fill());
10573 text: String.format(this.todayText, today),
10574 tooltip: String.format(this.todayTip, today),
10575 handler: this.selectToday,
10579 //var todayBtn = new Roo.Button(this.el.child("td.x-date-bottom", true), {
10582 if (this.showClear) {
10584 baseTb.add( new Roo.Toolbar.Fill());
10587 cls: 'x-btn-icon x-btn-clear',
10588 handler: function() {
10590 this.fireEvent("select", this, '');
10600 this.update(this.value);
10603 createMonthPicker : function(){
10604 if(!this.monthPicker.dom.firstChild){
10605 var buf = ['<table border="0" cellspacing="0">'];
10606 for(var i = 0; i < 6; i++){
10608 '<tr><td class="x-date-mp-month"><a href="#">', this.monthNames[i].substr(0, 3), '</a></td>',
10609 '<td class="x-date-mp-month x-date-mp-sep"><a href="#">', this.monthNames[i+6].substr(0, 3), '</a></td>',
10611 '<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>' :
10612 '<td class="x-date-mp-year"><a href="#"></a></td><td class="x-date-mp-year"><a href="#"></a></td></tr>'
10616 '<tr class="x-date-mp-btns"><td colspan="4"><button type="button" class="x-date-mp-ok">',
10618 '</button><button type="button" class="x-date-mp-cancel">',
10620 '</button></td></tr>',
10623 this.monthPicker.update(buf.join(''));
10624 this.monthPicker.on('click', this.onMonthClick, this);
10625 this.monthPicker.on('dblclick', this.onMonthDblClick, this);
10627 this.mpMonths = this.monthPicker.select('td.x-date-mp-month');
10628 this.mpYears = this.monthPicker.select('td.x-date-mp-year');
10630 this.mpMonths.each(function(m, a, i){
10633 m.dom.xmonth = 5 + Math.round(i * .5);
10635 m.dom.xmonth = Math.round((i-1) * .5);
10641 showMonthPicker : function(){
10642 this.createMonthPicker();
10643 var size = this.el.getSize();
10644 this.monthPicker.setSize(size);
10645 this.monthPicker.child('table').setSize(size);
10647 this.mpSelMonth = (this.activeDate || this.value).getMonth();
10648 this.updateMPMonth(this.mpSelMonth);
10649 this.mpSelYear = (this.activeDate || this.value).getFullYear();
10650 this.updateMPYear(this.mpSelYear);
10652 this.monthPicker.slideIn('t', {duration:.2});
10655 updateMPYear : function(y){
10657 var ys = this.mpYears.elements;
10658 for(var i = 1; i <= 10; i++){
10659 var td = ys[i-1], y2;
10661 y2 = y + Math.round(i * .5);
10662 td.firstChild.innerHTML = y2;
10665 y2 = y - (5-Math.round(i * .5));
10666 td.firstChild.innerHTML = y2;
10669 this.mpYears.item(i-1)[y2 == this.mpSelYear ? 'addClass' : 'removeClass']('x-date-mp-sel');
10673 updateMPMonth : function(sm){
10674 this.mpMonths.each(function(m, a, i){
10675 m[m.dom.xmonth == sm ? 'addClass' : 'removeClass']('x-date-mp-sel');
10679 selectMPMonth: function(m){
10683 onMonthClick : function(e, t){
10685 var el = new Roo.Element(t), pn;
10686 if(el.is('button.x-date-mp-cancel')){
10687 this.hideMonthPicker();
10689 else if(el.is('button.x-date-mp-ok')){
10690 this.update(new Date(this.mpSelYear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
10691 this.hideMonthPicker();
10693 else if(pn = el.up('td.x-date-mp-month', 2)){
10694 this.mpMonths.removeClass('x-date-mp-sel');
10695 pn.addClass('x-date-mp-sel');
10696 this.mpSelMonth = pn.dom.xmonth;
10698 else if(pn = el.up('td.x-date-mp-year', 2)){
10699 this.mpYears.removeClass('x-date-mp-sel');
10700 pn.addClass('x-date-mp-sel');
10701 this.mpSelYear = pn.dom.xyear;
10703 else if(el.is('a.x-date-mp-prev')){
10704 this.updateMPYear(this.mpyear-10);
10706 else if(el.is('a.x-date-mp-next')){
10707 this.updateMPYear(this.mpyear+10);
10711 onMonthDblClick : function(e, t){
10713 var el = new Roo.Element(t), pn;
10714 if(pn = el.up('td.x-date-mp-month', 2)){
10715 this.update(new Date(this.mpSelYear, pn.dom.xmonth, (this.activeDate || this.value).getDate()));
10716 this.hideMonthPicker();
10718 else if(pn = el.up('td.x-date-mp-year', 2)){
10719 this.update(new Date(pn.dom.xyear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
10720 this.hideMonthPicker();
10724 hideMonthPicker : function(disableAnim){
10725 if(this.monthPicker){
10726 if(disableAnim === true){
10727 this.monthPicker.hide();
10729 this.monthPicker.slideOut('t', {duration:.2});
10735 showPrevMonth : function(e){
10736 this.update(this.activeDate.add("mo", -1));
10740 showNextMonth : function(e){
10741 this.update(this.activeDate.add("mo", 1));
10745 showPrevYear : function(){
10746 this.update(this.activeDate.add("y", -1));
10750 showNextYear : function(){
10751 this.update(this.activeDate.add("y", 1));
10755 handleMouseWheel : function(e){
10756 var delta = e.getWheelDelta();
10758 this.showPrevMonth();
10760 } else if(delta < 0){
10761 this.showNextMonth();
10767 handleDateClick : function(e, t){
10769 if(t.dateValue && !Roo.fly(t.parentNode).hasClass("x-date-disabled")){
10770 this.setValue(new Date(t.dateValue));
10771 this.fireEvent("select", this, this.value);
10776 selectToday : function(){
10777 this.setValue(new Date().clearTime());
10778 this.fireEvent("select", this, this.value);
10782 update : function(date)
10784 var vd = this.activeDate;
10785 this.activeDate = date;
10787 var t = date.getTime();
10788 if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
10789 this.cells.removeClass("x-date-selected");
10790 this.cells.each(function(c){
10791 if(c.dom.firstChild.dateValue == t){
10792 c.addClass("x-date-selected");
10793 setTimeout(function(){
10794 try{c.dom.firstChild.focus();}catch(e){}
10803 var days = date.getDaysInMonth();
10804 var firstOfMonth = date.getFirstDateOfMonth();
10805 var startingPos = firstOfMonth.getDay()-this.startDay;
10807 if(startingPos <= this.startDay){
10811 var pm = date.add("mo", -1);
10812 var prevStart = pm.getDaysInMonth()-startingPos;
10814 var cells = this.cells.elements;
10815 var textEls = this.textNodes;
10816 days += startingPos;
10818 // convert everything to numbers so it's fast
10819 var day = 86400000;
10820 var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
10821 var today = new Date().clearTime().getTime();
10822 var sel = date.clearTime().getTime();
10823 var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
10824 var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
10825 var ddMatch = this.disabledDatesRE;
10826 var ddText = this.disabledDatesText;
10827 var ddays = this.disabledDays ? this.disabledDays.join("") : false;
10828 var ddaysText = this.disabledDaysText;
10829 var format = this.format;
10831 var setCellClass = function(cal, cell){
10833 var t = d.getTime();
10834 cell.firstChild.dateValue = t;
10836 cell.className += " x-date-today";
10837 cell.title = cal.todayText;
10840 cell.className += " x-date-selected";
10841 setTimeout(function(){
10842 try{cell.firstChild.focus();}catch(e){}
10847 cell.className = " x-date-disabled";
10848 cell.title = cal.minText;
10852 cell.className = " x-date-disabled";
10853 cell.title = cal.maxText;
10857 if(ddays.indexOf(d.getDay()) != -1){
10858 cell.title = ddaysText;
10859 cell.className = " x-date-disabled";
10862 if(ddMatch && format){
10863 var fvalue = d.dateFormat(format);
10864 if(ddMatch.test(fvalue)){
10865 cell.title = ddText.replace("%0", fvalue);
10866 cell.className = " x-date-disabled";
10872 for(; i < startingPos; i++) {
10873 textEls[i].innerHTML = (++prevStart);
10874 d.setDate(d.getDate()+1);
10875 cells[i].className = "x-date-prevday";
10876 setCellClass(this, cells[i]);
10878 for(; i < days; i++){
10879 intDay = i - startingPos + 1;
10880 textEls[i].innerHTML = (intDay);
10881 d.setDate(d.getDate()+1);
10882 cells[i].className = "x-date-active";
10883 setCellClass(this, cells[i]);
10886 for(; i < 42; i++) {
10887 textEls[i].innerHTML = (++extraDays);
10888 d.setDate(d.getDate()+1);
10889 cells[i].className = "x-date-nextday";
10890 setCellClass(this, cells[i]);
10893 this.mbtn.setText(this.monthNames[date.getMonth()] + " " + date.getFullYear());
10894 this.fireEvent('monthchange', this, date);
10896 if(!this.internalRender){
10897 var main = this.el.dom.firstChild;
10898 var w = main.offsetWidth;
10899 this.el.setWidth(w + this.el.getBorderWidth("lr"));
10900 Roo.fly(main).setWidth(w);
10901 this.internalRender = true;
10902 // opera does not respect the auto grow header center column
10903 // then, after it gets a width opera refuses to recalculate
10904 // without a second pass
10905 if(Roo.isOpera && !this.secondPass){
10906 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
10907 this.secondPass = true;
10908 this.update.defer(10, this, [date]);
10916 * Ext JS Library 1.1.1
10917 * Copyright(c) 2006-2007, Ext JS, LLC.
10919 * Originally Released Under LGPL - original licence link has changed is not relivant.
10922 * <script type="text/javascript">
10925 * @class Roo.TabPanel
10926 * @extends Roo.util.Observable
10927 * A lightweight tab container.
10931 // basic tabs 1, built from existing content
10932 var tabs = new Roo.TabPanel("tabs1");
10933 tabs.addTab("script", "View Script");
10934 tabs.addTab("markup", "View Markup");
10935 tabs.activate("script");
10937 // more advanced tabs, built from javascript
10938 var jtabs = new Roo.TabPanel("jtabs");
10939 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
10941 // set up the UpdateManager
10942 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
10943 var updater = tab2.getUpdateManager();
10944 updater.setDefaultUrl("ajax1.htm");
10945 tab2.on('activate', updater.refresh, updater, true);
10947 // Use setUrl for Ajax loading
10948 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
10949 tab3.setUrl("ajax2.htm", null, true);
10952 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
10955 jtabs.activate("jtabs-1");
10958 * Create a new TabPanel.
10959 * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
10960 * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
10962 Roo.TabPanel = function(container, config){
10964 * The container element for this TabPanel.
10965 * @type Roo.Element
10967 this.el = Roo.get(container, true);
10969 if(typeof config == "boolean"){
10970 this.tabPosition = config ? "bottom" : "top";
10972 Roo.apply(this, config);
10975 if(this.tabPosition == "bottom"){
10976 this.bodyEl = Roo.get(this.createBody(this.el.dom));
10977 this.el.addClass("x-tabs-bottom");
10979 this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
10980 this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
10981 this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
10983 Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
10985 if(this.tabPosition != "bottom"){
10986 /** The body element that contains {@link Roo.TabPanelItem} bodies. +
10987 * @type Roo.Element
10989 this.bodyEl = Roo.get(this.createBody(this.el.dom));
10990 this.el.addClass("x-tabs-top");
10994 this.bodyEl.setStyle("position", "relative");
10996 this.active = null;
10997 this.activateDelegate = this.activate.createDelegate(this);
11002 * Fires when the active tab changes
11003 * @param {Roo.TabPanel} this
11004 * @param {Roo.TabPanelItem} activePanel The new active tab
11008 * @event beforetabchange
11009 * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
11010 * @param {Roo.TabPanel} this
11011 * @param {Object} e Set cancel to true on this object to cancel the tab change
11012 * @param {Roo.TabPanelItem} tab The tab being changed to
11014 "beforetabchange" : true
11017 Roo.EventManager.onWindowResize(this.onResize, this);
11018 this.cpad = this.el.getPadding("lr");
11019 this.hiddenCount = 0;
11022 // toolbar on the tabbar support...
11023 if (this.toolbar) {
11024 var tcfg = this.toolbar;
11025 tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');
11026 this.toolbar = new Roo.Toolbar(tcfg);
11027 if (Roo.isSafari) {
11028 var tbl = tcfg.container.child('table', true);
11029 tbl.setAttribute('width', '100%');
11036 Roo.TabPanel.superclass.constructor.call(this);
11039 Roo.extend(Roo.TabPanel, Roo.util.Observable, {
11041 *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
11043 tabPosition : "top",
11045 *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
11047 currentTabWidth : 0,
11049 *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
11053 *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
11057 *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
11059 preferredTabWidth : 175,
11061 *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
11063 resizeTabs : false,
11065 *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
11067 monitorResize : true,
11069 *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar.
11074 * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
11075 * @param {String} id The id of the div to use <b>or create</b>
11076 * @param {String} text The text for the tab
11077 * @param {String} content (optional) Content to put in the TabPanelItem body
11078 * @param {Boolean} closable (optional) True to create a close icon on the tab
11079 * @return {Roo.TabPanelItem} The created TabPanelItem
11081 addTab : function(id, text, content, closable){
11082 var item = new Roo.TabPanelItem(this, id, text, closable);
11083 this.addTabItem(item);
11085 item.setContent(content);
11091 * Returns the {@link Roo.TabPanelItem} with the specified id/index
11092 * @param {String/Number} id The id or index of the TabPanelItem to fetch.
11093 * @return {Roo.TabPanelItem}
11095 getTab : function(id){
11096 return this.items[id];
11100 * Hides the {@link Roo.TabPanelItem} with the specified id/index
11101 * @param {String/Number} id The id or index of the TabPanelItem to hide.
11103 hideTab : function(id){
11104 var t = this.items[id];
11107 this.hiddenCount++;
11108 this.autoSizeTabs();
11113 * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
11114 * @param {String/Number} id The id or index of the TabPanelItem to unhide.
11116 unhideTab : function(id){
11117 var t = this.items[id];
11119 t.setHidden(false);
11120 this.hiddenCount--;
11121 this.autoSizeTabs();
11126 * Adds an existing {@link Roo.TabPanelItem}.
11127 * @param {Roo.TabPanelItem} item The TabPanelItem to add
11129 addTabItem : function(item){
11130 this.items[item.id] = item;
11131 this.items.push(item);
11132 if(this.resizeTabs){
11133 item.setWidth(this.currentTabWidth || this.preferredTabWidth);
11134 this.autoSizeTabs();
11141 * Removes a {@link Roo.TabPanelItem}.
11142 * @param {String/Number} id The id or index of the TabPanelItem to remove.
11144 removeTab : function(id){
11145 var items = this.items;
11146 var tab = items[id];
11147 if(!tab) { return; }
11148 var index = items.indexOf(tab);
11149 if(this.active == tab && items.length > 1){
11150 var newTab = this.getNextAvailable(index);
11155 this.stripEl.dom.removeChild(tab.pnode.dom);
11156 if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
11157 this.bodyEl.dom.removeChild(tab.bodyEl.dom);
11159 items.splice(index, 1);
11160 delete this.items[tab.id];
11161 tab.fireEvent("close", tab);
11162 tab.purgeListeners();
11163 this.autoSizeTabs();
11166 getNextAvailable : function(start){
11167 var items = this.items;
11169 // look for a next tab that will slide over to
11170 // replace the one being removed
11171 while(index < items.length){
11172 var item = items[++index];
11173 if(item && !item.isHidden()){
11177 // if one isn't found select the previous tab (on the left)
11180 var item = items[--index];
11181 if(item && !item.isHidden()){
11189 * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
11190 * @param {String/Number} id The id or index of the TabPanelItem to disable.
11192 disableTab : function(id){
11193 var tab = this.items[id];
11194 if(tab && this.active != tab){
11200 * Enables a {@link Roo.TabPanelItem} that is disabled.
11201 * @param {String/Number} id The id or index of the TabPanelItem to enable.
11203 enableTab : function(id){
11204 var tab = this.items[id];
11209 * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
11210 * @param {String/Number} id The id or index of the TabPanelItem to activate.
11211 * @return {Roo.TabPanelItem} The TabPanelItem.
11213 activate : function(id){
11214 var tab = this.items[id];
11218 if(tab == this.active || tab.disabled){
11222 this.fireEvent("beforetabchange", this, e, tab);
11223 if(e.cancel !== true && !tab.disabled){
11225 this.active.hide();
11227 this.active = this.items[id];
11228 this.active.show();
11229 this.fireEvent("tabchange", this, this.active);
11235 * Gets the active {@link Roo.TabPanelItem}.
11236 * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
11238 getActiveTab : function(){
11239 return this.active;
11243 * Updates the tab body element to fit the height of the container element
11244 * for overflow scrolling
11245 * @param {Number} targetHeight (optional) Override the starting height from the elements height
11247 syncHeight : function(targetHeight){
11248 var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
11249 var bm = this.bodyEl.getMargins();
11250 var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
11251 this.bodyEl.setHeight(newHeight);
11255 onResize : function(){
11256 if(this.monitorResize){
11257 this.autoSizeTabs();
11262 * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
11264 beginUpdate : function(){
11265 this.updating = true;
11269 * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
11271 endUpdate : function(){
11272 this.updating = false;
11273 this.autoSizeTabs();
11277 * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
11279 autoSizeTabs : function(){
11280 var count = this.items.length;
11281 var vcount = count - this.hiddenCount;
11282 if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) return;
11283 var w = Math.max(this.el.getWidth() - this.cpad, 10);
11284 var availWidth = Math.floor(w / vcount);
11285 var b = this.stripBody;
11286 if(b.getWidth() > w){
11287 var tabs = this.items;
11288 this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
11289 if(availWidth < this.minTabWidth){
11290 /*if(!this.sleft){ // incomplete scrolling code
11291 this.createScrollButtons();
11294 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
11297 if(this.currentTabWidth < this.preferredTabWidth){
11298 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
11304 * Returns the number of tabs in this TabPanel.
11307 getCount : function(){
11308 return this.items.length;
11312 * Resizes all the tabs to the passed width
11313 * @param {Number} The new width
11315 setTabWidth : function(width){
11316 this.currentTabWidth = width;
11317 for(var i = 0, len = this.items.length; i < len; i++) {
11318 if(!this.items[i].isHidden())this.items[i].setWidth(width);
11323 * Destroys this TabPanel
11324 * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
11326 destroy : function(removeEl){
11327 Roo.EventManager.removeResizeListener(this.onResize, this);
11328 for(var i = 0, len = this.items.length; i < len; i++){
11329 this.items[i].purgeListeners();
11331 if(removeEl === true){
11332 this.el.update("");
11339 * @class Roo.TabPanelItem
11340 * @extends Roo.util.Observable
11341 * Represents an individual item (tab plus body) in a TabPanel.
11342 * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
11343 * @param {String} id The id of this TabPanelItem
11344 * @param {String} text The text for the tab of this TabPanelItem
11345 * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
11347 Roo.TabPanelItem = function(tabPanel, id, text, closable){
11349 * The {@link Roo.TabPanel} this TabPanelItem belongs to
11350 * @type Roo.TabPanel
11352 this.tabPanel = tabPanel;
11354 * The id for this TabPanelItem
11359 this.disabled = false;
11363 this.loaded = false;
11364 this.closable = closable;
11367 * The body element for this TabPanelItem.
11368 * @type Roo.Element
11370 this.bodyEl = Roo.get(tabPanel.createItemBody(tabPanel.bodyEl.dom, id));
11371 this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
11372 this.bodyEl.setStyle("display", "block");
11373 this.bodyEl.setStyle("zoom", "1");
11376 var els = tabPanel.createStripElements(tabPanel.stripEl.dom, text, closable);
11378 this.el = Roo.get(els.el, true);
11379 this.inner = Roo.get(els.inner, true);
11380 this.textEl = Roo.get(this.el.dom.firstChild.firstChild.firstChild, true);
11381 this.pnode = Roo.get(els.el.parentNode, true);
11382 this.el.on("mousedown", this.onTabMouseDown, this);
11383 this.el.on("click", this.onTabClick, this);
11386 var c = Roo.get(els.close, true);
11387 c.dom.title = this.closeText;
11388 c.addClassOnOver("close-over");
11389 c.on("click", this.closeClick, this);
11395 * Fires when this tab becomes the active tab.
11396 * @param {Roo.TabPanel} tabPanel The parent TabPanel
11397 * @param {Roo.TabPanelItem} this
11401 * @event beforeclose
11402 * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
11403 * @param {Roo.TabPanelItem} this
11404 * @param {Object} e Set cancel to true on this object to cancel the close.
11406 "beforeclose": true,
11409 * Fires when this tab is closed.
11410 * @param {Roo.TabPanelItem} this
11414 * @event deactivate
11415 * Fires when this tab is no longer the active tab.
11416 * @param {Roo.TabPanel} tabPanel The parent TabPanel
11417 * @param {Roo.TabPanelItem} this
11419 "deactivate" : true
11421 this.hidden = false;
11423 Roo.TabPanelItem.superclass.constructor.call(this);
11426 Roo.extend(Roo.TabPanelItem, Roo.util.Observable, {
11427 purgeListeners : function(){
11428 Roo.util.Observable.prototype.purgeListeners.call(this);
11429 this.el.removeAllListeners();
11432 * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
11435 this.pnode.addClass("on");
11438 this.tabPanel.stripWrap.repaint();
11440 this.fireEvent("activate", this.tabPanel, this);
11444 * Returns true if this tab is the active tab.
11445 * @return {Boolean}
11447 isActive : function(){
11448 return this.tabPanel.getActiveTab() == this;
11452 * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
11455 this.pnode.removeClass("on");
11457 this.fireEvent("deactivate", this.tabPanel, this);
11460 hideAction : function(){
11461 this.bodyEl.hide();
11462 this.bodyEl.setStyle("position", "absolute");
11463 this.bodyEl.setLeft("-20000px");
11464 this.bodyEl.setTop("-20000px");
11467 showAction : function(){
11468 this.bodyEl.setStyle("position", "relative");
11469 this.bodyEl.setTop("");
11470 this.bodyEl.setLeft("");
11471 this.bodyEl.show();
11475 * Set the tooltip for the tab.
11476 * @param {String} tooltip The tab's tooltip
11478 setTooltip : function(text){
11479 if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
11480 this.textEl.dom.qtip = text;
11481 this.textEl.dom.removeAttribute('title');
11483 this.textEl.dom.title = text;
11487 onTabClick : function(e){
11488 e.preventDefault();
11489 this.tabPanel.activate(this.id);
11492 onTabMouseDown : function(e){
11493 e.preventDefault();
11494 this.tabPanel.activate(this.id);
11497 getWidth : function(){
11498 return this.inner.getWidth();
11501 setWidth : function(width){
11502 var iwidth = width - this.pnode.getPadding("lr");
11503 this.inner.setWidth(iwidth);
11504 this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
11505 this.pnode.setWidth(width);
11509 * Show or hide the tab
11510 * @param {Boolean} hidden True to hide or false to show.
11512 setHidden : function(hidden){
11513 this.hidden = hidden;
11514 this.pnode.setStyle("display", hidden ? "none" : "");
11518 * Returns true if this tab is "hidden"
11519 * @return {Boolean}
11521 isHidden : function(){
11522 return this.hidden;
11526 * Returns the text for this tab
11529 getText : function(){
11533 autoSize : function(){
11534 //this.el.beginMeasure();
11535 this.textEl.setWidth(1);
11536 this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr"));
11537 //this.el.endMeasure();
11541 * Sets the text for the tab (Note: this also sets the tooltip text)
11542 * @param {String} text The tab's text and tooltip
11544 setText : function(text){
11546 this.textEl.update(text);
11547 this.setTooltip(text);
11548 if(!this.tabPanel.resizeTabs){
11553 * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
11555 activate : function(){
11556 this.tabPanel.activate(this.id);
11560 * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
11562 disable : function(){
11563 if(this.tabPanel.active != this){
11564 this.disabled = true;
11565 this.pnode.addClass("disabled");
11570 * Enables this TabPanelItem if it was previously disabled.
11572 enable : function(){
11573 this.disabled = false;
11574 this.pnode.removeClass("disabled");
11578 * Sets the content for this TabPanelItem.
11579 * @param {String} content The content
11580 * @param {Boolean} loadScripts true to look for and load scripts
11582 setContent : function(content, loadScripts){
11583 this.bodyEl.update(content, loadScripts);
11587 * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
11588 * @return {Roo.UpdateManager} The UpdateManager
11590 getUpdateManager : function(){
11591 return this.bodyEl.getUpdateManager();
11595 * Set a URL to be used to load the content for this TabPanelItem.
11596 * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
11597 * @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)
11598 * @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)
11599 * @return {Roo.UpdateManager} The UpdateManager
11601 setUrl : function(url, params, loadOnce){
11602 if(this.refreshDelegate){
11603 this.un('activate', this.refreshDelegate);
11605 this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
11606 this.on("activate", this.refreshDelegate);
11607 return this.bodyEl.getUpdateManager();
11611 _handleRefresh : function(url, params, loadOnce){
11612 if(!loadOnce || !this.loaded){
11613 var updater = this.bodyEl.getUpdateManager();
11614 updater.update(url, params, this._setLoaded.createDelegate(this));
11619 * Forces a content refresh from the URL specified in the {@link #setUrl} method.
11620 * Will fail silently if the setUrl method has not been called.
11621 * This does not activate the panel, just updates its content.
11623 refresh : function(){
11624 if(this.refreshDelegate){
11625 this.loaded = false;
11626 this.refreshDelegate();
11631 _setLoaded : function(){
11632 this.loaded = true;
11636 closeClick : function(e){
11639 this.fireEvent("beforeclose", this, o);
11640 if(o.cancel !== true){
11641 this.tabPanel.removeTab(this.id);
11645 * The text displayed in the tooltip for the close icon.
11648 closeText : "Close this tab"
11652 Roo.TabPanel.prototype.createStrip = function(container){
11653 var strip = document.createElement("div");
11654 strip.className = "x-tabs-wrap";
11655 container.appendChild(strip);
11659 Roo.TabPanel.prototype.createStripList = function(strip){
11660 // div wrapper for retard IE
11661 // returns the "tr" element.
11662 strip.innerHTML = '<div class="x-tabs-strip-wrap">'+
11663 '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
11664 '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
11665 return strip.firstChild.firstChild.firstChild.firstChild;
11668 Roo.TabPanel.prototype.createBody = function(container){
11669 var body = document.createElement("div");
11670 Roo.id(body, "tab-body");
11671 Roo.fly(body).addClass("x-tabs-body");
11672 container.appendChild(body);
11676 Roo.TabPanel.prototype.createItemBody = function(bodyEl, id){
11677 var body = Roo.getDom(id);
11679 body = document.createElement("div");
11682 Roo.fly(body).addClass("x-tabs-item-body");
11683 bodyEl.insertBefore(body, bodyEl.firstChild);
11687 Roo.TabPanel.prototype.createStripElements = function(stripEl, text, closable){
11688 var td = document.createElement("td");
11689 stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
11690 //stripEl.appendChild(td);
11692 td.className = "x-tabs-closable";
11693 if(!this.closeTpl){
11694 this.closeTpl = new Roo.Template(
11695 '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
11696 '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
11697 '<div unselectable="on" class="close-icon"> </div></em></span></a>'
11700 var el = this.closeTpl.overwrite(td, {"text": text});
11701 var close = el.getElementsByTagName("div")[0];
11702 var inner = el.getElementsByTagName("em")[0];
11703 return {"el": el, "close": close, "inner": inner};
11706 this.tabTpl = new Roo.Template(
11707 '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
11708 '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
11711 var el = this.tabTpl.overwrite(td, {"text": text});
11712 var inner = el.getElementsByTagName("em")[0];
11713 return {"el": el, "inner": inner};
11717 * Ext JS Library 1.1.1
11718 * Copyright(c) 2006-2007, Ext JS, LLC.
11720 * Originally Released Under LGPL - original licence link has changed is not relivant.
11723 * <script type="text/javascript">
11727 * @class Roo.Button
11728 * @extends Roo.util.Observable
11729 * Simple Button class
11730 * @cfg {String} text The button text
11731 * @cfg {String} icon The path to an image to display in the button (the image will be set as the background-image
11732 * CSS property of the button by default, so if you want a mixed icon/text button, set cls:"x-btn-text-icon")
11733 * @cfg {Function} handler A function called when the button is clicked (can be used instead of click event)
11734 * @cfg {Object} scope The scope of the handler
11735 * @cfg {Number} minWidth The minimum width for this button (used to give a set of buttons a common width)
11736 * @cfg {String/Object} tooltip The tooltip for the button - can be a string or QuickTips config object
11737 * @cfg {Boolean} hidden True to start hidden (defaults to false)
11738 * @cfg {Boolean} disabled True to start disabled (defaults to false)
11739 * @cfg {Boolean} pressed True to start pressed (only if enableToggle = true)
11740 * @cfg {String} toggleGroup The group this toggle button is a member of (only 1 per group can be pressed, only
11741 applies if enableToggle = true)
11742 * @cfg {String/HTMLElement/Element} renderTo The element to append the button to
11743 * @cfg {Boolean/Object} repeat True to repeat fire the click event while the mouse is down. This can also be
11744 an {@link Roo.util.ClickRepeater} config object (defaults to false).
11746 * Create a new button
11747 * @param {Object} config The config object
11749 Roo.Button = function(renderTo, config)
11753 renderTo = config.renderTo || false;
11756 Roo.apply(this, config);
11760 * Fires when this button is clicked
11761 * @param {Button} this
11762 * @param {EventObject} e The click event
11767 * Fires when the "pressed" state of this button changes (only if enableToggle = true)
11768 * @param {Button} this
11769 * @param {Boolean} pressed
11774 * Fires when the mouse hovers over the button
11775 * @param {Button} this
11776 * @param {Event} e The event object
11778 'mouseover' : true,
11781 * Fires when the mouse exits the button
11782 * @param {Button} this
11783 * @param {Event} e The event object
11788 * Fires when the button is rendered
11789 * @param {Button} this
11794 this.menu = Roo.menu.MenuMgr.get(this.menu);
11796 // register listeners first!! - so render can be captured..
11797 Roo.util.Observable.call(this);
11799 this.render(renderTo);
11805 Roo.extend(Roo.Button, Roo.util.Observable, {
11811 * Read-only. True if this button is hidden
11816 * Read-only. True if this button is disabled
11821 * Read-only. True if this button is pressed (only if enableToggle = true)
11827 * @cfg {Number} tabIndex
11828 * The DOM tabIndex for this button (defaults to undefined)
11830 tabIndex : undefined,
11833 * @cfg {Boolean} enableToggle
11834 * True to enable pressed/not pressed toggling (defaults to false)
11836 enableToggle: false,
11838 * @cfg {Mixed} menu
11839 * Standard menu attribute consisting of a reference to a menu object, a menu id or a menu config blob (defaults to undefined).
11843 * @cfg {String} menuAlign
11844 * The position to align the menu to (see {@link Roo.Element#alignTo} for more details, defaults to 'tl-bl?').
11846 menuAlign : "tl-bl?",
11849 * @cfg {String} iconCls
11850 * A css class which sets a background image to be used as the icon for this button (defaults to undefined).
11852 iconCls : undefined,
11854 * @cfg {String} type
11855 * The button's type, corresponding to the DOM input element type attribute. Either "submit," "reset" or "button" (default).
11860 menuClassTarget: 'tr',
11863 * @cfg {String} clickEvent
11864 * The type of event to map to the button's event handler (defaults to 'click')
11866 clickEvent : 'click',
11869 * @cfg {Boolean} handleMouseEvents
11870 * False to disable visual cues on mouseover, mouseout and mousedown (defaults to true)
11872 handleMouseEvents : true,
11875 * @cfg {String} tooltipType
11876 * The type of tooltip to use. Either "qtip" (default) for QuickTips or "title" for title attribute.
11878 tooltipType : 'qtip',
11881 * @cfg {String} cls
11882 * A CSS class to apply to the button's main element.
11886 * @cfg {Roo.Template} template (Optional)
11887 * An {@link Roo.Template} with which to create the Button's main element. This Template must
11888 * contain numeric substitution parameter 0 if it is to display the tRoo property. Changing the template could
11889 * require code modifications if required elements (e.g. a button) aren't present.
11893 render : function(renderTo){
11895 if(this.hideParent){
11896 this.parentEl = Roo.get(renderTo);
11898 if(!this.dhconfig){
11899 if(!this.template){
11900 if(!Roo.Button.buttonTemplate){
11901 // hideous table template
11902 Roo.Button.buttonTemplate = new Roo.Template(
11903 '<table border="0" cellpadding="0" cellspacing="0" class="x-btn-wrap"><tbody><tr>',
11904 '<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>',
11905 "</tr></tbody></table>");
11907 this.template = Roo.Button.buttonTemplate;
11909 btn = this.template.append(renderTo, [this.text || ' ', this.type], true);
11910 var btnEl = btn.child("button:first");
11911 btnEl.on('focus', this.onFocus, this);
11912 btnEl.on('blur', this.onBlur, this);
11914 btn.addClass(this.cls);
11917 btnEl.setStyle('background-image', 'url(' +this.icon +')');
11920 btnEl.addClass(this.iconCls);
11922 btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
11925 if(this.tabIndex !== undefined){
11926 btnEl.dom.tabIndex = this.tabIndex;
11929 if(typeof this.tooltip == 'object'){
11930 Roo.QuickTips.tips(Roo.apply({
11934 btnEl.dom[this.tooltipType] = this.tooltip;
11938 btn = Roo.DomHelper.append(Roo.get(renderTo).dom, this.dhconfig, true);
11942 this.el.dom.id = this.el.id = this.id;
11945 this.el.child(this.menuClassTarget).addClass("x-btn-with-menu");
11946 this.menu.on("show", this.onMenuShow, this);
11947 this.menu.on("hide", this.onMenuHide, this);
11949 btn.addClass("x-btn");
11950 if(Roo.isIE && !Roo.isIE7){
11951 this.autoWidth.defer(1, this);
11955 if(this.handleMouseEvents){
11956 btn.on("mouseover", this.onMouseOver, this);
11957 btn.on("mouseout", this.onMouseOut, this);
11958 btn.on("mousedown", this.onMouseDown, this);
11960 btn.on(this.clickEvent, this.onClick, this);
11961 //btn.on("mouseup", this.onMouseUp, this);
11968 Roo.ButtonToggleMgr.register(this);
11970 this.el.addClass("x-btn-pressed");
11973 var repeater = new Roo.util.ClickRepeater(btn,
11974 typeof this.repeat == "object" ? this.repeat : {}
11976 repeater.on("click", this.onClick, this);
11979 this.fireEvent('render', this);
11983 * Returns the button's underlying element
11984 * @return {Roo.Element} The element
11986 getEl : function(){
11991 * Destroys this Button and removes any listeners.
11993 destroy : function(){
11994 Roo.ButtonToggleMgr.unregister(this);
11995 this.el.removeAllListeners();
11996 this.purgeListeners();
12001 autoWidth : function(){
12003 this.el.setWidth("auto");
12004 if(Roo.isIE7 && Roo.isStrict){
12005 var ib = this.el.child('button');
12006 if(ib && ib.getWidth() > 20){
12008 ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
12013 this.el.beginMeasure();
12015 if(this.el.getWidth() < this.minWidth){
12016 this.el.setWidth(this.minWidth);
12019 this.el.endMeasure();
12026 * Assigns this button's click handler
12027 * @param {Function} handler The function to call when the button is clicked
12028 * @param {Object} scope (optional) Scope for the function passed in
12030 setHandler : function(handler, scope){
12031 this.handler = handler;
12032 this.scope = scope;
12036 * Sets this button's text
12037 * @param {String} text The button text
12039 setText : function(text){
12042 this.el.child("td.x-btn-center button.x-btn-text").update(text);
12048 * Gets the text for this button
12049 * @return {String} The button text
12051 getText : function(){
12059 this.hidden = false;
12061 this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "");
12069 this.hidden = true;
12071 this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "none");
12076 * Convenience function for boolean show/hide
12077 * @param {Boolean} visible True to show, false to hide
12079 setVisible: function(visible){
12088 * If a state it passed, it becomes the pressed state otherwise the current state is toggled.
12089 * @param {Boolean} state (optional) Force a particular state
12091 toggle : function(state){
12092 state = state === undefined ? !this.pressed : state;
12093 if(state != this.pressed){
12095 this.el.addClass("x-btn-pressed");
12096 this.pressed = true;
12097 this.fireEvent("toggle", this, true);
12099 this.el.removeClass("x-btn-pressed");
12100 this.pressed = false;
12101 this.fireEvent("toggle", this, false);
12103 if(this.toggleHandler){
12104 this.toggleHandler.call(this.scope || this, this, state);
12112 focus : function(){
12113 this.el.child('button:first').focus();
12117 * Disable this button
12119 disable : function(){
12121 this.el.addClass("x-btn-disabled");
12123 this.disabled = true;
12127 * Enable this button
12129 enable : function(){
12131 this.el.removeClass("x-btn-disabled");
12133 this.disabled = false;
12137 * Convenience function for boolean enable/disable
12138 * @param {Boolean} enabled True to enable, false to disable
12140 setDisabled : function(v){
12141 this[v !== true ? "enable" : "disable"]();
12145 onClick : function(e){
12147 e.preventDefault();
12152 if(!this.disabled){
12153 if(this.enableToggle){
12156 if(this.menu && !this.menu.isVisible()){
12157 this.menu.show(this.el, this.menuAlign);
12159 this.fireEvent("click", this, e);
12161 this.el.removeClass("x-btn-over");
12162 this.handler.call(this.scope || this, this, e);
12167 onMouseOver : function(e){
12168 if(!this.disabled){
12169 this.el.addClass("x-btn-over");
12170 this.fireEvent('mouseover', this, e);
12174 onMouseOut : function(e){
12175 if(!e.within(this.el, true)){
12176 this.el.removeClass("x-btn-over");
12177 this.fireEvent('mouseout', this, e);
12181 onFocus : function(e){
12182 if(!this.disabled){
12183 this.el.addClass("x-btn-focus");
12187 onBlur : function(e){
12188 this.el.removeClass("x-btn-focus");
12191 onMouseDown : function(e){
12192 if(!this.disabled && e.button == 0){
12193 this.el.addClass("x-btn-click");
12194 Roo.get(document).on('mouseup', this.onMouseUp, this);
12198 onMouseUp : function(e){
12200 this.el.removeClass("x-btn-click");
12201 Roo.get(document).un('mouseup', this.onMouseUp, this);
12205 onMenuShow : function(e){
12206 this.el.addClass("x-btn-menu-active");
12209 onMenuHide : function(e){
12210 this.el.removeClass("x-btn-menu-active");
12214 // Private utility class used by Button
12215 Roo.ButtonToggleMgr = function(){
12218 function toggleGroup(btn, state){
12220 var g = groups[btn.toggleGroup];
12221 for(var i = 0, l = g.length; i < l; i++){
12223 g[i].toggle(false);
12230 register : function(btn){
12231 if(!btn.toggleGroup){
12234 var g = groups[btn.toggleGroup];
12236 g = groups[btn.toggleGroup] = [];
12239 btn.on("toggle", toggleGroup);
12242 unregister : function(btn){
12243 if(!btn.toggleGroup){
12246 var g = groups[btn.toggleGroup];
12249 btn.un("toggle", toggleGroup);
12255 * Ext JS Library 1.1.1
12256 * Copyright(c) 2006-2007, Ext JS, LLC.
12258 * Originally Released Under LGPL - original licence link has changed is not relivant.
12261 * <script type="text/javascript">
12265 * @class Roo.SplitButton
12266 * @extends Roo.Button
12267 * A split button that provides a built-in dropdown arrow that can fire an event separately from the default
12268 * click event of the button. Typically this would be used to display a dropdown menu that provides additional
12269 * options to the primary button action, but any custom handler can provide the arrowclick implementation.
12270 * @cfg {Function} arrowHandler A function called when the arrow button is clicked (can be used instead of click event)
12271 * @cfg {String} arrowTooltip The title attribute of the arrow
12273 * Create a new menu button
12274 * @param {String/HTMLElement/Element} renderTo The element to append the button to
12275 * @param {Object} config The config object
12277 Roo.SplitButton = function(renderTo, config){
12278 Roo.SplitButton.superclass.constructor.call(this, renderTo, config);
12280 * @event arrowclick
12281 * Fires when this button's arrow is clicked
12282 * @param {SplitButton} this
12283 * @param {EventObject} e The click event
12285 this.addEvents({"arrowclick":true});
12288 Roo.extend(Roo.SplitButton, Roo.Button, {
12289 render : function(renderTo){
12290 // this is one sweet looking template!
12291 var tpl = new Roo.Template(
12292 '<table cellspacing="0" class="x-btn-menu-wrap x-btn"><tr><td>',
12293 '<table cellspacing="0" class="x-btn-wrap x-btn-menu-text-wrap"><tbody>',
12294 '<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>',
12295 "</tbody></table></td><td>",
12296 '<table cellspacing="0" class="x-btn-wrap x-btn-menu-arrow-wrap"><tbody>',
12297 '<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>',
12298 "</tbody></table></td></tr></table>"
12300 var btn = tpl.append(renderTo, [this.text, this.type], true);
12301 var btnEl = btn.child("button");
12303 btn.addClass(this.cls);
12306 btnEl.setStyle('background-image', 'url(' +this.icon +')');
12309 btnEl.addClass(this.iconCls);
12311 btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
12315 if(this.handleMouseEvents){
12316 btn.on("mouseover", this.onMouseOver, this);
12317 btn.on("mouseout", this.onMouseOut, this);
12318 btn.on("mousedown", this.onMouseDown, this);
12319 btn.on("mouseup", this.onMouseUp, this);
12321 btn.on(this.clickEvent, this.onClick, this);
12323 if(typeof this.tooltip == 'object'){
12324 Roo.QuickTips.tips(Roo.apply({
12328 btnEl.dom[this.tooltipType] = this.tooltip;
12331 if(this.arrowTooltip){
12332 btn.child("button:nth(2)").dom[this.tooltipType] = this.arrowTooltip;
12341 this.el.addClass("x-btn-pressed");
12343 if(Roo.isIE && !Roo.isIE7){
12344 this.autoWidth.defer(1, this);
12349 this.menu.on("show", this.onMenuShow, this);
12350 this.menu.on("hide", this.onMenuHide, this);
12352 this.fireEvent('render', this);
12356 autoWidth : function(){
12358 var tbl = this.el.child("table:first");
12359 var tbl2 = this.el.child("table:last");
12360 this.el.setWidth("auto");
12361 tbl.setWidth("auto");
12362 if(Roo.isIE7 && Roo.isStrict){
12363 var ib = this.el.child('button:first');
12364 if(ib && ib.getWidth() > 20){
12366 ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
12371 this.el.beginMeasure();
12373 if((tbl.getWidth()+tbl2.getWidth()) < this.minWidth){
12374 tbl.setWidth(this.minWidth-tbl2.getWidth());
12377 this.el.endMeasure();
12380 this.el.setWidth(tbl.getWidth()+tbl2.getWidth());
12384 * Sets this button's click handler
12385 * @param {Function} handler The function to call when the button is clicked
12386 * @param {Object} scope (optional) Scope for the function passed above
12388 setHandler : function(handler, scope){
12389 this.handler = handler;
12390 this.scope = scope;
12394 * Sets this button's arrow click handler
12395 * @param {Function} handler The function to call when the arrow is clicked
12396 * @param {Object} scope (optional) Scope for the function passed above
12398 setArrowHandler : function(handler, scope){
12399 this.arrowHandler = handler;
12400 this.scope = scope;
12406 focus : function(){
12408 this.el.child("button:first").focus();
12413 onClick : function(e){
12414 e.preventDefault();
12415 if(!this.disabled){
12416 if(e.getTarget(".x-btn-menu-arrow-wrap")){
12417 if(this.menu && !this.menu.isVisible()){
12418 this.menu.show(this.el, this.menuAlign);
12420 this.fireEvent("arrowclick", this, e);
12421 if(this.arrowHandler){
12422 this.arrowHandler.call(this.scope || this, this, e);
12425 this.fireEvent("click", this, e);
12427 this.handler.call(this.scope || this, this, e);
12433 onMouseDown : function(e){
12434 if(!this.disabled){
12435 Roo.fly(e.getTarget("table")).addClass("x-btn-click");
12439 onMouseUp : function(e){
12440 Roo.fly(e.getTarget("table")).removeClass("x-btn-click");
12445 // backwards compat
12446 Roo.MenuButton = Roo.SplitButton;/*
12448 * Ext JS Library 1.1.1
12449 * Copyright(c) 2006-2007, Ext JS, LLC.
12451 * Originally Released Under LGPL - original licence link has changed is not relivant.
12454 * <script type="text/javascript">
12458 * @class Roo.Toolbar
12459 * Basic Toolbar class.
12461 * Creates a new Toolbar
12462 * @param {Object} container The config object
12464 Roo.Toolbar = function(container, buttons, config)
12466 /// old consturctor format still supported..
12467 if(container instanceof Array){ // omit the container for later rendering
12468 buttons = container;
12472 if (typeof(container) == 'object' && container.xtype) {
12473 config = container;
12474 container = config.container;
12475 buttons = config.buttons || []; // not really - use items!!
12478 if (config && config.items) {
12479 xitems = config.items;
12480 delete config.items;
12482 Roo.apply(this, config);
12483 this.buttons = buttons;
12486 this.render(container);
12488 this.xitems = xitems;
12489 Roo.each(xitems, function(b) {
12495 Roo.Toolbar.prototype = {
12497 * @cfg {Array} items
12498 * array of button configs or elements to add (will be converted to a MixedCollection)
12502 * @cfg {String/HTMLElement/Element} container
12503 * The id or element that will contain the toolbar
12506 render : function(ct){
12507 this.el = Roo.get(ct);
12509 this.el.addClass(this.cls);
12511 // using a table allows for vertical alignment
12512 // 100% width is needed by Safari...
12513 this.el.update('<div class="x-toolbar x-small-editor"><table cellspacing="0"><tr></tr></table></div>');
12514 this.tr = this.el.child("tr", true);
12516 this.items = new Roo.util.MixedCollection(false, function(o){
12517 return o.id || ("item" + (++autoId));
12520 this.add.apply(this, this.buttons);
12521 delete this.buttons;
12526 * Adds element(s) to the toolbar -- this function takes a variable number of
12527 * arguments of mixed type and adds them to the toolbar.
12528 * @param {Mixed} arg1 The following types of arguments are all valid:<br />
12530 * <li>{@link Roo.Toolbar.Button} config: A valid button config object (equivalent to {@link #addButton})</li>
12531 * <li>HtmlElement: Any standard HTML element (equivalent to {@link #addElement})</li>
12532 * <li>Field: Any form field (equivalent to {@link #addField})</li>
12533 * <li>Item: Any subclass of {@link Roo.Toolbar.Item} (equivalent to {@link #addItem})</li>
12534 * <li>String: Any generic string (gets wrapped in a {@link Roo.Toolbar.TextItem}, equivalent to {@link #addText}).
12535 * Note that there are a few special strings that are treated differently as explained nRoo.</li>
12536 * <li>'separator' or '-': Creates a separator element (equivalent to {@link #addSeparator})</li>
12537 * <li>' ': Creates a spacer element (equivalent to {@link #addSpacer})</li>
12538 * <li>'->': Creates a fill element (equivalent to {@link #addFill})</li>
12540 * @param {Mixed} arg2
12541 * @param {Mixed} etc.
12544 var a = arguments, l = a.length;
12545 for(var i = 0; i < l; i++){
12550 _add : function(el) {
12553 el = Roo.factory(el, typeof(Roo.Toolbar[el.xtype]) == 'undefined' ? Roo.form : Roo.Toolbar);
12556 if (el.applyTo){ // some kind of form field
12557 return this.addField(el);
12559 if (el.render){ // some kind of Toolbar.Item
12560 return this.addItem(el);
12562 if (typeof el == "string"){ // string
12563 if(el == "separator" || el == "-"){
12564 return this.addSeparator();
12567 return this.addSpacer();
12570 return this.addFill();
12572 return this.addText(el);
12575 if(el.tagName){ // element
12576 return this.addElement(el);
12578 if(typeof el == "object"){ // must be button config?
12579 return this.addButton(el);
12581 // and now what?!?!
12587 * Add an Xtype element
12588 * @param {Object} xtype Xtype Object
12589 * @return {Object} created Object
12591 addxtype : function(e){
12592 return this.add(e);
12596 * Returns the Element for this toolbar.
12597 * @return {Roo.Element}
12599 getEl : function(){
12605 * @return {Roo.Toolbar.Item} The separator item
12607 addSeparator : function(){
12608 return this.addItem(new Roo.Toolbar.Separator());
12612 * Adds a spacer element
12613 * @return {Roo.Toolbar.Spacer} The spacer item
12615 addSpacer : function(){
12616 return this.addItem(new Roo.Toolbar.Spacer());
12620 * Adds a fill element that forces subsequent additions to the right side of the toolbar
12621 * @return {Roo.Toolbar.Fill} The fill item
12623 addFill : function(){
12624 return this.addItem(new Roo.Toolbar.Fill());
12628 * Adds any standard HTML element to the toolbar
12629 * @param {String/HTMLElement/Element} el The element or id of the element to add
12630 * @return {Roo.Toolbar.Item} The element's item
12632 addElement : function(el){
12633 return this.addItem(new Roo.Toolbar.Item(el));
12636 * Collection of items on the toolbar.. (only Toolbar Items, so use fields to retrieve fields)
12637 * @type Roo.util.MixedCollection
12642 * Adds any Toolbar.Item or subclass
12643 * @param {Roo.Toolbar.Item} item
12644 * @return {Roo.Toolbar.Item} The item
12646 addItem : function(item){
12647 var td = this.nextBlock();
12649 this.items.add(item);
12654 * Adds a button (or buttons). See {@link Roo.Toolbar.Button} for more info on the config.
12655 * @param {Object/Array} config A button config or array of configs
12656 * @return {Roo.Toolbar.Button/Array}
12658 addButton : function(config){
12659 if(config instanceof Array){
12661 for(var i = 0, len = config.length; i < len; i++) {
12662 buttons.push(this.addButton(config[i]));
12667 if(!(config instanceof Roo.Toolbar.Button)){
12669 new Roo.Toolbar.SplitButton(config) :
12670 new Roo.Toolbar.Button(config);
12672 var td = this.nextBlock();
12679 * Adds text to the toolbar
12680 * @param {String} text The text to add
12681 * @return {Roo.Toolbar.Item} The element's item
12683 addText : function(text){
12684 return this.addItem(new Roo.Toolbar.TextItem(text));
12688 * Inserts any {@link Roo.Toolbar.Item}/{@link Roo.Toolbar.Button} at the specified index.
12689 * @param {Number} index The index where the item is to be inserted
12690 * @param {Object/Roo.Toolbar.Item/Roo.Toolbar.Button (may be Array)} item The button, or button config object to be inserted.
12691 * @return {Roo.Toolbar.Button/Item}
12693 insertButton : function(index, item){
12694 if(item instanceof Array){
12696 for(var i = 0, len = item.length; i < len; i++) {
12697 buttons.push(this.insertButton(index + i, item[i]));
12701 if (!(item instanceof Roo.Toolbar.Button)){
12702 item = new Roo.Toolbar.Button(item);
12704 var td = document.createElement("td");
12705 this.tr.insertBefore(td, this.tr.childNodes[index]);
12707 this.items.insert(index, item);
12712 * Adds a new element to the toolbar from the passed {@link Roo.DomHelper} config.
12713 * @param {Object} config
12714 * @return {Roo.Toolbar.Item} The element's item
12716 addDom : function(config, returnEl){
12717 var td = this.nextBlock();
12718 Roo.DomHelper.overwrite(td, config);
12719 var ti = new Roo.Toolbar.Item(td.firstChild);
12721 this.items.add(ti);
12726 * Collection of fields on the toolbar.. usefull for quering (value is false if there are no fields)
12727 * @type Roo.util.MixedCollection
12732 * Adds a dynamically rendered Roo.form field (TextField, ComboBox, etc).
12733 * Note: the field should not have been rendered yet. For a field that has already been
12734 * rendered, use {@link #addElement}.
12735 * @param {Roo.form.Field} field
12736 * @return {Roo.ToolbarItem}
12740 addField : function(field) {
12741 if (!this.fields) {
12743 this.fields = new Roo.util.MixedCollection(false, function(o){
12744 return o.id || ("item" + (++autoId));
12749 var td = this.nextBlock();
12751 var ti = new Roo.Toolbar.Item(td.firstChild);
12753 this.items.add(ti);
12754 this.fields.add(field);
12765 this.el.child('div').setVisibilityMode(Roo.Element.DISPLAY);
12766 this.el.child('div').hide();
12774 this.el.child('div').show();
12778 nextBlock : function(){
12779 var td = document.createElement("td");
12780 this.tr.appendChild(td);
12785 destroy : function(){
12786 if(this.items){ // rendered?
12787 Roo.destroy.apply(Roo, this.items.items);
12789 if(this.fields){ // rendered?
12790 Roo.destroy.apply(Roo, this.fields.items);
12792 Roo.Element.uncache(this.el, this.tr);
12797 * @class Roo.Toolbar.Item
12798 * The base class that other classes should extend in order to get some basic common toolbar item functionality.
12800 * Creates a new Item
12801 * @param {HTMLElement} el
12803 Roo.Toolbar.Item = function(el){
12804 this.el = Roo.getDom(el);
12805 this.id = Roo.id(this.el);
12806 this.hidden = false;
12809 Roo.Toolbar.Item.prototype = {
12812 * Get this item's HTML Element
12813 * @return {HTMLElement}
12815 getEl : function(){
12820 render : function(td){
12822 td.appendChild(this.el);
12826 * Removes and destroys this item.
12828 destroy : function(){
12829 this.td.parentNode.removeChild(this.td);
12836 this.hidden = false;
12837 this.td.style.display = "";
12844 this.hidden = true;
12845 this.td.style.display = "none";
12849 * Convenience function for boolean show/hide.
12850 * @param {Boolean} visible true to show/false to hide
12852 setVisible: function(visible){
12861 * Try to focus this item.
12863 focus : function(){
12864 Roo.fly(this.el).focus();
12868 * Disables this item.
12870 disable : function(){
12871 Roo.fly(this.td).addClass("x-item-disabled");
12872 this.disabled = true;
12873 this.el.disabled = true;
12877 * Enables this item.
12879 enable : function(){
12880 Roo.fly(this.td).removeClass("x-item-disabled");
12881 this.disabled = false;
12882 this.el.disabled = false;
12888 * @class Roo.Toolbar.Separator
12889 * @extends Roo.Toolbar.Item
12890 * A simple toolbar separator class
12892 * Creates a new Separator
12894 Roo.Toolbar.Separator = function(){
12895 var s = document.createElement("span");
12896 s.className = "ytb-sep";
12897 Roo.Toolbar.Separator.superclass.constructor.call(this, s);
12899 Roo.extend(Roo.Toolbar.Separator, Roo.Toolbar.Item, {
12900 enable:Roo.emptyFn,
12901 disable:Roo.emptyFn,
12906 * @class Roo.Toolbar.Spacer
12907 * @extends Roo.Toolbar.Item
12908 * A simple element that adds extra horizontal space to a toolbar.
12910 * Creates a new Spacer
12912 Roo.Toolbar.Spacer = function(){
12913 var s = document.createElement("div");
12914 s.className = "ytb-spacer";
12915 Roo.Toolbar.Spacer.superclass.constructor.call(this, s);
12917 Roo.extend(Roo.Toolbar.Spacer, Roo.Toolbar.Item, {
12918 enable:Roo.emptyFn,
12919 disable:Roo.emptyFn,
12924 * @class Roo.Toolbar.Fill
12925 * @extends Roo.Toolbar.Spacer
12926 * A simple element that adds a greedy (100% width) horizontal space to a toolbar.
12928 * Creates a new Spacer
12930 Roo.Toolbar.Fill = Roo.extend(Roo.Toolbar.Spacer, {
12932 render : function(td){
12933 td.style.width = '100%';
12934 Roo.Toolbar.Fill.superclass.render.call(this, td);
12939 * @class Roo.Toolbar.TextItem
12940 * @extends Roo.Toolbar.Item
12941 * A simple class that renders text directly into a toolbar.
12943 * Creates a new TextItem
12944 * @param {String} text
12946 Roo.Toolbar.TextItem = function(text){
12947 if (typeof(text) == 'object') {
12950 var s = document.createElement("span");
12951 s.className = "ytb-text";
12952 s.innerHTML = text;
12953 Roo.Toolbar.TextItem.superclass.constructor.call(this, s);
12955 Roo.extend(Roo.Toolbar.TextItem, Roo.Toolbar.Item, {
12956 enable:Roo.emptyFn,
12957 disable:Roo.emptyFn,
12962 * @class Roo.Toolbar.Button
12963 * @extends Roo.Button
12964 * A button that renders into a toolbar.
12966 * Creates a new Button
12967 * @param {Object} config A standard {@link Roo.Button} config object
12969 Roo.Toolbar.Button = function(config){
12970 Roo.Toolbar.Button.superclass.constructor.call(this, null, config);
12972 Roo.extend(Roo.Toolbar.Button, Roo.Button, {
12973 render : function(td){
12975 Roo.Toolbar.Button.superclass.render.call(this, td);
12979 * Removes and destroys this button
12981 destroy : function(){
12982 Roo.Toolbar.Button.superclass.destroy.call(this);
12983 this.td.parentNode.removeChild(this.td);
12987 * Shows this button
12990 this.hidden = false;
12991 this.td.style.display = "";
12995 * Hides this button
12998 this.hidden = true;
12999 this.td.style.display = "none";
13003 * Disables this item
13005 disable : function(){
13006 Roo.fly(this.td).addClass("x-item-disabled");
13007 this.disabled = true;
13011 * Enables this item
13013 enable : function(){
13014 Roo.fly(this.td).removeClass("x-item-disabled");
13015 this.disabled = false;
13018 // backwards compat
13019 Roo.ToolbarButton = Roo.Toolbar.Button;
13022 * @class Roo.Toolbar.SplitButton
13023 * @extends Roo.SplitButton
13024 * A menu button that renders into a toolbar.
13026 * Creates a new SplitButton
13027 * @param {Object} config A standard {@link Roo.SplitButton} config object
13029 Roo.Toolbar.SplitButton = function(config){
13030 Roo.Toolbar.SplitButton.superclass.constructor.call(this, null, config);
13032 Roo.extend(Roo.Toolbar.SplitButton, Roo.SplitButton, {
13033 render : function(td){
13035 Roo.Toolbar.SplitButton.superclass.render.call(this, td);
13039 * Removes and destroys this button
13041 destroy : function(){
13042 Roo.Toolbar.SplitButton.superclass.destroy.call(this);
13043 this.td.parentNode.removeChild(this.td);
13047 * Shows this button
13050 this.hidden = false;
13051 this.td.style.display = "";
13055 * Hides this button
13058 this.hidden = true;
13059 this.td.style.display = "none";
13063 // backwards compat
13064 Roo.Toolbar.MenuButton = Roo.Toolbar.SplitButton;/*
13066 * Ext JS Library 1.1.1
13067 * Copyright(c) 2006-2007, Ext JS, LLC.
13069 * Originally Released Under LGPL - original licence link has changed is not relivant.
13072 * <script type="text/javascript">
13076 * @class Roo.PagingToolbar
13077 * @extends Roo.Toolbar
13078 * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
13080 * Create a new PagingToolbar
13081 * @param {Object} config The config object
13083 Roo.PagingToolbar = function(el, ds, config)
13085 // old args format still supported... - xtype is prefered..
13086 if (typeof(el) == 'object' && el.xtype) {
13087 // created from xtype...
13089 ds = el.dataSource;
13090 el = config.container;
13093 if (config.items) {
13094 items = config.items;
13098 Roo.PagingToolbar.superclass.constructor.call(this, el, null, config);
13101 this.renderButtons(this.el);
13104 // supprot items array.
13106 Roo.each(items, function(e) {
13107 this.add(Roo.factory(e));
13112 Roo.extend(Roo.PagingToolbar, Roo.Toolbar, {
13114 * @cfg {Roo.data.Store} dataSource
13115 * The underlying data store providing the paged data
13118 * @cfg {String/HTMLElement/Element} container
13119 * container The id or element that will contain the toolbar
13122 * @cfg {Boolean} displayInfo
13123 * True to display the displayMsg (defaults to false)
13126 * @cfg {Number} pageSize
13127 * The number of records to display per page (defaults to 20)
13131 * @cfg {String} displayMsg
13132 * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
13134 displayMsg : 'Displaying {0} - {1} of {2}',
13136 * @cfg {String} emptyMsg
13137 * The message to display when no records are found (defaults to "No data to display")
13139 emptyMsg : 'No data to display',
13141 * Customizable piece of the default paging text (defaults to "Page")
13144 beforePageText : "Page",
13146 * Customizable piece of the default paging text (defaults to "of %0")
13149 afterPageText : "of {0}",
13151 * Customizable piece of the default paging text (defaults to "First Page")
13154 firstText : "First Page",
13156 * Customizable piece of the default paging text (defaults to "Previous Page")
13159 prevText : "Previous Page",
13161 * Customizable piece of the default paging text (defaults to "Next Page")
13164 nextText : "Next Page",
13166 * Customizable piece of the default paging text (defaults to "Last Page")
13169 lastText : "Last Page",
13171 * Customizable piece of the default paging text (defaults to "Refresh")
13174 refreshText : "Refresh",
13177 renderButtons : function(el){
13178 Roo.PagingToolbar.superclass.render.call(this, el);
13179 this.first = this.addButton({
13180 tooltip: this.firstText,
13181 cls: "x-btn-icon x-grid-page-first",
13183 handler: this.onClick.createDelegate(this, ["first"])
13185 this.prev = this.addButton({
13186 tooltip: this.prevText,
13187 cls: "x-btn-icon x-grid-page-prev",
13189 handler: this.onClick.createDelegate(this, ["prev"])
13191 //this.addSeparator();
13192 this.add(this.beforePageText);
13193 this.field = Roo.get(this.addDom({
13198 cls: "x-grid-page-number"
13200 this.field.on("keydown", this.onPagingKeydown, this);
13201 this.field.on("focus", function(){this.dom.select();});
13202 this.afterTextEl = this.addText(String.format(this.afterPageText, 1));
13203 this.field.setHeight(18);
13204 //this.addSeparator();
13205 this.next = this.addButton({
13206 tooltip: this.nextText,
13207 cls: "x-btn-icon x-grid-page-next",
13209 handler: this.onClick.createDelegate(this, ["next"])
13211 this.last = this.addButton({
13212 tooltip: this.lastText,
13213 cls: "x-btn-icon x-grid-page-last",
13215 handler: this.onClick.createDelegate(this, ["last"])
13217 //this.addSeparator();
13218 this.loading = this.addButton({
13219 tooltip: this.refreshText,
13220 cls: "x-btn-icon x-grid-loading",
13221 handler: this.onClick.createDelegate(this, ["refresh"])
13224 if(this.displayInfo){
13225 this.displayEl = Roo.fly(this.el.dom.firstChild).createChild({cls:'x-paging-info'});
13230 updateInfo : function(){
13231 if(this.displayEl){
13232 var count = this.ds.getCount();
13233 var msg = count == 0 ?
13237 this.cursor+1, this.cursor+count, this.ds.getTotalCount()
13239 this.displayEl.update(msg);
13244 onLoad : function(ds, r, o){
13245 this.cursor = o.params ? o.params.start : 0;
13246 var d = this.getPageData(), ap = d.activePage, ps = d.pages;
13248 this.afterTextEl.el.innerHTML = String.format(this.afterPageText, d.pages);
13249 this.field.dom.value = ap;
13250 this.first.setDisabled(ap == 1);
13251 this.prev.setDisabled(ap == 1);
13252 this.next.setDisabled(ap == ps);
13253 this.last.setDisabled(ap == ps);
13254 this.loading.enable();
13259 getPageData : function(){
13260 var total = this.ds.getTotalCount();
13263 activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
13264 pages : total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
13269 onLoadError : function(){
13270 this.loading.enable();
13274 onPagingKeydown : function(e){
13275 var k = e.getKey();
13276 var d = this.getPageData();
13278 var v = this.field.dom.value, pageNum;
13279 if(!v || isNaN(pageNum = parseInt(v, 10))){
13280 this.field.dom.value = d.activePage;
13283 pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
13284 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
13287 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))
13289 var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
13290 this.field.dom.value = pageNum;
13291 this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
13294 else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
13296 var v = this.field.dom.value, pageNum;
13297 var increment = (e.shiftKey) ? 10 : 1;
13298 if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
13300 if(!v || isNaN(pageNum = parseInt(v, 10))) {
13301 this.field.dom.value = d.activePage;
13304 else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
13306 this.field.dom.value = parseInt(v, 10) + increment;
13307 pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
13308 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
13315 beforeLoad : function(){
13317 this.loading.disable();
13322 onClick : function(which){
13326 ds.load({params:{start: 0, limit: this.pageSize}});
13329 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
13332 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
13335 var total = ds.getTotalCount();
13336 var extra = total % this.pageSize;
13337 var lastStart = extra ? (total - extra) : total-this.pageSize;
13338 ds.load({params:{start: lastStart, limit: this.pageSize}});
13341 ds.load({params:{start: this.cursor, limit: this.pageSize}});
13347 * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
13348 * @param {Roo.data.Store} store The data store to unbind
13350 unbind : function(ds){
13351 ds.un("beforeload", this.beforeLoad, this);
13352 ds.un("load", this.onLoad, this);
13353 ds.un("loadexception", this.onLoadError, this);
13354 ds.un("remove", this.updateInfo, this);
13355 ds.un("add", this.updateInfo, this);
13356 this.ds = undefined;
13360 * Binds the paging toolbar to the specified {@link Roo.data.Store}
13361 * @param {Roo.data.Store} store The data store to bind
13363 bind : function(ds){
13364 ds.on("beforeload", this.beforeLoad, this);
13365 ds.on("load", this.onLoad, this);
13366 ds.on("loadexception", this.onLoadError, this);
13367 ds.on("remove", this.updateInfo, this);
13368 ds.on("add", this.updateInfo, this);
13373 * Ext JS Library 1.1.1
13374 * Copyright(c) 2006-2007, Ext JS, LLC.
13376 * Originally Released Under LGPL - original licence link has changed is not relivant.
13379 * <script type="text/javascript">
13383 * @class Roo.Resizable
13384 * @extends Roo.util.Observable
13385 * <p>Applies drag handles to an element to make it resizable. The drag handles are inserted into the element
13386 * and positioned absolute. Some elements, such as a textarea or image, don't support this. To overcome that, you can wrap
13387 * 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
13388 * the element will be wrapped for you automatically.</p>
13389 * <p>Here is the list of valid resize handles:</p>
13392 ------ -------------------
13401 'hd' horizontal drag
13404 * <p>Here's an example showing the creation of a typical Resizable:</p>
13406 var resizer = new Roo.Resizable("element-id", {
13414 resizer.on("resize", myHandler);
13416 * <p>To hide a particular handle, set its display to none in CSS, or through script:<br>
13417 * resizer.east.setDisplayed(false);</p>
13418 * @cfg {Boolean/String/Element} resizeChild True to resize the first child, or id/element to resize (defaults to false)
13419 * @cfg {Array/String} adjustments String "auto" or an array [width, height] with values to be <b>added</b> to the
13420 * resize operation's new size (defaults to [0, 0])
13421 * @cfg {Number} minWidth The minimum width for the element (defaults to 5)
13422 * @cfg {Number} minHeight The minimum height for the element (defaults to 5)
13423 * @cfg {Number} maxWidth The maximum width for the element (defaults to 10000)
13424 * @cfg {Number} maxHeight The maximum height for the element (defaults to 10000)
13425 * @cfg {Boolean} enabled False to disable resizing (defaults to true)
13426 * @cfg {Boolean} wrap True to wrap an element with a div if needed (required for textareas and images, defaults to false)
13427 * @cfg {Number} width The width of the element in pixels (defaults to null)
13428 * @cfg {Number} height The height of the element in pixels (defaults to null)
13429 * @cfg {Boolean} animate True to animate the resize (not compatible with dynamic sizing, defaults to false)
13430 * @cfg {Number} duration Animation duration if animate = true (defaults to .35)
13431 * @cfg {Boolean} dynamic True to resize the element while dragging instead of using a proxy (defaults to false)
13432 * @cfg {String} handles String consisting of the resize handles to display (defaults to undefined)
13433 * @cfg {Boolean} multiDirectional <b>Deprecated</b>. The old style of adding multi-direction resize handles, deprecated
13434 * in favor of the handles config option (defaults to false)
13435 * @cfg {Boolean} disableTrackOver True to disable mouse tracking. This is only applied at config time. (defaults to false)
13436 * @cfg {String} easing Animation easing if animate = true (defaults to 'easingOutStrong')
13437 * @cfg {Number} widthIncrement The increment to snap the width resize in pixels (dynamic must be true, defaults to 0)
13438 * @cfg {Number} heightIncrement The increment to snap the height resize in pixels (dynamic must be true, defaults to 0)
13439 * @cfg {Boolean} pinned True to ensure that the resize handles are always visible, false to display them only when the
13440 * user mouses over the resizable borders. This is only applied at config time. (defaults to false)
13441 * @cfg {Boolean} preserveRatio True to preserve the original ratio between height and width during resize (defaults to false)
13442 * @cfg {Boolean} transparent True for transparent handles. This is only applied at config time. (defaults to false)
13443 * @cfg {Number} minX The minimum allowed page X for the element (only used for west resizing, defaults to 0)
13444 * @cfg {Number} minY The minimum allowed page Y for the element (only used for north resizing, defaults to 0)
13445 * @cfg {Boolean} draggable Convenience to initialize drag drop (defaults to false)
13447 * Create a new resizable component
13448 * @param {String/HTMLElement/Roo.Element} el The id or element to resize
13449 * @param {Object} config configuration options
13451 Roo.Resizable = function(el, config)
13453 this.el = Roo.get(el);
13455 if(config && config.wrap){
13456 config.resizeChild = this.el;
13457 this.el = this.el.wrap(typeof config.wrap == "object" ? config.wrap : {cls:"xresizable-wrap"});
13458 this.el.id = this.el.dom.id = config.resizeChild.id + "-rzwrap";
13459 this.el.setStyle("overflow", "hidden");
13460 this.el.setPositioning(config.resizeChild.getPositioning());
13461 config.resizeChild.clearPositioning();
13462 if(!config.width || !config.height){
13463 var csize = config.resizeChild.getSize();
13464 this.el.setSize(csize.width, csize.height);
13466 if(config.pinned && !config.adjustments){
13467 config.adjustments = "auto";
13471 this.proxy = this.el.createProxy({tag: "div", cls: "x-resizable-proxy", id: this.el.id + "-rzproxy"});
13472 this.proxy.unselectable();
13473 this.proxy.enableDisplayMode('block');
13475 Roo.apply(this, config);
13478 this.disableTrackOver = true;
13479 this.el.addClass("x-resizable-pinned");
13481 // if the element isn't positioned, make it relative
13482 var position = this.el.getStyle("position");
13483 if(position != "absolute" && position != "fixed"){
13484 this.el.setStyle("position", "relative");
13486 if(!this.handles){ // no handles passed, must be legacy style
13487 this.handles = 's,e,se';
13488 if(this.multiDirectional){
13489 this.handles += ',n,w';
13492 if(this.handles == "all"){
13493 this.handles = "n s e w ne nw se sw";
13495 var hs = this.handles.split(/\s*?[,;]\s*?| /);
13496 var ps = Roo.Resizable.positions;
13497 for(var i = 0, len = hs.length; i < len; i++){
13498 if(hs[i] && ps[hs[i]]){
13499 var pos = ps[hs[i]];
13500 this[pos] = new Roo.Resizable.Handle(this, pos, this.disableTrackOver, this.transparent);
13504 this.corner = this.southeast;
13506 // updateBox = the box can move..
13507 if(this.handles.indexOf("n") != -1 || this.handles.indexOf("w") != -1 || this.handles.indexOf("hd") != -1) {
13508 this.updateBox = true;
13511 this.activeHandle = null;
13513 if(this.resizeChild){
13514 if(typeof this.resizeChild == "boolean"){
13515 this.resizeChild = Roo.get(this.el.dom.firstChild, true);
13517 this.resizeChild = Roo.get(this.resizeChild, true);
13521 if(this.adjustments == "auto"){
13522 var rc = this.resizeChild;
13523 var hw = this.west, he = this.east, hn = this.north, hs = this.south;
13524 if(rc && (hw || hn)){
13525 rc.position("relative");
13526 rc.setLeft(hw ? hw.el.getWidth() : 0);
13527 rc.setTop(hn ? hn.el.getHeight() : 0);
13529 this.adjustments = [
13530 (he ? -he.el.getWidth() : 0) + (hw ? -hw.el.getWidth() : 0),
13531 (hn ? -hn.el.getHeight() : 0) + (hs ? -hs.el.getHeight() : 0) -1
13535 if(this.draggable){
13536 this.dd = this.dynamic ?
13537 this.el.initDD(null) : this.el.initDDProxy(null, {dragElId: this.proxy.id});
13538 this.dd.setHandleElId(this.resizeChild ? this.resizeChild.id : this.el.id);
13544 * @event beforeresize
13545 * Fired before resize is allowed. Set enabled to false to cancel resize.
13546 * @param {Roo.Resizable} this
13547 * @param {Roo.EventObject} e The mousedown event
13549 "beforeresize" : true,
13552 * Fired a resizing.
13553 * @param {Roo.Resizable} this
13554 * @param {Number} x The new x position
13555 * @param {Number} y The new y position
13556 * @param {Number} w The new w width
13557 * @param {Number} h The new h hight
13558 * @param {Roo.EventObject} e The mouseup event
13563 * Fired after a resize.
13564 * @param {Roo.Resizable} this
13565 * @param {Number} width The new width
13566 * @param {Number} height The new height
13567 * @param {Roo.EventObject} e The mouseup event
13572 if(this.width !== null && this.height !== null){
13573 this.resizeTo(this.width, this.height);
13575 this.updateChildSize();
13578 this.el.dom.style.zoom = 1;
13580 Roo.Resizable.superclass.constructor.call(this);
13583 Roo.extend(Roo.Resizable, Roo.util.Observable, {
13584 resizeChild : false,
13585 adjustments : [0, 0],
13595 multiDirectional : false,
13596 disableTrackOver : false,
13597 easing : 'easeOutStrong',
13598 widthIncrement : 0,
13599 heightIncrement : 0,
13603 preserveRatio : false,
13604 transparent: false,
13610 * @cfg {String/HTMLElement/Element} constrainTo Constrain the resize to a particular element
13612 constrainTo: undefined,
13614 * @cfg {Roo.lib.Region} resizeRegion Constrain the resize to a particular region
13616 resizeRegion: undefined,
13620 * Perform a manual resize
13621 * @param {Number} width
13622 * @param {Number} height
13624 resizeTo : function(width, height){
13625 this.el.setSize(width, height);
13626 this.updateChildSize();
13627 this.fireEvent("resize", this, width, height, null);
13631 startSizing : function(e, handle){
13632 this.fireEvent("beforeresize", this, e);
13633 if(this.enabled){ // 2nd enabled check in case disabled before beforeresize handler
13636 this.overlay = this.el.createProxy({tag: "div", cls: "x-resizable-overlay", html: " "});
13637 this.overlay.unselectable();
13638 this.overlay.enableDisplayMode("block");
13639 this.overlay.on("mousemove", this.onMouseMove, this);
13640 this.overlay.on("mouseup", this.onMouseUp, this);
13642 this.overlay.setStyle("cursor", handle.el.getStyle("cursor"));
13644 this.resizing = true;
13645 this.startBox = this.el.getBox();
13646 this.startPoint = e.getXY();
13647 this.offsets = [(this.startBox.x + this.startBox.width) - this.startPoint[0],
13648 (this.startBox.y + this.startBox.height) - this.startPoint[1]];
13650 this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
13651 this.overlay.show();
13653 if(this.constrainTo) {
13654 var ct = Roo.get(this.constrainTo);
13655 this.resizeRegion = ct.getRegion().adjust(
13656 ct.getFrameWidth('t'),
13657 ct.getFrameWidth('l'),
13658 -ct.getFrameWidth('b'),
13659 -ct.getFrameWidth('r')
13663 this.proxy.setStyle('visibility', 'hidden'); // workaround display none
13665 this.proxy.setBox(this.startBox);
13667 this.proxy.setStyle('visibility', 'visible');
13673 onMouseDown : function(handle, e){
13676 this.activeHandle = handle;
13677 this.startSizing(e, handle);
13682 onMouseUp : function(e){
13683 var size = this.resizeElement();
13684 this.resizing = false;
13686 this.overlay.hide();
13688 this.fireEvent("resize", this, size.width, size.height, e);
13692 updateChildSize : function(){
13694 if(this.resizeChild){
13696 var child = this.resizeChild;
13697 var adj = this.adjustments;
13698 if(el.dom.offsetWidth){
13699 var b = el.getSize(true);
13700 child.setSize(b.width+adj[0], b.height+adj[1]);
13702 // Second call here for IE
13703 // The first call enables instant resizing and
13704 // the second call corrects scroll bars if they
13707 setTimeout(function(){
13708 if(el.dom.offsetWidth){
13709 var b = el.getSize(true);
13710 child.setSize(b.width+adj[0], b.height+adj[1]);
13718 snap : function(value, inc, min){
13719 if(!inc || !value) return value;
13720 var newValue = value;
13721 var m = value % inc;
13724 newValue = value + (inc-m);
13726 newValue = value - m;
13729 return Math.max(min, newValue);
13733 resizeElement : function(){
13734 var box = this.proxy.getBox();
13735 if(this.updateBox){
13736 this.el.setBox(box, false, this.animate, this.duration, null, this.easing);
13738 this.el.setSize(box.width, box.height, this.animate, this.duration, null, this.easing);
13740 this.updateChildSize();
13748 constrain : function(v, diff, m, mx){
13751 }else if(v - diff > mx){
13758 onMouseMove : function(e){
13761 try{// try catch so if something goes wrong the user doesn't get hung
13763 if(this.resizeRegion && !this.resizeRegion.contains(e.getPoint())) {
13767 //var curXY = this.startPoint;
13768 var curSize = this.curSize || this.startBox;
13769 var x = this.startBox.x, y = this.startBox.y;
13770 var ox = x, oy = y;
13771 var w = curSize.width, h = curSize.height;
13772 var ow = w, oh = h;
13773 var mw = this.minWidth, mh = this.minHeight;
13774 var mxw = this.maxWidth, mxh = this.maxHeight;
13775 var wi = this.widthIncrement;
13776 var hi = this.heightIncrement;
13778 var eventXY = e.getXY();
13779 var diffX = -(this.startPoint[0] - Math.max(this.minX, eventXY[0]));
13780 var diffY = -(this.startPoint[1] - Math.max(this.minY, eventXY[1]));
13782 var pos = this.activeHandle.position;
13787 w = Math.min(Math.max(mw, w), mxw);
13792 h = Math.min(Math.max(mh, h), mxh);
13797 w = Math.min(Math.max(mw, w), mxw);
13798 h = Math.min(Math.max(mh, h), mxh);
13801 diffY = this.constrain(h, diffY, mh, mxh);
13808 var adiffX = Math.abs(diffX);
13809 var sub = (adiffX % wi); // how much
13810 if (sub > (wi/2)) { // far enough to snap
13811 diffX = (diffX > 0) ? diffX-sub + wi : diffX+sub - wi;
13813 // remove difference..
13814 diffX = (diffX > 0) ? diffX-sub : diffX+sub;
13818 x = Math.max(this.minX, x);
13821 diffX = this.constrain(w, diffX, mw, mxw);
13827 w = Math.min(Math.max(mw, w), mxw);
13828 diffY = this.constrain(h, diffY, mh, mxh);
13833 diffX = this.constrain(w, diffX, mw, mxw);
13834 diffY = this.constrain(h, diffY, mh, mxh);
13841 diffX = this.constrain(w, diffX, mw, mxw);
13843 h = Math.min(Math.max(mh, h), mxh);
13849 var sw = this.snap(w, wi, mw);
13850 var sh = this.snap(h, hi, mh);
13851 if(sw != w || sh != h){
13874 if(this.preserveRatio){
13879 h = Math.min(Math.max(mh, h), mxh);
13884 w = Math.min(Math.max(mw, w), mxw);
13889 w = Math.min(Math.max(mw, w), mxw);
13895 w = Math.min(Math.max(mw, w), mxw);
13901 h = Math.min(Math.max(mh, h), mxh);
13909 h = Math.min(Math.max(mh, h), mxh);
13919 h = Math.min(Math.max(mh, h), mxh);
13927 if (pos == 'hdrag') {
13930 this.proxy.setBounds(x, y, w, h);
13932 this.resizeElement();
13936 this.fireEvent("resizing", this, x, y, w, h, e);
13940 handleOver : function(){
13942 this.el.addClass("x-resizable-over");
13947 handleOut : function(){
13948 if(!this.resizing){
13949 this.el.removeClass("x-resizable-over");
13954 * Returns the element this component is bound to.
13955 * @return {Roo.Element}
13957 getEl : function(){
13962 * Returns the resizeChild element (or null).
13963 * @return {Roo.Element}
13965 getResizeChild : function(){
13966 return this.resizeChild;
13968 groupHandler : function()
13973 * Destroys this resizable. If the element was wrapped and
13974 * removeEl is not true then the element remains.
13975 * @param {Boolean} removeEl (optional) true to remove the element from the DOM
13977 destroy : function(removeEl){
13978 this.proxy.remove();
13980 this.overlay.removeAllListeners();
13981 this.overlay.remove();
13983 var ps = Roo.Resizable.positions;
13985 if(typeof ps[k] != "function" && this[ps[k]]){
13986 var h = this[ps[k]];
13987 h.el.removeAllListeners();
13992 this.el.update("");
13999 // hash to map config positions to true positions
14000 Roo.Resizable.positions = {
14001 n: "north", s: "south", e: "east", w: "west", se: "southeast", sw: "southwest", nw: "northwest", ne: "northeast",
14006 Roo.Resizable.Handle = function(rz, pos, disableTrackOver, transparent){
14008 // only initialize the template if resizable is used
14009 var tpl = Roo.DomHelper.createTemplate(
14010 {tag: "div", cls: "x-resizable-handle x-resizable-handle-{0}"}
14013 Roo.Resizable.Handle.prototype.tpl = tpl;
14015 this.position = pos;
14017 // show north drag fro topdra
14018 var handlepos = pos == 'hdrag' ? 'north' : pos;
14020 this.el = this.tpl.append(rz.el.dom, [handlepos], true);
14021 if (pos == 'hdrag') {
14022 this.el.setStyle('cursor', 'pointer');
14024 this.el.unselectable();
14026 this.el.setOpacity(0);
14028 this.el.on("mousedown", this.onMouseDown, this);
14029 if(!disableTrackOver){
14030 this.el.on("mouseover", this.onMouseOver, this);
14031 this.el.on("mouseout", this.onMouseOut, this);
14036 Roo.Resizable.Handle.prototype = {
14037 afterResize : function(rz){
14041 onMouseDown : function(e){
14042 this.rz.onMouseDown(this, e);
14045 onMouseOver : function(e){
14046 this.rz.handleOver(this, e);
14049 onMouseOut : function(e){
14050 this.rz.handleOut(this, e);
14054 * Ext JS Library 1.1.1
14055 * Copyright(c) 2006-2007, Ext JS, LLC.
14057 * Originally Released Under LGPL - original licence link has changed is not relivant.
14060 * <script type="text/javascript">
14064 * @class Roo.Editor
14065 * @extends Roo.Component
14066 * A base editor field that handles displaying/hiding on demand and has some built-in sizing and event handling logic.
14068 * Create a new Editor
14069 * @param {Roo.form.Field} field The Field object (or descendant)
14070 * @param {Object} config The config object
14072 Roo.Editor = function(field, config){
14073 Roo.Editor.superclass.constructor.call(this, config);
14074 this.field = field;
14077 * @event beforestartedit
14078 * Fires when editing is initiated, but before the value changes. Editing can be canceled by returning
14079 * false from the handler of this event.
14080 * @param {Editor} this
14081 * @param {Roo.Element} boundEl The underlying element bound to this editor
14082 * @param {Mixed} value The field value being set
14084 "beforestartedit" : true,
14087 * Fires when this editor is displayed
14088 * @param {Roo.Element} boundEl The underlying element bound to this editor
14089 * @param {Mixed} value The starting field value
14091 "startedit" : true,
14093 * @event beforecomplete
14094 * Fires after a change has been made to the field, but before the change is reflected in the underlying
14095 * field. Saving the change to the field can be canceled by returning false from the handler of this event.
14096 * Note that if the value has not changed and ignoreNoChange = true, the editing will still end but this
14097 * event will not fire since no edit actually occurred.
14098 * @param {Editor} this
14099 * @param {Mixed} value The current field value
14100 * @param {Mixed} startValue The original field value
14102 "beforecomplete" : true,
14105 * Fires after editing is complete and any changed value has been written to the underlying field.
14106 * @param {Editor} this
14107 * @param {Mixed} value The current field value
14108 * @param {Mixed} startValue The original field value
14112 * @event specialkey
14113 * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed. You can check
14114 * {@link Roo.EventObject#getKey} to determine which key was pressed.
14115 * @param {Roo.form.Field} this
14116 * @param {Roo.EventObject} e The event object
14118 "specialkey" : true
14122 Roo.extend(Roo.Editor, Roo.Component, {
14124 * @cfg {Boolean/String} autosize
14125 * True for the editor to automatically adopt the size of the underlying field, "width" to adopt the width only,
14126 * or "height" to adopt the height only (defaults to false)
14129 * @cfg {Boolean} revertInvalid
14130 * True to automatically revert the field value and cancel the edit when the user completes an edit and the field
14131 * validation fails (defaults to true)
14134 * @cfg {Boolean} ignoreNoChange
14135 * True to skip the the edit completion process (no save, no events fired) if the user completes an edit and
14136 * the value has not changed (defaults to false). Applies only to string values - edits for other data types
14137 * will never be ignored.
14140 * @cfg {Boolean} hideEl
14141 * False to keep the bound element visible while the editor is displayed (defaults to true)
14144 * @cfg {Mixed} value
14145 * The data value of the underlying field (defaults to "")
14149 * @cfg {String} alignment
14150 * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "c-c?").
14154 * @cfg {Boolean/String} shadow "sides" for sides/bottom only, "frame" for 4-way shadow, and "drop"
14155 * for bottom-right shadow (defaults to "frame")
14159 * @cfg {Boolean} constrain True to constrain the editor to the viewport
14163 * @cfg {Boolean} completeOnEnter True to complete the edit when the enter key is pressed (defaults to false)
14165 completeOnEnter : false,
14167 * @cfg {Boolean} cancelOnEsc True to cancel the edit when the escape key is pressed (defaults to false)
14169 cancelOnEsc : false,
14171 * @cfg {Boolean} updateEl True to update the innerHTML of the bound element when the update completes (defaults to false)
14176 onRender : function(ct, position){
14177 this.el = new Roo.Layer({
14178 shadow: this.shadow,
14184 constrain: this.constrain
14186 this.el.setStyle("overflow", Roo.isGecko ? "auto" : "hidden");
14187 if(this.field.msgTarget != 'title'){
14188 this.field.msgTarget = 'qtip';
14190 this.field.render(this.el);
14192 this.field.el.dom.setAttribute('autocomplete', 'off');
14194 this.field.on("specialkey", this.onSpecialKey, this);
14195 if(this.swallowKeys){
14196 this.field.el.swallowEvent(['keydown','keypress']);
14199 this.field.on("blur", this.onBlur, this);
14200 if(this.field.grow){
14201 this.field.on("autosize", this.el.sync, this.el, {delay:1});
14205 onSpecialKey : function(field, e)
14207 //Roo.log('editor onSpecialKey');
14208 if(this.completeOnEnter && e.getKey() == e.ENTER){
14210 this.completeEdit();
14213 // do not fire special key otherwise it might hide close the editor...
14214 if(e.getKey() == e.ENTER){
14217 if(this.cancelOnEsc && e.getKey() == e.ESC){
14221 this.fireEvent('specialkey', field, e);
14226 * Starts the editing process and shows the editor.
14227 * @param {String/HTMLElement/Element} el The element to edit
14228 * @param {String} value (optional) A value to initialize the editor with. If a value is not provided, it defaults
14229 * to the innerHTML of el.
14231 startEdit : function(el, value){
14233 this.completeEdit();
14235 this.boundEl = Roo.get(el);
14236 var v = value !== undefined ? value : this.boundEl.dom.innerHTML;
14237 if(!this.rendered){
14238 this.render(this.parentEl || document.body);
14240 if(this.fireEvent("beforestartedit", this, this.boundEl, v) === false){
14243 this.startValue = v;
14244 this.field.setValue(v);
14246 var sz = this.boundEl.getSize();
14247 switch(this.autoSize){
14249 this.setSize(sz.width, "");
14252 this.setSize("", sz.height);
14255 this.setSize(sz.width, sz.height);
14258 this.el.alignTo(this.boundEl, this.alignment);
14259 this.editing = true;
14261 Roo.QuickTips.disable();
14267 * Sets the height and width of this editor.
14268 * @param {Number} width The new width
14269 * @param {Number} height The new height
14271 setSize : function(w, h){
14272 this.field.setSize(w, h);
14279 * Realigns the editor to the bound field based on the current alignment config value.
14281 realign : function(){
14282 this.el.alignTo(this.boundEl, this.alignment);
14286 * Ends the editing process, persists the changed value to the underlying field, and hides the editor.
14287 * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after edit (defaults to false)
14289 completeEdit : function(remainVisible){
14293 var v = this.getValue();
14294 if(this.revertInvalid !== false && !this.field.isValid()){
14295 v = this.startValue;
14296 this.cancelEdit(true);
14298 if(String(v) === String(this.startValue) && this.ignoreNoChange){
14299 this.editing = false;
14303 if(this.fireEvent("beforecomplete", this, v, this.startValue) !== false){
14304 this.editing = false;
14305 if(this.updateEl && this.boundEl){
14306 this.boundEl.update(v);
14308 if(remainVisible !== true){
14311 this.fireEvent("complete", this, v, this.startValue);
14316 onShow : function(){
14318 if(this.hideEl !== false){
14319 this.boundEl.hide();
14322 if(Roo.isIE && !this.fixIEFocus){ // IE has problems with focusing the first time
14323 this.fixIEFocus = true;
14324 this.deferredFocus.defer(50, this);
14326 this.field.focus();
14328 this.fireEvent("startedit", this.boundEl, this.startValue);
14331 deferredFocus : function(){
14333 this.field.focus();
14338 * Cancels the editing process and hides the editor without persisting any changes. The field value will be
14339 * reverted to the original starting value.
14340 * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after
14341 * cancel (defaults to false)
14343 cancelEdit : function(remainVisible){
14345 this.setValue(this.startValue);
14346 if(remainVisible !== true){
14353 onBlur : function(){
14354 if(this.allowBlur !== true && this.editing){
14355 this.completeEdit();
14360 onHide : function(){
14362 this.completeEdit();
14366 if(this.field.collapse){
14367 this.field.collapse();
14370 if(this.hideEl !== false){
14371 this.boundEl.show();
14374 Roo.QuickTips.enable();
14379 * Sets the data value of the editor
14380 * @param {Mixed} value Any valid value supported by the underlying field
14382 setValue : function(v){
14383 this.field.setValue(v);
14387 * Gets the data value of the editor
14388 * @return {Mixed} The data value
14390 getValue : function(){
14391 return this.field.getValue();
14395 * Ext JS Library 1.1.1
14396 * Copyright(c) 2006-2007, Ext JS, LLC.
14398 * Originally Released Under LGPL - original licence link has changed is not relivant.
14401 * <script type="text/javascript">
14405 * @class Roo.BasicDialog
14406 * @extends Roo.util.Observable
14407 * Lightweight Dialog Class. The code below shows the creation of a typical dialog using existing HTML markup:
14409 var dlg = new Roo.BasicDialog("my-dlg", {
14418 dlg.addKeyListener(27, dlg.hide, dlg); // ESC can also close the dialog
14419 dlg.addButton('OK', dlg.hide, dlg); // Could call a save function instead of hiding
14420 dlg.addButton('Cancel', dlg.hide, dlg);
14423 <b>A Dialog should always be a direct child of the body element.</b>
14424 * @cfg {Boolean/DomHelper} autoCreate True to auto create from scratch, or using a DomHelper Object (defaults to false)
14425 * @cfg {String} title Default text to display in the title bar (defaults to null)
14426 * @cfg {Number} width Width of the dialog in pixels (can also be set via CSS). Determined by browser if unspecified.
14427 * @cfg {Number} height Height of the dialog in pixels (can also be set via CSS). Determined by browser if unspecified.
14428 * @cfg {Number} x The default left page coordinate of the dialog (defaults to center screen)
14429 * @cfg {Number} y The default top page coordinate of the dialog (defaults to center screen)
14430 * @cfg {String/Element} animateTarget Id or element from which the dialog should animate while opening
14431 * (defaults to null with no animation)
14432 * @cfg {Boolean} resizable False to disable manual dialog resizing (defaults to true)
14433 * @cfg {String} resizeHandles Which resize handles to display - see the {@link Roo.Resizable} handles config
14434 * property for valid values (defaults to 'all')
14435 * @cfg {Number} minHeight The minimum allowable height for a resizable dialog (defaults to 80)
14436 * @cfg {Number} minWidth The minimum allowable width for a resizable dialog (defaults to 200)
14437 * @cfg {Boolean} modal True to show the dialog modally, preventing user interaction with the rest of the page (defaults to false)
14438 * @cfg {Boolean} autoScroll True to allow the dialog body contents to overflow and display scrollbars (defaults to false)
14439 * @cfg {Boolean} closable False to remove the built-in top-right corner close button (defaults to true)
14440 * @cfg {Boolean} collapsible False to remove the built-in top-right corner collapse button (defaults to true)
14441 * @cfg {Boolean} constraintoviewport True to keep the dialog constrained within the visible viewport boundaries (defaults to true)
14442 * @cfg {Boolean} syncHeightBeforeShow True to cause the dimensions to be recalculated before the dialog is shown (defaults to false)
14443 * @cfg {Boolean} draggable False to disable dragging of the dialog within the viewport (defaults to true)
14444 * @cfg {Boolean} autoTabs If true, all elements with class 'x-dlg-tab' will get automatically converted to tabs (defaults to false)
14445 * @cfg {String} tabTag The tag name of tab elements, used when autoTabs = true (defaults to 'div')
14446 * @cfg {Boolean} proxyDrag True to drag a lightweight proxy element rather than the dialog itself, used when
14447 * draggable = true (defaults to false)
14448 * @cfg {Boolean} fixedcenter True to ensure that anytime the dialog is shown or resized it gets centered (defaults to false)
14449 * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
14450 * shadow (defaults to false)
14451 * @cfg {Number} shadowOffset The number of pixels to offset the shadow if displayed (defaults to 5)
14452 * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "right")
14453 * @cfg {Number} minButtonWidth Minimum width of all dialog buttons (defaults to 75)
14454 * @cfg {Array} buttons Array of buttons
14455 * @cfg {Boolean} shim True to create an iframe shim that prevents selects from showing through (defaults to false)
14457 * Create a new BasicDialog.
14458 * @param {String/HTMLElement/Roo.Element} el The container element or DOM node, or its id
14459 * @param {Object} config Configuration options
14461 Roo.BasicDialog = function(el, config){
14462 this.el = Roo.get(el);
14463 var dh = Roo.DomHelper;
14464 if(!this.el && config && config.autoCreate){
14465 if(typeof config.autoCreate == "object"){
14466 if(!config.autoCreate.id){
14467 config.autoCreate.id = el;
14469 this.el = dh.append(document.body,
14470 config.autoCreate, true);
14472 this.el = dh.append(document.body,
14473 {tag: "div", id: el, style:'visibility:hidden;'}, true);
14477 el.setDisplayed(true);
14478 el.hide = this.hideAction;
14480 el.addClass("x-dlg");
14482 Roo.apply(this, config);
14484 this.proxy = el.createProxy("x-dlg-proxy");
14485 this.proxy.hide = this.hideAction;
14486 this.proxy.setOpacity(.5);
14490 el.setWidth(config.width);
14493 el.setHeight(config.height);
14495 this.size = el.getSize();
14496 if(typeof config.x != "undefined" && typeof config.y != "undefined"){
14497 this.xy = [config.x,config.y];
14499 this.xy = el.getCenterXY(true);
14501 /** The header element @type Roo.Element */
14502 this.header = el.child("> .x-dlg-hd");
14503 /** The body element @type Roo.Element */
14504 this.body = el.child("> .x-dlg-bd");
14505 /** The footer element @type Roo.Element */
14506 this.footer = el.child("> .x-dlg-ft");
14509 this.header = el.createChild({tag: "div", cls:"x-dlg-hd", html: " "}, this.body ? this.body.dom : null);
14512 this.body = el.createChild({tag: "div", cls:"x-dlg-bd"});
14515 this.header.unselectable();
14517 this.header.update(this.title);
14519 // this element allows the dialog to be focused for keyboard event
14520 this.focusEl = el.createChild({tag: "a", href:"#", cls:"x-dlg-focus", tabIndex:"-1"});
14521 this.focusEl.swallowEvent("click", true);
14523 this.header.wrap({cls:"x-dlg-hd-right"}).wrap({cls:"x-dlg-hd-left"}, true);
14525 // wrap the body and footer for special rendering
14526 this.bwrap = this.body.wrap({tag: "div", cls:"x-dlg-dlg-body"});
14528 this.bwrap.dom.appendChild(this.footer.dom);
14531 this.bg = this.el.createChild({
14532 tag: "div", cls:"x-dlg-bg",
14533 html: '<div class="x-dlg-bg-left"><div class="x-dlg-bg-right"><div class="x-dlg-bg-center"> </div></div></div>'
14535 this.centerBg = this.bg.child("div.x-dlg-bg-center");
14538 if(this.autoScroll !== false && !this.autoTabs){
14539 this.body.setStyle("overflow", "auto");
14542 this.toolbox = this.el.createChild({cls: "x-dlg-toolbox"});
14544 if(this.closable !== false){
14545 this.el.addClass("x-dlg-closable");
14546 this.close = this.toolbox.createChild({cls:"x-dlg-close"});
14547 this.close.on("click", this.closeClick, this);
14548 this.close.addClassOnOver("x-dlg-close-over");
14550 if(this.collapsible !== false){
14551 this.collapseBtn = this.toolbox.createChild({cls:"x-dlg-collapse"});
14552 this.collapseBtn.on("click", this.collapseClick, this);
14553 this.collapseBtn.addClassOnOver("x-dlg-collapse-over");
14554 this.header.on("dblclick", this.collapseClick, this);
14556 if(this.resizable !== false){
14557 this.el.addClass("x-dlg-resizable");
14558 this.resizer = new Roo.Resizable(el, {
14559 minWidth: this.minWidth || 80,
14560 minHeight:this.minHeight || 80,
14561 handles: this.resizeHandles || "all",
14564 this.resizer.on("beforeresize", this.beforeResize, this);
14565 this.resizer.on("resize", this.onResize, this);
14567 if(this.draggable !== false){
14568 el.addClass("x-dlg-draggable");
14569 if (!this.proxyDrag) {
14570 var dd = new Roo.dd.DD(el.dom.id, "WindowDrag");
14573 var dd = new Roo.dd.DDProxy(el.dom.id, "WindowDrag", {dragElId: this.proxy.id});
14575 dd.setHandleElId(this.header.id);
14576 dd.endDrag = this.endMove.createDelegate(this);
14577 dd.startDrag = this.startMove.createDelegate(this);
14578 dd.onDrag = this.onDrag.createDelegate(this);
14583 this.mask = dh.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
14584 this.mask.enableDisplayMode("block");
14586 this.el.addClass("x-dlg-modal");
14589 this.shadow = new Roo.Shadow({
14590 mode : typeof this.shadow == "string" ? this.shadow : "sides",
14591 offset : this.shadowOffset
14594 this.shadowOffset = 0;
14596 if(Roo.useShims && this.shim !== false){
14597 this.shim = this.el.createShim();
14598 this.shim.hide = this.hideAction;
14606 if (this.buttons) {
14607 var bts= this.buttons;
14609 Roo.each(bts, function(b) {
14618 * Fires when a key is pressed
14619 * @param {Roo.BasicDialog} this
14620 * @param {Roo.EventObject} e
14625 * Fires when this dialog is moved by the user.
14626 * @param {Roo.BasicDialog} this
14627 * @param {Number} x The new page X
14628 * @param {Number} y The new page Y
14633 * Fires when this dialog is resized by the user.
14634 * @param {Roo.BasicDialog} this
14635 * @param {Number} width The new width
14636 * @param {Number} height The new height
14640 * @event beforehide
14641 * Fires before this dialog is hidden.
14642 * @param {Roo.BasicDialog} this
14644 "beforehide" : true,
14647 * Fires when this dialog is hidden.
14648 * @param {Roo.BasicDialog} this
14652 * @event beforeshow
14653 * Fires before this dialog is shown.
14654 * @param {Roo.BasicDialog} this
14656 "beforeshow" : true,
14659 * Fires when this dialog is shown.
14660 * @param {Roo.BasicDialog} this
14664 el.on("keydown", this.onKeyDown, this);
14665 el.on("mousedown", this.toFront, this);
14666 Roo.EventManager.onWindowResize(this.adjustViewport, this, true);
14668 Roo.DialogManager.register(this);
14669 Roo.BasicDialog.superclass.constructor.call(this);
14672 Roo.extend(Roo.BasicDialog, Roo.util.Observable, {
14673 shadowOffset: Roo.isIE ? 6 : 5,
14676 minButtonWidth: 75,
14677 defaultButton: null,
14678 buttonAlign: "right",
14683 * Sets the dialog title text
14684 * @param {String} text The title text to display
14685 * @return {Roo.BasicDialog} this
14687 setTitle : function(text){
14688 this.header.update(text);
14693 closeClick : function(){
14698 collapseClick : function(){
14699 this[this.collapsed ? "expand" : "collapse"]();
14703 * Collapses the dialog to its minimized state (only the title bar is visible).
14704 * Equivalent to the user clicking the collapse dialog button.
14706 collapse : function(){
14707 if(!this.collapsed){
14708 this.collapsed = true;
14709 this.el.addClass("x-dlg-collapsed");
14710 this.restoreHeight = this.el.getHeight();
14711 this.resizeTo(this.el.getWidth(), this.header.getHeight());
14716 * Expands a collapsed dialog back to its normal state. Equivalent to the user
14717 * clicking the expand dialog button.
14719 expand : function(){
14720 if(this.collapsed){
14721 this.collapsed = false;
14722 this.el.removeClass("x-dlg-collapsed");
14723 this.resizeTo(this.el.getWidth(), this.restoreHeight);
14728 * Reinitializes the tabs component, clearing out old tabs and finding new ones.
14729 * @return {Roo.TabPanel} The tabs component
14731 initTabs : function(){
14732 var tabs = this.getTabs();
14733 while(tabs.getTab(0)){
14736 this.el.select(this.tabTag+'.x-dlg-tab').each(function(el){
14738 tabs.addTab(Roo.id(dom), dom.title);
14746 beforeResize : function(){
14747 this.resizer.minHeight = Math.max(this.minHeight, this.getHeaderFooterHeight(true)+40);
14751 onResize : function(){
14752 this.refreshSize();
14753 this.syncBodyHeight();
14754 this.adjustAssets();
14756 this.fireEvent("resize", this, this.size.width, this.size.height);
14760 onKeyDown : function(e){
14761 if(this.isVisible()){
14762 this.fireEvent("keydown", this, e);
14767 * Resizes the dialog.
14768 * @param {Number} width
14769 * @param {Number} height
14770 * @return {Roo.BasicDialog} this
14772 resizeTo : function(width, height){
14773 this.el.setSize(width, height);
14774 this.size = {width: width, height: height};
14775 this.syncBodyHeight();
14776 if(this.fixedcenter){
14779 if(this.isVisible()){
14780 this.constrainXY();
14781 this.adjustAssets();
14783 this.fireEvent("resize", this, width, height);
14789 * Resizes the dialog to fit the specified content size.
14790 * @param {Number} width
14791 * @param {Number} height
14792 * @return {Roo.BasicDialog} this
14794 setContentSize : function(w, h){
14795 h += this.getHeaderFooterHeight() + this.body.getMargins("tb");
14796 w += this.body.getMargins("lr") + this.bwrap.getMargins("lr") + this.centerBg.getPadding("lr");
14797 //if(!this.el.isBorderBox()){
14798 h += this.body.getPadding("tb") + this.bwrap.getBorderWidth("tb") + this.body.getBorderWidth("tb") + this.el.getBorderWidth("tb");
14799 w += this.body.getPadding("lr") + this.bwrap.getBorderWidth("lr") + this.body.getBorderWidth("lr") + this.bwrap.getPadding("lr") + this.el.getBorderWidth("lr");
14802 h += this.tabs.stripWrap.getHeight() + this.tabs.bodyEl.getMargins("tb") + this.tabs.bodyEl.getPadding("tb");
14803 w += this.tabs.bodyEl.getMargins("lr") + this.tabs.bodyEl.getPadding("lr");
14805 this.resizeTo(w, h);
14810 * Adds a key listener for when this dialog is displayed. This allows you to hook in a function that will be
14811 * executed in response to a particular key being pressed while the dialog is active.
14812 * @param {Number/Array/Object} key Either the numeric key code, array of key codes or an object with the following options:
14813 * {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
14814 * @param {Function} fn The function to call
14815 * @param {Object} scope (optional) The scope of the function
14816 * @return {Roo.BasicDialog} this
14818 addKeyListener : function(key, fn, scope){
14819 var keyCode, shift, ctrl, alt;
14820 if(typeof key == "object" && !(key instanceof Array)){
14821 keyCode = key["key"];
14822 shift = key["shift"];
14823 ctrl = key["ctrl"];
14828 var handler = function(dlg, e){
14829 if((!shift || e.shiftKey) && (!ctrl || e.ctrlKey) && (!alt || e.altKey)){
14830 var k = e.getKey();
14831 if(keyCode instanceof Array){
14832 for(var i = 0, len = keyCode.length; i < len; i++){
14833 if(keyCode[i] == k){
14834 fn.call(scope || window, dlg, k, e);
14840 fn.call(scope || window, dlg, k, e);
14845 this.on("keydown", handler);
14850 * Returns the TabPanel component (creates it if it doesn't exist).
14851 * Note: If you wish to simply check for the existence of tabs without creating them,
14852 * check for a null 'tabs' property.
14853 * @return {Roo.TabPanel} The tabs component
14855 getTabs : function(){
14857 this.el.addClass("x-dlg-auto-tabs");
14858 this.body.addClass(this.tabPosition == "bottom" ? "x-tabs-bottom" : "x-tabs-top");
14859 this.tabs = new Roo.TabPanel(this.body.dom, this.tabPosition == "bottom");
14865 * Adds a button to the footer section of the dialog.
14866 * @param {String/Object} config A string becomes the button text, an object can either be a Button config
14867 * object or a valid Roo.DomHelper element config
14868 * @param {Function} handler The function called when the button is clicked
14869 * @param {Object} scope (optional) The scope of the handler function (accepts position as a property)
14870 * @return {Roo.Button} The new button
14872 addButton : function(config, handler, scope){
14873 var dh = Roo.DomHelper;
14875 this.footer = dh.append(this.bwrap, {tag: "div", cls:"x-dlg-ft"}, true);
14877 if(!this.btnContainer){
14878 var tb = this.footer.createChild({
14880 cls:"x-dlg-btns x-dlg-btns-"+this.buttonAlign,
14881 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
14883 this.btnContainer = tb.firstChild.firstChild.firstChild;
14888 minWidth: this.minButtonWidth,
14891 if(typeof config == "string"){
14892 bconfig.text = config;
14895 bconfig.dhconfig = config;
14897 Roo.apply(bconfig, config);
14901 if ((typeof(bconfig.position) != 'undefined') && bconfig.position < this.btnContainer.childNodes.length-1) {
14902 bconfig.position = Math.max(0, bconfig.position);
14903 fc = this.btnContainer.childNodes[bconfig.position];
14906 var btn = new Roo.Button(
14908 this.btnContainer.insertBefore(document.createElement("td"),fc)
14909 : this.btnContainer.appendChild(document.createElement("td")),
14910 //Roo.get(this.btnContainer).createChild( { tag: 'td'}, fc ),
14913 this.syncBodyHeight();
14916 * Array of all the buttons that have been added to this dialog via addButton
14921 this.buttons.push(btn);
14926 * Sets the default button to be focused when the dialog is displayed.
14927 * @param {Roo.BasicDialog.Button} btn The button object returned by {@link #addButton}
14928 * @return {Roo.BasicDialog} this
14930 setDefaultButton : function(btn){
14931 this.defaultButton = btn;
14936 getHeaderFooterHeight : function(safe){
14939 height += this.header.getHeight();
14942 var fm = this.footer.getMargins();
14943 height += (this.footer.getHeight()+fm.top+fm.bottom);
14945 height += this.bwrap.getPadding("tb")+this.bwrap.getBorderWidth("tb");
14946 height += this.centerBg.getPadding("tb");
14951 syncBodyHeight : function()
14953 var bd = this.body, // the text
14954 cb = this.centerBg, // wrapper around bottom.. but does not seem to be used..
14956 var height = this.size.height - this.getHeaderFooterHeight(false);
14957 bd.setHeight(height-bd.getMargins("tb"));
14958 var hh = this.header.getHeight();
14959 var h = this.size.height-hh;
14962 bw.setLeftTop(cb.getPadding("l"), hh+cb.getPadding("t"));
14963 bw.setHeight(h-cb.getPadding("tb"));
14965 bw.setWidth(this.el.getWidth(true)-cb.getPadding("lr"));
14966 bd.setWidth(bw.getWidth(true));
14968 this.tabs.syncHeight();
14970 this.tabs.el.repaint();
14976 * Restores the previous state of the dialog if Roo.state is configured.
14977 * @return {Roo.BasicDialog} this
14979 restoreState : function(){
14980 var box = Roo.state.Manager.get(this.stateId || (this.el.id + "-state"));
14981 if(box && box.width){
14982 this.xy = [box.x, box.y];
14983 this.resizeTo(box.width, box.height);
14989 beforeShow : function(){
14991 if(this.fixedcenter){
14992 this.xy = this.el.getCenterXY(true);
14995 Roo.get(document.body).addClass("x-body-masked");
14996 this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
14999 this.constrainXY();
15003 animShow : function(){
15004 var b = Roo.get(this.animateTarget).getBox();
15005 this.proxy.setSize(b.width, b.height);
15006 this.proxy.setLocation(b.x, b.y);
15008 this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height,
15009 true, .35, this.showEl.createDelegate(this));
15013 * Shows the dialog.
15014 * @param {String/HTMLElement/Roo.Element} animateTarget (optional) Reset the animation target
15015 * @return {Roo.BasicDialog} this
15017 show : function(animateTarget){
15018 if (this.fireEvent("beforeshow", this) === false){
15021 if(this.syncHeightBeforeShow){
15022 this.syncBodyHeight();
15023 }else if(this.firstShow){
15024 this.firstShow = false;
15025 this.syncBodyHeight(); // sync the height on the first show instead of in the constructor
15027 this.animateTarget = animateTarget || this.animateTarget;
15028 if(!this.el.isVisible()){
15030 if(this.animateTarget && Roo.get(this.animateTarget)){
15040 showEl : function(){
15042 this.el.setXY(this.xy);
15044 this.adjustAssets(true);
15047 // IE peekaboo bug - fix found by Dave Fenwick
15051 this.fireEvent("show", this);
15055 * Focuses the dialog. If a defaultButton is set, it will receive focus, otherwise the
15056 * dialog itself will receive focus.
15058 focus : function(){
15059 if(this.defaultButton){
15060 this.defaultButton.focus();
15062 this.focusEl.focus();
15067 constrainXY : function(){
15068 if(this.constraintoviewport !== false){
15069 if(!this.viewSize){
15070 if(this.container){
15071 var s = this.container.getSize();
15072 this.viewSize = [s.width, s.height];
15074 this.viewSize = [Roo.lib.Dom.getViewWidth(),Roo.lib.Dom.getViewHeight()];
15077 var s = Roo.get(this.container||document).getScroll();
15079 var x = this.xy[0], y = this.xy[1];
15080 var w = this.size.width, h = this.size.height;
15081 var vw = this.viewSize[0], vh = this.viewSize[1];
15082 // only move it if it needs it
15084 // first validate right/bottom
15085 if(x + w > vw+s.left){
15089 if(y + h > vh+s.top){
15093 // then make sure top/left isn't negative
15105 if(this.isVisible()){
15106 this.el.setLocation(x, y);
15107 this.adjustAssets();
15114 onDrag : function(){
15115 if(!this.proxyDrag){
15116 this.xy = this.el.getXY();
15117 this.adjustAssets();
15122 adjustAssets : function(doShow){
15123 var x = this.xy[0], y = this.xy[1];
15124 var w = this.size.width, h = this.size.height;
15125 if(doShow === true){
15127 this.shadow.show(this.el);
15133 if(this.shadow && this.shadow.isVisible()){
15134 this.shadow.show(this.el);
15136 if(this.shim && this.shim.isVisible()){
15137 this.shim.setBounds(x, y, w, h);
15142 adjustViewport : function(w, h){
15144 w = Roo.lib.Dom.getViewWidth();
15145 h = Roo.lib.Dom.getViewHeight();
15148 this.viewSize = [w, h];
15149 if(this.modal && this.mask.isVisible()){
15150 this.mask.setSize(w, h); // first make sure the mask isn't causing overflow
15151 this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
15153 if(this.isVisible()){
15154 this.constrainXY();
15159 * Destroys this dialog and all its supporting elements (including any tabs, shim,
15160 * shadow, proxy, mask, etc.) Also removes all event listeners.
15161 * @param {Boolean} removeEl (optional) true to remove the element from the DOM
15163 destroy : function(removeEl){
15164 if(this.isVisible()){
15165 this.animateTarget = null;
15168 Roo.EventManager.removeResizeListener(this.adjustViewport, this);
15170 this.tabs.destroy(removeEl);
15183 for(var i = 0, len = this.buttons.length; i < len; i++){
15184 this.buttons[i].destroy();
15187 this.el.removeAllListeners();
15188 if(removeEl === true){
15189 this.el.update("");
15192 Roo.DialogManager.unregister(this);
15196 startMove : function(){
15197 if(this.proxyDrag){
15200 if(this.constraintoviewport !== false){
15201 this.dd.constrainTo(document.body, {right: this.shadowOffset, bottom: this.shadowOffset});
15206 endMove : function(){
15207 if(!this.proxyDrag){
15208 Roo.dd.DD.prototype.endDrag.apply(this.dd, arguments);
15210 Roo.dd.DDProxy.prototype.endDrag.apply(this.dd, arguments);
15213 this.refreshSize();
15214 this.adjustAssets();
15216 this.fireEvent("move", this, this.xy[0], this.xy[1]);
15220 * Brings this dialog to the front of any other visible dialogs
15221 * @return {Roo.BasicDialog} this
15223 toFront : function(){
15224 Roo.DialogManager.bringToFront(this);
15229 * Sends this dialog to the back (under) of any other visible dialogs
15230 * @return {Roo.BasicDialog} this
15232 toBack : function(){
15233 Roo.DialogManager.sendToBack(this);
15238 * Centers this dialog in the viewport
15239 * @return {Roo.BasicDialog} this
15241 center : function(){
15242 var xy = this.el.getCenterXY(true);
15243 this.moveTo(xy[0], xy[1]);
15248 * Moves the dialog's top-left corner to the specified point
15249 * @param {Number} x
15250 * @param {Number} y
15251 * @return {Roo.BasicDialog} this
15253 moveTo : function(x, y){
15255 if(this.isVisible()){
15256 this.el.setXY(this.xy);
15257 this.adjustAssets();
15263 * Aligns the dialog to the specified element
15264 * @param {String/HTMLElement/Roo.Element} element The element to align to.
15265 * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details).
15266 * @param {Array} offsets (optional) Offset the positioning by [x, y]
15267 * @return {Roo.BasicDialog} this
15269 alignTo : function(element, position, offsets){
15270 this.xy = this.el.getAlignToXY(element, position, offsets);
15271 if(this.isVisible()){
15272 this.el.setXY(this.xy);
15273 this.adjustAssets();
15279 * Anchors an element to another element and realigns it when the window is resized.
15280 * @param {String/HTMLElement/Roo.Element} element The element to align to.
15281 * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details)
15282 * @param {Array} offsets (optional) Offset the positioning by [x, y]
15283 * @param {Boolean/Number} monitorScroll (optional) true to monitor body scroll and reposition. If this parameter
15284 * is a number, it is used as the buffer delay (defaults to 50ms).
15285 * @return {Roo.BasicDialog} this
15287 anchorTo : function(el, alignment, offsets, monitorScroll){
15288 var action = function(){
15289 this.alignTo(el, alignment, offsets);
15291 Roo.EventManager.onWindowResize(action, this);
15292 var tm = typeof monitorScroll;
15293 if(tm != 'undefined'){
15294 Roo.EventManager.on(window, 'scroll', action, this,
15295 {buffer: tm == 'number' ? monitorScroll : 50});
15302 * Returns true if the dialog is visible
15303 * @return {Boolean}
15305 isVisible : function(){
15306 return this.el.isVisible();
15310 animHide : function(callback){
15311 var b = Roo.get(this.animateTarget).getBox();
15313 this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height);
15315 this.proxy.setBounds(b.x, b.y, b.width, b.height, true, .35,
15316 this.hideEl.createDelegate(this, [callback]));
15320 * Hides the dialog.
15321 * @param {Function} callback (optional) Function to call when the dialog is hidden
15322 * @return {Roo.BasicDialog} this
15324 hide : function(callback){
15325 if (this.fireEvent("beforehide", this) === false){
15329 this.shadow.hide();
15334 // sometimes animateTarget seems to get set.. causing problems...
15335 // this just double checks..
15336 if(this.animateTarget && Roo.get(this.animateTarget)) {
15337 this.animHide(callback);
15340 this.hideEl(callback);
15346 hideEl : function(callback){
15350 Roo.get(document.body).removeClass("x-body-masked");
15352 this.fireEvent("hide", this);
15353 if(typeof callback == "function"){
15359 hideAction : function(){
15360 this.setLeft("-10000px");
15361 this.setTop("-10000px");
15362 this.setStyle("visibility", "hidden");
15366 refreshSize : function(){
15367 this.size = this.el.getSize();
15368 this.xy = this.el.getXY();
15369 Roo.state.Manager.set(this.stateId || this.el.id + "-state", this.el.getBox());
15373 // z-index is managed by the DialogManager and may be overwritten at any time
15374 setZIndex : function(index){
15376 this.mask.setStyle("z-index", index);
15379 this.shim.setStyle("z-index", ++index);
15382 this.shadow.setZIndex(++index);
15384 this.el.setStyle("z-index", ++index);
15386 this.proxy.setStyle("z-index", ++index);
15389 this.resizer.proxy.setStyle("z-index", ++index);
15392 this.lastZIndex = index;
15396 * Returns the element for this dialog
15397 * @return {Roo.Element} The underlying dialog Element
15399 getEl : function(){
15405 * @class Roo.DialogManager
15406 * Provides global access to BasicDialogs that have been created and
15407 * support for z-indexing (layering) multiple open dialogs.
15409 Roo.DialogManager = function(){
15411 var accessList = [];
15415 var sortDialogs = function(d1, d2){
15416 return (!d1._lastAccess || d1._lastAccess < d2._lastAccess) ? -1 : 1;
15420 var orderDialogs = function(){
15421 accessList.sort(sortDialogs);
15422 var seed = Roo.DialogManager.zseed;
15423 for(var i = 0, len = accessList.length; i < len; i++){
15424 var dlg = accessList[i];
15426 dlg.setZIndex(seed + (i*10));
15433 * The starting z-index for BasicDialogs (defaults to 9000)
15434 * @type Number The z-index value
15439 register : function(dlg){
15440 list[dlg.id] = dlg;
15441 accessList.push(dlg);
15445 unregister : function(dlg){
15446 delete list[dlg.id];
15449 if(!accessList.indexOf){
15450 for( i = 0, len = accessList.length; i < len; i++){
15451 if(accessList[i] == dlg){
15452 accessList.splice(i, 1);
15457 i = accessList.indexOf(dlg);
15459 accessList.splice(i, 1);
15465 * Gets a registered dialog by id
15466 * @param {String/Object} id The id of the dialog or a dialog
15467 * @return {Roo.BasicDialog} this
15469 get : function(id){
15470 return typeof id == "object" ? id : list[id];
15474 * Brings the specified dialog to the front
15475 * @param {String/Object} dlg The id of the dialog or a dialog
15476 * @return {Roo.BasicDialog} this
15478 bringToFront : function(dlg){
15479 dlg = this.get(dlg);
15482 dlg._lastAccess = new Date().getTime();
15489 * Sends the specified dialog to the back
15490 * @param {String/Object} dlg The id of the dialog or a dialog
15491 * @return {Roo.BasicDialog} this
15493 sendToBack : function(dlg){
15494 dlg = this.get(dlg);
15495 dlg._lastAccess = -(new Date().getTime());
15501 * Hides all dialogs
15503 hideAll : function(){
15504 for(var id in list){
15505 if(list[id] && typeof list[id] != "function" && list[id].isVisible()){
15514 * @class Roo.LayoutDialog
15515 * @extends Roo.BasicDialog
15516 * Dialog which provides adjustments for working with a layout in a Dialog.
15517 * Add your necessary layout config options to the dialog's config.<br>
15518 * Example usage (including a nested layout):
15521 dialog = new Roo.LayoutDialog("download-dlg", {
15530 // layout config merges with the dialog config
15532 tabPosition: "top",
15533 alwaysShowTabs: true
15536 dialog.addKeyListener(27, dialog.hide, dialog);
15537 dialog.setDefaultButton(dialog.addButton("Close", dialog.hide, dialog));
15538 dialog.addButton("Build It!", this.getDownload, this);
15540 // we can even add nested layouts
15541 var innerLayout = new Roo.BorderLayout("dl-inner", {
15551 innerLayout.beginUpdate();
15552 innerLayout.add("east", new Roo.ContentPanel("dl-details"));
15553 innerLayout.add("center", new Roo.ContentPanel("selection-panel"));
15554 innerLayout.endUpdate(true);
15556 var layout = dialog.getLayout();
15557 layout.beginUpdate();
15558 layout.add("center", new Roo.ContentPanel("standard-panel",
15559 {title: "Download the Source", fitToFrame:true}));
15560 layout.add("center", new Roo.NestedLayoutPanel(innerLayout,
15561 {title: "Build your own roo.js"}));
15562 layout.getRegion("center").showPanel(sp);
15563 layout.endUpdate();
15567 * @param {String/HTMLElement/Roo.Element} el The id of or container element, or config
15568 * @param {Object} config configuration options
15570 Roo.LayoutDialog = function(el, cfg){
15573 if (typeof(cfg) == 'undefined') {
15574 config = Roo.apply({}, el);
15575 // not sure why we use documentElement here.. - it should always be body.
15576 // IE7 borks horribly if we use documentElement.
15577 // webkit also does not like documentElement - it creates a body element...
15578 el = Roo.get( document.body || document.documentElement ).createChild();
15579 //config.autoCreate = true;
15583 config.autoTabs = false;
15584 Roo.LayoutDialog.superclass.constructor.call(this, el, config);
15585 this.body.setStyle({overflow:"hidden", position:"relative"});
15586 this.layout = new Roo.BorderLayout(this.body.dom, config);
15587 this.layout.monitorWindowResize = false;
15588 this.el.addClass("x-dlg-auto-layout");
15589 // fix case when center region overwrites center function
15590 this.center = Roo.BasicDialog.prototype.center;
15591 this.on("show", this.layout.layout, this.layout, true);
15592 if (config.items) {
15593 var xitems = config.items;
15594 delete config.items;
15595 Roo.each(xitems, this.addxtype, this);
15600 Roo.extend(Roo.LayoutDialog, Roo.BasicDialog, {
15602 * Ends update of the layout <strike>and resets display to none</strike>. Use standard beginUpdate/endUpdate on the layout.
15605 endUpdate : function(){
15606 this.layout.endUpdate();
15610 * Begins an update of the layout <strike>and sets display to block and visibility to hidden</strike>. Use standard beginUpdate/endUpdate on the layout.
15613 beginUpdate : function(){
15614 this.layout.beginUpdate();
15618 * Get the BorderLayout for this dialog
15619 * @return {Roo.BorderLayout}
15621 getLayout : function(){
15622 return this.layout;
15625 showEl : function(){
15626 Roo.LayoutDialog.superclass.showEl.apply(this, arguments);
15628 this.layout.layout();
15633 // Use the syncHeightBeforeShow config option to control this automatically
15634 syncBodyHeight : function(){
15635 Roo.LayoutDialog.superclass.syncBodyHeight.call(this);
15636 if(this.layout){this.layout.layout();}
15640 * Add an xtype element (actually adds to the layout.)
15641 * @return {Object} xdata xtype object data.
15644 addxtype : function(c) {
15645 return this.layout.addxtype(c);
15649 * Ext JS Library 1.1.1
15650 * Copyright(c) 2006-2007, Ext JS, LLC.
15652 * Originally Released Under LGPL - original licence link has changed is not relivant.
15655 * <script type="text/javascript">
15659 * @class Roo.MessageBox
15660 * Utility class for generating different styles of message boxes. The alias Roo.Msg can also be used.
15664 Roo.Msg.alert('Status', 'Changes saved successfully.');
15666 // Prompt for user data:
15667 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
15669 // process text value...
15673 // Show a dialog using config options:
15675 title:'Save Changes?',
15676 msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
15677 buttons: Roo.Msg.YESNOCANCEL,
15684 Roo.MessageBox = function(){
15685 var dlg, opt, mask, waitTimer;
15686 var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
15687 var buttons, activeTextEl, bwidth;
15690 var handleButton = function(button){
15692 Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
15696 var handleHide = function(){
15697 if(opt && opt.cls){
15698 dlg.el.removeClass(opt.cls);
15701 Roo.TaskMgr.stop(waitTimer);
15707 var updateButtons = function(b){
15710 buttons["ok"].hide();
15711 buttons["cancel"].hide();
15712 buttons["yes"].hide();
15713 buttons["no"].hide();
15714 dlg.footer.dom.style.display = 'none';
15717 dlg.footer.dom.style.display = '';
15718 for(var k in buttons){
15719 if(typeof buttons[k] != "function"){
15722 buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.MessageBox.buttonText[k]);
15723 width += buttons[k].el.getWidth()+15;
15733 var handleEsc = function(d, k, e){
15734 if(opt && opt.closable !== false){
15744 * Returns a reference to the underlying {@link Roo.BasicDialog} element
15745 * @return {Roo.BasicDialog} The BasicDialog element
15747 getDialog : function(){
15749 dlg = new Roo.BasicDialog("x-msg-box", {
15754 constraintoviewport:false,
15756 collapsible : false,
15759 width:400, height:100,
15760 buttonAlign:"center",
15761 closeClick : function(){
15762 if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
15763 handleButton("no");
15765 handleButton("cancel");
15769 dlg.on("hide", handleHide);
15771 dlg.addKeyListener(27, handleEsc);
15773 var bt = this.buttonText;
15774 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
15775 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
15776 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
15777 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
15778 bodyEl = dlg.body.createChild({
15780 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>'
15782 msgEl = bodyEl.dom.firstChild;
15783 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
15784 textboxEl.enableDisplayMode();
15785 textboxEl.addKeyListener([10,13], function(){
15786 if(dlg.isVisible() && opt && opt.buttons){
15787 if(opt.buttons.ok){
15788 handleButton("ok");
15789 }else if(opt.buttons.yes){
15790 handleButton("yes");
15794 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
15795 textareaEl.enableDisplayMode();
15796 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
15797 progressEl.enableDisplayMode();
15798 var pf = progressEl.dom.firstChild;
15800 pp = Roo.get(pf.firstChild);
15801 pp.setHeight(pf.offsetHeight);
15809 * Updates the message box body text
15810 * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
15811 * the XHTML-compliant non-breaking space character '&#160;')
15812 * @return {Roo.MessageBox} This message box
15814 updateText : function(text){
15815 if(!dlg.isVisible() && !opt.width){
15816 dlg.resizeTo(this.maxWidth, 100); // resize first so content is never clipped from previous shows
15818 msgEl.innerHTML = text || ' ';
15820 var cw = Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
15821 //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
15823 Math.min(opt.width || cw , this.maxWidth),
15824 Math.max(opt.minWidth || this.minWidth, bwidth)
15827 activeTextEl.setWidth(w);
15829 if(dlg.isVisible()){
15830 dlg.fixedcenter = false;
15832 // to big, make it scroll. = But as usual stupid IE does not support
15835 if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
15836 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
15837 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
15839 bodyEl.dom.style.height = '';
15840 bodyEl.dom.style.overflowY = '';
15843 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
15845 bodyEl.dom.style.overflowX = '';
15848 dlg.setContentSize(w, bodyEl.getHeight());
15849 if(dlg.isVisible()){
15850 dlg.fixedcenter = true;
15856 * Updates a progress-style message box's text and progress bar. Only relevant on message boxes
15857 * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
15858 * @param {Number} value Any number between 0 and 1 (e.g., .5)
15859 * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
15860 * @return {Roo.MessageBox} This message box
15862 updateProgress : function(value, text){
15864 this.updateText(text);
15866 if (pp) { // weird bug on my firefox - for some reason this is not defined
15867 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
15873 * Returns true if the message box is currently displayed
15874 * @return {Boolean} True if the message box is visible, else false
15876 isVisible : function(){
15877 return dlg && dlg.isVisible();
15881 * Hides the message box if it is displayed
15884 if(this.isVisible()){
15890 * Displays a new message box, or reinitializes an existing message box, based on the config options
15891 * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
15892 * The following config object properties are supported:
15894 Property Type Description
15895 ---------- --------------- ------------------------------------------------------------------------------------
15896 animEl String/Element An id or Element from which the message box should animate as it opens and
15897 closes (defaults to undefined)
15898 buttons Object/Boolean A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
15899 cancel:'Bar'}), or false to not show any buttons (defaults to false)
15900 closable Boolean False to hide the top-right close button (defaults to true). Note that
15901 progress and wait dialogs will ignore this property and always hide the
15902 close button as they can only be closed programmatically.
15903 cls String A custom CSS class to apply to the message box element
15904 defaultTextHeight Number The default height in pixels of the message box's multiline textarea if
15905 displayed (defaults to 75)
15906 fn Function A callback function to execute after closing the dialog. The arguments to the
15907 function will be btn (the name of the button that was clicked, if applicable,
15908 e.g. "ok"), and text (the value of the active text field, if applicable).
15909 Progress and wait dialogs will ignore this option since they do not respond to
15910 user actions and can only be closed programmatically, so any required function
15911 should be called by the same code after it closes the dialog.
15912 icon String A CSS class that provides a background image to be used as an icon for
15913 the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
15914 maxWidth Number The maximum width in pixels of the message box (defaults to 600)
15915 minWidth Number The minimum width in pixels of the message box (defaults to 100)
15916 modal Boolean False to allow user interaction with the page while the message box is
15917 displayed (defaults to true)
15918 msg String A string that will replace the existing message box body text (defaults
15919 to the XHTML-compliant non-breaking space character ' ')
15920 multiline Boolean True to prompt the user to enter multi-line text (defaults to false)
15921 progress Boolean True to display a progress bar (defaults to false)
15922 progressText String The text to display inside the progress bar if progress = true (defaults to '')
15923 prompt Boolean True to prompt the user to enter single-line text (defaults to false)
15924 proxyDrag Boolean True to display a lightweight proxy while dragging (defaults to false)
15925 title String The title text
15926 value String The string value to set into the active textbox element if displayed
15927 wait Boolean True to display a progress bar (defaults to false)
15928 width Number The width of the dialog in pixels
15935 msg: 'Please enter your address:',
15937 buttons: Roo.MessageBox.OKCANCEL,
15940 animEl: 'addAddressBtn'
15943 * @param {Object} config Configuration options
15944 * @return {Roo.MessageBox} This message box
15946 show : function(options)
15949 // this causes nightmares if you show one dialog after another
15950 // especially on callbacks..
15952 if(this.isVisible()){
15955 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
15956 Roo.log("Old Dialog Message:" + msgEl.innerHTML );
15957 Roo.log("New Dialog Message:" + options.msg )
15958 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
15959 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
15962 var d = this.getDialog();
15964 d.setTitle(opt.title || " ");
15965 d.close.setDisplayed(opt.closable !== false);
15966 activeTextEl = textboxEl;
15967 opt.prompt = opt.prompt || (opt.multiline ? true : false);
15972 textareaEl.setHeight(typeof opt.multiline == "number" ?
15973 opt.multiline : this.defaultTextHeight);
15974 activeTextEl = textareaEl;
15983 progressEl.setDisplayed(opt.progress === true);
15984 this.updateProgress(0);
15985 activeTextEl.dom.value = opt.value || "";
15987 dlg.setDefaultButton(activeTextEl);
15989 var bs = opt.buttons;
15992 db = buttons["ok"];
15993 }else if(bs && bs.yes){
15994 db = buttons["yes"];
15996 dlg.setDefaultButton(db);
15998 bwidth = updateButtons(opt.buttons);
15999 this.updateText(opt.msg);
16001 d.el.addClass(opt.cls);
16003 d.proxyDrag = opt.proxyDrag === true;
16004 d.modal = opt.modal !== false;
16005 d.mask = opt.modal !== false ? mask : false;
16006 if(!d.isVisible()){
16007 // force it to the end of the z-index stack so it gets a cursor in FF
16008 document.body.appendChild(dlg.el.dom);
16009 d.animateTarget = null;
16010 d.show(options.animEl);
16016 * Displays a message box with a progress bar. This message box has no buttons and is not closeable by
16017 * the user. You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
16018 * and closing the message box when the process is complete.
16019 * @param {String} title The title bar text
16020 * @param {String} msg The message box body text
16021 * @return {Roo.MessageBox} This message box
16023 progress : function(title, msg){
16030 minWidth: this.minProgressWidth,
16037 * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
16038 * If a callback function is passed it will be called after the user clicks the button, and the
16039 * id of the button that was clicked will be passed as the only parameter to the callback
16040 * (could also be the top-right close button).
16041 * @param {String} title The title bar text
16042 * @param {String} msg The message box body text
16043 * @param {Function} fn (optional) The callback function invoked after the message box is closed
16044 * @param {Object} scope (optional) The scope of the callback function
16045 * @return {Roo.MessageBox} This message box
16047 alert : function(title, msg, fn, scope){
16060 * Displays a message box with an infinitely auto-updating progress bar. This can be used to block user
16061 * interaction while waiting for a long-running process to complete that does not have defined intervals.
16062 * You are responsible for closing the message box when the process is complete.
16063 * @param {String} msg The message box body text
16064 * @param {String} title (optional) The title bar text
16065 * @return {Roo.MessageBox} This message box
16067 wait : function(msg, title){
16078 waitTimer = Roo.TaskMgr.start({
16080 Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
16088 * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
16089 * If a callback function is passed it will be called after the user clicks either button, and the id of the
16090 * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
16091 * @param {String} title The title bar text
16092 * @param {String} msg The message box body text
16093 * @param {Function} fn (optional) The callback function invoked after the message box is closed
16094 * @param {Object} scope (optional) The scope of the callback function
16095 * @return {Roo.MessageBox} This message box
16097 confirm : function(title, msg, fn, scope){
16101 buttons: this.YESNO,
16110 * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
16111 * JavaScript's Window.prompt). The prompt can be a single-line or multi-line textbox. If a callback function
16112 * is passed it will be called after the user clicks either button, and the id of the button that was clicked
16113 * (could also be the top-right close button) and the text that was entered will be passed as the two
16114 * parameters to the callback.
16115 * @param {String} title The title bar text
16116 * @param {String} msg The message box body text
16117 * @param {Function} fn (optional) The callback function invoked after the message box is closed
16118 * @param {Object} scope (optional) The scope of the callback function
16119 * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
16120 * property, or the height in pixels to create the textbox (defaults to false / single-line)
16121 * @return {Roo.MessageBox} This message box
16123 prompt : function(title, msg, fn, scope, multiline){
16127 buttons: this.OKCANCEL,
16132 multiline: multiline,
16139 * Button config that displays a single OK button
16144 * Button config that displays Yes and No buttons
16147 YESNO : {yes:true, no:true},
16149 * Button config that displays OK and Cancel buttons
16152 OKCANCEL : {ok:true, cancel:true},
16154 * Button config that displays Yes, No and Cancel buttons
16157 YESNOCANCEL : {yes:true, no:true, cancel:true},
16160 * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
16163 defaultTextHeight : 75,
16165 * The maximum width in pixels of the message box (defaults to 600)
16170 * The minimum width in pixels of the message box (defaults to 100)
16175 * The minimum width in pixels of the message box if it is a progress-style dialog. This is useful
16176 * for setting a different minimum width than text-only dialogs may need (defaults to 250)
16179 minProgressWidth : 250,
16181 * An object containing the default button text strings that can be overriden for localized language support.
16182 * Supported properties are: ok, cancel, yes and no.
16183 * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
16196 * Shorthand for {@link Roo.MessageBox}
16198 Roo.Msg = Roo.MessageBox;/*
16200 * Ext JS Library 1.1.1
16201 * Copyright(c) 2006-2007, Ext JS, LLC.
16203 * Originally Released Under LGPL - original licence link has changed is not relivant.
16206 * <script type="text/javascript">
16209 * @class Roo.QuickTips
16210 * Provides attractive and customizable tooltips for any element.
16213 Roo.QuickTips = function(){
16214 var el, tipBody, tipBodyText, tipTitle, tm, cfg, close, tagEls = {}, esc, removeCls = null, bdLeft, bdRight;
16215 var ce, bd, xy, dd;
16216 var visible = false, disabled = true, inited = false;
16217 var showProc = 1, hideProc = 1, dismissProc = 1, locks = [];
16219 var onOver = function(e){
16223 var t = e.getTarget();
16224 if(!t || t.nodeType !== 1 || t == document || t == document.body){
16227 if(ce && t == ce.el){
16228 clearTimeout(hideProc);
16231 if(t && tagEls[t.id]){
16232 tagEls[t.id].el = t;
16233 showProc = show.defer(tm.showDelay, tm, [tagEls[t.id]]);
16236 var ttp, et = Roo.fly(t);
16237 var ns = cfg.namespace;
16238 if(tm.interceptTitles && t.title){
16241 t.removeAttribute("title");
16242 e.preventDefault();
16244 ttp = t.qtip || et.getAttributeNS(ns, cfg.attribute);
16247 showProc = show.defer(tm.showDelay, tm, [{
16250 width: et.getAttributeNS(ns, cfg.width),
16251 autoHide: et.getAttributeNS(ns, cfg.hide) != "user",
16252 title: et.getAttributeNS(ns, cfg.title),
16253 cls: et.getAttributeNS(ns, cfg.cls)
16258 var onOut = function(e){
16259 clearTimeout(showProc);
16260 var t = e.getTarget();
16261 if(t && ce && ce.el == t && (tm.autoHide && ce.autoHide !== false)){
16262 hideProc = setTimeout(hide, tm.hideDelay);
16266 var onMove = function(e){
16272 if(tm.trackMouse && ce){
16277 var onDown = function(e){
16278 clearTimeout(showProc);
16279 clearTimeout(hideProc);
16281 if(tm.hideOnClick){
16284 tm.enable.defer(100, tm);
16289 var getPad = function(){
16290 return 2;//bdLeft.getPadding('l')+bdRight.getPadding('r');
16293 var show = function(o){
16297 clearTimeout(dismissProc);
16299 if(removeCls){ // in case manually hidden
16300 el.removeClass(removeCls);
16304 el.addClass(ce.cls);
16305 removeCls = ce.cls;
16308 tipTitle.update(ce.title);
16311 tipTitle.update('');
16314 el.dom.style.width = tm.maxWidth+'px';
16315 //tipBody.dom.style.width = '';
16316 tipBodyText.update(o.text);
16317 var p = getPad(), w = ce.width;
16319 var td = tipBodyText.dom;
16320 var aw = Math.max(td.offsetWidth, td.clientWidth, td.scrollWidth);
16321 if(aw > tm.maxWidth){
16323 }else if(aw < tm.minWidth){
16329 //tipBody.setWidth(w);
16330 el.setWidth(parseInt(w, 10) + p);
16331 if(ce.autoHide === false){
16332 close.setDisplayed(true);
16337 close.setDisplayed(false);
16343 el.avoidY = xy[1]-18;
16348 el.setStyle("visibility", "visible");
16349 el.fadeIn({callback: afterShow});
16355 var afterShow = function(){
16359 if(tm.autoDismiss && ce.autoHide !== false){
16360 dismissProc = setTimeout(hide, tm.autoDismissDelay);
16365 var hide = function(noanim){
16366 clearTimeout(dismissProc);
16367 clearTimeout(hideProc);
16369 if(el.isVisible()){
16371 if(noanim !== true && tm.animate){
16372 el.fadeOut({callback: afterHide});
16379 var afterHide = function(){
16382 el.removeClass(removeCls);
16389 * @cfg {Number} minWidth
16390 * The minimum width of the quick tip (defaults to 40)
16394 * @cfg {Number} maxWidth
16395 * The maximum width of the quick tip (defaults to 300)
16399 * @cfg {Boolean} interceptTitles
16400 * True to automatically use the element's DOM title value if available (defaults to false)
16402 interceptTitles : false,
16404 * @cfg {Boolean} trackMouse
16405 * True to have the quick tip follow the mouse as it moves over the target element (defaults to false)
16407 trackMouse : false,
16409 * @cfg {Boolean} hideOnClick
16410 * True to hide the quick tip if the user clicks anywhere in the document (defaults to true)
16412 hideOnClick : true,
16414 * @cfg {Number} showDelay
16415 * Delay in milliseconds before the quick tip displays after the mouse enters the target element (defaults to 500)
16419 * @cfg {Number} hideDelay
16420 * Delay in milliseconds before the quick tip hides when autoHide = true (defaults to 200)
16424 * @cfg {Boolean} autoHide
16425 * True to automatically hide the quick tip after the mouse exits the target element (defaults to true).
16426 * Used in conjunction with hideDelay.
16431 * True to automatically hide the quick tip after a set period of time, regardless of the user's actions
16432 * (defaults to true). Used in conjunction with autoDismissDelay.
16434 autoDismiss : true,
16437 * Delay in milliseconds before the quick tip hides when autoDismiss = true (defaults to 5000)
16439 autoDismissDelay : 5000,
16441 * @cfg {Boolean} animate
16442 * True to turn on fade animation. Defaults to false (ClearType/scrollbar flicker issues in IE7).
16447 * @cfg {String} title
16448 * Title text to display (defaults to ''). This can be any valid HTML markup.
16452 * @cfg {String} text
16453 * Body text to display (defaults to ''). This can be any valid HTML markup.
16457 * @cfg {String} cls
16458 * A CSS class to apply to the base quick tip element (defaults to '').
16462 * @cfg {Number} width
16463 * Width in pixels of the quick tip (defaults to auto). Width will be ignored if it exceeds the bounds of
16464 * minWidth or maxWidth.
16469 * Initialize and enable QuickTips for first use. This should be called once before the first attempt to access
16470 * or display QuickTips in a page.
16473 tm = Roo.QuickTips;
16474 cfg = tm.tagConfig;
16476 if(!Roo.isReady){ // allow calling of init() before onReady
16477 Roo.onReady(Roo.QuickTips.init, Roo.QuickTips);
16480 el = new Roo.Layer({cls:"x-tip", shadow:"drop", shim: true, constrain:true, shadowOffset:4});
16481 el.fxDefaults = {stopFx: true};
16482 // maximum custom styling
16483 //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>');
16484 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>');
16485 tipTitle = el.child('h3');
16486 tipTitle.enableDisplayMode("block");
16487 tipBody = el.child('div.x-tip-bd');
16488 tipBodyText = el.child('div.x-tip-bd-inner');
16489 //bdLeft = el.child('div.x-tip-bd-left');
16490 //bdRight = el.child('div.x-tip-bd-right');
16491 close = el.child('div.x-tip-close');
16492 close.enableDisplayMode("block");
16493 close.on("click", hide);
16494 var d = Roo.get(document);
16495 d.on("mousedown", onDown);
16496 d.on("mouseover", onOver);
16497 d.on("mouseout", onOut);
16498 d.on("mousemove", onMove);
16499 esc = d.addKeyListener(27, hide);
16502 dd = el.initDD("default", null, {
16503 onDrag : function(){
16507 dd.setHandleElId(tipTitle.id);
16516 * Configures a new quick tip instance and assigns it to a target element. The following config options
16519 Property Type Description
16520 ---------- --------------------- ------------------------------------------------------------------------
16521 target Element/String/Array An Element, id or array of ids that this quick tip should be tied to
16523 * @param {Object} config The config object
16525 register : function(config){
16526 var cs = config instanceof Array ? config : arguments;
16527 for(var i = 0, len = cs.length; i < len; i++) {
16529 var target = c.target;
16531 if(target instanceof Array){
16532 for(var j = 0, jlen = target.length; j < jlen; j++){
16533 tagEls[target[j]] = c;
16536 tagEls[typeof target == 'string' ? target : Roo.id(target)] = c;
16543 * Removes this quick tip from its element and destroys it.
16544 * @param {String/HTMLElement/Element} el The element from which the quick tip is to be removed.
16546 unregister : function(el){
16547 delete tagEls[Roo.id(el)];
16551 * Enable this quick tip.
16553 enable : function(){
16554 if(inited && disabled){
16556 if(locks.length < 1){
16563 * Disable this quick tip.
16565 disable : function(){
16567 clearTimeout(showProc);
16568 clearTimeout(hideProc);
16569 clearTimeout(dismissProc);
16577 * Returns true if the quick tip is enabled, else false.
16579 isEnabled : function(){
16586 attribute : "qtip",
16596 // backwards compat
16597 Roo.QuickTips.tips = Roo.QuickTips.register;/*
16599 * Ext JS Library 1.1.1
16600 * Copyright(c) 2006-2007, Ext JS, LLC.
16602 * Originally Released Under LGPL - original licence link has changed is not relivant.
16605 * <script type="text/javascript">
16610 * @class Roo.tree.TreePanel
16611 * @extends Roo.data.Tree
16613 * @cfg {Boolean} rootVisible false to hide the root node (defaults to true)
16614 * @cfg {Boolean} lines false to disable tree lines (defaults to true)
16615 * @cfg {Boolean} enableDD true to enable drag and drop
16616 * @cfg {Boolean} enableDrag true to enable just drag
16617 * @cfg {Boolean} enableDrop true to enable just drop
16618 * @cfg {Object} dragConfig Custom config to pass to the {@link Roo.tree.TreeDragZone} instance
16619 * @cfg {Object} dropConfig Custom config to pass to the {@link Roo.tree.TreeDropZone} instance
16620 * @cfg {String} ddGroup The DD group this TreePanel belongs to
16621 * @cfg {String} ddAppendOnly True if the tree should only allow append drops (use for trees which are sorted)
16622 * @cfg {Boolean} ddScroll true to enable YUI body scrolling
16623 * @cfg {Boolean} containerScroll true to register this container with ScrollManager
16624 * @cfg {Boolean} hlDrop false to disable node highlight on drop (defaults to the value of Roo.enableFx)
16625 * @cfg {String} hlColor The color of the node highlight (defaults to C3DAF9)
16626 * @cfg {Boolean} animate true to enable animated expand/collapse (defaults to the value of Roo.enableFx)
16627 * @cfg {Boolean} singleExpand true if only 1 node per branch may be expanded
16628 * @cfg {Boolean} selModel A tree selection model to use with this TreePanel (defaults to a {@link Roo.tree.DefaultSelectionModel})
16629 * @cfg {Boolean} loader A TreeLoader for use with this TreePanel
16630 * @cfg {Object|Roo.tree.TreeEditor} editor The TreeEditor or xtype data to display when clicked.
16631 * @cfg {String} pathSeparator The token used to separate sub-paths in path strings (defaults to '/')
16632 * @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>
16633 * @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>
16636 * @param {String/HTMLElement/Element} el The container element
16637 * @param {Object} config
16639 Roo.tree.TreePanel = function(el, config){
16641 var loader = false;
16643 root = config.root;
16644 delete config.root;
16646 if (config.loader) {
16647 loader = config.loader;
16648 delete config.loader;
16651 Roo.apply(this, config);
16652 Roo.tree.TreePanel.superclass.constructor.call(this);
16653 this.el = Roo.get(el);
16654 this.el.addClass('x-tree');
16655 //console.log(root);
16657 this.setRootNode( Roo.factory(root, Roo.tree));
16660 this.loader = Roo.factory(loader, Roo.tree);
16663 * Read-only. The id of the container element becomes this TreePanel's id.
16665 this.id = this.el.id;
16668 * @event beforeload
16669 * Fires before a node is loaded, return false to cancel
16670 * @param {Node} node The node being loaded
16672 "beforeload" : true,
16675 * Fires when a node is loaded
16676 * @param {Node} node The node that was loaded
16680 * @event textchange
16681 * Fires when the text for a node is changed
16682 * @param {Node} node The node
16683 * @param {String} text The new text
16684 * @param {String} oldText The old text
16686 "textchange" : true,
16688 * @event beforeexpand
16689 * Fires before a node is expanded, return false to cancel.
16690 * @param {Node} node The node
16691 * @param {Boolean} deep
16692 * @param {Boolean} anim
16694 "beforeexpand" : true,
16696 * @event beforecollapse
16697 * Fires before a node is collapsed, return false to cancel.
16698 * @param {Node} node The node
16699 * @param {Boolean} deep
16700 * @param {Boolean} anim
16702 "beforecollapse" : true,
16705 * Fires when a node is expanded
16706 * @param {Node} node The node
16710 * @event disabledchange
16711 * Fires when the disabled status of a node changes
16712 * @param {Node} node The node
16713 * @param {Boolean} disabled
16715 "disabledchange" : true,
16718 * Fires when a node is collapsed
16719 * @param {Node} node The node
16723 * @event beforeclick
16724 * Fires before click processing on a node. Return false to cancel the default action.
16725 * @param {Node} node The node
16726 * @param {Roo.EventObject} e The event object
16728 "beforeclick":true,
16730 * @event checkchange
16731 * Fires when a node with a checkbox's checked property changes
16732 * @param {Node} this This node
16733 * @param {Boolean} checked
16735 "checkchange":true,
16738 * Fires when a node is clicked
16739 * @param {Node} node The node
16740 * @param {Roo.EventObject} e The event object
16745 * Fires when a node is double clicked
16746 * @param {Node} node The node
16747 * @param {Roo.EventObject} e The event object
16751 * @event contextmenu
16752 * Fires when a node is right clicked
16753 * @param {Node} node The node
16754 * @param {Roo.EventObject} e The event object
16756 "contextmenu":true,
16758 * @event beforechildrenrendered
16759 * Fires right before the child nodes for a node are rendered
16760 * @param {Node} node The node
16762 "beforechildrenrendered":true,
16765 * Fires when a node starts being dragged
16766 * @param {Roo.tree.TreePanel} this
16767 * @param {Roo.tree.TreeNode} node
16768 * @param {event} e The raw browser event
16770 "startdrag" : true,
16773 * Fires when a drag operation is complete
16774 * @param {Roo.tree.TreePanel} this
16775 * @param {Roo.tree.TreeNode} node
16776 * @param {event} e The raw browser event
16781 * Fires when a dragged node is dropped on a valid DD target
16782 * @param {Roo.tree.TreePanel} this
16783 * @param {Roo.tree.TreeNode} node
16784 * @param {DD} dd The dd it was dropped on
16785 * @param {event} e The raw browser event
16789 * @event beforenodedrop
16790 * Fires when a DD object is dropped on a node in this tree for preprocessing. Return false to cancel the drop. The dropEvent
16791 * passed to handlers has the following properties:<br />
16792 * <ul style="padding:5px;padding-left:16px;">
16793 * <li>tree - The TreePanel</li>
16794 * <li>target - The node being targeted for the drop</li>
16795 * <li>data - The drag data from the drag source</li>
16796 * <li>point - The point of the drop - append, above or below</li>
16797 * <li>source - The drag source</li>
16798 * <li>rawEvent - Raw mouse event</li>
16799 * <li>dropNode - Drop node(s) provided by the source <b>OR</b> you can supply node(s)
16800 * to be inserted by setting them on this object.</li>
16801 * <li>cancel - Set this to true to cancel the drop.</li>
16803 * @param {Object} dropEvent
16805 "beforenodedrop" : true,
16808 * Fires after a DD object is dropped on a node in this tree. The dropEvent
16809 * passed to handlers has the following properties:<br />
16810 * <ul style="padding:5px;padding-left:16px;">
16811 * <li>tree - The TreePanel</li>
16812 * <li>target - The node being targeted for the drop</li>
16813 * <li>data - The drag data from the drag source</li>
16814 * <li>point - The point of the drop - append, above or below</li>
16815 * <li>source - The drag source</li>
16816 * <li>rawEvent - Raw mouse event</li>
16817 * <li>dropNode - Dropped node(s).</li>
16819 * @param {Object} dropEvent
16823 * @event nodedragover
16824 * Fires when a tree node is being targeted for a drag drop, return false to signal drop not allowed. The dragOverEvent
16825 * passed to handlers has the following properties:<br />
16826 * <ul style="padding:5px;padding-left:16px;">
16827 * <li>tree - The TreePanel</li>
16828 * <li>target - The node being targeted for the drop</li>
16829 * <li>data - The drag data from the drag source</li>
16830 * <li>point - The point of the drop - append, above or below</li>
16831 * <li>source - The drag source</li>
16832 * <li>rawEvent - Raw mouse event</li>
16833 * <li>dropNode - Drop node(s) provided by the source.</li>
16834 * <li>cancel - Set this to true to signal drop not allowed.</li>
16836 * @param {Object} dragOverEvent
16838 "nodedragover" : true
16841 if(this.singleExpand){
16842 this.on("beforeexpand", this.restrictExpand, this);
16845 this.editor.tree = this;
16846 this.editor = Roo.factory(this.editor, Roo.tree);
16849 if (this.selModel) {
16850 this.selModel = Roo.factory(this.selModel, Roo.tree);
16854 Roo.extend(Roo.tree.TreePanel, Roo.data.Tree, {
16855 rootVisible : true,
16856 animate: Roo.enableFx,
16859 hlDrop : Roo.enableFx,
16863 rendererTip: false,
16865 restrictExpand : function(node){
16866 var p = node.parentNode;
16868 if(p.expandedChild && p.expandedChild.parentNode == p){
16869 p.expandedChild.collapse();
16871 p.expandedChild = node;
16875 // private override
16876 setRootNode : function(node){
16877 Roo.tree.TreePanel.superclass.setRootNode.call(this, node);
16878 if(!this.rootVisible){
16879 node.ui = new Roo.tree.RootTreeNodeUI(node);
16885 * Returns the container element for this TreePanel
16887 getEl : function(){
16892 * Returns the default TreeLoader for this TreePanel
16894 getLoader : function(){
16895 return this.loader;
16901 expandAll : function(){
16902 this.root.expand(true);
16906 * Collapse all nodes
16908 collapseAll : function(){
16909 this.root.collapse(true);
16913 * Returns the selection model used by this TreePanel
16915 getSelectionModel : function(){
16916 if(!this.selModel){
16917 this.selModel = new Roo.tree.DefaultSelectionModel();
16919 return this.selModel;
16923 * Retrieve an array of checked nodes, or an array of a specific attribute of checked nodes (e.g. "id")
16924 * @param {String} attribute (optional) Defaults to null (return the actual nodes)
16925 * @param {TreeNode} startNode (optional) The node to start from, defaults to the root
16928 getChecked : function(a, startNode){
16929 startNode = startNode || this.root;
16931 var f = function(){
16932 if(this.attributes.checked){
16933 r.push(!a ? this : (a == 'id' ? this.id : this.attributes[a]));
16936 startNode.cascade(f);
16941 * Expands a specified path in this TreePanel. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
16942 * @param {String} path
16943 * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
16944 * @param {Function} callback (optional) The callback to call when the expand is complete. The callback will be called with
16945 * (bSuccess, oLastNode) where bSuccess is if the expand was successful and oLastNode is the last node that was expanded.
16947 expandPath : function(path, attr, callback){
16948 attr = attr || "id";
16949 var keys = path.split(this.pathSeparator);
16950 var curNode = this.root;
16951 if(curNode.attributes[attr] != keys[1]){ // invalid root
16953 callback(false, null);
16958 var f = function(){
16959 if(++index == keys.length){
16961 callback(true, curNode);
16965 var c = curNode.findChild(attr, keys[index]);
16968 callback(false, curNode);
16973 c.expand(false, false, f);
16975 curNode.expand(false, false, f);
16979 * Selects the node in this tree at the specified path. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
16980 * @param {String} path
16981 * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
16982 * @param {Function} callback (optional) The callback to call when the selection is complete. The callback will be called with
16983 * (bSuccess, oSelNode) where bSuccess is if the selection was successful and oSelNode is the selected node.
16985 selectPath : function(path, attr, callback){
16986 attr = attr || "id";
16987 var keys = path.split(this.pathSeparator);
16988 var v = keys.pop();
16989 if(keys.length > 0){
16990 var f = function(success, node){
16991 if(success && node){
16992 var n = node.findChild(attr, v);
16998 }else if(callback){
16999 callback(false, n);
17003 callback(false, n);
17007 this.expandPath(keys.join(this.pathSeparator), attr, f);
17009 this.root.select();
17011 callback(true, this.root);
17016 getTreeEl : function(){
17021 * Trigger rendering of this TreePanel
17023 render : function(){
17024 if (this.innerCt) {
17025 return this; // stop it rendering more than once!!
17028 this.innerCt = this.el.createChild({tag:"ul",
17029 cls:"x-tree-root-ct " +
17030 (this.lines ? "x-tree-lines" : "x-tree-no-lines")});
17032 if(this.containerScroll){
17033 Roo.dd.ScrollManager.register(this.el);
17035 if((this.enableDD || this.enableDrop) && !this.dropZone){
17037 * The dropZone used by this tree if drop is enabled
17038 * @type Roo.tree.TreeDropZone
17040 this.dropZone = new Roo.tree.TreeDropZone(this, this.dropConfig || {
17041 ddGroup: this.ddGroup || "TreeDD", appendOnly: this.ddAppendOnly === true
17044 if((this.enableDD || this.enableDrag) && !this.dragZone){
17046 * The dragZone used by this tree if drag is enabled
17047 * @type Roo.tree.TreeDragZone
17049 this.dragZone = new Roo.tree.TreeDragZone(this, this.dragConfig || {
17050 ddGroup: this.ddGroup || "TreeDD",
17051 scroll: this.ddScroll
17054 this.getSelectionModel().init(this);
17056 Roo.log("ROOT not set in tree");
17059 this.root.render();
17060 if(!this.rootVisible){
17061 this.root.renderChildren();
17067 * Ext JS Library 1.1.1
17068 * Copyright(c) 2006-2007, Ext JS, LLC.
17070 * Originally Released Under LGPL - original licence link has changed is not relivant.
17073 * <script type="text/javascript">
17078 * @class Roo.tree.DefaultSelectionModel
17079 * @extends Roo.util.Observable
17080 * The default single selection for a TreePanel.
17081 * @param {Object} cfg Configuration
17083 Roo.tree.DefaultSelectionModel = function(cfg){
17084 this.selNode = null;
17090 * @event selectionchange
17091 * Fires when the selected node changes
17092 * @param {DefaultSelectionModel} this
17093 * @param {TreeNode} node the new selection
17095 "selectionchange" : true,
17098 * @event beforeselect
17099 * Fires before the selected node changes, return false to cancel the change
17100 * @param {DefaultSelectionModel} this
17101 * @param {TreeNode} node the new selection
17102 * @param {TreeNode} node the old selection
17104 "beforeselect" : true
17107 Roo.tree.DefaultSelectionModel.superclass.constructor.call(this,cfg);
17110 Roo.extend(Roo.tree.DefaultSelectionModel, Roo.util.Observable, {
17111 init : function(tree){
17113 tree.getTreeEl().on("keydown", this.onKeyDown, this);
17114 tree.on("click", this.onNodeClick, this);
17117 onNodeClick : function(node, e){
17118 if (e.ctrlKey && this.selNode == node) {
17119 this.unselect(node);
17127 * @param {TreeNode} node The node to select
17128 * @return {TreeNode} The selected node
17130 select : function(node){
17131 var last = this.selNode;
17132 if(last != node && this.fireEvent('beforeselect', this, node, last) !== false){
17134 last.ui.onSelectedChange(false);
17136 this.selNode = node;
17137 node.ui.onSelectedChange(true);
17138 this.fireEvent("selectionchange", this, node, last);
17145 * @param {TreeNode} node The node to unselect
17147 unselect : function(node){
17148 if(this.selNode == node){
17149 this.clearSelections();
17154 * Clear all selections
17156 clearSelections : function(){
17157 var n = this.selNode;
17159 n.ui.onSelectedChange(false);
17160 this.selNode = null;
17161 this.fireEvent("selectionchange", this, null);
17167 * Get the selected node
17168 * @return {TreeNode} The selected node
17170 getSelectedNode : function(){
17171 return this.selNode;
17175 * Returns true if the node is selected
17176 * @param {TreeNode} node The node to check
17177 * @return {Boolean}
17179 isSelected : function(node){
17180 return this.selNode == node;
17184 * Selects the node above the selected node in the tree, intelligently walking the nodes
17185 * @return TreeNode The new selection
17187 selectPrevious : function(){
17188 var s = this.selNode || this.lastSelNode;
17192 var ps = s.previousSibling;
17194 if(!ps.isExpanded() || ps.childNodes.length < 1){
17195 return this.select(ps);
17197 var lc = ps.lastChild;
17198 while(lc && lc.isExpanded() && lc.childNodes.length > 0){
17201 return this.select(lc);
17203 } else if(s.parentNode && (this.tree.rootVisible || !s.parentNode.isRoot)){
17204 return this.select(s.parentNode);
17210 * Selects the node above the selected node in the tree, intelligently walking the nodes
17211 * @return TreeNode The new selection
17213 selectNext : function(){
17214 var s = this.selNode || this.lastSelNode;
17218 if(s.firstChild && s.isExpanded()){
17219 return this.select(s.firstChild);
17220 }else if(s.nextSibling){
17221 return this.select(s.nextSibling);
17222 }else if(s.parentNode){
17224 s.parentNode.bubble(function(){
17225 if(this.nextSibling){
17226 newS = this.getOwnerTree().selModel.select(this.nextSibling);
17235 onKeyDown : function(e){
17236 var s = this.selNode || this.lastSelNode;
17237 // undesirable, but required
17242 var k = e.getKey();
17250 this.selectPrevious();
17253 e.preventDefault();
17254 if(s.hasChildNodes()){
17255 if(!s.isExpanded()){
17257 }else if(s.firstChild){
17258 this.select(s.firstChild, e);
17263 e.preventDefault();
17264 if(s.hasChildNodes() && s.isExpanded()){
17266 }else if(s.parentNode && (this.tree.rootVisible || s.parentNode != this.tree.getRootNode())){
17267 this.select(s.parentNode, e);
17275 * @class Roo.tree.MultiSelectionModel
17276 * @extends Roo.util.Observable
17277 * Multi selection for a TreePanel.
17278 * @param {Object} cfg Configuration
17280 Roo.tree.MultiSelectionModel = function(){
17281 this.selNodes = [];
17285 * @event selectionchange
17286 * Fires when the selected nodes change
17287 * @param {MultiSelectionModel} this
17288 * @param {Array} nodes Array of the selected nodes
17290 "selectionchange" : true
17292 Roo.tree.MultiSelectionModel.superclass.constructor.call(this,cfg);
17296 Roo.extend(Roo.tree.MultiSelectionModel, Roo.util.Observable, {
17297 init : function(tree){
17299 tree.getTreeEl().on("keydown", this.onKeyDown, this);
17300 tree.on("click", this.onNodeClick, this);
17303 onNodeClick : function(node, e){
17304 this.select(node, e, e.ctrlKey);
17309 * @param {TreeNode} node The node to select
17310 * @param {EventObject} e (optional) An event associated with the selection
17311 * @param {Boolean} keepExisting True to retain existing selections
17312 * @return {TreeNode} The selected node
17314 select : function(node, e, keepExisting){
17315 if(keepExisting !== true){
17316 this.clearSelections(true);
17318 if(this.isSelected(node)){
17319 this.lastSelNode = node;
17322 this.selNodes.push(node);
17323 this.selMap[node.id] = node;
17324 this.lastSelNode = node;
17325 node.ui.onSelectedChange(true);
17326 this.fireEvent("selectionchange", this, this.selNodes);
17332 * @param {TreeNode} node The node to unselect
17334 unselect : function(node){
17335 if(this.selMap[node.id]){
17336 node.ui.onSelectedChange(false);
17337 var sn = this.selNodes;
17340 index = sn.indexOf(node);
17342 for(var i = 0, len = sn.length; i < len; i++){
17350 this.selNodes.splice(index, 1);
17352 delete this.selMap[node.id];
17353 this.fireEvent("selectionchange", this, this.selNodes);
17358 * Clear all selections
17360 clearSelections : function(suppressEvent){
17361 var sn = this.selNodes;
17363 for(var i = 0, len = sn.length; i < len; i++){
17364 sn[i].ui.onSelectedChange(false);
17366 this.selNodes = [];
17368 if(suppressEvent !== true){
17369 this.fireEvent("selectionchange", this, this.selNodes);
17375 * Returns true if the node is selected
17376 * @param {TreeNode} node The node to check
17377 * @return {Boolean}
17379 isSelected : function(node){
17380 return this.selMap[node.id] ? true : false;
17384 * Returns an array of the selected nodes
17387 getSelectedNodes : function(){
17388 return this.selNodes;
17391 onKeyDown : Roo.tree.DefaultSelectionModel.prototype.onKeyDown,
17393 selectNext : Roo.tree.DefaultSelectionModel.prototype.selectNext,
17395 selectPrevious : Roo.tree.DefaultSelectionModel.prototype.selectPrevious
17398 * Ext JS Library 1.1.1
17399 * Copyright(c) 2006-2007, Ext JS, LLC.
17401 * Originally Released Under LGPL - original licence link has changed is not relivant.
17404 * <script type="text/javascript">
17408 * @class Roo.tree.TreeNode
17409 * @extends Roo.data.Node
17410 * @cfg {String} text The text for this node
17411 * @cfg {Boolean} expanded true to start the node expanded
17412 * @cfg {Boolean} allowDrag false to make this node undraggable if DD is on (defaults to true)
17413 * @cfg {Boolean} allowDrop false if this node cannot be drop on
17414 * @cfg {Boolean} disabled true to start the node disabled
17415 * @cfg {String} icon The path to an icon for the node. The preferred way to do this
17416 * is to use the cls or iconCls attributes and add the icon via a CSS background image.
17417 * @cfg {String} cls A css class to be added to the node
17418 * @cfg {String} iconCls A css class to be added to the nodes icon element for applying css background images
17419 * @cfg {String} href URL of the link used for the node (defaults to #)
17420 * @cfg {String} hrefTarget target frame for the link
17421 * @cfg {String} qtip An Ext QuickTip for the node
17422 * @cfg {String} qtipCfg An Ext QuickTip config for the node (used instead of qtip)
17423 * @cfg {Boolean} singleClickExpand True for single click expand on this node
17424 * @cfg {Function} uiProvider A UI <b>class</b> to use for this node (defaults to Roo.tree.TreeNodeUI)
17425 * @cfg {Boolean} checked True to render a checked checkbox for this node, false to render an unchecked checkbox
17426 * (defaults to undefined with no checkbox rendered)
17428 * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node
17430 Roo.tree.TreeNode = function(attributes){
17431 attributes = attributes || {};
17432 if(typeof attributes == "string"){
17433 attributes = {text: attributes};
17435 this.childrenRendered = false;
17436 this.rendered = false;
17437 Roo.tree.TreeNode.superclass.constructor.call(this, attributes);
17438 this.expanded = attributes.expanded === true;
17439 this.isTarget = attributes.isTarget !== false;
17440 this.draggable = attributes.draggable !== false && attributes.allowDrag !== false;
17441 this.allowChildren = attributes.allowChildren !== false && attributes.allowDrop !== false;
17444 * Read-only. The text for this node. To change it use setText().
17447 this.text = attributes.text;
17449 * True if this node is disabled.
17452 this.disabled = attributes.disabled === true;
17456 * @event textchange
17457 * Fires when the text for this node is changed
17458 * @param {Node} this This node
17459 * @param {String} text The new text
17460 * @param {String} oldText The old text
17462 "textchange" : true,
17464 * @event beforeexpand
17465 * Fires before this node is expanded, return false to cancel.
17466 * @param {Node} this This node
17467 * @param {Boolean} deep
17468 * @param {Boolean} anim
17470 "beforeexpand" : true,
17472 * @event beforecollapse
17473 * Fires before this node is collapsed, return false to cancel.
17474 * @param {Node} this This node
17475 * @param {Boolean} deep
17476 * @param {Boolean} anim
17478 "beforecollapse" : true,
17481 * Fires when this node is expanded
17482 * @param {Node} this This node
17486 * @event disabledchange
17487 * Fires when the disabled status of this node changes
17488 * @param {Node} this This node
17489 * @param {Boolean} disabled
17491 "disabledchange" : true,
17494 * Fires when this node is collapsed
17495 * @param {Node} this This node
17499 * @event beforeclick
17500 * Fires before click processing. Return false to cancel the default action.
17501 * @param {Node} this This node
17502 * @param {Roo.EventObject} e The event object
17504 "beforeclick":true,
17506 * @event checkchange
17507 * Fires when a node with a checkbox's checked property changes
17508 * @param {Node} this This node
17509 * @param {Boolean} checked
17511 "checkchange":true,
17514 * Fires when this node is clicked
17515 * @param {Node} this This node
17516 * @param {Roo.EventObject} e The event object
17521 * Fires when this node is double clicked
17522 * @param {Node} this This node
17523 * @param {Roo.EventObject} e The event object
17527 * @event contextmenu
17528 * Fires when this node is right clicked
17529 * @param {Node} this This node
17530 * @param {Roo.EventObject} e The event object
17532 "contextmenu":true,
17534 * @event beforechildrenrendered
17535 * Fires right before the child nodes for this node are rendered
17536 * @param {Node} this This node
17538 "beforechildrenrendered":true
17541 var uiClass = this.attributes.uiProvider || Roo.tree.TreeNodeUI;
17544 * Read-only. The UI for this node
17547 this.ui = new uiClass(this);
17549 // finally support items[]
17550 if (typeof(this.attributes.items) == 'undefined' || !this.attributes.items) {
17555 Roo.each(this.attributes.items, function(c) {
17556 this.appendChild(Roo.factory(c,Roo.Tree));
17558 delete this.attributes.items;
17563 Roo.extend(Roo.tree.TreeNode, Roo.data.Node, {
17564 preventHScroll: true,
17566 * Returns true if this node is expanded
17567 * @return {Boolean}
17569 isExpanded : function(){
17570 return this.expanded;
17574 * Returns the UI object for this node
17575 * @return {TreeNodeUI}
17577 getUI : function(){
17581 // private override
17582 setFirstChild : function(node){
17583 var of = this.firstChild;
17584 Roo.tree.TreeNode.superclass.setFirstChild.call(this, node);
17585 if(this.childrenRendered && of && node != of){
17586 of.renderIndent(true, true);
17589 this.renderIndent(true, true);
17593 // private override
17594 setLastChild : function(node){
17595 var ol = this.lastChild;
17596 Roo.tree.TreeNode.superclass.setLastChild.call(this, node);
17597 if(this.childrenRendered && ol && node != ol){
17598 ol.renderIndent(true, true);
17601 this.renderIndent(true, true);
17605 // these methods are overridden to provide lazy rendering support
17606 // private override
17607 appendChild : function()
17609 var node = Roo.tree.TreeNode.superclass.appendChild.apply(this, arguments);
17610 if(node && this.childrenRendered){
17613 this.ui.updateExpandIcon();
17617 // private override
17618 removeChild : function(node){
17619 this.ownerTree.getSelectionModel().unselect(node);
17620 Roo.tree.TreeNode.superclass.removeChild.apply(this, arguments);
17621 // if it's been rendered remove dom node
17622 if(this.childrenRendered){
17625 if(this.childNodes.length < 1){
17626 this.collapse(false, false);
17628 this.ui.updateExpandIcon();
17630 if(!this.firstChild) {
17631 this.childrenRendered = false;
17636 // private override
17637 insertBefore : function(node, refNode){
17638 var newNode = Roo.tree.TreeNode.superclass.insertBefore.apply(this, arguments);
17639 if(newNode && refNode && this.childrenRendered){
17642 this.ui.updateExpandIcon();
17647 * Sets the text for this node
17648 * @param {String} text
17650 setText : function(text){
17651 var oldText = this.text;
17653 this.attributes.text = text;
17654 if(this.rendered){ // event without subscribing
17655 this.ui.onTextChange(this, text, oldText);
17657 this.fireEvent("textchange", this, text, oldText);
17661 * Triggers selection of this node
17663 select : function(){
17664 this.getOwnerTree().getSelectionModel().select(this);
17668 * Triggers deselection of this node
17670 unselect : function(){
17671 this.getOwnerTree().getSelectionModel().unselect(this);
17675 * Returns true if this node is selected
17676 * @return {Boolean}
17678 isSelected : function(){
17679 return this.getOwnerTree().getSelectionModel().isSelected(this);
17683 * Expand this node.
17684 * @param {Boolean} deep (optional) True to expand all children as well
17685 * @param {Boolean} anim (optional) false to cancel the default animation
17686 * @param {Function} callback (optional) A callback to be called when
17687 * expanding this node completes (does not wait for deep expand to complete).
17688 * Called with 1 parameter, this node.
17690 expand : function(deep, anim, callback){
17691 if(!this.expanded){
17692 if(this.fireEvent("beforeexpand", this, deep, anim) === false){
17695 if(!this.childrenRendered){
17696 this.renderChildren();
17698 this.expanded = true;
17699 if(!this.isHiddenRoot() && (this.getOwnerTree().animate && anim !== false) || anim){
17700 this.ui.animExpand(function(){
17701 this.fireEvent("expand", this);
17702 if(typeof callback == "function"){
17706 this.expandChildNodes(true);
17708 }.createDelegate(this));
17712 this.fireEvent("expand", this);
17713 if(typeof callback == "function"){
17718 if(typeof callback == "function"){
17723 this.expandChildNodes(true);
17727 isHiddenRoot : function(){
17728 return this.isRoot && !this.getOwnerTree().rootVisible;
17732 * Collapse this node.
17733 * @param {Boolean} deep (optional) True to collapse all children as well
17734 * @param {Boolean} anim (optional) false to cancel the default animation
17736 collapse : function(deep, anim){
17737 if(this.expanded && !this.isHiddenRoot()){
17738 if(this.fireEvent("beforecollapse", this, deep, anim) === false){
17741 this.expanded = false;
17742 if((this.getOwnerTree().animate && anim !== false) || anim){
17743 this.ui.animCollapse(function(){
17744 this.fireEvent("collapse", this);
17746 this.collapseChildNodes(true);
17748 }.createDelegate(this));
17751 this.ui.collapse();
17752 this.fireEvent("collapse", this);
17756 var cs = this.childNodes;
17757 for(var i = 0, len = cs.length; i < len; i++) {
17758 cs[i].collapse(true, false);
17764 delayedExpand : function(delay){
17765 if(!this.expandProcId){
17766 this.expandProcId = this.expand.defer(delay, this);
17771 cancelExpand : function(){
17772 if(this.expandProcId){
17773 clearTimeout(this.expandProcId);
17775 this.expandProcId = false;
17779 * Toggles expanded/collapsed state of the node
17781 toggle : function(){
17790 * Ensures all parent nodes are expanded
17792 ensureVisible : function(callback){
17793 var tree = this.getOwnerTree();
17794 tree.expandPath(this.parentNode.getPath(), false, function(){
17795 tree.getTreeEl().scrollChildIntoView(this.ui.anchor);
17796 Roo.callback(callback);
17797 }.createDelegate(this));
17801 * Expand all child nodes
17802 * @param {Boolean} deep (optional) true if the child nodes should also expand their child nodes
17804 expandChildNodes : function(deep){
17805 var cs = this.childNodes;
17806 for(var i = 0, len = cs.length; i < len; i++) {
17807 cs[i].expand(deep);
17812 * Collapse all child nodes
17813 * @param {Boolean} deep (optional) true if the child nodes should also collapse their child nodes
17815 collapseChildNodes : function(deep){
17816 var cs = this.childNodes;
17817 for(var i = 0, len = cs.length; i < len; i++) {
17818 cs[i].collapse(deep);
17823 * Disables this node
17825 disable : function(){
17826 this.disabled = true;
17828 if(this.rendered && this.ui.onDisableChange){ // event without subscribing
17829 this.ui.onDisableChange(this, true);
17831 this.fireEvent("disabledchange", this, true);
17835 * Enables this node
17837 enable : function(){
17838 this.disabled = false;
17839 if(this.rendered && this.ui.onDisableChange){ // event without subscribing
17840 this.ui.onDisableChange(this, false);
17842 this.fireEvent("disabledchange", this, false);
17846 renderChildren : function(suppressEvent){
17847 if(suppressEvent !== false){
17848 this.fireEvent("beforechildrenrendered", this);
17850 var cs = this.childNodes;
17851 for(var i = 0, len = cs.length; i < len; i++){
17852 cs[i].render(true);
17854 this.childrenRendered = true;
17858 sort : function(fn, scope){
17859 Roo.tree.TreeNode.superclass.sort.apply(this, arguments);
17860 if(this.childrenRendered){
17861 var cs = this.childNodes;
17862 for(var i = 0, len = cs.length; i < len; i++){
17863 cs[i].render(true);
17869 render : function(bulkRender){
17870 this.ui.render(bulkRender);
17871 if(!this.rendered){
17872 this.rendered = true;
17874 this.expanded = false;
17875 this.expand(false, false);
17881 renderIndent : function(deep, refresh){
17883 this.ui.childIndent = null;
17885 this.ui.renderIndent();
17886 if(deep === true && this.childrenRendered){
17887 var cs = this.childNodes;
17888 for(var i = 0, len = cs.length; i < len; i++){
17889 cs[i].renderIndent(true, refresh);
17895 * Ext JS Library 1.1.1
17896 * Copyright(c) 2006-2007, Ext JS, LLC.
17898 * Originally Released Under LGPL - original licence link has changed is not relivant.
17901 * <script type="text/javascript">
17905 * @class Roo.tree.AsyncTreeNode
17906 * @extends Roo.tree.TreeNode
17907 * @cfg {TreeLoader} loader A TreeLoader to be used by this node (defaults to the loader defined on the tree)
17909 * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node
17911 Roo.tree.AsyncTreeNode = function(config){
17912 this.loaded = false;
17913 this.loading = false;
17914 Roo.tree.AsyncTreeNode.superclass.constructor.apply(this, arguments);
17916 * @event beforeload
17917 * Fires before this node is loaded, return false to cancel
17918 * @param {Node} this This node
17920 this.addEvents({'beforeload':true, 'load': true});
17923 * Fires when this node is loaded
17924 * @param {Node} this This node
17927 * The loader used by this node (defaults to using the tree's defined loader)
17932 Roo.extend(Roo.tree.AsyncTreeNode, Roo.tree.TreeNode, {
17933 expand : function(deep, anim, callback){
17934 if(this.loading){ // if an async load is already running, waiting til it's done
17936 var f = function(){
17937 if(!this.loading){ // done loading
17938 clearInterval(timer);
17939 this.expand(deep, anim, callback);
17941 }.createDelegate(this);
17942 timer = setInterval(f, 200);
17946 if(this.fireEvent("beforeload", this) === false){
17949 this.loading = true;
17950 this.ui.beforeLoad(this);
17951 var loader = this.loader || this.attributes.loader || this.getOwnerTree().getLoader();
17953 loader.load(this, this.loadComplete.createDelegate(this, [deep, anim, callback]));
17957 Roo.tree.AsyncTreeNode.superclass.expand.call(this, deep, anim, callback);
17961 * Returns true if this node is currently loading
17962 * @return {Boolean}
17964 isLoading : function(){
17965 return this.loading;
17968 loadComplete : function(deep, anim, callback){
17969 this.loading = false;
17970 this.loaded = true;
17971 this.ui.afterLoad(this);
17972 this.fireEvent("load", this);
17973 this.expand(deep, anim, callback);
17977 * Returns true if this node has been loaded
17978 * @return {Boolean}
17980 isLoaded : function(){
17981 return this.loaded;
17984 hasChildNodes : function(){
17985 if(!this.isLeaf() && !this.loaded){
17988 return Roo.tree.AsyncTreeNode.superclass.hasChildNodes.call(this);
17993 * Trigger a reload for this node
17994 * @param {Function} callback
17996 reload : function(callback){
17997 this.collapse(false, false);
17998 while(this.firstChild){
17999 this.removeChild(this.firstChild);
18001 this.childrenRendered = false;
18002 this.loaded = false;
18003 if(this.isHiddenRoot()){
18004 this.expanded = false;
18006 this.expand(false, false, callback);
18010 * Ext JS Library 1.1.1
18011 * Copyright(c) 2006-2007, Ext JS, LLC.
18013 * Originally Released Under LGPL - original licence link has changed is not relivant.
18016 * <script type="text/javascript">
18020 * @class Roo.tree.TreeNodeUI
18022 * @param {Object} node The node to render
18023 * The TreeNode UI implementation is separate from the
18024 * tree implementation. Unless you are customizing the tree UI,
18025 * you should never have to use this directly.
18027 Roo.tree.TreeNodeUI = function(node){
18029 this.rendered = false;
18030 this.animating = false;
18031 this.emptyIcon = Roo.BLANK_IMAGE_URL;
18034 Roo.tree.TreeNodeUI.prototype = {
18035 removeChild : function(node){
18037 this.ctNode.removeChild(node.ui.getEl());
18041 beforeLoad : function(){
18042 this.addClass("x-tree-node-loading");
18045 afterLoad : function(){
18046 this.removeClass("x-tree-node-loading");
18049 onTextChange : function(node, text, oldText){
18051 this.textNode.innerHTML = text;
18055 onDisableChange : function(node, state){
18056 this.disabled = state;
18058 this.addClass("x-tree-node-disabled");
18060 this.removeClass("x-tree-node-disabled");
18064 onSelectedChange : function(state){
18067 this.addClass("x-tree-selected");
18070 this.removeClass("x-tree-selected");
18074 onMove : function(tree, node, oldParent, newParent, index, refNode){
18075 this.childIndent = null;
18077 var targetNode = newParent.ui.getContainer();
18078 if(!targetNode){//target not rendered
18079 this.holder = document.createElement("div");
18080 this.holder.appendChild(this.wrap);
18083 var insertBefore = refNode ? refNode.ui.getEl() : null;
18085 targetNode.insertBefore(this.wrap, insertBefore);
18087 targetNode.appendChild(this.wrap);
18089 this.node.renderIndent(true);
18093 addClass : function(cls){
18095 Roo.fly(this.elNode).addClass(cls);
18099 removeClass : function(cls){
18101 Roo.fly(this.elNode).removeClass(cls);
18105 remove : function(){
18107 this.holder = document.createElement("div");
18108 this.holder.appendChild(this.wrap);
18112 fireEvent : function(){
18113 return this.node.fireEvent.apply(this.node, arguments);
18116 initEvents : function(){
18117 this.node.on("move", this.onMove, this);
18118 var E = Roo.EventManager;
18119 var a = this.anchor;
18121 var el = Roo.fly(a, '_treeui');
18123 if(Roo.isOpera){ // opera render bug ignores the CSS
18124 el.setStyle("text-decoration", "none");
18127 el.on("click", this.onClick, this);
18128 el.on("dblclick", this.onDblClick, this);
18131 Roo.EventManager.on(this.checkbox,
18132 Roo.isIE ? 'click' : 'change', this.onCheckChange, this);
18135 el.on("contextmenu", this.onContextMenu, this);
18137 var icon = Roo.fly(this.iconNode);
18138 icon.on("click", this.onClick, this);
18139 icon.on("dblclick", this.onDblClick, this);
18140 icon.on("contextmenu", this.onContextMenu, this);
18141 E.on(this.ecNode, "click", this.ecClick, this, true);
18143 if(this.node.disabled){
18144 this.addClass("x-tree-node-disabled");
18146 if(this.node.hidden){
18147 this.addClass("x-tree-node-disabled");
18149 var ot = this.node.getOwnerTree();
18150 var dd = ot.enableDD || ot.enableDrag || ot.enableDrop;
18151 if(dd && (!this.node.isRoot || ot.rootVisible)){
18152 Roo.dd.Registry.register(this.elNode, {
18154 handles: this.getDDHandles(),
18160 getDDHandles : function(){
18161 return [this.iconNode, this.textNode];
18166 this.wrap.style.display = "none";
18172 this.wrap.style.display = "";
18176 onContextMenu : function(e){
18177 if (this.node.hasListener("contextmenu") || this.node.getOwnerTree().hasListener("contextmenu")) {
18178 e.preventDefault();
18180 this.fireEvent("contextmenu", this.node, e);
18184 onClick : function(e){
18189 if(this.fireEvent("beforeclick", this.node, e) !== false){
18190 if(!this.disabled && this.node.attributes.href){
18191 this.fireEvent("click", this.node, e);
18194 e.preventDefault();
18199 if(this.node.attributes.singleClickExpand && !this.animating && this.node.hasChildNodes()){
18200 this.node.toggle();
18203 this.fireEvent("click", this.node, e);
18209 onDblClick : function(e){
18210 e.preventDefault();
18215 this.toggleCheck();
18217 if(!this.animating && this.node.hasChildNodes()){
18218 this.node.toggle();
18220 this.fireEvent("dblclick", this.node, e);
18223 onCheckChange : function(){
18224 var checked = this.checkbox.checked;
18225 this.node.attributes.checked = checked;
18226 this.fireEvent('checkchange', this.node, checked);
18229 ecClick : function(e){
18230 if(!this.animating && this.node.hasChildNodes()){
18231 this.node.toggle();
18235 startDrop : function(){
18236 this.dropping = true;
18239 // delayed drop so the click event doesn't get fired on a drop
18240 endDrop : function(){
18241 setTimeout(function(){
18242 this.dropping = false;
18243 }.createDelegate(this), 50);
18246 expand : function(){
18247 this.updateExpandIcon();
18248 this.ctNode.style.display = "";
18251 focus : function(){
18252 if(!this.node.preventHScroll){
18253 try{this.anchor.focus();
18255 }else if(!Roo.isIE){
18257 var noscroll = this.node.getOwnerTree().getTreeEl().dom;
18258 var l = noscroll.scrollLeft;
18259 this.anchor.focus();
18260 noscroll.scrollLeft = l;
18265 toggleCheck : function(value){
18266 var cb = this.checkbox;
18268 cb.checked = (value === undefined ? !cb.checked : value);
18274 this.anchor.blur();
18278 animExpand : function(callback){
18279 var ct = Roo.get(this.ctNode);
18281 if(!this.node.hasChildNodes()){
18282 this.updateExpandIcon();
18283 this.ctNode.style.display = "";
18284 Roo.callback(callback);
18287 this.animating = true;
18288 this.updateExpandIcon();
18291 callback : function(){
18292 this.animating = false;
18293 Roo.callback(callback);
18296 duration: this.node.ownerTree.duration || .25
18300 highlight : function(){
18301 var tree = this.node.getOwnerTree();
18302 Roo.fly(this.wrap).highlight(
18303 tree.hlColor || "C3DAF9",
18304 {endColor: tree.hlBaseColor}
18308 collapse : function(){
18309 this.updateExpandIcon();
18310 this.ctNode.style.display = "none";
18313 animCollapse : function(callback){
18314 var ct = Roo.get(this.ctNode);
18315 ct.enableDisplayMode('block');
18318 this.animating = true;
18319 this.updateExpandIcon();
18322 callback : function(){
18323 this.animating = false;
18324 Roo.callback(callback);
18327 duration: this.node.ownerTree.duration || .25
18331 getContainer : function(){
18332 return this.ctNode;
18335 getEl : function(){
18339 appendDDGhost : function(ghostNode){
18340 ghostNode.appendChild(this.elNode.cloneNode(true));
18343 getDDRepairXY : function(){
18344 return Roo.lib.Dom.getXY(this.iconNode);
18347 onRender : function(){
18351 render : function(bulkRender){
18352 var n = this.node, a = n.attributes;
18353 var targetNode = n.parentNode ?
18354 n.parentNode.ui.getContainer() : n.ownerTree.innerCt.dom;
18356 if(!this.rendered){
18357 this.rendered = true;
18359 this.renderElements(n, a, targetNode, bulkRender);
18362 if(this.textNode.setAttributeNS){
18363 this.textNode.setAttributeNS("ext", "qtip", a.qtip);
18365 this.textNode.setAttributeNS("ext", "qtitle", a.qtipTitle);
18368 this.textNode.setAttribute("ext:qtip", a.qtip);
18370 this.textNode.setAttribute("ext:qtitle", a.qtipTitle);
18373 }else if(a.qtipCfg){
18374 a.qtipCfg.target = Roo.id(this.textNode);
18375 Roo.QuickTips.register(a.qtipCfg);
18378 if(!this.node.expanded){
18379 this.updateExpandIcon();
18382 if(bulkRender === true) {
18383 targetNode.appendChild(this.wrap);
18388 renderElements : function(n, a, targetNode, bulkRender)
18390 // add some indent caching, this helps performance when rendering a large tree
18391 this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
18392 var t = n.getOwnerTree();
18393 var txt = t.renderer ? t.renderer(n.attributes) : Roo.util.Format.htmlEncode(n.text);
18394 if (typeof(n.attributes.html) != 'undefined') {
18395 txt = n.attributes.html;
18397 var tip = t.rendererTip ? t.rendererTip(n.attributes) : txt;
18398 var cb = typeof a.checked == 'boolean';
18399 var href = a.href ? a.href : Roo.isGecko ? "" : "#";
18400 var buf = ['<li class="x-tree-node"><div class="x-tree-node-el ', a.cls,'">',
18401 '<span class="x-tree-node-indent">',this.indentMarkup,"</span>",
18402 '<img src="', this.emptyIcon, '" class="x-tree-ec-icon" />',
18403 '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',(a.icon ? " x-tree-node-inline-icon" : ""),(a.iconCls ? " "+a.iconCls : ""),'" unselectable="on" />',
18404 cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + (a.checked ? 'checked="checked" />' : ' />')) : '',
18405 '<a hidefocus="on" href="',href,'" tabIndex="1" ',
18406 a.hrefTarget ? ' target="'+a.hrefTarget+'"' : "",
18407 '><span unselectable="on" qtip="' , tip ,'">',txt,"</span></a></div>",
18408 '<ul class="x-tree-node-ct" style="display:none;"></ul>',
18411 if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
18412 this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
18413 n.nextSibling.ui.getEl(), buf.join(""));
18415 this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
18418 this.elNode = this.wrap.childNodes[0];
18419 this.ctNode = this.wrap.childNodes[1];
18420 var cs = this.elNode.childNodes;
18421 this.indentNode = cs[0];
18422 this.ecNode = cs[1];
18423 this.iconNode = cs[2];
18426 this.checkbox = cs[3];
18429 this.anchor = cs[index];
18430 this.textNode = cs[index].firstChild;
18433 getAnchor : function(){
18434 return this.anchor;
18437 getTextEl : function(){
18438 return this.textNode;
18441 getIconEl : function(){
18442 return this.iconNode;
18445 isChecked : function(){
18446 return this.checkbox ? this.checkbox.checked : false;
18449 updateExpandIcon : function(){
18451 var n = this.node, c1, c2;
18452 var cls = n.isLast() ? "x-tree-elbow-end" : "x-tree-elbow";
18453 var hasChild = n.hasChildNodes();
18457 c1 = "x-tree-node-collapsed";
18458 c2 = "x-tree-node-expanded";
18461 c1 = "x-tree-node-expanded";
18462 c2 = "x-tree-node-collapsed";
18465 this.removeClass("x-tree-node-leaf");
18466 this.wasLeaf = false;
18468 if(this.c1 != c1 || this.c2 != c2){
18469 Roo.fly(this.elNode).replaceClass(c1, c2);
18470 this.c1 = c1; this.c2 = c2;
18473 // this changes non-leafs into leafs if they have no children.
18474 // it's not very rational behaviour..
18476 if(!this.wasLeaf && this.node.leaf){
18477 Roo.fly(this.elNode).replaceClass("x-tree-node-expanded", "x-tree-node-leaf");
18480 this.wasLeaf = true;
18483 var ecc = "x-tree-ec-icon "+cls;
18484 if(this.ecc != ecc){
18485 this.ecNode.className = ecc;
18491 getChildIndent : function(){
18492 if(!this.childIndent){
18496 if(!p.isRoot || (p.isRoot && p.ownerTree.rootVisible)){
18498 buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-elbow-line" />');
18500 buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-icon" />');
18505 this.childIndent = buf.join("");
18507 return this.childIndent;
18510 renderIndent : function(){
18513 var p = this.node.parentNode;
18515 indent = p.ui.getChildIndent();
18517 if(this.indentMarkup != indent){ // don't rerender if not required
18518 this.indentNode.innerHTML = indent;
18519 this.indentMarkup = indent;
18521 this.updateExpandIcon();
18526 Roo.tree.RootTreeNodeUI = function(){
18527 Roo.tree.RootTreeNodeUI.superclass.constructor.apply(this, arguments);
18529 Roo.extend(Roo.tree.RootTreeNodeUI, Roo.tree.TreeNodeUI, {
18530 render : function(){
18531 if(!this.rendered){
18532 var targetNode = this.node.ownerTree.innerCt.dom;
18533 this.node.expanded = true;
18534 targetNode.innerHTML = '<div class="x-tree-root-node"></div>';
18535 this.wrap = this.ctNode = targetNode.firstChild;
18538 collapse : function(){
18540 expand : function(){
18544 * Ext JS Library 1.1.1
18545 * Copyright(c) 2006-2007, Ext JS, LLC.
18547 * Originally Released Under LGPL - original licence link has changed is not relivant.
18550 * <script type="text/javascript">
18553 * @class Roo.tree.TreeLoader
18554 * @extends Roo.util.Observable
18555 * A TreeLoader provides for lazy loading of an {@link Roo.tree.TreeNode}'s child
18556 * nodes from a specified URL. The response must be a javascript Array definition
18557 * who's elements are node definition objects. eg:
18562 { 'id': 1, 'text': 'A folder Node', 'leaf': false },
18563 { 'id': 2, 'text': 'A leaf Node', 'leaf': true }
18570 * The old style respose with just an array is still supported, but not recommended.
18573 * A server request is sent, and child nodes are loaded only when a node is expanded.
18574 * The loading node's id is passed to the server under the parameter name "node" to
18575 * enable the server to produce the correct child nodes.
18577 * To pass extra parameters, an event handler may be attached to the "beforeload"
18578 * event, and the parameters specified in the TreeLoader's baseParams property:
18580 myTreeLoader.on("beforeload", function(treeLoader, node) {
18581 this.baseParams.category = node.attributes.category;
18584 * This would pass an HTTP parameter called "category" to the server containing
18585 * the value of the Node's "category" attribute.
18587 * Creates a new Treeloader.
18588 * @param {Object} config A config object containing config properties.
18590 Roo.tree.TreeLoader = function(config){
18591 this.baseParams = {};
18592 this.requestMethod = "POST";
18593 Roo.apply(this, config);
18598 * @event beforeload
18599 * Fires before a network request is made to retrieve the Json text which specifies a node's children.
18600 * @param {Object} This TreeLoader object.
18601 * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
18602 * @param {Object} callback The callback function specified in the {@link #load} call.
18607 * Fires when the node has been successfuly loaded.
18608 * @param {Object} This TreeLoader object.
18609 * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
18610 * @param {Object} response The response object containing the data from the server.
18614 * @event loadexception
18615 * Fires if the network request failed.
18616 * @param {Object} This TreeLoader object.
18617 * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
18618 * @param {Object} response The response object containing the data from the server.
18620 loadexception : true,
18623 * Fires before a node is created, enabling you to return custom Node types
18624 * @param {Object} This TreeLoader object.
18625 * @param {Object} attr - the data returned from the AJAX call (modify it to suit)
18630 Roo.tree.TreeLoader.superclass.constructor.call(this);
18633 Roo.extend(Roo.tree.TreeLoader, Roo.util.Observable, {
18635 * @cfg {String} dataUrl The URL from which to request a Json string which
18636 * specifies an array of node definition object representing the child nodes
18640 * @cfg {String} requestMethod either GET or POST
18641 * defaults to POST (due to BC)
18645 * @cfg {Object} baseParams (optional) An object containing properties which
18646 * specify HTTP parameters to be passed to each request for child nodes.
18649 * @cfg {Object} baseAttrs (optional) An object containing attributes to be added to all nodes
18650 * created by this loader. If the attributes sent by the server have an attribute in this object,
18651 * they take priority.
18654 * @cfg {Object} uiProviders (optional) An object containing properties which
18656 * DEPRECATED - use 'create' event handler to modify attributes - which affect creation.
18657 * specify custom {@link Roo.tree.TreeNodeUI} implementations. If the optional
18658 * <i>uiProvider</i> attribute of a returned child node is a string rather
18659 * than a reference to a TreeNodeUI implementation, this that string value
18660 * is used as a property name in the uiProviders object. You can define the provider named
18661 * 'default' , and this will be used for all nodes (if no uiProvider is delivered by the node data)
18666 * @cfg {Boolean} clearOnLoad (optional) Default to true. Remove previously existing
18667 * child nodes before loading.
18669 clearOnLoad : true,
18672 * @cfg {String} root (optional) Default to false. Use this to read data from an object
18673 * property on loading, rather than expecting an array. (eg. more compatible to a standard
18674 * Grid query { data : [ .....] }
18679 * @cfg {String} queryParam (optional)
18680 * Name of the query as it will be passed on the querystring (defaults to 'node')
18681 * eg. the request will be ?node=[id]
18688 * Load an {@link Roo.tree.TreeNode} from the URL specified in the constructor.
18689 * This is called automatically when a node is expanded, but may be used to reload
18690 * a node (or append new children if the {@link #clearOnLoad} option is false.)
18691 * @param {Roo.tree.TreeNode} node
18692 * @param {Function} callback
18694 load : function(node, callback){
18695 if(this.clearOnLoad){
18696 while(node.firstChild){
18697 node.removeChild(node.firstChild);
18700 if(node.attributes.children){ // preloaded json children
18701 var cs = node.attributes.children;
18702 for(var i = 0, len = cs.length; i < len; i++){
18703 node.appendChild(this.createNode(cs[i]));
18705 if(typeof callback == "function"){
18708 }else if(this.dataUrl){
18709 this.requestData(node, callback);
18713 getParams: function(node){
18714 var buf = [], bp = this.baseParams;
18715 for(var key in bp){
18716 if(typeof bp[key] != "function"){
18717 buf.push(encodeURIComponent(key), "=", encodeURIComponent(bp[key]), "&");
18720 var n = this.queryParam === false ? 'node' : this.queryParam;
18721 buf.push(n + "=", encodeURIComponent(node.id));
18722 return buf.join("");
18725 requestData : function(node, callback){
18726 if(this.fireEvent("beforeload", this, node, callback) !== false){
18727 this.transId = Roo.Ajax.request({
18728 method:this.requestMethod,
18729 url: this.dataUrl||this.url,
18730 success: this.handleResponse,
18731 failure: this.handleFailure,
18733 argument: {callback: callback, node: node},
18734 params: this.getParams(node)
18737 // if the load is cancelled, make sure we notify
18738 // the node that we are done
18739 if(typeof callback == "function"){
18745 isLoading : function(){
18746 return this.transId ? true : false;
18749 abort : function(){
18750 if(this.isLoading()){
18751 Roo.Ajax.abort(this.transId);
18756 createNode : function(attr)
18758 // apply baseAttrs, nice idea Corey!
18759 if(this.baseAttrs){
18760 Roo.applyIf(attr, this.baseAttrs);
18762 if(this.applyLoader !== false){
18763 attr.loader = this;
18765 // uiProvider = depreciated..
18767 if(typeof(attr.uiProvider) == 'string'){
18768 attr.uiProvider = this.uiProviders[attr.uiProvider] ||
18769 /** eval:var:attr */ eval(attr.uiProvider);
18771 if(typeof(this.uiProviders['default']) != 'undefined') {
18772 attr.uiProvider = this.uiProviders['default'];
18775 this.fireEvent('create', this, attr);
18777 attr.leaf = typeof(attr.leaf) == 'string' ? attr.leaf * 1 : attr.leaf;
18779 new Roo.tree.TreeNode(attr) :
18780 new Roo.tree.AsyncTreeNode(attr));
18783 processResponse : function(response, node, callback)
18785 var json = response.responseText;
18788 var o = Roo.decode(json);
18790 if (this.root === false && typeof(o.success) != undefined) {
18791 this.root = 'data'; // the default behaviour for list like data..
18794 if (this.root !== false && !o.success) {
18795 // it's a failure condition.
18796 var a = response.argument;
18797 this.fireEvent("loadexception", this, a.node, response);
18798 Roo.log("Load failed - should have a handler really");
18804 if (this.root !== false) {
18808 for(var i = 0, len = o.length; i < len; i++){
18809 var n = this.createNode(o[i]);
18811 node.appendChild(n);
18814 if(typeof callback == "function"){
18815 callback(this, node);
18818 this.handleFailure(response);
18822 handleResponse : function(response){
18823 this.transId = false;
18824 var a = response.argument;
18825 this.processResponse(response, a.node, a.callback);
18826 this.fireEvent("load", this, a.node, response);
18829 handleFailure : function(response)
18831 // should handle failure better..
18832 this.transId = false;
18833 var a = response.argument;
18834 this.fireEvent("loadexception", this, a.node, response);
18835 if(typeof a.callback == "function"){
18836 a.callback(this, a.node);
18841 * Ext JS Library 1.1.1
18842 * Copyright(c) 2006-2007, Ext JS, LLC.
18844 * Originally Released Under LGPL - original licence link has changed is not relivant.
18847 * <script type="text/javascript">
18851 * @class Roo.tree.TreeFilter
18852 * Note this class is experimental and doesn't update the indent (lines) or expand collapse icons of the nodes
18853 * @param {TreePanel} tree
18854 * @param {Object} config (optional)
18856 Roo.tree.TreeFilter = function(tree, config){
18858 this.filtered = {};
18859 Roo.apply(this, config);
18862 Roo.tree.TreeFilter.prototype = {
18869 * Filter the data by a specific attribute.
18870 * @param {String/RegExp} value Either string that the attribute value
18871 * should start with or a RegExp to test against the attribute
18872 * @param {String} attr (optional) The attribute passed in your node's attributes collection. Defaults to "text".
18873 * @param {TreeNode} startNode (optional) The node to start the filter at.
18875 filter : function(value, attr, startNode){
18876 attr = attr || "text";
18878 if(typeof value == "string"){
18879 var vlen = value.length;
18880 // auto clear empty filter
18881 if(vlen == 0 && this.clearBlank){
18885 value = value.toLowerCase();
18887 return n.attributes[attr].substr(0, vlen).toLowerCase() == value;
18889 }else if(value.exec){ // regex?
18891 return value.test(n.attributes[attr]);
18894 throw 'Illegal filter type, must be string or regex';
18896 this.filterBy(f, null, startNode);
18900 * Filter by a function. The passed function will be called with each
18901 * node in the tree (or from the startNode). If the function returns true, the node is kept
18902 * otherwise it is filtered. If a node is filtered, its children are also filtered.
18903 * @param {Function} fn The filter function
18904 * @param {Object} scope (optional) The scope of the function (defaults to the current node)
18906 filterBy : function(fn, scope, startNode){
18907 startNode = startNode || this.tree.root;
18908 if(this.autoClear){
18911 var af = this.filtered, rv = this.reverse;
18912 var f = function(n){
18913 if(n == startNode){
18919 var m = fn.call(scope || n, n);
18927 startNode.cascade(f);
18930 if(typeof id != "function"){
18932 if(n && n.parentNode){
18933 n.parentNode.removeChild(n);
18941 * Clears the current filter. Note: with the "remove" option
18942 * set a filter cannot be cleared.
18944 clear : function(){
18946 var af = this.filtered;
18948 if(typeof id != "function"){
18955 this.filtered = {};
18960 * Ext JS Library 1.1.1
18961 * Copyright(c) 2006-2007, Ext JS, LLC.
18963 * Originally Released Under LGPL - original licence link has changed is not relivant.
18966 * <script type="text/javascript">
18971 * @class Roo.tree.TreeSorter
18972 * Provides sorting of nodes in a TreePanel
18974 * @cfg {Boolean} folderSort True to sort leaf nodes under non leaf nodes
18975 * @cfg {String} property The named attribute on the node to sort by (defaults to text)
18976 * @cfg {String} dir The direction to sort (asc or desc) (defaults to asc)
18977 * @cfg {String} leafAttr The attribute used to determine leaf nodes in folder sort (defaults to "leaf")
18978 * @cfg {Boolean} caseSensitive true for case sensitive sort (defaults to false)
18979 * @cfg {Function} sortType A custom "casting" function used to convert node values before sorting
18981 * @param {TreePanel} tree
18982 * @param {Object} config
18984 Roo.tree.TreeSorter = function(tree, config){
18985 Roo.apply(this, config);
18986 tree.on("beforechildrenrendered", this.doSort, this);
18987 tree.on("append", this.updateSort, this);
18988 tree.on("insert", this.updateSort, this);
18990 var dsc = this.dir && this.dir.toLowerCase() == "desc";
18991 var p = this.property || "text";
18992 var sortType = this.sortType;
18993 var fs = this.folderSort;
18994 var cs = this.caseSensitive === true;
18995 var leafAttr = this.leafAttr || 'leaf';
18997 this.sortFn = function(n1, n2){
18999 if(n1.attributes[leafAttr] && !n2.attributes[leafAttr]){
19002 if(!n1.attributes[leafAttr] && n2.attributes[leafAttr]){
19006 var v1 = sortType ? sortType(n1) : (cs ? n1.attributes[p] : n1.attributes[p].toUpperCase());
19007 var v2 = sortType ? sortType(n2) : (cs ? n2.attributes[p] : n2.attributes[p].toUpperCase());
19009 return dsc ? +1 : -1;
19011 return dsc ? -1 : +1;
19018 Roo.tree.TreeSorter.prototype = {
19019 doSort : function(node){
19020 node.sort(this.sortFn);
19023 compareNodes : function(n1, n2){
19024 return (n1.text.toUpperCase() > n2.text.toUpperCase() ? 1 : -1);
19027 updateSort : function(tree, node){
19028 if(node.childrenRendered){
19029 this.doSort.defer(1, this, [node]);
19034 * Ext JS Library 1.1.1
19035 * Copyright(c) 2006-2007, Ext JS, LLC.
19037 * Originally Released Under LGPL - original licence link has changed is not relivant.
19040 * <script type="text/javascript">
19043 if(Roo.dd.DropZone){
19045 Roo.tree.TreeDropZone = function(tree, config){
19046 this.allowParentInsert = false;
19047 this.allowContainerDrop = false;
19048 this.appendOnly = false;
19049 Roo.tree.TreeDropZone.superclass.constructor.call(this, tree.innerCt, config);
19051 this.lastInsertClass = "x-tree-no-status";
19052 this.dragOverData = {};
19055 Roo.extend(Roo.tree.TreeDropZone, Roo.dd.DropZone, {
19056 ddGroup : "TreeDD",
19059 expandDelay : 1000,
19061 expandNode : function(node){
19062 if(node.hasChildNodes() && !node.isExpanded()){
19063 node.expand(false, null, this.triggerCacheRefresh.createDelegate(this));
19067 queueExpand : function(node){
19068 this.expandProcId = this.expandNode.defer(this.expandDelay, this, [node]);
19071 cancelExpand : function(){
19072 if(this.expandProcId){
19073 clearTimeout(this.expandProcId);
19074 this.expandProcId = false;
19078 isValidDropPoint : function(n, pt, dd, e, data){
19079 if(!n || !data){ return false; }
19080 var targetNode = n.node;
19081 var dropNode = data.node;
19082 // default drop rules
19083 if(!(targetNode && targetNode.isTarget && pt)){
19086 if(pt == "append" && targetNode.allowChildren === false){
19089 if((pt == "above" || pt == "below") && (targetNode.parentNode && targetNode.parentNode.allowChildren === false)){
19092 if(dropNode && (targetNode == dropNode || dropNode.contains(targetNode))){
19095 // reuse the object
19096 var overEvent = this.dragOverData;
19097 overEvent.tree = this.tree;
19098 overEvent.target = targetNode;
19099 overEvent.data = data;
19100 overEvent.point = pt;
19101 overEvent.source = dd;
19102 overEvent.rawEvent = e;
19103 overEvent.dropNode = dropNode;
19104 overEvent.cancel = false;
19105 var result = this.tree.fireEvent("nodedragover", overEvent);
19106 return overEvent.cancel === false && result !== false;
19109 getDropPoint : function(e, n, dd)
19113 return tn.allowChildren !== false ? "append" : false; // always append for root
19115 var dragEl = n.ddel;
19116 var t = Roo.lib.Dom.getY(dragEl), b = t + dragEl.offsetHeight;
19117 var y = Roo.lib.Event.getPageY(e);
19118 //var noAppend = tn.allowChildren === false || tn.isLeaf();
19120 // we may drop nodes anywhere, as long as allowChildren has not been set to false..
19121 var noAppend = tn.allowChildren === false;
19122 if(this.appendOnly || tn.parentNode.allowChildren === false){
19123 return noAppend ? false : "append";
19125 var noBelow = false;
19126 if(!this.allowParentInsert){
19127 noBelow = tn.hasChildNodes() && tn.isExpanded();
19129 var q = (b - t) / (noAppend ? 2 : 3);
19130 if(y >= t && y < (t + q)){
19132 }else if(!noBelow && (noAppend || y >= b-q && y <= b)){
19139 onNodeEnter : function(n, dd, e, data)
19141 this.cancelExpand();
19144 onNodeOver : function(n, dd, e, data)
19147 var pt = this.getDropPoint(e, n, dd);
19150 // auto node expand check
19151 if(!this.expandProcId && pt == "append" && node.hasChildNodes() && !n.node.isExpanded()){
19152 this.queueExpand(node);
19153 }else if(pt != "append"){
19154 this.cancelExpand();
19157 // set the insert point style on the target node
19158 var returnCls = this.dropNotAllowed;
19159 if(this.isValidDropPoint(n, pt, dd, e, data)){
19164 returnCls = n.node.isFirst() ? "x-tree-drop-ok-above" : "x-tree-drop-ok-between";
19165 cls = "x-tree-drag-insert-above";
19166 }else if(pt == "below"){
19167 returnCls = n.node.isLast() ? "x-tree-drop-ok-below" : "x-tree-drop-ok-between";
19168 cls = "x-tree-drag-insert-below";
19170 returnCls = "x-tree-drop-ok-append";
19171 cls = "x-tree-drag-append";
19173 if(this.lastInsertClass != cls){
19174 Roo.fly(el).replaceClass(this.lastInsertClass, cls);
19175 this.lastInsertClass = cls;
19182 onNodeOut : function(n, dd, e, data){
19184 this.cancelExpand();
19185 this.removeDropIndicators(n);
19188 onNodeDrop : function(n, dd, e, data){
19189 var point = this.getDropPoint(e, n, dd);
19190 var targetNode = n.node;
19191 targetNode.ui.startDrop();
19192 if(!this.isValidDropPoint(n, point, dd, e, data)){
19193 targetNode.ui.endDrop();
19196 // first try to find the drop node
19197 var dropNode = data.node || (dd.getTreeNode ? dd.getTreeNode(data, targetNode, point, e) : null);
19200 target: targetNode,
19205 dropNode: dropNode,
19208 var retval = this.tree.fireEvent("beforenodedrop", dropEvent);
19209 if(retval === false || dropEvent.cancel === true || !dropEvent.dropNode){
19210 targetNode.ui.endDrop();
19213 // allow target changing
19214 targetNode = dropEvent.target;
19215 if(point == "append" && !targetNode.isExpanded()){
19216 targetNode.expand(false, null, function(){
19217 this.completeDrop(dropEvent);
19218 }.createDelegate(this));
19220 this.completeDrop(dropEvent);
19225 completeDrop : function(de){
19226 var ns = de.dropNode, p = de.point, t = de.target;
19227 if(!(ns instanceof Array)){
19231 for(var i = 0, len = ns.length; i < len; i++){
19234 t.parentNode.insertBefore(n, t);
19235 }else if(p == "below"){
19236 t.parentNode.insertBefore(n, t.nextSibling);
19242 if(this.tree.hlDrop){
19246 this.tree.fireEvent("nodedrop", de);
19249 afterNodeMoved : function(dd, data, e, targetNode, dropNode){
19250 if(this.tree.hlDrop){
19251 dropNode.ui.focus();
19252 dropNode.ui.highlight();
19254 this.tree.fireEvent("nodedrop", this.tree, targetNode, data, dd, e);
19257 getTree : function(){
19261 removeDropIndicators : function(n){
19264 Roo.fly(el).removeClass([
19265 "x-tree-drag-insert-above",
19266 "x-tree-drag-insert-below",
19267 "x-tree-drag-append"]);
19268 this.lastInsertClass = "_noclass";
19272 beforeDragDrop : function(target, e, id){
19273 this.cancelExpand();
19277 afterRepair : function(data){
19278 if(data && Roo.enableFx){
19279 data.node.ui.highlight();
19289 * Ext JS Library 1.1.1
19290 * Copyright(c) 2006-2007, Ext JS, LLC.
19292 * Originally Released Under LGPL - original licence link has changed is not relivant.
19295 * <script type="text/javascript">
19299 if(Roo.dd.DragZone){
19300 Roo.tree.TreeDragZone = function(tree, config){
19301 Roo.tree.TreeDragZone.superclass.constructor.call(this, tree.getTreeEl(), config);
19305 Roo.extend(Roo.tree.TreeDragZone, Roo.dd.DragZone, {
19306 ddGroup : "TreeDD",
19308 onBeforeDrag : function(data, e){
19310 return n && n.draggable && !n.disabled;
19314 onInitDrag : function(e){
19315 var data = this.dragData;
19316 this.tree.getSelectionModel().select(data.node);
19317 this.proxy.update("");
19318 data.node.ui.appendDDGhost(this.proxy.ghost.dom);
19319 this.tree.fireEvent("startdrag", this.tree, data.node, e);
19322 getRepairXY : function(e, data){
19323 return data.node.ui.getDDRepairXY();
19326 onEndDrag : function(data, e){
19327 this.tree.fireEvent("enddrag", this.tree, data.node, e);
19332 onValidDrop : function(dd, e, id){
19333 this.tree.fireEvent("dragdrop", this.tree, this.dragData.node, dd, e);
19337 beforeInvalidDrop : function(e, id){
19338 // this scrolls the original position back into view
19339 var sm = this.tree.getSelectionModel();
19340 sm.clearSelections();
19341 sm.select(this.dragData.node);
19346 * Ext JS Library 1.1.1
19347 * Copyright(c) 2006-2007, Ext JS, LLC.
19349 * Originally Released Under LGPL - original licence link has changed is not relivant.
19352 * <script type="text/javascript">
19355 * @class Roo.tree.TreeEditor
19356 * @extends Roo.Editor
19357 * Provides editor functionality for inline tree node editing. Any valid {@link Roo.form.Field} can be used
19358 * as the editor field.
19360 * @param {Object} config (used to be the tree panel.)
19361 * @param {Object} oldconfig DEPRECIATED Either a prebuilt {@link Roo.form.Field} instance or a Field config object
19363 * @cfg {Roo.tree.TreePanel} tree The tree to bind to.
19364 * @cfg {Roo.form.TextField|Object} field The field configuration
19368 Roo.tree.TreeEditor = function(config, oldconfig) { // was -- (tree, config){
19371 if (oldconfig) { // old style..
19372 field = oldconfig.events ? oldconfig : new Roo.form.TextField(oldconfig);
19375 tree = config.tree;
19376 config.field = config.field || {};
19377 config.field.xtype = 'TextField';
19378 field = Roo.factory(config.field, Roo.form);
19380 config = config || {};
19385 * @event beforenodeedit
19386 * Fires when editing is initiated, but before the value changes. Editing can be canceled by returning
19387 * false from the handler of this event.
19388 * @param {Editor} this
19389 * @param {Roo.tree.Node} node
19391 "beforenodeedit" : true
19395 Roo.tree.TreeEditor.superclass.constructor.call(this, field, config);
19399 tree.on('beforeclick', this.beforeNodeClick, this);
19400 tree.getTreeEl().on('mousedown', this.hide, this);
19401 this.on('complete', this.updateNode, this);
19402 this.on('beforestartedit', this.fitToTree, this);
19403 this.on('startedit', this.bindScroll, this, {delay:10});
19404 this.on('specialkey', this.onSpecialKey, this);
19407 Roo.extend(Roo.tree.TreeEditor, Roo.Editor, {
19409 * @cfg {String} alignment
19410 * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "l-l").
19416 * @cfg {Boolean} hideEl
19417 * True to hide the bound element while the editor is displayed (defaults to false)
19421 * @cfg {String} cls
19422 * CSS class to apply to the editor (defaults to "x-small-editor x-tree-editor")
19424 cls: "x-small-editor x-tree-editor",
19426 * @cfg {Boolean} shim
19427 * True to shim the editor if selects/iframes could be displayed beneath it (defaults to false)
19433 * @cfg {Number} maxWidth
19434 * The maximum width in pixels of the editor field (defaults to 250). Note that if the maxWidth would exceed
19435 * the containing tree element's size, it will be automatically limited for you to the container width, taking
19436 * scroll and client offsets into account prior to each edit.
19443 fitToTree : function(ed, el){
19444 var td = this.tree.getTreeEl().dom, nd = el.dom;
19445 if(td.scrollLeft > nd.offsetLeft){ // ensure the node left point is visible
19446 td.scrollLeft = nd.offsetLeft;
19450 (td.clientWidth > 20 ? td.clientWidth : td.offsetWidth) - Math.max(0, nd.offsetLeft-td.scrollLeft) - /*cushion*/5);
19451 this.setSize(w, '');
19453 return this.fireEvent('beforenodeedit', this, this.editNode);
19458 triggerEdit : function(node){
19459 this.completeEdit();
19460 this.editNode = node;
19461 this.startEdit(node.ui.textNode, node.text);
19465 bindScroll : function(){
19466 this.tree.getTreeEl().on('scroll', this.cancelEdit, this);
19470 beforeNodeClick : function(node, e){
19471 var sinceLast = (this.lastClick ? this.lastClick.getElapsed() : 0);
19472 this.lastClick = new Date();
19473 if(sinceLast > this.editDelay && this.tree.getSelectionModel().isSelected(node)){
19475 this.triggerEdit(node);
19482 updateNode : function(ed, value){
19483 this.tree.getTreeEl().un('scroll', this.cancelEdit, this);
19484 this.editNode.setText(value);
19488 onHide : function(){
19489 Roo.tree.TreeEditor.superclass.onHide.call(this);
19491 this.editNode.ui.focus();
19496 onSpecialKey : function(field, e){
19497 var k = e.getKey();
19501 }else if(k == e.ENTER && !e.hasModifier()){
19503 this.completeEdit();
19506 });//<Script type="text/javascript">
19509 * Ext JS Library 1.1.1
19510 * Copyright(c) 2006-2007, Ext JS, LLC.
19512 * Originally Released Under LGPL - original licence link has changed is not relivant.
19515 * <script type="text/javascript">
19519 * Not documented??? - probably should be...
19522 Roo.tree.ColumnNodeUI = Roo.extend(Roo.tree.TreeNodeUI, {
19523 //focus: Roo.emptyFn, // prevent odd scrolling behavior
19525 renderElements : function(n, a, targetNode, bulkRender){
19526 //consel.log("renderElements?");
19527 this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
19529 var t = n.getOwnerTree();
19530 var tid = Pman.Tab.Document_TypesTree.tree.el.id;
19532 var cols = t.columns;
19533 var bw = t.borderWidth;
19535 var href = a.href ? a.href : Roo.isGecko ? "" : "#";
19536 var cb = typeof a.checked == "boolean";
19537 var tx = String.format('{0}',n.text || (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
19538 var colcls = 'x-t-' + tid + '-c0';
19540 '<li class="x-tree-node">',
19543 '<div class="x-tree-node-el ', a.cls,'">',
19545 '<div class="x-tree-col ', colcls, '" style="width:', c.width-bw, 'px;">',
19548 '<span class="x-tree-node-indent">',this.indentMarkup,'</span>',
19549 '<img src="', this.emptyIcon, '" class="x-tree-ec-icon " />',
19550 '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',
19551 (a.icon ? ' x-tree-node-inline-icon' : ''),
19552 (a.iconCls ? ' '+a.iconCls : ''),
19553 '" unselectable="on" />',
19554 (cb ? ('<input class="x-tree-node-cb" type="checkbox" ' +
19555 (a.checked ? 'checked="checked" />' : ' />')) : ''),
19557 '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
19558 (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>',
19559 '<span unselectable="on" qtip="' + tx + '">',
19563 '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
19564 (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>'
19566 for(var i = 1, len = cols.length; i < len; i++){
19568 colcls = 'x-t-' + tid + '-c' +i;
19569 tx = String.format('{0}', (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
19570 buf.push('<div class="x-tree-col ', colcls, ' ' ,(c.cls?c.cls:''),'" style="width:',c.width-bw,'px;">',
19571 '<div class="x-tree-col-text" qtip="' + tx +'">',tx,"</div>",
19577 '<div class="x-clear"></div></div>',
19578 '<ul class="x-tree-node-ct" style="display:none;"></ul>',
19581 if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
19582 this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
19583 n.nextSibling.ui.getEl(), buf.join(""));
19585 this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
19587 var el = this.wrap.firstChild;
19589 this.elNode = el.firstChild;
19590 this.ranchor = el.childNodes[1];
19591 this.ctNode = this.wrap.childNodes[1];
19592 var cs = el.firstChild.childNodes;
19593 this.indentNode = cs[0];
19594 this.ecNode = cs[1];
19595 this.iconNode = cs[2];
19598 this.checkbox = cs[3];
19601 this.anchor = cs[index];
19603 this.textNode = cs[index].firstChild;
19605 //el.on("click", this.onClick, this);
19606 //el.on("dblclick", this.onDblClick, this);
19609 // console.log(this);
19611 initEvents : function(){
19612 Roo.tree.ColumnNodeUI.superclass.initEvents.call(this);
19615 var a = this.ranchor;
19617 var el = Roo.get(a);
19619 if(Roo.isOpera){ // opera render bug ignores the CSS
19620 el.setStyle("text-decoration", "none");
19623 el.on("click", this.onClick, this);
19624 el.on("dblclick", this.onDblClick, this);
19625 el.on("contextmenu", this.onContextMenu, this);
19629 /*onSelectedChange : function(state){
19632 this.addClass("x-tree-selected");
19635 this.removeClass("x-tree-selected");
19638 addClass : function(cls){
19640 Roo.fly(this.elRow).addClass(cls);
19646 removeClass : function(cls){
19648 Roo.fly(this.elRow).removeClass(cls);
19654 });//<Script type="text/javascript">
19658 * Ext JS Library 1.1.1
19659 * Copyright(c) 2006-2007, Ext JS, LLC.
19661 * Originally Released Under LGPL - original licence link has changed is not relivant.
19664 * <script type="text/javascript">
19669 * @class Roo.tree.ColumnTree
19670 * @extends Roo.data.TreePanel
19671 * @cfg {Object} columns Including width, header, renderer, cls, dataIndex
19672 * @cfg {int} borderWidth compined right/left border allowance
19674 * @param {String/HTMLElement/Element} el The container element
19675 * @param {Object} config
19677 Roo.tree.ColumnTree = function(el, config)
19679 Roo.tree.ColumnTree.superclass.constructor.call(this, el , config);
19683 * Fire this event on a container when it resizes
19684 * @param {int} w Width
19685 * @param {int} h Height
19689 this.on('resize', this.onResize, this);
19692 Roo.extend(Roo.tree.ColumnTree, Roo.tree.TreePanel, {
19696 borderWidth: Roo.isBorderBox ? 0 : 2,
19699 render : function(){
19700 // add the header.....
19702 Roo.tree.ColumnTree.superclass.render.apply(this);
19704 this.el.addClass('x-column-tree');
19706 this.headers = this.el.createChild(
19707 {cls:'x-tree-headers'},this.innerCt.dom);
19709 var cols = this.columns, c;
19710 var totalWidth = 0;
19712 var len = cols.length;
19713 for(var i = 0; i < len; i++){
19715 totalWidth += c.width;
19716 this.headEls.push(this.headers.createChild({
19717 cls:'x-tree-hd ' + (c.cls?c.cls+'-hd':''),
19719 cls:'x-tree-hd-text',
19722 style:'width:'+(c.width-this.borderWidth)+'px;'
19725 this.headers.createChild({cls:'x-clear'});
19726 // prevent floats from wrapping when clipped
19727 this.headers.setWidth(totalWidth);
19728 //this.innerCt.setWidth(totalWidth);
19729 this.innerCt.setStyle({ overflow: 'auto' });
19730 this.onResize(this.width, this.height);
19734 onResize : function(w,h)
19739 this.innerCt.setWidth(this.width);
19740 this.innerCt.setHeight(this.height-20);
19743 var cols = this.columns, c;
19744 var totalWidth = 0;
19746 var len = cols.length;
19747 for(var i = 0; i < len; i++){
19749 if (this.autoExpandColumn !== false && c.dataIndex == this.autoExpandColumn) {
19750 // it's the expander..
19751 expEl = this.headEls[i];
19754 totalWidth += c.width;
19758 expEl.setWidth( ((w - totalWidth)-this.borderWidth - 20));
19760 this.headers.setWidth(w-20);
19769 * Ext JS Library 1.1.1
19770 * Copyright(c) 2006-2007, Ext JS, LLC.
19772 * Originally Released Under LGPL - original licence link has changed is not relivant.
19775 * <script type="text/javascript">
19779 * @class Roo.menu.Menu
19780 * @extends Roo.util.Observable
19781 * A menu object. This is the container to which you add all other menu items. Menu can also serve a as a base class
19782 * when you want a specialzed menu based off of another component (like {@link Roo.menu.DateMenu} for example).
19784 * Creates a new Menu
19785 * @param {Object} config Configuration options
19787 Roo.menu.Menu = function(config){
19788 Roo.apply(this, config);
19789 this.id = this.id || Roo.id();
19792 * @event beforeshow
19793 * Fires before this menu is displayed
19794 * @param {Roo.menu.Menu} this
19798 * @event beforehide
19799 * Fires before this menu is hidden
19800 * @param {Roo.menu.Menu} this
19805 * Fires after this menu is displayed
19806 * @param {Roo.menu.Menu} this
19811 * Fires after this menu is hidden
19812 * @param {Roo.menu.Menu} this
19817 * Fires when this menu is clicked (or when the enter key is pressed while it is active)
19818 * @param {Roo.menu.Menu} this
19819 * @param {Roo.menu.Item} menuItem The menu item that was clicked
19820 * @param {Roo.EventObject} e
19825 * Fires when the mouse is hovering over this menu
19826 * @param {Roo.menu.Menu} this
19827 * @param {Roo.EventObject} e
19828 * @param {Roo.menu.Item} menuItem The menu item that was clicked
19833 * Fires when the mouse exits this menu
19834 * @param {Roo.menu.Menu} this
19835 * @param {Roo.EventObject} e
19836 * @param {Roo.menu.Item} menuItem The menu item that was clicked
19841 * Fires when a menu item contained in this menu is clicked
19842 * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
19843 * @param {Roo.EventObject} e
19847 if (this.registerMenu) {
19848 Roo.menu.MenuMgr.register(this);
19851 var mis = this.items;
19852 this.items = new Roo.util.MixedCollection();
19854 this.add.apply(this, mis);
19858 Roo.extend(Roo.menu.Menu, Roo.util.Observable, {
19860 * @cfg {Number} minWidth The minimum width of the menu in pixels (defaults to 120)
19864 * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop"
19865 * for bottom-right shadow (defaults to "sides")
19869 * @cfg {String} subMenuAlign The {@link Roo.Element#alignTo} anchor position value to use for submenus of
19870 * this menu (defaults to "tl-tr?")
19872 subMenuAlign : "tl-tr?",
19874 * @cfg {String} defaultAlign The default {@link Roo.Element#alignTo) anchor position value for this menu
19875 * relative to its element of origin (defaults to "tl-bl?")
19877 defaultAlign : "tl-bl?",
19879 * @cfg {Boolean} allowOtherMenus True to allow multiple menus to be displayed at the same time (defaults to false)
19881 allowOtherMenus : false,
19883 * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
19885 registerMenu : true,
19890 render : function(){
19894 var el = this.el = new Roo.Layer({
19896 shadow:this.shadow,
19898 parentEl: this.parentEl || document.body,
19902 this.keyNav = new Roo.menu.MenuNav(this);
19905 el.addClass("x-menu-plain");
19908 el.addClass(this.cls);
19910 // generic focus element
19911 this.focusEl = el.createChild({
19912 tag: "a", cls: "x-menu-focus", href: "#", onclick: "return false;", tabIndex:"-1"
19914 var ul = el.createChild({tag: "ul", cls: "x-menu-list"});
19915 ul.on("click", this.onClick, this);
19916 ul.on("mouseover", this.onMouseOver, this);
19917 ul.on("mouseout", this.onMouseOut, this);
19918 this.items.each(function(item){
19923 var li = document.createElement("li");
19924 li.className = "x-menu-list-item";
19925 ul.dom.appendChild(li);
19926 item.render(li, this);
19933 autoWidth : function(){
19934 var el = this.el, ul = this.ul;
19938 var w = this.width;
19941 }else if(Roo.isIE){
19942 el.setWidth(this.minWidth);
19943 var t = el.dom.offsetWidth; // force recalc
19944 el.setWidth(ul.getWidth()+el.getFrameWidth("lr"));
19949 delayAutoWidth : function(){
19952 this.awTask = new Roo.util.DelayedTask(this.autoWidth, this);
19954 this.awTask.delay(20);
19959 findTargetItem : function(e){
19960 var t = e.getTarget(".x-menu-list-item", this.ul, true);
19961 if(t && t.menuItemId){
19962 return this.items.get(t.menuItemId);
19967 onClick : function(e){
19969 if(t = this.findTargetItem(e)){
19971 this.fireEvent("click", this, t, e);
19976 setActiveItem : function(item, autoExpand){
19977 if(item != this.activeItem){
19978 if(this.activeItem){
19979 this.activeItem.deactivate();
19981 this.activeItem = item;
19982 item.activate(autoExpand);
19983 }else if(autoExpand){
19989 tryActivate : function(start, step){
19990 var items = this.items;
19991 for(var i = start, len = items.length; i >= 0 && i < len; i+= step){
19992 var item = items.get(i);
19993 if(!item.disabled && item.canActivate){
19994 this.setActiveItem(item, false);
20002 onMouseOver : function(e){
20004 if(t = this.findTargetItem(e)){
20005 if(t.canActivate && !t.disabled){
20006 this.setActiveItem(t, true);
20009 this.fireEvent("mouseover", this, e, t);
20013 onMouseOut : function(e){
20015 if(t = this.findTargetItem(e)){
20016 if(t == this.activeItem && t.shouldDeactivate(e)){
20017 this.activeItem.deactivate();
20018 delete this.activeItem;
20021 this.fireEvent("mouseout", this, e, t);
20025 * Read-only. Returns true if the menu is currently displayed, else false.
20028 isVisible : function(){
20029 return this.el && !this.hidden;
20033 * Displays this menu relative to another element
20034 * @param {String/HTMLElement/Roo.Element} element The element to align to
20035 * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
20036 * the element (defaults to this.defaultAlign)
20037 * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
20039 show : function(el, pos, parentMenu){
20040 this.parentMenu = parentMenu;
20044 this.fireEvent("beforeshow", this);
20045 this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
20049 * Displays this menu at a specific xy position
20050 * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
20051 * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
20053 showAt : function(xy, parentMenu, /* private: */_e){
20054 this.parentMenu = parentMenu;
20059 this.fireEvent("beforeshow", this);
20060 xy = this.el.adjustForConstraints(xy);
20064 this.hidden = false;
20066 this.fireEvent("show", this);
20069 focus : function(){
20071 this.doFocus.defer(50, this);
20075 doFocus : function(){
20077 this.focusEl.focus();
20082 * Hides this menu and optionally all parent menus
20083 * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
20085 hide : function(deep){
20086 if(this.el && this.isVisible()){
20087 this.fireEvent("beforehide", this);
20088 if(this.activeItem){
20089 this.activeItem.deactivate();
20090 this.activeItem = null;
20093 this.hidden = true;
20094 this.fireEvent("hide", this);
20096 if(deep === true && this.parentMenu){
20097 this.parentMenu.hide(true);
20102 * Addds one or more items of any type supported by the Menu class, or that can be converted into menu items.
20103 * Any of the following are valid:
20105 * <li>Any menu item object based on {@link Roo.menu.Item}</li>
20106 * <li>An HTMLElement object which will be converted to a menu item</li>
20107 * <li>A menu item config object that will be created as a new menu item</li>
20108 * <li>A string, which can either be '-' or 'separator' to add a menu separator, otherwise
20109 * it will be converted into a {@link Roo.menu.TextItem} and added</li>
20114 var menu = new Roo.menu.Menu();
20116 // Create a menu item to add by reference
20117 var menuItem = new Roo.menu.Item({ text: 'New Item!' });
20119 // Add a bunch of items at once using different methods.
20120 // Only the last item added will be returned.
20121 var item = menu.add(
20122 menuItem, // add existing item by ref
20123 'Dynamic Item', // new TextItem
20124 '-', // new separator
20125 { text: 'Config Item' } // new item by config
20128 * @param {Mixed} args One or more menu items, menu item configs or other objects that can be converted to menu items
20129 * @return {Roo.menu.Item} The menu item that was added, or the last one if multiple items were added
20132 var a = arguments, l = a.length, item;
20133 for(var i = 0; i < l; i++){
20135 if ((typeof(el) == "object") && el.xtype && el.xns) {
20136 el = Roo.factory(el, Roo.menu);
20139 if(el.render){ // some kind of Item
20140 item = this.addItem(el);
20141 }else if(typeof el == "string"){ // string
20142 if(el == "separator" || el == "-"){
20143 item = this.addSeparator();
20145 item = this.addText(el);
20147 }else if(el.tagName || el.el){ // element
20148 item = this.addElement(el);
20149 }else if(typeof el == "object"){ // must be menu item config?
20150 item = this.addMenuItem(el);
20157 * Returns this menu's underlying {@link Roo.Element} object
20158 * @return {Roo.Element} The element
20160 getEl : function(){
20168 * Adds a separator bar to the menu
20169 * @return {Roo.menu.Item} The menu item that was added
20171 addSeparator : function(){
20172 return this.addItem(new Roo.menu.Separator());
20176 * Adds an {@link Roo.Element} object to the menu
20177 * @param {String/HTMLElement/Roo.Element} el The element or DOM node to add, or its id
20178 * @return {Roo.menu.Item} The menu item that was added
20180 addElement : function(el){
20181 return this.addItem(new Roo.menu.BaseItem(el));
20185 * Adds an existing object based on {@link Roo.menu.Item} to the menu
20186 * @param {Roo.menu.Item} item The menu item to add
20187 * @return {Roo.menu.Item} The menu item that was added
20189 addItem : function(item){
20190 this.items.add(item);
20192 var li = document.createElement("li");
20193 li.className = "x-menu-list-item";
20194 this.ul.dom.appendChild(li);
20195 item.render(li, this);
20196 this.delayAutoWidth();
20202 * Creates a new {@link Roo.menu.Item} based an the supplied config object and adds it to the menu
20203 * @param {Object} config A MenuItem config object
20204 * @return {Roo.menu.Item} The menu item that was added
20206 addMenuItem : function(config){
20207 if(!(config instanceof Roo.menu.Item)){
20208 if(typeof config.checked == "boolean"){ // must be check menu item config?
20209 config = new Roo.menu.CheckItem(config);
20211 config = new Roo.menu.Item(config);
20214 return this.addItem(config);
20218 * Creates a new {@link Roo.menu.TextItem} with the supplied text and adds it to the menu
20219 * @param {String} text The text to display in the menu item
20220 * @return {Roo.menu.Item} The menu item that was added
20222 addText : function(text){
20223 return this.addItem(new Roo.menu.TextItem({ text : text }));
20227 * Inserts an existing object based on {@link Roo.menu.Item} to the menu at a specified index
20228 * @param {Number} index The index in the menu's list of current items where the new item should be inserted
20229 * @param {Roo.menu.Item} item The menu item to add
20230 * @return {Roo.menu.Item} The menu item that was added
20232 insert : function(index, item){
20233 this.items.insert(index, item);
20235 var li = document.createElement("li");
20236 li.className = "x-menu-list-item";
20237 this.ul.dom.insertBefore(li, this.ul.dom.childNodes[index]);
20238 item.render(li, this);
20239 this.delayAutoWidth();
20245 * Removes an {@link Roo.menu.Item} from the menu and destroys the object
20246 * @param {Roo.menu.Item} item The menu item to remove
20248 remove : function(item){
20249 this.items.removeKey(item.id);
20254 * Removes and destroys all items in the menu
20256 removeAll : function(){
20258 while(f = this.items.first()){
20264 // MenuNav is a private utility class used internally by the Menu
20265 Roo.menu.MenuNav = function(menu){
20266 Roo.menu.MenuNav.superclass.constructor.call(this, menu.el);
20267 this.scope = this.menu = menu;
20270 Roo.extend(Roo.menu.MenuNav, Roo.KeyNav, {
20271 doRelay : function(e, h){
20272 var k = e.getKey();
20273 if(!this.menu.activeItem && e.isNavKeyPress() && k != e.SPACE && k != e.RETURN){
20274 this.menu.tryActivate(0, 1);
20277 return h.call(this.scope || this, e, this.menu);
20280 up : function(e, m){
20281 if(!m.tryActivate(m.items.indexOf(m.activeItem)-1, -1)){
20282 m.tryActivate(m.items.length-1, -1);
20286 down : function(e, m){
20287 if(!m.tryActivate(m.items.indexOf(m.activeItem)+1, 1)){
20288 m.tryActivate(0, 1);
20292 right : function(e, m){
20294 m.activeItem.expandMenu(true);
20298 left : function(e, m){
20300 if(m.parentMenu && m.parentMenu.activeItem){
20301 m.parentMenu.activeItem.activate();
20305 enter : function(e, m){
20307 e.stopPropagation();
20308 m.activeItem.onClick(e);
20309 m.fireEvent("click", this, m.activeItem);
20315 * Ext JS Library 1.1.1
20316 * Copyright(c) 2006-2007, Ext JS, LLC.
20318 * Originally Released Under LGPL - original licence link has changed is not relivant.
20321 * <script type="text/javascript">
20325 * @class Roo.menu.MenuMgr
20326 * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
20329 Roo.menu.MenuMgr = function(){
20330 var menus, active, groups = {}, attached = false, lastShow = new Date();
20332 // private - called when first menu is created
20335 active = new Roo.util.MixedCollection();
20336 Roo.get(document).addKeyListener(27, function(){
20337 if(active.length > 0){
20344 function hideAll(){
20345 if(active && active.length > 0){
20346 var c = active.clone();
20347 c.each(function(m){
20354 function onHide(m){
20356 if(active.length < 1){
20357 Roo.get(document).un("mousedown", onMouseDown);
20363 function onShow(m){
20364 var last = active.last();
20365 lastShow = new Date();
20368 Roo.get(document).on("mousedown", onMouseDown);
20372 m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
20373 m.parentMenu.activeChild = m;
20374 }else if(last && last.isVisible()){
20375 m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
20380 function onBeforeHide(m){
20382 m.activeChild.hide();
20384 if(m.autoHideTimer){
20385 clearTimeout(m.autoHideTimer);
20386 delete m.autoHideTimer;
20391 function onBeforeShow(m){
20392 var pm = m.parentMenu;
20393 if(!pm && !m.allowOtherMenus){
20395 }else if(pm && pm.activeChild && active != m){
20396 pm.activeChild.hide();
20401 function onMouseDown(e){
20402 if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".x-menu")){
20408 function onBeforeCheck(mi, state){
20410 var g = groups[mi.group];
20411 for(var i = 0, l = g.length; i < l; i++){
20413 g[i].setChecked(false);
20422 * Hides all menus that are currently visible
20424 hideAll : function(){
20429 register : function(menu){
20433 menus[menu.id] = menu;
20434 menu.on("beforehide", onBeforeHide);
20435 menu.on("hide", onHide);
20436 menu.on("beforeshow", onBeforeShow);
20437 menu.on("show", onShow);
20438 var g = menu.group;
20439 if(g && menu.events["checkchange"]){
20443 groups[g].push(menu);
20444 menu.on("checkchange", onCheck);
20449 * Returns a {@link Roo.menu.Menu} object
20450 * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
20451 * be used to generate and return a new Menu instance.
20453 get : function(menu){
20454 if(typeof menu == "string"){ // menu id
20455 return menus[menu];
20456 }else if(menu.events){ // menu instance
20458 }else if(typeof menu.length == 'number'){ // array of menu items?
20459 return new Roo.menu.Menu({items:menu});
20460 }else{ // otherwise, must be a config
20461 return new Roo.menu.Menu(menu);
20466 unregister : function(menu){
20467 delete menus[menu.id];
20468 menu.un("beforehide", onBeforeHide);
20469 menu.un("hide", onHide);
20470 menu.un("beforeshow", onBeforeShow);
20471 menu.un("show", onShow);
20472 var g = menu.group;
20473 if(g && menu.events["checkchange"]){
20474 groups[g].remove(menu);
20475 menu.un("checkchange", onCheck);
20480 registerCheckable : function(menuItem){
20481 var g = menuItem.group;
20486 groups[g].push(menuItem);
20487 menuItem.on("beforecheckchange", onBeforeCheck);
20492 unregisterCheckable : function(menuItem){
20493 var g = menuItem.group;
20495 groups[g].remove(menuItem);
20496 menuItem.un("beforecheckchange", onBeforeCheck);
20502 * Ext JS Library 1.1.1
20503 * Copyright(c) 2006-2007, Ext JS, LLC.
20505 * Originally Released Under LGPL - original licence link has changed is not relivant.
20508 * <script type="text/javascript">
20513 * @class Roo.menu.BaseItem
20514 * @extends Roo.Component
20515 * The base class for all items that render into menus. BaseItem provides default rendering, activated state
20516 * management and base configuration options shared by all menu components.
20518 * Creates a new BaseItem
20519 * @param {Object} config Configuration options
20521 Roo.menu.BaseItem = function(config){
20522 Roo.menu.BaseItem.superclass.constructor.call(this, config);
20527 * Fires when this item is clicked
20528 * @param {Roo.menu.BaseItem} this
20529 * @param {Roo.EventObject} e
20534 * Fires when this item is activated
20535 * @param {Roo.menu.BaseItem} this
20539 * @event deactivate
20540 * Fires when this item is deactivated
20541 * @param {Roo.menu.BaseItem} this
20547 this.on("click", this.handler, this.scope, true);
20551 Roo.extend(Roo.menu.BaseItem, Roo.Component, {
20553 * @cfg {Function} handler
20554 * A function that will handle the click event of this menu item (defaults to undefined)
20557 * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to false)
20559 canActivate : false,
20562 * @cfg {Boolean} hidden True to prevent creation of this menu item (defaults to false)
20567 * @cfg {String} activeClass The CSS class to use when the item becomes activated (defaults to "x-menu-item-active")
20569 activeClass : "x-menu-item-active",
20571 * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to true)
20573 hideOnClick : true,
20575 * @cfg {Number} hideDelay Length of time in milliseconds to wait before hiding after a click (defaults to 100)
20580 ctype: "Roo.menu.BaseItem",
20583 actionMode : "container",
20586 render : function(container, parentMenu){
20587 this.parentMenu = parentMenu;
20588 Roo.menu.BaseItem.superclass.render.call(this, container);
20589 this.container.menuItemId = this.id;
20593 onRender : function(container, position){
20594 this.el = Roo.get(this.el);
20595 container.dom.appendChild(this.el.dom);
20599 onClick : function(e){
20600 if(!this.disabled && this.fireEvent("click", this, e) !== false
20601 && this.parentMenu.fireEvent("itemclick", this, e) !== false){
20602 this.handleClick(e);
20609 activate : function(){
20613 var li = this.container;
20614 li.addClass(this.activeClass);
20615 this.region = li.getRegion().adjust(2, 2, -2, -2);
20616 this.fireEvent("activate", this);
20621 deactivate : function(){
20622 this.container.removeClass(this.activeClass);
20623 this.fireEvent("deactivate", this);
20627 shouldDeactivate : function(e){
20628 return !this.region || !this.region.contains(e.getPoint());
20632 handleClick : function(e){
20633 if(this.hideOnClick){
20634 this.parentMenu.hide.defer(this.hideDelay, this.parentMenu, [true]);
20639 expandMenu : function(autoActivate){
20644 hideMenu : function(){
20649 * Ext JS Library 1.1.1
20650 * Copyright(c) 2006-2007, Ext JS, LLC.
20652 * Originally Released Under LGPL - original licence link has changed is not relivant.
20655 * <script type="text/javascript">
20659 * @class Roo.menu.Adapter
20660 * @extends Roo.menu.BaseItem
20661 * 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.
20662 * It provides basic rendering, activation management and enable/disable logic required to work in menus.
20664 * Creates a new Adapter
20665 * @param {Object} config Configuration options
20667 Roo.menu.Adapter = function(component, config){
20668 Roo.menu.Adapter.superclass.constructor.call(this, config);
20669 this.component = component;
20671 Roo.extend(Roo.menu.Adapter, Roo.menu.BaseItem, {
20673 canActivate : true,
20676 onRender : function(container, position){
20677 this.component.render(container);
20678 this.el = this.component.getEl();
20682 activate : function(){
20686 this.component.focus();
20687 this.fireEvent("activate", this);
20692 deactivate : function(){
20693 this.fireEvent("deactivate", this);
20697 disable : function(){
20698 this.component.disable();
20699 Roo.menu.Adapter.superclass.disable.call(this);
20703 enable : function(){
20704 this.component.enable();
20705 Roo.menu.Adapter.superclass.enable.call(this);
20709 * Ext JS Library 1.1.1
20710 * Copyright(c) 2006-2007, Ext JS, LLC.
20712 * Originally Released Under LGPL - original licence link has changed is not relivant.
20715 * <script type="text/javascript">
20719 * @class Roo.menu.TextItem
20720 * @extends Roo.menu.BaseItem
20721 * Adds a static text string to a menu, usually used as either a heading or group separator.
20722 * Note: old style constructor with text is still supported.
20725 * Creates a new TextItem
20726 * @param {Object} cfg Configuration
20728 Roo.menu.TextItem = function(cfg){
20729 if (typeof(cfg) == 'string') {
20732 Roo.apply(this,cfg);
20735 Roo.menu.TextItem.superclass.constructor.call(this);
20738 Roo.extend(Roo.menu.TextItem, Roo.menu.BaseItem, {
20740 * @cfg {Boolean} text Text to show on item.
20745 * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
20747 hideOnClick : false,
20749 * @cfg {String} itemCls The default CSS class to use for text items (defaults to "x-menu-text")
20751 itemCls : "x-menu-text",
20754 onRender : function(){
20755 var s = document.createElement("span");
20756 s.className = this.itemCls;
20757 s.innerHTML = this.text;
20759 Roo.menu.TextItem.superclass.onRender.apply(this, arguments);
20763 * Ext JS Library 1.1.1
20764 * Copyright(c) 2006-2007, Ext JS, LLC.
20766 * Originally Released Under LGPL - original licence link has changed is not relivant.
20769 * <script type="text/javascript">
20773 * @class Roo.menu.Separator
20774 * @extends Roo.menu.BaseItem
20775 * Adds a separator bar to a menu, used to divide logical groups of menu items. Generally you will
20776 * add one of these by using "-" in you call to add() or in your items config rather than creating one directly.
20778 * @param {Object} config Configuration options
20780 Roo.menu.Separator = function(config){
20781 Roo.menu.Separator.superclass.constructor.call(this, config);
20784 Roo.extend(Roo.menu.Separator, Roo.menu.BaseItem, {
20786 * @cfg {String} itemCls The default CSS class to use for separators (defaults to "x-menu-sep")
20788 itemCls : "x-menu-sep",
20790 * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
20792 hideOnClick : false,
20795 onRender : function(li){
20796 var s = document.createElement("span");
20797 s.className = this.itemCls;
20798 s.innerHTML = " ";
20800 li.addClass("x-menu-sep-li");
20801 Roo.menu.Separator.superclass.onRender.apply(this, arguments);
20805 * Ext JS Library 1.1.1
20806 * Copyright(c) 2006-2007, Ext JS, LLC.
20808 * Originally Released Under LGPL - original licence link has changed is not relivant.
20811 * <script type="text/javascript">
20814 * @class Roo.menu.Item
20815 * @extends Roo.menu.BaseItem
20816 * A base class for all menu items that require menu-related functionality (like sub-menus) and are not static
20817 * display items. Item extends the base functionality of {@link Roo.menu.BaseItem} by adding menu-specific
20818 * activation and click handling.
20820 * Creates a new Item
20821 * @param {Object} config Configuration options
20823 Roo.menu.Item = function(config){
20824 Roo.menu.Item.superclass.constructor.call(this, config);
20826 this.menu = Roo.menu.MenuMgr.get(this.menu);
20829 Roo.extend(Roo.menu.Item, Roo.menu.BaseItem, {
20832 * @cfg {String} text
20833 * The text to show on the menu item.
20837 * @cfg {String} HTML to render in menu
20838 * The text to show on the menu item (HTML version).
20842 * @cfg {String} icon
20843 * The path to an icon to display in this menu item (defaults to Roo.BLANK_IMAGE_URL)
20847 * @cfg {String} itemCls The default CSS class to use for menu items (defaults to "x-menu-item")
20849 itemCls : "x-menu-item",
20851 * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to true)
20853 canActivate : true,
20855 * @cfg {Number} showDelay Length of time in milliseconds to wait before showing this item (defaults to 200)
20858 // doc'd in BaseItem
20862 ctype: "Roo.menu.Item",
20865 onRender : function(container, position){
20866 var el = document.createElement("a");
20867 el.hideFocus = true;
20868 el.unselectable = "on";
20869 el.href = this.href || "#";
20870 if(this.hrefTarget){
20871 el.target = this.hrefTarget;
20873 el.className = this.itemCls + (this.menu ? " x-menu-item-arrow" : "") + (this.cls ? " " + this.cls : "");
20875 var html = this.html.length ? this.html : String.format('{0}',this.text);
20877 el.innerHTML = String.format(
20878 '<img src="{0}" class="x-menu-item-icon {1}" />' + html,
20879 this.icon || Roo.BLANK_IMAGE_URL, this.iconCls || '');
20881 Roo.menu.Item.superclass.onRender.call(this, container, position);
20885 * Sets the text to display in this menu item
20886 * @param {String} text The text to display
20887 * @param {Boolean} isHTML true to indicate text is pure html.
20889 setText : function(text, isHTML){
20897 var html = this.html.length ? this.html : String.format('{0}',this.text);
20899 this.el.update(String.format(
20900 '<img src="{0}" class="x-menu-item-icon {2}">' + html,
20901 this.icon || Roo.BLANK_IMAGE_URL, this.text, this.iconCls || ''));
20902 this.parentMenu.autoWidth();
20907 handleClick : function(e){
20908 if(!this.href){ // if no link defined, stop the event automatically
20911 Roo.menu.Item.superclass.handleClick.apply(this, arguments);
20915 activate : function(autoExpand){
20916 if(Roo.menu.Item.superclass.activate.apply(this, arguments)){
20926 shouldDeactivate : function(e){
20927 if(Roo.menu.Item.superclass.shouldDeactivate.call(this, e)){
20928 if(this.menu && this.menu.isVisible()){
20929 return !this.menu.getEl().getRegion().contains(e.getPoint());
20937 deactivate : function(){
20938 Roo.menu.Item.superclass.deactivate.apply(this, arguments);
20943 expandMenu : function(autoActivate){
20944 if(!this.disabled && this.menu){
20945 clearTimeout(this.hideTimer);
20946 delete this.hideTimer;
20947 if(!this.menu.isVisible() && !this.showTimer){
20948 this.showTimer = this.deferExpand.defer(this.showDelay, this, [autoActivate]);
20949 }else if (this.menu.isVisible() && autoActivate){
20950 this.menu.tryActivate(0, 1);
20956 deferExpand : function(autoActivate){
20957 delete this.showTimer;
20958 this.menu.show(this.container, this.parentMenu.subMenuAlign || "tl-tr?", this.parentMenu);
20960 this.menu.tryActivate(0, 1);
20965 hideMenu : function(){
20966 clearTimeout(this.showTimer);
20967 delete this.showTimer;
20968 if(!this.hideTimer && this.menu && this.menu.isVisible()){
20969 this.hideTimer = this.deferHide.defer(this.hideDelay, this);
20974 deferHide : function(){
20975 delete this.hideTimer;
20980 * Ext JS Library 1.1.1
20981 * Copyright(c) 2006-2007, Ext JS, LLC.
20983 * Originally Released Under LGPL - original licence link has changed is not relivant.
20986 * <script type="text/javascript">
20990 * @class Roo.menu.CheckItem
20991 * @extends Roo.menu.Item
20992 * Adds a menu item that contains a checkbox by default, but can also be part of a radio group.
20994 * Creates a new CheckItem
20995 * @param {Object} config Configuration options
20997 Roo.menu.CheckItem = function(config){
20998 Roo.menu.CheckItem.superclass.constructor.call(this, config);
21001 * @event beforecheckchange
21002 * Fires before the checked value is set, providing an opportunity to cancel if needed
21003 * @param {Roo.menu.CheckItem} this
21004 * @param {Boolean} checked The new checked value that will be set
21006 "beforecheckchange" : true,
21008 * @event checkchange
21009 * Fires after the checked value has been set
21010 * @param {Roo.menu.CheckItem} this
21011 * @param {Boolean} checked The checked value that was set
21013 "checkchange" : true
21015 if(this.checkHandler){
21016 this.on('checkchange', this.checkHandler, this.scope);
21019 Roo.extend(Roo.menu.CheckItem, Roo.menu.Item, {
21021 * @cfg {String} group
21022 * All check items with the same group name will automatically be grouped into a single-select
21023 * radio button group (defaults to '')
21026 * @cfg {String} itemCls The default CSS class to use for check items (defaults to "x-menu-item x-menu-check-item")
21028 itemCls : "x-menu-item x-menu-check-item",
21030 * @cfg {String} groupClass The default CSS class to use for radio group check items (defaults to "x-menu-group-item")
21032 groupClass : "x-menu-group-item",
21035 * @cfg {Boolean} checked True to initialize this checkbox as checked (defaults to false). Note that
21036 * if this checkbox is part of a radio group (group = true) only the last item in the group that is
21037 * initialized with checked = true will be rendered as checked.
21042 ctype: "Roo.menu.CheckItem",
21045 onRender : function(c){
21046 Roo.menu.CheckItem.superclass.onRender.apply(this, arguments);
21048 this.el.addClass(this.groupClass);
21050 Roo.menu.MenuMgr.registerCheckable(this);
21052 this.checked = false;
21053 this.setChecked(true, true);
21058 destroy : function(){
21060 Roo.menu.MenuMgr.unregisterCheckable(this);
21062 Roo.menu.CheckItem.superclass.destroy.apply(this, arguments);
21066 * Set the checked state of this item
21067 * @param {Boolean} checked The new checked value
21068 * @param {Boolean} suppressEvent (optional) True to prevent the checkchange event from firing (defaults to false)
21070 setChecked : function(state, suppressEvent){
21071 if(this.checked != state && this.fireEvent("beforecheckchange", this, state) !== false){
21072 if(this.container){
21073 this.container[state ? "addClass" : "removeClass"]("x-menu-item-checked");
21075 this.checked = state;
21076 if(suppressEvent !== true){
21077 this.fireEvent("checkchange", this, state);
21083 handleClick : function(e){
21084 if(!this.disabled && !(this.checked && this.group)){// disable unselect on radio item
21085 this.setChecked(!this.checked);
21087 Roo.menu.CheckItem.superclass.handleClick.apply(this, arguments);
21091 * Ext JS Library 1.1.1
21092 * Copyright(c) 2006-2007, Ext JS, LLC.
21094 * Originally Released Under LGPL - original licence link has changed is not relivant.
21097 * <script type="text/javascript">
21101 * @class Roo.menu.DateItem
21102 * @extends Roo.menu.Adapter
21103 * A menu item that wraps the {@link Roo.DatPicker} component.
21105 * Creates a new DateItem
21106 * @param {Object} config Configuration options
21108 Roo.menu.DateItem = function(config){
21109 Roo.menu.DateItem.superclass.constructor.call(this, new Roo.DatePicker(config), config);
21110 /** The Roo.DatePicker object @type Roo.DatePicker */
21111 this.picker = this.component;
21112 this.addEvents({select: true});
21114 this.picker.on("render", function(picker){
21115 picker.getEl().swallowEvent("click");
21116 picker.container.addClass("x-menu-date-item");
21119 this.picker.on("select", this.onSelect, this);
21122 Roo.extend(Roo.menu.DateItem, Roo.menu.Adapter, {
21124 onSelect : function(picker, date){
21125 this.fireEvent("select", this, date, picker);
21126 Roo.menu.DateItem.superclass.handleClick.call(this);
21130 * Ext JS Library 1.1.1
21131 * Copyright(c) 2006-2007, Ext JS, LLC.
21133 * Originally Released Under LGPL - original licence link has changed is not relivant.
21136 * <script type="text/javascript">
21140 * @class Roo.menu.ColorItem
21141 * @extends Roo.menu.Adapter
21142 * A menu item that wraps the {@link Roo.ColorPalette} component.
21144 * Creates a new ColorItem
21145 * @param {Object} config Configuration options
21147 Roo.menu.ColorItem = function(config){
21148 Roo.menu.ColorItem.superclass.constructor.call(this, new Roo.ColorPalette(config), config);
21149 /** The Roo.ColorPalette object @type Roo.ColorPalette */
21150 this.palette = this.component;
21151 this.relayEvents(this.palette, ["select"]);
21152 if(this.selectHandler){
21153 this.on('select', this.selectHandler, this.scope);
21156 Roo.extend(Roo.menu.ColorItem, Roo.menu.Adapter);/*
21158 * Ext JS Library 1.1.1
21159 * Copyright(c) 2006-2007, Ext JS, LLC.
21161 * Originally Released Under LGPL - original licence link has changed is not relivant.
21164 * <script type="text/javascript">
21169 * @class Roo.menu.DateMenu
21170 * @extends Roo.menu.Menu
21171 * A menu containing a {@link Roo.menu.DateItem} component (which provides a date picker).
21173 * Creates a new DateMenu
21174 * @param {Object} config Configuration options
21176 Roo.menu.DateMenu = function(config){
21177 Roo.menu.DateMenu.superclass.constructor.call(this, config);
21179 var di = new Roo.menu.DateItem(config);
21182 * The {@link Roo.DatePicker} instance for this DateMenu
21185 this.picker = di.picker;
21188 * @param {DatePicker} picker
21189 * @param {Date} date
21191 this.relayEvents(di, ["select"]);
21192 this.on('beforeshow', function(){
21194 this.picker.hideMonthPicker(false);
21198 Roo.extend(Roo.menu.DateMenu, Roo.menu.Menu, {
21202 * Ext JS Library 1.1.1
21203 * Copyright(c) 2006-2007, Ext JS, LLC.
21205 * Originally Released Under LGPL - original licence link has changed is not relivant.
21208 * <script type="text/javascript">
21213 * @class Roo.menu.ColorMenu
21214 * @extends Roo.menu.Menu
21215 * A menu containing a {@link Roo.menu.ColorItem} component (which provides a basic color picker).
21217 * Creates a new ColorMenu
21218 * @param {Object} config Configuration options
21220 Roo.menu.ColorMenu = function(config){
21221 Roo.menu.ColorMenu.superclass.constructor.call(this, config);
21223 var ci = new Roo.menu.ColorItem(config);
21226 * The {@link Roo.ColorPalette} instance for this ColorMenu
21227 * @type ColorPalette
21229 this.palette = ci.palette;
21232 * @param {ColorPalette} palette
21233 * @param {String} color
21235 this.relayEvents(ci, ["select"]);
21237 Roo.extend(Roo.menu.ColorMenu, Roo.menu.Menu);/*
21239 * Ext JS Library 1.1.1
21240 * Copyright(c) 2006-2007, Ext JS, LLC.
21242 * Originally Released Under LGPL - original licence link has changed is not relivant.
21245 * <script type="text/javascript">
21249 * @class Roo.form.Field
21250 * @extends Roo.BoxComponent
21251 * Base class for form fields that provides default event handling, sizing, value handling and other functionality.
21253 * Creates a new Field
21254 * @param {Object} config Configuration options
21256 Roo.form.Field = function(config){
21257 Roo.form.Field.superclass.constructor.call(this, config);
21260 Roo.extend(Roo.form.Field, Roo.BoxComponent, {
21262 * @cfg {String} fieldLabel Label to use when rendering a form.
21265 * @cfg {String} qtip Mouse over tip
21269 * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
21271 invalidClass : "x-form-invalid",
21273 * @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")
21275 invalidText : "The value in this field is invalid",
21277 * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
21279 focusClass : "x-form-focus",
21281 * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
21282 automatic validation (defaults to "keyup").
21284 validationEvent : "keyup",
21286 * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
21288 validateOnBlur : true,
21290 * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
21292 validationDelay : 250,
21294 * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
21295 * {tag: "input", type: "text", size: "20", autocomplete: "off"})
21297 defaultAutoCreate : {tag: "input", type: "text", size: "20", autocomplete: "off"},
21299 * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field")
21301 fieldClass : "x-form-field",
21303 * @cfg {String} msgTarget The location where error text should display. Should be one of the following values (defaults to 'qtip'):
21306 ----------- ----------------------------------------------------------------------
21307 qtip Display a quick tip when the user hovers over the field
21308 title Display a default browser title attribute popup
21309 under Add a block div beneath the field containing the error text
21310 side Add an error icon to the right of the field with a popup on hover
21311 [element id] Add the error text directly to the innerHTML of the specified element
21314 msgTarget : 'qtip',
21316 * @cfg {String} msgFx <b>Experimental</b> The effect used when displaying a validation message under the field (defaults to 'normal').
21321 * @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.
21326 * @cfg {Boolean} disabled True to disable the field (defaults to false).
21331 * @cfg {String} inputType The type attribute for input fields -- e.g. radio, text, password (defaults to "text").
21333 inputType : undefined,
21336 * @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).
21338 tabIndex : undefined,
21341 isFormField : true,
21346 * @property {Roo.Element} fieldEl
21347 * Element Containing the rendered Field (with label etc.)
21350 * @cfg {Mixed} value A value to initialize this field with.
21355 * @cfg {String} name The field's HTML name attribute.
21358 * @cfg {String} cls A CSS class to apply to the field's underlying element.
21362 initComponent : function(){
21363 Roo.form.Field.superclass.initComponent.call(this);
21367 * Fires when this field receives input focus.
21368 * @param {Roo.form.Field} this
21373 * Fires when this field loses input focus.
21374 * @param {Roo.form.Field} this
21378 * @event specialkey
21379 * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed. You can check
21380 * {@link Roo.EventObject#getKey} to determine which key was pressed.
21381 * @param {Roo.form.Field} this
21382 * @param {Roo.EventObject} e The event object
21387 * Fires just before the field blurs if the field value has changed.
21388 * @param {Roo.form.Field} this
21389 * @param {Mixed} newValue The new value
21390 * @param {Mixed} oldValue The original value
21395 * Fires after the field has been marked as invalid.
21396 * @param {Roo.form.Field} this
21397 * @param {String} msg The validation message
21402 * Fires after the field has been validated with no errors.
21403 * @param {Roo.form.Field} this
21408 * Fires after the key up
21409 * @param {Roo.form.Field} this
21410 * @param {Roo.EventObject} e The event Object
21417 * Returns the name attribute of the field if available
21418 * @return {String} name The field name
21420 getName: function(){
21421 return this.rendered && this.el.dom.name ? this.el.dom.name : (this.hiddenName || '');
21425 onRender : function(ct, position){
21426 Roo.form.Field.superclass.onRender.call(this, ct, position);
21428 var cfg = this.getAutoCreate();
21430 cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
21432 if (!cfg.name.length) {
21435 if(this.inputType){
21436 cfg.type = this.inputType;
21438 this.el = ct.createChild(cfg, position);
21440 var type = this.el.dom.type;
21442 if(type == 'password'){
21445 this.el.addClass('x-form-'+type);
21448 this.el.dom.readOnly = true;
21450 if(this.tabIndex !== undefined){
21451 this.el.dom.setAttribute('tabIndex', this.tabIndex);
21454 this.el.addClass([this.fieldClass, this.cls]);
21459 * Apply the behaviors of this component to an existing element. <b>This is used instead of render().</b>
21460 * @param {String/HTMLElement/Element} el The id of the node, a DOM node or an existing Element
21461 * @return {Roo.form.Field} this
21463 applyTo : function(target){
21464 this.allowDomMove = false;
21465 this.el = Roo.get(target);
21466 this.render(this.el.dom.parentNode);
21471 initValue : function(){
21472 if(this.value !== undefined){
21473 this.setValue(this.value);
21474 }else if(this.el.dom.value.length > 0){
21475 this.setValue(this.el.dom.value);
21480 * Returns true if this field has been changed since it was originally loaded and is not disabled.
21482 isDirty : function() {
21483 if(this.disabled) {
21486 return String(this.getValue()) !== String(this.originalValue);
21490 afterRender : function(){
21491 Roo.form.Field.superclass.afterRender.call(this);
21496 fireKey : function(e){
21497 //Roo.log('field ' + e.getKey());
21498 if(e.isNavKeyPress()){
21499 this.fireEvent("specialkey", this, e);
21504 * Resets the current field value to the originally loaded value and clears any validation messages
21506 reset : function(){
21507 this.setValue(this.originalValue);
21508 this.clearInvalid();
21512 initEvents : function(){
21513 // safari killled keypress - so keydown is now used..
21514 this.el.on("keydown" , this.fireKey, this);
21515 this.el.on("focus", this.onFocus, this);
21516 this.el.on("blur", this.onBlur, this);
21517 this.el.relayEvent('keyup', this);
21519 // reference to original value for reset
21520 this.originalValue = this.getValue();
21524 onFocus : function(){
21525 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
21526 this.el.addClass(this.focusClass);
21528 if(!this.hasFocus){
21529 this.hasFocus = true;
21530 this.startValue = this.getValue();
21531 this.fireEvent("focus", this);
21535 beforeBlur : Roo.emptyFn,
21538 onBlur : function(){
21540 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
21541 this.el.removeClass(this.focusClass);
21543 this.hasFocus = false;
21544 if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
21547 var v = this.getValue();
21548 if(String(v) !== String(this.startValue)){
21549 this.fireEvent('change', this, v, this.startValue);
21551 this.fireEvent("blur", this);
21555 * Returns whether or not the field value is currently valid
21556 * @param {Boolean} preventMark True to disable marking the field invalid
21557 * @return {Boolean} True if the value is valid, else false
21559 isValid : function(preventMark){
21563 var restore = this.preventMark;
21564 this.preventMark = preventMark === true;
21565 var v = this.validateValue(this.processValue(this.getRawValue()));
21566 this.preventMark = restore;
21571 * Validates the field value
21572 * @return {Boolean} True if the value is valid, else false
21574 validate : function(){
21575 if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
21576 this.clearInvalid();
21582 processValue : function(value){
21587 // Subclasses should provide the validation implementation by overriding this
21588 validateValue : function(value){
21593 * Mark this field as invalid
21594 * @param {String} msg The validation message
21596 markInvalid : function(msg){
21597 if(!this.rendered || this.preventMark){ // not rendered
21600 this.el.addClass(this.invalidClass);
21601 msg = msg || this.invalidText;
21602 switch(this.msgTarget){
21604 this.el.dom.qtip = msg;
21605 this.el.dom.qclass = 'x-form-invalid-tip';
21606 if(Roo.QuickTips){ // fix for floating editors interacting with DND
21607 Roo.QuickTips.enable();
21611 this.el.dom.title = msg;
21615 var elp = this.el.findParent('.x-form-element', 5, true);
21616 this.errorEl = elp.createChild({cls:'x-form-invalid-msg'});
21617 this.errorEl.setWidth(elp.getWidth(true)-20);
21619 this.errorEl.update(msg);
21620 Roo.form.Field.msgFx[this.msgFx].show(this.errorEl, this);
21623 if(!this.errorIcon){
21624 var elp = this.el.findParent('.x-form-element', 5, true);
21625 this.errorIcon = elp.createChild({cls:'x-form-invalid-icon'});
21627 this.alignErrorIcon();
21628 this.errorIcon.dom.qtip = msg;
21629 this.errorIcon.dom.qclass = 'x-form-invalid-tip';
21630 this.errorIcon.show();
21631 this.on('resize', this.alignErrorIcon, this);
21634 var t = Roo.getDom(this.msgTarget);
21636 t.style.display = this.msgDisplay;
21639 this.fireEvent('invalid', this, msg);
21643 alignErrorIcon : function(){
21644 this.errorIcon.alignTo(this.el, 'tl-tr', [2, 0]);
21648 * Clear any invalid styles/messages for this field
21650 clearInvalid : function(){
21651 if(!this.rendered || this.preventMark){ // not rendered
21654 this.el.removeClass(this.invalidClass);
21655 switch(this.msgTarget){
21657 this.el.dom.qtip = '';
21660 this.el.dom.title = '';
21664 Roo.form.Field.msgFx[this.msgFx].hide(this.errorEl, this);
21668 if(this.errorIcon){
21669 this.errorIcon.dom.qtip = '';
21670 this.errorIcon.hide();
21671 this.un('resize', this.alignErrorIcon, this);
21675 var t = Roo.getDom(this.msgTarget);
21677 t.style.display = 'none';
21680 this.fireEvent('valid', this);
21684 * Returns the raw data value which may or may not be a valid, defined value. To return a normalized value see {@link #getValue}.
21685 * @return {Mixed} value The field value
21687 getRawValue : function(){
21688 var v = this.el.getValue();
21694 * Returns the normalized data value (undefined or emptyText will be returned as ''). To return the raw value see {@link #getRawValue}.
21695 * @return {Mixed} value The field value
21697 getValue : function(){
21698 var v = this.el.getValue();
21704 * Sets the underlying DOM field's value directly, bypassing validation. To set the value with validation see {@link #setValue}.
21705 * @param {Mixed} value The value to set
21707 setRawValue : function(v){
21708 return this.el.dom.value = (v === null || v === undefined ? '' : v);
21712 * Sets a data value into the field and validates it. To set the value directly without validation see {@link #setRawValue}.
21713 * @param {Mixed} value The value to set
21715 setValue : function(v){
21718 this.el.dom.value = (v === null || v === undefined ? '' : v);
21723 adjustSize : function(w, h){
21724 var s = Roo.form.Field.superclass.adjustSize.call(this, w, h);
21725 s.width = this.adjustWidth(this.el.dom.tagName, s.width);
21729 adjustWidth : function(tag, w){
21730 tag = tag.toLowerCase();
21731 if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
21732 if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
21733 if(tag == 'input'){
21736 if(tag == 'textarea'){
21739 }else if(Roo.isOpera){
21740 if(tag == 'input'){
21743 if(tag == 'textarea'){
21753 // anything other than normal should be considered experimental
21754 Roo.form.Field.msgFx = {
21756 show: function(msgEl, f){
21757 msgEl.setDisplayed('block');
21760 hide : function(msgEl, f){
21761 msgEl.setDisplayed(false).update('');
21766 show: function(msgEl, f){
21767 msgEl.slideIn('t', {stopFx:true});
21770 hide : function(msgEl, f){
21771 msgEl.slideOut('t', {stopFx:true,useDisplay:true});
21776 show: function(msgEl, f){
21777 msgEl.fixDisplay();
21778 msgEl.alignTo(f.el, 'tl-tr');
21779 msgEl.slideIn('l', {stopFx:true});
21782 hide : function(msgEl, f){
21783 msgEl.slideOut('l', {stopFx:true,useDisplay:true});
21788 * Ext JS Library 1.1.1
21789 * Copyright(c) 2006-2007, Ext JS, LLC.
21791 * Originally Released Under LGPL - original licence link has changed is not relivant.
21794 * <script type="text/javascript">
21799 * @class Roo.form.TextField
21800 * @extends Roo.form.Field
21801 * Basic text field. Can be used as a direct replacement for traditional text inputs, or as the base
21802 * class for more sophisticated input controls (like {@link Roo.form.TextArea} and {@link Roo.form.ComboBox}).
21804 * Creates a new TextField
21805 * @param {Object} config Configuration options
21807 Roo.form.TextField = function(config){
21808 Roo.form.TextField.superclass.constructor.call(this, config);
21812 * Fires when the autosize function is triggered. The field may or may not have actually changed size
21813 * according to the default logic, but this event provides a hook for the developer to apply additional
21814 * logic at runtime to resize the field if needed.
21815 * @param {Roo.form.Field} this This text field
21816 * @param {Number} width The new field width
21822 Roo.extend(Roo.form.TextField, Roo.form.Field, {
21824 * @cfg {Boolean} grow True if this field should automatically grow and shrink to its content
21828 * @cfg {Number} growMin The minimum width to allow when grow = true (defaults to 30)
21832 * @cfg {Number} growMax The maximum width to allow when grow = true (defaults to 800)
21836 * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
21840 * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
21844 * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
21846 disableKeyFilter : false,
21848 * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
21852 * @cfg {Number} minLength Minimum input field length required (defaults to 0)
21856 * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
21858 maxLength : Number.MAX_VALUE,
21860 * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
21862 minLengthText : "The minimum length for this field is {0}",
21864 * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
21866 maxLengthText : "The maximum length for this field is {0}",
21868 * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
21870 selectOnFocus : false,
21872 * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
21874 blankText : "This field is required",
21876 * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
21877 * If available, this function will be called only after the basic validators all return true, and will be passed the
21878 * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
21882 * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
21883 * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
21884 * current field value. If the test fails, the field will be marked invalid using {@link #regexText}.
21888 * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
21892 * @cfg {String} emptyText The default text to display in an empty field - placeholder... (defaults to null).
21898 initEvents : function()
21900 if (this.emptyText) {
21901 this.el.attr('placeholder', this.emptyText);
21904 Roo.form.TextField.superclass.initEvents.call(this);
21905 if(this.validationEvent == 'keyup'){
21906 this.validationTask = new Roo.util.DelayedTask(this.validate, this);
21907 this.el.on('keyup', this.filterValidation, this);
21909 else if(this.validationEvent !== false){
21910 this.el.on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
21913 if(this.selectOnFocus){
21914 this.on("focus", this.preFocus, this);
21917 if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
21918 this.el.on("keypress", this.filterKeys, this);
21921 this.el.on("keyup", this.onKeyUp, this, {buffer:50});
21922 this.el.on("click", this.autoSize, this);
21924 if(this.el.is('input[type=password]') && Roo.isSafari){
21925 this.el.on('keydown', this.SafariOnKeyDown, this);
21929 processValue : function(value){
21930 if(this.stripCharsRe){
21931 var newValue = value.replace(this.stripCharsRe, '');
21932 if(newValue !== value){
21933 this.setRawValue(newValue);
21940 filterValidation : function(e){
21941 if(!e.isNavKeyPress()){
21942 this.validationTask.delay(this.validationDelay);
21947 onKeyUp : function(e){
21948 if(!e.isNavKeyPress()){
21954 * Resets the current field value to the originally-loaded value and clears any validation messages.
21957 reset : function(){
21958 Roo.form.TextField.superclass.reset.call(this);
21964 preFocus : function(){
21966 if(this.selectOnFocus){
21967 this.el.dom.select();
21973 filterKeys : function(e){
21974 var k = e.getKey();
21975 if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
21978 var c = e.getCharCode(), cc = String.fromCharCode(c);
21979 if(Roo.isIE && (e.isSpecialKey() || !cc)){
21982 if(!this.maskRe.test(cc)){
21987 setValue : function(v){
21989 Roo.form.TextField.superclass.setValue.apply(this, arguments);
21995 * Validates a value according to the field's validation rules and marks the field as invalid
21996 * if the validation fails
21997 * @param {Mixed} value The value to validate
21998 * @return {Boolean} True if the value is valid, else false
22000 validateValue : function(value){
22001 if(value.length < 1) { // if it's blank
22002 if(this.allowBlank){
22003 this.clearInvalid();
22006 this.markInvalid(this.blankText);
22010 if(value.length < this.minLength){
22011 this.markInvalid(String.format(this.minLengthText, this.minLength));
22014 if(value.length > this.maxLength){
22015 this.markInvalid(String.format(this.maxLengthText, this.maxLength));
22019 var vt = Roo.form.VTypes;
22020 if(!vt[this.vtype](value, this)){
22021 this.markInvalid(this.vtypeText || vt[this.vtype +'Text']);
22025 if(typeof this.validator == "function"){
22026 var msg = this.validator(value);
22028 this.markInvalid(msg);
22032 if(this.regex && !this.regex.test(value)){
22033 this.markInvalid(this.regexText);
22040 * Selects text in this field
22041 * @param {Number} start (optional) The index where the selection should start (defaults to 0)
22042 * @param {Number} end (optional) The index where the selection should end (defaults to the text length)
22044 selectText : function(start, end){
22045 var v = this.getRawValue();
22047 start = start === undefined ? 0 : start;
22048 end = end === undefined ? v.length : end;
22049 var d = this.el.dom;
22050 if(d.setSelectionRange){
22051 d.setSelectionRange(start, end);
22052 }else if(d.createTextRange){
22053 var range = d.createTextRange();
22054 range.moveStart("character", start);
22055 range.moveEnd("character", v.length-end);
22062 * Automatically grows the field to accomodate the width of the text up to the maximum field width allowed.
22063 * This only takes effect if grow = true, and fires the autosize event.
22065 autoSize : function(){
22066 if(!this.grow || !this.rendered){
22070 this.metrics = Roo.util.TextMetrics.createInstance(this.el);
22073 var v = el.dom.value;
22074 var d = document.createElement('div');
22075 d.appendChild(document.createTextNode(v));
22079 var w = Math.min(this.growMax, Math.max(this.metrics.getWidth(v) + /* add extra padding */ 10, this.growMin));
22080 this.el.setWidth(w);
22081 this.fireEvent("autosize", this, w);
22085 SafariOnKeyDown : function(event)
22087 // this is a workaround for a password hang bug on chrome/ webkit.
22089 var isSelectAll = false;
22091 if(this.el.dom.selectionEnd > 0){
22092 isSelectAll = (this.el.dom.selectionEnd - this.el.dom.selectionStart - this.getValue().length == 0) ? true : false;
22094 if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
22095 event.preventDefault();
22100 if(isSelectAll){ // backspace and delete key
22102 event.preventDefault();
22103 // this is very hacky as keydown always get's upper case.
22105 var cc = String.fromCharCode(event.getCharCode());
22106 this.setValue( event.shiftKey ? cc : cc.toLowerCase());
22114 * Ext JS Library 1.1.1
22115 * Copyright(c) 2006-2007, Ext JS, LLC.
22117 * Originally Released Under LGPL - original licence link has changed is not relivant.
22120 * <script type="text/javascript">
22124 * @class Roo.form.Hidden
22125 * @extends Roo.form.TextField
22126 * Simple Hidden element used on forms
22128 * usage: form.add(new Roo.form.HiddenField({ 'name' : 'test1' }));
22131 * Creates a new Hidden form element.
22132 * @param {Object} config Configuration options
22137 // easy hidden field...
22138 Roo.form.Hidden = function(config){
22139 Roo.form.Hidden.superclass.constructor.call(this, config);
22142 Roo.extend(Roo.form.Hidden, Roo.form.TextField, {
22144 inputType: 'hidden',
22147 labelSeparator: '',
22149 itemCls : 'x-form-item-display-none'
22157 * Ext JS Library 1.1.1
22158 * Copyright(c) 2006-2007, Ext JS, LLC.
22160 * Originally Released Under LGPL - original licence link has changed is not relivant.
22163 * <script type="text/javascript">
22167 * @class Roo.form.TriggerField
22168 * @extends Roo.form.TextField
22169 * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
22170 * The trigger has no default action, so you must assign a function to implement the trigger click handler by
22171 * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
22172 * for which you can provide a custom implementation. For example:
22174 var trigger = new Roo.form.TriggerField();
22175 trigger.onTriggerClick = myTriggerFn;
22176 trigger.applyTo('my-field');
22179 * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
22180 * {@link Roo.form.DateField} and {@link Roo.form.ComboBox} are perfect examples of this.
22181 * @cfg {String} triggerClass An additional CSS class used to style the trigger button. The trigger will always get the
22182 * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
22184 * Create a new TriggerField.
22185 * @param {Object} config Configuration options (valid {@Roo.form.TextField} config options will also be applied
22186 * to the base TextField)
22188 Roo.form.TriggerField = function(config){
22189 this.mimicing = false;
22190 Roo.form.TriggerField.superclass.constructor.call(this, config);
22193 Roo.extend(Roo.form.TriggerField, Roo.form.TextField, {
22195 * @cfg {String} triggerClass A CSS class to apply to the trigger
22198 * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
22199 * {tag: "input", type: "text", size: "16", autocomplete: "off"})
22201 defaultAutoCreate : {tag: "input", type: "text", size: "16", autocomplete: "off"},
22203 * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
22207 /** @cfg {Boolean} grow @hide */
22208 /** @cfg {Number} growMin @hide */
22209 /** @cfg {Number} growMax @hide */
22215 autoSize: Roo.emptyFn,
22219 deferHeight : true,
22222 actionMode : 'wrap',
22224 onResize : function(w, h){
22225 Roo.form.TriggerField.superclass.onResize.apply(this, arguments);
22226 if(typeof w == 'number'){
22227 var x = w - this.trigger.getWidth();
22228 this.el.setWidth(this.adjustWidth('input', x));
22229 this.trigger.setStyle('left', x+'px');
22234 adjustSize : Roo.BoxComponent.prototype.adjustSize,
22237 getResizeEl : function(){
22242 getPositionEl : function(){
22247 alignErrorIcon : function(){
22248 this.errorIcon.alignTo(this.wrap, 'tl-tr', [2, 0]);
22252 onRender : function(ct, position){
22253 Roo.form.TriggerField.superclass.onRender.call(this, ct, position);
22254 this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
22255 this.trigger = this.wrap.createChild(this.triggerConfig ||
22256 {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.triggerClass});
22257 if(this.hideTrigger){
22258 this.trigger.setDisplayed(false);
22260 this.initTrigger();
22262 this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
22267 initTrigger : function(){
22268 this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
22269 this.trigger.addClassOnOver('x-form-trigger-over');
22270 this.trigger.addClassOnClick('x-form-trigger-click');
22274 onDestroy : function(){
22276 this.trigger.removeAllListeners();
22277 this.trigger.remove();
22280 this.wrap.remove();
22282 Roo.form.TriggerField.superclass.onDestroy.call(this);
22286 onFocus : function(){
22287 Roo.form.TriggerField.superclass.onFocus.call(this);
22288 if(!this.mimicing){
22289 this.wrap.addClass('x-trigger-wrap-focus');
22290 this.mimicing = true;
22291 Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
22292 if(this.monitorTab){
22293 this.el.on("keydown", this.checkTab, this);
22299 checkTab : function(e){
22300 if(e.getKey() == e.TAB){
22301 this.triggerBlur();
22306 onBlur : function(){
22311 mimicBlur : function(e, t){
22312 if(!this.wrap.contains(t) && this.validateBlur()){
22313 this.triggerBlur();
22318 triggerBlur : function(){
22319 this.mimicing = false;
22320 Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
22321 if(this.monitorTab){
22322 this.el.un("keydown", this.checkTab, this);
22324 this.wrap.removeClass('x-trigger-wrap-focus');
22325 Roo.form.TriggerField.superclass.onBlur.call(this);
22329 // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
22330 validateBlur : function(e, t){
22335 onDisable : function(){
22336 Roo.form.TriggerField.superclass.onDisable.call(this);
22338 this.wrap.addClass('x-item-disabled');
22343 onEnable : function(){
22344 Roo.form.TriggerField.superclass.onEnable.call(this);
22346 this.wrap.removeClass('x-item-disabled');
22351 onShow : function(){
22352 var ae = this.getActionEl();
22355 ae.dom.style.display = '';
22356 ae.dom.style.visibility = 'visible';
22362 onHide : function(){
22363 var ae = this.getActionEl();
22364 ae.dom.style.display = 'none';
22368 * The function that should handle the trigger's click event. This method does nothing by default until overridden
22369 * by an implementing function.
22371 * @param {EventObject} e
22373 onTriggerClick : Roo.emptyFn
22376 // TwinTriggerField is not a public class to be used directly. It is meant as an abstract base class
22377 // to be extended by an implementing class. For an example of implementing this class, see the custom
22378 // SearchField implementation here: http://extjs.com/deploy/ext/examples/form/custom.html
22379 Roo.form.TwinTriggerField = Roo.extend(Roo.form.TriggerField, {
22380 initComponent : function(){
22381 Roo.form.TwinTriggerField.superclass.initComponent.call(this);
22383 this.triggerConfig = {
22384 tag:'span', cls:'x-form-twin-triggers', cn:[
22385 {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger1Class},
22386 {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger2Class}
22390 getTrigger : function(index){
22391 return this.triggers[index];
22394 initTrigger : function(){
22395 var ts = this.trigger.select('.x-form-trigger', true);
22396 this.wrap.setStyle('overflow', 'hidden');
22397 var triggerField = this;
22398 ts.each(function(t, all, index){
22399 t.hide = function(){
22400 var w = triggerField.wrap.getWidth();
22401 this.dom.style.display = 'none';
22402 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
22404 t.show = function(){
22405 var w = triggerField.wrap.getWidth();
22406 this.dom.style.display = '';
22407 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
22409 var triggerIndex = 'Trigger'+(index+1);
22411 if(this['hide'+triggerIndex]){
22412 t.dom.style.display = 'none';
22414 t.on("click", this['on'+triggerIndex+'Click'], this, {preventDefault:true});
22415 t.addClassOnOver('x-form-trigger-over');
22416 t.addClassOnClick('x-form-trigger-click');
22418 this.triggers = ts.elements;
22421 onTrigger1Click : Roo.emptyFn,
22422 onTrigger2Click : Roo.emptyFn
22425 * Ext JS Library 1.1.1
22426 * Copyright(c) 2006-2007, Ext JS, LLC.
22428 * Originally Released Under LGPL - original licence link has changed is not relivant.
22431 * <script type="text/javascript">
22435 * @class Roo.form.TextArea
22436 * @extends Roo.form.TextField
22437 * Multiline text field. Can be used as a direct replacement for traditional textarea fields, plus adds
22438 * support for auto-sizing.
22440 * Creates a new TextArea
22441 * @param {Object} config Configuration options
22443 Roo.form.TextArea = function(config){
22444 Roo.form.TextArea.superclass.constructor.call(this, config);
22445 // these are provided exchanges for backwards compat
22446 // minHeight/maxHeight were replaced by growMin/growMax to be
22447 // compatible with TextField growing config values
22448 if(this.minHeight !== undefined){
22449 this.growMin = this.minHeight;
22451 if(this.maxHeight !== undefined){
22452 this.growMax = this.maxHeight;
22456 Roo.extend(Roo.form.TextArea, Roo.form.TextField, {
22458 * @cfg {Number} growMin The minimum height to allow when grow = true (defaults to 60)
22462 * @cfg {Number} growMax The maximum height to allow when grow = true (defaults to 1000)
22466 * @cfg {Boolean} preventScrollbars True to prevent scrollbars from appearing regardless of how much text is
22467 * in the field (equivalent to setting overflow: hidden, defaults to false)
22469 preventScrollbars: false,
22471 * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
22472 * {tag: "textarea", style: "width:300px;height:60px;", autocomplete: "off"})
22476 onRender : function(ct, position){
22478 this.defaultAutoCreate = {
22480 style:"width:300px;height:60px;",
22481 autocomplete: "off"
22484 Roo.form.TextArea.superclass.onRender.call(this, ct, position);
22486 this.textSizeEl = Roo.DomHelper.append(document.body, {
22487 tag: "pre", cls: "x-form-grow-sizer"
22489 if(this.preventScrollbars){
22490 this.el.setStyle("overflow", "hidden");
22492 this.el.setHeight(this.growMin);
22496 onDestroy : function(){
22497 if(this.textSizeEl){
22498 this.textSizeEl.parentNode.removeChild(this.textSizeEl);
22500 Roo.form.TextArea.superclass.onDestroy.call(this);
22504 onKeyUp : function(e){
22505 if(!e.isNavKeyPress() || e.getKey() == e.ENTER){
22511 * Automatically grows the field to accomodate the height of the text up to the maximum field height allowed.
22512 * This only takes effect if grow = true, and fires the autosize event if the height changes.
22514 autoSize : function(){
22515 if(!this.grow || !this.textSizeEl){
22519 var v = el.dom.value;
22520 var ts = this.textSizeEl;
22523 ts.appendChild(document.createTextNode(v));
22526 Roo.fly(ts).setWidth(this.el.getWidth());
22528 v = "  ";
22531 v = v.replace(/\n/g, '<p> </p>');
22533 v += " \n ";
22536 var h = Math.min(this.growMax, Math.max(ts.offsetHeight, this.growMin));
22537 if(h != this.lastHeight){
22538 this.lastHeight = h;
22539 this.el.setHeight(h);
22540 this.fireEvent("autosize", this, h);
22545 * Ext JS Library 1.1.1
22546 * Copyright(c) 2006-2007, Ext JS, LLC.
22548 * Originally Released Under LGPL - original licence link has changed is not relivant.
22551 * <script type="text/javascript">
22556 * @class Roo.form.NumberField
22557 * @extends Roo.form.TextField
22558 * Numeric text field that provides automatic keystroke filtering and numeric validation.
22560 * Creates a new NumberField
22561 * @param {Object} config Configuration options
22563 Roo.form.NumberField = function(config){
22564 Roo.form.NumberField.superclass.constructor.call(this, config);
22567 Roo.extend(Roo.form.NumberField, Roo.form.TextField, {
22569 * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field x-form-num-field")
22571 fieldClass: "x-form-field x-form-num-field",
22573 * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
22575 allowDecimals : true,
22577 * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
22579 decimalSeparator : ".",
22581 * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
22583 decimalPrecision : 2,
22585 * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
22587 allowNegative : true,
22589 * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
22591 minValue : Number.NEGATIVE_INFINITY,
22593 * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
22595 maxValue : Number.MAX_VALUE,
22597 * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
22599 minText : "The minimum value for this field is {0}",
22601 * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
22603 maxText : "The maximum value for this field is {0}",
22605 * @cfg {String} nanText Error text to display if the value is not a valid number. For example, this can happen
22606 * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
22608 nanText : "{0} is not a valid number",
22611 initEvents : function(){
22612 Roo.form.NumberField.superclass.initEvents.call(this);
22613 var allowed = "0123456789";
22614 if(this.allowDecimals){
22615 allowed += this.decimalSeparator;
22617 if(this.allowNegative){
22620 this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
22621 var keyPress = function(e){
22622 var k = e.getKey();
22623 if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
22626 var c = e.getCharCode();
22627 if(allowed.indexOf(String.fromCharCode(c)) === -1){
22631 this.el.on("keypress", keyPress, this);
22635 validateValue : function(value){
22636 if(!Roo.form.NumberField.superclass.validateValue.call(this, value)){
22639 if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
22642 var num = this.parseValue(value);
22644 this.markInvalid(String.format(this.nanText, value));
22647 if(num < this.minValue){
22648 this.markInvalid(String.format(this.minText, this.minValue));
22651 if(num > this.maxValue){
22652 this.markInvalid(String.format(this.maxText, this.maxValue));
22658 getValue : function(){
22659 return this.fixPrecision(this.parseValue(Roo.form.NumberField.superclass.getValue.call(this)));
22663 parseValue : function(value){
22664 value = parseFloat(String(value).replace(this.decimalSeparator, "."));
22665 return isNaN(value) ? '' : value;
22669 fixPrecision : function(value){
22670 var nan = isNaN(value);
22671 if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
22672 return nan ? '' : value;
22674 return parseFloat(value).toFixed(this.decimalPrecision);
22677 setValue : function(v){
22678 v = this.fixPrecision(v);
22679 Roo.form.NumberField.superclass.setValue.call(this, String(v).replace(".", this.decimalSeparator));
22683 decimalPrecisionFcn : function(v){
22684 return Math.floor(v);
22687 beforeBlur : function(){
22688 var v = this.parseValue(this.getRawValue());
22695 * Ext JS Library 1.1.1
22696 * Copyright(c) 2006-2007, Ext JS, LLC.
22698 * Originally Released Under LGPL - original licence link has changed is not relivant.
22701 * <script type="text/javascript">
22705 * @class Roo.form.DateField
22706 * @extends Roo.form.TriggerField
22707 * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
22709 * Create a new DateField
22710 * @param {Object} config
22712 Roo.form.DateField = function(config){
22713 Roo.form.DateField.superclass.constructor.call(this, config);
22719 * Fires when a date is selected
22720 * @param {Roo.form.DateField} combo This combo box
22721 * @param {Date} date The date selected
22728 if(typeof this.minValue == "string") this.minValue = this.parseDate(this.minValue);
22729 if(typeof this.maxValue == "string") this.maxValue = this.parseDate(this.maxValue);
22730 this.ddMatch = null;
22731 if(this.disabledDates){
22732 var dd = this.disabledDates;
22734 for(var i = 0; i < dd.length; i++){
22736 if(i != dd.length-1) re += "|";
22738 this.ddMatch = new RegExp(re + ")");
22742 Roo.extend(Roo.form.DateField, Roo.form.TriggerField, {
22744 * @cfg {String} format
22745 * The default date format string which can be overriden for localization support. The format must be
22746 * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
22750 * @cfg {String} altFormats
22751 * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
22752 * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
22754 altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
22756 * @cfg {Array} disabledDays
22757 * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
22759 disabledDays : null,
22761 * @cfg {String} disabledDaysText
22762 * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
22764 disabledDaysText : "Disabled",
22766 * @cfg {Array} disabledDates
22767 * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
22768 * expression so they are very powerful. Some examples:
22770 * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
22771 * <li>["03/08", "09/16"] would disable those days for every year</li>
22772 * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
22773 * <li>["03/../2006"] would disable every day in March 2006</li>
22774 * <li>["^03"] would disable every day in every March</li>
22776 * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
22777 * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
22779 disabledDates : null,
22781 * @cfg {String} disabledDatesText
22782 * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
22784 disabledDatesText : "Disabled",
22786 * @cfg {Date/String} minValue
22787 * The minimum allowed date. Can be either a Javascript date object or a string date in a
22788 * valid format (defaults to null).
22792 * @cfg {Date/String} maxValue
22793 * The maximum allowed date. Can be either a Javascript date object or a string date in a
22794 * valid format (defaults to null).
22798 * @cfg {String} minText
22799 * The error text to display when the date in the cell is before minValue (defaults to
22800 * 'The date in this field must be after {minValue}').
22802 minText : "The date in this field must be equal to or after {0}",
22804 * @cfg {String} maxText
22805 * The error text to display when the date in the cell is after maxValue (defaults to
22806 * 'The date in this field must be before {maxValue}').
22808 maxText : "The date in this field must be equal to or before {0}",
22810 * @cfg {String} invalidText
22811 * The error text to display when the date in the field is invalid (defaults to
22812 * '{value} is not a valid date - it must be in the format {format}').
22814 invalidText : "{0} is not a valid date - it must be in the format {1}",
22816 * @cfg {String} triggerClass
22817 * An additional CSS class used to style the trigger button. The trigger will always get the
22818 * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
22819 * which displays a calendar icon).
22821 triggerClass : 'x-form-date-trigger',
22825 * @cfg {Boolean} useIso
22826 * if enabled, then the date field will use a hidden field to store the
22827 * real value as iso formated date. default (false)
22831 * @cfg {String/Object} autoCreate
22832 * A DomHelper element spec, or true for a default element spec (defaults to
22833 * {tag: "input", type: "text", size: "10", autocomplete: "off"})
22836 defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "off"},
22839 hiddenField: false,
22841 onRender : function(ct, position)
22843 Roo.form.DateField.superclass.onRender.call(this, ct, position);
22845 //this.el.dom.removeAttribute('name');
22846 Roo.log("Changing name?");
22847 this.el.dom.setAttribute('name', this.name + '____hidden___' );
22848 this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
22850 this.hiddenField.value = this.value ? this.formatDate(this.value, 'Y-m-d') : '';
22851 // prevent input submission
22852 this.hiddenName = this.name;
22859 validateValue : function(value)
22861 value = this.formatDate(value);
22862 if(!Roo.form.DateField.superclass.validateValue.call(this, value)){
22863 Roo.log('super failed');
22866 if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
22869 var svalue = value;
22870 value = this.parseDate(value);
22872 Roo.log('parse date failed' + svalue);
22873 this.markInvalid(String.format(this.invalidText, svalue, this.format));
22876 var time = value.getTime();
22877 if(this.minValue && time < this.minValue.getTime()){
22878 this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
22881 if(this.maxValue && time > this.maxValue.getTime()){
22882 this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
22885 if(this.disabledDays){
22886 var day = value.getDay();
22887 for(var i = 0; i < this.disabledDays.length; i++) {
22888 if(day === this.disabledDays[i]){
22889 this.markInvalid(this.disabledDaysText);
22894 var fvalue = this.formatDate(value);
22895 if(this.ddMatch && this.ddMatch.test(fvalue)){
22896 this.markInvalid(String.format(this.disabledDatesText, fvalue));
22903 // Provides logic to override the default TriggerField.validateBlur which just returns true
22904 validateBlur : function(){
22905 return !this.menu || !this.menu.isVisible();
22908 getName: function()
22910 // returns hidden if it's set..
22911 if (!this.rendered) {return ''};
22912 return !this.hiddenName && this.el.dom.name ? this.el.dom.name : (this.hiddenName || '');
22917 * Returns the current date value of the date field.
22918 * @return {Date} The date value
22920 getValue : function(){
22922 return this.hiddenField ?
22923 this.hiddenField.value :
22924 this.parseDate(Roo.form.DateField.superclass.getValue.call(this)) || "";
22928 * Sets the value of the date field. You can pass a date object or any string that can be parsed into a valid
22929 * date, using DateField.format as the date format, according to the same rules as {@link Date#parseDate}
22930 * (the default format used is "m/d/y").
22933 //All of these calls set the same date value (May 4, 2006)
22935 //Pass a date object:
22936 var dt = new Date('5/4/06');
22937 dateField.setValue(dt);
22939 //Pass a date string (default format):
22940 dateField.setValue('5/4/06');
22942 //Pass a date string (custom format):
22943 dateField.format = 'Y-m-d';
22944 dateField.setValue('2006-5-4');
22946 * @param {String/Date} date The date or valid date string
22948 setValue : function(date){
22949 if (this.hiddenField) {
22950 this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
22952 Roo.form.DateField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
22953 // make sure the value field is always stored as a date..
22954 this.value = this.parseDate(date);
22960 parseDate : function(value){
22961 if(!value || value instanceof Date){
22964 var v = Date.parseDate(value, this.format);
22965 if (!v && this.useIso) {
22966 v = Date.parseDate(value, 'Y-m-d');
22968 if(!v && this.altFormats){
22969 if(!this.altFormatsArray){
22970 this.altFormatsArray = this.altFormats.split("|");
22972 for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
22973 v = Date.parseDate(value, this.altFormatsArray[i]);
22980 formatDate : function(date, fmt){
22981 return (!date || !(date instanceof Date)) ?
22982 date : date.dateFormat(fmt || this.format);
22987 select: function(m, d){
22990 this.fireEvent('select', this, d);
22992 show : function(){ // retain focus styling
22996 this.focus.defer(10, this);
22997 var ml = this.menuListeners;
22998 this.menu.un("select", ml.select, this);
22999 this.menu.un("show", ml.show, this);
23000 this.menu.un("hide", ml.hide, this);
23005 // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
23006 onTriggerClick : function(){
23010 if(this.menu == null){
23011 this.menu = new Roo.menu.DateMenu();
23013 Roo.apply(this.menu.picker, {
23014 showClear: this.allowBlank,
23015 minDate : this.minValue,
23016 maxDate : this.maxValue,
23017 disabledDatesRE : this.ddMatch,
23018 disabledDatesText : this.disabledDatesText,
23019 disabledDays : this.disabledDays,
23020 disabledDaysText : this.disabledDaysText,
23021 format : this.useIso ? 'Y-m-d' : this.format,
23022 minText : String.format(this.minText, this.formatDate(this.minValue)),
23023 maxText : String.format(this.maxText, this.formatDate(this.maxValue))
23025 this.menu.on(Roo.apply({}, this.menuListeners, {
23028 this.menu.picker.setValue(this.getValue() || new Date());
23029 this.menu.show(this.el, "tl-bl?");
23032 beforeBlur : function(){
23033 var v = this.parseDate(this.getRawValue());
23039 /** @cfg {Boolean} grow @hide */
23040 /** @cfg {Number} growMin @hide */
23041 /** @cfg {Number} growMax @hide */
23048 * Ext JS Library 1.1.1
23049 * Copyright(c) 2006-2007, Ext JS, LLC.
23051 * Originally Released Under LGPL - original licence link has changed is not relivant.
23054 * <script type="text/javascript">
23058 * @class Roo.form.MonthField
23059 * @extends Roo.form.TriggerField
23060 * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
23062 * Create a new MonthField
23063 * @param {Object} config
23065 Roo.form.MonthField = function(config){
23067 Roo.form.MonthField.superclass.constructor.call(this, config);
23073 * Fires when a date is selected
23074 * @param {Roo.form.MonthFieeld} combo This combo box
23075 * @param {Date} date The date selected
23082 if(typeof this.minValue == "string") this.minValue = this.parseDate(this.minValue);
23083 if(typeof this.maxValue == "string") this.maxValue = this.parseDate(this.maxValue);
23084 this.ddMatch = null;
23085 if(this.disabledDates){
23086 var dd = this.disabledDates;
23088 for(var i = 0; i < dd.length; i++){
23090 if(i != dd.length-1) re += "|";
23092 this.ddMatch = new RegExp(re + ")");
23096 Roo.extend(Roo.form.MonthField, Roo.form.TriggerField, {
23098 * @cfg {String} format
23099 * The default date format string which can be overriden for localization support. The format must be
23100 * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
23104 * @cfg {String} altFormats
23105 * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
23106 * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
23108 altFormats : "M Y|m/Y|m-y|m-Y|my|mY",
23110 * @cfg {Array} disabledDays
23111 * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
23113 disabledDays : [0,1,2,3,4,5,6],
23115 * @cfg {String} disabledDaysText
23116 * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
23118 disabledDaysText : "Disabled",
23120 * @cfg {Array} disabledDates
23121 * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
23122 * expression so they are very powerful. Some examples:
23124 * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
23125 * <li>["03/08", "09/16"] would disable those days for every year</li>
23126 * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
23127 * <li>["03/../2006"] would disable every day in March 2006</li>
23128 * <li>["^03"] would disable every day in every March</li>
23130 * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
23131 * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
23133 disabledDates : null,
23135 * @cfg {String} disabledDatesText
23136 * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
23138 disabledDatesText : "Disabled",
23140 * @cfg {Date/String} minValue
23141 * The minimum allowed date. Can be either a Javascript date object or a string date in a
23142 * valid format (defaults to null).
23146 * @cfg {Date/String} maxValue
23147 * The maximum allowed date. Can be either a Javascript date object or a string date in a
23148 * valid format (defaults to null).
23152 * @cfg {String} minText
23153 * The error text to display when the date in the cell is before minValue (defaults to
23154 * 'The date in this field must be after {minValue}').
23156 minText : "The date in this field must be equal to or after {0}",
23158 * @cfg {String} maxTextf
23159 * The error text to display when the date in the cell is after maxValue (defaults to
23160 * 'The date in this field must be before {maxValue}').
23162 maxText : "The date in this field must be equal to or before {0}",
23164 * @cfg {String} invalidText
23165 * The error text to display when the date in the field is invalid (defaults to
23166 * '{value} is not a valid date - it must be in the format {format}').
23168 invalidText : "{0} is not a valid date - it must be in the format {1}",
23170 * @cfg {String} triggerClass
23171 * An additional CSS class used to style the trigger button. The trigger will always get the
23172 * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
23173 * which displays a calendar icon).
23175 triggerClass : 'x-form-date-trigger',
23179 * @cfg {Boolean} useIso
23180 * if enabled, then the date field will use a hidden field to store the
23181 * real value as iso formated date. default (true)
23185 * @cfg {String/Object} autoCreate
23186 * A DomHelper element spec, or true for a default element spec (defaults to
23187 * {tag: "input", type: "text", size: "10", autocomplete: "off"})
23190 defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "off"},
23193 hiddenField: false,
23195 hideMonthPicker : false,
23197 onRender : function(ct, position)
23199 Roo.form.MonthField.superclass.onRender.call(this, ct, position);
23201 this.el.dom.removeAttribute('name');
23202 this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
23204 this.hiddenField.value = this.value ? this.formatDate(this.value, 'Y-m-d') : '';
23205 // prevent input submission
23206 this.hiddenName = this.name;
23213 validateValue : function(value)
23215 value = this.formatDate(value);
23216 if(!Roo.form.MonthField.superclass.validateValue.call(this, value)){
23219 if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
23222 var svalue = value;
23223 value = this.parseDate(value);
23225 this.markInvalid(String.format(this.invalidText, svalue, this.format));
23228 var time = value.getTime();
23229 if(this.minValue && time < this.minValue.getTime()){
23230 this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
23233 if(this.maxValue && time > this.maxValue.getTime()){
23234 this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
23237 /*if(this.disabledDays){
23238 var day = value.getDay();
23239 for(var i = 0; i < this.disabledDays.length; i++) {
23240 if(day === this.disabledDays[i]){
23241 this.markInvalid(this.disabledDaysText);
23247 var fvalue = this.formatDate(value);
23248 /*if(this.ddMatch && this.ddMatch.test(fvalue)){
23249 this.markInvalid(String.format(this.disabledDatesText, fvalue));
23257 // Provides logic to override the default TriggerField.validateBlur which just returns true
23258 validateBlur : function(){
23259 return !this.menu || !this.menu.isVisible();
23263 * Returns the current date value of the date field.
23264 * @return {Date} The date value
23266 getValue : function(){
23270 return this.hiddenField ?
23271 this.hiddenField.value :
23272 this.parseDate(Roo.form.MonthField.superclass.getValue.call(this)) || "";
23276 * Sets the value of the date field. You can pass a date object or any string that can be parsed into a valid
23277 * date, using MonthField.format as the date format, according to the same rules as {@link Date#parseDate}
23278 * (the default format used is "m/d/y").
23281 //All of these calls set the same date value (May 4, 2006)
23283 //Pass a date object:
23284 var dt = new Date('5/4/06');
23285 monthField.setValue(dt);
23287 //Pass a date string (default format):
23288 monthField.setValue('5/4/06');
23290 //Pass a date string (custom format):
23291 monthField.format = 'Y-m-d';
23292 monthField.setValue('2006-5-4');
23294 * @param {String/Date} date The date or valid date string
23296 setValue : function(date){
23297 Roo.log('month setValue' + date);
23298 // can only be first of month..
23300 var val = this.parseDate(date);
23302 if (this.hiddenField) {
23303 this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
23305 Roo.form.MonthField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
23306 this.value = this.parseDate(date);
23310 parseDate : function(value){
23311 if(!value || value instanceof Date){
23312 value = value ? Date.parseDate(value.format('Y-m') + '-01', 'Y-m-d') : null;
23315 var v = Date.parseDate(value, this.format);
23316 if (!v && this.useIso) {
23317 v = Date.parseDate(value, 'Y-m-d');
23321 v = Date.parseDate(v.format('Y-m') +'-01', 'Y-m-d');
23325 if(!v && this.altFormats){
23326 if(!this.altFormatsArray){
23327 this.altFormatsArray = this.altFormats.split("|");
23329 for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
23330 v = Date.parseDate(value, this.altFormatsArray[i]);
23337 formatDate : function(date, fmt){
23338 return (!date || !(date instanceof Date)) ?
23339 date : date.dateFormat(fmt || this.format);
23344 select: function(m, d){
23346 this.fireEvent('select', this, d);
23348 show : function(){ // retain focus styling
23352 this.focus.defer(10, this);
23353 var ml = this.menuListeners;
23354 this.menu.un("select", ml.select, this);
23355 this.menu.un("show", ml.show, this);
23356 this.menu.un("hide", ml.hide, this);
23360 // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
23361 onTriggerClick : function(){
23365 if(this.menu == null){
23366 this.menu = new Roo.menu.DateMenu();
23370 Roo.apply(this.menu.picker, {
23372 showClear: this.allowBlank,
23373 minDate : this.minValue,
23374 maxDate : this.maxValue,
23375 disabledDatesRE : this.ddMatch,
23376 disabledDatesText : this.disabledDatesText,
23378 format : this.useIso ? 'Y-m-d' : this.format,
23379 minText : String.format(this.minText, this.formatDate(this.minValue)),
23380 maxText : String.format(this.maxText, this.formatDate(this.maxValue))
23383 this.menu.on(Roo.apply({}, this.menuListeners, {
23391 // hide month picker get's called when we called by 'before hide';
23393 var ignorehide = true;
23394 p.hideMonthPicker = function(disableAnim){
23398 if(this.monthPicker){
23399 Roo.log("hideMonthPicker called");
23400 if(disableAnim === true){
23401 this.monthPicker.hide();
23403 this.monthPicker.slideOut('t', {duration:.2});
23404 p.setValue(new Date(m.picker.mpSelYear, m.picker.mpSelMonth, 1));
23405 p.fireEvent("select", this, this.value);
23411 Roo.log('picker set value');
23412 Roo.log(this.getValue());
23413 p.setValue(this.getValue() ? this.parseDate(this.getValue()) : new Date());
23414 m.show(this.el, 'tl-bl?');
23415 ignorehide = false;
23416 // this will trigger hideMonthPicker..
23419 // hidden the day picker
23420 Roo.select('.x-date-picker table', true).first().dom.style.visibility = "hidden";
23426 p.showMonthPicker.defer(100, p);
23432 beforeBlur : function(){
23433 var v = this.parseDate(this.getRawValue());
23439 /** @cfg {Boolean} grow @hide */
23440 /** @cfg {Number} growMin @hide */
23441 /** @cfg {Number} growMax @hide */
23448 * Ext JS Library 1.1.1
23449 * Copyright(c) 2006-2007, Ext JS, LLC.
23451 * Originally Released Under LGPL - original licence link has changed is not relivant.
23454 * <script type="text/javascript">
23459 * @class Roo.form.ComboBox
23460 * @extends Roo.form.TriggerField
23461 * A combobox control with support for autocomplete, remote-loading, paging and many other features.
23463 * Create a new ComboBox.
23464 * @param {Object} config Configuration options
23466 Roo.form.ComboBox = function(config){
23467 Roo.form.ComboBox.superclass.constructor.call(this, config);
23471 * Fires when the dropdown list is expanded
23472 * @param {Roo.form.ComboBox} combo This combo box
23477 * Fires when the dropdown list is collapsed
23478 * @param {Roo.form.ComboBox} combo This combo box
23482 * @event beforeselect
23483 * Fires before a list item is selected. Return false to cancel the selection.
23484 * @param {Roo.form.ComboBox} combo This combo box
23485 * @param {Roo.data.Record} record The data record returned from the underlying store
23486 * @param {Number} index The index of the selected item in the dropdown list
23488 'beforeselect' : true,
23491 * Fires when a list item is selected
23492 * @param {Roo.form.ComboBox} combo This combo box
23493 * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
23494 * @param {Number} index The index of the selected item in the dropdown list
23498 * @event beforequery
23499 * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
23500 * The event object passed has these properties:
23501 * @param {Roo.form.ComboBox} combo This combo box
23502 * @param {String} query The query
23503 * @param {Boolean} forceAll true to force "all" query
23504 * @param {Boolean} cancel true to cancel the query
23505 * @param {Object} e The query event object
23507 'beforequery': true,
23510 * Fires when the 'add' icon is pressed (add a listener to enable add button)
23511 * @param {Roo.form.ComboBox} combo This combo box
23516 * Fires when the 'edit' icon is pressed (add a listener to enable add button)
23517 * @param {Roo.form.ComboBox} combo This combo box
23518 * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
23524 if(this.transform){
23525 this.allowDomMove = false;
23526 var s = Roo.getDom(this.transform);
23527 if(!this.hiddenName){
23528 this.hiddenName = s.name;
23531 this.mode = 'local';
23532 var d = [], opts = s.options;
23533 for(var i = 0, len = opts.length;i < len; i++){
23535 var value = (Roo.isIE ? o.getAttributeNode('value').specified : o.hasAttribute('value')) ? o.value : o.text;
23537 this.value = value;
23539 d.push([value, o.text]);
23541 this.store = new Roo.data.SimpleStore({
23543 fields: ['value', 'text'],
23546 this.valueField = 'value';
23547 this.displayField = 'text';
23549 s.name = Roo.id(); // wipe out the name in case somewhere else they have a reference
23550 if(!this.lazyRender){
23551 this.target = true;
23552 this.el = Roo.DomHelper.insertBefore(s, this.autoCreate || this.defaultAutoCreate);
23553 s.parentNode.removeChild(s); // remove it
23554 this.render(this.el.parentNode);
23556 s.parentNode.removeChild(s); // remove it
23561 this.store = Roo.factory(this.store, Roo.data);
23564 this.selectedIndex = -1;
23565 if(this.mode == 'local'){
23566 if(config.queryDelay === undefined){
23567 this.queryDelay = 10;
23569 if(config.minChars === undefined){
23575 Roo.extend(Roo.form.ComboBox, Roo.form.TriggerField, {
23577 * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
23580 * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
23581 * rendering into an Roo.Editor, defaults to false)
23584 * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
23585 * {tag: "input", type: "text", size: "24", autocomplete: "off"})
23588 * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
23591 * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
23592 * the dropdown list (defaults to undefined, with no header element)
23596 * @cfg {String/Roo.Template} tpl The template to use to render the output
23600 defaultAutoCreate : {tag: "input", type: "text", size: "24", autocomplete: "off"},
23602 * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
23604 listWidth: undefined,
23606 * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
23607 * mode = 'remote' or 'text' if mode = 'local')
23609 displayField: undefined,
23611 * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
23612 * mode = 'remote' or 'value' if mode = 'local').
23613 * Note: use of a valueField requires the user make a selection
23614 * in order for a value to be mapped.
23616 valueField: undefined,
23620 * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
23621 * field's data value (defaults to the underlying DOM element's name)
23623 hiddenName: undefined,
23625 * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
23629 * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
23631 selectedClass: 'x-combo-selected',
23633 * @cfg {String} triggerClass An additional CSS class used to style the trigger button. The trigger will always get the
23634 * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
23635 * which displays a downward arrow icon).
23637 triggerClass : 'x-form-arrow-trigger',
23639 * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
23643 * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
23644 * anchor positions (defaults to 'tl-bl')
23646 listAlign: 'tl-bl?',
23648 * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
23652 * @cfg {String} triggerAction The action to execute when the trigger field is activated. Use 'all' to run the
23653 * query specified by the allQuery config option (defaults to 'query')
23655 triggerAction: 'query',
23657 * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
23658 * (defaults to 4, does not apply if editable = false)
23662 * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
23663 * delay (typeAheadDelay) if it matches a known value (defaults to false)
23667 * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
23668 * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
23672 * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
23673 * filter queries will execute with page start and limit parameters. Only applies when mode = 'remote' (defaults to 0)
23677 * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus. Only applies
23678 * when editable = true (defaults to false)
23680 selectOnFocus:false,
23682 * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
23684 queryParam: 'query',
23686 * @cfg {String} loadingText The text to display in the dropdown list while data is loading. Only applies
23687 * when mode = 'remote' (defaults to 'Loading...')
23689 loadingText: 'Loading...',
23691 * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
23695 * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
23699 * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
23700 * traditional select (defaults to true)
23704 * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
23708 * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
23712 * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
23713 * listWidth has a higher value)
23717 * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
23718 * allow the user to set arbitrary text into the field (defaults to false)
23720 forceSelection:false,
23722 * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
23723 * if typeAhead = true (defaults to 250)
23725 typeAheadDelay : 250,
23727 * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
23728 * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
23730 valueNotFoundText : undefined,
23732 * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
23734 blockFocus : false,
23737 * @cfg {Boolean} disableClear Disable showing of clear button.
23739 disableClear : false,
23741 * @cfg {Boolean} alwaysQuery Disable caching of results, and always send query
23743 alwaysQuery : false,
23749 // element that contains real text value.. (when hidden is used..)
23752 onRender : function(ct, position){
23753 Roo.form.ComboBox.superclass.onRender.call(this, ct, position);
23754 if(this.hiddenName){
23755 this.hiddenField = this.el.insertSibling({tag:'input', type:'hidden', name: this.hiddenName, id: (this.hiddenId||this.hiddenName)},
23757 this.hiddenField.value =
23758 this.hiddenValue !== undefined ? this.hiddenValue :
23759 this.value !== undefined ? this.value : '';
23761 // prevent input submission
23762 this.el.dom.removeAttribute('name');
23767 this.el.dom.setAttribute('autocomplete', 'off');
23770 var cls = 'x-combo-list';
23772 this.list = new Roo.Layer({
23773 shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
23776 var lw = this.listWidth || Math.max(this.wrap.getWidth(), this.minListWidth);
23777 this.list.setWidth(lw);
23778 this.list.swallowEvent('mousewheel');
23779 this.assetHeight = 0;
23782 this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
23783 this.assetHeight += this.header.getHeight();
23786 this.innerList = this.list.createChild({cls:cls+'-inner'});
23787 this.innerList.on('mouseover', this.onViewOver, this);
23788 this.innerList.on('mousemove', this.onViewMove, this);
23789 this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
23791 if(this.allowBlank && !this.pageSize && !this.disableClear){
23792 this.footer = this.list.createChild({cls:cls+'-ft'});
23793 this.pageTb = new Roo.Toolbar(this.footer);
23797 this.footer = this.list.createChild({cls:cls+'-ft'});
23798 this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
23799 {pageSize: this.pageSize});
23803 if (this.pageTb && this.allowBlank && !this.disableClear) {
23805 this.pageTb.add(new Roo.Toolbar.Fill(), {
23806 cls: 'x-btn-icon x-btn-clear',
23808 handler: function()
23811 _this.clearValue();
23812 _this.onSelect(false, -1);
23817 this.assetHeight += this.footer.getHeight();
23822 this.tpl = '<div class="'+cls+'-item">{' + this.displayField + '}</div>';
23825 this.view = new Roo.View(this.innerList, this.tpl, {
23826 singleSelect:true, store: this.store, selectedClass: this.selectedClass
23829 this.view.on('click', this.onViewClick, this);
23831 this.store.on('beforeload', this.onBeforeLoad, this);
23832 this.store.on('load', this.onLoad, this);
23833 this.store.on('loadexception', this.onLoadException, this);
23835 if(this.resizable){
23836 this.resizer = new Roo.Resizable(this.list, {
23837 pinned:true, handles:'se'
23839 this.resizer.on('resize', function(r, w, h){
23840 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
23841 this.listWidth = w;
23842 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
23843 this.restrictHeight();
23845 this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
23847 if(!this.editable){
23848 this.editable = true;
23849 this.setEditable(false);
23853 if (typeof(this.events.add.listeners) != 'undefined') {
23855 this.addicon = this.wrap.createChild(
23856 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });
23858 this.addicon.on('click', function(e) {
23859 this.fireEvent('add', this);
23862 if (typeof(this.events.edit.listeners) != 'undefined') {
23864 this.editicon = this.wrap.createChild(
23865 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });
23866 if (this.addicon) {
23867 this.editicon.setStyle('margin-left', '40px');
23869 this.editicon.on('click', function(e) {
23871 // we fire even if inothing is selected..
23872 this.fireEvent('edit', this, this.lastData );
23882 initEvents : function(){
23883 Roo.form.ComboBox.superclass.initEvents.call(this);
23885 this.keyNav = new Roo.KeyNav(this.el, {
23886 "up" : function(e){
23887 this.inKeyMode = true;
23891 "down" : function(e){
23892 if(!this.isExpanded()){
23893 this.onTriggerClick();
23895 this.inKeyMode = true;
23900 "enter" : function(e){
23901 this.onViewClick();
23905 "esc" : function(e){
23909 "tab" : function(e){
23910 this.onViewClick(false);
23911 this.fireEvent("specialkey", this, e);
23917 doRelay : function(foo, bar, hname){
23918 if(hname == 'down' || this.scope.isExpanded()){
23919 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
23926 this.queryDelay = Math.max(this.queryDelay || 10,
23927 this.mode == 'local' ? 10 : 250);
23928 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
23929 if(this.typeAhead){
23930 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
23932 if(this.editable !== false){
23933 this.el.on("keyup", this.onKeyUp, this);
23935 if(this.forceSelection){
23936 this.on('blur', this.doForce, this);
23940 onDestroy : function(){
23942 this.view.setStore(null);
23943 this.view.el.removeAllListeners();
23944 this.view.el.remove();
23945 this.view.purgeListeners();
23948 this.list.destroy();
23951 this.store.un('beforeload', this.onBeforeLoad, this);
23952 this.store.un('load', this.onLoad, this);
23953 this.store.un('loadexception', this.onLoadException, this);
23955 Roo.form.ComboBox.superclass.onDestroy.call(this);
23959 fireKey : function(e){
23960 if(e.isNavKeyPress() && !this.list.isVisible()){
23961 this.fireEvent("specialkey", this, e);
23966 onResize: function(w, h){
23967 Roo.form.ComboBox.superclass.onResize.apply(this, arguments);
23969 if(typeof w != 'number'){
23970 // we do not handle it!?!?
23973 var tw = this.trigger.getWidth();
23974 tw += this.addicon ? this.addicon.getWidth() : 0;
23975 tw += this.editicon ? this.editicon.getWidth() : 0;
23977 this.el.setWidth( this.adjustWidth('input', x));
23979 this.trigger.setStyle('left', x+'px');
23981 if(this.list && this.listWidth === undefined){
23982 var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
23983 this.list.setWidth(lw);
23984 this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
23992 * Allow or prevent the user from directly editing the field text. If false is passed,
23993 * the user will only be able to select from the items defined in the dropdown list. This method
23994 * is the runtime equivalent of setting the 'editable' config option at config time.
23995 * @param {Boolean} value True to allow the user to directly edit the field text
23997 setEditable : function(value){
23998 if(value == this.editable){
24001 this.editable = value;
24003 this.el.dom.setAttribute('readOnly', true);
24004 this.el.on('mousedown', this.onTriggerClick, this);
24005 this.el.addClass('x-combo-noedit');
24007 this.el.dom.setAttribute('readOnly', false);
24008 this.el.un('mousedown', this.onTriggerClick, this);
24009 this.el.removeClass('x-combo-noedit');
24014 onBeforeLoad : function(){
24015 if(!this.hasFocus){
24018 this.innerList.update(this.loadingText ?
24019 '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
24020 this.restrictHeight();
24021 this.selectedIndex = -1;
24025 onLoad : function(){
24026 if(!this.hasFocus){
24029 if(this.store.getCount() > 0){
24031 this.restrictHeight();
24032 if(this.lastQuery == this.allQuery){
24034 this.el.dom.select();
24036 if(!this.selectByValue(this.value, true)){
24037 this.select(0, true);
24041 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
24042 this.taTask.delay(this.typeAheadDelay);
24046 this.onEmptyResults();
24051 onLoadException : function()
24054 Roo.log(this.store.reader.jsonData);
24055 if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
24056 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
24062 onTypeAhead : function(){
24063 if(this.store.getCount() > 0){
24064 var r = this.store.getAt(0);
24065 var newValue = r.data[this.displayField];
24066 var len = newValue.length;
24067 var selStart = this.getRawValue().length;
24068 if(selStart != len){
24069 this.setRawValue(newValue);
24070 this.selectText(selStart, newValue.length);
24076 onSelect : function(record, index){
24077 if(this.fireEvent('beforeselect', this, record, index) !== false){
24078 this.setFromData(index > -1 ? record.data : false);
24080 this.fireEvent('select', this, record, index);
24085 * Returns the currently selected field value or empty string if no value is set.
24086 * @return {String} value The selected value
24088 getValue : function(){
24089 if(this.valueField){
24090 return typeof this.value != 'undefined' ? this.value : '';
24092 return Roo.form.ComboBox.superclass.getValue.call(this);
24097 * Clears any text/value currently set in the field
24099 clearValue : function(){
24100 if(this.hiddenField){
24101 this.hiddenField.value = '';
24104 this.setRawValue('');
24105 this.lastSelectionText = '';
24110 * Sets the specified value into the field. If the value finds a match, the corresponding record text
24111 * will be displayed in the field. If the value does not match the data value of an existing item,
24112 * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
24113 * Otherwise the field will be blank (although the value will still be set).
24114 * @param {String} value The value to match
24116 setValue : function(v){
24118 if(this.valueField){
24119 var r = this.findRecord(this.valueField, v);
24121 text = r.data[this.displayField];
24122 }else if(this.valueNotFoundText !== undefined){
24123 text = this.valueNotFoundText;
24126 this.lastSelectionText = text;
24127 if(this.hiddenField){
24128 this.hiddenField.value = v;
24130 Roo.form.ComboBox.superclass.setValue.call(this, text);
24134 * @property {Object} the last set data for the element
24139 * Sets the value of the field based on a object which is related to the record format for the store.
24140 * @param {Object} value the value to set as. or false on reset?
24142 setFromData : function(o){
24143 var dv = ''; // display value
24144 var vv = ''; // value value..
24146 if (this.displayField) {
24147 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
24149 // this is an error condition!!!
24150 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
24153 if(this.valueField){
24154 vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
24156 if(this.hiddenField){
24157 this.hiddenField.value = vv;
24159 this.lastSelectionText = dv;
24160 Roo.form.ComboBox.superclass.setValue.call(this, dv);
24164 // no hidden field.. - we store the value in 'value', but still display
24165 // display field!!!!
24166 this.lastSelectionText = dv;
24167 Roo.form.ComboBox.superclass.setValue.call(this, dv);
24173 reset : function(){
24174 // overridden so that last data is reset..
24175 this.setValue(this.originalValue);
24176 this.clearInvalid();
24177 this.lastData = false;
24179 this.view.clearSelections();
24183 findRecord : function(prop, value){
24185 if(this.store.getCount() > 0){
24186 this.store.each(function(r){
24187 if(r.data[prop] == value){
24197 getName: function()
24199 // returns hidden if it's set..
24200 if (!this.rendered) {return ''};
24201 return !this.hiddenName && this.el.dom.name ? this.el.dom.name : (this.hiddenName || '');
24205 onViewMove : function(e, t){
24206 this.inKeyMode = false;
24210 onViewOver : function(e, t){
24211 if(this.inKeyMode){ // prevent key nav and mouse over conflicts
24214 var item = this.view.findItemFromChild(t);
24216 var index = this.view.indexOf(item);
24217 this.select(index, false);
24222 onViewClick : function(doFocus)
24224 var index = this.view.getSelectedIndexes()[0];
24225 var r = this.store.getAt(index);
24227 this.onSelect(r, index);
24229 if(doFocus !== false && !this.blockFocus){
24235 restrictHeight : function(){
24236 this.innerList.dom.style.height = '';
24237 var inner = this.innerList.dom;
24238 var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
24239 this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
24240 this.list.beginUpdate();
24241 this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
24242 this.list.alignTo(this.el, this.listAlign);
24243 this.list.endUpdate();
24247 onEmptyResults : function(){
24252 * Returns true if the dropdown list is expanded, else false.
24254 isExpanded : function(){
24255 return this.list.isVisible();
24259 * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
24260 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
24261 * @param {String} value The data value of the item to select
24262 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
24263 * selected item if it is not currently in view (defaults to true)
24264 * @return {Boolean} True if the value matched an item in the list, else false
24266 selectByValue : function(v, scrollIntoView){
24267 if(v !== undefined && v !== null){
24268 var r = this.findRecord(this.valueField || this.displayField, v);
24270 this.select(this.store.indexOf(r), scrollIntoView);
24278 * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
24279 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
24280 * @param {Number} index The zero-based index of the list item to select
24281 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
24282 * selected item if it is not currently in view (defaults to true)
24284 select : function(index, scrollIntoView){
24285 this.selectedIndex = index;
24286 this.view.select(index);
24287 if(scrollIntoView !== false){
24288 var el = this.view.getNode(index);
24290 this.innerList.scrollChildIntoView(el, false);
24296 selectNext : function(){
24297 var ct = this.store.getCount();
24299 if(this.selectedIndex == -1){
24301 }else if(this.selectedIndex < ct-1){
24302 this.select(this.selectedIndex+1);
24308 selectPrev : function(){
24309 var ct = this.store.getCount();
24311 if(this.selectedIndex == -1){
24313 }else if(this.selectedIndex != 0){
24314 this.select(this.selectedIndex-1);
24320 onKeyUp : function(e){
24321 if(this.editable !== false && !e.isSpecialKey()){
24322 this.lastKey = e.getKey();
24323 this.dqTask.delay(this.queryDelay);
24328 validateBlur : function(){
24329 return !this.list || !this.list.isVisible();
24333 initQuery : function(){
24334 this.doQuery(this.getRawValue());
24338 doForce : function(){
24339 if(this.el.dom.value.length > 0){
24340 this.el.dom.value =
24341 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
24347 * Execute a query to filter the dropdown list. Fires the beforequery event prior to performing the
24348 * query allowing the query action to be canceled if needed.
24349 * @param {String} query The SQL query to execute
24350 * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
24351 * in the field than the minimum specified by the minChars config option. It also clears any filter previously
24352 * saved in the current store (defaults to false)
24354 doQuery : function(q, forceAll){
24355 if(q === undefined || q === null){
24360 forceAll: forceAll,
24364 if(this.fireEvent('beforequery', qe)===false || qe.cancel){
24368 forceAll = qe.forceAll;
24369 if(forceAll === true || (q.length >= this.minChars)){
24370 if(this.lastQuery != q || this.alwaysQuery){
24371 this.lastQuery = q;
24372 if(this.mode == 'local'){
24373 this.selectedIndex = -1;
24375 this.store.clearFilter();
24377 this.store.filter(this.displayField, q);
24381 this.store.baseParams[this.queryParam] = q;
24383 params: this.getParams(q)
24388 this.selectedIndex = -1;
24395 getParams : function(q){
24397 //p[this.queryParam] = q;
24400 p.limit = this.pageSize;
24406 * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
24408 collapse : function(){
24409 if(!this.isExpanded()){
24413 Roo.get(document).un('mousedown', this.collapseIf, this);
24414 Roo.get(document).un('mousewheel', this.collapseIf, this);
24415 if (!this.editable) {
24416 Roo.get(document).un('keydown', this.listKeyPress, this);
24418 this.fireEvent('collapse', this);
24422 collapseIf : function(e){
24423 if(!e.within(this.wrap) && !e.within(this.list)){
24429 * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
24431 expand : function(){
24432 if(this.isExpanded() || !this.hasFocus){
24435 this.list.alignTo(this.el, this.listAlign);
24437 Roo.get(document).on('mousedown', this.collapseIf, this);
24438 Roo.get(document).on('mousewheel', this.collapseIf, this);
24439 if (!this.editable) {
24440 Roo.get(document).on('keydown', this.listKeyPress, this);
24443 this.fireEvent('expand', this);
24447 // Implements the default empty TriggerField.onTriggerClick function
24448 onTriggerClick : function(){
24452 if(this.isExpanded()){
24454 if (!this.blockFocus) {
24459 this.hasFocus = true;
24460 if(this.triggerAction == 'all') {
24461 this.doQuery(this.allQuery, true);
24463 this.doQuery(this.getRawValue());
24465 if (!this.blockFocus) {
24470 listKeyPress : function(e)
24472 //Roo.log('listkeypress');
24473 // scroll to first matching element based on key pres..
24474 if (e.isSpecialKey()) {
24477 var k = String.fromCharCode(e.getKey()).toUpperCase();
24480 var csel = this.view.getSelectedNodes();
24481 var cselitem = false;
24483 var ix = this.view.indexOf(csel[0]);
24484 cselitem = this.store.getAt(ix);
24485 if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
24491 this.store.each(function(v) {
24493 // start at existing selection.
24494 if (cselitem.id == v.id) {
24500 if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
24501 match = this.store.indexOf(v);
24506 if (match === false) {
24507 return true; // no more action?
24510 this.view.select(match);
24511 var sn = Roo.get(this.view.getSelectedNodes()[0])
24512 sn.scrollIntoView(sn.dom.parentNode, false);
24516 * @cfg {Boolean} grow
24520 * @cfg {Number} growMin
24524 * @cfg {Number} growMax
24532 * Copyright(c) 2010-2012, Roo J Solutions Limited
24539 * @class Roo.form.ComboBoxArray
24540 * @extends Roo.form.TextField
24541 * A facebook style adder... for lists of email / people / countries etc...
24542 * pick multiple items from a combo box, and shows each one.
24544 * Fred [x] Brian [x] [Pick another |v]
24547 * For this to work: it needs various extra information
24548 * - normal combo problay has
24550 * + displayField, valueField
24552 * For our purpose...
24555 * If we change from 'extends' to wrapping...
24562 * Create a new ComboBoxArray.
24563 * @param {Object} config Configuration options
24567 Roo.form.ComboBoxArray = function(config)
24570 Roo.form.ComboBoxArray.superclass.constructor.call(this, config);
24572 this.items = new Roo.util.MixedCollection(false);
24574 // construct the child combo...
24584 Roo.extend(Roo.form.ComboBoxArray, Roo.form.TextField,
24587 * @cfg {Roo.form.Combo} combo The combo box that is wrapped
24592 // behavies liek a hiddne field
24593 inputType: 'hidden',
24595 * @cfg {Number} width The width of the box that displays the selected element
24602 * @cfg {String} name The name of the visable items on this form (eg. titles not ids)
24606 * @cfg {String} hiddenName The hidden name of the field, often contains an comma seperated list of names
24608 hiddenName : false,
24611 // private the array of items that are displayed..
24613 // private - the hidden field el.
24615 // private - the filed el..
24618 //validateValue : function() { return true; }, // all values are ok!
24619 //onAddClick: function() { },
24621 onRender : function(ct, position)
24624 // create the standard hidden element
24625 //Roo.form.ComboBoxArray.superclass.onRender.call(this, ct, position);
24628 // give fake names to child combo;
24629 this.combo.hiddenName = this.hiddenName ? (this.hiddenName+'-subcombo') : this.hiddenName;
24630 this.combo.name = this.name? (this.name+'-subcombo') : this.name;
24632 this.combo = Roo.factory(this.combo, Roo.form);
24633 this.combo.onRender(ct, position);
24634 if (typeof(this.combo.width) != 'undefined') {
24635 this.combo.onResize(this.combo.width,0);
24638 this.combo.initEvents();
24640 // assigned so form know we need to do this..
24641 this.store = this.combo.store;
24642 this.valueField = this.combo.valueField;
24643 this.displayField = this.combo.displayField ;
24646 this.combo.wrap.addClass('x-cbarray-grp');
24648 var cbwrap = this.combo.wrap.createChild(
24649 {tag: 'div', cls: 'x-cbarray-cb'},
24654 this.hiddenEl = this.combo.wrap.createChild({
24655 tag: 'input', type:'hidden' , name: this.hiddenName, value : ''
24657 this.el = this.combo.wrap.createChild({
24658 tag: 'input', type:'hidden' , name: this.name, value : ''
24660 // this.el.dom.removeAttribute("name");
24663 this.outerWrap = this.combo.wrap;
24664 this.wrap = cbwrap;
24666 this.outerWrap.setWidth(this.width);
24667 this.outerWrap.dom.removeChild(this.el.dom);
24669 this.wrap.dom.appendChild(this.el.dom);
24670 this.outerWrap.dom.removeChild(this.combo.trigger.dom);
24671 this.combo.wrap.dom.appendChild(this.combo.trigger.dom);
24673 this.combo.trigger.setStyle('position','relative');
24674 this.combo.trigger.setStyle('left', '0px');
24675 this.combo.trigger.setStyle('top', '2px');
24677 this.combo.el.setStyle('vertical-align', 'text-bottom');
24679 //this.trigger.setStyle('vertical-align', 'top');
24681 // this should use the code from combo really... on('add' ....)
24685 this.adder = this.outerWrap.createChild(
24686 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-adder', style: 'margin-left:2px'});
24688 this.adder.on('click', function(e) {
24689 _t.fireEvent('adderclick', this, e);
24693 //this.adder.on('click', this.onAddClick, _t);
24696 this.combo.on('select', function(cb, rec, ix) {
24697 this.addItem(rec.data);
24700 cb.el.dom.value = '';
24701 //cb.lastData = rec.data;
24710 getName: function()
24712 // returns hidden if it's set..
24713 if (!this.rendered) {return ''};
24714 return this.hiddenName ? this.hiddenName : this.name;
24719 onResize: function(w, h){
24722 // not sure if this is needed..
24723 //this.combo.onResize(w,h);
24725 if(typeof w != 'number'){
24726 // we do not handle it!?!?
24729 var tw = this.combo.trigger.getWidth();
24730 tw += this.addicon ? this.addicon.getWidth() : 0;
24731 tw += this.editicon ? this.editicon.getWidth() : 0;
24733 this.combo.el.setWidth( this.combo.adjustWidth('input', x));
24735 this.combo.trigger.setStyle('left', '0px');
24737 if(this.list && this.listWidth === undefined){
24738 var lw = Math.max(x + this.combo.trigger.getWidth(), this.combo.minListWidth);
24739 this.list.setWidth(lw);
24740 this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
24747 addItem: function(rec)
24749 var valueField = this.combo.valueField;
24750 var displayField = this.combo.displayField;
24751 if (this.items.indexOfKey(rec[valueField]) > -1) {
24752 //console.log("GOT " + rec.data.id);
24756 var x = new Roo.form.ComboBoxArray.Item({
24757 //id : rec[this.idField],
24759 displayField : displayField ,
24760 tipField : displayField ,
24764 this.items.add(rec[valueField],x);
24765 // add it before the element..
24766 this.updateHiddenEl();
24767 x.render(this.outerWrap, this.wrap.dom);
24768 // add the image handler..
24771 updateHiddenEl : function()
24774 if (!this.hiddenEl) {
24778 var idField = this.combo.valueField;
24780 this.items.each(function(f) {
24781 ar.push(f.data[idField]);
24784 this.hiddenEl.dom.value = ar.join(',');
24790 //Roo.form.ComboBoxArray.superclass.reset.call(this);
24791 this.items.each(function(f) {
24794 this.el.dom.value = '';
24795 if (this.hiddenEl) {
24796 this.hiddenEl.dom.value = '';
24800 getValue: function()
24802 return this.hiddenEl ? this.hiddenEl.dom.value : '';
24804 setValue: function(v) // not a valid action - must use addItems..
24811 if (this.store.isLocal && (typeof(v) == 'string')) {
24812 // then we can use the store to find the values..
24813 // comma seperated at present.. this needs to allow JSON based encoding..
24814 this.hiddenEl.value = v;
24816 Roo.each(v.split(','), function(k) {
24817 Roo.log("CHECK " + this.valueField + ',' + k);
24818 var li = this.store.query(this.valueField, k);
24823 add[this.valueField] = k;
24824 add[this.displayField] = li.item(0).data[this.displayField];
24830 if (typeof(v) == 'object') {
24831 // then let's assume it's an array of objects..
24832 Roo.each(v, function(l) {
24840 setFromData: function(v)
24842 // this recieves an object, if setValues is called.
24844 this.el.dom.value = v[this.displayField];
24845 this.hiddenEl.dom.value = v[this.valueField];
24846 if (typeof(v[this.valueField]) != 'string' || !v[this.valueField].length) {
24849 var kv = v[this.valueField];
24850 var dv = v[this.displayField];
24851 kv = typeof(kv) != 'string' ? '' : kv;
24852 dv = typeof(dv) != 'string' ? '' : dv;
24855 var keys = kv.split(',');
24856 var display = dv.split(',');
24857 for (var i = 0 ; i < keys.length; i++) {
24860 add[this.valueField] = keys[i];
24861 add[this.displayField] = display[i];
24869 validateValue : function(value){
24870 return Roo.form.ComboBoxArray.superclass.validateValue.call(this, this.getValue());
24879 * @class Roo.form.ComboBoxArray.Item
24880 * @extends Roo.BoxComponent
24881 * A selected item in the list
24882 * Fred [x] Brian [x] [Pick another |v]
24885 * Create a new item.
24886 * @param {Object} config Configuration options
24889 Roo.form.ComboBoxArray.Item = function(config) {
24890 config.id = Roo.id();
24891 Roo.form.ComboBoxArray.Item.superclass.constructor.call(this, config);
24894 Roo.extend(Roo.form.ComboBoxArray.Item, Roo.BoxComponent, {
24897 displayField : false,
24901 defaultAutoCreate : {
24903 cls: 'x-cbarray-item',
24910 src : Roo.BLANK_IMAGE_URL ,
24918 onRender : function(ct, position)
24920 Roo.form.Field.superclass.onRender.call(this, ct, position);
24923 var cfg = this.getAutoCreate();
24924 this.el = ct.createChild(cfg, position);
24927 this.el.child('img').dom.setAttribute('src', Roo.BLANK_IMAGE_URL);
24929 this.el.child('div').dom.innerHTML = this.cb.renderer ?
24930 this.cb.renderer(this.data) :
24931 String.format('{0}',this.data[this.displayField]);
24934 this.el.child('div').dom.setAttribute('qtip',
24935 String.format('{0}',this.data[this.tipField])
24938 this.el.child('img').on('click', this.remove, this);
24942 remove : function()
24945 this.cb.items.remove(this);
24946 this.el.child('img').un('click', this.remove, this);
24948 this.cb.updateHiddenEl();
24954 * Ext JS Library 1.1.1
24955 * Copyright(c) 2006-2007, Ext JS, LLC.
24957 * Originally Released Under LGPL - original licence link has changed is not relivant.
24960 * <script type="text/javascript">
24963 * @class Roo.form.Checkbox
24964 * @extends Roo.form.Field
24965 * Single checkbox field. Can be used as a direct replacement for traditional checkbox fields.
24967 * Creates a new Checkbox
24968 * @param {Object} config Configuration options
24970 Roo.form.Checkbox = function(config){
24971 Roo.form.Checkbox.superclass.constructor.call(this, config);
24975 * Fires when the checkbox is checked or unchecked.
24976 * @param {Roo.form.Checkbox} this This checkbox
24977 * @param {Boolean} checked The new checked value
24983 Roo.extend(Roo.form.Checkbox, Roo.form.Field, {
24985 * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
24987 focusClass : undefined,
24989 * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
24991 fieldClass: "x-form-field",
24993 * @cfg {Boolean} checked True if the the checkbox should render already checked (defaults to false)
24997 * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
24998 * {tag: "input", type: "checkbox", autocomplete: "off"})
25000 defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "off"},
25002 * @cfg {String} boxLabel The text that appears beside the checkbox
25006 * @cfg {String} inputValue The value that should go into the generated input element's value attribute
25010 * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
25012 valueOff: '0', // value when not checked..
25014 actionMode : 'viewEl',
25017 itemCls : 'x-menu-check-item x-form-item',
25018 groupClass : 'x-menu-group-item',
25019 inputType : 'hidden',
25022 inSetChecked: false, // check that we are not calling self...
25024 inputElement: false, // real input element?
25025 basedOn: false, // ????
25027 isFormField: true, // not sure where this is needed!!!!
25029 onResize : function(){
25030 Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
25031 if(!this.boxLabel){
25032 this.el.alignTo(this.wrap, 'c-c');
25036 initEvents : function(){
25037 Roo.form.Checkbox.superclass.initEvents.call(this);
25038 this.el.on("click", this.onClick, this);
25039 this.el.on("change", this.onClick, this);
25043 getResizeEl : function(){
25047 getPositionEl : function(){
25052 onRender : function(ct, position){
25053 Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
25055 if(this.inputValue !== undefined){
25056 this.el.dom.value = this.inputValue;
25059 //this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
25060 this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
25061 var viewEl = this.wrap.createChild({
25062 tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
25063 this.viewEl = viewEl;
25064 this.wrap.on('click', this.onClick, this);
25066 this.el.on('DOMAttrModified', this.setFromHidden, this); //ff
25067 this.el.on('propertychange', this.setFromHidden, this); //ie
25072 this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
25073 // viewEl.on('click', this.onClick, this);
25075 //if(this.checked){
25076 this.setChecked(this.checked);
25078 //this.checked = this.el.dom;
25084 initValue : Roo.emptyFn,
25087 * Returns the checked state of the checkbox.
25088 * @return {Boolean} True if checked, else false
25090 getValue : function(){
25092 return String(this.el.dom.value) == String(this.inputValue ) ? this.inputValue : this.valueOff;
25094 return this.valueOff;
25099 onClick : function(){
25100 this.setChecked(!this.checked);
25102 //if(this.el.dom.checked != this.checked){
25103 // this.setValue(this.el.dom.checked);
25108 * Sets the checked state of the checkbox.
25109 * On is always based on a string comparison between inputValue and the param.
25110 * @param {Boolean/String} value - the value to set
25111 * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
25113 setValue : function(v,suppressEvent){
25116 //this.checked = (v === true || v === 'true' || v == '1' || String(v).toLowerCase() == 'on');
25117 //if(this.el && this.el.dom){
25118 // this.el.dom.checked = this.checked;
25119 // this.el.dom.defaultChecked = this.checked;
25121 this.setChecked(String(v) === String(this.inputValue), suppressEvent);
25122 //this.fireEvent("check", this, this.checked);
25125 setChecked : function(state,suppressEvent)
25127 if (this.inSetChecked) {
25128 this.checked = state;
25134 this.wrap[state ? 'addClass' : 'removeClass']('x-menu-item-checked');
25136 this.checked = state;
25137 if(suppressEvent !== true){
25138 this.fireEvent('check', this, state);
25140 this.inSetChecked = true;
25141 this.el.dom.value = state ? this.inputValue : this.valueOff;
25142 this.inSetChecked = false;
25145 // handle setting of hidden value by some other method!!?!?
25146 setFromHidden: function()
25151 //console.log("SET FROM HIDDEN");
25152 //alert('setFrom hidden');
25153 this.setValue(this.el.dom.value);
25156 onDestroy : function()
25159 Roo.get(this.viewEl).remove();
25162 Roo.form.Checkbox.superclass.onDestroy.call(this);
25167 * Ext JS Library 1.1.1
25168 * Copyright(c) 2006-2007, Ext JS, LLC.
25170 * Originally Released Under LGPL - original licence link has changed is not relivant.
25173 * <script type="text/javascript">
25177 * @class Roo.form.Radio
25178 * @extends Roo.form.Checkbox
25179 * Single radio field. Same as Checkbox, but provided as a convenience for automatically setting the input type.
25180 * Radio grouping is handled automatically by the browser if you give each radio in a group the same name.
25182 * Creates a new Radio
25183 * @param {Object} config Configuration options
25185 Roo.form.Radio = function(){
25186 Roo.form.Radio.superclass.constructor.apply(this, arguments);
25188 Roo.extend(Roo.form.Radio, Roo.form.Checkbox, {
25189 inputType: 'radio',
25192 * If this radio is part of a group, it will return the selected value
25195 getGroupValue : function(){
25196 return this.el.up('form').child('input[name='+this.el.dom.name+']:checked', true).value;
25200 onRender : function(ct, position){
25201 Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
25203 if(this.inputValue !== undefined){
25204 this.el.dom.value = this.inputValue;
25207 this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
25208 //this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
25209 //var viewEl = this.wrap.createChild({
25210 // tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
25211 //this.viewEl = viewEl;
25212 //this.wrap.on('click', this.onClick, this);
25214 //this.el.on('DOMAttrModified', this.setFromHidden, this); //ff
25215 //this.el.on('propertychange', this.setFromHidden, this); //ie
25220 this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
25221 // viewEl.on('click', this.onClick, this);
25224 this.el.dom.checked = 'checked' ;
25230 });//<script type="text/javascript">
25233 * Ext JS Library 1.1.1
25234 * Copyright(c) 2006-2007, Ext JS, LLC.
25235 * licensing@extjs.com
25237 * http://www.extjs.com/license
25243 * Default CSS appears to render it as fixed text by default (should really be Sans-Serif)
25244 * - IE ? - no idea how much works there.
25252 * @class Ext.form.HtmlEditor
25253 * @extends Ext.form.Field
25254 * Provides a lightweight HTML Editor component.
25256 * This has been tested on Fireforx / Chrome.. IE may not be so great..
25258 * <br><br><b>Note: The focus/blur and validation marking functionality inherited from Ext.form.Field is NOT
25259 * supported by this editor.</b><br/><br/>
25260 * An Editor is a sensitive component that can't be used in all spots standard fields can be used. Putting an Editor within
25261 * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
25263 Roo.form.HtmlEditor = Roo.extend(Roo.form.Field, {
25265 * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
25269 * @cfg {String} createLinkText The default text for the create link prompt
25271 createLinkText : 'Please enter the URL for the link:',
25273 * @cfg {String} defaultLinkValue The default value for the create link prompt (defaults to http:/ /)
25275 defaultLinkValue : 'http:/'+'/',
25278 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
25283 * @cfg {Number} height (in pixels)
25287 * @cfg {Number} width (in pixels)
25292 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
25295 stylesheets: false,
25300 // private properties
25301 validationEvent : false,
25303 initialized : false,
25305 sourceEditMode : false,
25306 onFocus : Roo.emptyFn,
25308 hideMode:'offsets',
25310 defaultAutoCreate : { // modified by initCompnoent..
25312 style:"width:500px;height:300px;",
25313 autocomplete: "off"
25317 initComponent : function(){
25320 * @event initialize
25321 * Fires when the editor is fully initialized (including the iframe)
25322 * @param {HtmlEditor} this
25327 * Fires when the editor is first receives the focus. Any insertion must wait
25328 * until after this event.
25329 * @param {HtmlEditor} this
25333 * @event beforesync
25334 * Fires before the textarea is updated with content from the editor iframe. Return false
25335 * to cancel the sync.
25336 * @param {HtmlEditor} this
25337 * @param {String} html
25341 * @event beforepush
25342 * Fires before the iframe editor is updated with content from the textarea. Return false
25343 * to cancel the push.
25344 * @param {HtmlEditor} this
25345 * @param {String} html
25350 * Fires when the textarea is updated with content from the editor iframe.
25351 * @param {HtmlEditor} this
25352 * @param {String} html
25357 * Fires when the iframe editor is updated with content from the textarea.
25358 * @param {HtmlEditor} this
25359 * @param {String} html
25363 * @event editmodechange
25364 * Fires when the editor switches edit modes
25365 * @param {HtmlEditor} this
25366 * @param {Boolean} sourceEdit True if source edit, false if standard editing.
25368 editmodechange: true,
25370 * @event editorevent
25371 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
25372 * @param {HtmlEditor} this
25376 this.defaultAutoCreate = {
25378 style:'width: ' + this.width + 'px;height: ' + this.height + 'px;',
25379 autocomplete: "off"
25384 * Protected method that will not generally be called directly. It
25385 * is called when the editor creates its toolbar. Override this method if you need to
25386 * add custom toolbar buttons.
25387 * @param {HtmlEditor} editor
25389 createToolbar : function(editor){
25390 if (!editor.toolbars || !editor.toolbars.length) {
25391 editor.toolbars = [ new Roo.form.HtmlEditor.ToolbarStandard() ]; // can be empty?
25394 for (var i =0 ; i < editor.toolbars.length;i++) {
25395 editor.toolbars[i] = Roo.factory(
25396 typeof(editor.toolbars[i]) == 'string' ?
25397 { xtype: editor.toolbars[i]} : editor.toolbars[i],
25398 Roo.form.HtmlEditor);
25399 editor.toolbars[i].init(editor);
25406 * Protected method that will not generally be called directly. It
25407 * is called when the editor initializes the iframe with HTML contents. Override this method if you
25408 * want to change the initialization markup of the iframe (e.g. to add stylesheets).
25410 getDocMarkup : function(){
25413 if (this.stylesheets === false) {
25415 Roo.get(document.head).select('style').each(function(node) {
25416 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
25419 Roo.get(document.head).select('link').each(function(node) {
25420 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
25423 } else if (!this.stylesheets.length) {
25425 st = '<style type="text/css">' +
25426 'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
25429 Roo.each(this.stylesheets, function(s) {
25430 st += '<link rel="stylesheet" type="text/css" href="' + s +'" />'
25435 st += '<style type="text/css">' +
25436 'IMG { cursor: pointer } ' +
25440 return '<html><head>' + st +
25441 //<style type="text/css">' +
25442 //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
25444 ' </head><body class="roo-htmleditor-body"></body></html>';
25448 onRender : function(ct, position)
25451 Roo.form.HtmlEditor.superclass.onRender.call(this, ct, position);
25452 this.el.dom.style.border = '0 none';
25453 this.el.dom.setAttribute('tabIndex', -1);
25454 this.el.addClass('x-hidden');
25455 if(Roo.isIE){ // fix IE 1px bogus margin
25456 this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
25458 this.wrap = this.el.wrap({
25459 cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
25462 if (this.resizable) {
25463 this.resizeEl = new Roo.Resizable(this.wrap, {
25467 minHeight : this.height,
25468 height: this.height,
25469 handles : this.resizable,
25472 resize : function(r, w, h) {
25473 _t.onResize(w,h); // -something
25480 this.frameId = Roo.id();
25482 this.createToolbar(this);
25486 var iframe = this.wrap.createChild({
25489 name: this.frameId,
25490 frameBorder : 'no',
25491 'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL : "javascript:false"
25495 // console.log(iframe);
25496 //this.wrap.dom.appendChild(iframe);
25498 this.iframe = iframe.dom;
25500 this.assignDocWin();
25502 this.doc.designMode = 'on';
25505 this.doc.write(this.getDocMarkup());
25509 var task = { // must defer to wait for browser to be ready
25511 //console.log("run task?" + this.doc.readyState);
25512 this.assignDocWin();
25513 if(this.doc.body || this.doc.readyState == 'complete'){
25515 this.doc.designMode="on";
25519 Roo.TaskMgr.stop(task);
25520 this.initEditor.defer(10, this);
25527 Roo.TaskMgr.start(task);
25530 this.setSize(this.wrap.getSize());
25532 if (this.resizeEl) {
25533 this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
25534 // should trigger onReize..
25539 onResize : function(w, h)
25541 //Roo.log('resize: ' +w + ',' + h );
25542 Roo.form.HtmlEditor.superclass.onResize.apply(this, arguments);
25543 if(this.el && this.iframe){
25544 if(typeof w == 'number'){
25545 var aw = w - this.wrap.getFrameWidth('lr');
25546 this.el.setWidth(this.adjustWidth('textarea', aw));
25547 this.iframe.style.width = aw + 'px';
25549 if(typeof h == 'number'){
25551 for (var i =0; i < this.toolbars.length;i++) {
25552 // fixme - ask toolbars for heights?
25553 tbh += this.toolbars[i].tb.el.getHeight();
25554 if (this.toolbars[i].footer) {
25555 tbh += this.toolbars[i].footer.el.getHeight();
25562 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
25563 ah -= 5; // knock a few pixes off for look..
25564 this.el.setHeight(this.adjustWidth('textarea', ah));
25565 this.iframe.style.height = ah + 'px';
25567 (this.doc.body || this.doc.documentElement).style.height = (ah - (this.iframePad*2)) + 'px';
25574 * Toggles the editor between standard and source edit mode.
25575 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
25577 toggleSourceEdit : function(sourceEditMode){
25579 this.sourceEditMode = sourceEditMode === true;
25581 if(this.sourceEditMode){
25583 // Roo.log(this.syncValue());
25585 this.iframe.className = 'x-hidden';
25586 this.el.removeClass('x-hidden');
25587 this.el.dom.removeAttribute('tabIndex');
25591 // Roo.log(this.pushValue());
25593 this.iframe.className = '';
25594 this.el.addClass('x-hidden');
25595 this.el.dom.setAttribute('tabIndex', -1);
25598 this.setSize(this.wrap.getSize());
25599 this.fireEvent('editmodechange', this, this.sourceEditMode);
25602 // private used internally
25603 createLink : function(){
25604 var url = prompt(this.createLinkText, this.defaultLinkValue);
25605 if(url && url != 'http:/'+'/'){
25606 this.relayCmd('createlink', url);
25610 // private (for BoxComponent)
25611 adjustSize : Roo.BoxComponent.prototype.adjustSize,
25613 // private (for BoxComponent)
25614 getResizeEl : function(){
25618 // private (for BoxComponent)
25619 getPositionEl : function(){
25624 initEvents : function(){
25625 this.originalValue = this.getValue();
25629 * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
25632 markInvalid : Roo.emptyFn,
25634 * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
25637 clearInvalid : Roo.emptyFn,
25639 setValue : function(v){
25640 Roo.form.HtmlEditor.superclass.setValue.call(this, v);
25645 * Protected method that will not generally be called directly. If you need/want
25646 * custom HTML cleanup, this is the method you should override.
25647 * @param {String} html The HTML to be cleaned
25648 * return {String} The cleaned HTML
25650 cleanHtml : function(html){
25651 html = String(html);
25652 if(html.length > 5){
25653 if(Roo.isSafari){ // strip safari nonsense
25654 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
25657 if(html == ' '){
25664 * Protected method that will not generally be called directly. Syncs the contents
25665 * of the editor iframe with the textarea.
25667 syncValue : function(){
25668 if(this.initialized){
25669 var bd = (this.doc.body || this.doc.documentElement);
25670 //this.cleanUpPaste(); -- this is done else where and causes havoc..
25671 var html = bd.innerHTML;
25673 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
25674 var m = bs.match(/text-align:(.*?);/i);
25676 html = '<div style="'+m[0]+'">' + html + '</div>';
25679 html = this.cleanHtml(html);
25680 // fix up the special chars.. normaly like back quotes in word...
25681 // however we do not want to do this with chinese..
25682 html = html.replace(/([\x80-\uffff])/g, function (a, b) {
25683 var cc = b.charCodeAt();
25685 (cc >= 0x4E00 && cc < 0xA000 ) ||
25686 (cc >= 0x3400 && cc < 0x4E00 ) ||
25687 (cc >= 0xf900 && cc < 0xfb00 )
25693 if(this.fireEvent('beforesync', this, html) !== false){
25694 this.el.dom.value = html;
25695 this.fireEvent('sync', this, html);
25701 * Protected method that will not generally be called directly. Pushes the value of the textarea
25702 * into the iframe editor.
25704 pushValue : function(){
25705 if(this.initialized){
25706 var v = this.el.dom.value;
25712 if(this.fireEvent('beforepush', this, v) !== false){
25713 var d = (this.doc.body || this.doc.documentElement);
25715 this.cleanUpPaste();
25716 this.el.dom.value = d.innerHTML;
25717 this.fireEvent('push', this, v);
25723 deferFocus : function(){
25724 this.focus.defer(10, this);
25728 focus : function(){
25729 if(this.win && !this.sourceEditMode){
25736 assignDocWin: function()
25738 var iframe = this.iframe;
25741 this.doc = iframe.contentWindow.document;
25742 this.win = iframe.contentWindow;
25744 if (!Roo.get(this.frameId)) {
25747 this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
25748 this.win = Roo.get(this.frameId).dom.contentWindow;
25753 initEditor : function(){
25754 //console.log("INIT EDITOR");
25755 this.assignDocWin();
25759 this.doc.designMode="on";
25761 this.doc.write(this.getDocMarkup());
25764 var dbody = (this.doc.body || this.doc.documentElement);
25765 //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
25766 // this copies styles from the containing element into thsi one..
25767 // not sure why we need all of this..
25768 var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
25769 ss['background-attachment'] = 'fixed'; // w3c
25770 dbody.bgProperties = 'fixed'; // ie
25771 Roo.DomHelper.applyStyles(dbody, ss);
25772 Roo.EventManager.on(this.doc, {
25773 //'mousedown': this.onEditorEvent,
25774 'mouseup': this.onEditorEvent,
25775 'dblclick': this.onEditorEvent,
25776 'click': this.onEditorEvent,
25777 'keyup': this.onEditorEvent,
25782 Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
25784 if(Roo.isIE || Roo.isSafari || Roo.isOpera){
25785 Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
25787 this.initialized = true;
25789 this.fireEvent('initialize', this);
25794 onDestroy : function(){
25800 for (var i =0; i < this.toolbars.length;i++) {
25801 // fixme - ask toolbars for heights?
25802 this.toolbars[i].onDestroy();
25805 this.wrap.dom.innerHTML = '';
25806 this.wrap.remove();
25811 onFirstFocus : function(){
25813 this.assignDocWin();
25816 this.activated = true;
25817 for (var i =0; i < this.toolbars.length;i++) {
25818 this.toolbars[i].onFirstFocus();
25821 if(Roo.isGecko){ // prevent silly gecko errors
25823 var s = this.win.getSelection();
25824 if(!s.focusNode || s.focusNode.nodeType != 3){
25825 var r = s.getRangeAt(0);
25826 r.selectNodeContents((this.doc.body || this.doc.documentElement));
25831 this.execCmd('useCSS', true);
25832 this.execCmd('styleWithCSS', false);
25835 this.fireEvent('activate', this);
25839 adjustFont: function(btn){
25840 var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
25841 //if(Roo.isSafari){ // safari
25844 var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
25845 if(Roo.isSafari){ // safari
25846 var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
25847 v = (v < 10) ? 10 : v;
25848 v = (v > 48) ? 48 : v;
25849 v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
25854 v = Math.max(1, v+adjust);
25856 this.execCmd('FontSize', v );
25859 onEditorEvent : function(e){
25860 this.fireEvent('editorevent', this, e);
25861 // this.updateToolbar();
25862 this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
25865 insertTag : function(tg)
25867 // could be a bit smarter... -> wrap the current selected tRoo..
25868 if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
25870 range = this.createRange(this.getSelection());
25871 var wrappingNode = this.doc.createElement(tg.toLowerCase());
25872 wrappingNode.appendChild(range.extractContents());
25873 range.insertNode(wrappingNode);
25880 this.execCmd("formatblock", tg);
25884 insertText : function(txt)
25888 var range = this.createRange();
25889 range.deleteContents();
25890 //alert(Sender.getAttribute('label'));
25892 range.insertNode(this.doc.createTextNode(txt));
25896 relayBtnCmd : function(btn){
25897 this.relayCmd(btn.cmd);
25901 * Executes a Midas editor command on the editor document and performs necessary focus and
25902 * toolbar updates. <b>This should only be called after the editor is initialized.</b>
25903 * @param {String} cmd The Midas command
25904 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
25906 relayCmd : function(cmd, value){
25908 this.execCmd(cmd, value);
25909 this.fireEvent('editorevent', this);
25910 //this.updateToolbar();
25915 * Executes a Midas editor command directly on the editor document.
25916 * For visual commands, you should use {@link #relayCmd} instead.
25917 * <b>This should only be called after the editor is initialized.</b>
25918 * @param {String} cmd The Midas command
25919 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
25921 execCmd : function(cmd, value){
25922 this.doc.execCommand(cmd, false, value === undefined ? null : value);
25929 * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
25931 * @param {String} text | dom node..
25933 insertAtCursor : function(text)
25938 if(!this.activated){
25944 var r = this.doc.selection.createRange();
25955 if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
25959 // from jquery ui (MIT licenced)
25961 var win = this.win;
25963 if (win.getSelection && win.getSelection().getRangeAt) {
25964 range = win.getSelection().getRangeAt(0);
25965 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
25966 range.insertNode(node);
25967 } else if (win.document.selection && win.document.selection.createRange) {
25968 // no firefox support
25969 var txt = typeof(text) == 'string' ? text : text.outerHTML;
25970 win.document.selection.createRange().pasteHTML(txt);
25972 // no firefox support
25973 var txt = typeof(text) == 'string' ? text : text.outerHTML;
25974 this.execCmd('InsertHTML', txt);
25983 mozKeyPress : function(e){
25985 var c = e.getCharCode(), cmd;
25988 c = String.fromCharCode(c).toLowerCase();
26002 this.cleanUpPaste.defer(100, this);
26010 e.preventDefault();
26018 fixKeys : function(){ // load time branching for fastest keydown performance
26020 return function(e){
26021 var k = e.getKey(), r;
26024 r = this.doc.selection.createRange();
26027 r.pasteHTML('    ');
26034 r = this.doc.selection.createRange();
26036 var target = r.parentElement();
26037 if(!target || target.tagName.toLowerCase() != 'li'){
26039 r.pasteHTML('<br />');
26045 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
26046 this.cleanUpPaste.defer(100, this);
26052 }else if(Roo.isOpera){
26053 return function(e){
26054 var k = e.getKey();
26058 this.execCmd('InsertHTML','    ');
26061 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
26062 this.cleanUpPaste.defer(100, this);
26067 }else if(Roo.isSafari){
26068 return function(e){
26069 var k = e.getKey();
26073 this.execCmd('InsertText','\t');
26077 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
26078 this.cleanUpPaste.defer(100, this);
26086 getAllAncestors: function()
26088 var p = this.getSelectedNode();
26091 a.push(p); // push blank onto stack..
26092 p = this.getParentElement();
26096 while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
26100 a.push(this.doc.body);
26104 lastSelNode : false,
26107 getSelection : function()
26109 this.assignDocWin();
26110 return Roo.isIE ? this.doc.selection : this.win.getSelection();
26113 getSelectedNode: function()
26115 // this may only work on Gecko!!!
26117 // should we cache this!!!!
26122 var range = this.createRange(this.getSelection()).cloneRange();
26125 var parent = range.parentElement();
26127 var testRange = range.duplicate();
26128 testRange.moveToElementText(parent);
26129 if (testRange.inRange(range)) {
26132 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
26135 parent = parent.parentElement;
26140 // is ancestor a text element.
26141 var ac = range.commonAncestorContainer;
26142 if (ac.nodeType == 3) {
26143 ac = ac.parentNode;
26146 var ar = ac.childNodes;
26149 var other_nodes = [];
26150 var has_other_nodes = false;
26151 for (var i=0;i<ar.length;i++) {
26152 if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ?
26155 // fullly contained node.
26157 if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
26162 // probably selected..
26163 if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
26164 other_nodes.push(ar[i]);
26168 if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0)) {
26173 has_other_nodes = true;
26175 if (!nodes.length && other_nodes.length) {
26176 nodes= other_nodes;
26178 if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
26184 createRange: function(sel)
26186 // this has strange effects when using with
26187 // top toolbar - not sure if it's a great idea.
26188 //this.editor.contentWindow.focus();
26189 if (typeof sel != "undefined") {
26191 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
26193 return this.doc.createRange();
26196 return this.doc.createRange();
26199 getParentElement: function()
26202 this.assignDocWin();
26203 var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
26205 var range = this.createRange(sel);
26208 var p = range.commonAncestorContainer;
26209 while (p.nodeType == 3) { // text node
26220 * Range intersection.. the hard stuff...
26224 * [ -- selected range --- ]
26228 * if end is before start or hits it. fail.
26229 * if start is after end or hits it fail.
26231 * if either hits (but other is outside. - then it's not
26237 // @see http://www.thismuchiknow.co.uk/?p=64.
26238 rangeIntersectsNode : function(range, node)
26240 var nodeRange = node.ownerDocument.createRange();
26242 nodeRange.selectNode(node);
26244 nodeRange.selectNodeContents(node);
26247 var rangeStartRange = range.cloneRange();
26248 rangeStartRange.collapse(true);
26250 var rangeEndRange = range.cloneRange();
26251 rangeEndRange.collapse(false);
26253 var nodeStartRange = nodeRange.cloneRange();
26254 nodeStartRange.collapse(true);
26256 var nodeEndRange = nodeRange.cloneRange();
26257 nodeEndRange.collapse(false);
26259 return rangeStartRange.compareBoundaryPoints(
26260 Range.START_TO_START, nodeEndRange) == -1 &&
26261 rangeEndRange.compareBoundaryPoints(
26262 Range.START_TO_START, nodeStartRange) == 1;
26266 rangeCompareNode : function(range, node)
26268 var nodeRange = node.ownerDocument.createRange();
26270 nodeRange.selectNode(node);
26272 nodeRange.selectNodeContents(node);
26276 range.collapse(true);
26278 nodeRange.collapse(true);
26280 var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
26281 var ee = range.compareBoundaryPoints( Range.END_TO_END, nodeRange);
26283 //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
26285 var nodeIsBefore = ss == 1;
26286 var nodeIsAfter = ee == -1;
26288 if (nodeIsBefore && nodeIsAfter)
26290 if (!nodeIsBefore && nodeIsAfter)
26291 return 1; //right trailed.
26293 if (nodeIsBefore && !nodeIsAfter)
26294 return 2; // left trailed.
26299 // private? - in a new class?
26300 cleanUpPaste : function()
26302 // cleans up the whole document..
26303 Roo.log('cleanuppaste');
26304 this.cleanUpChildren(this.doc.body);
26305 var clean = this.cleanWordChars(this.doc.body.innerHTML);
26306 if (clean != this.doc.body.innerHTML) {
26307 this.doc.body.innerHTML = clean;
26312 cleanWordChars : function(input) {// change the chars to hex code
26313 var he = Roo.form.HtmlEditor;
26315 var output = input;
26316 Roo.each(he.swapCodes, function(sw) {
26317 var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
26319 output = output.replace(swapper, sw[1]);
26326 cleanUpChildren : function (n)
26328 if (!n.childNodes.length) {
26331 for (var i = n.childNodes.length-1; i > -1 ; i--) {
26332 this.cleanUpChild(n.childNodes[i]);
26339 cleanUpChild : function (node)
26342 //console.log(node);
26343 if (node.nodeName == "#text") {
26344 // clean up silly Windows -- stuff?
26347 if (node.nodeName == "#comment") {
26348 node.parentNode.removeChild(node);
26349 // clean up silly Windows -- stuff?
26353 if (Roo.form.HtmlEditor.black.indexOf(node.tagName.toLowerCase()) > -1) {
26355 node.parentNode.removeChild(node);
26360 var remove_keep_children= Roo.form.HtmlEditor.remove.indexOf(node.tagName.toLowerCase()) > -1;
26362 // remove <a name=....> as rendering on yahoo mailer is borked with this.
26363 // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
26365 //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
26366 // remove_keep_children = true;
26369 if (remove_keep_children) {
26370 this.cleanUpChildren(node);
26371 // inserts everything just before this node...
26372 while (node.childNodes.length) {
26373 var cn = node.childNodes[0];
26374 node.removeChild(cn);
26375 node.parentNode.insertBefore(cn, node);
26377 node.parentNode.removeChild(node);
26381 if (!node.attributes || !node.attributes.length) {
26382 this.cleanUpChildren(node);
26386 function cleanAttr(n,v)
26389 if (v.match(/^\./) || v.match(/^\//)) {
26392 if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
26395 if (v.match(/^#/)) {
26398 // Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
26399 node.removeAttribute(n);
26403 function cleanStyle(n,v)
26405 if (v.match(/expression/)) { //XSS?? should we even bother..
26406 node.removeAttribute(n);
26409 var cwhite = typeof(ed.cwhite) == 'undefined' ? Roo.form.HtmlEditor.cwhite : ed.cwhite;
26410 var cblack = typeof(ed.cblack) == 'undefined' ? Roo.form.HtmlEditor.cblack : ed.cblack;
26413 var parts = v.split(/;/);
26416 Roo.each(parts, function(p) {
26417 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
26421 var l = p.split(':').shift().replace(/\s+/g,'');
26422 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
26425 if ( cblack.indexOf(l) > -1) {
26426 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
26427 //node.removeAttribute(n);
26431 // only allow 'c whitelisted system attributes'
26432 if ( cwhite.length && cwhite.indexOf(l) < 0) {
26433 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
26434 //node.removeAttribute(n);
26444 if (clean.length) {
26445 node.setAttribute(n, clean.join(';'));
26447 node.removeAttribute(n);
26453 for (var i = node.attributes.length-1; i > -1 ; i--) {
26454 var a = node.attributes[i];
26457 if (a.name.toLowerCase().substr(0,2)=='on') {
26458 node.removeAttribute(a.name);
26461 if (Roo.form.HtmlEditor.ablack.indexOf(a.name.toLowerCase()) > -1) {
26462 node.removeAttribute(a.name);
26465 if (Roo.form.HtmlEditor.aclean.indexOf(a.name.toLowerCase()) > -1) {
26466 cleanAttr(a.name,a.value); // fixme..
26469 if (a.name == 'style') {
26470 cleanStyle(a.name,a.value);
26473 /// clean up MS crap..
26474 // tecnically this should be a list of valid class'es..
26477 if (a.name == 'class') {
26478 if (a.value.match(/^Mso/)) {
26479 node.className = '';
26482 if (a.value.match(/body/)) {
26483 node.className = '';
26494 this.cleanUpChildren(node);
26500 // hide stuff that is not compatible
26514 * @event specialkey
26518 * @cfg {String} fieldClass @hide
26521 * @cfg {String} focusClass @hide
26524 * @cfg {String} autoCreate @hide
26527 * @cfg {String} inputType @hide
26530 * @cfg {String} invalidClass @hide
26533 * @cfg {String} invalidText @hide
26536 * @cfg {String} msgFx @hide
26539 * @cfg {String} validateOnBlur @hide
26543 Roo.form.HtmlEditor.white = [
26544 'area', 'br', 'img', 'input', 'hr', 'wbr',
26546 'address', 'blockquote', 'center', 'dd', 'dir', 'div',
26547 'dl', 'dt', 'h1', 'h2', 'h3', 'h4',
26548 'h5', 'h6', 'hr', 'isindex', 'listing', 'marquee',
26549 'menu', 'multicol', 'ol', 'p', 'plaintext', 'pre',
26550 'table', 'ul', 'xmp',
26552 'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th',
26555 'dir', 'menu', 'ol', 'ul', 'dl',
26561 Roo.form.HtmlEditor.black = [
26562 // 'embed', 'object', // enable - backend responsiblity to clean thiese
26564 'base', 'basefont', 'bgsound', 'blink', 'body',
26565 'frame', 'frameset', 'head', 'html', 'ilayer',
26566 'iframe', 'layer', 'link', 'meta', 'object',
26567 'script', 'style' ,'title', 'xml' // clean later..
26569 Roo.form.HtmlEditor.clean = [
26570 'script', 'style', 'title', 'xml'
26572 Roo.form.HtmlEditor.remove = [
26577 Roo.form.HtmlEditor.ablack = [
26581 Roo.form.HtmlEditor.aclean = [
26582 'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc'
26586 Roo.form.HtmlEditor.pwhite= [
26587 'http', 'https', 'mailto'
26590 // white listed style attributes.
26591 Roo.form.HtmlEditor.cwhite= [
26592 // 'text-align', /// default is to allow most things..
26598 // black listed style attributes.
26599 Roo.form.HtmlEditor.cblack= [
26600 // 'font-size' -- this can be set by the project
26604 Roo.form.HtmlEditor.swapCodes =[
26615 // <script type="text/javascript">
26618 * Ext JS Library 1.1.1
26619 * Copyright(c) 2006-2007, Ext JS, LLC.
26625 * @class Roo.form.HtmlEditorToolbar1
26630 new Roo.form.HtmlEditor({
26633 new Roo.form.HtmlEditorToolbar1({
26634 disable : { fonts: 1 , format: 1, ..., ... , ...],
26640 * @cfg {Object} disable List of elements to disable..
26641 * @cfg {Array} btns List of additional buttons.
26645 * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
26648 Roo.form.HtmlEditor.ToolbarStandard = function(config)
26651 Roo.apply(this, config);
26653 // default disabled, based on 'good practice'..
26654 this.disable = this.disable || {};
26655 Roo.applyIf(this.disable, {
26658 specialElements : true
26662 //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
26663 // dont call parent... till later.
26666 Roo.apply(Roo.form.HtmlEditor.ToolbarStandard.prototype, {
26674 * @cfg {Object} disable List of toolbar elements to disable
26679 * @cfg {Array} fontFamilies An array of available font families
26697 // "á" , ?? a acute?
26702 "°" // , // degrees
26704 // "é" , // e ecute
26705 // "ú" , // u ecute?
26708 specialElements : [
26710 text: "Insert Table",
26713 ihtml : '<table><tr><td>Cell</td></tr></table>'
26717 text: "Insert Image",
26720 ihtml : '<img src="about:blank"/>'
26729 "form", "input:text", "input:hidden", "input:checkbox", "input:radio", "input:password",
26730 "input:submit", "input:button", "select", "textarea", "label" ],
26733 ["h1"],["h2"],["h3"],["h4"],["h5"],["h6"],
26735 ["abbr"],[ "acronym"],[ "address"],[ "cite"],[ "samp"],[ "var"],
26743 * @cfg {String} defaultFont default font to use.
26745 defaultFont: 'tahoma',
26747 fontSelect : false,
26750 formatCombo : false,
26752 init : function(editor)
26754 this.editor = editor;
26757 var fid = editor.frameId;
26759 function btn(id, toggle, handler){
26760 var xid = fid + '-'+ id ;
26764 cls : 'x-btn-icon x-edit-'+id,
26765 enableToggle:toggle !== false,
26766 scope: editor, // was editor...
26767 handler:handler||editor.relayBtnCmd,
26768 clickEvent:'mousedown',
26769 tooltip: etb.buttonTips[id] || undefined, ///tips ???
26776 var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
26778 // stop form submits
26779 tb.el.on('click', function(e){
26780 e.preventDefault(); // what does this do?
26783 if(!this.disable.font) { // && !Roo.isSafari){
26784 /* why no safari for fonts
26785 editor.fontSelect = tb.el.createChild({
26788 cls:'x-font-select',
26789 html: this.createFontOptions()
26792 editor.fontSelect.on('change', function(){
26793 var font = editor.fontSelect.dom.value;
26794 editor.relayCmd('fontname', font);
26795 editor.deferFocus();
26799 editor.fontSelect.dom,
26805 if(!this.disable.formats){
26806 this.formatCombo = new Roo.form.ComboBox({
26807 store: new Roo.data.SimpleStore({
26810 data : this.formats // from states.js
26814 //autoCreate : {tag: "div", size: "20"},
26815 displayField:'tag',
26819 triggerAction: 'all',
26820 emptyText:'Add tag',
26821 selectOnFocus:true,
26824 'select': function(c, r, i) {
26825 editor.insertTag(r.get('tag'));
26831 tb.addField(this.formatCombo);
26835 if(!this.disable.format){
26842 if(!this.disable.fontSize){
26847 btn('increasefontsize', false, editor.adjustFont),
26848 btn('decreasefontsize', false, editor.adjustFont)
26853 if(!this.disable.colors){
26856 id:editor.frameId +'-forecolor',
26857 cls:'x-btn-icon x-edit-forecolor',
26858 clickEvent:'mousedown',
26859 tooltip: this.buttonTips['forecolor'] || undefined,
26861 menu : new Roo.menu.ColorMenu({
26862 allowReselect: true,
26863 focus: Roo.emptyFn,
26866 selectHandler: function(cp, color){
26867 editor.execCmd('forecolor', Roo.isSafari || Roo.isIE ? '#'+color : color);
26868 editor.deferFocus();
26871 clickEvent:'mousedown'
26874 id:editor.frameId +'backcolor',
26875 cls:'x-btn-icon x-edit-backcolor',
26876 clickEvent:'mousedown',
26877 tooltip: this.buttonTips['backcolor'] || undefined,
26879 menu : new Roo.menu.ColorMenu({
26880 focus: Roo.emptyFn,
26883 allowReselect: true,
26884 selectHandler: function(cp, color){
26886 editor.execCmd('useCSS', false);
26887 editor.execCmd('hilitecolor', color);
26888 editor.execCmd('useCSS', true);
26889 editor.deferFocus();
26891 editor.execCmd(Roo.isOpera ? 'hilitecolor' : 'backcolor',
26892 Roo.isSafari || Roo.isIE ? '#'+color : color);
26893 editor.deferFocus();
26897 clickEvent:'mousedown'
26902 // now add all the items...
26905 if(!this.disable.alignments){
26908 btn('justifyleft'),
26909 btn('justifycenter'),
26910 btn('justifyright')
26914 //if(!Roo.isSafari){
26915 if(!this.disable.links){
26918 btn('createlink', false, editor.createLink) /// MOVE TO HERE?!!?!?!?!
26922 if(!this.disable.lists){
26925 btn('insertorderedlist'),
26926 btn('insertunorderedlist')
26929 if(!this.disable.sourceEdit){
26932 btn('sourceedit', true, function(btn){
26933 this.toggleSourceEdit(btn.pressed);
26940 // special menu.. - needs to be tidied up..
26941 if (!this.disable.special) {
26944 cls: 'x-edit-none',
26950 for (var i =0; i < this.specialChars.length; i++) {
26951 smenu.menu.items.push({
26953 html: this.specialChars[i],
26954 handler: function(a,b) {
26955 editor.insertAtCursor(String.fromCharCode(a.html.replace('&#','').replace(';', '')));
26956 //editor.insertAtCursor(a.html);
26970 if (!this.disable.cleanStyles) {
26972 cls: 'x-btn-icon x-btn-clear',
26978 for (var i =0; i < this.cleanStyles.length; i++) {
26979 cmenu.menu.items.push({
26980 actiontype : this.cleanStyles[i],
26981 html: 'Remove ' + this.cleanStyles[i],
26982 handler: function(a,b) {
26985 var c = Roo.get(editor.doc.body);
26986 c.select('[style]').each(function(s) {
26987 s.dom.style.removeProperty(a.actiontype);
26998 if (!this.disable.specialElements) {
27001 cls: 'x-edit-none',
27006 for (var i =0; i < this.specialElements.length; i++) {
27007 semenu.menu.items.push(
27009 handler: function(a,b) {
27010 editor.insertAtCursor(this.ihtml);
27012 }, this.specialElements[i])
27024 for(var i =0; i< this.btns.length;i++) {
27025 var b = Roo.factory(this.btns[i],Roo.form);
27026 b.cls = 'x-edit-none';
27035 // disable everything...
27037 this.tb.items.each(function(item){
27038 if(item.id != editor.frameId+ '-sourceedit'){
27042 this.rendered = true;
27044 // the all the btns;
27045 editor.on('editorevent', this.updateToolbar, this);
27046 // other toolbars need to implement this..
27047 //editor.on('editmodechange', this.updateToolbar, this);
27053 * Protected method that will not generally be called directly. It triggers
27054 * a toolbar update by reading the markup state of the current selection in the editor.
27056 updateToolbar: function(){
27058 if(!this.editor.activated){
27059 this.editor.onFirstFocus();
27063 var btns = this.tb.items.map,
27064 doc = this.editor.doc,
27065 frameId = this.editor.frameId;
27067 if(!this.disable.font && !Roo.isSafari){
27069 var name = (doc.queryCommandValue('FontName')||this.editor.defaultFont).toLowerCase();
27070 if(name != this.fontSelect.dom.value){
27071 this.fontSelect.dom.value = name;
27075 if(!this.disable.format){
27076 btns[frameId + '-bold'].toggle(doc.queryCommandState('bold'));
27077 btns[frameId + '-italic'].toggle(doc.queryCommandState('italic'));
27078 btns[frameId + '-underline'].toggle(doc.queryCommandState('underline'));
27080 if(!this.disable.alignments){
27081 btns[frameId + '-justifyleft'].toggle(doc.queryCommandState('justifyleft'));
27082 btns[frameId + '-justifycenter'].toggle(doc.queryCommandState('justifycenter'));
27083 btns[frameId + '-justifyright'].toggle(doc.queryCommandState('justifyright'));
27085 if(!Roo.isSafari && !this.disable.lists){
27086 btns[frameId + '-insertorderedlist'].toggle(doc.queryCommandState('insertorderedlist'));
27087 btns[frameId + '-insertunorderedlist'].toggle(doc.queryCommandState('insertunorderedlist'));
27090 var ans = this.editor.getAllAncestors();
27091 if (this.formatCombo) {
27094 var store = this.formatCombo.store;
27095 this.formatCombo.setValue("");
27096 for (var i =0; i < ans.length;i++) {
27097 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
27099 this.formatCombo.setValue(ans[i].tagName.toLowerCase());
27107 // hides menus... - so this cant be on a menu...
27108 Roo.menu.MenuMgr.hideAll();
27110 //this.editorsyncValue();
27114 createFontOptions : function(){
27115 var buf = [], fs = this.fontFamilies, ff, lc;
27119 for(var i = 0, len = fs.length; i< len; i++){
27121 lc = ff.toLowerCase();
27123 '<option value="',lc,'" style="font-family:',ff,';"',
27124 (this.defaultFont == lc ? ' selected="true">' : '>'),
27129 return buf.join('');
27132 toggleSourceEdit : function(sourceEditMode){
27133 if(sourceEditMode === undefined){
27134 sourceEditMode = !this.sourceEditMode;
27136 this.sourceEditMode = sourceEditMode === true;
27137 var btn = this.tb.items.get(this.editor.frameId +'-sourceedit');
27138 // just toggle the button?
27139 if(btn.pressed !== this.editor.sourceEditMode){
27140 btn.toggle(this.editor.sourceEditMode);
27144 if(this.sourceEditMode){
27145 this.tb.items.each(function(item){
27146 if(item.cmd != 'sourceedit'){
27152 if(this.initialized){
27153 this.tb.items.each(function(item){
27159 // tell the editor that it's been pressed..
27160 this.editor.toggleSourceEdit(sourceEditMode);
27164 * Object collection of toolbar tooltips for the buttons in the editor. The key
27165 * is the command id associated with that button and the value is a valid QuickTips object.
27170 title: 'Bold (Ctrl+B)',
27171 text: 'Make the selected text bold.',
27172 cls: 'x-html-editor-tip'
27175 title: 'Italic (Ctrl+I)',
27176 text: 'Make the selected text italic.',
27177 cls: 'x-html-editor-tip'
27185 title: 'Bold (Ctrl+B)',
27186 text: 'Make the selected text bold.',
27187 cls: 'x-html-editor-tip'
27190 title: 'Italic (Ctrl+I)',
27191 text: 'Make the selected text italic.',
27192 cls: 'x-html-editor-tip'
27195 title: 'Underline (Ctrl+U)',
27196 text: 'Underline the selected text.',
27197 cls: 'x-html-editor-tip'
27199 increasefontsize : {
27200 title: 'Grow Text',
27201 text: 'Increase the font size.',
27202 cls: 'x-html-editor-tip'
27204 decreasefontsize : {
27205 title: 'Shrink Text',
27206 text: 'Decrease the font size.',
27207 cls: 'x-html-editor-tip'
27210 title: 'Text Highlight Color',
27211 text: 'Change the background color of the selected text.',
27212 cls: 'x-html-editor-tip'
27215 title: 'Font Color',
27216 text: 'Change the color of the selected text.',
27217 cls: 'x-html-editor-tip'
27220 title: 'Align Text Left',
27221 text: 'Align text to the left.',
27222 cls: 'x-html-editor-tip'
27225 title: 'Center Text',
27226 text: 'Center text in the editor.',
27227 cls: 'x-html-editor-tip'
27230 title: 'Align Text Right',
27231 text: 'Align text to the right.',
27232 cls: 'x-html-editor-tip'
27234 insertunorderedlist : {
27235 title: 'Bullet List',
27236 text: 'Start a bulleted list.',
27237 cls: 'x-html-editor-tip'
27239 insertorderedlist : {
27240 title: 'Numbered List',
27241 text: 'Start a numbered list.',
27242 cls: 'x-html-editor-tip'
27245 title: 'Hyperlink',
27246 text: 'Make the selected text a hyperlink.',
27247 cls: 'x-html-editor-tip'
27250 title: 'Source Edit',
27251 text: 'Switch to source editing mode.',
27252 cls: 'x-html-editor-tip'
27256 onDestroy : function(){
27259 this.tb.items.each(function(item){
27261 item.menu.removeAll();
27263 item.menu.el.destroy();
27271 onFirstFocus: function() {
27272 this.tb.items.each(function(item){
27281 // <script type="text/javascript">
27284 * Ext JS Library 1.1.1
27285 * Copyright(c) 2006-2007, Ext JS, LLC.
27292 * @class Roo.form.HtmlEditor.ToolbarContext
27297 new Roo.form.HtmlEditor({
27300 { xtype: 'ToolbarStandard', styles : {} }
27301 { xtype: 'ToolbarContext', disable : {} }
27307 * @config : {Object} disable List of elements to disable.. (not done yet.)
27308 * @config : {Object} styles Map of styles available.
27312 Roo.form.HtmlEditor.ToolbarContext = function(config)
27315 Roo.apply(this, config);
27316 //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
27317 // dont call parent... till later.
27318 this.styles = this.styles || {};
27323 Roo.form.HtmlEditor.ToolbarContext.types = {
27335 opts : [ [""],[ "left"],[ "right"],[ "center"],[ "top"]],
27401 opts : [[""],[ "left"],[ "center"],[ "right"],[ "justify"],[ "char"]],
27406 opts : [[""],[ "top"],[ "middle"],[ "bottom"],[ "baseline"]],
27416 style : 'fontFamily',
27417 displayField: 'display',
27418 optname : 'font-family',
27467 // should we really allow this??
27468 // should this just be
27479 style : 'fontFamily',
27480 displayField: 'display',
27481 optname : 'font-family',
27488 style : 'fontFamily',
27489 displayField: 'display',
27490 optname : 'font-family',
27497 style : 'fontFamily',
27498 displayField: 'display',
27499 optname : 'font-family',
27510 // this should be configurable.. - you can either set it up using stores, or modify options somehwere..
27511 Roo.form.HtmlEditor.ToolbarContext.stores = false;
27513 Roo.form.HtmlEditor.ToolbarContext.options = {
27515 [ 'Helvetica,Arial,sans-serif', 'Helvetica'],
27516 [ 'Courier New', 'Courier New'],
27517 [ 'Tahoma', 'Tahoma'],
27518 [ 'Times New Roman,serif', 'Times'],
27519 [ 'Verdana','Verdana' ]
27523 // fixme - these need to be configurable..
27526 Roo.form.HtmlEditor.ToolbarContext.types
27529 Roo.apply(Roo.form.HtmlEditor.ToolbarContext.prototype, {
27537 * @cfg {Object} disable List of toolbar elements to disable
27542 * @cfg {Object} styles List of styles
27543 * eg. { '*' : [ 'headline' ] , 'TD' : [ 'underline', 'double-underline' ] }
27545 * These must be defined in the page, so they get rendered correctly..
27556 init : function(editor)
27558 this.editor = editor;
27561 var fid = editor.frameId;
27563 function btn(id, toggle, handler){
27564 var xid = fid + '-'+ id ;
27568 cls : 'x-btn-icon x-edit-'+id,
27569 enableToggle:toggle !== false,
27570 scope: editor, // was editor...
27571 handler:handler||editor.relayBtnCmd,
27572 clickEvent:'mousedown',
27573 tooltip: etb.buttonTips[id] || undefined, ///tips ???
27577 // create a new element.
27578 var wdiv = editor.wrap.createChild({
27580 }, editor.wrap.dom.firstChild.nextSibling, true);
27582 // can we do this more than once??
27584 // stop form submits
27587 // disable everything...
27588 var ty= Roo.form.HtmlEditor.ToolbarContext.types;
27589 this.toolbars = {};
27591 for (var i in ty) {
27593 this.toolbars[i] = this.buildToolbar(ty[i],i);
27595 this.tb = this.toolbars.BODY;
27597 this.buildFooter();
27598 this.footer.show();
27599 editor.on('hide', function( ) { this.footer.hide() }, this);
27600 editor.on('show', function( ) { this.footer.show() }, this);
27603 this.rendered = true;
27605 // the all the btns;
27606 editor.on('editorevent', this.updateToolbar, this);
27607 // other toolbars need to implement this..
27608 //editor.on('editmodechange', this.updateToolbar, this);
27614 * Protected method that will not generally be called directly. It triggers
27615 * a toolbar update by reading the markup state of the current selection in the editor.
27617 updateToolbar: function(editor,ev,sel){
27620 // capture mouse up - this is handy for selecting images..
27621 // perhaps should go somewhere else...
27622 if(!this.editor.activated){
27623 this.editor.onFirstFocus();
27627 // http://developer.yahoo.com/yui/docs/simple-editor.js.html
27628 // selectNode - might want to handle IE?
27630 (ev.type == 'mouseup' || ev.type == 'click' ) &&
27631 ev.target && ev.target.tagName == 'IMG') {
27632 // they have click on an image...
27633 // let's see if we can change the selection...
27636 var nodeRange = sel.ownerDocument.createRange();
27638 nodeRange.selectNode(sel);
27640 nodeRange.selectNodeContents(sel);
27642 //nodeRange.collapse(true);
27643 var s = editor.win.getSelection();
27644 s.removeAllRanges();
27645 s.addRange(nodeRange);
27649 var updateFooter = sel ? false : true;
27652 var ans = this.editor.getAllAncestors();
27655 var ty= Roo.form.HtmlEditor.ToolbarContext.types;
27658 sel = ans.length ? (ans[0] ? ans[0] : ans[1]) : this.editor.doc.body;
27659 sel = sel ? sel : this.editor.doc.body;
27660 sel = sel.tagName.length ? sel : this.editor.doc.body;
27663 // pick a menu that exists..
27664 var tn = sel.tagName.toUpperCase();
27665 //sel = typeof(ty[tn]) != 'undefined' ? sel : this.editor.doc.body;
27667 tn = sel.tagName.toUpperCase();
27669 var lastSel = this.tb.selectedNode
27671 this.tb.selectedNode = sel;
27673 // if current menu does not match..
27674 if ((this.tb.name != tn) || (lastSel != this.tb.selectedNode)) {
27677 ///console.log("show: " + tn);
27678 this.tb = typeof(ty[tn]) != 'undefined' ? this.toolbars[tn] : this.toolbars['*'];
27681 this.tb.items.first().el.innerHTML = tn + ': ';
27684 // update attributes
27685 if (this.tb.fields) {
27686 this.tb.fields.each(function(e) {
27688 e.setValue(sel.style[e.stylename]);
27691 e.setValue(sel.getAttribute(e.attrname));
27695 var hasStyles = false;
27696 for(var i in this.styles) {
27703 var st = this.tb.fields.item(0);
27705 st.store.removeAll();
27708 var cn = sel.className.split(/\s+/);
27711 if (this.styles['*']) {
27713 Roo.each(this.styles['*'], function(v) {
27714 avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );
27717 if (this.styles[tn]) {
27718 Roo.each(this.styles[tn], function(v) {
27719 avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );
27723 st.store.loadData(avs);
27727 // flag our selected Node.
27728 this.tb.selectedNode = sel;
27731 Roo.menu.MenuMgr.hideAll();
27735 if (!updateFooter) {
27736 //this.footDisp.dom.innerHTML = '';
27739 // update the footer
27743 this.footerEls = ans.reverse();
27744 Roo.each(this.footerEls, function(a,i) {
27745 if (!a) { return; }
27746 html += html.length ? ' > ' : '';
27748 html += '<span class="x-ed-loc-' + i + '">' + a.tagName + '</span>';
27753 var sz = this.footDisp.up('td').getSize();
27754 this.footDisp.dom.style.width = (sz.width -10) + 'px';
27755 this.footDisp.dom.style.marginLeft = '5px';
27757 this.footDisp.dom.style.overflow = 'hidden';
27759 this.footDisp.dom.innerHTML = html;
27761 //this.editorsyncValue();
27768 onDestroy : function(){
27771 this.tb.items.each(function(item){
27773 item.menu.removeAll();
27775 item.menu.el.destroy();
27783 onFirstFocus: function() {
27784 // need to do this for all the toolbars..
27785 this.tb.items.each(function(item){
27789 buildToolbar: function(tlist, nm)
27791 var editor = this.editor;
27792 // create a new element.
27793 var wdiv = editor.wrap.createChild({
27795 }, editor.wrap.dom.firstChild.nextSibling, true);
27798 var tb = new Roo.Toolbar(wdiv);
27801 tb.add(nm+ ": ");
27804 for(var i in this.styles) {
27809 if (styles && styles.length) {
27811 // this needs a multi-select checkbox...
27812 tb.addField( new Roo.form.ComboBox({
27813 store: new Roo.data.SimpleStore({
27815 fields: ['val', 'selected'],
27818 name : '-roo-edit-className',
27819 attrname : 'className',
27820 displayField: 'val',
27824 triggerAction: 'all',
27825 emptyText:'Select Style',
27826 selectOnFocus:true,
27829 'select': function(c, r, i) {
27830 // initial support only for on class per el..
27831 tb.selectedNode.className = r ? r.get('val') : '';
27832 editor.syncValue();
27839 var tbc = Roo.form.HtmlEditor.ToolbarContext;
27840 var tbops = tbc.options;
27842 for (var i in tlist) {
27844 var item = tlist[i];
27845 tb.add(item.title + ": ");
27848 //optname == used so you can configure the options available..
27849 var opts = item.opts ? item.opts : false;
27850 if (item.optname) {
27851 opts = tbops[item.optname];
27856 // opts == pulldown..
27857 tb.addField( new Roo.form.ComboBox({
27858 store: typeof(tbc.stores[i]) != 'undefined' ? Roo.factory(tbc.stores[i],Roo.data) : new Roo.data.SimpleStore({
27860 fields: ['val', 'display'],
27863 name : '-roo-edit-' + i,
27865 stylename : item.style ? item.style : false,
27866 displayField: item.displayField ? item.displayField : 'val',
27867 valueField : 'val',
27869 mode: typeof(tbc.stores[i]) != 'undefined' ? 'remote' : 'local',
27871 triggerAction: 'all',
27872 emptyText:'Select',
27873 selectOnFocus:true,
27874 width: item.width ? item.width : 130,
27876 'select': function(c, r, i) {
27878 tb.selectedNode.style[c.stylename] = r.get('val');
27881 tb.selectedNode.setAttribute(c.attrname, r.get('val'));
27890 tb.addField( new Roo.form.TextField({
27893 //allowBlank:false,
27898 tb.addField( new Roo.form.TextField({
27899 name: '-roo-edit-' + i,
27906 'change' : function(f, nv, ov) {
27907 tb.selectedNode.setAttribute(f.attrname, nv);
27916 text: 'Remove Tag',
27919 click : function ()
27922 // undo does not work.
27924 var sn = tb.selectedNode;
27926 var pn = sn.parentNode;
27928 var stn = sn.childNodes[0];
27929 var en = sn.childNodes[sn.childNodes.length - 1 ];
27930 while (sn.childNodes.length) {
27931 var node = sn.childNodes[0];
27932 sn.removeChild(node);
27934 pn.insertBefore(node, sn);
27937 pn.removeChild(sn);
27938 var range = editor.createRange();
27940 range.setStart(stn,0);
27941 range.setEnd(en,0); //????
27942 //range.selectNode(sel);
27945 var selection = editor.getSelection();
27946 selection.removeAllRanges();
27947 selection.addRange(range);
27951 //_this.updateToolbar(null, null, pn);
27952 _this.updateToolbar(null, null, null);
27953 _this.footDisp.dom.innerHTML = '';
27963 tb.el.on('click', function(e){
27964 e.preventDefault(); // what does this do?
27966 tb.el.setVisibilityMode( Roo.Element.DISPLAY);
27969 // dont need to disable them... as they will get hidden
27974 buildFooter : function()
27977 var fel = this.editor.wrap.createChild();
27978 this.footer = new Roo.Toolbar(fel);
27979 // toolbar has scrolly on left / right?
27980 var footDisp= new Roo.Toolbar.Fill();
27986 handler : function() {
27987 _t.footDisp.scrollTo('left',0,true)
27991 this.footer.add( footDisp );
27996 handler : function() {
27998 _t.footDisp.select('span').last().scrollIntoView(_t.footDisp,true);
28002 var fel = Roo.get(footDisp.el);
28003 fel.addClass('x-editor-context');
28004 this.footDispWrap = fel;
28005 this.footDispWrap.overflow = 'hidden';
28007 this.footDisp = fel.createChild();
28008 this.footDispWrap.on('click', this.onContextClick, this)
28012 onContextClick : function (ev,dom)
28014 ev.preventDefault();
28015 var cn = dom.className;
28017 if (!cn.match(/x-ed-loc-/)) {
28020 var n = cn.split('-').pop();
28021 var ans = this.footerEls;
28025 var range = this.editor.createRange();
28027 range.selectNodeContents(sel);
28028 //range.selectNode(sel);
28031 var selection = this.editor.getSelection();
28032 selection.removeAllRanges();
28033 selection.addRange(range);
28037 this.updateToolbar(null, null, sel);
28054 * Ext JS Library 1.1.1
28055 * Copyright(c) 2006-2007, Ext JS, LLC.
28057 * Originally Released Under LGPL - original licence link has changed is not relivant.
28060 * <script type="text/javascript">
28064 * @class Roo.form.BasicForm
28065 * @extends Roo.util.Observable
28066 * Supplies the functionality to do "actions" on forms and initialize Roo.form.Field types on existing markup.
28068 * @param {String/HTMLElement/Roo.Element} el The form element or its id
28069 * @param {Object} config Configuration options
28071 Roo.form.BasicForm = function(el, config){
28072 this.allItems = [];
28073 this.childForms = [];
28074 Roo.apply(this, config);
28076 * The Roo.form.Field items in this form.
28077 * @type MixedCollection
28081 this.items = new Roo.util.MixedCollection(false, function(o){
28082 return o.id || (o.id = Roo.id());
28086 * @event beforeaction
28087 * Fires before any action is performed. Return false to cancel the action.
28088 * @param {Form} this
28089 * @param {Action} action The action to be performed
28091 beforeaction: true,
28093 * @event actionfailed
28094 * Fires when an action fails.
28095 * @param {Form} this
28096 * @param {Action} action The action that failed
28098 actionfailed : true,
28100 * @event actioncomplete
28101 * Fires when an action is completed.
28102 * @param {Form} this
28103 * @param {Action} action The action that completed
28105 actioncomplete : true
28110 Roo.form.BasicForm.superclass.constructor.call(this);
28113 Roo.extend(Roo.form.BasicForm, Roo.util.Observable, {
28115 * @cfg {String} method
28116 * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
28119 * @cfg {DataReader} reader
28120 * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when executing "load" actions.
28121 * This is optional as there is built-in support for processing JSON.
28124 * @cfg {DataReader} errorReader
28125 * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when reading validation errors on "submit" actions.
28126 * This is completely optional as there is built-in support for processing JSON.
28129 * @cfg {String} url
28130 * The URL to use for form actions if one isn't supplied in the action options.
28133 * @cfg {Boolean} fileUpload
28134 * Set to true if this form is a file upload.
28138 * @cfg {Object} baseParams
28139 * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
28144 * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
28149 activeAction : null,
28152 * @cfg {Boolean} trackResetOnLoad If set to true, form.reset() resets to the last loaded
28153 * or setValues() data instead of when the form was first created.
28155 trackResetOnLoad : false,
28159 * childForms - used for multi-tab forms
28162 childForms : false,
28165 * allItems - full list of fields.
28171 * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
28172 * element by passing it or its id or mask the form itself by passing in true.
28175 waitMsgTarget : false,
28178 initEl : function(el){
28179 this.el = Roo.get(el);
28180 this.id = this.el.id || Roo.id();
28181 this.el.on('submit', this.onSubmit, this);
28182 this.el.addClass('x-form');
28186 onSubmit : function(e){
28191 * Returns true if client-side validation on the form is successful.
28194 isValid : function(){
28196 this.items.each(function(f){
28205 * Returns true if any fields in this form have changed since their original load.
28208 isDirty : function(){
28210 this.items.each(function(f){
28220 * Performs a predefined action (submit or load) or custom actions you define on this form.
28221 * @param {String} actionName The name of the action type
28222 * @param {Object} options (optional) The options to pass to the action. All of the config options listed
28223 * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
28224 * accept other config options):
28226 Property Type Description
28227 ---------------- --------------- ----------------------------------------------------------------------------------
28228 url String The url for the action (defaults to the form's url)
28229 method String The form method to use (defaults to the form's method, or POST if not defined)
28230 params String/Object The params to pass (defaults to the form's baseParams, or none if not defined)
28231 clientValidation Boolean Applies to submit only. Pass true to call form.isValid() prior to posting to
28232 validate the form on the client (defaults to false)
28234 * @return {BasicForm} this
28236 doAction : function(action, options){
28237 if(typeof action == 'string'){
28238 action = new Roo.form.Action.ACTION_TYPES[action](this, options);
28240 if(this.fireEvent('beforeaction', this, action) !== false){
28241 this.beforeAction(action);
28242 action.run.defer(100, action);
28248 * Shortcut to do a submit action.
28249 * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
28250 * @return {BasicForm} this
28252 submit : function(options){
28253 this.doAction('submit', options);
28258 * Shortcut to do a load action.
28259 * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
28260 * @return {BasicForm} this
28262 load : function(options){
28263 this.doAction('load', options);
28268 * Persists the values in this form into the passed Roo.data.Record object in a beginEdit/endEdit block.
28269 * @param {Record} record The record to edit
28270 * @return {BasicForm} this
28272 updateRecord : function(record){
28273 record.beginEdit();
28274 var fs = record.fields;
28275 fs.each(function(f){
28276 var field = this.findField(f.name);
28278 record.set(f.name, field.getValue());
28286 * Loads an Roo.data.Record into this form.
28287 * @param {Record} record The record to load
28288 * @return {BasicForm} this
28290 loadRecord : function(record){
28291 this.setValues(record.data);
28296 beforeAction : function(action){
28297 var o = action.options;
28300 if(this.waitMsgTarget === true){
28301 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
28302 }else if(this.waitMsgTarget){
28303 this.waitMsgTarget = Roo.get(this.waitMsgTarget);
28304 this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
28306 Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
28312 afterAction : function(action, success){
28313 this.activeAction = null;
28314 var o = action.options;
28316 if(this.waitMsgTarget === true){
28318 }else if(this.waitMsgTarget){
28319 this.waitMsgTarget.unmask();
28321 Roo.MessageBox.updateProgress(1);
28322 Roo.MessageBox.hide();
28329 Roo.callback(o.success, o.scope, [this, action]);
28330 this.fireEvent('actioncomplete', this, action);
28334 // failure condition..
28335 // we have a scenario where updates need confirming.
28336 // eg. if a locking scenario exists..
28337 // we look for { errors : { needs_confirm : true }} in the response.
28339 (typeof(action.result) != 'undefined') &&
28340 (typeof(action.result.errors) != 'undefined') &&
28341 (typeof(action.result.errors.needs_confirm) != 'undefined')
28344 Roo.MessageBox.confirm(
28345 "Change requires confirmation",
28346 action.result.errorMsg,
28351 _t.doAction('submit', { params : { _submit_confirmed : 1 } } );
28361 Roo.callback(o.failure, o.scope, [this, action]);
28362 // show an error message if no failed handler is set..
28363 if (!this.hasListener('actionfailed')) {
28364 Roo.MessageBox.alert("Error",
28365 (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
28366 action.result.errorMsg :
28367 "Saving Failed, please check your entries or try again"
28371 this.fireEvent('actionfailed', this, action);
28377 * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
28378 * @param {String} id The value to search for
28381 findField : function(id){
28382 var field = this.items.get(id);
28384 this.items.each(function(f){
28385 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
28391 return field || null;
28395 * Add a secondary form to this one,
28396 * Used to provide tabbed forms. One form is primary, with hidden values
28397 * which mirror the elements from the other forms.
28399 * @param {Roo.form.Form} form to add.
28402 addForm : function(form)
28405 if (this.childForms.indexOf(form) > -1) {
28409 this.childForms.push(form);
28411 Roo.each(form.allItems, function (fe) {
28413 n = typeof(fe.getName) == 'undefined' ? fe.name : fe.getName();
28414 if (this.findField(n)) { // already added..
28417 var add = new Roo.form.Hidden({
28420 add.render(this.el);
28427 * Mark fields in this form invalid in bulk.
28428 * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
28429 * @return {BasicForm} this
28431 markInvalid : function(errors){
28432 if(errors instanceof Array){
28433 for(var i = 0, len = errors.length; i < len; i++){
28434 var fieldError = errors[i];
28435 var f = this.findField(fieldError.id);
28437 f.markInvalid(fieldError.msg);
28443 if(typeof errors[id] != 'function' && (field = this.findField(id))){
28444 field.markInvalid(errors[id]);
28448 Roo.each(this.childForms || [], function (f) {
28449 f.markInvalid(errors);
28456 * Set values for fields in this form in bulk.
28457 * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
28458 * @return {BasicForm} this
28460 setValues : function(values){
28461 if(values instanceof Array){ // array of objects
28462 for(var i = 0, len = values.length; i < len; i++){
28464 var f = this.findField(v.id);
28466 f.setValue(v.value);
28467 if(this.trackResetOnLoad){
28468 f.originalValue = f.getValue();
28472 }else{ // object hash
28475 if(typeof values[id] != 'function' && (field = this.findField(id))){
28477 if (field.setFromData &&
28478 field.valueField &&
28479 field.displayField &&
28480 // combos' with local stores can
28481 // be queried via setValue()
28482 // to set their value..
28483 (field.store && !field.store.isLocal)
28487 sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
28488 sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
28489 field.setFromData(sd);
28492 field.setValue(values[id]);
28496 if(this.trackResetOnLoad){
28497 field.originalValue = field.getValue();
28503 Roo.each(this.childForms || [], function (f) {
28504 f.setValues(values);
28511 * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
28512 * they are returned as an array.
28513 * @param {Boolean} asString
28516 getValues : function(asString){
28517 if (this.childForms) {
28518 // copy values from the child forms
28519 Roo.each(this.childForms, function (f) {
28520 this.setValues(f.getValues());
28526 var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
28527 if(asString === true){
28530 return Roo.urlDecode(fs);
28534 * Returns the fields in this form as an object with key/value pairs.
28535 * This differs from getValues as it calls getValue on each child item, rather than using dom data.
28538 getFieldValues : function(with_hidden)
28540 if (this.childForms) {
28541 // copy values from the child forms
28542 // should this call getFieldValues - probably not as we do not currently copy
28543 // hidden fields when we generate..
28544 Roo.each(this.childForms, function (f) {
28545 this.setValues(f.getValues());
28550 this.items.each(function(f){
28551 if (!f.getName()) {
28554 var v = f.getValue();
28555 if (f.inputType =='radio') {
28556 if (typeof(ret[f.getName()]) == 'undefined') {
28557 ret[f.getName()] = ''; // empty..
28560 if (!f.el.dom.checked) {
28564 v = f.el.dom.value;
28568 // not sure if this supported any more..
28569 if ((typeof(v) == 'object') && f.getRawValue) {
28570 v = f.getRawValue() ; // dates..
28572 // combo boxes where name != hiddenName...
28573 if (f.name != f.getName()) {
28574 ret[f.name] = f.getRawValue();
28576 ret[f.getName()] = v;
28583 * Clears all invalid messages in this form.
28584 * @return {BasicForm} this
28586 clearInvalid : function(){
28587 this.items.each(function(f){
28591 Roo.each(this.childForms || [], function (f) {
28600 * Resets this form.
28601 * @return {BasicForm} this
28603 reset : function(){
28604 this.items.each(function(f){
28608 Roo.each(this.childForms || [], function (f) {
28617 * Add Roo.form components to this form.
28618 * @param {Field} field1
28619 * @param {Field} field2 (optional)
28620 * @param {Field} etc (optional)
28621 * @return {BasicForm} this
28624 this.items.addAll(Array.prototype.slice.call(arguments, 0));
28630 * Removes a field from the items collection (does NOT remove its markup).
28631 * @param {Field} field
28632 * @return {BasicForm} this
28634 remove : function(field){
28635 this.items.remove(field);
28640 * Looks at the fields in this form, checks them for an id attribute,
28641 * and calls applyTo on the existing dom element with that id.
28642 * @return {BasicForm} this
28644 render : function(){
28645 this.items.each(function(f){
28646 if(f.isFormField && !f.rendered && document.getElementById(f.id)){ // if the element exists
28654 * Calls {@link Ext#apply} for all fields in this form with the passed object.
28655 * @param {Object} values
28656 * @return {BasicForm} this
28658 applyToFields : function(o){
28659 this.items.each(function(f){
28666 * Calls {@link Ext#applyIf} for all field in this form with the passed object.
28667 * @param {Object} values
28668 * @return {BasicForm} this
28670 applyIfToFields : function(o){
28671 this.items.each(function(f){
28679 Roo.BasicForm = Roo.form.BasicForm;/*
28681 * Ext JS Library 1.1.1
28682 * Copyright(c) 2006-2007, Ext JS, LLC.
28684 * Originally Released Under LGPL - original licence link has changed is not relivant.
28687 * <script type="text/javascript">
28691 * @class Roo.form.Form
28692 * @extends Roo.form.BasicForm
28693 * Adds the ability to dynamically render forms with JavaScript to {@link Roo.form.BasicForm}.
28695 * @param {Object} config Configuration options
28697 Roo.form.Form = function(config){
28699 if (config.items) {
28700 xitems = config.items;
28701 delete config.items;
28705 Roo.form.Form.superclass.constructor.call(this, null, config);
28706 this.url = this.url || this.action;
28708 this.root = new Roo.form.Layout(Roo.applyIf({
28712 this.active = this.root;
28714 * Array of all the buttons that have been added to this form via {@link addButton}
28718 this.allItems = [];
28721 * @event clientvalidation
28722 * If the monitorValid config option is true, this event fires repetitively to notify of valid state
28723 * @param {Form} this
28724 * @param {Boolean} valid true if the form has passed client-side validation
28726 clientvalidation: true,
28729 * Fires when the form is rendered
28730 * @param {Roo.form.Form} form
28735 if (this.progressUrl) {
28736 // push a hidden field onto the list of fields..
28740 name : 'UPLOAD_IDENTIFIER'
28745 Roo.each(xitems, this.addxtype, this);
28751 Roo.extend(Roo.form.Form, Roo.form.BasicForm, {
28753 * @cfg {Number} labelWidth The width of labels. This property cascades to child containers.
28756 * @cfg {String} itemCls A css class to apply to the x-form-item of fields. This property cascades to child containers.
28759 * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "center")
28761 buttonAlign:'center',
28764 * @cfg {Number} minButtonWidth Minimum width of all buttons in pixels (defaults to 75)
28769 * @cfg {String} labelAlign Valid values are "left," "top" and "right" (defaults to "left").
28770 * This property cascades to child containers if not set.
28775 * @cfg {Boolean} monitorValid If true the form monitors its valid state <b>client-side</b> and
28776 * fires a looping event with that state. This is required to bind buttons to the valid
28777 * state using the config value formBind:true on the button.
28779 monitorValid : false,
28782 * @cfg {Number} monitorPoll The milliseconds to poll valid state, ignored if monitorValid is not true (defaults to 200)
28787 * @cfg {String} progressUrl - Url to return progress data
28790 progressUrl : false,
28793 * Opens a new {@link Roo.form.Column} container in the layout stack. If fields are passed after the config, the
28794 * fields are added and the column is closed. If no fields are passed the column remains open
28795 * until end() is called.
28796 * @param {Object} config The config to pass to the column
28797 * @param {Field} field1 (optional)
28798 * @param {Field} field2 (optional)
28799 * @param {Field} etc (optional)
28800 * @return Column The column container object
28802 column : function(c){
28803 var col = new Roo.form.Column(c);
28805 if(arguments.length > 1){ // duplicate code required because of Opera
28806 this.add.apply(this, Array.prototype.slice.call(arguments, 1));
28813 * Opens a new {@link Roo.form.FieldSet} container in the layout stack. If fields are passed after the config, the
28814 * fields are added and the fieldset is closed. If no fields are passed the fieldset remains open
28815 * until end() is called.
28816 * @param {Object} config The config to pass to the fieldset
28817 * @param {Field} field1 (optional)
28818 * @param {Field} field2 (optional)
28819 * @param {Field} etc (optional)
28820 * @return FieldSet The fieldset container object
28822 fieldset : function(c){
28823 var fs = new Roo.form.FieldSet(c);
28825 if(arguments.length > 1){ // duplicate code required because of Opera
28826 this.add.apply(this, Array.prototype.slice.call(arguments, 1));
28833 * Opens a new {@link Roo.form.Layout} container in the layout stack. If fields are passed after the config, the
28834 * fields are added and the container is closed. If no fields are passed the container remains open
28835 * until end() is called.
28836 * @param {Object} config The config to pass to the Layout
28837 * @param {Field} field1 (optional)
28838 * @param {Field} field2 (optional)
28839 * @param {Field} etc (optional)
28840 * @return Layout The container object
28842 container : function(c){
28843 var l = new Roo.form.Layout(c);
28845 if(arguments.length > 1){ // duplicate code required because of Opera
28846 this.add.apply(this, Array.prototype.slice.call(arguments, 1));
28853 * Opens the passed container in the layout stack. The container can be any {@link Roo.form.Layout} or subclass.
28854 * @param {Object} container A Roo.form.Layout or subclass of Layout
28855 * @return {Form} this
28857 start : function(c){
28858 // cascade label info
28859 Roo.applyIf(c, {'labelAlign': this.active.labelAlign, 'labelWidth': this.active.labelWidth, 'itemCls': this.active.itemCls});
28860 this.active.stack.push(c);
28861 c.ownerCt = this.active;
28867 * Closes the current open container
28868 * @return {Form} this
28871 if(this.active == this.root){
28874 this.active = this.active.ownerCt;
28879 * Add Roo.form components to the current open container (e.g. column, fieldset, etc.). Fields added via this method
28880 * can also be passed with an additional property of fieldLabel, which if supplied, will provide the text to display
28881 * as the label of the field.
28882 * @param {Field} field1
28883 * @param {Field} field2 (optional)
28884 * @param {Field} etc. (optional)
28885 * @return {Form} this
28888 this.active.stack.push.apply(this.active.stack, arguments);
28889 this.allItems.push.apply(this.allItems,arguments);
28891 for(var i = 0, a = arguments, len = a.length; i < len; i++) {
28892 if(a[i].isFormField){
28897 Roo.form.Form.superclass.add.apply(this, r);
28907 * Find any element that has been added to a form, using it's ID or name
28908 * This can include framesets, columns etc. along with regular fields..
28909 * @param {String} id - id or name to find.
28911 * @return {Element} e - or false if nothing found.
28913 findbyId : function(id)
28919 Roo.each(this.allItems, function(f){
28920 if (f.id == id || f.name == id ){
28931 * Render this form into the passed container. This should only be called once!
28932 * @param {String/HTMLElement/Element} container The element this component should be rendered into
28933 * @return {Form} this
28935 render : function(ct)
28941 var o = this.autoCreate || {
28943 method : this.method || 'POST',
28944 id : this.id || Roo.id()
28946 this.initEl(ct.createChild(o));
28948 this.root.render(this.el);
28952 this.items.each(function(f){
28953 f.render('x-form-el-'+f.id);
28956 if(this.buttons.length > 0){
28957 // tables are required to maintain order and for correct IE layout
28958 var tb = this.el.createChild({cls:'x-form-btns-ct', cn: {
28959 cls:"x-form-btns x-form-btns-"+this.buttonAlign,
28960 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
28962 var tr = tb.getElementsByTagName('tr')[0];
28963 for(var i = 0, len = this.buttons.length; i < len; i++) {
28964 var b = this.buttons[i];
28965 var td = document.createElement('td');
28966 td.className = 'x-form-btn-td';
28967 b.render(tr.appendChild(td));
28970 if(this.monitorValid){ // initialize after render
28971 this.startMonitoring();
28973 this.fireEvent('rendered', this);
28978 * Adds a button to the footer of the form - this <b>must</b> be called before the form is rendered.
28979 * @param {String/Object} config A string becomes the button text, an object can either be a Button config
28980 * object or a valid Roo.DomHelper element config
28981 * @param {Function} handler The function called when the button is clicked
28982 * @param {Object} scope (optional) The scope of the handler function
28983 * @return {Roo.Button}
28985 addButton : function(config, handler, scope){
28989 minWidth: this.minButtonWidth,
28992 if(typeof config == "string"){
28995 Roo.apply(bc, config);
28997 var btn = new Roo.Button(null, bc);
28998 this.buttons.push(btn);
29003 * Adds a series of form elements (using the xtype property as the factory method.
29004 * Valid xtypes are: TextField, TextArea .... Button, Layout, FieldSet, Column, (and 'end' to close a block)
29005 * @param {Object} config
29008 addxtype : function()
29010 var ar = Array.prototype.slice.call(arguments, 0);
29012 for(var i = 0; i < ar.length; i++) {
29014 continue; // skip -- if this happends something invalid got sent, we
29015 // should ignore it, as basically that interface element will not show up
29016 // and that should be pretty obvious!!
29019 if (Roo.form[ar[i].xtype]) {
29021 var fe = Roo.factory(ar[i], Roo.form);
29027 fe.store.form = this;
29032 this.allItems.push(fe);
29033 if (fe.items && fe.addxtype) {
29034 fe.addxtype.apply(fe, fe.items);
29044 // console.log('adding ' + ar[i].xtype);
29046 if (ar[i].xtype == 'Button') {
29047 //console.log('adding button');
29048 //console.log(ar[i]);
29049 this.addButton(ar[i]);
29050 this.allItems.push(fe);
29054 if (ar[i].xtype == 'end') { // so we can add fieldsets... / layout etc.
29055 alert('end is not supported on xtype any more, use items');
29057 // //console.log('adding end');
29065 * Starts monitoring of the valid state of this form. Usually this is done by passing the config
29066 * option "monitorValid"
29068 startMonitoring : function(){
29071 Roo.TaskMgr.start({
29072 run : this.bindHandler,
29073 interval : this.monitorPoll || 200,
29080 * Stops monitoring of the valid state of this form
29082 stopMonitoring : function(){
29083 this.bound = false;
29087 bindHandler : function(){
29089 return false; // stops binding
29092 this.items.each(function(f){
29093 if(!f.isValid(true)){
29098 for(var i = 0, len = this.buttons.length; i < len; i++){
29099 var btn = this.buttons[i];
29100 if(btn.formBind === true && btn.disabled === valid){
29101 btn.setDisabled(!valid);
29104 this.fireEvent('clientvalidation', this, valid);
29118 Roo.Form = Roo.form.Form;
29121 * Ext JS Library 1.1.1
29122 * Copyright(c) 2006-2007, Ext JS, LLC.
29124 * Originally Released Under LGPL - original licence link has changed is not relivant.
29127 * <script type="text/javascript">
29131 * @class Roo.form.Action
29132 * Internal Class used to handle form actions
29134 * @param {Roo.form.BasicForm} el The form element or its id
29135 * @param {Object} config Configuration options
29139 // define the action interface
29140 Roo.form.Action = function(form, options){
29142 this.options = options || {};
29145 * Client Validation Failed
29148 Roo.form.Action.CLIENT_INVALID = 'client';
29150 * Server Validation Failed
29153 Roo.form.Action.SERVER_INVALID = 'server';
29155 * Connect to Server Failed
29158 Roo.form.Action.CONNECT_FAILURE = 'connect';
29160 * Reading Data from Server Failed
29163 Roo.form.Action.LOAD_FAILURE = 'load';
29165 Roo.form.Action.prototype = {
29167 failureType : undefined,
29168 response : undefined,
29169 result : undefined,
29171 // interface method
29172 run : function(options){
29176 // interface method
29177 success : function(response){
29181 // interface method
29182 handleResponse : function(response){
29186 // default connection failure
29187 failure : function(response){
29189 this.response = response;
29190 this.failureType = Roo.form.Action.CONNECT_FAILURE;
29191 this.form.afterAction(this, false);
29194 processResponse : function(response){
29195 this.response = response;
29196 if(!response.responseText){
29199 this.result = this.handleResponse(response);
29200 return this.result;
29203 // utility functions used internally
29204 getUrl : function(appendParams){
29205 var url = this.options.url || this.form.url || this.form.el.dom.action;
29207 var p = this.getParams();
29209 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
29215 getMethod : function(){
29216 return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
29219 getParams : function(){
29220 var bp = this.form.baseParams;
29221 var p = this.options.params;
29223 if(typeof p == "object"){
29224 p = Roo.urlEncode(Roo.applyIf(p, bp));
29225 }else if(typeof p == 'string' && bp){
29226 p += '&' + Roo.urlEncode(bp);
29229 p = Roo.urlEncode(bp);
29234 createCallback : function(){
29236 success: this.success,
29237 failure: this.failure,
29239 timeout: (this.form.timeout*1000),
29240 upload: this.form.fileUpload ? this.success : undefined
29245 Roo.form.Action.Submit = function(form, options){
29246 Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
29249 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
29252 haveProgress : false,
29253 uploadComplete : false,
29255 // uploadProgress indicator.
29256 uploadProgress : function()
29258 if (!this.form.progressUrl) {
29262 if (!this.haveProgress) {
29263 Roo.MessageBox.progress("Uploading", "Uploading");
29265 if (this.uploadComplete) {
29266 Roo.MessageBox.hide();
29270 this.haveProgress = true;
29272 var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
29274 var c = new Roo.data.Connection();
29276 url : this.form.progressUrl,
29281 success : function(req){
29282 //console.log(data);
29286 rdata = Roo.decode(req.responseText)
29288 Roo.log("Invalid data from server..");
29292 if (!rdata || !rdata.success) {
29294 Roo.MessageBox.alert(Roo.encode(rdata));
29297 var data = rdata.data;
29299 if (this.uploadComplete) {
29300 Roo.MessageBox.hide();
29305 Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
29306 Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
29309 this.uploadProgress.defer(2000,this);
29312 failure: function(data) {
29313 Roo.log('progress url failed ');
29324 // run get Values on the form, so it syncs any secondary forms.
29325 this.form.getValues();
29327 var o = this.options;
29328 var method = this.getMethod();
29329 var isPost = method == 'POST';
29330 if(o.clientValidation === false || this.form.isValid()){
29332 if (this.form.progressUrl) {
29333 this.form.findField('UPLOAD_IDENTIFIER').setValue(
29334 (new Date() * 1) + '' + Math.random());
29339 Roo.Ajax.request(Roo.apply(this.createCallback(), {
29340 form:this.form.el.dom,
29341 url:this.getUrl(!isPost),
29343 params:isPost ? this.getParams() : null,
29344 isUpload: this.form.fileUpload
29347 this.uploadProgress();
29349 }else if (o.clientValidation !== false){ // client validation failed
29350 this.failureType = Roo.form.Action.CLIENT_INVALID;
29351 this.form.afterAction(this, false);
29355 success : function(response)
29357 this.uploadComplete= true;
29358 if (this.haveProgress) {
29359 Roo.MessageBox.hide();
29363 var result = this.processResponse(response);
29364 if(result === true || result.success){
29365 this.form.afterAction(this, true);
29369 this.form.markInvalid(result.errors);
29370 this.failureType = Roo.form.Action.SERVER_INVALID;
29372 this.form.afterAction(this, false);
29374 failure : function(response)
29376 this.uploadComplete= true;
29377 if (this.haveProgress) {
29378 Roo.MessageBox.hide();
29381 this.response = response;
29382 this.failureType = Roo.form.Action.CONNECT_FAILURE;
29383 this.form.afterAction(this, false);
29386 handleResponse : function(response){
29387 if(this.form.errorReader){
29388 var rs = this.form.errorReader.read(response);
29391 for(var i = 0, len = rs.records.length; i < len; i++) {
29392 var r = rs.records[i];
29393 errors[i] = r.data;
29396 if(errors.length < 1){
29400 success : rs.success,
29406 ret = Roo.decode(response.responseText);
29410 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
29420 Roo.form.Action.Load = function(form, options){
29421 Roo.form.Action.Load.superclass.constructor.call(this, form, options);
29422 this.reader = this.form.reader;
29425 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
29430 Roo.Ajax.request(Roo.apply(
29431 this.createCallback(), {
29432 method:this.getMethod(),
29433 url:this.getUrl(false),
29434 params:this.getParams()
29438 success : function(response){
29440 var result = this.processResponse(response);
29441 if(result === true || !result.success || !result.data){
29442 this.failureType = Roo.form.Action.LOAD_FAILURE;
29443 this.form.afterAction(this, false);
29446 this.form.clearInvalid();
29447 this.form.setValues(result.data);
29448 this.form.afterAction(this, true);
29451 handleResponse : function(response){
29452 if(this.form.reader){
29453 var rs = this.form.reader.read(response);
29454 var data = rs.records && rs.records[0] ? rs.records[0].data : null;
29456 success : rs.success,
29460 return Roo.decode(response.responseText);
29464 Roo.form.Action.ACTION_TYPES = {
29465 'load' : Roo.form.Action.Load,
29466 'submit' : Roo.form.Action.Submit
29469 * Ext JS Library 1.1.1
29470 * Copyright(c) 2006-2007, Ext JS, LLC.
29472 * Originally Released Under LGPL - original licence link has changed is not relivant.
29475 * <script type="text/javascript">
29479 * @class Roo.form.Layout
29480 * @extends Roo.Component
29481 * Creates a container for layout and rendering of fields in an {@link Roo.form.Form}.
29483 * @param {Object} config Configuration options
29485 Roo.form.Layout = function(config){
29487 if (config.items) {
29488 xitems = config.items;
29489 delete config.items;
29491 Roo.form.Layout.superclass.constructor.call(this, config);
29493 Roo.each(xitems, this.addxtype, this);
29497 Roo.extend(Roo.form.Layout, Roo.Component, {
29499 * @cfg {String/Object} autoCreate
29500 * A DomHelper element spec used to autocreate the layout (defaults to {tag: 'div', cls: 'x-form-ct'})
29503 * @cfg {String/Object/Function} style
29504 * A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
29505 * a function which returns such a specification.
29508 * @cfg {String} labelAlign
29509 * Valid values are "left," "top" and "right" (defaults to "left")
29512 * @cfg {Number} labelWidth
29513 * Fixed width in pixels of all field labels (defaults to undefined)
29516 * @cfg {Boolean} clear
29517 * True to add a clearing element at the end of this layout, equivalent to CSS clear: both (defaults to true)
29521 * @cfg {String} labelSeparator
29522 * The separator to use after field labels (defaults to ':')
29524 labelSeparator : ':',
29526 * @cfg {Boolean} hideLabels
29527 * True to suppress the display of field labels in this layout (defaults to false)
29529 hideLabels : false,
29532 defaultAutoCreate : {tag: 'div', cls: 'x-form-ct'},
29537 onRender : function(ct, position){
29538 if(this.el){ // from markup
29539 this.el = Roo.get(this.el);
29540 }else { // generate
29541 var cfg = this.getAutoCreate();
29542 this.el = ct.createChild(cfg, position);
29545 this.el.applyStyles(this.style);
29547 if(this.labelAlign){
29548 this.el.addClass('x-form-label-'+this.labelAlign);
29550 if(this.hideLabels){
29551 this.labelStyle = "display:none";
29552 this.elementStyle = "padding-left:0;";
29554 if(typeof this.labelWidth == 'number'){
29555 this.labelStyle = "width:"+this.labelWidth+"px;";
29556 this.elementStyle = "padding-left:"+((this.labelWidth+(typeof this.labelPad == 'number' ? this.labelPad : 5))+'px')+";";
29558 if(this.labelAlign == 'top'){
29559 this.labelStyle = "width:auto;";
29560 this.elementStyle = "padding-left:0;";
29563 var stack = this.stack;
29564 var slen = stack.length;
29566 if(!this.fieldTpl){
29567 var t = new Roo.Template(
29568 '<div class="x-form-item {5}">',
29569 '<label for="{0}" style="{2}">{1}{4}</label>',
29570 '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
29572 '</div><div class="x-form-clear-left"></div>'
29574 t.disableFormats = true;
29576 Roo.form.Layout.prototype.fieldTpl = t;
29578 for(var i = 0; i < slen; i++) {
29579 if(stack[i].isFormField){
29580 this.renderField(stack[i]);
29582 this.renderComponent(stack[i]);
29587 this.el.createChild({cls:'x-form-clear'});
29592 renderField : function(f){
29593 f.fieldEl = Roo.get(this.fieldTpl.append(this.el, [
29596 f.labelStyle||this.labelStyle||'', //2
29597 this.elementStyle||'', //3
29598 typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator, //4
29599 f.itemCls||this.itemCls||'' //5
29600 ], true).getPrevSibling());
29604 renderComponent : function(c){
29605 c.render(c.isLayout ? this.el : this.el.createChild());
29608 * Adds a object form elements (using the xtype property as the factory method.)
29609 * Valid xtypes are: TextField, TextArea .... Button, Layout, FieldSet, Column
29610 * @param {Object} config
29612 addxtype : function(o)
29614 // create the lement.
29615 o.form = this.form;
29616 var fe = Roo.factory(o, Roo.form);
29617 this.form.allItems.push(fe);
29618 this.stack.push(fe);
29620 if (fe.isFormField) {
29621 this.form.items.add(fe);
29629 * @class Roo.form.Column
29630 * @extends Roo.form.Layout
29631 * Creates a column container for layout and rendering of fields in an {@link Roo.form.Form}.
29633 * @param {Object} config Configuration options
29635 Roo.form.Column = function(config){
29636 Roo.form.Column.superclass.constructor.call(this, config);
29639 Roo.extend(Roo.form.Column, Roo.form.Layout, {
29641 * @cfg {Number/String} width
29642 * The fixed width of the column in pixels or CSS value (defaults to "auto")
29645 * @cfg {String/Object} autoCreate
29646 * A DomHelper element spec used to autocreate the column (defaults to {tag: 'div', cls: 'x-form-ct x-form-column'})
29650 defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-column'},
29653 onRender : function(ct, position){
29654 Roo.form.Column.superclass.onRender.call(this, ct, position);
29656 this.el.setWidth(this.width);
29663 * @class Roo.form.Row
29664 * @extends Roo.form.Layout
29665 * Creates a row container for layout and rendering of fields in an {@link Roo.form.Form}.
29667 * @param {Object} config Configuration options
29671 Roo.form.Row = function(config){
29672 Roo.form.Row.superclass.constructor.call(this, config);
29675 Roo.extend(Roo.form.Row, Roo.form.Layout, {
29677 * @cfg {Number/String} width
29678 * The fixed width of the column in pixels or CSS value (defaults to "auto")
29681 * @cfg {Number/String} height
29682 * The fixed height of the column in pixels or CSS value (defaults to "auto")
29684 defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-row'},
29688 onRender : function(ct, position){
29689 //console.log('row render');
29691 var t = new Roo.Template(
29692 '<div class="x-form-item {5}" style="float:left;width:{6}px">',
29693 '<label for="{0}" style="{2}">{1}{4}</label>',
29694 '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
29698 t.disableFormats = true;
29700 Roo.form.Layout.prototype.rowTpl = t;
29702 this.fieldTpl = this.rowTpl;
29704 //console.log('lw' + this.labelWidth +', la:' + this.labelAlign);
29705 var labelWidth = 100;
29707 if ((this.labelAlign != 'top')) {
29708 if (typeof this.labelWidth == 'number') {
29709 labelWidth = this.labelWidth
29711 this.padWidth = 20 + labelWidth;
29715 Roo.form.Column.superclass.onRender.call(this, ct, position);
29717 this.el.setWidth(this.width);
29720 this.el.setHeight(this.height);
29725 renderField : function(f){
29726 f.fieldEl = this.fieldTpl.append(this.el, [
29727 f.id, f.fieldLabel,
29728 f.labelStyle||this.labelStyle||'',
29729 this.elementStyle||'',
29730 typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator,
29731 f.itemCls||this.itemCls||'',
29732 f.width ? f.width + this.padWidth : 160 + this.padWidth
29739 * @class Roo.form.FieldSet
29740 * @extends Roo.form.Layout
29741 * Creates a fieldset container for layout and rendering of fields in an {@link Roo.form.Form}.
29743 * @param {Object} config Configuration options
29745 Roo.form.FieldSet = function(config){
29746 Roo.form.FieldSet.superclass.constructor.call(this, config);
29749 Roo.extend(Roo.form.FieldSet, Roo.form.Layout, {
29751 * @cfg {String} legend
29752 * The text to display as the legend for the FieldSet (defaults to '')
29755 * @cfg {String/Object} autoCreate
29756 * A DomHelper element spec used to autocreate the fieldset (defaults to {tag: 'fieldset', cn: {tag:'legend'}})
29760 defaultAutoCreate : {tag: 'fieldset', cn: {tag:'legend'}},
29763 onRender : function(ct, position){
29764 Roo.form.FieldSet.superclass.onRender.call(this, ct, position);
29766 this.setLegend(this.legend);
29771 setLegend : function(text){
29773 this.el.child('legend').update(text);
29778 * Ext JS Library 1.1.1
29779 * Copyright(c) 2006-2007, Ext JS, LLC.
29781 * Originally Released Under LGPL - original licence link has changed is not relivant.
29784 * <script type="text/javascript">
29787 * @class Roo.form.VTypes
29788 * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
29791 Roo.form.VTypes = function(){
29792 // closure these in so they are only created once.
29793 var alpha = /^[a-zA-Z_]+$/;
29794 var alphanum = /^[a-zA-Z0-9_]+$/;
29795 var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,4}$/;
29796 var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
29798 // All these messages and functions are configurable
29801 * The function used to validate email addresses
29802 * @param {String} value The email address
29804 'email' : function(v){
29805 return email.test(v);
29808 * The error text to display when the email validation function returns false
29811 'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
29813 * The keystroke filter mask to be applied on email input
29816 'emailMask' : /[a-z0-9_\.\-@]/i,
29819 * The function used to validate URLs
29820 * @param {String} value The URL
29822 'url' : function(v){
29823 return url.test(v);
29826 * The error text to display when the url validation function returns false
29829 'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
29832 * The function used to validate alpha values
29833 * @param {String} value The value
29835 'alpha' : function(v){
29836 return alpha.test(v);
29839 * The error text to display when the alpha validation function returns false
29842 'alphaText' : 'This field should only contain letters and _',
29844 * The keystroke filter mask to be applied on alpha input
29847 'alphaMask' : /[a-z_]/i,
29850 * The function used to validate alphanumeric values
29851 * @param {String} value The value
29853 'alphanum' : function(v){
29854 return alphanum.test(v);
29857 * The error text to display when the alphanumeric validation function returns false
29860 'alphanumText' : 'This field should only contain letters, numbers and _',
29862 * The keystroke filter mask to be applied on alphanumeric input
29865 'alphanumMask' : /[a-z0-9_]/i
29867 }();//<script type="text/javascript">
29870 * @class Roo.form.FCKeditor
29871 * @extends Roo.form.TextArea
29872 * Wrapper around the FCKEditor http://www.fckeditor.net
29874 * Creates a new FCKeditor
29875 * @param {Object} config Configuration options
29877 Roo.form.FCKeditor = function(config){
29878 Roo.form.FCKeditor.superclass.constructor.call(this, config);
29881 * @event editorinit
29882 * Fired when the editor is initialized - you can add extra handlers here..
29883 * @param {FCKeditor} this
29884 * @param {Object} the FCK object.
29891 Roo.form.FCKeditor.editors = { };
29892 Roo.extend(Roo.form.FCKeditor, Roo.form.TextArea,
29894 //defaultAutoCreate : {
29895 // tag : "textarea",style : "width:100px;height:60px;" ,autocomplete : "off"
29899 * @cfg {Object} fck options - see fck manual for details.
29904 * @cfg {Object} fck toolbar set (Basic or Default)
29906 toolbarSet : 'Basic',
29908 * @cfg {Object} fck BasePath
29910 basePath : '/fckeditor/',
29918 onRender : function(ct, position)
29921 this.defaultAutoCreate = {
29923 style:"width:300px;height:60px;",
29924 autocomplete: "off"
29927 Roo.form.FCKeditor.superclass.onRender.call(this, ct, position);
29930 this.textSizeEl = Roo.DomHelper.append(document.body, {tag: "pre", cls: "x-form-grow-sizer"});
29931 if(this.preventScrollbars){
29932 this.el.setStyle("overflow", "hidden");
29934 this.el.setHeight(this.growMin);
29937 //console.log('onrender' + this.getId() );
29938 Roo.form.FCKeditor.editors[this.getId()] = this;
29941 this.replaceTextarea() ;
29945 getEditor : function() {
29946 return this.fckEditor;
29949 * Sets a data value into the field and validates it. To set the value directly without validation see {@link #setRawValue}.
29950 * @param {Mixed} value The value to set
29954 setValue : function(value)
29956 //console.log('setValue: ' + value);
29958 if(typeof(value) == 'undefined') { // not sure why this is happending...
29961 Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
29963 //if(!this.el || !this.getEditor()) {
29964 // this.value = value;
29965 //this.setValue.defer(100,this,[value]);
29969 if(!this.getEditor()) {
29973 this.getEditor().SetData(value);
29980 * Returns the normalized data value (undefined or emptyText will be returned as ''). To return the raw value see {@link #getRawValue}.
29981 * @return {Mixed} value The field value
29983 getValue : function()
29986 if (this.frame && this.frame.dom.style.display == 'none') {
29987 return Roo.form.FCKeditor.superclass.getValue.call(this);
29990 if(!this.el || !this.getEditor()) {
29992 // this.getValue.defer(100,this);
29997 var value=this.getEditor().GetData();
29998 Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
29999 return Roo.form.FCKeditor.superclass.getValue.call(this);
30005 * Returns the raw data value which may or may not be a valid, defined value. To return a normalized value see {@link #getValue}.
30006 * @return {Mixed} value The field value
30008 getRawValue : function()
30010 if (this.frame && this.frame.dom.style.display == 'none') {
30011 return Roo.form.FCKeditor.superclass.getRawValue.call(this);
30014 if(!this.el || !this.getEditor()) {
30015 //this.getRawValue.defer(100,this);
30022 var value=this.getEditor().GetData();
30023 Roo.form.FCKeditor.superclass.setRawValue.apply(this,[value]);
30024 return Roo.form.FCKeditor.superclass.getRawValue.call(this);
30028 setSize : function(w,h) {
30032 //if (this.frame && this.frame.dom.style.display == 'none') {
30033 // Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
30036 //if(!this.el || !this.getEditor()) {
30037 // this.setSize.defer(100,this, [w,h]);
30043 Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
30045 this.frame.dom.setAttribute('width', w);
30046 this.frame.dom.setAttribute('height', h);
30047 this.frame.setSize(w,h);
30051 toggleSourceEdit : function(value) {
30055 this.el.dom.style.display = value ? '' : 'none';
30056 this.frame.dom.style.display = value ? 'none' : '';
30061 focus: function(tag)
30063 if (this.frame.dom.style.display == 'none') {
30064 return Roo.form.FCKeditor.superclass.focus.call(this);
30066 if(!this.el || !this.getEditor()) {
30067 this.focus.defer(100,this, [tag]);
30074 var tgs = this.getEditor().EditorDocument.getElementsByTagName(tag);
30075 this.getEditor().Focus();
30077 if (!this.getEditor().Selection.GetSelection()) {
30078 this.focus.defer(100,this, [tag]);
30083 var r = this.getEditor().EditorDocument.createRange();
30084 r.setStart(tgs[0],0);
30085 r.setEnd(tgs[0],0);
30086 this.getEditor().Selection.GetSelection().removeAllRanges();
30087 this.getEditor().Selection.GetSelection().addRange(r);
30088 this.getEditor().Focus();
30095 replaceTextarea : function()
30097 if ( document.getElementById( this.getId() + '___Frame' ) )
30099 //if ( !this.checkBrowser || this._isCompatibleBrowser() )
30101 // We must check the elements firstly using the Id and then the name.
30102 var oTextarea = document.getElementById( this.getId() );
30104 var colElementsByName = document.getElementsByName( this.getId() ) ;
30106 oTextarea.style.display = 'none' ;
30108 if ( oTextarea.tabIndex ) {
30109 this.TabIndex = oTextarea.tabIndex ;
30112 this._insertHtmlBefore( this._getConfigHtml(), oTextarea ) ;
30113 this._insertHtmlBefore( this._getIFrameHtml(), oTextarea ) ;
30114 this.frame = Roo.get(this.getId() + '___Frame')
30117 _getConfigHtml : function()
30121 for ( var o in this.fckconfig ) {
30122 sConfig += sConfig.length > 0 ? '&' : '';
30123 sConfig += encodeURIComponent( o ) + '=' + encodeURIComponent( this.fckconfig[o] ) ;
30126 return '<input type="hidden" id="' + this.getId() + '___Config" value="' + sConfig + '" style="display:none" />' ;
30130 _getIFrameHtml : function()
30132 var sFile = 'fckeditor.html' ;
30133 /* no idea what this is about..
30136 if ( (/fcksource=true/i).test( window.top.location.search ) )
30137 sFile = 'fckeditor.original.html' ;
30142 var sLink = this.basePath + 'editor/' + sFile + '?InstanceName=' + encodeURIComponent( this.getId() ) ;
30143 sLink += this.toolbarSet ? ( '&Toolbar=' + this.toolbarSet) : '';
30146 var html = '<iframe id="' + this.getId() +
30147 '___Frame" src="' + sLink +
30148 '" width="' + this.width +
30149 '" height="' + this.height + '"' +
30150 (this.tabIndex ? ' tabindex="' + this.tabIndex + '"' :'' ) +
30151 ' frameborder="0" scrolling="no"></iframe>' ;
30156 _insertHtmlBefore : function( html, element )
30158 if ( element.insertAdjacentHTML ) {
30160 element.insertAdjacentHTML( 'beforeBegin', html ) ;
30162 var oRange = document.createRange() ;
30163 oRange.setStartBefore( element ) ;
30164 var oFragment = oRange.createContextualFragment( html );
30165 element.parentNode.insertBefore( oFragment, element ) ;
30178 //Roo.reg('fckeditor', Roo.form.FCKeditor);
30180 function FCKeditor_OnComplete(editorInstance){
30181 var f = Roo.form.FCKeditor.editors[editorInstance.Name];
30182 f.fckEditor = editorInstance;
30183 //console.log("loaded");
30184 f.fireEvent('editorinit', f, editorInstance);
30204 //<script type="text/javascript">
30206 * @class Roo.form.GridField
30207 * @extends Roo.form.Field
30208 * Embed a grid (or editable grid into a form)
30211 * This embeds a grid in a form, the value of the field should be the json encoded array of rows
30213 * xgrid.store = Roo.data.Store
30214 * xgrid.store.proxy = Roo.data.MemoryProxy (data = [] )
30215 * xgrid.store.reader = Roo.data.JsonReader
30219 * Creates a new GridField
30220 * @param {Object} config Configuration options
30222 Roo.form.GridField = function(config){
30223 Roo.form.GridField.superclass.constructor.call(this, config);
30227 Roo.extend(Roo.form.GridField, Roo.form.Field, {
30229 * @cfg {Number} width - used to restrict width of grid..
30233 * @cfg {Number} height - used to restrict height of grid..
30237 * @cfg {Object} xgrid (xtype'd description of grid) { xtype : 'Grid', dataSource: .... }
30243 * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
30244 * {tag: "input", type: "checkbox", autocomplete: "off"})
30246 // defaultAutoCreate : { tag: 'div' },
30247 defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'off'},
30249 * @cfg {String} addTitle Text to include for adding a title.
30253 onResize : function(){
30254 Roo.form.Field.superclass.onResize.apply(this, arguments);
30257 initEvents : function(){
30258 // Roo.form.Checkbox.superclass.initEvents.call(this);
30259 // has no events...
30264 getResizeEl : function(){
30268 getPositionEl : function(){
30273 onRender : function(ct, position){
30275 this.style = this.style || 'overflow: hidden; border:1px solid #c3daf9;';
30276 var style = this.style;
30279 Roo.form.GridField.superclass.onRender.call(this, ct, position);
30280 this.wrap = this.el.wrap({cls: ''}); // not sure why ive done thsi...
30281 this.viewEl = this.wrap.createChild({ tag: 'div' });
30283 this.viewEl.applyStyles(style);
30286 this.viewEl.setWidth(this.width);
30289 this.viewEl.setHeight(this.height);
30291 //if(this.inputValue !== undefined){
30292 //this.setValue(this.value);
30295 this.grid = new Roo.grid[this.xgrid.xtype](this.viewEl, this.xgrid);
30298 this.grid.render();
30299 this.grid.getDataSource().on('remove', this.refreshValue, this);
30300 this.grid.getDataSource().on('update', this.refreshValue, this);
30301 this.grid.on('afteredit', this.refreshValue, this);
30307 * Sets the value of the item.
30308 * @param {String} either an object or a string..
30310 setValue : function(v){
30312 v = v || []; // empty set..
30313 // this does not seem smart - it really only affects memoryproxy grids..
30314 if (this.grid && this.grid.getDataSource() && typeof(v) != 'undefined') {
30315 var ds = this.grid.getDataSource();
30316 // assumes a json reader..
30318 data[ds.reader.meta.root ] = typeof(v) == 'string' ? Roo.decode(v) : v;
30319 ds.loadData( data);
30321 // clear selection so it does not get stale.
30322 if (this.grid.sm) {
30323 this.grid.sm.clearSelections();
30326 Roo.form.GridField.superclass.setValue.call(this, v);
30327 this.refreshValue();
30328 // should load data in the grid really....
30332 refreshValue: function() {
30334 this.grid.getDataSource().each(function(r) {
30337 this.el.dom.value = Roo.encode(val);
30345 * Ext JS Library 1.1.1
30346 * Copyright(c) 2006-2007, Ext JS, LLC.
30348 * Originally Released Under LGPL - original licence link has changed is not relivant.
30351 * <script type="text/javascript">
30354 * @class Roo.form.DisplayField
30355 * @extends Roo.form.Field
30356 * A generic Field to display non-editable data.
30358 * Creates a new Display Field item.
30359 * @param {Object} config Configuration options
30361 Roo.form.DisplayField = function(config){
30362 Roo.form.DisplayField.superclass.constructor.call(this, config);
30366 Roo.extend(Roo.form.DisplayField, Roo.form.TextField, {
30367 inputType: 'hidden',
30373 * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
30375 focusClass : undefined,
30377 * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
30379 fieldClass: 'x-form-field',
30382 * @cfg {Function} valueRenderer The renderer for the field (so you can reformat output). should return raw HTML
30384 valueRenderer: undefined,
30388 * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
30389 * {tag: "input", type: "checkbox", autocomplete: "off"})
30392 // defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'off'},
30394 onResize : function(){
30395 Roo.form.DisplayField.superclass.onResize.apply(this, arguments);
30399 initEvents : function(){
30400 // Roo.form.Checkbox.superclass.initEvents.call(this);
30401 // has no events...
30406 getResizeEl : function(){
30410 getPositionEl : function(){
30415 onRender : function(ct, position){
30417 Roo.form.DisplayField.superclass.onRender.call(this, ct, position);
30418 //if(this.inputValue !== undefined){
30419 this.wrap = this.el.wrap();
30421 this.viewEl = this.wrap.createChild({ tag: 'div', cls: 'x-form-displayfield'});
30423 if (this.bodyStyle) {
30424 this.viewEl.applyStyles(this.bodyStyle);
30426 //this.viewEl.setStyle('padding', '2px');
30428 this.setValue(this.value);
30433 initValue : Roo.emptyFn,
30438 onClick : function(){
30443 * Sets the checked state of the checkbox.
30444 * @param {Boolean/String} checked True, 'true', '1', or 'on' to check the checkbox, any other value will uncheck it.
30446 setValue : function(v){
30448 var html = this.valueRenderer ? this.valueRenderer(v) : String.format('{0}', v);
30449 // this might be called before we have a dom element..
30450 if (!this.viewEl) {
30453 this.viewEl.dom.innerHTML = html;
30454 Roo.form.DisplayField.superclass.setValue.call(this, v);
30464 * @class Roo.form.DayPicker
30465 * @extends Roo.form.Field
30466 * A Day picker show [M] [T] [W] ....
30468 * Creates a new Day Picker
30469 * @param {Object} config Configuration options
30471 Roo.form.DayPicker= function(config){
30472 Roo.form.DayPicker.superclass.constructor.call(this, config);
30476 Roo.extend(Roo.form.DayPicker, Roo.form.Field, {
30478 * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
30480 focusClass : undefined,
30482 * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
30484 fieldClass: "x-form-field",
30487 * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
30488 * {tag: "input", type: "checkbox", autocomplete: "off"})
30490 defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "off"},
30493 actionMode : 'viewEl',
30497 inputType : 'hidden',
30500 inputElement: false, // real input element?
30501 basedOn: false, // ????
30503 isFormField: true, // not sure where this is needed!!!!
30505 onResize : function(){
30506 Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
30507 if(!this.boxLabel){
30508 this.el.alignTo(this.wrap, 'c-c');
30512 initEvents : function(){
30513 Roo.form.Checkbox.superclass.initEvents.call(this);
30514 this.el.on("click", this.onClick, this);
30515 this.el.on("change", this.onClick, this);
30519 getResizeEl : function(){
30523 getPositionEl : function(){
30529 onRender : function(ct, position){
30530 Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
30532 this.wrap = this.el.wrap({cls: 'x-form-daypick-item '});
30534 var r1 = '<table><tr>';
30535 var r2 = '<tr class="x-form-daypick-icons">';
30536 for (var i=0; i < 7; i++) {
30537 r1+= '<td><div>' + Date.dayNames[i].substring(0,3) + '</div></td>';
30538 r2+= '<td><img class="x-menu-item-icon" src="' + Roo.BLANK_IMAGE_URL +'"></td>';
30541 var viewEl = this.wrap.createChild( r1 + '</tr>' + r2 + '</tr></table>');
30542 viewEl.select('img').on('click', this.onClick, this);
30543 this.viewEl = viewEl;
30546 // this will not work on Chrome!!!
30547 this.el.on('DOMAttrModified', this.setFromHidden, this); //ff
30548 this.el.on('propertychange', this.setFromHidden, this); //ie
30556 initValue : Roo.emptyFn,
30559 * Returns the checked state of the checkbox.
30560 * @return {Boolean} True if checked, else false
30562 getValue : function(){
30563 return this.el.dom.value;
30568 onClick : function(e){
30569 //this.setChecked(!this.checked);
30570 Roo.get(e.target).toggleClass('x-menu-item-checked');
30571 this.refreshValue();
30572 //if(this.el.dom.checked != this.checked){
30573 // this.setValue(this.el.dom.checked);
30578 refreshValue : function()
30581 this.viewEl.select('img',true).each(function(e,i,n) {
30582 val += e.is(".x-menu-item-checked") ? String(n) : '';
30584 this.setValue(val, true);
30588 * Sets the checked state of the checkbox.
30589 * On is always based on a string comparison between inputValue and the param.
30590 * @param {Boolean/String} value - the value to set
30591 * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
30593 setValue : function(v,suppressEvent){
30594 if (!this.el.dom) {
30597 var old = this.el.dom.value ;
30598 this.el.dom.value = v;
30599 if (suppressEvent) {
30603 // update display..
30604 this.viewEl.select('img',true).each(function(e,i,n) {
30606 var on = e.is(".x-menu-item-checked");
30607 var newv = v.indexOf(String(n)) > -1;
30609 e.toggleClass('x-menu-item-checked');
30615 this.fireEvent('change', this, v, old);
30620 // handle setting of hidden value by some other method!!?!?
30621 setFromHidden: function()
30626 //console.log("SET FROM HIDDEN");
30627 //alert('setFrom hidden');
30628 this.setValue(this.el.dom.value);
30631 onDestroy : function()
30634 Roo.get(this.viewEl).remove();
30637 Roo.form.DayPicker.superclass.onDestroy.call(this);
30641 * RooJS Library 1.1.1
30642 * Copyright(c) 2008-2011 Alan Knowles
30649 * @class Roo.form.ComboCheck
30650 * @extends Roo.form.ComboBox
30651 * A combobox for multiple select items.
30653 * FIXME - could do with a reset button..
30656 * Create a new ComboCheck
30657 * @param {Object} config Configuration options
30659 Roo.form.ComboCheck = function(config){
30660 Roo.form.ComboCheck.superclass.constructor.call(this, config);
30661 // should verify some data...
30663 // hiddenName = required..
30664 // displayField = required
30665 // valudField == required
30666 var req= [ 'hiddenName', 'displayField', 'valueField' ];
30668 Roo.each(req, function(e) {
30669 if ((typeof(_t[e]) == 'undefined' ) || !_t[e].length) {
30670 throw "Roo.form.ComboCheck : missing value for: " + e;
30677 Roo.extend(Roo.form.ComboCheck, Roo.form.ComboBox, {
30682 selectedClass: 'x-menu-item-checked',
30685 onRender : function(ct, position){
30691 var cls = 'x-combo-list';
30694 this.tpl = new Roo.Template({
30695 html : '<div class="'+cls+'-item x-menu-check-item">' +
30696 '<img class="x-menu-item-icon" style="margin: 0px;" src="' + Roo.BLANK_IMAGE_URL + '">' +
30697 '<span>{' + this.displayField + '}</span>' +
30704 Roo.form.ComboCheck.superclass.onRender.call(this, ct, position);
30705 this.view.singleSelect = false;
30706 this.view.multiSelect = true;
30707 this.view.toggleSelect = true;
30708 this.pageTb.add(new Roo.Toolbar.Fill(), {
30711 handler: function()
30718 onViewOver : function(e, t){
30724 onViewClick : function(doFocus,index){
30728 select: function () {
30729 //Roo.log("SELECT CALLED");
30732 selectByValue : function(xv, scrollIntoView){
30733 var ar = this.getValueArray();
30736 Roo.each(ar, function(v) {
30737 if(v === undefined || v === null){
30740 var r = this.findRecord(this.valueField, v);
30742 sels.push(this.store.indexOf(r))
30746 this.view.select(sels);
30752 onSelect : function(record, index){
30753 // Roo.log("onselect Called");
30754 // this is only called by the clear button now..
30755 this.view.clearSelections();
30756 this.setValue('[]');
30757 if (this.value != this.valueBefore) {
30758 this.fireEvent('change', this, this.value, this.valueBefore);
30759 this.valueBefore = this.value;
30762 getValueArray : function()
30767 //Roo.log(this.value);
30768 if (typeof(this.value) == 'undefined') {
30771 var ar = Roo.decode(this.value);
30772 return ar instanceof Array ? ar : []; //?? valid?
30775 Roo.log(e + "\nRoo.form.ComboCheck:getValueArray invalid data:" + this.getValue());
30780 expand : function ()
30783 Roo.form.ComboCheck.superclass.expand.call(this);
30784 this.valueBefore = typeof(this.value) == 'undefined' ? '' : this.value;
30785 //this.valueBefore = typeof(this.valueBefore) == 'undefined' ? '' : this.valueBefore;
30790 collapse : function(){
30791 Roo.form.ComboCheck.superclass.collapse.call(this);
30792 var sl = this.view.getSelectedIndexes();
30793 var st = this.store;
30797 Roo.each(sl, function(i) {
30799 nv.push(r.get(this.valueField));
30801 this.setValue(Roo.encode(nv));
30802 if (this.value != this.valueBefore) {
30804 this.fireEvent('change', this, this.value, this.valueBefore);
30805 this.valueBefore = this.value;
30810 setValue : function(v){
30814 var vals = this.getValueArray();
30816 Roo.each(vals, function(k) {
30817 var r = this.findRecord(this.valueField, k);
30819 tv.push(r.data[this.displayField]);
30820 }else if(this.valueNotFoundText !== undefined){
30821 tv.push( this.valueNotFoundText );
30826 Roo.form.ComboBox.superclass.setValue.call(this, tv.join(', '));
30827 this.hiddenField.value = v;
30833 * Ext JS Library 1.1.1
30834 * Copyright(c) 2006-2007, Ext JS, LLC.
30836 * Originally Released Under LGPL - original licence link has changed is not relivant.
30839 * <script type="text/javascript">
30843 * @class Roo.form.Signature
30844 * @extends Roo.form.Field
30848 * @param {Object} config Configuration options
30851 Roo.form.Signature = function(config){
30852 Roo.form.Signature.superclass.constructor.call(this, config);
30854 this.addEvents({// not in used??
30857 * Fires when the 'confirm' icon is pressed (add a listener to enable add button)
30858 * @param {Roo.form.Signature} combo This combo box
30863 * Fires when the 'edit' icon is pressed (add a listener to enable add button)
30864 * @param {Roo.form.ComboBox} combo This combo box
30865 * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
30871 Roo.extend(Roo.form.Signature, Roo.form.Field, {
30873 * @cfg {Object} labels Label to use when rendering a form.
30877 * confirm : "Confirm"
30882 confirm : "Confirm"
30885 * @cfg {Number} width The signature panel width (defaults to 300)
30889 * @cfg {Number} height The signature panel height (defaults to 100)
30893 * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to false)
30895 allowBlank : false,
30898 // {Object} signPanel The signature SVG panel element (defaults to {})
30900 // {Boolean} isMouseDown False to validate that the mouse down event (defaults to false)
30901 isMouseDown : false,
30902 // {Boolean} isConfirmed validate the signature is confirmed or not for submitting form (defaults to false)
30903 isConfirmed : false,
30904 // {String} signatureTmp SVG mapping string (defaults to empty string)
30908 defaultAutoCreate : { // modified by initCompnoent..
30914 onRender : function(ct, position){
30916 Roo.form.Signature.superclass.onRender.call(this, ct, position);
30918 this.wrap = this.el.wrap({
30919 cls:'x-form-signature-wrap', style : 'width: ' + this.width + 'px', cn:{cls:'x-form-signature'}
30922 this.createToolbar(this);
30923 this.signPanel = this.wrap.createChild({
30925 style: 'width: ' + this.width + 'px; height: ' + this.height + 'px; border: 0;'
30929 this.svgID = Roo.id();
30930 this.svgEl = this.signPanel.createChild({
30931 xmlns : 'http://www.w3.org/2000/svg',
30933 id : this.svgID + "-svg",
30935 height: this.height,
30936 viewBox: '0 0 '+this.width+' '+this.height,
30940 id: this.svgID + "-svg-r",
30942 height: this.height,
30947 id: this.svgID + "-svg-l",
30949 y1: (this.height*0.8), // start set the line in 80% of height
30950 x2: this.width, // end
30951 y2: (this.height*0.8), // end set the line in 80% of height
30953 'stroke-width': "1",
30954 'stroke-dasharray': "3",
30955 'shape-rendering': "crispEdges",
30956 'pointer-events': "none"
30960 id: this.svgID + "-svg-p",
30962 'stroke-width': "3",
30964 'pointer-events': 'none'
30969 this.svgBox = this.svgEl.dom.getScreenCTM();
30971 createSVG : function(){
30972 var svg = this.signPanel;
30973 var r = svg.select('#'+ this.svgID + '-svg-r', true).first().dom;
30976 r.addEventListener('mousedown', function(e) { return t.down(e); }, false);
30977 r.addEventListener('mousemove', function(e) { return t.move(e); }, false);
30978 r.addEventListener('mouseup', function(e) { return t.up(e); }, false);
30979 r.addEventListener('mouseout', function(e) { return t.up(e); }, false);
30980 r.addEventListener('touchstart', function(e) { return t.down(e); }, false);
30981 r.addEventListener('touchmove', function(e) { return t.move(e); }, false);
30982 r.addEventListener('touchend', function(e) { return t.up(e); }, false);
30985 isTouchEvent : function(e){
30986 return e.type.match(/^touch/);
30988 getCoords : function (e) {
30989 var pt = this.svgEl.dom.createSVGPoint();
30992 if (this.isTouchEvent(e)) {
30993 pt.x = e.targetTouches[0].clientX
30994 pt.y = e.targetTouches[0].clientY;
30996 var a = this.svgEl.dom.getScreenCTM();
30997 var b = a.inverse();
30998 var mx = pt.matrixTransform(b);
30999 return mx.x + ',' + mx.y;
31001 //mouse event headler
31002 down : function (e) {
31003 this.signatureTmp += 'M' + this.getCoords(e) + ' ';
31004 this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr('d', this.signatureTmp);
31006 this.isMouseDown = true;
31008 e.preventDefault();
31010 move : function (e) {
31011 if (this.isMouseDown) {
31012 this.signatureTmp += 'L' + this.getCoords(e) + ' ';
31013 this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', this.signatureTmp);
31016 e.preventDefault();
31018 up : function (e) {
31019 this.isMouseDown = false;
31020 var sp = this.signatureTmp.split(' ');
31023 if(!sp[sp.length-2].match(/^L/)){
31027 this.signatureTmp = sp.join(" ");
31030 if(this.getValue() != this.signatureTmp){
31031 this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
31032 this.isConfirmed = false;
31034 e.preventDefault();
31038 * Protected method that will not generally be called directly. It
31039 * is called when the editor creates its toolbar. Override this method if you need to
31040 * add custom toolbar buttons.
31041 * @param {HtmlEditor} editor
31043 createToolbar : function(editor){
31044 function btn(id, toggle, handler){
31045 var xid = fid + '-'+ id ;
31049 cls : 'x-btn-icon x-edit-'+id,
31050 enableToggle:toggle !== false,
31051 scope: editor, // was editor...
31052 handler:handler||editor.relayBtnCmd,
31053 clickEvent:'mousedown',
31054 tooltip: etb.buttonTips[id] || undefined, ///tips ???
31060 var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
31064 cls : ' x-signature-btn x-signature-'+id,
31065 scope: editor, // was editor...
31066 handler: this.reset,
31067 clickEvent:'mousedown',
31068 text: this.labels.clear
31075 cls : ' x-signature-btn x-signature-'+id,
31076 scope: editor, // was editor...
31077 handler: this.confirmHandler,
31078 clickEvent:'mousedown',
31079 text: this.labels.confirm
31086 * when user is clicked confirm then show this image.....
31088 * @return {String} Image Data URI
31090 getImageDataURI : function(){
31091 var svg = this.svgEl.dom.parentNode.innerHTML;
31092 var src = 'data:image/svg+xml;base64,'+window.btoa(svg);
31097 * @return {Boolean} this.isConfirmed
31099 getConfirmed : function(){
31100 return this.isConfirmed;
31104 * @return {Number} this.width
31106 getWidth : function(){
31111 * @return {Number} this.height
31113 getHeight : function(){
31114 return this.height;
31117 getSignature : function(){
31118 return this.signatureTmp;
31121 reset : function(){
31122 this.signatureTmp = '';
31123 this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
31124 this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', '');
31125 this.isConfirmed = false;
31126 Roo.form.Signature.superclass.reset.call(this);
31128 setSignature : function(s){
31129 this.signatureTmp = s;
31130 this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
31131 this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', s);
31133 this.isConfirmed = false;
31134 Roo.form.Signature.superclass.reset.call(this);
31137 // Roo.log(this.signPanel.dom.contentWindow.up())
31140 setConfirmed : function(){
31144 // Roo.log(Roo.get(this.signPanel.dom.contentWindow.r).attr('fill', '#cfc'));
31147 confirmHandler : function(){
31148 if(!this.getSignature()){
31152 this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#cfc');
31153 this.setValue(this.getSignature());
31154 this.isConfirmed = true;
31156 this.fireEvent('confirm', this);
31159 // Subclasses should provide the validation implementation by overriding this
31160 validateValue : function(value){
31161 if(this.allowBlank){
31165 if(this.isConfirmed){
31172 * Ext JS Library 1.1.1
31173 * Copyright(c) 2006-2007, Ext JS, LLC.
31175 * Originally Released Under LGPL - original licence link has changed is not relivant.
31178 * <script type="text/javascript">
31183 * @class Roo.form.ComboBox
31184 * @extends Roo.form.TriggerField
31185 * A combobox control with support for autocomplete, remote-loading, paging and many other features.
31187 * Create a new ComboBox.
31188 * @param {Object} config Configuration options
31190 Roo.form.Select = function(config){
31191 Roo.form.Select.superclass.constructor.call(this, config);
31195 Roo.extend(Roo.form.Select , Roo.form.ComboBox, {
31197 * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
31200 * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
31201 * rendering into an Roo.Editor, defaults to false)
31204 * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
31205 * {tag: "input", type: "text", size: "24", autocomplete: "off"})
31208 * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
31211 * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
31212 * the dropdown list (defaults to undefined, with no header element)
31216 * @cfg {String/Roo.Template} tpl The template to use to render the output
31220 defaultAutoCreate : {tag: "select" },
31222 * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
31224 listWidth: undefined,
31226 * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
31227 * mode = 'remote' or 'text' if mode = 'local')
31229 displayField: undefined,
31231 * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
31232 * mode = 'remote' or 'value' if mode = 'local').
31233 * Note: use of a valueField requires the user make a selection
31234 * in order for a value to be mapped.
31236 valueField: undefined,
31240 * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
31241 * field's data value (defaults to the underlying DOM element's name)
31243 hiddenName: undefined,
31245 * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
31249 * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
31251 selectedClass: 'x-combo-selected',
31253 * @cfg {String} triggerClass An additional CSS class used to style the trigger button. The trigger will always get the
31254 * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
31255 * which displays a downward arrow icon).
31257 triggerClass : 'x-form-arrow-trigger',
31259 * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
31263 * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
31264 * anchor positions (defaults to 'tl-bl')
31266 listAlign: 'tl-bl?',
31268 * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
31272 * @cfg {String} triggerAction The action to execute when the trigger field is activated. Use 'all' to run the
31273 * query specified by the allQuery config option (defaults to 'query')
31275 triggerAction: 'query',
31277 * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
31278 * (defaults to 4, does not apply if editable = false)
31282 * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
31283 * delay (typeAheadDelay) if it matches a known value (defaults to false)
31287 * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
31288 * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
31292 * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
31293 * filter queries will execute with page start and limit parameters. Only applies when mode = 'remote' (defaults to 0)
31297 * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus. Only applies
31298 * when editable = true (defaults to false)
31300 selectOnFocus:false,
31302 * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
31304 queryParam: 'query',
31306 * @cfg {String} loadingText The text to display in the dropdown list while data is loading. Only applies
31307 * when mode = 'remote' (defaults to 'Loading...')
31309 loadingText: 'Loading...',
31311 * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
31315 * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
31319 * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
31320 * traditional select (defaults to true)
31324 * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
31328 * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
31332 * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
31333 * listWidth has a higher value)
31337 * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
31338 * allow the user to set arbitrary text into the field (defaults to false)
31340 forceSelection:false,
31342 * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
31343 * if typeAhead = true (defaults to 250)
31345 typeAheadDelay : 250,
31347 * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
31348 * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
31350 valueNotFoundText : undefined,
31353 * @cfg {String} defaultValue The value displayed after loading the store.
31358 * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
31360 blockFocus : false,
31363 * @cfg {Boolean} disableClear Disable showing of clear button.
31365 disableClear : false,
31367 * @cfg {Boolean} alwaysQuery Disable caching of results, and always send query
31369 alwaysQuery : false,
31375 // element that contains real text value.. (when hidden is used..)
31378 onRender : function(ct, position){
31379 Roo.form.Field.prototype.onRender.call(this, ct, position);
31382 this.store.on('beforeload', this.onBeforeLoad, this);
31383 this.store.on('load', this.onLoad, this);
31384 this.store.on('loadexception', this.onLoadException, this);
31385 this.store.load({});
31393 initEvents : function(){
31394 //Roo.form.ComboBox.superclass.initEvents.call(this);
31398 onDestroy : function(){
31401 this.store.un('beforeload', this.onBeforeLoad, this);
31402 this.store.un('load', this.onLoad, this);
31403 this.store.un('loadexception', this.onLoadException, this);
31405 //Roo.form.ComboBox.superclass.onDestroy.call(this);
31409 fireKey : function(e){
31410 if(e.isNavKeyPress() && !this.list.isVisible()){
31411 this.fireEvent("specialkey", this, e);
31416 onResize: function(w, h){
31424 * Allow or prevent the user from directly editing the field text. If false is passed,
31425 * the user will only be able to select from the items defined in the dropdown list. This method
31426 * is the runtime equivalent of setting the 'editable' config option at config time.
31427 * @param {Boolean} value True to allow the user to directly edit the field text
31429 setEditable : function(value){
31434 onBeforeLoad : function(){
31436 Roo.log("Select before load");
31439 this.innerList.update(this.loadingText ?
31440 '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
31441 //this.restrictHeight();
31442 this.selectedIndex = -1;
31446 onLoad : function(){
31449 var dom = this.el.dom;
31450 dom.innerHTML = '';
31451 var od = dom.ownerDocument;
31453 if (this.emptyText) {
31454 var op = od.createElement('option');
31455 op.setAttribute('value', '');
31456 op.innerHTML = String.format('{0}', this.emptyText);
31457 dom.appendChild(op);
31459 if(this.store.getCount() > 0){
31461 var vf = this.valueField;
31462 var df = this.displayField;
31463 this.store.data.each(function(r) {
31464 // which colmsn to use... testing - cdoe / title..
31465 var op = od.createElement('option');
31466 op.setAttribute('value', r.data[vf]);
31467 op.innerHTML = String.format('{0}', r.data[df]);
31468 dom.appendChild(op);
31470 if (typeof(this.defaultValue != 'undefined')) {
31471 this.setValue(this.defaultValue);
31476 //this.onEmptyResults();
31481 onLoadException : function()
31483 dom.innerHTML = '';
31485 Roo.log("Select on load exception");
31489 Roo.log(this.store.reader.jsonData);
31490 if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
31491 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
31497 onTypeAhead : function(){
31502 onSelect : function(record, index){
31503 Roo.log('on select?');
31505 if(this.fireEvent('beforeselect', this, record, index) !== false){
31506 this.setFromData(index > -1 ? record.data : false);
31508 this.fireEvent('select', this, record, index);
31513 * Returns the currently selected field value or empty string if no value is set.
31514 * @return {String} value The selected value
31516 getValue : function(){
31517 var dom = this.el.dom;
31518 this.value = dom.options[dom.selectedIndex].value;
31524 * Clears any text/value currently set in the field
31526 clearValue : function(){
31528 this.el.dom.selectedIndex = this.emptyText ? 0 : -1;
31533 * Sets the specified value into the field. If the value finds a match, the corresponding record text
31534 * will be displayed in the field. If the value does not match the data value of an existing item,
31535 * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
31536 * Otherwise the field will be blank (although the value will still be set).
31537 * @param {String} value The value to match
31539 setValue : function(v){
31540 var d = this.el.dom;
31541 for (var i =0; i < d.options.length;i++) {
31542 if (v == d.options[i].value) {
31543 d.selectedIndex = i;
31551 * @property {Object} the last set data for the element
31556 * Sets the value of the field based on a object which is related to the record format for the store.
31557 * @param {Object} value the value to set as. or false on reset?
31559 setFromData : function(o){
31560 Roo.log('setfrom data?');
31566 reset : function(){
31570 findRecord : function(prop, value){
31575 if(this.store.getCount() > 0){
31576 this.store.each(function(r){
31577 if(r.data[prop] == value){
31587 getName: function()
31589 // returns hidden if it's set..
31590 if (!this.rendered) {return ''};
31591 return !this.hiddenName && this.el.dom.name ? this.el.dom.name : (this.hiddenName || '');
31599 onEmptyResults : function(){
31600 Roo.log('empty results');
31605 * Returns true if the dropdown list is expanded, else false.
31607 isExpanded : function(){
31612 * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
31613 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
31614 * @param {String} value The data value of the item to select
31615 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
31616 * selected item if it is not currently in view (defaults to true)
31617 * @return {Boolean} True if the value matched an item in the list, else false
31619 selectByValue : function(v, scrollIntoView){
31620 Roo.log('select By Value');
31623 if(v !== undefined && v !== null){
31624 var r = this.findRecord(this.valueField || this.displayField, v);
31626 this.select(this.store.indexOf(r), scrollIntoView);
31634 * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
31635 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
31636 * @param {Number} index The zero-based index of the list item to select
31637 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
31638 * selected item if it is not currently in view (defaults to true)
31640 select : function(index, scrollIntoView){
31641 Roo.log('select ');
31644 this.selectedIndex = index;
31645 this.view.select(index);
31646 if(scrollIntoView !== false){
31647 var el = this.view.getNode(index);
31649 this.innerList.scrollChildIntoView(el, false);
31657 validateBlur : function(){
31664 initQuery : function(){
31665 this.doQuery(this.getRawValue());
31669 doForce : function(){
31670 if(this.el.dom.value.length > 0){
31671 this.el.dom.value =
31672 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
31678 * Execute a query to filter the dropdown list. Fires the beforequery event prior to performing the
31679 * query allowing the query action to be canceled if needed.
31680 * @param {String} query The SQL query to execute
31681 * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
31682 * in the field than the minimum specified by the minChars config option. It also clears any filter previously
31683 * saved in the current store (defaults to false)
31685 doQuery : function(q, forceAll){
31687 Roo.log('doQuery?');
31688 if(q === undefined || q === null){
31693 forceAll: forceAll,
31697 if(this.fireEvent('beforequery', qe)===false || qe.cancel){
31701 forceAll = qe.forceAll;
31702 if(forceAll === true || (q.length >= this.minChars)){
31703 if(this.lastQuery != q || this.alwaysQuery){
31704 this.lastQuery = q;
31705 if(this.mode == 'local'){
31706 this.selectedIndex = -1;
31708 this.store.clearFilter();
31710 this.store.filter(this.displayField, q);
31714 this.store.baseParams[this.queryParam] = q;
31716 params: this.getParams(q)
31721 this.selectedIndex = -1;
31728 getParams : function(q){
31730 //p[this.queryParam] = q;
31733 p.limit = this.pageSize;
31739 * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
31741 collapse : function(){
31746 collapseIf : function(e){
31751 * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
31753 expand : function(){
31761 * @cfg {Boolean} grow
31765 * @cfg {Number} growMin
31769 * @cfg {Number} growMax
31777 setWidth : function()
31781 getResizeEl : function(){
31784 });//<script type="text/javasscript">
31788 * @class Roo.DDView
31789 * A DnD enabled version of Roo.View.
31790 * @param {Element/String} container The Element in which to create the View.
31791 * @param {String} tpl The template string used to create the markup for each element of the View
31792 * @param {Object} config The configuration properties. These include all the config options of
31793 * {@link Roo.View} plus some specific to this class.<br>
31795 * Drag/drop is implemented by adding {@link Roo.data.Record}s to the target DDView. If copying is
31796 * not being performed, the original {@link Roo.data.Record} is removed from the source DDView.<br>
31798 * The following extra CSS rules are needed to provide insertion point highlighting:<pre><code>
31799 .x-view-drag-insert-above {
31800 border-top:1px dotted #3366cc;
31802 .x-view-drag-insert-below {
31803 border-bottom:1px dotted #3366cc;
31809 Roo.DDView = function(container, tpl, config) {
31810 Roo.DDView.superclass.constructor.apply(this, arguments);
31811 this.getEl().setStyle("outline", "0px none");
31812 this.getEl().unselectable();
31813 if (this.dragGroup) {
31814 this.setDraggable(this.dragGroup.split(","));
31816 if (this.dropGroup) {
31817 this.setDroppable(this.dropGroup.split(","));
31819 if (this.deletable) {
31820 this.setDeletable();
31822 this.isDirtyFlag = false;
31828 Roo.extend(Roo.DDView, Roo.View, {
31829 /** @cfg {String/Array} dragGroup The ddgroup name(s) for the View's DragZone. */
31830 /** @cfg {String/Array} dropGroup The ddgroup name(s) for the View's DropZone. */
31831 /** @cfg {Boolean} copy Causes drag operations to copy nodes rather than move. */
31832 /** @cfg {Boolean} allowCopy Causes ctrl/drag operations to copy nodes rather than move. */
31836 reset: Roo.emptyFn,
31838 clearInvalid: Roo.form.Field.prototype.clearInvalid,
31840 validate: function() {
31844 destroy: function() {
31845 this.purgeListeners();
31846 this.getEl.removeAllListeners();
31847 this.getEl().remove();
31848 if (this.dragZone) {
31849 if (this.dragZone.destroy) {
31850 this.dragZone.destroy();
31853 if (this.dropZone) {
31854 if (this.dropZone.destroy) {
31855 this.dropZone.destroy();
31860 /** Allows this class to be an Roo.form.Field so it can be found using {@link Roo.form.BasicForm#findField}. */
31861 getName: function() {
31865 /** Loads the View from a JSON string representing the Records to put into the Store. */
31866 setValue: function(v) {
31868 throw "DDView.setValue(). DDView must be constructed with a valid Store";
31871 data[this.store.reader.meta.root] = v ? [].concat(v) : [];
31872 this.store.proxy = new Roo.data.MemoryProxy(data);
31876 /** @return {String} a parenthesised list of the ids of the Records in the View. */
31877 getValue: function() {
31879 this.store.each(function(rec) {
31880 result += rec.id + ',';
31882 return result.substr(0, result.length - 1) + ')';
31885 getIds: function() {
31886 var i = 0, result = new Array(this.store.getCount());
31887 this.store.each(function(rec) {
31888 result[i++] = rec.id;
31893 isDirty: function() {
31894 return this.isDirtyFlag;
31898 * Part of the Roo.dd.DropZone interface. If no target node is found, the
31899 * whole Element becomes the target, and this causes the drop gesture to append.
31901 getTargetFromEvent : function(e) {
31902 var target = e.getTarget();
31903 while ((target !== null) && (target.parentNode != this.el.dom)) {
31904 target = target.parentNode;
31907 target = this.el.dom.lastChild || this.el.dom;
31913 * Create the drag data which consists of an object which has the property "ddel" as
31914 * the drag proxy element.
31916 getDragData : function(e) {
31917 var target = this.findItemFromChild(e.getTarget());
31919 this.handleSelection(e);
31920 var selNodes = this.getSelectedNodes();
31923 copy: this.copy || (this.allowCopy && e.ctrlKey),
31927 var selectedIndices = this.getSelectedIndexes();
31928 for (var i = 0; i < selectedIndices.length; i++) {
31929 dragData.records.push(this.store.getAt(selectedIndices[i]));
31931 if (selNodes.length == 1) {
31932 dragData.ddel = target.cloneNode(true); // the div element
31934 var div = document.createElement('div'); // create the multi element drag "ghost"
31935 div.className = 'multi-proxy';
31936 for (var i = 0, len = selNodes.length; i < len; i++) {
31937 div.appendChild(selNodes[i].cloneNode(true));
31939 dragData.ddel = div;
31941 //console.log(dragData)
31942 //console.log(dragData.ddel.innerHTML)
31945 //console.log('nodragData')
31949 /** Specify to which ddGroup items in this DDView may be dragged. */
31950 setDraggable: function(ddGroup) {
31951 if (ddGroup instanceof Array) {
31952 Roo.each(ddGroup, this.setDraggable, this);
31955 if (this.dragZone) {
31956 this.dragZone.addToGroup(ddGroup);
31958 this.dragZone = new Roo.dd.DragZone(this.getEl(), {
31959 containerScroll: true,
31963 // Draggability implies selection. DragZone's mousedown selects the element.
31964 if (!this.multiSelect) { this.singleSelect = true; }
31966 // Wire the DragZone's handlers up to methods in *this*
31967 this.dragZone.getDragData = this.getDragData.createDelegate(this);
31971 /** Specify from which ddGroup this DDView accepts drops. */
31972 setDroppable: function(ddGroup) {
31973 if (ddGroup instanceof Array) {
31974 Roo.each(ddGroup, this.setDroppable, this);
31977 if (this.dropZone) {
31978 this.dropZone.addToGroup(ddGroup);
31980 this.dropZone = new Roo.dd.DropZone(this.getEl(), {
31981 containerScroll: true,
31985 // Wire the DropZone's handlers up to methods in *this*
31986 this.dropZone.getTargetFromEvent = this.getTargetFromEvent.createDelegate(this);
31987 this.dropZone.onNodeEnter = this.onNodeEnter.createDelegate(this);
31988 this.dropZone.onNodeOver = this.onNodeOver.createDelegate(this);
31989 this.dropZone.onNodeOut = this.onNodeOut.createDelegate(this);
31990 this.dropZone.onNodeDrop = this.onNodeDrop.createDelegate(this);
31994 /** Decide whether to drop above or below a View node. */
31995 getDropPoint : function(e, n, dd){
31996 if (n == this.el.dom) { return "above"; }
31997 var t = Roo.lib.Dom.getY(n), b = t + n.offsetHeight;
31998 var c = t + (b - t) / 2;
31999 var y = Roo.lib.Event.getPageY(e);
32007 onNodeEnter : function(n, dd, e, data){
32011 onNodeOver : function(n, dd, e, data){
32012 var pt = this.getDropPoint(e, n, dd);
32013 // set the insert point style on the target node
32014 var dragElClass = this.dropNotAllowed;
32017 if (pt == "above"){
32018 dragElClass = n.previousSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-above";
32019 targetElClass = "x-view-drag-insert-above";
32021 dragElClass = n.nextSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-below";
32022 targetElClass = "x-view-drag-insert-below";
32024 if (this.lastInsertClass != targetElClass){
32025 Roo.fly(n).replaceClass(this.lastInsertClass, targetElClass);
32026 this.lastInsertClass = targetElClass;
32029 return dragElClass;
32032 onNodeOut : function(n, dd, e, data){
32033 this.removeDropIndicators(n);
32036 onNodeDrop : function(n, dd, e, data){
32037 if (this.fireEvent("drop", this, n, dd, e, data) === false) {
32040 var pt = this.getDropPoint(e, n, dd);
32041 var insertAt = (n == this.el.dom) ? this.nodes.length : n.nodeIndex;
32042 if (pt == "below") { insertAt++; }
32043 for (var i = 0; i < data.records.length; i++) {
32044 var r = data.records[i];
32045 var dup = this.store.getById(r.id);
32046 if (dup && (dd != this.dragZone)) {
32047 Roo.fly(this.getNode(this.store.indexOf(dup))).frame("red", 1);
32050 this.store.insert(insertAt++, r.copy());
32052 data.source.isDirtyFlag = true;
32054 this.store.insert(insertAt++, r);
32056 this.isDirtyFlag = true;
32059 this.dragZone.cachedTarget = null;
32063 removeDropIndicators : function(n){
32065 Roo.fly(n).removeClass([
32066 "x-view-drag-insert-above",
32067 "x-view-drag-insert-below"]);
32068 this.lastInsertClass = "_noclass";
32073 * Utility method. Add a delete option to the DDView's context menu.
32074 * @param {String} imageUrl The URL of the "delete" icon image.
32076 setDeletable: function(imageUrl) {
32077 if (!this.singleSelect && !this.multiSelect) {
32078 this.singleSelect = true;
32080 var c = this.getContextMenu();
32081 this.contextMenu.on("itemclick", function(item) {
32084 this.remove(this.getSelectedIndexes());
32088 this.contextMenu.add({
32095 /** Return the context menu for this DDView. */
32096 getContextMenu: function() {
32097 if (!this.contextMenu) {
32098 // Create the View's context menu
32099 this.contextMenu = new Roo.menu.Menu({
32100 id: this.id + "-contextmenu"
32102 this.el.on("contextmenu", this.showContextMenu, this);
32104 return this.contextMenu;
32107 disableContextMenu: function() {
32108 if (this.contextMenu) {
32109 this.el.un("contextmenu", this.showContextMenu, this);
32113 showContextMenu: function(e, item) {
32114 item = this.findItemFromChild(e.getTarget());
32117 this.select(this.getNode(item), this.multiSelect && e.ctrlKey, true);
32118 this.contextMenu.showAt(e.getXY());
32123 * Remove {@link Roo.data.Record}s at the specified indices.
32124 * @param {Array/Number} selectedIndices The index (or Array of indices) of Records to remove.
32126 remove: function(selectedIndices) {
32127 selectedIndices = [].concat(selectedIndices);
32128 for (var i = 0; i < selectedIndices.length; i++) {
32129 var rec = this.store.getAt(selectedIndices[i]);
32130 this.store.remove(rec);
32135 * Double click fires the event, but also, if this is draggable, and there is only one other
32136 * related DropZone, it transfers the selected node.
32138 onDblClick : function(e){
32139 var item = this.findItemFromChild(e.getTarget());
32141 if (this.fireEvent("dblclick", this, this.indexOf(item), item, e) === false) {
32144 if (this.dragGroup) {
32145 var targets = Roo.dd.DragDropMgr.getRelated(this.dragZone, true);
32146 while (targets.indexOf(this.dropZone) > -1) {
32147 targets.remove(this.dropZone);
32149 if (targets.length == 1) {
32150 this.dragZone.cachedTarget = null;
32151 var el = Roo.get(targets[0].getEl());
32152 var box = el.getBox(true);
32153 targets[0].onNodeDrop(el.dom, {
32155 xy: [box.x, box.y + box.height - 1]
32156 }, null, this.getDragData(e));
32162 handleSelection: function(e) {
32163 this.dragZone.cachedTarget = null;
32164 var item = this.findItemFromChild(e.getTarget());
32166 this.clearSelections(true);
32169 if (item && (this.multiSelect || this.singleSelect)){
32170 if(this.multiSelect && e.shiftKey && (!e.ctrlKey) && this.lastSelection){
32171 this.select(this.getNodes(this.indexOf(this.lastSelection), item.nodeIndex), false);
32172 }else if (this.isSelected(this.getNode(item)) && e.ctrlKey){
32173 this.unselect(item);
32175 this.select(item, this.multiSelect && e.ctrlKey);
32176 this.lastSelection = item;
32181 onItemClick : function(item, index, e){
32182 if(this.fireEvent("beforeclick", this, index, item, e) === false){
32188 unselect : function(nodeInfo, suppressEvent){
32189 var node = this.getNode(nodeInfo);
32190 if(node && this.isSelected(node)){
32191 if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
32192 Roo.fly(node).removeClass(this.selectedClass);
32193 this.selections.remove(node);
32194 if(!suppressEvent){
32195 this.fireEvent("selectionchange", this, this.selections);
32203 * Ext JS Library 1.1.1
32204 * Copyright(c) 2006-2007, Ext JS, LLC.
32206 * Originally Released Under LGPL - original licence link has changed is not relivant.
32209 * <script type="text/javascript">
32213 * @class Roo.LayoutManager
32214 * @extends Roo.util.Observable
32215 * Base class for layout managers.
32217 Roo.LayoutManager = function(container, config){
32218 Roo.LayoutManager.superclass.constructor.call(this);
32219 this.el = Roo.get(container);
32220 // ie scrollbar fix
32221 if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
32222 document.body.scroll = "no";
32223 }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
32224 this.el.position('relative');
32226 this.id = this.el.id;
32227 this.el.addClass("x-layout-container");
32228 /** false to disable window resize monitoring @type Boolean */
32229 this.monitorWindowResize = true;
32234 * Fires when a layout is performed.
32235 * @param {Roo.LayoutManager} this
32239 * @event regionresized
32240 * Fires when the user resizes a region.
32241 * @param {Roo.LayoutRegion} region The resized region
32242 * @param {Number} newSize The new size (width for east/west, height for north/south)
32244 "regionresized" : true,
32246 * @event regioncollapsed
32247 * Fires when a region is collapsed.
32248 * @param {Roo.LayoutRegion} region The collapsed region
32250 "regioncollapsed" : true,
32252 * @event regionexpanded
32253 * Fires when a region is expanded.
32254 * @param {Roo.LayoutRegion} region The expanded region
32256 "regionexpanded" : true
32258 this.updating = false;
32259 Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
32262 Roo.extend(Roo.LayoutManager, Roo.util.Observable, {
32264 * Returns true if this layout is currently being updated
32265 * @return {Boolean}
32267 isUpdating : function(){
32268 return this.updating;
32272 * Suspend the LayoutManager from doing auto-layouts while
32273 * making multiple add or remove calls
32275 beginUpdate : function(){
32276 this.updating = true;
32280 * Restore auto-layouts and optionally disable the manager from performing a layout
32281 * @param {Boolean} noLayout true to disable a layout update
32283 endUpdate : function(noLayout){
32284 this.updating = false;
32290 layout: function(){
32294 onRegionResized : function(region, newSize){
32295 this.fireEvent("regionresized", region, newSize);
32299 onRegionCollapsed : function(region){
32300 this.fireEvent("regioncollapsed", region);
32303 onRegionExpanded : function(region){
32304 this.fireEvent("regionexpanded", region);
32308 * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
32309 * performs box-model adjustments.
32310 * @return {Object} The size as an object {width: (the width), height: (the height)}
32312 getViewSize : function(){
32314 if(this.el.dom != document.body){
32315 size = this.el.getSize();
32317 size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
32319 size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
32320 size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
32325 * Returns the Element this layout is bound to.
32326 * @return {Roo.Element}
32328 getEl : function(){
32333 * Returns the specified region.
32334 * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
32335 * @return {Roo.LayoutRegion}
32337 getRegion : function(target){
32338 return this.regions[target.toLowerCase()];
32341 onWindowResize : function(){
32342 if(this.monitorWindowResize){
32348 * Ext JS Library 1.1.1
32349 * Copyright(c) 2006-2007, Ext JS, LLC.
32351 * Originally Released Under LGPL - original licence link has changed is not relivant.
32354 * <script type="text/javascript">
32357 * @class Roo.BorderLayout
32358 * @extends Roo.LayoutManager
32359 * This class represents a common layout manager used in desktop applications. For screenshots and more details,
32360 * please see: <br><br>
32361 * <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>
32362 * <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>
32365 var layout = new Roo.BorderLayout(document.body, {
32399 preferredTabWidth: 150
32404 var CP = Roo.ContentPanel;
32406 layout.beginUpdate();
32407 layout.add("north", new CP("north", "North"));
32408 layout.add("south", new CP("south", {title: "South", closable: true}));
32409 layout.add("west", new CP("west", {title: "West"}));
32410 layout.add("east", new CP("autoTabs", {title: "Auto Tabs", closable: true}));
32411 layout.add("center", new CP("center1", {title: "Close Me", closable: true}));
32412 layout.add("center", new CP("center2", {title: "Center Panel", closable: false}));
32413 layout.getRegion("center").showPanel("center1");
32414 layout.endUpdate();
32417 <b>The container the layout is rendered into can be either the body element or any other element.
32418 If it is not the body element, the container needs to either be an absolute positioned element,
32419 or you will need to add "position:relative" to the css of the container. You will also need to specify
32420 the container size if it is not the body element.</b>
32423 * Create a new BorderLayout
32424 * @param {String/HTMLElement/Element} container The container this layout is bound to
32425 * @param {Object} config Configuration options
32427 Roo.BorderLayout = function(container, config){
32428 config = config || {};
32429 Roo.BorderLayout.superclass.constructor.call(this, container, config);
32430 this.factory = config.factory || Roo.BorderLayout.RegionFactory;
32431 for(var i = 0, len = this.factory.validRegions.length; i < len; i++) {
32432 var target = this.factory.validRegions[i];
32433 if(config[target]){
32434 this.addRegion(target, config[target]);
32439 Roo.extend(Roo.BorderLayout, Roo.LayoutManager, {
32441 * Creates and adds a new region if it doesn't already exist.
32442 * @param {String} target The target region key (north, south, east, west or center).
32443 * @param {Object} config The regions config object
32444 * @return {BorderLayoutRegion} The new region
32446 addRegion : function(target, config){
32447 if(!this.regions[target]){
32448 var r = this.factory.create(target, this, config);
32449 this.bindRegion(target, r);
32451 return this.regions[target];
32455 bindRegion : function(name, r){
32456 this.regions[name] = r;
32457 r.on("visibilitychange", this.layout, this);
32458 r.on("paneladded", this.layout, this);
32459 r.on("panelremoved", this.layout, this);
32460 r.on("invalidated", this.layout, this);
32461 r.on("resized", this.onRegionResized, this);
32462 r.on("collapsed", this.onRegionCollapsed, this);
32463 r.on("expanded", this.onRegionExpanded, this);
32467 * Performs a layout update.
32469 layout : function(){
32470 if(this.updating) return;
32471 var size = this.getViewSize();
32472 var w = size.width;
32473 var h = size.height;
32478 //var x = 0, y = 0;
32480 var rs = this.regions;
32481 var north = rs["north"];
32482 var south = rs["south"];
32483 var west = rs["west"];
32484 var east = rs["east"];
32485 var center = rs["center"];
32486 //if(this.hideOnLayout){ // not supported anymore
32487 //c.el.setStyle("display", "none");
32489 if(north && north.isVisible()){
32490 var b = north.getBox();
32491 var m = north.getMargins();
32492 b.width = w - (m.left+m.right);
32495 centerY = b.height + b.y + m.bottom;
32496 centerH -= centerY;
32497 north.updateBox(this.safeBox(b));
32499 if(south && south.isVisible()){
32500 var b = south.getBox();
32501 var m = south.getMargins();
32502 b.width = w - (m.left+m.right);
32504 var totalHeight = (b.height + m.top + m.bottom);
32505 b.y = h - totalHeight + m.top;
32506 centerH -= totalHeight;
32507 south.updateBox(this.safeBox(b));
32509 if(west && west.isVisible()){
32510 var b = west.getBox();
32511 var m = west.getMargins();
32512 b.height = centerH - (m.top+m.bottom);
32514 b.y = centerY + m.top;
32515 var totalWidth = (b.width + m.left + m.right);
32516 centerX += totalWidth;
32517 centerW -= totalWidth;
32518 west.updateBox(this.safeBox(b));
32520 if(east && east.isVisible()){
32521 var b = east.getBox();
32522 var m = east.getMargins();
32523 b.height = centerH - (m.top+m.bottom);
32524 var totalWidth = (b.width + m.left + m.right);
32525 b.x = w - totalWidth + m.left;
32526 b.y = centerY + m.top;
32527 centerW -= totalWidth;
32528 east.updateBox(this.safeBox(b));
32531 var m = center.getMargins();
32533 x: centerX + m.left,
32534 y: centerY + m.top,
32535 width: centerW - (m.left+m.right),
32536 height: centerH - (m.top+m.bottom)
32538 //if(this.hideOnLayout){
32539 //center.el.setStyle("display", "block");
32541 center.updateBox(this.safeBox(centerBox));
32544 this.fireEvent("layout", this);
32548 safeBox : function(box){
32549 box.width = Math.max(0, box.width);
32550 box.height = Math.max(0, box.height);
32555 * Adds a ContentPanel (or subclass) to this layout.
32556 * @param {String} target The target region key (north, south, east, west or center).
32557 * @param {Roo.ContentPanel} panel The panel to add
32558 * @return {Roo.ContentPanel} The added panel
32560 add : function(target, panel){
32562 target = target.toLowerCase();
32563 return this.regions[target].add(panel);
32567 * Remove a ContentPanel (or subclass) to this layout.
32568 * @param {String} target The target region key (north, south, east, west or center).
32569 * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
32570 * @return {Roo.ContentPanel} The removed panel
32572 remove : function(target, panel){
32573 target = target.toLowerCase();
32574 return this.regions[target].remove(panel);
32578 * Searches all regions for a panel with the specified id
32579 * @param {String} panelId
32580 * @return {Roo.ContentPanel} The panel or null if it wasn't found
32582 findPanel : function(panelId){
32583 var rs = this.regions;
32584 for(var target in rs){
32585 if(typeof rs[target] != "function"){
32586 var p = rs[target].getPanel(panelId);
32596 * Searches all regions for a panel with the specified id and activates (shows) it.
32597 * @param {String/ContentPanel} panelId The panels id or the panel itself
32598 * @return {Roo.ContentPanel} The shown panel or null
32600 showPanel : function(panelId) {
32601 var rs = this.regions;
32602 for(var target in rs){
32603 var r = rs[target];
32604 if(typeof r != "function"){
32605 if(r.hasPanel(panelId)){
32606 return r.showPanel(panelId);
32614 * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
32615 * @param {Roo.state.Provider} provider (optional) An alternate state provider
32617 restoreState : function(provider){
32619 provider = Roo.state.Manager;
32621 var sm = new Roo.LayoutStateManager();
32622 sm.init(this, provider);
32626 * Adds a batch of multiple ContentPanels dynamically by passing a special regions config object. This config
32627 * object should contain properties for each region to add ContentPanels to, and each property's value should be
32628 * a valid ContentPanel config object. Example:
32630 // Create the main layout
32631 var layout = new Roo.BorderLayout('main-ct', {
32642 // Create and add multiple ContentPanels at once via configs
32645 id: 'source-files',
32647 title:'Ext Source Files',
32660 * @param {Object} regions An object containing ContentPanel configs by region name
32662 batchAdd : function(regions){
32663 this.beginUpdate();
32664 for(var rname in regions){
32665 var lr = this.regions[rname];
32667 this.addTypedPanels(lr, regions[rname]);
32674 addTypedPanels : function(lr, ps){
32675 if(typeof ps == 'string'){
32676 lr.add(new Roo.ContentPanel(ps));
32678 else if(ps instanceof Array){
32679 for(var i =0, len = ps.length; i < len; i++){
32680 this.addTypedPanels(lr, ps[i]);
32683 else if(!ps.events){ // raw config?
32685 delete ps.el; // prevent conflict
32686 lr.add(new Roo.ContentPanel(el || Roo.id(), ps));
32688 else { // panel object assumed!
32693 * Adds a xtype elements to the layout.
32697 xtype : 'ContentPanel',
32704 xtype : 'NestedLayoutPanel',
32710 items : [ ... list of content panels or nested layout panels.. ]
32714 * @param {Object} cfg Xtype definition of item to add.
32716 addxtype : function(cfg)
32718 // basically accepts a pannel...
32719 // can accept a layout region..!?!?
32720 //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
32722 if (!cfg.xtype.match(/Panel$/)) {
32727 if (typeof(cfg.region) == 'undefined') {
32728 Roo.log("Failed to add Panel, region was not set");
32732 var region = cfg.region;
32738 xitems = cfg.items;
32745 case 'ContentPanel': // ContentPanel (el, cfg)
32746 case 'ScrollPanel': // ContentPanel (el, cfg)
32748 if(cfg.autoCreate) {
32749 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
32751 var el = this.el.createChild();
32752 ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
32755 this.add(region, ret);
32759 case 'TreePanel': // our new panel!
32760 cfg.el = this.el.createChild();
32761 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
32762 this.add(region, ret);
32765 case 'NestedLayoutPanel':
32766 // create a new Layout (which is a Border Layout...
32767 var el = this.el.createChild();
32768 var clayout = cfg.layout;
32770 clayout.items = clayout.items || [];
32771 // replace this exitems with the clayout ones..
32772 xitems = clayout.items;
32775 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
32776 cfg.background = false;
32778 var layout = new Roo.BorderLayout(el, clayout);
32780 ret = new Roo[cfg.xtype](layout, cfg); // new panel!!!!!
32781 //console.log('adding nested layout panel ' + cfg.toSource());
32782 this.add(region, ret);
32783 nb = {}; /// find first...
32788 // needs grid and region
32790 //var el = this.getRegion(region).el.createChild();
32791 var el = this.el.createChild();
32792 // create the grid first...
32794 var grid = new Roo.grid[cfg.grid.xtype](el, cfg.grid);
32796 if (region == 'center' && this.active ) {
32797 cfg.background = false;
32799 ret = new Roo[cfg.xtype](grid, cfg); // new panel!!!!!
32801 this.add(region, ret);
32802 if (cfg.background) {
32803 ret.on('activate', function(gp) {
32804 if (!gp.grid.rendered) {
32819 alert("Can not add '" + cfg.xtype + "' to BorderLayout");
32821 // GridPanel (grid, cfg)
32824 this.beginUpdate();
32828 Roo.each(xitems, function(i) {
32829 region = nb && i.region ? i.region : false;
32831 var add = ret.addxtype(i);
32834 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
32835 if (!i.background) {
32836 abn[region] = nb[region] ;
32843 // make the last non-background panel active..
32844 //if (nb) { Roo.log(abn); }
32847 for(var r in abn) {
32848 region = this.getRegion(r);
32850 // tried using nb[r], but it does not work..
32852 region.showPanel(abn[r]);
32863 * Shortcut for creating a new BorderLayout object and adding one or more ContentPanels to it in a single step, handling
32864 * the beginUpdate and endUpdate calls internally. The key to this method is the <b>panels</b> property that can be
32865 * provided with each region config, which allows you to add ContentPanel configs in addition to the region configs
32866 * during creation. The following code is equivalent to the constructor-based example at the beginning of this class:
32869 var CP = Roo.ContentPanel;
32871 var layout = Roo.BorderLayout.create({
32875 panels: [new CP("north", "North")]
32884 panels: [new CP("west", {title: "West"})]
32893 panels: [new CP("autoTabs", {title: "Auto Tabs", closable: true})]
32902 panels: [new CP("south", {title: "South", closable: true})]
32909 preferredTabWidth: 150,
32911 new CP("center1", {title: "Close Me", closable: true}),
32912 new CP("center2", {title: "Center Panel", closable: false})
32917 layout.getRegion("center").showPanel("center1");
32922 Roo.BorderLayout.create = function(config, targetEl){
32923 var layout = new Roo.BorderLayout(targetEl || document.body, config);
32924 layout.beginUpdate();
32925 var regions = Roo.BorderLayout.RegionFactory.validRegions;
32926 for(var j = 0, jlen = regions.length; j < jlen; j++){
32927 var lr = regions[j];
32928 if(layout.regions[lr] && config[lr].panels){
32929 var r = layout.regions[lr];
32930 var ps = config[lr].panels;
32931 layout.addTypedPanels(r, ps);
32934 layout.endUpdate();
32939 Roo.BorderLayout.RegionFactory = {
32941 validRegions : ["north","south","east","west","center"],
32944 create : function(target, mgr, config){
32945 target = target.toLowerCase();
32946 if(config.lightweight || config.basic){
32947 return new Roo.BasicLayoutRegion(mgr, config, target);
32951 return new Roo.NorthLayoutRegion(mgr, config);
32953 return new Roo.SouthLayoutRegion(mgr, config);
32955 return new Roo.EastLayoutRegion(mgr, config);
32957 return new Roo.WestLayoutRegion(mgr, config);
32959 return new Roo.CenterLayoutRegion(mgr, config);
32961 throw 'Layout region "'+target+'" not supported.';
32965 * Ext JS Library 1.1.1
32966 * Copyright(c) 2006-2007, Ext JS, LLC.
32968 * Originally Released Under LGPL - original licence link has changed is not relivant.
32971 * <script type="text/javascript">
32975 * @class Roo.BasicLayoutRegion
32976 * @extends Roo.util.Observable
32977 * This class represents a lightweight region in a layout manager. This region does not move dom nodes
32978 * and does not have a titlebar, tabs or any other features. All it does is size and position
32979 * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
32981 Roo.BasicLayoutRegion = function(mgr, config, pos, skipConfig){
32983 this.position = pos;
32986 * @scope Roo.BasicLayoutRegion
32990 * @event beforeremove
32991 * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
32992 * @param {Roo.LayoutRegion} this
32993 * @param {Roo.ContentPanel} panel The panel
32994 * @param {Object} e The cancel event object
32996 "beforeremove" : true,
32998 * @event invalidated
32999 * Fires when the layout for this region is changed.
33000 * @param {Roo.LayoutRegion} this
33002 "invalidated" : true,
33004 * @event visibilitychange
33005 * Fires when this region is shown or hidden
33006 * @param {Roo.LayoutRegion} this
33007 * @param {Boolean} visibility true or false
33009 "visibilitychange" : true,
33011 * @event paneladded
33012 * Fires when a panel is added.
33013 * @param {Roo.LayoutRegion} this
33014 * @param {Roo.ContentPanel} panel The panel
33016 "paneladded" : true,
33018 * @event panelremoved
33019 * Fires when a panel is removed.
33020 * @param {Roo.LayoutRegion} this
33021 * @param {Roo.ContentPanel} panel The panel
33023 "panelremoved" : true,
33026 * Fires when this region is collapsed.
33027 * @param {Roo.LayoutRegion} this
33029 "collapsed" : true,
33032 * Fires when this region is expanded.
33033 * @param {Roo.LayoutRegion} this
33038 * Fires when this region is slid into view.
33039 * @param {Roo.LayoutRegion} this
33041 "slideshow" : true,
33044 * Fires when this region slides out of view.
33045 * @param {Roo.LayoutRegion} this
33047 "slidehide" : true,
33049 * @event panelactivated
33050 * Fires when a panel is activated.
33051 * @param {Roo.LayoutRegion} this
33052 * @param {Roo.ContentPanel} panel The activated panel
33054 "panelactivated" : true,
33057 * Fires when the user resizes this region.
33058 * @param {Roo.LayoutRegion} this
33059 * @param {Number} newSize The new size (width for east/west, height for north/south)
33063 /** A collection of panels in this region. @type Roo.util.MixedCollection */
33064 this.panels = new Roo.util.MixedCollection();
33065 this.panels.getKey = this.getPanelId.createDelegate(this);
33067 this.activePanel = null;
33068 // ensure listeners are added...
33070 if (config.listeners || config.events) {
33071 Roo.BasicLayoutRegion.superclass.constructor.call(this, {
33072 listeners : config.listeners || {},
33073 events : config.events || {}
33077 if(skipConfig !== true){
33078 this.applyConfig(config);
33082 Roo.extend(Roo.BasicLayoutRegion, Roo.util.Observable, {
33083 getPanelId : function(p){
33087 applyConfig : function(config){
33088 this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
33089 this.config = config;
33094 * Resizes the region to the specified size. For vertical regions (west, east) this adjusts
33095 * the width, for horizontal (north, south) the height.
33096 * @param {Number} newSize The new width or height
33098 resizeTo : function(newSize){
33099 var el = this.el ? this.el :
33100 (this.activePanel ? this.activePanel.getEl() : null);
33102 switch(this.position){
33105 el.setWidth(newSize);
33106 this.fireEvent("resized", this, newSize);
33110 el.setHeight(newSize);
33111 this.fireEvent("resized", this, newSize);
33117 getBox : function(){
33118 return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
33121 getMargins : function(){
33122 return this.margins;
33125 updateBox : function(box){
33127 var el = this.activePanel.getEl();
33128 el.dom.style.left = box.x + "px";
33129 el.dom.style.top = box.y + "px";
33130 this.activePanel.setSize(box.width, box.height);
33134 * Returns the container element for this region.
33135 * @return {Roo.Element}
33137 getEl : function(){
33138 return this.activePanel;
33142 * Returns true if this region is currently visible.
33143 * @return {Boolean}
33145 isVisible : function(){
33146 return this.activePanel ? true : false;
33149 setActivePanel : function(panel){
33150 panel = this.getPanel(panel);
33151 if(this.activePanel && this.activePanel != panel){
33152 this.activePanel.setActiveState(false);
33153 this.activePanel.getEl().setLeftTop(-10000,-10000);
33155 this.activePanel = panel;
33156 panel.setActiveState(true);
33158 panel.setSize(this.box.width, this.box.height);
33160 this.fireEvent("panelactivated", this, panel);
33161 this.fireEvent("invalidated");
33165 * Show the specified panel.
33166 * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
33167 * @return {Roo.ContentPanel} The shown panel or null
33169 showPanel : function(panel){
33170 if(panel = this.getPanel(panel)){
33171 this.setActivePanel(panel);
33177 * Get the active panel for this region.
33178 * @return {Roo.ContentPanel} The active panel or null
33180 getActivePanel : function(){
33181 return this.activePanel;
33185 * Add the passed ContentPanel(s)
33186 * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
33187 * @return {Roo.ContentPanel} The panel added (if only one was added)
33189 add : function(panel){
33190 if(arguments.length > 1){
33191 for(var i = 0, len = arguments.length; i < len; i++) {
33192 this.add(arguments[i]);
33196 if(this.hasPanel(panel)){
33197 this.showPanel(panel);
33200 var el = panel.getEl();
33201 if(el.dom.parentNode != this.mgr.el.dom){
33202 this.mgr.el.dom.appendChild(el.dom);
33204 if(panel.setRegion){
33205 panel.setRegion(this);
33207 this.panels.add(panel);
33208 el.setStyle("position", "absolute");
33209 if(!panel.background){
33210 this.setActivePanel(panel);
33211 if(this.config.initialSize && this.panels.getCount()==1){
33212 this.resizeTo(this.config.initialSize);
33215 this.fireEvent("paneladded", this, panel);
33220 * Returns true if the panel is in this region.
33221 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
33222 * @return {Boolean}
33224 hasPanel : function(panel){
33225 if(typeof panel == "object"){ // must be panel obj
33226 panel = panel.getId();
33228 return this.getPanel(panel) ? true : false;
33232 * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
33233 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
33234 * @param {Boolean} preservePanel Overrides the config preservePanel option
33235 * @return {Roo.ContentPanel} The panel that was removed
33237 remove : function(panel, preservePanel){
33238 panel = this.getPanel(panel);
33243 this.fireEvent("beforeremove", this, panel, e);
33244 if(e.cancel === true){
33247 var panelId = panel.getId();
33248 this.panels.removeKey(panelId);
33253 * Returns the panel specified or null if it's not in this region.
33254 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
33255 * @return {Roo.ContentPanel}
33257 getPanel : function(id){
33258 if(typeof id == "object"){ // must be panel obj
33261 return this.panels.get(id);
33265 * Returns this regions position (north/south/east/west/center).
33268 getPosition: function(){
33269 return this.position;
33273 * Ext JS Library 1.1.1
33274 * Copyright(c) 2006-2007, Ext JS, LLC.
33276 * Originally Released Under LGPL - original licence link has changed is not relivant.
33279 * <script type="text/javascript">
33283 * @class Roo.LayoutRegion
33284 * @extends Roo.BasicLayoutRegion
33285 * This class represents a region in a layout manager.
33286 * @cfg {Boolean} collapsible False to disable collapsing (defaults to true)
33287 * @cfg {Boolean} collapsed True to set the initial display to collapsed (defaults to false)
33288 * @cfg {Boolean} floatable False to disable floating (defaults to true)
33289 * @cfg {Object} margins Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
33290 * @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})
33291 * @cfg {String} tabPosition "top" or "bottom" (defaults to "bottom")
33292 * @cfg {String} collapsedTitle Optional string message to display in the collapsed block of a north or south region
33293 * @cfg {Boolean} alwaysShowTabs True to always display tabs even when there is only 1 panel (defaults to false)
33294 * @cfg {Boolean} autoScroll True to enable overflow scrolling (defaults to false)
33295 * @cfg {Boolean} titlebar True to display a title bar (defaults to true)
33296 * @cfg {String} title The title for the region (overrides panel titles)
33297 * @cfg {Boolean} animate True to animate expand/collapse (defaults to false)
33298 * @cfg {Boolean} autoHide False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
33299 * @cfg {Boolean} preservePanels True to preserve removed panels so they can be readded later (defaults to false)
33300 * @cfg {Boolean} closeOnTab True to place the close icon on the tabs instead of the region titlebar (defaults to false)
33301 * @cfg {Boolean} hideTabs True to hide the tab strip (defaults to false)
33302 * @cfg {Boolean} resizeTabs True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
33303 * the space available, similar to FireFox 1.5 tabs (defaults to false)
33304 * @cfg {Number} minTabWidth The minimum tab width (defaults to 40)
33305 * @cfg {Number} preferredTabWidth The preferred tab width (defaults to 150)
33306 * @cfg {Boolean} showPin True to show a pin button
33307 * @cfg {Boolean} hidden True to start the region hidden (defaults to false)
33308 * @cfg {Boolean} hideWhenEmpty True to hide the region when it has no panels
33309 * @cfg {Boolean} disableTabTips True to disable tab tooltips
33310 * @cfg {Number} width For East/West panels
33311 * @cfg {Number} height For North/South panels
33312 * @cfg {Boolean} split To show the splitter
33313 * @cfg {Boolean} toolbar xtype configuration for a toolbar - shows on right of tabbar
33315 Roo.LayoutRegion = function(mgr, config, pos){
33316 Roo.LayoutRegion.superclass.constructor.call(this, mgr, config, pos, true);
33317 var dh = Roo.DomHelper;
33318 /** This region's container element
33319 * @type Roo.Element */
33320 this.el = dh.append(mgr.el.dom, {tag: "div", cls: "x-layout-panel x-layout-panel-" + this.position}, true);
33321 /** This region's title element
33322 * @type Roo.Element */
33324 this.titleEl = dh.append(this.el.dom, {tag: "div", unselectable: "on", cls: "x-unselectable x-layout-panel-hd x-layout-title-"+this.position, children:[
33325 {tag: "span", cls: "x-unselectable x-layout-panel-hd-text", unselectable: "on", html: " "},
33326 {tag: "div", cls: "x-unselectable x-layout-panel-hd-tools", unselectable: "on"}
33328 this.titleEl.enableDisplayMode();
33329 /** This region's title text element
33330 * @type HTMLElement */
33331 this.titleTextEl = this.titleEl.dom.firstChild;
33332 this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
33333 this.closeBtn = this.createTool(this.tools.dom, "x-layout-close");
33334 this.closeBtn.enableDisplayMode();
33335 this.closeBtn.on("click", this.closeClicked, this);
33336 this.closeBtn.hide();
33338 this.createBody(config);
33339 this.visible = true;
33340 this.collapsed = false;
33342 if(config.hideWhenEmpty){
33344 this.on("paneladded", this.validateVisibility, this);
33345 this.on("panelremoved", this.validateVisibility, this);
33347 this.applyConfig(config);
33350 Roo.extend(Roo.LayoutRegion, Roo.BasicLayoutRegion, {
33352 createBody : function(){
33353 /** This region's body element
33354 * @type Roo.Element */
33355 this.bodyEl = this.el.createChild({tag: "div", cls: "x-layout-panel-body"});
33358 applyConfig : function(c){
33359 if(c.collapsible && this.position != "center" && !this.collapsedEl){
33360 var dh = Roo.DomHelper;
33361 if(c.titlebar !== false){
33362 this.collapseBtn = this.createTool(this.tools.dom, "x-layout-collapse-"+this.position);
33363 this.collapseBtn.on("click", this.collapse, this);
33364 this.collapseBtn.enableDisplayMode();
33366 if(c.showPin === true || this.showPin){
33367 this.stickBtn = this.createTool(this.tools.dom, "x-layout-stick");
33368 this.stickBtn.enableDisplayMode();
33369 this.stickBtn.on("click", this.expand, this);
33370 this.stickBtn.hide();
33373 /** This region's collapsed element
33374 * @type Roo.Element */
33375 this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
33376 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
33378 if(c.floatable !== false){
33379 this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
33380 this.collapsedEl.on("click", this.collapseClick, this);
33383 if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
33384 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
33385 id: "message", unselectable: "on", style:{"float":"left"}});
33386 this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
33388 this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
33389 this.expandBtn.on("click", this.expand, this);
33391 if(this.collapseBtn){
33392 this.collapseBtn.setVisible(c.collapsible == true);
33394 this.cmargins = c.cmargins || this.cmargins ||
33395 (this.position == "west" || this.position == "east" ?
33396 {top: 0, left: 2, right:2, bottom: 0} :
33397 {top: 2, left: 0, right:0, bottom: 2});
33398 this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
33399 this.bottomTabs = c.tabPosition != "top";
33400 this.autoScroll = c.autoScroll || false;
33401 if(this.autoScroll){
33402 this.bodyEl.setStyle("overflow", "auto");
33404 this.bodyEl.setStyle("overflow", "hidden");
33406 //if(c.titlebar !== false){
33407 if((!c.titlebar && !c.title) || c.titlebar === false){
33408 this.titleEl.hide();
33410 this.titleEl.show();
33412 this.titleTextEl.innerHTML = c.title;
33416 this.duration = c.duration || .30;
33417 this.slideDuration = c.slideDuration || .45;
33420 this.collapse(true);
33427 * Returns true if this region is currently visible.
33428 * @return {Boolean}
33430 isVisible : function(){
33431 return this.visible;
33435 * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
33436 * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&#160;")
33438 setCollapsedTitle : function(title){
33439 title = title || " ";
33440 if(this.collapsedTitleTextEl){
33441 this.collapsedTitleTextEl.innerHTML = title;
33445 getBox : function(){
33447 if(!this.collapsed){
33448 b = this.el.getBox(false, true);
33450 b = this.collapsedEl.getBox(false, true);
33455 getMargins : function(){
33456 return this.collapsed ? this.cmargins : this.margins;
33459 highlight : function(){
33460 this.el.addClass("x-layout-panel-dragover");
33463 unhighlight : function(){
33464 this.el.removeClass("x-layout-panel-dragover");
33467 updateBox : function(box){
33469 if(!this.collapsed){
33470 this.el.dom.style.left = box.x + "px";
33471 this.el.dom.style.top = box.y + "px";
33472 this.updateBody(box.width, box.height);
33474 this.collapsedEl.dom.style.left = box.x + "px";
33475 this.collapsedEl.dom.style.top = box.y + "px";
33476 this.collapsedEl.setSize(box.width, box.height);
33479 this.tabs.autoSizeTabs();
33483 updateBody : function(w, h){
33485 this.el.setWidth(w);
33486 w -= this.el.getBorderWidth("rl");
33487 if(this.config.adjustments){
33488 w += this.config.adjustments[0];
33492 this.el.setHeight(h);
33493 h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
33494 h -= this.el.getBorderWidth("tb");
33495 if(this.config.adjustments){
33496 h += this.config.adjustments[1];
33498 this.bodyEl.setHeight(h);
33500 h = this.tabs.syncHeight(h);
33503 if(this.panelSize){
33504 w = w !== null ? w : this.panelSize.width;
33505 h = h !== null ? h : this.panelSize.height;
33507 if(this.activePanel){
33508 var el = this.activePanel.getEl();
33509 w = w !== null ? w : el.getWidth();
33510 h = h !== null ? h : el.getHeight();
33511 this.panelSize = {width: w, height: h};
33512 this.activePanel.setSize(w, h);
33514 if(Roo.isIE && this.tabs){
33515 this.tabs.el.repaint();
33520 * Returns the container element for this region.
33521 * @return {Roo.Element}
33523 getEl : function(){
33528 * Hides this region.
33531 if(!this.collapsed){
33532 this.el.dom.style.left = "-2000px";
33535 this.collapsedEl.dom.style.left = "-2000px";
33536 this.collapsedEl.hide();
33538 this.visible = false;
33539 this.fireEvent("visibilitychange", this, false);
33543 * Shows this region if it was previously hidden.
33546 if(!this.collapsed){
33549 this.collapsedEl.show();
33551 this.visible = true;
33552 this.fireEvent("visibilitychange", this, true);
33555 closeClicked : function(){
33556 if(this.activePanel){
33557 this.remove(this.activePanel);
33561 collapseClick : function(e){
33563 e.stopPropagation();
33566 e.stopPropagation();
33572 * Collapses this region.
33573 * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
33575 collapse : function(skipAnim){
33576 if(this.collapsed) return;
33577 this.collapsed = true;
33579 this.split.el.hide();
33581 if(this.config.animate && skipAnim !== true){
33582 this.fireEvent("invalidated", this);
33583 this.animateCollapse();
33585 this.el.setLocation(-20000,-20000);
33587 this.collapsedEl.show();
33588 this.fireEvent("collapsed", this);
33589 this.fireEvent("invalidated", this);
33593 animateCollapse : function(){
33598 * Expands this region if it was previously collapsed.
33599 * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
33600 * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
33602 expand : function(e, skipAnim){
33603 if(e) e.stopPropagation();
33604 if(!this.collapsed || this.el.hasActiveFx()) return;
33606 this.afterSlideIn();
33609 this.collapsed = false;
33610 if(this.config.animate && skipAnim !== true){
33611 this.animateExpand();
33615 this.split.el.show();
33617 this.collapsedEl.setLocation(-2000,-2000);
33618 this.collapsedEl.hide();
33619 this.fireEvent("invalidated", this);
33620 this.fireEvent("expanded", this);
33624 animateExpand : function(){
33628 initTabs : function()
33630 this.bodyEl.setStyle("overflow", "hidden");
33631 var ts = new Roo.TabPanel(
33634 tabPosition: this.bottomTabs ? 'bottom' : 'top',
33635 disableTooltips: this.config.disableTabTips,
33636 toolbar : this.config.toolbar
33639 if(this.config.hideTabs){
33640 ts.stripWrap.setDisplayed(false);
33643 ts.resizeTabs = this.config.resizeTabs === true;
33644 ts.minTabWidth = this.config.minTabWidth || 40;
33645 ts.maxTabWidth = this.config.maxTabWidth || 250;
33646 ts.preferredTabWidth = this.config.preferredTabWidth || 150;
33647 ts.monitorResize = false;
33648 ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
33649 ts.bodyEl.addClass('x-layout-tabs-body');
33650 this.panels.each(this.initPanelAsTab, this);
33653 initPanelAsTab : function(panel){
33654 var ti = this.tabs.addTab(panel.getEl().id, panel.getTitle(), null,
33655 this.config.closeOnTab && panel.isClosable());
33656 if(panel.tabTip !== undefined){
33657 ti.setTooltip(panel.tabTip);
33659 ti.on("activate", function(){
33660 this.setActivePanel(panel);
33662 if(this.config.closeOnTab){
33663 ti.on("beforeclose", function(t, e){
33665 this.remove(panel);
33671 updatePanelTitle : function(panel, title){
33672 if(this.activePanel == panel){
33673 this.updateTitle(title);
33676 var ti = this.tabs.getTab(panel.getEl().id);
33678 if(panel.tabTip !== undefined){
33679 ti.setTooltip(panel.tabTip);
33684 updateTitle : function(title){
33685 if(this.titleTextEl && !this.config.title){
33686 this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : " ");
33690 setActivePanel : function(panel){
33691 panel = this.getPanel(panel);
33692 if(this.activePanel && this.activePanel != panel){
33693 this.activePanel.setActiveState(false);
33695 this.activePanel = panel;
33696 panel.setActiveState(true);
33697 if(this.panelSize){
33698 panel.setSize(this.panelSize.width, this.panelSize.height);
33701 this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
33703 this.updateTitle(panel.getTitle());
33705 this.fireEvent("invalidated", this);
33707 this.fireEvent("panelactivated", this, panel);
33711 * Shows the specified panel.
33712 * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
33713 * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
33715 showPanel : function(panel){
33716 if(panel = this.getPanel(panel)){
33718 var tab = this.tabs.getTab(panel.getEl().id);
33719 if(tab.isHidden()){
33720 this.tabs.unhideTab(tab.id);
33724 this.setActivePanel(panel);
33731 * Get the active panel for this region.
33732 * @return {Roo.ContentPanel} The active panel or null
33734 getActivePanel : function(){
33735 return this.activePanel;
33738 validateVisibility : function(){
33739 if(this.panels.getCount() < 1){
33740 this.updateTitle(" ");
33741 this.closeBtn.hide();
33744 if(!this.isVisible()){
33751 * Adds the passed ContentPanel(s) to this region.
33752 * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
33753 * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
33755 add : function(panel){
33756 if(arguments.length > 1){
33757 for(var i = 0, len = arguments.length; i < len; i++) {
33758 this.add(arguments[i]);
33762 if(this.hasPanel(panel)){
33763 this.showPanel(panel);
33766 panel.setRegion(this);
33767 this.panels.add(panel);
33768 if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
33769 this.bodyEl.dom.appendChild(panel.getEl().dom);
33770 if(panel.background !== true){
33771 this.setActivePanel(panel);
33773 this.fireEvent("paneladded", this, panel);
33779 this.initPanelAsTab(panel);
33781 if(panel.background !== true){
33782 this.tabs.activate(panel.getEl().id);
33784 this.fireEvent("paneladded", this, panel);
33789 * Hides the tab for the specified panel.
33790 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
33792 hidePanel : function(panel){
33793 if(this.tabs && (panel = this.getPanel(panel))){
33794 this.tabs.hideTab(panel.getEl().id);
33799 * Unhides the tab for a previously hidden panel.
33800 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
33802 unhidePanel : function(panel){
33803 if(this.tabs && (panel = this.getPanel(panel))){
33804 this.tabs.unhideTab(panel.getEl().id);
33808 clearPanels : function(){
33809 while(this.panels.getCount() > 0){
33810 this.remove(this.panels.first());
33815 * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
33816 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
33817 * @param {Boolean} preservePanel Overrides the config preservePanel option
33818 * @return {Roo.ContentPanel} The panel that was removed
33820 remove : function(panel, preservePanel){
33821 panel = this.getPanel(panel);
33826 this.fireEvent("beforeremove", this, panel, e);
33827 if(e.cancel === true){
33830 preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
33831 var panelId = panel.getId();
33832 this.panels.removeKey(panelId);
33834 document.body.appendChild(panel.getEl().dom);
33837 this.tabs.removeTab(panel.getEl().id);
33838 }else if (!preservePanel){
33839 this.bodyEl.dom.removeChild(panel.getEl().dom);
33841 if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
33842 var p = this.panels.first();
33843 var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
33844 tempEl.appendChild(p.getEl().dom);
33845 this.bodyEl.update("");
33846 this.bodyEl.dom.appendChild(p.getEl().dom);
33848 this.updateTitle(p.getTitle());
33850 this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
33851 this.setActivePanel(p);
33853 panel.setRegion(null);
33854 if(this.activePanel == panel){
33855 this.activePanel = null;
33857 if(this.config.autoDestroy !== false && preservePanel !== true){
33858 try{panel.destroy();}catch(e){}
33860 this.fireEvent("panelremoved", this, panel);
33865 * Returns the TabPanel component used by this region
33866 * @return {Roo.TabPanel}
33868 getTabs : function(){
33872 createTool : function(parentEl, className){
33873 var btn = Roo.DomHelper.append(parentEl, {tag: "div", cls: "x-layout-tools-button",
33874 children: [{tag: "div", cls: "x-layout-tools-button-inner " + className, html: " "}]}, true);
33875 btn.addClassOnOver("x-layout-tools-button-over");
33880 * Ext JS Library 1.1.1
33881 * Copyright(c) 2006-2007, Ext JS, LLC.
33883 * Originally Released Under LGPL - original licence link has changed is not relivant.
33886 * <script type="text/javascript">
33892 * @class Roo.SplitLayoutRegion
33893 * @extends Roo.LayoutRegion
33894 * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
33896 Roo.SplitLayoutRegion = function(mgr, config, pos, cursor){
33897 this.cursor = cursor;
33898 Roo.SplitLayoutRegion.superclass.constructor.call(this, mgr, config, pos);
33901 Roo.extend(Roo.SplitLayoutRegion, Roo.LayoutRegion, {
33902 splitTip : "Drag to resize.",
33903 collapsibleSplitTip : "Drag to resize. Double click to hide.",
33904 useSplitTips : false,
33906 applyConfig : function(config){
33907 Roo.SplitLayoutRegion.superclass.applyConfig.call(this, config);
33910 var splitEl = Roo.DomHelper.append(this.mgr.el.dom,
33911 {tag: "div", id: this.el.id + "-split", cls: "x-layout-split x-layout-split-"+this.position, html: " "});
33912 /** The SplitBar for this region
33913 * @type Roo.SplitBar */
33914 this.split = new Roo.SplitBar(splitEl, this.el, this.orientation);
33915 this.split.on("moved", this.onSplitMove, this);
33916 this.split.useShim = config.useShim === true;
33917 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
33918 if(this.useSplitTips){
33919 this.split.el.dom.title = config.collapsible ? this.collapsibleSplitTip : this.splitTip;
33921 if(config.collapsible){
33922 this.split.el.on("dblclick", this.collapse, this);
33925 if(typeof config.minSize != "undefined"){
33926 this.split.minSize = config.minSize;
33928 if(typeof config.maxSize != "undefined"){
33929 this.split.maxSize = config.maxSize;
33931 if(config.hideWhenEmpty || config.hidden || config.collapsed){
33932 this.hideSplitter();
33937 getHMaxSize : function(){
33938 var cmax = this.config.maxSize || 10000;
33939 var center = this.mgr.getRegion("center");
33940 return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
33943 getVMaxSize : function(){
33944 var cmax = this.config.maxSize || 10000;
33945 var center = this.mgr.getRegion("center");
33946 return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
33949 onSplitMove : function(split, newSize){
33950 this.fireEvent("resized", this, newSize);
33954 * Returns the {@link Roo.SplitBar} for this region.
33955 * @return {Roo.SplitBar}
33957 getSplitBar : function(){
33962 this.hideSplitter();
33963 Roo.SplitLayoutRegion.superclass.hide.call(this);
33966 hideSplitter : function(){
33968 this.split.el.setLocation(-2000,-2000);
33969 this.split.el.hide();
33975 this.split.el.show();
33977 Roo.SplitLayoutRegion.superclass.show.call(this);
33980 beforeSlide: function(){
33981 if(Roo.isGecko){// firefox overflow auto bug workaround
33982 this.bodyEl.clip();
33983 if(this.tabs) this.tabs.bodyEl.clip();
33984 if(this.activePanel){
33985 this.activePanel.getEl().clip();
33987 if(this.activePanel.beforeSlide){
33988 this.activePanel.beforeSlide();
33994 afterSlide : function(){
33995 if(Roo.isGecko){// firefox overflow auto bug workaround
33996 this.bodyEl.unclip();
33997 if(this.tabs) this.tabs.bodyEl.unclip();
33998 if(this.activePanel){
33999 this.activePanel.getEl().unclip();
34000 if(this.activePanel.afterSlide){
34001 this.activePanel.afterSlide();
34007 initAutoHide : function(){
34008 if(this.autoHide !== false){
34009 if(!this.autoHideHd){
34010 var st = new Roo.util.DelayedTask(this.slideIn, this);
34011 this.autoHideHd = {
34012 "mouseout": function(e){
34013 if(!e.within(this.el, true)){
34017 "mouseover" : function(e){
34023 this.el.on(this.autoHideHd);
34027 clearAutoHide : function(){
34028 if(this.autoHide !== false){
34029 this.el.un("mouseout", this.autoHideHd.mouseout);
34030 this.el.un("mouseover", this.autoHideHd.mouseover);
34034 clearMonitor : function(){
34035 Roo.get(document).un("click", this.slideInIf, this);
34038 // these names are backwards but not changed for compat
34039 slideOut : function(){
34040 if(this.isSlid || this.el.hasActiveFx()){
34043 this.isSlid = true;
34044 if(this.collapseBtn){
34045 this.collapseBtn.hide();
34047 this.closeBtnState = this.closeBtn.getStyle('display');
34048 this.closeBtn.hide();
34050 this.stickBtn.show();
34053 this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
34054 this.beforeSlide();
34055 this.el.setStyle("z-index", 10001);
34056 this.el.slideIn(this.getSlideAnchor(), {
34057 callback: function(){
34059 this.initAutoHide();
34060 Roo.get(document).on("click", this.slideInIf, this);
34061 this.fireEvent("slideshow", this);
34068 afterSlideIn : function(){
34069 this.clearAutoHide();
34070 this.isSlid = false;
34071 this.clearMonitor();
34072 this.el.setStyle("z-index", "");
34073 if(this.collapseBtn){
34074 this.collapseBtn.show();
34076 this.closeBtn.setStyle('display', this.closeBtnState);
34078 this.stickBtn.hide();
34080 this.fireEvent("slidehide", this);
34083 slideIn : function(cb){
34084 if(!this.isSlid || this.el.hasActiveFx()){
34088 this.isSlid = false;
34089 this.beforeSlide();
34090 this.el.slideOut(this.getSlideAnchor(), {
34091 callback: function(){
34092 this.el.setLeftTop(-10000, -10000);
34094 this.afterSlideIn();
34102 slideInIf : function(e){
34103 if(!e.within(this.el)){
34108 animateCollapse : function(){
34109 this.beforeSlide();
34110 this.el.setStyle("z-index", 20000);
34111 var anchor = this.getSlideAnchor();
34112 this.el.slideOut(anchor, {
34113 callback : function(){
34114 this.el.setStyle("z-index", "");
34115 this.collapsedEl.slideIn(anchor, {duration:.3});
34117 this.el.setLocation(-10000,-10000);
34119 this.fireEvent("collapsed", this);
34126 animateExpand : function(){
34127 this.beforeSlide();
34128 this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
34129 this.el.setStyle("z-index", 20000);
34130 this.collapsedEl.hide({
34133 this.el.slideIn(this.getSlideAnchor(), {
34134 callback : function(){
34135 this.el.setStyle("z-index", "");
34138 this.split.el.show();
34140 this.fireEvent("invalidated", this);
34141 this.fireEvent("expanded", this);
34169 getAnchor : function(){
34170 return this.anchors[this.position];
34173 getCollapseAnchor : function(){
34174 return this.canchors[this.position];
34177 getSlideAnchor : function(){
34178 return this.sanchors[this.position];
34181 getAlignAdj : function(){
34182 var cm = this.cmargins;
34183 switch(this.position){
34199 getExpandAdj : function(){
34200 var c = this.collapsedEl, cm = this.cmargins;
34201 switch(this.position){
34203 return [-(cm.right+c.getWidth()+cm.left), 0];
34206 return [cm.right+c.getWidth()+cm.left, 0];
34209 return [0, -(cm.top+cm.bottom+c.getHeight())];
34212 return [0, cm.top+cm.bottom+c.getHeight()];
34218 * Ext JS Library 1.1.1
34219 * Copyright(c) 2006-2007, Ext JS, LLC.
34221 * Originally Released Under LGPL - original licence link has changed is not relivant.
34224 * <script type="text/javascript">
34227 * These classes are private internal classes
34229 Roo.CenterLayoutRegion = function(mgr, config){
34230 Roo.LayoutRegion.call(this, mgr, config, "center");
34231 this.visible = true;
34232 this.minWidth = config.minWidth || 20;
34233 this.minHeight = config.minHeight || 20;
34236 Roo.extend(Roo.CenterLayoutRegion, Roo.LayoutRegion, {
34238 // center panel can't be hidden
34242 // center panel can't be hidden
34245 getMinWidth: function(){
34246 return this.minWidth;
34249 getMinHeight: function(){
34250 return this.minHeight;
34255 Roo.NorthLayoutRegion = function(mgr, config){
34256 Roo.LayoutRegion.call(this, mgr, config, "north", "n-resize");
34258 this.split.placement = Roo.SplitBar.TOP;
34259 this.split.orientation = Roo.SplitBar.VERTICAL;
34260 this.split.el.addClass("x-layout-split-v");
34262 var size = config.initialSize || config.height;
34263 if(typeof size != "undefined"){
34264 this.el.setHeight(size);
34267 Roo.extend(Roo.NorthLayoutRegion, Roo.SplitLayoutRegion, {
34268 orientation: Roo.SplitBar.VERTICAL,
34269 getBox : function(){
34270 if(this.collapsed){
34271 return this.collapsedEl.getBox();
34273 var box = this.el.getBox();
34275 box.height += this.split.el.getHeight();
34280 updateBox : function(box){
34281 if(this.split && !this.collapsed){
34282 box.height -= this.split.el.getHeight();
34283 this.split.el.setLeft(box.x);
34284 this.split.el.setTop(box.y+box.height);
34285 this.split.el.setWidth(box.width);
34287 if(this.collapsed){
34288 this.updateBody(box.width, null);
34290 Roo.LayoutRegion.prototype.updateBox.call(this, box);
34294 Roo.SouthLayoutRegion = function(mgr, config){
34295 Roo.SplitLayoutRegion.call(this, mgr, config, "south", "s-resize");
34297 this.split.placement = Roo.SplitBar.BOTTOM;
34298 this.split.orientation = Roo.SplitBar.VERTICAL;
34299 this.split.el.addClass("x-layout-split-v");
34301 var size = config.initialSize || config.height;
34302 if(typeof size != "undefined"){
34303 this.el.setHeight(size);
34306 Roo.extend(Roo.SouthLayoutRegion, Roo.SplitLayoutRegion, {
34307 orientation: Roo.SplitBar.VERTICAL,
34308 getBox : function(){
34309 if(this.collapsed){
34310 return this.collapsedEl.getBox();
34312 var box = this.el.getBox();
34314 var sh = this.split.el.getHeight();
34321 updateBox : function(box){
34322 if(this.split && !this.collapsed){
34323 var sh = this.split.el.getHeight();
34326 this.split.el.setLeft(box.x);
34327 this.split.el.setTop(box.y-sh);
34328 this.split.el.setWidth(box.width);
34330 if(this.collapsed){
34331 this.updateBody(box.width, null);
34333 Roo.LayoutRegion.prototype.updateBox.call(this, box);
34337 Roo.EastLayoutRegion = function(mgr, config){
34338 Roo.SplitLayoutRegion.call(this, mgr, config, "east", "e-resize");
34340 this.split.placement = Roo.SplitBar.RIGHT;
34341 this.split.orientation = Roo.SplitBar.HORIZONTAL;
34342 this.split.el.addClass("x-layout-split-h");
34344 var size = config.initialSize || config.width;
34345 if(typeof size != "undefined"){
34346 this.el.setWidth(size);
34349 Roo.extend(Roo.EastLayoutRegion, Roo.SplitLayoutRegion, {
34350 orientation: Roo.SplitBar.HORIZONTAL,
34351 getBox : function(){
34352 if(this.collapsed){
34353 return this.collapsedEl.getBox();
34355 var box = this.el.getBox();
34357 var sw = 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);
34369 this.split.el.setTop(box.y);
34370 this.split.el.setHeight(box.height);
34373 if(this.collapsed){
34374 this.updateBody(null, box.height);
34376 Roo.LayoutRegion.prototype.updateBox.call(this, box);
34380 Roo.WestLayoutRegion = function(mgr, config){
34381 Roo.SplitLayoutRegion.call(this, mgr, config, "west", "w-resize");
34383 this.split.placement = Roo.SplitBar.LEFT;
34384 this.split.orientation = Roo.SplitBar.HORIZONTAL;
34385 this.split.el.addClass("x-layout-split-h");
34387 var size = config.initialSize || config.width;
34388 if(typeof size != "undefined"){
34389 this.el.setWidth(size);
34392 Roo.extend(Roo.WestLayoutRegion, Roo.SplitLayoutRegion, {
34393 orientation: Roo.SplitBar.HORIZONTAL,
34394 getBox : function(){
34395 if(this.collapsed){
34396 return this.collapsedEl.getBox();
34398 var box = this.el.getBox();
34400 box.width += this.split.el.getWidth();
34405 updateBox : function(box){
34406 if(this.split && !this.collapsed){
34407 var sw = this.split.el.getWidth();
34409 this.split.el.setLeft(box.x+box.width);
34410 this.split.el.setTop(box.y);
34411 this.split.el.setHeight(box.height);
34413 if(this.collapsed){
34414 this.updateBody(null, box.height);
34416 Roo.LayoutRegion.prototype.updateBox.call(this, box);
34421 * Ext JS Library 1.1.1
34422 * Copyright(c) 2006-2007, Ext JS, LLC.
34424 * Originally Released Under LGPL - original licence link has changed is not relivant.
34427 * <script type="text/javascript">
34432 * Private internal class for reading and applying state
34434 Roo.LayoutStateManager = function(layout){
34435 // default empty state
34444 Roo.LayoutStateManager.prototype = {
34445 init : function(layout, provider){
34446 this.provider = provider;
34447 var state = provider.get(layout.id+"-layout-state");
34449 var wasUpdating = layout.isUpdating();
34451 layout.beginUpdate();
34453 for(var key in state){
34454 if(typeof state[key] != "function"){
34455 var rstate = state[key];
34456 var r = layout.getRegion(key);
34459 r.resizeTo(rstate.size);
34461 if(rstate.collapsed == true){
34464 r.expand(null, true);
34470 layout.endUpdate();
34472 this.state = state;
34474 this.layout = layout;
34475 layout.on("regionresized", this.onRegionResized, this);
34476 layout.on("regioncollapsed", this.onRegionCollapsed, this);
34477 layout.on("regionexpanded", this.onRegionExpanded, this);
34480 storeState : function(){
34481 this.provider.set(this.layout.id+"-layout-state", this.state);
34484 onRegionResized : function(region, newSize){
34485 this.state[region.getPosition()].size = newSize;
34489 onRegionCollapsed : function(region){
34490 this.state[region.getPosition()].collapsed = true;
34494 onRegionExpanded : function(region){
34495 this.state[region.getPosition()].collapsed = false;
34500 * Ext JS Library 1.1.1
34501 * Copyright(c) 2006-2007, Ext JS, LLC.
34503 * Originally Released Under LGPL - original licence link has changed is not relivant.
34506 * <script type="text/javascript">
34509 * @class Roo.ContentPanel
34510 * @extends Roo.util.Observable
34511 * A basic ContentPanel element.
34512 * @cfg {Boolean} fitToFrame True for this panel to adjust its size to fit when the region resizes (defaults to false)
34513 * @cfg {Boolean} fitContainer When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container (defaults to false)
34514 * @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
34515 * @cfg {Boolean} closable True if the panel can be closed/removed
34516 * @cfg {Boolean} background True if the panel should not be activated when it is added (defaults to false)
34517 * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
34518 * @cfg {Toolbar} toolbar A toolbar for this panel
34519 * @cfg {Boolean} autoScroll True to scroll overflow in this panel (use with {@link #fitToFrame})
34520 * @cfg {String} title The title for this panel
34521 * @cfg {Array} adjustments Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
34522 * @cfg {String} url Calls {@link #setUrl} with this value
34523 * @cfg {String} region (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
34524 * @cfg {String/Object} params When used with {@link #url}, calls {@link #setUrl} with this value
34525 * @cfg {Boolean} loadOnce When used with {@link #url}, calls {@link #setUrl} with this value
34526 * @cfg {String} content Raw content to fill content panel with (uses setContent on construction.)
34529 * Create a new ContentPanel.
34530 * @param {String/HTMLElement/Roo.Element} el The container element for this panel
34531 * @param {String/Object} config A string to set only the title or a config object
34532 * @param {String} content (optional) Set the HTML content for this panel
34533 * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
34535 Roo.ContentPanel = function(el, config, content){
34539 if(el.autoCreate || el.xtype){ // xtype is available if this is called from factory
34543 if (config && config.parentLayout) {
34544 el = config.parentLayout.el.createChild();
34547 if(el.autoCreate){ // xtype is available if this is called from factory
34551 this.el = Roo.get(el);
34552 if(!this.el && config && config.autoCreate){
34553 if(typeof config.autoCreate == "object"){
34554 if(!config.autoCreate.id){
34555 config.autoCreate.id = config.id||el;
34557 this.el = Roo.DomHelper.append(document.body,
34558 config.autoCreate, true);
34560 this.el = Roo.DomHelper.append(document.body,
34561 {tag: "div", cls: "x-layout-inactive-content", id: config.id||el}, true);
34564 this.closable = false;
34565 this.loaded = false;
34566 this.active = false;
34567 if(typeof config == "string"){
34568 this.title = config;
34570 Roo.apply(this, config);
34573 if (this.toolbar && !this.toolbar.el && this.toolbar.xtype) {
34574 this.wrapEl = this.el.wrap();
34575 this.toolbar.container = this.el.insertSibling(false, 'before');
34576 this.toolbar = new Roo.Toolbar(this.toolbar);
34579 // xtype created footer. - not sure if will work as we normally have to render first..
34580 if (this.footer && !this.footer.el && this.footer.xtype) {
34581 if (!this.wrapEl) {
34582 this.wrapEl = this.el.wrap();
34585 this.footer.container = this.wrapEl.createChild();
34587 this.footer = Roo.factory(this.footer, Roo);
34592 this.resizeEl = Roo.get(this.resizeEl, true);
34594 this.resizeEl = this.el;
34596 // handle view.xtype
34604 * Fires when this panel is activated.
34605 * @param {Roo.ContentPanel} this
34609 * @event deactivate
34610 * Fires when this panel is activated.
34611 * @param {Roo.ContentPanel} this
34613 "deactivate" : true,
34617 * Fires when this panel is resized if fitToFrame is true.
34618 * @param {Roo.ContentPanel} this
34619 * @param {Number} width The width after any component adjustments
34620 * @param {Number} height The height after any component adjustments
34626 * Fires when this tab is created
34627 * @param {Roo.ContentPanel} this
34638 if(this.autoScroll){
34639 this.resizeEl.setStyle("overflow", "auto");
34641 // fix randome scrolling
34642 this.el.on('scroll', function() {
34643 Roo.log('fix random scolling');
34644 this.scrollTo('top',0);
34647 content = content || this.content;
34649 this.setContent(content);
34651 if(config && config.url){
34652 this.setUrl(this.url, this.params, this.loadOnce);
34657 Roo.ContentPanel.superclass.constructor.call(this);
34659 if (this.view && typeof(this.view.xtype) != 'undefined') {
34660 this.view.el = this.el.appendChild(document.createElement("div"));
34661 this.view = Roo.factory(this.view);
34662 this.view.render && this.view.render(false, '');
34666 this.fireEvent('render', this);
34669 Roo.extend(Roo.ContentPanel, Roo.util.Observable, {
34671 setRegion : function(region){
34672 this.region = region;
34674 this.el.replaceClass("x-layout-inactive-content", "x-layout-active-content");
34676 this.el.replaceClass("x-layout-active-content", "x-layout-inactive-content");
34681 * Returns the toolbar for this Panel if one was configured.
34682 * @return {Roo.Toolbar}
34684 getToolbar : function(){
34685 return this.toolbar;
34688 setActiveState : function(active){
34689 this.active = active;
34691 this.fireEvent("deactivate", this);
34693 this.fireEvent("activate", this);
34697 * Updates this panel's element
34698 * @param {String} content The new content
34699 * @param {Boolean} loadScripts (optional) true to look for and process scripts
34701 setContent : function(content, loadScripts){
34702 this.el.update(content, loadScripts);
34705 ignoreResize : function(w, h){
34706 if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
34709 this.lastSize = {width: w, height: h};
34714 * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
34715 * @return {Roo.UpdateManager} The UpdateManager
34717 getUpdateManager : function(){
34718 return this.el.getUpdateManager();
34721 * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
34722 * @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:
34725 url: "your-url.php",
34726 params: {param1: "foo", param2: "bar"}, // or a URL encoded string
34727 callback: yourFunction,
34728 scope: yourObject, //(optional scope)
34731 text: "Loading...",
34736 * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
34737 * 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.
34738 * @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}
34739 * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
34740 * @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.
34741 * @return {Roo.ContentPanel} this
34744 var um = this.el.getUpdateManager();
34745 um.update.apply(um, arguments);
34751 * 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.
34752 * @param {String/Function} url The URL to load the content from or a function to call to get the URL
34753 * @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)
34754 * @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)
34755 * @return {Roo.UpdateManager} The UpdateManager
34757 setUrl : function(url, params, loadOnce){
34758 if(this.refreshDelegate){
34759 this.removeListener("activate", this.refreshDelegate);
34761 this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
34762 this.on("activate", this.refreshDelegate);
34763 return this.el.getUpdateManager();
34766 _handleRefresh : function(url, params, loadOnce){
34767 if(!loadOnce || !this.loaded){
34768 var updater = this.el.getUpdateManager();
34769 updater.update(url, params, this._setLoaded.createDelegate(this));
34773 _setLoaded : function(){
34774 this.loaded = true;
34778 * Returns this panel's id
34781 getId : function(){
34786 * Returns this panel's element - used by regiosn to add.
34787 * @return {Roo.Element}
34789 getEl : function(){
34790 return this.wrapEl || this.el;
34793 adjustForComponents : function(width, height)
34795 //Roo.log('adjustForComponents ');
34796 if(this.resizeEl != this.el){
34797 width -= this.el.getFrameWidth('lr');
34798 height -= this.el.getFrameWidth('tb');
34801 var te = this.toolbar.getEl();
34802 height -= te.getHeight();
34803 te.setWidth(width);
34806 var te = this.footer.getEl();
34807 Roo.log("footer:" + te.getHeight());
34809 height -= te.getHeight();
34810 te.setWidth(width);
34814 if(this.adjustments){
34815 width += this.adjustments[0];
34816 height += this.adjustments[1];
34818 return {"width": width, "height": height};
34821 setSize : function(width, height){
34822 if(this.fitToFrame && !this.ignoreResize(width, height)){
34823 if(this.fitContainer && this.resizeEl != this.el){
34824 this.el.setSize(width, height);
34826 var size = this.adjustForComponents(width, height);
34827 this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
34828 this.fireEvent('resize', this, size.width, size.height);
34833 * Returns this panel's title
34836 getTitle : function(){
34841 * Set this panel's title
34842 * @param {String} title
34844 setTitle : function(title){
34845 this.title = title;
34847 this.region.updatePanelTitle(this, title);
34852 * Returns true is this panel was configured to be closable
34853 * @return {Boolean}
34855 isClosable : function(){
34856 return this.closable;
34859 beforeSlide : function(){
34861 this.resizeEl.clip();
34864 afterSlide : function(){
34866 this.resizeEl.unclip();
34870 * Force a content refresh from the URL specified in the {@link #setUrl} method.
34871 * Will fail silently if the {@link #setUrl} method has not been called.
34872 * This does not activate the panel, just updates its content.
34874 refresh : function(){
34875 if(this.refreshDelegate){
34876 this.loaded = false;
34877 this.refreshDelegate();
34882 * Destroys this panel
34884 destroy : function(){
34885 this.el.removeAllListeners();
34886 var tempEl = document.createElement("span");
34887 tempEl.appendChild(this.el.dom);
34888 tempEl.innerHTML = "";
34894 * form - if the content panel contains a form - this is a reference to it.
34895 * @type {Roo.form.Form}
34899 * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
34900 * This contains a reference to it.
34906 * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
34916 * @param {Object} cfg Xtype definition of item to add.
34919 addxtype : function(cfg) {
34921 if (cfg.xtype.match(/^Form$/)) {
34924 //if (this.footer) {
34925 // el = this.footer.container.insertSibling(false, 'before');
34927 el = this.el.createChild();
34930 this.form = new Roo.form.Form(cfg);
34933 if ( this.form.allItems.length) this.form.render(el.dom);
34936 // should only have one of theses..
34937 if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
34938 // views.. should not be just added - used named prop 'view''
34940 cfg.el = this.el.appendChild(document.createElement("div"));
34943 var ret = new Roo.factory(cfg);
34945 ret.render && ret.render(false, ''); // render blank..
34954 * @class Roo.GridPanel
34955 * @extends Roo.ContentPanel
34957 * Create a new GridPanel.
34958 * @param {Roo.grid.Grid} grid The grid for this panel
34959 * @param {String/Object} config A string to set only the panel's title, or a config object
34961 Roo.GridPanel = function(grid, config){
34964 this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
34965 {tag: "div", cls: "x-layout-grid-wrapper x-layout-inactive-content"}, true);
34967 this.wrapper.dom.appendChild(grid.getGridEl().dom);
34969 Roo.GridPanel.superclass.constructor.call(this, this.wrapper, config);
34972 this.toolbar.el.insertBefore(this.wrapper.dom.firstChild);
34974 // xtype created footer. - not sure if will work as we normally have to render first..
34975 if (this.footer && !this.footer.el && this.footer.xtype) {
34977 this.footer.container = this.grid.getView().getFooterPanel(true);
34978 this.footer.dataSource = this.grid.dataSource;
34979 this.footer = Roo.factory(this.footer, Roo);
34983 grid.monitorWindowResize = false; // turn off autosizing
34984 grid.autoHeight = false;
34985 grid.autoWidth = false;
34987 this.grid.getGridEl().replaceClass("x-layout-inactive-content", "x-layout-component-panel");
34990 Roo.extend(Roo.GridPanel, Roo.ContentPanel, {
34991 getId : function(){
34992 return this.grid.id;
34996 * Returns the grid for this panel
34997 * @return {Roo.grid.Grid}
34999 getGrid : function(){
35003 setSize : function(width, height){
35004 if(!this.ignoreResize(width, height)){
35005 var grid = this.grid;
35006 var size = this.adjustForComponents(width, height);
35007 grid.getGridEl().setSize(size.width, size.height);
35012 beforeSlide : function(){
35013 this.grid.getView().scroller.clip();
35016 afterSlide : function(){
35017 this.grid.getView().scroller.unclip();
35020 destroy : function(){
35021 this.grid.destroy();
35023 Roo.GridPanel.superclass.destroy.call(this);
35029 * @class Roo.NestedLayoutPanel
35030 * @extends Roo.ContentPanel
35032 * Create a new NestedLayoutPanel.
35035 * @param {Roo.BorderLayout} layout The layout for this panel
35036 * @param {String/Object} config A string to set only the title or a config object
35038 Roo.NestedLayoutPanel = function(layout, config)
35040 // construct with only one argument..
35041 /* FIXME - implement nicer consturctors
35042 if (layout.layout) {
35044 layout = config.layout;
35045 delete config.layout;
35047 if (layout.xtype && !layout.getEl) {
35048 // then layout needs constructing..
35049 layout = Roo.factory(layout, Roo);
35054 Roo.NestedLayoutPanel.superclass.constructor.call(this, layout.getEl(), config);
35056 layout.monitorWindowResize = false; // turn off autosizing
35057 this.layout = layout;
35058 this.layout.getEl().addClass("x-layout-nested-layout");
35065 Roo.extend(Roo.NestedLayoutPanel, Roo.ContentPanel, {
35067 setSize : function(width, height){
35068 if(!this.ignoreResize(width, height)){
35069 var size = this.adjustForComponents(width, height);
35070 var el = this.layout.getEl();
35071 el.setSize(size.width, size.height);
35072 var touch = el.dom.offsetWidth;
35073 this.layout.layout();
35074 // ie requires a double layout on the first pass
35075 if(Roo.isIE && !this.initialized){
35076 this.initialized = true;
35077 this.layout.layout();
35082 // activate all subpanels if not currently active..
35084 setActiveState : function(active){
35085 this.active = active;
35087 this.fireEvent("deactivate", this);
35091 this.fireEvent("activate", this);
35092 // not sure if this should happen before or after..
35093 if (!this.layout) {
35094 return; // should not happen..
35097 for (var r in this.layout.regions) {
35098 reg = this.layout.getRegion(r);
35099 if (reg.getActivePanel()) {
35100 //reg.showPanel(reg.getActivePanel()); // force it to activate..
35101 reg.setActivePanel(reg.getActivePanel());
35104 if (!reg.panels.length) {
35107 reg.showPanel(reg.getPanel(0));
35116 * Returns the nested BorderLayout for this panel
35117 * @return {Roo.BorderLayout}
35119 getLayout : function(){
35120 return this.layout;
35124 * Adds a xtype elements to the layout of the nested panel
35128 xtype : 'ContentPanel',
35135 xtype : 'NestedLayoutPanel',
35141 items : [ ... list of content panels or nested layout panels.. ]
35145 * @param {Object} cfg Xtype definition of item to add.
35147 addxtype : function(cfg) {
35148 return this.layout.addxtype(cfg);
35153 Roo.ScrollPanel = function(el, config, content){
35154 config = config || {};
35155 config.fitToFrame = true;
35156 Roo.ScrollPanel.superclass.constructor.call(this, el, config, content);
35158 this.el.dom.style.overflow = "hidden";
35159 var wrap = this.el.wrap({cls: "x-scroller x-layout-inactive-content"});
35160 this.el.removeClass("x-layout-inactive-content");
35161 this.el.on("mousewheel", this.onWheel, this);
35163 var up = wrap.createChild({cls: "x-scroller-up", html: " "}, this.el.dom);
35164 var down = wrap.createChild({cls: "x-scroller-down", html: " "});
35165 up.unselectable(); down.unselectable();
35166 up.on("click", this.scrollUp, this);
35167 down.on("click", this.scrollDown, this);
35168 up.addClassOnOver("x-scroller-btn-over");
35169 down.addClassOnOver("x-scroller-btn-over");
35170 up.addClassOnClick("x-scroller-btn-click");
35171 down.addClassOnClick("x-scroller-btn-click");
35172 this.adjustments = [0, -(up.getHeight() + down.getHeight())];
35174 this.resizeEl = this.el;
35175 this.el = wrap; this.up = up; this.down = down;
35178 Roo.extend(Roo.ScrollPanel, Roo.ContentPanel, {
35180 wheelIncrement : 5,
35181 scrollUp : function(){
35182 this.resizeEl.scroll("up", this.increment, {callback: this.afterScroll, scope: this});
35185 scrollDown : function(){
35186 this.resizeEl.scroll("down", this.increment, {callback: this.afterScroll, scope: this});
35189 afterScroll : function(){
35190 var el = this.resizeEl;
35191 var t = el.dom.scrollTop, h = el.dom.scrollHeight, ch = el.dom.clientHeight;
35192 this.up[t == 0 ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
35193 this.down[h - t <= ch ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
35196 setSize : function(){
35197 Roo.ScrollPanel.superclass.setSize.apply(this, arguments);
35198 this.afterScroll();
35201 onWheel : function(e){
35202 var d = e.getWheelDelta();
35203 this.resizeEl.dom.scrollTop -= (d*this.wheelIncrement);
35204 this.afterScroll();
35208 setContent : function(content, loadScripts){
35209 this.resizeEl.update(content, loadScripts);
35223 * @class Roo.TreePanel
35224 * @extends Roo.ContentPanel
35226 * Create a new TreePanel. - defaults to fit/scoll contents.
35227 * @param {String/Object} config A string to set only the panel's title, or a config object
35228 * @cfg {Roo.tree.TreePanel} tree The tree TreePanel, with config etc.
35230 Roo.TreePanel = function(config){
35231 var el = config.el;
35232 var tree = config.tree;
35233 delete config.tree;
35234 delete config.el; // hopefull!
35236 // wrapper for IE7 strict & safari scroll issue
35238 var treeEl = el.createChild();
35239 config.resizeEl = treeEl;
35243 Roo.TreePanel.superclass.constructor.call(this, el, config);
35246 this.tree = new Roo.tree.TreePanel(treeEl , tree);
35247 //console.log(tree);
35248 this.on('activate', function()
35250 if (this.tree.rendered) {
35253 //console.log('render tree');
35254 this.tree.render();
35256 // this should not be needed.. - it's actually the 'el' that resizes?
35257 // actuall it breaks the containerScroll - dragging nodes auto scroll at top
35259 //this.on('resize', function (cp, w, h) {
35260 // this.tree.innerCt.setWidth(w);
35261 // this.tree.innerCt.setHeight(h);
35262 // //this.tree.innerCt.setStyle('overflow-y', 'auto');
35269 Roo.extend(Roo.TreePanel, Roo.ContentPanel, {
35286 * Ext JS Library 1.1.1
35287 * Copyright(c) 2006-2007, Ext JS, LLC.
35289 * Originally Released Under LGPL - original licence link has changed is not relivant.
35292 * <script type="text/javascript">
35297 * @class Roo.ReaderLayout
35298 * @extends Roo.BorderLayout
35299 * This is a pre-built layout that represents a classic, 5-pane application. It consists of a header, a primary
35300 * center region containing two nested regions (a top one for a list view and one for item preview below),
35301 * and regions on either side that can be used for navigation, application commands, informational displays, etc.
35302 * The setup and configuration work exactly the same as it does for a {@link Roo.BorderLayout} - this class simply
35303 * expedites the setup of the overall layout and regions for this common application style.
35306 var reader = new Roo.ReaderLayout();
35307 var CP = Roo.ContentPanel; // shortcut for adding
35309 reader.beginUpdate();
35310 reader.add("north", new CP("north", "North"));
35311 reader.add("west", new CP("west", {title: "West"}));
35312 reader.add("east", new CP("east", {title: "East"}));
35314 reader.regions.listView.add(new CP("listView", "List"));
35315 reader.regions.preview.add(new CP("preview", "Preview"));
35316 reader.endUpdate();
35319 * Create a new ReaderLayout
35320 * @param {Object} config Configuration options
35321 * @param {String/HTMLElement/Element} container (optional) The container this layout is bound to (defaults to
35322 * document.body if omitted)
35324 Roo.ReaderLayout = function(config, renderTo){
35325 var c = config || {size:{}};
35326 Roo.ReaderLayout.superclass.constructor.call(this, renderTo || document.body, {
35327 north: c.north !== false ? Roo.apply({
35331 }, c.north) : false,
35332 west: c.west !== false ? Roo.apply({
35340 margins:{left:5,right:0,bottom:5,top:5},
35341 cmargins:{left:5,right:5,bottom:5,top:5}
35342 }, c.west) : false,
35343 east: c.east !== false ? Roo.apply({
35351 margins:{left:0,right:5,bottom:5,top:5},
35352 cmargins:{left:5,right:5,bottom:5,top:5}
35353 }, c.east) : false,
35354 center: Roo.apply({
35355 tabPosition: 'top',
35359 margins:{left:c.west!==false ? 0 : 5,right:c.east!==false ? 0 : 5,bottom:5,top:2}
35363 this.el.addClass('x-reader');
35365 this.beginUpdate();
35367 var inner = new Roo.BorderLayout(Roo.get(document.body).createChild(), {
35368 south: c.preview !== false ? Roo.apply({
35375 cmargins:{top:5,left:0, right:0, bottom:0}
35376 }, c.preview) : false,
35377 center: Roo.apply({
35383 this.add('center', new Roo.NestedLayoutPanel(inner,
35384 Roo.apply({title: c.mainTitle || '',tabTip:''},c.innerPanelCfg)));
35388 this.regions.preview = inner.getRegion('south');
35389 this.regions.listView = inner.getRegion('center');
35392 Roo.extend(Roo.ReaderLayout, Roo.BorderLayout);/*
35394 * Ext JS Library 1.1.1
35395 * Copyright(c) 2006-2007, Ext JS, LLC.
35397 * Originally Released Under LGPL - original licence link has changed is not relivant.
35400 * <script type="text/javascript">
35404 * @class Roo.grid.Grid
35405 * @extends Roo.util.Observable
35406 * This class represents the primary interface of a component based grid control.
35407 * <br><br>Usage:<pre><code>
35408 var grid = new Roo.grid.Grid("my-container-id", {
35411 selModel: mySelectionModel,
35412 autoSizeColumns: true,
35413 monitorWindowResize: false,
35414 trackMouseOver: true
35419 * <b>Common Problems:</b><br/>
35420 * - Grid does not resize properly when going smaller: Setting overflow hidden on the container
35421 * element will correct this<br/>
35422 * - If you get el.style[camel]= NaNpx or -2px or something related, be certain you have given your container element
35423 * dimensions. The grid adapts to your container's size, if your container has no size defined then the results
35424 * are unpredictable.<br/>
35425 * - Do not render the grid into an element with display:none. Try using visibility:hidden. Otherwise there is no way for the
35426 * grid to calculate dimensions/offsets.<br/>
35428 * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
35429 * The container MUST have some type of size defined for the grid to fill. The container will be
35430 * automatically set to position relative if it isn't already.
35431 * @param {Object} config A config object that sets properties on this grid.
35433 Roo.grid.Grid = function(container, config){
35434 // initialize the container
35435 this.container = Roo.get(container);
35436 this.container.update("");
35437 this.container.setStyle("overflow", "hidden");
35438 this.container.addClass('x-grid-container');
35440 this.id = this.container.id;
35442 Roo.apply(this, config);
35443 // check and correct shorthanded configs
35445 this.dataSource = this.ds;
35449 this.colModel = this.cm;
35453 this.selModel = this.sm;
35457 if (this.selModel) {
35458 this.selModel = Roo.factory(this.selModel, Roo.grid);
35459 this.sm = this.selModel;
35460 this.sm.xmodule = this.xmodule || false;
35462 if (typeof(this.colModel.config) == 'undefined') {
35463 this.colModel = new Roo.grid.ColumnModel(this.colModel);
35464 this.cm = this.colModel;
35465 this.cm.xmodule = this.xmodule || false;
35467 if (this.dataSource) {
35468 this.dataSource= Roo.factory(this.dataSource, Roo.data);
35469 this.ds = this.dataSource;
35470 this.ds.xmodule = this.xmodule || false;
35477 this.container.setWidth(this.width);
35481 this.container.setHeight(this.height);
35488 * The raw click event for the entire grid.
35489 * @param {Roo.EventObject} e
35494 * The raw dblclick event for the entire grid.
35495 * @param {Roo.EventObject} e
35499 * @event contextmenu
35500 * The raw contextmenu event for the entire grid.
35501 * @param {Roo.EventObject} e
35503 "contextmenu" : true,
35506 * The raw mousedown event for the entire grid.
35507 * @param {Roo.EventObject} e
35509 "mousedown" : true,
35512 * The raw mouseup event for the entire grid.
35513 * @param {Roo.EventObject} e
35518 * The raw mouseover event for the entire grid.
35519 * @param {Roo.EventObject} e
35521 "mouseover" : true,
35524 * The raw mouseout event for the entire grid.
35525 * @param {Roo.EventObject} e
35530 * The raw keypress event for the entire grid.
35531 * @param {Roo.EventObject} e
35536 * The raw keydown event for the entire grid.
35537 * @param {Roo.EventObject} e
35545 * Fires when a cell is clicked
35546 * @param {Grid} this
35547 * @param {Number} rowIndex
35548 * @param {Number} columnIndex
35549 * @param {Roo.EventObject} e
35551 "cellclick" : true,
35553 * @event celldblclick
35554 * Fires when a cell is double clicked
35555 * @param {Grid} this
35556 * @param {Number} rowIndex
35557 * @param {Number} columnIndex
35558 * @param {Roo.EventObject} e
35560 "celldblclick" : true,
35563 * Fires when a row is clicked
35564 * @param {Grid} this
35565 * @param {Number} rowIndex
35566 * @param {Roo.EventObject} e
35570 * @event rowdblclick
35571 * Fires when a row is double clicked
35572 * @param {Grid} this
35573 * @param {Number} rowIndex
35574 * @param {Roo.EventObject} e
35576 "rowdblclick" : true,
35578 * @event headerclick
35579 * Fires when a header is clicked
35580 * @param {Grid} this
35581 * @param {Number} columnIndex
35582 * @param {Roo.EventObject} e
35584 "headerclick" : true,
35586 * @event headerdblclick
35587 * Fires when a header cell is double clicked
35588 * @param {Grid} this
35589 * @param {Number} columnIndex
35590 * @param {Roo.EventObject} e
35592 "headerdblclick" : true,
35594 * @event rowcontextmenu
35595 * Fires when a row is right clicked
35596 * @param {Grid} this
35597 * @param {Number} rowIndex
35598 * @param {Roo.EventObject} e
35600 "rowcontextmenu" : true,
35602 * @event cellcontextmenu
35603 * Fires when a cell is right clicked
35604 * @param {Grid} this
35605 * @param {Number} rowIndex
35606 * @param {Number} cellIndex
35607 * @param {Roo.EventObject} e
35609 "cellcontextmenu" : true,
35611 * @event headercontextmenu
35612 * Fires when a header is right clicked
35613 * @param {Grid} this
35614 * @param {Number} columnIndex
35615 * @param {Roo.EventObject} e
35617 "headercontextmenu" : true,
35619 * @event bodyscroll
35620 * Fires when the body element is scrolled
35621 * @param {Number} scrollLeft
35622 * @param {Number} scrollTop
35624 "bodyscroll" : true,
35626 * @event columnresize
35627 * Fires when the user resizes a column
35628 * @param {Number} columnIndex
35629 * @param {Number} newSize
35631 "columnresize" : true,
35633 * @event columnmove
35634 * Fires when the user moves a column
35635 * @param {Number} oldIndex
35636 * @param {Number} newIndex
35638 "columnmove" : true,
35641 * Fires when row(s) start being dragged
35642 * @param {Grid} this
35643 * @param {Roo.GridDD} dd The drag drop object
35644 * @param {event} e The raw browser event
35646 "startdrag" : true,
35649 * Fires when a drag operation is complete
35650 * @param {Grid} this
35651 * @param {Roo.GridDD} dd The drag drop object
35652 * @param {event} e The raw browser event
35657 * Fires when dragged row(s) are dropped on a valid DD target
35658 * @param {Grid} this
35659 * @param {Roo.GridDD} dd The drag drop object
35660 * @param {String} targetId The target drag drop object
35661 * @param {event} e The raw browser event
35666 * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
35667 * @param {Grid} this
35668 * @param {Roo.GridDD} dd The drag drop object
35669 * @param {String} targetId The target drag drop object
35670 * @param {event} e The raw browser event
35675 * Fires when the dragged row(s) first cross another DD target while being dragged
35676 * @param {Grid} this
35677 * @param {Roo.GridDD} dd The drag drop object
35678 * @param {String} targetId The target drag drop object
35679 * @param {event} e The raw browser event
35681 "dragenter" : true,
35684 * Fires when the dragged row(s) leave another DD target while being dragged
35685 * @param {Grid} this
35686 * @param {Roo.GridDD} dd The drag drop object
35687 * @param {String} targetId The target drag drop object
35688 * @param {event} e The raw browser event
35693 * Fires when a row is rendered, so you can change add a style to it.
35694 * @param {GridView} gridview The grid view
35695 * @param {Object} rowcfg contains record rowIndex and rowClass - set rowClass to add a style.
35701 * Fires when the grid is rendered
35702 * @param {Grid} grid
35707 Roo.grid.Grid.superclass.constructor.call(this);
35709 Roo.extend(Roo.grid.Grid, Roo.util.Observable, {
35712 * @cfg {String} ddGroup - drag drop group.
35716 * @cfg {Number} minColumnWidth The minimum width a column can be resized to. Default is 25.
35718 minColumnWidth : 25,
35721 * @cfg {Boolean} autoSizeColumns True to automatically resize the columns to fit their content
35722 * <b>on initial render.</b> It is more efficient to explicitly size the columns
35723 * through the ColumnModel's {@link Roo.grid.ColumnModel#width} config option. Default is false.
35725 autoSizeColumns : false,
35728 * @cfg {Boolean} autoSizeHeaders True to measure headers with column data when auto sizing columns. Default is true.
35730 autoSizeHeaders : true,
35733 * @cfg {Boolean} monitorWindowResize True to autoSize the grid when the window resizes. Default is true.
35735 monitorWindowResize : true,
35738 * @cfg {Boolean} maxRowsToMeasure If autoSizeColumns is on, maxRowsToMeasure can be used to limit the number of
35739 * rows measured to get a columns size. Default is 0 (all rows).
35741 maxRowsToMeasure : 0,
35744 * @cfg {Boolean} trackMouseOver True to highlight rows when the mouse is over. Default is true.
35746 trackMouseOver : true,
35749 * @cfg {Boolean} enableDrag True to enable drag of rows. Default is false. (double check if this is needed?)
35753 * @cfg {Boolean} enableDragDrop True to enable drag and drop of rows. Default is false.
35755 enableDragDrop : false,
35758 * @cfg {Boolean} enableColumnMove True to enable drag and drop reorder of columns. Default is true.
35760 enableColumnMove : true,
35763 * @cfg {Boolean} enableColumnHide True to enable hiding of columns with the header context menu. Default is true.
35765 enableColumnHide : true,
35768 * @cfg {Boolean} enableRowHeightSync True to manually sync row heights across locked and not locked rows. Default is false.
35770 enableRowHeightSync : false,
35773 * @cfg {Boolean} stripeRows True to stripe the rows. Default is true.
35778 * @cfg {Boolean} autoHeight True to fit the height of the grid container to the height of the data. Default is false.
35780 autoHeight : false,
35783 * @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.
35785 autoExpandColumn : false,
35788 * @cfg {Number} autoExpandMin The minimum width the autoExpandColumn can have (if enabled).
35791 autoExpandMin : 50,
35794 * @cfg {Number} autoExpandMax The maximum width the autoExpandColumn can have (if enabled). Default is 1000.
35796 autoExpandMax : 1000,
35799 * @cfg {Object} view The {@link Roo.grid.GridView} used by the grid. This can be set before a call to render().
35804 * @cfg {Object} loadMask An {@link Roo.LoadMask} config or true to mask the grid while loading. Default is false.
35808 * @cfg {Roo.dd.DropTarget} dropTarget An {@link Roo.dd.DropTarget} config
35818 * @cfg {Boolean} autoWidth True to set the grid's width to the default total width of the grid's columns instead
35819 * of a fixed width. Default is false.
35822 * @cfg {Number} maxHeight Sets the maximum height of the grid - ignored if autoHeight is not on.
35825 * Called once after all setup has been completed and the grid is ready to be rendered.
35826 * @return {Roo.grid.Grid} this
35828 render : function()
35830 var c = this.container;
35831 // try to detect autoHeight/width mode
35832 if((!c.dom.offsetHeight || c.dom.offsetHeight < 20) || c.getStyle("height") == "auto"){
35833 this.autoHeight = true;
35835 var view = this.getView();
35838 c.on("click", this.onClick, this);
35839 c.on("dblclick", this.onDblClick, this);
35840 c.on("contextmenu", this.onContextMenu, this);
35841 c.on("keydown", this.onKeyDown, this);
35843 c.on("touchstart", this.onTouchStart, this);
35846 this.relayEvents(c, ["mousedown","mouseup","mouseover","mouseout","keypress"]);
35848 this.getSelectionModel().init(this);
35853 this.loadMask = new Roo.LoadMask(this.container,
35854 Roo.apply({store:this.dataSource}, this.loadMask));
35858 if (this.toolbar && this.toolbar.xtype) {
35859 this.toolbar.container = this.getView().getHeaderPanel(true);
35860 this.toolbar = new Roo.Toolbar(this.toolbar);
35862 if (this.footer && this.footer.xtype) {
35863 this.footer.dataSource = this.getDataSource();
35864 this.footer.container = this.getView().getFooterPanel(true);
35865 this.footer = Roo.factory(this.footer, Roo);
35867 if (this.dropTarget && this.dropTarget.xtype) {
35868 delete this.dropTarget.xtype;
35869 this.dropTarget = new Roo.dd.DropTarget(this.getView().mainBody, this.dropTarget);
35873 this.rendered = true;
35874 this.fireEvent('render', this);
35879 * Reconfigures the grid to use a different Store and Column Model.
35880 * The View will be bound to the new objects and refreshed.
35881 * @param {Roo.data.Store} dataSource The new {@link Roo.data.Store} object
35882 * @param {Roo.grid.ColumnModel} The new {@link Roo.grid.ColumnModel} object
35884 reconfigure : function(dataSource, colModel){
35886 this.loadMask.destroy();
35887 this.loadMask = new Roo.LoadMask(this.container,
35888 Roo.apply({store:dataSource}, this.loadMask));
35890 this.view.bind(dataSource, colModel);
35891 this.dataSource = dataSource;
35892 this.colModel = colModel;
35893 this.view.refresh(true);
35897 onKeyDown : function(e){
35898 this.fireEvent("keydown", e);
35902 * Destroy this grid.
35903 * @param {Boolean} removeEl True to remove the element
35905 destroy : function(removeEl, keepListeners){
35907 this.loadMask.destroy();
35909 var c = this.container;
35910 c.removeAllListeners();
35911 this.view.destroy();
35912 this.colModel.purgeListeners();
35913 if(!keepListeners){
35914 this.purgeListeners();
35917 if(removeEl === true){
35923 processEvent : function(name, e){
35924 // does this fire select???
35925 Roo.log('grid:processEvent ' + name);
35927 if (name != 'touchstart' ) {
35928 this.fireEvent(name, e);
35931 var t = e.getTarget();
35933 var header = v.findHeaderIndex(t);
35934 if(header !== false){
35935 this.fireEvent("header" + (name == 'touchstart' ? 'click' : name), this, header, e);
35937 var row = v.findRowIndex(t);
35938 var cell = v.findCellIndex(t);
35939 if (name == 'touchstart') {
35940 // first touch is always a click.
35941 // hopefull this happens after selection is updated.?
35944 if (typeof(this.selModel.getSelectedCell) != 'undefined') {
35945 var cs = this.selModel.getSelectedCell();
35946 if (row == cs[0] && cell == cs[1]){
35950 if (typeof(this.selModel.getSelections) != 'undefined') {
35951 var cs = this.selModel.getSelections();
35952 var ds = this.dataSource;
35953 if (cs.length == 1 && ds.getAt(row) == cs[0]){
35964 this.fireEvent("row" + name, this, row, e);
35965 if(cell !== false){
35966 this.fireEvent("cell" + name, this, row, cell, e);
35973 onClick : function(e){
35974 this.processEvent("click", e);
35977 onTouchStart : function(e){
35978 this.processEvent("touchstart", e);
35982 onContextMenu : function(e, t){
35983 this.processEvent("contextmenu", e);
35987 onDblClick : function(e){
35988 this.processEvent("dblclick", e);
35992 walkCells : function(row, col, step, fn, scope){
35993 var cm = this.colModel, clen = cm.getColumnCount();
35994 var ds = this.dataSource, rlen = ds.getCount(), first = true;
36006 if(fn.call(scope || this, row, col, cm) === true){
36024 if(fn.call(scope || this, row, col, cm) === true){
36036 getSelections : function(){
36037 return this.selModel.getSelections();
36041 * Causes the grid to manually recalculate its dimensions. Generally this is done automatically,
36042 * but if manual update is required this method will initiate it.
36044 autoSize : function(){
36046 this.view.layout();
36047 if(this.view.adjustForScroll){
36048 this.view.adjustForScroll();
36054 * Returns the grid's underlying element.
36055 * @return {Element} The element
36057 getGridEl : function(){
36058 return this.container;
36061 // private for compatibility, overridden by editor grid
36062 stopEditing : function(){},
36065 * Returns the grid's SelectionModel.
36066 * @return {SelectionModel}
36068 getSelectionModel : function(){
36069 if(!this.selModel){
36070 this.selModel = new Roo.grid.RowSelectionModel();
36072 return this.selModel;
36076 * Returns the grid's DataSource.
36077 * @return {DataSource}
36079 getDataSource : function(){
36080 return this.dataSource;
36084 * Returns the grid's ColumnModel.
36085 * @return {ColumnModel}
36087 getColumnModel : function(){
36088 return this.colModel;
36092 * Returns the grid's GridView object.
36093 * @return {GridView}
36095 getView : function(){
36097 this.view = new Roo.grid.GridView(this.viewConfig);
36102 * Called to get grid's drag proxy text, by default returns this.ddText.
36105 getDragDropText : function(){
36106 var count = this.selModel.getCount();
36107 return String.format(this.ddText, count, count == 1 ? '' : 's');
36111 * Configures the text is the drag proxy (defaults to "%0 selected row(s)").
36112 * %0 is replaced with the number of selected rows.
36115 Roo.grid.Grid.prototype.ddText = "{0} selected row{1}";/*
36117 * Ext JS Library 1.1.1
36118 * Copyright(c) 2006-2007, Ext JS, LLC.
36120 * Originally Released Under LGPL - original licence link has changed is not relivant.
36123 * <script type="text/javascript">
36126 Roo.grid.AbstractGridView = function(){
36130 "beforerowremoved" : true,
36131 "beforerowsinserted" : true,
36132 "beforerefresh" : true,
36133 "rowremoved" : true,
36134 "rowsinserted" : true,
36135 "rowupdated" : true,
36138 Roo.grid.AbstractGridView.superclass.constructor.call(this);
36141 Roo.extend(Roo.grid.AbstractGridView, Roo.util.Observable, {
36142 rowClass : "x-grid-row",
36143 cellClass : "x-grid-cell",
36144 tdClass : "x-grid-td",
36145 hdClass : "x-grid-hd",
36146 splitClass : "x-grid-hd-split",
36148 init: function(grid){
36150 var cid = this.grid.getGridEl().id;
36151 this.colSelector = "#" + cid + " ." + this.cellClass + "-";
36152 this.tdSelector = "#" + cid + " ." + this.tdClass + "-";
36153 this.hdSelector = "#" + cid + " ." + this.hdClass + "-";
36154 this.splitSelector = "#" + cid + " ." + this.splitClass + "-";
36157 getColumnRenderers : function(){
36158 var renderers = [];
36159 var cm = this.grid.colModel;
36160 var colCount = cm.getColumnCount();
36161 for(var i = 0; i < colCount; i++){
36162 renderers[i] = cm.getRenderer(i);
36167 getColumnIds : function(){
36169 var cm = this.grid.colModel;
36170 var colCount = cm.getColumnCount();
36171 for(var i = 0; i < colCount; i++){
36172 ids[i] = cm.getColumnId(i);
36177 getDataIndexes : function(){
36178 if(!this.indexMap){
36179 this.indexMap = this.buildIndexMap();
36181 return this.indexMap.colToData;
36184 getColumnIndexByDataIndex : function(dataIndex){
36185 if(!this.indexMap){
36186 this.indexMap = this.buildIndexMap();
36188 return this.indexMap.dataToCol[dataIndex];
36192 * Set a css style for a column dynamically.
36193 * @param {Number} colIndex The index of the column
36194 * @param {String} name The css property name
36195 * @param {String} value The css value
36197 setCSSStyle : function(colIndex, name, value){
36198 var selector = "#" + this.grid.id + " .x-grid-col-" + colIndex;
36199 Roo.util.CSS.updateRule(selector, name, value);
36202 generateRules : function(cm){
36203 var ruleBuf = [], rulesId = this.grid.id + '-cssrules';
36204 Roo.util.CSS.removeStyleSheet(rulesId);
36205 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
36206 var cid = cm.getColumnId(i);
36207 ruleBuf.push(this.colSelector, cid, " {\n", cm.config[i].css, "}\n",
36208 this.tdSelector, cid, " {\n}\n",
36209 this.hdSelector, cid, " {\n}\n",
36210 this.splitSelector, cid, " {\n}\n");
36212 return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
36216 * Ext JS Library 1.1.1
36217 * Copyright(c) 2006-2007, Ext JS, LLC.
36219 * Originally Released Under LGPL - original licence link has changed is not relivant.
36222 * <script type="text/javascript">
36226 // This is a support class used internally by the Grid components
36227 Roo.grid.HeaderDragZone = function(grid, hd, hd2){
36229 this.view = grid.getView();
36230 this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
36231 Roo.grid.HeaderDragZone.superclass.constructor.call(this, hd);
36233 this.setHandleElId(Roo.id(hd));
36234 this.setOuterHandleElId(Roo.id(hd2));
36236 this.scroll = false;
36238 Roo.extend(Roo.grid.HeaderDragZone, Roo.dd.DragZone, {
36240 getDragData : function(e){
36241 var t = Roo.lib.Event.getTarget(e);
36242 var h = this.view.findHeaderCell(t);
36244 return {ddel: h.firstChild, header:h};
36249 onInitDrag : function(e){
36250 this.view.headersDisabled = true;
36251 var clone = this.dragData.ddel.cloneNode(true);
36252 clone.id = Roo.id();
36253 clone.style.width = Math.min(this.dragData.header.offsetWidth,this.maxDragWidth) + "px";
36254 this.proxy.update(clone);
36258 afterValidDrop : function(){
36260 setTimeout(function(){
36261 v.headersDisabled = false;
36265 afterInvalidDrop : function(){
36267 setTimeout(function(){
36268 v.headersDisabled = false;
36274 * Ext JS Library 1.1.1
36275 * Copyright(c) 2006-2007, Ext JS, LLC.
36277 * Originally Released Under LGPL - original licence link has changed is not relivant.
36280 * <script type="text/javascript">
36283 // This is a support class used internally by the Grid components
36284 Roo.grid.HeaderDropZone = function(grid, hd, hd2){
36286 this.view = grid.getView();
36287 // split the proxies so they don't interfere with mouse events
36288 this.proxyTop = Roo.DomHelper.append(document.body, {
36289 cls:"col-move-top", html:" "
36291 this.proxyBottom = Roo.DomHelper.append(document.body, {
36292 cls:"col-move-bottom", html:" "
36294 this.proxyTop.hide = this.proxyBottom.hide = function(){
36295 this.setLeftTop(-100,-100);
36296 this.setStyle("visibility", "hidden");
36298 this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
36299 // temporarily disabled
36300 //Roo.dd.ScrollManager.register(this.view.scroller.dom);
36301 Roo.grid.HeaderDropZone.superclass.constructor.call(this, grid.getGridEl().dom);
36303 Roo.extend(Roo.grid.HeaderDropZone, Roo.dd.DropZone, {
36304 proxyOffsets : [-4, -9],
36305 fly: Roo.Element.fly,
36307 getTargetFromEvent : function(e){
36308 var t = Roo.lib.Event.getTarget(e);
36309 var cindex = this.view.findCellIndex(t);
36310 if(cindex !== false){
36311 return this.view.getHeaderCell(cindex);
36316 nextVisible : function(h){
36317 var v = this.view, cm = this.grid.colModel;
36320 if(!cm.isHidden(v.getCellIndex(h))){
36328 prevVisible : function(h){
36329 var v = this.view, cm = this.grid.colModel;
36332 if(!cm.isHidden(v.getCellIndex(h))){
36340 positionIndicator : function(h, n, e){
36341 var x = Roo.lib.Event.getPageX(e);
36342 var r = Roo.lib.Dom.getRegion(n.firstChild);
36343 var px, pt, py = r.top + this.proxyOffsets[1];
36344 if((r.right - x) <= (r.right-r.left)/2){
36345 px = r.right+this.view.borderWidth;
36351 var oldIndex = this.view.getCellIndex(h);
36352 var newIndex = this.view.getCellIndex(n);
36354 if(this.grid.colModel.isFixed(newIndex)){
36358 var locked = this.grid.colModel.isLocked(newIndex);
36363 if(oldIndex < newIndex){
36366 if(oldIndex == newIndex && (locked == this.grid.colModel.isLocked(oldIndex))){
36369 px += this.proxyOffsets[0];
36370 this.proxyTop.setLeftTop(px, py);
36371 this.proxyTop.show();
36372 if(!this.bottomOffset){
36373 this.bottomOffset = this.view.mainHd.getHeight();
36375 this.proxyBottom.setLeftTop(px, py+this.proxyTop.dom.offsetHeight+this.bottomOffset);
36376 this.proxyBottom.show();
36380 onNodeEnter : function(n, dd, e, data){
36381 if(data.header != n){
36382 this.positionIndicator(data.header, n, e);
36386 onNodeOver : function(n, dd, e, data){
36387 var result = false;
36388 if(data.header != n){
36389 result = this.positionIndicator(data.header, n, e);
36392 this.proxyTop.hide();
36393 this.proxyBottom.hide();
36395 return result ? this.dropAllowed : this.dropNotAllowed;
36398 onNodeOut : function(n, dd, e, data){
36399 this.proxyTop.hide();
36400 this.proxyBottom.hide();
36403 onNodeDrop : function(n, dd, e, data){
36404 var h = data.header;
36406 var cm = this.grid.colModel;
36407 var x = Roo.lib.Event.getPageX(e);
36408 var r = Roo.lib.Dom.getRegion(n.firstChild);
36409 var pt = (r.right - x) <= ((r.right-r.left)/2) ? "after" : "before";
36410 var oldIndex = this.view.getCellIndex(h);
36411 var newIndex = this.view.getCellIndex(n);
36412 var locked = cm.isLocked(newIndex);
36416 if(oldIndex < newIndex){
36419 if(oldIndex == newIndex && (locked == cm.isLocked(oldIndex))){
36422 cm.setLocked(oldIndex, locked, true);
36423 cm.moveColumn(oldIndex, newIndex);
36424 this.grid.fireEvent("columnmove", oldIndex, newIndex);
36432 * Ext JS Library 1.1.1
36433 * Copyright(c) 2006-2007, Ext JS, LLC.
36435 * Originally Released Under LGPL - original licence link has changed is not relivant.
36438 * <script type="text/javascript">
36442 * @class Roo.grid.GridView
36443 * @extends Roo.util.Observable
36446 * @param {Object} config
36448 Roo.grid.GridView = function(config){
36449 Roo.grid.GridView.superclass.constructor.call(this);
36452 Roo.apply(this, config);
36455 Roo.extend(Roo.grid.GridView, Roo.grid.AbstractGridView, {
36457 unselectable : 'unselectable="on"',
36458 unselectableCls : 'x-unselectable',
36461 rowClass : "x-grid-row",
36463 cellClass : "x-grid-col",
36465 tdClass : "x-grid-td",
36467 hdClass : "x-grid-hd",
36469 splitClass : "x-grid-split",
36471 sortClasses : ["sort-asc", "sort-desc"],
36473 enableMoveAnim : false,
36477 dh : Roo.DomHelper,
36479 fly : Roo.Element.fly,
36481 css : Roo.util.CSS,
36487 scrollIncrement : 22,
36489 cellRE: /(?:.*?)x-grid-(?:hd|cell|csplit)-(?:[\d]+)-([\d]+)(?:.*?)/,
36491 findRE: /\s?(?:x-grid-hd|x-grid-col|x-grid-csplit)\s/,
36493 bind : function(ds, cm){
36495 this.ds.un("load", this.onLoad, this);
36496 this.ds.un("datachanged", this.onDataChange, this);
36497 this.ds.un("add", this.onAdd, this);
36498 this.ds.un("remove", this.onRemove, this);
36499 this.ds.un("update", this.onUpdate, this);
36500 this.ds.un("clear", this.onClear, this);
36503 ds.on("load", this.onLoad, this);
36504 ds.on("datachanged", this.onDataChange, this);
36505 ds.on("add", this.onAdd, this);
36506 ds.on("remove", this.onRemove, this);
36507 ds.on("update", this.onUpdate, this);
36508 ds.on("clear", this.onClear, this);
36513 this.cm.un("widthchange", this.onColWidthChange, this);
36514 this.cm.un("headerchange", this.onHeaderChange, this);
36515 this.cm.un("hiddenchange", this.onHiddenChange, this);
36516 this.cm.un("columnmoved", this.onColumnMove, this);
36517 this.cm.un("columnlockchange", this.onColumnLock, this);
36520 this.generateRules(cm);
36521 cm.on("widthchange", this.onColWidthChange, this);
36522 cm.on("headerchange", this.onHeaderChange, this);
36523 cm.on("hiddenchange", this.onHiddenChange, this);
36524 cm.on("columnmoved", this.onColumnMove, this);
36525 cm.on("columnlockchange", this.onColumnLock, this);
36530 init: function(grid){
36531 Roo.grid.GridView.superclass.init.call(this, grid);
36533 this.bind(grid.dataSource, grid.colModel);
36535 grid.on("headerclick", this.handleHeaderClick, this);
36537 if(grid.trackMouseOver){
36538 grid.on("mouseover", this.onRowOver, this);
36539 grid.on("mouseout", this.onRowOut, this);
36541 grid.cancelTextSelection = function(){};
36542 this.gridId = grid.id;
36544 var tpls = this.templates || {};
36547 tpls.master = new Roo.Template(
36548 '<div class="x-grid" hidefocus="true">',
36549 '<a href="#" class="x-grid-focus" tabIndex="-1"></a>',
36550 '<div class="x-grid-topbar"></div>',
36551 '<div class="x-grid-scroller"><div></div></div>',
36552 '<div class="x-grid-locked">',
36553 '<div class="x-grid-header">{lockedHeader}</div>',
36554 '<div class="x-grid-body">{lockedBody}</div>',
36556 '<div class="x-grid-viewport">',
36557 '<div class="x-grid-header">{header}</div>',
36558 '<div class="x-grid-body">{body}</div>',
36560 '<div class="x-grid-bottombar"></div>',
36562 '<div class="x-grid-resize-proxy"> </div>',
36565 tpls.master.disableformats = true;
36569 tpls.header = new Roo.Template(
36570 '<table border="0" cellspacing="0" cellpadding="0">',
36571 '<tbody><tr class="x-grid-hd-row">{cells}</tr></tbody>',
36574 tpls.header.disableformats = true;
36576 tpls.header.compile();
36579 tpls.hcell = new Roo.Template(
36580 '<td class="x-grid-hd x-grid-td-{id} {cellId}"><div title="{title}" class="x-grid-hd-inner x-grid-hd-{id}">',
36581 '<div class="x-grid-hd-text ' + this.unselectableCls + '" ' + this.unselectable +'>{value}<img class="x-grid-sort-icon" src="', Roo.BLANK_IMAGE_URL, '" /></div>',
36584 tpls.hcell.disableFormats = true;
36586 tpls.hcell.compile();
36589 tpls.hsplit = new Roo.Template('<div class="x-grid-split {splitId} x-grid-split-{id}" style="{style} ' +
36590 this.unselectableCls + '" ' + this.unselectable +'> </div>');
36591 tpls.hsplit.disableFormats = true;
36593 tpls.hsplit.compile();
36596 tpls.body = new Roo.Template(
36597 '<table border="0" cellspacing="0" cellpadding="0">',
36598 "<tbody>{rows}</tbody>",
36601 tpls.body.disableFormats = true;
36603 tpls.body.compile();
36606 tpls.row = new Roo.Template('<tr class="x-grid-row {alt}">{cells}</tr>');
36607 tpls.row.disableFormats = true;
36609 tpls.row.compile();
36612 tpls.cell = new Roo.Template(
36613 '<td class="x-grid-col x-grid-td-{id} {cellId} {css}" tabIndex="0">',
36614 '<div class="x-grid-col-{id} x-grid-cell-inner"><div class="x-grid-cell-text ' +
36615 this.unselectableCls + '" ' + this.unselectable +'" {attr}>{value}</div></div>',
36618 tpls.cell.disableFormats = true;
36620 tpls.cell.compile();
36622 this.templates = tpls;
36625 // remap these for backwards compat
36626 onColWidthChange : function(){
36627 this.updateColumns.apply(this, arguments);
36629 onHeaderChange : function(){
36630 this.updateHeaders.apply(this, arguments);
36632 onHiddenChange : function(){
36633 this.handleHiddenChange.apply(this, arguments);
36635 onColumnMove : function(){
36636 this.handleColumnMove.apply(this, arguments);
36638 onColumnLock : function(){
36639 this.handleLockChange.apply(this, arguments);
36642 onDataChange : function(){
36644 this.updateHeaderSortState();
36647 onClear : function(){
36651 onUpdate : function(ds, record){
36652 this.refreshRow(record);
36655 refreshRow : function(record){
36656 var ds = this.ds, index;
36657 if(typeof record == 'number'){
36659 record = ds.getAt(index);
36661 index = ds.indexOf(record);
36663 this.insertRows(ds, index, index, true);
36664 this.onRemove(ds, record, index+1, true);
36665 this.syncRowHeights(index, index);
36667 this.fireEvent("rowupdated", this, index, record);
36670 onAdd : function(ds, records, index){
36671 this.insertRows(ds, index, index + (records.length-1));
36674 onRemove : function(ds, record, index, isUpdate){
36675 if(isUpdate !== true){
36676 this.fireEvent("beforerowremoved", this, index, record);
36678 var bt = this.getBodyTable(), lt = this.getLockedTable();
36679 if(bt.rows[index]){
36680 bt.firstChild.removeChild(bt.rows[index]);
36682 if(lt.rows[index]){
36683 lt.firstChild.removeChild(lt.rows[index]);
36685 if(isUpdate !== true){
36686 this.stripeRows(index);
36687 this.syncRowHeights(index, index);
36689 this.fireEvent("rowremoved", this, index, record);
36693 onLoad : function(){
36694 this.scrollToTop();
36698 * Scrolls the grid to the top
36700 scrollToTop : function(){
36702 this.scroller.dom.scrollTop = 0;
36708 * Gets a panel in the header of the grid that can be used for toolbars etc.
36709 * After modifying the contents of this panel a call to grid.autoSize() may be
36710 * required to register any changes in size.
36711 * @param {Boolean} doShow By default the header is hidden. Pass true to show the panel
36712 * @return Roo.Element
36714 getHeaderPanel : function(doShow){
36716 this.headerPanel.show();
36718 return this.headerPanel;
36722 * Gets a panel in the footer of the grid that can be used for toolbars etc.
36723 * After modifying the contents of this panel a call to grid.autoSize() may be
36724 * required to register any changes in size.
36725 * @param {Boolean} doShow By default the footer is hidden. Pass true to show the panel
36726 * @return Roo.Element
36728 getFooterPanel : function(doShow){
36730 this.footerPanel.show();
36732 return this.footerPanel;
36735 initElements : function(){
36736 var E = Roo.Element;
36737 var el = this.grid.getGridEl().dom.firstChild;
36738 var cs = el.childNodes;
36740 this.el = new E(el);
36742 this.focusEl = new E(el.firstChild);
36743 this.focusEl.swallowEvent("click", true);
36745 this.headerPanel = new E(cs[1]);
36746 this.headerPanel.enableDisplayMode("block");
36748 this.scroller = new E(cs[2]);
36749 this.scrollSizer = new E(this.scroller.dom.firstChild);
36751 this.lockedWrap = new E(cs[3]);
36752 this.lockedHd = new E(this.lockedWrap.dom.firstChild);
36753 this.lockedBody = new E(this.lockedWrap.dom.childNodes[1]);
36755 this.mainWrap = new E(cs[4]);
36756 this.mainHd = new E(this.mainWrap.dom.firstChild);
36757 this.mainBody = new E(this.mainWrap.dom.childNodes[1]);
36759 this.footerPanel = new E(cs[5]);
36760 this.footerPanel.enableDisplayMode("block");
36762 this.resizeProxy = new E(cs[6]);
36764 this.headerSelector = String.format(
36765 '#{0} td.x-grid-hd, #{1} td.x-grid-hd',
36766 this.lockedHd.id, this.mainHd.id
36769 this.splitterSelector = String.format(
36770 '#{0} div.x-grid-split, #{1} div.x-grid-split',
36771 this.idToCssName(this.lockedHd.id), this.idToCssName(this.mainHd.id)
36774 idToCssName : function(s)
36776 return s.replace(/[^a-z0-9]+/ig, '-');
36779 getHeaderCell : function(index){
36780 return Roo.DomQuery.select(this.headerSelector)[index];
36783 getHeaderCellMeasure : function(index){
36784 return this.getHeaderCell(index).firstChild;
36787 getHeaderCellText : function(index){
36788 return this.getHeaderCell(index).firstChild.firstChild;
36791 getLockedTable : function(){
36792 return this.lockedBody.dom.firstChild;
36795 getBodyTable : function(){
36796 return this.mainBody.dom.firstChild;
36799 getLockedRow : function(index){
36800 return this.getLockedTable().rows[index];
36803 getRow : function(index){
36804 return this.getBodyTable().rows[index];
36807 getRowComposite : function(index){
36809 this.rowEl = new Roo.CompositeElementLite();
36811 var els = [], lrow, mrow;
36812 if(lrow = this.getLockedRow(index)){
36815 if(mrow = this.getRow(index)){
36818 this.rowEl.elements = els;
36822 * Gets the 'td' of the cell
36824 * @param {Integer} rowIndex row to select
36825 * @param {Integer} colIndex column to select
36829 getCell : function(rowIndex, colIndex){
36830 var locked = this.cm.getLockedCount();
36832 if(colIndex < locked){
36833 source = this.lockedBody.dom.firstChild;
36835 source = this.mainBody.dom.firstChild;
36836 colIndex -= locked;
36838 return source.rows[rowIndex].childNodes[colIndex];
36841 getCellText : function(rowIndex, colIndex){
36842 return this.getCell(rowIndex, colIndex).firstChild.firstChild;
36845 getCellBox : function(cell){
36846 var b = this.fly(cell).getBox();
36847 if(Roo.isOpera){ // opera fails to report the Y
36848 b.y = cell.offsetTop + this.mainBody.getY();
36853 getCellIndex : function(cell){
36854 var id = String(cell.className).match(this.cellRE);
36856 return parseInt(id[1], 10);
36861 findHeaderIndex : function(n){
36862 var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
36863 return r ? this.getCellIndex(r) : false;
36866 findHeaderCell : function(n){
36867 var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
36868 return r ? r : false;
36871 findRowIndex : function(n){
36875 var r = Roo.fly(n).findParent("tr." + this.rowClass, 6);
36876 return r ? r.rowIndex : false;
36879 findCellIndex : function(node){
36880 var stop = this.el.dom;
36881 while(node && node != stop){
36882 if(this.findRE.test(node.className)){
36883 return this.getCellIndex(node);
36885 node = node.parentNode;
36890 getColumnId : function(index){
36891 return this.cm.getColumnId(index);
36894 getSplitters : function()
36896 if(this.splitterSelector){
36897 return Roo.DomQuery.select(this.splitterSelector);
36903 getSplitter : function(index){
36904 return this.getSplitters()[index];
36907 onRowOver : function(e, t){
36909 if((row = this.findRowIndex(t)) !== false){
36910 this.getRowComposite(row).addClass("x-grid-row-over");
36914 onRowOut : function(e, t){
36916 if((row = this.findRowIndex(t)) !== false && row !== this.findRowIndex(e.getRelatedTarget())){
36917 this.getRowComposite(row).removeClass("x-grid-row-over");
36921 renderHeaders : function(){
36923 var ct = this.templates.hcell, ht = this.templates.header, st = this.templates.hsplit;
36924 var cb = [], lb = [], sb = [], lsb = [], p = {};
36925 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
36926 p.cellId = "x-grid-hd-0-" + i;
36927 p.splitId = "x-grid-csplit-0-" + i;
36928 p.id = cm.getColumnId(i);
36929 p.title = cm.getColumnTooltip(i) || "";
36930 p.value = cm.getColumnHeader(i) || "";
36931 p.style = (this.grid.enableColumnResize === false || !cm.isResizable(i) || cm.isFixed(i)) ? 'cursor:default' : '';
36932 if(!cm.isLocked(i)){
36933 cb[cb.length] = ct.apply(p);
36934 sb[sb.length] = st.apply(p);
36936 lb[lb.length] = ct.apply(p);
36937 lsb[lsb.length] = st.apply(p);
36940 return [ht.apply({cells: lb.join(""), splits:lsb.join("")}),
36941 ht.apply({cells: cb.join(""), splits:sb.join("")})];
36944 updateHeaders : function(){
36945 var html = this.renderHeaders();
36946 this.lockedHd.update(html[0]);
36947 this.mainHd.update(html[1]);
36951 * Focuses the specified row.
36952 * @param {Number} row The row index
36954 focusRow : function(row)
36956 //Roo.log('GridView.focusRow');
36957 var x = this.scroller.dom.scrollLeft;
36958 this.focusCell(row, 0, false);
36959 this.scroller.dom.scrollLeft = x;
36963 * Focuses the specified cell.
36964 * @param {Number} row The row index
36965 * @param {Number} col The column index
36966 * @param {Boolean} hscroll false to disable horizontal scrolling
36968 focusCell : function(row, col, hscroll)
36970 //Roo.log('GridView.focusCell');
36971 var el = this.ensureVisible(row, col, hscroll);
36972 this.focusEl.alignTo(el, "tl-tl");
36974 this.focusEl.focus();
36976 this.focusEl.focus.defer(1, this.focusEl);
36981 * Scrolls the specified cell into view
36982 * @param {Number} row The row index
36983 * @param {Number} col The column index
36984 * @param {Boolean} hscroll false to disable horizontal scrolling
36986 ensureVisible : function(row, col, hscroll)
36988 //Roo.log('GridView.ensureVisible,' + row + ',' + col);
36989 //return null; //disable for testing.
36990 if(typeof row != "number"){
36991 row = row.rowIndex;
36993 if(row < 0 && row >= this.ds.getCount()){
36996 col = (col !== undefined ? col : 0);
36997 var cm = this.grid.colModel;
36998 while(cm.isHidden(col)){
37002 var el = this.getCell(row, col);
37006 var c = this.scroller.dom;
37008 var ctop = parseInt(el.offsetTop, 10);
37009 var cleft = parseInt(el.offsetLeft, 10);
37010 var cbot = ctop + el.offsetHeight;
37011 var cright = cleft + el.offsetWidth;
37013 var ch = c.clientHeight - this.mainHd.dom.offsetHeight;
37014 var stop = parseInt(c.scrollTop, 10);
37015 var sleft = parseInt(c.scrollLeft, 10);
37016 var sbot = stop + ch;
37017 var sright = sleft + c.clientWidth;
37019 Roo.log('GridView.ensureVisible:' +
37021 ' c.clientHeight:' + c.clientHeight +
37022 ' this.mainHd.dom.offsetHeight:' + this.mainHd.dom.offsetHeight +
37030 c.scrollTop = ctop;
37031 //Roo.log("set scrolltop to ctop DISABLE?");
37032 }else if(cbot > sbot){
37033 //Roo.log("set scrolltop to cbot-ch");
37034 c.scrollTop = cbot-ch;
37037 if(hscroll !== false){
37039 c.scrollLeft = cleft;
37040 }else if(cright > sright){
37041 c.scrollLeft = cright-c.clientWidth;
37048 updateColumns : function(){
37049 this.grid.stopEditing();
37050 var cm = this.grid.colModel, colIds = this.getColumnIds();
37051 //var totalWidth = cm.getTotalWidth();
37053 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
37054 //if(cm.isHidden(i)) continue;
37055 var w = cm.getColumnWidth(i);
37056 this.css.updateRule(this.colSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
37057 this.css.updateRule(this.hdSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
37059 this.updateSplitters();
37062 generateRules : function(cm){
37063 var ruleBuf = [], rulesId = this.idToCssName(this.grid.id)+ '-cssrules';
37064 Roo.util.CSS.removeStyleSheet(rulesId);
37065 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
37066 var cid = cm.getColumnId(i);
37068 if(cm.config[i].align){
37069 align = 'text-align:'+cm.config[i].align+';';
37072 if(cm.isHidden(i)){
37073 hidden = 'display:none;';
37075 var width = "width:" + (cm.getColumnWidth(i) - this.borderWidth) + "px;";
37077 this.colSelector, cid, " {\n", cm.config[i].css, align, width, "\n}\n",
37078 this.hdSelector, cid, " {\n", align, width, "}\n",
37079 this.tdSelector, cid, " {\n",hidden,"\n}\n",
37080 this.splitSelector, cid, " {\n", hidden , "\n}\n");
37082 return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
37085 updateSplitters : function(){
37086 var cm = this.cm, s = this.getSplitters();
37087 if(s){ // splitters not created yet
37088 var pos = 0, locked = true;
37089 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
37090 if(cm.isHidden(i)) continue;
37091 var w = cm.getColumnWidth(i); // make sure it's a number
37092 if(!cm.isLocked(i) && locked){
37097 s[i].style.left = (pos-this.splitOffset) + "px";
37102 handleHiddenChange : function(colModel, colIndex, hidden){
37104 this.hideColumn(colIndex);
37106 this.unhideColumn(colIndex);
37110 hideColumn : function(colIndex){
37111 var cid = this.getColumnId(colIndex);
37112 this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "none");
37113 this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "none");
37115 this.updateHeaders();
37117 this.updateSplitters();
37121 unhideColumn : function(colIndex){
37122 var cid = this.getColumnId(colIndex);
37123 this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "");
37124 this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "");
37127 this.updateHeaders();
37129 this.updateSplitters();
37133 insertRows : function(dm, firstRow, lastRow, isUpdate){
37134 if(firstRow == 0 && lastRow == dm.getCount()-1){
37138 this.fireEvent("beforerowsinserted", this, firstRow, lastRow);
37140 var s = this.getScrollState();
37141 var markup = this.renderRows(firstRow, lastRow);
37142 this.bufferRows(markup[0], this.getLockedTable(), firstRow);
37143 this.bufferRows(markup[1], this.getBodyTable(), firstRow);
37144 this.restoreScroll(s);
37146 this.fireEvent("rowsinserted", this, firstRow, lastRow);
37147 this.syncRowHeights(firstRow, lastRow);
37148 this.stripeRows(firstRow);
37154 bufferRows : function(markup, target, index){
37155 var before = null, trows = target.rows, tbody = target.tBodies[0];
37156 if(index < trows.length){
37157 before = trows[index];
37159 var b = document.createElement("div");
37160 b.innerHTML = "<table><tbody>"+markup+"</tbody></table>";
37161 var rows = b.firstChild.rows;
37162 for(var i = 0, len = rows.length; i < len; i++){
37164 tbody.insertBefore(rows[0], before);
37166 tbody.appendChild(rows[0]);
37173 deleteRows : function(dm, firstRow, lastRow){
37174 if(dm.getRowCount()<1){
37175 this.fireEvent("beforerefresh", this);
37176 this.mainBody.update("");
37177 this.lockedBody.update("");
37178 this.fireEvent("refresh", this);
37180 this.fireEvent("beforerowsdeleted", this, firstRow, lastRow);
37181 var bt = this.getBodyTable();
37182 var tbody = bt.firstChild;
37183 var rows = bt.rows;
37184 for(var rowIndex = firstRow; rowIndex <= lastRow; rowIndex++){
37185 tbody.removeChild(rows[firstRow]);
37187 this.stripeRows(firstRow);
37188 this.fireEvent("rowsdeleted", this, firstRow, lastRow);
37192 updateRows : function(dataSource, firstRow, lastRow){
37193 var s = this.getScrollState();
37195 this.restoreScroll(s);
37198 handleSort : function(dataSource, sortColumnIndex, sortDir, noRefresh){
37202 this.updateHeaderSortState();
37205 getScrollState : function(){
37207 var sb = this.scroller.dom;
37208 return {left: sb.scrollLeft, top: sb.scrollTop};
37211 stripeRows : function(startRow){
37212 if(!this.grid.stripeRows || this.ds.getCount() < 1){
37215 startRow = startRow || 0;
37216 var rows = this.getBodyTable().rows;
37217 var lrows = this.getLockedTable().rows;
37218 var cls = ' x-grid-row-alt ';
37219 for(var i = startRow, len = rows.length; i < len; i++){
37220 var row = rows[i], lrow = lrows[i];
37221 var isAlt = ((i+1) % 2 == 0);
37222 var hasAlt = (' '+row.className + ' ').indexOf(cls) != -1;
37223 if(isAlt == hasAlt){
37227 row.className += " x-grid-row-alt";
37229 row.className = row.className.replace("x-grid-row-alt", "");
37232 lrow.className = row.className;
37237 restoreScroll : function(state){
37238 //Roo.log('GridView.restoreScroll');
37239 var sb = this.scroller.dom;
37240 sb.scrollLeft = state.left;
37241 sb.scrollTop = state.top;
37245 syncScroll : function(){
37246 //Roo.log('GridView.syncScroll');
37247 var sb = this.scroller.dom;
37248 var sh = this.mainHd.dom;
37249 var bs = this.mainBody.dom;
37250 var lv = this.lockedBody.dom;
37251 sh.scrollLeft = bs.scrollLeft = sb.scrollLeft;
37252 lv.scrollTop = bs.scrollTop = sb.scrollTop;
37255 handleScroll : function(e){
37257 var sb = this.scroller.dom;
37258 this.grid.fireEvent("bodyscroll", sb.scrollLeft, sb.scrollTop);
37262 handleWheel : function(e){
37263 var d = e.getWheelDelta();
37264 this.scroller.dom.scrollTop -= d*22;
37265 // set this here to prevent jumpy scrolling on large tables
37266 this.lockedBody.dom.scrollTop = this.mainBody.dom.scrollTop = this.scroller.dom.scrollTop;
37270 renderRows : function(startRow, endRow){
37271 // pull in all the crap needed to render rows
37272 var g = this.grid, cm = g.colModel, ds = g.dataSource, stripe = g.stripeRows;
37273 var colCount = cm.getColumnCount();
37275 if(ds.getCount() < 1){
37279 // build a map for all the columns
37281 for(var i = 0; i < colCount; i++){
37282 var name = cm.getDataIndex(i);
37284 name : typeof name == 'undefined' ? ds.fields.get(i).name : name,
37285 renderer : cm.getRenderer(i),
37286 id : cm.getColumnId(i),
37287 locked : cm.isLocked(i)
37291 startRow = startRow || 0;
37292 endRow = typeof endRow == "undefined"? ds.getCount()-1 : endRow;
37294 // records to render
37295 var rs = ds.getRange(startRow, endRow);
37297 return this.doRender(cs, rs, ds, startRow, colCount, stripe);
37300 // As much as I hate to duplicate code, this was branched because FireFox really hates
37301 // [].join("") on strings. The performance difference was substantial enough to
37302 // branch this function
37303 doRender : Roo.isGecko ?
37304 function(cs, rs, ds, startRow, colCount, stripe){
37305 var ts = this.templates, ct = ts.cell, rt = ts.row;
37307 var buf = "", lbuf = "", cb, lcb, c, p = {}, rp = {}, r, rowIndex;
37309 var hasListener = this.grid.hasListener('rowclass');
37311 for(var j = 0, len = rs.length; j < len; j++){
37312 r = rs[j]; cb = ""; lcb = ""; rowIndex = (j+startRow);
37313 for(var i = 0; i < colCount; i++){
37315 p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
37317 p.css = p.attr = "";
37318 p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
37319 if(p.value == undefined || p.value === "") p.value = " ";
37320 if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
37321 p.css += p.css ? ' x-grid-dirty-cell' : 'x-grid-dirty-cell';
37323 var markup = ct.apply(p);
37331 if(stripe && ((rowIndex+1) % 2 == 0)){
37332 alt.push("x-grid-row-alt")
37335 alt.push( " x-grid-dirty-row");
37338 if(this.getRowClass){
37339 alt.push(this.getRowClass(r, rowIndex));
37345 rowIndex : rowIndex,
37348 this.grid.fireEvent('rowclass', this, rowcfg);
37349 alt.push(rowcfg.rowClass);
37351 rp.alt = alt.join(" ");
37352 lbuf+= rt.apply(rp);
37354 buf+= rt.apply(rp);
37356 return [lbuf, buf];
37358 function(cs, rs, ds, startRow, colCount, stripe){
37359 var ts = this.templates, ct = ts.cell, rt = ts.row;
37361 var buf = [], lbuf = [], cb, lcb, c, p = {}, rp = {}, r, rowIndex;
37362 var hasListener = this.grid.hasListener('rowclass');
37365 for(var j = 0, len = rs.length; j < len; j++){
37366 r = rs[j]; cb = []; lcb = []; rowIndex = (j+startRow);
37367 for(var i = 0; i < colCount; i++){
37369 p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
37371 p.css = p.attr = "";
37372 p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
37373 if(p.value == undefined || p.value === "") p.value = " ";
37374 if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
37375 p.css += p.css ? ' x-grid-dirty-cell' : 'x-grid-dirty-cell';
37378 var markup = ct.apply(p);
37380 cb[cb.length] = markup;
37382 lcb[lcb.length] = markup;
37386 if(stripe && ((rowIndex+1) % 2 == 0)){
37387 alt.push( "x-grid-row-alt");
37390 alt.push(" x-grid-dirty-row");
37393 if(this.getRowClass){
37394 alt.push( this.getRowClass(r, rowIndex));
37400 rowIndex : rowIndex,
37403 this.grid.fireEvent('rowclass', this, rowcfg);
37404 alt.push(rowcfg.rowClass);
37406 rp.alt = alt.join(" ");
37407 rp.cells = lcb.join("");
37408 lbuf[lbuf.length] = rt.apply(rp);
37409 rp.cells = cb.join("");
37410 buf[buf.length] = rt.apply(rp);
37412 return [lbuf.join(""), buf.join("")];
37415 renderBody : function(){
37416 var markup = this.renderRows();
37417 var bt = this.templates.body;
37418 return [bt.apply({rows: markup[0]}), bt.apply({rows: markup[1]})];
37422 * Refreshes the grid
37423 * @param {Boolean} headersToo
37425 refresh : function(headersToo){
37426 this.fireEvent("beforerefresh", this);
37427 this.grid.stopEditing();
37428 var result = this.renderBody();
37429 this.lockedBody.update(result[0]);
37430 this.mainBody.update(result[1]);
37431 if(headersToo === true){
37432 this.updateHeaders();
37433 this.updateColumns();
37434 this.updateSplitters();
37435 this.updateHeaderSortState();
37437 this.syncRowHeights();
37439 this.fireEvent("refresh", this);
37442 handleColumnMove : function(cm, oldIndex, newIndex){
37443 this.indexMap = null;
37444 var s = this.getScrollState();
37445 this.refresh(true);
37446 this.restoreScroll(s);
37447 this.afterMove(newIndex);
37450 afterMove : function(colIndex){
37451 if(this.enableMoveAnim && Roo.enableFx){
37452 this.fly(this.getHeaderCell(colIndex).firstChild).highlight(this.hlColor);
37454 // if multisort - fix sortOrder, and reload..
37455 if (this.grid.dataSource.multiSort) {
37456 // the we can call sort again..
37457 var dm = this.grid.dataSource;
37458 var cm = this.grid.colModel;
37460 for(var i = 0; i < cm.config.length; i++ ) {
37462 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined')) {
37463 continue; // dont' bother, it's not in sort list or being set.
37466 so.push(cm.config[i].dataIndex);
37469 dm.load(dm.lastOptions);
37476 updateCell : function(dm, rowIndex, dataIndex){
37477 var colIndex = this.getColumnIndexByDataIndex(dataIndex);
37478 if(typeof colIndex == "undefined"){ // not present in grid
37481 var cm = this.grid.colModel;
37482 var cell = this.getCell(rowIndex, colIndex);
37483 var cellText = this.getCellText(rowIndex, colIndex);
37486 cellId : "x-grid-cell-" + rowIndex + "-" + colIndex,
37487 id : cm.getColumnId(colIndex),
37488 css: colIndex == cm.getColumnCount()-1 ? "x-grid-col-last" : ""
37490 var renderer = cm.getRenderer(colIndex);
37491 var val = renderer(dm.getValueAt(rowIndex, dataIndex), p, rowIndex, colIndex, dm);
37492 if(typeof val == "undefined" || val === "") val = " ";
37493 cellText.innerHTML = val;
37494 cell.className = this.cellClass + " " + this.idToCssName(p.cellId) + " " + p.css;
37495 this.syncRowHeights(rowIndex, rowIndex);
37498 calcColumnWidth : function(colIndex, maxRowsToMeasure){
37500 if(this.grid.autoSizeHeaders){
37501 var h = this.getHeaderCellMeasure(colIndex);
37502 maxWidth = Math.max(maxWidth, h.scrollWidth);
37505 if(this.cm.isLocked(colIndex)){
37506 tb = this.getLockedTable();
37509 tb = this.getBodyTable();
37510 index = colIndex - this.cm.getLockedCount();
37513 var rows = tb.rows;
37514 var stopIndex = Math.min(maxRowsToMeasure || rows.length, rows.length);
37515 for(var i = 0; i < stopIndex; i++){
37516 var cell = rows[i].childNodes[index].firstChild;
37517 maxWidth = Math.max(maxWidth, cell.scrollWidth);
37520 return maxWidth + /*margin for error in IE*/ 5;
37523 * Autofit a column to its content.
37524 * @param {Number} colIndex
37525 * @param {Boolean} forceMinSize true to force the column to go smaller if possible
37527 autoSizeColumn : function(colIndex, forceMinSize, suppressEvent){
37528 if(this.cm.isHidden(colIndex)){
37529 return; // can't calc a hidden column
37532 var cid = this.cm.getColumnId(colIndex);
37533 this.css.updateRule(this.colSelector +this.idToCssName( cid), "width", this.grid.minColumnWidth + "px");
37534 if(this.grid.autoSizeHeaders){
37535 this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", this.grid.minColumnWidth + "px");
37538 var newWidth = this.calcColumnWidth(colIndex);
37539 this.cm.setColumnWidth(colIndex,
37540 Math.max(this.grid.minColumnWidth, newWidth), suppressEvent);
37541 if(!suppressEvent){
37542 this.grid.fireEvent("columnresize", colIndex, newWidth);
37547 * Autofits all columns to their content and then expands to fit any extra space in the grid
37549 autoSizeColumns : function(){
37550 var cm = this.grid.colModel;
37551 var colCount = cm.getColumnCount();
37552 for(var i = 0; i < colCount; i++){
37553 this.autoSizeColumn(i, true, true);
37555 if(cm.getTotalWidth() < this.scroller.dom.clientWidth){
37558 this.updateColumns();
37564 * Autofits all columns to the grid's width proportionate with their current size
37565 * @param {Boolean} reserveScrollSpace Reserve space for a scrollbar
37567 fitColumns : function(reserveScrollSpace){
37568 var cm = this.grid.colModel;
37569 var colCount = cm.getColumnCount();
37573 for (i = 0; i < colCount; i++){
37574 if(!cm.isHidden(i) && !cm.isFixed(i)){
37575 w = cm.getColumnWidth(i);
37581 var avail = Math.min(this.scroller.dom.clientWidth, this.el.getWidth());
37582 if(reserveScrollSpace){
37585 var frac = (avail - cm.getTotalWidth())/width;
37586 while (cols.length){
37589 cm.setColumnWidth(i, Math.floor(w + w*frac), true);
37591 this.updateColumns();
37595 onRowSelect : function(rowIndex){
37596 var row = this.getRowComposite(rowIndex);
37597 row.addClass("x-grid-row-selected");
37600 onRowDeselect : function(rowIndex){
37601 var row = this.getRowComposite(rowIndex);
37602 row.removeClass("x-grid-row-selected");
37605 onCellSelect : function(row, col){
37606 var cell = this.getCell(row, col);
37608 Roo.fly(cell).addClass("x-grid-cell-selected");
37612 onCellDeselect : function(row, col){
37613 var cell = this.getCell(row, col);
37615 Roo.fly(cell).removeClass("x-grid-cell-selected");
37619 updateHeaderSortState : function(){
37621 // sort state can be single { field: xxx, direction : yyy}
37622 // or { xxx=>ASC , yyy : DESC ..... }
37625 if (!this.ds.multiSort) {
37626 var state = this.ds.getSortState();
37630 mstate[state.field] = state.direction;
37631 // FIXME... - this is not used here.. but might be elsewhere..
37632 this.sortState = state;
37635 mstate = this.ds.sortToggle;
37637 //remove existing sort classes..
37639 var sc = this.sortClasses;
37640 var hds = this.el.select(this.headerSelector).removeClass(sc);
37642 for(var f in mstate) {
37644 var sortColumn = this.cm.findColumnIndex(f);
37646 if(sortColumn != -1){
37647 var sortDir = mstate[f];
37648 hds.item(sortColumn).addClass(sc[sortDir == "DESC" ? 1 : 0]);
37657 handleHeaderClick : function(g, index){
37658 if(this.headersDisabled){
37661 var dm = g.dataSource, cm = g.colModel;
37662 if(!cm.isSortable(index)){
37667 if (dm.multiSort) {
37668 // update the sortOrder
37670 for(var i = 0; i < cm.config.length; i++ ) {
37672 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined') && (index != i)) {
37673 continue; // dont' bother, it's not in sort list or being set.
37676 so.push(cm.config[i].dataIndex);
37682 dm.sort(cm.getDataIndex(index));
37686 destroy : function(){
37688 this.colMenu.removeAll();
37689 Roo.menu.MenuMgr.unregister(this.colMenu);
37690 this.colMenu.getEl().remove();
37691 delete this.colMenu;
37694 this.hmenu.removeAll();
37695 Roo.menu.MenuMgr.unregister(this.hmenu);
37696 this.hmenu.getEl().remove();
37699 if(this.grid.enableColumnMove){
37700 var dds = Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
37702 for(var dd in dds){
37703 if(!dds[dd].config.isTarget && dds[dd].dragElId){
37704 var elid = dds[dd].dragElId;
37706 Roo.get(elid).remove();
37707 } else if(dds[dd].config.isTarget){
37708 dds[dd].proxyTop.remove();
37709 dds[dd].proxyBottom.remove();
37712 if(Roo.dd.DDM.locationCache[dd]){
37713 delete Roo.dd.DDM.locationCache[dd];
37716 delete Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
37719 Roo.util.CSS.removeStyleSheet(this.idToCssName(this.grid.id) + '-cssrules');
37720 this.bind(null, null);
37721 Roo.EventManager.removeResizeListener(this.onWindowResize, this);
37724 handleLockChange : function(){
37725 this.refresh(true);
37728 onDenyColumnLock : function(){
37732 onDenyColumnHide : function(){
37736 handleHdMenuClick : function(item){
37737 var index = this.hdCtxIndex;
37738 var cm = this.cm, ds = this.ds;
37741 ds.sort(cm.getDataIndex(index), "ASC");
37744 ds.sort(cm.getDataIndex(index), "DESC");
37747 var lc = cm.getLockedCount();
37748 if(cm.getColumnCount(true) <= lc+1){
37749 this.onDenyColumnLock();
37753 cm.setLocked(index, true, true);
37754 cm.moveColumn(index, lc);
37755 this.grid.fireEvent("columnmove", index, lc);
37757 cm.setLocked(index, true);
37761 var lc = cm.getLockedCount();
37762 if((lc-1) != index){
37763 cm.setLocked(index, false, true);
37764 cm.moveColumn(index, lc-1);
37765 this.grid.fireEvent("columnmove", index, lc-1);
37767 cm.setLocked(index, false);
37771 index = cm.getIndexById(item.id.substr(4));
37773 if(item.checked && cm.getColumnCount(true) <= 1){
37774 this.onDenyColumnHide();
37777 cm.setHidden(index, item.checked);
37783 beforeColMenuShow : function(){
37784 var cm = this.cm, colCount = cm.getColumnCount();
37785 this.colMenu.removeAll();
37786 for(var i = 0; i < colCount; i++){
37787 this.colMenu.add(new Roo.menu.CheckItem({
37788 id: "col-"+cm.getColumnId(i),
37789 text: cm.getColumnHeader(i),
37790 checked: !cm.isHidden(i),
37796 handleHdCtx : function(g, index, e){
37798 var hd = this.getHeaderCell(index);
37799 this.hdCtxIndex = index;
37800 var ms = this.hmenu.items, cm = this.cm;
37801 ms.get("asc").setDisabled(!cm.isSortable(index));
37802 ms.get("desc").setDisabled(!cm.isSortable(index));
37803 if(this.grid.enableColLock !== false){
37804 ms.get("lock").setDisabled(cm.isLocked(index));
37805 ms.get("unlock").setDisabled(!cm.isLocked(index));
37807 this.hmenu.show(hd, "tl-bl");
37810 handleHdOver : function(e){
37811 var hd = this.findHeaderCell(e.getTarget());
37812 if(hd && !this.headersDisabled){
37813 if(this.grid.colModel.isSortable(this.getCellIndex(hd))){
37814 this.fly(hd).addClass("x-grid-hd-over");
37819 handleHdOut : function(e){
37820 var hd = this.findHeaderCell(e.getTarget());
37822 this.fly(hd).removeClass("x-grid-hd-over");
37826 handleSplitDblClick : function(e, t){
37827 var i = this.getCellIndex(t);
37828 if(this.grid.enableColumnResize !== false && this.cm.isResizable(i) && !this.cm.isFixed(i)){
37829 this.autoSizeColumn(i, true);
37834 render : function(){
37837 var colCount = cm.getColumnCount();
37839 if(this.grid.monitorWindowResize === true){
37840 Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
37842 var header = this.renderHeaders();
37843 var body = this.templates.body.apply({rows:""});
37844 var html = this.templates.master.apply({
37847 lockedHeader: header[0],
37851 //this.updateColumns();
37853 this.grid.getGridEl().dom.innerHTML = html;
37855 this.initElements();
37857 // a kludge to fix the random scolling effect in webkit
37858 this.el.on("scroll", function() {
37859 this.el.dom.scrollTop=0; // hopefully not recursive..
37862 this.scroller.on("scroll", this.handleScroll, this);
37863 this.lockedBody.on("mousewheel", this.handleWheel, this);
37864 this.mainBody.on("mousewheel", this.handleWheel, this);
37866 this.mainHd.on("mouseover", this.handleHdOver, this);
37867 this.mainHd.on("mouseout", this.handleHdOut, this);
37868 this.mainHd.on("dblclick", this.handleSplitDblClick, this,
37869 {delegate: "."+this.splitClass});
37871 this.lockedHd.on("mouseover", this.handleHdOver, this);
37872 this.lockedHd.on("mouseout", this.handleHdOut, this);
37873 this.lockedHd.on("dblclick", this.handleSplitDblClick, this,
37874 {delegate: "."+this.splitClass});
37876 if(this.grid.enableColumnResize !== false && Roo.grid.SplitDragZone){
37877 new Roo.grid.SplitDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
37880 this.updateSplitters();
37882 if(this.grid.enableColumnMove && Roo.grid.HeaderDragZone){
37883 new Roo.grid.HeaderDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
37884 new Roo.grid.HeaderDropZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
37887 if(this.grid.enableCtxMenu !== false && Roo.menu.Menu){
37888 this.hmenu = new Roo.menu.Menu({id: this.grid.id + "-hctx"});
37890 {id:"asc", text: this.sortAscText, cls: "xg-hmenu-sort-asc"},
37891 {id:"desc", text: this.sortDescText, cls: "xg-hmenu-sort-desc"}
37893 if(this.grid.enableColLock !== false){
37894 this.hmenu.add('-',
37895 {id:"lock", text: this.lockText, cls: "xg-hmenu-lock"},
37896 {id:"unlock", text: this.unlockText, cls: "xg-hmenu-unlock"}
37899 if(this.grid.enableColumnHide !== false){
37901 this.colMenu = new Roo.menu.Menu({id:this.grid.id + "-hcols-menu"});
37902 this.colMenu.on("beforeshow", this.beforeColMenuShow, this);
37903 this.colMenu.on("itemclick", this.handleHdMenuClick, this);
37905 this.hmenu.add('-',
37906 {id:"columns", text: this.columnsText, menu: this.colMenu}
37909 this.hmenu.on("itemclick", this.handleHdMenuClick, this);
37911 this.grid.on("headercontextmenu", this.handleHdCtx, this);
37914 if((this.grid.enableDragDrop || this.grid.enableDrag) && Roo.grid.GridDragZone){
37915 this.dd = new Roo.grid.GridDragZone(this.grid, {
37916 ddGroup : this.grid.ddGroup || 'GridDD'
37922 for(var i = 0; i < colCount; i++){
37923 if(cm.isHidden(i)){
37924 this.hideColumn(i);
37926 if(cm.config[i].align){
37927 this.css.updateRule(this.colSelector + i, "textAlign", cm.config[i].align);
37928 this.css.updateRule(this.hdSelector + i, "textAlign", cm.config[i].align);
37932 this.updateHeaderSortState();
37934 this.beforeInitialResize();
37937 // two part rendering gives faster view to the user
37938 this.renderPhase2.defer(1, this);
37941 renderPhase2 : function(){
37942 // render the rows now
37944 if(this.grid.autoSizeColumns){
37945 this.autoSizeColumns();
37949 beforeInitialResize : function(){
37953 onColumnSplitterMoved : function(i, w){
37954 this.userResized = true;
37955 var cm = this.grid.colModel;
37956 cm.setColumnWidth(i, w, true);
37957 var cid = cm.getColumnId(i);
37958 this.css.updateRule(this.colSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
37959 this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
37960 this.updateSplitters();
37962 this.grid.fireEvent("columnresize", i, w);
37965 syncRowHeights : function(startIndex, endIndex){
37966 if(this.grid.enableRowHeightSync === true && this.cm.getLockedCount() > 0){
37967 startIndex = startIndex || 0;
37968 var mrows = this.getBodyTable().rows;
37969 var lrows = this.getLockedTable().rows;
37970 var len = mrows.length-1;
37971 endIndex = Math.min(endIndex || len, len);
37972 for(var i = startIndex; i <= endIndex; i++){
37973 var m = mrows[i], l = lrows[i];
37974 var h = Math.max(m.offsetHeight, l.offsetHeight);
37975 m.style.height = l.style.height = h + "px";
37980 layout : function(initialRender, is2ndPass){
37982 var auto = g.autoHeight;
37983 var scrollOffset = 16;
37984 var c = g.getGridEl(), cm = this.cm,
37985 expandCol = g.autoExpandColumn,
37987 //c.beginMeasure();
37989 if(!c.dom.offsetWidth){ // display:none?
37991 this.lockedWrap.show();
37992 this.mainWrap.show();
37997 var hasLock = this.cm.isLocked(0);
37999 var tbh = this.headerPanel.getHeight();
38000 var bbh = this.footerPanel.getHeight();
38003 var ch = this.getBodyTable().offsetHeight + tbh + bbh + this.mainHd.getHeight();
38004 var newHeight = ch + c.getBorderWidth("tb");
38006 newHeight = Math.min(g.maxHeight, newHeight);
38008 c.setHeight(newHeight);
38012 c.setWidth(cm.getTotalWidth()+c.getBorderWidth('lr'));
38015 var s = this.scroller;
38017 var csize = c.getSize(true);
38019 this.el.setSize(csize.width, csize.height);
38021 this.headerPanel.setWidth(csize.width);
38022 this.footerPanel.setWidth(csize.width);
38024 var hdHeight = this.mainHd.getHeight();
38025 var vw = csize.width;
38026 var vh = csize.height - (tbh + bbh);
38030 var bt = this.getBodyTable();
38031 var ltWidth = hasLock ?
38032 Math.max(this.getLockedTable().offsetWidth, this.lockedHd.dom.firstChild.offsetWidth) : 0;
38034 var scrollHeight = bt.offsetHeight;
38035 var scrollWidth = ltWidth + bt.offsetWidth;
38036 var vscroll = false, hscroll = false;
38038 this.scrollSizer.setSize(scrollWidth, scrollHeight+hdHeight);
38040 var lw = this.lockedWrap, mw = this.mainWrap;
38041 var lb = this.lockedBody, mb = this.mainBody;
38043 setTimeout(function(){
38044 var t = s.dom.offsetTop;
38045 var w = s.dom.clientWidth,
38046 h = s.dom.clientHeight;
38049 lw.setSize(ltWidth, h);
38051 mw.setLeftTop(ltWidth, t);
38052 mw.setSize(w-ltWidth, h);
38054 lb.setHeight(h-hdHeight);
38055 mb.setHeight(h-hdHeight);
38057 if(is2ndPass !== true && !gv.userResized && expandCol){
38058 // high speed resize without full column calculation
38060 var ci = cm.getIndexById(expandCol);
38062 ci = cm.findColumnIndex(expandCol);
38064 ci = Math.max(0, ci); // make sure it's got at least the first col.
38065 var expandId = cm.getColumnId(ci);
38066 var tw = cm.getTotalWidth(false);
38067 var currentWidth = cm.getColumnWidth(ci);
38068 var cw = Math.min(Math.max(((w-tw)+currentWidth-2)-/*scrollbar*/(w <= s.dom.offsetWidth ? 0 : 18), g.autoExpandMin), g.autoExpandMax);
38069 if(currentWidth != cw){
38070 cm.setColumnWidth(ci, cw, true);
38071 gv.css.updateRule(gv.colSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
38072 gv.css.updateRule(gv.hdSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
38073 gv.updateSplitters();
38074 gv.layout(false, true);
38086 onWindowResize : function(){
38087 if(!this.grid.monitorWindowResize || this.grid.autoHeight){
38093 appendFooter : function(parentEl){
38097 sortAscText : "Sort Ascending",
38098 sortDescText : "Sort Descending",
38099 lockText : "Lock Column",
38100 unlockText : "Unlock Column",
38101 columnsText : "Columns"
38105 Roo.grid.GridView.ColumnDragZone = function(grid, hd){
38106 Roo.grid.GridView.ColumnDragZone.superclass.constructor.call(this, grid, hd, null);
38107 this.proxy.el.addClass('x-grid3-col-dd');
38110 Roo.extend(Roo.grid.GridView.ColumnDragZone, Roo.grid.HeaderDragZone, {
38111 handleMouseDown : function(e){
38115 callHandleMouseDown : function(e){
38116 Roo.grid.GridView.ColumnDragZone.superclass.handleMouseDown.call(this, e);
38121 * Ext JS Library 1.1.1
38122 * Copyright(c) 2006-2007, Ext JS, LLC.
38124 * Originally Released Under LGPL - original licence link has changed is not relivant.
38127 * <script type="text/javascript">
38131 // This is a support class used internally by the Grid components
38132 Roo.grid.SplitDragZone = function(grid, hd, hd2){
38134 this.view = grid.getView();
38135 this.proxy = this.view.resizeProxy;
38136 Roo.grid.SplitDragZone.superclass.constructor.call(this, hd,
38137 "gridSplitters" + this.grid.getGridEl().id, {
38138 dragElId : Roo.id(this.proxy.dom), resizeFrame:false
38140 this.setHandleElId(Roo.id(hd));
38141 this.setOuterHandleElId(Roo.id(hd2));
38142 this.scroll = false;
38144 Roo.extend(Roo.grid.SplitDragZone, Roo.dd.DDProxy, {
38145 fly: Roo.Element.fly,
38147 b4StartDrag : function(x, y){
38148 this.view.headersDisabled = true;
38149 this.proxy.setHeight(this.view.mainWrap.getHeight());
38150 var w = this.cm.getColumnWidth(this.cellIndex);
38151 var minw = Math.max(w-this.grid.minColumnWidth, 0);
38152 this.resetConstraints();
38153 this.setXConstraint(minw, 1000);
38154 this.setYConstraint(0, 0);
38155 this.minX = x - minw;
38156 this.maxX = x + 1000;
38158 Roo.dd.DDProxy.prototype.b4StartDrag.call(this, x, y);
38162 handleMouseDown : function(e){
38163 ev = Roo.EventObject.setEvent(e);
38164 var t = this.fly(ev.getTarget());
38165 if(t.hasClass("x-grid-split")){
38166 this.cellIndex = this.view.getCellIndex(t.dom);
38167 this.split = t.dom;
38168 this.cm = this.grid.colModel;
38169 if(this.cm.isResizable(this.cellIndex) && !this.cm.isFixed(this.cellIndex)){
38170 Roo.grid.SplitDragZone.superclass.handleMouseDown.apply(this, arguments);
38175 endDrag : function(e){
38176 this.view.headersDisabled = false;
38177 var endX = Math.max(this.minX, Roo.lib.Event.getPageX(e));
38178 var diff = endX - this.startPos;
38179 this.view.onColumnSplitterMoved(this.cellIndex, this.cm.getColumnWidth(this.cellIndex)+diff);
38182 autoOffset : function(){
38183 this.setDelta(0,0);
38187 * Ext JS Library 1.1.1
38188 * Copyright(c) 2006-2007, Ext JS, LLC.
38190 * Originally Released Under LGPL - original licence link has changed is not relivant.
38193 * <script type="text/javascript">
38197 // This is a support class used internally by the Grid components
38198 Roo.grid.GridDragZone = function(grid, config){
38199 this.view = grid.getView();
38200 Roo.grid.GridDragZone.superclass.constructor.call(this, this.view.mainBody.dom, config);
38201 if(this.view.lockedBody){
38202 this.setHandleElId(Roo.id(this.view.mainBody.dom));
38203 this.setOuterHandleElId(Roo.id(this.view.lockedBody.dom));
38205 this.scroll = false;
38207 this.ddel = document.createElement('div');
38208 this.ddel.className = 'x-grid-dd-wrap';
38211 Roo.extend(Roo.grid.GridDragZone, Roo.dd.DragZone, {
38212 ddGroup : "GridDD",
38214 getDragData : function(e){
38215 var t = Roo.lib.Event.getTarget(e);
38216 var rowIndex = this.view.findRowIndex(t);
38217 var sm = this.grid.selModel;
38219 //Roo.log(rowIndex);
38221 if (sm.getSelectedCell) {
38222 // cell selection..
38223 if (!sm.getSelectedCell()) {
38226 if (rowIndex != sm.getSelectedCell()[0]) {
38232 if(rowIndex !== false){
38237 //Roo.log([ sm.getSelectedCell() ? sm.getSelectedCell()[0] : 'NO' , rowIndex ]);
38239 //if(!sm.isSelected(rowIndex) || e.hasModifier()){
38242 if (e.hasModifier()){
38243 sm.handleMouseDown(e, t); // non modifier buttons are handled by row select.
38246 Roo.log("getDragData");
38251 rowIndex: rowIndex,
38252 selections:sm.getSelections ? sm.getSelections() : (
38253 sm.getSelectedCell() ? [ this.grid.ds.getAt(sm.getSelectedCell()[0]) ] : []
38260 onInitDrag : function(e){
38261 var data = this.dragData;
38262 this.ddel.innerHTML = this.grid.getDragDropText();
38263 this.proxy.update(this.ddel);
38264 // fire start drag?
38267 afterRepair : function(){
38268 this.dragging = false;
38271 getRepairXY : function(e, data){
38275 onEndDrag : function(data, e){
38279 onValidDrop : function(dd, e, id){
38284 beforeInvalidDrop : function(e, id){
38289 * Ext JS Library 1.1.1
38290 * Copyright(c) 2006-2007, Ext JS, LLC.
38292 * Originally Released Under LGPL - original licence link has changed is not relivant.
38295 * <script type="text/javascript">
38300 * @class Roo.grid.ColumnModel
38301 * @extends Roo.util.Observable
38302 * This is the default implementation of a ColumnModel used by the Grid. It defines
38303 * the columns in the grid.
38306 var colModel = new Roo.grid.ColumnModel([
38307 {header: "Ticker", width: 60, sortable: true, locked: true},
38308 {header: "Company Name", width: 150, sortable: true},
38309 {header: "Market Cap.", width: 100, sortable: true},
38310 {header: "$ Sales", width: 100, sortable: true, renderer: money},
38311 {header: "Employees", width: 100, sortable: true, resizable: false}
38316 * The config options listed for this class are options which may appear in each
38317 * individual column definition.
38318 * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
38320 * @param {Object} config An Array of column config objects. See this class's
38321 * config objects for details.
38323 Roo.grid.ColumnModel = function(config){
38325 * The config passed into the constructor
38327 this.config = config;
38330 // if no id, create one
38331 // if the column does not have a dataIndex mapping,
38332 // map it to the order it is in the config
38333 for(var i = 0, len = config.length; i < len; i++){
38335 if(typeof c.dataIndex == "undefined"){
38338 if(typeof c.renderer == "string"){
38339 c.renderer = Roo.util.Format[c.renderer];
38341 if(typeof c.id == "undefined"){
38344 if(c.editor && c.editor.xtype){
38345 c.editor = Roo.factory(c.editor, Roo.grid);
38347 if(c.editor && c.editor.isFormField){
38348 c.editor = new Roo.grid.GridEditor(c.editor);
38350 this.lookup[c.id] = c;
38354 * The width of columns which have no width specified (defaults to 100)
38357 this.defaultWidth = 100;
38360 * Default sortable of columns which have no sortable specified (defaults to false)
38363 this.defaultSortable = false;
38367 * @event widthchange
38368 * Fires when the width of a column changes.
38369 * @param {ColumnModel} this
38370 * @param {Number} columnIndex The column index
38371 * @param {Number} newWidth The new width
38373 "widthchange": true,
38375 * @event headerchange
38376 * Fires when the text of a header changes.
38377 * @param {ColumnModel} this
38378 * @param {Number} columnIndex The column index
38379 * @param {Number} newText The new header text
38381 "headerchange": true,
38383 * @event hiddenchange
38384 * Fires when a column is hidden or "unhidden".
38385 * @param {ColumnModel} this
38386 * @param {Number} columnIndex The column index
38387 * @param {Boolean} hidden true if hidden, false otherwise
38389 "hiddenchange": true,
38391 * @event columnmoved
38392 * Fires when a column is moved.
38393 * @param {ColumnModel} this
38394 * @param {Number} oldIndex
38395 * @param {Number} newIndex
38397 "columnmoved" : true,
38399 * @event columlockchange
38400 * Fires when a column's locked state is changed
38401 * @param {ColumnModel} this
38402 * @param {Number} colIndex
38403 * @param {Boolean} locked true if locked
38405 "columnlockchange" : true
38407 Roo.grid.ColumnModel.superclass.constructor.call(this);
38409 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
38411 * @cfg {String} header The header text to display in the Grid view.
38414 * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
38415 * {@link Roo.data.Record} definition from which to draw the column's value. If not
38416 * specified, the column's index is used as an index into the Record's data Array.
38419 * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
38420 * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
38423 * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
38424 * Defaults to the value of the {@link #defaultSortable} property.
38425 * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
38428 * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid. Defaults to false.
38431 * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed. Defaults to false.
38434 * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
38437 * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
38440 * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
38441 * given the cell's data value. See {@link #setRenderer}. If not specified, the
38442 * default renderer uses the raw data value.
38445 * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor
38448 * @cfg {String} align (Optional) Set the CSS text-align property of the column. Defaults to undefined.
38452 * Returns the id of the column at the specified index.
38453 * @param {Number} index The column index
38454 * @return {String} the id
38456 getColumnId : function(index){
38457 return this.config[index].id;
38461 * Returns the column for a specified id.
38462 * @param {String} id The column id
38463 * @return {Object} the column
38465 getColumnById : function(id){
38466 return this.lookup[id];
38471 * Returns the column for a specified dataIndex.
38472 * @param {String} dataIndex The column dataIndex
38473 * @return {Object|Boolean} the column or false if not found
38475 getColumnByDataIndex: function(dataIndex){
38476 var index = this.findColumnIndex(dataIndex);
38477 return index > -1 ? this.config[index] : false;
38481 * Returns the index for a specified column id.
38482 * @param {String} id The column id
38483 * @return {Number} the index, or -1 if not found
38485 getIndexById : function(id){
38486 for(var i = 0, len = this.config.length; i < len; i++){
38487 if(this.config[i].id == id){
38495 * Returns the index for a specified column dataIndex.
38496 * @param {String} dataIndex The column dataIndex
38497 * @return {Number} the index, or -1 if not found
38500 findColumnIndex : function(dataIndex){
38501 for(var i = 0, len = this.config.length; i < len; i++){
38502 if(this.config[i].dataIndex == dataIndex){
38510 moveColumn : function(oldIndex, newIndex){
38511 var c = this.config[oldIndex];
38512 this.config.splice(oldIndex, 1);
38513 this.config.splice(newIndex, 0, c);
38514 this.dataMap = null;
38515 this.fireEvent("columnmoved", this, oldIndex, newIndex);
38518 isLocked : function(colIndex){
38519 return this.config[colIndex].locked === true;
38522 setLocked : function(colIndex, value, suppressEvent){
38523 if(this.isLocked(colIndex) == value){
38526 this.config[colIndex].locked = value;
38527 if(!suppressEvent){
38528 this.fireEvent("columnlockchange", this, colIndex, value);
38532 getTotalLockedWidth : function(){
38533 var totalWidth = 0;
38534 for(var i = 0; i < this.config.length; i++){
38535 if(this.isLocked(i) && !this.isHidden(i)){
38536 this.totalWidth += this.getColumnWidth(i);
38542 getLockedCount : function(){
38543 for(var i = 0, len = this.config.length; i < len; i++){
38544 if(!this.isLocked(i)){
38551 * Returns the number of columns.
38554 getColumnCount : function(visibleOnly){
38555 if(visibleOnly === true){
38557 for(var i = 0, len = this.config.length; i < len; i++){
38558 if(!this.isHidden(i)){
38564 return this.config.length;
38568 * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
38569 * @param {Function} fn
38570 * @param {Object} scope (optional)
38571 * @return {Array} result
38573 getColumnsBy : function(fn, scope){
38575 for(var i = 0, len = this.config.length; i < len; i++){
38576 var c = this.config[i];
38577 if(fn.call(scope||this, c, i) === true){
38585 * Returns true if the specified column is sortable.
38586 * @param {Number} col The column index
38587 * @return {Boolean}
38589 isSortable : function(col){
38590 if(typeof this.config[col].sortable == "undefined"){
38591 return this.defaultSortable;
38593 return this.config[col].sortable;
38597 * Returns the rendering (formatting) function defined for the column.
38598 * @param {Number} col The column index.
38599 * @return {Function} The function used to render the cell. See {@link #setRenderer}.
38601 getRenderer : function(col){
38602 if(!this.config[col].renderer){
38603 return Roo.grid.ColumnModel.defaultRenderer;
38605 return this.config[col].renderer;
38609 * Sets the rendering (formatting) function for a column.
38610 * @param {Number} col The column index
38611 * @param {Function} fn The function to use to process the cell's raw data
38612 * to return HTML markup for the grid view. The render function is called with
38613 * the following parameters:<ul>
38614 * <li>Data value.</li>
38615 * <li>Cell metadata. An object in which you may set the following attributes:<ul>
38616 * <li>css A CSS style string to apply to the table cell.</li>
38617 * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
38618 * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
38619 * <li>Row index</li>
38620 * <li>Column index</li>
38621 * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
38623 setRenderer : function(col, fn){
38624 this.config[col].renderer = fn;
38628 * Returns the width for the specified column.
38629 * @param {Number} col The column index
38632 getColumnWidth : function(col){
38633 return this.config[col].width * 1 || this.defaultWidth;
38637 * Sets the width for a column.
38638 * @param {Number} col The column index
38639 * @param {Number} width The new width
38641 setColumnWidth : function(col, width, suppressEvent){
38642 this.config[col].width = width;
38643 this.totalWidth = null;
38644 if(!suppressEvent){
38645 this.fireEvent("widthchange", this, col, width);
38650 * Returns the total width of all columns.
38651 * @param {Boolean} includeHidden True to include hidden column widths
38654 getTotalWidth : function(includeHidden){
38655 if(!this.totalWidth){
38656 this.totalWidth = 0;
38657 for(var i = 0, len = this.config.length; i < len; i++){
38658 if(includeHidden || !this.isHidden(i)){
38659 this.totalWidth += this.getColumnWidth(i);
38663 return this.totalWidth;
38667 * Returns the header for the specified column.
38668 * @param {Number} col The column index
38671 getColumnHeader : function(col){
38672 return this.config[col].header;
38676 * Sets the header for a column.
38677 * @param {Number} col The column index
38678 * @param {String} header The new header
38680 setColumnHeader : function(col, header){
38681 this.config[col].header = header;
38682 this.fireEvent("headerchange", this, col, header);
38686 * Returns the tooltip for the specified column.
38687 * @param {Number} col The column index
38690 getColumnTooltip : function(col){
38691 return this.config[col].tooltip;
38694 * Sets the tooltip for a column.
38695 * @param {Number} col The column index
38696 * @param {String} tooltip The new tooltip
38698 setColumnTooltip : function(col, tooltip){
38699 this.config[col].tooltip = tooltip;
38703 * Returns the dataIndex for the specified column.
38704 * @param {Number} col The column index
38707 getDataIndex : function(col){
38708 return this.config[col].dataIndex;
38712 * Sets the dataIndex for a column.
38713 * @param {Number} col The column index
38714 * @param {Number} dataIndex The new dataIndex
38716 setDataIndex : function(col, dataIndex){
38717 this.config[col].dataIndex = dataIndex;
38723 * Returns true if the cell is editable.
38724 * @param {Number} colIndex The column index
38725 * @param {Number} rowIndex The row index
38726 * @return {Boolean}
38728 isCellEditable : function(colIndex, rowIndex){
38729 return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
38733 * Returns the editor defined for the cell/column.
38734 * return false or null to disable editing.
38735 * @param {Number} colIndex The column index
38736 * @param {Number} rowIndex The row index
38739 getCellEditor : function(colIndex, rowIndex){
38740 return this.config[colIndex].editor;
38744 * Sets if a column is editable.
38745 * @param {Number} col The column index
38746 * @param {Boolean} editable True if the column is editable
38748 setEditable : function(col, editable){
38749 this.config[col].editable = editable;
38754 * Returns true if the column is hidden.
38755 * @param {Number} colIndex The column index
38756 * @return {Boolean}
38758 isHidden : function(colIndex){
38759 return this.config[colIndex].hidden;
38764 * Returns true if the column width cannot be changed
38766 isFixed : function(colIndex){
38767 return this.config[colIndex].fixed;
38771 * Returns true if the column can be resized
38772 * @return {Boolean}
38774 isResizable : function(colIndex){
38775 return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
38778 * Sets if a column is hidden.
38779 * @param {Number} colIndex The column index
38780 * @param {Boolean} hidden True if the column is hidden
38782 setHidden : function(colIndex, hidden){
38783 this.config[colIndex].hidden = hidden;
38784 this.totalWidth = null;
38785 this.fireEvent("hiddenchange", this, colIndex, hidden);
38789 * Sets the editor for a column.
38790 * @param {Number} col The column index
38791 * @param {Object} editor The editor object
38793 setEditor : function(col, editor){
38794 this.config[col].editor = editor;
38798 Roo.grid.ColumnModel.defaultRenderer = function(value){
38799 if(typeof value == "string" && value.length < 1){
38805 // Alias for backwards compatibility
38806 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
38809 * Ext JS Library 1.1.1
38810 * Copyright(c) 2006-2007, Ext JS, LLC.
38812 * Originally Released Under LGPL - original licence link has changed is not relivant.
38815 * <script type="text/javascript">
38819 * @class Roo.grid.AbstractSelectionModel
38820 * @extends Roo.util.Observable
38821 * Abstract base class for grid SelectionModels. It provides the interface that should be
38822 * implemented by descendant classes. This class should not be directly instantiated.
38825 Roo.grid.AbstractSelectionModel = function(){
38826 this.locked = false;
38827 Roo.grid.AbstractSelectionModel.superclass.constructor.call(this);
38830 Roo.extend(Roo.grid.AbstractSelectionModel, Roo.util.Observable, {
38831 /** @ignore Called by the grid automatically. Do not call directly. */
38832 init : function(grid){
38838 * Locks the selections.
38841 this.locked = true;
38845 * Unlocks the selections.
38847 unlock : function(){
38848 this.locked = false;
38852 * Returns true if the selections are locked.
38853 * @return {Boolean}
38855 isLocked : function(){
38856 return this.locked;
38860 * Ext JS Library 1.1.1
38861 * Copyright(c) 2006-2007, Ext JS, LLC.
38863 * Originally Released Under LGPL - original licence link has changed is not relivant.
38866 * <script type="text/javascript">
38869 * @extends Roo.grid.AbstractSelectionModel
38870 * @class Roo.grid.RowSelectionModel
38871 * The default SelectionModel used by {@link Roo.grid.Grid}.
38872 * It supports multiple selections and keyboard selection/navigation.
38874 * @param {Object} config
38876 Roo.grid.RowSelectionModel = function(config){
38877 Roo.apply(this, config);
38878 this.selections = new Roo.util.MixedCollection(false, function(o){
38883 this.lastActive = false;
38887 * @event selectionchange
38888 * Fires when the selection changes
38889 * @param {SelectionModel} this
38891 "selectionchange" : true,
38893 * @event afterselectionchange
38894 * Fires after the selection changes (eg. by key press or clicking)
38895 * @param {SelectionModel} this
38897 "afterselectionchange" : true,
38899 * @event beforerowselect
38900 * Fires when a row is selected being selected, return false to cancel.
38901 * @param {SelectionModel} this
38902 * @param {Number} rowIndex The selected index
38903 * @param {Boolean} keepExisting False if other selections will be cleared
38905 "beforerowselect" : true,
38908 * Fires when a row is selected.
38909 * @param {SelectionModel} this
38910 * @param {Number} rowIndex The selected index
38911 * @param {Roo.data.Record} r The record
38913 "rowselect" : true,
38915 * @event rowdeselect
38916 * Fires when a row is deselected.
38917 * @param {SelectionModel} this
38918 * @param {Number} rowIndex The selected index
38920 "rowdeselect" : true
38922 Roo.grid.RowSelectionModel.superclass.constructor.call(this);
38923 this.locked = false;
38926 Roo.extend(Roo.grid.RowSelectionModel, Roo.grid.AbstractSelectionModel, {
38928 * @cfg {Boolean} singleSelect
38929 * True to allow selection of only one row at a time (defaults to false)
38931 singleSelect : false,
38934 initEvents : function(){
38936 if(!this.grid.enableDragDrop && !this.grid.enableDrag){
38937 this.grid.on("mousedown", this.handleMouseDown, this);
38938 }else{ // allow click to work like normal
38939 this.grid.on("rowclick", this.handleDragableRowClick, this);
38942 this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
38943 "up" : function(e){
38945 this.selectPrevious(e.shiftKey);
38946 }else if(this.last !== false && this.lastActive !== false){
38947 var last = this.last;
38948 this.selectRange(this.last, this.lastActive-1);
38949 this.grid.getView().focusRow(this.lastActive);
38950 if(last !== false){
38954 this.selectFirstRow();
38956 this.fireEvent("afterselectionchange", this);
38958 "down" : function(e){
38960 this.selectNext(e.shiftKey);
38961 }else if(this.last !== false && this.lastActive !== false){
38962 var last = this.last;
38963 this.selectRange(this.last, this.lastActive+1);
38964 this.grid.getView().focusRow(this.lastActive);
38965 if(last !== false){
38969 this.selectFirstRow();
38971 this.fireEvent("afterselectionchange", this);
38976 var view = this.grid.view;
38977 view.on("refresh", this.onRefresh, this);
38978 view.on("rowupdated", this.onRowUpdated, this);
38979 view.on("rowremoved", this.onRemove, this);
38983 onRefresh : function(){
38984 var ds = this.grid.dataSource, i, v = this.grid.view;
38985 var s = this.selections;
38986 s.each(function(r){
38987 if((i = ds.indexOfId(r.id)) != -1){
38996 onRemove : function(v, index, r){
38997 this.selections.remove(r);
39001 onRowUpdated : function(v, index, r){
39002 if(this.isSelected(r)){
39003 v.onRowSelect(index);
39009 * @param {Array} records The records to select
39010 * @param {Boolean} keepExisting (optional) True to keep existing selections
39012 selectRecords : function(records, keepExisting){
39014 this.clearSelections();
39016 var ds = this.grid.dataSource;
39017 for(var i = 0, len = records.length; i < len; i++){
39018 this.selectRow(ds.indexOf(records[i]), true);
39023 * Gets the number of selected rows.
39026 getCount : function(){
39027 return this.selections.length;
39031 * Selects the first row in the grid.
39033 selectFirstRow : function(){
39038 * Select the last row.
39039 * @param {Boolean} keepExisting (optional) True to keep existing selections
39041 selectLastRow : function(keepExisting){
39042 this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
39046 * Selects the row immediately following the last selected row.
39047 * @param {Boolean} keepExisting (optional) True to keep existing selections
39049 selectNext : function(keepExisting){
39050 if(this.last !== false && (this.last+1) < this.grid.dataSource.getCount()){
39051 this.selectRow(this.last+1, keepExisting);
39052 this.grid.getView().focusRow(this.last);
39057 * Selects the row that precedes the last selected row.
39058 * @param {Boolean} keepExisting (optional) True to keep existing selections
39060 selectPrevious : function(keepExisting){
39062 this.selectRow(this.last-1, keepExisting);
39063 this.grid.getView().focusRow(this.last);
39068 * Returns the selected records
39069 * @return {Array} Array of selected records
39071 getSelections : function(){
39072 return [].concat(this.selections.items);
39076 * Returns the first selected record.
39079 getSelected : function(){
39080 return this.selections.itemAt(0);
39085 * Clears all selections.
39087 clearSelections : function(fast){
39088 if(this.locked) return;
39090 var ds = this.grid.dataSource;
39091 var s = this.selections;
39092 s.each(function(r){
39093 this.deselectRow(ds.indexOfId(r.id));
39097 this.selections.clear();
39104 * Selects all rows.
39106 selectAll : function(){
39107 if(this.locked) return;
39108 this.selections.clear();
39109 for(var i = 0, len = this.grid.dataSource.getCount(); i < len; i++){
39110 this.selectRow(i, true);
39115 * Returns True if there is a selection.
39116 * @return {Boolean}
39118 hasSelection : function(){
39119 return this.selections.length > 0;
39123 * Returns True if the specified row is selected.
39124 * @param {Number/Record} record The record or index of the record to check
39125 * @return {Boolean}
39127 isSelected : function(index){
39128 var r = typeof index == "number" ? this.grid.dataSource.getAt(index) : index;
39129 return (r && this.selections.key(r.id) ? true : false);
39133 * Returns True if the specified record id is selected.
39134 * @param {String} id The id of record to check
39135 * @return {Boolean}
39137 isIdSelected : function(id){
39138 return (this.selections.key(id) ? true : false);
39142 handleMouseDown : function(e, t){
39143 var view = this.grid.getView(), rowIndex;
39144 if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
39147 if(e.shiftKey && this.last !== false){
39148 var last = this.last;
39149 this.selectRange(last, rowIndex, e.ctrlKey);
39150 this.last = last; // reset the last
39151 view.focusRow(rowIndex);
39153 var isSelected = this.isSelected(rowIndex);
39154 if(e.button !== 0 && isSelected){
39155 view.focusRow(rowIndex);
39156 }else if(e.ctrlKey && isSelected){
39157 this.deselectRow(rowIndex);
39158 }else if(!isSelected){
39159 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
39160 view.focusRow(rowIndex);
39163 this.fireEvent("afterselectionchange", this);
39166 handleDragableRowClick : function(grid, rowIndex, e)
39168 if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
39169 this.selectRow(rowIndex, false);
39170 grid.view.focusRow(rowIndex);
39171 this.fireEvent("afterselectionchange", this);
39176 * Selects multiple rows.
39177 * @param {Array} rows Array of the indexes of the row to select
39178 * @param {Boolean} keepExisting (optional) True to keep existing selections
39180 selectRows : function(rows, keepExisting){
39182 this.clearSelections();
39184 for(var i = 0, len = rows.length; i < len; i++){
39185 this.selectRow(rows[i], true);
39190 * Selects a range of rows. All rows in between startRow and endRow are also selected.
39191 * @param {Number} startRow The index of the first row in the range
39192 * @param {Number} endRow The index of the last row in the range
39193 * @param {Boolean} keepExisting (optional) True to retain existing selections
39195 selectRange : function(startRow, endRow, keepExisting){
39196 if(this.locked) return;
39198 this.clearSelections();
39200 if(startRow <= endRow){
39201 for(var i = startRow; i <= endRow; i++){
39202 this.selectRow(i, true);
39205 for(var i = startRow; i >= endRow; i--){
39206 this.selectRow(i, true);
39212 * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
39213 * @param {Number} startRow The index of the first row in the range
39214 * @param {Number} endRow The index of the last row in the range
39216 deselectRange : function(startRow, endRow, preventViewNotify){
39217 if(this.locked) return;
39218 for(var i = startRow; i <= endRow; i++){
39219 this.deselectRow(i, preventViewNotify);
39225 * @param {Number} row The index of the row to select
39226 * @param {Boolean} keepExisting (optional) True to keep existing selections
39228 selectRow : function(index, keepExisting, preventViewNotify){
39229 if(this.locked || (index < 0 || index >= this.grid.dataSource.getCount())) return;
39230 if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
39231 if(!keepExisting || this.singleSelect){
39232 this.clearSelections();
39234 var r = this.grid.dataSource.getAt(index);
39235 this.selections.add(r);
39236 this.last = this.lastActive = index;
39237 if(!preventViewNotify){
39238 this.grid.getView().onRowSelect(index);
39240 this.fireEvent("rowselect", this, index, r);
39241 this.fireEvent("selectionchange", this);
39247 * @param {Number} row The index of the row to deselect
39249 deselectRow : function(index, preventViewNotify){
39250 if(this.locked) return;
39251 if(this.last == index){
39254 if(this.lastActive == index){
39255 this.lastActive = false;
39257 var r = this.grid.dataSource.getAt(index);
39258 this.selections.remove(r);
39259 if(!preventViewNotify){
39260 this.grid.getView().onRowDeselect(index);
39262 this.fireEvent("rowdeselect", this, index);
39263 this.fireEvent("selectionchange", this);
39267 restoreLast : function(){
39269 this.last = this._last;
39274 acceptsNav : function(row, col, cm){
39275 return !cm.isHidden(col) && cm.isCellEditable(col, row);
39279 onEditorKey : function(field, e){
39280 var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
39285 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
39287 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
39289 }else if(k == e.ENTER && !e.ctrlKey){
39293 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
39295 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
39297 }else if(k == e.ESC){
39301 g.startEditing(newCell[0], newCell[1]);
39306 * Ext JS Library 1.1.1
39307 * Copyright(c) 2006-2007, Ext JS, LLC.
39309 * Originally Released Under LGPL - original licence link has changed is not relivant.
39312 * <script type="text/javascript">
39315 * @class Roo.grid.CellSelectionModel
39316 * @extends Roo.grid.AbstractSelectionModel
39317 * This class provides the basic implementation for cell selection in a grid.
39319 * @param {Object} config The object containing the configuration of this model.
39320 * @cfg {Boolean} enter_is_tab Enter behaves the same as tab. (eg. goes to next cell) default: false
39322 Roo.grid.CellSelectionModel = function(config){
39323 Roo.apply(this, config);
39325 this.selection = null;
39329 * @event beforerowselect
39330 * Fires before a cell is selected.
39331 * @param {SelectionModel} this
39332 * @param {Number} rowIndex The selected row index
39333 * @param {Number} colIndex The selected cell index
39335 "beforecellselect" : true,
39337 * @event cellselect
39338 * Fires when a cell is selected.
39339 * @param {SelectionModel} this
39340 * @param {Number} rowIndex The selected row index
39341 * @param {Number} colIndex The selected cell index
39343 "cellselect" : true,
39345 * @event selectionchange
39346 * Fires when the active selection changes.
39347 * @param {SelectionModel} this
39348 * @param {Object} selection null for no selection or an object (o) with two properties
39350 <li>o.record: the record object for the row the selection is in</li>
39351 <li>o.cell: An array of [rowIndex, columnIndex]</li>
39354 "selectionchange" : true,
39357 * Fires when the tab (or enter) was pressed on the last editable cell
39358 * You can use this to trigger add new row.
39359 * @param {SelectionModel} this
39363 * @event beforeeditnext
39364 * Fires before the next editable sell is made active
39365 * You can use this to skip to another cell or fire the tabend
39366 * if you set cell to false
39367 * @param {Object} eventdata object : { cell : [ row, col ] }
39369 "beforeeditnext" : true
39371 Roo.grid.CellSelectionModel.superclass.constructor.call(this);
39374 Roo.extend(Roo.grid.CellSelectionModel, Roo.grid.AbstractSelectionModel, {
39376 enter_is_tab: false,
39379 initEvents : function(){
39380 this.grid.on("mousedown", this.handleMouseDown, this);
39381 this.grid.getGridEl().on(Roo.isIE ? "keydown" : "keypress", this.handleKeyDown, this);
39382 var view = this.grid.view;
39383 view.on("refresh", this.onViewChange, this);
39384 view.on("rowupdated", this.onRowUpdated, this);
39385 view.on("beforerowremoved", this.clearSelections, this);
39386 view.on("beforerowsinserted", this.clearSelections, this);
39387 if(this.grid.isEditor){
39388 this.grid.on("beforeedit", this.beforeEdit, this);
39393 beforeEdit : function(e){
39394 this.select(e.row, e.column, false, true, e.record);
39398 onRowUpdated : function(v, index, r){
39399 if(this.selection && this.selection.record == r){
39400 v.onCellSelect(index, this.selection.cell[1]);
39405 onViewChange : function(){
39406 this.clearSelections(true);
39410 * Returns the currently selected cell,.
39411 * @return {Array} The selected cell (row, column) or null if none selected.
39413 getSelectedCell : function(){
39414 return this.selection ? this.selection.cell : null;
39418 * Clears all selections.
39419 * @param {Boolean} true to prevent the gridview from being notified about the change.
39421 clearSelections : function(preventNotify){
39422 var s = this.selection;
39424 if(preventNotify !== true){
39425 this.grid.view.onCellDeselect(s.cell[0], s.cell[1]);
39427 this.selection = null;
39428 this.fireEvent("selectionchange", this, null);
39433 * Returns true if there is a selection.
39434 * @return {Boolean}
39436 hasSelection : function(){
39437 return this.selection ? true : false;
39441 handleMouseDown : function(e, t){
39442 var v = this.grid.getView();
39443 if(this.isLocked()){
39446 var row = v.findRowIndex(t);
39447 var cell = v.findCellIndex(t);
39448 if(row !== false && cell !== false){
39449 this.select(row, cell);
39455 * @param {Number} rowIndex
39456 * @param {Number} collIndex
39458 select : function(rowIndex, colIndex, preventViewNotify, preventFocus, /*internal*/ r){
39459 if(this.fireEvent("beforecellselect", this, rowIndex, colIndex) !== false){
39460 this.clearSelections();
39461 r = r || this.grid.dataSource.getAt(rowIndex);
39464 cell : [rowIndex, colIndex]
39466 if(!preventViewNotify){
39467 var v = this.grid.getView();
39468 v.onCellSelect(rowIndex, colIndex);
39469 if(preventFocus !== true){
39470 v.focusCell(rowIndex, colIndex);
39473 this.fireEvent("cellselect", this, rowIndex, colIndex);
39474 this.fireEvent("selectionchange", this, this.selection);
39479 isSelectable : function(rowIndex, colIndex, cm){
39480 return !cm.isHidden(colIndex);
39484 handleKeyDown : function(e){
39485 //Roo.log('Cell Sel Model handleKeyDown');
39486 if(!e.isNavKeyPress()){
39489 var g = this.grid, s = this.selection;
39492 var cell = g.walkCells(0, 0, 1, this.isSelectable, this);
39494 this.select(cell[0], cell[1]);
39499 var walk = function(row, col, step){
39500 return g.walkCells(row, col, step, sm.isSelectable, sm);
39502 var k = e.getKey(), r = s.cell[0], c = s.cell[1];
39509 // handled by onEditorKey
39510 if (g.isEditor && g.editing) {
39514 newCell = walk(r, c-1, -1);
39516 newCell = walk(r, c+1, 1);
39521 newCell = walk(r+1, c, 1);
39525 newCell = walk(r-1, c, -1);
39529 newCell = walk(r, c+1, 1);
39533 newCell = walk(r, c-1, -1);
39538 if(g.isEditor && !g.editing){
39539 g.startEditing(r, c);
39548 this.select(newCell[0], newCell[1]);
39554 acceptsNav : function(row, col, cm){
39555 return !cm.isHidden(col) && cm.isCellEditable(col, row);
39559 * @param {Number} field (not used) - as it's normally used as a listener
39560 * @param {Number} e - event - fake it by using
39562 * var e = Roo.EventObjectImpl.prototype;
39563 * e.keyCode = e.TAB
39567 onEditorKey : function(field, e){
39569 var k = e.getKey(),
39572 ed = g.activeEditor,
39574 ///Roo.log('onEditorKey' + k);
39577 if (this.enter_is_tab && k == e.ENTER) {
39583 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
39585 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
39591 } else if(k == e.ENTER && !e.ctrlKey){
39594 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
39596 } else if(k == e.ESC){
39601 var ecall = { cell : newCell, forward : forward };
39602 this.fireEvent('beforeeditnext', ecall );
39603 newCell = ecall.cell;
39604 forward = ecall.forward;
39608 //Roo.log('next cell after edit');
39609 g.startEditing.defer(100, g, [newCell[0], newCell[1]]);
39610 } else if (forward) {
39611 // tabbed past last
39612 this.fireEvent.defer(100, this, ['tabend',this]);
39617 * Ext JS Library 1.1.1
39618 * Copyright(c) 2006-2007, Ext JS, LLC.
39620 * Originally Released Under LGPL - original licence link has changed is not relivant.
39623 * <script type="text/javascript">
39627 * @class Roo.grid.EditorGrid
39628 * @extends Roo.grid.Grid
39629 * Class for creating and editable grid.
39630 * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
39631 * The container MUST have some type of size defined for the grid to fill. The container will be
39632 * automatically set to position relative if it isn't already.
39633 * @param {Object} dataSource The data model to bind to
39634 * @param {Object} colModel The column model with info about this grid's columns
39636 Roo.grid.EditorGrid = function(container, config){
39637 Roo.grid.EditorGrid.superclass.constructor.call(this, container, config);
39638 this.getGridEl().addClass("xedit-grid");
39640 if(!this.selModel){
39641 this.selModel = new Roo.grid.CellSelectionModel();
39644 this.activeEditor = null;
39648 * @event beforeedit
39649 * Fires before cell editing is triggered. The edit event object has the following properties <br />
39650 * <ul style="padding:5px;padding-left:16px;">
39651 * <li>grid - This grid</li>
39652 * <li>record - The record being edited</li>
39653 * <li>field - The field name being edited</li>
39654 * <li>value - The value for the field being edited.</li>
39655 * <li>row - The grid row index</li>
39656 * <li>column - The grid column index</li>
39657 * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
39659 * @param {Object} e An edit event (see above for description)
39661 "beforeedit" : true,
39664 * Fires after a cell is edited. <br />
39665 * <ul style="padding:5px;padding-left:16px;">
39666 * <li>grid - This grid</li>
39667 * <li>record - The record being edited</li>
39668 * <li>field - The field name being edited</li>
39669 * <li>value - The value being set</li>
39670 * <li>originalValue - The original value for the field, before the edit.</li>
39671 * <li>row - The grid row index</li>
39672 * <li>column - The grid column index</li>
39674 * @param {Object} e An edit event (see above for description)
39676 "afteredit" : true,
39678 * @event validateedit
39679 * Fires after a cell is edited, but before the value is set in the record.
39680 * You can use this to modify the value being set in the field, Return false
39681 * to cancel the change. The edit event object has the following properties <br />
39682 * <ul style="padding:5px;padding-left:16px;">
39683 * <li>editor - This editor</li>
39684 * <li>grid - This grid</li>
39685 * <li>record - The record being edited</li>
39686 * <li>field - The field name being edited</li>
39687 * <li>value - The value being set</li>
39688 * <li>originalValue - The original value for the field, before the edit.</li>
39689 * <li>row - The grid row index</li>
39690 * <li>column - The grid column index</li>
39691 * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
39693 * @param {Object} e An edit event (see above for description)
39695 "validateedit" : true
39697 this.on("bodyscroll", this.stopEditing, this);
39698 this.on(this.clicksToEdit == 1 ? "cellclick" : "celldblclick", this.onCellDblClick, this);
39701 Roo.extend(Roo.grid.EditorGrid, Roo.grid.Grid, {
39703 * @cfg {Number} clicksToEdit
39704 * The number of clicks on a cell required to display the cell's editor (defaults to 2)
39711 trackMouseOver: false, // causes very odd FF errors
39713 onCellDblClick : function(g, row, col){
39714 this.startEditing(row, col);
39717 onEditComplete : function(ed, value, startValue){
39718 this.editing = false;
39719 this.activeEditor = null;
39720 ed.un("specialkey", this.selModel.onEditorKey, this.selModel);
39722 var field = this.colModel.getDataIndex(ed.col);
39727 originalValue: startValue,
39734 var cell = Roo.get(this.view.getCell(ed.row,ed.col))
39737 if(String(value) !== String(startValue)){
39739 if(this.fireEvent("validateedit", e) !== false && !e.cancel){
39740 r.set(field, e.value);
39741 // if we are dealing with a combo box..
39742 // then we also set the 'name' colum to be the displayField
39743 if (ed.field.displayField && ed.field.name) {
39744 r.set(ed.field.name, ed.field.el.dom.value);
39747 delete e.cancel; //?? why!!!
39748 this.fireEvent("afteredit", e);
39751 this.fireEvent("afteredit", e); // always fire it!
39753 this.view.focusCell(ed.row, ed.col);
39757 * Starts editing the specified for the specified row/column
39758 * @param {Number} rowIndex
39759 * @param {Number} colIndex
39761 startEditing : function(row, col){
39762 this.stopEditing();
39763 if(this.colModel.isCellEditable(col, row)){
39764 this.view.ensureVisible(row, col, true);
39766 var r = this.dataSource.getAt(row);
39767 var field = this.colModel.getDataIndex(col);
39768 var cell = Roo.get(this.view.getCell(row,col));
39773 value: r.data[field],
39778 if(this.fireEvent("beforeedit", e) !== false && !e.cancel){
39779 this.editing = true;
39780 var ed = this.colModel.getCellEditor(col, row);
39786 ed.render(ed.parentEl || document.body);
39792 (function(){ // complex but required for focus issues in safari, ie and opera
39796 ed.on("complete", this.onEditComplete, this, {single: true});
39797 ed.on("specialkey", this.selModel.onEditorKey, this.selModel);
39798 this.activeEditor = ed;
39799 var v = r.data[field];
39800 ed.startEdit(this.view.getCell(row, col), v);
39801 // combo's with 'displayField and name set
39802 if (ed.field.displayField && ed.field.name) {
39803 ed.field.el.dom.value = r.data[ed.field.name];
39807 }).defer(50, this);
39813 * Stops any active editing
39815 stopEditing : function(){
39816 if(this.activeEditor){
39817 this.activeEditor.completeEdit();
39819 this.activeEditor = null;
39823 * Called to get grid's drag proxy text, by default returns this.ddText.
39826 getDragDropText : function(){
39827 var count = this.selModel.getSelectedCell() ? 1 : 0;
39828 return String.format(this.ddText, count, count == 1 ? '' : 's');
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">
39842 // private - not really -- you end up using it !
39843 // This is a support class used internally by the Grid components
39846 * @class Roo.grid.GridEditor
39847 * @extends Roo.Editor
39848 * Class for creating and editable grid elements.
39849 * @param {Object} config any settings (must include field)
39851 Roo.grid.GridEditor = function(field, config){
39852 if (!config && field.field) {
39854 field = Roo.factory(config.field, Roo.form);
39856 Roo.grid.GridEditor.superclass.constructor.call(this, field, config);
39857 field.monitorTab = false;
39860 Roo.extend(Roo.grid.GridEditor, Roo.Editor, {
39863 * @cfg {Roo.form.Field} field Field to wrap (or xtyped)
39866 alignment: "tl-tl",
39869 cls: "x-small-editor x-grid-editor",
39874 * Ext JS Library 1.1.1
39875 * Copyright(c) 2006-2007, Ext JS, LLC.
39877 * Originally Released Under LGPL - original licence link has changed is not relivant.
39880 * <script type="text/javascript">
39885 Roo.grid.PropertyRecord = Roo.data.Record.create([
39886 {name:'name',type:'string'}, 'value'
39890 Roo.grid.PropertyStore = function(grid, source){
39892 this.store = new Roo.data.Store({
39893 recordType : Roo.grid.PropertyRecord
39895 this.store.on('update', this.onUpdate, this);
39897 this.setSource(source);
39899 Roo.grid.PropertyStore.superclass.constructor.call(this);
39904 Roo.extend(Roo.grid.PropertyStore, Roo.util.Observable, {
39905 setSource : function(o){
39907 this.store.removeAll();
39910 if(this.isEditableValue(o[k])){
39911 data.push(new Roo.grid.PropertyRecord({name: k, value: o[k]}, k));
39914 this.store.loadRecords({records: data}, {}, true);
39917 onUpdate : function(ds, record, type){
39918 if(type == Roo.data.Record.EDIT){
39919 var v = record.data['value'];
39920 var oldValue = record.modified['value'];
39921 if(this.grid.fireEvent('beforepropertychange', this.source, record.id, v, oldValue) !== false){
39922 this.source[record.id] = v;
39924 this.grid.fireEvent('propertychange', this.source, record.id, v, oldValue);
39931 getProperty : function(row){
39932 return this.store.getAt(row);
39935 isEditableValue: function(val){
39936 if(val && val instanceof Date){
39938 }else if(typeof val == 'object' || typeof val == 'function'){
39944 setValue : function(prop, value){
39945 this.source[prop] = value;
39946 this.store.getById(prop).set('value', value);
39949 getSource : function(){
39950 return this.source;
39954 Roo.grid.PropertyColumnModel = function(grid, store){
39957 g.PropertyColumnModel.superclass.constructor.call(this, [
39958 {header: this.nameText, sortable: true, dataIndex:'name', id: 'name'},
39959 {header: this.valueText, resizable:false, dataIndex: 'value', id: 'value'}
39961 this.store = store;
39962 this.bselect = Roo.DomHelper.append(document.body, {
39963 tag: 'select', style:'display:none', cls: 'x-grid-editor', children: [
39964 {tag: 'option', value: 'true', html: 'true'},
39965 {tag: 'option', value: 'false', html: 'false'}
39968 Roo.id(this.bselect);
39971 'date' : new g.GridEditor(new f.DateField({selectOnFocus:true})),
39972 'string' : new g.GridEditor(new f.TextField({selectOnFocus:true})),
39973 'number' : new g.GridEditor(new f.NumberField({selectOnFocus:true, style:'text-align:left;'})),
39974 'int' : new g.GridEditor(new f.NumberField({selectOnFocus:true, allowDecimals:false, style:'text-align:left;'})),
39975 'boolean' : new g.GridEditor(new f.Field({el:this.bselect,selectOnFocus:true}))
39977 this.renderCellDelegate = this.renderCell.createDelegate(this);
39978 this.renderPropDelegate = this.renderProp.createDelegate(this);
39981 Roo.extend(Roo.grid.PropertyColumnModel, Roo.grid.ColumnModel, {
39985 valueText : 'Value',
39987 dateFormat : 'm/j/Y',
39990 renderDate : function(dateVal){
39991 return dateVal.dateFormat(this.dateFormat);
39994 renderBool : function(bVal){
39995 return bVal ? 'true' : 'false';
39998 isCellEditable : function(colIndex, rowIndex){
39999 return colIndex == 1;
40002 getRenderer : function(col){
40004 this.renderCellDelegate : this.renderPropDelegate;
40007 renderProp : function(v){
40008 return this.getPropertyName(v);
40011 renderCell : function(val){
40013 if(val instanceof Date){
40014 rv = this.renderDate(val);
40015 }else if(typeof val == 'boolean'){
40016 rv = this.renderBool(val);
40018 return Roo.util.Format.htmlEncode(rv);
40021 getPropertyName : function(name){
40022 var pn = this.grid.propertyNames;
40023 return pn && pn[name] ? pn[name] : name;
40026 getCellEditor : function(colIndex, rowIndex){
40027 var p = this.store.getProperty(rowIndex);
40028 var n = p.data['name'], val = p.data['value'];
40030 if(typeof(this.grid.customEditors[n]) == 'string'){
40031 return this.editors[this.grid.customEditors[n]];
40033 if(typeof(this.grid.customEditors[n]) != 'undefined'){
40034 return this.grid.customEditors[n];
40036 if(val instanceof Date){
40037 return this.editors['date'];
40038 }else if(typeof val == 'number'){
40039 return this.editors['number'];
40040 }else if(typeof val == 'boolean'){
40041 return this.editors['boolean'];
40043 return this.editors['string'];
40049 * @class Roo.grid.PropertyGrid
40050 * @extends Roo.grid.EditorGrid
40051 * This class represents the interface of a component based property grid control.
40052 * <br><br>Usage:<pre><code>
40053 var grid = new Roo.grid.PropertyGrid("my-container-id", {
40061 * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
40062 * The container MUST have some type of size defined for the grid to fill. The container will be
40063 * automatically set to position relative if it isn't already.
40064 * @param {Object} config A config object that sets properties on this grid.
40066 Roo.grid.PropertyGrid = function(container, config){
40067 config = config || {};
40068 var store = new Roo.grid.PropertyStore(this);
40069 this.store = store;
40070 var cm = new Roo.grid.PropertyColumnModel(this, store);
40071 store.store.sort('name', 'ASC');
40072 Roo.grid.PropertyGrid.superclass.constructor.call(this, container, Roo.apply({
40075 enableColLock:false,
40076 enableColumnMove:false,
40078 trackMouseOver: false,
40081 this.getGridEl().addClass('x-props-grid');
40082 this.lastEditRow = null;
40083 this.on('columnresize', this.onColumnResize, this);
40086 * @event beforepropertychange
40087 * Fires before a property changes (return false to stop?)
40088 * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
40089 * @param {String} id Record Id
40090 * @param {String} newval New Value
40091 * @param {String} oldval Old Value
40093 "beforepropertychange": true,
40095 * @event propertychange
40096 * Fires after a property changes
40097 * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
40098 * @param {String} id Record Id
40099 * @param {String} newval New Value
40100 * @param {String} oldval Old Value
40102 "propertychange": true
40104 this.customEditors = this.customEditors || {};
40106 Roo.extend(Roo.grid.PropertyGrid, Roo.grid.EditorGrid, {
40109 * @cfg {Object} customEditors map of colnames=> custom editors.
40110 * the custom editor can be one of the standard ones (date|string|number|int|boolean), or a
40111 * grid editor eg. Roo.grid.GridEditor(new Roo.form.TextArea({selectOnFocus:true})),
40112 * false disables editing of the field.
40116 * @cfg {Object} propertyNames map of property Names to their displayed value
40119 render : function(){
40120 Roo.grid.PropertyGrid.superclass.render.call(this);
40121 this.autoSize.defer(100, this);
40124 autoSize : function(){
40125 Roo.grid.PropertyGrid.superclass.autoSize.call(this);
40127 this.view.fitColumns();
40131 onColumnResize : function(){
40132 this.colModel.setColumnWidth(1, this.container.getWidth(true)-this.colModel.getColumnWidth(0));
40136 * Sets the data for the Grid
40137 * accepts a Key => Value object of all the elements avaiable.
40138 * @param {Object} data to appear in grid.
40140 setSource : function(source){
40141 this.store.setSource(source);
40145 * Gets all the data from the grid.
40146 * @return {Object} data data stored in grid
40148 getSource : function(){
40149 return this.store.getSource();
40153 * Ext JS Library 1.1.1
40154 * Copyright(c) 2006-2007, Ext JS, LLC.
40156 * Originally Released Under LGPL - original licence link has changed is not relivant.
40159 * <script type="text/javascript">
40163 * @class Roo.LoadMask
40164 * A simple utility class for generically masking elements while loading data. If the element being masked has
40165 * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
40166 * process and the mask element will be cached for reuse. For all other elements, this mask will replace the
40167 * element's UpdateManager load indicator and will be destroyed after the initial load.
40169 * Create a new LoadMask
40170 * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
40171 * @param {Object} config The config object
40173 Roo.LoadMask = function(el, config){
40174 this.el = Roo.get(el);
40175 Roo.apply(this, config);
40177 this.store.on('beforeload', this.onBeforeLoad, this);
40178 this.store.on('load', this.onLoad, this);
40179 this.store.on('loadexception', this.onLoadException, this);
40180 this.removeMask = false;
40182 var um = this.el.getUpdateManager();
40183 um.showLoadIndicator = false; // disable the default indicator
40184 um.on('beforeupdate', this.onBeforeLoad, this);
40185 um.on('update', this.onLoad, this);
40186 um.on('failure', this.onLoad, this);
40187 this.removeMask = true;
40191 Roo.LoadMask.prototype = {
40193 * @cfg {Boolean} removeMask
40194 * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
40195 * False to persist the mask element reference for multiple uses (e.g., for paged data widgets). Defaults to false.
40198 * @cfg {String} msg
40199 * The text to display in a centered loading message box (defaults to 'Loading...')
40201 msg : 'Loading...',
40203 * @cfg {String} msgCls
40204 * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
40206 msgCls : 'x-mask-loading',
40209 * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
40215 * Disables the mask to prevent it from being displayed
40217 disable : function(){
40218 this.disabled = true;
40222 * Enables the mask so that it can be displayed
40224 enable : function(){
40225 this.disabled = false;
40228 onLoadException : function()
40230 Roo.log(arguments);
40232 if (typeof(arguments[3]) != 'undefined') {
40233 Roo.MessageBox.alert("Error loading",arguments[3]);
40237 if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
40238 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
40247 this.el.unmask(this.removeMask);
40250 onLoad : function()
40252 this.el.unmask(this.removeMask);
40256 onBeforeLoad : function(){
40257 if(!this.disabled){
40258 this.el.mask(this.msg, this.msgCls);
40263 destroy : function(){
40265 this.store.un('beforeload', this.onBeforeLoad, this);
40266 this.store.un('load', this.onLoad, this);
40267 this.store.un('loadexception', this.onLoadException, this);
40269 var um = this.el.getUpdateManager();
40270 um.un('beforeupdate', this.onBeforeLoad, this);
40271 um.un('update', this.onLoad, this);
40272 um.un('failure', this.onLoad, this);
40277 * Ext JS Library 1.1.1
40278 * Copyright(c) 2006-2007, Ext JS, LLC.
40280 * Originally Released Under LGPL - original licence link has changed is not relivant.
40283 * <script type="text/javascript">
40288 * @class Roo.XTemplate
40289 * @extends Roo.Template
40290 * Provides a template that can have nested templates for loops or conditionals. The syntax is:
40292 var t = new Roo.XTemplate(
40293 '<select name="{name}">',
40294 '<tpl for="options"><option value="{value:trim}">{text:ellipsis(10)}</option></tpl>',
40298 // then append, applying the master template values
40301 * Supported features:
40306 {a_variable} - output encoded.
40307 {a_variable.format:("Y-m-d")} - call a method on the variable
40308 {a_variable:raw} - unencoded output
40309 {a_variable:toFixed(1,2)} - Roo.util.Format."toFixed"
40310 {a_variable:this.method_on_template(...)} - call a method on the template object.
40315 <tpl for="a_variable or condition.."></tpl>
40316 <tpl if="a_variable or condition"></tpl>
40317 <tpl exec="some javascript"></tpl>
40318 <tpl name="named_template"></tpl> (experimental)
40320 <tpl for="."></tpl> - just iterate the property..
40321 <tpl for=".."></tpl> - iterates with the parent (probably the template)
40325 Roo.XTemplate = function()
40327 Roo.XTemplate.superclass.constructor.apply(this, arguments);
40334 Roo.extend(Roo.XTemplate, Roo.Template, {
40337 * The various sub templates
40342 * basic tag replacing syntax
40345 * // you can fake an object call by doing this
40349 re : /\{([\w-\.]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
40352 * compile the template
40354 * This is not recursive, so I'm not sure how nested templates are really going to be handled..
40357 compile: function()
40361 s = ['<tpl>', s, '</tpl>'].join('');
40363 var re = /<tpl\b[^>]*>((?:(?=([^<]+))\2|<(?!tpl\b[^>]*>))*?)<\/tpl>/,
40364 nameRe = /^<tpl\b[^>]*?for="(.*?)"/,
40365 ifRe = /^<tpl\b[^>]*?if="(.*?)"/,
40366 execRe = /^<tpl\b[^>]*?exec="(.*?)"/,
40367 namedRe = /^<tpl\b[^>]*?name="(\w+)"/, // named templates..
40372 while(true == !!(m = s.match(re))){
40373 var forMatch = m[0].match(nameRe),
40374 ifMatch = m[0].match(ifRe),
40375 execMatch = m[0].match(execRe),
40376 namedMatch = m[0].match(namedRe),
40381 name = forMatch && forMatch[1] ? forMatch[1] : '';
40384 // if - puts fn into test..
40385 exp = ifMatch && ifMatch[1] ? ifMatch[1] : null;
40387 fn = new Function('values', 'parent', 'with(values){ return '+(Roo.util.Format.htmlDecode(exp))+'; }');
40392 // exec - calls a function... returns empty if true is returned.
40393 exp = execMatch && execMatch[1] ? execMatch[1] : null;
40395 exec = new Function('values', 'parent', 'with(values){ '+(Roo.util.Format.htmlDecode(exp))+'; }');
40403 case '.': name = new Function('values', 'parent', 'with(values){ return values; }'); break;
40404 case '..': name = new Function('values', 'parent', 'with(values){ return parent; }'); break;
40405 default: name = new Function('values', 'parent', 'with(values){ return '+name+'; }');
40408 var uid = namedMatch ? namedMatch[1] : id;
40412 id: namedMatch ? namedMatch[1] : id,
40419 s = s.replace(m[0], '');
40421 s = s.replace(m[0], '{xtpl'+ id + '}');
40426 for(var i = tpls.length-1; i >= 0; --i){
40427 this.compileTpl(tpls[i]);
40428 this.tpls[tpls[i].id] = tpls[i];
40430 this.master = tpls[tpls.length-1];
40434 * same as applyTemplate, except it's done to one of the subTemplates
40435 * when using named templates, you can do:
40437 * var str = pl.applySubTemplate('your-name', values);
40440 * @param {Number} id of the template
40441 * @param {Object} values to apply to template
40442 * @param {Object} parent (normaly the instance of this object)
40444 applySubTemplate : function(id, values, parent)
40448 var t = this.tpls[id];
40452 if(t.test && !t.test.call(this, values, parent)){
40456 Roo.log("Xtemplate.applySubTemplate 'test': Exception thrown");
40457 Roo.log(e.toString());
40463 if(t.exec && t.exec.call(this, values, parent)){
40467 Roo.log("Xtemplate.applySubTemplate 'exec': Exception thrown");
40468 Roo.log(e.toString());
40473 var vs = t.target ? t.target.call(this, values, parent) : values;
40474 parent = t.target ? values : parent;
40475 if(t.target && vs instanceof Array){
40477 for(var i = 0, len = vs.length; i < len; i++){
40478 buf[buf.length] = t.compiled.call(this, vs[i], parent);
40480 return buf.join('');
40482 return t.compiled.call(this, vs, parent);
40484 Roo.log("Xtemplate.applySubTemplate : Exception thrown");
40485 Roo.log(e.toString());
40486 Roo.log(t.compiled);
40491 compileTpl : function(tpl)
40493 var fm = Roo.util.Format;
40494 var useF = this.disableFormats !== true;
40495 var sep = Roo.isGecko ? "+" : ",";
40496 var undef = function(str) {
40497 Roo.log("Property not found :" + str);
40501 var fn = function(m, name, format, args)
40503 //Roo.log(arguments);
40504 args = args ? args.replace(/\\'/g,"'") : args;
40505 //["{TEST:(a,b,c)}", "TEST", "", "a,b,c", 0, "{TEST:(a,b,c)}"]
40506 if (typeof(format) == 'undefined') {
40507 format= 'htmlEncode';
40509 if (format == 'raw' ) {
40513 if(name.substr(0, 4) == 'xtpl'){
40514 return "'"+ sep +'this.applySubTemplate('+name.substr(4)+', values, parent)'+sep+"'";
40517 // build an array of options to determine if value is undefined..
40519 // basically get 'xxxx.yyyy' then do
40520 // (typeof(xxxx) == 'undefined' || typeof(xxx.yyyy) == 'undefined') ?
40521 // (function () { Roo.log("Property not found"); return ''; })() :
40526 Roo.each(name.split('.'), function(st) {
40527 lookfor += (lookfor.length ? '.': '') + st;
40528 udef_ar.push( "(typeof(" + lookfor + ") == 'undefined')" );
40531 var udef_st = '((' + udef_ar.join(" || ") +") ? undef('" + name + "') : "; // .. needs )
40534 if(format && useF){
40536 args = args ? ',' + args : "";
40538 if(format.substr(0, 5) != "this."){
40539 format = "fm." + format + '(';
40541 format = 'this.call("'+ format.substr(5) + '", ';
40545 return "'"+ sep + udef_st + format + name + args + "))"+sep+"'";
40549 // called with xxyx.yuu:(test,test)
40551 return "'"+ sep + udef_st + name + '(' + args + "))"+sep+"'";
40553 // raw.. - :raw modifier..
40554 return "'"+ sep + udef_st + name + ")"+sep+"'";
40558 // branched to use + in gecko and [].join() in others
40560 body = "tpl.compiled = function(values, parent){ with(values) { return '" +
40561 tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
40564 body = ["tpl.compiled = function(values, parent){ with (values) { return ['"];
40565 body.push(tpl.body.replace(/(\r\n|\n)/g,
40566 '\\n').replace(/'/g, "\\'").replace(this.re, fn));
40567 body.push("'].join('');};};");
40568 body = body.join('');
40571 Roo.debug && Roo.log(body.replace(/\\n/,'\n'));
40573 /** eval:var:tpl eval:var:fm eval:var:useF eval:var:undef */
40579 applyTemplate : function(values){
40580 return this.master.compiled.call(this, values, {});
40581 //var s = this.subs;
40584 apply : function(){
40585 return this.applyTemplate.apply(this, arguments);
40590 Roo.XTemplate.from = function(el){
40591 el = Roo.getDom(el);
40592 return new Roo.XTemplate(el.value || el.innerHTML);
40594 * Original code for Roojs - LGPL
40595 * <script type="text/javascript">
40599 * @class Roo.XComponent
40600 * A delayed Element creator...
40601 * Or a way to group chunks of interface together.
40603 * Mypart.xyx = new Roo.XComponent({
40605 parent : 'Mypart.xyz', // empty == document.element.!!
40609 disabled : function() {}
40611 tree : function() { // return an tree of xtype declared components
40615 xtype : 'NestedLayoutPanel',
40622 * It can be used to build a big heiracy, with parent etc.
40623 * or you can just use this to render a single compoent to a dom element
40624 * MYPART.render(Roo.Element | String(id) | dom_element )
40626 * @extends Roo.util.Observable
40628 * @param cfg {Object} configuration of component
40631 Roo.XComponent = function(cfg) {
40632 Roo.apply(this, cfg);
40636 * Fires when this the componnt is built
40637 * @param {Roo.XComponent} c the component
40642 this.region = this.region || 'center'; // default..
40643 Roo.XComponent.register(this);
40644 this.modules = false;
40645 this.el = false; // where the layout goes..
40649 Roo.extend(Roo.XComponent, Roo.util.Observable, {
40652 * The created element (with Roo.factory())
40653 * @type {Roo.Layout}
40659 * for BC - use el in new code
40660 * @type {Roo.Layout}
40666 * for BC - use el in new code
40667 * @type {Roo.Layout}
40672 * @cfg {Function|boolean} disabled
40673 * If this module is disabled by some rule, return true from the funtion
40678 * @cfg {String} parent
40679 * Name of parent element which it get xtype added to..
40684 * @cfg {String} order
40685 * Used to set the order in which elements are created (usefull for multiple tabs)
40690 * @cfg {String} name
40691 * String to display while loading.
40695 * @cfg {String} region
40696 * Region to render component to (defaults to center)
40701 * @cfg {Array} items
40702 * A single item array - the first element is the root of the tree..
40703 * It's done this way to stay compatible with the Xtype system...
40709 * The method that retuns the tree of parts that make up this compoennt
40716 * render element to dom or tree
40717 * @param {Roo.Element|String|DomElement} optional render to if parent is not set.
40720 render : function(el)
40724 var hp = this.parent ? 1 : 0;
40726 if (!el && typeof(this.parent) == 'string' && this.parent.substring(0,1) == '#') {
40727 // if parent is a '#.....' string, then let's use that..
40728 var ename = this.parent.substr(1)
40729 this.parent = (this.parent == '#bootstrap') ? { el : true} : false; // flags it as a top module...
40730 el = Roo.get(ename);
40731 if (!el && !this.parent) {
40732 Roo.log("Warning - element can not be found :#" + ename );
40738 if (!this.parent) {
40740 el = el ? Roo.get(el) : false;
40742 // it's a top level one..
40744 el : new Roo.BorderLayout(el || document.body, {
40750 tabPosition: 'top',
40751 //resizeTabs: true,
40752 alwaysShowTabs: el && hp? false : true,
40753 hideTabs: el || !hp ? true : false,
40760 if (!this.parent.el) {
40761 // probably an old style ctor, which has been disabled.
40765 // The 'tree' method is '_tree now'
40767 var tree = this._tree ? this._tree() : this.tree();
40768 tree.region = tree.region || this.region;
40769 this.el = this.parent.el.addxtype(tree);
40770 this.fireEvent('built', this);
40772 this.panel = this.el;
40773 this.layout = this.panel.layout;
40774 this.parentLayout = this.parent.layout || false;
40780 Roo.apply(Roo.XComponent, {
40782 * @property hideProgress
40783 * true to disable the building progress bar.. usefull on single page renders.
40786 hideProgress : false,
40788 * @property buildCompleted
40789 * True when the builder has completed building the interface.
40792 buildCompleted : false,
40795 * @property topModule
40796 * the upper most module - uses document.element as it's constructor.
40803 * @property modules
40804 * array of modules to be created by registration system.
40805 * @type {Array} of Roo.XComponent
40810 * @property elmodules
40811 * array of modules to be created by which use #ID
40812 * @type {Array} of Roo.XComponent
40819 * Register components to be built later.
40821 * This solves the following issues
40822 * - Building is not done on page load, but after an authentication process has occured.
40823 * - Interface elements are registered on page load
40824 * - Parent Interface elements may not be loaded before child, so this handles that..
40831 module : 'Pman.Tab.projectMgr',
40833 parent : 'Pman.layout',
40834 disabled : false, // or use a function..
40837 * * @param {Object} details about module
40839 register : function(obj) {
40841 Roo.XComponent.event.fireEvent('register', obj);
40842 switch(typeof(obj.disabled) ) {
40848 if ( obj.disabled() ) {
40854 if (obj.disabled) {
40860 this.modules.push(obj);
40864 * convert a string to an object..
40865 * eg. 'AAA.BBB' -> finds AAA.BBB
40869 toObject : function(str)
40871 if (!str || typeof(str) == 'object') {
40874 if (str.substring(0,1) == '#') {
40878 var ar = str.split('.');
40883 eval('if (typeof ' + rt + ' == "undefined"){ o = false;} o = ' + rt + ';');
40885 throw "Module not found : " + str;
40889 throw "Module not found : " + str;
40891 Roo.each(ar, function(e) {
40892 if (typeof(o[e]) == 'undefined') {
40893 throw "Module not found : " + str;
40904 * move modules into their correct place in the tree..
40907 preBuild : function ()
40910 Roo.each(this.modules , function (obj)
40912 Roo.XComponent.event.fireEvent('beforebuild', obj);
40914 var opar = obj.parent;
40916 obj.parent = this.toObject(opar);
40918 Roo.log("parent:toObject failed: " + e.toString());
40923 Roo.debug && Roo.log("GOT top level module");
40924 Roo.debug && Roo.log(obj);
40925 obj.modules = new Roo.util.MixedCollection(false,
40926 function(o) { return o.order + '' }
40928 this.topModule = obj;
40931 // parent is a string (usually a dom element name..)
40932 if (typeof(obj.parent) == 'string') {
40933 this.elmodules.push(obj);
40936 if (obj.parent.constructor != Roo.XComponent) {
40937 Roo.log("Warning : Object Parent is not instance of XComponent:" + obj.name)
40939 if (!obj.parent.modules) {
40940 obj.parent.modules = new Roo.util.MixedCollection(false,
40941 function(o) { return o.order + '' }
40944 if (obj.parent.disabled) {
40945 obj.disabled = true;
40947 obj.parent.modules.add(obj);
40952 * make a list of modules to build.
40953 * @return {Array} list of modules.
40956 buildOrder : function()
40959 var cmp = function(a,b) {
40960 return String(a).toUpperCase() > String(b).toUpperCase() ? 1 : -1;
40962 if ((!this.topModule || !this.topModule.modules) && !this.elmodules.length) {
40963 throw "No top level modules to build";
40966 // make a flat list in order of modules to build.
40967 var mods = this.topModule ? [ this.topModule ] : [];
40970 // elmodules (is a list of DOM based modules )
40971 Roo.each(this.elmodules, function(e) {
40973 if (!this.topModule &&
40974 typeof(e.parent) == 'string' &&
40975 e.parent.substring(0,1) == '#' &&
40976 Roo.get(e.parent.substr(1))
40979 _this.topModule = e;
40985 // add modules to their parents..
40986 var addMod = function(m) {
40987 Roo.debug && Roo.log("build Order: add: " + m.name);
40990 if (m.modules && !m.disabled) {
40991 Roo.debug && Roo.log("build Order: " + m.modules.length + " child modules");
40992 m.modules.keySort('ASC', cmp );
40993 Roo.debug && Roo.log("build Order: " + m.modules.length + " child modules (after sort)");
40995 m.modules.each(addMod);
40997 Roo.debug && Roo.log("build Order: no child modules");
40999 // not sure if this is used any more..
41001 m.finalize.name = m.name + " (clean up) ";
41002 mods.push(m.finalize);
41006 if (this.topModule && this.topModule.modules) {
41007 this.topModule.modules.keySort('ASC', cmp );
41008 this.topModule.modules.each(addMod);
41014 * Build the registered modules.
41015 * @param {Object} parent element.
41016 * @param {Function} optional method to call after module has been added.
41024 var mods = this.buildOrder();
41026 //this.allmods = mods;
41027 //Roo.debug && Roo.log(mods);
41029 if (!mods.length) { // should not happen
41030 throw "NO modules!!!";
41034 var msg = "Building Interface...";
41035 // flash it up as modal - so we store the mask!?
41036 if (!this.hideProgress) {
41037 Roo.MessageBox.show({ title: 'loading' });
41038 Roo.MessageBox.show({
41039 title: "Please wait...",
41048 var total = mods.length;
41051 var progressRun = function() {
41052 if (!mods.length) {
41053 Roo.debug && Roo.log('hide?');
41054 if (!this.hideProgress) {
41055 Roo.MessageBox.hide();
41057 Roo.XComponent.event.fireEvent('buildcomplete', _this.topModule);
41063 var m = mods.shift();
41066 Roo.debug && Roo.log(m);
41067 // not sure if this is supported any more.. - modules that are are just function
41068 if (typeof(m) == 'function') {
41070 return progressRun.defer(10, _this);
41074 msg = "Building Interface " + (total - mods.length) +
41076 (m.name ? (' - ' + m.name) : '');
41077 Roo.debug && Roo.log(msg);
41078 if (!this.hideProgress) {
41079 Roo.MessageBox.updateProgress( (total - mods.length)/total, msg );
41083 // is the module disabled?
41084 var disabled = (typeof(m.disabled) == 'function') ?
41085 m.disabled.call(m.module.disabled) : m.disabled;
41089 return progressRun(); // we do not update the display!
41097 // it's 10 on top level, and 1 on others??? why...
41098 return progressRun.defer(10, _this);
41101 progressRun.defer(1, _this);
41115 * wrapper for event.on - aliased later..
41116 * Typically use to register a event handler for register:
41118 * eg. Roo.XComponent.on('register', function(comp) { comp.disable = true } );
41127 Roo.XComponent.event = new Roo.util.Observable({
41131 * Fires when an Component is registered,
41132 * set the disable property on the Component to stop registration.
41133 * @param {Roo.XComponent} c the component being registerd.
41138 * @event beforebuild
41139 * Fires before each Component is built
41140 * can be used to apply permissions.
41141 * @param {Roo.XComponent} c the component being registerd.
41144 'beforebuild' : true,
41146 * @event buildcomplete
41147 * Fires on the top level element when all elements have been built
41148 * @param {Roo.XComponent} the top level component.
41150 'buildcomplete' : true
41155 Roo.XComponent.on = Roo.XComponent.event.on.createDelegate(Roo.XComponent.event);