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"]],
27397 opts : [[""],[ "left"],[ "center"],[ "right"],[ "justify"],[ "char"]],
27402 opts : [[""],[ "top"],[ "middle"],[ "bottom"],[ "baseline"]],
27412 style : 'fontFamily',
27413 displayField: 'display',
27414 optname : 'font-family',
27463 // should we really allow this??
27464 // should this just be
27475 style : 'fontFamily',
27476 displayField: 'display',
27477 optname : 'font-family',
27484 style : 'fontFamily',
27485 displayField: 'display',
27486 optname : 'font-family',
27493 style : 'fontFamily',
27494 displayField: 'display',
27495 optname : 'font-family',
27506 // this should be configurable.. - you can either set it up using stores, or modify options somehwere..
27507 Roo.form.HtmlEditor.ToolbarContext.stores = false;
27509 Roo.form.HtmlEditor.ToolbarContext.options = {
27511 [ 'Helvetica,Arial,sans-serif', 'Helvetica'],
27512 [ 'Courier New', 'Courier New'],
27513 [ 'Tahoma', 'Tahoma'],
27514 [ 'Times New Roman,serif', 'Times'],
27515 [ 'Verdana','Verdana' ]
27519 // fixme - these need to be configurable..
27522 Roo.form.HtmlEditor.ToolbarContext.types
27525 Roo.apply(Roo.form.HtmlEditor.ToolbarContext.prototype, {
27533 * @cfg {Object} disable List of toolbar elements to disable
27538 * @cfg {Object} styles List of styles
27539 * eg. { '*' : [ 'headline' ] , 'TD' : [ 'underline', 'double-underline' ] }
27541 * These must be defined in the page, so they get rendered correctly..
27552 init : function(editor)
27554 this.editor = editor;
27557 var fid = editor.frameId;
27559 function btn(id, toggle, handler){
27560 var xid = fid + '-'+ id ;
27564 cls : 'x-btn-icon x-edit-'+id,
27565 enableToggle:toggle !== false,
27566 scope: editor, // was editor...
27567 handler:handler||editor.relayBtnCmd,
27568 clickEvent:'mousedown',
27569 tooltip: etb.buttonTips[id] || undefined, ///tips ???
27573 // create a new element.
27574 var wdiv = editor.wrap.createChild({
27576 }, editor.wrap.dom.firstChild.nextSibling, true);
27578 // can we do this more than once??
27580 // stop form submits
27583 // disable everything...
27584 var ty= Roo.form.HtmlEditor.ToolbarContext.types;
27585 this.toolbars = {};
27587 for (var i in ty) {
27589 this.toolbars[i] = this.buildToolbar(ty[i],i);
27591 this.tb = this.toolbars.BODY;
27593 this.buildFooter();
27594 this.footer.show();
27595 editor.on('hide', function( ) { this.footer.hide() }, this);
27596 editor.on('show', function( ) { this.footer.show() }, this);
27599 this.rendered = true;
27601 // the all the btns;
27602 editor.on('editorevent', this.updateToolbar, this);
27603 // other toolbars need to implement this..
27604 //editor.on('editmodechange', this.updateToolbar, this);
27610 * Protected method that will not generally be called directly. It triggers
27611 * a toolbar update by reading the markup state of the current selection in the editor.
27613 updateToolbar: function(editor,ev,sel){
27616 // capture mouse up - this is handy for selecting images..
27617 // perhaps should go somewhere else...
27618 if(!this.editor.activated){
27619 this.editor.onFirstFocus();
27623 // http://developer.yahoo.com/yui/docs/simple-editor.js.html
27624 // selectNode - might want to handle IE?
27626 (ev.type == 'mouseup' || ev.type == 'click' ) &&
27627 ev.target && ev.target.tagName == 'IMG') {
27628 // they have click on an image...
27629 // let's see if we can change the selection...
27632 var nodeRange = sel.ownerDocument.createRange();
27634 nodeRange.selectNode(sel);
27636 nodeRange.selectNodeContents(sel);
27638 //nodeRange.collapse(true);
27639 var s = editor.win.getSelection();
27640 s.removeAllRanges();
27641 s.addRange(nodeRange);
27645 var updateFooter = sel ? false : true;
27648 var ans = this.editor.getAllAncestors();
27651 var ty= Roo.form.HtmlEditor.ToolbarContext.types;
27654 sel = ans.length ? (ans[0] ? ans[0] : ans[1]) : this.editor.doc.body;
27655 sel = sel ? sel : this.editor.doc.body;
27656 sel = sel.tagName.length ? sel : this.editor.doc.body;
27659 // pick a menu that exists..
27660 var tn = sel.tagName.toUpperCase();
27661 //sel = typeof(ty[tn]) != 'undefined' ? sel : this.editor.doc.body;
27663 tn = sel.tagName.toUpperCase();
27665 var lastSel = this.tb.selectedNode
27667 this.tb.selectedNode = sel;
27669 // if current menu does not match..
27670 if ((this.tb.name != tn) || (lastSel != this.tb.selectedNode)) {
27673 ///console.log("show: " + tn);
27674 this.tb = typeof(ty[tn]) != 'undefined' ? this.toolbars[tn] : this.toolbars['*'];
27677 this.tb.items.first().el.innerHTML = tn + ': ';
27680 // update attributes
27681 if (this.tb.fields) {
27682 this.tb.fields.each(function(e) {
27684 e.setValue(sel.style[e.stylename]);
27687 e.setValue(sel.getAttribute(e.attrname));
27691 var hasStyles = false;
27692 for(var i in this.styles) {
27699 var st = this.tb.fields.item(0);
27701 st.store.removeAll();
27704 var cn = sel.className.split(/\s+/);
27707 if (this.styles['*']) {
27709 Roo.each(this.styles['*'], function(v) {
27710 avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );
27713 if (this.styles[tn]) {
27714 Roo.each(this.styles[tn], function(v) {
27715 avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );
27719 st.store.loadData(avs);
27723 // flag our selected Node.
27724 this.tb.selectedNode = sel;
27727 Roo.menu.MenuMgr.hideAll();
27731 if (!updateFooter) {
27732 //this.footDisp.dom.innerHTML = '';
27735 // update the footer
27739 this.footerEls = ans.reverse();
27740 Roo.each(this.footerEls, function(a,i) {
27741 if (!a) { return; }
27742 html += html.length ? ' > ' : '';
27744 html += '<span class="x-ed-loc-' + i + '">' + a.tagName + '</span>';
27749 var sz = this.footDisp.up('td').getSize();
27750 this.footDisp.dom.style.width = (sz.width -10) + 'px';
27751 this.footDisp.dom.style.marginLeft = '5px';
27753 this.footDisp.dom.style.overflow = 'hidden';
27755 this.footDisp.dom.innerHTML = html;
27757 //this.editorsyncValue();
27764 onDestroy : function(){
27767 this.tb.items.each(function(item){
27769 item.menu.removeAll();
27771 item.menu.el.destroy();
27779 onFirstFocus: function() {
27780 // need to do this for all the toolbars..
27781 this.tb.items.each(function(item){
27785 buildToolbar: function(tlist, nm)
27787 var editor = this.editor;
27788 // create a new element.
27789 var wdiv = editor.wrap.createChild({
27791 }, editor.wrap.dom.firstChild.nextSibling, true);
27794 var tb = new Roo.Toolbar(wdiv);
27797 tb.add(nm+ ": ");
27800 for(var i in this.styles) {
27805 if (styles && styles.length) {
27807 // this needs a multi-select checkbox...
27808 tb.addField( new Roo.form.ComboBox({
27809 store: new Roo.data.SimpleStore({
27811 fields: ['val', 'selected'],
27814 name : '-roo-edit-className',
27815 attrname : 'className',
27816 displayField: 'val',
27820 triggerAction: 'all',
27821 emptyText:'Select Style',
27822 selectOnFocus:true,
27825 'select': function(c, r, i) {
27826 // initial support only for on class per el..
27827 tb.selectedNode.className = r ? r.get('val') : '';
27828 editor.syncValue();
27835 var tbc = Roo.form.HtmlEditor.ToolbarContext;
27836 var tbops = tbc.options;
27838 for (var i in tlist) {
27840 var item = tlist[i];
27841 tb.add(item.title + ": ");
27844 //optname == used so you can configure the options available..
27845 var opts = item.opts ? item.opts : false;
27846 if (item.optname) {
27847 opts = tbops[item.optname];
27852 // opts == pulldown..
27853 tb.addField( new Roo.form.ComboBox({
27854 store: typeof(tbc.stores[i]) != 'undefined' ? Roo.factory(tbc.stores[i],Roo.data) : new Roo.data.SimpleStore({
27856 fields: ['val', 'display'],
27859 name : '-roo-edit-' + i,
27861 stylename : item.style ? item.style : false,
27862 displayField: item.displayField ? item.displayField : 'val',
27863 valueField : 'val',
27865 mode: typeof(tbc.stores[i]) != 'undefined' ? 'remote' : 'local',
27867 triggerAction: 'all',
27868 emptyText:'Select',
27869 selectOnFocus:true,
27870 width: item.width ? item.width : 130,
27872 'select': function(c, r, i) {
27874 tb.selectedNode.style[c.stylename] = r.get('val');
27877 tb.selectedNode.setAttribute(c.attrname, r.get('val'));
27886 tb.addField( new Roo.form.TextField({
27889 //allowBlank:false,
27894 tb.addField( new Roo.form.TextField({
27895 name: '-roo-edit-' + i,
27902 'change' : function(f, nv, ov) {
27903 tb.selectedNode.setAttribute(f.attrname, nv);
27912 text: 'Remove Tag',
27915 click : function ()
27918 // undo does not work.
27920 var sn = tb.selectedNode;
27922 var pn = sn.parentNode;
27924 var stn = sn.childNodes[0];
27925 var en = sn.childNodes[sn.childNodes.length - 1 ];
27926 while (sn.childNodes.length) {
27927 var node = sn.childNodes[0];
27928 sn.removeChild(node);
27930 pn.insertBefore(node, sn);
27933 pn.removeChild(sn);
27934 var range = editor.createRange();
27936 range.setStart(stn,0);
27937 range.setEnd(en,0); //????
27938 //range.selectNode(sel);
27941 var selection = editor.getSelection();
27942 selection.removeAllRanges();
27943 selection.addRange(range);
27947 //_this.updateToolbar(null, null, pn);
27948 _this.updateToolbar(null, null, null);
27949 _this.footDisp.dom.innerHTML = '';
27959 tb.el.on('click', function(e){
27960 e.preventDefault(); // what does this do?
27962 tb.el.setVisibilityMode( Roo.Element.DISPLAY);
27965 // dont need to disable them... as they will get hidden
27970 buildFooter : function()
27973 var fel = this.editor.wrap.createChild();
27974 this.footer = new Roo.Toolbar(fel);
27975 // toolbar has scrolly on left / right?
27976 var footDisp= new Roo.Toolbar.Fill();
27982 handler : function() {
27983 _t.footDisp.scrollTo('left',0,true)
27987 this.footer.add( footDisp );
27992 handler : function() {
27994 _t.footDisp.select('span').last().scrollIntoView(_t.footDisp,true);
27998 var fel = Roo.get(footDisp.el);
27999 fel.addClass('x-editor-context');
28000 this.footDispWrap = fel;
28001 this.footDispWrap.overflow = 'hidden';
28003 this.footDisp = fel.createChild();
28004 this.footDispWrap.on('click', this.onContextClick, this)
28008 onContextClick : function (ev,dom)
28010 ev.preventDefault();
28011 var cn = dom.className;
28013 if (!cn.match(/x-ed-loc-/)) {
28016 var n = cn.split('-').pop();
28017 var ans = this.footerEls;
28021 var range = this.editor.createRange();
28023 range.selectNodeContents(sel);
28024 //range.selectNode(sel);
28027 var selection = this.editor.getSelection();
28028 selection.removeAllRanges();
28029 selection.addRange(range);
28033 this.updateToolbar(null, null, sel);
28050 * Ext JS Library 1.1.1
28051 * Copyright(c) 2006-2007, Ext JS, LLC.
28053 * Originally Released Under LGPL - original licence link has changed is not relivant.
28056 * <script type="text/javascript">
28060 * @class Roo.form.BasicForm
28061 * @extends Roo.util.Observable
28062 * Supplies the functionality to do "actions" on forms and initialize Roo.form.Field types on existing markup.
28064 * @param {String/HTMLElement/Roo.Element} el The form element or its id
28065 * @param {Object} config Configuration options
28067 Roo.form.BasicForm = function(el, config){
28068 this.allItems = [];
28069 this.childForms = [];
28070 Roo.apply(this, config);
28072 * The Roo.form.Field items in this form.
28073 * @type MixedCollection
28077 this.items = new Roo.util.MixedCollection(false, function(o){
28078 return o.id || (o.id = Roo.id());
28082 * @event beforeaction
28083 * Fires before any action is performed. Return false to cancel the action.
28084 * @param {Form} this
28085 * @param {Action} action The action to be performed
28087 beforeaction: true,
28089 * @event actionfailed
28090 * Fires when an action fails.
28091 * @param {Form} this
28092 * @param {Action} action The action that failed
28094 actionfailed : true,
28096 * @event actioncomplete
28097 * Fires when an action is completed.
28098 * @param {Form} this
28099 * @param {Action} action The action that completed
28101 actioncomplete : true
28106 Roo.form.BasicForm.superclass.constructor.call(this);
28109 Roo.extend(Roo.form.BasicForm, Roo.util.Observable, {
28111 * @cfg {String} method
28112 * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
28115 * @cfg {DataReader} reader
28116 * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when executing "load" actions.
28117 * This is optional as there is built-in support for processing JSON.
28120 * @cfg {DataReader} errorReader
28121 * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when reading validation errors on "submit" actions.
28122 * This is completely optional as there is built-in support for processing JSON.
28125 * @cfg {String} url
28126 * The URL to use for form actions if one isn't supplied in the action options.
28129 * @cfg {Boolean} fileUpload
28130 * Set to true if this form is a file upload.
28134 * @cfg {Object} baseParams
28135 * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
28140 * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
28145 activeAction : null,
28148 * @cfg {Boolean} trackResetOnLoad If set to true, form.reset() resets to the last loaded
28149 * or setValues() data instead of when the form was first created.
28151 trackResetOnLoad : false,
28155 * childForms - used for multi-tab forms
28158 childForms : false,
28161 * allItems - full list of fields.
28167 * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
28168 * element by passing it or its id or mask the form itself by passing in true.
28171 waitMsgTarget : false,
28174 initEl : function(el){
28175 this.el = Roo.get(el);
28176 this.id = this.el.id || Roo.id();
28177 this.el.on('submit', this.onSubmit, this);
28178 this.el.addClass('x-form');
28182 onSubmit : function(e){
28187 * Returns true if client-side validation on the form is successful.
28190 isValid : function(){
28192 this.items.each(function(f){
28201 * Returns true if any fields in this form have changed since their original load.
28204 isDirty : function(){
28206 this.items.each(function(f){
28216 * Performs a predefined action (submit or load) or custom actions you define on this form.
28217 * @param {String} actionName The name of the action type
28218 * @param {Object} options (optional) The options to pass to the action. All of the config options listed
28219 * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
28220 * accept other config options):
28222 Property Type Description
28223 ---------------- --------------- ----------------------------------------------------------------------------------
28224 url String The url for the action (defaults to the form's url)
28225 method String The form method to use (defaults to the form's method, or POST if not defined)
28226 params String/Object The params to pass (defaults to the form's baseParams, or none if not defined)
28227 clientValidation Boolean Applies to submit only. Pass true to call form.isValid() prior to posting to
28228 validate the form on the client (defaults to false)
28230 * @return {BasicForm} this
28232 doAction : function(action, options){
28233 if(typeof action == 'string'){
28234 action = new Roo.form.Action.ACTION_TYPES[action](this, options);
28236 if(this.fireEvent('beforeaction', this, action) !== false){
28237 this.beforeAction(action);
28238 action.run.defer(100, action);
28244 * Shortcut to do a submit action.
28245 * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
28246 * @return {BasicForm} this
28248 submit : function(options){
28249 this.doAction('submit', options);
28254 * Shortcut to do a load action.
28255 * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
28256 * @return {BasicForm} this
28258 load : function(options){
28259 this.doAction('load', options);
28264 * Persists the values in this form into the passed Roo.data.Record object in a beginEdit/endEdit block.
28265 * @param {Record} record The record to edit
28266 * @return {BasicForm} this
28268 updateRecord : function(record){
28269 record.beginEdit();
28270 var fs = record.fields;
28271 fs.each(function(f){
28272 var field = this.findField(f.name);
28274 record.set(f.name, field.getValue());
28282 * Loads an Roo.data.Record into this form.
28283 * @param {Record} record The record to load
28284 * @return {BasicForm} this
28286 loadRecord : function(record){
28287 this.setValues(record.data);
28292 beforeAction : function(action){
28293 var o = action.options;
28296 if(this.waitMsgTarget === true){
28297 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
28298 }else if(this.waitMsgTarget){
28299 this.waitMsgTarget = Roo.get(this.waitMsgTarget);
28300 this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
28302 Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
28308 afterAction : function(action, success){
28309 this.activeAction = null;
28310 var o = action.options;
28312 if(this.waitMsgTarget === true){
28314 }else if(this.waitMsgTarget){
28315 this.waitMsgTarget.unmask();
28317 Roo.MessageBox.updateProgress(1);
28318 Roo.MessageBox.hide();
28325 Roo.callback(o.success, o.scope, [this, action]);
28326 this.fireEvent('actioncomplete', this, action);
28330 // failure condition..
28331 // we have a scenario where updates need confirming.
28332 // eg. if a locking scenario exists..
28333 // we look for { errors : { needs_confirm : true }} in the response.
28335 (typeof(action.result) != 'undefined') &&
28336 (typeof(action.result.errors) != 'undefined') &&
28337 (typeof(action.result.errors.needs_confirm) != 'undefined')
28340 Roo.MessageBox.confirm(
28341 "Change requires confirmation",
28342 action.result.errorMsg,
28347 _t.doAction('submit', { params : { _submit_confirmed : 1 } } );
28357 Roo.callback(o.failure, o.scope, [this, action]);
28358 // show an error message if no failed handler is set..
28359 if (!this.hasListener('actionfailed')) {
28360 Roo.MessageBox.alert("Error",
28361 (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
28362 action.result.errorMsg :
28363 "Saving Failed, please check your entries or try again"
28367 this.fireEvent('actionfailed', this, action);
28373 * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
28374 * @param {String} id The value to search for
28377 findField : function(id){
28378 var field = this.items.get(id);
28380 this.items.each(function(f){
28381 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
28387 return field || null;
28391 * Add a secondary form to this one,
28392 * Used to provide tabbed forms. One form is primary, with hidden values
28393 * which mirror the elements from the other forms.
28395 * @param {Roo.form.Form} form to add.
28398 addForm : function(form)
28401 if (this.childForms.indexOf(form) > -1) {
28405 this.childForms.push(form);
28407 Roo.each(form.allItems, function (fe) {
28409 n = typeof(fe.getName) == 'undefined' ? fe.name : fe.getName();
28410 if (this.findField(n)) { // already added..
28413 var add = new Roo.form.Hidden({
28416 add.render(this.el);
28423 * Mark fields in this form invalid in bulk.
28424 * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
28425 * @return {BasicForm} this
28427 markInvalid : function(errors){
28428 if(errors instanceof Array){
28429 for(var i = 0, len = errors.length; i < len; i++){
28430 var fieldError = errors[i];
28431 var f = this.findField(fieldError.id);
28433 f.markInvalid(fieldError.msg);
28439 if(typeof errors[id] != 'function' && (field = this.findField(id))){
28440 field.markInvalid(errors[id]);
28444 Roo.each(this.childForms || [], function (f) {
28445 f.markInvalid(errors);
28452 * Set values for fields in this form in bulk.
28453 * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
28454 * @return {BasicForm} this
28456 setValues : function(values){
28457 if(values instanceof Array){ // array of objects
28458 for(var i = 0, len = values.length; i < len; i++){
28460 var f = this.findField(v.id);
28462 f.setValue(v.value);
28463 if(this.trackResetOnLoad){
28464 f.originalValue = f.getValue();
28468 }else{ // object hash
28471 if(typeof values[id] != 'function' && (field = this.findField(id))){
28473 if (field.setFromData &&
28474 field.valueField &&
28475 field.displayField &&
28476 // combos' with local stores can
28477 // be queried via setValue()
28478 // to set their value..
28479 (field.store && !field.store.isLocal)
28483 sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
28484 sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
28485 field.setFromData(sd);
28488 field.setValue(values[id]);
28492 if(this.trackResetOnLoad){
28493 field.originalValue = field.getValue();
28499 Roo.each(this.childForms || [], function (f) {
28500 f.setValues(values);
28507 * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
28508 * they are returned as an array.
28509 * @param {Boolean} asString
28512 getValues : function(asString){
28513 if (this.childForms) {
28514 // copy values from the child forms
28515 Roo.each(this.childForms, function (f) {
28516 this.setValues(f.getValues());
28522 var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
28523 if(asString === true){
28526 return Roo.urlDecode(fs);
28530 * Returns the fields in this form as an object with key/value pairs.
28531 * This differs from getValues as it calls getValue on each child item, rather than using dom data.
28534 getFieldValues : function(with_hidden)
28536 if (this.childForms) {
28537 // copy values from the child forms
28538 // should this call getFieldValues - probably not as we do not currently copy
28539 // hidden fields when we generate..
28540 Roo.each(this.childForms, function (f) {
28541 this.setValues(f.getValues());
28546 this.items.each(function(f){
28547 if (!f.getName()) {
28550 var v = f.getValue();
28551 if (f.inputType =='radio') {
28552 if (typeof(ret[f.getName()]) == 'undefined') {
28553 ret[f.getName()] = ''; // empty..
28556 if (!f.el.dom.checked) {
28560 v = f.el.dom.value;
28564 // not sure if this supported any more..
28565 if ((typeof(v) == 'object') && f.getRawValue) {
28566 v = f.getRawValue() ; // dates..
28568 // combo boxes where name != hiddenName...
28569 if (f.name != f.getName()) {
28570 ret[f.name] = f.getRawValue();
28572 ret[f.getName()] = v;
28579 * Clears all invalid messages in this form.
28580 * @return {BasicForm} this
28582 clearInvalid : function(){
28583 this.items.each(function(f){
28587 Roo.each(this.childForms || [], function (f) {
28596 * Resets this form.
28597 * @return {BasicForm} this
28599 reset : function(){
28600 this.items.each(function(f){
28604 Roo.each(this.childForms || [], function (f) {
28613 * Add Roo.form components to this form.
28614 * @param {Field} field1
28615 * @param {Field} field2 (optional)
28616 * @param {Field} etc (optional)
28617 * @return {BasicForm} this
28620 this.items.addAll(Array.prototype.slice.call(arguments, 0));
28626 * Removes a field from the items collection (does NOT remove its markup).
28627 * @param {Field} field
28628 * @return {BasicForm} this
28630 remove : function(field){
28631 this.items.remove(field);
28636 * Looks at the fields in this form, checks them for an id attribute,
28637 * and calls applyTo on the existing dom element with that id.
28638 * @return {BasicForm} this
28640 render : function(){
28641 this.items.each(function(f){
28642 if(f.isFormField && !f.rendered && document.getElementById(f.id)){ // if the element exists
28650 * Calls {@link Ext#apply} for all fields in this form with the passed object.
28651 * @param {Object} values
28652 * @return {BasicForm} this
28654 applyToFields : function(o){
28655 this.items.each(function(f){
28662 * Calls {@link Ext#applyIf} for all field in this form with the passed object.
28663 * @param {Object} values
28664 * @return {BasicForm} this
28666 applyIfToFields : function(o){
28667 this.items.each(function(f){
28675 Roo.BasicForm = Roo.form.BasicForm;/*
28677 * Ext JS Library 1.1.1
28678 * Copyright(c) 2006-2007, Ext JS, LLC.
28680 * Originally Released Under LGPL - original licence link has changed is not relivant.
28683 * <script type="text/javascript">
28687 * @class Roo.form.Form
28688 * @extends Roo.form.BasicForm
28689 * Adds the ability to dynamically render forms with JavaScript to {@link Roo.form.BasicForm}.
28691 * @param {Object} config Configuration options
28693 Roo.form.Form = function(config){
28695 if (config.items) {
28696 xitems = config.items;
28697 delete config.items;
28701 Roo.form.Form.superclass.constructor.call(this, null, config);
28702 this.url = this.url || this.action;
28704 this.root = new Roo.form.Layout(Roo.applyIf({
28708 this.active = this.root;
28710 * Array of all the buttons that have been added to this form via {@link addButton}
28714 this.allItems = [];
28717 * @event clientvalidation
28718 * If the monitorValid config option is true, this event fires repetitively to notify of valid state
28719 * @param {Form} this
28720 * @param {Boolean} valid true if the form has passed client-side validation
28722 clientvalidation: true,
28725 * Fires when the form is rendered
28726 * @param {Roo.form.Form} form
28731 if (this.progressUrl) {
28732 // push a hidden field onto the list of fields..
28736 name : 'UPLOAD_IDENTIFIER'
28741 Roo.each(xitems, this.addxtype, this);
28747 Roo.extend(Roo.form.Form, Roo.form.BasicForm, {
28749 * @cfg {Number} labelWidth The width of labels. This property cascades to child containers.
28752 * @cfg {String} itemCls A css class to apply to the x-form-item of fields. This property cascades to child containers.
28755 * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "center")
28757 buttonAlign:'center',
28760 * @cfg {Number} minButtonWidth Minimum width of all buttons in pixels (defaults to 75)
28765 * @cfg {String} labelAlign Valid values are "left," "top" and "right" (defaults to "left").
28766 * This property cascades to child containers if not set.
28771 * @cfg {Boolean} monitorValid If true the form monitors its valid state <b>client-side</b> and
28772 * fires a looping event with that state. This is required to bind buttons to the valid
28773 * state using the config value formBind:true on the button.
28775 monitorValid : false,
28778 * @cfg {Number} monitorPoll The milliseconds to poll valid state, ignored if monitorValid is not true (defaults to 200)
28783 * @cfg {String} progressUrl - Url to return progress data
28786 progressUrl : false,
28789 * Opens a new {@link Roo.form.Column} container in the layout stack. If fields are passed after the config, the
28790 * fields are added and the column is closed. If no fields are passed the column remains open
28791 * until end() is called.
28792 * @param {Object} config The config to pass to the column
28793 * @param {Field} field1 (optional)
28794 * @param {Field} field2 (optional)
28795 * @param {Field} etc (optional)
28796 * @return Column The column container object
28798 column : function(c){
28799 var col = new Roo.form.Column(c);
28801 if(arguments.length > 1){ // duplicate code required because of Opera
28802 this.add.apply(this, Array.prototype.slice.call(arguments, 1));
28809 * Opens a new {@link Roo.form.FieldSet} container in the layout stack. If fields are passed after the config, the
28810 * fields are added and the fieldset is closed. If no fields are passed the fieldset remains open
28811 * until end() is called.
28812 * @param {Object} config The config to pass to the fieldset
28813 * @param {Field} field1 (optional)
28814 * @param {Field} field2 (optional)
28815 * @param {Field} etc (optional)
28816 * @return FieldSet The fieldset container object
28818 fieldset : function(c){
28819 var fs = new Roo.form.FieldSet(c);
28821 if(arguments.length > 1){ // duplicate code required because of Opera
28822 this.add.apply(this, Array.prototype.slice.call(arguments, 1));
28829 * Opens a new {@link Roo.form.Layout} container in the layout stack. If fields are passed after the config, the
28830 * fields are added and the container is closed. If no fields are passed the container remains open
28831 * until end() is called.
28832 * @param {Object} config The config to pass to the Layout
28833 * @param {Field} field1 (optional)
28834 * @param {Field} field2 (optional)
28835 * @param {Field} etc (optional)
28836 * @return Layout The container object
28838 container : function(c){
28839 var l = new Roo.form.Layout(c);
28841 if(arguments.length > 1){ // duplicate code required because of Opera
28842 this.add.apply(this, Array.prototype.slice.call(arguments, 1));
28849 * Opens the passed container in the layout stack. The container can be any {@link Roo.form.Layout} or subclass.
28850 * @param {Object} container A Roo.form.Layout or subclass of Layout
28851 * @return {Form} this
28853 start : function(c){
28854 // cascade label info
28855 Roo.applyIf(c, {'labelAlign': this.active.labelAlign, 'labelWidth': this.active.labelWidth, 'itemCls': this.active.itemCls});
28856 this.active.stack.push(c);
28857 c.ownerCt = this.active;
28863 * Closes the current open container
28864 * @return {Form} this
28867 if(this.active == this.root){
28870 this.active = this.active.ownerCt;
28875 * Add Roo.form components to the current open container (e.g. column, fieldset, etc.). Fields added via this method
28876 * can also be passed with an additional property of fieldLabel, which if supplied, will provide the text to display
28877 * as the label of the field.
28878 * @param {Field} field1
28879 * @param {Field} field2 (optional)
28880 * @param {Field} etc. (optional)
28881 * @return {Form} this
28884 this.active.stack.push.apply(this.active.stack, arguments);
28885 this.allItems.push.apply(this.allItems,arguments);
28887 for(var i = 0, a = arguments, len = a.length; i < len; i++) {
28888 if(a[i].isFormField){
28893 Roo.form.Form.superclass.add.apply(this, r);
28903 * Find any element that has been added to a form, using it's ID or name
28904 * This can include framesets, columns etc. along with regular fields..
28905 * @param {String} id - id or name to find.
28907 * @return {Element} e - or false if nothing found.
28909 findbyId : function(id)
28915 Roo.each(this.allItems, function(f){
28916 if (f.id == id || f.name == id ){
28927 * Render this form into the passed container. This should only be called once!
28928 * @param {String/HTMLElement/Element} container The element this component should be rendered into
28929 * @return {Form} this
28931 render : function(ct)
28937 var o = this.autoCreate || {
28939 method : this.method || 'POST',
28940 id : this.id || Roo.id()
28942 this.initEl(ct.createChild(o));
28944 this.root.render(this.el);
28948 this.items.each(function(f){
28949 f.render('x-form-el-'+f.id);
28952 if(this.buttons.length > 0){
28953 // tables are required to maintain order and for correct IE layout
28954 var tb = this.el.createChild({cls:'x-form-btns-ct', cn: {
28955 cls:"x-form-btns x-form-btns-"+this.buttonAlign,
28956 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
28958 var tr = tb.getElementsByTagName('tr')[0];
28959 for(var i = 0, len = this.buttons.length; i < len; i++) {
28960 var b = this.buttons[i];
28961 var td = document.createElement('td');
28962 td.className = 'x-form-btn-td';
28963 b.render(tr.appendChild(td));
28966 if(this.monitorValid){ // initialize after render
28967 this.startMonitoring();
28969 this.fireEvent('rendered', this);
28974 * Adds a button to the footer of the form - this <b>must</b> be called before the form is rendered.
28975 * @param {String/Object} config A string becomes the button text, an object can either be a Button config
28976 * object or a valid Roo.DomHelper element config
28977 * @param {Function} handler The function called when the button is clicked
28978 * @param {Object} scope (optional) The scope of the handler function
28979 * @return {Roo.Button}
28981 addButton : function(config, handler, scope){
28985 minWidth: this.minButtonWidth,
28988 if(typeof config == "string"){
28991 Roo.apply(bc, config);
28993 var btn = new Roo.Button(null, bc);
28994 this.buttons.push(btn);
28999 * Adds a series of form elements (using the xtype property as the factory method.
29000 * Valid xtypes are: TextField, TextArea .... Button, Layout, FieldSet, Column, (and 'end' to close a block)
29001 * @param {Object} config
29004 addxtype : function()
29006 var ar = Array.prototype.slice.call(arguments, 0);
29008 for(var i = 0; i < ar.length; i++) {
29010 continue; // skip -- if this happends something invalid got sent, we
29011 // should ignore it, as basically that interface element will not show up
29012 // and that should be pretty obvious!!
29015 if (Roo.form[ar[i].xtype]) {
29017 var fe = Roo.factory(ar[i], Roo.form);
29023 fe.store.form = this;
29028 this.allItems.push(fe);
29029 if (fe.items && fe.addxtype) {
29030 fe.addxtype.apply(fe, fe.items);
29040 // console.log('adding ' + ar[i].xtype);
29042 if (ar[i].xtype == 'Button') {
29043 //console.log('adding button');
29044 //console.log(ar[i]);
29045 this.addButton(ar[i]);
29046 this.allItems.push(fe);
29050 if (ar[i].xtype == 'end') { // so we can add fieldsets... / layout etc.
29051 alert('end is not supported on xtype any more, use items');
29053 // //console.log('adding end');
29061 * Starts monitoring of the valid state of this form. Usually this is done by passing the config
29062 * option "monitorValid"
29064 startMonitoring : function(){
29067 Roo.TaskMgr.start({
29068 run : this.bindHandler,
29069 interval : this.monitorPoll || 200,
29076 * Stops monitoring of the valid state of this form
29078 stopMonitoring : function(){
29079 this.bound = false;
29083 bindHandler : function(){
29085 return false; // stops binding
29088 this.items.each(function(f){
29089 if(!f.isValid(true)){
29094 for(var i = 0, len = this.buttons.length; i < len; i++){
29095 var btn = this.buttons[i];
29096 if(btn.formBind === true && btn.disabled === valid){
29097 btn.setDisabled(!valid);
29100 this.fireEvent('clientvalidation', this, valid);
29114 Roo.Form = Roo.form.Form;
29117 * Ext JS Library 1.1.1
29118 * Copyright(c) 2006-2007, Ext JS, LLC.
29120 * Originally Released Under LGPL - original licence link has changed is not relivant.
29123 * <script type="text/javascript">
29127 * @class Roo.form.Action
29128 * Internal Class used to handle form actions
29130 * @param {Roo.form.BasicForm} el The form element or its id
29131 * @param {Object} config Configuration options
29135 // define the action interface
29136 Roo.form.Action = function(form, options){
29138 this.options = options || {};
29141 * Client Validation Failed
29144 Roo.form.Action.CLIENT_INVALID = 'client';
29146 * Server Validation Failed
29149 Roo.form.Action.SERVER_INVALID = 'server';
29151 * Connect to Server Failed
29154 Roo.form.Action.CONNECT_FAILURE = 'connect';
29156 * Reading Data from Server Failed
29159 Roo.form.Action.LOAD_FAILURE = 'load';
29161 Roo.form.Action.prototype = {
29163 failureType : undefined,
29164 response : undefined,
29165 result : undefined,
29167 // interface method
29168 run : function(options){
29172 // interface method
29173 success : function(response){
29177 // interface method
29178 handleResponse : function(response){
29182 // default connection failure
29183 failure : function(response){
29185 this.response = response;
29186 this.failureType = Roo.form.Action.CONNECT_FAILURE;
29187 this.form.afterAction(this, false);
29190 processResponse : function(response){
29191 this.response = response;
29192 if(!response.responseText){
29195 this.result = this.handleResponse(response);
29196 return this.result;
29199 // utility functions used internally
29200 getUrl : function(appendParams){
29201 var url = this.options.url || this.form.url || this.form.el.dom.action;
29203 var p = this.getParams();
29205 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
29211 getMethod : function(){
29212 return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
29215 getParams : function(){
29216 var bp = this.form.baseParams;
29217 var p = this.options.params;
29219 if(typeof p == "object"){
29220 p = Roo.urlEncode(Roo.applyIf(p, bp));
29221 }else if(typeof p == 'string' && bp){
29222 p += '&' + Roo.urlEncode(bp);
29225 p = Roo.urlEncode(bp);
29230 createCallback : function(){
29232 success: this.success,
29233 failure: this.failure,
29235 timeout: (this.form.timeout*1000),
29236 upload: this.form.fileUpload ? this.success : undefined
29241 Roo.form.Action.Submit = function(form, options){
29242 Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
29245 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
29248 haveProgress : false,
29249 uploadComplete : false,
29251 // uploadProgress indicator.
29252 uploadProgress : function()
29254 if (!this.form.progressUrl) {
29258 if (!this.haveProgress) {
29259 Roo.MessageBox.progress("Uploading", "Uploading");
29261 if (this.uploadComplete) {
29262 Roo.MessageBox.hide();
29266 this.haveProgress = true;
29268 var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
29270 var c = new Roo.data.Connection();
29272 url : this.form.progressUrl,
29277 success : function(req){
29278 //console.log(data);
29282 rdata = Roo.decode(req.responseText)
29284 Roo.log("Invalid data from server..");
29288 if (!rdata || !rdata.success) {
29290 Roo.MessageBox.alert(Roo.encode(rdata));
29293 var data = rdata.data;
29295 if (this.uploadComplete) {
29296 Roo.MessageBox.hide();
29301 Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
29302 Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
29305 this.uploadProgress.defer(2000,this);
29308 failure: function(data) {
29309 Roo.log('progress url failed ');
29320 // run get Values on the form, so it syncs any secondary forms.
29321 this.form.getValues();
29323 var o = this.options;
29324 var method = this.getMethod();
29325 var isPost = method == 'POST';
29326 if(o.clientValidation === false || this.form.isValid()){
29328 if (this.form.progressUrl) {
29329 this.form.findField('UPLOAD_IDENTIFIER').setValue(
29330 (new Date() * 1) + '' + Math.random());
29335 Roo.Ajax.request(Roo.apply(this.createCallback(), {
29336 form:this.form.el.dom,
29337 url:this.getUrl(!isPost),
29339 params:isPost ? this.getParams() : null,
29340 isUpload: this.form.fileUpload
29343 this.uploadProgress();
29345 }else if (o.clientValidation !== false){ // client validation failed
29346 this.failureType = Roo.form.Action.CLIENT_INVALID;
29347 this.form.afterAction(this, false);
29351 success : function(response)
29353 this.uploadComplete= true;
29354 if (this.haveProgress) {
29355 Roo.MessageBox.hide();
29359 var result = this.processResponse(response);
29360 if(result === true || result.success){
29361 this.form.afterAction(this, true);
29365 this.form.markInvalid(result.errors);
29366 this.failureType = Roo.form.Action.SERVER_INVALID;
29368 this.form.afterAction(this, false);
29370 failure : function(response)
29372 this.uploadComplete= true;
29373 if (this.haveProgress) {
29374 Roo.MessageBox.hide();
29377 this.response = response;
29378 this.failureType = Roo.form.Action.CONNECT_FAILURE;
29379 this.form.afterAction(this, false);
29382 handleResponse : function(response){
29383 if(this.form.errorReader){
29384 var rs = this.form.errorReader.read(response);
29387 for(var i = 0, len = rs.records.length; i < len; i++) {
29388 var r = rs.records[i];
29389 errors[i] = r.data;
29392 if(errors.length < 1){
29396 success : rs.success,
29402 ret = Roo.decode(response.responseText);
29406 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
29416 Roo.form.Action.Load = function(form, options){
29417 Roo.form.Action.Load.superclass.constructor.call(this, form, options);
29418 this.reader = this.form.reader;
29421 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
29426 Roo.Ajax.request(Roo.apply(
29427 this.createCallback(), {
29428 method:this.getMethod(),
29429 url:this.getUrl(false),
29430 params:this.getParams()
29434 success : function(response){
29436 var result = this.processResponse(response);
29437 if(result === true || !result.success || !result.data){
29438 this.failureType = Roo.form.Action.LOAD_FAILURE;
29439 this.form.afterAction(this, false);
29442 this.form.clearInvalid();
29443 this.form.setValues(result.data);
29444 this.form.afterAction(this, true);
29447 handleResponse : function(response){
29448 if(this.form.reader){
29449 var rs = this.form.reader.read(response);
29450 var data = rs.records && rs.records[0] ? rs.records[0].data : null;
29452 success : rs.success,
29456 return Roo.decode(response.responseText);
29460 Roo.form.Action.ACTION_TYPES = {
29461 'load' : Roo.form.Action.Load,
29462 'submit' : Roo.form.Action.Submit
29465 * Ext JS Library 1.1.1
29466 * Copyright(c) 2006-2007, Ext JS, LLC.
29468 * Originally Released Under LGPL - original licence link has changed is not relivant.
29471 * <script type="text/javascript">
29475 * @class Roo.form.Layout
29476 * @extends Roo.Component
29477 * Creates a container for layout and rendering of fields in an {@link Roo.form.Form}.
29479 * @param {Object} config Configuration options
29481 Roo.form.Layout = function(config){
29483 if (config.items) {
29484 xitems = config.items;
29485 delete config.items;
29487 Roo.form.Layout.superclass.constructor.call(this, config);
29489 Roo.each(xitems, this.addxtype, this);
29493 Roo.extend(Roo.form.Layout, Roo.Component, {
29495 * @cfg {String/Object} autoCreate
29496 * A DomHelper element spec used to autocreate the layout (defaults to {tag: 'div', cls: 'x-form-ct'})
29499 * @cfg {String/Object/Function} style
29500 * A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
29501 * a function which returns such a specification.
29504 * @cfg {String} labelAlign
29505 * Valid values are "left," "top" and "right" (defaults to "left")
29508 * @cfg {Number} labelWidth
29509 * Fixed width in pixels of all field labels (defaults to undefined)
29512 * @cfg {Boolean} clear
29513 * True to add a clearing element at the end of this layout, equivalent to CSS clear: both (defaults to true)
29517 * @cfg {String} labelSeparator
29518 * The separator to use after field labels (defaults to ':')
29520 labelSeparator : ':',
29522 * @cfg {Boolean} hideLabels
29523 * True to suppress the display of field labels in this layout (defaults to false)
29525 hideLabels : false,
29528 defaultAutoCreate : {tag: 'div', cls: 'x-form-ct'},
29533 onRender : function(ct, position){
29534 if(this.el){ // from markup
29535 this.el = Roo.get(this.el);
29536 }else { // generate
29537 var cfg = this.getAutoCreate();
29538 this.el = ct.createChild(cfg, position);
29541 this.el.applyStyles(this.style);
29543 if(this.labelAlign){
29544 this.el.addClass('x-form-label-'+this.labelAlign);
29546 if(this.hideLabels){
29547 this.labelStyle = "display:none";
29548 this.elementStyle = "padding-left:0;";
29550 if(typeof this.labelWidth == 'number'){
29551 this.labelStyle = "width:"+this.labelWidth+"px;";
29552 this.elementStyle = "padding-left:"+((this.labelWidth+(typeof this.labelPad == 'number' ? this.labelPad : 5))+'px')+";";
29554 if(this.labelAlign == 'top'){
29555 this.labelStyle = "width:auto;";
29556 this.elementStyle = "padding-left:0;";
29559 var stack = this.stack;
29560 var slen = stack.length;
29562 if(!this.fieldTpl){
29563 var t = new Roo.Template(
29564 '<div class="x-form-item {5}">',
29565 '<label for="{0}" style="{2}">{1}{4}</label>',
29566 '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
29568 '</div><div class="x-form-clear-left"></div>'
29570 t.disableFormats = true;
29572 Roo.form.Layout.prototype.fieldTpl = t;
29574 for(var i = 0; i < slen; i++) {
29575 if(stack[i].isFormField){
29576 this.renderField(stack[i]);
29578 this.renderComponent(stack[i]);
29583 this.el.createChild({cls:'x-form-clear'});
29588 renderField : function(f){
29589 f.fieldEl = Roo.get(this.fieldTpl.append(this.el, [
29592 f.labelStyle||this.labelStyle||'', //2
29593 this.elementStyle||'', //3
29594 typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator, //4
29595 f.itemCls||this.itemCls||'' //5
29596 ], true).getPrevSibling());
29600 renderComponent : function(c){
29601 c.render(c.isLayout ? this.el : this.el.createChild());
29604 * Adds a object form elements (using the xtype property as the factory method.)
29605 * Valid xtypes are: TextField, TextArea .... Button, Layout, FieldSet, Column
29606 * @param {Object} config
29608 addxtype : function(o)
29610 // create the lement.
29611 o.form = this.form;
29612 var fe = Roo.factory(o, Roo.form);
29613 this.form.allItems.push(fe);
29614 this.stack.push(fe);
29616 if (fe.isFormField) {
29617 this.form.items.add(fe);
29625 * @class Roo.form.Column
29626 * @extends Roo.form.Layout
29627 * Creates a column container for layout and rendering of fields in an {@link Roo.form.Form}.
29629 * @param {Object} config Configuration options
29631 Roo.form.Column = function(config){
29632 Roo.form.Column.superclass.constructor.call(this, config);
29635 Roo.extend(Roo.form.Column, Roo.form.Layout, {
29637 * @cfg {Number/String} width
29638 * The fixed width of the column in pixels or CSS value (defaults to "auto")
29641 * @cfg {String/Object} autoCreate
29642 * A DomHelper element spec used to autocreate the column (defaults to {tag: 'div', cls: 'x-form-ct x-form-column'})
29646 defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-column'},
29649 onRender : function(ct, position){
29650 Roo.form.Column.superclass.onRender.call(this, ct, position);
29652 this.el.setWidth(this.width);
29659 * @class Roo.form.Row
29660 * @extends Roo.form.Layout
29661 * Creates a row container for layout and rendering of fields in an {@link Roo.form.Form}.
29663 * @param {Object} config Configuration options
29667 Roo.form.Row = function(config){
29668 Roo.form.Row.superclass.constructor.call(this, config);
29671 Roo.extend(Roo.form.Row, Roo.form.Layout, {
29673 * @cfg {Number/String} width
29674 * The fixed width of the column in pixels or CSS value (defaults to "auto")
29677 * @cfg {Number/String} height
29678 * The fixed height of the column in pixels or CSS value (defaults to "auto")
29680 defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-row'},
29684 onRender : function(ct, position){
29685 //console.log('row render');
29687 var t = new Roo.Template(
29688 '<div class="x-form-item {5}" style="float:left;width:{6}px">',
29689 '<label for="{0}" style="{2}">{1}{4}</label>',
29690 '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
29694 t.disableFormats = true;
29696 Roo.form.Layout.prototype.rowTpl = t;
29698 this.fieldTpl = this.rowTpl;
29700 //console.log('lw' + this.labelWidth +', la:' + this.labelAlign);
29701 var labelWidth = 100;
29703 if ((this.labelAlign != 'top')) {
29704 if (typeof this.labelWidth == 'number') {
29705 labelWidth = this.labelWidth
29707 this.padWidth = 20 + labelWidth;
29711 Roo.form.Column.superclass.onRender.call(this, ct, position);
29713 this.el.setWidth(this.width);
29716 this.el.setHeight(this.height);
29721 renderField : function(f){
29722 f.fieldEl = this.fieldTpl.append(this.el, [
29723 f.id, f.fieldLabel,
29724 f.labelStyle||this.labelStyle||'',
29725 this.elementStyle||'',
29726 typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator,
29727 f.itemCls||this.itemCls||'',
29728 f.width ? f.width + this.padWidth : 160 + this.padWidth
29735 * @class Roo.form.FieldSet
29736 * @extends Roo.form.Layout
29737 * Creates a fieldset container for layout and rendering of fields in an {@link Roo.form.Form}.
29739 * @param {Object} config Configuration options
29741 Roo.form.FieldSet = function(config){
29742 Roo.form.FieldSet.superclass.constructor.call(this, config);
29745 Roo.extend(Roo.form.FieldSet, Roo.form.Layout, {
29747 * @cfg {String} legend
29748 * The text to display as the legend for the FieldSet (defaults to '')
29751 * @cfg {String/Object} autoCreate
29752 * A DomHelper element spec used to autocreate the fieldset (defaults to {tag: 'fieldset', cn: {tag:'legend'}})
29756 defaultAutoCreate : {tag: 'fieldset', cn: {tag:'legend'}},
29759 onRender : function(ct, position){
29760 Roo.form.FieldSet.superclass.onRender.call(this, ct, position);
29762 this.setLegend(this.legend);
29767 setLegend : function(text){
29769 this.el.child('legend').update(text);
29774 * Ext JS Library 1.1.1
29775 * Copyright(c) 2006-2007, Ext JS, LLC.
29777 * Originally Released Under LGPL - original licence link has changed is not relivant.
29780 * <script type="text/javascript">
29783 * @class Roo.form.VTypes
29784 * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
29787 Roo.form.VTypes = function(){
29788 // closure these in so they are only created once.
29789 var alpha = /^[a-zA-Z_]+$/;
29790 var alphanum = /^[a-zA-Z0-9_]+$/;
29791 var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,4}$/;
29792 var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
29794 // All these messages and functions are configurable
29797 * The function used to validate email addresses
29798 * @param {String} value The email address
29800 'email' : function(v){
29801 return email.test(v);
29804 * The error text to display when the email validation function returns false
29807 'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
29809 * The keystroke filter mask to be applied on email input
29812 'emailMask' : /[a-z0-9_\.\-@]/i,
29815 * The function used to validate URLs
29816 * @param {String} value The URL
29818 'url' : function(v){
29819 return url.test(v);
29822 * The error text to display when the url validation function returns false
29825 'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
29828 * The function used to validate alpha values
29829 * @param {String} value The value
29831 'alpha' : function(v){
29832 return alpha.test(v);
29835 * The error text to display when the alpha validation function returns false
29838 'alphaText' : 'This field should only contain letters and _',
29840 * The keystroke filter mask to be applied on alpha input
29843 'alphaMask' : /[a-z_]/i,
29846 * The function used to validate alphanumeric values
29847 * @param {String} value The value
29849 'alphanum' : function(v){
29850 return alphanum.test(v);
29853 * The error text to display when the alphanumeric validation function returns false
29856 'alphanumText' : 'This field should only contain letters, numbers and _',
29858 * The keystroke filter mask to be applied on alphanumeric input
29861 'alphanumMask' : /[a-z0-9_]/i
29863 }();//<script type="text/javascript">
29866 * @class Roo.form.FCKeditor
29867 * @extends Roo.form.TextArea
29868 * Wrapper around the FCKEditor http://www.fckeditor.net
29870 * Creates a new FCKeditor
29871 * @param {Object} config Configuration options
29873 Roo.form.FCKeditor = function(config){
29874 Roo.form.FCKeditor.superclass.constructor.call(this, config);
29877 * @event editorinit
29878 * Fired when the editor is initialized - you can add extra handlers here..
29879 * @param {FCKeditor} this
29880 * @param {Object} the FCK object.
29887 Roo.form.FCKeditor.editors = { };
29888 Roo.extend(Roo.form.FCKeditor, Roo.form.TextArea,
29890 //defaultAutoCreate : {
29891 // tag : "textarea",style : "width:100px;height:60px;" ,autocomplete : "off"
29895 * @cfg {Object} fck options - see fck manual for details.
29900 * @cfg {Object} fck toolbar set (Basic or Default)
29902 toolbarSet : 'Basic',
29904 * @cfg {Object} fck BasePath
29906 basePath : '/fckeditor/',
29914 onRender : function(ct, position)
29917 this.defaultAutoCreate = {
29919 style:"width:300px;height:60px;",
29920 autocomplete: "off"
29923 Roo.form.FCKeditor.superclass.onRender.call(this, ct, position);
29926 this.textSizeEl = Roo.DomHelper.append(document.body, {tag: "pre", cls: "x-form-grow-sizer"});
29927 if(this.preventScrollbars){
29928 this.el.setStyle("overflow", "hidden");
29930 this.el.setHeight(this.growMin);
29933 //console.log('onrender' + this.getId() );
29934 Roo.form.FCKeditor.editors[this.getId()] = this;
29937 this.replaceTextarea() ;
29941 getEditor : function() {
29942 return this.fckEditor;
29945 * Sets a data value into the field and validates it. To set the value directly without validation see {@link #setRawValue}.
29946 * @param {Mixed} value The value to set
29950 setValue : function(value)
29952 //console.log('setValue: ' + value);
29954 if(typeof(value) == 'undefined') { // not sure why this is happending...
29957 Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
29959 //if(!this.el || !this.getEditor()) {
29960 // this.value = value;
29961 //this.setValue.defer(100,this,[value]);
29965 if(!this.getEditor()) {
29969 this.getEditor().SetData(value);
29976 * Returns the normalized data value (undefined or emptyText will be returned as ''). To return the raw value see {@link #getRawValue}.
29977 * @return {Mixed} value The field value
29979 getValue : function()
29982 if (this.frame && this.frame.dom.style.display == 'none') {
29983 return Roo.form.FCKeditor.superclass.getValue.call(this);
29986 if(!this.el || !this.getEditor()) {
29988 // this.getValue.defer(100,this);
29993 var value=this.getEditor().GetData();
29994 Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
29995 return Roo.form.FCKeditor.superclass.getValue.call(this);
30001 * Returns the raw data value which may or may not be a valid, defined value. To return a normalized value see {@link #getValue}.
30002 * @return {Mixed} value The field value
30004 getRawValue : function()
30006 if (this.frame && this.frame.dom.style.display == 'none') {
30007 return Roo.form.FCKeditor.superclass.getRawValue.call(this);
30010 if(!this.el || !this.getEditor()) {
30011 //this.getRawValue.defer(100,this);
30018 var value=this.getEditor().GetData();
30019 Roo.form.FCKeditor.superclass.setRawValue.apply(this,[value]);
30020 return Roo.form.FCKeditor.superclass.getRawValue.call(this);
30024 setSize : function(w,h) {
30028 //if (this.frame && this.frame.dom.style.display == 'none') {
30029 // Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
30032 //if(!this.el || !this.getEditor()) {
30033 // this.setSize.defer(100,this, [w,h]);
30039 Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
30041 this.frame.dom.setAttribute('width', w);
30042 this.frame.dom.setAttribute('height', h);
30043 this.frame.setSize(w,h);
30047 toggleSourceEdit : function(value) {
30051 this.el.dom.style.display = value ? '' : 'none';
30052 this.frame.dom.style.display = value ? 'none' : '';
30057 focus: function(tag)
30059 if (this.frame.dom.style.display == 'none') {
30060 return Roo.form.FCKeditor.superclass.focus.call(this);
30062 if(!this.el || !this.getEditor()) {
30063 this.focus.defer(100,this, [tag]);
30070 var tgs = this.getEditor().EditorDocument.getElementsByTagName(tag);
30071 this.getEditor().Focus();
30073 if (!this.getEditor().Selection.GetSelection()) {
30074 this.focus.defer(100,this, [tag]);
30079 var r = this.getEditor().EditorDocument.createRange();
30080 r.setStart(tgs[0],0);
30081 r.setEnd(tgs[0],0);
30082 this.getEditor().Selection.GetSelection().removeAllRanges();
30083 this.getEditor().Selection.GetSelection().addRange(r);
30084 this.getEditor().Focus();
30091 replaceTextarea : function()
30093 if ( document.getElementById( this.getId() + '___Frame' ) )
30095 //if ( !this.checkBrowser || this._isCompatibleBrowser() )
30097 // We must check the elements firstly using the Id and then the name.
30098 var oTextarea = document.getElementById( this.getId() );
30100 var colElementsByName = document.getElementsByName( this.getId() ) ;
30102 oTextarea.style.display = 'none' ;
30104 if ( oTextarea.tabIndex ) {
30105 this.TabIndex = oTextarea.tabIndex ;
30108 this._insertHtmlBefore( this._getConfigHtml(), oTextarea ) ;
30109 this._insertHtmlBefore( this._getIFrameHtml(), oTextarea ) ;
30110 this.frame = Roo.get(this.getId() + '___Frame')
30113 _getConfigHtml : function()
30117 for ( var o in this.fckconfig ) {
30118 sConfig += sConfig.length > 0 ? '&' : '';
30119 sConfig += encodeURIComponent( o ) + '=' + encodeURIComponent( this.fckconfig[o] ) ;
30122 return '<input type="hidden" id="' + this.getId() + '___Config" value="' + sConfig + '" style="display:none" />' ;
30126 _getIFrameHtml : function()
30128 var sFile = 'fckeditor.html' ;
30129 /* no idea what this is about..
30132 if ( (/fcksource=true/i).test( window.top.location.search ) )
30133 sFile = 'fckeditor.original.html' ;
30138 var sLink = this.basePath + 'editor/' + sFile + '?InstanceName=' + encodeURIComponent( this.getId() ) ;
30139 sLink += this.toolbarSet ? ( '&Toolbar=' + this.toolbarSet) : '';
30142 var html = '<iframe id="' + this.getId() +
30143 '___Frame" src="' + sLink +
30144 '" width="' + this.width +
30145 '" height="' + this.height + '"' +
30146 (this.tabIndex ? ' tabindex="' + this.tabIndex + '"' :'' ) +
30147 ' frameborder="0" scrolling="no"></iframe>' ;
30152 _insertHtmlBefore : function( html, element )
30154 if ( element.insertAdjacentHTML ) {
30156 element.insertAdjacentHTML( 'beforeBegin', html ) ;
30158 var oRange = document.createRange() ;
30159 oRange.setStartBefore( element ) ;
30160 var oFragment = oRange.createContextualFragment( html );
30161 element.parentNode.insertBefore( oFragment, element ) ;
30174 //Roo.reg('fckeditor', Roo.form.FCKeditor);
30176 function FCKeditor_OnComplete(editorInstance){
30177 var f = Roo.form.FCKeditor.editors[editorInstance.Name];
30178 f.fckEditor = editorInstance;
30179 //console.log("loaded");
30180 f.fireEvent('editorinit', f, editorInstance);
30200 //<script type="text/javascript">
30202 * @class Roo.form.GridField
30203 * @extends Roo.form.Field
30204 * Embed a grid (or editable grid into a form)
30207 * This embeds a grid in a form, the value of the field should be the json encoded array of rows
30209 * xgrid.store = Roo.data.Store
30210 * xgrid.store.proxy = Roo.data.MemoryProxy (data = [] )
30211 * xgrid.store.reader = Roo.data.JsonReader
30215 * Creates a new GridField
30216 * @param {Object} config Configuration options
30218 Roo.form.GridField = function(config){
30219 Roo.form.GridField.superclass.constructor.call(this, config);
30223 Roo.extend(Roo.form.GridField, Roo.form.Field, {
30225 * @cfg {Number} width - used to restrict width of grid..
30229 * @cfg {Number} height - used to restrict height of grid..
30233 * @cfg {Object} xgrid (xtype'd description of grid) { xtype : 'Grid', dataSource: .... }
30239 * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
30240 * {tag: "input", type: "checkbox", autocomplete: "off"})
30242 // defaultAutoCreate : { tag: 'div' },
30243 defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'off'},
30245 * @cfg {String} addTitle Text to include for adding a title.
30249 onResize : function(){
30250 Roo.form.Field.superclass.onResize.apply(this, arguments);
30253 initEvents : function(){
30254 // Roo.form.Checkbox.superclass.initEvents.call(this);
30255 // has no events...
30260 getResizeEl : function(){
30264 getPositionEl : function(){
30269 onRender : function(ct, position){
30271 this.style = this.style || 'overflow: hidden; border:1px solid #c3daf9;';
30272 var style = this.style;
30275 Roo.form.GridField.superclass.onRender.call(this, ct, position);
30276 this.wrap = this.el.wrap({cls: ''}); // not sure why ive done thsi...
30277 this.viewEl = this.wrap.createChild({ tag: 'div' });
30279 this.viewEl.applyStyles(style);
30282 this.viewEl.setWidth(this.width);
30285 this.viewEl.setHeight(this.height);
30287 //if(this.inputValue !== undefined){
30288 //this.setValue(this.value);
30291 this.grid = new Roo.grid[this.xgrid.xtype](this.viewEl, this.xgrid);
30294 this.grid.render();
30295 this.grid.getDataSource().on('remove', this.refreshValue, this);
30296 this.grid.getDataSource().on('update', this.refreshValue, this);
30297 this.grid.on('afteredit', this.refreshValue, this);
30303 * Sets the value of the item.
30304 * @param {String} either an object or a string..
30306 setValue : function(v){
30308 v = v || []; // empty set..
30309 // this does not seem smart - it really only affects memoryproxy grids..
30310 if (this.grid && this.grid.getDataSource() && typeof(v) != 'undefined') {
30311 var ds = this.grid.getDataSource();
30312 // assumes a json reader..
30314 data[ds.reader.meta.root ] = typeof(v) == 'string' ? Roo.decode(v) : v;
30315 ds.loadData( data);
30317 // clear selection so it does not get stale.
30318 if (this.grid.sm) {
30319 this.grid.sm.clearSelections();
30322 Roo.form.GridField.superclass.setValue.call(this, v);
30323 this.refreshValue();
30324 // should load data in the grid really....
30328 refreshValue: function() {
30330 this.grid.getDataSource().each(function(r) {
30333 this.el.dom.value = Roo.encode(val);
30341 * Ext JS Library 1.1.1
30342 * Copyright(c) 2006-2007, Ext JS, LLC.
30344 * Originally Released Under LGPL - original licence link has changed is not relivant.
30347 * <script type="text/javascript">
30350 * @class Roo.form.DisplayField
30351 * @extends Roo.form.Field
30352 * A generic Field to display non-editable data.
30354 * Creates a new Display Field item.
30355 * @param {Object} config Configuration options
30357 Roo.form.DisplayField = function(config){
30358 Roo.form.DisplayField.superclass.constructor.call(this, config);
30362 Roo.extend(Roo.form.DisplayField, Roo.form.TextField, {
30363 inputType: 'hidden',
30369 * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
30371 focusClass : undefined,
30373 * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
30375 fieldClass: 'x-form-field',
30378 * @cfg {Function} valueRenderer The renderer for the field (so you can reformat output). should return raw HTML
30380 valueRenderer: undefined,
30384 * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
30385 * {tag: "input", type: "checkbox", autocomplete: "off"})
30388 // defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'off'},
30390 onResize : function(){
30391 Roo.form.DisplayField.superclass.onResize.apply(this, arguments);
30395 initEvents : function(){
30396 // Roo.form.Checkbox.superclass.initEvents.call(this);
30397 // has no events...
30402 getResizeEl : function(){
30406 getPositionEl : function(){
30411 onRender : function(ct, position){
30413 Roo.form.DisplayField.superclass.onRender.call(this, ct, position);
30414 //if(this.inputValue !== undefined){
30415 this.wrap = this.el.wrap();
30417 this.viewEl = this.wrap.createChild({ tag: 'div', cls: 'x-form-displayfield'});
30419 if (this.bodyStyle) {
30420 this.viewEl.applyStyles(this.bodyStyle);
30422 //this.viewEl.setStyle('padding', '2px');
30424 this.setValue(this.value);
30429 initValue : Roo.emptyFn,
30434 onClick : function(){
30439 * Sets the checked state of the checkbox.
30440 * @param {Boolean/String} checked True, 'true', '1', or 'on' to check the checkbox, any other value will uncheck it.
30442 setValue : function(v){
30444 var html = this.valueRenderer ? this.valueRenderer(v) : String.format('{0}', v);
30445 // this might be called before we have a dom element..
30446 if (!this.viewEl) {
30449 this.viewEl.dom.innerHTML = html;
30450 Roo.form.DisplayField.superclass.setValue.call(this, v);
30460 * @class Roo.form.DayPicker
30461 * @extends Roo.form.Field
30462 * A Day picker show [M] [T] [W] ....
30464 * Creates a new Day Picker
30465 * @param {Object} config Configuration options
30467 Roo.form.DayPicker= function(config){
30468 Roo.form.DayPicker.superclass.constructor.call(this, config);
30472 Roo.extend(Roo.form.DayPicker, Roo.form.Field, {
30474 * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
30476 focusClass : undefined,
30478 * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
30480 fieldClass: "x-form-field",
30483 * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
30484 * {tag: "input", type: "checkbox", autocomplete: "off"})
30486 defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "off"},
30489 actionMode : 'viewEl',
30493 inputType : 'hidden',
30496 inputElement: false, // real input element?
30497 basedOn: false, // ????
30499 isFormField: true, // not sure where this is needed!!!!
30501 onResize : function(){
30502 Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
30503 if(!this.boxLabel){
30504 this.el.alignTo(this.wrap, 'c-c');
30508 initEvents : function(){
30509 Roo.form.Checkbox.superclass.initEvents.call(this);
30510 this.el.on("click", this.onClick, this);
30511 this.el.on("change", this.onClick, this);
30515 getResizeEl : function(){
30519 getPositionEl : function(){
30525 onRender : function(ct, position){
30526 Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
30528 this.wrap = this.el.wrap({cls: 'x-form-daypick-item '});
30530 var r1 = '<table><tr>';
30531 var r2 = '<tr class="x-form-daypick-icons">';
30532 for (var i=0; i < 7; i++) {
30533 r1+= '<td><div>' + Date.dayNames[i].substring(0,3) + '</div></td>';
30534 r2+= '<td><img class="x-menu-item-icon" src="' + Roo.BLANK_IMAGE_URL +'"></td>';
30537 var viewEl = this.wrap.createChild( r1 + '</tr>' + r2 + '</tr></table>');
30538 viewEl.select('img').on('click', this.onClick, this);
30539 this.viewEl = viewEl;
30542 // this will not work on Chrome!!!
30543 this.el.on('DOMAttrModified', this.setFromHidden, this); //ff
30544 this.el.on('propertychange', this.setFromHidden, this); //ie
30552 initValue : Roo.emptyFn,
30555 * Returns the checked state of the checkbox.
30556 * @return {Boolean} True if checked, else false
30558 getValue : function(){
30559 return this.el.dom.value;
30564 onClick : function(e){
30565 //this.setChecked(!this.checked);
30566 Roo.get(e.target).toggleClass('x-menu-item-checked');
30567 this.refreshValue();
30568 //if(this.el.dom.checked != this.checked){
30569 // this.setValue(this.el.dom.checked);
30574 refreshValue : function()
30577 this.viewEl.select('img',true).each(function(e,i,n) {
30578 val += e.is(".x-menu-item-checked") ? String(n) : '';
30580 this.setValue(val, true);
30584 * Sets the checked state of the checkbox.
30585 * On is always based on a string comparison between inputValue and the param.
30586 * @param {Boolean/String} value - the value to set
30587 * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
30589 setValue : function(v,suppressEvent){
30590 if (!this.el.dom) {
30593 var old = this.el.dom.value ;
30594 this.el.dom.value = v;
30595 if (suppressEvent) {
30599 // update display..
30600 this.viewEl.select('img',true).each(function(e,i,n) {
30602 var on = e.is(".x-menu-item-checked");
30603 var newv = v.indexOf(String(n)) > -1;
30605 e.toggleClass('x-menu-item-checked');
30611 this.fireEvent('change', this, v, old);
30616 // handle setting of hidden value by some other method!!?!?
30617 setFromHidden: function()
30622 //console.log("SET FROM HIDDEN");
30623 //alert('setFrom hidden');
30624 this.setValue(this.el.dom.value);
30627 onDestroy : function()
30630 Roo.get(this.viewEl).remove();
30633 Roo.form.DayPicker.superclass.onDestroy.call(this);
30637 * RooJS Library 1.1.1
30638 * Copyright(c) 2008-2011 Alan Knowles
30645 * @class Roo.form.ComboCheck
30646 * @extends Roo.form.ComboBox
30647 * A combobox for multiple select items.
30649 * FIXME - could do with a reset button..
30652 * Create a new ComboCheck
30653 * @param {Object} config Configuration options
30655 Roo.form.ComboCheck = function(config){
30656 Roo.form.ComboCheck.superclass.constructor.call(this, config);
30657 // should verify some data...
30659 // hiddenName = required..
30660 // displayField = required
30661 // valudField == required
30662 var req= [ 'hiddenName', 'displayField', 'valueField' ];
30664 Roo.each(req, function(e) {
30665 if ((typeof(_t[e]) == 'undefined' ) || !_t[e].length) {
30666 throw "Roo.form.ComboCheck : missing value for: " + e;
30673 Roo.extend(Roo.form.ComboCheck, Roo.form.ComboBox, {
30678 selectedClass: 'x-menu-item-checked',
30681 onRender : function(ct, position){
30687 var cls = 'x-combo-list';
30690 this.tpl = new Roo.Template({
30691 html : '<div class="'+cls+'-item x-menu-check-item">' +
30692 '<img class="x-menu-item-icon" style="margin: 0px;" src="' + Roo.BLANK_IMAGE_URL + '">' +
30693 '<span>{' + this.displayField + '}</span>' +
30700 Roo.form.ComboCheck.superclass.onRender.call(this, ct, position);
30701 this.view.singleSelect = false;
30702 this.view.multiSelect = true;
30703 this.view.toggleSelect = true;
30704 this.pageTb.add(new Roo.Toolbar.Fill(), {
30707 handler: function()
30714 onViewOver : function(e, t){
30720 onViewClick : function(doFocus,index){
30724 select: function () {
30725 //Roo.log("SELECT CALLED");
30728 selectByValue : function(xv, scrollIntoView){
30729 var ar = this.getValueArray();
30732 Roo.each(ar, function(v) {
30733 if(v === undefined || v === null){
30736 var r = this.findRecord(this.valueField, v);
30738 sels.push(this.store.indexOf(r))
30742 this.view.select(sels);
30748 onSelect : function(record, index){
30749 // Roo.log("onselect Called");
30750 // this is only called by the clear button now..
30751 this.view.clearSelections();
30752 this.setValue('[]');
30753 if (this.value != this.valueBefore) {
30754 this.fireEvent('change', this, this.value, this.valueBefore);
30755 this.valueBefore = this.value;
30758 getValueArray : function()
30763 //Roo.log(this.value);
30764 if (typeof(this.value) == 'undefined') {
30767 var ar = Roo.decode(this.value);
30768 return ar instanceof Array ? ar : []; //?? valid?
30771 Roo.log(e + "\nRoo.form.ComboCheck:getValueArray invalid data:" + this.getValue());
30776 expand : function ()
30779 Roo.form.ComboCheck.superclass.expand.call(this);
30780 this.valueBefore = typeof(this.value) == 'undefined' ? '' : this.value;
30781 //this.valueBefore = typeof(this.valueBefore) == 'undefined' ? '' : this.valueBefore;
30786 collapse : function(){
30787 Roo.form.ComboCheck.superclass.collapse.call(this);
30788 var sl = this.view.getSelectedIndexes();
30789 var st = this.store;
30793 Roo.each(sl, function(i) {
30795 nv.push(r.get(this.valueField));
30797 this.setValue(Roo.encode(nv));
30798 if (this.value != this.valueBefore) {
30800 this.fireEvent('change', this, this.value, this.valueBefore);
30801 this.valueBefore = this.value;
30806 setValue : function(v){
30810 var vals = this.getValueArray();
30812 Roo.each(vals, function(k) {
30813 var r = this.findRecord(this.valueField, k);
30815 tv.push(r.data[this.displayField]);
30816 }else if(this.valueNotFoundText !== undefined){
30817 tv.push( this.valueNotFoundText );
30822 Roo.form.ComboBox.superclass.setValue.call(this, tv.join(', '));
30823 this.hiddenField.value = v;
30829 * Ext JS Library 1.1.1
30830 * Copyright(c) 2006-2007, Ext JS, LLC.
30832 * Originally Released Under LGPL - original licence link has changed is not relivant.
30835 * <script type="text/javascript">
30839 * @class Roo.form.Signature
30840 * @extends Roo.form.Field
30844 * @param {Object} config Configuration options
30847 Roo.form.Signature = function(config){
30848 Roo.form.Signature.superclass.constructor.call(this, config);
30850 this.addEvents({// not in used??
30853 * Fires when the 'confirm' icon is pressed (add a listener to enable add button)
30854 * @param {Roo.form.Signature} combo This combo box
30859 * Fires when the 'edit' icon is pressed (add a listener to enable add button)
30860 * @param {Roo.form.ComboBox} combo This combo box
30861 * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
30867 Roo.extend(Roo.form.Signature, Roo.form.Field, {
30869 * @cfg {Object} labels Label to use when rendering a form.
30873 * confirm : "Confirm"
30878 confirm : "Confirm"
30881 * @cfg {Number} width The signature panel width (defaults to 300)
30885 * @cfg {Number} height The signature panel height (defaults to 100)
30889 * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to false)
30891 allowBlank : false,
30894 // {Object} signPanel The signature SVG panel element (defaults to {})
30896 // {Boolean} isMouseDown False to validate that the mouse down event (defaults to false)
30897 isMouseDown : false,
30898 // {Boolean} isConfirmed validate the signature is confirmed or not for submitting form (defaults to false)
30899 isConfirmed : false,
30900 // {String} signatureTmp SVG mapping string (defaults to empty string)
30904 defaultAutoCreate : { // modified by initCompnoent..
30910 onRender : function(ct, position){
30912 Roo.form.Signature.superclass.onRender.call(this, ct, position);
30914 this.wrap = this.el.wrap({
30915 cls:'x-form-signature-wrap', style : 'width: ' + this.width + 'px', cn:{cls:'x-form-signature'}
30918 this.createToolbar(this);
30919 this.signPanel = this.wrap.createChild({
30921 style: 'width: ' + this.width + 'px; height: ' + this.height + 'px; border: 0;'
30925 this.svgID = Roo.id();
30926 this.svgEl = this.signPanel.createChild({
30927 xmlns : 'http://www.w3.org/2000/svg',
30929 id : this.svgID + "-svg",
30931 height: this.height,
30932 viewBox: '0 0 '+this.width+' '+this.height,
30936 id: this.svgID + "-svg-r",
30938 height: this.height,
30943 id: this.svgID + "-svg-l",
30945 y1: (this.height*0.8), // start set the line in 80% of height
30946 x2: this.width, // end
30947 y2: (this.height*0.8), // end set the line in 80% of height
30949 'stroke-width': "1",
30950 'stroke-dasharray': "3",
30951 'shape-rendering': "crispEdges",
30952 'pointer-events': "none"
30956 id: this.svgID + "-svg-p",
30958 'stroke-width': "3",
30960 'pointer-events': 'none'
30965 this.svgBox = this.svgEl.dom.getScreenCTM();
30967 createSVG : function(){
30968 var svg = this.signPanel;
30969 var r = svg.select('#'+ this.svgID + '-svg-r', true).first().dom;
30972 r.addEventListener('mousedown', function(e) { return t.down(e); }, false);
30973 r.addEventListener('mousemove', function(e) { return t.move(e); }, false);
30974 r.addEventListener('mouseup', function(e) { return t.up(e); }, false);
30975 r.addEventListener('mouseout', function(e) { return t.up(e); }, false);
30976 r.addEventListener('touchstart', function(e) { return t.down(e); }, false);
30977 r.addEventListener('touchmove', function(e) { return t.move(e); }, false);
30978 r.addEventListener('touchend', function(e) { return t.up(e); }, false);
30981 isTouchEvent : function(e){
30982 return e.type.match(/^touch/);
30984 getCoords : function (e) {
30985 var pt = this.svgEl.dom.createSVGPoint();
30988 if (this.isTouchEvent(e)) {
30989 pt.x = e.targetTouches[0].clientX
30990 pt.y = e.targetTouches[0].clientY;
30992 var a = this.svgEl.dom.getScreenCTM();
30993 var b = a.inverse();
30994 var mx = pt.matrixTransform(b);
30995 return mx.x + ',' + mx.y;
30997 //mouse event headler
30998 down : function (e) {
30999 this.signatureTmp += 'M' + this.getCoords(e) + ' ';
31000 this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr('d', this.signatureTmp);
31002 this.isMouseDown = true;
31004 e.preventDefault();
31006 move : function (e) {
31007 if (this.isMouseDown) {
31008 this.signatureTmp += 'L' + this.getCoords(e) + ' ';
31009 this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', this.signatureTmp);
31012 e.preventDefault();
31014 up : function (e) {
31015 this.isMouseDown = false;
31016 var sp = this.signatureTmp.split(' ');
31019 if(!sp[sp.length-2].match(/^L/)){
31023 this.signatureTmp = sp.join(" ");
31026 if(this.getValue() != this.signatureTmp){
31027 this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
31028 this.isConfirmed = false;
31030 e.preventDefault();
31034 * Protected method that will not generally be called directly. It
31035 * is called when the editor creates its toolbar. Override this method if you need to
31036 * add custom toolbar buttons.
31037 * @param {HtmlEditor} editor
31039 createToolbar : function(editor){
31040 function btn(id, toggle, handler){
31041 var xid = fid + '-'+ id ;
31045 cls : 'x-btn-icon x-edit-'+id,
31046 enableToggle:toggle !== false,
31047 scope: editor, // was editor...
31048 handler:handler||editor.relayBtnCmd,
31049 clickEvent:'mousedown',
31050 tooltip: etb.buttonTips[id] || undefined, ///tips ???
31056 var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
31060 cls : ' x-signature-btn x-signature-'+id,
31061 scope: editor, // was editor...
31062 handler: this.reset,
31063 clickEvent:'mousedown',
31064 text: this.labels.clear
31071 cls : ' x-signature-btn x-signature-'+id,
31072 scope: editor, // was editor...
31073 handler: this.confirmHandler,
31074 clickEvent:'mousedown',
31075 text: this.labels.confirm
31082 * when user is clicked confirm then show this image.....
31084 * @return {String} Image Data URI
31086 getImageDataURI : function(){
31087 var svg = this.svgEl.dom.parentNode.innerHTML;
31088 var src = 'data:image/svg+xml;base64,'+window.btoa(svg);
31093 * @return {Boolean} this.isConfirmed
31095 getConfirmed : function(){
31096 return this.isConfirmed;
31100 * @return {Number} this.width
31102 getWidth : function(){
31107 * @return {Number} this.height
31109 getHeight : function(){
31110 return this.height;
31113 getSignature : function(){
31114 return this.signatureTmp;
31117 reset : function(){
31118 this.signatureTmp = '';
31119 this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
31120 this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', '');
31121 this.isConfirmed = false;
31122 Roo.form.Signature.superclass.reset.call(this);
31124 setSignature : function(s){
31125 this.signatureTmp = s;
31126 this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
31127 this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', s);
31129 this.isConfirmed = false;
31130 Roo.form.Signature.superclass.reset.call(this);
31133 // Roo.log(this.signPanel.dom.contentWindow.up())
31136 setConfirmed : function(){
31140 // Roo.log(Roo.get(this.signPanel.dom.contentWindow.r).attr('fill', '#cfc'));
31143 confirmHandler : function(){
31144 if(!this.getSignature()){
31148 this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#cfc');
31149 this.setValue(this.getSignature());
31150 this.isConfirmed = true;
31152 this.fireEvent('confirm', this);
31155 // Subclasses should provide the validation implementation by overriding this
31156 validateValue : function(value){
31157 if(this.allowBlank){
31161 if(this.isConfirmed){
31168 * Ext JS Library 1.1.1
31169 * Copyright(c) 2006-2007, Ext JS, LLC.
31171 * Originally Released Under LGPL - original licence link has changed is not relivant.
31174 * <script type="text/javascript">
31179 * @class Roo.form.ComboBox
31180 * @extends Roo.form.TriggerField
31181 * A combobox control with support for autocomplete, remote-loading, paging and many other features.
31183 * Create a new ComboBox.
31184 * @param {Object} config Configuration options
31186 Roo.form.Select = function(config){
31187 Roo.form.Select.superclass.constructor.call(this, config);
31191 Roo.extend(Roo.form.Select , Roo.form.ComboBox, {
31193 * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
31196 * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
31197 * rendering into an Roo.Editor, defaults to false)
31200 * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
31201 * {tag: "input", type: "text", size: "24", autocomplete: "off"})
31204 * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
31207 * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
31208 * the dropdown list (defaults to undefined, with no header element)
31212 * @cfg {String/Roo.Template} tpl The template to use to render the output
31216 defaultAutoCreate : {tag: "select" },
31218 * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
31220 listWidth: undefined,
31222 * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
31223 * mode = 'remote' or 'text' if mode = 'local')
31225 displayField: undefined,
31227 * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
31228 * mode = 'remote' or 'value' if mode = 'local').
31229 * Note: use of a valueField requires the user make a selection
31230 * in order for a value to be mapped.
31232 valueField: undefined,
31236 * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
31237 * field's data value (defaults to the underlying DOM element's name)
31239 hiddenName: undefined,
31241 * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
31245 * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
31247 selectedClass: 'x-combo-selected',
31249 * @cfg {String} triggerClass An additional CSS class used to style the trigger button. The trigger will always get the
31250 * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
31251 * which displays a downward arrow icon).
31253 triggerClass : 'x-form-arrow-trigger',
31255 * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
31259 * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
31260 * anchor positions (defaults to 'tl-bl')
31262 listAlign: 'tl-bl?',
31264 * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
31268 * @cfg {String} triggerAction The action to execute when the trigger field is activated. Use 'all' to run the
31269 * query specified by the allQuery config option (defaults to 'query')
31271 triggerAction: 'query',
31273 * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
31274 * (defaults to 4, does not apply if editable = false)
31278 * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
31279 * delay (typeAheadDelay) if it matches a known value (defaults to false)
31283 * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
31284 * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
31288 * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
31289 * filter queries will execute with page start and limit parameters. Only applies when mode = 'remote' (defaults to 0)
31293 * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus. Only applies
31294 * when editable = true (defaults to false)
31296 selectOnFocus:false,
31298 * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
31300 queryParam: 'query',
31302 * @cfg {String} loadingText The text to display in the dropdown list while data is loading. Only applies
31303 * when mode = 'remote' (defaults to 'Loading...')
31305 loadingText: 'Loading...',
31307 * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
31311 * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
31315 * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
31316 * traditional select (defaults to true)
31320 * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
31324 * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
31328 * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
31329 * listWidth has a higher value)
31333 * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
31334 * allow the user to set arbitrary text into the field (defaults to false)
31336 forceSelection:false,
31338 * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
31339 * if typeAhead = true (defaults to 250)
31341 typeAheadDelay : 250,
31343 * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
31344 * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
31346 valueNotFoundText : undefined,
31349 * @cfg {String} defaultValue The value displayed after loading the store.
31354 * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
31356 blockFocus : false,
31359 * @cfg {Boolean} disableClear Disable showing of clear button.
31361 disableClear : false,
31363 * @cfg {Boolean} alwaysQuery Disable caching of results, and always send query
31365 alwaysQuery : false,
31371 // element that contains real text value.. (when hidden is used..)
31374 onRender : function(ct, position){
31375 Roo.form.Field.prototype.onRender.call(this, ct, position);
31378 this.store.on('beforeload', this.onBeforeLoad, this);
31379 this.store.on('load', this.onLoad, this);
31380 this.store.on('loadexception', this.onLoadException, this);
31381 this.store.load({});
31389 initEvents : function(){
31390 //Roo.form.ComboBox.superclass.initEvents.call(this);
31394 onDestroy : function(){
31397 this.store.un('beforeload', this.onBeforeLoad, this);
31398 this.store.un('load', this.onLoad, this);
31399 this.store.un('loadexception', this.onLoadException, this);
31401 //Roo.form.ComboBox.superclass.onDestroy.call(this);
31405 fireKey : function(e){
31406 if(e.isNavKeyPress() && !this.list.isVisible()){
31407 this.fireEvent("specialkey", this, e);
31412 onResize: function(w, h){
31420 * Allow or prevent the user from directly editing the field text. If false is passed,
31421 * the user will only be able to select from the items defined in the dropdown list. This method
31422 * is the runtime equivalent of setting the 'editable' config option at config time.
31423 * @param {Boolean} value True to allow the user to directly edit the field text
31425 setEditable : function(value){
31430 onBeforeLoad : function(){
31432 Roo.log("Select before load");
31435 this.innerList.update(this.loadingText ?
31436 '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
31437 //this.restrictHeight();
31438 this.selectedIndex = -1;
31442 onLoad : function(){
31445 var dom = this.el.dom;
31446 dom.innerHTML = '';
31447 var od = dom.ownerDocument;
31449 if (this.emptyText) {
31450 var op = od.createElement('option');
31451 op.setAttribute('value', '');
31452 op.innerHTML = String.format('{0}', this.emptyText);
31453 dom.appendChild(op);
31455 if(this.store.getCount() > 0){
31457 var vf = this.valueField;
31458 var df = this.displayField;
31459 this.store.data.each(function(r) {
31460 // which colmsn to use... testing - cdoe / title..
31461 var op = od.createElement('option');
31462 op.setAttribute('value', r.data[vf]);
31463 op.innerHTML = String.format('{0}', r.data[df]);
31464 dom.appendChild(op);
31466 if (typeof(this.defaultValue != 'undefined')) {
31467 this.setValue(this.defaultValue);
31472 //this.onEmptyResults();
31477 onLoadException : function()
31479 dom.innerHTML = '';
31481 Roo.log("Select on load exception");
31485 Roo.log(this.store.reader.jsonData);
31486 if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
31487 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
31493 onTypeAhead : function(){
31498 onSelect : function(record, index){
31499 Roo.log('on select?');
31501 if(this.fireEvent('beforeselect', this, record, index) !== false){
31502 this.setFromData(index > -1 ? record.data : false);
31504 this.fireEvent('select', this, record, index);
31509 * Returns the currently selected field value or empty string if no value is set.
31510 * @return {String} value The selected value
31512 getValue : function(){
31513 var dom = this.el.dom;
31514 this.value = dom.options[dom.selectedIndex].value;
31520 * Clears any text/value currently set in the field
31522 clearValue : function(){
31524 this.el.dom.selectedIndex = this.emptyText ? 0 : -1;
31529 * Sets the specified value into the field. If the value finds a match, the corresponding record text
31530 * will be displayed in the field. If the value does not match the data value of an existing item,
31531 * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
31532 * Otherwise the field will be blank (although the value will still be set).
31533 * @param {String} value The value to match
31535 setValue : function(v){
31536 var d = this.el.dom;
31537 for (var i =0; i < d.options.length;i++) {
31538 if (v == d.options[i].value) {
31539 d.selectedIndex = i;
31547 * @property {Object} the last set data for the element
31552 * Sets the value of the field based on a object which is related to the record format for the store.
31553 * @param {Object} value the value to set as. or false on reset?
31555 setFromData : function(o){
31556 Roo.log('setfrom data?');
31562 reset : function(){
31566 findRecord : function(prop, value){
31571 if(this.store.getCount() > 0){
31572 this.store.each(function(r){
31573 if(r.data[prop] == value){
31583 getName: function()
31585 // returns hidden if it's set..
31586 if (!this.rendered) {return ''};
31587 return !this.hiddenName && this.el.dom.name ? this.el.dom.name : (this.hiddenName || '');
31595 onEmptyResults : function(){
31596 Roo.log('empty results');
31601 * Returns true if the dropdown list is expanded, else false.
31603 isExpanded : function(){
31608 * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
31609 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
31610 * @param {String} value The data value of the item to select
31611 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
31612 * selected item if it is not currently in view (defaults to true)
31613 * @return {Boolean} True if the value matched an item in the list, else false
31615 selectByValue : function(v, scrollIntoView){
31616 Roo.log('select By Value');
31619 if(v !== undefined && v !== null){
31620 var r = this.findRecord(this.valueField || this.displayField, v);
31622 this.select(this.store.indexOf(r), scrollIntoView);
31630 * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
31631 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
31632 * @param {Number} index The zero-based index of the list item to select
31633 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
31634 * selected item if it is not currently in view (defaults to true)
31636 select : function(index, scrollIntoView){
31637 Roo.log('select ');
31640 this.selectedIndex = index;
31641 this.view.select(index);
31642 if(scrollIntoView !== false){
31643 var el = this.view.getNode(index);
31645 this.innerList.scrollChildIntoView(el, false);
31653 validateBlur : function(){
31660 initQuery : function(){
31661 this.doQuery(this.getRawValue());
31665 doForce : function(){
31666 if(this.el.dom.value.length > 0){
31667 this.el.dom.value =
31668 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
31674 * Execute a query to filter the dropdown list. Fires the beforequery event prior to performing the
31675 * query allowing the query action to be canceled if needed.
31676 * @param {String} query The SQL query to execute
31677 * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
31678 * in the field than the minimum specified by the minChars config option. It also clears any filter previously
31679 * saved in the current store (defaults to false)
31681 doQuery : function(q, forceAll){
31683 Roo.log('doQuery?');
31684 if(q === undefined || q === null){
31689 forceAll: forceAll,
31693 if(this.fireEvent('beforequery', qe)===false || qe.cancel){
31697 forceAll = qe.forceAll;
31698 if(forceAll === true || (q.length >= this.minChars)){
31699 if(this.lastQuery != q || this.alwaysQuery){
31700 this.lastQuery = q;
31701 if(this.mode == 'local'){
31702 this.selectedIndex = -1;
31704 this.store.clearFilter();
31706 this.store.filter(this.displayField, q);
31710 this.store.baseParams[this.queryParam] = q;
31712 params: this.getParams(q)
31717 this.selectedIndex = -1;
31724 getParams : function(q){
31726 //p[this.queryParam] = q;
31729 p.limit = this.pageSize;
31735 * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
31737 collapse : function(){
31742 collapseIf : function(e){
31747 * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
31749 expand : function(){
31757 * @cfg {Boolean} grow
31761 * @cfg {Number} growMin
31765 * @cfg {Number} growMax
31773 setWidth : function()
31777 getResizeEl : function(){
31780 });//<script type="text/javasscript">
31784 * @class Roo.DDView
31785 * A DnD enabled version of Roo.View.
31786 * @param {Element/String} container The Element in which to create the View.
31787 * @param {String} tpl The template string used to create the markup for each element of the View
31788 * @param {Object} config The configuration properties. These include all the config options of
31789 * {@link Roo.View} plus some specific to this class.<br>
31791 * Drag/drop is implemented by adding {@link Roo.data.Record}s to the target DDView. If copying is
31792 * not being performed, the original {@link Roo.data.Record} is removed from the source DDView.<br>
31794 * The following extra CSS rules are needed to provide insertion point highlighting:<pre><code>
31795 .x-view-drag-insert-above {
31796 border-top:1px dotted #3366cc;
31798 .x-view-drag-insert-below {
31799 border-bottom:1px dotted #3366cc;
31805 Roo.DDView = function(container, tpl, config) {
31806 Roo.DDView.superclass.constructor.apply(this, arguments);
31807 this.getEl().setStyle("outline", "0px none");
31808 this.getEl().unselectable();
31809 if (this.dragGroup) {
31810 this.setDraggable(this.dragGroup.split(","));
31812 if (this.dropGroup) {
31813 this.setDroppable(this.dropGroup.split(","));
31815 if (this.deletable) {
31816 this.setDeletable();
31818 this.isDirtyFlag = false;
31824 Roo.extend(Roo.DDView, Roo.View, {
31825 /** @cfg {String/Array} dragGroup The ddgroup name(s) for the View's DragZone. */
31826 /** @cfg {String/Array} dropGroup The ddgroup name(s) for the View's DropZone. */
31827 /** @cfg {Boolean} copy Causes drag operations to copy nodes rather than move. */
31828 /** @cfg {Boolean} allowCopy Causes ctrl/drag operations to copy nodes rather than move. */
31832 reset: Roo.emptyFn,
31834 clearInvalid: Roo.form.Field.prototype.clearInvalid,
31836 validate: function() {
31840 destroy: function() {
31841 this.purgeListeners();
31842 this.getEl.removeAllListeners();
31843 this.getEl().remove();
31844 if (this.dragZone) {
31845 if (this.dragZone.destroy) {
31846 this.dragZone.destroy();
31849 if (this.dropZone) {
31850 if (this.dropZone.destroy) {
31851 this.dropZone.destroy();
31856 /** Allows this class to be an Roo.form.Field so it can be found using {@link Roo.form.BasicForm#findField}. */
31857 getName: function() {
31861 /** Loads the View from a JSON string representing the Records to put into the Store. */
31862 setValue: function(v) {
31864 throw "DDView.setValue(). DDView must be constructed with a valid Store";
31867 data[this.store.reader.meta.root] = v ? [].concat(v) : [];
31868 this.store.proxy = new Roo.data.MemoryProxy(data);
31872 /** @return {String} a parenthesised list of the ids of the Records in the View. */
31873 getValue: function() {
31875 this.store.each(function(rec) {
31876 result += rec.id + ',';
31878 return result.substr(0, result.length - 1) + ')';
31881 getIds: function() {
31882 var i = 0, result = new Array(this.store.getCount());
31883 this.store.each(function(rec) {
31884 result[i++] = rec.id;
31889 isDirty: function() {
31890 return this.isDirtyFlag;
31894 * Part of the Roo.dd.DropZone interface. If no target node is found, the
31895 * whole Element becomes the target, and this causes the drop gesture to append.
31897 getTargetFromEvent : function(e) {
31898 var target = e.getTarget();
31899 while ((target !== null) && (target.parentNode != this.el.dom)) {
31900 target = target.parentNode;
31903 target = this.el.dom.lastChild || this.el.dom;
31909 * Create the drag data which consists of an object which has the property "ddel" as
31910 * the drag proxy element.
31912 getDragData : function(e) {
31913 var target = this.findItemFromChild(e.getTarget());
31915 this.handleSelection(e);
31916 var selNodes = this.getSelectedNodes();
31919 copy: this.copy || (this.allowCopy && e.ctrlKey),
31923 var selectedIndices = this.getSelectedIndexes();
31924 for (var i = 0; i < selectedIndices.length; i++) {
31925 dragData.records.push(this.store.getAt(selectedIndices[i]));
31927 if (selNodes.length == 1) {
31928 dragData.ddel = target.cloneNode(true); // the div element
31930 var div = document.createElement('div'); // create the multi element drag "ghost"
31931 div.className = 'multi-proxy';
31932 for (var i = 0, len = selNodes.length; i < len; i++) {
31933 div.appendChild(selNodes[i].cloneNode(true));
31935 dragData.ddel = div;
31937 //console.log(dragData)
31938 //console.log(dragData.ddel.innerHTML)
31941 //console.log('nodragData')
31945 /** Specify to which ddGroup items in this DDView may be dragged. */
31946 setDraggable: function(ddGroup) {
31947 if (ddGroup instanceof Array) {
31948 Roo.each(ddGroup, this.setDraggable, this);
31951 if (this.dragZone) {
31952 this.dragZone.addToGroup(ddGroup);
31954 this.dragZone = new Roo.dd.DragZone(this.getEl(), {
31955 containerScroll: true,
31959 // Draggability implies selection. DragZone's mousedown selects the element.
31960 if (!this.multiSelect) { this.singleSelect = true; }
31962 // Wire the DragZone's handlers up to methods in *this*
31963 this.dragZone.getDragData = this.getDragData.createDelegate(this);
31967 /** Specify from which ddGroup this DDView accepts drops. */
31968 setDroppable: function(ddGroup) {
31969 if (ddGroup instanceof Array) {
31970 Roo.each(ddGroup, this.setDroppable, this);
31973 if (this.dropZone) {
31974 this.dropZone.addToGroup(ddGroup);
31976 this.dropZone = new Roo.dd.DropZone(this.getEl(), {
31977 containerScroll: true,
31981 // Wire the DropZone's handlers up to methods in *this*
31982 this.dropZone.getTargetFromEvent = this.getTargetFromEvent.createDelegate(this);
31983 this.dropZone.onNodeEnter = this.onNodeEnter.createDelegate(this);
31984 this.dropZone.onNodeOver = this.onNodeOver.createDelegate(this);
31985 this.dropZone.onNodeOut = this.onNodeOut.createDelegate(this);
31986 this.dropZone.onNodeDrop = this.onNodeDrop.createDelegate(this);
31990 /** Decide whether to drop above or below a View node. */
31991 getDropPoint : function(e, n, dd){
31992 if (n == this.el.dom) { return "above"; }
31993 var t = Roo.lib.Dom.getY(n), b = t + n.offsetHeight;
31994 var c = t + (b - t) / 2;
31995 var y = Roo.lib.Event.getPageY(e);
32003 onNodeEnter : function(n, dd, e, data){
32007 onNodeOver : function(n, dd, e, data){
32008 var pt = this.getDropPoint(e, n, dd);
32009 // set the insert point style on the target node
32010 var dragElClass = this.dropNotAllowed;
32013 if (pt == "above"){
32014 dragElClass = n.previousSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-above";
32015 targetElClass = "x-view-drag-insert-above";
32017 dragElClass = n.nextSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-below";
32018 targetElClass = "x-view-drag-insert-below";
32020 if (this.lastInsertClass != targetElClass){
32021 Roo.fly(n).replaceClass(this.lastInsertClass, targetElClass);
32022 this.lastInsertClass = targetElClass;
32025 return dragElClass;
32028 onNodeOut : function(n, dd, e, data){
32029 this.removeDropIndicators(n);
32032 onNodeDrop : function(n, dd, e, data){
32033 if (this.fireEvent("drop", this, n, dd, e, data) === false) {
32036 var pt = this.getDropPoint(e, n, dd);
32037 var insertAt = (n == this.el.dom) ? this.nodes.length : n.nodeIndex;
32038 if (pt == "below") { insertAt++; }
32039 for (var i = 0; i < data.records.length; i++) {
32040 var r = data.records[i];
32041 var dup = this.store.getById(r.id);
32042 if (dup && (dd != this.dragZone)) {
32043 Roo.fly(this.getNode(this.store.indexOf(dup))).frame("red", 1);
32046 this.store.insert(insertAt++, r.copy());
32048 data.source.isDirtyFlag = true;
32050 this.store.insert(insertAt++, r);
32052 this.isDirtyFlag = true;
32055 this.dragZone.cachedTarget = null;
32059 removeDropIndicators : function(n){
32061 Roo.fly(n).removeClass([
32062 "x-view-drag-insert-above",
32063 "x-view-drag-insert-below"]);
32064 this.lastInsertClass = "_noclass";
32069 * Utility method. Add a delete option to the DDView's context menu.
32070 * @param {String} imageUrl The URL of the "delete" icon image.
32072 setDeletable: function(imageUrl) {
32073 if (!this.singleSelect && !this.multiSelect) {
32074 this.singleSelect = true;
32076 var c = this.getContextMenu();
32077 this.contextMenu.on("itemclick", function(item) {
32080 this.remove(this.getSelectedIndexes());
32084 this.contextMenu.add({
32091 /** Return the context menu for this DDView. */
32092 getContextMenu: function() {
32093 if (!this.contextMenu) {
32094 // Create the View's context menu
32095 this.contextMenu = new Roo.menu.Menu({
32096 id: this.id + "-contextmenu"
32098 this.el.on("contextmenu", this.showContextMenu, this);
32100 return this.contextMenu;
32103 disableContextMenu: function() {
32104 if (this.contextMenu) {
32105 this.el.un("contextmenu", this.showContextMenu, this);
32109 showContextMenu: function(e, item) {
32110 item = this.findItemFromChild(e.getTarget());
32113 this.select(this.getNode(item), this.multiSelect && e.ctrlKey, true);
32114 this.contextMenu.showAt(e.getXY());
32119 * Remove {@link Roo.data.Record}s at the specified indices.
32120 * @param {Array/Number} selectedIndices The index (or Array of indices) of Records to remove.
32122 remove: function(selectedIndices) {
32123 selectedIndices = [].concat(selectedIndices);
32124 for (var i = 0; i < selectedIndices.length; i++) {
32125 var rec = this.store.getAt(selectedIndices[i]);
32126 this.store.remove(rec);
32131 * Double click fires the event, but also, if this is draggable, and there is only one other
32132 * related DropZone, it transfers the selected node.
32134 onDblClick : function(e){
32135 var item = this.findItemFromChild(e.getTarget());
32137 if (this.fireEvent("dblclick", this, this.indexOf(item), item, e) === false) {
32140 if (this.dragGroup) {
32141 var targets = Roo.dd.DragDropMgr.getRelated(this.dragZone, true);
32142 while (targets.indexOf(this.dropZone) > -1) {
32143 targets.remove(this.dropZone);
32145 if (targets.length == 1) {
32146 this.dragZone.cachedTarget = null;
32147 var el = Roo.get(targets[0].getEl());
32148 var box = el.getBox(true);
32149 targets[0].onNodeDrop(el.dom, {
32151 xy: [box.x, box.y + box.height - 1]
32152 }, null, this.getDragData(e));
32158 handleSelection: function(e) {
32159 this.dragZone.cachedTarget = null;
32160 var item = this.findItemFromChild(e.getTarget());
32162 this.clearSelections(true);
32165 if (item && (this.multiSelect || this.singleSelect)){
32166 if(this.multiSelect && e.shiftKey && (!e.ctrlKey) && this.lastSelection){
32167 this.select(this.getNodes(this.indexOf(this.lastSelection), item.nodeIndex), false);
32168 }else if (this.isSelected(this.getNode(item)) && e.ctrlKey){
32169 this.unselect(item);
32171 this.select(item, this.multiSelect && e.ctrlKey);
32172 this.lastSelection = item;
32177 onItemClick : function(item, index, e){
32178 if(this.fireEvent("beforeclick", this, index, item, e) === false){
32184 unselect : function(nodeInfo, suppressEvent){
32185 var node = this.getNode(nodeInfo);
32186 if(node && this.isSelected(node)){
32187 if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
32188 Roo.fly(node).removeClass(this.selectedClass);
32189 this.selections.remove(node);
32190 if(!suppressEvent){
32191 this.fireEvent("selectionchange", this, this.selections);
32199 * Ext JS Library 1.1.1
32200 * Copyright(c) 2006-2007, Ext JS, LLC.
32202 * Originally Released Under LGPL - original licence link has changed is not relivant.
32205 * <script type="text/javascript">
32209 * @class Roo.LayoutManager
32210 * @extends Roo.util.Observable
32211 * Base class for layout managers.
32213 Roo.LayoutManager = function(container, config){
32214 Roo.LayoutManager.superclass.constructor.call(this);
32215 this.el = Roo.get(container);
32216 // ie scrollbar fix
32217 if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
32218 document.body.scroll = "no";
32219 }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
32220 this.el.position('relative');
32222 this.id = this.el.id;
32223 this.el.addClass("x-layout-container");
32224 /** false to disable window resize monitoring @type Boolean */
32225 this.monitorWindowResize = true;
32230 * Fires when a layout is performed.
32231 * @param {Roo.LayoutManager} this
32235 * @event regionresized
32236 * Fires when the user resizes a region.
32237 * @param {Roo.LayoutRegion} region The resized region
32238 * @param {Number} newSize The new size (width for east/west, height for north/south)
32240 "regionresized" : true,
32242 * @event regioncollapsed
32243 * Fires when a region is collapsed.
32244 * @param {Roo.LayoutRegion} region The collapsed region
32246 "regioncollapsed" : true,
32248 * @event regionexpanded
32249 * Fires when a region is expanded.
32250 * @param {Roo.LayoutRegion} region The expanded region
32252 "regionexpanded" : true
32254 this.updating = false;
32255 Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
32258 Roo.extend(Roo.LayoutManager, Roo.util.Observable, {
32260 * Returns true if this layout is currently being updated
32261 * @return {Boolean}
32263 isUpdating : function(){
32264 return this.updating;
32268 * Suspend the LayoutManager from doing auto-layouts while
32269 * making multiple add or remove calls
32271 beginUpdate : function(){
32272 this.updating = true;
32276 * Restore auto-layouts and optionally disable the manager from performing a layout
32277 * @param {Boolean} noLayout true to disable a layout update
32279 endUpdate : function(noLayout){
32280 this.updating = false;
32286 layout: function(){
32290 onRegionResized : function(region, newSize){
32291 this.fireEvent("regionresized", region, newSize);
32295 onRegionCollapsed : function(region){
32296 this.fireEvent("regioncollapsed", region);
32299 onRegionExpanded : function(region){
32300 this.fireEvent("regionexpanded", region);
32304 * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
32305 * performs box-model adjustments.
32306 * @return {Object} The size as an object {width: (the width), height: (the height)}
32308 getViewSize : function(){
32310 if(this.el.dom != document.body){
32311 size = this.el.getSize();
32313 size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
32315 size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
32316 size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
32321 * Returns the Element this layout is bound to.
32322 * @return {Roo.Element}
32324 getEl : function(){
32329 * Returns the specified region.
32330 * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
32331 * @return {Roo.LayoutRegion}
32333 getRegion : function(target){
32334 return this.regions[target.toLowerCase()];
32337 onWindowResize : function(){
32338 if(this.monitorWindowResize){
32344 * Ext JS Library 1.1.1
32345 * Copyright(c) 2006-2007, Ext JS, LLC.
32347 * Originally Released Under LGPL - original licence link has changed is not relivant.
32350 * <script type="text/javascript">
32353 * @class Roo.BorderLayout
32354 * @extends Roo.LayoutManager
32355 * This class represents a common layout manager used in desktop applications. For screenshots and more details,
32356 * please see: <br><br>
32357 * <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>
32358 * <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>
32361 var layout = new Roo.BorderLayout(document.body, {
32395 preferredTabWidth: 150
32400 var CP = Roo.ContentPanel;
32402 layout.beginUpdate();
32403 layout.add("north", new CP("north", "North"));
32404 layout.add("south", new CP("south", {title: "South", closable: true}));
32405 layout.add("west", new CP("west", {title: "West"}));
32406 layout.add("east", new CP("autoTabs", {title: "Auto Tabs", closable: true}));
32407 layout.add("center", new CP("center1", {title: "Close Me", closable: true}));
32408 layout.add("center", new CP("center2", {title: "Center Panel", closable: false}));
32409 layout.getRegion("center").showPanel("center1");
32410 layout.endUpdate();
32413 <b>The container the layout is rendered into can be either the body element or any other element.
32414 If it is not the body element, the container needs to either be an absolute positioned element,
32415 or you will need to add "position:relative" to the css of the container. You will also need to specify
32416 the container size if it is not the body element.</b>
32419 * Create a new BorderLayout
32420 * @param {String/HTMLElement/Element} container The container this layout is bound to
32421 * @param {Object} config Configuration options
32423 Roo.BorderLayout = function(container, config){
32424 config = config || {};
32425 Roo.BorderLayout.superclass.constructor.call(this, container, config);
32426 this.factory = config.factory || Roo.BorderLayout.RegionFactory;
32427 for(var i = 0, len = this.factory.validRegions.length; i < len; i++) {
32428 var target = this.factory.validRegions[i];
32429 if(config[target]){
32430 this.addRegion(target, config[target]);
32435 Roo.extend(Roo.BorderLayout, Roo.LayoutManager, {
32437 * Creates and adds a new region if it doesn't already exist.
32438 * @param {String} target The target region key (north, south, east, west or center).
32439 * @param {Object} config The regions config object
32440 * @return {BorderLayoutRegion} The new region
32442 addRegion : function(target, config){
32443 if(!this.regions[target]){
32444 var r = this.factory.create(target, this, config);
32445 this.bindRegion(target, r);
32447 return this.regions[target];
32451 bindRegion : function(name, r){
32452 this.regions[name] = r;
32453 r.on("visibilitychange", this.layout, this);
32454 r.on("paneladded", this.layout, this);
32455 r.on("panelremoved", this.layout, this);
32456 r.on("invalidated", this.layout, this);
32457 r.on("resized", this.onRegionResized, this);
32458 r.on("collapsed", this.onRegionCollapsed, this);
32459 r.on("expanded", this.onRegionExpanded, this);
32463 * Performs a layout update.
32465 layout : function(){
32466 if(this.updating) return;
32467 var size = this.getViewSize();
32468 var w = size.width;
32469 var h = size.height;
32474 //var x = 0, y = 0;
32476 var rs = this.regions;
32477 var north = rs["north"];
32478 var south = rs["south"];
32479 var west = rs["west"];
32480 var east = rs["east"];
32481 var center = rs["center"];
32482 //if(this.hideOnLayout){ // not supported anymore
32483 //c.el.setStyle("display", "none");
32485 if(north && north.isVisible()){
32486 var b = north.getBox();
32487 var m = north.getMargins();
32488 b.width = w - (m.left+m.right);
32491 centerY = b.height + b.y + m.bottom;
32492 centerH -= centerY;
32493 north.updateBox(this.safeBox(b));
32495 if(south && south.isVisible()){
32496 var b = south.getBox();
32497 var m = south.getMargins();
32498 b.width = w - (m.left+m.right);
32500 var totalHeight = (b.height + m.top + m.bottom);
32501 b.y = h - totalHeight + m.top;
32502 centerH -= totalHeight;
32503 south.updateBox(this.safeBox(b));
32505 if(west && west.isVisible()){
32506 var b = west.getBox();
32507 var m = west.getMargins();
32508 b.height = centerH - (m.top+m.bottom);
32510 b.y = centerY + m.top;
32511 var totalWidth = (b.width + m.left + m.right);
32512 centerX += totalWidth;
32513 centerW -= totalWidth;
32514 west.updateBox(this.safeBox(b));
32516 if(east && east.isVisible()){
32517 var b = east.getBox();
32518 var m = east.getMargins();
32519 b.height = centerH - (m.top+m.bottom);
32520 var totalWidth = (b.width + m.left + m.right);
32521 b.x = w - totalWidth + m.left;
32522 b.y = centerY + m.top;
32523 centerW -= totalWidth;
32524 east.updateBox(this.safeBox(b));
32527 var m = center.getMargins();
32529 x: centerX + m.left,
32530 y: centerY + m.top,
32531 width: centerW - (m.left+m.right),
32532 height: centerH - (m.top+m.bottom)
32534 //if(this.hideOnLayout){
32535 //center.el.setStyle("display", "block");
32537 center.updateBox(this.safeBox(centerBox));
32540 this.fireEvent("layout", this);
32544 safeBox : function(box){
32545 box.width = Math.max(0, box.width);
32546 box.height = Math.max(0, box.height);
32551 * Adds a ContentPanel (or subclass) to this layout.
32552 * @param {String} target The target region key (north, south, east, west or center).
32553 * @param {Roo.ContentPanel} panel The panel to add
32554 * @return {Roo.ContentPanel} The added panel
32556 add : function(target, panel){
32558 target = target.toLowerCase();
32559 return this.regions[target].add(panel);
32563 * Remove a ContentPanel (or subclass) to this layout.
32564 * @param {String} target The target region key (north, south, east, west or center).
32565 * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
32566 * @return {Roo.ContentPanel} The removed panel
32568 remove : function(target, panel){
32569 target = target.toLowerCase();
32570 return this.regions[target].remove(panel);
32574 * Searches all regions for a panel with the specified id
32575 * @param {String} panelId
32576 * @return {Roo.ContentPanel} The panel or null if it wasn't found
32578 findPanel : function(panelId){
32579 var rs = this.regions;
32580 for(var target in rs){
32581 if(typeof rs[target] != "function"){
32582 var p = rs[target].getPanel(panelId);
32592 * Searches all regions for a panel with the specified id and activates (shows) it.
32593 * @param {String/ContentPanel} panelId The panels id or the panel itself
32594 * @return {Roo.ContentPanel} The shown panel or null
32596 showPanel : function(panelId) {
32597 var rs = this.regions;
32598 for(var target in rs){
32599 var r = rs[target];
32600 if(typeof r != "function"){
32601 if(r.hasPanel(panelId)){
32602 return r.showPanel(panelId);
32610 * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
32611 * @param {Roo.state.Provider} provider (optional) An alternate state provider
32613 restoreState : function(provider){
32615 provider = Roo.state.Manager;
32617 var sm = new Roo.LayoutStateManager();
32618 sm.init(this, provider);
32622 * Adds a batch of multiple ContentPanels dynamically by passing a special regions config object. This config
32623 * object should contain properties for each region to add ContentPanels to, and each property's value should be
32624 * a valid ContentPanel config object. Example:
32626 // Create the main layout
32627 var layout = new Roo.BorderLayout('main-ct', {
32638 // Create and add multiple ContentPanels at once via configs
32641 id: 'source-files',
32643 title:'Ext Source Files',
32656 * @param {Object} regions An object containing ContentPanel configs by region name
32658 batchAdd : function(regions){
32659 this.beginUpdate();
32660 for(var rname in regions){
32661 var lr = this.regions[rname];
32663 this.addTypedPanels(lr, regions[rname]);
32670 addTypedPanels : function(lr, ps){
32671 if(typeof ps == 'string'){
32672 lr.add(new Roo.ContentPanel(ps));
32674 else if(ps instanceof Array){
32675 for(var i =0, len = ps.length; i < len; i++){
32676 this.addTypedPanels(lr, ps[i]);
32679 else if(!ps.events){ // raw config?
32681 delete ps.el; // prevent conflict
32682 lr.add(new Roo.ContentPanel(el || Roo.id(), ps));
32684 else { // panel object assumed!
32689 * Adds a xtype elements to the layout.
32693 xtype : 'ContentPanel',
32700 xtype : 'NestedLayoutPanel',
32706 items : [ ... list of content panels or nested layout panels.. ]
32710 * @param {Object} cfg Xtype definition of item to add.
32712 addxtype : function(cfg)
32714 // basically accepts a pannel...
32715 // can accept a layout region..!?!?
32716 //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
32718 if (!cfg.xtype.match(/Panel$/)) {
32723 if (typeof(cfg.region) == 'undefined') {
32724 Roo.log("Failed to add Panel, region was not set");
32728 var region = cfg.region;
32734 xitems = cfg.items;
32741 case 'ContentPanel': // ContentPanel (el, cfg)
32742 case 'ScrollPanel': // ContentPanel (el, cfg)
32744 if(cfg.autoCreate) {
32745 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
32747 var el = this.el.createChild();
32748 ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
32751 this.add(region, ret);
32755 case 'TreePanel': // our new panel!
32756 cfg.el = this.el.createChild();
32757 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
32758 this.add(region, ret);
32761 case 'NestedLayoutPanel':
32762 // create a new Layout (which is a Border Layout...
32763 var el = this.el.createChild();
32764 var clayout = cfg.layout;
32766 clayout.items = clayout.items || [];
32767 // replace this exitems with the clayout ones..
32768 xitems = clayout.items;
32771 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
32772 cfg.background = false;
32774 var layout = new Roo.BorderLayout(el, clayout);
32776 ret = new Roo[cfg.xtype](layout, cfg); // new panel!!!!!
32777 //console.log('adding nested layout panel ' + cfg.toSource());
32778 this.add(region, ret);
32779 nb = {}; /// find first...
32784 // needs grid and region
32786 //var el = this.getRegion(region).el.createChild();
32787 var el = this.el.createChild();
32788 // create the grid first...
32790 var grid = new Roo.grid[cfg.grid.xtype](el, cfg.grid);
32792 if (region == 'center' && this.active ) {
32793 cfg.background = false;
32795 ret = new Roo[cfg.xtype](grid, cfg); // new panel!!!!!
32797 this.add(region, ret);
32798 if (cfg.background) {
32799 ret.on('activate', function(gp) {
32800 if (!gp.grid.rendered) {
32815 alert("Can not add '" + cfg.xtype + "' to BorderLayout");
32817 // GridPanel (grid, cfg)
32820 this.beginUpdate();
32824 Roo.each(xitems, function(i) {
32825 region = nb && i.region ? i.region : false;
32827 var add = ret.addxtype(i);
32830 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
32831 if (!i.background) {
32832 abn[region] = nb[region] ;
32839 // make the last non-background panel active..
32840 //if (nb) { Roo.log(abn); }
32843 for(var r in abn) {
32844 region = this.getRegion(r);
32846 // tried using nb[r], but it does not work..
32848 region.showPanel(abn[r]);
32859 * Shortcut for creating a new BorderLayout object and adding one or more ContentPanels to it in a single step, handling
32860 * the beginUpdate and endUpdate calls internally. The key to this method is the <b>panels</b> property that can be
32861 * provided with each region config, which allows you to add ContentPanel configs in addition to the region configs
32862 * during creation. The following code is equivalent to the constructor-based example at the beginning of this class:
32865 var CP = Roo.ContentPanel;
32867 var layout = Roo.BorderLayout.create({
32871 panels: [new CP("north", "North")]
32880 panels: [new CP("west", {title: "West"})]
32889 panels: [new CP("autoTabs", {title: "Auto Tabs", closable: true})]
32898 panels: [new CP("south", {title: "South", closable: true})]
32905 preferredTabWidth: 150,
32907 new CP("center1", {title: "Close Me", closable: true}),
32908 new CP("center2", {title: "Center Panel", closable: false})
32913 layout.getRegion("center").showPanel("center1");
32918 Roo.BorderLayout.create = function(config, targetEl){
32919 var layout = new Roo.BorderLayout(targetEl || document.body, config);
32920 layout.beginUpdate();
32921 var regions = Roo.BorderLayout.RegionFactory.validRegions;
32922 for(var j = 0, jlen = regions.length; j < jlen; j++){
32923 var lr = regions[j];
32924 if(layout.regions[lr] && config[lr].panels){
32925 var r = layout.regions[lr];
32926 var ps = config[lr].panels;
32927 layout.addTypedPanels(r, ps);
32930 layout.endUpdate();
32935 Roo.BorderLayout.RegionFactory = {
32937 validRegions : ["north","south","east","west","center"],
32940 create : function(target, mgr, config){
32941 target = target.toLowerCase();
32942 if(config.lightweight || config.basic){
32943 return new Roo.BasicLayoutRegion(mgr, config, target);
32947 return new Roo.NorthLayoutRegion(mgr, config);
32949 return new Roo.SouthLayoutRegion(mgr, config);
32951 return new Roo.EastLayoutRegion(mgr, config);
32953 return new Roo.WestLayoutRegion(mgr, config);
32955 return new Roo.CenterLayoutRegion(mgr, config);
32957 throw 'Layout region "'+target+'" not supported.';
32961 * Ext JS Library 1.1.1
32962 * Copyright(c) 2006-2007, Ext JS, LLC.
32964 * Originally Released Under LGPL - original licence link has changed is not relivant.
32967 * <script type="text/javascript">
32971 * @class Roo.BasicLayoutRegion
32972 * @extends Roo.util.Observable
32973 * This class represents a lightweight region in a layout manager. This region does not move dom nodes
32974 * and does not have a titlebar, tabs or any other features. All it does is size and position
32975 * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
32977 Roo.BasicLayoutRegion = function(mgr, config, pos, skipConfig){
32979 this.position = pos;
32982 * @scope Roo.BasicLayoutRegion
32986 * @event beforeremove
32987 * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
32988 * @param {Roo.LayoutRegion} this
32989 * @param {Roo.ContentPanel} panel The panel
32990 * @param {Object} e The cancel event object
32992 "beforeremove" : true,
32994 * @event invalidated
32995 * Fires when the layout for this region is changed.
32996 * @param {Roo.LayoutRegion} this
32998 "invalidated" : true,
33000 * @event visibilitychange
33001 * Fires when this region is shown or hidden
33002 * @param {Roo.LayoutRegion} this
33003 * @param {Boolean} visibility true or false
33005 "visibilitychange" : true,
33007 * @event paneladded
33008 * Fires when a panel is added.
33009 * @param {Roo.LayoutRegion} this
33010 * @param {Roo.ContentPanel} panel The panel
33012 "paneladded" : true,
33014 * @event panelremoved
33015 * Fires when a panel is removed.
33016 * @param {Roo.LayoutRegion} this
33017 * @param {Roo.ContentPanel} panel The panel
33019 "panelremoved" : true,
33022 * Fires when this region is collapsed.
33023 * @param {Roo.LayoutRegion} this
33025 "collapsed" : true,
33028 * Fires when this region is expanded.
33029 * @param {Roo.LayoutRegion} this
33034 * Fires when this region is slid into view.
33035 * @param {Roo.LayoutRegion} this
33037 "slideshow" : true,
33040 * Fires when this region slides out of view.
33041 * @param {Roo.LayoutRegion} this
33043 "slidehide" : true,
33045 * @event panelactivated
33046 * Fires when a panel is activated.
33047 * @param {Roo.LayoutRegion} this
33048 * @param {Roo.ContentPanel} panel The activated panel
33050 "panelactivated" : true,
33053 * Fires when the user resizes this region.
33054 * @param {Roo.LayoutRegion} this
33055 * @param {Number} newSize The new size (width for east/west, height for north/south)
33059 /** A collection of panels in this region. @type Roo.util.MixedCollection */
33060 this.panels = new Roo.util.MixedCollection();
33061 this.panels.getKey = this.getPanelId.createDelegate(this);
33063 this.activePanel = null;
33064 // ensure listeners are added...
33066 if (config.listeners || config.events) {
33067 Roo.BasicLayoutRegion.superclass.constructor.call(this, {
33068 listeners : config.listeners || {},
33069 events : config.events || {}
33073 if(skipConfig !== true){
33074 this.applyConfig(config);
33078 Roo.extend(Roo.BasicLayoutRegion, Roo.util.Observable, {
33079 getPanelId : function(p){
33083 applyConfig : function(config){
33084 this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
33085 this.config = config;
33090 * Resizes the region to the specified size. For vertical regions (west, east) this adjusts
33091 * the width, for horizontal (north, south) the height.
33092 * @param {Number} newSize The new width or height
33094 resizeTo : function(newSize){
33095 var el = this.el ? this.el :
33096 (this.activePanel ? this.activePanel.getEl() : null);
33098 switch(this.position){
33101 el.setWidth(newSize);
33102 this.fireEvent("resized", this, newSize);
33106 el.setHeight(newSize);
33107 this.fireEvent("resized", this, newSize);
33113 getBox : function(){
33114 return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
33117 getMargins : function(){
33118 return this.margins;
33121 updateBox : function(box){
33123 var el = this.activePanel.getEl();
33124 el.dom.style.left = box.x + "px";
33125 el.dom.style.top = box.y + "px";
33126 this.activePanel.setSize(box.width, box.height);
33130 * Returns the container element for this region.
33131 * @return {Roo.Element}
33133 getEl : function(){
33134 return this.activePanel;
33138 * Returns true if this region is currently visible.
33139 * @return {Boolean}
33141 isVisible : function(){
33142 return this.activePanel ? true : false;
33145 setActivePanel : function(panel){
33146 panel = this.getPanel(panel);
33147 if(this.activePanel && this.activePanel != panel){
33148 this.activePanel.setActiveState(false);
33149 this.activePanel.getEl().setLeftTop(-10000,-10000);
33151 this.activePanel = panel;
33152 panel.setActiveState(true);
33154 panel.setSize(this.box.width, this.box.height);
33156 this.fireEvent("panelactivated", this, panel);
33157 this.fireEvent("invalidated");
33161 * Show the specified panel.
33162 * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
33163 * @return {Roo.ContentPanel} The shown panel or null
33165 showPanel : function(panel){
33166 if(panel = this.getPanel(panel)){
33167 this.setActivePanel(panel);
33173 * Get the active panel for this region.
33174 * @return {Roo.ContentPanel} The active panel or null
33176 getActivePanel : function(){
33177 return this.activePanel;
33181 * Add the passed ContentPanel(s)
33182 * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
33183 * @return {Roo.ContentPanel} The panel added (if only one was added)
33185 add : function(panel){
33186 if(arguments.length > 1){
33187 for(var i = 0, len = arguments.length; i < len; i++) {
33188 this.add(arguments[i]);
33192 if(this.hasPanel(panel)){
33193 this.showPanel(panel);
33196 var el = panel.getEl();
33197 if(el.dom.parentNode != this.mgr.el.dom){
33198 this.mgr.el.dom.appendChild(el.dom);
33200 if(panel.setRegion){
33201 panel.setRegion(this);
33203 this.panels.add(panel);
33204 el.setStyle("position", "absolute");
33205 if(!panel.background){
33206 this.setActivePanel(panel);
33207 if(this.config.initialSize && this.panels.getCount()==1){
33208 this.resizeTo(this.config.initialSize);
33211 this.fireEvent("paneladded", this, panel);
33216 * Returns true if the panel is in this region.
33217 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
33218 * @return {Boolean}
33220 hasPanel : function(panel){
33221 if(typeof panel == "object"){ // must be panel obj
33222 panel = panel.getId();
33224 return this.getPanel(panel) ? true : false;
33228 * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
33229 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
33230 * @param {Boolean} preservePanel Overrides the config preservePanel option
33231 * @return {Roo.ContentPanel} The panel that was removed
33233 remove : function(panel, preservePanel){
33234 panel = this.getPanel(panel);
33239 this.fireEvent("beforeremove", this, panel, e);
33240 if(e.cancel === true){
33243 var panelId = panel.getId();
33244 this.panels.removeKey(panelId);
33249 * Returns the panel specified or null if it's not in this region.
33250 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
33251 * @return {Roo.ContentPanel}
33253 getPanel : function(id){
33254 if(typeof id == "object"){ // must be panel obj
33257 return this.panels.get(id);
33261 * Returns this regions position (north/south/east/west/center).
33264 getPosition: function(){
33265 return this.position;
33269 * Ext JS Library 1.1.1
33270 * Copyright(c) 2006-2007, Ext JS, LLC.
33272 * Originally Released Under LGPL - original licence link has changed is not relivant.
33275 * <script type="text/javascript">
33279 * @class Roo.LayoutRegion
33280 * @extends Roo.BasicLayoutRegion
33281 * This class represents a region in a layout manager.
33282 * @cfg {Boolean} collapsible False to disable collapsing (defaults to true)
33283 * @cfg {Boolean} collapsed True to set the initial display to collapsed (defaults to false)
33284 * @cfg {Boolean} floatable False to disable floating (defaults to true)
33285 * @cfg {Object} margins Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
33286 * @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})
33287 * @cfg {String} tabPosition "top" or "bottom" (defaults to "bottom")
33288 * @cfg {String} collapsedTitle Optional string message to display in the collapsed block of a north or south region
33289 * @cfg {Boolean} alwaysShowTabs True to always display tabs even when there is only 1 panel (defaults to false)
33290 * @cfg {Boolean} autoScroll True to enable overflow scrolling (defaults to false)
33291 * @cfg {Boolean} titlebar True to display a title bar (defaults to true)
33292 * @cfg {String} title The title for the region (overrides panel titles)
33293 * @cfg {Boolean} animate True to animate expand/collapse (defaults to false)
33294 * @cfg {Boolean} autoHide False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
33295 * @cfg {Boolean} preservePanels True to preserve removed panels so they can be readded later (defaults to false)
33296 * @cfg {Boolean} closeOnTab True to place the close icon on the tabs instead of the region titlebar (defaults to false)
33297 * @cfg {Boolean} hideTabs True to hide the tab strip (defaults to false)
33298 * @cfg {Boolean} resizeTabs True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
33299 * the space available, similar to FireFox 1.5 tabs (defaults to false)
33300 * @cfg {Number} minTabWidth The minimum tab width (defaults to 40)
33301 * @cfg {Number} preferredTabWidth The preferred tab width (defaults to 150)
33302 * @cfg {Boolean} showPin True to show a pin button
33303 * @cfg {Boolean} hidden True to start the region hidden (defaults to false)
33304 * @cfg {Boolean} hideWhenEmpty True to hide the region when it has no panels
33305 * @cfg {Boolean} disableTabTips True to disable tab tooltips
33306 * @cfg {Number} width For East/West panels
33307 * @cfg {Number} height For North/South panels
33308 * @cfg {Boolean} split To show the splitter
33309 * @cfg {Boolean} toolbar xtype configuration for a toolbar - shows on right of tabbar
33311 Roo.LayoutRegion = function(mgr, config, pos){
33312 Roo.LayoutRegion.superclass.constructor.call(this, mgr, config, pos, true);
33313 var dh = Roo.DomHelper;
33314 /** This region's container element
33315 * @type Roo.Element */
33316 this.el = dh.append(mgr.el.dom, {tag: "div", cls: "x-layout-panel x-layout-panel-" + this.position}, true);
33317 /** This region's title element
33318 * @type Roo.Element */
33320 this.titleEl = dh.append(this.el.dom, {tag: "div", unselectable: "on", cls: "x-unselectable x-layout-panel-hd x-layout-title-"+this.position, children:[
33321 {tag: "span", cls: "x-unselectable x-layout-panel-hd-text", unselectable: "on", html: " "},
33322 {tag: "div", cls: "x-unselectable x-layout-panel-hd-tools", unselectable: "on"}
33324 this.titleEl.enableDisplayMode();
33325 /** This region's title text element
33326 * @type HTMLElement */
33327 this.titleTextEl = this.titleEl.dom.firstChild;
33328 this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
33329 this.closeBtn = this.createTool(this.tools.dom, "x-layout-close");
33330 this.closeBtn.enableDisplayMode();
33331 this.closeBtn.on("click", this.closeClicked, this);
33332 this.closeBtn.hide();
33334 this.createBody(config);
33335 this.visible = true;
33336 this.collapsed = false;
33338 if(config.hideWhenEmpty){
33340 this.on("paneladded", this.validateVisibility, this);
33341 this.on("panelremoved", this.validateVisibility, this);
33343 this.applyConfig(config);
33346 Roo.extend(Roo.LayoutRegion, Roo.BasicLayoutRegion, {
33348 createBody : function(){
33349 /** This region's body element
33350 * @type Roo.Element */
33351 this.bodyEl = this.el.createChild({tag: "div", cls: "x-layout-panel-body"});
33354 applyConfig : function(c){
33355 if(c.collapsible && this.position != "center" && !this.collapsedEl){
33356 var dh = Roo.DomHelper;
33357 if(c.titlebar !== false){
33358 this.collapseBtn = this.createTool(this.tools.dom, "x-layout-collapse-"+this.position);
33359 this.collapseBtn.on("click", this.collapse, this);
33360 this.collapseBtn.enableDisplayMode();
33362 if(c.showPin === true || this.showPin){
33363 this.stickBtn = this.createTool(this.tools.dom, "x-layout-stick");
33364 this.stickBtn.enableDisplayMode();
33365 this.stickBtn.on("click", this.expand, this);
33366 this.stickBtn.hide();
33369 /** This region's collapsed element
33370 * @type Roo.Element */
33371 this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
33372 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
33374 if(c.floatable !== false){
33375 this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
33376 this.collapsedEl.on("click", this.collapseClick, this);
33379 if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
33380 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
33381 id: "message", unselectable: "on", style:{"float":"left"}});
33382 this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
33384 this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
33385 this.expandBtn.on("click", this.expand, this);
33387 if(this.collapseBtn){
33388 this.collapseBtn.setVisible(c.collapsible == true);
33390 this.cmargins = c.cmargins || this.cmargins ||
33391 (this.position == "west" || this.position == "east" ?
33392 {top: 0, left: 2, right:2, bottom: 0} :
33393 {top: 2, left: 0, right:0, bottom: 2});
33394 this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
33395 this.bottomTabs = c.tabPosition != "top";
33396 this.autoScroll = c.autoScroll || false;
33397 if(this.autoScroll){
33398 this.bodyEl.setStyle("overflow", "auto");
33400 this.bodyEl.setStyle("overflow", "hidden");
33402 //if(c.titlebar !== false){
33403 if((!c.titlebar && !c.title) || c.titlebar === false){
33404 this.titleEl.hide();
33406 this.titleEl.show();
33408 this.titleTextEl.innerHTML = c.title;
33412 this.duration = c.duration || .30;
33413 this.slideDuration = c.slideDuration || .45;
33416 this.collapse(true);
33423 * Returns true if this region is currently visible.
33424 * @return {Boolean}
33426 isVisible : function(){
33427 return this.visible;
33431 * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
33432 * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&#160;")
33434 setCollapsedTitle : function(title){
33435 title = title || " ";
33436 if(this.collapsedTitleTextEl){
33437 this.collapsedTitleTextEl.innerHTML = title;
33441 getBox : function(){
33443 if(!this.collapsed){
33444 b = this.el.getBox(false, true);
33446 b = this.collapsedEl.getBox(false, true);
33451 getMargins : function(){
33452 return this.collapsed ? this.cmargins : this.margins;
33455 highlight : function(){
33456 this.el.addClass("x-layout-panel-dragover");
33459 unhighlight : function(){
33460 this.el.removeClass("x-layout-panel-dragover");
33463 updateBox : function(box){
33465 if(!this.collapsed){
33466 this.el.dom.style.left = box.x + "px";
33467 this.el.dom.style.top = box.y + "px";
33468 this.updateBody(box.width, box.height);
33470 this.collapsedEl.dom.style.left = box.x + "px";
33471 this.collapsedEl.dom.style.top = box.y + "px";
33472 this.collapsedEl.setSize(box.width, box.height);
33475 this.tabs.autoSizeTabs();
33479 updateBody : function(w, h){
33481 this.el.setWidth(w);
33482 w -= this.el.getBorderWidth("rl");
33483 if(this.config.adjustments){
33484 w += this.config.adjustments[0];
33488 this.el.setHeight(h);
33489 h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
33490 h -= this.el.getBorderWidth("tb");
33491 if(this.config.adjustments){
33492 h += this.config.adjustments[1];
33494 this.bodyEl.setHeight(h);
33496 h = this.tabs.syncHeight(h);
33499 if(this.panelSize){
33500 w = w !== null ? w : this.panelSize.width;
33501 h = h !== null ? h : this.panelSize.height;
33503 if(this.activePanel){
33504 var el = this.activePanel.getEl();
33505 w = w !== null ? w : el.getWidth();
33506 h = h !== null ? h : el.getHeight();
33507 this.panelSize = {width: w, height: h};
33508 this.activePanel.setSize(w, h);
33510 if(Roo.isIE && this.tabs){
33511 this.tabs.el.repaint();
33516 * Returns the container element for this region.
33517 * @return {Roo.Element}
33519 getEl : function(){
33524 * Hides this region.
33527 if(!this.collapsed){
33528 this.el.dom.style.left = "-2000px";
33531 this.collapsedEl.dom.style.left = "-2000px";
33532 this.collapsedEl.hide();
33534 this.visible = false;
33535 this.fireEvent("visibilitychange", this, false);
33539 * Shows this region if it was previously hidden.
33542 if(!this.collapsed){
33545 this.collapsedEl.show();
33547 this.visible = true;
33548 this.fireEvent("visibilitychange", this, true);
33551 closeClicked : function(){
33552 if(this.activePanel){
33553 this.remove(this.activePanel);
33557 collapseClick : function(e){
33559 e.stopPropagation();
33562 e.stopPropagation();
33568 * Collapses this region.
33569 * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
33571 collapse : function(skipAnim){
33572 if(this.collapsed) return;
33573 this.collapsed = true;
33575 this.split.el.hide();
33577 if(this.config.animate && skipAnim !== true){
33578 this.fireEvent("invalidated", this);
33579 this.animateCollapse();
33581 this.el.setLocation(-20000,-20000);
33583 this.collapsedEl.show();
33584 this.fireEvent("collapsed", this);
33585 this.fireEvent("invalidated", this);
33589 animateCollapse : function(){
33594 * Expands this region if it was previously collapsed.
33595 * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
33596 * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
33598 expand : function(e, skipAnim){
33599 if(e) e.stopPropagation();
33600 if(!this.collapsed || this.el.hasActiveFx()) return;
33602 this.afterSlideIn();
33605 this.collapsed = false;
33606 if(this.config.animate && skipAnim !== true){
33607 this.animateExpand();
33611 this.split.el.show();
33613 this.collapsedEl.setLocation(-2000,-2000);
33614 this.collapsedEl.hide();
33615 this.fireEvent("invalidated", this);
33616 this.fireEvent("expanded", this);
33620 animateExpand : function(){
33624 initTabs : function()
33626 this.bodyEl.setStyle("overflow", "hidden");
33627 var ts = new Roo.TabPanel(
33630 tabPosition: this.bottomTabs ? 'bottom' : 'top',
33631 disableTooltips: this.config.disableTabTips,
33632 toolbar : this.config.toolbar
33635 if(this.config.hideTabs){
33636 ts.stripWrap.setDisplayed(false);
33639 ts.resizeTabs = this.config.resizeTabs === true;
33640 ts.minTabWidth = this.config.minTabWidth || 40;
33641 ts.maxTabWidth = this.config.maxTabWidth || 250;
33642 ts.preferredTabWidth = this.config.preferredTabWidth || 150;
33643 ts.monitorResize = false;
33644 ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
33645 ts.bodyEl.addClass('x-layout-tabs-body');
33646 this.panels.each(this.initPanelAsTab, this);
33649 initPanelAsTab : function(panel){
33650 var ti = this.tabs.addTab(panel.getEl().id, panel.getTitle(), null,
33651 this.config.closeOnTab && panel.isClosable());
33652 if(panel.tabTip !== undefined){
33653 ti.setTooltip(panel.tabTip);
33655 ti.on("activate", function(){
33656 this.setActivePanel(panel);
33658 if(this.config.closeOnTab){
33659 ti.on("beforeclose", function(t, e){
33661 this.remove(panel);
33667 updatePanelTitle : function(panel, title){
33668 if(this.activePanel == panel){
33669 this.updateTitle(title);
33672 var ti = this.tabs.getTab(panel.getEl().id);
33674 if(panel.tabTip !== undefined){
33675 ti.setTooltip(panel.tabTip);
33680 updateTitle : function(title){
33681 if(this.titleTextEl && !this.config.title){
33682 this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : " ");
33686 setActivePanel : function(panel){
33687 panel = this.getPanel(panel);
33688 if(this.activePanel && this.activePanel != panel){
33689 this.activePanel.setActiveState(false);
33691 this.activePanel = panel;
33692 panel.setActiveState(true);
33693 if(this.panelSize){
33694 panel.setSize(this.panelSize.width, this.panelSize.height);
33697 this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
33699 this.updateTitle(panel.getTitle());
33701 this.fireEvent("invalidated", this);
33703 this.fireEvent("panelactivated", this, panel);
33707 * Shows the specified panel.
33708 * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
33709 * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
33711 showPanel : function(panel){
33712 if(panel = this.getPanel(panel)){
33714 var tab = this.tabs.getTab(panel.getEl().id);
33715 if(tab.isHidden()){
33716 this.tabs.unhideTab(tab.id);
33720 this.setActivePanel(panel);
33727 * Get the active panel for this region.
33728 * @return {Roo.ContentPanel} The active panel or null
33730 getActivePanel : function(){
33731 return this.activePanel;
33734 validateVisibility : function(){
33735 if(this.panels.getCount() < 1){
33736 this.updateTitle(" ");
33737 this.closeBtn.hide();
33740 if(!this.isVisible()){
33747 * Adds the passed ContentPanel(s) to this region.
33748 * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
33749 * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
33751 add : function(panel){
33752 if(arguments.length > 1){
33753 for(var i = 0, len = arguments.length; i < len; i++) {
33754 this.add(arguments[i]);
33758 if(this.hasPanel(panel)){
33759 this.showPanel(panel);
33762 panel.setRegion(this);
33763 this.panels.add(panel);
33764 if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
33765 this.bodyEl.dom.appendChild(panel.getEl().dom);
33766 if(panel.background !== true){
33767 this.setActivePanel(panel);
33769 this.fireEvent("paneladded", this, panel);
33775 this.initPanelAsTab(panel);
33777 if(panel.background !== true){
33778 this.tabs.activate(panel.getEl().id);
33780 this.fireEvent("paneladded", this, panel);
33785 * Hides the tab for the specified panel.
33786 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
33788 hidePanel : function(panel){
33789 if(this.tabs && (panel = this.getPanel(panel))){
33790 this.tabs.hideTab(panel.getEl().id);
33795 * Unhides the tab for a previously hidden panel.
33796 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
33798 unhidePanel : function(panel){
33799 if(this.tabs && (panel = this.getPanel(panel))){
33800 this.tabs.unhideTab(panel.getEl().id);
33804 clearPanels : function(){
33805 while(this.panels.getCount() > 0){
33806 this.remove(this.panels.first());
33811 * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
33812 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
33813 * @param {Boolean} preservePanel Overrides the config preservePanel option
33814 * @return {Roo.ContentPanel} The panel that was removed
33816 remove : function(panel, preservePanel){
33817 panel = this.getPanel(panel);
33822 this.fireEvent("beforeremove", this, panel, e);
33823 if(e.cancel === true){
33826 preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
33827 var panelId = panel.getId();
33828 this.panels.removeKey(panelId);
33830 document.body.appendChild(panel.getEl().dom);
33833 this.tabs.removeTab(panel.getEl().id);
33834 }else if (!preservePanel){
33835 this.bodyEl.dom.removeChild(panel.getEl().dom);
33837 if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
33838 var p = this.panels.first();
33839 var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
33840 tempEl.appendChild(p.getEl().dom);
33841 this.bodyEl.update("");
33842 this.bodyEl.dom.appendChild(p.getEl().dom);
33844 this.updateTitle(p.getTitle());
33846 this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
33847 this.setActivePanel(p);
33849 panel.setRegion(null);
33850 if(this.activePanel == panel){
33851 this.activePanel = null;
33853 if(this.config.autoDestroy !== false && preservePanel !== true){
33854 try{panel.destroy();}catch(e){}
33856 this.fireEvent("panelremoved", this, panel);
33861 * Returns the TabPanel component used by this region
33862 * @return {Roo.TabPanel}
33864 getTabs : function(){
33868 createTool : function(parentEl, className){
33869 var btn = Roo.DomHelper.append(parentEl, {tag: "div", cls: "x-layout-tools-button",
33870 children: [{tag: "div", cls: "x-layout-tools-button-inner " + className, html: " "}]}, true);
33871 btn.addClassOnOver("x-layout-tools-button-over");
33876 * Ext JS Library 1.1.1
33877 * Copyright(c) 2006-2007, Ext JS, LLC.
33879 * Originally Released Under LGPL - original licence link has changed is not relivant.
33882 * <script type="text/javascript">
33888 * @class Roo.SplitLayoutRegion
33889 * @extends Roo.LayoutRegion
33890 * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
33892 Roo.SplitLayoutRegion = function(mgr, config, pos, cursor){
33893 this.cursor = cursor;
33894 Roo.SplitLayoutRegion.superclass.constructor.call(this, mgr, config, pos);
33897 Roo.extend(Roo.SplitLayoutRegion, Roo.LayoutRegion, {
33898 splitTip : "Drag to resize.",
33899 collapsibleSplitTip : "Drag to resize. Double click to hide.",
33900 useSplitTips : false,
33902 applyConfig : function(config){
33903 Roo.SplitLayoutRegion.superclass.applyConfig.call(this, config);
33906 var splitEl = Roo.DomHelper.append(this.mgr.el.dom,
33907 {tag: "div", id: this.el.id + "-split", cls: "x-layout-split x-layout-split-"+this.position, html: " "});
33908 /** The SplitBar for this region
33909 * @type Roo.SplitBar */
33910 this.split = new Roo.SplitBar(splitEl, this.el, this.orientation);
33911 this.split.on("moved", this.onSplitMove, this);
33912 this.split.useShim = config.useShim === true;
33913 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
33914 if(this.useSplitTips){
33915 this.split.el.dom.title = config.collapsible ? this.collapsibleSplitTip : this.splitTip;
33917 if(config.collapsible){
33918 this.split.el.on("dblclick", this.collapse, this);
33921 if(typeof config.minSize != "undefined"){
33922 this.split.minSize = config.minSize;
33924 if(typeof config.maxSize != "undefined"){
33925 this.split.maxSize = config.maxSize;
33927 if(config.hideWhenEmpty || config.hidden || config.collapsed){
33928 this.hideSplitter();
33933 getHMaxSize : function(){
33934 var cmax = this.config.maxSize || 10000;
33935 var center = this.mgr.getRegion("center");
33936 return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
33939 getVMaxSize : function(){
33940 var cmax = this.config.maxSize || 10000;
33941 var center = this.mgr.getRegion("center");
33942 return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
33945 onSplitMove : function(split, newSize){
33946 this.fireEvent("resized", this, newSize);
33950 * Returns the {@link Roo.SplitBar} for this region.
33951 * @return {Roo.SplitBar}
33953 getSplitBar : function(){
33958 this.hideSplitter();
33959 Roo.SplitLayoutRegion.superclass.hide.call(this);
33962 hideSplitter : function(){
33964 this.split.el.setLocation(-2000,-2000);
33965 this.split.el.hide();
33971 this.split.el.show();
33973 Roo.SplitLayoutRegion.superclass.show.call(this);
33976 beforeSlide: function(){
33977 if(Roo.isGecko){// firefox overflow auto bug workaround
33978 this.bodyEl.clip();
33979 if(this.tabs) this.tabs.bodyEl.clip();
33980 if(this.activePanel){
33981 this.activePanel.getEl().clip();
33983 if(this.activePanel.beforeSlide){
33984 this.activePanel.beforeSlide();
33990 afterSlide : function(){
33991 if(Roo.isGecko){// firefox overflow auto bug workaround
33992 this.bodyEl.unclip();
33993 if(this.tabs) this.tabs.bodyEl.unclip();
33994 if(this.activePanel){
33995 this.activePanel.getEl().unclip();
33996 if(this.activePanel.afterSlide){
33997 this.activePanel.afterSlide();
34003 initAutoHide : function(){
34004 if(this.autoHide !== false){
34005 if(!this.autoHideHd){
34006 var st = new Roo.util.DelayedTask(this.slideIn, this);
34007 this.autoHideHd = {
34008 "mouseout": function(e){
34009 if(!e.within(this.el, true)){
34013 "mouseover" : function(e){
34019 this.el.on(this.autoHideHd);
34023 clearAutoHide : function(){
34024 if(this.autoHide !== false){
34025 this.el.un("mouseout", this.autoHideHd.mouseout);
34026 this.el.un("mouseover", this.autoHideHd.mouseover);
34030 clearMonitor : function(){
34031 Roo.get(document).un("click", this.slideInIf, this);
34034 // these names are backwards but not changed for compat
34035 slideOut : function(){
34036 if(this.isSlid || this.el.hasActiveFx()){
34039 this.isSlid = true;
34040 if(this.collapseBtn){
34041 this.collapseBtn.hide();
34043 this.closeBtnState = this.closeBtn.getStyle('display');
34044 this.closeBtn.hide();
34046 this.stickBtn.show();
34049 this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
34050 this.beforeSlide();
34051 this.el.setStyle("z-index", 10001);
34052 this.el.slideIn(this.getSlideAnchor(), {
34053 callback: function(){
34055 this.initAutoHide();
34056 Roo.get(document).on("click", this.slideInIf, this);
34057 this.fireEvent("slideshow", this);
34064 afterSlideIn : function(){
34065 this.clearAutoHide();
34066 this.isSlid = false;
34067 this.clearMonitor();
34068 this.el.setStyle("z-index", "");
34069 if(this.collapseBtn){
34070 this.collapseBtn.show();
34072 this.closeBtn.setStyle('display', this.closeBtnState);
34074 this.stickBtn.hide();
34076 this.fireEvent("slidehide", this);
34079 slideIn : function(cb){
34080 if(!this.isSlid || this.el.hasActiveFx()){
34084 this.isSlid = false;
34085 this.beforeSlide();
34086 this.el.slideOut(this.getSlideAnchor(), {
34087 callback: function(){
34088 this.el.setLeftTop(-10000, -10000);
34090 this.afterSlideIn();
34098 slideInIf : function(e){
34099 if(!e.within(this.el)){
34104 animateCollapse : function(){
34105 this.beforeSlide();
34106 this.el.setStyle("z-index", 20000);
34107 var anchor = this.getSlideAnchor();
34108 this.el.slideOut(anchor, {
34109 callback : function(){
34110 this.el.setStyle("z-index", "");
34111 this.collapsedEl.slideIn(anchor, {duration:.3});
34113 this.el.setLocation(-10000,-10000);
34115 this.fireEvent("collapsed", this);
34122 animateExpand : function(){
34123 this.beforeSlide();
34124 this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
34125 this.el.setStyle("z-index", 20000);
34126 this.collapsedEl.hide({
34129 this.el.slideIn(this.getSlideAnchor(), {
34130 callback : function(){
34131 this.el.setStyle("z-index", "");
34134 this.split.el.show();
34136 this.fireEvent("invalidated", this);
34137 this.fireEvent("expanded", this);
34165 getAnchor : function(){
34166 return this.anchors[this.position];
34169 getCollapseAnchor : function(){
34170 return this.canchors[this.position];
34173 getSlideAnchor : function(){
34174 return this.sanchors[this.position];
34177 getAlignAdj : function(){
34178 var cm = this.cmargins;
34179 switch(this.position){
34195 getExpandAdj : function(){
34196 var c = this.collapsedEl, cm = this.cmargins;
34197 switch(this.position){
34199 return [-(cm.right+c.getWidth()+cm.left), 0];
34202 return [cm.right+c.getWidth()+cm.left, 0];
34205 return [0, -(cm.top+cm.bottom+c.getHeight())];
34208 return [0, cm.top+cm.bottom+c.getHeight()];
34214 * Ext JS Library 1.1.1
34215 * Copyright(c) 2006-2007, Ext JS, LLC.
34217 * Originally Released Under LGPL - original licence link has changed is not relivant.
34220 * <script type="text/javascript">
34223 * These classes are private internal classes
34225 Roo.CenterLayoutRegion = function(mgr, config){
34226 Roo.LayoutRegion.call(this, mgr, config, "center");
34227 this.visible = true;
34228 this.minWidth = config.minWidth || 20;
34229 this.minHeight = config.minHeight || 20;
34232 Roo.extend(Roo.CenterLayoutRegion, Roo.LayoutRegion, {
34234 // center panel can't be hidden
34238 // center panel can't be hidden
34241 getMinWidth: function(){
34242 return this.minWidth;
34245 getMinHeight: function(){
34246 return this.minHeight;
34251 Roo.NorthLayoutRegion = function(mgr, config){
34252 Roo.LayoutRegion.call(this, mgr, config, "north", "n-resize");
34254 this.split.placement = Roo.SplitBar.TOP;
34255 this.split.orientation = Roo.SplitBar.VERTICAL;
34256 this.split.el.addClass("x-layout-split-v");
34258 var size = config.initialSize || config.height;
34259 if(typeof size != "undefined"){
34260 this.el.setHeight(size);
34263 Roo.extend(Roo.NorthLayoutRegion, Roo.SplitLayoutRegion, {
34264 orientation: Roo.SplitBar.VERTICAL,
34265 getBox : function(){
34266 if(this.collapsed){
34267 return this.collapsedEl.getBox();
34269 var box = this.el.getBox();
34271 box.height += this.split.el.getHeight();
34276 updateBox : function(box){
34277 if(this.split && !this.collapsed){
34278 box.height -= this.split.el.getHeight();
34279 this.split.el.setLeft(box.x);
34280 this.split.el.setTop(box.y+box.height);
34281 this.split.el.setWidth(box.width);
34283 if(this.collapsed){
34284 this.updateBody(box.width, null);
34286 Roo.LayoutRegion.prototype.updateBox.call(this, box);
34290 Roo.SouthLayoutRegion = function(mgr, config){
34291 Roo.SplitLayoutRegion.call(this, mgr, config, "south", "s-resize");
34293 this.split.placement = Roo.SplitBar.BOTTOM;
34294 this.split.orientation = Roo.SplitBar.VERTICAL;
34295 this.split.el.addClass("x-layout-split-v");
34297 var size = config.initialSize || config.height;
34298 if(typeof size != "undefined"){
34299 this.el.setHeight(size);
34302 Roo.extend(Roo.SouthLayoutRegion, Roo.SplitLayoutRegion, {
34303 orientation: Roo.SplitBar.VERTICAL,
34304 getBox : function(){
34305 if(this.collapsed){
34306 return this.collapsedEl.getBox();
34308 var box = this.el.getBox();
34310 var sh = this.split.el.getHeight();
34317 updateBox : function(box){
34318 if(this.split && !this.collapsed){
34319 var sh = this.split.el.getHeight();
34322 this.split.el.setLeft(box.x);
34323 this.split.el.setTop(box.y-sh);
34324 this.split.el.setWidth(box.width);
34326 if(this.collapsed){
34327 this.updateBody(box.width, null);
34329 Roo.LayoutRegion.prototype.updateBox.call(this, box);
34333 Roo.EastLayoutRegion = function(mgr, config){
34334 Roo.SplitLayoutRegion.call(this, mgr, config, "east", "e-resize");
34336 this.split.placement = Roo.SplitBar.RIGHT;
34337 this.split.orientation = Roo.SplitBar.HORIZONTAL;
34338 this.split.el.addClass("x-layout-split-h");
34340 var size = config.initialSize || config.width;
34341 if(typeof size != "undefined"){
34342 this.el.setWidth(size);
34345 Roo.extend(Roo.EastLayoutRegion, Roo.SplitLayoutRegion, {
34346 orientation: Roo.SplitBar.HORIZONTAL,
34347 getBox : function(){
34348 if(this.collapsed){
34349 return this.collapsedEl.getBox();
34351 var box = this.el.getBox();
34353 var sw = this.split.el.getWidth();
34360 updateBox : function(box){
34361 if(this.split && !this.collapsed){
34362 var sw = this.split.el.getWidth();
34364 this.split.el.setLeft(box.x);
34365 this.split.el.setTop(box.y);
34366 this.split.el.setHeight(box.height);
34369 if(this.collapsed){
34370 this.updateBody(null, box.height);
34372 Roo.LayoutRegion.prototype.updateBox.call(this, box);
34376 Roo.WestLayoutRegion = function(mgr, config){
34377 Roo.SplitLayoutRegion.call(this, mgr, config, "west", "w-resize");
34379 this.split.placement = Roo.SplitBar.LEFT;
34380 this.split.orientation = Roo.SplitBar.HORIZONTAL;
34381 this.split.el.addClass("x-layout-split-h");
34383 var size = config.initialSize || config.width;
34384 if(typeof size != "undefined"){
34385 this.el.setWidth(size);
34388 Roo.extend(Roo.WestLayoutRegion, Roo.SplitLayoutRegion, {
34389 orientation: Roo.SplitBar.HORIZONTAL,
34390 getBox : function(){
34391 if(this.collapsed){
34392 return this.collapsedEl.getBox();
34394 var box = this.el.getBox();
34396 box.width += this.split.el.getWidth();
34401 updateBox : function(box){
34402 if(this.split && !this.collapsed){
34403 var sw = this.split.el.getWidth();
34405 this.split.el.setLeft(box.x+box.width);
34406 this.split.el.setTop(box.y);
34407 this.split.el.setHeight(box.height);
34409 if(this.collapsed){
34410 this.updateBody(null, box.height);
34412 Roo.LayoutRegion.prototype.updateBox.call(this, box);
34417 * Ext JS Library 1.1.1
34418 * Copyright(c) 2006-2007, Ext JS, LLC.
34420 * Originally Released Under LGPL - original licence link has changed is not relivant.
34423 * <script type="text/javascript">
34428 * Private internal class for reading and applying state
34430 Roo.LayoutStateManager = function(layout){
34431 // default empty state
34440 Roo.LayoutStateManager.prototype = {
34441 init : function(layout, provider){
34442 this.provider = provider;
34443 var state = provider.get(layout.id+"-layout-state");
34445 var wasUpdating = layout.isUpdating();
34447 layout.beginUpdate();
34449 for(var key in state){
34450 if(typeof state[key] != "function"){
34451 var rstate = state[key];
34452 var r = layout.getRegion(key);
34455 r.resizeTo(rstate.size);
34457 if(rstate.collapsed == true){
34460 r.expand(null, true);
34466 layout.endUpdate();
34468 this.state = state;
34470 this.layout = layout;
34471 layout.on("regionresized", this.onRegionResized, this);
34472 layout.on("regioncollapsed", this.onRegionCollapsed, this);
34473 layout.on("regionexpanded", this.onRegionExpanded, this);
34476 storeState : function(){
34477 this.provider.set(this.layout.id+"-layout-state", this.state);
34480 onRegionResized : function(region, newSize){
34481 this.state[region.getPosition()].size = newSize;
34485 onRegionCollapsed : function(region){
34486 this.state[region.getPosition()].collapsed = true;
34490 onRegionExpanded : function(region){
34491 this.state[region.getPosition()].collapsed = false;
34496 * Ext JS Library 1.1.1
34497 * Copyright(c) 2006-2007, Ext JS, LLC.
34499 * Originally Released Under LGPL - original licence link has changed is not relivant.
34502 * <script type="text/javascript">
34505 * @class Roo.ContentPanel
34506 * @extends Roo.util.Observable
34507 * A basic ContentPanel element.
34508 * @cfg {Boolean} fitToFrame True for this panel to adjust its size to fit when the region resizes (defaults to false)
34509 * @cfg {Boolean} fitContainer When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container (defaults to false)
34510 * @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
34511 * @cfg {Boolean} closable True if the panel can be closed/removed
34512 * @cfg {Boolean} background True if the panel should not be activated when it is added (defaults to false)
34513 * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
34514 * @cfg {Toolbar} toolbar A toolbar for this panel
34515 * @cfg {Boolean} autoScroll True to scroll overflow in this panel (use with {@link #fitToFrame})
34516 * @cfg {String} title The title for this panel
34517 * @cfg {Array} adjustments Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
34518 * @cfg {String} url Calls {@link #setUrl} with this value
34519 * @cfg {String} region (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
34520 * @cfg {String/Object} params When used with {@link #url}, calls {@link #setUrl} with this value
34521 * @cfg {Boolean} loadOnce When used with {@link #url}, calls {@link #setUrl} with this value
34522 * @cfg {String} content Raw content to fill content panel with (uses setContent on construction.)
34525 * Create a new ContentPanel.
34526 * @param {String/HTMLElement/Roo.Element} el The container element for this panel
34527 * @param {String/Object} config A string to set only the title or a config object
34528 * @param {String} content (optional) Set the HTML content for this panel
34529 * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
34531 Roo.ContentPanel = function(el, config, content){
34535 if(el.autoCreate || el.xtype){ // xtype is available if this is called from factory
34539 if (config && config.parentLayout) {
34540 el = config.parentLayout.el.createChild();
34543 if(el.autoCreate){ // xtype is available if this is called from factory
34547 this.el = Roo.get(el);
34548 if(!this.el && config && config.autoCreate){
34549 if(typeof config.autoCreate == "object"){
34550 if(!config.autoCreate.id){
34551 config.autoCreate.id = config.id||el;
34553 this.el = Roo.DomHelper.append(document.body,
34554 config.autoCreate, true);
34556 this.el = Roo.DomHelper.append(document.body,
34557 {tag: "div", cls: "x-layout-inactive-content", id: config.id||el}, true);
34560 this.closable = false;
34561 this.loaded = false;
34562 this.active = false;
34563 if(typeof config == "string"){
34564 this.title = config;
34566 Roo.apply(this, config);
34569 if (this.toolbar && !this.toolbar.el && this.toolbar.xtype) {
34570 this.wrapEl = this.el.wrap();
34571 this.toolbar.container = this.el.insertSibling(false, 'before');
34572 this.toolbar = new Roo.Toolbar(this.toolbar);
34575 // xtype created footer. - not sure if will work as we normally have to render first..
34576 if (this.footer && !this.footer.el && this.footer.xtype) {
34577 if (!this.wrapEl) {
34578 this.wrapEl = this.el.wrap();
34581 this.footer.container = this.wrapEl.createChild();
34583 this.footer = Roo.factory(this.footer, Roo);
34588 this.resizeEl = Roo.get(this.resizeEl, true);
34590 this.resizeEl = this.el;
34592 // handle view.xtype
34600 * Fires when this panel is activated.
34601 * @param {Roo.ContentPanel} this
34605 * @event deactivate
34606 * Fires when this panel is activated.
34607 * @param {Roo.ContentPanel} this
34609 "deactivate" : true,
34613 * Fires when this panel is resized if fitToFrame is true.
34614 * @param {Roo.ContentPanel} this
34615 * @param {Number} width The width after any component adjustments
34616 * @param {Number} height The height after any component adjustments
34622 * Fires when this tab is created
34623 * @param {Roo.ContentPanel} this
34634 if(this.autoScroll){
34635 this.resizeEl.setStyle("overflow", "auto");
34637 // fix randome scrolling
34638 this.el.on('scroll', function() {
34639 Roo.log('fix random scolling');
34640 this.scrollTo('top',0);
34643 content = content || this.content;
34645 this.setContent(content);
34647 if(config && config.url){
34648 this.setUrl(this.url, this.params, this.loadOnce);
34653 Roo.ContentPanel.superclass.constructor.call(this);
34655 if (this.view && typeof(this.view.xtype) != 'undefined') {
34656 this.view.el = this.el.appendChild(document.createElement("div"));
34657 this.view = Roo.factory(this.view);
34658 this.view.render && this.view.render(false, '');
34662 this.fireEvent('render', this);
34665 Roo.extend(Roo.ContentPanel, Roo.util.Observable, {
34667 setRegion : function(region){
34668 this.region = region;
34670 this.el.replaceClass("x-layout-inactive-content", "x-layout-active-content");
34672 this.el.replaceClass("x-layout-active-content", "x-layout-inactive-content");
34677 * Returns the toolbar for this Panel if one was configured.
34678 * @return {Roo.Toolbar}
34680 getToolbar : function(){
34681 return this.toolbar;
34684 setActiveState : function(active){
34685 this.active = active;
34687 this.fireEvent("deactivate", this);
34689 this.fireEvent("activate", this);
34693 * Updates this panel's element
34694 * @param {String} content The new content
34695 * @param {Boolean} loadScripts (optional) true to look for and process scripts
34697 setContent : function(content, loadScripts){
34698 this.el.update(content, loadScripts);
34701 ignoreResize : function(w, h){
34702 if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
34705 this.lastSize = {width: w, height: h};
34710 * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
34711 * @return {Roo.UpdateManager} The UpdateManager
34713 getUpdateManager : function(){
34714 return this.el.getUpdateManager();
34717 * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
34718 * @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:
34721 url: "your-url.php",
34722 params: {param1: "foo", param2: "bar"}, // or a URL encoded string
34723 callback: yourFunction,
34724 scope: yourObject, //(optional scope)
34727 text: "Loading...",
34732 * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
34733 * 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.
34734 * @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}
34735 * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
34736 * @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.
34737 * @return {Roo.ContentPanel} this
34740 var um = this.el.getUpdateManager();
34741 um.update.apply(um, arguments);
34747 * 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.
34748 * @param {String/Function} url The URL to load the content from or a function to call to get the URL
34749 * @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)
34750 * @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)
34751 * @return {Roo.UpdateManager} The UpdateManager
34753 setUrl : function(url, params, loadOnce){
34754 if(this.refreshDelegate){
34755 this.removeListener("activate", this.refreshDelegate);
34757 this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
34758 this.on("activate", this.refreshDelegate);
34759 return this.el.getUpdateManager();
34762 _handleRefresh : function(url, params, loadOnce){
34763 if(!loadOnce || !this.loaded){
34764 var updater = this.el.getUpdateManager();
34765 updater.update(url, params, this._setLoaded.createDelegate(this));
34769 _setLoaded : function(){
34770 this.loaded = true;
34774 * Returns this panel's id
34777 getId : function(){
34782 * Returns this panel's element - used by regiosn to add.
34783 * @return {Roo.Element}
34785 getEl : function(){
34786 return this.wrapEl || this.el;
34789 adjustForComponents : function(width, height)
34791 //Roo.log('adjustForComponents ');
34792 if(this.resizeEl != this.el){
34793 width -= this.el.getFrameWidth('lr');
34794 height -= this.el.getFrameWidth('tb');
34797 var te = this.toolbar.getEl();
34798 height -= te.getHeight();
34799 te.setWidth(width);
34802 var te = this.footer.getEl();
34803 Roo.log("footer:" + te.getHeight());
34805 height -= te.getHeight();
34806 te.setWidth(width);
34810 if(this.adjustments){
34811 width += this.adjustments[0];
34812 height += this.adjustments[1];
34814 return {"width": width, "height": height};
34817 setSize : function(width, height){
34818 if(this.fitToFrame && !this.ignoreResize(width, height)){
34819 if(this.fitContainer && this.resizeEl != this.el){
34820 this.el.setSize(width, height);
34822 var size = this.adjustForComponents(width, height);
34823 this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
34824 this.fireEvent('resize', this, size.width, size.height);
34829 * Returns this panel's title
34832 getTitle : function(){
34837 * Set this panel's title
34838 * @param {String} title
34840 setTitle : function(title){
34841 this.title = title;
34843 this.region.updatePanelTitle(this, title);
34848 * Returns true is this panel was configured to be closable
34849 * @return {Boolean}
34851 isClosable : function(){
34852 return this.closable;
34855 beforeSlide : function(){
34857 this.resizeEl.clip();
34860 afterSlide : function(){
34862 this.resizeEl.unclip();
34866 * Force a content refresh from the URL specified in the {@link #setUrl} method.
34867 * Will fail silently if the {@link #setUrl} method has not been called.
34868 * This does not activate the panel, just updates its content.
34870 refresh : function(){
34871 if(this.refreshDelegate){
34872 this.loaded = false;
34873 this.refreshDelegate();
34878 * Destroys this panel
34880 destroy : function(){
34881 this.el.removeAllListeners();
34882 var tempEl = document.createElement("span");
34883 tempEl.appendChild(this.el.dom);
34884 tempEl.innerHTML = "";
34890 * form - if the content panel contains a form - this is a reference to it.
34891 * @type {Roo.form.Form}
34895 * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
34896 * This contains a reference to it.
34902 * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
34912 * @param {Object} cfg Xtype definition of item to add.
34915 addxtype : function(cfg) {
34917 if (cfg.xtype.match(/^Form$/)) {
34920 //if (this.footer) {
34921 // el = this.footer.container.insertSibling(false, 'before');
34923 el = this.el.createChild();
34926 this.form = new Roo.form.Form(cfg);
34929 if ( this.form.allItems.length) this.form.render(el.dom);
34932 // should only have one of theses..
34933 if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
34934 // views.. should not be just added - used named prop 'view''
34936 cfg.el = this.el.appendChild(document.createElement("div"));
34939 var ret = new Roo.factory(cfg);
34941 ret.render && ret.render(false, ''); // render blank..
34950 * @class Roo.GridPanel
34951 * @extends Roo.ContentPanel
34953 * Create a new GridPanel.
34954 * @param {Roo.grid.Grid} grid The grid for this panel
34955 * @param {String/Object} config A string to set only the panel's title, or a config object
34957 Roo.GridPanel = function(grid, config){
34960 this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
34961 {tag: "div", cls: "x-layout-grid-wrapper x-layout-inactive-content"}, true);
34963 this.wrapper.dom.appendChild(grid.getGridEl().dom);
34965 Roo.GridPanel.superclass.constructor.call(this, this.wrapper, config);
34968 this.toolbar.el.insertBefore(this.wrapper.dom.firstChild);
34970 // xtype created footer. - not sure if will work as we normally have to render first..
34971 if (this.footer && !this.footer.el && this.footer.xtype) {
34973 this.footer.container = this.grid.getView().getFooterPanel(true);
34974 this.footer.dataSource = this.grid.dataSource;
34975 this.footer = Roo.factory(this.footer, Roo);
34979 grid.monitorWindowResize = false; // turn off autosizing
34980 grid.autoHeight = false;
34981 grid.autoWidth = false;
34983 this.grid.getGridEl().replaceClass("x-layout-inactive-content", "x-layout-component-panel");
34986 Roo.extend(Roo.GridPanel, Roo.ContentPanel, {
34987 getId : function(){
34988 return this.grid.id;
34992 * Returns the grid for this panel
34993 * @return {Roo.grid.Grid}
34995 getGrid : function(){
34999 setSize : function(width, height){
35000 if(!this.ignoreResize(width, height)){
35001 var grid = this.grid;
35002 var size = this.adjustForComponents(width, height);
35003 grid.getGridEl().setSize(size.width, size.height);
35008 beforeSlide : function(){
35009 this.grid.getView().scroller.clip();
35012 afterSlide : function(){
35013 this.grid.getView().scroller.unclip();
35016 destroy : function(){
35017 this.grid.destroy();
35019 Roo.GridPanel.superclass.destroy.call(this);
35025 * @class Roo.NestedLayoutPanel
35026 * @extends Roo.ContentPanel
35028 * Create a new NestedLayoutPanel.
35031 * @param {Roo.BorderLayout} layout The layout for this panel
35032 * @param {String/Object} config A string to set only the title or a config object
35034 Roo.NestedLayoutPanel = function(layout, config)
35036 // construct with only one argument..
35037 /* FIXME - implement nicer consturctors
35038 if (layout.layout) {
35040 layout = config.layout;
35041 delete config.layout;
35043 if (layout.xtype && !layout.getEl) {
35044 // then layout needs constructing..
35045 layout = Roo.factory(layout, Roo);
35050 Roo.NestedLayoutPanel.superclass.constructor.call(this, layout.getEl(), config);
35052 layout.monitorWindowResize = false; // turn off autosizing
35053 this.layout = layout;
35054 this.layout.getEl().addClass("x-layout-nested-layout");
35061 Roo.extend(Roo.NestedLayoutPanel, Roo.ContentPanel, {
35063 setSize : function(width, height){
35064 if(!this.ignoreResize(width, height)){
35065 var size = this.adjustForComponents(width, height);
35066 var el = this.layout.getEl();
35067 el.setSize(size.width, size.height);
35068 var touch = el.dom.offsetWidth;
35069 this.layout.layout();
35070 // ie requires a double layout on the first pass
35071 if(Roo.isIE && !this.initialized){
35072 this.initialized = true;
35073 this.layout.layout();
35078 // activate all subpanels if not currently active..
35080 setActiveState : function(active){
35081 this.active = active;
35083 this.fireEvent("deactivate", this);
35087 this.fireEvent("activate", this);
35088 // not sure if this should happen before or after..
35089 if (!this.layout) {
35090 return; // should not happen..
35093 for (var r in this.layout.regions) {
35094 reg = this.layout.getRegion(r);
35095 if (reg.getActivePanel()) {
35096 //reg.showPanel(reg.getActivePanel()); // force it to activate..
35097 reg.setActivePanel(reg.getActivePanel());
35100 if (!reg.panels.length) {
35103 reg.showPanel(reg.getPanel(0));
35112 * Returns the nested BorderLayout for this panel
35113 * @return {Roo.BorderLayout}
35115 getLayout : function(){
35116 return this.layout;
35120 * Adds a xtype elements to the layout of the nested panel
35124 xtype : 'ContentPanel',
35131 xtype : 'NestedLayoutPanel',
35137 items : [ ... list of content panels or nested layout panels.. ]
35141 * @param {Object} cfg Xtype definition of item to add.
35143 addxtype : function(cfg) {
35144 return this.layout.addxtype(cfg);
35149 Roo.ScrollPanel = function(el, config, content){
35150 config = config || {};
35151 config.fitToFrame = true;
35152 Roo.ScrollPanel.superclass.constructor.call(this, el, config, content);
35154 this.el.dom.style.overflow = "hidden";
35155 var wrap = this.el.wrap({cls: "x-scroller x-layout-inactive-content"});
35156 this.el.removeClass("x-layout-inactive-content");
35157 this.el.on("mousewheel", this.onWheel, this);
35159 var up = wrap.createChild({cls: "x-scroller-up", html: " "}, this.el.dom);
35160 var down = wrap.createChild({cls: "x-scroller-down", html: " "});
35161 up.unselectable(); down.unselectable();
35162 up.on("click", this.scrollUp, this);
35163 down.on("click", this.scrollDown, this);
35164 up.addClassOnOver("x-scroller-btn-over");
35165 down.addClassOnOver("x-scroller-btn-over");
35166 up.addClassOnClick("x-scroller-btn-click");
35167 down.addClassOnClick("x-scroller-btn-click");
35168 this.adjustments = [0, -(up.getHeight() + down.getHeight())];
35170 this.resizeEl = this.el;
35171 this.el = wrap; this.up = up; this.down = down;
35174 Roo.extend(Roo.ScrollPanel, Roo.ContentPanel, {
35176 wheelIncrement : 5,
35177 scrollUp : function(){
35178 this.resizeEl.scroll("up", this.increment, {callback: this.afterScroll, scope: this});
35181 scrollDown : function(){
35182 this.resizeEl.scroll("down", this.increment, {callback: this.afterScroll, scope: this});
35185 afterScroll : function(){
35186 var el = this.resizeEl;
35187 var t = el.dom.scrollTop, h = el.dom.scrollHeight, ch = el.dom.clientHeight;
35188 this.up[t == 0 ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
35189 this.down[h - t <= ch ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
35192 setSize : function(){
35193 Roo.ScrollPanel.superclass.setSize.apply(this, arguments);
35194 this.afterScroll();
35197 onWheel : function(e){
35198 var d = e.getWheelDelta();
35199 this.resizeEl.dom.scrollTop -= (d*this.wheelIncrement);
35200 this.afterScroll();
35204 setContent : function(content, loadScripts){
35205 this.resizeEl.update(content, loadScripts);
35219 * @class Roo.TreePanel
35220 * @extends Roo.ContentPanel
35222 * Create a new TreePanel. - defaults to fit/scoll contents.
35223 * @param {String/Object} config A string to set only the panel's title, or a config object
35224 * @cfg {Roo.tree.TreePanel} tree The tree TreePanel, with config etc.
35226 Roo.TreePanel = function(config){
35227 var el = config.el;
35228 var tree = config.tree;
35229 delete config.tree;
35230 delete config.el; // hopefull!
35232 // wrapper for IE7 strict & safari scroll issue
35234 var treeEl = el.createChild();
35235 config.resizeEl = treeEl;
35239 Roo.TreePanel.superclass.constructor.call(this, el, config);
35242 this.tree = new Roo.tree.TreePanel(treeEl , tree);
35243 //console.log(tree);
35244 this.on('activate', function()
35246 if (this.tree.rendered) {
35249 //console.log('render tree');
35250 this.tree.render();
35252 // this should not be needed.. - it's actually the 'el' that resizes?
35253 // actuall it breaks the containerScroll - dragging nodes auto scroll at top
35255 //this.on('resize', function (cp, w, h) {
35256 // this.tree.innerCt.setWidth(w);
35257 // this.tree.innerCt.setHeight(h);
35258 // //this.tree.innerCt.setStyle('overflow-y', 'auto');
35265 Roo.extend(Roo.TreePanel, Roo.ContentPanel, {
35282 * Ext JS Library 1.1.1
35283 * Copyright(c) 2006-2007, Ext JS, LLC.
35285 * Originally Released Under LGPL - original licence link has changed is not relivant.
35288 * <script type="text/javascript">
35293 * @class Roo.ReaderLayout
35294 * @extends Roo.BorderLayout
35295 * This is a pre-built layout that represents a classic, 5-pane application. It consists of a header, a primary
35296 * center region containing two nested regions (a top one for a list view and one for item preview below),
35297 * and regions on either side that can be used for navigation, application commands, informational displays, etc.
35298 * The setup and configuration work exactly the same as it does for a {@link Roo.BorderLayout} - this class simply
35299 * expedites the setup of the overall layout and regions for this common application style.
35302 var reader = new Roo.ReaderLayout();
35303 var CP = Roo.ContentPanel; // shortcut for adding
35305 reader.beginUpdate();
35306 reader.add("north", new CP("north", "North"));
35307 reader.add("west", new CP("west", {title: "West"}));
35308 reader.add("east", new CP("east", {title: "East"}));
35310 reader.regions.listView.add(new CP("listView", "List"));
35311 reader.regions.preview.add(new CP("preview", "Preview"));
35312 reader.endUpdate();
35315 * Create a new ReaderLayout
35316 * @param {Object} config Configuration options
35317 * @param {String/HTMLElement/Element} container (optional) The container this layout is bound to (defaults to
35318 * document.body if omitted)
35320 Roo.ReaderLayout = function(config, renderTo){
35321 var c = config || {size:{}};
35322 Roo.ReaderLayout.superclass.constructor.call(this, renderTo || document.body, {
35323 north: c.north !== false ? Roo.apply({
35327 }, c.north) : false,
35328 west: c.west !== false ? Roo.apply({
35336 margins:{left:5,right:0,bottom:5,top:5},
35337 cmargins:{left:5,right:5,bottom:5,top:5}
35338 }, c.west) : false,
35339 east: c.east !== false ? Roo.apply({
35347 margins:{left:0,right:5,bottom:5,top:5},
35348 cmargins:{left:5,right:5,bottom:5,top:5}
35349 }, c.east) : false,
35350 center: Roo.apply({
35351 tabPosition: 'top',
35355 margins:{left:c.west!==false ? 0 : 5,right:c.east!==false ? 0 : 5,bottom:5,top:2}
35359 this.el.addClass('x-reader');
35361 this.beginUpdate();
35363 var inner = new Roo.BorderLayout(Roo.get(document.body).createChild(), {
35364 south: c.preview !== false ? Roo.apply({
35371 cmargins:{top:5,left:0, right:0, bottom:0}
35372 }, c.preview) : false,
35373 center: Roo.apply({
35379 this.add('center', new Roo.NestedLayoutPanel(inner,
35380 Roo.apply({title: c.mainTitle || '',tabTip:''},c.innerPanelCfg)));
35384 this.regions.preview = inner.getRegion('south');
35385 this.regions.listView = inner.getRegion('center');
35388 Roo.extend(Roo.ReaderLayout, Roo.BorderLayout);/*
35390 * Ext JS Library 1.1.1
35391 * Copyright(c) 2006-2007, Ext JS, LLC.
35393 * Originally Released Under LGPL - original licence link has changed is not relivant.
35396 * <script type="text/javascript">
35400 * @class Roo.grid.Grid
35401 * @extends Roo.util.Observable
35402 * This class represents the primary interface of a component based grid control.
35403 * <br><br>Usage:<pre><code>
35404 var grid = new Roo.grid.Grid("my-container-id", {
35407 selModel: mySelectionModel,
35408 autoSizeColumns: true,
35409 monitorWindowResize: false,
35410 trackMouseOver: true
35415 * <b>Common Problems:</b><br/>
35416 * - Grid does not resize properly when going smaller: Setting overflow hidden on the container
35417 * element will correct this<br/>
35418 * - If you get el.style[camel]= NaNpx or -2px or something related, be certain you have given your container element
35419 * dimensions. The grid adapts to your container's size, if your container has no size defined then the results
35420 * are unpredictable.<br/>
35421 * - Do not render the grid into an element with display:none. Try using visibility:hidden. Otherwise there is no way for the
35422 * grid to calculate dimensions/offsets.<br/>
35424 * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
35425 * The container MUST have some type of size defined for the grid to fill. The container will be
35426 * automatically set to position relative if it isn't already.
35427 * @param {Object} config A config object that sets properties on this grid.
35429 Roo.grid.Grid = function(container, config){
35430 // initialize the container
35431 this.container = Roo.get(container);
35432 this.container.update("");
35433 this.container.setStyle("overflow", "hidden");
35434 this.container.addClass('x-grid-container');
35436 this.id = this.container.id;
35438 Roo.apply(this, config);
35439 // check and correct shorthanded configs
35441 this.dataSource = this.ds;
35445 this.colModel = this.cm;
35449 this.selModel = this.sm;
35453 if (this.selModel) {
35454 this.selModel = Roo.factory(this.selModel, Roo.grid);
35455 this.sm = this.selModel;
35456 this.sm.xmodule = this.xmodule || false;
35458 if (typeof(this.colModel.config) == 'undefined') {
35459 this.colModel = new Roo.grid.ColumnModel(this.colModel);
35460 this.cm = this.colModel;
35461 this.cm.xmodule = this.xmodule || false;
35463 if (this.dataSource) {
35464 this.dataSource= Roo.factory(this.dataSource, Roo.data);
35465 this.ds = this.dataSource;
35466 this.ds.xmodule = this.xmodule || false;
35473 this.container.setWidth(this.width);
35477 this.container.setHeight(this.height);
35484 * The raw click event for the entire grid.
35485 * @param {Roo.EventObject} e
35490 * The raw dblclick event for the entire grid.
35491 * @param {Roo.EventObject} e
35495 * @event contextmenu
35496 * The raw contextmenu event for the entire grid.
35497 * @param {Roo.EventObject} e
35499 "contextmenu" : true,
35502 * The raw mousedown event for the entire grid.
35503 * @param {Roo.EventObject} e
35505 "mousedown" : true,
35508 * The raw mouseup event for the entire grid.
35509 * @param {Roo.EventObject} e
35514 * The raw mouseover event for the entire grid.
35515 * @param {Roo.EventObject} e
35517 "mouseover" : true,
35520 * The raw mouseout event for the entire grid.
35521 * @param {Roo.EventObject} e
35526 * The raw keypress event for the entire grid.
35527 * @param {Roo.EventObject} e
35532 * The raw keydown event for the entire grid.
35533 * @param {Roo.EventObject} e
35541 * Fires when a cell is clicked
35542 * @param {Grid} this
35543 * @param {Number} rowIndex
35544 * @param {Number} columnIndex
35545 * @param {Roo.EventObject} e
35547 "cellclick" : true,
35549 * @event celldblclick
35550 * Fires when a cell is double clicked
35551 * @param {Grid} this
35552 * @param {Number} rowIndex
35553 * @param {Number} columnIndex
35554 * @param {Roo.EventObject} e
35556 "celldblclick" : true,
35559 * Fires when a row is clicked
35560 * @param {Grid} this
35561 * @param {Number} rowIndex
35562 * @param {Roo.EventObject} e
35566 * @event rowdblclick
35567 * Fires when a row is double clicked
35568 * @param {Grid} this
35569 * @param {Number} rowIndex
35570 * @param {Roo.EventObject} e
35572 "rowdblclick" : true,
35574 * @event headerclick
35575 * Fires when a header is clicked
35576 * @param {Grid} this
35577 * @param {Number} columnIndex
35578 * @param {Roo.EventObject} e
35580 "headerclick" : true,
35582 * @event headerdblclick
35583 * Fires when a header cell is double clicked
35584 * @param {Grid} this
35585 * @param {Number} columnIndex
35586 * @param {Roo.EventObject} e
35588 "headerdblclick" : true,
35590 * @event rowcontextmenu
35591 * Fires when a row is right clicked
35592 * @param {Grid} this
35593 * @param {Number} rowIndex
35594 * @param {Roo.EventObject} e
35596 "rowcontextmenu" : true,
35598 * @event cellcontextmenu
35599 * Fires when a cell is right clicked
35600 * @param {Grid} this
35601 * @param {Number} rowIndex
35602 * @param {Number} cellIndex
35603 * @param {Roo.EventObject} e
35605 "cellcontextmenu" : true,
35607 * @event headercontextmenu
35608 * Fires when a header is right clicked
35609 * @param {Grid} this
35610 * @param {Number} columnIndex
35611 * @param {Roo.EventObject} e
35613 "headercontextmenu" : true,
35615 * @event bodyscroll
35616 * Fires when the body element is scrolled
35617 * @param {Number} scrollLeft
35618 * @param {Number} scrollTop
35620 "bodyscroll" : true,
35622 * @event columnresize
35623 * Fires when the user resizes a column
35624 * @param {Number} columnIndex
35625 * @param {Number} newSize
35627 "columnresize" : true,
35629 * @event columnmove
35630 * Fires when the user moves a column
35631 * @param {Number} oldIndex
35632 * @param {Number} newIndex
35634 "columnmove" : true,
35637 * Fires when row(s) start being dragged
35638 * @param {Grid} this
35639 * @param {Roo.GridDD} dd The drag drop object
35640 * @param {event} e The raw browser event
35642 "startdrag" : true,
35645 * Fires when a drag operation is complete
35646 * @param {Grid} this
35647 * @param {Roo.GridDD} dd The drag drop object
35648 * @param {event} e The raw browser event
35653 * Fires when dragged row(s) are dropped on a valid DD target
35654 * @param {Grid} this
35655 * @param {Roo.GridDD} dd The drag drop object
35656 * @param {String} targetId The target drag drop object
35657 * @param {event} e The raw browser event
35662 * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
35663 * @param {Grid} this
35664 * @param {Roo.GridDD} dd The drag drop object
35665 * @param {String} targetId The target drag drop object
35666 * @param {event} e The raw browser event
35671 * Fires when the dragged row(s) first cross another DD target while being dragged
35672 * @param {Grid} this
35673 * @param {Roo.GridDD} dd The drag drop object
35674 * @param {String} targetId The target drag drop object
35675 * @param {event} e The raw browser event
35677 "dragenter" : true,
35680 * Fires when the dragged row(s) leave another DD target while being dragged
35681 * @param {Grid} this
35682 * @param {Roo.GridDD} dd The drag drop object
35683 * @param {String} targetId The target drag drop object
35684 * @param {event} e The raw browser event
35689 * Fires when a row is rendered, so you can change add a style to it.
35690 * @param {GridView} gridview The grid view
35691 * @param {Object} rowcfg contains record rowIndex and rowClass - set rowClass to add a style.
35697 * Fires when the grid is rendered
35698 * @param {Grid} grid
35703 Roo.grid.Grid.superclass.constructor.call(this);
35705 Roo.extend(Roo.grid.Grid, Roo.util.Observable, {
35708 * @cfg {String} ddGroup - drag drop group.
35712 * @cfg {Number} minColumnWidth The minimum width a column can be resized to. Default is 25.
35714 minColumnWidth : 25,
35717 * @cfg {Boolean} autoSizeColumns True to automatically resize the columns to fit their content
35718 * <b>on initial render.</b> It is more efficient to explicitly size the columns
35719 * through the ColumnModel's {@link Roo.grid.ColumnModel#width} config option. Default is false.
35721 autoSizeColumns : false,
35724 * @cfg {Boolean} autoSizeHeaders True to measure headers with column data when auto sizing columns. Default is true.
35726 autoSizeHeaders : true,
35729 * @cfg {Boolean} monitorWindowResize True to autoSize the grid when the window resizes. Default is true.
35731 monitorWindowResize : true,
35734 * @cfg {Boolean} maxRowsToMeasure If autoSizeColumns is on, maxRowsToMeasure can be used to limit the number of
35735 * rows measured to get a columns size. Default is 0 (all rows).
35737 maxRowsToMeasure : 0,
35740 * @cfg {Boolean} trackMouseOver True to highlight rows when the mouse is over. Default is true.
35742 trackMouseOver : true,
35745 * @cfg {Boolean} enableDrag True to enable drag of rows. Default is false. (double check if this is needed?)
35749 * @cfg {Boolean} enableDragDrop True to enable drag and drop of rows. Default is false.
35751 enableDragDrop : false,
35754 * @cfg {Boolean} enableColumnMove True to enable drag and drop reorder of columns. Default is true.
35756 enableColumnMove : true,
35759 * @cfg {Boolean} enableColumnHide True to enable hiding of columns with the header context menu. Default is true.
35761 enableColumnHide : true,
35764 * @cfg {Boolean} enableRowHeightSync True to manually sync row heights across locked and not locked rows. Default is false.
35766 enableRowHeightSync : false,
35769 * @cfg {Boolean} stripeRows True to stripe the rows. Default is true.
35774 * @cfg {Boolean} autoHeight True to fit the height of the grid container to the height of the data. Default is false.
35776 autoHeight : false,
35779 * @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.
35781 autoExpandColumn : false,
35784 * @cfg {Number} autoExpandMin The minimum width the autoExpandColumn can have (if enabled).
35787 autoExpandMin : 50,
35790 * @cfg {Number} autoExpandMax The maximum width the autoExpandColumn can have (if enabled). Default is 1000.
35792 autoExpandMax : 1000,
35795 * @cfg {Object} view The {@link Roo.grid.GridView} used by the grid. This can be set before a call to render().
35800 * @cfg {Object} loadMask An {@link Roo.LoadMask} config or true to mask the grid while loading. Default is false.
35804 * @cfg {Roo.dd.DropTarget} dropTarget An {@link Roo.dd.DropTarget} config
35814 * @cfg {Boolean} autoWidth True to set the grid's width to the default total width of the grid's columns instead
35815 * of a fixed width. Default is false.
35818 * @cfg {Number} maxHeight Sets the maximum height of the grid - ignored if autoHeight is not on.
35821 * Called once after all setup has been completed and the grid is ready to be rendered.
35822 * @return {Roo.grid.Grid} this
35824 render : function()
35826 var c = this.container;
35827 // try to detect autoHeight/width mode
35828 if((!c.dom.offsetHeight || c.dom.offsetHeight < 20) || c.getStyle("height") == "auto"){
35829 this.autoHeight = true;
35831 var view = this.getView();
35834 c.on("click", this.onClick, this);
35835 c.on("dblclick", this.onDblClick, this);
35836 c.on("contextmenu", this.onContextMenu, this);
35837 c.on("keydown", this.onKeyDown, this);
35839 c.on("touchstart", this.onTouchStart, this);
35842 this.relayEvents(c, ["mousedown","mouseup","mouseover","mouseout","keypress"]);
35844 this.getSelectionModel().init(this);
35849 this.loadMask = new Roo.LoadMask(this.container,
35850 Roo.apply({store:this.dataSource}, this.loadMask));
35854 if (this.toolbar && this.toolbar.xtype) {
35855 this.toolbar.container = this.getView().getHeaderPanel(true);
35856 this.toolbar = new Roo.Toolbar(this.toolbar);
35858 if (this.footer && this.footer.xtype) {
35859 this.footer.dataSource = this.getDataSource();
35860 this.footer.container = this.getView().getFooterPanel(true);
35861 this.footer = Roo.factory(this.footer, Roo);
35863 if (this.dropTarget && this.dropTarget.xtype) {
35864 delete this.dropTarget.xtype;
35865 this.dropTarget = new Roo.dd.DropTarget(this.getView().mainBody, this.dropTarget);
35869 this.rendered = true;
35870 this.fireEvent('render', this);
35875 * Reconfigures the grid to use a different Store and Column Model.
35876 * The View will be bound to the new objects and refreshed.
35877 * @param {Roo.data.Store} dataSource The new {@link Roo.data.Store} object
35878 * @param {Roo.grid.ColumnModel} The new {@link Roo.grid.ColumnModel} object
35880 reconfigure : function(dataSource, colModel){
35882 this.loadMask.destroy();
35883 this.loadMask = new Roo.LoadMask(this.container,
35884 Roo.apply({store:dataSource}, this.loadMask));
35886 this.view.bind(dataSource, colModel);
35887 this.dataSource = dataSource;
35888 this.colModel = colModel;
35889 this.view.refresh(true);
35893 onKeyDown : function(e){
35894 this.fireEvent("keydown", e);
35898 * Destroy this grid.
35899 * @param {Boolean} removeEl True to remove the element
35901 destroy : function(removeEl, keepListeners){
35903 this.loadMask.destroy();
35905 var c = this.container;
35906 c.removeAllListeners();
35907 this.view.destroy();
35908 this.colModel.purgeListeners();
35909 if(!keepListeners){
35910 this.purgeListeners();
35913 if(removeEl === true){
35919 processEvent : function(name, e){
35920 // does this fire select???
35921 Roo.log('grid:processEvent ' + name);
35923 if (name != 'touchstart' ) {
35924 this.fireEvent(name, e);
35927 var t = e.getTarget();
35929 var header = v.findHeaderIndex(t);
35930 if(header !== false){
35931 this.fireEvent("header" + (name == 'touchstart' ? 'click' : name), this, header, e);
35933 var row = v.findRowIndex(t);
35934 var cell = v.findCellIndex(t);
35935 if (name == 'touchstart') {
35936 // first touch is always a click.
35937 // hopefull this happens after selection is updated.?
35940 if (typeof(this.selModel.getSelectedCell) != 'undefined') {
35941 var cs = this.selModel.getSelectedCell();
35942 if (row == cs[0] && cell == cs[1]){
35946 if (typeof(this.selModel.getSelections) != 'undefined') {
35947 var cs = this.selModel.getSelections();
35948 var ds = this.dataSource;
35949 if (cs.length == 1 && ds.getAt(row) == cs[0]){
35960 this.fireEvent("row" + name, this, row, e);
35961 if(cell !== false){
35962 this.fireEvent("cell" + name, this, row, cell, e);
35969 onClick : function(e){
35970 this.processEvent("click", e);
35973 onTouchStart : function(e){
35974 this.processEvent("touchstart", e);
35978 onContextMenu : function(e, t){
35979 this.processEvent("contextmenu", e);
35983 onDblClick : function(e){
35984 this.processEvent("dblclick", e);
35988 walkCells : function(row, col, step, fn, scope){
35989 var cm = this.colModel, clen = cm.getColumnCount();
35990 var ds = this.dataSource, rlen = ds.getCount(), first = true;
36002 if(fn.call(scope || this, row, col, cm) === true){
36020 if(fn.call(scope || this, row, col, cm) === true){
36032 getSelections : function(){
36033 return this.selModel.getSelections();
36037 * Causes the grid to manually recalculate its dimensions. Generally this is done automatically,
36038 * but if manual update is required this method will initiate it.
36040 autoSize : function(){
36042 this.view.layout();
36043 if(this.view.adjustForScroll){
36044 this.view.adjustForScroll();
36050 * Returns the grid's underlying element.
36051 * @return {Element} The element
36053 getGridEl : function(){
36054 return this.container;
36057 // private for compatibility, overridden by editor grid
36058 stopEditing : function(){},
36061 * Returns the grid's SelectionModel.
36062 * @return {SelectionModel}
36064 getSelectionModel : function(){
36065 if(!this.selModel){
36066 this.selModel = new Roo.grid.RowSelectionModel();
36068 return this.selModel;
36072 * Returns the grid's DataSource.
36073 * @return {DataSource}
36075 getDataSource : function(){
36076 return this.dataSource;
36080 * Returns the grid's ColumnModel.
36081 * @return {ColumnModel}
36083 getColumnModel : function(){
36084 return this.colModel;
36088 * Returns the grid's GridView object.
36089 * @return {GridView}
36091 getView : function(){
36093 this.view = new Roo.grid.GridView(this.viewConfig);
36098 * Called to get grid's drag proxy text, by default returns this.ddText.
36101 getDragDropText : function(){
36102 var count = this.selModel.getCount();
36103 return String.format(this.ddText, count, count == 1 ? '' : 's');
36107 * Configures the text is the drag proxy (defaults to "%0 selected row(s)").
36108 * %0 is replaced with the number of selected rows.
36111 Roo.grid.Grid.prototype.ddText = "{0} selected row{1}";/*
36113 * Ext JS Library 1.1.1
36114 * Copyright(c) 2006-2007, Ext JS, LLC.
36116 * Originally Released Under LGPL - original licence link has changed is not relivant.
36119 * <script type="text/javascript">
36122 Roo.grid.AbstractGridView = function(){
36126 "beforerowremoved" : true,
36127 "beforerowsinserted" : true,
36128 "beforerefresh" : true,
36129 "rowremoved" : true,
36130 "rowsinserted" : true,
36131 "rowupdated" : true,
36134 Roo.grid.AbstractGridView.superclass.constructor.call(this);
36137 Roo.extend(Roo.grid.AbstractGridView, Roo.util.Observable, {
36138 rowClass : "x-grid-row",
36139 cellClass : "x-grid-cell",
36140 tdClass : "x-grid-td",
36141 hdClass : "x-grid-hd",
36142 splitClass : "x-grid-hd-split",
36144 init: function(grid){
36146 var cid = this.grid.getGridEl().id;
36147 this.colSelector = "#" + cid + " ." + this.cellClass + "-";
36148 this.tdSelector = "#" + cid + " ." + this.tdClass + "-";
36149 this.hdSelector = "#" + cid + " ." + this.hdClass + "-";
36150 this.splitSelector = "#" + cid + " ." + this.splitClass + "-";
36153 getColumnRenderers : function(){
36154 var renderers = [];
36155 var cm = this.grid.colModel;
36156 var colCount = cm.getColumnCount();
36157 for(var i = 0; i < colCount; i++){
36158 renderers[i] = cm.getRenderer(i);
36163 getColumnIds : function(){
36165 var cm = this.grid.colModel;
36166 var colCount = cm.getColumnCount();
36167 for(var i = 0; i < colCount; i++){
36168 ids[i] = cm.getColumnId(i);
36173 getDataIndexes : function(){
36174 if(!this.indexMap){
36175 this.indexMap = this.buildIndexMap();
36177 return this.indexMap.colToData;
36180 getColumnIndexByDataIndex : function(dataIndex){
36181 if(!this.indexMap){
36182 this.indexMap = this.buildIndexMap();
36184 return this.indexMap.dataToCol[dataIndex];
36188 * Set a css style for a column dynamically.
36189 * @param {Number} colIndex The index of the column
36190 * @param {String} name The css property name
36191 * @param {String} value The css value
36193 setCSSStyle : function(colIndex, name, value){
36194 var selector = "#" + this.grid.id + " .x-grid-col-" + colIndex;
36195 Roo.util.CSS.updateRule(selector, name, value);
36198 generateRules : function(cm){
36199 var ruleBuf = [], rulesId = this.grid.id + '-cssrules';
36200 Roo.util.CSS.removeStyleSheet(rulesId);
36201 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
36202 var cid = cm.getColumnId(i);
36203 ruleBuf.push(this.colSelector, cid, " {\n", cm.config[i].css, "}\n",
36204 this.tdSelector, cid, " {\n}\n",
36205 this.hdSelector, cid, " {\n}\n",
36206 this.splitSelector, cid, " {\n}\n");
36208 return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
36212 * Ext JS Library 1.1.1
36213 * Copyright(c) 2006-2007, Ext JS, LLC.
36215 * Originally Released Under LGPL - original licence link has changed is not relivant.
36218 * <script type="text/javascript">
36222 // This is a support class used internally by the Grid components
36223 Roo.grid.HeaderDragZone = function(grid, hd, hd2){
36225 this.view = grid.getView();
36226 this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
36227 Roo.grid.HeaderDragZone.superclass.constructor.call(this, hd);
36229 this.setHandleElId(Roo.id(hd));
36230 this.setOuterHandleElId(Roo.id(hd2));
36232 this.scroll = false;
36234 Roo.extend(Roo.grid.HeaderDragZone, Roo.dd.DragZone, {
36236 getDragData : function(e){
36237 var t = Roo.lib.Event.getTarget(e);
36238 var h = this.view.findHeaderCell(t);
36240 return {ddel: h.firstChild, header:h};
36245 onInitDrag : function(e){
36246 this.view.headersDisabled = true;
36247 var clone = this.dragData.ddel.cloneNode(true);
36248 clone.id = Roo.id();
36249 clone.style.width = Math.min(this.dragData.header.offsetWidth,this.maxDragWidth) + "px";
36250 this.proxy.update(clone);
36254 afterValidDrop : function(){
36256 setTimeout(function(){
36257 v.headersDisabled = false;
36261 afterInvalidDrop : function(){
36263 setTimeout(function(){
36264 v.headersDisabled = false;
36270 * Ext JS Library 1.1.1
36271 * Copyright(c) 2006-2007, Ext JS, LLC.
36273 * Originally Released Under LGPL - original licence link has changed is not relivant.
36276 * <script type="text/javascript">
36279 // This is a support class used internally by the Grid components
36280 Roo.grid.HeaderDropZone = function(grid, hd, hd2){
36282 this.view = grid.getView();
36283 // split the proxies so they don't interfere with mouse events
36284 this.proxyTop = Roo.DomHelper.append(document.body, {
36285 cls:"col-move-top", html:" "
36287 this.proxyBottom = Roo.DomHelper.append(document.body, {
36288 cls:"col-move-bottom", html:" "
36290 this.proxyTop.hide = this.proxyBottom.hide = function(){
36291 this.setLeftTop(-100,-100);
36292 this.setStyle("visibility", "hidden");
36294 this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
36295 // temporarily disabled
36296 //Roo.dd.ScrollManager.register(this.view.scroller.dom);
36297 Roo.grid.HeaderDropZone.superclass.constructor.call(this, grid.getGridEl().dom);
36299 Roo.extend(Roo.grid.HeaderDropZone, Roo.dd.DropZone, {
36300 proxyOffsets : [-4, -9],
36301 fly: Roo.Element.fly,
36303 getTargetFromEvent : function(e){
36304 var t = Roo.lib.Event.getTarget(e);
36305 var cindex = this.view.findCellIndex(t);
36306 if(cindex !== false){
36307 return this.view.getHeaderCell(cindex);
36312 nextVisible : function(h){
36313 var v = this.view, cm = this.grid.colModel;
36316 if(!cm.isHidden(v.getCellIndex(h))){
36324 prevVisible : function(h){
36325 var v = this.view, cm = this.grid.colModel;
36328 if(!cm.isHidden(v.getCellIndex(h))){
36336 positionIndicator : function(h, n, e){
36337 var x = Roo.lib.Event.getPageX(e);
36338 var r = Roo.lib.Dom.getRegion(n.firstChild);
36339 var px, pt, py = r.top + this.proxyOffsets[1];
36340 if((r.right - x) <= (r.right-r.left)/2){
36341 px = r.right+this.view.borderWidth;
36347 var oldIndex = this.view.getCellIndex(h);
36348 var newIndex = this.view.getCellIndex(n);
36350 if(this.grid.colModel.isFixed(newIndex)){
36354 var locked = this.grid.colModel.isLocked(newIndex);
36359 if(oldIndex < newIndex){
36362 if(oldIndex == newIndex && (locked == this.grid.colModel.isLocked(oldIndex))){
36365 px += this.proxyOffsets[0];
36366 this.proxyTop.setLeftTop(px, py);
36367 this.proxyTop.show();
36368 if(!this.bottomOffset){
36369 this.bottomOffset = this.view.mainHd.getHeight();
36371 this.proxyBottom.setLeftTop(px, py+this.proxyTop.dom.offsetHeight+this.bottomOffset);
36372 this.proxyBottom.show();
36376 onNodeEnter : function(n, dd, e, data){
36377 if(data.header != n){
36378 this.positionIndicator(data.header, n, e);
36382 onNodeOver : function(n, dd, e, data){
36383 var result = false;
36384 if(data.header != n){
36385 result = this.positionIndicator(data.header, n, e);
36388 this.proxyTop.hide();
36389 this.proxyBottom.hide();
36391 return result ? this.dropAllowed : this.dropNotAllowed;
36394 onNodeOut : function(n, dd, e, data){
36395 this.proxyTop.hide();
36396 this.proxyBottom.hide();
36399 onNodeDrop : function(n, dd, e, data){
36400 var h = data.header;
36402 var cm = this.grid.colModel;
36403 var x = Roo.lib.Event.getPageX(e);
36404 var r = Roo.lib.Dom.getRegion(n.firstChild);
36405 var pt = (r.right - x) <= ((r.right-r.left)/2) ? "after" : "before";
36406 var oldIndex = this.view.getCellIndex(h);
36407 var newIndex = this.view.getCellIndex(n);
36408 var locked = cm.isLocked(newIndex);
36412 if(oldIndex < newIndex){
36415 if(oldIndex == newIndex && (locked == cm.isLocked(oldIndex))){
36418 cm.setLocked(oldIndex, locked, true);
36419 cm.moveColumn(oldIndex, newIndex);
36420 this.grid.fireEvent("columnmove", oldIndex, newIndex);
36428 * Ext JS Library 1.1.1
36429 * Copyright(c) 2006-2007, Ext JS, LLC.
36431 * Originally Released Under LGPL - original licence link has changed is not relivant.
36434 * <script type="text/javascript">
36438 * @class Roo.grid.GridView
36439 * @extends Roo.util.Observable
36442 * @param {Object} config
36444 Roo.grid.GridView = function(config){
36445 Roo.grid.GridView.superclass.constructor.call(this);
36448 Roo.apply(this, config);
36451 Roo.extend(Roo.grid.GridView, Roo.grid.AbstractGridView, {
36453 unselectable : 'unselectable="on"',
36454 unselectableCls : 'x-unselectable',
36457 rowClass : "x-grid-row",
36459 cellClass : "x-grid-col",
36461 tdClass : "x-grid-td",
36463 hdClass : "x-grid-hd",
36465 splitClass : "x-grid-split",
36467 sortClasses : ["sort-asc", "sort-desc"],
36469 enableMoveAnim : false,
36473 dh : Roo.DomHelper,
36475 fly : Roo.Element.fly,
36477 css : Roo.util.CSS,
36483 scrollIncrement : 22,
36485 cellRE: /(?:.*?)x-grid-(?:hd|cell|csplit)-(?:[\d]+)-([\d]+)(?:.*?)/,
36487 findRE: /\s?(?:x-grid-hd|x-grid-col|x-grid-csplit)\s/,
36489 bind : function(ds, cm){
36491 this.ds.un("load", this.onLoad, this);
36492 this.ds.un("datachanged", this.onDataChange, this);
36493 this.ds.un("add", this.onAdd, this);
36494 this.ds.un("remove", this.onRemove, this);
36495 this.ds.un("update", this.onUpdate, this);
36496 this.ds.un("clear", this.onClear, this);
36499 ds.on("load", this.onLoad, this);
36500 ds.on("datachanged", this.onDataChange, this);
36501 ds.on("add", this.onAdd, this);
36502 ds.on("remove", this.onRemove, this);
36503 ds.on("update", this.onUpdate, this);
36504 ds.on("clear", this.onClear, this);
36509 this.cm.un("widthchange", this.onColWidthChange, this);
36510 this.cm.un("headerchange", this.onHeaderChange, this);
36511 this.cm.un("hiddenchange", this.onHiddenChange, this);
36512 this.cm.un("columnmoved", this.onColumnMove, this);
36513 this.cm.un("columnlockchange", this.onColumnLock, this);
36516 this.generateRules(cm);
36517 cm.on("widthchange", this.onColWidthChange, this);
36518 cm.on("headerchange", this.onHeaderChange, this);
36519 cm.on("hiddenchange", this.onHiddenChange, this);
36520 cm.on("columnmoved", this.onColumnMove, this);
36521 cm.on("columnlockchange", this.onColumnLock, this);
36526 init: function(grid){
36527 Roo.grid.GridView.superclass.init.call(this, grid);
36529 this.bind(grid.dataSource, grid.colModel);
36531 grid.on("headerclick", this.handleHeaderClick, this);
36533 if(grid.trackMouseOver){
36534 grid.on("mouseover", this.onRowOver, this);
36535 grid.on("mouseout", this.onRowOut, this);
36537 grid.cancelTextSelection = function(){};
36538 this.gridId = grid.id;
36540 var tpls = this.templates || {};
36543 tpls.master = new Roo.Template(
36544 '<div class="x-grid" hidefocus="true">',
36545 '<a href="#" class="x-grid-focus" tabIndex="-1"></a>',
36546 '<div class="x-grid-topbar"></div>',
36547 '<div class="x-grid-scroller"><div></div></div>',
36548 '<div class="x-grid-locked">',
36549 '<div class="x-grid-header">{lockedHeader}</div>',
36550 '<div class="x-grid-body">{lockedBody}</div>',
36552 '<div class="x-grid-viewport">',
36553 '<div class="x-grid-header">{header}</div>',
36554 '<div class="x-grid-body">{body}</div>',
36556 '<div class="x-grid-bottombar"></div>',
36558 '<div class="x-grid-resize-proxy"> </div>',
36561 tpls.master.disableformats = true;
36565 tpls.header = new Roo.Template(
36566 '<table border="0" cellspacing="0" cellpadding="0">',
36567 '<tbody><tr class="x-grid-hd-row">{cells}</tr></tbody>',
36570 tpls.header.disableformats = true;
36572 tpls.header.compile();
36575 tpls.hcell = new Roo.Template(
36576 '<td class="x-grid-hd x-grid-td-{id} {cellId}"><div title="{title}" class="x-grid-hd-inner x-grid-hd-{id}">',
36577 '<div class="x-grid-hd-text ' + this.unselectableCls + '" ' + this.unselectable +'>{value}<img class="x-grid-sort-icon" src="', Roo.BLANK_IMAGE_URL, '" /></div>',
36580 tpls.hcell.disableFormats = true;
36582 tpls.hcell.compile();
36585 tpls.hsplit = new Roo.Template('<div class="x-grid-split {splitId} x-grid-split-{id}" style="{style} ' +
36586 this.unselectableCls + '" ' + this.unselectable +'> </div>');
36587 tpls.hsplit.disableFormats = true;
36589 tpls.hsplit.compile();
36592 tpls.body = new Roo.Template(
36593 '<table border="0" cellspacing="0" cellpadding="0">',
36594 "<tbody>{rows}</tbody>",
36597 tpls.body.disableFormats = true;
36599 tpls.body.compile();
36602 tpls.row = new Roo.Template('<tr class="x-grid-row {alt}">{cells}</tr>');
36603 tpls.row.disableFormats = true;
36605 tpls.row.compile();
36608 tpls.cell = new Roo.Template(
36609 '<td class="x-grid-col x-grid-td-{id} {cellId} {css}" tabIndex="0">',
36610 '<div class="x-grid-col-{id} x-grid-cell-inner"><div class="x-grid-cell-text ' +
36611 this.unselectableCls + '" ' + this.unselectable +'" {attr}>{value}</div></div>',
36614 tpls.cell.disableFormats = true;
36616 tpls.cell.compile();
36618 this.templates = tpls;
36621 // remap these for backwards compat
36622 onColWidthChange : function(){
36623 this.updateColumns.apply(this, arguments);
36625 onHeaderChange : function(){
36626 this.updateHeaders.apply(this, arguments);
36628 onHiddenChange : function(){
36629 this.handleHiddenChange.apply(this, arguments);
36631 onColumnMove : function(){
36632 this.handleColumnMove.apply(this, arguments);
36634 onColumnLock : function(){
36635 this.handleLockChange.apply(this, arguments);
36638 onDataChange : function(){
36640 this.updateHeaderSortState();
36643 onClear : function(){
36647 onUpdate : function(ds, record){
36648 this.refreshRow(record);
36651 refreshRow : function(record){
36652 var ds = this.ds, index;
36653 if(typeof record == 'number'){
36655 record = ds.getAt(index);
36657 index = ds.indexOf(record);
36659 this.insertRows(ds, index, index, true);
36660 this.onRemove(ds, record, index+1, true);
36661 this.syncRowHeights(index, index);
36663 this.fireEvent("rowupdated", this, index, record);
36666 onAdd : function(ds, records, index){
36667 this.insertRows(ds, index, index + (records.length-1));
36670 onRemove : function(ds, record, index, isUpdate){
36671 if(isUpdate !== true){
36672 this.fireEvent("beforerowremoved", this, index, record);
36674 var bt = this.getBodyTable(), lt = this.getLockedTable();
36675 if(bt.rows[index]){
36676 bt.firstChild.removeChild(bt.rows[index]);
36678 if(lt.rows[index]){
36679 lt.firstChild.removeChild(lt.rows[index]);
36681 if(isUpdate !== true){
36682 this.stripeRows(index);
36683 this.syncRowHeights(index, index);
36685 this.fireEvent("rowremoved", this, index, record);
36689 onLoad : function(){
36690 this.scrollToTop();
36694 * Scrolls the grid to the top
36696 scrollToTop : function(){
36698 this.scroller.dom.scrollTop = 0;
36704 * Gets a panel in the header of the grid that can be used for toolbars etc.
36705 * After modifying the contents of this panel a call to grid.autoSize() may be
36706 * required to register any changes in size.
36707 * @param {Boolean} doShow By default the header is hidden. Pass true to show the panel
36708 * @return Roo.Element
36710 getHeaderPanel : function(doShow){
36712 this.headerPanel.show();
36714 return this.headerPanel;
36718 * Gets a panel in the footer of the grid that can be used for toolbars etc.
36719 * After modifying the contents of this panel a call to grid.autoSize() may be
36720 * required to register any changes in size.
36721 * @param {Boolean} doShow By default the footer is hidden. Pass true to show the panel
36722 * @return Roo.Element
36724 getFooterPanel : function(doShow){
36726 this.footerPanel.show();
36728 return this.footerPanel;
36731 initElements : function(){
36732 var E = Roo.Element;
36733 var el = this.grid.getGridEl().dom.firstChild;
36734 var cs = el.childNodes;
36736 this.el = new E(el);
36738 this.focusEl = new E(el.firstChild);
36739 this.focusEl.swallowEvent("click", true);
36741 this.headerPanel = new E(cs[1]);
36742 this.headerPanel.enableDisplayMode("block");
36744 this.scroller = new E(cs[2]);
36745 this.scrollSizer = new E(this.scroller.dom.firstChild);
36747 this.lockedWrap = new E(cs[3]);
36748 this.lockedHd = new E(this.lockedWrap.dom.firstChild);
36749 this.lockedBody = new E(this.lockedWrap.dom.childNodes[1]);
36751 this.mainWrap = new E(cs[4]);
36752 this.mainHd = new E(this.mainWrap.dom.firstChild);
36753 this.mainBody = new E(this.mainWrap.dom.childNodes[1]);
36755 this.footerPanel = new E(cs[5]);
36756 this.footerPanel.enableDisplayMode("block");
36758 this.resizeProxy = new E(cs[6]);
36760 this.headerSelector = String.format(
36761 '#{0} td.x-grid-hd, #{1} td.x-grid-hd',
36762 this.lockedHd.id, this.mainHd.id
36765 this.splitterSelector = String.format(
36766 '#{0} div.x-grid-split, #{1} div.x-grid-split',
36767 this.idToCssName(this.lockedHd.id), this.idToCssName(this.mainHd.id)
36770 idToCssName : function(s)
36772 return s.replace(/[^a-z0-9]+/ig, '-');
36775 getHeaderCell : function(index){
36776 return Roo.DomQuery.select(this.headerSelector)[index];
36779 getHeaderCellMeasure : function(index){
36780 return this.getHeaderCell(index).firstChild;
36783 getHeaderCellText : function(index){
36784 return this.getHeaderCell(index).firstChild.firstChild;
36787 getLockedTable : function(){
36788 return this.lockedBody.dom.firstChild;
36791 getBodyTable : function(){
36792 return this.mainBody.dom.firstChild;
36795 getLockedRow : function(index){
36796 return this.getLockedTable().rows[index];
36799 getRow : function(index){
36800 return this.getBodyTable().rows[index];
36803 getRowComposite : function(index){
36805 this.rowEl = new Roo.CompositeElementLite();
36807 var els = [], lrow, mrow;
36808 if(lrow = this.getLockedRow(index)){
36811 if(mrow = this.getRow(index)){
36814 this.rowEl.elements = els;
36818 * Gets the 'td' of the cell
36820 * @param {Integer} rowIndex row to select
36821 * @param {Integer} colIndex column to select
36825 getCell : function(rowIndex, colIndex){
36826 var locked = this.cm.getLockedCount();
36828 if(colIndex < locked){
36829 source = this.lockedBody.dom.firstChild;
36831 source = this.mainBody.dom.firstChild;
36832 colIndex -= locked;
36834 return source.rows[rowIndex].childNodes[colIndex];
36837 getCellText : function(rowIndex, colIndex){
36838 return this.getCell(rowIndex, colIndex).firstChild.firstChild;
36841 getCellBox : function(cell){
36842 var b = this.fly(cell).getBox();
36843 if(Roo.isOpera){ // opera fails to report the Y
36844 b.y = cell.offsetTop + this.mainBody.getY();
36849 getCellIndex : function(cell){
36850 var id = String(cell.className).match(this.cellRE);
36852 return parseInt(id[1], 10);
36857 findHeaderIndex : function(n){
36858 var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
36859 return r ? this.getCellIndex(r) : false;
36862 findHeaderCell : function(n){
36863 var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
36864 return r ? r : false;
36867 findRowIndex : function(n){
36871 var r = Roo.fly(n).findParent("tr." + this.rowClass, 6);
36872 return r ? r.rowIndex : false;
36875 findCellIndex : function(node){
36876 var stop = this.el.dom;
36877 while(node && node != stop){
36878 if(this.findRE.test(node.className)){
36879 return this.getCellIndex(node);
36881 node = node.parentNode;
36886 getColumnId : function(index){
36887 return this.cm.getColumnId(index);
36890 getSplitters : function()
36892 if(this.splitterSelector){
36893 return Roo.DomQuery.select(this.splitterSelector);
36899 getSplitter : function(index){
36900 return this.getSplitters()[index];
36903 onRowOver : function(e, t){
36905 if((row = this.findRowIndex(t)) !== false){
36906 this.getRowComposite(row).addClass("x-grid-row-over");
36910 onRowOut : function(e, t){
36912 if((row = this.findRowIndex(t)) !== false && row !== this.findRowIndex(e.getRelatedTarget())){
36913 this.getRowComposite(row).removeClass("x-grid-row-over");
36917 renderHeaders : function(){
36919 var ct = this.templates.hcell, ht = this.templates.header, st = this.templates.hsplit;
36920 var cb = [], lb = [], sb = [], lsb = [], p = {};
36921 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
36922 p.cellId = "x-grid-hd-0-" + i;
36923 p.splitId = "x-grid-csplit-0-" + i;
36924 p.id = cm.getColumnId(i);
36925 p.title = cm.getColumnTooltip(i) || "";
36926 p.value = cm.getColumnHeader(i) || "";
36927 p.style = (this.grid.enableColumnResize === false || !cm.isResizable(i) || cm.isFixed(i)) ? 'cursor:default' : '';
36928 if(!cm.isLocked(i)){
36929 cb[cb.length] = ct.apply(p);
36930 sb[sb.length] = st.apply(p);
36932 lb[lb.length] = ct.apply(p);
36933 lsb[lsb.length] = st.apply(p);
36936 return [ht.apply({cells: lb.join(""), splits:lsb.join("")}),
36937 ht.apply({cells: cb.join(""), splits:sb.join("")})];
36940 updateHeaders : function(){
36941 var html = this.renderHeaders();
36942 this.lockedHd.update(html[0]);
36943 this.mainHd.update(html[1]);
36947 * Focuses the specified row.
36948 * @param {Number} row The row index
36950 focusRow : function(row)
36952 //Roo.log('GridView.focusRow');
36953 var x = this.scroller.dom.scrollLeft;
36954 this.focusCell(row, 0, false);
36955 this.scroller.dom.scrollLeft = x;
36959 * Focuses the specified cell.
36960 * @param {Number} row The row index
36961 * @param {Number} col The column index
36962 * @param {Boolean} hscroll false to disable horizontal scrolling
36964 focusCell : function(row, col, hscroll)
36966 //Roo.log('GridView.focusCell');
36967 var el = this.ensureVisible(row, col, hscroll);
36968 this.focusEl.alignTo(el, "tl-tl");
36970 this.focusEl.focus();
36972 this.focusEl.focus.defer(1, this.focusEl);
36977 * Scrolls the specified cell into view
36978 * @param {Number} row The row index
36979 * @param {Number} col The column index
36980 * @param {Boolean} hscroll false to disable horizontal scrolling
36982 ensureVisible : function(row, col, hscroll)
36984 //Roo.log('GridView.ensureVisible,' + row + ',' + col);
36985 //return null; //disable for testing.
36986 if(typeof row != "number"){
36987 row = row.rowIndex;
36989 if(row < 0 && row >= this.ds.getCount()){
36992 col = (col !== undefined ? col : 0);
36993 var cm = this.grid.colModel;
36994 while(cm.isHidden(col)){
36998 var el = this.getCell(row, col);
37002 var c = this.scroller.dom;
37004 var ctop = parseInt(el.offsetTop, 10);
37005 var cleft = parseInt(el.offsetLeft, 10);
37006 var cbot = ctop + el.offsetHeight;
37007 var cright = cleft + el.offsetWidth;
37009 var ch = c.clientHeight - this.mainHd.dom.offsetHeight;
37010 var stop = parseInt(c.scrollTop, 10);
37011 var sleft = parseInt(c.scrollLeft, 10);
37012 var sbot = stop + ch;
37013 var sright = sleft + c.clientWidth;
37015 Roo.log('GridView.ensureVisible:' +
37017 ' c.clientHeight:' + c.clientHeight +
37018 ' this.mainHd.dom.offsetHeight:' + this.mainHd.dom.offsetHeight +
37026 c.scrollTop = ctop;
37027 //Roo.log("set scrolltop to ctop DISABLE?");
37028 }else if(cbot > sbot){
37029 //Roo.log("set scrolltop to cbot-ch");
37030 c.scrollTop = cbot-ch;
37033 if(hscroll !== false){
37035 c.scrollLeft = cleft;
37036 }else if(cright > sright){
37037 c.scrollLeft = cright-c.clientWidth;
37044 updateColumns : function(){
37045 this.grid.stopEditing();
37046 var cm = this.grid.colModel, colIds = this.getColumnIds();
37047 //var totalWidth = cm.getTotalWidth();
37049 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
37050 //if(cm.isHidden(i)) continue;
37051 var w = cm.getColumnWidth(i);
37052 this.css.updateRule(this.colSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
37053 this.css.updateRule(this.hdSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
37055 this.updateSplitters();
37058 generateRules : function(cm){
37059 var ruleBuf = [], rulesId = this.idToCssName(this.grid.id)+ '-cssrules';
37060 Roo.util.CSS.removeStyleSheet(rulesId);
37061 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
37062 var cid = cm.getColumnId(i);
37064 if(cm.config[i].align){
37065 align = 'text-align:'+cm.config[i].align+';';
37068 if(cm.isHidden(i)){
37069 hidden = 'display:none;';
37071 var width = "width:" + (cm.getColumnWidth(i) - this.borderWidth) + "px;";
37073 this.colSelector, cid, " {\n", cm.config[i].css, align, width, "\n}\n",
37074 this.hdSelector, cid, " {\n", align, width, "}\n",
37075 this.tdSelector, cid, " {\n",hidden,"\n}\n",
37076 this.splitSelector, cid, " {\n", hidden , "\n}\n");
37078 return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
37081 updateSplitters : function(){
37082 var cm = this.cm, s = this.getSplitters();
37083 if(s){ // splitters not created yet
37084 var pos = 0, locked = true;
37085 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
37086 if(cm.isHidden(i)) continue;
37087 var w = cm.getColumnWidth(i); // make sure it's a number
37088 if(!cm.isLocked(i) && locked){
37093 s[i].style.left = (pos-this.splitOffset) + "px";
37098 handleHiddenChange : function(colModel, colIndex, hidden){
37100 this.hideColumn(colIndex);
37102 this.unhideColumn(colIndex);
37106 hideColumn : function(colIndex){
37107 var cid = this.getColumnId(colIndex);
37108 this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "none");
37109 this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "none");
37111 this.updateHeaders();
37113 this.updateSplitters();
37117 unhideColumn : function(colIndex){
37118 var cid = this.getColumnId(colIndex);
37119 this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "");
37120 this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "");
37123 this.updateHeaders();
37125 this.updateSplitters();
37129 insertRows : function(dm, firstRow, lastRow, isUpdate){
37130 if(firstRow == 0 && lastRow == dm.getCount()-1){
37134 this.fireEvent("beforerowsinserted", this, firstRow, lastRow);
37136 var s = this.getScrollState();
37137 var markup = this.renderRows(firstRow, lastRow);
37138 this.bufferRows(markup[0], this.getLockedTable(), firstRow);
37139 this.bufferRows(markup[1], this.getBodyTable(), firstRow);
37140 this.restoreScroll(s);
37142 this.fireEvent("rowsinserted", this, firstRow, lastRow);
37143 this.syncRowHeights(firstRow, lastRow);
37144 this.stripeRows(firstRow);
37150 bufferRows : function(markup, target, index){
37151 var before = null, trows = target.rows, tbody = target.tBodies[0];
37152 if(index < trows.length){
37153 before = trows[index];
37155 var b = document.createElement("div");
37156 b.innerHTML = "<table><tbody>"+markup+"</tbody></table>";
37157 var rows = b.firstChild.rows;
37158 for(var i = 0, len = rows.length; i < len; i++){
37160 tbody.insertBefore(rows[0], before);
37162 tbody.appendChild(rows[0]);
37169 deleteRows : function(dm, firstRow, lastRow){
37170 if(dm.getRowCount()<1){
37171 this.fireEvent("beforerefresh", this);
37172 this.mainBody.update("");
37173 this.lockedBody.update("");
37174 this.fireEvent("refresh", this);
37176 this.fireEvent("beforerowsdeleted", this, firstRow, lastRow);
37177 var bt = this.getBodyTable();
37178 var tbody = bt.firstChild;
37179 var rows = bt.rows;
37180 for(var rowIndex = firstRow; rowIndex <= lastRow; rowIndex++){
37181 tbody.removeChild(rows[firstRow]);
37183 this.stripeRows(firstRow);
37184 this.fireEvent("rowsdeleted", this, firstRow, lastRow);
37188 updateRows : function(dataSource, firstRow, lastRow){
37189 var s = this.getScrollState();
37191 this.restoreScroll(s);
37194 handleSort : function(dataSource, sortColumnIndex, sortDir, noRefresh){
37198 this.updateHeaderSortState();
37201 getScrollState : function(){
37203 var sb = this.scroller.dom;
37204 return {left: sb.scrollLeft, top: sb.scrollTop};
37207 stripeRows : function(startRow){
37208 if(!this.grid.stripeRows || this.ds.getCount() < 1){
37211 startRow = startRow || 0;
37212 var rows = this.getBodyTable().rows;
37213 var lrows = this.getLockedTable().rows;
37214 var cls = ' x-grid-row-alt ';
37215 for(var i = startRow, len = rows.length; i < len; i++){
37216 var row = rows[i], lrow = lrows[i];
37217 var isAlt = ((i+1) % 2 == 0);
37218 var hasAlt = (' '+row.className + ' ').indexOf(cls) != -1;
37219 if(isAlt == hasAlt){
37223 row.className += " x-grid-row-alt";
37225 row.className = row.className.replace("x-grid-row-alt", "");
37228 lrow.className = row.className;
37233 restoreScroll : function(state){
37234 //Roo.log('GridView.restoreScroll');
37235 var sb = this.scroller.dom;
37236 sb.scrollLeft = state.left;
37237 sb.scrollTop = state.top;
37241 syncScroll : function(){
37242 //Roo.log('GridView.syncScroll');
37243 var sb = this.scroller.dom;
37244 var sh = this.mainHd.dom;
37245 var bs = this.mainBody.dom;
37246 var lv = this.lockedBody.dom;
37247 sh.scrollLeft = bs.scrollLeft = sb.scrollLeft;
37248 lv.scrollTop = bs.scrollTop = sb.scrollTop;
37251 handleScroll : function(e){
37253 var sb = this.scroller.dom;
37254 this.grid.fireEvent("bodyscroll", sb.scrollLeft, sb.scrollTop);
37258 handleWheel : function(e){
37259 var d = e.getWheelDelta();
37260 this.scroller.dom.scrollTop -= d*22;
37261 // set this here to prevent jumpy scrolling on large tables
37262 this.lockedBody.dom.scrollTop = this.mainBody.dom.scrollTop = this.scroller.dom.scrollTop;
37266 renderRows : function(startRow, endRow){
37267 // pull in all the crap needed to render rows
37268 var g = this.grid, cm = g.colModel, ds = g.dataSource, stripe = g.stripeRows;
37269 var colCount = cm.getColumnCount();
37271 if(ds.getCount() < 1){
37275 // build a map for all the columns
37277 for(var i = 0; i < colCount; i++){
37278 var name = cm.getDataIndex(i);
37280 name : typeof name == 'undefined' ? ds.fields.get(i).name : name,
37281 renderer : cm.getRenderer(i),
37282 id : cm.getColumnId(i),
37283 locked : cm.isLocked(i)
37287 startRow = startRow || 0;
37288 endRow = typeof endRow == "undefined"? ds.getCount()-1 : endRow;
37290 // records to render
37291 var rs = ds.getRange(startRow, endRow);
37293 return this.doRender(cs, rs, ds, startRow, colCount, stripe);
37296 // As much as I hate to duplicate code, this was branched because FireFox really hates
37297 // [].join("") on strings. The performance difference was substantial enough to
37298 // branch this function
37299 doRender : Roo.isGecko ?
37300 function(cs, rs, ds, startRow, colCount, stripe){
37301 var ts = this.templates, ct = ts.cell, rt = ts.row;
37303 var buf = "", lbuf = "", cb, lcb, c, p = {}, rp = {}, r, rowIndex;
37305 var hasListener = this.grid.hasListener('rowclass');
37307 for(var j = 0, len = rs.length; j < len; j++){
37308 r = rs[j]; cb = ""; lcb = ""; rowIndex = (j+startRow);
37309 for(var i = 0; i < colCount; i++){
37311 p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
37313 p.css = p.attr = "";
37314 p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
37315 if(p.value == undefined || p.value === "") p.value = " ";
37316 if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
37317 p.css += p.css ? ' x-grid-dirty-cell' : 'x-grid-dirty-cell';
37319 var markup = ct.apply(p);
37327 if(stripe && ((rowIndex+1) % 2 == 0)){
37328 alt.push("x-grid-row-alt")
37331 alt.push( " x-grid-dirty-row");
37334 if(this.getRowClass){
37335 alt.push(this.getRowClass(r, rowIndex));
37341 rowIndex : rowIndex,
37344 this.grid.fireEvent('rowclass', this, rowcfg);
37345 alt.push(rowcfg.rowClass);
37347 rp.alt = alt.join(" ");
37348 lbuf+= rt.apply(rp);
37350 buf+= rt.apply(rp);
37352 return [lbuf, buf];
37354 function(cs, rs, ds, startRow, colCount, stripe){
37355 var ts = this.templates, ct = ts.cell, rt = ts.row;
37357 var buf = [], lbuf = [], cb, lcb, c, p = {}, rp = {}, r, rowIndex;
37358 var hasListener = this.grid.hasListener('rowclass');
37361 for(var j = 0, len = rs.length; j < len; j++){
37362 r = rs[j]; cb = []; lcb = []; rowIndex = (j+startRow);
37363 for(var i = 0; i < colCount; i++){
37365 p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
37367 p.css = p.attr = "";
37368 p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
37369 if(p.value == undefined || p.value === "") p.value = " ";
37370 if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
37371 p.css += p.css ? ' x-grid-dirty-cell' : 'x-grid-dirty-cell';
37374 var markup = ct.apply(p);
37376 cb[cb.length] = markup;
37378 lcb[lcb.length] = markup;
37382 if(stripe && ((rowIndex+1) % 2 == 0)){
37383 alt.push( "x-grid-row-alt");
37386 alt.push(" x-grid-dirty-row");
37389 if(this.getRowClass){
37390 alt.push( this.getRowClass(r, rowIndex));
37396 rowIndex : rowIndex,
37399 this.grid.fireEvent('rowclass', this, rowcfg);
37400 alt.push(rowcfg.rowClass);
37402 rp.alt = alt.join(" ");
37403 rp.cells = lcb.join("");
37404 lbuf[lbuf.length] = rt.apply(rp);
37405 rp.cells = cb.join("");
37406 buf[buf.length] = rt.apply(rp);
37408 return [lbuf.join(""), buf.join("")];
37411 renderBody : function(){
37412 var markup = this.renderRows();
37413 var bt = this.templates.body;
37414 return [bt.apply({rows: markup[0]}), bt.apply({rows: markup[1]})];
37418 * Refreshes the grid
37419 * @param {Boolean} headersToo
37421 refresh : function(headersToo){
37422 this.fireEvent("beforerefresh", this);
37423 this.grid.stopEditing();
37424 var result = this.renderBody();
37425 this.lockedBody.update(result[0]);
37426 this.mainBody.update(result[1]);
37427 if(headersToo === true){
37428 this.updateHeaders();
37429 this.updateColumns();
37430 this.updateSplitters();
37431 this.updateHeaderSortState();
37433 this.syncRowHeights();
37435 this.fireEvent("refresh", this);
37438 handleColumnMove : function(cm, oldIndex, newIndex){
37439 this.indexMap = null;
37440 var s = this.getScrollState();
37441 this.refresh(true);
37442 this.restoreScroll(s);
37443 this.afterMove(newIndex);
37446 afterMove : function(colIndex){
37447 if(this.enableMoveAnim && Roo.enableFx){
37448 this.fly(this.getHeaderCell(colIndex).firstChild).highlight(this.hlColor);
37450 // if multisort - fix sortOrder, and reload..
37451 if (this.grid.dataSource.multiSort) {
37452 // the we can call sort again..
37453 var dm = this.grid.dataSource;
37454 var cm = this.grid.colModel;
37456 for(var i = 0; i < cm.config.length; i++ ) {
37458 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined')) {
37459 continue; // dont' bother, it's not in sort list or being set.
37462 so.push(cm.config[i].dataIndex);
37465 dm.load(dm.lastOptions);
37472 updateCell : function(dm, rowIndex, dataIndex){
37473 var colIndex = this.getColumnIndexByDataIndex(dataIndex);
37474 if(typeof colIndex == "undefined"){ // not present in grid
37477 var cm = this.grid.colModel;
37478 var cell = this.getCell(rowIndex, colIndex);
37479 var cellText = this.getCellText(rowIndex, colIndex);
37482 cellId : "x-grid-cell-" + rowIndex + "-" + colIndex,
37483 id : cm.getColumnId(colIndex),
37484 css: colIndex == cm.getColumnCount()-1 ? "x-grid-col-last" : ""
37486 var renderer = cm.getRenderer(colIndex);
37487 var val = renderer(dm.getValueAt(rowIndex, dataIndex), p, rowIndex, colIndex, dm);
37488 if(typeof val == "undefined" || val === "") val = " ";
37489 cellText.innerHTML = val;
37490 cell.className = this.cellClass + " " + this.idToCssName(p.cellId) + " " + p.css;
37491 this.syncRowHeights(rowIndex, rowIndex);
37494 calcColumnWidth : function(colIndex, maxRowsToMeasure){
37496 if(this.grid.autoSizeHeaders){
37497 var h = this.getHeaderCellMeasure(colIndex);
37498 maxWidth = Math.max(maxWidth, h.scrollWidth);
37501 if(this.cm.isLocked(colIndex)){
37502 tb = this.getLockedTable();
37505 tb = this.getBodyTable();
37506 index = colIndex - this.cm.getLockedCount();
37509 var rows = tb.rows;
37510 var stopIndex = Math.min(maxRowsToMeasure || rows.length, rows.length);
37511 for(var i = 0; i < stopIndex; i++){
37512 var cell = rows[i].childNodes[index].firstChild;
37513 maxWidth = Math.max(maxWidth, cell.scrollWidth);
37516 return maxWidth + /*margin for error in IE*/ 5;
37519 * Autofit a column to its content.
37520 * @param {Number} colIndex
37521 * @param {Boolean} forceMinSize true to force the column to go smaller if possible
37523 autoSizeColumn : function(colIndex, forceMinSize, suppressEvent){
37524 if(this.cm.isHidden(colIndex)){
37525 return; // can't calc a hidden column
37528 var cid = this.cm.getColumnId(colIndex);
37529 this.css.updateRule(this.colSelector +this.idToCssName( cid), "width", this.grid.minColumnWidth + "px");
37530 if(this.grid.autoSizeHeaders){
37531 this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", this.grid.minColumnWidth + "px");
37534 var newWidth = this.calcColumnWidth(colIndex);
37535 this.cm.setColumnWidth(colIndex,
37536 Math.max(this.grid.minColumnWidth, newWidth), suppressEvent);
37537 if(!suppressEvent){
37538 this.grid.fireEvent("columnresize", colIndex, newWidth);
37543 * Autofits all columns to their content and then expands to fit any extra space in the grid
37545 autoSizeColumns : function(){
37546 var cm = this.grid.colModel;
37547 var colCount = cm.getColumnCount();
37548 for(var i = 0; i < colCount; i++){
37549 this.autoSizeColumn(i, true, true);
37551 if(cm.getTotalWidth() < this.scroller.dom.clientWidth){
37554 this.updateColumns();
37560 * Autofits all columns to the grid's width proportionate with their current size
37561 * @param {Boolean} reserveScrollSpace Reserve space for a scrollbar
37563 fitColumns : function(reserveScrollSpace){
37564 var cm = this.grid.colModel;
37565 var colCount = cm.getColumnCount();
37569 for (i = 0; i < colCount; i++){
37570 if(!cm.isHidden(i) && !cm.isFixed(i)){
37571 w = cm.getColumnWidth(i);
37577 var avail = Math.min(this.scroller.dom.clientWidth, this.el.getWidth());
37578 if(reserveScrollSpace){
37581 var frac = (avail - cm.getTotalWidth())/width;
37582 while (cols.length){
37585 cm.setColumnWidth(i, Math.floor(w + w*frac), true);
37587 this.updateColumns();
37591 onRowSelect : function(rowIndex){
37592 var row = this.getRowComposite(rowIndex);
37593 row.addClass("x-grid-row-selected");
37596 onRowDeselect : function(rowIndex){
37597 var row = this.getRowComposite(rowIndex);
37598 row.removeClass("x-grid-row-selected");
37601 onCellSelect : function(row, col){
37602 var cell = this.getCell(row, col);
37604 Roo.fly(cell).addClass("x-grid-cell-selected");
37608 onCellDeselect : function(row, col){
37609 var cell = this.getCell(row, col);
37611 Roo.fly(cell).removeClass("x-grid-cell-selected");
37615 updateHeaderSortState : function(){
37617 // sort state can be single { field: xxx, direction : yyy}
37618 // or { xxx=>ASC , yyy : DESC ..... }
37621 if (!this.ds.multiSort) {
37622 var state = this.ds.getSortState();
37626 mstate[state.field] = state.direction;
37627 // FIXME... - this is not used here.. but might be elsewhere..
37628 this.sortState = state;
37631 mstate = this.ds.sortToggle;
37633 //remove existing sort classes..
37635 var sc = this.sortClasses;
37636 var hds = this.el.select(this.headerSelector).removeClass(sc);
37638 for(var f in mstate) {
37640 var sortColumn = this.cm.findColumnIndex(f);
37642 if(sortColumn != -1){
37643 var sortDir = mstate[f];
37644 hds.item(sortColumn).addClass(sc[sortDir == "DESC" ? 1 : 0]);
37653 handleHeaderClick : function(g, index){
37654 if(this.headersDisabled){
37657 var dm = g.dataSource, cm = g.colModel;
37658 if(!cm.isSortable(index)){
37663 if (dm.multiSort) {
37664 // update the sortOrder
37666 for(var i = 0; i < cm.config.length; i++ ) {
37668 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined') && (index != i)) {
37669 continue; // dont' bother, it's not in sort list or being set.
37672 so.push(cm.config[i].dataIndex);
37678 dm.sort(cm.getDataIndex(index));
37682 destroy : function(){
37684 this.colMenu.removeAll();
37685 Roo.menu.MenuMgr.unregister(this.colMenu);
37686 this.colMenu.getEl().remove();
37687 delete this.colMenu;
37690 this.hmenu.removeAll();
37691 Roo.menu.MenuMgr.unregister(this.hmenu);
37692 this.hmenu.getEl().remove();
37695 if(this.grid.enableColumnMove){
37696 var dds = Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
37698 for(var dd in dds){
37699 if(!dds[dd].config.isTarget && dds[dd].dragElId){
37700 var elid = dds[dd].dragElId;
37702 Roo.get(elid).remove();
37703 } else if(dds[dd].config.isTarget){
37704 dds[dd].proxyTop.remove();
37705 dds[dd].proxyBottom.remove();
37708 if(Roo.dd.DDM.locationCache[dd]){
37709 delete Roo.dd.DDM.locationCache[dd];
37712 delete Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
37715 Roo.util.CSS.removeStyleSheet(this.idToCssName(this.grid.id) + '-cssrules');
37716 this.bind(null, null);
37717 Roo.EventManager.removeResizeListener(this.onWindowResize, this);
37720 handleLockChange : function(){
37721 this.refresh(true);
37724 onDenyColumnLock : function(){
37728 onDenyColumnHide : function(){
37732 handleHdMenuClick : function(item){
37733 var index = this.hdCtxIndex;
37734 var cm = this.cm, ds = this.ds;
37737 ds.sort(cm.getDataIndex(index), "ASC");
37740 ds.sort(cm.getDataIndex(index), "DESC");
37743 var lc = cm.getLockedCount();
37744 if(cm.getColumnCount(true) <= lc+1){
37745 this.onDenyColumnLock();
37749 cm.setLocked(index, true, true);
37750 cm.moveColumn(index, lc);
37751 this.grid.fireEvent("columnmove", index, lc);
37753 cm.setLocked(index, true);
37757 var lc = cm.getLockedCount();
37758 if((lc-1) != index){
37759 cm.setLocked(index, false, true);
37760 cm.moveColumn(index, lc-1);
37761 this.grid.fireEvent("columnmove", index, lc-1);
37763 cm.setLocked(index, false);
37767 index = cm.getIndexById(item.id.substr(4));
37769 if(item.checked && cm.getColumnCount(true) <= 1){
37770 this.onDenyColumnHide();
37773 cm.setHidden(index, item.checked);
37779 beforeColMenuShow : function(){
37780 var cm = this.cm, colCount = cm.getColumnCount();
37781 this.colMenu.removeAll();
37782 for(var i = 0; i < colCount; i++){
37783 this.colMenu.add(new Roo.menu.CheckItem({
37784 id: "col-"+cm.getColumnId(i),
37785 text: cm.getColumnHeader(i),
37786 checked: !cm.isHidden(i),
37792 handleHdCtx : function(g, index, e){
37794 var hd = this.getHeaderCell(index);
37795 this.hdCtxIndex = index;
37796 var ms = this.hmenu.items, cm = this.cm;
37797 ms.get("asc").setDisabled(!cm.isSortable(index));
37798 ms.get("desc").setDisabled(!cm.isSortable(index));
37799 if(this.grid.enableColLock !== false){
37800 ms.get("lock").setDisabled(cm.isLocked(index));
37801 ms.get("unlock").setDisabled(!cm.isLocked(index));
37803 this.hmenu.show(hd, "tl-bl");
37806 handleHdOver : function(e){
37807 var hd = this.findHeaderCell(e.getTarget());
37808 if(hd && !this.headersDisabled){
37809 if(this.grid.colModel.isSortable(this.getCellIndex(hd))){
37810 this.fly(hd).addClass("x-grid-hd-over");
37815 handleHdOut : function(e){
37816 var hd = this.findHeaderCell(e.getTarget());
37818 this.fly(hd).removeClass("x-grid-hd-over");
37822 handleSplitDblClick : function(e, t){
37823 var i = this.getCellIndex(t);
37824 if(this.grid.enableColumnResize !== false && this.cm.isResizable(i) && !this.cm.isFixed(i)){
37825 this.autoSizeColumn(i, true);
37830 render : function(){
37833 var colCount = cm.getColumnCount();
37835 if(this.grid.monitorWindowResize === true){
37836 Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
37838 var header = this.renderHeaders();
37839 var body = this.templates.body.apply({rows:""});
37840 var html = this.templates.master.apply({
37843 lockedHeader: header[0],
37847 //this.updateColumns();
37849 this.grid.getGridEl().dom.innerHTML = html;
37851 this.initElements();
37853 // a kludge to fix the random scolling effect in webkit
37854 this.el.on("scroll", function() {
37855 this.el.dom.scrollTop=0; // hopefully not recursive..
37858 this.scroller.on("scroll", this.handleScroll, this);
37859 this.lockedBody.on("mousewheel", this.handleWheel, this);
37860 this.mainBody.on("mousewheel", this.handleWheel, this);
37862 this.mainHd.on("mouseover", this.handleHdOver, this);
37863 this.mainHd.on("mouseout", this.handleHdOut, this);
37864 this.mainHd.on("dblclick", this.handleSplitDblClick, this,
37865 {delegate: "."+this.splitClass});
37867 this.lockedHd.on("mouseover", this.handleHdOver, this);
37868 this.lockedHd.on("mouseout", this.handleHdOut, this);
37869 this.lockedHd.on("dblclick", this.handleSplitDblClick, this,
37870 {delegate: "."+this.splitClass});
37872 if(this.grid.enableColumnResize !== false && Roo.grid.SplitDragZone){
37873 new Roo.grid.SplitDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
37876 this.updateSplitters();
37878 if(this.grid.enableColumnMove && Roo.grid.HeaderDragZone){
37879 new Roo.grid.HeaderDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
37880 new Roo.grid.HeaderDropZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
37883 if(this.grid.enableCtxMenu !== false && Roo.menu.Menu){
37884 this.hmenu = new Roo.menu.Menu({id: this.grid.id + "-hctx"});
37886 {id:"asc", text: this.sortAscText, cls: "xg-hmenu-sort-asc"},
37887 {id:"desc", text: this.sortDescText, cls: "xg-hmenu-sort-desc"}
37889 if(this.grid.enableColLock !== false){
37890 this.hmenu.add('-',
37891 {id:"lock", text: this.lockText, cls: "xg-hmenu-lock"},
37892 {id:"unlock", text: this.unlockText, cls: "xg-hmenu-unlock"}
37895 if(this.grid.enableColumnHide !== false){
37897 this.colMenu = new Roo.menu.Menu({id:this.grid.id + "-hcols-menu"});
37898 this.colMenu.on("beforeshow", this.beforeColMenuShow, this);
37899 this.colMenu.on("itemclick", this.handleHdMenuClick, this);
37901 this.hmenu.add('-',
37902 {id:"columns", text: this.columnsText, menu: this.colMenu}
37905 this.hmenu.on("itemclick", this.handleHdMenuClick, this);
37907 this.grid.on("headercontextmenu", this.handleHdCtx, this);
37910 if((this.grid.enableDragDrop || this.grid.enableDrag) && Roo.grid.GridDragZone){
37911 this.dd = new Roo.grid.GridDragZone(this.grid, {
37912 ddGroup : this.grid.ddGroup || 'GridDD'
37918 for(var i = 0; i < colCount; i++){
37919 if(cm.isHidden(i)){
37920 this.hideColumn(i);
37922 if(cm.config[i].align){
37923 this.css.updateRule(this.colSelector + i, "textAlign", cm.config[i].align);
37924 this.css.updateRule(this.hdSelector + i, "textAlign", cm.config[i].align);
37928 this.updateHeaderSortState();
37930 this.beforeInitialResize();
37933 // two part rendering gives faster view to the user
37934 this.renderPhase2.defer(1, this);
37937 renderPhase2 : function(){
37938 // render the rows now
37940 if(this.grid.autoSizeColumns){
37941 this.autoSizeColumns();
37945 beforeInitialResize : function(){
37949 onColumnSplitterMoved : function(i, w){
37950 this.userResized = true;
37951 var cm = this.grid.colModel;
37952 cm.setColumnWidth(i, w, true);
37953 var cid = cm.getColumnId(i);
37954 this.css.updateRule(this.colSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
37955 this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
37956 this.updateSplitters();
37958 this.grid.fireEvent("columnresize", i, w);
37961 syncRowHeights : function(startIndex, endIndex){
37962 if(this.grid.enableRowHeightSync === true && this.cm.getLockedCount() > 0){
37963 startIndex = startIndex || 0;
37964 var mrows = this.getBodyTable().rows;
37965 var lrows = this.getLockedTable().rows;
37966 var len = mrows.length-1;
37967 endIndex = Math.min(endIndex || len, len);
37968 for(var i = startIndex; i <= endIndex; i++){
37969 var m = mrows[i], l = lrows[i];
37970 var h = Math.max(m.offsetHeight, l.offsetHeight);
37971 m.style.height = l.style.height = h + "px";
37976 layout : function(initialRender, is2ndPass){
37978 var auto = g.autoHeight;
37979 var scrollOffset = 16;
37980 var c = g.getGridEl(), cm = this.cm,
37981 expandCol = g.autoExpandColumn,
37983 //c.beginMeasure();
37985 if(!c.dom.offsetWidth){ // display:none?
37987 this.lockedWrap.show();
37988 this.mainWrap.show();
37993 var hasLock = this.cm.isLocked(0);
37995 var tbh = this.headerPanel.getHeight();
37996 var bbh = this.footerPanel.getHeight();
37999 var ch = this.getBodyTable().offsetHeight + tbh + bbh + this.mainHd.getHeight();
38000 var newHeight = ch + c.getBorderWidth("tb");
38002 newHeight = Math.min(g.maxHeight, newHeight);
38004 c.setHeight(newHeight);
38008 c.setWidth(cm.getTotalWidth()+c.getBorderWidth('lr'));
38011 var s = this.scroller;
38013 var csize = c.getSize(true);
38015 this.el.setSize(csize.width, csize.height);
38017 this.headerPanel.setWidth(csize.width);
38018 this.footerPanel.setWidth(csize.width);
38020 var hdHeight = this.mainHd.getHeight();
38021 var vw = csize.width;
38022 var vh = csize.height - (tbh + bbh);
38026 var bt = this.getBodyTable();
38027 var ltWidth = hasLock ?
38028 Math.max(this.getLockedTable().offsetWidth, this.lockedHd.dom.firstChild.offsetWidth) : 0;
38030 var scrollHeight = bt.offsetHeight;
38031 var scrollWidth = ltWidth + bt.offsetWidth;
38032 var vscroll = false, hscroll = false;
38034 this.scrollSizer.setSize(scrollWidth, scrollHeight+hdHeight);
38036 var lw = this.lockedWrap, mw = this.mainWrap;
38037 var lb = this.lockedBody, mb = this.mainBody;
38039 setTimeout(function(){
38040 var t = s.dom.offsetTop;
38041 var w = s.dom.clientWidth,
38042 h = s.dom.clientHeight;
38045 lw.setSize(ltWidth, h);
38047 mw.setLeftTop(ltWidth, t);
38048 mw.setSize(w-ltWidth, h);
38050 lb.setHeight(h-hdHeight);
38051 mb.setHeight(h-hdHeight);
38053 if(is2ndPass !== true && !gv.userResized && expandCol){
38054 // high speed resize without full column calculation
38056 var ci = cm.getIndexById(expandCol);
38058 ci = cm.findColumnIndex(expandCol);
38060 ci = Math.max(0, ci); // make sure it's got at least the first col.
38061 var expandId = cm.getColumnId(ci);
38062 var tw = cm.getTotalWidth(false);
38063 var currentWidth = cm.getColumnWidth(ci);
38064 var cw = Math.min(Math.max(((w-tw)+currentWidth-2)-/*scrollbar*/(w <= s.dom.offsetWidth ? 0 : 18), g.autoExpandMin), g.autoExpandMax);
38065 if(currentWidth != cw){
38066 cm.setColumnWidth(ci, cw, true);
38067 gv.css.updateRule(gv.colSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
38068 gv.css.updateRule(gv.hdSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
38069 gv.updateSplitters();
38070 gv.layout(false, true);
38082 onWindowResize : function(){
38083 if(!this.grid.monitorWindowResize || this.grid.autoHeight){
38089 appendFooter : function(parentEl){
38093 sortAscText : "Sort Ascending",
38094 sortDescText : "Sort Descending",
38095 lockText : "Lock Column",
38096 unlockText : "Unlock Column",
38097 columnsText : "Columns"
38101 Roo.grid.GridView.ColumnDragZone = function(grid, hd){
38102 Roo.grid.GridView.ColumnDragZone.superclass.constructor.call(this, grid, hd, null);
38103 this.proxy.el.addClass('x-grid3-col-dd');
38106 Roo.extend(Roo.grid.GridView.ColumnDragZone, Roo.grid.HeaderDragZone, {
38107 handleMouseDown : function(e){
38111 callHandleMouseDown : function(e){
38112 Roo.grid.GridView.ColumnDragZone.superclass.handleMouseDown.call(this, e);
38117 * Ext JS Library 1.1.1
38118 * Copyright(c) 2006-2007, Ext JS, LLC.
38120 * Originally Released Under LGPL - original licence link has changed is not relivant.
38123 * <script type="text/javascript">
38127 // This is a support class used internally by the Grid components
38128 Roo.grid.SplitDragZone = function(grid, hd, hd2){
38130 this.view = grid.getView();
38131 this.proxy = this.view.resizeProxy;
38132 Roo.grid.SplitDragZone.superclass.constructor.call(this, hd,
38133 "gridSplitters" + this.grid.getGridEl().id, {
38134 dragElId : Roo.id(this.proxy.dom), resizeFrame:false
38136 this.setHandleElId(Roo.id(hd));
38137 this.setOuterHandleElId(Roo.id(hd2));
38138 this.scroll = false;
38140 Roo.extend(Roo.grid.SplitDragZone, Roo.dd.DDProxy, {
38141 fly: Roo.Element.fly,
38143 b4StartDrag : function(x, y){
38144 this.view.headersDisabled = true;
38145 this.proxy.setHeight(this.view.mainWrap.getHeight());
38146 var w = this.cm.getColumnWidth(this.cellIndex);
38147 var minw = Math.max(w-this.grid.minColumnWidth, 0);
38148 this.resetConstraints();
38149 this.setXConstraint(minw, 1000);
38150 this.setYConstraint(0, 0);
38151 this.minX = x - minw;
38152 this.maxX = x + 1000;
38154 Roo.dd.DDProxy.prototype.b4StartDrag.call(this, x, y);
38158 handleMouseDown : function(e){
38159 ev = Roo.EventObject.setEvent(e);
38160 var t = this.fly(ev.getTarget());
38161 if(t.hasClass("x-grid-split")){
38162 this.cellIndex = this.view.getCellIndex(t.dom);
38163 this.split = t.dom;
38164 this.cm = this.grid.colModel;
38165 if(this.cm.isResizable(this.cellIndex) && !this.cm.isFixed(this.cellIndex)){
38166 Roo.grid.SplitDragZone.superclass.handleMouseDown.apply(this, arguments);
38171 endDrag : function(e){
38172 this.view.headersDisabled = false;
38173 var endX = Math.max(this.minX, Roo.lib.Event.getPageX(e));
38174 var diff = endX - this.startPos;
38175 this.view.onColumnSplitterMoved(this.cellIndex, this.cm.getColumnWidth(this.cellIndex)+diff);
38178 autoOffset : function(){
38179 this.setDelta(0,0);
38183 * Ext JS Library 1.1.1
38184 * Copyright(c) 2006-2007, Ext JS, LLC.
38186 * Originally Released Under LGPL - original licence link has changed is not relivant.
38189 * <script type="text/javascript">
38193 // This is a support class used internally by the Grid components
38194 Roo.grid.GridDragZone = function(grid, config){
38195 this.view = grid.getView();
38196 Roo.grid.GridDragZone.superclass.constructor.call(this, this.view.mainBody.dom, config);
38197 if(this.view.lockedBody){
38198 this.setHandleElId(Roo.id(this.view.mainBody.dom));
38199 this.setOuterHandleElId(Roo.id(this.view.lockedBody.dom));
38201 this.scroll = false;
38203 this.ddel = document.createElement('div');
38204 this.ddel.className = 'x-grid-dd-wrap';
38207 Roo.extend(Roo.grid.GridDragZone, Roo.dd.DragZone, {
38208 ddGroup : "GridDD",
38210 getDragData : function(e){
38211 var t = Roo.lib.Event.getTarget(e);
38212 var rowIndex = this.view.findRowIndex(t);
38213 var sm = this.grid.selModel;
38215 //Roo.log(rowIndex);
38217 if (sm.getSelectedCell) {
38218 // cell selection..
38219 if (!sm.getSelectedCell()) {
38222 if (rowIndex != sm.getSelectedCell()[0]) {
38228 if(rowIndex !== false){
38233 //Roo.log([ sm.getSelectedCell() ? sm.getSelectedCell()[0] : 'NO' , rowIndex ]);
38235 //if(!sm.isSelected(rowIndex) || e.hasModifier()){
38238 if (e.hasModifier()){
38239 sm.handleMouseDown(e, t); // non modifier buttons are handled by row select.
38242 Roo.log("getDragData");
38247 rowIndex: rowIndex,
38248 selections:sm.getSelections ? sm.getSelections() : (
38249 sm.getSelectedCell() ? [ this.grid.ds.getAt(sm.getSelectedCell()[0]) ] : []
38256 onInitDrag : function(e){
38257 var data = this.dragData;
38258 this.ddel.innerHTML = this.grid.getDragDropText();
38259 this.proxy.update(this.ddel);
38260 // fire start drag?
38263 afterRepair : function(){
38264 this.dragging = false;
38267 getRepairXY : function(e, data){
38271 onEndDrag : function(data, e){
38275 onValidDrop : function(dd, e, id){
38280 beforeInvalidDrop : function(e, id){
38285 * Ext JS Library 1.1.1
38286 * Copyright(c) 2006-2007, Ext JS, LLC.
38288 * Originally Released Under LGPL - original licence link has changed is not relivant.
38291 * <script type="text/javascript">
38296 * @class Roo.grid.ColumnModel
38297 * @extends Roo.util.Observable
38298 * This is the default implementation of a ColumnModel used by the Grid. It defines
38299 * the columns in the grid.
38302 var colModel = new Roo.grid.ColumnModel([
38303 {header: "Ticker", width: 60, sortable: true, locked: true},
38304 {header: "Company Name", width: 150, sortable: true},
38305 {header: "Market Cap.", width: 100, sortable: true},
38306 {header: "$ Sales", width: 100, sortable: true, renderer: money},
38307 {header: "Employees", width: 100, sortable: true, resizable: false}
38312 * The config options listed for this class are options which may appear in each
38313 * individual column definition.
38314 * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
38316 * @param {Object} config An Array of column config objects. See this class's
38317 * config objects for details.
38319 Roo.grid.ColumnModel = function(config){
38321 * The config passed into the constructor
38323 this.config = config;
38326 // if no id, create one
38327 // if the column does not have a dataIndex mapping,
38328 // map it to the order it is in the config
38329 for(var i = 0, len = config.length; i < len; i++){
38331 if(typeof c.dataIndex == "undefined"){
38334 if(typeof c.renderer == "string"){
38335 c.renderer = Roo.util.Format[c.renderer];
38337 if(typeof c.id == "undefined"){
38340 if(c.editor && c.editor.xtype){
38341 c.editor = Roo.factory(c.editor, Roo.grid);
38343 if(c.editor && c.editor.isFormField){
38344 c.editor = new Roo.grid.GridEditor(c.editor);
38346 this.lookup[c.id] = c;
38350 * The width of columns which have no width specified (defaults to 100)
38353 this.defaultWidth = 100;
38356 * Default sortable of columns which have no sortable specified (defaults to false)
38359 this.defaultSortable = false;
38363 * @event widthchange
38364 * Fires when the width of a column changes.
38365 * @param {ColumnModel} this
38366 * @param {Number} columnIndex The column index
38367 * @param {Number} newWidth The new width
38369 "widthchange": true,
38371 * @event headerchange
38372 * Fires when the text of a header changes.
38373 * @param {ColumnModel} this
38374 * @param {Number} columnIndex The column index
38375 * @param {Number} newText The new header text
38377 "headerchange": true,
38379 * @event hiddenchange
38380 * Fires when a column is hidden or "unhidden".
38381 * @param {ColumnModel} this
38382 * @param {Number} columnIndex The column index
38383 * @param {Boolean} hidden true if hidden, false otherwise
38385 "hiddenchange": true,
38387 * @event columnmoved
38388 * Fires when a column is moved.
38389 * @param {ColumnModel} this
38390 * @param {Number} oldIndex
38391 * @param {Number} newIndex
38393 "columnmoved" : true,
38395 * @event columlockchange
38396 * Fires when a column's locked state is changed
38397 * @param {ColumnModel} this
38398 * @param {Number} colIndex
38399 * @param {Boolean} locked true if locked
38401 "columnlockchange" : true
38403 Roo.grid.ColumnModel.superclass.constructor.call(this);
38405 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
38407 * @cfg {String} header The header text to display in the Grid view.
38410 * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
38411 * {@link Roo.data.Record} definition from which to draw the column's value. If not
38412 * specified, the column's index is used as an index into the Record's data Array.
38415 * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
38416 * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
38419 * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
38420 * Defaults to the value of the {@link #defaultSortable} property.
38421 * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
38424 * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid. Defaults to false.
38427 * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed. Defaults to false.
38430 * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
38433 * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
38436 * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
38437 * given the cell's data value. See {@link #setRenderer}. If not specified, the
38438 * default renderer uses the raw data value.
38441 * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor
38444 * @cfg {String} align (Optional) Set the CSS text-align property of the column. Defaults to undefined.
38448 * Returns the id of the column at the specified index.
38449 * @param {Number} index The column index
38450 * @return {String} the id
38452 getColumnId : function(index){
38453 return this.config[index].id;
38457 * Returns the column for a specified id.
38458 * @param {String} id The column id
38459 * @return {Object} the column
38461 getColumnById : function(id){
38462 return this.lookup[id];
38467 * Returns the column for a specified dataIndex.
38468 * @param {String} dataIndex The column dataIndex
38469 * @return {Object|Boolean} the column or false if not found
38471 getColumnByDataIndex: function(dataIndex){
38472 var index = this.findColumnIndex(dataIndex);
38473 return index > -1 ? this.config[index] : false;
38477 * Returns the index for a specified column id.
38478 * @param {String} id The column id
38479 * @return {Number} the index, or -1 if not found
38481 getIndexById : function(id){
38482 for(var i = 0, len = this.config.length; i < len; i++){
38483 if(this.config[i].id == id){
38491 * Returns the index for a specified column dataIndex.
38492 * @param {String} dataIndex The column dataIndex
38493 * @return {Number} the index, or -1 if not found
38496 findColumnIndex : function(dataIndex){
38497 for(var i = 0, len = this.config.length; i < len; i++){
38498 if(this.config[i].dataIndex == dataIndex){
38506 moveColumn : function(oldIndex, newIndex){
38507 var c = this.config[oldIndex];
38508 this.config.splice(oldIndex, 1);
38509 this.config.splice(newIndex, 0, c);
38510 this.dataMap = null;
38511 this.fireEvent("columnmoved", this, oldIndex, newIndex);
38514 isLocked : function(colIndex){
38515 return this.config[colIndex].locked === true;
38518 setLocked : function(colIndex, value, suppressEvent){
38519 if(this.isLocked(colIndex) == value){
38522 this.config[colIndex].locked = value;
38523 if(!suppressEvent){
38524 this.fireEvent("columnlockchange", this, colIndex, value);
38528 getTotalLockedWidth : function(){
38529 var totalWidth = 0;
38530 for(var i = 0; i < this.config.length; i++){
38531 if(this.isLocked(i) && !this.isHidden(i)){
38532 this.totalWidth += this.getColumnWidth(i);
38538 getLockedCount : function(){
38539 for(var i = 0, len = this.config.length; i < len; i++){
38540 if(!this.isLocked(i)){
38547 * Returns the number of columns.
38550 getColumnCount : function(visibleOnly){
38551 if(visibleOnly === true){
38553 for(var i = 0, len = this.config.length; i < len; i++){
38554 if(!this.isHidden(i)){
38560 return this.config.length;
38564 * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
38565 * @param {Function} fn
38566 * @param {Object} scope (optional)
38567 * @return {Array} result
38569 getColumnsBy : function(fn, scope){
38571 for(var i = 0, len = this.config.length; i < len; i++){
38572 var c = this.config[i];
38573 if(fn.call(scope||this, c, i) === true){
38581 * Returns true if the specified column is sortable.
38582 * @param {Number} col The column index
38583 * @return {Boolean}
38585 isSortable : function(col){
38586 if(typeof this.config[col].sortable == "undefined"){
38587 return this.defaultSortable;
38589 return this.config[col].sortable;
38593 * Returns the rendering (formatting) function defined for the column.
38594 * @param {Number} col The column index.
38595 * @return {Function} The function used to render the cell. See {@link #setRenderer}.
38597 getRenderer : function(col){
38598 if(!this.config[col].renderer){
38599 return Roo.grid.ColumnModel.defaultRenderer;
38601 return this.config[col].renderer;
38605 * Sets the rendering (formatting) function for a column.
38606 * @param {Number} col The column index
38607 * @param {Function} fn The function to use to process the cell's raw data
38608 * to return HTML markup for the grid view. The render function is called with
38609 * the following parameters:<ul>
38610 * <li>Data value.</li>
38611 * <li>Cell metadata. An object in which you may set the following attributes:<ul>
38612 * <li>css A CSS style string to apply to the table cell.</li>
38613 * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
38614 * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
38615 * <li>Row index</li>
38616 * <li>Column index</li>
38617 * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
38619 setRenderer : function(col, fn){
38620 this.config[col].renderer = fn;
38624 * Returns the width for the specified column.
38625 * @param {Number} col The column index
38628 getColumnWidth : function(col){
38629 return this.config[col].width * 1 || this.defaultWidth;
38633 * Sets the width for a column.
38634 * @param {Number} col The column index
38635 * @param {Number} width The new width
38637 setColumnWidth : function(col, width, suppressEvent){
38638 this.config[col].width = width;
38639 this.totalWidth = null;
38640 if(!suppressEvent){
38641 this.fireEvent("widthchange", this, col, width);
38646 * Returns the total width of all columns.
38647 * @param {Boolean} includeHidden True to include hidden column widths
38650 getTotalWidth : function(includeHidden){
38651 if(!this.totalWidth){
38652 this.totalWidth = 0;
38653 for(var i = 0, len = this.config.length; i < len; i++){
38654 if(includeHidden || !this.isHidden(i)){
38655 this.totalWidth += this.getColumnWidth(i);
38659 return this.totalWidth;
38663 * Returns the header for the specified column.
38664 * @param {Number} col The column index
38667 getColumnHeader : function(col){
38668 return this.config[col].header;
38672 * Sets the header for a column.
38673 * @param {Number} col The column index
38674 * @param {String} header The new header
38676 setColumnHeader : function(col, header){
38677 this.config[col].header = header;
38678 this.fireEvent("headerchange", this, col, header);
38682 * Returns the tooltip for the specified column.
38683 * @param {Number} col The column index
38686 getColumnTooltip : function(col){
38687 return this.config[col].tooltip;
38690 * Sets the tooltip for a column.
38691 * @param {Number} col The column index
38692 * @param {String} tooltip The new tooltip
38694 setColumnTooltip : function(col, tooltip){
38695 this.config[col].tooltip = tooltip;
38699 * Returns the dataIndex for the specified column.
38700 * @param {Number} col The column index
38703 getDataIndex : function(col){
38704 return this.config[col].dataIndex;
38708 * Sets the dataIndex for a column.
38709 * @param {Number} col The column index
38710 * @param {Number} dataIndex The new dataIndex
38712 setDataIndex : function(col, dataIndex){
38713 this.config[col].dataIndex = dataIndex;
38719 * Returns true if the cell is editable.
38720 * @param {Number} colIndex The column index
38721 * @param {Number} rowIndex The row index
38722 * @return {Boolean}
38724 isCellEditable : function(colIndex, rowIndex){
38725 return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
38729 * Returns the editor defined for the cell/column.
38730 * return false or null to disable editing.
38731 * @param {Number} colIndex The column index
38732 * @param {Number} rowIndex The row index
38735 getCellEditor : function(colIndex, rowIndex){
38736 return this.config[colIndex].editor;
38740 * Sets if a column is editable.
38741 * @param {Number} col The column index
38742 * @param {Boolean} editable True if the column is editable
38744 setEditable : function(col, editable){
38745 this.config[col].editable = editable;
38750 * Returns true if the column is hidden.
38751 * @param {Number} colIndex The column index
38752 * @return {Boolean}
38754 isHidden : function(colIndex){
38755 return this.config[colIndex].hidden;
38760 * Returns true if the column width cannot be changed
38762 isFixed : function(colIndex){
38763 return this.config[colIndex].fixed;
38767 * Returns true if the column can be resized
38768 * @return {Boolean}
38770 isResizable : function(colIndex){
38771 return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
38774 * Sets if a column is hidden.
38775 * @param {Number} colIndex The column index
38776 * @param {Boolean} hidden True if the column is hidden
38778 setHidden : function(colIndex, hidden){
38779 this.config[colIndex].hidden = hidden;
38780 this.totalWidth = null;
38781 this.fireEvent("hiddenchange", this, colIndex, hidden);
38785 * Sets the editor for a column.
38786 * @param {Number} col The column index
38787 * @param {Object} editor The editor object
38789 setEditor : function(col, editor){
38790 this.config[col].editor = editor;
38794 Roo.grid.ColumnModel.defaultRenderer = function(value){
38795 if(typeof value == "string" && value.length < 1){
38801 // Alias for backwards compatibility
38802 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
38805 * Ext JS Library 1.1.1
38806 * Copyright(c) 2006-2007, Ext JS, LLC.
38808 * Originally Released Under LGPL - original licence link has changed is not relivant.
38811 * <script type="text/javascript">
38815 * @class Roo.grid.AbstractSelectionModel
38816 * @extends Roo.util.Observable
38817 * Abstract base class for grid SelectionModels. It provides the interface that should be
38818 * implemented by descendant classes. This class should not be directly instantiated.
38821 Roo.grid.AbstractSelectionModel = function(){
38822 this.locked = false;
38823 Roo.grid.AbstractSelectionModel.superclass.constructor.call(this);
38826 Roo.extend(Roo.grid.AbstractSelectionModel, Roo.util.Observable, {
38827 /** @ignore Called by the grid automatically. Do not call directly. */
38828 init : function(grid){
38834 * Locks the selections.
38837 this.locked = true;
38841 * Unlocks the selections.
38843 unlock : function(){
38844 this.locked = false;
38848 * Returns true if the selections are locked.
38849 * @return {Boolean}
38851 isLocked : function(){
38852 return this.locked;
38856 * Ext JS Library 1.1.1
38857 * Copyright(c) 2006-2007, Ext JS, LLC.
38859 * Originally Released Under LGPL - original licence link has changed is not relivant.
38862 * <script type="text/javascript">
38865 * @extends Roo.grid.AbstractSelectionModel
38866 * @class Roo.grid.RowSelectionModel
38867 * The default SelectionModel used by {@link Roo.grid.Grid}.
38868 * It supports multiple selections and keyboard selection/navigation.
38870 * @param {Object} config
38872 Roo.grid.RowSelectionModel = function(config){
38873 Roo.apply(this, config);
38874 this.selections = new Roo.util.MixedCollection(false, function(o){
38879 this.lastActive = false;
38883 * @event selectionchange
38884 * Fires when the selection changes
38885 * @param {SelectionModel} this
38887 "selectionchange" : true,
38889 * @event afterselectionchange
38890 * Fires after the selection changes (eg. by key press or clicking)
38891 * @param {SelectionModel} this
38893 "afterselectionchange" : true,
38895 * @event beforerowselect
38896 * Fires when a row is selected being selected, return false to cancel.
38897 * @param {SelectionModel} this
38898 * @param {Number} rowIndex The selected index
38899 * @param {Boolean} keepExisting False if other selections will be cleared
38901 "beforerowselect" : true,
38904 * Fires when a row is selected.
38905 * @param {SelectionModel} this
38906 * @param {Number} rowIndex The selected index
38907 * @param {Roo.data.Record} r The record
38909 "rowselect" : true,
38911 * @event rowdeselect
38912 * Fires when a row is deselected.
38913 * @param {SelectionModel} this
38914 * @param {Number} rowIndex The selected index
38916 "rowdeselect" : true
38918 Roo.grid.RowSelectionModel.superclass.constructor.call(this);
38919 this.locked = false;
38922 Roo.extend(Roo.grid.RowSelectionModel, Roo.grid.AbstractSelectionModel, {
38924 * @cfg {Boolean} singleSelect
38925 * True to allow selection of only one row at a time (defaults to false)
38927 singleSelect : false,
38930 initEvents : function(){
38932 if(!this.grid.enableDragDrop && !this.grid.enableDrag){
38933 this.grid.on("mousedown", this.handleMouseDown, this);
38934 }else{ // allow click to work like normal
38935 this.grid.on("rowclick", this.handleDragableRowClick, this);
38938 this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
38939 "up" : function(e){
38941 this.selectPrevious(e.shiftKey);
38942 }else if(this.last !== false && this.lastActive !== false){
38943 var last = this.last;
38944 this.selectRange(this.last, this.lastActive-1);
38945 this.grid.getView().focusRow(this.lastActive);
38946 if(last !== false){
38950 this.selectFirstRow();
38952 this.fireEvent("afterselectionchange", this);
38954 "down" : function(e){
38956 this.selectNext(e.shiftKey);
38957 }else if(this.last !== false && this.lastActive !== false){
38958 var last = this.last;
38959 this.selectRange(this.last, this.lastActive+1);
38960 this.grid.getView().focusRow(this.lastActive);
38961 if(last !== false){
38965 this.selectFirstRow();
38967 this.fireEvent("afterselectionchange", this);
38972 var view = this.grid.view;
38973 view.on("refresh", this.onRefresh, this);
38974 view.on("rowupdated", this.onRowUpdated, this);
38975 view.on("rowremoved", this.onRemove, this);
38979 onRefresh : function(){
38980 var ds = this.grid.dataSource, i, v = this.grid.view;
38981 var s = this.selections;
38982 s.each(function(r){
38983 if((i = ds.indexOfId(r.id)) != -1){
38992 onRemove : function(v, index, r){
38993 this.selections.remove(r);
38997 onRowUpdated : function(v, index, r){
38998 if(this.isSelected(r)){
38999 v.onRowSelect(index);
39005 * @param {Array} records The records to select
39006 * @param {Boolean} keepExisting (optional) True to keep existing selections
39008 selectRecords : function(records, keepExisting){
39010 this.clearSelections();
39012 var ds = this.grid.dataSource;
39013 for(var i = 0, len = records.length; i < len; i++){
39014 this.selectRow(ds.indexOf(records[i]), true);
39019 * Gets the number of selected rows.
39022 getCount : function(){
39023 return this.selections.length;
39027 * Selects the first row in the grid.
39029 selectFirstRow : function(){
39034 * Select the last row.
39035 * @param {Boolean} keepExisting (optional) True to keep existing selections
39037 selectLastRow : function(keepExisting){
39038 this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
39042 * Selects the row immediately following the last selected row.
39043 * @param {Boolean} keepExisting (optional) True to keep existing selections
39045 selectNext : function(keepExisting){
39046 if(this.last !== false && (this.last+1) < this.grid.dataSource.getCount()){
39047 this.selectRow(this.last+1, keepExisting);
39048 this.grid.getView().focusRow(this.last);
39053 * Selects the row that precedes the last selected row.
39054 * @param {Boolean} keepExisting (optional) True to keep existing selections
39056 selectPrevious : function(keepExisting){
39058 this.selectRow(this.last-1, keepExisting);
39059 this.grid.getView().focusRow(this.last);
39064 * Returns the selected records
39065 * @return {Array} Array of selected records
39067 getSelections : function(){
39068 return [].concat(this.selections.items);
39072 * Returns the first selected record.
39075 getSelected : function(){
39076 return this.selections.itemAt(0);
39081 * Clears all selections.
39083 clearSelections : function(fast){
39084 if(this.locked) return;
39086 var ds = this.grid.dataSource;
39087 var s = this.selections;
39088 s.each(function(r){
39089 this.deselectRow(ds.indexOfId(r.id));
39093 this.selections.clear();
39100 * Selects all rows.
39102 selectAll : function(){
39103 if(this.locked) return;
39104 this.selections.clear();
39105 for(var i = 0, len = this.grid.dataSource.getCount(); i < len; i++){
39106 this.selectRow(i, true);
39111 * Returns True if there is a selection.
39112 * @return {Boolean}
39114 hasSelection : function(){
39115 return this.selections.length > 0;
39119 * Returns True if the specified row is selected.
39120 * @param {Number/Record} record The record or index of the record to check
39121 * @return {Boolean}
39123 isSelected : function(index){
39124 var r = typeof index == "number" ? this.grid.dataSource.getAt(index) : index;
39125 return (r && this.selections.key(r.id) ? true : false);
39129 * Returns True if the specified record id is selected.
39130 * @param {String} id The id of record to check
39131 * @return {Boolean}
39133 isIdSelected : function(id){
39134 return (this.selections.key(id) ? true : false);
39138 handleMouseDown : function(e, t){
39139 var view = this.grid.getView(), rowIndex;
39140 if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
39143 if(e.shiftKey && this.last !== false){
39144 var last = this.last;
39145 this.selectRange(last, rowIndex, e.ctrlKey);
39146 this.last = last; // reset the last
39147 view.focusRow(rowIndex);
39149 var isSelected = this.isSelected(rowIndex);
39150 if(e.button !== 0 && isSelected){
39151 view.focusRow(rowIndex);
39152 }else if(e.ctrlKey && isSelected){
39153 this.deselectRow(rowIndex);
39154 }else if(!isSelected){
39155 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
39156 view.focusRow(rowIndex);
39159 this.fireEvent("afterselectionchange", this);
39162 handleDragableRowClick : function(grid, rowIndex, e)
39164 if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
39165 this.selectRow(rowIndex, false);
39166 grid.view.focusRow(rowIndex);
39167 this.fireEvent("afterselectionchange", this);
39172 * Selects multiple rows.
39173 * @param {Array} rows Array of the indexes of the row to select
39174 * @param {Boolean} keepExisting (optional) True to keep existing selections
39176 selectRows : function(rows, keepExisting){
39178 this.clearSelections();
39180 for(var i = 0, len = rows.length; i < len; i++){
39181 this.selectRow(rows[i], true);
39186 * Selects a range of rows. All rows in between startRow and endRow are also selected.
39187 * @param {Number} startRow The index of the first row in the range
39188 * @param {Number} endRow The index of the last row in the range
39189 * @param {Boolean} keepExisting (optional) True to retain existing selections
39191 selectRange : function(startRow, endRow, keepExisting){
39192 if(this.locked) return;
39194 this.clearSelections();
39196 if(startRow <= endRow){
39197 for(var i = startRow; i <= endRow; i++){
39198 this.selectRow(i, true);
39201 for(var i = startRow; i >= endRow; i--){
39202 this.selectRow(i, true);
39208 * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
39209 * @param {Number} startRow The index of the first row in the range
39210 * @param {Number} endRow The index of the last row in the range
39212 deselectRange : function(startRow, endRow, preventViewNotify){
39213 if(this.locked) return;
39214 for(var i = startRow; i <= endRow; i++){
39215 this.deselectRow(i, preventViewNotify);
39221 * @param {Number} row The index of the row to select
39222 * @param {Boolean} keepExisting (optional) True to keep existing selections
39224 selectRow : function(index, keepExisting, preventViewNotify){
39225 if(this.locked || (index < 0 || index >= this.grid.dataSource.getCount())) return;
39226 if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
39227 if(!keepExisting || this.singleSelect){
39228 this.clearSelections();
39230 var r = this.grid.dataSource.getAt(index);
39231 this.selections.add(r);
39232 this.last = this.lastActive = index;
39233 if(!preventViewNotify){
39234 this.grid.getView().onRowSelect(index);
39236 this.fireEvent("rowselect", this, index, r);
39237 this.fireEvent("selectionchange", this);
39243 * @param {Number} row The index of the row to deselect
39245 deselectRow : function(index, preventViewNotify){
39246 if(this.locked) return;
39247 if(this.last == index){
39250 if(this.lastActive == index){
39251 this.lastActive = false;
39253 var r = this.grid.dataSource.getAt(index);
39254 this.selections.remove(r);
39255 if(!preventViewNotify){
39256 this.grid.getView().onRowDeselect(index);
39258 this.fireEvent("rowdeselect", this, index);
39259 this.fireEvent("selectionchange", this);
39263 restoreLast : function(){
39265 this.last = this._last;
39270 acceptsNav : function(row, col, cm){
39271 return !cm.isHidden(col) && cm.isCellEditable(col, row);
39275 onEditorKey : function(field, e){
39276 var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
39281 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
39283 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
39285 }else if(k == e.ENTER && !e.ctrlKey){
39289 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
39291 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
39293 }else if(k == e.ESC){
39297 g.startEditing(newCell[0], newCell[1]);
39302 * Ext JS Library 1.1.1
39303 * Copyright(c) 2006-2007, Ext JS, LLC.
39305 * Originally Released Under LGPL - original licence link has changed is not relivant.
39308 * <script type="text/javascript">
39311 * @class Roo.grid.CellSelectionModel
39312 * @extends Roo.grid.AbstractSelectionModel
39313 * This class provides the basic implementation for cell selection in a grid.
39315 * @param {Object} config The object containing the configuration of this model.
39316 * @cfg {Boolean} enter_is_tab Enter behaves the same as tab. (eg. goes to next cell) default: false
39318 Roo.grid.CellSelectionModel = function(config){
39319 Roo.apply(this, config);
39321 this.selection = null;
39325 * @event beforerowselect
39326 * Fires before a cell is selected.
39327 * @param {SelectionModel} this
39328 * @param {Number} rowIndex The selected row index
39329 * @param {Number} colIndex The selected cell index
39331 "beforecellselect" : true,
39333 * @event cellselect
39334 * Fires when a cell is selected.
39335 * @param {SelectionModel} this
39336 * @param {Number} rowIndex The selected row index
39337 * @param {Number} colIndex The selected cell index
39339 "cellselect" : true,
39341 * @event selectionchange
39342 * Fires when the active selection changes.
39343 * @param {SelectionModel} this
39344 * @param {Object} selection null for no selection or an object (o) with two properties
39346 <li>o.record: the record object for the row the selection is in</li>
39347 <li>o.cell: An array of [rowIndex, columnIndex]</li>
39350 "selectionchange" : true,
39353 * Fires when the tab (or enter) was pressed on the last editable cell
39354 * You can use this to trigger add new row.
39355 * @param {SelectionModel} this
39359 * @event beforeeditnext
39360 * Fires before the next editable sell is made active
39361 * You can use this to skip to another cell or fire the tabend
39362 * if you set cell to false
39363 * @param {Object} eventdata object : { cell : [ row, col ] }
39365 "beforeeditnext" : true
39367 Roo.grid.CellSelectionModel.superclass.constructor.call(this);
39370 Roo.extend(Roo.grid.CellSelectionModel, Roo.grid.AbstractSelectionModel, {
39372 enter_is_tab: false,
39375 initEvents : function(){
39376 this.grid.on("mousedown", this.handleMouseDown, this);
39377 this.grid.getGridEl().on(Roo.isIE ? "keydown" : "keypress", this.handleKeyDown, this);
39378 var view = this.grid.view;
39379 view.on("refresh", this.onViewChange, this);
39380 view.on("rowupdated", this.onRowUpdated, this);
39381 view.on("beforerowremoved", this.clearSelections, this);
39382 view.on("beforerowsinserted", this.clearSelections, this);
39383 if(this.grid.isEditor){
39384 this.grid.on("beforeedit", this.beforeEdit, this);
39389 beforeEdit : function(e){
39390 this.select(e.row, e.column, false, true, e.record);
39394 onRowUpdated : function(v, index, r){
39395 if(this.selection && this.selection.record == r){
39396 v.onCellSelect(index, this.selection.cell[1]);
39401 onViewChange : function(){
39402 this.clearSelections(true);
39406 * Returns the currently selected cell,.
39407 * @return {Array} The selected cell (row, column) or null if none selected.
39409 getSelectedCell : function(){
39410 return this.selection ? this.selection.cell : null;
39414 * Clears all selections.
39415 * @param {Boolean} true to prevent the gridview from being notified about the change.
39417 clearSelections : function(preventNotify){
39418 var s = this.selection;
39420 if(preventNotify !== true){
39421 this.grid.view.onCellDeselect(s.cell[0], s.cell[1]);
39423 this.selection = null;
39424 this.fireEvent("selectionchange", this, null);
39429 * Returns true if there is a selection.
39430 * @return {Boolean}
39432 hasSelection : function(){
39433 return this.selection ? true : false;
39437 handleMouseDown : function(e, t){
39438 var v = this.grid.getView();
39439 if(this.isLocked()){
39442 var row = v.findRowIndex(t);
39443 var cell = v.findCellIndex(t);
39444 if(row !== false && cell !== false){
39445 this.select(row, cell);
39451 * @param {Number} rowIndex
39452 * @param {Number} collIndex
39454 select : function(rowIndex, colIndex, preventViewNotify, preventFocus, /*internal*/ r){
39455 if(this.fireEvent("beforecellselect", this, rowIndex, colIndex) !== false){
39456 this.clearSelections();
39457 r = r || this.grid.dataSource.getAt(rowIndex);
39460 cell : [rowIndex, colIndex]
39462 if(!preventViewNotify){
39463 var v = this.grid.getView();
39464 v.onCellSelect(rowIndex, colIndex);
39465 if(preventFocus !== true){
39466 v.focusCell(rowIndex, colIndex);
39469 this.fireEvent("cellselect", this, rowIndex, colIndex);
39470 this.fireEvent("selectionchange", this, this.selection);
39475 isSelectable : function(rowIndex, colIndex, cm){
39476 return !cm.isHidden(colIndex);
39480 handleKeyDown : function(e){
39481 //Roo.log('Cell Sel Model handleKeyDown');
39482 if(!e.isNavKeyPress()){
39485 var g = this.grid, s = this.selection;
39488 var cell = g.walkCells(0, 0, 1, this.isSelectable, this);
39490 this.select(cell[0], cell[1]);
39495 var walk = function(row, col, step){
39496 return g.walkCells(row, col, step, sm.isSelectable, sm);
39498 var k = e.getKey(), r = s.cell[0], c = s.cell[1];
39505 // handled by onEditorKey
39506 if (g.isEditor && g.editing) {
39510 newCell = walk(r, c-1, -1);
39512 newCell = walk(r, c+1, 1);
39517 newCell = walk(r+1, c, 1);
39521 newCell = walk(r-1, c, -1);
39525 newCell = walk(r, c+1, 1);
39529 newCell = walk(r, c-1, -1);
39534 if(g.isEditor && !g.editing){
39535 g.startEditing(r, c);
39544 this.select(newCell[0], newCell[1]);
39550 acceptsNav : function(row, col, cm){
39551 return !cm.isHidden(col) && cm.isCellEditable(col, row);
39555 * @param {Number} field (not used) - as it's normally used as a listener
39556 * @param {Number} e - event - fake it by using
39558 * var e = Roo.EventObjectImpl.prototype;
39559 * e.keyCode = e.TAB
39563 onEditorKey : function(field, e){
39565 var k = e.getKey(),
39568 ed = g.activeEditor,
39570 ///Roo.log('onEditorKey' + k);
39573 if (this.enter_is_tab && k == e.ENTER) {
39579 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
39581 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
39587 } else if(k == e.ENTER && !e.ctrlKey){
39590 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
39592 } else if(k == e.ESC){
39597 var ecall = { cell : newCell, forward : forward };
39598 this.fireEvent('beforeeditnext', ecall );
39599 newCell = ecall.cell;
39600 forward = ecall.forward;
39604 //Roo.log('next cell after edit');
39605 g.startEditing.defer(100, g, [newCell[0], newCell[1]]);
39606 } else if (forward) {
39607 // tabbed past last
39608 this.fireEvent.defer(100, this, ['tabend',this]);
39613 * Ext JS Library 1.1.1
39614 * Copyright(c) 2006-2007, Ext JS, LLC.
39616 * Originally Released Under LGPL - original licence link has changed is not relivant.
39619 * <script type="text/javascript">
39623 * @class Roo.grid.EditorGrid
39624 * @extends Roo.grid.Grid
39625 * Class for creating and editable grid.
39626 * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
39627 * The container MUST have some type of size defined for the grid to fill. The container will be
39628 * automatically set to position relative if it isn't already.
39629 * @param {Object} dataSource The data model to bind to
39630 * @param {Object} colModel The column model with info about this grid's columns
39632 Roo.grid.EditorGrid = function(container, config){
39633 Roo.grid.EditorGrid.superclass.constructor.call(this, container, config);
39634 this.getGridEl().addClass("xedit-grid");
39636 if(!this.selModel){
39637 this.selModel = new Roo.grid.CellSelectionModel();
39640 this.activeEditor = null;
39644 * @event beforeedit
39645 * Fires before cell editing is triggered. The edit event object has the following properties <br />
39646 * <ul style="padding:5px;padding-left:16px;">
39647 * <li>grid - This grid</li>
39648 * <li>record - The record being edited</li>
39649 * <li>field - The field name being edited</li>
39650 * <li>value - The value for the field being edited.</li>
39651 * <li>row - The grid row index</li>
39652 * <li>column - The grid column index</li>
39653 * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
39655 * @param {Object} e An edit event (see above for description)
39657 "beforeedit" : true,
39660 * Fires after a cell is edited. <br />
39661 * <ul style="padding:5px;padding-left:16px;">
39662 * <li>grid - This grid</li>
39663 * <li>record - The record being edited</li>
39664 * <li>field - The field name being edited</li>
39665 * <li>value - The value being set</li>
39666 * <li>originalValue - The original value for the field, before the edit.</li>
39667 * <li>row - The grid row index</li>
39668 * <li>column - The grid column index</li>
39670 * @param {Object} e An edit event (see above for description)
39672 "afteredit" : true,
39674 * @event validateedit
39675 * Fires after a cell is edited, but before the value is set in the record.
39676 * You can use this to modify the value being set in the field, Return false
39677 * to cancel the change. The edit event object has the following properties <br />
39678 * <ul style="padding:5px;padding-left:16px;">
39679 * <li>editor - This editor</li>
39680 * <li>grid - This grid</li>
39681 * <li>record - The record being edited</li>
39682 * <li>field - The field name being edited</li>
39683 * <li>value - The value being set</li>
39684 * <li>originalValue - The original value for the field, before the edit.</li>
39685 * <li>row - The grid row index</li>
39686 * <li>column - The grid column index</li>
39687 * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
39689 * @param {Object} e An edit event (see above for description)
39691 "validateedit" : true
39693 this.on("bodyscroll", this.stopEditing, this);
39694 this.on(this.clicksToEdit == 1 ? "cellclick" : "celldblclick", this.onCellDblClick, this);
39697 Roo.extend(Roo.grid.EditorGrid, Roo.grid.Grid, {
39699 * @cfg {Number} clicksToEdit
39700 * The number of clicks on a cell required to display the cell's editor (defaults to 2)
39707 trackMouseOver: false, // causes very odd FF errors
39709 onCellDblClick : function(g, row, col){
39710 this.startEditing(row, col);
39713 onEditComplete : function(ed, value, startValue){
39714 this.editing = false;
39715 this.activeEditor = null;
39716 ed.un("specialkey", this.selModel.onEditorKey, this.selModel);
39718 var field = this.colModel.getDataIndex(ed.col);
39723 originalValue: startValue,
39730 var cell = Roo.get(this.view.getCell(ed.row,ed.col))
39733 if(String(value) !== String(startValue)){
39735 if(this.fireEvent("validateedit", e) !== false && !e.cancel){
39736 r.set(field, e.value);
39737 // if we are dealing with a combo box..
39738 // then we also set the 'name' colum to be the displayField
39739 if (ed.field.displayField && ed.field.name) {
39740 r.set(ed.field.name, ed.field.el.dom.value);
39743 delete e.cancel; //?? why!!!
39744 this.fireEvent("afteredit", e);
39747 this.fireEvent("afteredit", e); // always fire it!
39749 this.view.focusCell(ed.row, ed.col);
39753 * Starts editing the specified for the specified row/column
39754 * @param {Number} rowIndex
39755 * @param {Number} colIndex
39757 startEditing : function(row, col){
39758 this.stopEditing();
39759 if(this.colModel.isCellEditable(col, row)){
39760 this.view.ensureVisible(row, col, true);
39762 var r = this.dataSource.getAt(row);
39763 var field = this.colModel.getDataIndex(col);
39764 var cell = Roo.get(this.view.getCell(row,col));
39769 value: r.data[field],
39774 if(this.fireEvent("beforeedit", e) !== false && !e.cancel){
39775 this.editing = true;
39776 var ed = this.colModel.getCellEditor(col, row);
39782 ed.render(ed.parentEl || document.body);
39788 (function(){ // complex but required for focus issues in safari, ie and opera
39792 ed.on("complete", this.onEditComplete, this, {single: true});
39793 ed.on("specialkey", this.selModel.onEditorKey, this.selModel);
39794 this.activeEditor = ed;
39795 var v = r.data[field];
39796 ed.startEdit(this.view.getCell(row, col), v);
39797 // combo's with 'displayField and name set
39798 if (ed.field.displayField && ed.field.name) {
39799 ed.field.el.dom.value = r.data[ed.field.name];
39803 }).defer(50, this);
39809 * Stops any active editing
39811 stopEditing : function(){
39812 if(this.activeEditor){
39813 this.activeEditor.completeEdit();
39815 this.activeEditor = null;
39819 * Called to get grid's drag proxy text, by default returns this.ddText.
39822 getDragDropText : function(){
39823 var count = this.selModel.getSelectedCell() ? 1 : 0;
39824 return String.format(this.ddText, count, count == 1 ? '' : 's');
39829 * Ext JS Library 1.1.1
39830 * Copyright(c) 2006-2007, Ext JS, LLC.
39832 * Originally Released Under LGPL - original licence link has changed is not relivant.
39835 * <script type="text/javascript">
39838 // private - not really -- you end up using it !
39839 // This is a support class used internally by the Grid components
39842 * @class Roo.grid.GridEditor
39843 * @extends Roo.Editor
39844 * Class for creating and editable grid elements.
39845 * @param {Object} config any settings (must include field)
39847 Roo.grid.GridEditor = function(field, config){
39848 if (!config && field.field) {
39850 field = Roo.factory(config.field, Roo.form);
39852 Roo.grid.GridEditor.superclass.constructor.call(this, field, config);
39853 field.monitorTab = false;
39856 Roo.extend(Roo.grid.GridEditor, Roo.Editor, {
39859 * @cfg {Roo.form.Field} field Field to wrap (or xtyped)
39862 alignment: "tl-tl",
39865 cls: "x-small-editor x-grid-editor",
39870 * Ext JS Library 1.1.1
39871 * Copyright(c) 2006-2007, Ext JS, LLC.
39873 * Originally Released Under LGPL - original licence link has changed is not relivant.
39876 * <script type="text/javascript">
39881 Roo.grid.PropertyRecord = Roo.data.Record.create([
39882 {name:'name',type:'string'}, 'value'
39886 Roo.grid.PropertyStore = function(grid, source){
39888 this.store = new Roo.data.Store({
39889 recordType : Roo.grid.PropertyRecord
39891 this.store.on('update', this.onUpdate, this);
39893 this.setSource(source);
39895 Roo.grid.PropertyStore.superclass.constructor.call(this);
39900 Roo.extend(Roo.grid.PropertyStore, Roo.util.Observable, {
39901 setSource : function(o){
39903 this.store.removeAll();
39906 if(this.isEditableValue(o[k])){
39907 data.push(new Roo.grid.PropertyRecord({name: k, value: o[k]}, k));
39910 this.store.loadRecords({records: data}, {}, true);
39913 onUpdate : function(ds, record, type){
39914 if(type == Roo.data.Record.EDIT){
39915 var v = record.data['value'];
39916 var oldValue = record.modified['value'];
39917 if(this.grid.fireEvent('beforepropertychange', this.source, record.id, v, oldValue) !== false){
39918 this.source[record.id] = v;
39920 this.grid.fireEvent('propertychange', this.source, record.id, v, oldValue);
39927 getProperty : function(row){
39928 return this.store.getAt(row);
39931 isEditableValue: function(val){
39932 if(val && val instanceof Date){
39934 }else if(typeof val == 'object' || typeof val == 'function'){
39940 setValue : function(prop, value){
39941 this.source[prop] = value;
39942 this.store.getById(prop).set('value', value);
39945 getSource : function(){
39946 return this.source;
39950 Roo.grid.PropertyColumnModel = function(grid, store){
39953 g.PropertyColumnModel.superclass.constructor.call(this, [
39954 {header: this.nameText, sortable: true, dataIndex:'name', id: 'name'},
39955 {header: this.valueText, resizable:false, dataIndex: 'value', id: 'value'}
39957 this.store = store;
39958 this.bselect = Roo.DomHelper.append(document.body, {
39959 tag: 'select', style:'display:none', cls: 'x-grid-editor', children: [
39960 {tag: 'option', value: 'true', html: 'true'},
39961 {tag: 'option', value: 'false', html: 'false'}
39964 Roo.id(this.bselect);
39967 'date' : new g.GridEditor(new f.DateField({selectOnFocus:true})),
39968 'string' : new g.GridEditor(new f.TextField({selectOnFocus:true})),
39969 'number' : new g.GridEditor(new f.NumberField({selectOnFocus:true, style:'text-align:left;'})),
39970 'int' : new g.GridEditor(new f.NumberField({selectOnFocus:true, allowDecimals:false, style:'text-align:left;'})),
39971 'boolean' : new g.GridEditor(new f.Field({el:this.bselect,selectOnFocus:true}))
39973 this.renderCellDelegate = this.renderCell.createDelegate(this);
39974 this.renderPropDelegate = this.renderProp.createDelegate(this);
39977 Roo.extend(Roo.grid.PropertyColumnModel, Roo.grid.ColumnModel, {
39981 valueText : 'Value',
39983 dateFormat : 'm/j/Y',
39986 renderDate : function(dateVal){
39987 return dateVal.dateFormat(this.dateFormat);
39990 renderBool : function(bVal){
39991 return bVal ? 'true' : 'false';
39994 isCellEditable : function(colIndex, rowIndex){
39995 return colIndex == 1;
39998 getRenderer : function(col){
40000 this.renderCellDelegate : this.renderPropDelegate;
40003 renderProp : function(v){
40004 return this.getPropertyName(v);
40007 renderCell : function(val){
40009 if(val instanceof Date){
40010 rv = this.renderDate(val);
40011 }else if(typeof val == 'boolean'){
40012 rv = this.renderBool(val);
40014 return Roo.util.Format.htmlEncode(rv);
40017 getPropertyName : function(name){
40018 var pn = this.grid.propertyNames;
40019 return pn && pn[name] ? pn[name] : name;
40022 getCellEditor : function(colIndex, rowIndex){
40023 var p = this.store.getProperty(rowIndex);
40024 var n = p.data['name'], val = p.data['value'];
40026 if(typeof(this.grid.customEditors[n]) == 'string'){
40027 return this.editors[this.grid.customEditors[n]];
40029 if(typeof(this.grid.customEditors[n]) != 'undefined'){
40030 return this.grid.customEditors[n];
40032 if(val instanceof Date){
40033 return this.editors['date'];
40034 }else if(typeof val == 'number'){
40035 return this.editors['number'];
40036 }else if(typeof val == 'boolean'){
40037 return this.editors['boolean'];
40039 return this.editors['string'];
40045 * @class Roo.grid.PropertyGrid
40046 * @extends Roo.grid.EditorGrid
40047 * This class represents the interface of a component based property grid control.
40048 * <br><br>Usage:<pre><code>
40049 var grid = new Roo.grid.PropertyGrid("my-container-id", {
40057 * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
40058 * The container MUST have some type of size defined for the grid to fill. The container will be
40059 * automatically set to position relative if it isn't already.
40060 * @param {Object} config A config object that sets properties on this grid.
40062 Roo.grid.PropertyGrid = function(container, config){
40063 config = config || {};
40064 var store = new Roo.grid.PropertyStore(this);
40065 this.store = store;
40066 var cm = new Roo.grid.PropertyColumnModel(this, store);
40067 store.store.sort('name', 'ASC');
40068 Roo.grid.PropertyGrid.superclass.constructor.call(this, container, Roo.apply({
40071 enableColLock:false,
40072 enableColumnMove:false,
40074 trackMouseOver: false,
40077 this.getGridEl().addClass('x-props-grid');
40078 this.lastEditRow = null;
40079 this.on('columnresize', this.onColumnResize, this);
40082 * @event beforepropertychange
40083 * Fires before a property changes (return false to stop?)
40084 * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
40085 * @param {String} id Record Id
40086 * @param {String} newval New Value
40087 * @param {String} oldval Old Value
40089 "beforepropertychange": true,
40091 * @event propertychange
40092 * Fires after a property changes
40093 * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
40094 * @param {String} id Record Id
40095 * @param {String} newval New Value
40096 * @param {String} oldval Old Value
40098 "propertychange": true
40100 this.customEditors = this.customEditors || {};
40102 Roo.extend(Roo.grid.PropertyGrid, Roo.grid.EditorGrid, {
40105 * @cfg {Object} customEditors map of colnames=> custom editors.
40106 * the custom editor can be one of the standard ones (date|string|number|int|boolean), or a
40107 * grid editor eg. Roo.grid.GridEditor(new Roo.form.TextArea({selectOnFocus:true})),
40108 * false disables editing of the field.
40112 * @cfg {Object} propertyNames map of property Names to their displayed value
40115 render : function(){
40116 Roo.grid.PropertyGrid.superclass.render.call(this);
40117 this.autoSize.defer(100, this);
40120 autoSize : function(){
40121 Roo.grid.PropertyGrid.superclass.autoSize.call(this);
40123 this.view.fitColumns();
40127 onColumnResize : function(){
40128 this.colModel.setColumnWidth(1, this.container.getWidth(true)-this.colModel.getColumnWidth(0));
40132 * Sets the data for the Grid
40133 * accepts a Key => Value object of all the elements avaiable.
40134 * @param {Object} data to appear in grid.
40136 setSource : function(source){
40137 this.store.setSource(source);
40141 * Gets all the data from the grid.
40142 * @return {Object} data data stored in grid
40144 getSource : function(){
40145 return this.store.getSource();
40149 * Ext JS Library 1.1.1
40150 * Copyright(c) 2006-2007, Ext JS, LLC.
40152 * Originally Released Under LGPL - original licence link has changed is not relivant.
40155 * <script type="text/javascript">
40159 * @class Roo.LoadMask
40160 * A simple utility class for generically masking elements while loading data. If the element being masked has
40161 * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
40162 * process and the mask element will be cached for reuse. For all other elements, this mask will replace the
40163 * element's UpdateManager load indicator and will be destroyed after the initial load.
40165 * Create a new LoadMask
40166 * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
40167 * @param {Object} config The config object
40169 Roo.LoadMask = function(el, config){
40170 this.el = Roo.get(el);
40171 Roo.apply(this, config);
40173 this.store.on('beforeload', this.onBeforeLoad, this);
40174 this.store.on('load', this.onLoad, this);
40175 this.store.on('loadexception', this.onLoadException, this);
40176 this.removeMask = false;
40178 var um = this.el.getUpdateManager();
40179 um.showLoadIndicator = false; // disable the default indicator
40180 um.on('beforeupdate', this.onBeforeLoad, this);
40181 um.on('update', this.onLoad, this);
40182 um.on('failure', this.onLoad, this);
40183 this.removeMask = true;
40187 Roo.LoadMask.prototype = {
40189 * @cfg {Boolean} removeMask
40190 * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
40191 * False to persist the mask element reference for multiple uses (e.g., for paged data widgets). Defaults to false.
40194 * @cfg {String} msg
40195 * The text to display in a centered loading message box (defaults to 'Loading...')
40197 msg : 'Loading...',
40199 * @cfg {String} msgCls
40200 * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
40202 msgCls : 'x-mask-loading',
40205 * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
40211 * Disables the mask to prevent it from being displayed
40213 disable : function(){
40214 this.disabled = true;
40218 * Enables the mask so that it can be displayed
40220 enable : function(){
40221 this.disabled = false;
40224 onLoadException : function()
40226 Roo.log(arguments);
40228 if (typeof(arguments[3]) != 'undefined') {
40229 Roo.MessageBox.alert("Error loading",arguments[3]);
40233 if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
40234 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
40243 this.el.unmask(this.removeMask);
40246 onLoad : function()
40248 this.el.unmask(this.removeMask);
40252 onBeforeLoad : function(){
40253 if(!this.disabled){
40254 this.el.mask(this.msg, this.msgCls);
40259 destroy : function(){
40261 this.store.un('beforeload', this.onBeforeLoad, this);
40262 this.store.un('load', this.onLoad, this);
40263 this.store.un('loadexception', this.onLoadException, this);
40265 var um = this.el.getUpdateManager();
40266 um.un('beforeupdate', this.onBeforeLoad, this);
40267 um.un('update', this.onLoad, this);
40268 um.un('failure', this.onLoad, this);
40273 * Ext JS Library 1.1.1
40274 * Copyright(c) 2006-2007, Ext JS, LLC.
40276 * Originally Released Under LGPL - original licence link has changed is not relivant.
40279 * <script type="text/javascript">
40284 * @class Roo.XTemplate
40285 * @extends Roo.Template
40286 * Provides a template that can have nested templates for loops or conditionals. The syntax is:
40288 var t = new Roo.XTemplate(
40289 '<select name="{name}">',
40290 '<tpl for="options"><option value="{value:trim}">{text:ellipsis(10)}</option></tpl>',
40294 // then append, applying the master template values
40297 * Supported features:
40302 {a_variable} - output encoded.
40303 {a_variable.format:("Y-m-d")} - call a method on the variable
40304 {a_variable:raw} - unencoded output
40305 {a_variable:toFixed(1,2)} - Roo.util.Format."toFixed"
40306 {a_variable:this.method_on_template(...)} - call a method on the template object.
40311 <tpl for="a_variable or condition.."></tpl>
40312 <tpl if="a_variable or condition"></tpl>
40313 <tpl exec="some javascript"></tpl>
40314 <tpl name="named_template"></tpl> (experimental)
40316 <tpl for="."></tpl> - just iterate the property..
40317 <tpl for=".."></tpl> - iterates with the parent (probably the template)
40321 Roo.XTemplate = function()
40323 Roo.XTemplate.superclass.constructor.apply(this, arguments);
40330 Roo.extend(Roo.XTemplate, Roo.Template, {
40333 * The various sub templates
40338 * basic tag replacing syntax
40341 * // you can fake an object call by doing this
40345 re : /\{([\w-\.]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
40348 * compile the template
40350 * This is not recursive, so I'm not sure how nested templates are really going to be handled..
40353 compile: function()
40357 s = ['<tpl>', s, '</tpl>'].join('');
40359 var re = /<tpl\b[^>]*>((?:(?=([^<]+))\2|<(?!tpl\b[^>]*>))*?)<\/tpl>/,
40360 nameRe = /^<tpl\b[^>]*?for="(.*?)"/,
40361 ifRe = /^<tpl\b[^>]*?if="(.*?)"/,
40362 execRe = /^<tpl\b[^>]*?exec="(.*?)"/,
40363 namedRe = /^<tpl\b[^>]*?name="(\w+)"/, // named templates..
40368 while(true == !!(m = s.match(re))){
40369 var forMatch = m[0].match(nameRe),
40370 ifMatch = m[0].match(ifRe),
40371 execMatch = m[0].match(execRe),
40372 namedMatch = m[0].match(namedRe),
40377 name = forMatch && forMatch[1] ? forMatch[1] : '';
40380 // if - puts fn into test..
40381 exp = ifMatch && ifMatch[1] ? ifMatch[1] : null;
40383 fn = new Function('values', 'parent', 'with(values){ return '+(Roo.util.Format.htmlDecode(exp))+'; }');
40388 // exec - calls a function... returns empty if true is returned.
40389 exp = execMatch && execMatch[1] ? execMatch[1] : null;
40391 exec = new Function('values', 'parent', 'with(values){ '+(Roo.util.Format.htmlDecode(exp))+'; }');
40399 case '.': name = new Function('values', 'parent', 'with(values){ return values; }'); break;
40400 case '..': name = new Function('values', 'parent', 'with(values){ return parent; }'); break;
40401 default: name = new Function('values', 'parent', 'with(values){ return '+name+'; }');
40404 var uid = namedMatch ? namedMatch[1] : id;
40408 id: namedMatch ? namedMatch[1] : id,
40415 s = s.replace(m[0], '');
40417 s = s.replace(m[0], '{xtpl'+ id + '}');
40422 for(var i = tpls.length-1; i >= 0; --i){
40423 this.compileTpl(tpls[i]);
40424 this.tpls[tpls[i].id] = tpls[i];
40426 this.master = tpls[tpls.length-1];
40430 * same as applyTemplate, except it's done to one of the subTemplates
40431 * when using named templates, you can do:
40433 * var str = pl.applySubTemplate('your-name', values);
40436 * @param {Number} id of the template
40437 * @param {Object} values to apply to template
40438 * @param {Object} parent (normaly the instance of this object)
40440 applySubTemplate : function(id, values, parent)
40444 var t = this.tpls[id];
40448 if(t.test && !t.test.call(this, values, parent)){
40452 Roo.log("Xtemplate.applySubTemplate 'test': Exception thrown");
40453 Roo.log(e.toString());
40459 if(t.exec && t.exec.call(this, values, parent)){
40463 Roo.log("Xtemplate.applySubTemplate 'exec': Exception thrown");
40464 Roo.log(e.toString());
40469 var vs = t.target ? t.target.call(this, values, parent) : values;
40470 parent = t.target ? values : parent;
40471 if(t.target && vs instanceof Array){
40473 for(var i = 0, len = vs.length; i < len; i++){
40474 buf[buf.length] = t.compiled.call(this, vs[i], parent);
40476 return buf.join('');
40478 return t.compiled.call(this, vs, parent);
40480 Roo.log("Xtemplate.applySubTemplate : Exception thrown");
40481 Roo.log(e.toString());
40482 Roo.log(t.compiled);
40487 compileTpl : function(tpl)
40489 var fm = Roo.util.Format;
40490 var useF = this.disableFormats !== true;
40491 var sep = Roo.isGecko ? "+" : ",";
40492 var undef = function(str) {
40493 Roo.log("Property not found :" + str);
40497 var fn = function(m, name, format, args)
40499 //Roo.log(arguments);
40500 args = args ? args.replace(/\\'/g,"'") : args;
40501 //["{TEST:(a,b,c)}", "TEST", "", "a,b,c", 0, "{TEST:(a,b,c)}"]
40502 if (typeof(format) == 'undefined') {
40503 format= 'htmlEncode';
40505 if (format == 'raw' ) {
40509 if(name.substr(0, 4) == 'xtpl'){
40510 return "'"+ sep +'this.applySubTemplate('+name.substr(4)+', values, parent)'+sep+"'";
40513 // build an array of options to determine if value is undefined..
40515 // basically get 'xxxx.yyyy' then do
40516 // (typeof(xxxx) == 'undefined' || typeof(xxx.yyyy) == 'undefined') ?
40517 // (function () { Roo.log("Property not found"); return ''; })() :
40522 Roo.each(name.split('.'), function(st) {
40523 lookfor += (lookfor.length ? '.': '') + st;
40524 udef_ar.push( "(typeof(" + lookfor + ") == 'undefined')" );
40527 var udef_st = '((' + udef_ar.join(" || ") +") ? undef('" + name + "') : "; // .. needs )
40530 if(format && useF){
40532 args = args ? ',' + args : "";
40534 if(format.substr(0, 5) != "this."){
40535 format = "fm." + format + '(';
40537 format = 'this.call("'+ format.substr(5) + '", ';
40541 return "'"+ sep + udef_st + format + name + args + "))"+sep+"'";
40545 // called with xxyx.yuu:(test,test)
40547 return "'"+ sep + udef_st + name + '(' + args + "))"+sep+"'";
40549 // raw.. - :raw modifier..
40550 return "'"+ sep + udef_st + name + ")"+sep+"'";
40554 // branched to use + in gecko and [].join() in others
40556 body = "tpl.compiled = function(values, parent){ with(values) { return '" +
40557 tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
40560 body = ["tpl.compiled = function(values, parent){ with (values) { return ['"];
40561 body.push(tpl.body.replace(/(\r\n|\n)/g,
40562 '\\n').replace(/'/g, "\\'").replace(this.re, fn));
40563 body.push("'].join('');};};");
40564 body = body.join('');
40567 Roo.debug && Roo.log(body.replace(/\\n/,'\n'));
40569 /** eval:var:tpl eval:var:fm eval:var:useF eval:var:undef */
40575 applyTemplate : function(values){
40576 return this.master.compiled.call(this, values, {});
40577 //var s = this.subs;
40580 apply : function(){
40581 return this.applyTemplate.apply(this, arguments);
40586 Roo.XTemplate.from = function(el){
40587 el = Roo.getDom(el);
40588 return new Roo.XTemplate(el.value || el.innerHTML);
40590 * Original code for Roojs - LGPL
40591 * <script type="text/javascript">
40595 * @class Roo.XComponent
40596 * A delayed Element creator...
40597 * Or a way to group chunks of interface together.
40599 * Mypart.xyx = new Roo.XComponent({
40601 parent : 'Mypart.xyz', // empty == document.element.!!
40605 disabled : function() {}
40607 tree : function() { // return an tree of xtype declared components
40611 xtype : 'NestedLayoutPanel',
40618 * It can be used to build a big heiracy, with parent etc.
40619 * or you can just use this to render a single compoent to a dom element
40620 * MYPART.render(Roo.Element | String(id) | dom_element )
40622 * @extends Roo.util.Observable
40624 * @param cfg {Object} configuration of component
40627 Roo.XComponent = function(cfg) {
40628 Roo.apply(this, cfg);
40632 * Fires when this the componnt is built
40633 * @param {Roo.XComponent} c the component
40638 this.region = this.region || 'center'; // default..
40639 Roo.XComponent.register(this);
40640 this.modules = false;
40641 this.el = false; // where the layout goes..
40645 Roo.extend(Roo.XComponent, Roo.util.Observable, {
40648 * The created element (with Roo.factory())
40649 * @type {Roo.Layout}
40655 * for BC - use el in new code
40656 * @type {Roo.Layout}
40662 * for BC - use el in new code
40663 * @type {Roo.Layout}
40668 * @cfg {Function|boolean} disabled
40669 * If this module is disabled by some rule, return true from the funtion
40674 * @cfg {String} parent
40675 * Name of parent element which it get xtype added to..
40680 * @cfg {String} order
40681 * Used to set the order in which elements are created (usefull for multiple tabs)
40686 * @cfg {String} name
40687 * String to display while loading.
40691 * @cfg {String} region
40692 * Region to render component to (defaults to center)
40697 * @cfg {Array} items
40698 * A single item array - the first element is the root of the tree..
40699 * It's done this way to stay compatible with the Xtype system...
40705 * The method that retuns the tree of parts that make up this compoennt
40712 * render element to dom or tree
40713 * @param {Roo.Element|String|DomElement} optional render to if parent is not set.
40716 render : function(el)
40720 var hp = this.parent ? 1 : 0;
40722 if (!el && typeof(this.parent) == 'string' && this.parent.substring(0,1) == '#') {
40723 // if parent is a '#.....' string, then let's use that..
40724 var ename = this.parent.substr(1)
40725 this.parent = (this.parent == '#bootstrap') ? { el : true} : false; // flags it as a top module...
40726 el = Roo.get(ename);
40727 if (!el && !this.parent) {
40728 Roo.log("Warning - element can not be found :#" + ename );
40734 if (!this.parent) {
40736 el = el ? Roo.get(el) : false;
40738 // it's a top level one..
40740 el : new Roo.BorderLayout(el || document.body, {
40746 tabPosition: 'top',
40747 //resizeTabs: true,
40748 alwaysShowTabs: el && hp? false : true,
40749 hideTabs: el || !hp ? true : false,
40756 if (!this.parent.el) {
40757 // probably an old style ctor, which has been disabled.
40761 // The 'tree' method is '_tree now'
40763 var tree = this._tree ? this._tree() : this.tree();
40764 tree.region = tree.region || this.region;
40765 this.el = this.parent.el.addxtype(tree);
40766 this.fireEvent('built', this);
40768 this.panel = this.el;
40769 this.layout = this.panel.layout;
40770 this.parentLayout = this.parent.layout || false;
40776 Roo.apply(Roo.XComponent, {
40778 * @property hideProgress
40779 * true to disable the building progress bar.. usefull on single page renders.
40782 hideProgress : false,
40784 * @property buildCompleted
40785 * True when the builder has completed building the interface.
40788 buildCompleted : false,
40791 * @property topModule
40792 * the upper most module - uses document.element as it's constructor.
40799 * @property modules
40800 * array of modules to be created by registration system.
40801 * @type {Array} of Roo.XComponent
40806 * @property elmodules
40807 * array of modules to be created by which use #ID
40808 * @type {Array} of Roo.XComponent
40815 * Register components to be built later.
40817 * This solves the following issues
40818 * - Building is not done on page load, but after an authentication process has occured.
40819 * - Interface elements are registered on page load
40820 * - Parent Interface elements may not be loaded before child, so this handles that..
40827 module : 'Pman.Tab.projectMgr',
40829 parent : 'Pman.layout',
40830 disabled : false, // or use a function..
40833 * * @param {Object} details about module
40835 register : function(obj) {
40837 Roo.XComponent.event.fireEvent('register', obj);
40838 switch(typeof(obj.disabled) ) {
40844 if ( obj.disabled() ) {
40850 if (obj.disabled) {
40856 this.modules.push(obj);
40860 * convert a string to an object..
40861 * eg. 'AAA.BBB' -> finds AAA.BBB
40865 toObject : function(str)
40867 if (!str || typeof(str) == 'object') {
40870 if (str.substring(0,1) == '#') {
40874 var ar = str.split('.');
40879 eval('if (typeof ' + rt + ' == "undefined"){ o = false;} o = ' + rt + ';');
40881 throw "Module not found : " + str;
40885 throw "Module not found : " + str;
40887 Roo.each(ar, function(e) {
40888 if (typeof(o[e]) == 'undefined') {
40889 throw "Module not found : " + str;
40900 * move modules into their correct place in the tree..
40903 preBuild : function ()
40906 Roo.each(this.modules , function (obj)
40908 Roo.XComponent.event.fireEvent('beforebuild', obj);
40910 var opar = obj.parent;
40912 obj.parent = this.toObject(opar);
40914 Roo.log("parent:toObject failed: " + e.toString());
40919 Roo.debug && Roo.log("GOT top level module");
40920 Roo.debug && Roo.log(obj);
40921 obj.modules = new Roo.util.MixedCollection(false,
40922 function(o) { return o.order + '' }
40924 this.topModule = obj;
40927 // parent is a string (usually a dom element name..)
40928 if (typeof(obj.parent) == 'string') {
40929 this.elmodules.push(obj);
40932 if (obj.parent.constructor != Roo.XComponent) {
40933 Roo.log("Warning : Object Parent is not instance of XComponent:" + obj.name)
40935 if (!obj.parent.modules) {
40936 obj.parent.modules = new Roo.util.MixedCollection(false,
40937 function(o) { return o.order + '' }
40940 if (obj.parent.disabled) {
40941 obj.disabled = true;
40943 obj.parent.modules.add(obj);
40948 * make a list of modules to build.
40949 * @return {Array} list of modules.
40952 buildOrder : function()
40955 var cmp = function(a,b) {
40956 return String(a).toUpperCase() > String(b).toUpperCase() ? 1 : -1;
40958 if ((!this.topModule || !this.topModule.modules) && !this.elmodules.length) {
40959 throw "No top level modules to build";
40962 // make a flat list in order of modules to build.
40963 var mods = this.topModule ? [ this.topModule ] : [];
40966 // elmodules (is a list of DOM based modules )
40967 Roo.each(this.elmodules, function(e) {
40969 if (!this.topModule &&
40970 typeof(e.parent) == 'string' &&
40971 e.parent.substring(0,1) == '#' &&
40972 Roo.get(e.parent.substr(1))
40975 _this.topModule = e;
40981 // add modules to their parents..
40982 var addMod = function(m) {
40983 Roo.debug && Roo.log("build Order: add: " + m.name);
40986 if (m.modules && !m.disabled) {
40987 Roo.debug && Roo.log("build Order: " + m.modules.length + " child modules");
40988 m.modules.keySort('ASC', cmp );
40989 Roo.debug && Roo.log("build Order: " + m.modules.length + " child modules (after sort)");
40991 m.modules.each(addMod);
40993 Roo.debug && Roo.log("build Order: no child modules");
40995 // not sure if this is used any more..
40997 m.finalize.name = m.name + " (clean up) ";
40998 mods.push(m.finalize);
41002 if (this.topModule && this.topModule.modules) {
41003 this.topModule.modules.keySort('ASC', cmp );
41004 this.topModule.modules.each(addMod);
41010 * Build the registered modules.
41011 * @param {Object} parent element.
41012 * @param {Function} optional method to call after module has been added.
41020 var mods = this.buildOrder();
41022 //this.allmods = mods;
41023 //Roo.debug && Roo.log(mods);
41025 if (!mods.length) { // should not happen
41026 throw "NO modules!!!";
41030 var msg = "Building Interface...";
41031 // flash it up as modal - so we store the mask!?
41032 if (!this.hideProgress) {
41033 Roo.MessageBox.show({ title: 'loading' });
41034 Roo.MessageBox.show({
41035 title: "Please wait...",
41044 var total = mods.length;
41047 var progressRun = function() {
41048 if (!mods.length) {
41049 Roo.debug && Roo.log('hide?');
41050 if (!this.hideProgress) {
41051 Roo.MessageBox.hide();
41053 Roo.XComponent.event.fireEvent('buildcomplete', _this.topModule);
41059 var m = mods.shift();
41062 Roo.debug && Roo.log(m);
41063 // not sure if this is supported any more.. - modules that are are just function
41064 if (typeof(m) == 'function') {
41066 return progressRun.defer(10, _this);
41070 msg = "Building Interface " + (total - mods.length) +
41072 (m.name ? (' - ' + m.name) : '');
41073 Roo.debug && Roo.log(msg);
41074 if (!this.hideProgress) {
41075 Roo.MessageBox.updateProgress( (total - mods.length)/total, msg );
41079 // is the module disabled?
41080 var disabled = (typeof(m.disabled) == 'function') ?
41081 m.disabled.call(m.module.disabled) : m.disabled;
41085 return progressRun(); // we do not update the display!
41093 // it's 10 on top level, and 1 on others??? why...
41094 return progressRun.defer(10, _this);
41097 progressRun.defer(1, _this);
41111 * wrapper for event.on - aliased later..
41112 * Typically use to register a event handler for register:
41114 * eg. Roo.XComponent.on('register', function(comp) { comp.disable = true } );
41123 Roo.XComponent.event = new Roo.util.Observable({
41127 * Fires when an Component is registered,
41128 * set the disable property on the Component to stop registration.
41129 * @param {Roo.XComponent} c the component being registerd.
41134 * @event beforebuild
41135 * Fires before each Component is built
41136 * can be used to apply permissions.
41137 * @param {Roo.XComponent} c the component being registerd.
41140 'beforebuild' : true,
41142 * @event buildcomplete
41143 * Fires on the top level element when all elements have been built
41144 * @param {Roo.XComponent} the top level component.
41146 'buildcomplete' : true
41151 Roo.XComponent.on = Roo.XComponent.event.on.createDelegate(Roo.XComponent.event);