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);
5424 moveIndex : function(data, type)
5426 var index = this.indexOf(data);
5428 var newIndex = index + type;
5432 this.insert(newIndex, data);
5437 * Ext JS Library 1.1.1
5438 * Copyright(c) 2006-2007, Ext JS, LLC.
5440 * Originally Released Under LGPL - original licence link has changed is not relivant.
5443 * <script type="text/javascript">
5447 * @class Roo.data.SimpleStore
5448 * @extends Roo.data.Store
5449 * Small helper class to make creating Stores from Array data easier.
5450 * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
5451 * @cfg {Array} fields An array of field definition objects, or field name strings.
5452 * @cfg {Array} data The multi-dimensional array of data
5454 * @param {Object} config
5456 Roo.data.SimpleStore = function(config){
5457 Roo.data.SimpleStore.superclass.constructor.call(this, {
5459 reader: new Roo.data.ArrayReader({
5462 Roo.data.Record.create(config.fields)
5464 proxy : new Roo.data.MemoryProxy(config.data)
5468 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
5470 * Ext JS Library 1.1.1
5471 * Copyright(c) 2006-2007, Ext JS, LLC.
5473 * Originally Released Under LGPL - original licence link has changed is not relivant.
5476 * <script type="text/javascript">
5481 * @extends Roo.data.Store
5482 * @class Roo.data.JsonStore
5483 * Small helper class to make creating Stores for JSON data easier. <br/>
5485 var store = new Roo.data.JsonStore({
5486 url: 'get-images.php',
5488 fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
5491 * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
5492 * JsonReader and HttpProxy (unless inline data is provided).</b>
5493 * @cfg {Array} fields An array of field definition objects, or field name strings.
5495 * @param {Object} config
5497 Roo.data.JsonStore = function(c){
5498 Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
5499 proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
5500 reader: new Roo.data.JsonReader(c, c.fields)
5503 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
5505 * Ext JS Library 1.1.1
5506 * Copyright(c) 2006-2007, Ext JS, LLC.
5508 * Originally Released Under LGPL - original licence link has changed is not relivant.
5511 * <script type="text/javascript">
5515 Roo.data.Field = function(config){
5516 if(typeof config == "string"){
5517 config = {name: config};
5519 Roo.apply(this, config);
5525 var st = Roo.data.SortTypes;
5526 // named sortTypes are supported, here we look them up
5527 if(typeof this.sortType == "string"){
5528 this.sortType = st[this.sortType];
5531 // set default sortType for strings and dates
5535 this.sortType = st.asUCString;
5538 this.sortType = st.asDate;
5541 this.sortType = st.none;
5546 var stripRe = /[\$,%]/g;
5548 // prebuilt conversion function for this field, instead of
5549 // switching every time we're reading a value
5551 var cv, dateFormat = this.dateFormat;
5556 cv = function(v){ return v; };
5559 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
5563 return v !== undefined && v !== null && v !== '' ?
5564 parseInt(String(v).replace(stripRe, ""), 10) : '';
5569 return v !== undefined && v !== null && v !== '' ?
5570 parseFloat(String(v).replace(stripRe, ""), 10) : '';
5575 cv = function(v){ return v === true || v === "true" || v == 1; };
5582 if(v instanceof Date){
5586 if(dateFormat == "timestamp"){
5587 return new Date(v*1000);
5589 return Date.parseDate(v, dateFormat);
5591 var parsed = Date.parse(v);
5592 return parsed ? new Date(parsed) : null;
5601 Roo.data.Field.prototype = {
5609 * Ext JS Library 1.1.1
5610 * Copyright(c) 2006-2007, Ext JS, LLC.
5612 * Originally Released Under LGPL - original licence link has changed is not relivant.
5615 * <script type="text/javascript">
5618 // Base class for reading structured data from a data source. This class is intended to be
5619 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
5622 * @class Roo.data.DataReader
5623 * Base class for reading structured data from a data source. This class is intended to be
5624 * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
5627 Roo.data.DataReader = function(meta, recordType){
5631 this.recordType = recordType instanceof Array ?
5632 Roo.data.Record.create(recordType) : recordType;
5635 Roo.data.DataReader.prototype = {
5637 * Create an empty record
5638 * @param {Object} data (optional) - overlay some values
5639 * @return {Roo.data.Record} record created.
5641 newRow : function(d) {
5643 this.recordType.prototype.fields.each(function(c) {
5645 case 'int' : da[c.name] = 0; break;
5646 case 'date' : da[c.name] = new Date(); break;
5647 case 'float' : da[c.name] = 0.0; break;
5648 case 'boolean' : da[c.name] = false; break;
5649 default : da[c.name] = ""; break;
5653 return new this.recordType(Roo.apply(da, d));
5658 * Ext JS Library 1.1.1
5659 * Copyright(c) 2006-2007, Ext JS, LLC.
5661 * Originally Released Under LGPL - original licence link has changed is not relivant.
5664 * <script type="text/javascript">
5668 * @class Roo.data.DataProxy
5669 * @extends Roo.data.Observable
5670 * This class is an abstract base class for implementations which provide retrieval of
5671 * unformatted data objects.<br>
5673 * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
5674 * (of the appropriate type which knows how to parse the data object) to provide a block of
5675 * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
5677 * Custom implementations must implement the load method as described in
5678 * {@link Roo.data.HttpProxy#load}.
5680 Roo.data.DataProxy = function(){
5684 * Fires before a network request is made to retrieve a data object.
5685 * @param {Object} This DataProxy object.
5686 * @param {Object} params The params parameter to the load function.
5691 * Fires before the load method's callback is called.
5692 * @param {Object} This DataProxy object.
5693 * @param {Object} o The data object.
5694 * @param {Object} arg The callback argument object passed to the load function.
5698 * @event loadexception
5699 * Fires if an Exception occurs during data retrieval.
5700 * @param {Object} This DataProxy object.
5701 * @param {Object} o The data object.
5702 * @param {Object} arg The callback argument object passed to the load function.
5703 * @param {Object} e The Exception.
5705 loadexception : true
5707 Roo.data.DataProxy.superclass.constructor.call(this);
5710 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
5713 * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
5717 * Ext JS Library 1.1.1
5718 * Copyright(c) 2006-2007, Ext JS, LLC.
5720 * Originally Released Under LGPL - original licence link has changed is not relivant.
5723 * <script type="text/javascript">
5726 * @class Roo.data.MemoryProxy
5727 * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
5728 * to the Reader when its load method is called.
5730 * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
5732 Roo.data.MemoryProxy = function(data){
5736 Roo.data.MemoryProxy.superclass.constructor.call(this);
5740 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
5742 * Load data from the requested source (in this case an in-memory
5743 * data object passed to the constructor), read the data object into
5744 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
5745 * process that block using the passed callback.
5746 * @param {Object} params This parameter is not used by the MemoryProxy class.
5747 * @param {Roo.data.DataReader} reader The Reader object which converts the data
5748 * object into a block of Roo.data.Records.
5749 * @param {Function} callback The function into which to pass the block of Roo.data.records.
5750 * The function must be passed <ul>
5751 * <li>The Record block object</li>
5752 * <li>The "arg" argument from the load function</li>
5753 * <li>A boolean success indicator</li>
5755 * @param {Object} scope The scope in which to call the callback
5756 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
5758 load : function(params, reader, callback, scope, arg){
5759 params = params || {};
5762 result = reader.readRecords(this.data);
5764 this.fireEvent("loadexception", this, arg, null, e);
5765 callback.call(scope, null, arg, false);
5768 callback.call(scope, result, arg, true);
5772 update : function(params, records){
5777 * Ext JS Library 1.1.1
5778 * Copyright(c) 2006-2007, Ext JS, LLC.
5780 * Originally Released Under LGPL - original licence link has changed is not relivant.
5783 * <script type="text/javascript">
5786 * @class Roo.data.HttpProxy
5787 * @extends Roo.data.DataProxy
5788 * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
5789 * configured to reference a certain URL.<br><br>
5791 * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
5792 * from which the running page was served.<br><br>
5794 * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
5796 * Be aware that to enable the browser to parse an XML document, the server must set
5797 * the Content-Type header in the HTTP response to "text/xml".
5799 * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
5800 * an {@link Roo.data.Connection} object. If a Connection config is passed, the singleton {@link Roo.Ajax} object
5801 * will be used to make the request.
5803 Roo.data.HttpProxy = function(conn){
5804 Roo.data.HttpProxy.superclass.constructor.call(this);
5805 // is conn a conn config or a real conn?
5807 this.useAjax = !conn || !conn.events;
5811 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
5812 // thse are take from connection...
5815 * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
5818 * @cfg {Object} extraParams (Optional) An object containing properties which are used as
5819 * extra parameters to each request made by this object. (defaults to undefined)
5822 * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
5823 * to each request made by this object. (defaults to undefined)
5826 * @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)
5829 * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
5832 * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
5838 * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
5842 * Return the {@link Roo.data.Connection} object being used by this Proxy.
5843 * @return {Connection} The Connection object. This object may be used to subscribe to events on
5844 * a finer-grained basis than the DataProxy events.
5846 getConnection : function(){
5847 return this.useAjax ? Roo.Ajax : this.conn;
5851 * Load data from the configured {@link Roo.data.Connection}, read the data object into
5852 * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
5853 * process that block using the passed callback.
5854 * @param {Object} params An object containing properties which are to be used as HTTP parameters
5855 * for the request to the remote server.
5856 * @param {Roo.data.DataReader} reader The Reader object which converts the data
5857 * object into a block of Roo.data.Records.
5858 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
5859 * The function must be passed <ul>
5860 * <li>The Record block object</li>
5861 * <li>The "arg" argument from the load function</li>
5862 * <li>A boolean success indicator</li>
5864 * @param {Object} scope The scope in which to call the callback
5865 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
5867 load : function(params, reader, callback, scope, arg){
5868 if(this.fireEvent("beforeload", this, params) !== false){
5870 params : params || {},
5872 callback : callback,
5877 callback : this.loadResponse,
5881 Roo.applyIf(o, this.conn);
5882 if(this.activeRequest){
5883 Roo.Ajax.abort(this.activeRequest);
5885 this.activeRequest = Roo.Ajax.request(o);
5887 this.conn.request(o);
5890 callback.call(scope||this, null, arg, false);
5895 loadResponse : function(o, success, response){
5896 delete this.activeRequest;
5898 this.fireEvent("loadexception", this, o, response);
5899 o.request.callback.call(o.request.scope, null, o.request.arg, false);
5904 result = o.reader.read(response);
5906 this.fireEvent("loadexception", this, o, response, e);
5907 o.request.callback.call(o.request.scope, null, o.request.arg, false);
5911 this.fireEvent("load", this, o, o.request.arg);
5912 o.request.callback.call(o.request.scope, result, o.request.arg, true);
5916 update : function(dataSet){
5921 updateResponse : function(dataSet){
5926 * Ext JS Library 1.1.1
5927 * Copyright(c) 2006-2007, Ext JS, LLC.
5929 * Originally Released Under LGPL - original licence link has changed is not relivant.
5932 * <script type="text/javascript">
5936 * @class Roo.data.ScriptTagProxy
5937 * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
5938 * other than the originating domain of the running page.<br><br>
5940 * <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
5941 * of the running page, you must use this class, rather than DataProxy.</em><br><br>
5943 * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
5944 * source code that is used as the source inside a <script> tag.<br><br>
5946 * In order for the browser to process the returned data, the server must wrap the data object
5947 * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
5948 * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
5949 * depending on whether the callback name was passed:
5952 boolean scriptTag = false;
5953 String cb = request.getParameter("callback");
5956 response.setContentType("text/javascript");
5958 response.setContentType("application/x-json");
5960 Writer out = response.getWriter();
5962 out.write(cb + "(");
5964 out.print(dataBlock.toJsonString());
5971 * @param {Object} config A configuration object.
5973 Roo.data.ScriptTagProxy = function(config){
5974 Roo.data.ScriptTagProxy.superclass.constructor.call(this);
5975 Roo.apply(this, config);
5976 this.head = document.getElementsByTagName("head")[0];
5979 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
5981 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
5983 * @cfg {String} url The URL from which to request the data object.
5986 * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
5990 * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
5991 * the server the name of the callback function set up by the load call to process the returned data object.
5992 * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
5993 * javascript output which calls this named function passing the data object as its only parameter.
5995 callbackParam : "callback",
5997 * @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
5998 * name to the request.
6003 * Load data from the configured URL, read the data object into
6004 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
6005 * process that block using the passed callback.
6006 * @param {Object} params An object containing properties which are to be used as HTTP parameters
6007 * for the request to the remote server.
6008 * @param {Roo.data.DataReader} reader The Reader object which converts the data
6009 * object into a block of Roo.data.Records.
6010 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
6011 * The function must be passed <ul>
6012 * <li>The Record block object</li>
6013 * <li>The "arg" argument from the load function</li>
6014 * <li>A boolean success indicator</li>
6016 * @param {Object} scope The scope in which to call the callback
6017 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
6019 load : function(params, reader, callback, scope, arg){
6020 if(this.fireEvent("beforeload", this, params) !== false){
6022 var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
6025 url += (url.indexOf("?") != -1 ? "&" : "?") + p;
6027 url += "&_dc=" + (new Date().getTime());
6029 var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
6032 cb : "stcCallback"+transId,
6033 scriptId : "stcScript"+transId,
6037 callback : callback,
6043 window[trans.cb] = function(o){
6044 conn.handleResponse(o, trans);
6047 url += String.format("&{0}={1}", this.callbackParam, trans.cb);
6049 if(this.autoAbort !== false){
6053 trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
6055 var script = document.createElement("script");
6056 script.setAttribute("src", url);
6057 script.setAttribute("type", "text/javascript");
6058 script.setAttribute("id", trans.scriptId);
6059 this.head.appendChild(script);
6063 callback.call(scope||this, null, arg, false);
6068 isLoading : function(){
6069 return this.trans ? true : false;
6073 * Abort the current server request.
6076 if(this.isLoading()){
6077 this.destroyTrans(this.trans);
6082 destroyTrans : function(trans, isLoaded){
6083 this.head.removeChild(document.getElementById(trans.scriptId));
6084 clearTimeout(trans.timeoutId);
6086 window[trans.cb] = undefined;
6088 delete window[trans.cb];
6091 // if hasn't been loaded, wait for load to remove it to prevent script error
6092 window[trans.cb] = function(){
6093 window[trans.cb] = undefined;
6095 delete window[trans.cb];
6102 handleResponse : function(o, trans){
6104 this.destroyTrans(trans, true);
6107 result = trans.reader.readRecords(o);
6109 this.fireEvent("loadexception", this, o, trans.arg, e);
6110 trans.callback.call(trans.scope||window, null, trans.arg, false);
6113 this.fireEvent("load", this, o, trans.arg);
6114 trans.callback.call(trans.scope||window, result, trans.arg, true);
6118 handleFailure : function(trans){
6120 this.destroyTrans(trans, false);
6121 this.fireEvent("loadexception", this, null, trans.arg);
6122 trans.callback.call(trans.scope||window, null, trans.arg, false);
6126 * Ext JS Library 1.1.1
6127 * Copyright(c) 2006-2007, Ext JS, LLC.
6129 * Originally Released Under LGPL - original licence link has changed is not relivant.
6132 * <script type="text/javascript">
6136 * @class Roo.data.JsonReader
6137 * @extends Roo.data.DataReader
6138 * Data reader class to create an Array of Roo.data.Record objects from a JSON response
6139 * based on mappings in a provided Roo.data.Record constructor.
6141 * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
6142 * in the reply previously.
6147 var RecordDef = Roo.data.Record.create([
6148 {name: 'name', mapping: 'name'}, // "mapping" property not needed if it's the same as "name"
6149 {name: 'occupation'} // This field will use "occupation" as the mapping.
6151 var myReader = new Roo.data.JsonReader({
6152 totalProperty: "results", // The property which contains the total dataset size (optional)
6153 root: "rows", // The property which contains an Array of row objects
6154 id: "id" // The property within each row object that provides an ID for the record (optional)
6158 * This would consume a JSON file like this:
6160 { 'results': 2, 'rows': [
6161 { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
6162 { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
6165 * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
6166 * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
6167 * paged from the remote server.
6168 * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
6169 * @cfg {String} root name of the property which contains the Array of row objects.
6170 * @cfg {String} id Name of the property within a row object that contains a record identifier value.
6172 * Create a new JsonReader
6173 * @param {Object} meta Metadata configuration options
6174 * @param {Object} recordType Either an Array of field definition objects,
6175 * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
6177 Roo.data.JsonReader = function(meta, recordType){
6180 // set some defaults:
6182 totalProperty: 'total',
6183 successProperty : 'success',
6188 Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
6190 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
6193 * @prop {Boolean} metaFromRemote - if the meta data was loaded from the remote source.
6194 * Used by Store query builder to append _requestMeta to params.
6197 metaFromRemote : false,
6199 * This method is only used by a DataProxy which has retrieved data from a remote server.
6200 * @param {Object} response The XHR object which contains the JSON data in its responseText.
6201 * @return {Object} data A data block which is used by an Roo.data.Store object as
6202 * a cache of Roo.data.Records.
6204 read : function(response){
6205 var json = response.responseText;
6207 var o = /* eval:var:o */ eval("("+json+")");
6209 throw {message: "JsonReader.read: Json object not found"};
6215 this.metaFromRemote = true;
6216 this.meta = o.metaData;
6217 this.recordType = Roo.data.Record.create(o.metaData.fields);
6218 this.onMetaChange(this.meta, this.recordType, o);
6220 return this.readRecords(o);
6223 // private function a store will implement
6224 onMetaChange : function(meta, recordType, o){
6231 simpleAccess: function(obj, subsc) {
6238 getJsonAccessor: function(){
6240 return function(expr) {
6242 return(re.test(expr))
6243 ? new Function("obj", "return obj." + expr)
6253 * Create a data block containing Roo.data.Records from an XML document.
6254 * @param {Object} o An object which contains an Array of row objects in the property specified
6255 * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
6256 * which contains the total size of the dataset.
6257 * @return {Object} data A data block which is used by an Roo.data.Store object as
6258 * a cache of Roo.data.Records.
6260 readRecords : function(o){
6262 * After any data loads, the raw JSON data is available for further custom processing.
6266 var s = this.meta, Record = this.recordType,
6267 f = Record ? Record.prototype.fields : null, fi = f.items, fl = f.length;
6269 // Generate extraction functions for the totalProperty, the root, the id, and for each field
6271 if(s.totalProperty) {
6272 this.getTotal = this.getJsonAccessor(s.totalProperty);
6274 if(s.successProperty) {
6275 this.getSuccess = this.getJsonAccessor(s.successProperty);
6277 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
6279 var g = this.getJsonAccessor(s.id);
6280 this.getId = function(rec) {
6282 return (r === undefined || r === "") ? null : r;
6285 this.getId = function(){return null;};
6288 for(var jj = 0; jj < fl; jj++){
6290 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
6291 this.ef[jj] = this.getJsonAccessor(map);
6295 var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
6296 if(s.totalProperty){
6297 var vt = parseInt(this.getTotal(o), 10);
6302 if(s.successProperty){
6303 var vs = this.getSuccess(o);
6304 if(vs === false || vs === 'false'){
6309 for(var i = 0; i < c; i++){
6312 var id = this.getId(n);
6313 for(var j = 0; j < fl; j++){
6315 var v = this.ef[j](n);
6317 Roo.log('missing convert for ' + f.name);
6321 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
6323 var record = new Record(values, id);
6325 records[i] = record;
6331 totalRecords : totalRecords
6336 * Ext JS Library 1.1.1
6337 * Copyright(c) 2006-2007, Ext JS, LLC.
6339 * Originally Released Under LGPL - original licence link has changed is not relivant.
6342 * <script type="text/javascript">
6346 * @class Roo.data.XmlReader
6347 * @extends Roo.data.DataReader
6348 * Data reader class to create an Array of {@link Roo.data.Record} objects from an XML document
6349 * based on mappings in a provided Roo.data.Record constructor.<br><br>
6351 * <em>Note that in order for the browser to parse a returned XML document, the Content-Type
6352 * header in the HTTP response must be set to "text/xml".</em>
6356 var RecordDef = Roo.data.Record.create([
6357 {name: 'name', mapping: 'name'}, // "mapping" property not needed if it's the same as "name"
6358 {name: 'occupation'} // This field will use "occupation" as the mapping.
6360 var myReader = new Roo.data.XmlReader({
6361 totalRecords: "results", // The element which contains the total dataset size (optional)
6362 record: "row", // The repeated element which contains row information
6363 id: "id" // The element within the row that provides an ID for the record (optional)
6367 * This would consume an XML file like this:
6371 <results>2</results>
6374 <name>Bill</name>
6375 <occupation>Gardener</occupation>
6379 <name>Ben</name>
6380 <occupation>Horticulturalist</occupation>
6384 * @cfg {String} totalRecords The DomQuery path from which to retrieve the total number of records
6385 * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
6386 * paged from the remote server.
6387 * @cfg {String} record The DomQuery path to the repeated element which contains record information.
6388 * @cfg {String} success The DomQuery path to the success attribute used by forms.
6389 * @cfg {String} id The DomQuery path relative from the record element to the element that contains
6390 * a record identifier value.
6392 * Create a new XmlReader
6393 * @param {Object} meta Metadata configuration options
6394 * @param {Mixed} recordType The definition of the data record type to produce. This can be either a valid
6395 * Record subclass created with {@link Roo.data.Record#create}, or an array of objects with which to call
6396 * Roo.data.Record.create. See the {@link Roo.data.Record} class for more details.
6398 Roo.data.XmlReader = function(meta, recordType){
6400 Roo.data.XmlReader.superclass.constructor.call(this, meta, recordType||meta.fields);
6402 Roo.extend(Roo.data.XmlReader, Roo.data.DataReader, {
6404 * This method is only used by a DataProxy which has retrieved data from a remote server.
6405 * @param {Object} response The XHR object which contains the parsed XML document. The response is expected
6406 * to contain a method called 'responseXML' that returns an XML document object.
6407 * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
6408 * a cache of Roo.data.Records.
6410 read : function(response){
6411 var doc = response.responseXML;
6413 throw {message: "XmlReader.read: XML Document not available"};
6415 return this.readRecords(doc);
6419 * Create a data block containing Roo.data.Records from an XML document.
6420 * @param {Object} doc A parsed XML document.
6421 * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
6422 * a cache of Roo.data.Records.
6424 readRecords : function(doc){
6426 * After any data loads/reads, the raw XML Document is available for further custom processing.
6430 var root = doc.documentElement || doc;
6431 var q = Roo.DomQuery;
6432 var recordType = this.recordType, fields = recordType.prototype.fields;
6433 var sid = this.meta.id;
6434 var totalRecords = 0, success = true;
6435 if(this.meta.totalRecords){
6436 totalRecords = q.selectNumber(this.meta.totalRecords, root, 0);
6439 if(this.meta.success){
6440 var sv = q.selectValue(this.meta.success, root, true);
6441 success = sv !== false && sv !== 'false';
6444 var ns = q.select(this.meta.record, root);
6445 for(var i = 0, len = ns.length; i < len; i++) {
6448 var id = sid ? q.selectValue(sid, n) : undefined;
6449 for(var j = 0, jlen = fields.length; j < jlen; j++){
6450 var f = fields.items[j];
6451 var v = q.selectValue(f.mapping || f.name, n, f.defaultValue);
6455 var record = new recordType(values, id);
6457 records[records.length] = record;
6463 totalRecords : totalRecords || records.length
6468 * Ext JS Library 1.1.1
6469 * Copyright(c) 2006-2007, Ext JS, LLC.
6471 * Originally Released Under LGPL - original licence link has changed is not relivant.
6474 * <script type="text/javascript">
6478 * @class Roo.data.ArrayReader
6479 * @extends Roo.data.DataReader
6480 * Data reader class to create an Array of Roo.data.Record objects from an Array.
6481 * Each element of that Array represents a row of data fields. The
6482 * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
6483 * of the field definition if it exists, or the field's ordinal position in the definition.<br>
6487 var RecordDef = Roo.data.Record.create([
6488 {name: 'name', mapping: 1}, // "mapping" only needed if an "id" field is present which
6489 {name: 'occupation', mapping: 2} // precludes using the ordinal position as the index.
6491 var myReader = new Roo.data.ArrayReader({
6492 id: 0 // The subscript within row Array that provides an ID for the Record (optional)
6496 * This would consume an Array like this:
6498 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
6500 * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
6502 * Create a new JsonReader
6503 * @param {Object} meta Metadata configuration options.
6504 * @param {Object} recordType Either an Array of field definition objects
6505 * as specified to {@link Roo.data.Record#create},
6506 * or an {@link Roo.data.Record} object
6507 * created using {@link Roo.data.Record#create}.
6509 Roo.data.ArrayReader = function(meta, recordType){
6510 Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
6513 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
6515 * Create a data block containing Roo.data.Records from an XML document.
6516 * @param {Object} o An Array of row objects which represents the dataset.
6517 * @return {Object} data A data block which is used by an Roo.data.Store object as
6518 * a cache of Roo.data.Records.
6520 readRecords : function(o){
6521 var sid = this.meta ? this.meta.id : null;
6522 var recordType = this.recordType, fields = recordType.prototype.fields;
6525 for(var i = 0; i < root.length; i++){
6528 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
6529 for(var j = 0, jlen = fields.length; j < jlen; j++){
6530 var f = fields.items[j];
6531 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
6532 var v = n[k] !== undefined ? n[k] : f.defaultValue;
6536 var record = new recordType(values, id);
6538 records[records.length] = record;
6542 totalRecords : records.length
6547 * Ext JS Library 1.1.1
6548 * Copyright(c) 2006-2007, Ext JS, LLC.
6550 * Originally Released Under LGPL - original licence link has changed is not relivant.
6553 * <script type="text/javascript">
6558 * @class Roo.data.Tree
6559 * @extends Roo.util.Observable
6560 * Represents a tree data structure and bubbles all the events for its nodes. The nodes
6561 * in the tree have most standard DOM functionality.
6563 * @param {Node} root (optional) The root node
6565 Roo.data.Tree = function(root){
6568 * The root node for this tree
6573 this.setRootNode(root);
6578 * Fires when a new child node is appended to a node in this tree.
6579 * @param {Tree} tree The owner tree
6580 * @param {Node} parent The parent node
6581 * @param {Node} node The newly appended node
6582 * @param {Number} index The index of the newly appended node
6587 * Fires when a child node is removed from a node in this tree.
6588 * @param {Tree} tree The owner tree
6589 * @param {Node} parent The parent node
6590 * @param {Node} node The child node removed
6595 * Fires when a node is moved to a new location in the tree
6596 * @param {Tree} tree The owner tree
6597 * @param {Node} node The node moved
6598 * @param {Node} oldParent The old parent of this node
6599 * @param {Node} newParent The new parent of this node
6600 * @param {Number} index The index it was moved to
6605 * Fires when a new child node is inserted in a node in this tree.
6606 * @param {Tree} tree The owner tree
6607 * @param {Node} parent The parent node
6608 * @param {Node} node The child node inserted
6609 * @param {Node} refNode The child node the node was inserted before
6613 * @event beforeappend
6614 * Fires before a new child is appended to a node in this tree, return false to cancel the append.
6615 * @param {Tree} tree The owner tree
6616 * @param {Node} parent The parent node
6617 * @param {Node} node The child node to be appended
6619 "beforeappend" : true,
6621 * @event beforeremove
6622 * Fires before a child is removed from a node in this tree, return false to cancel the remove.
6623 * @param {Tree} tree The owner tree
6624 * @param {Node} parent The parent node
6625 * @param {Node} node The child node to be removed
6627 "beforeremove" : true,
6630 * Fires before a node is moved to a new location in the tree. Return false to cancel the move.
6631 * @param {Tree} tree The owner tree
6632 * @param {Node} node The node being moved
6633 * @param {Node} oldParent The parent of the node
6634 * @param {Node} newParent The new parent the node is moving to
6635 * @param {Number} index The index it is being moved to
6637 "beforemove" : true,
6639 * @event beforeinsert
6640 * Fires before a new child is inserted in a node in this tree, return false to cancel the insert.
6641 * @param {Tree} tree The owner tree
6642 * @param {Node} parent The parent node
6643 * @param {Node} node The child node to be inserted
6644 * @param {Node} refNode The child node the node is being inserted before
6646 "beforeinsert" : true
6649 Roo.data.Tree.superclass.constructor.call(this);
6652 Roo.extend(Roo.data.Tree, Roo.util.Observable, {
6655 proxyNodeEvent : function(){
6656 return this.fireEvent.apply(this, arguments);
6660 * Returns the root node for this tree.
6663 getRootNode : function(){
6668 * Sets the root node for this tree.
6669 * @param {Node} node
6672 setRootNode : function(node){
6674 node.ownerTree = this;
6676 this.registerNode(node);
6681 * Gets a node in this tree by its id.
6682 * @param {String} id
6685 getNodeById : function(id){
6686 return this.nodeHash[id];
6689 registerNode : function(node){
6690 this.nodeHash[node.id] = node;
6693 unregisterNode : function(node){
6694 delete this.nodeHash[node.id];
6697 toString : function(){
6698 return "[Tree"+(this.id?" "+this.id:"")+"]";
6703 * @class Roo.data.Node
6704 * @extends Roo.util.Observable
6705 * @cfg {Boolean} leaf true if this node is a leaf and does not have children
6706 * @cfg {String} id The id for this node. If one is not specified, one is generated.
6708 * @param {Object} attributes The attributes/config for the node
6710 Roo.data.Node = function(attributes){
6712 * The attributes supplied for the node. You can use this property to access any custom attributes you supplied.
6715 this.attributes = attributes || {};
6716 this.leaf = this.attributes.leaf;
6718 * The node id. @type String
6720 this.id = this.attributes.id;
6722 this.id = Roo.id(null, "ynode-");
6723 this.attributes.id = this.id;
6728 * All child nodes of this node. @type Array
6730 this.childNodes = [];
6731 if(!this.childNodes.indexOf){ // indexOf is a must
6732 this.childNodes.indexOf = function(o){
6733 for(var i = 0, len = this.length; i < len; i++){
6742 * The parent node for this node. @type Node
6744 this.parentNode = null;
6746 * The first direct child node of this node, or null if this node has no child nodes. @type Node
6748 this.firstChild = null;
6750 * The last direct child node of this node, or null if this node has no child nodes. @type Node
6752 this.lastChild = null;
6754 * The node immediately preceding this node in the tree, or null if there is no sibling node. @type Node
6756 this.previousSibling = null;
6758 * The node immediately following this node in the tree, or null if there is no sibling node. @type Node
6760 this.nextSibling = null;
6765 * Fires when a new child node is appended
6766 * @param {Tree} tree The owner tree
6767 * @param {Node} this This node
6768 * @param {Node} node The newly appended node
6769 * @param {Number} index The index of the newly appended node
6774 * Fires when a child node is removed
6775 * @param {Tree} tree The owner tree
6776 * @param {Node} this This node
6777 * @param {Node} node The removed node
6782 * Fires when this node is moved to a new location in the tree
6783 * @param {Tree} tree The owner tree
6784 * @param {Node} this This node
6785 * @param {Node} oldParent The old parent of this node
6786 * @param {Node} newParent The new parent of this node
6787 * @param {Number} index The index it was moved to
6792 * Fires when a new child node is inserted.
6793 * @param {Tree} tree The owner tree
6794 * @param {Node} this This node
6795 * @param {Node} node The child node inserted
6796 * @param {Node} refNode The child node the node was inserted before
6800 * @event beforeappend
6801 * Fires before a new child is appended, return false to cancel the append.
6802 * @param {Tree} tree The owner tree
6803 * @param {Node} this This node
6804 * @param {Node} node The child node to be appended
6806 "beforeappend" : true,
6808 * @event beforeremove
6809 * Fires before a child is removed, return false to cancel the remove.
6810 * @param {Tree} tree The owner tree
6811 * @param {Node} this This node
6812 * @param {Node} node The child node to be removed
6814 "beforeremove" : true,
6817 * Fires before this node is moved to a new location in the tree. Return false to cancel the move.
6818 * @param {Tree} tree The owner tree
6819 * @param {Node} this This node
6820 * @param {Node} oldParent The parent of this node
6821 * @param {Node} newParent The new parent this node is moving to
6822 * @param {Number} index The index it is being moved to
6824 "beforemove" : true,
6826 * @event beforeinsert
6827 * Fires before a new child is inserted, return false to cancel the insert.
6828 * @param {Tree} tree The owner tree
6829 * @param {Node} this This node
6830 * @param {Node} node The child node to be inserted
6831 * @param {Node} refNode The child node the node is being inserted before
6833 "beforeinsert" : true
6835 this.listeners = this.attributes.listeners;
6836 Roo.data.Node.superclass.constructor.call(this);
6839 Roo.extend(Roo.data.Node, Roo.util.Observable, {
6840 fireEvent : function(evtName){
6841 // first do standard event for this node
6842 if(Roo.data.Node.superclass.fireEvent.apply(this, arguments) === false){
6845 // then bubble it up to the tree if the event wasn't cancelled
6846 var ot = this.getOwnerTree();
6848 if(ot.proxyNodeEvent.apply(ot, arguments) === false){
6856 * Returns true if this node is a leaf
6859 isLeaf : function(){
6860 return this.leaf === true;
6864 setFirstChild : function(node){
6865 this.firstChild = node;
6869 setLastChild : function(node){
6870 this.lastChild = node;
6875 * Returns true if this node is the last child of its parent
6878 isLast : function(){
6879 return (!this.parentNode ? true : this.parentNode.lastChild == this);
6883 * Returns true if this node is the first child of its parent
6886 isFirst : function(){
6887 return (!this.parentNode ? true : this.parentNode.firstChild == this);
6890 hasChildNodes : function(){
6891 return !this.isLeaf() && this.childNodes.length > 0;
6895 * Insert node(s) as the last child node of this node.
6896 * @param {Node/Array} node The node or Array of nodes to append
6897 * @return {Node} The appended node if single append, or null if an array was passed
6899 appendChild : function(node){
6901 if(node instanceof Array){
6903 }else if(arguments.length > 1){
6906 // if passed an array or multiple args do them one by one
6908 for(var i = 0, len = multi.length; i < len; i++) {
6909 this.appendChild(multi[i]);
6912 if(this.fireEvent("beforeappend", this.ownerTree, this, node) === false){
6915 var index = this.childNodes.length;
6916 var oldParent = node.parentNode;
6917 // it's a move, make sure we move it cleanly
6919 if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index) === false){
6922 oldParent.removeChild(node);
6924 index = this.childNodes.length;
6926 this.setFirstChild(node);
6928 this.childNodes.push(node);
6929 node.parentNode = this;
6930 var ps = this.childNodes[index-1];
6932 node.previousSibling = ps;
6933 ps.nextSibling = node;
6935 node.previousSibling = null;
6937 node.nextSibling = null;
6938 this.setLastChild(node);
6939 node.setOwnerTree(this.getOwnerTree());
6940 this.fireEvent("append", this.ownerTree, this, node, index);
6942 node.fireEvent("move", this.ownerTree, node, oldParent, this, index);
6949 * Removes a child node from this node.
6950 * @param {Node} node The node to remove
6951 * @return {Node} The removed node
6953 removeChild : function(node){
6954 var index = this.childNodes.indexOf(node);
6958 if(this.fireEvent("beforeremove", this.ownerTree, this, node) === false){
6962 // remove it from childNodes collection
6963 this.childNodes.splice(index, 1);
6966 if(node.previousSibling){
6967 node.previousSibling.nextSibling = node.nextSibling;
6969 if(node.nextSibling){
6970 node.nextSibling.previousSibling = node.previousSibling;
6973 // update child refs
6974 if(this.firstChild == node){
6975 this.setFirstChild(node.nextSibling);
6977 if(this.lastChild == node){
6978 this.setLastChild(node.previousSibling);
6981 node.setOwnerTree(null);
6982 // clear any references from the node
6983 node.parentNode = null;
6984 node.previousSibling = null;
6985 node.nextSibling = null;
6986 this.fireEvent("remove", this.ownerTree, this, node);
6991 * Inserts the first node before the second node in this nodes childNodes collection.
6992 * @param {Node} node The node to insert
6993 * @param {Node} refNode The node to insert before (if null the node is appended)
6994 * @return {Node} The inserted node
6996 insertBefore : function(node, refNode){
6997 if(!refNode){ // like standard Dom, refNode can be null for append
6998 return this.appendChild(node);
7001 if(node == refNode){
7005 if(this.fireEvent("beforeinsert", this.ownerTree, this, node, refNode) === false){
7008 var index = this.childNodes.indexOf(refNode);
7009 var oldParent = node.parentNode;
7010 var refIndex = index;
7012 // when moving internally, indexes will change after remove
7013 if(oldParent == this && this.childNodes.indexOf(node) < index){
7017 // it's a move, make sure we move it cleanly
7019 if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index, refNode) === false){
7022 oldParent.removeChild(node);
7025 this.setFirstChild(node);
7027 this.childNodes.splice(refIndex, 0, node);
7028 node.parentNode = this;
7029 var ps = this.childNodes[refIndex-1];
7031 node.previousSibling = ps;
7032 ps.nextSibling = node;
7034 node.previousSibling = null;
7036 node.nextSibling = refNode;
7037 refNode.previousSibling = node;
7038 node.setOwnerTree(this.getOwnerTree());
7039 this.fireEvent("insert", this.ownerTree, this, node, refNode);
7041 node.fireEvent("move", this.ownerTree, node, oldParent, this, refIndex, refNode);
7047 * Returns the child node at the specified index.
7048 * @param {Number} index
7051 item : function(index){
7052 return this.childNodes[index];
7056 * Replaces one child node in this node with another.
7057 * @param {Node} newChild The replacement node
7058 * @param {Node} oldChild The node to replace
7059 * @return {Node} The replaced node
7061 replaceChild : function(newChild, oldChild){
7062 this.insertBefore(newChild, oldChild);
7063 this.removeChild(oldChild);
7068 * Returns the index of a child node
7069 * @param {Node} node
7070 * @return {Number} The index of the node or -1 if it was not found
7072 indexOf : function(child){
7073 return this.childNodes.indexOf(child);
7077 * Returns the tree this node is in.
7080 getOwnerTree : function(){
7081 // if it doesn't have one, look for one
7082 if(!this.ownerTree){
7086 this.ownerTree = p.ownerTree;
7092 return this.ownerTree;
7096 * Returns depth of this node (the root node has a depth of 0)
7099 getDepth : function(){
7102 while(p.parentNode){
7110 setOwnerTree : function(tree){
7111 // if it's move, we need to update everyone
7112 if(tree != this.ownerTree){
7114 this.ownerTree.unregisterNode(this);
7116 this.ownerTree = tree;
7117 var cs = this.childNodes;
7118 for(var i = 0, len = cs.length; i < len; i++) {
7119 cs[i].setOwnerTree(tree);
7122 tree.registerNode(this);
7128 * Returns the path for this node. The path can be used to expand or select this node programmatically.
7129 * @param {String} attr (optional) The attr to use for the path (defaults to the node's id)
7130 * @return {String} The path
7132 getPath : function(attr){
7133 attr = attr || "id";
7134 var p = this.parentNode;
7135 var b = [this.attributes[attr]];
7137 b.unshift(p.attributes[attr]);
7140 var sep = this.getOwnerTree().pathSeparator;
7141 return sep + b.join(sep);
7145 * Bubbles up the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
7146 * function call will be the scope provided or the current node. The arguments to the function
7147 * will be the args provided or the current node. If the function returns false at any point,
7148 * the bubble is stopped.
7149 * @param {Function} fn The function to call
7150 * @param {Object} scope (optional) The scope of the function (defaults to current node)
7151 * @param {Array} args (optional) The args to call the function with (default to passing the current node)
7153 bubble : function(fn, scope, args){
7156 if(fn.call(scope || p, args || p) === false){
7164 * Cascades down the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
7165 * function call will be the scope provided or the current node. The arguments to the function
7166 * will be the args provided or the current node. If the function returns false at any point,
7167 * the cascade is stopped on that branch.
7168 * @param {Function} fn The function to call
7169 * @param {Object} scope (optional) The scope of the function (defaults to current node)
7170 * @param {Array} args (optional) The args to call the function with (default to passing the current node)
7172 cascade : function(fn, scope, args){
7173 if(fn.call(scope || this, args || this) !== false){
7174 var cs = this.childNodes;
7175 for(var i = 0, len = cs.length; i < len; i++) {
7176 cs[i].cascade(fn, scope, args);
7182 * Interates the child nodes of this node, calling the specified function with each node. The scope (<i>this</i>) of
7183 * function call will be the scope provided or the current node. The arguments to the function
7184 * will be the args provided or the current node. If the function returns false at any point,
7185 * the iteration stops.
7186 * @param {Function} fn The function to call
7187 * @param {Object} scope (optional) The scope of the function (defaults to current node)
7188 * @param {Array} args (optional) The args to call the function with (default to passing the current node)
7190 eachChild : function(fn, scope, args){
7191 var cs = this.childNodes;
7192 for(var i = 0, len = cs.length; i < len; i++) {
7193 if(fn.call(scope || this, args || cs[i]) === false){
7200 * Finds the first child that has the attribute with the specified value.
7201 * @param {String} attribute The attribute name
7202 * @param {Mixed} value The value to search for
7203 * @return {Node} The found child or null if none was found
7205 findChild : function(attribute, value){
7206 var cs = this.childNodes;
7207 for(var i = 0, len = cs.length; i < len; i++) {
7208 if(cs[i].attributes[attribute] == value){
7216 * Finds the first child by a custom function. The child matches if the function passed
7218 * @param {Function} fn
7219 * @param {Object} scope (optional)
7220 * @return {Node} The found child or null if none was found
7222 findChildBy : function(fn, scope){
7223 var cs = this.childNodes;
7224 for(var i = 0, len = cs.length; i < len; i++) {
7225 if(fn.call(scope||cs[i], cs[i]) === true){
7233 * Sorts this nodes children using the supplied sort function
7234 * @param {Function} fn
7235 * @param {Object} scope (optional)
7237 sort : function(fn, scope){
7238 var cs = this.childNodes;
7239 var len = cs.length;
7241 var sortFn = scope ? function(){fn.apply(scope, arguments);} : fn;
7243 for(var i = 0; i < len; i++){
7245 n.previousSibling = cs[i-1];
7246 n.nextSibling = cs[i+1];
7248 this.setFirstChild(n);
7251 this.setLastChild(n);
7258 * Returns true if this node is an ancestor (at any point) of the passed node.
7259 * @param {Node} node
7262 contains : function(node){
7263 return node.isAncestor(this);
7267 * Returns true if the passed node is an ancestor (at any point) of this node.
7268 * @param {Node} node
7271 isAncestor : function(node){
7272 var p = this.parentNode;
7282 toString : function(){
7283 return "[Node"+(this.id?" "+this.id:"")+"]";
7287 * Ext JS Library 1.1.1
7288 * Copyright(c) 2006-2007, Ext JS, LLC.
7290 * Originally Released Under LGPL - original licence link has changed is not relivant.
7293 * <script type="text/javascript">
7298 * @extends Roo.Element
7299 * An extended {@link Roo.Element} object that supports a shadow and shim, constrain to viewport and
7300 * automatic maintaining of shadow/shim positions.
7301 * @cfg {Boolean} shim False to disable the iframe shim in browsers which need one (defaults to true)
7302 * @cfg {String/Boolean} shadow True to create a shadow element with default class "x-layer-shadow", or
7303 * you can pass a string with a CSS class name. False turns off the shadow.
7304 * @cfg {Object} dh DomHelper object config to create element with (defaults to {tag: "div", cls: "x-layer"}).
7305 * @cfg {Boolean} constrain False to disable constrain to viewport (defaults to true)
7306 * @cfg {String} cls CSS class to add to the element
7307 * @cfg {Number} zindex Starting z-index (defaults to 11000)
7308 * @cfg {Number} shadowOffset Number of pixels to offset the shadow (defaults to 3)
7310 * @param {Object} config An object with config options.
7311 * @param {String/HTMLElement} existingEl (optional) Uses an existing DOM element. If the element is not found it creates it.
7314 Roo.Layer = function(config, existingEl){
7315 config = config || {};
7316 var dh = Roo.DomHelper;
7317 var cp = config.parentEl, pel = cp ? Roo.getDom(cp) : document.body;
7319 this.dom = Roo.getDom(existingEl);
7322 var o = config.dh || {tag: "div", cls: "x-layer"};
7323 this.dom = dh.append(pel, o);
7326 this.addClass(config.cls);
7328 this.constrain = config.constrain !== false;
7329 this.visibilityMode = Roo.Element.VISIBILITY;
7331 this.id = this.dom.id = config.id;
7333 this.id = Roo.id(this.dom);
7335 this.zindex = config.zindex || this.getZIndex();
7336 this.position("absolute", this.zindex);
7338 this.shadowOffset = config.shadowOffset || 4;
7339 this.shadow = new Roo.Shadow({
7340 offset : this.shadowOffset,
7341 mode : config.shadow
7344 this.shadowOffset = 0;
7346 this.useShim = config.shim !== false && Roo.useShims;
7347 this.useDisplay = config.useDisplay;
7351 var supr = Roo.Element.prototype;
7353 // shims are shared among layer to keep from having 100 iframes
7356 Roo.extend(Roo.Layer, Roo.Element, {
7358 getZIndex : function(){
7359 return this.zindex || parseInt(this.getStyle("z-index"), 10) || 11000;
7362 getShim : function(){
7369 var shim = shims.shift();
7371 shim = this.createShim();
7372 shim.enableDisplayMode('block');
7373 shim.dom.style.display = 'none';
7374 shim.dom.style.visibility = 'visible';
7376 var pn = this.dom.parentNode;
7377 if(shim.dom.parentNode != pn){
7378 pn.insertBefore(shim.dom, this.dom);
7380 shim.setStyle('z-index', this.getZIndex()-2);
7385 hideShim : function(){
7387 this.shim.setDisplayed(false);
7388 shims.push(this.shim);
7393 disableShadow : function(){
7395 this.shadowDisabled = true;
7397 this.lastShadowOffset = this.shadowOffset;
7398 this.shadowOffset = 0;
7402 enableShadow : function(show){
7404 this.shadowDisabled = false;
7405 this.shadowOffset = this.lastShadowOffset;
7406 delete this.lastShadowOffset;
7414 // this code can execute repeatedly in milliseconds (i.e. during a drag) so
7415 // code size was sacrificed for effeciency (e.g. no getBox/setBox, no XY calls)
7416 sync : function(doShow){
7417 var sw = this.shadow;
7418 if(!this.updating && this.isVisible() && (sw || this.useShim)){
7419 var sh = this.getShim();
7421 var w = this.getWidth(),
7422 h = this.getHeight();
7424 var l = this.getLeft(true),
7425 t = this.getTop(true);
7427 if(sw && !this.shadowDisabled){
7428 if(doShow && !sw.isVisible()){
7431 sw.realign(l, t, w, h);
7437 // fit the shim behind the shadow, so it is shimmed too
7438 var a = sw.adjusts, s = sh.dom.style;
7439 s.left = (Math.min(l, l+a.l))+"px";
7440 s.top = (Math.min(t, t+a.t))+"px";
7441 s.width = (w+a.w)+"px";
7442 s.height = (h+a.h)+"px";
7449 sh.setLeftTop(l, t);
7456 destroy : function(){
7461 this.removeAllListeners();
7462 var pn = this.dom.parentNode;
7464 pn.removeChild(this.dom);
7466 Roo.Element.uncache(this.id);
7469 remove : function(){
7474 beginUpdate : function(){
7475 this.updating = true;
7479 endUpdate : function(){
7480 this.updating = false;
7485 hideUnders : function(negOffset){
7493 constrainXY : function(){
7495 var vw = Roo.lib.Dom.getViewWidth(),
7496 vh = Roo.lib.Dom.getViewHeight();
7497 var s = Roo.get(document).getScroll();
7499 var xy = this.getXY();
7500 var x = xy[0], y = xy[1];
7501 var w = this.dom.offsetWidth+this.shadowOffset, h = this.dom.offsetHeight+this.shadowOffset;
7502 // only move it if it needs it
7504 // first validate right/bottom
7505 if((x + w) > vw+s.left){
7506 x = vw - w - this.shadowOffset;
7509 if((y + h) > vh+s.top){
7510 y = vh - h - this.shadowOffset;
7513 // then make sure top/left isn't negative
7524 var ay = this.avoidY;
7525 if(y <= ay && (y+h) >= ay){
7531 supr.setXY.call(this, xy);
7537 isVisible : function(){
7538 return this.visible;
7542 showAction : function(){
7543 this.visible = true; // track visibility to prevent getStyle calls
7544 if(this.useDisplay === true){
7545 this.setDisplayed("");
7546 }else if(this.lastXY){
7547 supr.setXY.call(this, this.lastXY);
7548 }else if(this.lastLT){
7549 supr.setLeftTop.call(this, this.lastLT[0], this.lastLT[1]);
7554 hideAction : function(){
7555 this.visible = false;
7556 if(this.useDisplay === true){
7557 this.setDisplayed(false);
7559 this.setLeftTop(-10000,-10000);
7563 // overridden Element method
7564 setVisible : function(v, a, d, c, e){
7569 var cb = function(){
7574 }.createDelegate(this);
7575 supr.setVisible.call(this, true, true, d, cb, e);
7578 this.hideUnders(true);
7587 }.createDelegate(this);
7589 supr.setVisible.call(this, v, a, d, cb, e);
7598 storeXY : function(xy){
7603 storeLeftTop : function(left, top){
7605 this.lastLT = [left, top];
7609 beforeFx : function(){
7610 this.beforeAction();
7611 return Roo.Layer.superclass.beforeFx.apply(this, arguments);
7615 afterFx : function(){
7616 Roo.Layer.superclass.afterFx.apply(this, arguments);
7617 this.sync(this.isVisible());
7621 beforeAction : function(){
7622 if(!this.updating && this.shadow){
7627 // overridden Element method
7628 setLeft : function(left){
7629 this.storeLeftTop(left, this.getTop(true));
7630 supr.setLeft.apply(this, arguments);
7634 setTop : function(top){
7635 this.storeLeftTop(this.getLeft(true), top);
7636 supr.setTop.apply(this, arguments);
7640 setLeftTop : function(left, top){
7641 this.storeLeftTop(left, top);
7642 supr.setLeftTop.apply(this, arguments);
7646 setXY : function(xy, a, d, c, e){
7648 this.beforeAction();
7650 var cb = this.createCB(c);
7651 supr.setXY.call(this, xy, a, d, cb, e);
7658 createCB : function(c){
7669 // overridden Element method
7670 setX : function(x, a, d, c, e){
7671 this.setXY([x, this.getY()], a, d, c, e);
7674 // overridden Element method
7675 setY : function(y, a, d, c, e){
7676 this.setXY([this.getX(), y], a, d, c, e);
7679 // overridden Element method
7680 setSize : function(w, h, a, d, c, e){
7681 this.beforeAction();
7682 var cb = this.createCB(c);
7683 supr.setSize.call(this, w, h, a, d, cb, e);
7689 // overridden Element method
7690 setWidth : function(w, a, d, c, e){
7691 this.beforeAction();
7692 var cb = this.createCB(c);
7693 supr.setWidth.call(this, w, a, d, cb, e);
7699 // overridden Element method
7700 setHeight : function(h, a, d, c, e){
7701 this.beforeAction();
7702 var cb = this.createCB(c);
7703 supr.setHeight.call(this, h, a, d, cb, e);
7709 // overridden Element method
7710 setBounds : function(x, y, w, h, a, d, c, e){
7711 this.beforeAction();
7712 var cb = this.createCB(c);
7714 this.storeXY([x, y]);
7715 supr.setXY.call(this, [x, y]);
7716 supr.setSize.call(this, w, h, a, d, cb, e);
7719 supr.setBounds.call(this, x, y, w, h, a, d, cb, e);
7725 * Sets the z-index of this layer and adjusts any shadow and shim z-indexes. The layer z-index is automatically
7726 * incremented by two more than the value passed in so that it always shows above any shadow or shim (the shadow
7727 * element, if any, will be assigned z-index + 1, and the shim element, if any, will be assigned the unmodified z-index).
7728 * @param {Number} zindex The new z-index to set
7729 * @return {this} The Layer
7731 setZIndex : function(zindex){
7732 this.zindex = zindex;
7733 this.setStyle("z-index", zindex + 2);
7735 this.shadow.setZIndex(zindex + 1);
7738 this.shim.setStyle("z-index", zindex);
7744 * Ext JS Library 1.1.1
7745 * Copyright(c) 2006-2007, Ext JS, LLC.
7747 * Originally Released Under LGPL - original licence link has changed is not relivant.
7750 * <script type="text/javascript">
7756 * Simple class that can provide a shadow effect for any element. Note that the element MUST be absolutely positioned,
7757 * and the shadow does not provide any shimming. This should be used only in simple cases -- for more advanced
7758 * functionality that can also provide the same shadow effect, see the {@link Roo.Layer} class.
7760 * Create a new Shadow
7761 * @param {Object} config The config object
7763 Roo.Shadow = function(config){
7764 Roo.apply(this, config);
7765 if(typeof this.mode != "string"){
7766 this.mode = this.defaultMode;
7768 var o = this.offset, a = {h: 0};
7769 var rad = Math.floor(this.offset/2);
7770 switch(this.mode.toLowerCase()){ // all this hideous nonsense calculates the various offsets for shadows
7776 a.l -= this.offset + rad;
7777 a.t -= this.offset + rad;
7788 a.l -= (this.offset - rad);
7789 a.t -= this.offset + rad;
7791 a.w -= (this.offset - rad)*2;
7802 a.l -= (this.offset - rad);
7803 a.t -= (this.offset - rad);
7805 a.w -= (this.offset + rad + 1);
7806 a.h -= (this.offset + rad);
7815 Roo.Shadow.prototype = {
7817 * @cfg {String} mode
7818 * The shadow display mode. Supports the following options:<br />
7819 * sides: Shadow displays on both sides and bottom only<br />
7820 * frame: Shadow displays equally on all four sides<br />
7821 * drop: Traditional bottom-right drop shadow (default)
7824 * @cfg {String} offset
7825 * The number of pixels to offset the shadow from the element (defaults to 4)
7830 defaultMode: "drop",
7833 * Displays the shadow under the target element
7834 * @param {String/HTMLElement/Element} targetEl The id or element under which the shadow should display
7836 show : function(target){
7837 target = Roo.get(target);
7839 this.el = Roo.Shadow.Pool.pull();
7840 if(this.el.dom.nextSibling != target.dom){
7841 this.el.insertBefore(target);
7844 this.el.setStyle("z-index", this.zIndex || parseInt(target.getStyle("z-index"), 10)-1);
7846 this.el.dom.style.filter="progid:DXImageTransform.Microsoft.alpha(opacity=50) progid:DXImageTransform.Microsoft.Blur(pixelradius="+(this.offset)+")";
7849 target.getLeft(true),
7850 target.getTop(true),
7854 this.el.dom.style.display = "block";
7858 * Returns true if the shadow is visible, else false
7860 isVisible : function(){
7861 return this.el ? true : false;
7865 * Direct alignment when values are already available. Show must be called at least once before
7866 * calling this method to ensure it is initialized.
7867 * @param {Number} left The target element left position
7868 * @param {Number} top The target element top position
7869 * @param {Number} width The target element width
7870 * @param {Number} height The target element height
7872 realign : function(l, t, w, h){
7876 var a = this.adjusts, d = this.el.dom, s = d.style;
7878 s.left = (l+a.l)+"px";
7879 s.top = (t+a.t)+"px";
7880 var sw = (w+a.w), sh = (h+a.h), sws = sw +"px", shs = sh + "px";
7882 if(s.width != sws || s.height != shs){
7886 var cn = d.childNodes;
7887 var sww = Math.max(0, (sw-12))+"px";
7888 cn[0].childNodes[1].style.width = sww;
7889 cn[1].childNodes[1].style.width = sww;
7890 cn[2].childNodes[1].style.width = sww;
7891 cn[1].style.height = Math.max(0, (sh-12))+"px";
7901 this.el.dom.style.display = "none";
7902 Roo.Shadow.Pool.push(this.el);
7908 * Adjust the z-index of this shadow
7909 * @param {Number} zindex The new z-index
7911 setZIndex : function(z){
7914 this.el.setStyle("z-index", z);
7919 // Private utility class that manages the internal Shadow cache
7920 Roo.Shadow.Pool = function(){
7922 var markup = Roo.isIE ?
7923 '<div class="x-ie-shadow"></div>' :
7924 '<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>';
7929 sh = Roo.get(Roo.DomHelper.insertHtml("beforeBegin", document.body.firstChild, markup));
7930 sh.autoBoxAdjust = false;
7935 push : function(sh){
7941 * Ext JS Library 1.1.1
7942 * Copyright(c) 2006-2007, Ext JS, LLC.
7944 * Originally Released Under LGPL - original licence link has changed is not relivant.
7947 * <script type="text/javascript">
7952 * @class Roo.SplitBar
7953 * @extends Roo.util.Observable
7954 * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
7958 var split = new Roo.SplitBar("elementToDrag", "elementToSize",
7959 Roo.SplitBar.HORIZONTAL, Roo.SplitBar.LEFT);
7960 split.setAdapter(new Roo.SplitBar.AbsoluteLayoutAdapter("container"));
7961 split.minSize = 100;
7962 split.maxSize = 600;
7963 split.animate = true;
7964 split.on('moved', splitterMoved);
7967 * Create a new SplitBar
7968 * @param {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar.
7969 * @param {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged
7970 * @param {Number} orientation (optional) Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
7971 * @param {Number} placement (optional) Either Roo.SplitBar.LEFT or Roo.SplitBar.RIGHT for horizontal or
7972 Roo.SplitBar.TOP or Roo.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
7973 position of the SplitBar).
7975 Roo.SplitBar = function(dragElement, resizingElement, orientation, placement, existingProxy){
7978 this.el = Roo.get(dragElement, true);
7979 this.el.dom.unselectable = "on";
7981 this.resizingEl = Roo.get(resizingElement, true);
7985 * The orientation of the split. Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
7986 * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
7989 this.orientation = orientation || Roo.SplitBar.HORIZONTAL;
7992 * The minimum size of the resizing element. (Defaults to 0)
7998 * The maximum size of the resizing element. (Defaults to 2000)
8001 this.maxSize = 2000;
8004 * Whether to animate the transition to the new size
8007 this.animate = false;
8010 * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
8013 this.useShim = false;
8020 this.proxy = Roo.SplitBar.createProxy(this.orientation);
8022 this.proxy = Roo.get(existingProxy).dom;
8025 this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
8028 this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
8031 this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
8034 this.dragSpecs = {};
8037 * @private The adapter to use to positon and resize elements
8039 this.adapter = new Roo.SplitBar.BasicLayoutAdapter();
8040 this.adapter.init(this);
8042 if(this.orientation == Roo.SplitBar.HORIZONTAL){
8044 this.placement = placement || (this.el.getX() > this.resizingEl.getX() ? Roo.SplitBar.LEFT : Roo.SplitBar.RIGHT);
8045 this.el.addClass("x-splitbar-h");
8048 this.placement = placement || (this.el.getY() > this.resizingEl.getY() ? Roo.SplitBar.TOP : Roo.SplitBar.BOTTOM);
8049 this.el.addClass("x-splitbar-v");
8055 * Fires when the splitter is moved (alias for {@link #event-moved})
8056 * @param {Roo.SplitBar} this
8057 * @param {Number} newSize the new width or height
8062 * Fires when the splitter is moved
8063 * @param {Roo.SplitBar} this
8064 * @param {Number} newSize the new width or height
8068 * @event beforeresize
8069 * Fires before the splitter is dragged
8070 * @param {Roo.SplitBar} this
8072 "beforeresize" : true,
8074 "beforeapply" : true
8077 Roo.util.Observable.call(this);
8080 Roo.extend(Roo.SplitBar, Roo.util.Observable, {
8081 onStartProxyDrag : function(x, y){
8082 this.fireEvent("beforeresize", this);
8084 var o = Roo.DomHelper.insertFirst(document.body, {cls: "x-drag-overlay", html: " "}, true);
8086 o.enableDisplayMode("block");
8087 // all splitbars share the same overlay
8088 Roo.SplitBar.prototype.overlay = o;
8090 this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
8091 this.overlay.show();
8092 Roo.get(this.proxy).setDisplayed("block");
8093 var size = this.adapter.getElementSize(this);
8094 this.activeMinSize = this.getMinimumSize();;
8095 this.activeMaxSize = this.getMaximumSize();;
8096 var c1 = size - this.activeMinSize;
8097 var c2 = Math.max(this.activeMaxSize - size, 0);
8098 if(this.orientation == Roo.SplitBar.HORIZONTAL){
8099 this.dd.resetConstraints();
8100 this.dd.setXConstraint(
8101 this.placement == Roo.SplitBar.LEFT ? c1 : c2,
8102 this.placement == Roo.SplitBar.LEFT ? c2 : c1
8104 this.dd.setYConstraint(0, 0);
8106 this.dd.resetConstraints();
8107 this.dd.setXConstraint(0, 0);
8108 this.dd.setYConstraint(
8109 this.placement == Roo.SplitBar.TOP ? c1 : c2,
8110 this.placement == Roo.SplitBar.TOP ? c2 : c1
8113 this.dragSpecs.startSize = size;
8114 this.dragSpecs.startPoint = [x, y];
8115 Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
8119 * @private Called after the drag operation by the DDProxy
8121 onEndProxyDrag : function(e){
8122 Roo.get(this.proxy).setDisplayed(false);
8123 var endPoint = Roo.lib.Event.getXY(e);
8125 this.overlay.hide();
8128 if(this.orientation == Roo.SplitBar.HORIZONTAL){
8129 newSize = this.dragSpecs.startSize +
8130 (this.placement == Roo.SplitBar.LEFT ?
8131 endPoint[0] - this.dragSpecs.startPoint[0] :
8132 this.dragSpecs.startPoint[0] - endPoint[0]
8135 newSize = this.dragSpecs.startSize +
8136 (this.placement == Roo.SplitBar.TOP ?
8137 endPoint[1] - this.dragSpecs.startPoint[1] :
8138 this.dragSpecs.startPoint[1] - endPoint[1]
8141 newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
8142 if(newSize != this.dragSpecs.startSize){
8143 if(this.fireEvent('beforeapply', this, newSize) !== false){
8144 this.adapter.setElementSize(this, newSize);
8145 this.fireEvent("moved", this, newSize);
8146 this.fireEvent("resize", this, newSize);
8152 * Get the adapter this SplitBar uses
8153 * @return The adapter object
8155 getAdapter : function(){
8156 return this.adapter;
8160 * Set the adapter this SplitBar uses
8161 * @param {Object} adapter A SplitBar adapter object
8163 setAdapter : function(adapter){
8164 this.adapter = adapter;
8165 this.adapter.init(this);
8169 * Gets the minimum size for the resizing element
8170 * @return {Number} The minimum size
8172 getMinimumSize : function(){
8173 return this.minSize;
8177 * Sets the minimum size for the resizing element
8178 * @param {Number} minSize The minimum size
8180 setMinimumSize : function(minSize){
8181 this.minSize = minSize;
8185 * Gets the maximum size for the resizing element
8186 * @return {Number} The maximum size
8188 getMaximumSize : function(){
8189 return this.maxSize;
8193 * Sets the maximum size for the resizing element
8194 * @param {Number} maxSize The maximum size
8196 setMaximumSize : function(maxSize){
8197 this.maxSize = maxSize;
8201 * Sets the initialize size for the resizing element
8202 * @param {Number} size The initial size
8204 setCurrentSize : function(size){
8205 var oldAnimate = this.animate;
8206 this.animate = false;
8207 this.adapter.setElementSize(this, size);
8208 this.animate = oldAnimate;
8212 * Destroy this splitbar.
8213 * @param {Boolean} removeEl True to remove the element
8215 destroy : function(removeEl){
8220 this.proxy.parentNode.removeChild(this.proxy);
8228 * @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.
8230 Roo.SplitBar.createProxy = function(dir){
8231 var proxy = new Roo.Element(document.createElement("div"));
8232 proxy.unselectable();
8233 var cls = 'x-splitbar-proxy';
8234 proxy.addClass(cls + ' ' + (dir == Roo.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
8235 document.body.appendChild(proxy.dom);
8240 * @class Roo.SplitBar.BasicLayoutAdapter
8241 * Default Adapter. It assumes the splitter and resizing element are not positioned
8242 * elements and only gets/sets the width of the element. Generally used for table based layouts.
8244 Roo.SplitBar.BasicLayoutAdapter = function(){
8247 Roo.SplitBar.BasicLayoutAdapter.prototype = {
8248 // do nothing for now
8253 * Called before drag operations to get the current size of the resizing element.
8254 * @param {Roo.SplitBar} s The SplitBar using this adapter
8256 getElementSize : function(s){
8257 if(s.orientation == Roo.SplitBar.HORIZONTAL){
8258 return s.resizingEl.getWidth();
8260 return s.resizingEl.getHeight();
8265 * Called after drag operations to set the size of the resizing element.
8266 * @param {Roo.SplitBar} s The SplitBar using this adapter
8267 * @param {Number} newSize The new size to set
8268 * @param {Function} onComplete A function to be invoked when resizing is complete
8270 setElementSize : function(s, newSize, onComplete){
8271 if(s.orientation == Roo.SplitBar.HORIZONTAL){
8273 s.resizingEl.setWidth(newSize);
8275 onComplete(s, newSize);
8278 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
8283 s.resizingEl.setHeight(newSize);
8285 onComplete(s, newSize);
8288 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
8295 *@class Roo.SplitBar.AbsoluteLayoutAdapter
8296 * @extends Roo.SplitBar.BasicLayoutAdapter
8297 * Adapter that moves the splitter element to align with the resized sizing element.
8298 * Used with an absolute positioned SplitBar.
8299 * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
8300 * document.body, make sure you assign an id to the body element.
8302 Roo.SplitBar.AbsoluteLayoutAdapter = function(container){
8303 this.basic = new Roo.SplitBar.BasicLayoutAdapter();
8304 this.container = Roo.get(container);
8307 Roo.SplitBar.AbsoluteLayoutAdapter.prototype = {
8312 getElementSize : function(s){
8313 return this.basic.getElementSize(s);
8316 setElementSize : function(s, newSize, onComplete){
8317 this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
8320 moveSplitter : function(s){
8321 var yes = Roo.SplitBar;
8322 switch(s.placement){
8324 s.el.setX(s.resizingEl.getRight());
8327 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
8330 s.el.setY(s.resizingEl.getBottom());
8333 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
8340 * Orientation constant - Create a vertical SplitBar
8344 Roo.SplitBar.VERTICAL = 1;
8347 * Orientation constant - Create a horizontal SplitBar
8351 Roo.SplitBar.HORIZONTAL = 2;
8354 * Placement constant - The resizing element is to the left of the splitter element
8358 Roo.SplitBar.LEFT = 1;
8361 * Placement constant - The resizing element is to the right of the splitter element
8365 Roo.SplitBar.RIGHT = 2;
8368 * Placement constant - The resizing element is positioned above the splitter element
8372 Roo.SplitBar.TOP = 3;
8375 * Placement constant - The resizing element is positioned under splitter element
8379 Roo.SplitBar.BOTTOM = 4;
8382 * Ext JS Library 1.1.1
8383 * Copyright(c) 2006-2007, Ext JS, LLC.
8385 * Originally Released Under LGPL - original licence link has changed is not relivant.
8388 * <script type="text/javascript">
8393 * @extends Roo.util.Observable
8394 * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template.
8395 * This class also supports single and multi selection modes. <br>
8396 * Create a data model bound view:
8398 var store = new Roo.data.Store(...);
8400 var view = new Roo.View({
8402 tpl : '<div id="{0}">{2} - {1}</div>', // auto create template
8405 selectedClass: "ydataview-selected",
8409 // listen for node click?
8410 view.on("click", function(vw, index, node, e){
8411 alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
8415 dataModel.load("foobar.xml");
8417 For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
8419 * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
8420 * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
8422 * Note: old style constructor is still suported (container, template, config)
8426 * @param {Object} config The config object
8429 Roo.View = function(config, depreciated_tpl, depreciated_config){
8431 this.parent = false;
8433 if (typeof(depreciated_tpl) == 'undefined') {
8434 // new way.. - universal constructor.
8435 Roo.apply(this, config);
8436 this.el = Roo.get(this.el);
8439 this.el = Roo.get(config);
8440 this.tpl = depreciated_tpl;
8441 Roo.apply(this, depreciated_config);
8443 this.wrapEl = this.el.wrap().wrap();
8444 ///this.el = this.wrapEla.appendChild(document.createElement("div"));
8447 if(typeof(this.tpl) == "string"){
8448 this.tpl = new Roo.Template(this.tpl);
8450 // support xtype ctors..
8451 this.tpl = new Roo.factory(this.tpl, Roo);
8460 * @event beforeclick
8461 * Fires before a click is processed. Returns false to cancel the default action.
8462 * @param {Roo.View} this
8463 * @param {Number} index The index of the target node
8464 * @param {HTMLElement} node The target node
8465 * @param {Roo.EventObject} e The raw event object
8467 "beforeclick" : true,
8470 * Fires when a template node is clicked.
8471 * @param {Roo.View} this
8472 * @param {Number} index The index of the target node
8473 * @param {HTMLElement} node The target node
8474 * @param {Roo.EventObject} e The raw event object
8479 * Fires when a template node is double clicked.
8480 * @param {Roo.View} this
8481 * @param {Number} index The index of the target node
8482 * @param {HTMLElement} node The target node
8483 * @param {Roo.EventObject} e The raw event object
8487 * @event contextmenu
8488 * Fires when a template node is right clicked.
8489 * @param {Roo.View} this
8490 * @param {Number} index The index of the target node
8491 * @param {HTMLElement} node The target node
8492 * @param {Roo.EventObject} e The raw event object
8494 "contextmenu" : true,
8496 * @event selectionchange
8497 * Fires when the selected nodes change.
8498 * @param {Roo.View} this
8499 * @param {Array} selections Array of the selected nodes
8501 "selectionchange" : true,
8504 * @event beforeselect
8505 * Fires before a selection is made. If any handlers return false, the selection is cancelled.
8506 * @param {Roo.View} this
8507 * @param {HTMLElement} node The node to be selected
8508 * @param {Array} selections Array of currently selected nodes
8510 "beforeselect" : true,
8512 * @event preparedata
8513 * Fires on every row to render, to allow you to change the data.
8514 * @param {Roo.View} this
8515 * @param {Object} data to be rendered (change this)
8517 "preparedata" : true
8525 "click": this.onClick,
8526 "dblclick": this.onDblClick,
8527 "contextmenu": this.onContextMenu,
8531 this.selections = [];
8533 this.cmp = new Roo.CompositeElementLite([]);
8535 this.store = Roo.factory(this.store, Roo.data);
8536 this.setStore(this.store, true);
8539 if ( this.footer && this.footer.xtype) {
8541 var fctr = this.wrapEl.appendChild(document.createElement("div"));
8543 this.footer.dataSource = this.store
8544 this.footer.container = fctr;
8545 this.footer = Roo.factory(this.footer, Roo);
8546 fctr.insertFirst(this.el);
8548 // this is a bit insane - as the paging toolbar seems to detach the el..
8549 // dom.parentNode.parentNode.parentNode
8550 // they get detached?
8554 Roo.View.superclass.constructor.call(this);
8559 Roo.extend(Roo.View, Roo.util.Observable, {
8562 * @cfg {Roo.data.Store} store Data store to load data from.
8567 * @cfg {String|Roo.Element} el The container element.
8572 * @cfg {String|Roo.Template} tpl The template used by this View
8576 * @cfg {String} dataName the named area of the template to use as the data area
8577 * Works with domtemplates roo-name="name"
8581 * @cfg {String} selectedClass The css class to add to selected nodes
8583 selectedClass : "x-view-selected",
8585 * @cfg {String} emptyText The empty text to show when nothing is loaded.
8590 * @cfg {String} text to display on mask (default Loading)
8594 * @cfg {Boolean} multiSelect Allow multiple selection
8596 multiSelect : false,
8598 * @cfg {Boolean} singleSelect Allow single selection
8600 singleSelect: false,
8603 * @cfg {Boolean} toggleSelect - selecting
8605 toggleSelect : false,
8608 * @cfg {Boolean} tickable - selecting
8613 * Returns the element this view is bound to.
8614 * @return {Roo.Element}
8623 * Refreshes the view. - called by datachanged on the store. - do not call directly.
8625 refresh : function(){
8629 // if we are using something like 'domtemplate', then
8630 // the what gets used is:
8631 // t.applySubtemplate(NAME, data, wrapping data..)
8632 // the outer template then get' applied with
8633 // the store 'extra data'
8634 // and the body get's added to the
8635 // roo-name="data" node?
8636 // <span class='roo-tpl-{name}'></span> ?????
8640 this.clearSelections();
8643 var records = this.store.getRange();
8644 if(records.length < 1) {
8646 // is this valid?? = should it render a template??
8648 this.el.update(this.emptyText);
8652 if (this.dataName) {
8653 this.el.update(t.apply(this.store.meta)); //????
8654 el = this.el.child('.roo-tpl-' + this.dataName);
8657 for(var i = 0, len = records.length; i < len; i++){
8658 var data = this.prepareData(records[i].data, i, records[i]);
8659 this.fireEvent("preparedata", this, data, i, records[i]);
8661 var d = Roo.apply({}, data);
8664 Roo.apply(d, {'roo-id' : Roo.id()});
8668 Roo.each(this.parent.item, function(item){
8669 if(item[_this.parent.valueField] != data[_this.parent.valueField]){
8672 Roo.apply(d, {'roo-data-checked' : 'checked'});
8676 html[html.length] = Roo.util.Format.trim(
8678 t.applySubtemplate(this.dataName, d, this.store.meta) :
8685 el.update(html.join(""));
8686 this.nodes = el.dom.childNodes;
8687 this.updateIndexes(0);
8692 * Function to override to reformat the data that is sent to
8693 * the template for each node.
8694 * DEPRICATED - use the preparedata event handler.
8695 * @param {Array/Object} data The raw data (array of colData for a data model bound view or
8696 * a JSON object for an UpdateManager bound view).
8698 prepareData : function(data, index, record)
8700 this.fireEvent("preparedata", this, data, index, record);
8704 onUpdate : function(ds, record){
8705 Roo.log('on update');
8706 this.clearSelections();
8707 var index = this.store.indexOf(record);
8708 var n = this.nodes[index];
8709 this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
8710 n.parentNode.removeChild(n);
8711 this.updateIndexes(index, index);
8717 onAdd : function(ds, records, index)
8719 Roo.log(['on Add', ds, records, index] );
8720 this.clearSelections();
8721 if(this.nodes.length == 0){
8725 var n = this.nodes[index];
8726 for(var i = 0, len = records.length; i < len; i++){
8727 var d = this.prepareData(records[i].data, i, records[i]);
8729 this.tpl.insertBefore(n, d);
8732 this.tpl.append(this.el, d);
8735 this.updateIndexes(index);
8738 onRemove : function(ds, record, index){
8739 Roo.log('onRemove');
8740 this.clearSelections();
8741 var el = this.dataName ?
8742 this.el.child('.roo-tpl-' + this.dataName) :
8745 el.dom.removeChild(this.nodes[index]);
8746 this.updateIndexes(index);
8750 * Refresh an individual node.
8751 * @param {Number} index
8753 refreshNode : function(index){
8754 this.onUpdate(this.store, this.store.getAt(index));
8757 updateIndexes : function(startIndex, endIndex){
8758 var ns = this.nodes;
8759 startIndex = startIndex || 0;
8760 endIndex = endIndex || ns.length - 1;
8761 for(var i = startIndex; i <= endIndex; i++){
8762 ns[i].nodeIndex = i;
8767 * Changes the data store this view uses and refresh the view.
8768 * @param {Store} store
8770 setStore : function(store, initial){
8771 if(!initial && this.store){
8772 this.store.un("datachanged", this.refresh);
8773 this.store.un("add", this.onAdd);
8774 this.store.un("remove", this.onRemove);
8775 this.store.un("update", this.onUpdate);
8776 this.store.un("clear", this.refresh);
8777 this.store.un("beforeload", this.onBeforeLoad);
8778 this.store.un("load", this.onLoad);
8779 this.store.un("loadexception", this.onLoad);
8783 store.on("datachanged", this.refresh, this);
8784 store.on("add", this.onAdd, this);
8785 store.on("remove", this.onRemove, this);
8786 store.on("update", this.onUpdate, this);
8787 store.on("clear", this.refresh, this);
8788 store.on("beforeload", this.onBeforeLoad, this);
8789 store.on("load", this.onLoad, this);
8790 store.on("loadexception", this.onLoad, this);
8798 * onbeforeLoad - masks the loading area.
8801 onBeforeLoad : function(store,opts)
8803 Roo.log('onBeforeLoad');
8807 this.el.mask(this.mask ? this.mask : "Loading" );
8809 onLoad : function ()
8816 * Returns the template node the passed child belongs to or null if it doesn't belong to one.
8817 * @param {HTMLElement} node
8818 * @return {HTMLElement} The template node
8820 findItemFromChild : function(node){
8821 var el = this.dataName ?
8822 this.el.child('.roo-tpl-' + this.dataName,true) :
8825 if(!node || node.parentNode == el){
8828 var p = node.parentNode;
8829 while(p && p != el){
8830 if(p.parentNode == el){
8839 onClick : function(e){
8840 var item = this.findItemFromChild(e.getTarget());
8842 var index = this.indexOf(item);
8843 if(this.onItemClick(item, index, e) !== false){
8844 this.fireEvent("click", this, index, item, e);
8847 this.clearSelections();
8852 onContextMenu : function(e){
8853 var item = this.findItemFromChild(e.getTarget());
8855 this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
8860 onDblClick : function(e){
8861 var item = this.findItemFromChild(e.getTarget());
8863 this.fireEvent("dblclick", this, this.indexOf(item), item, e);
8867 onItemClick : function(item, index, e)
8869 if(this.fireEvent("beforeclick", this, index, item, e) === false){
8872 if (this.toggleSelect) {
8873 var m = this.isSelected(item) ? 'unselect' : 'select';
8876 _t[m](item, true, false);
8879 if(this.multiSelect || this.singleSelect){
8880 if(this.multiSelect && e.shiftKey && this.lastSelection){
8881 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
8883 this.select(item, this.multiSelect && e.ctrlKey);
8884 this.lastSelection = item;
8896 * Get the number of selected nodes.
8899 getSelectionCount : function(){
8900 return this.selections.length;
8904 * Get the currently selected nodes.
8905 * @return {Array} An array of HTMLElements
8907 getSelectedNodes : function(){
8908 return this.selections;
8912 * Get the indexes of the selected nodes.
8915 getSelectedIndexes : function(){
8916 var indexes = [], s = this.selections;
8917 for(var i = 0, len = s.length; i < len; i++){
8918 indexes.push(s[i].nodeIndex);
8924 * Clear all selections
8925 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
8927 clearSelections : function(suppressEvent){
8928 if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
8929 this.cmp.elements = this.selections;
8930 this.cmp.removeClass(this.selectedClass);
8931 this.selections = [];
8933 this.fireEvent("selectionchange", this, this.selections);
8939 * Returns true if the passed node is selected
8940 * @param {HTMLElement/Number} node The node or node index
8943 isSelected : function(node){
8944 var s = this.selections;
8948 node = this.getNode(node);
8949 return s.indexOf(node) !== -1;
8954 * @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
8955 * @param {Boolean} keepExisting (optional) true to keep existing selections
8956 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
8958 select : function(nodeInfo, keepExisting, suppressEvent){
8959 if(nodeInfo instanceof Array){
8961 this.clearSelections(true);
8963 for(var i = 0, len = nodeInfo.length; i < len; i++){
8964 this.select(nodeInfo[i], true, true);
8968 var node = this.getNode(nodeInfo);
8969 if(!node || this.isSelected(node)){
8970 return; // already selected.
8973 this.clearSelections(true);
8976 if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
8977 Roo.fly(node).addClass(this.selectedClass);
8978 this.selections.push(node);
8980 this.fireEvent("selectionchange", this, this.selections);
8988 * @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
8989 * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
8990 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
8992 unselect : function(nodeInfo, keepExisting, suppressEvent)
8994 if(nodeInfo instanceof Array){
8995 Roo.each(this.selections, function(s) {
8996 this.unselect(s, nodeInfo);
9000 var node = this.getNode(nodeInfo);
9001 if(!node || !this.isSelected(node)){
9002 Roo.log("not selected");
9003 return; // not selected.
9007 Roo.each(this.selections, function(s) {
9009 Roo.fly(node).removeClass(this.selectedClass);
9016 this.selections= ns;
9017 this.fireEvent("selectionchange", this, this.selections);
9021 * Gets a template node.
9022 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
9023 * @return {HTMLElement} The node or null if it wasn't found
9025 getNode : function(nodeInfo){
9026 if(typeof nodeInfo == "string"){
9027 return document.getElementById(nodeInfo);
9028 }else if(typeof nodeInfo == "number"){
9029 return this.nodes[nodeInfo];
9035 * Gets a range template nodes.
9036 * @param {Number} startIndex
9037 * @param {Number} endIndex
9038 * @return {Array} An array of nodes
9040 getNodes : function(start, end){
9041 var ns = this.nodes;
9043 end = typeof end == "undefined" ? ns.length - 1 : end;
9046 for(var i = start; i <= end; i++){
9050 for(var i = start; i >= end; i--){
9058 * Finds the index of the passed node
9059 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
9060 * @return {Number} The index of the node or -1
9062 indexOf : function(node){
9063 node = this.getNode(node);
9064 if(typeof node.nodeIndex == "number"){
9065 return node.nodeIndex;
9067 var ns = this.nodes;
9068 for(var i = 0, len = ns.length; i < len; i++){
9078 * Ext JS Library 1.1.1
9079 * Copyright(c) 2006-2007, Ext JS, LLC.
9081 * Originally Released Under LGPL - original licence link has changed is not relivant.
9084 * <script type="text/javascript">
9088 * @class Roo.JsonView
9090 * Shortcut class to create a JSON + {@link Roo.UpdateManager} template view. Usage:
9092 var view = new Roo.JsonView({
9093 container: "my-element",
9094 tpl: '<div id="{id}">{foo} - {bar}</div>', // auto create template
9099 // listen for node click?
9100 view.on("click", function(vw, index, node, e){
9101 alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
9104 // direct load of JSON data
9105 view.load("foobar.php");
9107 // Example from my blog list
9108 var tpl = new Roo.Template(
9109 '<div class="entry">' +
9110 '<a class="entry-title" href="{link}">{title}</a>' +
9111 "<h4>{date} by {author} | {comments} Comments</h4>{description}" +
9112 "</div><hr />"
9115 var moreView = new Roo.JsonView({
9116 container : "entry-list",
9120 moreView.on("beforerender", this.sortEntries, this);
9122 url: "/blog/get-posts.php",
9123 params: "allposts=true",
9124 text: "Loading Blog Entries..."
9128 * Note: old code is supported with arguments : (container, template, config)
9132 * Create a new JsonView
9134 * @param {Object} config The config object
9137 Roo.JsonView = function(config, depreciated_tpl, depreciated_config){
9140 Roo.JsonView.superclass.constructor.call(this, config, depreciated_tpl, depreciated_config);
9142 var um = this.el.getUpdateManager();
9143 um.setRenderer(this);
9144 um.on("update", this.onLoad, this);
9145 um.on("failure", this.onLoadException, this);
9148 * @event beforerender
9149 * Fires before rendering of the downloaded JSON data.
9150 * @param {Roo.JsonView} this
9151 * @param {Object} data The JSON data loaded
9155 * Fires when data is loaded.
9156 * @param {Roo.JsonView} this
9157 * @param {Object} data The JSON data loaded
9158 * @param {Object} response The raw Connect response object
9161 * @event loadexception
9162 * Fires when loading fails.
9163 * @param {Roo.JsonView} this
9164 * @param {Object} response The raw Connect response object
9167 'beforerender' : true,
9169 'loadexception' : true
9172 Roo.extend(Roo.JsonView, Roo.View, {
9174 * @type {String} The root property in the loaded JSON object that contains the data
9179 * Refreshes the view.
9181 refresh : function(){
9182 this.clearSelections();
9185 var o = this.jsonData;
9186 if(o && o.length > 0){
9187 for(var i = 0, len = o.length; i < len; i++){
9188 var data = this.prepareData(o[i], i, o);
9189 html[html.length] = this.tpl.apply(data);
9192 html.push(this.emptyText);
9194 this.el.update(html.join(""));
9195 this.nodes = this.el.dom.childNodes;
9196 this.updateIndexes(0);
9200 * 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.
9201 * @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:
9204 url: "your-url.php",
9205 params: {param1: "foo", param2: "bar"}, // or a URL encoded string
9206 callback: yourFunction,
9207 scope: yourObject, //(optional scope)
9215 * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
9216 * 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.
9217 * @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}
9218 * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
9219 * @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.
9222 var um = this.el.getUpdateManager();
9223 um.update.apply(um, arguments);
9226 render : function(el, response){
9227 this.clearSelections();
9231 o = Roo.util.JSON.decode(response.responseText);
9234 o = o[this.jsonRoot];
9239 * The current JSON data or null
9242 this.beforeRender();
9247 * Get the number of records in the current JSON dataset
9250 getCount : function(){
9251 return this.jsonData ? this.jsonData.length : 0;
9255 * Returns the JSON object for the specified node(s)
9256 * @param {HTMLElement/Array} node The node or an array of nodes
9257 * @return {Object/Array} If you pass in an array, you get an array back, otherwise
9258 * you get the JSON object for the node
9260 getNodeData : function(node){
9261 if(node instanceof Array){
9263 for(var i = 0, len = node.length; i < len; i++){
9264 data.push(this.getNodeData(node[i]));
9268 return this.jsonData[this.indexOf(node)] || null;
9271 beforeRender : function(){
9272 this.snapshot = this.jsonData;
9274 this.sort.apply(this, this.sortInfo);
9276 this.fireEvent("beforerender", this, this.jsonData);
9279 onLoad : function(el, o){
9280 this.fireEvent("load", this, this.jsonData, o);
9283 onLoadException : function(el, o){
9284 this.fireEvent("loadexception", this, o);
9288 * Filter the data by a specific property.
9289 * @param {String} property A property on your JSON objects
9290 * @param {String/RegExp} value Either string that the property values
9291 * should start with, or a RegExp to test against the property
9293 filter : function(property, value){
9296 var ss = this.snapshot;
9297 if(typeof value == "string"){
9298 var vlen = value.length;
9303 value = value.toLowerCase();
9304 for(var i = 0, len = ss.length; i < len; i++){
9306 if(o[property].substr(0, vlen).toLowerCase() == value){
9310 } else if(value.exec){ // regex?
9311 for(var i = 0, len = ss.length; i < len; i++){
9313 if(value.test(o[property])){
9320 this.jsonData = data;
9326 * Filter by a function. The passed function will be called with each
9327 * object in the current dataset. If the function returns true the value is kept,
9328 * otherwise it is filtered.
9329 * @param {Function} fn
9330 * @param {Object} scope (optional) The scope of the function (defaults to this JsonView)
9332 filterBy : function(fn, scope){
9335 var ss = this.snapshot;
9336 for(var i = 0, len = ss.length; i < len; i++){
9338 if(fn.call(scope || this, o)){
9342 this.jsonData = data;
9348 * Clears the current filter.
9350 clearFilter : function(){
9351 if(this.snapshot && this.jsonData != this.snapshot){
9352 this.jsonData = this.snapshot;
9359 * Sorts the data for this view and refreshes it.
9360 * @param {String} property A property on your JSON objects to sort on
9361 * @param {String} direction (optional) "desc" or "asc" (defaults to "asc")
9362 * @param {Function} sortType (optional) A function to call to convert the data to a sortable value.
9364 sort : function(property, dir, sortType){
9365 this.sortInfo = Array.prototype.slice.call(arguments, 0);
9368 var dsc = dir && dir.toLowerCase() == "desc";
9369 var f = function(o1, o2){
9370 var v1 = sortType ? sortType(o1[p]) : o1[p];
9371 var v2 = sortType ? sortType(o2[p]) : o2[p];
9374 return dsc ? +1 : -1;
9376 return dsc ? -1 : +1;
9381 this.jsonData.sort(f);
9383 if(this.jsonData != this.snapshot){
9384 this.snapshot.sort(f);
9390 * Ext JS Library 1.1.1
9391 * Copyright(c) 2006-2007, Ext JS, LLC.
9393 * Originally Released Under LGPL - original licence link has changed is not relivant.
9396 * <script type="text/javascript">
9401 * @class Roo.ColorPalette
9402 * @extends Roo.Component
9403 * Simple color palette class for choosing colors. The palette can be rendered to any container.<br />
9404 * Here's an example of typical usage:
9406 var cp = new Roo.ColorPalette({value:'993300'}); // initial selected color
9407 cp.render('my-div');
9409 cp.on('select', function(palette, selColor){
9410 // do something with selColor
9414 * Create a new ColorPalette
9415 * @param {Object} config The config object
9417 Roo.ColorPalette = function(config){
9418 Roo.ColorPalette.superclass.constructor.call(this, config);
9422 * Fires when a color is selected
9423 * @param {ColorPalette} this
9424 * @param {String} color The 6-digit color hex code (without the # symbol)
9430 this.on("select", this.handler, this.scope, true);
9433 Roo.extend(Roo.ColorPalette, Roo.Component, {
9435 * @cfg {String} itemCls
9436 * The CSS class to apply to the containing element (defaults to "x-color-palette")
9438 itemCls : "x-color-palette",
9440 * @cfg {String} value
9441 * The initial color to highlight (should be a valid 6-digit color hex code without the # symbol). Note that
9442 * the hex codes are case-sensitive.
9447 ctype: "Roo.ColorPalette",
9450 * @cfg {Boolean} allowReselect If set to true then reselecting a color that is already selected fires the selection event
9452 allowReselect : false,
9455 * <p>An array of 6-digit color hex code strings (without the # symbol). This array can contain any number
9456 * of colors, and each hex code should be unique. The width of the palette is controlled via CSS by adjusting
9457 * the width property of the 'x-color-palette' class (or assigning a custom class), so you can balance the number
9458 * of colors with the width setting until the box is symmetrical.</p>
9459 * <p>You can override individual colors if needed:</p>
9461 var cp = new Roo.ColorPalette();
9462 cp.colors[0] = "FF0000"; // change the first box to red
9465 Or you can provide a custom array of your own for complete control:
9467 var cp = new Roo.ColorPalette();
9468 cp.colors = ["000000", "993300", "333300"];
9473 "000000", "993300", "333300", "003300", "003366", "000080", "333399", "333333",
9474 "800000", "FF6600", "808000", "008000", "008080", "0000FF", "666699", "808080",
9475 "FF0000", "FF9900", "99CC00", "339966", "33CCCC", "3366FF", "800080", "969696",
9476 "FF00FF", "FFCC00", "FFFF00", "00FF00", "00FFFF", "00CCFF", "993366", "C0C0C0",
9477 "FF99CC", "FFCC99", "FFFF99", "CCFFCC", "CCFFFF", "99CCFF", "CC99FF", "FFFFFF"
9481 onRender : function(container, position){
9482 var t = new Roo.MasterTemplate(
9483 '<tpl><a href="#" class="color-{0}" hidefocus="on"><em><span style="background:#{0}" unselectable="on"> </span></em></a></tpl>'
9485 var c = this.colors;
9486 for(var i = 0, len = c.length; i < len; i++){
9489 var el = document.createElement("div");
9490 el.className = this.itemCls;
9492 container.dom.insertBefore(el, position);
9493 this.el = Roo.get(el);
9494 this.el.on(this.clickEvent, this.handleClick, this, {delegate: "a"});
9495 if(this.clickEvent != 'click'){
9496 this.el.on('click', Roo.emptyFn, this, {delegate: "a", preventDefault:true});
9501 afterRender : function(){
9502 Roo.ColorPalette.superclass.afterRender.call(this);
9511 handleClick : function(e, t){
9514 var c = t.className.match(/(?:^|\s)color-(.{6})(?:\s|$)/)[1];
9515 this.select(c.toUpperCase());
9520 * Selects the specified color in the palette (fires the select event)
9521 * @param {String} color A valid 6-digit color hex code (# will be stripped if included)
9523 select : function(color){
9524 color = color.replace("#", "");
9525 if(color != this.value || this.allowReselect){
9528 el.child("a.color-"+this.value).removeClass("x-color-palette-sel");
9530 el.child("a.color-"+color).addClass("x-color-palette-sel");
9532 this.fireEvent("select", this, color);
9537 * Ext JS Library 1.1.1
9538 * Copyright(c) 2006-2007, Ext JS, LLC.
9540 * Originally Released Under LGPL - original licence link has changed is not relivant.
9543 * <script type="text/javascript">
9547 * @class Roo.DatePicker
9548 * @extends Roo.Component
9549 * Simple date picker class.
9551 * Create a new DatePicker
9552 * @param {Object} config The config object
9554 Roo.DatePicker = function(config){
9555 Roo.DatePicker.superclass.constructor.call(this, config);
9557 this.value = config && config.value ?
9558 config.value.clearTime() : new Date().clearTime();
9563 * Fires when a date is selected
9564 * @param {DatePicker} this
9565 * @param {Date} date The selected date
9569 * @event monthchange
9570 * Fires when the displayed month changes
9571 * @param {DatePicker} this
9572 * @param {Date} date The selected month
9578 this.on("select", this.handler, this.scope || this);
9580 // build the disabledDatesRE
9581 if(!this.disabledDatesRE && this.disabledDates){
9582 var dd = this.disabledDates;
9584 for(var i = 0; i < dd.length; i++){
9586 if(i != dd.length-1) re += "|";
9588 this.disabledDatesRE = new RegExp(re + ")");
9592 Roo.extend(Roo.DatePicker, Roo.Component, {
9594 * @cfg {String} todayText
9595 * The text to display on the button that selects the current date (defaults to "Today")
9597 todayText : "Today",
9599 * @cfg {String} okText
9600 * The text to display on the ok button
9602 okText : " OK ", //   to give the user extra clicking room
9604 * @cfg {String} cancelText
9605 * The text to display on the cancel button
9607 cancelText : "Cancel",
9609 * @cfg {String} todayTip
9610 * The tooltip to display for the button that selects the current date (defaults to "{current date} (Spacebar)")
9612 todayTip : "{0} (Spacebar)",
9614 * @cfg {Date} minDate
9615 * Minimum allowable date (JavaScript date object, defaults to null)
9619 * @cfg {Date} maxDate
9620 * Maximum allowable date (JavaScript date object, defaults to null)
9624 * @cfg {String} minText
9625 * The error text to display if the minDate validation fails (defaults to "This date is before the minimum date")
9627 minText : "This date is before the minimum date",
9629 * @cfg {String} maxText
9630 * The error text to display if the maxDate validation fails (defaults to "This date is after the maximum date")
9632 maxText : "This date is after the maximum date",
9634 * @cfg {String} format
9635 * The default date format string which can be overriden for localization support. The format must be
9636 * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
9640 * @cfg {Array} disabledDays
9641 * An array of days to disable, 0-based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
9643 disabledDays : null,
9645 * @cfg {String} disabledDaysText
9646 * The tooltip to display when the date falls on a disabled day (defaults to "")
9648 disabledDaysText : "",
9650 * @cfg {RegExp} disabledDatesRE
9651 * JavaScript regular expression used to disable a pattern of dates (defaults to null)
9653 disabledDatesRE : null,
9655 * @cfg {String} disabledDatesText
9656 * The tooltip text to display when the date falls on a disabled date (defaults to "")
9658 disabledDatesText : "",
9660 * @cfg {Boolean} constrainToViewport
9661 * True to constrain the date picker to the viewport (defaults to true)
9663 constrainToViewport : true,
9665 * @cfg {Array} monthNames
9666 * An array of textual month names which can be overriden for localization support (defaults to Date.monthNames)
9668 monthNames : Date.monthNames,
9670 * @cfg {Array} dayNames
9671 * An array of textual day names which can be overriden for localization support (defaults to Date.dayNames)
9673 dayNames : Date.dayNames,
9675 * @cfg {String} nextText
9676 * The next month navigation button tooltip (defaults to 'Next Month (Control+Right)')
9678 nextText: 'Next Month (Control+Right)',
9680 * @cfg {String} prevText
9681 * The previous month navigation button tooltip (defaults to 'Previous Month (Control+Left)')
9683 prevText: 'Previous Month (Control+Left)',
9685 * @cfg {String} monthYearText
9686 * The header month selector tooltip (defaults to 'Choose a month (Control+Up/Down to move years)')
9688 monthYearText: 'Choose a month (Control+Up/Down to move years)',
9690 * @cfg {Number} startDay
9691 * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
9695 * @cfg {Bool} showClear
9696 * Show a clear button (usefull for date form elements that can be blank.)
9702 * Sets the value of the date field
9703 * @param {Date} value The date to set
9705 setValue : function(value){
9706 var old = this.value;
9708 if (typeof(value) == 'string') {
9710 value = Date.parseDate(value, this.format);
9716 this.value = value.clearTime(true);
9718 this.update(this.value);
9723 * Gets the current selected value of the date field
9724 * @return {Date} The selected date
9726 getValue : function(){
9733 this.update(this.activeDate);
9738 onRender : function(container, position){
9741 '<table cellspacing="0">',
9742 '<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>',
9743 '<tr><td colspan="3"><table class="x-date-inner" cellspacing="0"><thead><tr>'];
9744 var dn = this.dayNames;
9745 for(var i = 0; i < 7; i++){
9746 var d = this.startDay+i;
9750 m.push("<th><span>", dn[d].substr(0,1), "</span></th>");
9752 m[m.length] = "</tr></thead><tbody><tr>";
9753 for(var i = 0; i < 42; i++) {
9754 if(i % 7 == 0 && i != 0){
9755 m[m.length] = "</tr><tr>";
9757 m[m.length] = '<td><a href="#" hidefocus="on" class="x-date-date" tabIndex="1"><em><span></span></em></a></td>';
9759 m[m.length] = '</tr></tbody></table></td></tr><tr>'+
9760 '<td colspan="3" class="x-date-bottom" align="center"></td></tr></table><div class="x-date-mp"></div>';
9762 var el = document.createElement("div");
9763 el.className = "x-date-picker";
9764 el.innerHTML = m.join("");
9766 container.dom.insertBefore(el, position);
9768 this.el = Roo.get(el);
9769 this.eventEl = Roo.get(el.firstChild);
9771 new Roo.util.ClickRepeater(this.el.child("td.x-date-left a"), {
9772 handler: this.showPrevMonth,
9774 preventDefault:true,
9778 new Roo.util.ClickRepeater(this.el.child("td.x-date-right a"), {
9779 handler: this.showNextMonth,
9781 preventDefault:true,
9785 this.eventEl.on("mousewheel", this.handleMouseWheel, this);
9787 this.monthPicker = this.el.down('div.x-date-mp');
9788 this.monthPicker.enableDisplayMode('block');
9790 var kn = new Roo.KeyNav(this.eventEl, {
9791 "left" : function(e){
9793 this.showPrevMonth() :
9794 this.update(this.activeDate.add("d", -1));
9797 "right" : function(e){
9799 this.showNextMonth() :
9800 this.update(this.activeDate.add("d", 1));
9805 this.showNextYear() :
9806 this.update(this.activeDate.add("d", -7));
9809 "down" : function(e){
9811 this.showPrevYear() :
9812 this.update(this.activeDate.add("d", 7));
9815 "pageUp" : function(e){
9816 this.showNextMonth();
9819 "pageDown" : function(e){
9820 this.showPrevMonth();
9823 "enter" : function(e){
9824 e.stopPropagation();
9831 this.eventEl.on("click", this.handleDateClick, this, {delegate: "a.x-date-date"});
9833 this.eventEl.addKeyListener(Roo.EventObject.SPACE, this.selectToday, this);
9835 this.el.unselectable();
9837 this.cells = this.el.select("table.x-date-inner tbody td");
9838 this.textNodes = this.el.query("table.x-date-inner tbody span");
9840 this.mbtn = new Roo.Button(this.el.child("td.x-date-middle", true), {
9842 tooltip: this.monthYearText
9845 this.mbtn.on('click', this.showMonthPicker, this);
9846 this.mbtn.el.child(this.mbtn.menuClassTarget).addClass("x-btn-with-menu");
9849 var today = (new Date()).dateFormat(this.format);
9851 var baseTb = new Roo.Toolbar(this.el.child("td.x-date-bottom", true));
9852 if (this.showClear) {
9853 baseTb.add( new Roo.Toolbar.Fill());
9856 text: String.format(this.todayText, today),
9857 tooltip: String.format(this.todayTip, today),
9858 handler: this.selectToday,
9862 //var todayBtn = new Roo.Button(this.el.child("td.x-date-bottom", true), {
9865 if (this.showClear) {
9867 baseTb.add( new Roo.Toolbar.Fill());
9870 cls: 'x-btn-icon x-btn-clear',
9871 handler: function() {
9873 this.fireEvent("select", this, '');
9883 this.update(this.value);
9886 createMonthPicker : function(){
9887 if(!this.monthPicker.dom.firstChild){
9888 var buf = ['<table border="0" cellspacing="0">'];
9889 for(var i = 0; i < 6; i++){
9891 '<tr><td class="x-date-mp-month"><a href="#">', this.monthNames[i].substr(0, 3), '</a></td>',
9892 '<td class="x-date-mp-month x-date-mp-sep"><a href="#">', this.monthNames[i+6].substr(0, 3), '</a></td>',
9894 '<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>' :
9895 '<td class="x-date-mp-year"><a href="#"></a></td><td class="x-date-mp-year"><a href="#"></a></td></tr>'
9899 '<tr class="x-date-mp-btns"><td colspan="4"><button type="button" class="x-date-mp-ok">',
9901 '</button><button type="button" class="x-date-mp-cancel">',
9903 '</button></td></tr>',
9906 this.monthPicker.update(buf.join(''));
9907 this.monthPicker.on('click', this.onMonthClick, this);
9908 this.monthPicker.on('dblclick', this.onMonthDblClick, this);
9910 this.mpMonths = this.monthPicker.select('td.x-date-mp-month');
9911 this.mpYears = this.monthPicker.select('td.x-date-mp-year');
9913 this.mpMonths.each(function(m, a, i){
9916 m.dom.xmonth = 5 + Math.round(i * .5);
9918 m.dom.xmonth = Math.round((i-1) * .5);
9924 showMonthPicker : function(){
9925 this.createMonthPicker();
9926 var size = this.el.getSize();
9927 this.monthPicker.setSize(size);
9928 this.monthPicker.child('table').setSize(size);
9930 this.mpSelMonth = (this.activeDate || this.value).getMonth();
9931 this.updateMPMonth(this.mpSelMonth);
9932 this.mpSelYear = (this.activeDate || this.value).getFullYear();
9933 this.updateMPYear(this.mpSelYear);
9935 this.monthPicker.slideIn('t', {duration:.2});
9938 updateMPYear : function(y){
9940 var ys = this.mpYears.elements;
9941 for(var i = 1; i <= 10; i++){
9942 var td = ys[i-1], y2;
9944 y2 = y + Math.round(i * .5);
9945 td.firstChild.innerHTML = y2;
9948 y2 = y - (5-Math.round(i * .5));
9949 td.firstChild.innerHTML = y2;
9952 this.mpYears.item(i-1)[y2 == this.mpSelYear ? 'addClass' : 'removeClass']('x-date-mp-sel');
9956 updateMPMonth : function(sm){
9957 this.mpMonths.each(function(m, a, i){
9958 m[m.dom.xmonth == sm ? 'addClass' : 'removeClass']('x-date-mp-sel');
9962 selectMPMonth: function(m){
9966 onMonthClick : function(e, t){
9968 var el = new Roo.Element(t), pn;
9969 if(el.is('button.x-date-mp-cancel')){
9970 this.hideMonthPicker();
9972 else if(el.is('button.x-date-mp-ok')){
9973 this.update(new Date(this.mpSelYear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
9974 this.hideMonthPicker();
9976 else if(pn = el.up('td.x-date-mp-month', 2)){
9977 this.mpMonths.removeClass('x-date-mp-sel');
9978 pn.addClass('x-date-mp-sel');
9979 this.mpSelMonth = pn.dom.xmonth;
9981 else if(pn = el.up('td.x-date-mp-year', 2)){
9982 this.mpYears.removeClass('x-date-mp-sel');
9983 pn.addClass('x-date-mp-sel');
9984 this.mpSelYear = pn.dom.xyear;
9986 else if(el.is('a.x-date-mp-prev')){
9987 this.updateMPYear(this.mpyear-10);
9989 else if(el.is('a.x-date-mp-next')){
9990 this.updateMPYear(this.mpyear+10);
9994 onMonthDblClick : function(e, t){
9996 var el = new Roo.Element(t), pn;
9997 if(pn = el.up('td.x-date-mp-month', 2)){
9998 this.update(new Date(this.mpSelYear, pn.dom.xmonth, (this.activeDate || this.value).getDate()));
9999 this.hideMonthPicker();
10001 else if(pn = el.up('td.x-date-mp-year', 2)){
10002 this.update(new Date(pn.dom.xyear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
10003 this.hideMonthPicker();
10007 hideMonthPicker : function(disableAnim){
10008 if(this.monthPicker){
10009 if(disableAnim === true){
10010 this.monthPicker.hide();
10012 this.monthPicker.slideOut('t', {duration:.2});
10018 showPrevMonth : function(e){
10019 this.update(this.activeDate.add("mo", -1));
10023 showNextMonth : function(e){
10024 this.update(this.activeDate.add("mo", 1));
10028 showPrevYear : function(){
10029 this.update(this.activeDate.add("y", -1));
10033 showNextYear : function(){
10034 this.update(this.activeDate.add("y", 1));
10038 handleMouseWheel : function(e){
10039 var delta = e.getWheelDelta();
10041 this.showPrevMonth();
10043 } else if(delta < 0){
10044 this.showNextMonth();
10050 handleDateClick : function(e, t){
10052 if(t.dateValue && !Roo.fly(t.parentNode).hasClass("x-date-disabled")){
10053 this.setValue(new Date(t.dateValue));
10054 this.fireEvent("select", this, this.value);
10059 selectToday : function(){
10060 this.setValue(new Date().clearTime());
10061 this.fireEvent("select", this, this.value);
10065 update : function(date)
10067 var vd = this.activeDate;
10068 this.activeDate = date;
10070 var t = date.getTime();
10071 if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
10072 this.cells.removeClass("x-date-selected");
10073 this.cells.each(function(c){
10074 if(c.dom.firstChild.dateValue == t){
10075 c.addClass("x-date-selected");
10076 setTimeout(function(){
10077 try{c.dom.firstChild.focus();}catch(e){}
10086 var days = date.getDaysInMonth();
10087 var firstOfMonth = date.getFirstDateOfMonth();
10088 var startingPos = firstOfMonth.getDay()-this.startDay;
10090 if(startingPos <= this.startDay){
10094 var pm = date.add("mo", -1);
10095 var prevStart = pm.getDaysInMonth()-startingPos;
10097 var cells = this.cells.elements;
10098 var textEls = this.textNodes;
10099 days += startingPos;
10101 // convert everything to numbers so it's fast
10102 var day = 86400000;
10103 var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
10104 var today = new Date().clearTime().getTime();
10105 var sel = date.clearTime().getTime();
10106 var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
10107 var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
10108 var ddMatch = this.disabledDatesRE;
10109 var ddText = this.disabledDatesText;
10110 var ddays = this.disabledDays ? this.disabledDays.join("") : false;
10111 var ddaysText = this.disabledDaysText;
10112 var format = this.format;
10114 var setCellClass = function(cal, cell){
10116 var t = d.getTime();
10117 cell.firstChild.dateValue = t;
10119 cell.className += " x-date-today";
10120 cell.title = cal.todayText;
10123 cell.className += " x-date-selected";
10124 setTimeout(function(){
10125 try{cell.firstChild.focus();}catch(e){}
10130 cell.className = " x-date-disabled";
10131 cell.title = cal.minText;
10135 cell.className = " x-date-disabled";
10136 cell.title = cal.maxText;
10140 if(ddays.indexOf(d.getDay()) != -1){
10141 cell.title = ddaysText;
10142 cell.className = " x-date-disabled";
10145 if(ddMatch && format){
10146 var fvalue = d.dateFormat(format);
10147 if(ddMatch.test(fvalue)){
10148 cell.title = ddText.replace("%0", fvalue);
10149 cell.className = " x-date-disabled";
10155 for(; i < startingPos; i++) {
10156 textEls[i].innerHTML = (++prevStart);
10157 d.setDate(d.getDate()+1);
10158 cells[i].className = "x-date-prevday";
10159 setCellClass(this, cells[i]);
10161 for(; i < days; i++){
10162 intDay = i - startingPos + 1;
10163 textEls[i].innerHTML = (intDay);
10164 d.setDate(d.getDate()+1);
10165 cells[i].className = "x-date-active";
10166 setCellClass(this, cells[i]);
10169 for(; i < 42; i++) {
10170 textEls[i].innerHTML = (++extraDays);
10171 d.setDate(d.getDate()+1);
10172 cells[i].className = "x-date-nextday";
10173 setCellClass(this, cells[i]);
10176 this.mbtn.setText(this.monthNames[date.getMonth()] + " " + date.getFullYear());
10177 this.fireEvent('monthchange', this, date);
10179 if(!this.internalRender){
10180 var main = this.el.dom.firstChild;
10181 var w = main.offsetWidth;
10182 this.el.setWidth(w + this.el.getBorderWidth("lr"));
10183 Roo.fly(main).setWidth(w);
10184 this.internalRender = true;
10185 // opera does not respect the auto grow header center column
10186 // then, after it gets a width opera refuses to recalculate
10187 // without a second pass
10188 if(Roo.isOpera && !this.secondPass){
10189 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
10190 this.secondPass = true;
10191 this.update.defer(10, this, [date]);
10199 * Ext JS Library 1.1.1
10200 * Copyright(c) 2006-2007, Ext JS, LLC.
10202 * Originally Released Under LGPL - original licence link has changed is not relivant.
10205 * <script type="text/javascript">
10208 * @class Roo.TabPanel
10209 * @extends Roo.util.Observable
10210 * A lightweight tab container.
10214 // basic tabs 1, built from existing content
10215 var tabs = new Roo.TabPanel("tabs1");
10216 tabs.addTab("script", "View Script");
10217 tabs.addTab("markup", "View Markup");
10218 tabs.activate("script");
10220 // more advanced tabs, built from javascript
10221 var jtabs = new Roo.TabPanel("jtabs");
10222 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
10224 // set up the UpdateManager
10225 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
10226 var updater = tab2.getUpdateManager();
10227 updater.setDefaultUrl("ajax1.htm");
10228 tab2.on('activate', updater.refresh, updater, true);
10230 // Use setUrl for Ajax loading
10231 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
10232 tab3.setUrl("ajax2.htm", null, true);
10235 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
10238 jtabs.activate("jtabs-1");
10241 * Create a new TabPanel.
10242 * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
10243 * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
10245 Roo.TabPanel = function(container, config){
10247 * The container element for this TabPanel.
10248 * @type Roo.Element
10250 this.el = Roo.get(container, true);
10252 if(typeof config == "boolean"){
10253 this.tabPosition = config ? "bottom" : "top";
10255 Roo.apply(this, config);
10258 if(this.tabPosition == "bottom"){
10259 this.bodyEl = Roo.get(this.createBody(this.el.dom));
10260 this.el.addClass("x-tabs-bottom");
10262 this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
10263 this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
10264 this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
10266 Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
10268 if(this.tabPosition != "bottom"){
10269 /** The body element that contains {@link Roo.TabPanelItem} bodies. +
10270 * @type Roo.Element
10272 this.bodyEl = Roo.get(this.createBody(this.el.dom));
10273 this.el.addClass("x-tabs-top");
10277 this.bodyEl.setStyle("position", "relative");
10279 this.active = null;
10280 this.activateDelegate = this.activate.createDelegate(this);
10285 * Fires when the active tab changes
10286 * @param {Roo.TabPanel} this
10287 * @param {Roo.TabPanelItem} activePanel The new active tab
10291 * @event beforetabchange
10292 * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
10293 * @param {Roo.TabPanel} this
10294 * @param {Object} e Set cancel to true on this object to cancel the tab change
10295 * @param {Roo.TabPanelItem} tab The tab being changed to
10297 "beforetabchange" : true
10300 Roo.EventManager.onWindowResize(this.onResize, this);
10301 this.cpad = this.el.getPadding("lr");
10302 this.hiddenCount = 0;
10305 // toolbar on the tabbar support...
10306 if (this.toolbar) {
10307 var tcfg = this.toolbar;
10308 tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');
10309 this.toolbar = new Roo.Toolbar(tcfg);
10310 if (Roo.isSafari) {
10311 var tbl = tcfg.container.child('table', true);
10312 tbl.setAttribute('width', '100%');
10319 Roo.TabPanel.superclass.constructor.call(this);
10322 Roo.extend(Roo.TabPanel, Roo.util.Observable, {
10324 *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
10326 tabPosition : "top",
10328 *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
10330 currentTabWidth : 0,
10332 *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
10336 *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
10340 *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
10342 preferredTabWidth : 175,
10344 *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
10346 resizeTabs : false,
10348 *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
10350 monitorResize : true,
10352 *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar.
10357 * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
10358 * @param {String} id The id of the div to use <b>or create</b>
10359 * @param {String} text The text for the tab
10360 * @param {String} content (optional) Content to put in the TabPanelItem body
10361 * @param {Boolean} closable (optional) True to create a close icon on the tab
10362 * @return {Roo.TabPanelItem} The created TabPanelItem
10364 addTab : function(id, text, content, closable){
10365 var item = new Roo.TabPanelItem(this, id, text, closable);
10366 this.addTabItem(item);
10368 item.setContent(content);
10374 * Returns the {@link Roo.TabPanelItem} with the specified id/index
10375 * @param {String/Number} id The id or index of the TabPanelItem to fetch.
10376 * @return {Roo.TabPanelItem}
10378 getTab : function(id){
10379 return this.items[id];
10383 * Hides the {@link Roo.TabPanelItem} with the specified id/index
10384 * @param {String/Number} id The id or index of the TabPanelItem to hide.
10386 hideTab : function(id){
10387 var t = this.items[id];
10390 this.hiddenCount++;
10391 this.autoSizeTabs();
10396 * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
10397 * @param {String/Number} id The id or index of the TabPanelItem to unhide.
10399 unhideTab : function(id){
10400 var t = this.items[id];
10402 t.setHidden(false);
10403 this.hiddenCount--;
10404 this.autoSizeTabs();
10409 * Adds an existing {@link Roo.TabPanelItem}.
10410 * @param {Roo.TabPanelItem} item The TabPanelItem to add
10412 addTabItem : function(item){
10413 this.items[item.id] = item;
10414 this.items.push(item);
10415 if(this.resizeTabs){
10416 item.setWidth(this.currentTabWidth || this.preferredTabWidth);
10417 this.autoSizeTabs();
10424 * Removes a {@link Roo.TabPanelItem}.
10425 * @param {String/Number} id The id or index of the TabPanelItem to remove.
10427 removeTab : function(id){
10428 var items = this.items;
10429 var tab = items[id];
10430 if(!tab) { return; }
10431 var index = items.indexOf(tab);
10432 if(this.active == tab && items.length > 1){
10433 var newTab = this.getNextAvailable(index);
10438 this.stripEl.dom.removeChild(tab.pnode.dom);
10439 if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
10440 this.bodyEl.dom.removeChild(tab.bodyEl.dom);
10442 items.splice(index, 1);
10443 delete this.items[tab.id];
10444 tab.fireEvent("close", tab);
10445 tab.purgeListeners();
10446 this.autoSizeTabs();
10449 getNextAvailable : function(start){
10450 var items = this.items;
10452 // look for a next tab that will slide over to
10453 // replace the one being removed
10454 while(index < items.length){
10455 var item = items[++index];
10456 if(item && !item.isHidden()){
10460 // if one isn't found select the previous tab (on the left)
10463 var item = items[--index];
10464 if(item && !item.isHidden()){
10472 * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
10473 * @param {String/Number} id The id or index of the TabPanelItem to disable.
10475 disableTab : function(id){
10476 var tab = this.items[id];
10477 if(tab && this.active != tab){
10483 * Enables a {@link Roo.TabPanelItem} that is disabled.
10484 * @param {String/Number} id The id or index of the TabPanelItem to enable.
10486 enableTab : function(id){
10487 var tab = this.items[id];
10492 * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
10493 * @param {String/Number} id The id or index of the TabPanelItem to activate.
10494 * @return {Roo.TabPanelItem} The TabPanelItem.
10496 activate : function(id){
10497 var tab = this.items[id];
10501 if(tab == this.active || tab.disabled){
10505 this.fireEvent("beforetabchange", this, e, tab);
10506 if(e.cancel !== true && !tab.disabled){
10508 this.active.hide();
10510 this.active = this.items[id];
10511 this.active.show();
10512 this.fireEvent("tabchange", this, this.active);
10518 * Gets the active {@link Roo.TabPanelItem}.
10519 * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
10521 getActiveTab : function(){
10522 return this.active;
10526 * Updates the tab body element to fit the height of the container element
10527 * for overflow scrolling
10528 * @param {Number} targetHeight (optional) Override the starting height from the elements height
10530 syncHeight : function(targetHeight){
10531 var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
10532 var bm = this.bodyEl.getMargins();
10533 var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
10534 this.bodyEl.setHeight(newHeight);
10538 onResize : function(){
10539 if(this.monitorResize){
10540 this.autoSizeTabs();
10545 * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
10547 beginUpdate : function(){
10548 this.updating = true;
10552 * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
10554 endUpdate : function(){
10555 this.updating = false;
10556 this.autoSizeTabs();
10560 * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
10562 autoSizeTabs : function(){
10563 var count = this.items.length;
10564 var vcount = count - this.hiddenCount;
10565 if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) return;
10566 var w = Math.max(this.el.getWidth() - this.cpad, 10);
10567 var availWidth = Math.floor(w / vcount);
10568 var b = this.stripBody;
10569 if(b.getWidth() > w){
10570 var tabs = this.items;
10571 this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
10572 if(availWidth < this.minTabWidth){
10573 /*if(!this.sleft){ // incomplete scrolling code
10574 this.createScrollButtons();
10577 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
10580 if(this.currentTabWidth < this.preferredTabWidth){
10581 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
10587 * Returns the number of tabs in this TabPanel.
10590 getCount : function(){
10591 return this.items.length;
10595 * Resizes all the tabs to the passed width
10596 * @param {Number} The new width
10598 setTabWidth : function(width){
10599 this.currentTabWidth = width;
10600 for(var i = 0, len = this.items.length; i < len; i++) {
10601 if(!this.items[i].isHidden())this.items[i].setWidth(width);
10606 * Destroys this TabPanel
10607 * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
10609 destroy : function(removeEl){
10610 Roo.EventManager.removeResizeListener(this.onResize, this);
10611 for(var i = 0, len = this.items.length; i < len; i++){
10612 this.items[i].purgeListeners();
10614 if(removeEl === true){
10615 this.el.update("");
10622 * @class Roo.TabPanelItem
10623 * @extends Roo.util.Observable
10624 * Represents an individual item (tab plus body) in a TabPanel.
10625 * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
10626 * @param {String} id The id of this TabPanelItem
10627 * @param {String} text The text for the tab of this TabPanelItem
10628 * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
10630 Roo.TabPanelItem = function(tabPanel, id, text, closable){
10632 * The {@link Roo.TabPanel} this TabPanelItem belongs to
10633 * @type Roo.TabPanel
10635 this.tabPanel = tabPanel;
10637 * The id for this TabPanelItem
10642 this.disabled = false;
10646 this.loaded = false;
10647 this.closable = closable;
10650 * The body element for this TabPanelItem.
10651 * @type Roo.Element
10653 this.bodyEl = Roo.get(tabPanel.createItemBody(tabPanel.bodyEl.dom, id));
10654 this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
10655 this.bodyEl.setStyle("display", "block");
10656 this.bodyEl.setStyle("zoom", "1");
10659 var els = tabPanel.createStripElements(tabPanel.stripEl.dom, text, closable);
10661 this.el = Roo.get(els.el, true);
10662 this.inner = Roo.get(els.inner, true);
10663 this.textEl = Roo.get(this.el.dom.firstChild.firstChild.firstChild, true);
10664 this.pnode = Roo.get(els.el.parentNode, true);
10665 this.el.on("mousedown", this.onTabMouseDown, this);
10666 this.el.on("click", this.onTabClick, this);
10669 var c = Roo.get(els.close, true);
10670 c.dom.title = this.closeText;
10671 c.addClassOnOver("close-over");
10672 c.on("click", this.closeClick, this);
10678 * Fires when this tab becomes the active tab.
10679 * @param {Roo.TabPanel} tabPanel The parent TabPanel
10680 * @param {Roo.TabPanelItem} this
10684 * @event beforeclose
10685 * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
10686 * @param {Roo.TabPanelItem} this
10687 * @param {Object} e Set cancel to true on this object to cancel the close.
10689 "beforeclose": true,
10692 * Fires when this tab is closed.
10693 * @param {Roo.TabPanelItem} this
10697 * @event deactivate
10698 * Fires when this tab is no longer the active tab.
10699 * @param {Roo.TabPanel} tabPanel The parent TabPanel
10700 * @param {Roo.TabPanelItem} this
10702 "deactivate" : true
10704 this.hidden = false;
10706 Roo.TabPanelItem.superclass.constructor.call(this);
10709 Roo.extend(Roo.TabPanelItem, Roo.util.Observable, {
10710 purgeListeners : function(){
10711 Roo.util.Observable.prototype.purgeListeners.call(this);
10712 this.el.removeAllListeners();
10715 * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
10718 this.pnode.addClass("on");
10721 this.tabPanel.stripWrap.repaint();
10723 this.fireEvent("activate", this.tabPanel, this);
10727 * Returns true if this tab is the active tab.
10728 * @return {Boolean}
10730 isActive : function(){
10731 return this.tabPanel.getActiveTab() == this;
10735 * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
10738 this.pnode.removeClass("on");
10740 this.fireEvent("deactivate", this.tabPanel, this);
10743 hideAction : function(){
10744 this.bodyEl.hide();
10745 this.bodyEl.setStyle("position", "absolute");
10746 this.bodyEl.setLeft("-20000px");
10747 this.bodyEl.setTop("-20000px");
10750 showAction : function(){
10751 this.bodyEl.setStyle("position", "relative");
10752 this.bodyEl.setTop("");
10753 this.bodyEl.setLeft("");
10754 this.bodyEl.show();
10758 * Set the tooltip for the tab.
10759 * @param {String} tooltip The tab's tooltip
10761 setTooltip : function(text){
10762 if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
10763 this.textEl.dom.qtip = text;
10764 this.textEl.dom.removeAttribute('title');
10766 this.textEl.dom.title = text;
10770 onTabClick : function(e){
10771 e.preventDefault();
10772 this.tabPanel.activate(this.id);
10775 onTabMouseDown : function(e){
10776 e.preventDefault();
10777 this.tabPanel.activate(this.id);
10780 getWidth : function(){
10781 return this.inner.getWidth();
10784 setWidth : function(width){
10785 var iwidth = width - this.pnode.getPadding("lr");
10786 this.inner.setWidth(iwidth);
10787 this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
10788 this.pnode.setWidth(width);
10792 * Show or hide the tab
10793 * @param {Boolean} hidden True to hide or false to show.
10795 setHidden : function(hidden){
10796 this.hidden = hidden;
10797 this.pnode.setStyle("display", hidden ? "none" : "");
10801 * Returns true if this tab is "hidden"
10802 * @return {Boolean}
10804 isHidden : function(){
10805 return this.hidden;
10809 * Returns the text for this tab
10812 getText : function(){
10816 autoSize : function(){
10817 //this.el.beginMeasure();
10818 this.textEl.setWidth(1);
10820 * #2804 [new] Tabs in Roojs
10821 * increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
10823 this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr") + 2);
10824 //this.el.endMeasure();
10828 * Sets the text for the tab (Note: this also sets the tooltip text)
10829 * @param {String} text The tab's text and tooltip
10831 setText : function(text){
10833 this.textEl.update(text);
10834 this.setTooltip(text);
10835 if(!this.tabPanel.resizeTabs){
10840 * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
10842 activate : function(){
10843 this.tabPanel.activate(this.id);
10847 * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
10849 disable : function(){
10850 if(this.tabPanel.active != this){
10851 this.disabled = true;
10852 this.pnode.addClass("disabled");
10857 * Enables this TabPanelItem if it was previously disabled.
10859 enable : function(){
10860 this.disabled = false;
10861 this.pnode.removeClass("disabled");
10865 * Sets the content for this TabPanelItem.
10866 * @param {String} content The content
10867 * @param {Boolean} loadScripts true to look for and load scripts
10869 setContent : function(content, loadScripts){
10870 this.bodyEl.update(content, loadScripts);
10874 * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
10875 * @return {Roo.UpdateManager} The UpdateManager
10877 getUpdateManager : function(){
10878 return this.bodyEl.getUpdateManager();
10882 * Set a URL to be used to load the content for this TabPanelItem.
10883 * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
10884 * @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)
10885 * @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)
10886 * @return {Roo.UpdateManager} The UpdateManager
10888 setUrl : function(url, params, loadOnce){
10889 if(this.refreshDelegate){
10890 this.un('activate', this.refreshDelegate);
10892 this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
10893 this.on("activate", this.refreshDelegate);
10894 return this.bodyEl.getUpdateManager();
10898 _handleRefresh : function(url, params, loadOnce){
10899 if(!loadOnce || !this.loaded){
10900 var updater = this.bodyEl.getUpdateManager();
10901 updater.update(url, params, this._setLoaded.createDelegate(this));
10906 * Forces a content refresh from the URL specified in the {@link #setUrl} method.
10907 * Will fail silently if the setUrl method has not been called.
10908 * This does not activate the panel, just updates its content.
10910 refresh : function(){
10911 if(this.refreshDelegate){
10912 this.loaded = false;
10913 this.refreshDelegate();
10918 _setLoaded : function(){
10919 this.loaded = true;
10923 closeClick : function(e){
10926 this.fireEvent("beforeclose", this, o);
10927 if(o.cancel !== true){
10928 this.tabPanel.removeTab(this.id);
10932 * The text displayed in the tooltip for the close icon.
10935 closeText : "Close this tab"
10939 Roo.TabPanel.prototype.createStrip = function(container){
10940 var strip = document.createElement("div");
10941 strip.className = "x-tabs-wrap";
10942 container.appendChild(strip);
10946 Roo.TabPanel.prototype.createStripList = function(strip){
10947 // div wrapper for retard IE
10948 // returns the "tr" element.
10949 strip.innerHTML = '<div class="x-tabs-strip-wrap">'+
10950 '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
10951 '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
10952 return strip.firstChild.firstChild.firstChild.firstChild;
10955 Roo.TabPanel.prototype.createBody = function(container){
10956 var body = document.createElement("div");
10957 Roo.id(body, "tab-body");
10958 Roo.fly(body).addClass("x-tabs-body");
10959 container.appendChild(body);
10963 Roo.TabPanel.prototype.createItemBody = function(bodyEl, id){
10964 var body = Roo.getDom(id);
10966 body = document.createElement("div");
10969 Roo.fly(body).addClass("x-tabs-item-body");
10970 bodyEl.insertBefore(body, bodyEl.firstChild);
10974 Roo.TabPanel.prototype.createStripElements = function(stripEl, text, closable){
10975 var td = document.createElement("td");
10976 stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
10977 //stripEl.appendChild(td);
10979 td.className = "x-tabs-closable";
10980 if(!this.closeTpl){
10981 this.closeTpl = new Roo.Template(
10982 '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
10983 '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
10984 '<div unselectable="on" class="close-icon"> </div></em></span></a>'
10987 var el = this.closeTpl.overwrite(td, {"text": text});
10988 var close = el.getElementsByTagName("div")[0];
10989 var inner = el.getElementsByTagName("em")[0];
10990 return {"el": el, "close": close, "inner": inner};
10993 this.tabTpl = new Roo.Template(
10994 '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
10995 '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
10998 var el = this.tabTpl.overwrite(td, {"text": text});
10999 var inner = el.getElementsByTagName("em")[0];
11000 return {"el": el, "inner": inner};
11004 * Ext JS Library 1.1.1
11005 * Copyright(c) 2006-2007, Ext JS, LLC.
11007 * Originally Released Under LGPL - original licence link has changed is not relivant.
11010 * <script type="text/javascript">
11014 * @class Roo.Button
11015 * @extends Roo.util.Observable
11016 * Simple Button class
11017 * @cfg {String} text The button text
11018 * @cfg {String} icon The path to an image to display in the button (the image will be set as the background-image
11019 * CSS property of the button by default, so if you want a mixed icon/text button, set cls:"x-btn-text-icon")
11020 * @cfg {Function} handler A function called when the button is clicked (can be used instead of click event)
11021 * @cfg {Object} scope The scope of the handler
11022 * @cfg {Number} minWidth The minimum width for this button (used to give a set of buttons a common width)
11023 * @cfg {String/Object} tooltip The tooltip for the button - can be a string or QuickTips config object
11024 * @cfg {Boolean} hidden True to start hidden (defaults to false)
11025 * @cfg {Boolean} disabled True to start disabled (defaults to false)
11026 * @cfg {Boolean} pressed True to start pressed (only if enableToggle = true)
11027 * @cfg {String} toggleGroup The group this toggle button is a member of (only 1 per group can be pressed, only
11028 applies if enableToggle = true)
11029 * @cfg {String/HTMLElement/Element} renderTo The element to append the button to
11030 * @cfg {Boolean/Object} repeat True to repeat fire the click event while the mouse is down. This can also be
11031 an {@link Roo.util.ClickRepeater} config object (defaults to false).
11033 * Create a new button
11034 * @param {Object} config The config object
11036 Roo.Button = function(renderTo, config)
11040 renderTo = config.renderTo || false;
11043 Roo.apply(this, config);
11047 * Fires when this button is clicked
11048 * @param {Button} this
11049 * @param {EventObject} e The click event
11054 * Fires when the "pressed" state of this button changes (only if enableToggle = true)
11055 * @param {Button} this
11056 * @param {Boolean} pressed
11061 * Fires when the mouse hovers over the button
11062 * @param {Button} this
11063 * @param {Event} e The event object
11065 'mouseover' : true,
11068 * Fires when the mouse exits the button
11069 * @param {Button} this
11070 * @param {Event} e The event object
11075 * Fires when the button is rendered
11076 * @param {Button} this
11081 this.menu = Roo.menu.MenuMgr.get(this.menu);
11083 // register listeners first!! - so render can be captured..
11084 Roo.util.Observable.call(this);
11086 this.render(renderTo);
11092 Roo.extend(Roo.Button, Roo.util.Observable, {
11098 * Read-only. True if this button is hidden
11103 * Read-only. True if this button is disabled
11108 * Read-only. True if this button is pressed (only if enableToggle = true)
11114 * @cfg {Number} tabIndex
11115 * The DOM tabIndex for this button (defaults to undefined)
11117 tabIndex : undefined,
11120 * @cfg {Boolean} enableToggle
11121 * True to enable pressed/not pressed toggling (defaults to false)
11123 enableToggle: false,
11125 * @cfg {Mixed} menu
11126 * Standard menu attribute consisting of a reference to a menu object, a menu id or a menu config blob (defaults to undefined).
11130 * @cfg {String} menuAlign
11131 * The position to align the menu to (see {@link Roo.Element#alignTo} for more details, defaults to 'tl-bl?').
11133 menuAlign : "tl-bl?",
11136 * @cfg {String} iconCls
11137 * A css class which sets a background image to be used as the icon for this button (defaults to undefined).
11139 iconCls : undefined,
11141 * @cfg {String} type
11142 * The button's type, corresponding to the DOM input element type attribute. Either "submit," "reset" or "button" (default).
11147 menuClassTarget: 'tr',
11150 * @cfg {String} clickEvent
11151 * The type of event to map to the button's event handler (defaults to 'click')
11153 clickEvent : 'click',
11156 * @cfg {Boolean} handleMouseEvents
11157 * False to disable visual cues on mouseover, mouseout and mousedown (defaults to true)
11159 handleMouseEvents : true,
11162 * @cfg {String} tooltipType
11163 * The type of tooltip to use. Either "qtip" (default) for QuickTips or "title" for title attribute.
11165 tooltipType : 'qtip',
11168 * @cfg {String} cls
11169 * A CSS class to apply to the button's main element.
11173 * @cfg {Roo.Template} template (Optional)
11174 * An {@link Roo.Template} with which to create the Button's main element. This Template must
11175 * contain numeric substitution parameter 0 if it is to display the tRoo property. Changing the template could
11176 * require code modifications if required elements (e.g. a button) aren't present.
11180 render : function(renderTo){
11182 if(this.hideParent){
11183 this.parentEl = Roo.get(renderTo);
11185 if(!this.dhconfig){
11186 if(!this.template){
11187 if(!Roo.Button.buttonTemplate){
11188 // hideous table template
11189 Roo.Button.buttonTemplate = new Roo.Template(
11190 '<table border="0" cellpadding="0" cellspacing="0" class="x-btn-wrap"><tbody><tr>',
11191 '<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>',
11192 "</tr></tbody></table>");
11194 this.template = Roo.Button.buttonTemplate;
11196 btn = this.template.append(renderTo, [this.text || ' ', this.type], true);
11197 var btnEl = btn.child("button:first");
11198 btnEl.on('focus', this.onFocus, this);
11199 btnEl.on('blur', this.onBlur, this);
11201 btn.addClass(this.cls);
11204 btnEl.setStyle('background-image', 'url(' +this.icon +')');
11207 btnEl.addClass(this.iconCls);
11209 btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
11212 if(this.tabIndex !== undefined){
11213 btnEl.dom.tabIndex = this.tabIndex;
11216 if(typeof this.tooltip == 'object'){
11217 Roo.QuickTips.tips(Roo.apply({
11221 btnEl.dom[this.tooltipType] = this.tooltip;
11225 btn = Roo.DomHelper.append(Roo.get(renderTo).dom, this.dhconfig, true);
11229 this.el.dom.id = this.el.id = this.id;
11232 this.el.child(this.menuClassTarget).addClass("x-btn-with-menu");
11233 this.menu.on("show", this.onMenuShow, this);
11234 this.menu.on("hide", this.onMenuHide, this);
11236 btn.addClass("x-btn");
11237 if(Roo.isIE && !Roo.isIE7){
11238 this.autoWidth.defer(1, this);
11242 if(this.handleMouseEvents){
11243 btn.on("mouseover", this.onMouseOver, this);
11244 btn.on("mouseout", this.onMouseOut, this);
11245 btn.on("mousedown", this.onMouseDown, this);
11247 btn.on(this.clickEvent, this.onClick, this);
11248 //btn.on("mouseup", this.onMouseUp, this);
11255 Roo.ButtonToggleMgr.register(this);
11257 this.el.addClass("x-btn-pressed");
11260 var repeater = new Roo.util.ClickRepeater(btn,
11261 typeof this.repeat == "object" ? this.repeat : {}
11263 repeater.on("click", this.onClick, this);
11266 this.fireEvent('render', this);
11270 * Returns the button's underlying element
11271 * @return {Roo.Element} The element
11273 getEl : function(){
11278 * Destroys this Button and removes any listeners.
11280 destroy : function(){
11281 Roo.ButtonToggleMgr.unregister(this);
11282 this.el.removeAllListeners();
11283 this.purgeListeners();
11288 autoWidth : function(){
11290 this.el.setWidth("auto");
11291 if(Roo.isIE7 && Roo.isStrict){
11292 var ib = this.el.child('button');
11293 if(ib && ib.getWidth() > 20){
11295 ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
11300 this.el.beginMeasure();
11302 if(this.el.getWidth() < this.minWidth){
11303 this.el.setWidth(this.minWidth);
11306 this.el.endMeasure();
11313 * Assigns this button's click handler
11314 * @param {Function} handler The function to call when the button is clicked
11315 * @param {Object} scope (optional) Scope for the function passed in
11317 setHandler : function(handler, scope){
11318 this.handler = handler;
11319 this.scope = scope;
11323 * Sets this button's text
11324 * @param {String} text The button text
11326 setText : function(text){
11329 this.el.child("td.x-btn-center button.x-btn-text").update(text);
11335 * Gets the text for this button
11336 * @return {String} The button text
11338 getText : function(){
11346 this.hidden = false;
11348 this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "");
11356 this.hidden = true;
11358 this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "none");
11363 * Convenience function for boolean show/hide
11364 * @param {Boolean} visible True to show, false to hide
11366 setVisible: function(visible){
11375 * If a state it passed, it becomes the pressed state otherwise the current state is toggled.
11376 * @param {Boolean} state (optional) Force a particular state
11378 toggle : function(state){
11379 state = state === undefined ? !this.pressed : state;
11380 if(state != this.pressed){
11382 this.el.addClass("x-btn-pressed");
11383 this.pressed = true;
11384 this.fireEvent("toggle", this, true);
11386 this.el.removeClass("x-btn-pressed");
11387 this.pressed = false;
11388 this.fireEvent("toggle", this, false);
11390 if(this.toggleHandler){
11391 this.toggleHandler.call(this.scope || this, this, state);
11399 focus : function(){
11400 this.el.child('button:first').focus();
11404 * Disable this button
11406 disable : function(){
11408 this.el.addClass("x-btn-disabled");
11410 this.disabled = true;
11414 * Enable this button
11416 enable : function(){
11418 this.el.removeClass("x-btn-disabled");
11420 this.disabled = false;
11424 * Convenience function for boolean enable/disable
11425 * @param {Boolean} enabled True to enable, false to disable
11427 setDisabled : function(v){
11428 this[v !== true ? "enable" : "disable"]();
11432 onClick : function(e){
11434 e.preventDefault();
11439 if(!this.disabled){
11440 if(this.enableToggle){
11443 if(this.menu && !this.menu.isVisible()){
11444 this.menu.show(this.el, this.menuAlign);
11446 this.fireEvent("click", this, e);
11448 this.el.removeClass("x-btn-over");
11449 this.handler.call(this.scope || this, this, e);
11454 onMouseOver : function(e){
11455 if(!this.disabled){
11456 this.el.addClass("x-btn-over");
11457 this.fireEvent('mouseover', this, e);
11461 onMouseOut : function(e){
11462 if(!e.within(this.el, true)){
11463 this.el.removeClass("x-btn-over");
11464 this.fireEvent('mouseout', this, e);
11468 onFocus : function(e){
11469 if(!this.disabled){
11470 this.el.addClass("x-btn-focus");
11474 onBlur : function(e){
11475 this.el.removeClass("x-btn-focus");
11478 onMouseDown : function(e){
11479 if(!this.disabled && e.button == 0){
11480 this.el.addClass("x-btn-click");
11481 Roo.get(document).on('mouseup', this.onMouseUp, this);
11485 onMouseUp : function(e){
11487 this.el.removeClass("x-btn-click");
11488 Roo.get(document).un('mouseup', this.onMouseUp, this);
11492 onMenuShow : function(e){
11493 this.el.addClass("x-btn-menu-active");
11496 onMenuHide : function(e){
11497 this.el.removeClass("x-btn-menu-active");
11501 // Private utility class used by Button
11502 Roo.ButtonToggleMgr = function(){
11505 function toggleGroup(btn, state){
11507 var g = groups[btn.toggleGroup];
11508 for(var i = 0, l = g.length; i < l; i++){
11510 g[i].toggle(false);
11517 register : function(btn){
11518 if(!btn.toggleGroup){
11521 var g = groups[btn.toggleGroup];
11523 g = groups[btn.toggleGroup] = [];
11526 btn.on("toggle", toggleGroup);
11529 unregister : function(btn){
11530 if(!btn.toggleGroup){
11533 var g = groups[btn.toggleGroup];
11536 btn.un("toggle", toggleGroup);
11542 * Ext JS Library 1.1.1
11543 * Copyright(c) 2006-2007, Ext JS, LLC.
11545 * Originally Released Under LGPL - original licence link has changed is not relivant.
11548 * <script type="text/javascript">
11552 * @class Roo.SplitButton
11553 * @extends Roo.Button
11554 * A split button that provides a built-in dropdown arrow that can fire an event separately from the default
11555 * click event of the button. Typically this would be used to display a dropdown menu that provides additional
11556 * options to the primary button action, but any custom handler can provide the arrowclick implementation.
11557 * @cfg {Function} arrowHandler A function called when the arrow button is clicked (can be used instead of click event)
11558 * @cfg {String} arrowTooltip The title attribute of the arrow
11560 * Create a new menu button
11561 * @param {String/HTMLElement/Element} renderTo The element to append the button to
11562 * @param {Object} config The config object
11564 Roo.SplitButton = function(renderTo, config){
11565 Roo.SplitButton.superclass.constructor.call(this, renderTo, config);
11567 * @event arrowclick
11568 * Fires when this button's arrow is clicked
11569 * @param {SplitButton} this
11570 * @param {EventObject} e The click event
11572 this.addEvents({"arrowclick":true});
11575 Roo.extend(Roo.SplitButton, Roo.Button, {
11576 render : function(renderTo){
11577 // this is one sweet looking template!
11578 var tpl = new Roo.Template(
11579 '<table cellspacing="0" class="x-btn-menu-wrap x-btn"><tr><td>',
11580 '<table cellspacing="0" class="x-btn-wrap x-btn-menu-text-wrap"><tbody>',
11581 '<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>',
11582 "</tbody></table></td><td>",
11583 '<table cellspacing="0" class="x-btn-wrap x-btn-menu-arrow-wrap"><tbody>',
11584 '<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>',
11585 "</tbody></table></td></tr></table>"
11587 var btn = tpl.append(renderTo, [this.text, this.type], true);
11588 var btnEl = btn.child("button");
11590 btn.addClass(this.cls);
11593 btnEl.setStyle('background-image', 'url(' +this.icon +')');
11596 btnEl.addClass(this.iconCls);
11598 btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
11602 if(this.handleMouseEvents){
11603 btn.on("mouseover", this.onMouseOver, this);
11604 btn.on("mouseout", this.onMouseOut, this);
11605 btn.on("mousedown", this.onMouseDown, this);
11606 btn.on("mouseup", this.onMouseUp, this);
11608 btn.on(this.clickEvent, this.onClick, this);
11610 if(typeof this.tooltip == 'object'){
11611 Roo.QuickTips.tips(Roo.apply({
11615 btnEl.dom[this.tooltipType] = this.tooltip;
11618 if(this.arrowTooltip){
11619 btn.child("button:nth(2)").dom[this.tooltipType] = this.arrowTooltip;
11628 this.el.addClass("x-btn-pressed");
11630 if(Roo.isIE && !Roo.isIE7){
11631 this.autoWidth.defer(1, this);
11636 this.menu.on("show", this.onMenuShow, this);
11637 this.menu.on("hide", this.onMenuHide, this);
11639 this.fireEvent('render', this);
11643 autoWidth : function(){
11645 var tbl = this.el.child("table:first");
11646 var tbl2 = this.el.child("table:last");
11647 this.el.setWidth("auto");
11648 tbl.setWidth("auto");
11649 if(Roo.isIE7 && Roo.isStrict){
11650 var ib = this.el.child('button:first');
11651 if(ib && ib.getWidth() > 20){
11653 ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
11658 this.el.beginMeasure();
11660 if((tbl.getWidth()+tbl2.getWidth()) < this.minWidth){
11661 tbl.setWidth(this.minWidth-tbl2.getWidth());
11664 this.el.endMeasure();
11667 this.el.setWidth(tbl.getWidth()+tbl2.getWidth());
11671 * Sets this button's click handler
11672 * @param {Function} handler The function to call when the button is clicked
11673 * @param {Object} scope (optional) Scope for the function passed above
11675 setHandler : function(handler, scope){
11676 this.handler = handler;
11677 this.scope = scope;
11681 * Sets this button's arrow click handler
11682 * @param {Function} handler The function to call when the arrow is clicked
11683 * @param {Object} scope (optional) Scope for the function passed above
11685 setArrowHandler : function(handler, scope){
11686 this.arrowHandler = handler;
11687 this.scope = scope;
11693 focus : function(){
11695 this.el.child("button:first").focus();
11700 onClick : function(e){
11701 e.preventDefault();
11702 if(!this.disabled){
11703 if(e.getTarget(".x-btn-menu-arrow-wrap")){
11704 if(this.menu && !this.menu.isVisible()){
11705 this.menu.show(this.el, this.menuAlign);
11707 this.fireEvent("arrowclick", this, e);
11708 if(this.arrowHandler){
11709 this.arrowHandler.call(this.scope || this, this, e);
11712 this.fireEvent("click", this, e);
11714 this.handler.call(this.scope || this, this, e);
11720 onMouseDown : function(e){
11721 if(!this.disabled){
11722 Roo.fly(e.getTarget("table")).addClass("x-btn-click");
11726 onMouseUp : function(e){
11727 Roo.fly(e.getTarget("table")).removeClass("x-btn-click");
11732 // backwards compat
11733 Roo.MenuButton = Roo.SplitButton;/*
11735 * Ext JS Library 1.1.1
11736 * Copyright(c) 2006-2007, Ext JS, LLC.
11738 * Originally Released Under LGPL - original licence link has changed is not relivant.
11741 * <script type="text/javascript">
11745 * @class Roo.Toolbar
11746 * Basic Toolbar class.
11748 * Creates a new Toolbar
11749 * @param {Object} container The config object
11751 Roo.Toolbar = function(container, buttons, config)
11753 /// old consturctor format still supported..
11754 if(container instanceof Array){ // omit the container for later rendering
11755 buttons = container;
11759 if (typeof(container) == 'object' && container.xtype) {
11760 config = container;
11761 container = config.container;
11762 buttons = config.buttons || []; // not really - use items!!
11765 if (config && config.items) {
11766 xitems = config.items;
11767 delete config.items;
11769 Roo.apply(this, config);
11770 this.buttons = buttons;
11773 this.render(container);
11775 this.xitems = xitems;
11776 Roo.each(xitems, function(b) {
11782 Roo.Toolbar.prototype = {
11784 * @cfg {Array} items
11785 * array of button configs or elements to add (will be converted to a MixedCollection)
11789 * @cfg {String/HTMLElement/Element} container
11790 * The id or element that will contain the toolbar
11793 render : function(ct){
11794 this.el = Roo.get(ct);
11796 this.el.addClass(this.cls);
11798 // using a table allows for vertical alignment
11799 // 100% width is needed by Safari...
11800 this.el.update('<div class="x-toolbar x-small-editor"><table cellspacing="0"><tr></tr></table></div>');
11801 this.tr = this.el.child("tr", true);
11803 this.items = new Roo.util.MixedCollection(false, function(o){
11804 return o.id || ("item" + (++autoId));
11807 this.add.apply(this, this.buttons);
11808 delete this.buttons;
11813 * Adds element(s) to the toolbar -- this function takes a variable number of
11814 * arguments of mixed type and adds them to the toolbar.
11815 * @param {Mixed} arg1 The following types of arguments are all valid:<br />
11817 * <li>{@link Roo.Toolbar.Button} config: A valid button config object (equivalent to {@link #addButton})</li>
11818 * <li>HtmlElement: Any standard HTML element (equivalent to {@link #addElement})</li>
11819 * <li>Field: Any form field (equivalent to {@link #addField})</li>
11820 * <li>Item: Any subclass of {@link Roo.Toolbar.Item} (equivalent to {@link #addItem})</li>
11821 * <li>String: Any generic string (gets wrapped in a {@link Roo.Toolbar.TextItem}, equivalent to {@link #addText}).
11822 * Note that there are a few special strings that are treated differently as explained nRoo.</li>
11823 * <li>'separator' or '-': Creates a separator element (equivalent to {@link #addSeparator})</li>
11824 * <li>' ': Creates a spacer element (equivalent to {@link #addSpacer})</li>
11825 * <li>'->': Creates a fill element (equivalent to {@link #addFill})</li>
11827 * @param {Mixed} arg2
11828 * @param {Mixed} etc.
11831 var a = arguments, l = a.length;
11832 for(var i = 0; i < l; i++){
11837 _add : function(el) {
11840 el = Roo.factory(el, typeof(Roo.Toolbar[el.xtype]) == 'undefined' ? Roo.form : Roo.Toolbar);
11843 if (el.applyTo){ // some kind of form field
11844 return this.addField(el);
11846 if (el.render){ // some kind of Toolbar.Item
11847 return this.addItem(el);
11849 if (typeof el == "string"){ // string
11850 if(el == "separator" || el == "-"){
11851 return this.addSeparator();
11854 return this.addSpacer();
11857 return this.addFill();
11859 return this.addText(el);
11862 if(el.tagName){ // element
11863 return this.addElement(el);
11865 if(typeof el == "object"){ // must be button config?
11866 return this.addButton(el);
11868 // and now what?!?!
11874 * Add an Xtype element
11875 * @param {Object} xtype Xtype Object
11876 * @return {Object} created Object
11878 addxtype : function(e){
11879 return this.add(e);
11883 * Returns the Element for this toolbar.
11884 * @return {Roo.Element}
11886 getEl : function(){
11892 * @return {Roo.Toolbar.Item} The separator item
11894 addSeparator : function(){
11895 return this.addItem(new Roo.Toolbar.Separator());
11899 * Adds a spacer element
11900 * @return {Roo.Toolbar.Spacer} The spacer item
11902 addSpacer : function(){
11903 return this.addItem(new Roo.Toolbar.Spacer());
11907 * Adds a fill element that forces subsequent additions to the right side of the toolbar
11908 * @return {Roo.Toolbar.Fill} The fill item
11910 addFill : function(){
11911 return this.addItem(new Roo.Toolbar.Fill());
11915 * Adds any standard HTML element to the toolbar
11916 * @param {String/HTMLElement/Element} el The element or id of the element to add
11917 * @return {Roo.Toolbar.Item} The element's item
11919 addElement : function(el){
11920 return this.addItem(new Roo.Toolbar.Item(el));
11923 * Collection of items on the toolbar.. (only Toolbar Items, so use fields to retrieve fields)
11924 * @type Roo.util.MixedCollection
11929 * Adds any Toolbar.Item or subclass
11930 * @param {Roo.Toolbar.Item} item
11931 * @return {Roo.Toolbar.Item} The item
11933 addItem : function(item){
11934 var td = this.nextBlock();
11936 this.items.add(item);
11941 * Adds a button (or buttons). See {@link Roo.Toolbar.Button} for more info on the config.
11942 * @param {Object/Array} config A button config or array of configs
11943 * @return {Roo.Toolbar.Button/Array}
11945 addButton : function(config){
11946 if(config instanceof Array){
11948 for(var i = 0, len = config.length; i < len; i++) {
11949 buttons.push(this.addButton(config[i]));
11954 if(!(config instanceof Roo.Toolbar.Button)){
11956 new Roo.Toolbar.SplitButton(config) :
11957 new Roo.Toolbar.Button(config);
11959 var td = this.nextBlock();
11966 * Adds text to the toolbar
11967 * @param {String} text The text to add
11968 * @return {Roo.Toolbar.Item} The element's item
11970 addText : function(text){
11971 return this.addItem(new Roo.Toolbar.TextItem(text));
11975 * Inserts any {@link Roo.Toolbar.Item}/{@link Roo.Toolbar.Button} at the specified index.
11976 * @param {Number} index The index where the item is to be inserted
11977 * @param {Object/Roo.Toolbar.Item/Roo.Toolbar.Button (may be Array)} item The button, or button config object to be inserted.
11978 * @return {Roo.Toolbar.Button/Item}
11980 insertButton : function(index, item){
11981 if(item instanceof Array){
11983 for(var i = 0, len = item.length; i < len; i++) {
11984 buttons.push(this.insertButton(index + i, item[i]));
11988 if (!(item instanceof Roo.Toolbar.Button)){
11989 item = new Roo.Toolbar.Button(item);
11991 var td = document.createElement("td");
11992 this.tr.insertBefore(td, this.tr.childNodes[index]);
11994 this.items.insert(index, item);
11999 * Adds a new element to the toolbar from the passed {@link Roo.DomHelper} config.
12000 * @param {Object} config
12001 * @return {Roo.Toolbar.Item} The element's item
12003 addDom : function(config, returnEl){
12004 var td = this.nextBlock();
12005 Roo.DomHelper.overwrite(td, config);
12006 var ti = new Roo.Toolbar.Item(td.firstChild);
12008 this.items.add(ti);
12013 * Collection of fields on the toolbar.. usefull for quering (value is false if there are no fields)
12014 * @type Roo.util.MixedCollection
12019 * Adds a dynamically rendered Roo.form field (TextField, ComboBox, etc).
12020 * Note: the field should not have been rendered yet. For a field that has already been
12021 * rendered, use {@link #addElement}.
12022 * @param {Roo.form.Field} field
12023 * @return {Roo.ToolbarItem}
12027 addField : function(field) {
12028 if (!this.fields) {
12030 this.fields = new Roo.util.MixedCollection(false, function(o){
12031 return o.id || ("item" + (++autoId));
12036 var td = this.nextBlock();
12038 var ti = new Roo.Toolbar.Item(td.firstChild);
12040 this.items.add(ti);
12041 this.fields.add(field);
12052 this.el.child('div').setVisibilityMode(Roo.Element.DISPLAY);
12053 this.el.child('div').hide();
12061 this.el.child('div').show();
12065 nextBlock : function(){
12066 var td = document.createElement("td");
12067 this.tr.appendChild(td);
12072 destroy : function(){
12073 if(this.items){ // rendered?
12074 Roo.destroy.apply(Roo, this.items.items);
12076 if(this.fields){ // rendered?
12077 Roo.destroy.apply(Roo, this.fields.items);
12079 Roo.Element.uncache(this.el, this.tr);
12084 * @class Roo.Toolbar.Item
12085 * The base class that other classes should extend in order to get some basic common toolbar item functionality.
12087 * Creates a new Item
12088 * @param {HTMLElement} el
12090 Roo.Toolbar.Item = function(el){
12091 this.el = Roo.getDom(el);
12092 this.id = Roo.id(this.el);
12093 this.hidden = false;
12096 Roo.Toolbar.Item.prototype = {
12099 * Get this item's HTML Element
12100 * @return {HTMLElement}
12102 getEl : function(){
12107 render : function(td){
12109 td.appendChild(this.el);
12113 * Removes and destroys this item.
12115 destroy : function(){
12116 this.td.parentNode.removeChild(this.td);
12123 this.hidden = false;
12124 this.td.style.display = "";
12131 this.hidden = true;
12132 this.td.style.display = "none";
12136 * Convenience function for boolean show/hide.
12137 * @param {Boolean} visible true to show/false to hide
12139 setVisible: function(visible){
12148 * Try to focus this item.
12150 focus : function(){
12151 Roo.fly(this.el).focus();
12155 * Disables this item.
12157 disable : function(){
12158 Roo.fly(this.td).addClass("x-item-disabled");
12159 this.disabled = true;
12160 this.el.disabled = true;
12164 * Enables this item.
12166 enable : function(){
12167 Roo.fly(this.td).removeClass("x-item-disabled");
12168 this.disabled = false;
12169 this.el.disabled = false;
12175 * @class Roo.Toolbar.Separator
12176 * @extends Roo.Toolbar.Item
12177 * A simple toolbar separator class
12179 * Creates a new Separator
12181 Roo.Toolbar.Separator = function(){
12182 var s = document.createElement("span");
12183 s.className = "ytb-sep";
12184 Roo.Toolbar.Separator.superclass.constructor.call(this, s);
12186 Roo.extend(Roo.Toolbar.Separator, Roo.Toolbar.Item, {
12187 enable:Roo.emptyFn,
12188 disable:Roo.emptyFn,
12193 * @class Roo.Toolbar.Spacer
12194 * @extends Roo.Toolbar.Item
12195 * A simple element that adds extra horizontal space to a toolbar.
12197 * Creates a new Spacer
12199 Roo.Toolbar.Spacer = function(){
12200 var s = document.createElement("div");
12201 s.className = "ytb-spacer";
12202 Roo.Toolbar.Spacer.superclass.constructor.call(this, s);
12204 Roo.extend(Roo.Toolbar.Spacer, Roo.Toolbar.Item, {
12205 enable:Roo.emptyFn,
12206 disable:Roo.emptyFn,
12211 * @class Roo.Toolbar.Fill
12212 * @extends Roo.Toolbar.Spacer
12213 * A simple element that adds a greedy (100% width) horizontal space to a toolbar.
12215 * Creates a new Spacer
12217 Roo.Toolbar.Fill = Roo.extend(Roo.Toolbar.Spacer, {
12219 render : function(td){
12220 td.style.width = '100%';
12221 Roo.Toolbar.Fill.superclass.render.call(this, td);
12226 * @class Roo.Toolbar.TextItem
12227 * @extends Roo.Toolbar.Item
12228 * A simple class that renders text directly into a toolbar.
12230 * Creates a new TextItem
12231 * @param {String} text
12233 Roo.Toolbar.TextItem = function(text){
12234 if (typeof(text) == 'object') {
12237 var s = document.createElement("span");
12238 s.className = "ytb-text";
12239 s.innerHTML = text;
12240 Roo.Toolbar.TextItem.superclass.constructor.call(this, s);
12242 Roo.extend(Roo.Toolbar.TextItem, Roo.Toolbar.Item, {
12243 enable:Roo.emptyFn,
12244 disable:Roo.emptyFn,
12249 * @class Roo.Toolbar.Button
12250 * @extends Roo.Button
12251 * A button that renders into a toolbar.
12253 * Creates a new Button
12254 * @param {Object} config A standard {@link Roo.Button} config object
12256 Roo.Toolbar.Button = function(config){
12257 Roo.Toolbar.Button.superclass.constructor.call(this, null, config);
12259 Roo.extend(Roo.Toolbar.Button, Roo.Button, {
12260 render : function(td){
12262 Roo.Toolbar.Button.superclass.render.call(this, td);
12266 * Removes and destroys this button
12268 destroy : function(){
12269 Roo.Toolbar.Button.superclass.destroy.call(this);
12270 this.td.parentNode.removeChild(this.td);
12274 * Shows this button
12277 this.hidden = false;
12278 this.td.style.display = "";
12282 * Hides this button
12285 this.hidden = true;
12286 this.td.style.display = "none";
12290 * Disables this item
12292 disable : function(){
12293 Roo.fly(this.td).addClass("x-item-disabled");
12294 this.disabled = true;
12298 * Enables this item
12300 enable : function(){
12301 Roo.fly(this.td).removeClass("x-item-disabled");
12302 this.disabled = false;
12305 // backwards compat
12306 Roo.ToolbarButton = Roo.Toolbar.Button;
12309 * @class Roo.Toolbar.SplitButton
12310 * @extends Roo.SplitButton
12311 * A menu button that renders into a toolbar.
12313 * Creates a new SplitButton
12314 * @param {Object} config A standard {@link Roo.SplitButton} config object
12316 Roo.Toolbar.SplitButton = function(config){
12317 Roo.Toolbar.SplitButton.superclass.constructor.call(this, null, config);
12319 Roo.extend(Roo.Toolbar.SplitButton, Roo.SplitButton, {
12320 render : function(td){
12322 Roo.Toolbar.SplitButton.superclass.render.call(this, td);
12326 * Removes and destroys this button
12328 destroy : function(){
12329 Roo.Toolbar.SplitButton.superclass.destroy.call(this);
12330 this.td.parentNode.removeChild(this.td);
12334 * Shows this button
12337 this.hidden = false;
12338 this.td.style.display = "";
12342 * Hides this button
12345 this.hidden = true;
12346 this.td.style.display = "none";
12350 // backwards compat
12351 Roo.Toolbar.MenuButton = Roo.Toolbar.SplitButton;/*
12353 * Ext JS Library 1.1.1
12354 * Copyright(c) 2006-2007, Ext JS, LLC.
12356 * Originally Released Under LGPL - original licence link has changed is not relivant.
12359 * <script type="text/javascript">
12363 * @class Roo.PagingToolbar
12364 * @extends Roo.Toolbar
12365 * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
12367 * Create a new PagingToolbar
12368 * @param {Object} config The config object
12370 Roo.PagingToolbar = function(el, ds, config)
12372 // old args format still supported... - xtype is prefered..
12373 if (typeof(el) == 'object' && el.xtype) {
12374 // created from xtype...
12376 ds = el.dataSource;
12377 el = config.container;
12380 if (config.items) {
12381 items = config.items;
12385 Roo.PagingToolbar.superclass.constructor.call(this, el, null, config);
12388 this.renderButtons(this.el);
12391 // supprot items array.
12393 Roo.each(items, function(e) {
12394 this.add(Roo.factory(e));
12399 Roo.extend(Roo.PagingToolbar, Roo.Toolbar, {
12401 * @cfg {Roo.data.Store} dataSource
12402 * The underlying data store providing the paged data
12405 * @cfg {String/HTMLElement/Element} container
12406 * container The id or element that will contain the toolbar
12409 * @cfg {Boolean} displayInfo
12410 * True to display the displayMsg (defaults to false)
12413 * @cfg {Number} pageSize
12414 * The number of records to display per page (defaults to 20)
12418 * @cfg {String} displayMsg
12419 * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
12421 displayMsg : 'Displaying {0} - {1} of {2}',
12423 * @cfg {String} emptyMsg
12424 * The message to display when no records are found (defaults to "No data to display")
12426 emptyMsg : 'No data to display',
12428 * Customizable piece of the default paging text (defaults to "Page")
12431 beforePageText : "Page",
12433 * Customizable piece of the default paging text (defaults to "of %0")
12436 afterPageText : "of {0}",
12438 * Customizable piece of the default paging text (defaults to "First Page")
12441 firstText : "First Page",
12443 * Customizable piece of the default paging text (defaults to "Previous Page")
12446 prevText : "Previous Page",
12448 * Customizable piece of the default paging text (defaults to "Next Page")
12451 nextText : "Next Page",
12453 * Customizable piece of the default paging text (defaults to "Last Page")
12456 lastText : "Last Page",
12458 * Customizable piece of the default paging text (defaults to "Refresh")
12461 refreshText : "Refresh",
12464 renderButtons : function(el){
12465 Roo.PagingToolbar.superclass.render.call(this, el);
12466 this.first = this.addButton({
12467 tooltip: this.firstText,
12468 cls: "x-btn-icon x-grid-page-first",
12470 handler: this.onClick.createDelegate(this, ["first"])
12472 this.prev = this.addButton({
12473 tooltip: this.prevText,
12474 cls: "x-btn-icon x-grid-page-prev",
12476 handler: this.onClick.createDelegate(this, ["prev"])
12478 //this.addSeparator();
12479 this.add(this.beforePageText);
12480 this.field = Roo.get(this.addDom({
12485 cls: "x-grid-page-number"
12487 this.field.on("keydown", this.onPagingKeydown, this);
12488 this.field.on("focus", function(){this.dom.select();});
12489 this.afterTextEl = this.addText(String.format(this.afterPageText, 1));
12490 this.field.setHeight(18);
12491 //this.addSeparator();
12492 this.next = this.addButton({
12493 tooltip: this.nextText,
12494 cls: "x-btn-icon x-grid-page-next",
12496 handler: this.onClick.createDelegate(this, ["next"])
12498 this.last = this.addButton({
12499 tooltip: this.lastText,
12500 cls: "x-btn-icon x-grid-page-last",
12502 handler: this.onClick.createDelegate(this, ["last"])
12504 //this.addSeparator();
12505 this.loading = this.addButton({
12506 tooltip: this.refreshText,
12507 cls: "x-btn-icon x-grid-loading",
12508 handler: this.onClick.createDelegate(this, ["refresh"])
12511 if(this.displayInfo){
12512 this.displayEl = Roo.fly(this.el.dom.firstChild).createChild({cls:'x-paging-info'});
12517 updateInfo : function(){
12518 if(this.displayEl){
12519 var count = this.ds.getCount();
12520 var msg = count == 0 ?
12524 this.cursor+1, this.cursor+count, this.ds.getTotalCount()
12526 this.displayEl.update(msg);
12531 onLoad : function(ds, r, o){
12532 this.cursor = o.params ? o.params.start : 0;
12533 var d = this.getPageData(), ap = d.activePage, ps = d.pages;
12535 this.afterTextEl.el.innerHTML = String.format(this.afterPageText, d.pages);
12536 this.field.dom.value = ap;
12537 this.first.setDisabled(ap == 1);
12538 this.prev.setDisabled(ap == 1);
12539 this.next.setDisabled(ap == ps);
12540 this.last.setDisabled(ap == ps);
12541 this.loading.enable();
12546 getPageData : function(){
12547 var total = this.ds.getTotalCount();
12550 activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
12551 pages : total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
12556 onLoadError : function(){
12557 this.loading.enable();
12561 onPagingKeydown : function(e){
12562 var k = e.getKey();
12563 var d = this.getPageData();
12565 var v = this.field.dom.value, pageNum;
12566 if(!v || isNaN(pageNum = parseInt(v, 10))){
12567 this.field.dom.value = d.activePage;
12570 pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
12571 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
12574 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))
12576 var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
12577 this.field.dom.value = pageNum;
12578 this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
12581 else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
12583 var v = this.field.dom.value, pageNum;
12584 var increment = (e.shiftKey) ? 10 : 1;
12585 if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
12587 if(!v || isNaN(pageNum = parseInt(v, 10))) {
12588 this.field.dom.value = d.activePage;
12591 else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
12593 this.field.dom.value = parseInt(v, 10) + increment;
12594 pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
12595 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
12602 beforeLoad : function(){
12604 this.loading.disable();
12609 onClick : function(which){
12613 ds.load({params:{start: 0, limit: this.pageSize}});
12616 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
12619 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
12622 var total = ds.getTotalCount();
12623 var extra = total % this.pageSize;
12624 var lastStart = extra ? (total - extra) : total-this.pageSize;
12625 ds.load({params:{start: lastStart, limit: this.pageSize}});
12628 ds.load({params:{start: this.cursor, limit: this.pageSize}});
12634 * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
12635 * @param {Roo.data.Store} store The data store to unbind
12637 unbind : function(ds){
12638 ds.un("beforeload", this.beforeLoad, this);
12639 ds.un("load", this.onLoad, this);
12640 ds.un("loadexception", this.onLoadError, this);
12641 ds.un("remove", this.updateInfo, this);
12642 ds.un("add", this.updateInfo, this);
12643 this.ds = undefined;
12647 * Binds the paging toolbar to the specified {@link Roo.data.Store}
12648 * @param {Roo.data.Store} store The data store to bind
12650 bind : function(ds){
12651 ds.on("beforeload", this.beforeLoad, this);
12652 ds.on("load", this.onLoad, this);
12653 ds.on("loadexception", this.onLoadError, this);
12654 ds.on("remove", this.updateInfo, this);
12655 ds.on("add", this.updateInfo, this);
12660 * Ext JS Library 1.1.1
12661 * Copyright(c) 2006-2007, Ext JS, LLC.
12663 * Originally Released Under LGPL - original licence link has changed is not relivant.
12666 * <script type="text/javascript">
12670 * @class Roo.Resizable
12671 * @extends Roo.util.Observable
12672 * <p>Applies drag handles to an element to make it resizable. The drag handles are inserted into the element
12673 * and positioned absolute. Some elements, such as a textarea or image, don't support this. To overcome that, you can wrap
12674 * 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
12675 * the element will be wrapped for you automatically.</p>
12676 * <p>Here is the list of valid resize handles:</p>
12679 ------ -------------------
12688 'hd' horizontal drag
12691 * <p>Here's an example showing the creation of a typical Resizable:</p>
12693 var resizer = new Roo.Resizable("element-id", {
12701 resizer.on("resize", myHandler);
12703 * <p>To hide a particular handle, set its display to none in CSS, or through script:<br>
12704 * resizer.east.setDisplayed(false);</p>
12705 * @cfg {Boolean/String/Element} resizeChild True to resize the first child, or id/element to resize (defaults to false)
12706 * @cfg {Array/String} adjustments String "auto" or an array [width, height] with values to be <b>added</b> to the
12707 * resize operation's new size (defaults to [0, 0])
12708 * @cfg {Number} minWidth The minimum width for the element (defaults to 5)
12709 * @cfg {Number} minHeight The minimum height for the element (defaults to 5)
12710 * @cfg {Number} maxWidth The maximum width for the element (defaults to 10000)
12711 * @cfg {Number} maxHeight The maximum height for the element (defaults to 10000)
12712 * @cfg {Boolean} enabled False to disable resizing (defaults to true)
12713 * @cfg {Boolean} wrap True to wrap an element with a div if needed (required for textareas and images, defaults to false)
12714 * @cfg {Number} width The width of the element in pixels (defaults to null)
12715 * @cfg {Number} height The height of the element in pixels (defaults to null)
12716 * @cfg {Boolean} animate True to animate the resize (not compatible with dynamic sizing, defaults to false)
12717 * @cfg {Number} duration Animation duration if animate = true (defaults to .35)
12718 * @cfg {Boolean} dynamic True to resize the element while dragging instead of using a proxy (defaults to false)
12719 * @cfg {String} handles String consisting of the resize handles to display (defaults to undefined)
12720 * @cfg {Boolean} multiDirectional <b>Deprecated</b>. The old style of adding multi-direction resize handles, deprecated
12721 * in favor of the handles config option (defaults to false)
12722 * @cfg {Boolean} disableTrackOver True to disable mouse tracking. This is only applied at config time. (defaults to false)
12723 * @cfg {String} easing Animation easing if animate = true (defaults to 'easingOutStrong')
12724 * @cfg {Number} widthIncrement The increment to snap the width resize in pixels (dynamic must be true, defaults to 0)
12725 * @cfg {Number} heightIncrement The increment to snap the height resize in pixels (dynamic must be true, defaults to 0)
12726 * @cfg {Boolean} pinned True to ensure that the resize handles are always visible, false to display them only when the
12727 * user mouses over the resizable borders. This is only applied at config time. (defaults to false)
12728 * @cfg {Boolean} preserveRatio True to preserve the original ratio between height and width during resize (defaults to false)
12729 * @cfg {Boolean} transparent True for transparent handles. This is only applied at config time. (defaults to false)
12730 * @cfg {Number} minX The minimum allowed page X for the element (only used for west resizing, defaults to 0)
12731 * @cfg {Number} minY The minimum allowed page Y for the element (only used for north resizing, defaults to 0)
12732 * @cfg {Boolean} draggable Convenience to initialize drag drop (defaults to false)
12734 * Create a new resizable component
12735 * @param {String/HTMLElement/Roo.Element} el The id or element to resize
12736 * @param {Object} config configuration options
12738 Roo.Resizable = function(el, config)
12740 this.el = Roo.get(el);
12742 if(config && config.wrap){
12743 config.resizeChild = this.el;
12744 this.el = this.el.wrap(typeof config.wrap == "object" ? config.wrap : {cls:"xresizable-wrap"});
12745 this.el.id = this.el.dom.id = config.resizeChild.id + "-rzwrap";
12746 this.el.setStyle("overflow", "hidden");
12747 this.el.setPositioning(config.resizeChild.getPositioning());
12748 config.resizeChild.clearPositioning();
12749 if(!config.width || !config.height){
12750 var csize = config.resizeChild.getSize();
12751 this.el.setSize(csize.width, csize.height);
12753 if(config.pinned && !config.adjustments){
12754 config.adjustments = "auto";
12758 this.proxy = this.el.createProxy({tag: "div", cls: "x-resizable-proxy", id: this.el.id + "-rzproxy"});
12759 this.proxy.unselectable();
12760 this.proxy.enableDisplayMode('block');
12762 Roo.apply(this, config);
12765 this.disableTrackOver = true;
12766 this.el.addClass("x-resizable-pinned");
12768 // if the element isn't positioned, make it relative
12769 var position = this.el.getStyle("position");
12770 if(position != "absolute" && position != "fixed"){
12771 this.el.setStyle("position", "relative");
12773 if(!this.handles){ // no handles passed, must be legacy style
12774 this.handles = 's,e,se';
12775 if(this.multiDirectional){
12776 this.handles += ',n,w';
12779 if(this.handles == "all"){
12780 this.handles = "n s e w ne nw se sw";
12782 var hs = this.handles.split(/\s*?[,;]\s*?| /);
12783 var ps = Roo.Resizable.positions;
12784 for(var i = 0, len = hs.length; i < len; i++){
12785 if(hs[i] && ps[hs[i]]){
12786 var pos = ps[hs[i]];
12787 this[pos] = new Roo.Resizable.Handle(this, pos, this.disableTrackOver, this.transparent);
12791 this.corner = this.southeast;
12793 // updateBox = the box can move..
12794 if(this.handles.indexOf("n") != -1 || this.handles.indexOf("w") != -1 || this.handles.indexOf("hd") != -1) {
12795 this.updateBox = true;
12798 this.activeHandle = null;
12800 if(this.resizeChild){
12801 if(typeof this.resizeChild == "boolean"){
12802 this.resizeChild = Roo.get(this.el.dom.firstChild, true);
12804 this.resizeChild = Roo.get(this.resizeChild, true);
12808 if(this.adjustments == "auto"){
12809 var rc = this.resizeChild;
12810 var hw = this.west, he = this.east, hn = this.north, hs = this.south;
12811 if(rc && (hw || hn)){
12812 rc.position("relative");
12813 rc.setLeft(hw ? hw.el.getWidth() : 0);
12814 rc.setTop(hn ? hn.el.getHeight() : 0);
12816 this.adjustments = [
12817 (he ? -he.el.getWidth() : 0) + (hw ? -hw.el.getWidth() : 0),
12818 (hn ? -hn.el.getHeight() : 0) + (hs ? -hs.el.getHeight() : 0) -1
12822 if(this.draggable){
12823 this.dd = this.dynamic ?
12824 this.el.initDD(null) : this.el.initDDProxy(null, {dragElId: this.proxy.id});
12825 this.dd.setHandleElId(this.resizeChild ? this.resizeChild.id : this.el.id);
12831 * @event beforeresize
12832 * Fired before resize is allowed. Set enabled to false to cancel resize.
12833 * @param {Roo.Resizable} this
12834 * @param {Roo.EventObject} e The mousedown event
12836 "beforeresize" : true,
12839 * Fired a resizing.
12840 * @param {Roo.Resizable} this
12841 * @param {Number} x The new x position
12842 * @param {Number} y The new y position
12843 * @param {Number} w The new w width
12844 * @param {Number} h The new h hight
12845 * @param {Roo.EventObject} e The mouseup event
12850 * Fired after a resize.
12851 * @param {Roo.Resizable} this
12852 * @param {Number} width The new width
12853 * @param {Number} height The new height
12854 * @param {Roo.EventObject} e The mouseup event
12859 if(this.width !== null && this.height !== null){
12860 this.resizeTo(this.width, this.height);
12862 this.updateChildSize();
12865 this.el.dom.style.zoom = 1;
12867 Roo.Resizable.superclass.constructor.call(this);
12870 Roo.extend(Roo.Resizable, Roo.util.Observable, {
12871 resizeChild : false,
12872 adjustments : [0, 0],
12882 multiDirectional : false,
12883 disableTrackOver : false,
12884 easing : 'easeOutStrong',
12885 widthIncrement : 0,
12886 heightIncrement : 0,
12890 preserveRatio : false,
12891 transparent: false,
12897 * @cfg {String/HTMLElement/Element} constrainTo Constrain the resize to a particular element
12899 constrainTo: undefined,
12901 * @cfg {Roo.lib.Region} resizeRegion Constrain the resize to a particular region
12903 resizeRegion: undefined,
12907 * Perform a manual resize
12908 * @param {Number} width
12909 * @param {Number} height
12911 resizeTo : function(width, height){
12912 this.el.setSize(width, height);
12913 this.updateChildSize();
12914 this.fireEvent("resize", this, width, height, null);
12918 startSizing : function(e, handle){
12919 this.fireEvent("beforeresize", this, e);
12920 if(this.enabled){ // 2nd enabled check in case disabled before beforeresize handler
12923 this.overlay = this.el.createProxy({tag: "div", cls: "x-resizable-overlay", html: " "});
12924 this.overlay.unselectable();
12925 this.overlay.enableDisplayMode("block");
12926 this.overlay.on("mousemove", this.onMouseMove, this);
12927 this.overlay.on("mouseup", this.onMouseUp, this);
12929 this.overlay.setStyle("cursor", handle.el.getStyle("cursor"));
12931 this.resizing = true;
12932 this.startBox = this.el.getBox();
12933 this.startPoint = e.getXY();
12934 this.offsets = [(this.startBox.x + this.startBox.width) - this.startPoint[0],
12935 (this.startBox.y + this.startBox.height) - this.startPoint[1]];
12937 this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
12938 this.overlay.show();
12940 if(this.constrainTo) {
12941 var ct = Roo.get(this.constrainTo);
12942 this.resizeRegion = ct.getRegion().adjust(
12943 ct.getFrameWidth('t'),
12944 ct.getFrameWidth('l'),
12945 -ct.getFrameWidth('b'),
12946 -ct.getFrameWidth('r')
12950 this.proxy.setStyle('visibility', 'hidden'); // workaround display none
12952 this.proxy.setBox(this.startBox);
12954 this.proxy.setStyle('visibility', 'visible');
12960 onMouseDown : function(handle, e){
12963 this.activeHandle = handle;
12964 this.startSizing(e, handle);
12969 onMouseUp : function(e){
12970 var size = this.resizeElement();
12971 this.resizing = false;
12973 this.overlay.hide();
12975 this.fireEvent("resize", this, size.width, size.height, e);
12979 updateChildSize : function(){
12981 if(this.resizeChild){
12983 var child = this.resizeChild;
12984 var adj = this.adjustments;
12985 if(el.dom.offsetWidth){
12986 var b = el.getSize(true);
12987 child.setSize(b.width+adj[0], b.height+adj[1]);
12989 // Second call here for IE
12990 // The first call enables instant resizing and
12991 // the second call corrects scroll bars if they
12994 setTimeout(function(){
12995 if(el.dom.offsetWidth){
12996 var b = el.getSize(true);
12997 child.setSize(b.width+adj[0], b.height+adj[1]);
13005 snap : function(value, inc, min){
13006 if(!inc || !value) return value;
13007 var newValue = value;
13008 var m = value % inc;
13011 newValue = value + (inc-m);
13013 newValue = value - m;
13016 return Math.max(min, newValue);
13020 resizeElement : function(){
13021 var box = this.proxy.getBox();
13022 if(this.updateBox){
13023 this.el.setBox(box, false, this.animate, this.duration, null, this.easing);
13025 this.el.setSize(box.width, box.height, this.animate, this.duration, null, this.easing);
13027 this.updateChildSize();
13035 constrain : function(v, diff, m, mx){
13038 }else if(v - diff > mx){
13045 onMouseMove : function(e){
13048 try{// try catch so if something goes wrong the user doesn't get hung
13050 if(this.resizeRegion && !this.resizeRegion.contains(e.getPoint())) {
13054 //var curXY = this.startPoint;
13055 var curSize = this.curSize || this.startBox;
13056 var x = this.startBox.x, y = this.startBox.y;
13057 var ox = x, oy = y;
13058 var w = curSize.width, h = curSize.height;
13059 var ow = w, oh = h;
13060 var mw = this.minWidth, mh = this.minHeight;
13061 var mxw = this.maxWidth, mxh = this.maxHeight;
13062 var wi = this.widthIncrement;
13063 var hi = this.heightIncrement;
13065 var eventXY = e.getXY();
13066 var diffX = -(this.startPoint[0] - Math.max(this.minX, eventXY[0]));
13067 var diffY = -(this.startPoint[1] - Math.max(this.minY, eventXY[1]));
13069 var pos = this.activeHandle.position;
13074 w = Math.min(Math.max(mw, w), mxw);
13079 h = Math.min(Math.max(mh, h), mxh);
13084 w = Math.min(Math.max(mw, w), mxw);
13085 h = Math.min(Math.max(mh, h), mxh);
13088 diffY = this.constrain(h, diffY, mh, mxh);
13095 var adiffX = Math.abs(diffX);
13096 var sub = (adiffX % wi); // how much
13097 if (sub > (wi/2)) { // far enough to snap
13098 diffX = (diffX > 0) ? diffX-sub + wi : diffX+sub - wi;
13100 // remove difference..
13101 diffX = (diffX > 0) ? diffX-sub : diffX+sub;
13105 x = Math.max(this.minX, x);
13108 diffX = this.constrain(w, diffX, mw, mxw);
13114 w = Math.min(Math.max(mw, w), mxw);
13115 diffY = this.constrain(h, diffY, mh, mxh);
13120 diffX = this.constrain(w, diffX, mw, mxw);
13121 diffY = this.constrain(h, diffY, mh, mxh);
13128 diffX = this.constrain(w, diffX, mw, mxw);
13130 h = Math.min(Math.max(mh, h), mxh);
13136 var sw = this.snap(w, wi, mw);
13137 var sh = this.snap(h, hi, mh);
13138 if(sw != w || sh != h){
13161 if(this.preserveRatio){
13166 h = Math.min(Math.max(mh, h), mxh);
13171 w = Math.min(Math.max(mw, w), mxw);
13176 w = Math.min(Math.max(mw, w), mxw);
13182 w = Math.min(Math.max(mw, w), mxw);
13188 h = Math.min(Math.max(mh, h), mxh);
13196 h = Math.min(Math.max(mh, h), mxh);
13206 h = Math.min(Math.max(mh, h), mxh);
13214 if (pos == 'hdrag') {
13217 this.proxy.setBounds(x, y, w, h);
13219 this.resizeElement();
13223 this.fireEvent("resizing", this, x, y, w, h, e);
13227 handleOver : function(){
13229 this.el.addClass("x-resizable-over");
13234 handleOut : function(){
13235 if(!this.resizing){
13236 this.el.removeClass("x-resizable-over");
13241 * Returns the element this component is bound to.
13242 * @return {Roo.Element}
13244 getEl : function(){
13249 * Returns the resizeChild element (or null).
13250 * @return {Roo.Element}
13252 getResizeChild : function(){
13253 return this.resizeChild;
13255 groupHandler : function()
13260 * Destroys this resizable. If the element was wrapped and
13261 * removeEl is not true then the element remains.
13262 * @param {Boolean} removeEl (optional) true to remove the element from the DOM
13264 destroy : function(removeEl){
13265 this.proxy.remove();
13267 this.overlay.removeAllListeners();
13268 this.overlay.remove();
13270 var ps = Roo.Resizable.positions;
13272 if(typeof ps[k] != "function" && this[ps[k]]){
13273 var h = this[ps[k]];
13274 h.el.removeAllListeners();
13279 this.el.update("");
13286 // hash to map config positions to true positions
13287 Roo.Resizable.positions = {
13288 n: "north", s: "south", e: "east", w: "west", se: "southeast", sw: "southwest", nw: "northwest", ne: "northeast",
13293 Roo.Resizable.Handle = function(rz, pos, disableTrackOver, transparent){
13295 // only initialize the template if resizable is used
13296 var tpl = Roo.DomHelper.createTemplate(
13297 {tag: "div", cls: "x-resizable-handle x-resizable-handle-{0}"}
13300 Roo.Resizable.Handle.prototype.tpl = tpl;
13302 this.position = pos;
13304 // show north drag fro topdra
13305 var handlepos = pos == 'hdrag' ? 'north' : pos;
13307 this.el = this.tpl.append(rz.el.dom, [handlepos], true);
13308 if (pos == 'hdrag') {
13309 this.el.setStyle('cursor', 'pointer');
13311 this.el.unselectable();
13313 this.el.setOpacity(0);
13315 this.el.on("mousedown", this.onMouseDown, this);
13316 if(!disableTrackOver){
13317 this.el.on("mouseover", this.onMouseOver, this);
13318 this.el.on("mouseout", this.onMouseOut, this);
13323 Roo.Resizable.Handle.prototype = {
13324 afterResize : function(rz){
13329 onMouseDown : function(e){
13330 this.rz.onMouseDown(this, e);
13333 onMouseOver : function(e){
13334 this.rz.handleOver(this, e);
13337 onMouseOut : function(e){
13338 this.rz.handleOut(this, e);
13342 * Ext JS Library 1.1.1
13343 * Copyright(c) 2006-2007, Ext JS, LLC.
13345 * Originally Released Under LGPL - original licence link has changed is not relivant.
13348 * <script type="text/javascript">
13352 * @class Roo.Editor
13353 * @extends Roo.Component
13354 * A base editor field that handles displaying/hiding on demand and has some built-in sizing and event handling logic.
13356 * Create a new Editor
13357 * @param {Roo.form.Field} field The Field object (or descendant)
13358 * @param {Object} config The config object
13360 Roo.Editor = function(field, config){
13361 Roo.Editor.superclass.constructor.call(this, config);
13362 this.field = field;
13365 * @event beforestartedit
13366 * Fires when editing is initiated, but before the value changes. Editing can be canceled by returning
13367 * false from the handler of this event.
13368 * @param {Editor} this
13369 * @param {Roo.Element} boundEl The underlying element bound to this editor
13370 * @param {Mixed} value The field value being set
13372 "beforestartedit" : true,
13375 * Fires when this editor is displayed
13376 * @param {Roo.Element} boundEl The underlying element bound to this editor
13377 * @param {Mixed} value The starting field value
13379 "startedit" : true,
13381 * @event beforecomplete
13382 * Fires after a change has been made to the field, but before the change is reflected in the underlying
13383 * field. Saving the change to the field can be canceled by returning false from the handler of this event.
13384 * Note that if the value has not changed and ignoreNoChange = true, the editing will still end but this
13385 * event will not fire since no edit actually occurred.
13386 * @param {Editor} this
13387 * @param {Mixed} value The current field value
13388 * @param {Mixed} startValue The original field value
13390 "beforecomplete" : true,
13393 * Fires after editing is complete and any changed value has been written to the underlying field.
13394 * @param {Editor} this
13395 * @param {Mixed} value The current field value
13396 * @param {Mixed} startValue The original field value
13400 * @event specialkey
13401 * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed. You can check
13402 * {@link Roo.EventObject#getKey} to determine which key was pressed.
13403 * @param {Roo.form.Field} this
13404 * @param {Roo.EventObject} e The event object
13406 "specialkey" : true
13410 Roo.extend(Roo.Editor, Roo.Component, {
13412 * @cfg {Boolean/String} autosize
13413 * True for the editor to automatically adopt the size of the underlying field, "width" to adopt the width only,
13414 * or "height" to adopt the height only (defaults to false)
13417 * @cfg {Boolean} revertInvalid
13418 * True to automatically revert the field value and cancel the edit when the user completes an edit and the field
13419 * validation fails (defaults to true)
13422 * @cfg {Boolean} ignoreNoChange
13423 * True to skip the the edit completion process (no save, no events fired) if the user completes an edit and
13424 * the value has not changed (defaults to false). Applies only to string values - edits for other data types
13425 * will never be ignored.
13428 * @cfg {Boolean} hideEl
13429 * False to keep the bound element visible while the editor is displayed (defaults to true)
13432 * @cfg {Mixed} value
13433 * The data value of the underlying field (defaults to "")
13437 * @cfg {String} alignment
13438 * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "c-c?").
13442 * @cfg {Boolean/String} shadow "sides" for sides/bottom only, "frame" for 4-way shadow, and "drop"
13443 * for bottom-right shadow (defaults to "frame")
13447 * @cfg {Boolean} constrain True to constrain the editor to the viewport
13451 * @cfg {Boolean} completeOnEnter True to complete the edit when the enter key is pressed (defaults to false)
13453 completeOnEnter : false,
13455 * @cfg {Boolean} cancelOnEsc True to cancel the edit when the escape key is pressed (defaults to false)
13457 cancelOnEsc : false,
13459 * @cfg {Boolean} updateEl True to update the innerHTML of the bound element when the update completes (defaults to false)
13464 onRender : function(ct, position){
13465 this.el = new Roo.Layer({
13466 shadow: this.shadow,
13472 constrain: this.constrain
13474 this.el.setStyle("overflow", Roo.isGecko ? "auto" : "hidden");
13475 if(this.field.msgTarget != 'title'){
13476 this.field.msgTarget = 'qtip';
13478 this.field.render(this.el);
13480 this.field.el.dom.setAttribute('autocomplete', 'off');
13482 this.field.on("specialkey", this.onSpecialKey, this);
13483 if(this.swallowKeys){
13484 this.field.el.swallowEvent(['keydown','keypress']);
13487 this.field.on("blur", this.onBlur, this);
13488 if(this.field.grow){
13489 this.field.on("autosize", this.el.sync, this.el, {delay:1});
13493 onSpecialKey : function(field, e)
13495 //Roo.log('editor onSpecialKey');
13496 if(this.completeOnEnter && e.getKey() == e.ENTER){
13498 this.completeEdit();
13501 // do not fire special key otherwise it might hide close the editor...
13502 if(e.getKey() == e.ENTER){
13505 if(this.cancelOnEsc && e.getKey() == e.ESC){
13509 this.fireEvent('specialkey', field, e);
13514 * Starts the editing process and shows the editor.
13515 * @param {String/HTMLElement/Element} el The element to edit
13516 * @param {String} value (optional) A value to initialize the editor with. If a value is not provided, it defaults
13517 * to the innerHTML of el.
13519 startEdit : function(el, value){
13521 this.completeEdit();
13523 this.boundEl = Roo.get(el);
13524 var v = value !== undefined ? value : this.boundEl.dom.innerHTML;
13525 if(!this.rendered){
13526 this.render(this.parentEl || document.body);
13528 if(this.fireEvent("beforestartedit", this, this.boundEl, v) === false){
13531 this.startValue = v;
13532 this.field.setValue(v);
13534 var sz = this.boundEl.getSize();
13535 switch(this.autoSize){
13537 this.setSize(sz.width, "");
13540 this.setSize("", sz.height);
13543 this.setSize(sz.width, sz.height);
13546 this.el.alignTo(this.boundEl, this.alignment);
13547 this.editing = true;
13549 Roo.QuickTips.disable();
13555 * Sets the height and width of this editor.
13556 * @param {Number} width The new width
13557 * @param {Number} height The new height
13559 setSize : function(w, h){
13560 this.field.setSize(w, h);
13567 * Realigns the editor to the bound field based on the current alignment config value.
13569 realign : function(){
13570 this.el.alignTo(this.boundEl, this.alignment);
13574 * Ends the editing process, persists the changed value to the underlying field, and hides the editor.
13575 * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after edit (defaults to false)
13577 completeEdit : function(remainVisible){
13581 var v = this.getValue();
13582 if(this.revertInvalid !== false && !this.field.isValid()){
13583 v = this.startValue;
13584 this.cancelEdit(true);
13586 if(String(v) === String(this.startValue) && this.ignoreNoChange){
13587 this.editing = false;
13591 if(this.fireEvent("beforecomplete", this, v, this.startValue) !== false){
13592 this.editing = false;
13593 if(this.updateEl && this.boundEl){
13594 this.boundEl.update(v);
13596 if(remainVisible !== true){
13599 this.fireEvent("complete", this, v, this.startValue);
13604 onShow : function(){
13606 if(this.hideEl !== false){
13607 this.boundEl.hide();
13610 if(Roo.isIE && !this.fixIEFocus){ // IE has problems with focusing the first time
13611 this.fixIEFocus = true;
13612 this.deferredFocus.defer(50, this);
13614 this.field.focus();
13616 this.fireEvent("startedit", this.boundEl, this.startValue);
13619 deferredFocus : function(){
13621 this.field.focus();
13626 * Cancels the editing process and hides the editor without persisting any changes. The field value will be
13627 * reverted to the original starting value.
13628 * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after
13629 * cancel (defaults to false)
13631 cancelEdit : function(remainVisible){
13633 this.setValue(this.startValue);
13634 if(remainVisible !== true){
13641 onBlur : function(){
13642 if(this.allowBlur !== true && this.editing){
13643 this.completeEdit();
13648 onHide : function(){
13650 this.completeEdit();
13654 if(this.field.collapse){
13655 this.field.collapse();
13658 if(this.hideEl !== false){
13659 this.boundEl.show();
13662 Roo.QuickTips.enable();
13667 * Sets the data value of the editor
13668 * @param {Mixed} value Any valid value supported by the underlying field
13670 setValue : function(v){
13671 this.field.setValue(v);
13675 * Gets the data value of the editor
13676 * @return {Mixed} The data value
13678 getValue : function(){
13679 return this.field.getValue();
13683 * Ext JS Library 1.1.1
13684 * Copyright(c) 2006-2007, Ext JS, LLC.
13686 * Originally Released Under LGPL - original licence link has changed is not relivant.
13689 * <script type="text/javascript">
13693 * @class Roo.BasicDialog
13694 * @extends Roo.util.Observable
13695 * Lightweight Dialog Class. The code below shows the creation of a typical dialog using existing HTML markup:
13697 var dlg = new Roo.BasicDialog("my-dlg", {
13706 dlg.addKeyListener(27, dlg.hide, dlg); // ESC can also close the dialog
13707 dlg.addButton('OK', dlg.hide, dlg); // Could call a save function instead of hiding
13708 dlg.addButton('Cancel', dlg.hide, dlg);
13711 <b>A Dialog should always be a direct child of the body element.</b>
13712 * @cfg {Boolean/DomHelper} autoCreate True to auto create from scratch, or using a DomHelper Object (defaults to false)
13713 * @cfg {String} title Default text to display in the title bar (defaults to null)
13714 * @cfg {Number} width Width of the dialog in pixels (can also be set via CSS). Determined by browser if unspecified.
13715 * @cfg {Number} height Height of the dialog in pixels (can also be set via CSS). Determined by browser if unspecified.
13716 * @cfg {Number} x The default left page coordinate of the dialog (defaults to center screen)
13717 * @cfg {Number} y The default top page coordinate of the dialog (defaults to center screen)
13718 * @cfg {String/Element} animateTarget Id or element from which the dialog should animate while opening
13719 * (defaults to null with no animation)
13720 * @cfg {Boolean} resizable False to disable manual dialog resizing (defaults to true)
13721 * @cfg {String} resizeHandles Which resize handles to display - see the {@link Roo.Resizable} handles config
13722 * property for valid values (defaults to 'all')
13723 * @cfg {Number} minHeight The minimum allowable height for a resizable dialog (defaults to 80)
13724 * @cfg {Number} minWidth The minimum allowable width for a resizable dialog (defaults to 200)
13725 * @cfg {Boolean} modal True to show the dialog modally, preventing user interaction with the rest of the page (defaults to false)
13726 * @cfg {Boolean} autoScroll True to allow the dialog body contents to overflow and display scrollbars (defaults to false)
13727 * @cfg {Boolean} closable False to remove the built-in top-right corner close button (defaults to true)
13728 * @cfg {Boolean} collapsible False to remove the built-in top-right corner collapse button (defaults to true)
13729 * @cfg {Boolean} constraintoviewport True to keep the dialog constrained within the visible viewport boundaries (defaults to true)
13730 * @cfg {Boolean} syncHeightBeforeShow True to cause the dimensions to be recalculated before the dialog is shown (defaults to false)
13731 * @cfg {Boolean} draggable False to disable dragging of the dialog within the viewport (defaults to true)
13732 * @cfg {Boolean} autoTabs If true, all elements with class 'x-dlg-tab' will get automatically converted to tabs (defaults to false)
13733 * @cfg {String} tabTag The tag name of tab elements, used when autoTabs = true (defaults to 'div')
13734 * @cfg {Boolean} proxyDrag True to drag a lightweight proxy element rather than the dialog itself, used when
13735 * draggable = true (defaults to false)
13736 * @cfg {Boolean} fixedcenter True to ensure that anytime the dialog is shown or resized it gets centered (defaults to false)
13737 * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
13738 * shadow (defaults to false)
13739 * @cfg {Number} shadowOffset The number of pixels to offset the shadow if displayed (defaults to 5)
13740 * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "right")
13741 * @cfg {Number} minButtonWidth Minimum width of all dialog buttons (defaults to 75)
13742 * @cfg {Array} buttons Array of buttons
13743 * @cfg {Boolean} shim True to create an iframe shim that prevents selects from showing through (defaults to false)
13745 * Create a new BasicDialog.
13746 * @param {String/HTMLElement/Roo.Element} el The container element or DOM node, or its id
13747 * @param {Object} config Configuration options
13749 Roo.BasicDialog = function(el, config){
13750 this.el = Roo.get(el);
13751 var dh = Roo.DomHelper;
13752 if(!this.el && config && config.autoCreate){
13753 if(typeof config.autoCreate == "object"){
13754 if(!config.autoCreate.id){
13755 config.autoCreate.id = el;
13757 this.el = dh.append(document.body,
13758 config.autoCreate, true);
13760 this.el = dh.append(document.body,
13761 {tag: "div", id: el, style:'visibility:hidden;'}, true);
13765 el.setDisplayed(true);
13766 el.hide = this.hideAction;
13768 el.addClass("x-dlg");
13770 Roo.apply(this, config);
13772 this.proxy = el.createProxy("x-dlg-proxy");
13773 this.proxy.hide = this.hideAction;
13774 this.proxy.setOpacity(.5);
13778 el.setWidth(config.width);
13781 el.setHeight(config.height);
13783 this.size = el.getSize();
13784 if(typeof config.x != "undefined" && typeof config.y != "undefined"){
13785 this.xy = [config.x,config.y];
13787 this.xy = el.getCenterXY(true);
13789 /** The header element @type Roo.Element */
13790 this.header = el.child("> .x-dlg-hd");
13791 /** The body element @type Roo.Element */
13792 this.body = el.child("> .x-dlg-bd");
13793 /** The footer element @type Roo.Element */
13794 this.footer = el.child("> .x-dlg-ft");
13797 this.header = el.createChild({tag: "div", cls:"x-dlg-hd", html: " "}, this.body ? this.body.dom : null);
13800 this.body = el.createChild({tag: "div", cls:"x-dlg-bd"});
13803 this.header.unselectable();
13805 this.header.update(this.title);
13807 // this element allows the dialog to be focused for keyboard event
13808 this.focusEl = el.createChild({tag: "a", href:"#", cls:"x-dlg-focus", tabIndex:"-1"});
13809 this.focusEl.swallowEvent("click", true);
13811 this.header.wrap({cls:"x-dlg-hd-right"}).wrap({cls:"x-dlg-hd-left"}, true);
13813 // wrap the body and footer for special rendering
13814 this.bwrap = this.body.wrap({tag: "div", cls:"x-dlg-dlg-body"});
13816 this.bwrap.dom.appendChild(this.footer.dom);
13819 this.bg = this.el.createChild({
13820 tag: "div", cls:"x-dlg-bg",
13821 html: '<div class="x-dlg-bg-left"><div class="x-dlg-bg-right"><div class="x-dlg-bg-center"> </div></div></div>'
13823 this.centerBg = this.bg.child("div.x-dlg-bg-center");
13826 if(this.autoScroll !== false && !this.autoTabs){
13827 this.body.setStyle("overflow", "auto");
13830 this.toolbox = this.el.createChild({cls: "x-dlg-toolbox"});
13832 if(this.closable !== false){
13833 this.el.addClass("x-dlg-closable");
13834 this.close = this.toolbox.createChild({cls:"x-dlg-close"});
13835 this.close.on("click", this.closeClick, this);
13836 this.close.addClassOnOver("x-dlg-close-over");
13838 if(this.collapsible !== false){
13839 this.collapseBtn = this.toolbox.createChild({cls:"x-dlg-collapse"});
13840 this.collapseBtn.on("click", this.collapseClick, this);
13841 this.collapseBtn.addClassOnOver("x-dlg-collapse-over");
13842 this.header.on("dblclick", this.collapseClick, this);
13844 if(this.resizable !== false){
13845 this.el.addClass("x-dlg-resizable");
13846 this.resizer = new Roo.Resizable(el, {
13847 minWidth: this.minWidth || 80,
13848 minHeight:this.minHeight || 80,
13849 handles: this.resizeHandles || "all",
13852 this.resizer.on("beforeresize", this.beforeResize, this);
13853 this.resizer.on("resize", this.onResize, this);
13855 if(this.draggable !== false){
13856 el.addClass("x-dlg-draggable");
13857 if (!this.proxyDrag) {
13858 var dd = new Roo.dd.DD(el.dom.id, "WindowDrag");
13861 var dd = new Roo.dd.DDProxy(el.dom.id, "WindowDrag", {dragElId: this.proxy.id});
13863 dd.setHandleElId(this.header.id);
13864 dd.endDrag = this.endMove.createDelegate(this);
13865 dd.startDrag = this.startMove.createDelegate(this);
13866 dd.onDrag = this.onDrag.createDelegate(this);
13871 this.mask = dh.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
13872 this.mask.enableDisplayMode("block");
13874 this.el.addClass("x-dlg-modal");
13877 this.shadow = new Roo.Shadow({
13878 mode : typeof this.shadow == "string" ? this.shadow : "sides",
13879 offset : this.shadowOffset
13882 this.shadowOffset = 0;
13884 if(Roo.useShims && this.shim !== false){
13885 this.shim = this.el.createShim();
13886 this.shim.hide = this.hideAction;
13894 if (this.buttons) {
13895 var bts= this.buttons;
13897 Roo.each(bts, function(b) {
13906 * Fires when a key is pressed
13907 * @param {Roo.BasicDialog} this
13908 * @param {Roo.EventObject} e
13913 * Fires when this dialog is moved by the user.
13914 * @param {Roo.BasicDialog} this
13915 * @param {Number} x The new page X
13916 * @param {Number} y The new page Y
13921 * Fires when this dialog is resized by the user.
13922 * @param {Roo.BasicDialog} this
13923 * @param {Number} width The new width
13924 * @param {Number} height The new height
13928 * @event beforehide
13929 * Fires before this dialog is hidden.
13930 * @param {Roo.BasicDialog} this
13932 "beforehide" : true,
13935 * Fires when this dialog is hidden.
13936 * @param {Roo.BasicDialog} this
13940 * @event beforeshow
13941 * Fires before this dialog is shown.
13942 * @param {Roo.BasicDialog} this
13944 "beforeshow" : true,
13947 * Fires when this dialog is shown.
13948 * @param {Roo.BasicDialog} this
13952 el.on("keydown", this.onKeyDown, this);
13953 el.on("mousedown", this.toFront, this);
13954 Roo.EventManager.onWindowResize(this.adjustViewport, this, true);
13956 Roo.DialogManager.register(this);
13957 Roo.BasicDialog.superclass.constructor.call(this);
13960 Roo.extend(Roo.BasicDialog, Roo.util.Observable, {
13961 shadowOffset: Roo.isIE ? 6 : 5,
13964 minButtonWidth: 75,
13965 defaultButton: null,
13966 buttonAlign: "right",
13971 * Sets the dialog title text
13972 * @param {String} text The title text to display
13973 * @return {Roo.BasicDialog} this
13975 setTitle : function(text){
13976 this.header.update(text);
13981 closeClick : function(){
13986 collapseClick : function(){
13987 this[this.collapsed ? "expand" : "collapse"]();
13991 * Collapses the dialog to its minimized state (only the title bar is visible).
13992 * Equivalent to the user clicking the collapse dialog button.
13994 collapse : function(){
13995 if(!this.collapsed){
13996 this.collapsed = true;
13997 this.el.addClass("x-dlg-collapsed");
13998 this.restoreHeight = this.el.getHeight();
13999 this.resizeTo(this.el.getWidth(), this.header.getHeight());
14004 * Expands a collapsed dialog back to its normal state. Equivalent to the user
14005 * clicking the expand dialog button.
14007 expand : function(){
14008 if(this.collapsed){
14009 this.collapsed = false;
14010 this.el.removeClass("x-dlg-collapsed");
14011 this.resizeTo(this.el.getWidth(), this.restoreHeight);
14016 * Reinitializes the tabs component, clearing out old tabs and finding new ones.
14017 * @return {Roo.TabPanel} The tabs component
14019 initTabs : function(){
14020 var tabs = this.getTabs();
14021 while(tabs.getTab(0)){
14024 this.el.select(this.tabTag+'.x-dlg-tab').each(function(el){
14026 tabs.addTab(Roo.id(dom), dom.title);
14034 beforeResize : function(){
14035 this.resizer.minHeight = Math.max(this.minHeight, this.getHeaderFooterHeight(true)+40);
14039 onResize : function(){
14040 this.refreshSize();
14041 this.syncBodyHeight();
14042 this.adjustAssets();
14044 this.fireEvent("resize", this, this.size.width, this.size.height);
14048 onKeyDown : function(e){
14049 if(this.isVisible()){
14050 this.fireEvent("keydown", this, e);
14055 * Resizes the dialog.
14056 * @param {Number} width
14057 * @param {Number} height
14058 * @return {Roo.BasicDialog} this
14060 resizeTo : function(width, height){
14061 this.el.setSize(width, height);
14062 this.size = {width: width, height: height};
14063 this.syncBodyHeight();
14064 if(this.fixedcenter){
14067 if(this.isVisible()){
14068 this.constrainXY();
14069 this.adjustAssets();
14071 this.fireEvent("resize", this, width, height);
14077 * Resizes the dialog to fit the specified content size.
14078 * @param {Number} width
14079 * @param {Number} height
14080 * @return {Roo.BasicDialog} this
14082 setContentSize : function(w, h){
14083 h += this.getHeaderFooterHeight() + this.body.getMargins("tb");
14084 w += this.body.getMargins("lr") + this.bwrap.getMargins("lr") + this.centerBg.getPadding("lr");
14085 //if(!this.el.isBorderBox()){
14086 h += this.body.getPadding("tb") + this.bwrap.getBorderWidth("tb") + this.body.getBorderWidth("tb") + this.el.getBorderWidth("tb");
14087 w += this.body.getPadding("lr") + this.bwrap.getBorderWidth("lr") + this.body.getBorderWidth("lr") + this.bwrap.getPadding("lr") + this.el.getBorderWidth("lr");
14090 h += this.tabs.stripWrap.getHeight() + this.tabs.bodyEl.getMargins("tb") + this.tabs.bodyEl.getPadding("tb");
14091 w += this.tabs.bodyEl.getMargins("lr") + this.tabs.bodyEl.getPadding("lr");
14093 this.resizeTo(w, h);
14098 * Adds a key listener for when this dialog is displayed. This allows you to hook in a function that will be
14099 * executed in response to a particular key being pressed while the dialog is active.
14100 * @param {Number/Array/Object} key Either the numeric key code, array of key codes or an object with the following options:
14101 * {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
14102 * @param {Function} fn The function to call
14103 * @param {Object} scope (optional) The scope of the function
14104 * @return {Roo.BasicDialog} this
14106 addKeyListener : function(key, fn, scope){
14107 var keyCode, shift, ctrl, alt;
14108 if(typeof key == "object" && !(key instanceof Array)){
14109 keyCode = key["key"];
14110 shift = key["shift"];
14111 ctrl = key["ctrl"];
14116 var handler = function(dlg, e){
14117 if((!shift || e.shiftKey) && (!ctrl || e.ctrlKey) && (!alt || e.altKey)){
14118 var k = e.getKey();
14119 if(keyCode instanceof Array){
14120 for(var i = 0, len = keyCode.length; i < len; i++){
14121 if(keyCode[i] == k){
14122 fn.call(scope || window, dlg, k, e);
14128 fn.call(scope || window, dlg, k, e);
14133 this.on("keydown", handler);
14138 * Returns the TabPanel component (creates it if it doesn't exist).
14139 * Note: If you wish to simply check for the existence of tabs without creating them,
14140 * check for a null 'tabs' property.
14141 * @return {Roo.TabPanel} The tabs component
14143 getTabs : function(){
14145 this.el.addClass("x-dlg-auto-tabs");
14146 this.body.addClass(this.tabPosition == "bottom" ? "x-tabs-bottom" : "x-tabs-top");
14147 this.tabs = new Roo.TabPanel(this.body.dom, this.tabPosition == "bottom");
14153 * Adds a button to the footer section of the dialog.
14154 * @param {String/Object} config A string becomes the button text, an object can either be a Button config
14155 * object or a valid Roo.DomHelper element config
14156 * @param {Function} handler The function called when the button is clicked
14157 * @param {Object} scope (optional) The scope of the handler function (accepts position as a property)
14158 * @return {Roo.Button} The new button
14160 addButton : function(config, handler, scope){
14161 var dh = Roo.DomHelper;
14163 this.footer = dh.append(this.bwrap, {tag: "div", cls:"x-dlg-ft"}, true);
14165 if(!this.btnContainer){
14166 var tb = this.footer.createChild({
14168 cls:"x-dlg-btns x-dlg-btns-"+this.buttonAlign,
14169 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
14171 this.btnContainer = tb.firstChild.firstChild.firstChild;
14176 minWidth: this.minButtonWidth,
14179 if(typeof config == "string"){
14180 bconfig.text = config;
14183 bconfig.dhconfig = config;
14185 Roo.apply(bconfig, config);
14189 if ((typeof(bconfig.position) != 'undefined') && bconfig.position < this.btnContainer.childNodes.length-1) {
14190 bconfig.position = Math.max(0, bconfig.position);
14191 fc = this.btnContainer.childNodes[bconfig.position];
14194 var btn = new Roo.Button(
14196 this.btnContainer.insertBefore(document.createElement("td"),fc)
14197 : this.btnContainer.appendChild(document.createElement("td")),
14198 //Roo.get(this.btnContainer).createChild( { tag: 'td'}, fc ),
14201 this.syncBodyHeight();
14204 * Array of all the buttons that have been added to this dialog via addButton
14209 this.buttons.push(btn);
14214 * Sets the default button to be focused when the dialog is displayed.
14215 * @param {Roo.BasicDialog.Button} btn The button object returned by {@link #addButton}
14216 * @return {Roo.BasicDialog} this
14218 setDefaultButton : function(btn){
14219 this.defaultButton = btn;
14224 getHeaderFooterHeight : function(safe){
14227 height += this.header.getHeight();
14230 var fm = this.footer.getMargins();
14231 height += (this.footer.getHeight()+fm.top+fm.bottom);
14233 height += this.bwrap.getPadding("tb")+this.bwrap.getBorderWidth("tb");
14234 height += this.centerBg.getPadding("tb");
14239 syncBodyHeight : function()
14241 var bd = this.body, // the text
14242 cb = this.centerBg, // wrapper around bottom.. but does not seem to be used..
14244 var height = this.size.height - this.getHeaderFooterHeight(false);
14245 bd.setHeight(height-bd.getMargins("tb"));
14246 var hh = this.header.getHeight();
14247 var h = this.size.height-hh;
14250 bw.setLeftTop(cb.getPadding("l"), hh+cb.getPadding("t"));
14251 bw.setHeight(h-cb.getPadding("tb"));
14253 bw.setWidth(this.el.getWidth(true)-cb.getPadding("lr"));
14254 bd.setWidth(bw.getWidth(true));
14256 this.tabs.syncHeight();
14258 this.tabs.el.repaint();
14264 * Restores the previous state of the dialog if Roo.state is configured.
14265 * @return {Roo.BasicDialog} this
14267 restoreState : function(){
14268 var box = Roo.state.Manager.get(this.stateId || (this.el.id + "-state"));
14269 if(box && box.width){
14270 this.xy = [box.x, box.y];
14271 this.resizeTo(box.width, box.height);
14277 beforeShow : function(){
14279 if(this.fixedcenter){
14280 this.xy = this.el.getCenterXY(true);
14283 Roo.get(document.body).addClass("x-body-masked");
14284 this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
14287 this.constrainXY();
14291 animShow : function(){
14292 var b = Roo.get(this.animateTarget).getBox();
14293 this.proxy.setSize(b.width, b.height);
14294 this.proxy.setLocation(b.x, b.y);
14296 this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height,
14297 true, .35, this.showEl.createDelegate(this));
14301 * Shows the dialog.
14302 * @param {String/HTMLElement/Roo.Element} animateTarget (optional) Reset the animation target
14303 * @return {Roo.BasicDialog} this
14305 show : function(animateTarget){
14306 if (this.fireEvent("beforeshow", this) === false){
14309 if(this.syncHeightBeforeShow){
14310 this.syncBodyHeight();
14311 }else if(this.firstShow){
14312 this.firstShow = false;
14313 this.syncBodyHeight(); // sync the height on the first show instead of in the constructor
14315 this.animateTarget = animateTarget || this.animateTarget;
14316 if(!this.el.isVisible()){
14318 if(this.animateTarget && Roo.get(this.animateTarget)){
14328 showEl : function(){
14330 this.el.setXY(this.xy);
14332 this.adjustAssets(true);
14335 // IE peekaboo bug - fix found by Dave Fenwick
14339 this.fireEvent("show", this);
14343 * Focuses the dialog. If a defaultButton is set, it will receive focus, otherwise the
14344 * dialog itself will receive focus.
14346 focus : function(){
14347 if(this.defaultButton){
14348 this.defaultButton.focus();
14350 this.focusEl.focus();
14355 constrainXY : function(){
14356 if(this.constraintoviewport !== false){
14357 if(!this.viewSize){
14358 if(this.container){
14359 var s = this.container.getSize();
14360 this.viewSize = [s.width, s.height];
14362 this.viewSize = [Roo.lib.Dom.getViewWidth(),Roo.lib.Dom.getViewHeight()];
14365 var s = Roo.get(this.container||document).getScroll();
14367 var x = this.xy[0], y = this.xy[1];
14368 var w = this.size.width, h = this.size.height;
14369 var vw = this.viewSize[0], vh = this.viewSize[1];
14370 // only move it if it needs it
14372 // first validate right/bottom
14373 if(x + w > vw+s.left){
14377 if(y + h > vh+s.top){
14381 // then make sure top/left isn't negative
14393 if(this.isVisible()){
14394 this.el.setLocation(x, y);
14395 this.adjustAssets();
14402 onDrag : function(){
14403 if(!this.proxyDrag){
14404 this.xy = this.el.getXY();
14405 this.adjustAssets();
14410 adjustAssets : function(doShow){
14411 var x = this.xy[0], y = this.xy[1];
14412 var w = this.size.width, h = this.size.height;
14413 if(doShow === true){
14415 this.shadow.show(this.el);
14421 if(this.shadow && this.shadow.isVisible()){
14422 this.shadow.show(this.el);
14424 if(this.shim && this.shim.isVisible()){
14425 this.shim.setBounds(x, y, w, h);
14430 adjustViewport : function(w, h){
14432 w = Roo.lib.Dom.getViewWidth();
14433 h = Roo.lib.Dom.getViewHeight();
14436 this.viewSize = [w, h];
14437 if(this.modal && this.mask.isVisible()){
14438 this.mask.setSize(w, h); // first make sure the mask isn't causing overflow
14439 this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
14441 if(this.isVisible()){
14442 this.constrainXY();
14447 * Destroys this dialog and all its supporting elements (including any tabs, shim,
14448 * shadow, proxy, mask, etc.) Also removes all event listeners.
14449 * @param {Boolean} removeEl (optional) true to remove the element from the DOM
14451 destroy : function(removeEl){
14452 if(this.isVisible()){
14453 this.animateTarget = null;
14456 Roo.EventManager.removeResizeListener(this.adjustViewport, this);
14458 this.tabs.destroy(removeEl);
14471 for(var i = 0, len = this.buttons.length; i < len; i++){
14472 this.buttons[i].destroy();
14475 this.el.removeAllListeners();
14476 if(removeEl === true){
14477 this.el.update("");
14480 Roo.DialogManager.unregister(this);
14484 startMove : function(){
14485 if(this.proxyDrag){
14488 if(this.constraintoviewport !== false){
14489 this.dd.constrainTo(document.body, {right: this.shadowOffset, bottom: this.shadowOffset});
14494 endMove : function(){
14495 if(!this.proxyDrag){
14496 Roo.dd.DD.prototype.endDrag.apply(this.dd, arguments);
14498 Roo.dd.DDProxy.prototype.endDrag.apply(this.dd, arguments);
14501 this.refreshSize();
14502 this.adjustAssets();
14504 this.fireEvent("move", this, this.xy[0], this.xy[1]);
14508 * Brings this dialog to the front of any other visible dialogs
14509 * @return {Roo.BasicDialog} this
14511 toFront : function(){
14512 Roo.DialogManager.bringToFront(this);
14517 * Sends this dialog to the back (under) of any other visible dialogs
14518 * @return {Roo.BasicDialog} this
14520 toBack : function(){
14521 Roo.DialogManager.sendToBack(this);
14526 * Centers this dialog in the viewport
14527 * @return {Roo.BasicDialog} this
14529 center : function(){
14530 var xy = this.el.getCenterXY(true);
14531 this.moveTo(xy[0], xy[1]);
14536 * Moves the dialog's top-left corner to the specified point
14537 * @param {Number} x
14538 * @param {Number} y
14539 * @return {Roo.BasicDialog} this
14541 moveTo : function(x, y){
14543 if(this.isVisible()){
14544 this.el.setXY(this.xy);
14545 this.adjustAssets();
14551 * Aligns the dialog to the specified element
14552 * @param {String/HTMLElement/Roo.Element} element The element to align to.
14553 * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details).
14554 * @param {Array} offsets (optional) Offset the positioning by [x, y]
14555 * @return {Roo.BasicDialog} this
14557 alignTo : function(element, position, offsets){
14558 this.xy = this.el.getAlignToXY(element, position, offsets);
14559 if(this.isVisible()){
14560 this.el.setXY(this.xy);
14561 this.adjustAssets();
14567 * Anchors an element to another element and realigns it when the window is resized.
14568 * @param {String/HTMLElement/Roo.Element} element The element to align to.
14569 * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details)
14570 * @param {Array} offsets (optional) Offset the positioning by [x, y]
14571 * @param {Boolean/Number} monitorScroll (optional) true to monitor body scroll and reposition. If this parameter
14572 * is a number, it is used as the buffer delay (defaults to 50ms).
14573 * @return {Roo.BasicDialog} this
14575 anchorTo : function(el, alignment, offsets, monitorScroll){
14576 var action = function(){
14577 this.alignTo(el, alignment, offsets);
14579 Roo.EventManager.onWindowResize(action, this);
14580 var tm = typeof monitorScroll;
14581 if(tm != 'undefined'){
14582 Roo.EventManager.on(window, 'scroll', action, this,
14583 {buffer: tm == 'number' ? monitorScroll : 50});
14590 * Returns true if the dialog is visible
14591 * @return {Boolean}
14593 isVisible : function(){
14594 return this.el.isVisible();
14598 animHide : function(callback){
14599 var b = Roo.get(this.animateTarget).getBox();
14601 this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height);
14603 this.proxy.setBounds(b.x, b.y, b.width, b.height, true, .35,
14604 this.hideEl.createDelegate(this, [callback]));
14608 * Hides the dialog.
14609 * @param {Function} callback (optional) Function to call when the dialog is hidden
14610 * @return {Roo.BasicDialog} this
14612 hide : function(callback){
14613 if (this.fireEvent("beforehide", this) === false){
14617 this.shadow.hide();
14622 // sometimes animateTarget seems to get set.. causing problems...
14623 // this just double checks..
14624 if(this.animateTarget && Roo.get(this.animateTarget)) {
14625 this.animHide(callback);
14628 this.hideEl(callback);
14634 hideEl : function(callback){
14638 Roo.get(document.body).removeClass("x-body-masked");
14640 this.fireEvent("hide", this);
14641 if(typeof callback == "function"){
14647 hideAction : function(){
14648 this.setLeft("-10000px");
14649 this.setTop("-10000px");
14650 this.setStyle("visibility", "hidden");
14654 refreshSize : function(){
14655 this.size = this.el.getSize();
14656 this.xy = this.el.getXY();
14657 Roo.state.Manager.set(this.stateId || this.el.id + "-state", this.el.getBox());
14661 // z-index is managed by the DialogManager and may be overwritten at any time
14662 setZIndex : function(index){
14664 this.mask.setStyle("z-index", index);
14667 this.shim.setStyle("z-index", ++index);
14670 this.shadow.setZIndex(++index);
14672 this.el.setStyle("z-index", ++index);
14674 this.proxy.setStyle("z-index", ++index);
14677 this.resizer.proxy.setStyle("z-index", ++index);
14680 this.lastZIndex = index;
14684 * Returns the element for this dialog
14685 * @return {Roo.Element} The underlying dialog Element
14687 getEl : function(){
14693 * @class Roo.DialogManager
14694 * Provides global access to BasicDialogs that have been created and
14695 * support for z-indexing (layering) multiple open dialogs.
14697 Roo.DialogManager = function(){
14699 var accessList = [];
14703 var sortDialogs = function(d1, d2){
14704 return (!d1._lastAccess || d1._lastAccess < d2._lastAccess) ? -1 : 1;
14708 var orderDialogs = function(){
14709 accessList.sort(sortDialogs);
14710 var seed = Roo.DialogManager.zseed;
14711 for(var i = 0, len = accessList.length; i < len; i++){
14712 var dlg = accessList[i];
14714 dlg.setZIndex(seed + (i*10));
14721 * The starting z-index for BasicDialogs (defaults to 9000)
14722 * @type Number The z-index value
14727 register : function(dlg){
14728 list[dlg.id] = dlg;
14729 accessList.push(dlg);
14733 unregister : function(dlg){
14734 delete list[dlg.id];
14737 if(!accessList.indexOf){
14738 for( i = 0, len = accessList.length; i < len; i++){
14739 if(accessList[i] == dlg){
14740 accessList.splice(i, 1);
14745 i = accessList.indexOf(dlg);
14747 accessList.splice(i, 1);
14753 * Gets a registered dialog by id
14754 * @param {String/Object} id The id of the dialog or a dialog
14755 * @return {Roo.BasicDialog} this
14757 get : function(id){
14758 return typeof id == "object" ? id : list[id];
14762 * Brings the specified dialog to the front
14763 * @param {String/Object} dlg The id of the dialog or a dialog
14764 * @return {Roo.BasicDialog} this
14766 bringToFront : function(dlg){
14767 dlg = this.get(dlg);
14770 dlg._lastAccess = new Date().getTime();
14777 * Sends the specified dialog to the back
14778 * @param {String/Object} dlg The id of the dialog or a dialog
14779 * @return {Roo.BasicDialog} this
14781 sendToBack : function(dlg){
14782 dlg = this.get(dlg);
14783 dlg._lastAccess = -(new Date().getTime());
14789 * Hides all dialogs
14791 hideAll : function(){
14792 for(var id in list){
14793 if(list[id] && typeof list[id] != "function" && list[id].isVisible()){
14802 * @class Roo.LayoutDialog
14803 * @extends Roo.BasicDialog
14804 * Dialog which provides adjustments for working with a layout in a Dialog.
14805 * Add your necessary layout config options to the dialog's config.<br>
14806 * Example usage (including a nested layout):
14809 dialog = new Roo.LayoutDialog("download-dlg", {
14818 // layout config merges with the dialog config
14820 tabPosition: "top",
14821 alwaysShowTabs: true
14824 dialog.addKeyListener(27, dialog.hide, dialog);
14825 dialog.setDefaultButton(dialog.addButton("Close", dialog.hide, dialog));
14826 dialog.addButton("Build It!", this.getDownload, this);
14828 // we can even add nested layouts
14829 var innerLayout = new Roo.BorderLayout("dl-inner", {
14839 innerLayout.beginUpdate();
14840 innerLayout.add("east", new Roo.ContentPanel("dl-details"));
14841 innerLayout.add("center", new Roo.ContentPanel("selection-panel"));
14842 innerLayout.endUpdate(true);
14844 var layout = dialog.getLayout();
14845 layout.beginUpdate();
14846 layout.add("center", new Roo.ContentPanel("standard-panel",
14847 {title: "Download the Source", fitToFrame:true}));
14848 layout.add("center", new Roo.NestedLayoutPanel(innerLayout,
14849 {title: "Build your own roo.js"}));
14850 layout.getRegion("center").showPanel(sp);
14851 layout.endUpdate();
14855 * @param {String/HTMLElement/Roo.Element} el The id of or container element, or config
14856 * @param {Object} config configuration options
14858 Roo.LayoutDialog = function(el, cfg){
14861 if (typeof(cfg) == 'undefined') {
14862 config = Roo.apply({}, el);
14863 // not sure why we use documentElement here.. - it should always be body.
14864 // IE7 borks horribly if we use documentElement.
14865 // webkit also does not like documentElement - it creates a body element...
14866 el = Roo.get( document.body || document.documentElement ).createChild();
14867 //config.autoCreate = true;
14871 config.autoTabs = false;
14872 Roo.LayoutDialog.superclass.constructor.call(this, el, config);
14873 this.body.setStyle({overflow:"hidden", position:"relative"});
14874 this.layout = new Roo.BorderLayout(this.body.dom, config);
14875 this.layout.monitorWindowResize = false;
14876 this.el.addClass("x-dlg-auto-layout");
14877 // fix case when center region overwrites center function
14878 this.center = Roo.BasicDialog.prototype.center;
14879 this.on("show", this.layout.layout, this.layout, true);
14880 if (config.items) {
14881 var xitems = config.items;
14882 delete config.items;
14883 Roo.each(xitems, this.addxtype, this);
14888 Roo.extend(Roo.LayoutDialog, Roo.BasicDialog, {
14890 * Ends update of the layout <strike>and resets display to none</strike>. Use standard beginUpdate/endUpdate on the layout.
14893 endUpdate : function(){
14894 this.layout.endUpdate();
14898 * Begins an update of the layout <strike>and sets display to block and visibility to hidden</strike>. Use standard beginUpdate/endUpdate on the layout.
14901 beginUpdate : function(){
14902 this.layout.beginUpdate();
14906 * Get the BorderLayout for this dialog
14907 * @return {Roo.BorderLayout}
14909 getLayout : function(){
14910 return this.layout;
14913 showEl : function(){
14914 Roo.LayoutDialog.superclass.showEl.apply(this, arguments);
14916 this.layout.layout();
14921 // Use the syncHeightBeforeShow config option to control this automatically
14922 syncBodyHeight : function(){
14923 Roo.LayoutDialog.superclass.syncBodyHeight.call(this);
14924 if(this.layout){this.layout.layout();}
14928 * Add an xtype element (actually adds to the layout.)
14929 * @return {Object} xdata xtype object data.
14932 addxtype : function(c) {
14933 return this.layout.addxtype(c);
14937 * Ext JS Library 1.1.1
14938 * Copyright(c) 2006-2007, Ext JS, LLC.
14940 * Originally Released Under LGPL - original licence link has changed is not relivant.
14943 * <script type="text/javascript">
14947 * @class Roo.MessageBox
14948 * Utility class for generating different styles of message boxes. The alias Roo.Msg can also be used.
14952 Roo.Msg.alert('Status', 'Changes saved successfully.');
14954 // Prompt for user data:
14955 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
14957 // process text value...
14961 // Show a dialog using config options:
14963 title:'Save Changes?',
14964 msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
14965 buttons: Roo.Msg.YESNOCANCEL,
14972 Roo.MessageBox = function(){
14973 var dlg, opt, mask, waitTimer;
14974 var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
14975 var buttons, activeTextEl, bwidth;
14978 var handleButton = function(button){
14980 Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
14984 var handleHide = function(){
14985 if(opt && opt.cls){
14986 dlg.el.removeClass(opt.cls);
14989 Roo.TaskMgr.stop(waitTimer);
14995 var updateButtons = function(b){
14998 buttons["ok"].hide();
14999 buttons["cancel"].hide();
15000 buttons["yes"].hide();
15001 buttons["no"].hide();
15002 dlg.footer.dom.style.display = 'none';
15005 dlg.footer.dom.style.display = '';
15006 for(var k in buttons){
15007 if(typeof buttons[k] != "function"){
15010 buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.MessageBox.buttonText[k]);
15011 width += buttons[k].el.getWidth()+15;
15021 var handleEsc = function(d, k, e){
15022 if(opt && opt.closable !== false){
15032 * Returns a reference to the underlying {@link Roo.BasicDialog} element
15033 * @return {Roo.BasicDialog} The BasicDialog element
15035 getDialog : function(){
15037 dlg = new Roo.BasicDialog("x-msg-box", {
15042 constraintoviewport:false,
15044 collapsible : false,
15047 width:400, height:100,
15048 buttonAlign:"center",
15049 closeClick : function(){
15050 if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
15051 handleButton("no");
15053 handleButton("cancel");
15057 dlg.on("hide", handleHide);
15059 dlg.addKeyListener(27, handleEsc);
15061 var bt = this.buttonText;
15062 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
15063 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
15064 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
15065 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
15066 bodyEl = dlg.body.createChild({
15068 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>'
15070 msgEl = bodyEl.dom.firstChild;
15071 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
15072 textboxEl.enableDisplayMode();
15073 textboxEl.addKeyListener([10,13], function(){
15074 if(dlg.isVisible() && opt && opt.buttons){
15075 if(opt.buttons.ok){
15076 handleButton("ok");
15077 }else if(opt.buttons.yes){
15078 handleButton("yes");
15082 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
15083 textareaEl.enableDisplayMode();
15084 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
15085 progressEl.enableDisplayMode();
15086 var pf = progressEl.dom.firstChild;
15088 pp = Roo.get(pf.firstChild);
15089 pp.setHeight(pf.offsetHeight);
15097 * Updates the message box body text
15098 * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
15099 * the XHTML-compliant non-breaking space character '&#160;')
15100 * @return {Roo.MessageBox} This message box
15102 updateText : function(text){
15103 if(!dlg.isVisible() && !opt.width){
15104 dlg.resizeTo(this.maxWidth, 100); // resize first so content is never clipped from previous shows
15106 msgEl.innerHTML = text || ' ';
15108 var cw = Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
15109 //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
15111 Math.min(opt.width || cw , this.maxWidth),
15112 Math.max(opt.minWidth || this.minWidth, bwidth)
15115 activeTextEl.setWidth(w);
15117 if(dlg.isVisible()){
15118 dlg.fixedcenter = false;
15120 // to big, make it scroll. = But as usual stupid IE does not support
15123 if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
15124 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
15125 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
15127 bodyEl.dom.style.height = '';
15128 bodyEl.dom.style.overflowY = '';
15131 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
15133 bodyEl.dom.style.overflowX = '';
15136 dlg.setContentSize(w, bodyEl.getHeight());
15137 if(dlg.isVisible()){
15138 dlg.fixedcenter = true;
15144 * Updates a progress-style message box's text and progress bar. Only relevant on message boxes
15145 * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
15146 * @param {Number} value Any number between 0 and 1 (e.g., .5)
15147 * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
15148 * @return {Roo.MessageBox} This message box
15150 updateProgress : function(value, text){
15152 this.updateText(text);
15154 if (pp) { // weird bug on my firefox - for some reason this is not defined
15155 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
15161 * Returns true if the message box is currently displayed
15162 * @return {Boolean} True if the message box is visible, else false
15164 isVisible : function(){
15165 return dlg && dlg.isVisible();
15169 * Hides the message box if it is displayed
15172 if(this.isVisible()){
15178 * Displays a new message box, or reinitializes an existing message box, based on the config options
15179 * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
15180 * The following config object properties are supported:
15182 Property Type Description
15183 ---------- --------------- ------------------------------------------------------------------------------------
15184 animEl String/Element An id or Element from which the message box should animate as it opens and
15185 closes (defaults to undefined)
15186 buttons Object/Boolean A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
15187 cancel:'Bar'}), or false to not show any buttons (defaults to false)
15188 closable Boolean False to hide the top-right close button (defaults to true). Note that
15189 progress and wait dialogs will ignore this property and always hide the
15190 close button as they can only be closed programmatically.
15191 cls String A custom CSS class to apply to the message box element
15192 defaultTextHeight Number The default height in pixels of the message box's multiline textarea if
15193 displayed (defaults to 75)
15194 fn Function A callback function to execute after closing the dialog. The arguments to the
15195 function will be btn (the name of the button that was clicked, if applicable,
15196 e.g. "ok"), and text (the value of the active text field, if applicable).
15197 Progress and wait dialogs will ignore this option since they do not respond to
15198 user actions and can only be closed programmatically, so any required function
15199 should be called by the same code after it closes the dialog.
15200 icon String A CSS class that provides a background image to be used as an icon for
15201 the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
15202 maxWidth Number The maximum width in pixels of the message box (defaults to 600)
15203 minWidth Number The minimum width in pixels of the message box (defaults to 100)
15204 modal Boolean False to allow user interaction with the page while the message box is
15205 displayed (defaults to true)
15206 msg String A string that will replace the existing message box body text (defaults
15207 to the XHTML-compliant non-breaking space character ' ')
15208 multiline Boolean True to prompt the user to enter multi-line text (defaults to false)
15209 progress Boolean True to display a progress bar (defaults to false)
15210 progressText String The text to display inside the progress bar if progress = true (defaults to '')
15211 prompt Boolean True to prompt the user to enter single-line text (defaults to false)
15212 proxyDrag Boolean True to display a lightweight proxy while dragging (defaults to false)
15213 title String The title text
15214 value String The string value to set into the active textbox element if displayed
15215 wait Boolean True to display a progress bar (defaults to false)
15216 width Number The width of the dialog in pixels
15223 msg: 'Please enter your address:',
15225 buttons: Roo.MessageBox.OKCANCEL,
15228 animEl: 'addAddressBtn'
15231 * @param {Object} config Configuration options
15232 * @return {Roo.MessageBox} This message box
15234 show : function(options)
15237 // this causes nightmares if you show one dialog after another
15238 // especially on callbacks..
15240 if(this.isVisible()){
15243 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
15244 Roo.log("Old Dialog Message:" + msgEl.innerHTML );
15245 Roo.log("New Dialog Message:" + options.msg )
15246 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
15247 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
15250 var d = this.getDialog();
15252 d.setTitle(opt.title || " ");
15253 d.close.setDisplayed(opt.closable !== false);
15254 activeTextEl = textboxEl;
15255 opt.prompt = opt.prompt || (opt.multiline ? true : false);
15260 textareaEl.setHeight(typeof opt.multiline == "number" ?
15261 opt.multiline : this.defaultTextHeight);
15262 activeTextEl = textareaEl;
15271 progressEl.setDisplayed(opt.progress === true);
15272 this.updateProgress(0);
15273 activeTextEl.dom.value = opt.value || "";
15275 dlg.setDefaultButton(activeTextEl);
15277 var bs = opt.buttons;
15280 db = buttons["ok"];
15281 }else if(bs && bs.yes){
15282 db = buttons["yes"];
15284 dlg.setDefaultButton(db);
15286 bwidth = updateButtons(opt.buttons);
15287 this.updateText(opt.msg);
15289 d.el.addClass(opt.cls);
15291 d.proxyDrag = opt.proxyDrag === true;
15292 d.modal = opt.modal !== false;
15293 d.mask = opt.modal !== false ? mask : false;
15294 if(!d.isVisible()){
15295 // force it to the end of the z-index stack so it gets a cursor in FF
15296 document.body.appendChild(dlg.el.dom);
15297 d.animateTarget = null;
15298 d.show(options.animEl);
15304 * Displays a message box with a progress bar. This message box has no buttons and is not closeable by
15305 * the user. You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
15306 * and closing the message box when the process is complete.
15307 * @param {String} title The title bar text
15308 * @param {String} msg The message box body text
15309 * @return {Roo.MessageBox} This message box
15311 progress : function(title, msg){
15318 minWidth: this.minProgressWidth,
15325 * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
15326 * If a callback function is passed it will be called after the user clicks the button, and the
15327 * id of the button that was clicked will be passed as the only parameter to the callback
15328 * (could also be the top-right close button).
15329 * @param {String} title The title bar text
15330 * @param {String} msg The message box body text
15331 * @param {Function} fn (optional) The callback function invoked after the message box is closed
15332 * @param {Object} scope (optional) The scope of the callback function
15333 * @return {Roo.MessageBox} This message box
15335 alert : function(title, msg, fn, scope){
15348 * Displays a message box with an infinitely auto-updating progress bar. This can be used to block user
15349 * interaction while waiting for a long-running process to complete that does not have defined intervals.
15350 * You are responsible for closing the message box when the process is complete.
15351 * @param {String} msg The message box body text
15352 * @param {String} title (optional) The title bar text
15353 * @return {Roo.MessageBox} This message box
15355 wait : function(msg, title){
15366 waitTimer = Roo.TaskMgr.start({
15368 Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
15376 * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
15377 * If a callback function is passed it will be called after the user clicks either button, and the id of the
15378 * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
15379 * @param {String} title The title bar text
15380 * @param {String} msg The message box body text
15381 * @param {Function} fn (optional) The callback function invoked after the message box is closed
15382 * @param {Object} scope (optional) The scope of the callback function
15383 * @return {Roo.MessageBox} This message box
15385 confirm : function(title, msg, fn, scope){
15389 buttons: this.YESNO,
15398 * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
15399 * JavaScript's Window.prompt). The prompt can be a single-line or multi-line textbox. If a callback function
15400 * is passed it will be called after the user clicks either button, and the id of the button that was clicked
15401 * (could also be the top-right close button) and the text that was entered will be passed as the two
15402 * parameters to the callback.
15403 * @param {String} title The title bar text
15404 * @param {String} msg The message box body text
15405 * @param {Function} fn (optional) The callback function invoked after the message box is closed
15406 * @param {Object} scope (optional) The scope of the callback function
15407 * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
15408 * property, or the height in pixels to create the textbox (defaults to false / single-line)
15409 * @return {Roo.MessageBox} This message box
15411 prompt : function(title, msg, fn, scope, multiline){
15415 buttons: this.OKCANCEL,
15420 multiline: multiline,
15427 * Button config that displays a single OK button
15432 * Button config that displays Yes and No buttons
15435 YESNO : {yes:true, no:true},
15437 * Button config that displays OK and Cancel buttons
15440 OKCANCEL : {ok:true, cancel:true},
15442 * Button config that displays Yes, No and Cancel buttons
15445 YESNOCANCEL : {yes:true, no:true, cancel:true},
15448 * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
15451 defaultTextHeight : 75,
15453 * The maximum width in pixels of the message box (defaults to 600)
15458 * The minimum width in pixels of the message box (defaults to 100)
15463 * The minimum width in pixels of the message box if it is a progress-style dialog. This is useful
15464 * for setting a different minimum width than text-only dialogs may need (defaults to 250)
15467 minProgressWidth : 250,
15469 * An object containing the default button text strings that can be overriden for localized language support.
15470 * Supported properties are: ok, cancel, yes and no.
15471 * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
15484 * Shorthand for {@link Roo.MessageBox}
15486 Roo.Msg = Roo.MessageBox;/*
15488 * Ext JS Library 1.1.1
15489 * Copyright(c) 2006-2007, Ext JS, LLC.
15491 * Originally Released Under LGPL - original licence link has changed is not relivant.
15494 * <script type="text/javascript">
15497 * @class Roo.QuickTips
15498 * Provides attractive and customizable tooltips for any element.
15501 Roo.QuickTips = function(){
15502 var el, tipBody, tipBodyText, tipTitle, tm, cfg, close, tagEls = {}, esc, removeCls = null, bdLeft, bdRight;
15503 var ce, bd, xy, dd;
15504 var visible = false, disabled = true, inited = false;
15505 var showProc = 1, hideProc = 1, dismissProc = 1, locks = [];
15507 var onOver = function(e){
15511 var t = e.getTarget();
15512 if(!t || t.nodeType !== 1 || t == document || t == document.body){
15515 if(ce && t == ce.el){
15516 clearTimeout(hideProc);
15519 if(t && tagEls[t.id]){
15520 tagEls[t.id].el = t;
15521 showProc = show.defer(tm.showDelay, tm, [tagEls[t.id]]);
15524 var ttp, et = Roo.fly(t);
15525 var ns = cfg.namespace;
15526 if(tm.interceptTitles && t.title){
15529 t.removeAttribute("title");
15530 e.preventDefault();
15532 ttp = t.qtip || et.getAttributeNS(ns, cfg.attribute);
15535 showProc = show.defer(tm.showDelay, tm, [{
15538 width: et.getAttributeNS(ns, cfg.width),
15539 autoHide: et.getAttributeNS(ns, cfg.hide) != "user",
15540 title: et.getAttributeNS(ns, cfg.title),
15541 cls: et.getAttributeNS(ns, cfg.cls)
15546 var onOut = function(e){
15547 clearTimeout(showProc);
15548 var t = e.getTarget();
15549 if(t && ce && ce.el == t && (tm.autoHide && ce.autoHide !== false)){
15550 hideProc = setTimeout(hide, tm.hideDelay);
15554 var onMove = function(e){
15560 if(tm.trackMouse && ce){
15565 var onDown = function(e){
15566 clearTimeout(showProc);
15567 clearTimeout(hideProc);
15569 if(tm.hideOnClick){
15572 tm.enable.defer(100, tm);
15577 var getPad = function(){
15578 return 2;//bdLeft.getPadding('l')+bdRight.getPadding('r');
15581 var show = function(o){
15585 clearTimeout(dismissProc);
15587 if(removeCls){ // in case manually hidden
15588 el.removeClass(removeCls);
15592 el.addClass(ce.cls);
15593 removeCls = ce.cls;
15596 tipTitle.update(ce.title);
15599 tipTitle.update('');
15602 el.dom.style.width = tm.maxWidth+'px';
15603 //tipBody.dom.style.width = '';
15604 tipBodyText.update(o.text);
15605 var p = getPad(), w = ce.width;
15607 var td = tipBodyText.dom;
15608 var aw = Math.max(td.offsetWidth, td.clientWidth, td.scrollWidth);
15609 if(aw > tm.maxWidth){
15611 }else if(aw < tm.minWidth){
15617 //tipBody.setWidth(w);
15618 el.setWidth(parseInt(w, 10) + p);
15619 if(ce.autoHide === false){
15620 close.setDisplayed(true);
15625 close.setDisplayed(false);
15631 el.avoidY = xy[1]-18;
15636 el.setStyle("visibility", "visible");
15637 el.fadeIn({callback: afterShow});
15643 var afterShow = function(){
15647 if(tm.autoDismiss && ce.autoHide !== false){
15648 dismissProc = setTimeout(hide, tm.autoDismissDelay);
15653 var hide = function(noanim){
15654 clearTimeout(dismissProc);
15655 clearTimeout(hideProc);
15657 if(el.isVisible()){
15659 if(noanim !== true && tm.animate){
15660 el.fadeOut({callback: afterHide});
15667 var afterHide = function(){
15670 el.removeClass(removeCls);
15677 * @cfg {Number} minWidth
15678 * The minimum width of the quick tip (defaults to 40)
15682 * @cfg {Number} maxWidth
15683 * The maximum width of the quick tip (defaults to 300)
15687 * @cfg {Boolean} interceptTitles
15688 * True to automatically use the element's DOM title value if available (defaults to false)
15690 interceptTitles : false,
15692 * @cfg {Boolean} trackMouse
15693 * True to have the quick tip follow the mouse as it moves over the target element (defaults to false)
15695 trackMouse : false,
15697 * @cfg {Boolean} hideOnClick
15698 * True to hide the quick tip if the user clicks anywhere in the document (defaults to true)
15700 hideOnClick : true,
15702 * @cfg {Number} showDelay
15703 * Delay in milliseconds before the quick tip displays after the mouse enters the target element (defaults to 500)
15707 * @cfg {Number} hideDelay
15708 * Delay in milliseconds before the quick tip hides when autoHide = true (defaults to 200)
15712 * @cfg {Boolean} autoHide
15713 * True to automatically hide the quick tip after the mouse exits the target element (defaults to true).
15714 * Used in conjunction with hideDelay.
15719 * True to automatically hide the quick tip after a set period of time, regardless of the user's actions
15720 * (defaults to true). Used in conjunction with autoDismissDelay.
15722 autoDismiss : true,
15725 * Delay in milliseconds before the quick tip hides when autoDismiss = true (defaults to 5000)
15727 autoDismissDelay : 5000,
15729 * @cfg {Boolean} animate
15730 * True to turn on fade animation. Defaults to false (ClearType/scrollbar flicker issues in IE7).
15735 * @cfg {String} title
15736 * Title text to display (defaults to ''). This can be any valid HTML markup.
15740 * @cfg {String} text
15741 * Body text to display (defaults to ''). This can be any valid HTML markup.
15745 * @cfg {String} cls
15746 * A CSS class to apply to the base quick tip element (defaults to '').
15750 * @cfg {Number} width
15751 * Width in pixels of the quick tip (defaults to auto). Width will be ignored if it exceeds the bounds of
15752 * minWidth or maxWidth.
15757 * Initialize and enable QuickTips for first use. This should be called once before the first attempt to access
15758 * or display QuickTips in a page.
15761 tm = Roo.QuickTips;
15762 cfg = tm.tagConfig;
15764 if(!Roo.isReady){ // allow calling of init() before onReady
15765 Roo.onReady(Roo.QuickTips.init, Roo.QuickTips);
15768 el = new Roo.Layer({cls:"x-tip", shadow:"drop", shim: true, constrain:true, shadowOffset:4});
15769 el.fxDefaults = {stopFx: true};
15770 // maximum custom styling
15771 //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>');
15772 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>');
15773 tipTitle = el.child('h3');
15774 tipTitle.enableDisplayMode("block");
15775 tipBody = el.child('div.x-tip-bd');
15776 tipBodyText = el.child('div.x-tip-bd-inner');
15777 //bdLeft = el.child('div.x-tip-bd-left');
15778 //bdRight = el.child('div.x-tip-bd-right');
15779 close = el.child('div.x-tip-close');
15780 close.enableDisplayMode("block");
15781 close.on("click", hide);
15782 var d = Roo.get(document);
15783 d.on("mousedown", onDown);
15784 d.on("mouseover", onOver);
15785 d.on("mouseout", onOut);
15786 d.on("mousemove", onMove);
15787 esc = d.addKeyListener(27, hide);
15790 dd = el.initDD("default", null, {
15791 onDrag : function(){
15795 dd.setHandleElId(tipTitle.id);
15804 * Configures a new quick tip instance and assigns it to a target element. The following config options
15807 Property Type Description
15808 ---------- --------------------- ------------------------------------------------------------------------
15809 target Element/String/Array An Element, id or array of ids that this quick tip should be tied to
15811 * @param {Object} config The config object
15813 register : function(config){
15814 var cs = config instanceof Array ? config : arguments;
15815 for(var i = 0, len = cs.length; i < len; i++) {
15817 var target = c.target;
15819 if(target instanceof Array){
15820 for(var j = 0, jlen = target.length; j < jlen; j++){
15821 tagEls[target[j]] = c;
15824 tagEls[typeof target == 'string' ? target : Roo.id(target)] = c;
15831 * Removes this quick tip from its element and destroys it.
15832 * @param {String/HTMLElement/Element} el The element from which the quick tip is to be removed.
15834 unregister : function(el){
15835 delete tagEls[Roo.id(el)];
15839 * Enable this quick tip.
15841 enable : function(){
15842 if(inited && disabled){
15844 if(locks.length < 1){
15851 * Disable this quick tip.
15853 disable : function(){
15855 clearTimeout(showProc);
15856 clearTimeout(hideProc);
15857 clearTimeout(dismissProc);
15865 * Returns true if the quick tip is enabled, else false.
15867 isEnabled : function(){
15874 attribute : "qtip",
15884 // backwards compat
15885 Roo.QuickTips.tips = Roo.QuickTips.register;/*
15887 * Ext JS Library 1.1.1
15888 * Copyright(c) 2006-2007, Ext JS, LLC.
15890 * Originally Released Under LGPL - original licence link has changed is not relivant.
15893 * <script type="text/javascript">
15898 * @class Roo.tree.TreePanel
15899 * @extends Roo.data.Tree
15901 * @cfg {Boolean} rootVisible false to hide the root node (defaults to true)
15902 * @cfg {Boolean} lines false to disable tree lines (defaults to true)
15903 * @cfg {Boolean} enableDD true to enable drag and drop
15904 * @cfg {Boolean} enableDrag true to enable just drag
15905 * @cfg {Boolean} enableDrop true to enable just drop
15906 * @cfg {Object} dragConfig Custom config to pass to the {@link Roo.tree.TreeDragZone} instance
15907 * @cfg {Object} dropConfig Custom config to pass to the {@link Roo.tree.TreeDropZone} instance
15908 * @cfg {String} ddGroup The DD group this TreePanel belongs to
15909 * @cfg {String} ddAppendOnly True if the tree should only allow append drops (use for trees which are sorted)
15910 * @cfg {Boolean} ddScroll true to enable YUI body scrolling
15911 * @cfg {Boolean} containerScroll true to register this container with ScrollManager
15912 * @cfg {Boolean} hlDrop false to disable node highlight on drop (defaults to the value of Roo.enableFx)
15913 * @cfg {String} hlColor The color of the node highlight (defaults to C3DAF9)
15914 * @cfg {Boolean} animate true to enable animated expand/collapse (defaults to the value of Roo.enableFx)
15915 * @cfg {Boolean} singleExpand true if only 1 node per branch may be expanded
15916 * @cfg {Boolean} selModel A tree selection model to use with this TreePanel (defaults to a {@link Roo.tree.DefaultSelectionModel})
15917 * @cfg {Boolean} loader A TreeLoader for use with this TreePanel
15918 * @cfg {Object|Roo.tree.TreeEditor} editor The TreeEditor or xtype data to display when clicked.
15919 * @cfg {String} pathSeparator The token used to separate sub-paths in path strings (defaults to '/')
15920 * @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>
15921 * @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>
15924 * @param {String/HTMLElement/Element} el The container element
15925 * @param {Object} config
15927 Roo.tree.TreePanel = function(el, config){
15929 var loader = false;
15931 root = config.root;
15932 delete config.root;
15934 if (config.loader) {
15935 loader = config.loader;
15936 delete config.loader;
15939 Roo.apply(this, config);
15940 Roo.tree.TreePanel.superclass.constructor.call(this);
15941 this.el = Roo.get(el);
15942 this.el.addClass('x-tree');
15943 //console.log(root);
15945 this.setRootNode( Roo.factory(root, Roo.tree));
15948 this.loader = Roo.factory(loader, Roo.tree);
15951 * Read-only. The id of the container element becomes this TreePanel's id.
15953 this.id = this.el.id;
15956 * @event beforeload
15957 * Fires before a node is loaded, return false to cancel
15958 * @param {Node} node The node being loaded
15960 "beforeload" : true,
15963 * Fires when a node is loaded
15964 * @param {Node} node The node that was loaded
15968 * @event textchange
15969 * Fires when the text for a node is changed
15970 * @param {Node} node The node
15971 * @param {String} text The new text
15972 * @param {String} oldText The old text
15974 "textchange" : true,
15976 * @event beforeexpand
15977 * Fires before a node is expanded, return false to cancel.
15978 * @param {Node} node The node
15979 * @param {Boolean} deep
15980 * @param {Boolean} anim
15982 "beforeexpand" : true,
15984 * @event beforecollapse
15985 * Fires before a node is collapsed, return false to cancel.
15986 * @param {Node} node The node
15987 * @param {Boolean} deep
15988 * @param {Boolean} anim
15990 "beforecollapse" : true,
15993 * Fires when a node is expanded
15994 * @param {Node} node The node
15998 * @event disabledchange
15999 * Fires when the disabled status of a node changes
16000 * @param {Node} node The node
16001 * @param {Boolean} disabled
16003 "disabledchange" : true,
16006 * Fires when a node is collapsed
16007 * @param {Node} node The node
16011 * @event beforeclick
16012 * Fires before click processing on a node. Return false to cancel the default action.
16013 * @param {Node} node The node
16014 * @param {Roo.EventObject} e The event object
16016 "beforeclick":true,
16018 * @event checkchange
16019 * Fires when a node with a checkbox's checked property changes
16020 * @param {Node} this This node
16021 * @param {Boolean} checked
16023 "checkchange":true,
16026 * Fires when a node is clicked
16027 * @param {Node} node The node
16028 * @param {Roo.EventObject} e The event object
16033 * Fires when a node is double clicked
16034 * @param {Node} node The node
16035 * @param {Roo.EventObject} e The event object
16039 * @event contextmenu
16040 * Fires when a node is right clicked
16041 * @param {Node} node The node
16042 * @param {Roo.EventObject} e The event object
16044 "contextmenu":true,
16046 * @event beforechildrenrendered
16047 * Fires right before the child nodes for a node are rendered
16048 * @param {Node} node The node
16050 "beforechildrenrendered":true,
16053 * Fires when a node starts being dragged
16054 * @param {Roo.tree.TreePanel} this
16055 * @param {Roo.tree.TreeNode} node
16056 * @param {event} e The raw browser event
16058 "startdrag" : true,
16061 * Fires when a drag operation is complete
16062 * @param {Roo.tree.TreePanel} this
16063 * @param {Roo.tree.TreeNode} node
16064 * @param {event} e The raw browser event
16069 * Fires when a dragged node is dropped on a valid DD target
16070 * @param {Roo.tree.TreePanel} this
16071 * @param {Roo.tree.TreeNode} node
16072 * @param {DD} dd The dd it was dropped on
16073 * @param {event} e The raw browser event
16077 * @event beforenodedrop
16078 * Fires when a DD object is dropped on a node in this tree for preprocessing. Return false to cancel the drop. The dropEvent
16079 * passed to handlers has the following properties:<br />
16080 * <ul style="padding:5px;padding-left:16px;">
16081 * <li>tree - The TreePanel</li>
16082 * <li>target - The node being targeted for the drop</li>
16083 * <li>data - The drag data from the drag source</li>
16084 * <li>point - The point of the drop - append, above or below</li>
16085 * <li>source - The drag source</li>
16086 * <li>rawEvent - Raw mouse event</li>
16087 * <li>dropNode - Drop node(s) provided by the source <b>OR</b> you can supply node(s)
16088 * to be inserted by setting them on this object.</li>
16089 * <li>cancel - Set this to true to cancel the drop.</li>
16091 * @param {Object} dropEvent
16093 "beforenodedrop" : true,
16096 * Fires after a DD object is dropped on a node in this tree. The dropEvent
16097 * passed to handlers has the following properties:<br />
16098 * <ul style="padding:5px;padding-left:16px;">
16099 * <li>tree - The TreePanel</li>
16100 * <li>target - The node being targeted for the drop</li>
16101 * <li>data - The drag data from the drag source</li>
16102 * <li>point - The point of the drop - append, above or below</li>
16103 * <li>source - The drag source</li>
16104 * <li>rawEvent - Raw mouse event</li>
16105 * <li>dropNode - Dropped node(s).</li>
16107 * @param {Object} dropEvent
16111 * @event nodedragover
16112 * Fires when a tree node is being targeted for a drag drop, return false to signal drop not allowed. The dragOverEvent
16113 * passed to handlers has the following properties:<br />
16114 * <ul style="padding:5px;padding-left:16px;">
16115 * <li>tree - The TreePanel</li>
16116 * <li>target - The node being targeted for the drop</li>
16117 * <li>data - The drag data from the drag source</li>
16118 * <li>point - The point of the drop - append, above or below</li>
16119 * <li>source - The drag source</li>
16120 * <li>rawEvent - Raw mouse event</li>
16121 * <li>dropNode - Drop node(s) provided by the source.</li>
16122 * <li>cancel - Set this to true to signal drop not allowed.</li>
16124 * @param {Object} dragOverEvent
16126 "nodedragover" : true
16129 if(this.singleExpand){
16130 this.on("beforeexpand", this.restrictExpand, this);
16133 this.editor.tree = this;
16134 this.editor = Roo.factory(this.editor, Roo.tree);
16137 if (this.selModel) {
16138 this.selModel = Roo.factory(this.selModel, Roo.tree);
16142 Roo.extend(Roo.tree.TreePanel, Roo.data.Tree, {
16143 rootVisible : true,
16144 animate: Roo.enableFx,
16147 hlDrop : Roo.enableFx,
16151 rendererTip: false,
16153 restrictExpand : function(node){
16154 var p = node.parentNode;
16156 if(p.expandedChild && p.expandedChild.parentNode == p){
16157 p.expandedChild.collapse();
16159 p.expandedChild = node;
16163 // private override
16164 setRootNode : function(node){
16165 Roo.tree.TreePanel.superclass.setRootNode.call(this, node);
16166 if(!this.rootVisible){
16167 node.ui = new Roo.tree.RootTreeNodeUI(node);
16173 * Returns the container element for this TreePanel
16175 getEl : function(){
16180 * Returns the default TreeLoader for this TreePanel
16182 getLoader : function(){
16183 return this.loader;
16189 expandAll : function(){
16190 this.root.expand(true);
16194 * Collapse all nodes
16196 collapseAll : function(){
16197 this.root.collapse(true);
16201 * Returns the selection model used by this TreePanel
16203 getSelectionModel : function(){
16204 if(!this.selModel){
16205 this.selModel = new Roo.tree.DefaultSelectionModel();
16207 return this.selModel;
16211 * Retrieve an array of checked nodes, or an array of a specific attribute of checked nodes (e.g. "id")
16212 * @param {String} attribute (optional) Defaults to null (return the actual nodes)
16213 * @param {TreeNode} startNode (optional) The node to start from, defaults to the root
16216 getChecked : function(a, startNode){
16217 startNode = startNode || this.root;
16219 var f = function(){
16220 if(this.attributes.checked){
16221 r.push(!a ? this : (a == 'id' ? this.id : this.attributes[a]));
16224 startNode.cascade(f);
16229 * Expands a specified path in this TreePanel. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
16230 * @param {String} path
16231 * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
16232 * @param {Function} callback (optional) The callback to call when the expand is complete. The callback will be called with
16233 * (bSuccess, oLastNode) where bSuccess is if the expand was successful and oLastNode is the last node that was expanded.
16235 expandPath : function(path, attr, callback){
16236 attr = attr || "id";
16237 var keys = path.split(this.pathSeparator);
16238 var curNode = this.root;
16239 if(curNode.attributes[attr] != keys[1]){ // invalid root
16241 callback(false, null);
16246 var f = function(){
16247 if(++index == keys.length){
16249 callback(true, curNode);
16253 var c = curNode.findChild(attr, keys[index]);
16256 callback(false, curNode);
16261 c.expand(false, false, f);
16263 curNode.expand(false, false, f);
16267 * Selects the node in this tree at the specified path. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
16268 * @param {String} path
16269 * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
16270 * @param {Function} callback (optional) The callback to call when the selection is complete. The callback will be called with
16271 * (bSuccess, oSelNode) where bSuccess is if the selection was successful and oSelNode is the selected node.
16273 selectPath : function(path, attr, callback){
16274 attr = attr || "id";
16275 var keys = path.split(this.pathSeparator);
16276 var v = keys.pop();
16277 if(keys.length > 0){
16278 var f = function(success, node){
16279 if(success && node){
16280 var n = node.findChild(attr, v);
16286 }else if(callback){
16287 callback(false, n);
16291 callback(false, n);
16295 this.expandPath(keys.join(this.pathSeparator), attr, f);
16297 this.root.select();
16299 callback(true, this.root);
16304 getTreeEl : function(){
16309 * Trigger rendering of this TreePanel
16311 render : function(){
16312 if (this.innerCt) {
16313 return this; // stop it rendering more than once!!
16316 this.innerCt = this.el.createChild({tag:"ul",
16317 cls:"x-tree-root-ct " +
16318 (this.lines ? "x-tree-lines" : "x-tree-no-lines")});
16320 if(this.containerScroll){
16321 Roo.dd.ScrollManager.register(this.el);
16323 if((this.enableDD || this.enableDrop) && !this.dropZone){
16325 * The dropZone used by this tree if drop is enabled
16326 * @type Roo.tree.TreeDropZone
16328 this.dropZone = new Roo.tree.TreeDropZone(this, this.dropConfig || {
16329 ddGroup: this.ddGroup || "TreeDD", appendOnly: this.ddAppendOnly === true
16332 if((this.enableDD || this.enableDrag) && !this.dragZone){
16334 * The dragZone used by this tree if drag is enabled
16335 * @type Roo.tree.TreeDragZone
16337 this.dragZone = new Roo.tree.TreeDragZone(this, this.dragConfig || {
16338 ddGroup: this.ddGroup || "TreeDD",
16339 scroll: this.ddScroll
16342 this.getSelectionModel().init(this);
16344 Roo.log("ROOT not set in tree");
16347 this.root.render();
16348 if(!this.rootVisible){
16349 this.root.renderChildren();
16355 * Ext JS Library 1.1.1
16356 * Copyright(c) 2006-2007, Ext JS, LLC.
16358 * Originally Released Under LGPL - original licence link has changed is not relivant.
16361 * <script type="text/javascript">
16366 * @class Roo.tree.DefaultSelectionModel
16367 * @extends Roo.util.Observable
16368 * The default single selection for a TreePanel.
16369 * @param {Object} cfg Configuration
16371 Roo.tree.DefaultSelectionModel = function(cfg){
16372 this.selNode = null;
16378 * @event selectionchange
16379 * Fires when the selected node changes
16380 * @param {DefaultSelectionModel} this
16381 * @param {TreeNode} node the new selection
16383 "selectionchange" : true,
16386 * @event beforeselect
16387 * Fires before the selected node changes, return false to cancel the change
16388 * @param {DefaultSelectionModel} this
16389 * @param {TreeNode} node the new selection
16390 * @param {TreeNode} node the old selection
16392 "beforeselect" : true
16395 Roo.tree.DefaultSelectionModel.superclass.constructor.call(this,cfg);
16398 Roo.extend(Roo.tree.DefaultSelectionModel, Roo.util.Observable, {
16399 init : function(tree){
16401 tree.getTreeEl().on("keydown", this.onKeyDown, this);
16402 tree.on("click", this.onNodeClick, this);
16405 onNodeClick : function(node, e){
16406 if (e.ctrlKey && this.selNode == node) {
16407 this.unselect(node);
16415 * @param {TreeNode} node The node to select
16416 * @return {TreeNode} The selected node
16418 select : function(node){
16419 var last = this.selNode;
16420 if(last != node && this.fireEvent('beforeselect', this, node, last) !== false){
16422 last.ui.onSelectedChange(false);
16424 this.selNode = node;
16425 node.ui.onSelectedChange(true);
16426 this.fireEvent("selectionchange", this, node, last);
16433 * @param {TreeNode} node The node to unselect
16435 unselect : function(node){
16436 if(this.selNode == node){
16437 this.clearSelections();
16442 * Clear all selections
16444 clearSelections : function(){
16445 var n = this.selNode;
16447 n.ui.onSelectedChange(false);
16448 this.selNode = null;
16449 this.fireEvent("selectionchange", this, null);
16455 * Get the selected node
16456 * @return {TreeNode} The selected node
16458 getSelectedNode : function(){
16459 return this.selNode;
16463 * Returns true if the node is selected
16464 * @param {TreeNode} node The node to check
16465 * @return {Boolean}
16467 isSelected : function(node){
16468 return this.selNode == node;
16472 * Selects the node above the selected node in the tree, intelligently walking the nodes
16473 * @return TreeNode The new selection
16475 selectPrevious : function(){
16476 var s = this.selNode || this.lastSelNode;
16480 var ps = s.previousSibling;
16482 if(!ps.isExpanded() || ps.childNodes.length < 1){
16483 return this.select(ps);
16485 var lc = ps.lastChild;
16486 while(lc && lc.isExpanded() && lc.childNodes.length > 0){
16489 return this.select(lc);
16491 } else if(s.parentNode && (this.tree.rootVisible || !s.parentNode.isRoot)){
16492 return this.select(s.parentNode);
16498 * Selects the node above the selected node in the tree, intelligently walking the nodes
16499 * @return TreeNode The new selection
16501 selectNext : function(){
16502 var s = this.selNode || this.lastSelNode;
16506 if(s.firstChild && s.isExpanded()){
16507 return this.select(s.firstChild);
16508 }else if(s.nextSibling){
16509 return this.select(s.nextSibling);
16510 }else if(s.parentNode){
16512 s.parentNode.bubble(function(){
16513 if(this.nextSibling){
16514 newS = this.getOwnerTree().selModel.select(this.nextSibling);
16523 onKeyDown : function(e){
16524 var s = this.selNode || this.lastSelNode;
16525 // undesirable, but required
16530 var k = e.getKey();
16538 this.selectPrevious();
16541 e.preventDefault();
16542 if(s.hasChildNodes()){
16543 if(!s.isExpanded()){
16545 }else if(s.firstChild){
16546 this.select(s.firstChild, e);
16551 e.preventDefault();
16552 if(s.hasChildNodes() && s.isExpanded()){
16554 }else if(s.parentNode && (this.tree.rootVisible || s.parentNode != this.tree.getRootNode())){
16555 this.select(s.parentNode, e);
16563 * @class Roo.tree.MultiSelectionModel
16564 * @extends Roo.util.Observable
16565 * Multi selection for a TreePanel.
16566 * @param {Object} cfg Configuration
16568 Roo.tree.MultiSelectionModel = function(){
16569 this.selNodes = [];
16573 * @event selectionchange
16574 * Fires when the selected nodes change
16575 * @param {MultiSelectionModel} this
16576 * @param {Array} nodes Array of the selected nodes
16578 "selectionchange" : true
16580 Roo.tree.MultiSelectionModel.superclass.constructor.call(this,cfg);
16584 Roo.extend(Roo.tree.MultiSelectionModel, Roo.util.Observable, {
16585 init : function(tree){
16587 tree.getTreeEl().on("keydown", this.onKeyDown, this);
16588 tree.on("click", this.onNodeClick, this);
16591 onNodeClick : function(node, e){
16592 this.select(node, e, e.ctrlKey);
16597 * @param {TreeNode} node The node to select
16598 * @param {EventObject} e (optional) An event associated with the selection
16599 * @param {Boolean} keepExisting True to retain existing selections
16600 * @return {TreeNode} The selected node
16602 select : function(node, e, keepExisting){
16603 if(keepExisting !== true){
16604 this.clearSelections(true);
16606 if(this.isSelected(node)){
16607 this.lastSelNode = node;
16610 this.selNodes.push(node);
16611 this.selMap[node.id] = node;
16612 this.lastSelNode = node;
16613 node.ui.onSelectedChange(true);
16614 this.fireEvent("selectionchange", this, this.selNodes);
16620 * @param {TreeNode} node The node to unselect
16622 unselect : function(node){
16623 if(this.selMap[node.id]){
16624 node.ui.onSelectedChange(false);
16625 var sn = this.selNodes;
16628 index = sn.indexOf(node);
16630 for(var i = 0, len = sn.length; i < len; i++){
16638 this.selNodes.splice(index, 1);
16640 delete this.selMap[node.id];
16641 this.fireEvent("selectionchange", this, this.selNodes);
16646 * Clear all selections
16648 clearSelections : function(suppressEvent){
16649 var sn = this.selNodes;
16651 for(var i = 0, len = sn.length; i < len; i++){
16652 sn[i].ui.onSelectedChange(false);
16654 this.selNodes = [];
16656 if(suppressEvent !== true){
16657 this.fireEvent("selectionchange", this, this.selNodes);
16663 * Returns true if the node is selected
16664 * @param {TreeNode} node The node to check
16665 * @return {Boolean}
16667 isSelected : function(node){
16668 return this.selMap[node.id] ? true : false;
16672 * Returns an array of the selected nodes
16675 getSelectedNodes : function(){
16676 return this.selNodes;
16679 onKeyDown : Roo.tree.DefaultSelectionModel.prototype.onKeyDown,
16681 selectNext : Roo.tree.DefaultSelectionModel.prototype.selectNext,
16683 selectPrevious : Roo.tree.DefaultSelectionModel.prototype.selectPrevious
16686 * Ext JS Library 1.1.1
16687 * Copyright(c) 2006-2007, Ext JS, LLC.
16689 * Originally Released Under LGPL - original licence link has changed is not relivant.
16692 * <script type="text/javascript">
16696 * @class Roo.tree.TreeNode
16697 * @extends Roo.data.Node
16698 * @cfg {String} text The text for this node
16699 * @cfg {Boolean} expanded true to start the node expanded
16700 * @cfg {Boolean} allowDrag false to make this node undraggable if DD is on (defaults to true)
16701 * @cfg {Boolean} allowDrop false if this node cannot be drop on
16702 * @cfg {Boolean} disabled true to start the node disabled
16703 * @cfg {String} icon The path to an icon for the node. The preferred way to do this
16704 * is to use the cls or iconCls attributes and add the icon via a CSS background image.
16705 * @cfg {String} cls A css class to be added to the node
16706 * @cfg {String} iconCls A css class to be added to the nodes icon element for applying css background images
16707 * @cfg {String} href URL of the link used for the node (defaults to #)
16708 * @cfg {String} hrefTarget target frame for the link
16709 * @cfg {String} qtip An Ext QuickTip for the node
16710 * @cfg {String} qtipCfg An Ext QuickTip config for the node (used instead of qtip)
16711 * @cfg {Boolean} singleClickExpand True for single click expand on this node
16712 * @cfg {Function} uiProvider A UI <b>class</b> to use for this node (defaults to Roo.tree.TreeNodeUI)
16713 * @cfg {Boolean} checked True to render a checked checkbox for this node, false to render an unchecked checkbox
16714 * (defaults to undefined with no checkbox rendered)
16716 * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node
16718 Roo.tree.TreeNode = function(attributes){
16719 attributes = attributes || {};
16720 if(typeof attributes == "string"){
16721 attributes = {text: attributes};
16723 this.childrenRendered = false;
16724 this.rendered = false;
16725 Roo.tree.TreeNode.superclass.constructor.call(this, attributes);
16726 this.expanded = attributes.expanded === true;
16727 this.isTarget = attributes.isTarget !== false;
16728 this.draggable = attributes.draggable !== false && attributes.allowDrag !== false;
16729 this.allowChildren = attributes.allowChildren !== false && attributes.allowDrop !== false;
16732 * Read-only. The text for this node. To change it use setText().
16735 this.text = attributes.text;
16737 * True if this node is disabled.
16740 this.disabled = attributes.disabled === true;
16744 * @event textchange
16745 * Fires when the text for this node is changed
16746 * @param {Node} this This node
16747 * @param {String} text The new text
16748 * @param {String} oldText The old text
16750 "textchange" : true,
16752 * @event beforeexpand
16753 * Fires before this node is expanded, return false to cancel.
16754 * @param {Node} this This node
16755 * @param {Boolean} deep
16756 * @param {Boolean} anim
16758 "beforeexpand" : true,
16760 * @event beforecollapse
16761 * Fires before this node is collapsed, return false to cancel.
16762 * @param {Node} this This node
16763 * @param {Boolean} deep
16764 * @param {Boolean} anim
16766 "beforecollapse" : true,
16769 * Fires when this node is expanded
16770 * @param {Node} this This node
16774 * @event disabledchange
16775 * Fires when the disabled status of this node changes
16776 * @param {Node} this This node
16777 * @param {Boolean} disabled
16779 "disabledchange" : true,
16782 * Fires when this node is collapsed
16783 * @param {Node} this This node
16787 * @event beforeclick
16788 * Fires before click processing. Return false to cancel the default action.
16789 * @param {Node} this This node
16790 * @param {Roo.EventObject} e The event object
16792 "beforeclick":true,
16794 * @event checkchange
16795 * Fires when a node with a checkbox's checked property changes
16796 * @param {Node} this This node
16797 * @param {Boolean} checked
16799 "checkchange":true,
16802 * Fires when this node is clicked
16803 * @param {Node} this This node
16804 * @param {Roo.EventObject} e The event object
16809 * Fires when this node is double clicked
16810 * @param {Node} this This node
16811 * @param {Roo.EventObject} e The event object
16815 * @event contextmenu
16816 * Fires when this node is right clicked
16817 * @param {Node} this This node
16818 * @param {Roo.EventObject} e The event object
16820 "contextmenu":true,
16822 * @event beforechildrenrendered
16823 * Fires right before the child nodes for this node are rendered
16824 * @param {Node} this This node
16826 "beforechildrenrendered":true
16829 var uiClass = this.attributes.uiProvider || Roo.tree.TreeNodeUI;
16832 * Read-only. The UI for this node
16835 this.ui = new uiClass(this);
16837 // finally support items[]
16838 if (typeof(this.attributes.items) == 'undefined' || !this.attributes.items) {
16843 Roo.each(this.attributes.items, function(c) {
16844 this.appendChild(Roo.factory(c,Roo.Tree));
16846 delete this.attributes.items;
16851 Roo.extend(Roo.tree.TreeNode, Roo.data.Node, {
16852 preventHScroll: true,
16854 * Returns true if this node is expanded
16855 * @return {Boolean}
16857 isExpanded : function(){
16858 return this.expanded;
16862 * Returns the UI object for this node
16863 * @return {TreeNodeUI}
16865 getUI : function(){
16869 // private override
16870 setFirstChild : function(node){
16871 var of = this.firstChild;
16872 Roo.tree.TreeNode.superclass.setFirstChild.call(this, node);
16873 if(this.childrenRendered && of && node != of){
16874 of.renderIndent(true, true);
16877 this.renderIndent(true, true);
16881 // private override
16882 setLastChild : function(node){
16883 var ol = this.lastChild;
16884 Roo.tree.TreeNode.superclass.setLastChild.call(this, node);
16885 if(this.childrenRendered && ol && node != ol){
16886 ol.renderIndent(true, true);
16889 this.renderIndent(true, true);
16893 // these methods are overridden to provide lazy rendering support
16894 // private override
16895 appendChild : function()
16897 var node = Roo.tree.TreeNode.superclass.appendChild.apply(this, arguments);
16898 if(node && this.childrenRendered){
16901 this.ui.updateExpandIcon();
16905 // private override
16906 removeChild : function(node){
16907 this.ownerTree.getSelectionModel().unselect(node);
16908 Roo.tree.TreeNode.superclass.removeChild.apply(this, arguments);
16909 // if it's been rendered remove dom node
16910 if(this.childrenRendered){
16913 if(this.childNodes.length < 1){
16914 this.collapse(false, false);
16916 this.ui.updateExpandIcon();
16918 if(!this.firstChild) {
16919 this.childrenRendered = false;
16924 // private override
16925 insertBefore : function(node, refNode){
16926 var newNode = Roo.tree.TreeNode.superclass.insertBefore.apply(this, arguments);
16927 if(newNode && refNode && this.childrenRendered){
16930 this.ui.updateExpandIcon();
16935 * Sets the text for this node
16936 * @param {String} text
16938 setText : function(text){
16939 var oldText = this.text;
16941 this.attributes.text = text;
16942 if(this.rendered){ // event without subscribing
16943 this.ui.onTextChange(this, text, oldText);
16945 this.fireEvent("textchange", this, text, oldText);
16949 * Triggers selection of this node
16951 select : function(){
16952 this.getOwnerTree().getSelectionModel().select(this);
16956 * Triggers deselection of this node
16958 unselect : function(){
16959 this.getOwnerTree().getSelectionModel().unselect(this);
16963 * Returns true if this node is selected
16964 * @return {Boolean}
16966 isSelected : function(){
16967 return this.getOwnerTree().getSelectionModel().isSelected(this);
16971 * Expand this node.
16972 * @param {Boolean} deep (optional) True to expand all children as well
16973 * @param {Boolean} anim (optional) false to cancel the default animation
16974 * @param {Function} callback (optional) A callback to be called when
16975 * expanding this node completes (does not wait for deep expand to complete).
16976 * Called with 1 parameter, this node.
16978 expand : function(deep, anim, callback){
16979 if(!this.expanded){
16980 if(this.fireEvent("beforeexpand", this, deep, anim) === false){
16983 if(!this.childrenRendered){
16984 this.renderChildren();
16986 this.expanded = true;
16987 if(!this.isHiddenRoot() && (this.getOwnerTree().animate && anim !== false) || anim){
16988 this.ui.animExpand(function(){
16989 this.fireEvent("expand", this);
16990 if(typeof callback == "function"){
16994 this.expandChildNodes(true);
16996 }.createDelegate(this));
17000 this.fireEvent("expand", this);
17001 if(typeof callback == "function"){
17006 if(typeof callback == "function"){
17011 this.expandChildNodes(true);
17015 isHiddenRoot : function(){
17016 return this.isRoot && !this.getOwnerTree().rootVisible;
17020 * Collapse this node.
17021 * @param {Boolean} deep (optional) True to collapse all children as well
17022 * @param {Boolean} anim (optional) false to cancel the default animation
17024 collapse : function(deep, anim){
17025 if(this.expanded && !this.isHiddenRoot()){
17026 if(this.fireEvent("beforecollapse", this, deep, anim) === false){
17029 this.expanded = false;
17030 if((this.getOwnerTree().animate && anim !== false) || anim){
17031 this.ui.animCollapse(function(){
17032 this.fireEvent("collapse", this);
17034 this.collapseChildNodes(true);
17036 }.createDelegate(this));
17039 this.ui.collapse();
17040 this.fireEvent("collapse", this);
17044 var cs = this.childNodes;
17045 for(var i = 0, len = cs.length; i < len; i++) {
17046 cs[i].collapse(true, false);
17052 delayedExpand : function(delay){
17053 if(!this.expandProcId){
17054 this.expandProcId = this.expand.defer(delay, this);
17059 cancelExpand : function(){
17060 if(this.expandProcId){
17061 clearTimeout(this.expandProcId);
17063 this.expandProcId = false;
17067 * Toggles expanded/collapsed state of the node
17069 toggle : function(){
17078 * Ensures all parent nodes are expanded
17080 ensureVisible : function(callback){
17081 var tree = this.getOwnerTree();
17082 tree.expandPath(this.parentNode.getPath(), false, function(){
17083 tree.getTreeEl().scrollChildIntoView(this.ui.anchor);
17084 Roo.callback(callback);
17085 }.createDelegate(this));
17089 * Expand all child nodes
17090 * @param {Boolean} deep (optional) true if the child nodes should also expand their child nodes
17092 expandChildNodes : function(deep){
17093 var cs = this.childNodes;
17094 for(var i = 0, len = cs.length; i < len; i++) {
17095 cs[i].expand(deep);
17100 * Collapse all child nodes
17101 * @param {Boolean} deep (optional) true if the child nodes should also collapse their child nodes
17103 collapseChildNodes : function(deep){
17104 var cs = this.childNodes;
17105 for(var i = 0, len = cs.length; i < len; i++) {
17106 cs[i].collapse(deep);
17111 * Disables this node
17113 disable : function(){
17114 this.disabled = true;
17116 if(this.rendered && this.ui.onDisableChange){ // event without subscribing
17117 this.ui.onDisableChange(this, true);
17119 this.fireEvent("disabledchange", this, true);
17123 * Enables this node
17125 enable : function(){
17126 this.disabled = false;
17127 if(this.rendered && this.ui.onDisableChange){ // event without subscribing
17128 this.ui.onDisableChange(this, false);
17130 this.fireEvent("disabledchange", this, false);
17134 renderChildren : function(suppressEvent){
17135 if(suppressEvent !== false){
17136 this.fireEvent("beforechildrenrendered", this);
17138 var cs = this.childNodes;
17139 for(var i = 0, len = cs.length; i < len; i++){
17140 cs[i].render(true);
17142 this.childrenRendered = true;
17146 sort : function(fn, scope){
17147 Roo.tree.TreeNode.superclass.sort.apply(this, arguments);
17148 if(this.childrenRendered){
17149 var cs = this.childNodes;
17150 for(var i = 0, len = cs.length; i < len; i++){
17151 cs[i].render(true);
17157 render : function(bulkRender){
17158 this.ui.render(bulkRender);
17159 if(!this.rendered){
17160 this.rendered = true;
17162 this.expanded = false;
17163 this.expand(false, false);
17169 renderIndent : function(deep, refresh){
17171 this.ui.childIndent = null;
17173 this.ui.renderIndent();
17174 if(deep === true && this.childrenRendered){
17175 var cs = this.childNodes;
17176 for(var i = 0, len = cs.length; i < len; i++){
17177 cs[i].renderIndent(true, refresh);
17183 * Ext JS Library 1.1.1
17184 * Copyright(c) 2006-2007, Ext JS, LLC.
17186 * Originally Released Under LGPL - original licence link has changed is not relivant.
17189 * <script type="text/javascript">
17193 * @class Roo.tree.AsyncTreeNode
17194 * @extends Roo.tree.TreeNode
17195 * @cfg {TreeLoader} loader A TreeLoader to be used by this node (defaults to the loader defined on the tree)
17197 * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node
17199 Roo.tree.AsyncTreeNode = function(config){
17200 this.loaded = false;
17201 this.loading = false;
17202 Roo.tree.AsyncTreeNode.superclass.constructor.apply(this, arguments);
17204 * @event beforeload
17205 * Fires before this node is loaded, return false to cancel
17206 * @param {Node} this This node
17208 this.addEvents({'beforeload':true, 'load': true});
17211 * Fires when this node is loaded
17212 * @param {Node} this This node
17215 * The loader used by this node (defaults to using the tree's defined loader)
17220 Roo.extend(Roo.tree.AsyncTreeNode, Roo.tree.TreeNode, {
17221 expand : function(deep, anim, callback){
17222 if(this.loading){ // if an async load is already running, waiting til it's done
17224 var f = function(){
17225 if(!this.loading){ // done loading
17226 clearInterval(timer);
17227 this.expand(deep, anim, callback);
17229 }.createDelegate(this);
17230 timer = setInterval(f, 200);
17234 if(this.fireEvent("beforeload", this) === false){
17237 this.loading = true;
17238 this.ui.beforeLoad(this);
17239 var loader = this.loader || this.attributes.loader || this.getOwnerTree().getLoader();
17241 loader.load(this, this.loadComplete.createDelegate(this, [deep, anim, callback]));
17245 Roo.tree.AsyncTreeNode.superclass.expand.call(this, deep, anim, callback);
17249 * Returns true if this node is currently loading
17250 * @return {Boolean}
17252 isLoading : function(){
17253 return this.loading;
17256 loadComplete : function(deep, anim, callback){
17257 this.loading = false;
17258 this.loaded = true;
17259 this.ui.afterLoad(this);
17260 this.fireEvent("load", this);
17261 this.expand(deep, anim, callback);
17265 * Returns true if this node has been loaded
17266 * @return {Boolean}
17268 isLoaded : function(){
17269 return this.loaded;
17272 hasChildNodes : function(){
17273 if(!this.isLeaf() && !this.loaded){
17276 return Roo.tree.AsyncTreeNode.superclass.hasChildNodes.call(this);
17281 * Trigger a reload for this node
17282 * @param {Function} callback
17284 reload : function(callback){
17285 this.collapse(false, false);
17286 while(this.firstChild){
17287 this.removeChild(this.firstChild);
17289 this.childrenRendered = false;
17290 this.loaded = false;
17291 if(this.isHiddenRoot()){
17292 this.expanded = false;
17294 this.expand(false, false, callback);
17298 * Ext JS Library 1.1.1
17299 * Copyright(c) 2006-2007, Ext JS, LLC.
17301 * Originally Released Under LGPL - original licence link has changed is not relivant.
17304 * <script type="text/javascript">
17308 * @class Roo.tree.TreeNodeUI
17310 * @param {Object} node The node to render
17311 * The TreeNode UI implementation is separate from the
17312 * tree implementation. Unless you are customizing the tree UI,
17313 * you should never have to use this directly.
17315 Roo.tree.TreeNodeUI = function(node){
17317 this.rendered = false;
17318 this.animating = false;
17319 this.emptyIcon = Roo.BLANK_IMAGE_URL;
17322 Roo.tree.TreeNodeUI.prototype = {
17323 removeChild : function(node){
17325 this.ctNode.removeChild(node.ui.getEl());
17329 beforeLoad : function(){
17330 this.addClass("x-tree-node-loading");
17333 afterLoad : function(){
17334 this.removeClass("x-tree-node-loading");
17337 onTextChange : function(node, text, oldText){
17339 this.textNode.innerHTML = text;
17343 onDisableChange : function(node, state){
17344 this.disabled = state;
17346 this.addClass("x-tree-node-disabled");
17348 this.removeClass("x-tree-node-disabled");
17352 onSelectedChange : function(state){
17355 this.addClass("x-tree-selected");
17358 this.removeClass("x-tree-selected");
17362 onMove : function(tree, node, oldParent, newParent, index, refNode){
17363 this.childIndent = null;
17365 var targetNode = newParent.ui.getContainer();
17366 if(!targetNode){//target not rendered
17367 this.holder = document.createElement("div");
17368 this.holder.appendChild(this.wrap);
17371 var insertBefore = refNode ? refNode.ui.getEl() : null;
17373 targetNode.insertBefore(this.wrap, insertBefore);
17375 targetNode.appendChild(this.wrap);
17377 this.node.renderIndent(true);
17381 addClass : function(cls){
17383 Roo.fly(this.elNode).addClass(cls);
17387 removeClass : function(cls){
17389 Roo.fly(this.elNode).removeClass(cls);
17393 remove : function(){
17395 this.holder = document.createElement("div");
17396 this.holder.appendChild(this.wrap);
17400 fireEvent : function(){
17401 return this.node.fireEvent.apply(this.node, arguments);
17404 initEvents : function(){
17405 this.node.on("move", this.onMove, this);
17406 var E = Roo.EventManager;
17407 var a = this.anchor;
17409 var el = Roo.fly(a, '_treeui');
17411 if(Roo.isOpera){ // opera render bug ignores the CSS
17412 el.setStyle("text-decoration", "none");
17415 el.on("click", this.onClick, this);
17416 el.on("dblclick", this.onDblClick, this);
17419 Roo.EventManager.on(this.checkbox,
17420 Roo.isIE ? 'click' : 'change', this.onCheckChange, this);
17423 el.on("contextmenu", this.onContextMenu, this);
17425 var icon = Roo.fly(this.iconNode);
17426 icon.on("click", this.onClick, this);
17427 icon.on("dblclick", this.onDblClick, this);
17428 icon.on("contextmenu", this.onContextMenu, this);
17429 E.on(this.ecNode, "click", this.ecClick, this, true);
17431 if(this.node.disabled){
17432 this.addClass("x-tree-node-disabled");
17434 if(this.node.hidden){
17435 this.addClass("x-tree-node-disabled");
17437 var ot = this.node.getOwnerTree();
17438 var dd = ot.enableDD || ot.enableDrag || ot.enableDrop;
17439 if(dd && (!this.node.isRoot || ot.rootVisible)){
17440 Roo.dd.Registry.register(this.elNode, {
17442 handles: this.getDDHandles(),
17448 getDDHandles : function(){
17449 return [this.iconNode, this.textNode];
17454 this.wrap.style.display = "none";
17460 this.wrap.style.display = "";
17464 onContextMenu : function(e){
17465 if (this.node.hasListener("contextmenu") || this.node.getOwnerTree().hasListener("contextmenu")) {
17466 e.preventDefault();
17468 this.fireEvent("contextmenu", this.node, e);
17472 onClick : function(e){
17477 if(this.fireEvent("beforeclick", this.node, e) !== false){
17478 if(!this.disabled && this.node.attributes.href){
17479 this.fireEvent("click", this.node, e);
17482 e.preventDefault();
17487 if(this.node.attributes.singleClickExpand && !this.animating && this.node.hasChildNodes()){
17488 this.node.toggle();
17491 this.fireEvent("click", this.node, e);
17497 onDblClick : function(e){
17498 e.preventDefault();
17503 this.toggleCheck();
17505 if(!this.animating && this.node.hasChildNodes()){
17506 this.node.toggle();
17508 this.fireEvent("dblclick", this.node, e);
17511 onCheckChange : function(){
17512 var checked = this.checkbox.checked;
17513 this.node.attributes.checked = checked;
17514 this.fireEvent('checkchange', this.node, checked);
17517 ecClick : function(e){
17518 if(!this.animating && this.node.hasChildNodes()){
17519 this.node.toggle();
17523 startDrop : function(){
17524 this.dropping = true;
17527 // delayed drop so the click event doesn't get fired on a drop
17528 endDrop : function(){
17529 setTimeout(function(){
17530 this.dropping = false;
17531 }.createDelegate(this), 50);
17534 expand : function(){
17535 this.updateExpandIcon();
17536 this.ctNode.style.display = "";
17539 focus : function(){
17540 if(!this.node.preventHScroll){
17541 try{this.anchor.focus();
17543 }else if(!Roo.isIE){
17545 var noscroll = this.node.getOwnerTree().getTreeEl().dom;
17546 var l = noscroll.scrollLeft;
17547 this.anchor.focus();
17548 noscroll.scrollLeft = l;
17553 toggleCheck : function(value){
17554 var cb = this.checkbox;
17556 cb.checked = (value === undefined ? !cb.checked : value);
17562 this.anchor.blur();
17566 animExpand : function(callback){
17567 var ct = Roo.get(this.ctNode);
17569 if(!this.node.hasChildNodes()){
17570 this.updateExpandIcon();
17571 this.ctNode.style.display = "";
17572 Roo.callback(callback);
17575 this.animating = true;
17576 this.updateExpandIcon();
17579 callback : function(){
17580 this.animating = false;
17581 Roo.callback(callback);
17584 duration: this.node.ownerTree.duration || .25
17588 highlight : function(){
17589 var tree = this.node.getOwnerTree();
17590 Roo.fly(this.wrap).highlight(
17591 tree.hlColor || "C3DAF9",
17592 {endColor: tree.hlBaseColor}
17596 collapse : function(){
17597 this.updateExpandIcon();
17598 this.ctNode.style.display = "none";
17601 animCollapse : function(callback){
17602 var ct = Roo.get(this.ctNode);
17603 ct.enableDisplayMode('block');
17606 this.animating = true;
17607 this.updateExpandIcon();
17610 callback : function(){
17611 this.animating = false;
17612 Roo.callback(callback);
17615 duration: this.node.ownerTree.duration || .25
17619 getContainer : function(){
17620 return this.ctNode;
17623 getEl : function(){
17627 appendDDGhost : function(ghostNode){
17628 ghostNode.appendChild(this.elNode.cloneNode(true));
17631 getDDRepairXY : function(){
17632 return Roo.lib.Dom.getXY(this.iconNode);
17635 onRender : function(){
17639 render : function(bulkRender){
17640 var n = this.node, a = n.attributes;
17641 var targetNode = n.parentNode ?
17642 n.parentNode.ui.getContainer() : n.ownerTree.innerCt.dom;
17644 if(!this.rendered){
17645 this.rendered = true;
17647 this.renderElements(n, a, targetNode, bulkRender);
17650 if(this.textNode.setAttributeNS){
17651 this.textNode.setAttributeNS("ext", "qtip", a.qtip);
17653 this.textNode.setAttributeNS("ext", "qtitle", a.qtipTitle);
17656 this.textNode.setAttribute("ext:qtip", a.qtip);
17658 this.textNode.setAttribute("ext:qtitle", a.qtipTitle);
17661 }else if(a.qtipCfg){
17662 a.qtipCfg.target = Roo.id(this.textNode);
17663 Roo.QuickTips.register(a.qtipCfg);
17666 if(!this.node.expanded){
17667 this.updateExpandIcon();
17670 if(bulkRender === true) {
17671 targetNode.appendChild(this.wrap);
17676 renderElements : function(n, a, targetNode, bulkRender)
17678 // add some indent caching, this helps performance when rendering a large tree
17679 this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
17680 var t = n.getOwnerTree();
17681 var txt = t.renderer ? t.renderer(n.attributes) : Roo.util.Format.htmlEncode(n.text);
17682 if (typeof(n.attributes.html) != 'undefined') {
17683 txt = n.attributes.html;
17685 var tip = t.rendererTip ? t.rendererTip(n.attributes) : txt;
17686 var cb = typeof a.checked == 'boolean';
17687 var href = a.href ? a.href : Roo.isGecko ? "" : "#";
17688 var buf = ['<li class="x-tree-node"><div class="x-tree-node-el ', a.cls,'">',
17689 '<span class="x-tree-node-indent">',this.indentMarkup,"</span>",
17690 '<img src="', this.emptyIcon, '" class="x-tree-ec-icon" />',
17691 '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',(a.icon ? " x-tree-node-inline-icon" : ""),(a.iconCls ? " "+a.iconCls : ""),'" unselectable="on" />',
17692 cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + (a.checked ? 'checked="checked" />' : ' />')) : '',
17693 '<a hidefocus="on" href="',href,'" tabIndex="1" ',
17694 a.hrefTarget ? ' target="'+a.hrefTarget+'"' : "",
17695 '><span unselectable="on" qtip="' , tip ,'">',txt,"</span></a></div>",
17696 '<ul class="x-tree-node-ct" style="display:none;"></ul>',
17699 if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
17700 this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
17701 n.nextSibling.ui.getEl(), buf.join(""));
17703 this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
17706 this.elNode = this.wrap.childNodes[0];
17707 this.ctNode = this.wrap.childNodes[1];
17708 var cs = this.elNode.childNodes;
17709 this.indentNode = cs[0];
17710 this.ecNode = cs[1];
17711 this.iconNode = cs[2];
17714 this.checkbox = cs[3];
17717 this.anchor = cs[index];
17718 this.textNode = cs[index].firstChild;
17721 getAnchor : function(){
17722 return this.anchor;
17725 getTextEl : function(){
17726 return this.textNode;
17729 getIconEl : function(){
17730 return this.iconNode;
17733 isChecked : function(){
17734 return this.checkbox ? this.checkbox.checked : false;
17737 updateExpandIcon : function(){
17739 var n = this.node, c1, c2;
17740 var cls = n.isLast() ? "x-tree-elbow-end" : "x-tree-elbow";
17741 var hasChild = n.hasChildNodes();
17745 c1 = "x-tree-node-collapsed";
17746 c2 = "x-tree-node-expanded";
17749 c1 = "x-tree-node-expanded";
17750 c2 = "x-tree-node-collapsed";
17753 this.removeClass("x-tree-node-leaf");
17754 this.wasLeaf = false;
17756 if(this.c1 != c1 || this.c2 != c2){
17757 Roo.fly(this.elNode).replaceClass(c1, c2);
17758 this.c1 = c1; this.c2 = c2;
17761 // this changes non-leafs into leafs if they have no children.
17762 // it's not very rational behaviour..
17764 if(!this.wasLeaf && this.node.leaf){
17765 Roo.fly(this.elNode).replaceClass("x-tree-node-expanded", "x-tree-node-leaf");
17768 this.wasLeaf = true;
17771 var ecc = "x-tree-ec-icon "+cls;
17772 if(this.ecc != ecc){
17773 this.ecNode.className = ecc;
17779 getChildIndent : function(){
17780 if(!this.childIndent){
17784 if(!p.isRoot || (p.isRoot && p.ownerTree.rootVisible)){
17786 buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-elbow-line" />');
17788 buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-icon" />');
17793 this.childIndent = buf.join("");
17795 return this.childIndent;
17798 renderIndent : function(){
17801 var p = this.node.parentNode;
17803 indent = p.ui.getChildIndent();
17805 if(this.indentMarkup != indent){ // don't rerender if not required
17806 this.indentNode.innerHTML = indent;
17807 this.indentMarkup = indent;
17809 this.updateExpandIcon();
17814 Roo.tree.RootTreeNodeUI = function(){
17815 Roo.tree.RootTreeNodeUI.superclass.constructor.apply(this, arguments);
17817 Roo.extend(Roo.tree.RootTreeNodeUI, Roo.tree.TreeNodeUI, {
17818 render : function(){
17819 if(!this.rendered){
17820 var targetNode = this.node.ownerTree.innerCt.dom;
17821 this.node.expanded = true;
17822 targetNode.innerHTML = '<div class="x-tree-root-node"></div>';
17823 this.wrap = this.ctNode = targetNode.firstChild;
17826 collapse : function(){
17828 expand : function(){
17832 * Ext JS Library 1.1.1
17833 * Copyright(c) 2006-2007, Ext JS, LLC.
17835 * Originally Released Under LGPL - original licence link has changed is not relivant.
17838 * <script type="text/javascript">
17841 * @class Roo.tree.TreeLoader
17842 * @extends Roo.util.Observable
17843 * A TreeLoader provides for lazy loading of an {@link Roo.tree.TreeNode}'s child
17844 * nodes from a specified URL. The response must be a javascript Array definition
17845 * who's elements are node definition objects. eg:
17850 { 'id': 1, 'text': 'A folder Node', 'leaf': false },
17851 { 'id': 2, 'text': 'A leaf Node', 'leaf': true }
17858 * The old style respose with just an array is still supported, but not recommended.
17861 * A server request is sent, and child nodes are loaded only when a node is expanded.
17862 * The loading node's id is passed to the server under the parameter name "node" to
17863 * enable the server to produce the correct child nodes.
17865 * To pass extra parameters, an event handler may be attached to the "beforeload"
17866 * event, and the parameters specified in the TreeLoader's baseParams property:
17868 myTreeLoader.on("beforeload", function(treeLoader, node) {
17869 this.baseParams.category = node.attributes.category;
17872 * This would pass an HTTP parameter called "category" to the server containing
17873 * the value of the Node's "category" attribute.
17875 * Creates a new Treeloader.
17876 * @param {Object} config A config object containing config properties.
17878 Roo.tree.TreeLoader = function(config){
17879 this.baseParams = {};
17880 this.requestMethod = "POST";
17881 Roo.apply(this, config);
17886 * @event beforeload
17887 * Fires before a network request is made to retrieve the Json text which specifies a node's children.
17888 * @param {Object} This TreeLoader object.
17889 * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
17890 * @param {Object} callback The callback function specified in the {@link #load} call.
17895 * Fires when the node has been successfuly loaded.
17896 * @param {Object} This TreeLoader object.
17897 * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
17898 * @param {Object} response The response object containing the data from the server.
17902 * @event loadexception
17903 * Fires if the network request failed.
17904 * @param {Object} This TreeLoader object.
17905 * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
17906 * @param {Object} response The response object containing the data from the server.
17908 loadexception : true,
17911 * Fires before a node is created, enabling you to return custom Node types
17912 * @param {Object} This TreeLoader object.
17913 * @param {Object} attr - the data returned from the AJAX call (modify it to suit)
17918 Roo.tree.TreeLoader.superclass.constructor.call(this);
17921 Roo.extend(Roo.tree.TreeLoader, Roo.util.Observable, {
17923 * @cfg {String} dataUrl The URL from which to request a Json string which
17924 * specifies an array of node definition object representing the child nodes
17928 * @cfg {String} requestMethod either GET or POST
17929 * defaults to POST (due to BC)
17933 * @cfg {Object} baseParams (optional) An object containing properties which
17934 * specify HTTP parameters to be passed to each request for child nodes.
17937 * @cfg {Object} baseAttrs (optional) An object containing attributes to be added to all nodes
17938 * created by this loader. If the attributes sent by the server have an attribute in this object,
17939 * they take priority.
17942 * @cfg {Object} uiProviders (optional) An object containing properties which
17944 * DEPRECATED - use 'create' event handler to modify attributes - which affect creation.
17945 * specify custom {@link Roo.tree.TreeNodeUI} implementations. If the optional
17946 * <i>uiProvider</i> attribute of a returned child node is a string rather
17947 * than a reference to a TreeNodeUI implementation, this that string value
17948 * is used as a property name in the uiProviders object. You can define the provider named
17949 * 'default' , and this will be used for all nodes (if no uiProvider is delivered by the node data)
17954 * @cfg {Boolean} clearOnLoad (optional) Default to true. Remove previously existing
17955 * child nodes before loading.
17957 clearOnLoad : true,
17960 * @cfg {String} root (optional) Default to false. Use this to read data from an object
17961 * property on loading, rather than expecting an array. (eg. more compatible to a standard
17962 * Grid query { data : [ .....] }
17967 * @cfg {String} queryParam (optional)
17968 * Name of the query as it will be passed on the querystring (defaults to 'node')
17969 * eg. the request will be ?node=[id]
17976 * Load an {@link Roo.tree.TreeNode} from the URL specified in the constructor.
17977 * This is called automatically when a node is expanded, but may be used to reload
17978 * a node (or append new children if the {@link #clearOnLoad} option is false.)
17979 * @param {Roo.tree.TreeNode} node
17980 * @param {Function} callback
17982 load : function(node, callback){
17983 if(this.clearOnLoad){
17984 while(node.firstChild){
17985 node.removeChild(node.firstChild);
17988 if(node.attributes.children){ // preloaded json children
17989 var cs = node.attributes.children;
17990 for(var i = 0, len = cs.length; i < len; i++){
17991 node.appendChild(this.createNode(cs[i]));
17993 if(typeof callback == "function"){
17996 }else if(this.dataUrl){
17997 this.requestData(node, callback);
18001 getParams: function(node){
18002 var buf = [], bp = this.baseParams;
18003 for(var key in bp){
18004 if(typeof bp[key] != "function"){
18005 buf.push(encodeURIComponent(key), "=", encodeURIComponent(bp[key]), "&");
18008 var n = this.queryParam === false ? 'node' : this.queryParam;
18009 buf.push(n + "=", encodeURIComponent(node.id));
18010 return buf.join("");
18013 requestData : function(node, callback){
18014 if(this.fireEvent("beforeload", this, node, callback) !== false){
18015 this.transId = Roo.Ajax.request({
18016 method:this.requestMethod,
18017 url: this.dataUrl||this.url,
18018 success: this.handleResponse,
18019 failure: this.handleFailure,
18021 argument: {callback: callback, node: node},
18022 params: this.getParams(node)
18025 // if the load is cancelled, make sure we notify
18026 // the node that we are done
18027 if(typeof callback == "function"){
18033 isLoading : function(){
18034 return this.transId ? true : false;
18037 abort : function(){
18038 if(this.isLoading()){
18039 Roo.Ajax.abort(this.transId);
18044 createNode : function(attr)
18046 // apply baseAttrs, nice idea Corey!
18047 if(this.baseAttrs){
18048 Roo.applyIf(attr, this.baseAttrs);
18050 if(this.applyLoader !== false){
18051 attr.loader = this;
18053 // uiProvider = depreciated..
18055 if(typeof(attr.uiProvider) == 'string'){
18056 attr.uiProvider = this.uiProviders[attr.uiProvider] ||
18057 /** eval:var:attr */ eval(attr.uiProvider);
18059 if(typeof(this.uiProviders['default']) != 'undefined') {
18060 attr.uiProvider = this.uiProviders['default'];
18063 this.fireEvent('create', this, attr);
18065 attr.leaf = typeof(attr.leaf) == 'string' ? attr.leaf * 1 : attr.leaf;
18067 new Roo.tree.TreeNode(attr) :
18068 new Roo.tree.AsyncTreeNode(attr));
18071 processResponse : function(response, node, callback)
18073 var json = response.responseText;
18076 var o = Roo.decode(json);
18078 if (this.root === false && typeof(o.success) != undefined) {
18079 this.root = 'data'; // the default behaviour for list like data..
18082 if (this.root !== false && !o.success) {
18083 // it's a failure condition.
18084 var a = response.argument;
18085 this.fireEvent("loadexception", this, a.node, response);
18086 Roo.log("Load failed - should have a handler really");
18092 if (this.root !== false) {
18096 for(var i = 0, len = o.length; i < len; i++){
18097 var n = this.createNode(o[i]);
18099 node.appendChild(n);
18102 if(typeof callback == "function"){
18103 callback(this, node);
18106 this.handleFailure(response);
18110 handleResponse : function(response){
18111 this.transId = false;
18112 var a = response.argument;
18113 this.processResponse(response, a.node, a.callback);
18114 this.fireEvent("load", this, a.node, response);
18117 handleFailure : function(response)
18119 // should handle failure better..
18120 this.transId = false;
18121 var a = response.argument;
18122 this.fireEvent("loadexception", this, a.node, response);
18123 if(typeof a.callback == "function"){
18124 a.callback(this, a.node);
18129 * Ext JS Library 1.1.1
18130 * Copyright(c) 2006-2007, Ext JS, LLC.
18132 * Originally Released Under LGPL - original licence link has changed is not relivant.
18135 * <script type="text/javascript">
18139 * @class Roo.tree.TreeFilter
18140 * Note this class is experimental and doesn't update the indent (lines) or expand collapse icons of the nodes
18141 * @param {TreePanel} tree
18142 * @param {Object} config (optional)
18144 Roo.tree.TreeFilter = function(tree, config){
18146 this.filtered = {};
18147 Roo.apply(this, config);
18150 Roo.tree.TreeFilter.prototype = {
18157 * Filter the data by a specific attribute.
18158 * @param {String/RegExp} value Either string that the attribute value
18159 * should start with or a RegExp to test against the attribute
18160 * @param {String} attr (optional) The attribute passed in your node's attributes collection. Defaults to "text".
18161 * @param {TreeNode} startNode (optional) The node to start the filter at.
18163 filter : function(value, attr, startNode){
18164 attr = attr || "text";
18166 if(typeof value == "string"){
18167 var vlen = value.length;
18168 // auto clear empty filter
18169 if(vlen == 0 && this.clearBlank){
18173 value = value.toLowerCase();
18175 return n.attributes[attr].substr(0, vlen).toLowerCase() == value;
18177 }else if(value.exec){ // regex?
18179 return value.test(n.attributes[attr]);
18182 throw 'Illegal filter type, must be string or regex';
18184 this.filterBy(f, null, startNode);
18188 * Filter by a function. The passed function will be called with each
18189 * node in the tree (or from the startNode). If the function returns true, the node is kept
18190 * otherwise it is filtered. If a node is filtered, its children are also filtered.
18191 * @param {Function} fn The filter function
18192 * @param {Object} scope (optional) The scope of the function (defaults to the current node)
18194 filterBy : function(fn, scope, startNode){
18195 startNode = startNode || this.tree.root;
18196 if(this.autoClear){
18199 var af = this.filtered, rv = this.reverse;
18200 var f = function(n){
18201 if(n == startNode){
18207 var m = fn.call(scope || n, n);
18215 startNode.cascade(f);
18218 if(typeof id != "function"){
18220 if(n && n.parentNode){
18221 n.parentNode.removeChild(n);
18229 * Clears the current filter. Note: with the "remove" option
18230 * set a filter cannot be cleared.
18232 clear : function(){
18234 var af = this.filtered;
18236 if(typeof id != "function"){
18243 this.filtered = {};
18248 * Ext JS Library 1.1.1
18249 * Copyright(c) 2006-2007, Ext JS, LLC.
18251 * Originally Released Under LGPL - original licence link has changed is not relivant.
18254 * <script type="text/javascript">
18259 * @class Roo.tree.TreeSorter
18260 * Provides sorting of nodes in a TreePanel
18262 * @cfg {Boolean} folderSort True to sort leaf nodes under non leaf nodes
18263 * @cfg {String} property The named attribute on the node to sort by (defaults to text)
18264 * @cfg {String} dir The direction to sort (asc or desc) (defaults to asc)
18265 * @cfg {String} leafAttr The attribute used to determine leaf nodes in folder sort (defaults to "leaf")
18266 * @cfg {Boolean} caseSensitive true for case sensitive sort (defaults to false)
18267 * @cfg {Function} sortType A custom "casting" function used to convert node values before sorting
18269 * @param {TreePanel} tree
18270 * @param {Object} config
18272 Roo.tree.TreeSorter = function(tree, config){
18273 Roo.apply(this, config);
18274 tree.on("beforechildrenrendered", this.doSort, this);
18275 tree.on("append", this.updateSort, this);
18276 tree.on("insert", this.updateSort, this);
18278 var dsc = this.dir && this.dir.toLowerCase() == "desc";
18279 var p = this.property || "text";
18280 var sortType = this.sortType;
18281 var fs = this.folderSort;
18282 var cs = this.caseSensitive === true;
18283 var leafAttr = this.leafAttr || 'leaf';
18285 this.sortFn = function(n1, n2){
18287 if(n1.attributes[leafAttr] && !n2.attributes[leafAttr]){
18290 if(!n1.attributes[leafAttr] && n2.attributes[leafAttr]){
18294 var v1 = sortType ? sortType(n1) : (cs ? n1.attributes[p] : n1.attributes[p].toUpperCase());
18295 var v2 = sortType ? sortType(n2) : (cs ? n2.attributes[p] : n2.attributes[p].toUpperCase());
18297 return dsc ? +1 : -1;
18299 return dsc ? -1 : +1;
18306 Roo.tree.TreeSorter.prototype = {
18307 doSort : function(node){
18308 node.sort(this.sortFn);
18311 compareNodes : function(n1, n2){
18312 return (n1.text.toUpperCase() > n2.text.toUpperCase() ? 1 : -1);
18315 updateSort : function(tree, node){
18316 if(node.childrenRendered){
18317 this.doSort.defer(1, this, [node]);
18322 * Ext JS Library 1.1.1
18323 * Copyright(c) 2006-2007, Ext JS, LLC.
18325 * Originally Released Under LGPL - original licence link has changed is not relivant.
18328 * <script type="text/javascript">
18331 if(Roo.dd.DropZone){
18333 Roo.tree.TreeDropZone = function(tree, config){
18334 this.allowParentInsert = false;
18335 this.allowContainerDrop = false;
18336 this.appendOnly = false;
18337 Roo.tree.TreeDropZone.superclass.constructor.call(this, tree.innerCt, config);
18339 this.lastInsertClass = "x-tree-no-status";
18340 this.dragOverData = {};
18343 Roo.extend(Roo.tree.TreeDropZone, Roo.dd.DropZone, {
18344 ddGroup : "TreeDD",
18347 expandDelay : 1000,
18349 expandNode : function(node){
18350 if(node.hasChildNodes() && !node.isExpanded()){
18351 node.expand(false, null, this.triggerCacheRefresh.createDelegate(this));
18355 queueExpand : function(node){
18356 this.expandProcId = this.expandNode.defer(this.expandDelay, this, [node]);
18359 cancelExpand : function(){
18360 if(this.expandProcId){
18361 clearTimeout(this.expandProcId);
18362 this.expandProcId = false;
18366 isValidDropPoint : function(n, pt, dd, e, data){
18367 if(!n || !data){ return false; }
18368 var targetNode = n.node;
18369 var dropNode = data.node;
18370 // default drop rules
18371 if(!(targetNode && targetNode.isTarget && pt)){
18374 if(pt == "append" && targetNode.allowChildren === false){
18377 if((pt == "above" || pt == "below") && (targetNode.parentNode && targetNode.parentNode.allowChildren === false)){
18380 if(dropNode && (targetNode == dropNode || dropNode.contains(targetNode))){
18383 // reuse the object
18384 var overEvent = this.dragOverData;
18385 overEvent.tree = this.tree;
18386 overEvent.target = targetNode;
18387 overEvent.data = data;
18388 overEvent.point = pt;
18389 overEvent.source = dd;
18390 overEvent.rawEvent = e;
18391 overEvent.dropNode = dropNode;
18392 overEvent.cancel = false;
18393 var result = this.tree.fireEvent("nodedragover", overEvent);
18394 return overEvent.cancel === false && result !== false;
18397 getDropPoint : function(e, n, dd)
18401 return tn.allowChildren !== false ? "append" : false; // always append for root
18403 var dragEl = n.ddel;
18404 var t = Roo.lib.Dom.getY(dragEl), b = t + dragEl.offsetHeight;
18405 var y = Roo.lib.Event.getPageY(e);
18406 //var noAppend = tn.allowChildren === false || tn.isLeaf();
18408 // we may drop nodes anywhere, as long as allowChildren has not been set to false..
18409 var noAppend = tn.allowChildren === false;
18410 if(this.appendOnly || tn.parentNode.allowChildren === false){
18411 return noAppend ? false : "append";
18413 var noBelow = false;
18414 if(!this.allowParentInsert){
18415 noBelow = tn.hasChildNodes() && tn.isExpanded();
18417 var q = (b - t) / (noAppend ? 2 : 3);
18418 if(y >= t && y < (t + q)){
18420 }else if(!noBelow && (noAppend || y >= b-q && y <= b)){
18427 onNodeEnter : function(n, dd, e, data)
18429 this.cancelExpand();
18432 onNodeOver : function(n, dd, e, data)
18435 var pt = this.getDropPoint(e, n, dd);
18438 // auto node expand check
18439 if(!this.expandProcId && pt == "append" && node.hasChildNodes() && !n.node.isExpanded()){
18440 this.queueExpand(node);
18441 }else if(pt != "append"){
18442 this.cancelExpand();
18445 // set the insert point style on the target node
18446 var returnCls = this.dropNotAllowed;
18447 if(this.isValidDropPoint(n, pt, dd, e, data)){
18452 returnCls = n.node.isFirst() ? "x-tree-drop-ok-above" : "x-tree-drop-ok-between";
18453 cls = "x-tree-drag-insert-above";
18454 }else if(pt == "below"){
18455 returnCls = n.node.isLast() ? "x-tree-drop-ok-below" : "x-tree-drop-ok-between";
18456 cls = "x-tree-drag-insert-below";
18458 returnCls = "x-tree-drop-ok-append";
18459 cls = "x-tree-drag-append";
18461 if(this.lastInsertClass != cls){
18462 Roo.fly(el).replaceClass(this.lastInsertClass, cls);
18463 this.lastInsertClass = cls;
18470 onNodeOut : function(n, dd, e, data){
18472 this.cancelExpand();
18473 this.removeDropIndicators(n);
18476 onNodeDrop : function(n, dd, e, data){
18477 var point = this.getDropPoint(e, n, dd);
18478 var targetNode = n.node;
18479 targetNode.ui.startDrop();
18480 if(!this.isValidDropPoint(n, point, dd, e, data)){
18481 targetNode.ui.endDrop();
18484 // first try to find the drop node
18485 var dropNode = data.node || (dd.getTreeNode ? dd.getTreeNode(data, targetNode, point, e) : null);
18488 target: targetNode,
18493 dropNode: dropNode,
18496 var retval = this.tree.fireEvent("beforenodedrop", dropEvent);
18497 if(retval === false || dropEvent.cancel === true || !dropEvent.dropNode){
18498 targetNode.ui.endDrop();
18501 // allow target changing
18502 targetNode = dropEvent.target;
18503 if(point == "append" && !targetNode.isExpanded()){
18504 targetNode.expand(false, null, function(){
18505 this.completeDrop(dropEvent);
18506 }.createDelegate(this));
18508 this.completeDrop(dropEvent);
18513 completeDrop : function(de){
18514 var ns = de.dropNode, p = de.point, t = de.target;
18515 if(!(ns instanceof Array)){
18519 for(var i = 0, len = ns.length; i < len; i++){
18522 t.parentNode.insertBefore(n, t);
18523 }else if(p == "below"){
18524 t.parentNode.insertBefore(n, t.nextSibling);
18530 if(this.tree.hlDrop){
18534 this.tree.fireEvent("nodedrop", de);
18537 afterNodeMoved : function(dd, data, e, targetNode, dropNode){
18538 if(this.tree.hlDrop){
18539 dropNode.ui.focus();
18540 dropNode.ui.highlight();
18542 this.tree.fireEvent("nodedrop", this.tree, targetNode, data, dd, e);
18545 getTree : function(){
18549 removeDropIndicators : function(n){
18552 Roo.fly(el).removeClass([
18553 "x-tree-drag-insert-above",
18554 "x-tree-drag-insert-below",
18555 "x-tree-drag-append"]);
18556 this.lastInsertClass = "_noclass";
18560 beforeDragDrop : function(target, e, id){
18561 this.cancelExpand();
18565 afterRepair : function(data){
18566 if(data && Roo.enableFx){
18567 data.node.ui.highlight();
18577 * Ext JS Library 1.1.1
18578 * Copyright(c) 2006-2007, Ext JS, LLC.
18580 * Originally Released Under LGPL - original licence link has changed is not relivant.
18583 * <script type="text/javascript">
18587 if(Roo.dd.DragZone){
18588 Roo.tree.TreeDragZone = function(tree, config){
18589 Roo.tree.TreeDragZone.superclass.constructor.call(this, tree.getTreeEl(), config);
18593 Roo.extend(Roo.tree.TreeDragZone, Roo.dd.DragZone, {
18594 ddGroup : "TreeDD",
18596 onBeforeDrag : function(data, e){
18598 return n && n.draggable && !n.disabled;
18602 onInitDrag : function(e){
18603 var data = this.dragData;
18604 this.tree.getSelectionModel().select(data.node);
18605 this.proxy.update("");
18606 data.node.ui.appendDDGhost(this.proxy.ghost.dom);
18607 this.tree.fireEvent("startdrag", this.tree, data.node, e);
18610 getRepairXY : function(e, data){
18611 return data.node.ui.getDDRepairXY();
18614 onEndDrag : function(data, e){
18615 this.tree.fireEvent("enddrag", this.tree, data.node, e);
18620 onValidDrop : function(dd, e, id){
18621 this.tree.fireEvent("dragdrop", this.tree, this.dragData.node, dd, e);
18625 beforeInvalidDrop : function(e, id){
18626 // this scrolls the original position back into view
18627 var sm = this.tree.getSelectionModel();
18628 sm.clearSelections();
18629 sm.select(this.dragData.node);
18634 * Ext JS Library 1.1.1
18635 * Copyright(c) 2006-2007, Ext JS, LLC.
18637 * Originally Released Under LGPL - original licence link has changed is not relivant.
18640 * <script type="text/javascript">
18643 * @class Roo.tree.TreeEditor
18644 * @extends Roo.Editor
18645 * Provides editor functionality for inline tree node editing. Any valid {@link Roo.form.Field} can be used
18646 * as the editor field.
18648 * @param {Object} config (used to be the tree panel.)
18649 * @param {Object} oldconfig DEPRECIATED Either a prebuilt {@link Roo.form.Field} instance or a Field config object
18651 * @cfg {Roo.tree.TreePanel} tree The tree to bind to.
18652 * @cfg {Roo.form.TextField|Object} field The field configuration
18656 Roo.tree.TreeEditor = function(config, oldconfig) { // was -- (tree, config){
18659 if (oldconfig) { // old style..
18660 field = oldconfig.events ? oldconfig : new Roo.form.TextField(oldconfig);
18663 tree = config.tree;
18664 config.field = config.field || {};
18665 config.field.xtype = 'TextField';
18666 field = Roo.factory(config.field, Roo.form);
18668 config = config || {};
18673 * @event beforenodeedit
18674 * Fires when editing is initiated, but before the value changes. Editing can be canceled by returning
18675 * false from the handler of this event.
18676 * @param {Editor} this
18677 * @param {Roo.tree.Node} node
18679 "beforenodeedit" : true
18683 Roo.tree.TreeEditor.superclass.constructor.call(this, field, config);
18687 tree.on('beforeclick', this.beforeNodeClick, this);
18688 tree.getTreeEl().on('mousedown', this.hide, this);
18689 this.on('complete', this.updateNode, this);
18690 this.on('beforestartedit', this.fitToTree, this);
18691 this.on('startedit', this.bindScroll, this, {delay:10});
18692 this.on('specialkey', this.onSpecialKey, this);
18695 Roo.extend(Roo.tree.TreeEditor, Roo.Editor, {
18697 * @cfg {String} alignment
18698 * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "l-l").
18704 * @cfg {Boolean} hideEl
18705 * True to hide the bound element while the editor is displayed (defaults to false)
18709 * @cfg {String} cls
18710 * CSS class to apply to the editor (defaults to "x-small-editor x-tree-editor")
18712 cls: "x-small-editor x-tree-editor",
18714 * @cfg {Boolean} shim
18715 * True to shim the editor if selects/iframes could be displayed beneath it (defaults to false)
18721 * @cfg {Number} maxWidth
18722 * The maximum width in pixels of the editor field (defaults to 250). Note that if the maxWidth would exceed
18723 * the containing tree element's size, it will be automatically limited for you to the container width, taking
18724 * scroll and client offsets into account prior to each edit.
18731 fitToTree : function(ed, el){
18732 var td = this.tree.getTreeEl().dom, nd = el.dom;
18733 if(td.scrollLeft > nd.offsetLeft){ // ensure the node left point is visible
18734 td.scrollLeft = nd.offsetLeft;
18738 (td.clientWidth > 20 ? td.clientWidth : td.offsetWidth) - Math.max(0, nd.offsetLeft-td.scrollLeft) - /*cushion*/5);
18739 this.setSize(w, '');
18741 return this.fireEvent('beforenodeedit', this, this.editNode);
18746 triggerEdit : function(node){
18747 this.completeEdit();
18748 this.editNode = node;
18749 this.startEdit(node.ui.textNode, node.text);
18753 bindScroll : function(){
18754 this.tree.getTreeEl().on('scroll', this.cancelEdit, this);
18758 beforeNodeClick : function(node, e){
18759 var sinceLast = (this.lastClick ? this.lastClick.getElapsed() : 0);
18760 this.lastClick = new Date();
18761 if(sinceLast > this.editDelay && this.tree.getSelectionModel().isSelected(node)){
18763 this.triggerEdit(node);
18770 updateNode : function(ed, value){
18771 this.tree.getTreeEl().un('scroll', this.cancelEdit, this);
18772 this.editNode.setText(value);
18776 onHide : function(){
18777 Roo.tree.TreeEditor.superclass.onHide.call(this);
18779 this.editNode.ui.focus();
18784 onSpecialKey : function(field, e){
18785 var k = e.getKey();
18789 }else if(k == e.ENTER && !e.hasModifier()){
18791 this.completeEdit();
18794 });//<Script type="text/javascript">
18797 * Ext JS Library 1.1.1
18798 * Copyright(c) 2006-2007, Ext JS, LLC.
18800 * Originally Released Under LGPL - original licence link has changed is not relivant.
18803 * <script type="text/javascript">
18807 * Not documented??? - probably should be...
18810 Roo.tree.ColumnNodeUI = Roo.extend(Roo.tree.TreeNodeUI, {
18811 //focus: Roo.emptyFn, // prevent odd scrolling behavior
18813 renderElements : function(n, a, targetNode, bulkRender){
18814 //consel.log("renderElements?");
18815 this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
18817 var t = n.getOwnerTree();
18818 var tid = Pman.Tab.Document_TypesTree.tree.el.id;
18820 var cols = t.columns;
18821 var bw = t.borderWidth;
18823 var href = a.href ? a.href : Roo.isGecko ? "" : "#";
18824 var cb = typeof a.checked == "boolean";
18825 var tx = String.format('{0}',n.text || (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
18826 var colcls = 'x-t-' + tid + '-c0';
18828 '<li class="x-tree-node">',
18831 '<div class="x-tree-node-el ', a.cls,'">',
18833 '<div class="x-tree-col ', colcls, '" style="width:', c.width-bw, 'px;">',
18836 '<span class="x-tree-node-indent">',this.indentMarkup,'</span>',
18837 '<img src="', this.emptyIcon, '" class="x-tree-ec-icon " />',
18838 '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',
18839 (a.icon ? ' x-tree-node-inline-icon' : ''),
18840 (a.iconCls ? ' '+a.iconCls : ''),
18841 '" unselectable="on" />',
18842 (cb ? ('<input class="x-tree-node-cb" type="checkbox" ' +
18843 (a.checked ? 'checked="checked" />' : ' />')) : ''),
18845 '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
18846 (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>',
18847 '<span unselectable="on" qtip="' + tx + '">',
18851 '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
18852 (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>'
18854 for(var i = 1, len = cols.length; i < len; i++){
18856 colcls = 'x-t-' + tid + '-c' +i;
18857 tx = String.format('{0}', (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
18858 buf.push('<div class="x-tree-col ', colcls, ' ' ,(c.cls?c.cls:''),'" style="width:',c.width-bw,'px;">',
18859 '<div class="x-tree-col-text" qtip="' + tx +'">',tx,"</div>",
18865 '<div class="x-clear"></div></div>',
18866 '<ul class="x-tree-node-ct" style="display:none;"></ul>',
18869 if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
18870 this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
18871 n.nextSibling.ui.getEl(), buf.join(""));
18873 this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
18875 var el = this.wrap.firstChild;
18877 this.elNode = el.firstChild;
18878 this.ranchor = el.childNodes[1];
18879 this.ctNode = this.wrap.childNodes[1];
18880 var cs = el.firstChild.childNodes;
18881 this.indentNode = cs[0];
18882 this.ecNode = cs[1];
18883 this.iconNode = cs[2];
18886 this.checkbox = cs[3];
18889 this.anchor = cs[index];
18891 this.textNode = cs[index].firstChild;
18893 //el.on("click", this.onClick, this);
18894 //el.on("dblclick", this.onDblClick, this);
18897 // console.log(this);
18899 initEvents : function(){
18900 Roo.tree.ColumnNodeUI.superclass.initEvents.call(this);
18903 var a = this.ranchor;
18905 var el = Roo.get(a);
18907 if(Roo.isOpera){ // opera render bug ignores the CSS
18908 el.setStyle("text-decoration", "none");
18911 el.on("click", this.onClick, this);
18912 el.on("dblclick", this.onDblClick, this);
18913 el.on("contextmenu", this.onContextMenu, this);
18917 /*onSelectedChange : function(state){
18920 this.addClass("x-tree-selected");
18923 this.removeClass("x-tree-selected");
18926 addClass : function(cls){
18928 Roo.fly(this.elRow).addClass(cls);
18934 removeClass : function(cls){
18936 Roo.fly(this.elRow).removeClass(cls);
18942 });//<Script type="text/javascript">
18946 * Ext JS Library 1.1.1
18947 * Copyright(c) 2006-2007, Ext JS, LLC.
18949 * Originally Released Under LGPL - original licence link has changed is not relivant.
18952 * <script type="text/javascript">
18957 * @class Roo.tree.ColumnTree
18958 * @extends Roo.data.TreePanel
18959 * @cfg {Object} columns Including width, header, renderer, cls, dataIndex
18960 * @cfg {int} borderWidth compined right/left border allowance
18962 * @param {String/HTMLElement/Element} el The container element
18963 * @param {Object} config
18965 Roo.tree.ColumnTree = function(el, config)
18967 Roo.tree.ColumnTree.superclass.constructor.call(this, el , config);
18971 * Fire this event on a container when it resizes
18972 * @param {int} w Width
18973 * @param {int} h Height
18977 this.on('resize', this.onResize, this);
18980 Roo.extend(Roo.tree.ColumnTree, Roo.tree.TreePanel, {
18984 borderWidth: Roo.isBorderBox ? 0 : 2,
18987 render : function(){
18988 // add the header.....
18990 Roo.tree.ColumnTree.superclass.render.apply(this);
18992 this.el.addClass('x-column-tree');
18994 this.headers = this.el.createChild(
18995 {cls:'x-tree-headers'},this.innerCt.dom);
18997 var cols = this.columns, c;
18998 var totalWidth = 0;
19000 var len = cols.length;
19001 for(var i = 0; i < len; i++){
19003 totalWidth += c.width;
19004 this.headEls.push(this.headers.createChild({
19005 cls:'x-tree-hd ' + (c.cls?c.cls+'-hd':''),
19007 cls:'x-tree-hd-text',
19010 style:'width:'+(c.width-this.borderWidth)+'px;'
19013 this.headers.createChild({cls:'x-clear'});
19014 // prevent floats from wrapping when clipped
19015 this.headers.setWidth(totalWidth);
19016 //this.innerCt.setWidth(totalWidth);
19017 this.innerCt.setStyle({ overflow: 'auto' });
19018 this.onResize(this.width, this.height);
19022 onResize : function(w,h)
19027 this.innerCt.setWidth(this.width);
19028 this.innerCt.setHeight(this.height-20);
19031 var cols = this.columns, c;
19032 var totalWidth = 0;
19034 var len = cols.length;
19035 for(var i = 0; i < len; i++){
19037 if (this.autoExpandColumn !== false && c.dataIndex == this.autoExpandColumn) {
19038 // it's the expander..
19039 expEl = this.headEls[i];
19042 totalWidth += c.width;
19046 expEl.setWidth( ((w - totalWidth)-this.borderWidth - 20));
19048 this.headers.setWidth(w-20);
19057 * Ext JS Library 1.1.1
19058 * Copyright(c) 2006-2007, Ext JS, LLC.
19060 * Originally Released Under LGPL - original licence link has changed is not relivant.
19063 * <script type="text/javascript">
19067 * @class Roo.menu.Menu
19068 * @extends Roo.util.Observable
19069 * A menu object. This is the container to which you add all other menu items. Menu can also serve a as a base class
19070 * when you want a specialzed menu based off of another component (like {@link Roo.menu.DateMenu} for example).
19072 * Creates a new Menu
19073 * @param {Object} config Configuration options
19075 Roo.menu.Menu = function(config){
19076 Roo.apply(this, config);
19077 this.id = this.id || Roo.id();
19080 * @event beforeshow
19081 * Fires before this menu is displayed
19082 * @param {Roo.menu.Menu} this
19086 * @event beforehide
19087 * Fires before this menu is hidden
19088 * @param {Roo.menu.Menu} this
19093 * Fires after this menu is displayed
19094 * @param {Roo.menu.Menu} this
19099 * Fires after this menu is hidden
19100 * @param {Roo.menu.Menu} this
19105 * Fires when this menu is clicked (or when the enter key is pressed while it is active)
19106 * @param {Roo.menu.Menu} this
19107 * @param {Roo.menu.Item} menuItem The menu item that was clicked
19108 * @param {Roo.EventObject} e
19113 * Fires when the mouse is hovering over this menu
19114 * @param {Roo.menu.Menu} this
19115 * @param {Roo.EventObject} e
19116 * @param {Roo.menu.Item} menuItem The menu item that was clicked
19121 * Fires when the mouse exits this menu
19122 * @param {Roo.menu.Menu} this
19123 * @param {Roo.EventObject} e
19124 * @param {Roo.menu.Item} menuItem The menu item that was clicked
19129 * Fires when a menu item contained in this menu is clicked
19130 * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
19131 * @param {Roo.EventObject} e
19135 if (this.registerMenu) {
19136 Roo.menu.MenuMgr.register(this);
19139 var mis = this.items;
19140 this.items = new Roo.util.MixedCollection();
19142 this.add.apply(this, mis);
19146 Roo.extend(Roo.menu.Menu, Roo.util.Observable, {
19148 * @cfg {Number} minWidth The minimum width of the menu in pixels (defaults to 120)
19152 * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop"
19153 * for bottom-right shadow (defaults to "sides")
19157 * @cfg {String} subMenuAlign The {@link Roo.Element#alignTo} anchor position value to use for submenus of
19158 * this menu (defaults to "tl-tr?")
19160 subMenuAlign : "tl-tr?",
19162 * @cfg {String} defaultAlign The default {@link Roo.Element#alignTo) anchor position value for this menu
19163 * relative to its element of origin (defaults to "tl-bl?")
19165 defaultAlign : "tl-bl?",
19167 * @cfg {Boolean} allowOtherMenus True to allow multiple menus to be displayed at the same time (defaults to false)
19169 allowOtherMenus : false,
19171 * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
19173 registerMenu : true,
19178 render : function(){
19182 var el = this.el = new Roo.Layer({
19184 shadow:this.shadow,
19186 parentEl: this.parentEl || document.body,
19190 this.keyNav = new Roo.menu.MenuNav(this);
19193 el.addClass("x-menu-plain");
19196 el.addClass(this.cls);
19198 // generic focus element
19199 this.focusEl = el.createChild({
19200 tag: "a", cls: "x-menu-focus", href: "#", onclick: "return false;", tabIndex:"-1"
19202 var ul = el.createChild({tag: "ul", cls: "x-menu-list"});
19203 ul.on(Roo.isTouch ? 'touchstart' : 'click' , this.onClick, this);
19205 ul.on("mouseover", this.onMouseOver, this);
19206 ul.on("mouseout", this.onMouseOut, this);
19207 this.items.each(function(item){
19212 var li = document.createElement("li");
19213 li.className = "x-menu-list-item";
19214 ul.dom.appendChild(li);
19215 item.render(li, this);
19222 autoWidth : function(){
19223 var el = this.el, ul = this.ul;
19227 var w = this.width;
19230 }else if(Roo.isIE){
19231 el.setWidth(this.minWidth);
19232 var t = el.dom.offsetWidth; // force recalc
19233 el.setWidth(ul.getWidth()+el.getFrameWidth("lr"));
19238 delayAutoWidth : function(){
19241 this.awTask = new Roo.util.DelayedTask(this.autoWidth, this);
19243 this.awTask.delay(20);
19248 findTargetItem : function(e){
19249 var t = e.getTarget(".x-menu-list-item", this.ul, true);
19250 if(t && t.menuItemId){
19251 return this.items.get(t.menuItemId);
19256 onClick : function(e){
19257 Roo.log("menu.onClick");
19258 var t = this.findTargetItem(e);
19263 if (Roo.isTouch && e.type == 'touchstart' && t.menu && !t.disabled) {
19264 if(t == this.activeItem && t.shouldDeactivate(e)){
19265 this.activeItem.deactivate();
19266 delete this.activeItem;
19270 this.setActiveItem(t, true);
19278 this.fireEvent("click", this, t, e);
19282 setActiveItem : function(item, autoExpand){
19283 if(item != this.activeItem){
19284 if(this.activeItem){
19285 this.activeItem.deactivate();
19287 this.activeItem = item;
19288 item.activate(autoExpand);
19289 }else if(autoExpand){
19295 tryActivate : function(start, step){
19296 var items = this.items;
19297 for(var i = start, len = items.length; i >= 0 && i < len; i+= step){
19298 var item = items.get(i);
19299 if(!item.disabled && item.canActivate){
19300 this.setActiveItem(item, false);
19308 onMouseOver : function(e){
19310 if(t = this.findTargetItem(e)){
19311 if(t.canActivate && !t.disabled){
19312 this.setActiveItem(t, true);
19315 this.fireEvent("mouseover", this, e, t);
19319 onMouseOut : function(e){
19321 if(t = this.findTargetItem(e)){
19322 if(t == this.activeItem && t.shouldDeactivate(e)){
19323 this.activeItem.deactivate();
19324 delete this.activeItem;
19327 this.fireEvent("mouseout", this, e, t);
19331 * Read-only. Returns true if the menu is currently displayed, else false.
19334 isVisible : function(){
19335 return this.el && !this.hidden;
19339 * Displays this menu relative to another element
19340 * @param {String/HTMLElement/Roo.Element} element The element to align to
19341 * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
19342 * the element (defaults to this.defaultAlign)
19343 * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
19345 show : function(el, pos, parentMenu){
19346 this.parentMenu = parentMenu;
19350 this.fireEvent("beforeshow", this);
19351 this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
19355 * Displays this menu at a specific xy position
19356 * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
19357 * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
19359 showAt : function(xy, parentMenu, /* private: */_e){
19360 this.parentMenu = parentMenu;
19365 this.fireEvent("beforeshow", this);
19366 xy = this.el.adjustForConstraints(xy);
19370 this.hidden = false;
19372 this.fireEvent("show", this);
19375 focus : function(){
19377 this.doFocus.defer(50, this);
19381 doFocus : function(){
19383 this.focusEl.focus();
19388 * Hides this menu and optionally all parent menus
19389 * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
19391 hide : function(deep){
19392 if(this.el && this.isVisible()){
19393 this.fireEvent("beforehide", this);
19394 if(this.activeItem){
19395 this.activeItem.deactivate();
19396 this.activeItem = null;
19399 this.hidden = true;
19400 this.fireEvent("hide", this);
19402 if(deep === true && this.parentMenu){
19403 this.parentMenu.hide(true);
19408 * Addds one or more items of any type supported by the Menu class, or that can be converted into menu items.
19409 * Any of the following are valid:
19411 * <li>Any menu item object based on {@link Roo.menu.Item}</li>
19412 * <li>An HTMLElement object which will be converted to a menu item</li>
19413 * <li>A menu item config object that will be created as a new menu item</li>
19414 * <li>A string, which can either be '-' or 'separator' to add a menu separator, otherwise
19415 * it will be converted into a {@link Roo.menu.TextItem} and added</li>
19420 var menu = new Roo.menu.Menu();
19422 // Create a menu item to add by reference
19423 var menuItem = new Roo.menu.Item({ text: 'New Item!' });
19425 // Add a bunch of items at once using different methods.
19426 // Only the last item added will be returned.
19427 var item = menu.add(
19428 menuItem, // add existing item by ref
19429 'Dynamic Item', // new TextItem
19430 '-', // new separator
19431 { text: 'Config Item' } // new item by config
19434 * @param {Mixed} args One or more menu items, menu item configs or other objects that can be converted to menu items
19435 * @return {Roo.menu.Item} The menu item that was added, or the last one if multiple items were added
19438 var a = arguments, l = a.length, item;
19439 for(var i = 0; i < l; i++){
19441 if ((typeof(el) == "object") && el.xtype && el.xns) {
19442 el = Roo.factory(el, Roo.menu);
19445 if(el.render){ // some kind of Item
19446 item = this.addItem(el);
19447 }else if(typeof el == "string"){ // string
19448 if(el == "separator" || el == "-"){
19449 item = this.addSeparator();
19451 item = this.addText(el);
19453 }else if(el.tagName || el.el){ // element
19454 item = this.addElement(el);
19455 }else if(typeof el == "object"){ // must be menu item config?
19456 item = this.addMenuItem(el);
19463 * Returns this menu's underlying {@link Roo.Element} object
19464 * @return {Roo.Element} The element
19466 getEl : function(){
19474 * Adds a separator bar to the menu
19475 * @return {Roo.menu.Item} The menu item that was added
19477 addSeparator : function(){
19478 return this.addItem(new Roo.menu.Separator());
19482 * Adds an {@link Roo.Element} object to the menu
19483 * @param {String/HTMLElement/Roo.Element} el The element or DOM node to add, or its id
19484 * @return {Roo.menu.Item} The menu item that was added
19486 addElement : function(el){
19487 return this.addItem(new Roo.menu.BaseItem(el));
19491 * Adds an existing object based on {@link Roo.menu.Item} to the menu
19492 * @param {Roo.menu.Item} item The menu item to add
19493 * @return {Roo.menu.Item} The menu item that was added
19495 addItem : function(item){
19496 this.items.add(item);
19498 var li = document.createElement("li");
19499 li.className = "x-menu-list-item";
19500 this.ul.dom.appendChild(li);
19501 item.render(li, this);
19502 this.delayAutoWidth();
19508 * Creates a new {@link Roo.menu.Item} based an the supplied config object and adds it to the menu
19509 * @param {Object} config A MenuItem config object
19510 * @return {Roo.menu.Item} The menu item that was added
19512 addMenuItem : function(config){
19513 if(!(config instanceof Roo.menu.Item)){
19514 if(typeof config.checked == "boolean"){ // must be check menu item config?
19515 config = new Roo.menu.CheckItem(config);
19517 config = new Roo.menu.Item(config);
19520 return this.addItem(config);
19524 * Creates a new {@link Roo.menu.TextItem} with the supplied text and adds it to the menu
19525 * @param {String} text The text to display in the menu item
19526 * @return {Roo.menu.Item} The menu item that was added
19528 addText : function(text){
19529 return this.addItem(new Roo.menu.TextItem({ text : text }));
19533 * Inserts an existing object based on {@link Roo.menu.Item} to the menu at a specified index
19534 * @param {Number} index The index in the menu's list of current items where the new item should be inserted
19535 * @param {Roo.menu.Item} item The menu item to add
19536 * @return {Roo.menu.Item} The menu item that was added
19538 insert : function(index, item){
19539 this.items.insert(index, item);
19541 var li = document.createElement("li");
19542 li.className = "x-menu-list-item";
19543 this.ul.dom.insertBefore(li, this.ul.dom.childNodes[index]);
19544 item.render(li, this);
19545 this.delayAutoWidth();
19551 * Removes an {@link Roo.menu.Item} from the menu and destroys the object
19552 * @param {Roo.menu.Item} item The menu item to remove
19554 remove : function(item){
19555 this.items.removeKey(item.id);
19560 * Removes and destroys all items in the menu
19562 removeAll : function(){
19564 while(f = this.items.first()){
19570 // MenuNav is a private utility class used internally by the Menu
19571 Roo.menu.MenuNav = function(menu){
19572 Roo.menu.MenuNav.superclass.constructor.call(this, menu.el);
19573 this.scope = this.menu = menu;
19576 Roo.extend(Roo.menu.MenuNav, Roo.KeyNav, {
19577 doRelay : function(e, h){
19578 var k = e.getKey();
19579 if(!this.menu.activeItem && e.isNavKeyPress() && k != e.SPACE && k != e.RETURN){
19580 this.menu.tryActivate(0, 1);
19583 return h.call(this.scope || this, e, this.menu);
19586 up : function(e, m){
19587 if(!m.tryActivate(m.items.indexOf(m.activeItem)-1, -1)){
19588 m.tryActivate(m.items.length-1, -1);
19592 down : function(e, m){
19593 if(!m.tryActivate(m.items.indexOf(m.activeItem)+1, 1)){
19594 m.tryActivate(0, 1);
19598 right : function(e, m){
19600 m.activeItem.expandMenu(true);
19604 left : function(e, m){
19606 if(m.parentMenu && m.parentMenu.activeItem){
19607 m.parentMenu.activeItem.activate();
19611 enter : function(e, m){
19613 e.stopPropagation();
19614 m.activeItem.onClick(e);
19615 m.fireEvent("click", this, m.activeItem);
19621 * Ext JS Library 1.1.1
19622 * Copyright(c) 2006-2007, Ext JS, LLC.
19624 * Originally Released Under LGPL - original licence link has changed is not relivant.
19627 * <script type="text/javascript">
19631 * @class Roo.menu.MenuMgr
19632 * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
19635 Roo.menu.MenuMgr = function(){
19636 var menus, active, groups = {}, attached = false, lastShow = new Date();
19638 // private - called when first menu is created
19641 active = new Roo.util.MixedCollection();
19642 Roo.get(document).addKeyListener(27, function(){
19643 if(active.length > 0){
19650 function hideAll(){
19651 if(active && active.length > 0){
19652 var c = active.clone();
19653 c.each(function(m){
19660 function onHide(m){
19662 if(active.length < 1){
19663 Roo.get(document).un("mousedown", onMouseDown);
19669 function onShow(m){
19670 var last = active.last();
19671 lastShow = new Date();
19674 Roo.get(document).on("mousedown", onMouseDown);
19678 m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
19679 m.parentMenu.activeChild = m;
19680 }else if(last && last.isVisible()){
19681 m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
19686 function onBeforeHide(m){
19688 m.activeChild.hide();
19690 if(m.autoHideTimer){
19691 clearTimeout(m.autoHideTimer);
19692 delete m.autoHideTimer;
19697 function onBeforeShow(m){
19698 var pm = m.parentMenu;
19699 if(!pm && !m.allowOtherMenus){
19701 }else if(pm && pm.activeChild && active != m){
19702 pm.activeChild.hide();
19707 function onMouseDown(e){
19708 if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".x-menu")){
19714 function onBeforeCheck(mi, state){
19716 var g = groups[mi.group];
19717 for(var i = 0, l = g.length; i < l; i++){
19719 g[i].setChecked(false);
19728 * Hides all menus that are currently visible
19730 hideAll : function(){
19735 register : function(menu){
19739 menus[menu.id] = menu;
19740 menu.on("beforehide", onBeforeHide);
19741 menu.on("hide", onHide);
19742 menu.on("beforeshow", onBeforeShow);
19743 menu.on("show", onShow);
19744 var g = menu.group;
19745 if(g && menu.events["checkchange"]){
19749 groups[g].push(menu);
19750 menu.on("checkchange", onCheck);
19755 * Returns a {@link Roo.menu.Menu} object
19756 * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
19757 * be used to generate and return a new Menu instance.
19759 get : function(menu){
19760 if(typeof menu == "string"){ // menu id
19761 return menus[menu];
19762 }else if(menu.events){ // menu instance
19764 }else if(typeof menu.length == 'number'){ // array of menu items?
19765 return new Roo.menu.Menu({items:menu});
19766 }else{ // otherwise, must be a config
19767 return new Roo.menu.Menu(menu);
19772 unregister : function(menu){
19773 delete menus[menu.id];
19774 menu.un("beforehide", onBeforeHide);
19775 menu.un("hide", onHide);
19776 menu.un("beforeshow", onBeforeShow);
19777 menu.un("show", onShow);
19778 var g = menu.group;
19779 if(g && menu.events["checkchange"]){
19780 groups[g].remove(menu);
19781 menu.un("checkchange", onCheck);
19786 registerCheckable : function(menuItem){
19787 var g = menuItem.group;
19792 groups[g].push(menuItem);
19793 menuItem.on("beforecheckchange", onBeforeCheck);
19798 unregisterCheckable : function(menuItem){
19799 var g = menuItem.group;
19801 groups[g].remove(menuItem);
19802 menuItem.un("beforecheckchange", onBeforeCheck);
19808 * Ext JS Library 1.1.1
19809 * Copyright(c) 2006-2007, Ext JS, LLC.
19811 * Originally Released Under LGPL - original licence link has changed is not relivant.
19814 * <script type="text/javascript">
19819 * @class Roo.menu.BaseItem
19820 * @extends Roo.Component
19821 * The base class for all items that render into menus. BaseItem provides default rendering, activated state
19822 * management and base configuration options shared by all menu components.
19824 * Creates a new BaseItem
19825 * @param {Object} config Configuration options
19827 Roo.menu.BaseItem = function(config){
19828 Roo.menu.BaseItem.superclass.constructor.call(this, config);
19833 * Fires when this item is clicked
19834 * @param {Roo.menu.BaseItem} this
19835 * @param {Roo.EventObject} e
19840 * Fires when this item is activated
19841 * @param {Roo.menu.BaseItem} this
19845 * @event deactivate
19846 * Fires when this item is deactivated
19847 * @param {Roo.menu.BaseItem} this
19853 this.on("click", this.handler, this.scope, true);
19857 Roo.extend(Roo.menu.BaseItem, Roo.Component, {
19859 * @cfg {Function} handler
19860 * A function that will handle the click event of this menu item (defaults to undefined)
19863 * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to false)
19865 canActivate : false,
19868 * @cfg {Boolean} hidden True to prevent creation of this menu item (defaults to false)
19873 * @cfg {String} activeClass The CSS class to use when the item becomes activated (defaults to "x-menu-item-active")
19875 activeClass : "x-menu-item-active",
19877 * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to true)
19879 hideOnClick : true,
19881 * @cfg {Number} hideDelay Length of time in milliseconds to wait before hiding after a click (defaults to 100)
19886 ctype: "Roo.menu.BaseItem",
19889 actionMode : "container",
19892 render : function(container, parentMenu){
19893 this.parentMenu = parentMenu;
19894 Roo.menu.BaseItem.superclass.render.call(this, container);
19895 this.container.menuItemId = this.id;
19899 onRender : function(container, position){
19900 this.el = Roo.get(this.el);
19901 container.dom.appendChild(this.el.dom);
19905 onClick : function(e){
19906 if(!this.disabled && this.fireEvent("click", this, e) !== false
19907 && this.parentMenu.fireEvent("itemclick", this, e) !== false){
19908 this.handleClick(e);
19915 activate : function(){
19919 var li = this.container;
19920 li.addClass(this.activeClass);
19921 this.region = li.getRegion().adjust(2, 2, -2, -2);
19922 this.fireEvent("activate", this);
19927 deactivate : function(){
19928 this.container.removeClass(this.activeClass);
19929 this.fireEvent("deactivate", this);
19933 shouldDeactivate : function(e){
19934 return !this.region || !this.region.contains(e.getPoint());
19938 handleClick : function(e){
19939 if(this.hideOnClick){
19940 this.parentMenu.hide.defer(this.hideDelay, this.parentMenu, [true]);
19945 expandMenu : function(autoActivate){
19950 hideMenu : function(){
19955 * Ext JS Library 1.1.1
19956 * Copyright(c) 2006-2007, Ext JS, LLC.
19958 * Originally Released Under LGPL - original licence link has changed is not relivant.
19961 * <script type="text/javascript">
19965 * @class Roo.menu.Adapter
19966 * @extends Roo.menu.BaseItem
19967 * 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.
19968 * It provides basic rendering, activation management and enable/disable logic required to work in menus.
19970 * Creates a new Adapter
19971 * @param {Object} config Configuration options
19973 Roo.menu.Adapter = function(component, config){
19974 Roo.menu.Adapter.superclass.constructor.call(this, config);
19975 this.component = component;
19977 Roo.extend(Roo.menu.Adapter, Roo.menu.BaseItem, {
19979 canActivate : true,
19982 onRender : function(container, position){
19983 this.component.render(container);
19984 this.el = this.component.getEl();
19988 activate : function(){
19992 this.component.focus();
19993 this.fireEvent("activate", this);
19998 deactivate : function(){
19999 this.fireEvent("deactivate", this);
20003 disable : function(){
20004 this.component.disable();
20005 Roo.menu.Adapter.superclass.disable.call(this);
20009 enable : function(){
20010 this.component.enable();
20011 Roo.menu.Adapter.superclass.enable.call(this);
20015 * Ext JS Library 1.1.1
20016 * Copyright(c) 2006-2007, Ext JS, LLC.
20018 * Originally Released Under LGPL - original licence link has changed is not relivant.
20021 * <script type="text/javascript">
20025 * @class Roo.menu.TextItem
20026 * @extends Roo.menu.BaseItem
20027 * Adds a static text string to a menu, usually used as either a heading or group separator.
20028 * Note: old style constructor with text is still supported.
20031 * Creates a new TextItem
20032 * @param {Object} cfg Configuration
20034 Roo.menu.TextItem = function(cfg){
20035 if (typeof(cfg) == 'string') {
20038 Roo.apply(this,cfg);
20041 Roo.menu.TextItem.superclass.constructor.call(this);
20044 Roo.extend(Roo.menu.TextItem, Roo.menu.BaseItem, {
20046 * @cfg {Boolean} text Text to show on item.
20051 * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
20053 hideOnClick : false,
20055 * @cfg {String} itemCls The default CSS class to use for text items (defaults to "x-menu-text")
20057 itemCls : "x-menu-text",
20060 onRender : function(){
20061 var s = document.createElement("span");
20062 s.className = this.itemCls;
20063 s.innerHTML = this.text;
20065 Roo.menu.TextItem.superclass.onRender.apply(this, arguments);
20069 * Ext JS Library 1.1.1
20070 * Copyright(c) 2006-2007, Ext JS, LLC.
20072 * Originally Released Under LGPL - original licence link has changed is not relivant.
20075 * <script type="text/javascript">
20079 * @class Roo.menu.Separator
20080 * @extends Roo.menu.BaseItem
20081 * Adds a separator bar to a menu, used to divide logical groups of menu items. Generally you will
20082 * add one of these by using "-" in you call to add() or in your items config rather than creating one directly.
20084 * @param {Object} config Configuration options
20086 Roo.menu.Separator = function(config){
20087 Roo.menu.Separator.superclass.constructor.call(this, config);
20090 Roo.extend(Roo.menu.Separator, Roo.menu.BaseItem, {
20092 * @cfg {String} itemCls The default CSS class to use for separators (defaults to "x-menu-sep")
20094 itemCls : "x-menu-sep",
20096 * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
20098 hideOnClick : false,
20101 onRender : function(li){
20102 var s = document.createElement("span");
20103 s.className = this.itemCls;
20104 s.innerHTML = " ";
20106 li.addClass("x-menu-sep-li");
20107 Roo.menu.Separator.superclass.onRender.apply(this, arguments);
20111 * Ext JS Library 1.1.1
20112 * Copyright(c) 2006-2007, Ext JS, LLC.
20114 * Originally Released Under LGPL - original licence link has changed is not relivant.
20117 * <script type="text/javascript">
20120 * @class Roo.menu.Item
20121 * @extends Roo.menu.BaseItem
20122 * A base class for all menu items that require menu-related functionality (like sub-menus) and are not static
20123 * display items. Item extends the base functionality of {@link Roo.menu.BaseItem} by adding menu-specific
20124 * activation and click handling.
20126 * Creates a new Item
20127 * @param {Object} config Configuration options
20129 Roo.menu.Item = function(config){
20130 Roo.menu.Item.superclass.constructor.call(this, config);
20132 this.menu = Roo.menu.MenuMgr.get(this.menu);
20135 Roo.extend(Roo.menu.Item, Roo.menu.BaseItem, {
20138 * @cfg {String} text
20139 * The text to show on the menu item.
20143 * @cfg {String} HTML to render in menu
20144 * The text to show on the menu item (HTML version).
20148 * @cfg {String} icon
20149 * The path to an icon to display in this menu item (defaults to Roo.BLANK_IMAGE_URL)
20153 * @cfg {String} itemCls The default CSS class to use for menu items (defaults to "x-menu-item")
20155 itemCls : "x-menu-item",
20157 * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to true)
20159 canActivate : true,
20161 * @cfg {Number} showDelay Length of time in milliseconds to wait before showing this item (defaults to 200)
20164 // doc'd in BaseItem
20168 ctype: "Roo.menu.Item",
20171 onRender : function(container, position){
20172 var el = document.createElement("a");
20173 el.hideFocus = true;
20174 el.unselectable = "on";
20175 el.href = this.href || "#";
20176 if(this.hrefTarget){
20177 el.target = this.hrefTarget;
20179 el.className = this.itemCls + (this.menu ? " x-menu-item-arrow" : "") + (this.cls ? " " + this.cls : "");
20181 var html = this.html.length ? this.html : String.format('{0}',this.text);
20183 el.innerHTML = String.format(
20184 '<img src="{0}" class="x-menu-item-icon {1}" />' + html,
20185 this.icon || Roo.BLANK_IMAGE_URL, this.iconCls || '');
20187 Roo.menu.Item.superclass.onRender.call(this, container, position);
20191 * Sets the text to display in this menu item
20192 * @param {String} text The text to display
20193 * @param {Boolean} isHTML true to indicate text is pure html.
20195 setText : function(text, isHTML){
20203 var html = this.html.length ? this.html : String.format('{0}',this.text);
20205 this.el.update(String.format(
20206 '<img src="{0}" class="x-menu-item-icon {2}">' + html,
20207 this.icon || Roo.BLANK_IMAGE_URL, this.text, this.iconCls || ''));
20208 this.parentMenu.autoWidth();
20213 handleClick : function(e){
20214 if(!this.href){ // if no link defined, stop the event automatically
20217 Roo.menu.Item.superclass.handleClick.apply(this, arguments);
20221 activate : function(autoExpand){
20222 if(Roo.menu.Item.superclass.activate.apply(this, arguments)){
20232 shouldDeactivate : function(e){
20233 if(Roo.menu.Item.superclass.shouldDeactivate.call(this, e)){
20234 if(this.menu && this.menu.isVisible()){
20235 return !this.menu.getEl().getRegion().contains(e.getPoint());
20243 deactivate : function(){
20244 Roo.menu.Item.superclass.deactivate.apply(this, arguments);
20249 expandMenu : function(autoActivate){
20250 if(!this.disabled && this.menu){
20251 clearTimeout(this.hideTimer);
20252 delete this.hideTimer;
20253 if(!this.menu.isVisible() && !this.showTimer){
20254 this.showTimer = this.deferExpand.defer(this.showDelay, this, [autoActivate]);
20255 }else if (this.menu.isVisible() && autoActivate){
20256 this.menu.tryActivate(0, 1);
20262 deferExpand : function(autoActivate){
20263 delete this.showTimer;
20264 this.menu.show(this.container, this.parentMenu.subMenuAlign || "tl-tr?", this.parentMenu);
20266 this.menu.tryActivate(0, 1);
20271 hideMenu : function(){
20272 clearTimeout(this.showTimer);
20273 delete this.showTimer;
20274 if(!this.hideTimer && this.menu && this.menu.isVisible()){
20275 this.hideTimer = this.deferHide.defer(this.hideDelay, this);
20280 deferHide : function(){
20281 delete this.hideTimer;
20286 * Ext JS Library 1.1.1
20287 * Copyright(c) 2006-2007, Ext JS, LLC.
20289 * Originally Released Under LGPL - original licence link has changed is not relivant.
20292 * <script type="text/javascript">
20296 * @class Roo.menu.CheckItem
20297 * @extends Roo.menu.Item
20298 * Adds a menu item that contains a checkbox by default, but can also be part of a radio group.
20300 * Creates a new CheckItem
20301 * @param {Object} config Configuration options
20303 Roo.menu.CheckItem = function(config){
20304 Roo.menu.CheckItem.superclass.constructor.call(this, config);
20307 * @event beforecheckchange
20308 * Fires before the checked value is set, providing an opportunity to cancel if needed
20309 * @param {Roo.menu.CheckItem} this
20310 * @param {Boolean} checked The new checked value that will be set
20312 "beforecheckchange" : true,
20314 * @event checkchange
20315 * Fires after the checked value has been set
20316 * @param {Roo.menu.CheckItem} this
20317 * @param {Boolean} checked The checked value that was set
20319 "checkchange" : true
20321 if(this.checkHandler){
20322 this.on('checkchange', this.checkHandler, this.scope);
20325 Roo.extend(Roo.menu.CheckItem, Roo.menu.Item, {
20327 * @cfg {String} group
20328 * All check items with the same group name will automatically be grouped into a single-select
20329 * radio button group (defaults to '')
20332 * @cfg {String} itemCls The default CSS class to use for check items (defaults to "x-menu-item x-menu-check-item")
20334 itemCls : "x-menu-item x-menu-check-item",
20336 * @cfg {String} groupClass The default CSS class to use for radio group check items (defaults to "x-menu-group-item")
20338 groupClass : "x-menu-group-item",
20341 * @cfg {Boolean} checked True to initialize this checkbox as checked (defaults to false). Note that
20342 * if this checkbox is part of a radio group (group = true) only the last item in the group that is
20343 * initialized with checked = true will be rendered as checked.
20348 ctype: "Roo.menu.CheckItem",
20351 onRender : function(c){
20352 Roo.menu.CheckItem.superclass.onRender.apply(this, arguments);
20354 this.el.addClass(this.groupClass);
20356 Roo.menu.MenuMgr.registerCheckable(this);
20358 this.checked = false;
20359 this.setChecked(true, true);
20364 destroy : function(){
20366 Roo.menu.MenuMgr.unregisterCheckable(this);
20368 Roo.menu.CheckItem.superclass.destroy.apply(this, arguments);
20372 * Set the checked state of this item
20373 * @param {Boolean} checked The new checked value
20374 * @param {Boolean} suppressEvent (optional) True to prevent the checkchange event from firing (defaults to false)
20376 setChecked : function(state, suppressEvent){
20377 if(this.checked != state && this.fireEvent("beforecheckchange", this, state) !== false){
20378 if(this.container){
20379 this.container[state ? "addClass" : "removeClass"]("x-menu-item-checked");
20381 this.checked = state;
20382 if(suppressEvent !== true){
20383 this.fireEvent("checkchange", this, state);
20389 handleClick : function(e){
20390 if(!this.disabled && !(this.checked && this.group)){// disable unselect on radio item
20391 this.setChecked(!this.checked);
20393 Roo.menu.CheckItem.superclass.handleClick.apply(this, arguments);
20397 * Ext JS Library 1.1.1
20398 * Copyright(c) 2006-2007, Ext JS, LLC.
20400 * Originally Released Under LGPL - original licence link has changed is not relivant.
20403 * <script type="text/javascript">
20407 * @class Roo.menu.DateItem
20408 * @extends Roo.menu.Adapter
20409 * A menu item that wraps the {@link Roo.DatPicker} component.
20411 * Creates a new DateItem
20412 * @param {Object} config Configuration options
20414 Roo.menu.DateItem = function(config){
20415 Roo.menu.DateItem.superclass.constructor.call(this, new Roo.DatePicker(config), config);
20416 /** The Roo.DatePicker object @type Roo.DatePicker */
20417 this.picker = this.component;
20418 this.addEvents({select: true});
20420 this.picker.on("render", function(picker){
20421 picker.getEl().swallowEvent("click");
20422 picker.container.addClass("x-menu-date-item");
20425 this.picker.on("select", this.onSelect, this);
20428 Roo.extend(Roo.menu.DateItem, Roo.menu.Adapter, {
20430 onSelect : function(picker, date){
20431 this.fireEvent("select", this, date, picker);
20432 Roo.menu.DateItem.superclass.handleClick.call(this);
20436 * Ext JS Library 1.1.1
20437 * Copyright(c) 2006-2007, Ext JS, LLC.
20439 * Originally Released Under LGPL - original licence link has changed is not relivant.
20442 * <script type="text/javascript">
20446 * @class Roo.menu.ColorItem
20447 * @extends Roo.menu.Adapter
20448 * A menu item that wraps the {@link Roo.ColorPalette} component.
20450 * Creates a new ColorItem
20451 * @param {Object} config Configuration options
20453 Roo.menu.ColorItem = function(config){
20454 Roo.menu.ColorItem.superclass.constructor.call(this, new Roo.ColorPalette(config), config);
20455 /** The Roo.ColorPalette object @type Roo.ColorPalette */
20456 this.palette = this.component;
20457 this.relayEvents(this.palette, ["select"]);
20458 if(this.selectHandler){
20459 this.on('select', this.selectHandler, this.scope);
20462 Roo.extend(Roo.menu.ColorItem, Roo.menu.Adapter);/*
20464 * Ext JS Library 1.1.1
20465 * Copyright(c) 2006-2007, Ext JS, LLC.
20467 * Originally Released Under LGPL - original licence link has changed is not relivant.
20470 * <script type="text/javascript">
20475 * @class Roo.menu.DateMenu
20476 * @extends Roo.menu.Menu
20477 * A menu containing a {@link Roo.menu.DateItem} component (which provides a date picker).
20479 * Creates a new DateMenu
20480 * @param {Object} config Configuration options
20482 Roo.menu.DateMenu = function(config){
20483 Roo.menu.DateMenu.superclass.constructor.call(this, config);
20485 var di = new Roo.menu.DateItem(config);
20488 * The {@link Roo.DatePicker} instance for this DateMenu
20491 this.picker = di.picker;
20494 * @param {DatePicker} picker
20495 * @param {Date} date
20497 this.relayEvents(di, ["select"]);
20498 this.on('beforeshow', function(){
20500 this.picker.hideMonthPicker(false);
20504 Roo.extend(Roo.menu.DateMenu, Roo.menu.Menu, {
20508 * Ext JS Library 1.1.1
20509 * Copyright(c) 2006-2007, Ext JS, LLC.
20511 * Originally Released Under LGPL - original licence link has changed is not relivant.
20514 * <script type="text/javascript">
20519 * @class Roo.menu.ColorMenu
20520 * @extends Roo.menu.Menu
20521 * A menu containing a {@link Roo.menu.ColorItem} component (which provides a basic color picker).
20523 * Creates a new ColorMenu
20524 * @param {Object} config Configuration options
20526 Roo.menu.ColorMenu = function(config){
20527 Roo.menu.ColorMenu.superclass.constructor.call(this, config);
20529 var ci = new Roo.menu.ColorItem(config);
20532 * The {@link Roo.ColorPalette} instance for this ColorMenu
20533 * @type ColorPalette
20535 this.palette = ci.palette;
20538 * @param {ColorPalette} palette
20539 * @param {String} color
20541 this.relayEvents(ci, ["select"]);
20543 Roo.extend(Roo.menu.ColorMenu, Roo.menu.Menu);/*
20545 * Ext JS Library 1.1.1
20546 * Copyright(c) 2006-2007, Ext JS, LLC.
20548 * Originally Released Under LGPL - original licence link has changed is not relivant.
20551 * <script type="text/javascript">
20555 * @class Roo.form.Field
20556 * @extends Roo.BoxComponent
20557 * Base class for form fields that provides default event handling, sizing, value handling and other functionality.
20559 * Creates a new Field
20560 * @param {Object} config Configuration options
20562 Roo.form.Field = function(config){
20563 Roo.form.Field.superclass.constructor.call(this, config);
20566 Roo.extend(Roo.form.Field, Roo.BoxComponent, {
20568 * @cfg {String} fieldLabel Label to use when rendering a form.
20571 * @cfg {String} qtip Mouse over tip
20575 * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
20577 invalidClass : "x-form-invalid",
20579 * @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")
20581 invalidText : "The value in this field is invalid",
20583 * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
20585 focusClass : "x-form-focus",
20587 * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
20588 automatic validation (defaults to "keyup").
20590 validationEvent : "keyup",
20592 * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
20594 validateOnBlur : true,
20596 * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
20598 validationDelay : 250,
20600 * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
20601 * {tag: "input", type: "text", size: "20", autocomplete: "off"})
20603 defaultAutoCreate : {tag: "input", type: "text", size: "20", autocomplete: "off"},
20605 * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field")
20607 fieldClass : "x-form-field",
20609 * @cfg {String} msgTarget The location where error text should display. Should be one of the following values (defaults to 'qtip'):
20612 ----------- ----------------------------------------------------------------------
20613 qtip Display a quick tip when the user hovers over the field
20614 title Display a default browser title attribute popup
20615 under Add a block div beneath the field containing the error text
20616 side Add an error icon to the right of the field with a popup on hover
20617 [element id] Add the error text directly to the innerHTML of the specified element
20620 msgTarget : 'qtip',
20622 * @cfg {String} msgFx <b>Experimental</b> The effect used when displaying a validation message under the field (defaults to 'normal').
20627 * @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.
20632 * @cfg {Boolean} disabled True to disable the field (defaults to false).
20637 * @cfg {String} inputType The type attribute for input fields -- e.g. radio, text, password (defaults to "text").
20639 inputType : undefined,
20642 * @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).
20644 tabIndex : undefined,
20647 isFormField : true,
20652 * @property {Roo.Element} fieldEl
20653 * Element Containing the rendered Field (with label etc.)
20656 * @cfg {Mixed} value A value to initialize this field with.
20661 * @cfg {String} name The field's HTML name attribute.
20664 * @cfg {String} cls A CSS class to apply to the field's underlying element.
20668 initComponent : function(){
20669 Roo.form.Field.superclass.initComponent.call(this);
20673 * Fires when this field receives input focus.
20674 * @param {Roo.form.Field} this
20679 * Fires when this field loses input focus.
20680 * @param {Roo.form.Field} this
20684 * @event specialkey
20685 * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed. You can check
20686 * {@link Roo.EventObject#getKey} to determine which key was pressed.
20687 * @param {Roo.form.Field} this
20688 * @param {Roo.EventObject} e The event object
20693 * Fires just before the field blurs if the field value has changed.
20694 * @param {Roo.form.Field} this
20695 * @param {Mixed} newValue The new value
20696 * @param {Mixed} oldValue The original value
20701 * Fires after the field has been marked as invalid.
20702 * @param {Roo.form.Field} this
20703 * @param {String} msg The validation message
20708 * Fires after the field has been validated with no errors.
20709 * @param {Roo.form.Field} this
20714 * Fires after the key up
20715 * @param {Roo.form.Field} this
20716 * @param {Roo.EventObject} e The event Object
20723 * Returns the name attribute of the field if available
20724 * @return {String} name The field name
20726 getName: function(){
20727 return this.rendered && this.el.dom.name ? this.el.dom.name : (this.hiddenName || '');
20731 onRender : function(ct, position){
20732 Roo.form.Field.superclass.onRender.call(this, ct, position);
20734 var cfg = this.getAutoCreate();
20736 cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
20738 if (!cfg.name.length) {
20741 if(this.inputType){
20742 cfg.type = this.inputType;
20744 this.el = ct.createChild(cfg, position);
20746 var type = this.el.dom.type;
20748 if(type == 'password'){
20751 this.el.addClass('x-form-'+type);
20754 this.el.dom.readOnly = true;
20756 if(this.tabIndex !== undefined){
20757 this.el.dom.setAttribute('tabIndex', this.tabIndex);
20760 this.el.addClass([this.fieldClass, this.cls]);
20765 * Apply the behaviors of this component to an existing element. <b>This is used instead of render().</b>
20766 * @param {String/HTMLElement/Element} el The id of the node, a DOM node or an existing Element
20767 * @return {Roo.form.Field} this
20769 applyTo : function(target){
20770 this.allowDomMove = false;
20771 this.el = Roo.get(target);
20772 this.render(this.el.dom.parentNode);
20777 initValue : function(){
20778 if(this.value !== undefined){
20779 this.setValue(this.value);
20780 }else if(this.el.dom.value.length > 0){
20781 this.setValue(this.el.dom.value);
20786 * Returns true if this field has been changed since it was originally loaded and is not disabled.
20788 isDirty : function() {
20789 if(this.disabled) {
20792 return String(this.getValue()) !== String(this.originalValue);
20796 afterRender : function(){
20797 Roo.form.Field.superclass.afterRender.call(this);
20802 fireKey : function(e){
20803 //Roo.log('field ' + e.getKey());
20804 if(e.isNavKeyPress()){
20805 this.fireEvent("specialkey", this, e);
20810 * Resets the current field value to the originally loaded value and clears any validation messages
20812 reset : function(){
20813 this.setValue(this.resetValue);
20814 this.clearInvalid();
20818 initEvents : function(){
20819 // safari killled keypress - so keydown is now used..
20820 this.el.on("keydown" , this.fireKey, this);
20821 this.el.on("focus", this.onFocus, this);
20822 this.el.on("blur", this.onBlur, this);
20823 this.el.relayEvent('keyup', this);
20825 // reference to original value for reset
20826 this.originalValue = this.getValue();
20827 this.resetValue = this.getValue();
20831 onFocus : function(){
20832 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
20833 this.el.addClass(this.focusClass);
20835 if(!this.hasFocus){
20836 this.hasFocus = true;
20837 this.startValue = this.getValue();
20838 this.fireEvent("focus", this);
20842 beforeBlur : Roo.emptyFn,
20845 onBlur : function(){
20847 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
20848 this.el.removeClass(this.focusClass);
20850 this.hasFocus = false;
20851 if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
20854 var v = this.getValue();
20855 if(String(v) !== String(this.startValue)){
20856 this.fireEvent('change', this, v, this.startValue);
20858 this.fireEvent("blur", this);
20862 * Returns whether or not the field value is currently valid
20863 * @param {Boolean} preventMark True to disable marking the field invalid
20864 * @return {Boolean} True if the value is valid, else false
20866 isValid : function(preventMark){
20870 var restore = this.preventMark;
20871 this.preventMark = preventMark === true;
20872 var v = this.validateValue(this.processValue(this.getRawValue()));
20873 this.preventMark = restore;
20878 * Validates the field value
20879 * @return {Boolean} True if the value is valid, else false
20881 validate : function(){
20882 if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
20883 this.clearInvalid();
20889 processValue : function(value){
20894 // Subclasses should provide the validation implementation by overriding this
20895 validateValue : function(value){
20900 * Mark this field as invalid
20901 * @param {String} msg The validation message
20903 markInvalid : function(msg){
20904 if(!this.rendered || this.preventMark){ // not rendered
20908 var obj = (typeof(this.combo) != 'undefined') ? this.combo : this; // fix the combox array!!
20910 obj.el.addClass(this.invalidClass);
20911 msg = msg || this.invalidText;
20912 switch(this.msgTarget){
20914 obj.el.dom.qtip = msg;
20915 obj.el.dom.qclass = 'x-form-invalid-tip';
20916 if(Roo.QuickTips){ // fix for floating editors interacting with DND
20917 Roo.QuickTips.enable();
20921 this.el.dom.title = msg;
20925 var elp = this.el.findParent('.x-form-element', 5, true);
20926 this.errorEl = elp.createChild({cls:'x-form-invalid-msg'});
20927 this.errorEl.setWidth(elp.getWidth(true)-20);
20929 this.errorEl.update(msg);
20930 Roo.form.Field.msgFx[this.msgFx].show(this.errorEl, this);
20933 if(!this.errorIcon){
20934 var elp = this.el.findParent('.x-form-element', 5, true);
20935 this.errorIcon = elp.createChild({cls:'x-form-invalid-icon'});
20937 this.alignErrorIcon();
20938 this.errorIcon.dom.qtip = msg;
20939 this.errorIcon.dom.qclass = 'x-form-invalid-tip';
20940 this.errorIcon.show();
20941 this.on('resize', this.alignErrorIcon, this);
20944 var t = Roo.getDom(this.msgTarget);
20946 t.style.display = this.msgDisplay;
20949 this.fireEvent('invalid', this, msg);
20953 alignErrorIcon : function(){
20954 this.errorIcon.alignTo(this.el, 'tl-tr', [2, 0]);
20958 * Clear any invalid styles/messages for this field
20960 clearInvalid : function(){
20961 if(!this.rendered || this.preventMark){ // not rendered
20964 var obj = (typeof(this.combo) != 'undefined') ? this.combo : this; // fix the combox array!!
20966 obj.el.removeClass(this.invalidClass);
20967 switch(this.msgTarget){
20969 obj.el.dom.qtip = '';
20972 this.el.dom.title = '';
20976 Roo.form.Field.msgFx[this.msgFx].hide(this.errorEl, this);
20980 if(this.errorIcon){
20981 this.errorIcon.dom.qtip = '';
20982 this.errorIcon.hide();
20983 this.un('resize', this.alignErrorIcon, this);
20987 var t = Roo.getDom(this.msgTarget);
20989 t.style.display = 'none';
20992 this.fireEvent('valid', this);
20996 * Returns the raw data value which may or may not be a valid, defined value. To return a normalized value see {@link #getValue}.
20997 * @return {Mixed} value The field value
20999 getRawValue : function(){
21000 var v = this.el.getValue();
21006 * Returns the normalized data value (undefined or emptyText will be returned as ''). To return the raw value see {@link #getRawValue}.
21007 * @return {Mixed} value The field value
21009 getValue : function(){
21010 var v = this.el.getValue();
21016 * Sets the underlying DOM field's value directly, bypassing validation. To set the value with validation see {@link #setValue}.
21017 * @param {Mixed} value The value to set
21019 setRawValue : function(v){
21020 return this.el.dom.value = (v === null || v === undefined ? '' : v);
21024 * Sets a data value into the field and validates it. To set the value directly without validation see {@link #setRawValue}.
21025 * @param {Mixed} value The value to set
21027 setValue : function(v){
21030 this.el.dom.value = (v === null || v === undefined ? '' : v);
21035 adjustSize : function(w, h){
21036 var s = Roo.form.Field.superclass.adjustSize.call(this, w, h);
21037 s.width = this.adjustWidth(this.el.dom.tagName, s.width);
21041 adjustWidth : function(tag, w){
21042 tag = tag.toLowerCase();
21043 if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
21044 if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
21045 if(tag == 'input'){
21048 if(tag == 'textarea'){
21051 }else if(Roo.isOpera){
21052 if(tag == 'input'){
21055 if(tag == 'textarea'){
21065 // anything other than normal should be considered experimental
21066 Roo.form.Field.msgFx = {
21068 show: function(msgEl, f){
21069 msgEl.setDisplayed('block');
21072 hide : function(msgEl, f){
21073 msgEl.setDisplayed(false).update('');
21078 show: function(msgEl, f){
21079 msgEl.slideIn('t', {stopFx:true});
21082 hide : function(msgEl, f){
21083 msgEl.slideOut('t', {stopFx:true,useDisplay:true});
21088 show: function(msgEl, f){
21089 msgEl.fixDisplay();
21090 msgEl.alignTo(f.el, 'tl-tr');
21091 msgEl.slideIn('l', {stopFx:true});
21094 hide : function(msgEl, f){
21095 msgEl.slideOut('l', {stopFx:true,useDisplay:true});
21100 * Ext JS Library 1.1.1
21101 * Copyright(c) 2006-2007, Ext JS, LLC.
21103 * Originally Released Under LGPL - original licence link has changed is not relivant.
21106 * <script type="text/javascript">
21111 * @class Roo.form.TextField
21112 * @extends Roo.form.Field
21113 * Basic text field. Can be used as a direct replacement for traditional text inputs, or as the base
21114 * class for more sophisticated input controls (like {@link Roo.form.TextArea} and {@link Roo.form.ComboBox}).
21116 * Creates a new TextField
21117 * @param {Object} config Configuration options
21119 Roo.form.TextField = function(config){
21120 Roo.form.TextField.superclass.constructor.call(this, config);
21124 * Fires when the autosize function is triggered. The field may or may not have actually changed size
21125 * according to the default logic, but this event provides a hook for the developer to apply additional
21126 * logic at runtime to resize the field if needed.
21127 * @param {Roo.form.Field} this This text field
21128 * @param {Number} width The new field width
21134 Roo.extend(Roo.form.TextField, Roo.form.Field, {
21136 * @cfg {Boolean} grow True if this field should automatically grow and shrink to its content
21140 * @cfg {Number} growMin The minimum width to allow when grow = true (defaults to 30)
21144 * @cfg {Number} growMax The maximum width to allow when grow = true (defaults to 800)
21148 * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
21152 * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
21156 * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
21158 disableKeyFilter : false,
21160 * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
21164 * @cfg {Number} minLength Minimum input field length required (defaults to 0)
21168 * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
21170 maxLength : Number.MAX_VALUE,
21172 * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
21174 minLengthText : "The minimum length for this field is {0}",
21176 * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
21178 maxLengthText : "The maximum length for this field is {0}",
21180 * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
21182 selectOnFocus : false,
21184 * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
21186 blankText : "This field is required",
21188 * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
21189 * If available, this function will be called only after the basic validators all return true, and will be passed the
21190 * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
21194 * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
21195 * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
21196 * current field value. If the test fails, the field will be marked invalid using {@link #regexText}.
21200 * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
21204 * @cfg {String} emptyText The default text to display in an empty field - placeholder... (defaults to null).
21210 initEvents : function()
21212 if (this.emptyText) {
21213 this.el.attr('placeholder', this.emptyText);
21216 Roo.form.TextField.superclass.initEvents.call(this);
21217 if(this.validationEvent == 'keyup'){
21218 this.validationTask = new Roo.util.DelayedTask(this.validate, this);
21219 this.el.on('keyup', this.filterValidation, this);
21221 else if(this.validationEvent !== false){
21222 this.el.on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
21225 if(this.selectOnFocus){
21226 this.on("focus", this.preFocus, this);
21229 if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
21230 this.el.on("keypress", this.filterKeys, this);
21233 this.el.on("keyup", this.onKeyUp, this, {buffer:50});
21234 this.el.on("click", this.autoSize, this);
21236 if(this.el.is('input[type=password]') && Roo.isSafari){
21237 this.el.on('keydown', this.SafariOnKeyDown, this);
21241 processValue : function(value){
21242 if(this.stripCharsRe){
21243 var newValue = value.replace(this.stripCharsRe, '');
21244 if(newValue !== value){
21245 this.setRawValue(newValue);
21252 filterValidation : function(e){
21253 if(!e.isNavKeyPress()){
21254 this.validationTask.delay(this.validationDelay);
21259 onKeyUp : function(e){
21260 if(!e.isNavKeyPress()){
21266 * Resets the current field value to the originally-loaded value and clears any validation messages.
21269 reset : function(){
21270 Roo.form.TextField.superclass.reset.call(this);
21276 preFocus : function(){
21278 if(this.selectOnFocus){
21279 this.el.dom.select();
21285 filterKeys : function(e){
21286 var k = e.getKey();
21287 if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
21290 var c = e.getCharCode(), cc = String.fromCharCode(c);
21291 if(Roo.isIE && (e.isSpecialKey() || !cc)){
21294 if(!this.maskRe.test(cc)){
21299 setValue : function(v){
21301 Roo.form.TextField.superclass.setValue.apply(this, arguments);
21307 * Validates a value according to the field's validation rules and marks the field as invalid
21308 * if the validation fails
21309 * @param {Mixed} value The value to validate
21310 * @return {Boolean} True if the value is valid, else false
21312 validateValue : function(value){
21313 if(value.length < 1) { // if it's blank
21314 if(this.allowBlank){
21315 this.clearInvalid();
21318 this.markInvalid(this.blankText);
21322 if(value.length < this.minLength){
21323 this.markInvalid(String.format(this.minLengthText, this.minLength));
21326 if(value.length > this.maxLength){
21327 this.markInvalid(String.format(this.maxLengthText, this.maxLength));
21331 var vt = Roo.form.VTypes;
21332 if(!vt[this.vtype](value, this)){
21333 this.markInvalid(this.vtypeText || vt[this.vtype +'Text']);
21337 if(typeof this.validator == "function"){
21338 var msg = this.validator(value);
21340 this.markInvalid(msg);
21344 if(this.regex && !this.regex.test(value)){
21345 this.markInvalid(this.regexText);
21352 * Selects text in this field
21353 * @param {Number} start (optional) The index where the selection should start (defaults to 0)
21354 * @param {Number} end (optional) The index where the selection should end (defaults to the text length)
21356 selectText : function(start, end){
21357 var v = this.getRawValue();
21359 start = start === undefined ? 0 : start;
21360 end = end === undefined ? v.length : end;
21361 var d = this.el.dom;
21362 if(d.setSelectionRange){
21363 d.setSelectionRange(start, end);
21364 }else if(d.createTextRange){
21365 var range = d.createTextRange();
21366 range.moveStart("character", start);
21367 range.moveEnd("character", v.length-end);
21374 * Automatically grows the field to accomodate the width of the text up to the maximum field width allowed.
21375 * This only takes effect if grow = true, and fires the autosize event.
21377 autoSize : function(){
21378 if(!this.grow || !this.rendered){
21382 this.metrics = Roo.util.TextMetrics.createInstance(this.el);
21385 var v = el.dom.value;
21386 var d = document.createElement('div');
21387 d.appendChild(document.createTextNode(v));
21391 var w = Math.min(this.growMax, Math.max(this.metrics.getWidth(v) + /* add extra padding */ 10, this.growMin));
21392 this.el.setWidth(w);
21393 this.fireEvent("autosize", this, w);
21397 SafariOnKeyDown : function(event)
21399 // this is a workaround for a password hang bug on chrome/ webkit.
21401 var isSelectAll = false;
21403 if(this.el.dom.selectionEnd > 0){
21404 isSelectAll = (this.el.dom.selectionEnd - this.el.dom.selectionStart - this.getValue().length == 0) ? true : false;
21406 if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
21407 event.preventDefault();
21412 if(isSelectAll){ // backspace and delete key
21414 event.preventDefault();
21415 // this is very hacky as keydown always get's upper case.
21417 var cc = String.fromCharCode(event.getCharCode());
21418 this.setValue( event.shiftKey ? cc : cc.toLowerCase());
21426 * Ext JS Library 1.1.1
21427 * Copyright(c) 2006-2007, Ext JS, LLC.
21429 * Originally Released Under LGPL - original licence link has changed is not relivant.
21432 * <script type="text/javascript">
21436 * @class Roo.form.Hidden
21437 * @extends Roo.form.TextField
21438 * Simple Hidden element used on forms
21440 * usage: form.add(new Roo.form.HiddenField({ 'name' : 'test1' }));
21443 * Creates a new Hidden form element.
21444 * @param {Object} config Configuration options
21449 // easy hidden field...
21450 Roo.form.Hidden = function(config){
21451 Roo.form.Hidden.superclass.constructor.call(this, config);
21454 Roo.extend(Roo.form.Hidden, Roo.form.TextField, {
21456 inputType: 'hidden',
21459 labelSeparator: '',
21461 itemCls : 'x-form-item-display-none'
21469 * Ext JS Library 1.1.1
21470 * Copyright(c) 2006-2007, Ext JS, LLC.
21472 * Originally Released Under LGPL - original licence link has changed is not relivant.
21475 * <script type="text/javascript">
21479 * @class Roo.form.TriggerField
21480 * @extends Roo.form.TextField
21481 * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
21482 * The trigger has no default action, so you must assign a function to implement the trigger click handler by
21483 * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
21484 * for which you can provide a custom implementation. For example:
21486 var trigger = new Roo.form.TriggerField();
21487 trigger.onTriggerClick = myTriggerFn;
21488 trigger.applyTo('my-field');
21491 * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
21492 * {@link Roo.form.DateField} and {@link Roo.form.ComboBox} are perfect examples of this.
21493 * @cfg {String} triggerClass An additional CSS class used to style the trigger button. The trigger will always get the
21494 * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
21496 * Create a new TriggerField.
21497 * @param {Object} config Configuration options (valid {@Roo.form.TextField} config options will also be applied
21498 * to the base TextField)
21500 Roo.form.TriggerField = function(config){
21501 this.mimicing = false;
21502 Roo.form.TriggerField.superclass.constructor.call(this, config);
21505 Roo.extend(Roo.form.TriggerField, Roo.form.TextField, {
21507 * @cfg {String} triggerClass A CSS class to apply to the trigger
21510 * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
21511 * {tag: "input", type: "text", size: "16", autocomplete: "off"})
21513 defaultAutoCreate : {tag: "input", type: "text", size: "16", autocomplete: "off"},
21515 * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
21519 /** @cfg {Boolean} grow @hide */
21520 /** @cfg {Number} growMin @hide */
21521 /** @cfg {Number} growMax @hide */
21527 autoSize: Roo.emptyFn,
21531 deferHeight : true,
21534 actionMode : 'wrap',
21536 onResize : function(w, h){
21537 Roo.form.TriggerField.superclass.onResize.apply(this, arguments);
21538 if(typeof w == 'number'){
21539 var x = w - this.trigger.getWidth();
21540 this.el.setWidth(this.adjustWidth('input', x));
21541 this.trigger.setStyle('left', x+'px');
21546 adjustSize : Roo.BoxComponent.prototype.adjustSize,
21549 getResizeEl : function(){
21554 getPositionEl : function(){
21559 alignErrorIcon : function(){
21560 this.errorIcon.alignTo(this.wrap, 'tl-tr', [2, 0]);
21564 onRender : function(ct, position){
21565 Roo.form.TriggerField.superclass.onRender.call(this, ct, position);
21566 this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
21567 this.trigger = this.wrap.createChild(this.triggerConfig ||
21568 {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.triggerClass});
21569 if(this.hideTrigger){
21570 this.trigger.setDisplayed(false);
21572 this.initTrigger();
21574 this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
21579 initTrigger : function(){
21580 this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
21581 this.trigger.addClassOnOver('x-form-trigger-over');
21582 this.trigger.addClassOnClick('x-form-trigger-click');
21586 onDestroy : function(){
21588 this.trigger.removeAllListeners();
21589 this.trigger.remove();
21592 this.wrap.remove();
21594 Roo.form.TriggerField.superclass.onDestroy.call(this);
21598 onFocus : function(){
21599 Roo.form.TriggerField.superclass.onFocus.call(this);
21600 if(!this.mimicing){
21601 this.wrap.addClass('x-trigger-wrap-focus');
21602 this.mimicing = true;
21603 Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
21604 if(this.monitorTab){
21605 this.el.on("keydown", this.checkTab, this);
21611 checkTab : function(e){
21612 if(e.getKey() == e.TAB){
21613 this.triggerBlur();
21618 onBlur : function(){
21623 mimicBlur : function(e, t){
21624 if(!this.wrap.contains(t) && this.validateBlur()){
21625 this.triggerBlur();
21630 triggerBlur : function(){
21631 this.mimicing = false;
21632 Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
21633 if(this.monitorTab){
21634 this.el.un("keydown", this.checkTab, this);
21636 this.wrap.removeClass('x-trigger-wrap-focus');
21637 Roo.form.TriggerField.superclass.onBlur.call(this);
21641 // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
21642 validateBlur : function(e, t){
21647 onDisable : function(){
21648 Roo.form.TriggerField.superclass.onDisable.call(this);
21650 this.wrap.addClass('x-item-disabled');
21655 onEnable : function(){
21656 Roo.form.TriggerField.superclass.onEnable.call(this);
21658 this.wrap.removeClass('x-item-disabled');
21663 onShow : function(){
21664 var ae = this.getActionEl();
21667 ae.dom.style.display = '';
21668 ae.dom.style.visibility = 'visible';
21674 onHide : function(){
21675 var ae = this.getActionEl();
21676 ae.dom.style.display = 'none';
21680 * The function that should handle the trigger's click event. This method does nothing by default until overridden
21681 * by an implementing function.
21683 * @param {EventObject} e
21685 onTriggerClick : Roo.emptyFn
21688 // TwinTriggerField is not a public class to be used directly. It is meant as an abstract base class
21689 // to be extended by an implementing class. For an example of implementing this class, see the custom
21690 // SearchField implementation here: http://extjs.com/deploy/ext/examples/form/custom.html
21691 Roo.form.TwinTriggerField = Roo.extend(Roo.form.TriggerField, {
21692 initComponent : function(){
21693 Roo.form.TwinTriggerField.superclass.initComponent.call(this);
21695 this.triggerConfig = {
21696 tag:'span', cls:'x-form-twin-triggers', cn:[
21697 {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger1Class},
21698 {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger2Class}
21702 getTrigger : function(index){
21703 return this.triggers[index];
21706 initTrigger : function(){
21707 var ts = this.trigger.select('.x-form-trigger', true);
21708 this.wrap.setStyle('overflow', 'hidden');
21709 var triggerField = this;
21710 ts.each(function(t, all, index){
21711 t.hide = function(){
21712 var w = triggerField.wrap.getWidth();
21713 this.dom.style.display = 'none';
21714 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
21716 t.show = function(){
21717 var w = triggerField.wrap.getWidth();
21718 this.dom.style.display = '';
21719 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
21721 var triggerIndex = 'Trigger'+(index+1);
21723 if(this['hide'+triggerIndex]){
21724 t.dom.style.display = 'none';
21726 t.on("click", this['on'+triggerIndex+'Click'], this, {preventDefault:true});
21727 t.addClassOnOver('x-form-trigger-over');
21728 t.addClassOnClick('x-form-trigger-click');
21730 this.triggers = ts.elements;
21733 onTrigger1Click : Roo.emptyFn,
21734 onTrigger2Click : Roo.emptyFn
21737 * Ext JS Library 1.1.1
21738 * Copyright(c) 2006-2007, Ext JS, LLC.
21740 * Originally Released Under LGPL - original licence link has changed is not relivant.
21743 * <script type="text/javascript">
21747 * @class Roo.form.TextArea
21748 * @extends Roo.form.TextField
21749 * Multiline text field. Can be used as a direct replacement for traditional textarea fields, plus adds
21750 * support for auto-sizing.
21752 * Creates a new TextArea
21753 * @param {Object} config Configuration options
21755 Roo.form.TextArea = function(config){
21756 Roo.form.TextArea.superclass.constructor.call(this, config);
21757 // these are provided exchanges for backwards compat
21758 // minHeight/maxHeight were replaced by growMin/growMax to be
21759 // compatible with TextField growing config values
21760 if(this.minHeight !== undefined){
21761 this.growMin = this.minHeight;
21763 if(this.maxHeight !== undefined){
21764 this.growMax = this.maxHeight;
21768 Roo.extend(Roo.form.TextArea, Roo.form.TextField, {
21770 * @cfg {Number} growMin The minimum height to allow when grow = true (defaults to 60)
21774 * @cfg {Number} growMax The maximum height to allow when grow = true (defaults to 1000)
21778 * @cfg {Boolean} preventScrollbars True to prevent scrollbars from appearing regardless of how much text is
21779 * in the field (equivalent to setting overflow: hidden, defaults to false)
21781 preventScrollbars: false,
21783 * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
21784 * {tag: "textarea", style: "width:300px;height:60px;", autocomplete: "off"})
21788 onRender : function(ct, position){
21790 this.defaultAutoCreate = {
21792 style:"width:300px;height:60px;",
21793 autocomplete: "off"
21796 Roo.form.TextArea.superclass.onRender.call(this, ct, position);
21798 this.textSizeEl = Roo.DomHelper.append(document.body, {
21799 tag: "pre", cls: "x-form-grow-sizer"
21801 if(this.preventScrollbars){
21802 this.el.setStyle("overflow", "hidden");
21804 this.el.setHeight(this.growMin);
21808 onDestroy : function(){
21809 if(this.textSizeEl){
21810 this.textSizeEl.parentNode.removeChild(this.textSizeEl);
21812 Roo.form.TextArea.superclass.onDestroy.call(this);
21816 onKeyUp : function(e){
21817 if(!e.isNavKeyPress() || e.getKey() == e.ENTER){
21823 * Automatically grows the field to accomodate the height of the text up to the maximum field height allowed.
21824 * This only takes effect if grow = true, and fires the autosize event if the height changes.
21826 autoSize : function(){
21827 if(!this.grow || !this.textSizeEl){
21831 var v = el.dom.value;
21832 var ts = this.textSizeEl;
21835 ts.appendChild(document.createTextNode(v));
21838 Roo.fly(ts).setWidth(this.el.getWidth());
21840 v = "  ";
21843 v = v.replace(/\n/g, '<p> </p>');
21845 v += " \n ";
21848 var h = Math.min(this.growMax, Math.max(ts.offsetHeight, this.growMin));
21849 if(h != this.lastHeight){
21850 this.lastHeight = h;
21851 this.el.setHeight(h);
21852 this.fireEvent("autosize", this, h);
21857 * Ext JS Library 1.1.1
21858 * Copyright(c) 2006-2007, Ext JS, LLC.
21860 * Originally Released Under LGPL - original licence link has changed is not relivant.
21863 * <script type="text/javascript">
21868 * @class Roo.form.NumberField
21869 * @extends Roo.form.TextField
21870 * Numeric text field that provides automatic keystroke filtering and numeric validation.
21872 * Creates a new NumberField
21873 * @param {Object} config Configuration options
21875 Roo.form.NumberField = function(config){
21876 Roo.form.NumberField.superclass.constructor.call(this, config);
21879 Roo.extend(Roo.form.NumberField, Roo.form.TextField, {
21881 * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field x-form-num-field")
21883 fieldClass: "x-form-field x-form-num-field",
21885 * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
21887 allowDecimals : true,
21889 * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
21891 decimalSeparator : ".",
21893 * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
21895 decimalPrecision : 2,
21897 * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
21899 allowNegative : true,
21901 * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
21903 minValue : Number.NEGATIVE_INFINITY,
21905 * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
21907 maxValue : Number.MAX_VALUE,
21909 * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
21911 minText : "The minimum value for this field is {0}",
21913 * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
21915 maxText : "The maximum value for this field is {0}",
21917 * @cfg {String} nanText Error text to display if the value is not a valid number. For example, this can happen
21918 * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
21920 nanText : "{0} is not a valid number",
21923 initEvents : function(){
21924 Roo.form.NumberField.superclass.initEvents.call(this);
21925 var allowed = "0123456789";
21926 if(this.allowDecimals){
21927 allowed += this.decimalSeparator;
21929 if(this.allowNegative){
21932 this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
21933 var keyPress = function(e){
21934 var k = e.getKey();
21935 if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
21938 var c = e.getCharCode();
21939 if(allowed.indexOf(String.fromCharCode(c)) === -1){
21943 this.el.on("keypress", keyPress, this);
21947 validateValue : function(value){
21948 if(!Roo.form.NumberField.superclass.validateValue.call(this, value)){
21951 if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
21954 var num = this.parseValue(value);
21956 this.markInvalid(String.format(this.nanText, value));
21959 if(num < this.minValue){
21960 this.markInvalid(String.format(this.minText, this.minValue));
21963 if(num > this.maxValue){
21964 this.markInvalid(String.format(this.maxText, this.maxValue));
21970 getValue : function(){
21971 return this.fixPrecision(this.parseValue(Roo.form.NumberField.superclass.getValue.call(this)));
21975 parseValue : function(value){
21976 value = parseFloat(String(value).replace(this.decimalSeparator, "."));
21977 return isNaN(value) ? '' : value;
21981 fixPrecision : function(value){
21982 var nan = isNaN(value);
21983 if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
21984 return nan ? '' : value;
21986 return parseFloat(value).toFixed(this.decimalPrecision);
21989 setValue : function(v){
21990 v = this.fixPrecision(v);
21991 Roo.form.NumberField.superclass.setValue.call(this, String(v).replace(".", this.decimalSeparator));
21995 decimalPrecisionFcn : function(v){
21996 return Math.floor(v);
21999 beforeBlur : function(){
22000 var v = this.parseValue(this.getRawValue());
22007 * Ext JS Library 1.1.1
22008 * Copyright(c) 2006-2007, Ext JS, LLC.
22010 * Originally Released Under LGPL - original licence link has changed is not relivant.
22013 * <script type="text/javascript">
22017 * @class Roo.form.DateField
22018 * @extends Roo.form.TriggerField
22019 * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
22021 * Create a new DateField
22022 * @param {Object} config
22024 Roo.form.DateField = function(config){
22025 Roo.form.DateField.superclass.constructor.call(this, config);
22031 * Fires when a date is selected
22032 * @param {Roo.form.DateField} combo This combo box
22033 * @param {Date} date The date selected
22040 if(typeof this.minValue == "string") this.minValue = this.parseDate(this.minValue);
22041 if(typeof this.maxValue == "string") this.maxValue = this.parseDate(this.maxValue);
22042 this.ddMatch = null;
22043 if(this.disabledDates){
22044 var dd = this.disabledDates;
22046 for(var i = 0; i < dd.length; i++){
22048 if(i != dd.length-1) re += "|";
22050 this.ddMatch = new RegExp(re + ")");
22054 Roo.extend(Roo.form.DateField, Roo.form.TriggerField, {
22056 * @cfg {String} format
22057 * The default date format string which can be overriden for localization support. The format must be
22058 * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
22062 * @cfg {String} altFormats
22063 * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
22064 * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
22066 altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
22068 * @cfg {Array} disabledDays
22069 * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
22071 disabledDays : null,
22073 * @cfg {String} disabledDaysText
22074 * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
22076 disabledDaysText : "Disabled",
22078 * @cfg {Array} disabledDates
22079 * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
22080 * expression so they are very powerful. Some examples:
22082 * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
22083 * <li>["03/08", "09/16"] would disable those days for every year</li>
22084 * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
22085 * <li>["03/../2006"] would disable every day in March 2006</li>
22086 * <li>["^03"] would disable every day in every March</li>
22088 * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
22089 * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
22091 disabledDates : null,
22093 * @cfg {String} disabledDatesText
22094 * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
22096 disabledDatesText : "Disabled",
22098 * @cfg {Date/String} minValue
22099 * The minimum allowed date. Can be either a Javascript date object or a string date in a
22100 * valid format (defaults to null).
22104 * @cfg {Date/String} maxValue
22105 * The maximum allowed date. Can be either a Javascript date object or a string date in a
22106 * valid format (defaults to null).
22110 * @cfg {String} minText
22111 * The error text to display when the date in the cell is before minValue (defaults to
22112 * 'The date in this field must be after {minValue}').
22114 minText : "The date in this field must be equal to or after {0}",
22116 * @cfg {String} maxText
22117 * The error text to display when the date in the cell is after maxValue (defaults to
22118 * 'The date in this field must be before {maxValue}').
22120 maxText : "The date in this field must be equal to or before {0}",
22122 * @cfg {String} invalidText
22123 * The error text to display when the date in the field is invalid (defaults to
22124 * '{value} is not a valid date - it must be in the format {format}').
22126 invalidText : "{0} is not a valid date - it must be in the format {1}",
22128 * @cfg {String} triggerClass
22129 * An additional CSS class used to style the trigger button. The trigger will always get the
22130 * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
22131 * which displays a calendar icon).
22133 triggerClass : 'x-form-date-trigger',
22137 * @cfg {Boolean} useIso
22138 * if enabled, then the date field will use a hidden field to store the
22139 * real value as iso formated date. default (false)
22143 * @cfg {String/Object} autoCreate
22144 * A DomHelper element spec, or true for a default element spec (defaults to
22145 * {tag: "input", type: "text", size: "10", autocomplete: "off"})
22148 defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "off"},
22151 hiddenField: false,
22153 onRender : function(ct, position)
22155 Roo.form.DateField.superclass.onRender.call(this, ct, position);
22157 //this.el.dom.removeAttribute('name');
22158 Roo.log("Changing name?");
22159 this.el.dom.setAttribute('name', this.name + '____hidden___' );
22160 this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
22162 this.hiddenField.value = this.value ? this.formatDate(this.value, 'Y-m-d') : '';
22163 // prevent input submission
22164 this.hiddenName = this.name;
22171 validateValue : function(value)
22173 value = this.formatDate(value);
22174 if(!Roo.form.DateField.superclass.validateValue.call(this, value)){
22175 Roo.log('super failed');
22178 if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
22181 var svalue = value;
22182 value = this.parseDate(value);
22184 Roo.log('parse date failed' + svalue);
22185 this.markInvalid(String.format(this.invalidText, svalue, this.format));
22188 var time = value.getTime();
22189 if(this.minValue && time < this.minValue.getTime()){
22190 this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
22193 if(this.maxValue && time > this.maxValue.getTime()){
22194 this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
22197 if(this.disabledDays){
22198 var day = value.getDay();
22199 for(var i = 0; i < this.disabledDays.length; i++) {
22200 if(day === this.disabledDays[i]){
22201 this.markInvalid(this.disabledDaysText);
22206 var fvalue = this.formatDate(value);
22207 if(this.ddMatch && this.ddMatch.test(fvalue)){
22208 this.markInvalid(String.format(this.disabledDatesText, fvalue));
22215 // Provides logic to override the default TriggerField.validateBlur which just returns true
22216 validateBlur : function(){
22217 return !this.menu || !this.menu.isVisible();
22220 getName: function()
22222 // returns hidden if it's set..
22223 if (!this.rendered) {return ''};
22224 return !this.hiddenName && this.el.dom.name ? this.el.dom.name : (this.hiddenName || '');
22229 * Returns the current date value of the date field.
22230 * @return {Date} The date value
22232 getValue : function(){
22234 return this.hiddenField ?
22235 this.hiddenField.value :
22236 this.parseDate(Roo.form.DateField.superclass.getValue.call(this)) || "";
22240 * Sets the value of the date field. You can pass a date object or any string that can be parsed into a valid
22241 * date, using DateField.format as the date format, according to the same rules as {@link Date#parseDate}
22242 * (the default format used is "m/d/y").
22245 //All of these calls set the same date value (May 4, 2006)
22247 //Pass a date object:
22248 var dt = new Date('5/4/06');
22249 dateField.setValue(dt);
22251 //Pass a date string (default format):
22252 dateField.setValue('5/4/06');
22254 //Pass a date string (custom format):
22255 dateField.format = 'Y-m-d';
22256 dateField.setValue('2006-5-4');
22258 * @param {String/Date} date The date or valid date string
22260 setValue : function(date){
22261 if (this.hiddenField) {
22262 this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
22264 Roo.form.DateField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
22265 // make sure the value field is always stored as a date..
22266 this.value = this.parseDate(date);
22272 parseDate : function(value){
22273 if(!value || value instanceof Date){
22276 var v = Date.parseDate(value, this.format);
22277 if (!v && this.useIso) {
22278 v = Date.parseDate(value, 'Y-m-d');
22280 if(!v && this.altFormats){
22281 if(!this.altFormatsArray){
22282 this.altFormatsArray = this.altFormats.split("|");
22284 for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
22285 v = Date.parseDate(value, this.altFormatsArray[i]);
22292 formatDate : function(date, fmt){
22293 return (!date || !(date instanceof Date)) ?
22294 date : date.dateFormat(fmt || this.format);
22299 select: function(m, d){
22302 this.fireEvent('select', this, d);
22304 show : function(){ // retain focus styling
22308 this.focus.defer(10, this);
22309 var ml = this.menuListeners;
22310 this.menu.un("select", ml.select, this);
22311 this.menu.un("show", ml.show, this);
22312 this.menu.un("hide", ml.hide, this);
22317 // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
22318 onTriggerClick : function(){
22322 if(this.menu == null){
22323 this.menu = new Roo.menu.DateMenu();
22325 Roo.apply(this.menu.picker, {
22326 showClear: this.allowBlank,
22327 minDate : this.minValue,
22328 maxDate : this.maxValue,
22329 disabledDatesRE : this.ddMatch,
22330 disabledDatesText : this.disabledDatesText,
22331 disabledDays : this.disabledDays,
22332 disabledDaysText : this.disabledDaysText,
22333 format : this.useIso ? 'Y-m-d' : this.format,
22334 minText : String.format(this.minText, this.formatDate(this.minValue)),
22335 maxText : String.format(this.maxText, this.formatDate(this.maxValue))
22337 this.menu.on(Roo.apply({}, this.menuListeners, {
22340 this.menu.picker.setValue(this.getValue() || new Date());
22341 this.menu.show(this.el, "tl-bl?");
22344 beforeBlur : function(){
22345 var v = this.parseDate(this.getRawValue());
22355 isDirty : function() {
22356 if(this.disabled) {
22360 if(typeof(this.startValue) === 'undefined'){
22364 return String(this.getValue()) !== String(this.startValue);
22369 * Ext JS Library 1.1.1
22370 * Copyright(c) 2006-2007, Ext JS, LLC.
22372 * Originally Released Under LGPL - original licence link has changed is not relivant.
22375 * <script type="text/javascript">
22379 * @class Roo.form.MonthField
22380 * @extends Roo.form.TriggerField
22381 * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
22383 * Create a new MonthField
22384 * @param {Object} config
22386 Roo.form.MonthField = function(config){
22388 Roo.form.MonthField.superclass.constructor.call(this, config);
22394 * Fires when a date is selected
22395 * @param {Roo.form.MonthFieeld} combo This combo box
22396 * @param {Date} date The date selected
22403 if(typeof this.minValue == "string") this.minValue = this.parseDate(this.minValue);
22404 if(typeof this.maxValue == "string") this.maxValue = this.parseDate(this.maxValue);
22405 this.ddMatch = null;
22406 if(this.disabledDates){
22407 var dd = this.disabledDates;
22409 for(var i = 0; i < dd.length; i++){
22411 if(i != dd.length-1) re += "|";
22413 this.ddMatch = new RegExp(re + ")");
22417 Roo.extend(Roo.form.MonthField, Roo.form.TriggerField, {
22419 * @cfg {String} format
22420 * The default date format string which can be overriden for localization support. The format must be
22421 * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
22425 * @cfg {String} altFormats
22426 * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
22427 * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
22429 altFormats : "M Y|m/Y|m-y|m-Y|my|mY",
22431 * @cfg {Array} disabledDays
22432 * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
22434 disabledDays : [0,1,2,3,4,5,6],
22436 * @cfg {String} disabledDaysText
22437 * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
22439 disabledDaysText : "Disabled",
22441 * @cfg {Array} disabledDates
22442 * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
22443 * expression so they are very powerful. Some examples:
22445 * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
22446 * <li>["03/08", "09/16"] would disable those days for every year</li>
22447 * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
22448 * <li>["03/../2006"] would disable every day in March 2006</li>
22449 * <li>["^03"] would disable every day in every March</li>
22451 * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
22452 * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
22454 disabledDates : null,
22456 * @cfg {String} disabledDatesText
22457 * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
22459 disabledDatesText : "Disabled",
22461 * @cfg {Date/String} minValue
22462 * The minimum allowed date. Can be either a Javascript date object or a string date in a
22463 * valid format (defaults to null).
22467 * @cfg {Date/String} maxValue
22468 * The maximum allowed date. Can be either a Javascript date object or a string date in a
22469 * valid format (defaults to null).
22473 * @cfg {String} minText
22474 * The error text to display when the date in the cell is before minValue (defaults to
22475 * 'The date in this field must be after {minValue}').
22477 minText : "The date in this field must be equal to or after {0}",
22479 * @cfg {String} maxTextf
22480 * The error text to display when the date in the cell is after maxValue (defaults to
22481 * 'The date in this field must be before {maxValue}').
22483 maxText : "The date in this field must be equal to or before {0}",
22485 * @cfg {String} invalidText
22486 * The error text to display when the date in the field is invalid (defaults to
22487 * '{value} is not a valid date - it must be in the format {format}').
22489 invalidText : "{0} is not a valid date - it must be in the format {1}",
22491 * @cfg {String} triggerClass
22492 * An additional CSS class used to style the trigger button. The trigger will always get the
22493 * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
22494 * which displays a calendar icon).
22496 triggerClass : 'x-form-date-trigger',
22500 * @cfg {Boolean} useIso
22501 * if enabled, then the date field will use a hidden field to store the
22502 * real value as iso formated date. default (true)
22506 * @cfg {String/Object} autoCreate
22507 * A DomHelper element spec, or true for a default element spec (defaults to
22508 * {tag: "input", type: "text", size: "10", autocomplete: "off"})
22511 defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "off"},
22514 hiddenField: false,
22516 hideMonthPicker : false,
22518 onRender : function(ct, position)
22520 Roo.form.MonthField.superclass.onRender.call(this, ct, position);
22522 this.el.dom.removeAttribute('name');
22523 this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
22525 this.hiddenField.value = this.value ? this.formatDate(this.value, 'Y-m-d') : '';
22526 // prevent input submission
22527 this.hiddenName = this.name;
22534 validateValue : function(value)
22536 value = this.formatDate(value);
22537 if(!Roo.form.MonthField.superclass.validateValue.call(this, value)){
22540 if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
22543 var svalue = value;
22544 value = this.parseDate(value);
22546 this.markInvalid(String.format(this.invalidText, svalue, this.format));
22549 var time = value.getTime();
22550 if(this.minValue && time < this.minValue.getTime()){
22551 this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
22554 if(this.maxValue && time > this.maxValue.getTime()){
22555 this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
22558 /*if(this.disabledDays){
22559 var day = value.getDay();
22560 for(var i = 0; i < this.disabledDays.length; i++) {
22561 if(day === this.disabledDays[i]){
22562 this.markInvalid(this.disabledDaysText);
22568 var fvalue = this.formatDate(value);
22569 /*if(this.ddMatch && this.ddMatch.test(fvalue)){
22570 this.markInvalid(String.format(this.disabledDatesText, fvalue));
22578 // Provides logic to override the default TriggerField.validateBlur which just returns true
22579 validateBlur : function(){
22580 return !this.menu || !this.menu.isVisible();
22584 * Returns the current date value of the date field.
22585 * @return {Date} The date value
22587 getValue : function(){
22591 return this.hiddenField ?
22592 this.hiddenField.value :
22593 this.parseDate(Roo.form.MonthField.superclass.getValue.call(this)) || "";
22597 * Sets the value of the date field. You can pass a date object or any string that can be parsed into a valid
22598 * date, using MonthField.format as the date format, according to the same rules as {@link Date#parseDate}
22599 * (the default format used is "m/d/y").
22602 //All of these calls set the same date value (May 4, 2006)
22604 //Pass a date object:
22605 var dt = new Date('5/4/06');
22606 monthField.setValue(dt);
22608 //Pass a date string (default format):
22609 monthField.setValue('5/4/06');
22611 //Pass a date string (custom format):
22612 monthField.format = 'Y-m-d';
22613 monthField.setValue('2006-5-4');
22615 * @param {String/Date} date The date or valid date string
22617 setValue : function(date){
22618 Roo.log('month setValue' + date);
22619 // can only be first of month..
22621 var val = this.parseDate(date);
22623 if (this.hiddenField) {
22624 this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
22626 Roo.form.MonthField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
22627 this.value = this.parseDate(date);
22631 parseDate : function(value){
22632 if(!value || value instanceof Date){
22633 value = value ? Date.parseDate(value.format('Y-m') + '-01', 'Y-m-d') : null;
22636 var v = Date.parseDate(value, this.format);
22637 if (!v && this.useIso) {
22638 v = Date.parseDate(value, 'Y-m-d');
22642 v = Date.parseDate(v.format('Y-m') +'-01', 'Y-m-d');
22646 if(!v && this.altFormats){
22647 if(!this.altFormatsArray){
22648 this.altFormatsArray = this.altFormats.split("|");
22650 for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
22651 v = Date.parseDate(value, this.altFormatsArray[i]);
22658 formatDate : function(date, fmt){
22659 return (!date || !(date instanceof Date)) ?
22660 date : date.dateFormat(fmt || this.format);
22665 select: function(m, d){
22667 this.fireEvent('select', this, d);
22669 show : function(){ // retain focus styling
22673 this.focus.defer(10, this);
22674 var ml = this.menuListeners;
22675 this.menu.un("select", ml.select, this);
22676 this.menu.un("show", ml.show, this);
22677 this.menu.un("hide", ml.hide, this);
22681 // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
22682 onTriggerClick : function(){
22686 if(this.menu == null){
22687 this.menu = new Roo.menu.DateMenu();
22691 Roo.apply(this.menu.picker, {
22693 showClear: this.allowBlank,
22694 minDate : this.minValue,
22695 maxDate : this.maxValue,
22696 disabledDatesRE : this.ddMatch,
22697 disabledDatesText : this.disabledDatesText,
22699 format : this.useIso ? 'Y-m-d' : this.format,
22700 minText : String.format(this.minText, this.formatDate(this.minValue)),
22701 maxText : String.format(this.maxText, this.formatDate(this.maxValue))
22704 this.menu.on(Roo.apply({}, this.menuListeners, {
22712 // hide month picker get's called when we called by 'before hide';
22714 var ignorehide = true;
22715 p.hideMonthPicker = function(disableAnim){
22719 if(this.monthPicker){
22720 Roo.log("hideMonthPicker called");
22721 if(disableAnim === true){
22722 this.monthPicker.hide();
22724 this.monthPicker.slideOut('t', {duration:.2});
22725 p.setValue(new Date(m.picker.mpSelYear, m.picker.mpSelMonth, 1));
22726 p.fireEvent("select", this, this.value);
22732 Roo.log('picker set value');
22733 Roo.log(this.getValue());
22734 p.setValue(this.getValue() ? this.parseDate(this.getValue()) : new Date());
22735 m.show(this.el, 'tl-bl?');
22736 ignorehide = false;
22737 // this will trigger hideMonthPicker..
22740 // hidden the day picker
22741 Roo.select('.x-date-picker table', true).first().dom.style.visibility = "hidden";
22747 p.showMonthPicker.defer(100, p);
22753 beforeBlur : function(){
22754 var v = this.parseDate(this.getRawValue());
22760 /** @cfg {Boolean} grow @hide */
22761 /** @cfg {Number} growMin @hide */
22762 /** @cfg {Number} growMax @hide */
22769 * Ext JS Library 1.1.1
22770 * Copyright(c) 2006-2007, Ext JS, LLC.
22772 * Originally Released Under LGPL - original licence link has changed is not relivant.
22775 * <script type="text/javascript">
22780 * @class Roo.form.ComboBox
22781 * @extends Roo.form.TriggerField
22782 * A combobox control with support for autocomplete, remote-loading, paging and many other features.
22784 * Create a new ComboBox.
22785 * @param {Object} config Configuration options
22787 Roo.form.ComboBox = function(config){
22788 Roo.form.ComboBox.superclass.constructor.call(this, config);
22792 * Fires when the dropdown list is expanded
22793 * @param {Roo.form.ComboBox} combo This combo box
22798 * Fires when the dropdown list is collapsed
22799 * @param {Roo.form.ComboBox} combo This combo box
22803 * @event beforeselect
22804 * Fires before a list item is selected. Return false to cancel the selection.
22805 * @param {Roo.form.ComboBox} combo This combo box
22806 * @param {Roo.data.Record} record The data record returned from the underlying store
22807 * @param {Number} index The index of the selected item in the dropdown list
22809 'beforeselect' : true,
22812 * Fires when a list item is selected
22813 * @param {Roo.form.ComboBox} combo This combo box
22814 * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
22815 * @param {Number} index The index of the selected item in the dropdown list
22819 * @event beforequery
22820 * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
22821 * The event object passed has these properties:
22822 * @param {Roo.form.ComboBox} combo This combo box
22823 * @param {String} query The query
22824 * @param {Boolean} forceAll true to force "all" query
22825 * @param {Boolean} cancel true to cancel the query
22826 * @param {Object} e The query event object
22828 'beforequery': true,
22831 * Fires when the 'add' icon is pressed (add a listener to enable add button)
22832 * @param {Roo.form.ComboBox} combo This combo box
22837 * Fires when the 'edit' icon is pressed (add a listener to enable add button)
22838 * @param {Roo.form.ComboBox} combo This combo box
22839 * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
22845 if(this.transform){
22846 this.allowDomMove = false;
22847 var s = Roo.getDom(this.transform);
22848 if(!this.hiddenName){
22849 this.hiddenName = s.name;
22852 this.mode = 'local';
22853 var d = [], opts = s.options;
22854 for(var i = 0, len = opts.length;i < len; i++){
22856 var value = (Roo.isIE ? o.getAttributeNode('value').specified : o.hasAttribute('value')) ? o.value : o.text;
22858 this.value = value;
22860 d.push([value, o.text]);
22862 this.store = new Roo.data.SimpleStore({
22864 fields: ['value', 'text'],
22867 this.valueField = 'value';
22868 this.displayField = 'text';
22870 s.name = Roo.id(); // wipe out the name in case somewhere else they have a reference
22871 if(!this.lazyRender){
22872 this.target = true;
22873 this.el = Roo.DomHelper.insertBefore(s, this.autoCreate || this.defaultAutoCreate);
22874 s.parentNode.removeChild(s); // remove it
22875 this.render(this.el.parentNode);
22877 s.parentNode.removeChild(s); // remove it
22882 this.store = Roo.factory(this.store, Roo.data);
22885 this.selectedIndex = -1;
22886 if(this.mode == 'local'){
22887 if(config.queryDelay === undefined){
22888 this.queryDelay = 10;
22890 if(config.minChars === undefined){
22896 Roo.extend(Roo.form.ComboBox, Roo.form.TriggerField, {
22898 * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
22901 * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
22902 * rendering into an Roo.Editor, defaults to false)
22905 * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
22906 * {tag: "input", type: "text", size: "24", autocomplete: "off"})
22909 * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
22912 * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
22913 * the dropdown list (defaults to undefined, with no header element)
22917 * @cfg {String/Roo.Template} tpl The template to use to render the output
22921 defaultAutoCreate : {tag: "input", type: "text", size: "24", autocomplete: "off"},
22923 * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
22925 listWidth: undefined,
22927 * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
22928 * mode = 'remote' or 'text' if mode = 'local')
22930 displayField: undefined,
22932 * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
22933 * mode = 'remote' or 'value' if mode = 'local').
22934 * Note: use of a valueField requires the user make a selection
22935 * in order for a value to be mapped.
22937 valueField: undefined,
22941 * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
22942 * field's data value (defaults to the underlying DOM element's name)
22944 hiddenName: undefined,
22946 * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
22950 * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
22952 selectedClass: 'x-combo-selected',
22954 * @cfg {String} triggerClass An additional CSS class used to style the trigger button. The trigger will always get the
22955 * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
22956 * which displays a downward arrow icon).
22958 triggerClass : 'x-form-arrow-trigger',
22960 * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
22964 * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
22965 * anchor positions (defaults to 'tl-bl')
22967 listAlign: 'tl-bl?',
22969 * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
22973 * @cfg {String} triggerAction The action to execute when the trigger field is activated. Use 'all' to run the
22974 * query specified by the allQuery config option (defaults to 'query')
22976 triggerAction: 'query',
22978 * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
22979 * (defaults to 4, does not apply if editable = false)
22983 * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
22984 * delay (typeAheadDelay) if it matches a known value (defaults to false)
22988 * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
22989 * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
22993 * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
22994 * filter queries will execute with page start and limit parameters. Only applies when mode = 'remote' (defaults to 0)
22998 * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus. Only applies
22999 * when editable = true (defaults to false)
23001 selectOnFocus:false,
23003 * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
23005 queryParam: 'query',
23007 * @cfg {String} loadingText The text to display in the dropdown list while data is loading. Only applies
23008 * when mode = 'remote' (defaults to 'Loading...')
23010 loadingText: 'Loading...',
23012 * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
23016 * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
23020 * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
23021 * traditional select (defaults to true)
23025 * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
23029 * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
23033 * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
23034 * listWidth has a higher value)
23038 * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
23039 * allow the user to set arbitrary text into the field (defaults to false)
23041 forceSelection:false,
23043 * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
23044 * if typeAhead = true (defaults to 250)
23046 typeAheadDelay : 250,
23048 * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
23049 * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
23051 valueNotFoundText : undefined,
23053 * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
23055 blockFocus : false,
23058 * @cfg {Boolean} disableClear Disable showing of clear button.
23060 disableClear : false,
23062 * @cfg {Boolean} alwaysQuery Disable caching of results, and always send query
23064 alwaysQuery : false,
23070 // element that contains real text value.. (when hidden is used..)
23073 onRender : function(ct, position){
23074 Roo.form.ComboBox.superclass.onRender.call(this, ct, position);
23075 if(this.hiddenName){
23076 this.hiddenField = this.el.insertSibling({tag:'input', type:'hidden', name: this.hiddenName, id: (this.hiddenId||this.hiddenName)},
23078 this.hiddenField.value =
23079 this.hiddenValue !== undefined ? this.hiddenValue :
23080 this.value !== undefined ? this.value : '';
23082 // prevent input submission
23083 this.el.dom.removeAttribute('name');
23088 this.el.dom.setAttribute('autocomplete', 'off');
23091 var cls = 'x-combo-list';
23093 this.list = new Roo.Layer({
23094 shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
23097 var lw = this.listWidth || Math.max(this.wrap.getWidth(), this.minListWidth);
23098 this.list.setWidth(lw);
23099 this.list.swallowEvent('mousewheel');
23100 this.assetHeight = 0;
23103 this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
23104 this.assetHeight += this.header.getHeight();
23107 this.innerList = this.list.createChild({cls:cls+'-inner'});
23108 this.innerList.on('mouseover', this.onViewOver, this);
23109 this.innerList.on('mousemove', this.onViewMove, this);
23110 this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
23112 if(this.allowBlank && !this.pageSize && !this.disableClear){
23113 this.footer = this.list.createChild({cls:cls+'-ft'});
23114 this.pageTb = new Roo.Toolbar(this.footer);
23118 this.footer = this.list.createChild({cls:cls+'-ft'});
23119 this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
23120 {pageSize: this.pageSize});
23124 if (this.pageTb && this.allowBlank && !this.disableClear) {
23126 this.pageTb.add(new Roo.Toolbar.Fill(), {
23127 cls: 'x-btn-icon x-btn-clear',
23129 handler: function()
23132 _this.clearValue();
23133 _this.onSelect(false, -1);
23138 this.assetHeight += this.footer.getHeight();
23143 this.tpl = '<div class="'+cls+'-item">{' + this.displayField + '}</div>';
23146 this.view = new Roo.View(this.innerList, this.tpl, {
23147 singleSelect:true, store: this.store, selectedClass: this.selectedClass
23150 this.view.on('click', this.onViewClick, this);
23152 this.store.on('beforeload', this.onBeforeLoad, this);
23153 this.store.on('load', this.onLoad, this);
23154 this.store.on('loadexception', this.onLoadException, this);
23156 if(this.resizable){
23157 this.resizer = new Roo.Resizable(this.list, {
23158 pinned:true, handles:'se'
23160 this.resizer.on('resize', function(r, w, h){
23161 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
23162 this.listWidth = w;
23163 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
23164 this.restrictHeight();
23166 this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
23168 if(!this.editable){
23169 this.editable = true;
23170 this.setEditable(false);
23174 if (typeof(this.events.add.listeners) != 'undefined') {
23176 this.addicon = this.wrap.createChild(
23177 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });
23179 this.addicon.on('click', function(e) {
23180 this.fireEvent('add', this);
23183 if (typeof(this.events.edit.listeners) != 'undefined') {
23185 this.editicon = this.wrap.createChild(
23186 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });
23187 if (this.addicon) {
23188 this.editicon.setStyle('margin-left', '40px');
23190 this.editicon.on('click', function(e) {
23192 // we fire even if inothing is selected..
23193 this.fireEvent('edit', this, this.lastData );
23203 initEvents : function(){
23204 Roo.form.ComboBox.superclass.initEvents.call(this);
23206 this.keyNav = new Roo.KeyNav(this.el, {
23207 "up" : function(e){
23208 this.inKeyMode = true;
23212 "down" : function(e){
23213 if(!this.isExpanded()){
23214 this.onTriggerClick();
23216 this.inKeyMode = true;
23221 "enter" : function(e){
23222 this.onViewClick();
23226 "esc" : function(e){
23230 "tab" : function(e){
23231 this.onViewClick(false);
23232 this.fireEvent("specialkey", this, e);
23238 doRelay : function(foo, bar, hname){
23239 if(hname == 'down' || this.scope.isExpanded()){
23240 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
23247 this.queryDelay = Math.max(this.queryDelay || 10,
23248 this.mode == 'local' ? 10 : 250);
23249 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
23250 if(this.typeAhead){
23251 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
23253 if(this.editable !== false){
23254 this.el.on("keyup", this.onKeyUp, this);
23256 if(this.forceSelection){
23257 this.on('blur', this.doForce, this);
23261 onDestroy : function(){
23263 this.view.setStore(null);
23264 this.view.el.removeAllListeners();
23265 this.view.el.remove();
23266 this.view.purgeListeners();
23269 this.list.destroy();
23272 this.store.un('beforeload', this.onBeforeLoad, this);
23273 this.store.un('load', this.onLoad, this);
23274 this.store.un('loadexception', this.onLoadException, this);
23276 Roo.form.ComboBox.superclass.onDestroy.call(this);
23280 fireKey : function(e){
23281 if(e.isNavKeyPress() && !this.list.isVisible()){
23282 this.fireEvent("specialkey", this, e);
23287 onResize: function(w, h){
23288 Roo.form.ComboBox.superclass.onResize.apply(this, arguments);
23290 if(typeof w != 'number'){
23291 // we do not handle it!?!?
23294 var tw = this.trigger.getWidth();
23295 tw += this.addicon ? this.addicon.getWidth() : 0;
23296 tw += this.editicon ? this.editicon.getWidth() : 0;
23298 this.el.setWidth( this.adjustWidth('input', x));
23300 this.trigger.setStyle('left', x+'px');
23302 if(this.list && this.listWidth === undefined){
23303 var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
23304 this.list.setWidth(lw);
23305 this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
23313 * Allow or prevent the user from directly editing the field text. If false is passed,
23314 * the user will only be able to select from the items defined in the dropdown list. This method
23315 * is the runtime equivalent of setting the 'editable' config option at config time.
23316 * @param {Boolean} value True to allow the user to directly edit the field text
23318 setEditable : function(value){
23319 if(value == this.editable){
23322 this.editable = value;
23324 this.el.dom.setAttribute('readOnly', true);
23325 this.el.on('mousedown', this.onTriggerClick, this);
23326 this.el.addClass('x-combo-noedit');
23328 this.el.dom.setAttribute('readOnly', false);
23329 this.el.un('mousedown', this.onTriggerClick, this);
23330 this.el.removeClass('x-combo-noedit');
23335 onBeforeLoad : function(){
23336 if(!this.hasFocus){
23339 this.innerList.update(this.loadingText ?
23340 '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
23341 this.restrictHeight();
23342 this.selectedIndex = -1;
23346 onLoad : function(){
23347 if(!this.hasFocus){
23350 if(this.store.getCount() > 0){
23352 this.restrictHeight();
23353 if(this.lastQuery == this.allQuery){
23355 this.el.dom.select();
23357 if(!this.selectByValue(this.value, true)){
23358 this.select(0, true);
23362 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
23363 this.taTask.delay(this.typeAheadDelay);
23367 this.onEmptyResults();
23372 onLoadException : function()
23375 Roo.log(this.store.reader.jsonData);
23376 if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
23377 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
23383 onTypeAhead : function(){
23384 if(this.store.getCount() > 0){
23385 var r = this.store.getAt(0);
23386 var newValue = r.data[this.displayField];
23387 var len = newValue.length;
23388 var selStart = this.getRawValue().length;
23389 if(selStart != len){
23390 this.setRawValue(newValue);
23391 this.selectText(selStart, newValue.length);
23397 onSelect : function(record, index){
23398 if(this.fireEvent('beforeselect', this, record, index) !== false){
23399 this.setFromData(index > -1 ? record.data : false);
23401 this.fireEvent('select', this, record, index);
23406 * Returns the currently selected field value or empty string if no value is set.
23407 * @return {String} value The selected value
23409 getValue : function(){
23410 if(this.valueField){
23411 return typeof this.value != 'undefined' ? this.value : '';
23413 return Roo.form.ComboBox.superclass.getValue.call(this);
23417 * Clears any text/value currently set in the field
23419 clearValue : function(){
23420 if(this.hiddenField){
23421 this.hiddenField.value = '';
23424 this.setRawValue('');
23425 this.lastSelectionText = '';
23430 * Sets the specified value into the field. If the value finds a match, the corresponding record text
23431 * will be displayed in the field. If the value does not match the data value of an existing item,
23432 * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
23433 * Otherwise the field will be blank (although the value will still be set).
23434 * @param {String} value The value to match
23436 setValue : function(v){
23438 if(this.valueField){
23439 var r = this.findRecord(this.valueField, v);
23441 text = r.data[this.displayField];
23442 }else if(this.valueNotFoundText !== undefined){
23443 text = this.valueNotFoundText;
23446 this.lastSelectionText = text;
23447 if(this.hiddenField){
23448 this.hiddenField.value = v;
23450 Roo.form.ComboBox.superclass.setValue.call(this, text);
23454 * @property {Object} the last set data for the element
23459 * Sets the value of the field based on a object which is related to the record format for the store.
23460 * @param {Object} value the value to set as. or false on reset?
23462 setFromData : function(o){
23463 var dv = ''; // display value
23464 var vv = ''; // value value..
23466 if (this.displayField) {
23467 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
23469 // this is an error condition!!!
23470 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
23473 if(this.valueField){
23474 vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
23476 if(this.hiddenField){
23477 this.hiddenField.value = vv;
23479 this.lastSelectionText = dv;
23480 Roo.form.ComboBox.superclass.setValue.call(this, dv);
23484 // no hidden field.. - we store the value in 'value', but still display
23485 // display field!!!!
23486 this.lastSelectionText = dv;
23487 Roo.form.ComboBox.superclass.setValue.call(this, dv);
23493 reset : function(){
23494 // overridden so that last data is reset..
23495 this.setValue(this.resetValue);
23496 this.clearInvalid();
23497 this.lastData = false;
23499 this.view.clearSelections();
23503 findRecord : function(prop, value){
23505 if(this.store.getCount() > 0){
23506 this.store.each(function(r){
23507 if(r.data[prop] == value){
23517 getName: function()
23519 // returns hidden if it's set..
23520 if (!this.rendered) {return ''};
23521 return !this.hiddenName && this.el.dom.name ? this.el.dom.name : (this.hiddenName || '');
23525 onViewMove : function(e, t){
23526 this.inKeyMode = false;
23530 onViewOver : function(e, t){
23531 if(this.inKeyMode){ // prevent key nav and mouse over conflicts
23534 var item = this.view.findItemFromChild(t);
23536 var index = this.view.indexOf(item);
23537 this.select(index, false);
23542 onViewClick : function(doFocus)
23544 var index = this.view.getSelectedIndexes()[0];
23545 var r = this.store.getAt(index);
23547 this.onSelect(r, index);
23549 if(doFocus !== false && !this.blockFocus){
23555 restrictHeight : function(){
23556 this.innerList.dom.style.height = '';
23557 var inner = this.innerList.dom;
23558 var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
23559 this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
23560 this.list.beginUpdate();
23561 this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
23562 this.list.alignTo(this.el, this.listAlign);
23563 this.list.endUpdate();
23567 onEmptyResults : function(){
23572 * Returns true if the dropdown list is expanded, else false.
23574 isExpanded : function(){
23575 return this.list.isVisible();
23579 * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
23580 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
23581 * @param {String} value The data value of the item to select
23582 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
23583 * selected item if it is not currently in view (defaults to true)
23584 * @return {Boolean} True if the value matched an item in the list, else false
23586 selectByValue : function(v, scrollIntoView){
23587 if(v !== undefined && v !== null){
23588 var r = this.findRecord(this.valueField || this.displayField, v);
23590 this.select(this.store.indexOf(r), scrollIntoView);
23598 * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
23599 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
23600 * @param {Number} index The zero-based index of the list item to select
23601 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
23602 * selected item if it is not currently in view (defaults to true)
23604 select : function(index, scrollIntoView){
23605 this.selectedIndex = index;
23606 this.view.select(index);
23607 if(scrollIntoView !== false){
23608 var el = this.view.getNode(index);
23610 this.innerList.scrollChildIntoView(el, false);
23616 selectNext : function(){
23617 var ct = this.store.getCount();
23619 if(this.selectedIndex == -1){
23621 }else if(this.selectedIndex < ct-1){
23622 this.select(this.selectedIndex+1);
23628 selectPrev : function(){
23629 var ct = this.store.getCount();
23631 if(this.selectedIndex == -1){
23633 }else if(this.selectedIndex != 0){
23634 this.select(this.selectedIndex-1);
23640 onKeyUp : function(e){
23641 if(this.editable !== false && !e.isSpecialKey()){
23642 this.lastKey = e.getKey();
23643 this.dqTask.delay(this.queryDelay);
23648 validateBlur : function(){
23649 return !this.list || !this.list.isVisible();
23653 initQuery : function(){
23654 this.doQuery(this.getRawValue());
23658 doForce : function(){
23659 if(this.el.dom.value.length > 0){
23660 this.el.dom.value =
23661 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
23667 * Execute a query to filter the dropdown list. Fires the beforequery event prior to performing the
23668 * query allowing the query action to be canceled if needed.
23669 * @param {String} query The SQL query to execute
23670 * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
23671 * in the field than the minimum specified by the minChars config option. It also clears any filter previously
23672 * saved in the current store (defaults to false)
23674 doQuery : function(q, forceAll){
23675 if(q === undefined || q === null){
23680 forceAll: forceAll,
23684 if(this.fireEvent('beforequery', qe)===false || qe.cancel){
23688 forceAll = qe.forceAll;
23689 if(forceAll === true || (q.length >= this.minChars)){
23690 if(this.lastQuery != q || this.alwaysQuery){
23691 this.lastQuery = q;
23692 if(this.mode == 'local'){
23693 this.selectedIndex = -1;
23695 this.store.clearFilter();
23697 this.store.filter(this.displayField, q);
23701 this.store.baseParams[this.queryParam] = q;
23703 params: this.getParams(q)
23708 this.selectedIndex = -1;
23715 getParams : function(q){
23717 //p[this.queryParam] = q;
23720 p.limit = this.pageSize;
23726 * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
23728 collapse : function(){
23729 if(!this.isExpanded()){
23733 Roo.get(document).un('mousedown', this.collapseIf, this);
23734 Roo.get(document).un('mousewheel', this.collapseIf, this);
23735 if (!this.editable) {
23736 Roo.get(document).un('keydown', this.listKeyPress, this);
23738 this.fireEvent('collapse', this);
23742 collapseIf : function(e){
23743 if(!e.within(this.wrap) && !e.within(this.list)){
23749 * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
23751 expand : function(){
23752 if(this.isExpanded() || !this.hasFocus){
23755 this.list.alignTo(this.el, this.listAlign);
23757 Roo.get(document).on('mousedown', this.collapseIf, this);
23758 Roo.get(document).on('mousewheel', this.collapseIf, this);
23759 if (!this.editable) {
23760 Roo.get(document).on('keydown', this.listKeyPress, this);
23763 this.fireEvent('expand', this);
23767 // Implements the default empty TriggerField.onTriggerClick function
23768 onTriggerClick : function(){
23772 if(this.isExpanded()){
23774 if (!this.blockFocus) {
23779 this.hasFocus = true;
23780 if(this.triggerAction == 'all') {
23781 this.doQuery(this.allQuery, true);
23783 this.doQuery(this.getRawValue());
23785 if (!this.blockFocus) {
23790 listKeyPress : function(e)
23792 //Roo.log('listkeypress');
23793 // scroll to first matching element based on key pres..
23794 if (e.isSpecialKey()) {
23797 var k = String.fromCharCode(e.getKey()).toUpperCase();
23800 var csel = this.view.getSelectedNodes();
23801 var cselitem = false;
23803 var ix = this.view.indexOf(csel[0]);
23804 cselitem = this.store.getAt(ix);
23805 if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
23811 this.store.each(function(v) {
23813 // start at existing selection.
23814 if (cselitem.id == v.id) {
23820 if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
23821 match = this.store.indexOf(v);
23826 if (match === false) {
23827 return true; // no more action?
23830 this.view.select(match);
23831 var sn = Roo.get(this.view.getSelectedNodes()[0])
23832 sn.scrollIntoView(sn.dom.parentNode, false);
23836 * @cfg {Boolean} grow
23840 * @cfg {Number} growMin
23844 * @cfg {Number} growMax
23852 * Copyright(c) 2010-2012, Roo J Solutions Limited
23859 * @class Roo.form.ComboBoxArray
23860 * @extends Roo.form.TextField
23861 * A facebook style adder... for lists of email / people / countries etc...
23862 * pick multiple items from a combo box, and shows each one.
23864 * Fred [x] Brian [x] [Pick another |v]
23867 * For this to work: it needs various extra information
23868 * - normal combo problay has
23870 * + displayField, valueField
23872 * For our purpose...
23875 * If we change from 'extends' to wrapping...
23882 * Create a new ComboBoxArray.
23883 * @param {Object} config Configuration options
23887 Roo.form.ComboBoxArray = function(config)
23891 * @event beforeremove
23892 * Fires before remove the value from the list
23893 * @param {Roo.form.ComboBoxArray} _self This combo box array
23894 * @param {Roo.form.ComboBoxArray.Item} item removed item
23896 'beforeremove' : true,
23899 * Fires when remove the value from the list
23900 * @param {Roo.form.ComboBoxArray} _self This combo box array
23901 * @param {Roo.form.ComboBoxArray.Item} item removed item
23908 Roo.form.ComboBoxArray.superclass.constructor.call(this, config);
23910 this.items = new Roo.util.MixedCollection(false);
23912 // construct the child combo...
23922 Roo.extend(Roo.form.ComboBoxArray, Roo.form.TextField,
23925 * @cfg {Roo.form.Combo} combo The combo box that is wrapped
23930 // behavies liek a hiddne field
23931 inputType: 'hidden',
23933 * @cfg {Number} width The width of the box that displays the selected element
23940 * @cfg {String} name The name of the visable items on this form (eg. titles not ids)
23944 * @cfg {String} hiddenName The hidden name of the field, often contains an comma seperated list of names
23946 hiddenName : false,
23949 // private the array of items that are displayed..
23951 // private - the hidden field el.
23953 // private - the filed el..
23956 //validateValue : function() { return true; }, // all values are ok!
23957 //onAddClick: function() { },
23959 onRender : function(ct, position)
23962 // create the standard hidden element
23963 //Roo.form.ComboBoxArray.superclass.onRender.call(this, ct, position);
23966 // give fake names to child combo;
23967 this.combo.hiddenName = this.hiddenName ? (this.hiddenName+'-subcombo') : this.hiddenName;
23968 this.combo.name = this.name? (this.name+'-subcombo') : this.name;
23970 this.combo = Roo.factory(this.combo, Roo.form);
23971 this.combo.onRender(ct, position);
23972 if (typeof(this.combo.width) != 'undefined') {
23973 this.combo.onResize(this.combo.width,0);
23976 this.combo.initEvents();
23978 // assigned so form know we need to do this..
23979 this.store = this.combo.store;
23980 this.valueField = this.combo.valueField;
23981 this.displayField = this.combo.displayField ;
23984 this.combo.wrap.addClass('x-cbarray-grp');
23986 var cbwrap = this.combo.wrap.createChild(
23987 {tag: 'div', cls: 'x-cbarray-cb'},
23992 this.hiddenEl = this.combo.wrap.createChild({
23993 tag: 'input', type:'hidden' , name: this.hiddenName, value : ''
23995 this.el = this.combo.wrap.createChild({
23996 tag: 'input', type:'hidden' , name: this.name, value : ''
23998 // this.el.dom.removeAttribute("name");
24001 this.outerWrap = this.combo.wrap;
24002 this.wrap = cbwrap;
24004 this.outerWrap.setWidth(this.width);
24005 this.outerWrap.dom.removeChild(this.el.dom);
24007 this.wrap.dom.appendChild(this.el.dom);
24008 this.outerWrap.dom.removeChild(this.combo.trigger.dom);
24009 this.combo.wrap.dom.appendChild(this.combo.trigger.dom);
24011 this.combo.trigger.setStyle('position','relative');
24012 this.combo.trigger.setStyle('left', '0px');
24013 this.combo.trigger.setStyle('top', '2px');
24015 this.combo.el.setStyle('vertical-align', 'text-bottom');
24017 //this.trigger.setStyle('vertical-align', 'top');
24019 // this should use the code from combo really... on('add' ....)
24023 this.adder = this.outerWrap.createChild(
24024 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-adder', style: 'margin-left:2px'});
24026 this.adder.on('click', function(e) {
24027 _t.fireEvent('adderclick', this, e);
24031 //this.adder.on('click', this.onAddClick, _t);
24034 this.combo.on('select', function(cb, rec, ix) {
24035 this.addItem(rec.data);
24038 cb.el.dom.value = '';
24039 //cb.lastData = rec.data;
24048 getName: function()
24050 // returns hidden if it's set..
24051 if (!this.rendered) {return ''};
24052 return this.hiddenName ? this.hiddenName : this.name;
24057 onResize: function(w, h){
24060 // not sure if this is needed..
24061 //this.combo.onResize(w,h);
24063 if(typeof w != 'number'){
24064 // we do not handle it!?!?
24067 var tw = this.combo.trigger.getWidth();
24068 tw += this.addicon ? this.addicon.getWidth() : 0;
24069 tw += this.editicon ? this.editicon.getWidth() : 0;
24071 this.combo.el.setWidth( this.combo.adjustWidth('input', x));
24073 this.combo.trigger.setStyle('left', '0px');
24075 if(this.list && this.listWidth === undefined){
24076 var lw = Math.max(x + this.combo.trigger.getWidth(), this.combo.minListWidth);
24077 this.list.setWidth(lw);
24078 this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
24085 addItem: function(rec)
24087 var valueField = this.combo.valueField;
24088 var displayField = this.combo.displayField;
24089 if (this.items.indexOfKey(rec[valueField]) > -1) {
24090 //console.log("GOT " + rec.data.id);
24094 var x = new Roo.form.ComboBoxArray.Item({
24095 //id : rec[this.idField],
24097 displayField : displayField ,
24098 tipField : displayField ,
24102 this.items.add(rec[valueField],x);
24103 // add it before the element..
24104 this.updateHiddenEl();
24105 x.render(this.outerWrap, this.wrap.dom);
24106 // add the image handler..
24109 updateHiddenEl : function()
24112 if (!this.hiddenEl) {
24116 var idField = this.combo.valueField;
24118 this.items.each(function(f) {
24119 ar.push(f.data[idField]);
24122 this.hiddenEl.dom.value = ar.join(',');
24128 //Roo.form.ComboBoxArray.superclass.reset.call(this);
24129 this.items.each(function(f) {
24132 this.el.dom.value = '';
24133 if (this.hiddenEl) {
24134 this.hiddenEl.dom.value = '';
24138 getValue: function()
24140 return this.hiddenEl ? this.hiddenEl.dom.value : '';
24142 setValue: function(v) // not a valid action - must use addItems..
24149 if (this.store.isLocal && (typeof(v) == 'string')) {
24150 // then we can use the store to find the values..
24151 // comma seperated at present.. this needs to allow JSON based encoding..
24152 this.hiddenEl.value = v;
24154 Roo.each(v.split(','), function(k) {
24155 Roo.log("CHECK " + this.valueField + ',' + k);
24156 var li = this.store.query(this.valueField, k);
24161 add[this.valueField] = k;
24162 add[this.displayField] = li.item(0).data[this.displayField];
24168 if (typeof(v) == 'object' ) {
24169 // then let's assume it's an array of objects..
24170 Roo.each(v, function(l) {
24178 setFromData: function(v)
24180 // this recieves an object, if setValues is called.
24182 this.el.dom.value = v[this.displayField];
24183 this.hiddenEl.dom.value = v[this.valueField];
24184 if (typeof(v[this.valueField]) != 'string' || !v[this.valueField].length) {
24187 var kv = v[this.valueField];
24188 var dv = v[this.displayField];
24189 kv = typeof(kv) != 'string' ? '' : kv;
24190 dv = typeof(dv) != 'string' ? '' : dv;
24193 var keys = kv.split(',');
24194 var display = dv.split(',');
24195 for (var i = 0 ; i < keys.length; i++) {
24198 add[this.valueField] = keys[i];
24199 add[this.displayField] = display[i];
24207 * Validates the combox array value
24208 * @return {Boolean} True if the value is valid, else false
24210 validate : function(){
24211 if(this.disabled || this.validateValue(this.processValue(this.getValue()))){
24212 this.clearInvalid();
24218 validateValue : function(value){
24219 return Roo.form.ComboBoxArray.superclass.validateValue.call(this, this.getValue());
24227 isDirty : function() {
24228 if(this.disabled) {
24233 var d = Roo.decode(String(this.originalValue));
24235 return String(this.getValue()) !== String(this.originalValue);
24238 var originalValue = [];
24240 for (var i = 0; i < d.length; i++){
24241 originalValue.push(d[i][this.valueField]);
24244 return String(this.getValue()) !== String(originalValue.join(','));
24253 * @class Roo.form.ComboBoxArray.Item
24254 * @extends Roo.BoxComponent
24255 * A selected item in the list
24256 * Fred [x] Brian [x] [Pick another |v]
24259 * Create a new item.
24260 * @param {Object} config Configuration options
24263 Roo.form.ComboBoxArray.Item = function(config) {
24264 config.id = Roo.id();
24265 Roo.form.ComboBoxArray.Item.superclass.constructor.call(this, config);
24268 Roo.extend(Roo.form.ComboBoxArray.Item, Roo.BoxComponent, {
24271 displayField : false,
24275 defaultAutoCreate : {
24277 cls: 'x-cbarray-item',
24284 src : Roo.BLANK_IMAGE_URL ,
24292 onRender : function(ct, position)
24294 Roo.form.Field.superclass.onRender.call(this, ct, position);
24297 var cfg = this.getAutoCreate();
24298 this.el = ct.createChild(cfg, position);
24301 this.el.child('img').dom.setAttribute('src', Roo.BLANK_IMAGE_URL);
24303 this.el.child('div').dom.innerHTML = this.cb.renderer ?
24304 this.cb.renderer(this.data) :
24305 String.format('{0}',this.data[this.displayField]);
24308 this.el.child('div').dom.setAttribute('qtip',
24309 String.format('{0}',this.data[this.tipField])
24312 this.el.child('img').on('click', this.remove, this);
24316 remove : function()
24318 if(this.cb.disabled){
24322 if(false !== this.cb.fireEvent('beforeremove', this.cb, this)){
24323 this.cb.items.remove(this);
24324 this.el.child('img').un('click', this.remove, this);
24326 this.cb.updateHiddenEl();
24328 this.cb.fireEvent('remove', this.cb, this);
24334 * Ext JS Library 1.1.1
24335 * Copyright(c) 2006-2007, Ext JS, LLC.
24337 * Originally Released Under LGPL - original licence link has changed is not relivant.
24340 * <script type="text/javascript">
24343 * @class Roo.form.Checkbox
24344 * @extends Roo.form.Field
24345 * Single checkbox field. Can be used as a direct replacement for traditional checkbox fields.
24347 * Creates a new Checkbox
24348 * @param {Object} config Configuration options
24350 Roo.form.Checkbox = function(config){
24351 Roo.form.Checkbox.superclass.constructor.call(this, config);
24355 * Fires when the checkbox is checked or unchecked.
24356 * @param {Roo.form.Checkbox} this This checkbox
24357 * @param {Boolean} checked The new checked value
24363 Roo.extend(Roo.form.Checkbox, Roo.form.Field, {
24365 * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
24367 focusClass : undefined,
24369 * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
24371 fieldClass: "x-form-field",
24373 * @cfg {Boolean} checked True if the the checkbox should render already checked (defaults to false)
24377 * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
24378 * {tag: "input", type: "checkbox", autocomplete: "off"})
24380 defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "off"},
24382 * @cfg {String} boxLabel The text that appears beside the checkbox
24386 * @cfg {String} inputValue The value that should go into the generated input element's value attribute
24390 * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
24392 valueOff: '0', // value when not checked..
24394 actionMode : 'viewEl',
24397 itemCls : 'x-menu-check-item x-form-item',
24398 groupClass : 'x-menu-group-item',
24399 inputType : 'hidden',
24402 inSetChecked: false, // check that we are not calling self...
24404 inputElement: false, // real input element?
24405 basedOn: false, // ????
24407 isFormField: true, // not sure where this is needed!!!!
24409 onResize : function(){
24410 Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
24411 if(!this.boxLabel){
24412 this.el.alignTo(this.wrap, 'c-c');
24416 initEvents : function(){
24417 Roo.form.Checkbox.superclass.initEvents.call(this);
24418 this.el.on("click", this.onClick, this);
24419 this.el.on("change", this.onClick, this);
24423 getResizeEl : function(){
24427 getPositionEl : function(){
24432 onRender : function(ct, position){
24433 Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
24435 if(this.inputValue !== undefined){
24436 this.el.dom.value = this.inputValue;
24439 //this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
24440 this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
24441 var viewEl = this.wrap.createChild({
24442 tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
24443 this.viewEl = viewEl;
24444 this.wrap.on('click', this.onClick, this);
24446 this.el.on('DOMAttrModified', this.setFromHidden, this); //ff
24447 this.el.on('propertychange', this.setFromHidden, this); //ie
24452 this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
24453 // viewEl.on('click', this.onClick, this);
24455 //if(this.checked){
24456 this.setChecked(this.checked);
24458 //this.checked = this.el.dom;
24464 initValue : Roo.emptyFn,
24467 * Returns the checked state of the checkbox.
24468 * @return {Boolean} True if checked, else false
24470 getValue : function(){
24472 return String(this.el.dom.value) == String(this.inputValue ) ? this.inputValue : this.valueOff;
24474 return this.valueOff;
24479 onClick : function(){
24480 if (this.disabled) {
24483 this.setChecked(!this.checked);
24485 //if(this.el.dom.checked != this.checked){
24486 // this.setValue(this.el.dom.checked);
24491 * Sets the checked state of the checkbox.
24492 * On is always based on a string comparison between inputValue and the param.
24493 * @param {Boolean/String} value - the value to set
24494 * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
24496 setValue : function(v,suppressEvent){
24499 //this.checked = (v === true || v === 'true' || v == '1' || String(v).toLowerCase() == 'on');
24500 //if(this.el && this.el.dom){
24501 // this.el.dom.checked = this.checked;
24502 // this.el.dom.defaultChecked = this.checked;
24504 this.setChecked(String(v) === String(this.inputValue), suppressEvent);
24505 //this.fireEvent("check", this, this.checked);
24508 setChecked : function(state,suppressEvent)
24510 if (this.inSetChecked) {
24511 this.checked = state;
24517 this.wrap[state ? 'addClass' : 'removeClass']('x-menu-item-checked');
24519 this.checked = state;
24520 if(suppressEvent !== true){
24521 this.fireEvent('check', this, state);
24523 this.inSetChecked = true;
24524 this.el.dom.value = state ? this.inputValue : this.valueOff;
24525 this.inSetChecked = false;
24528 // handle setting of hidden value by some other method!!?!?
24529 setFromHidden: function()
24534 //console.log("SET FROM HIDDEN");
24535 //alert('setFrom hidden');
24536 this.setValue(this.el.dom.value);
24539 onDestroy : function()
24542 Roo.get(this.viewEl).remove();
24545 Roo.form.Checkbox.superclass.onDestroy.call(this);
24550 * Ext JS Library 1.1.1
24551 * Copyright(c) 2006-2007, Ext JS, LLC.
24553 * Originally Released Under LGPL - original licence link has changed is not relivant.
24556 * <script type="text/javascript">
24560 * @class Roo.form.Radio
24561 * @extends Roo.form.Checkbox
24562 * Single radio field. Same as Checkbox, but provided as a convenience for automatically setting the input type.
24563 * Radio grouping is handled automatically by the browser if you give each radio in a group the same name.
24565 * Creates a new Radio
24566 * @param {Object} config Configuration options
24568 Roo.form.Radio = function(){
24569 Roo.form.Radio.superclass.constructor.apply(this, arguments);
24571 Roo.extend(Roo.form.Radio, Roo.form.Checkbox, {
24572 inputType: 'radio',
24575 * If this radio is part of a group, it will return the selected value
24578 getGroupValue : function(){
24579 return this.el.up('form').child('input[name='+this.el.dom.name+']:checked', true).value;
24583 onRender : function(ct, position){
24584 Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
24586 if(this.inputValue !== undefined){
24587 this.el.dom.value = this.inputValue;
24590 this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
24591 //this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
24592 //var viewEl = this.wrap.createChild({
24593 // tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
24594 //this.viewEl = viewEl;
24595 //this.wrap.on('click', this.onClick, this);
24597 //this.el.on('DOMAttrModified', this.setFromHidden, this); //ff
24598 //this.el.on('propertychange', this.setFromHidden, this); //ie
24603 this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
24604 // viewEl.on('click', this.onClick, this);
24607 this.el.dom.checked = 'checked' ;
24613 });//<script type="text/javascript">
24616 * Based Ext JS Library 1.1.1
24617 * Copyright(c) 2006-2007, Ext JS, LLC.
24623 * @class Roo.HtmlEditorCore
24624 * @extends Roo.Component
24625 * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
24627 * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
24630 Roo.HtmlEditorCore = function(config){
24633 Roo.HtmlEditorCore.superclass.constructor.call(this, config);
24638 * @event initialize
24639 * Fires when the editor is fully initialized (including the iframe)
24640 * @param {Roo.HtmlEditorCore} this
24645 * Fires when the editor is first receives the focus. Any insertion must wait
24646 * until after this event.
24647 * @param {Roo.HtmlEditorCore} this
24651 * @event beforesync
24652 * Fires before the textarea is updated with content from the editor iframe. Return false
24653 * to cancel the sync.
24654 * @param {Roo.HtmlEditorCore} this
24655 * @param {String} html
24659 * @event beforepush
24660 * Fires before the iframe editor is updated with content from the textarea. Return false
24661 * to cancel the push.
24662 * @param {Roo.HtmlEditorCore} this
24663 * @param {String} html
24668 * Fires when the textarea is updated with content from the editor iframe.
24669 * @param {Roo.HtmlEditorCore} this
24670 * @param {String} html
24675 * Fires when the iframe editor is updated with content from the textarea.
24676 * @param {Roo.HtmlEditorCore} this
24677 * @param {String} html
24682 * @event editorevent
24683 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
24684 * @param {Roo.HtmlEditorCore} this
24689 // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
24691 // defaults : white / black...
24692 this.applyBlacklists();
24699 Roo.extend(Roo.HtmlEditorCore, Roo.Component, {
24703 * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field
24709 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
24714 * @cfg {Number} height (in pixels)
24718 * @cfg {Number} width (in pixels)
24723 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
24726 stylesheets: false,
24731 // private properties
24732 validationEvent : false,
24734 initialized : false,
24736 sourceEditMode : false,
24737 onFocus : Roo.emptyFn,
24739 hideMode:'offsets',
24743 // blacklist + whitelisted elements..
24750 * Protected method that will not generally be called directly. It
24751 * is called when the editor initializes the iframe with HTML contents. Override this method if you
24752 * want to change the initialization markup of the iframe (e.g. to add stylesheets).
24754 getDocMarkup : function(){
24757 Roo.log(this.stylesheets);
24759 // inherit styels from page...??
24760 if (this.stylesheets === false) {
24762 Roo.get(document.head).select('style').each(function(node) {
24763 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
24766 Roo.get(document.head).select('link').each(function(node) {
24767 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
24770 } else if (!this.stylesheets.length) {
24772 st = '<style type="text/css">' +
24773 'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
24776 Roo.each(this.stylesheets, function(s) {
24777 st += '<link rel="stylesheet" type="text/css" href="' + s +'" />'
24782 st += '<style type="text/css">' +
24783 'IMG { cursor: pointer } ' +
24787 return '<html><head>' + st +
24788 //<style type="text/css">' +
24789 //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
24791 ' </head><body class="roo-htmleditor-body"></body></html>';
24795 onRender : function(ct, position)
24798 //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
24799 this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
24802 this.el.dom.style.border = '0 none';
24803 this.el.dom.setAttribute('tabIndex', -1);
24804 this.el.addClass('x-hidden hide');
24808 if(Roo.isIE){ // fix IE 1px bogus margin
24809 this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
24813 this.frameId = Roo.id();
24817 var iframe = this.owner.wrap.createChild({
24819 cls: 'form-control', // bootstrap..
24821 name: this.frameId,
24822 frameBorder : 'no',
24823 'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL : "javascript:false"
24828 this.iframe = iframe.dom;
24830 this.assignDocWin();
24832 this.doc.designMode = 'on';
24835 this.doc.write(this.getDocMarkup());
24839 var task = { // must defer to wait for browser to be ready
24841 //console.log("run task?" + this.doc.readyState);
24842 this.assignDocWin();
24843 if(this.doc.body || this.doc.readyState == 'complete'){
24845 this.doc.designMode="on";
24849 Roo.TaskMgr.stop(task);
24850 this.initEditor.defer(10, this);
24857 Roo.TaskMgr.start(task);
24864 onResize : function(w, h)
24866 Roo.log('resize: ' +w + ',' + h );
24867 //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
24871 if(typeof w == 'number'){
24873 this.iframe.style.width = w + 'px';
24875 if(typeof h == 'number'){
24877 this.iframe.style.height = h + 'px';
24879 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
24886 * Toggles the editor between standard and source edit mode.
24887 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
24889 toggleSourceEdit : function(sourceEditMode){
24891 this.sourceEditMode = sourceEditMode === true;
24893 if(this.sourceEditMode){
24895 Roo.get(this.iframe).addClass(['x-hidden','hide']); //FIXME - what's the BS styles for these
24898 Roo.get(this.iframe).removeClass(['x-hidden','hide']);
24899 //this.iframe.className = '';
24902 //this.setSize(this.owner.wrap.getSize());
24903 //this.fireEvent('editmodechange', this, this.sourceEditMode);
24910 * Protected method that will not generally be called directly. If you need/want
24911 * custom HTML cleanup, this is the method you should override.
24912 * @param {String} html The HTML to be cleaned
24913 * return {String} The cleaned HTML
24915 cleanHtml : function(html){
24916 html = String(html);
24917 if(html.length > 5){
24918 if(Roo.isSafari){ // strip safari nonsense
24919 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
24922 if(html == ' '){
24929 * HTML Editor -> Textarea
24930 * Protected method that will not generally be called directly. Syncs the contents
24931 * of the editor iframe with the textarea.
24933 syncValue : function(){
24934 if(this.initialized){
24935 var bd = (this.doc.body || this.doc.documentElement);
24936 //this.cleanUpPaste(); -- this is done else where and causes havoc..
24937 var html = bd.innerHTML;
24939 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
24940 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
24942 html = '<div style="'+m[0]+'">' + html + '</div>';
24945 html = this.cleanHtml(html);
24946 // fix up the special chars.. normaly like back quotes in word...
24947 // however we do not want to do this with chinese..
24948 html = html.replace(/([\x80-\uffff])/g, function (a, b) {
24949 var cc = b.charCodeAt();
24951 (cc >= 0x4E00 && cc < 0xA000 ) ||
24952 (cc >= 0x3400 && cc < 0x4E00 ) ||
24953 (cc >= 0xf900 && cc < 0xfb00 )
24959 if(this.owner.fireEvent('beforesync', this, html) !== false){
24960 this.el.dom.value = html;
24961 this.owner.fireEvent('sync', this, html);
24967 * Protected method that will not generally be called directly. Pushes the value of the textarea
24968 * into the iframe editor.
24970 pushValue : function(){
24971 if(this.initialized){
24972 var v = this.el.dom.value.trim();
24974 // if(v.length < 1){
24978 if(this.owner.fireEvent('beforepush', this, v) !== false){
24979 var d = (this.doc.body || this.doc.documentElement);
24981 this.cleanUpPaste();
24982 this.el.dom.value = d.innerHTML;
24983 this.owner.fireEvent('push', this, v);
24989 deferFocus : function(){
24990 this.focus.defer(10, this);
24994 focus : function(){
24995 if(this.win && !this.sourceEditMode){
25002 assignDocWin: function()
25004 var iframe = this.iframe;
25007 this.doc = iframe.contentWindow.document;
25008 this.win = iframe.contentWindow;
25010 // if (!Roo.get(this.frameId)) {
25013 // this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
25014 // this.win = Roo.get(this.frameId).dom.contentWindow;
25016 if (!Roo.get(this.frameId) && !iframe.contentDocument) {
25020 this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
25021 this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
25026 initEditor : function(){
25027 //console.log("INIT EDITOR");
25028 this.assignDocWin();
25032 this.doc.designMode="on";
25034 this.doc.write(this.getDocMarkup());
25037 var dbody = (this.doc.body || this.doc.documentElement);
25038 //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
25039 // this copies styles from the containing element into thsi one..
25040 // not sure why we need all of this..
25041 //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
25043 //var ss = this.el.getStyles( 'background-image', 'background-repeat');
25044 //ss['background-attachment'] = 'fixed'; // w3c
25045 dbody.bgProperties = 'fixed'; // ie
25046 //Roo.DomHelper.applyStyles(dbody, ss);
25047 Roo.EventManager.on(this.doc, {
25048 //'mousedown': this.onEditorEvent,
25049 'mouseup': this.onEditorEvent,
25050 'dblclick': this.onEditorEvent,
25051 'click': this.onEditorEvent,
25052 'keyup': this.onEditorEvent,
25057 Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
25059 if(Roo.isIE || Roo.isSafari || Roo.isOpera){
25060 Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
25062 this.initialized = true;
25064 this.owner.fireEvent('initialize', this);
25069 onDestroy : function(){
25075 //for (var i =0; i < this.toolbars.length;i++) {
25076 // // fixme - ask toolbars for heights?
25077 // this.toolbars[i].onDestroy();
25080 //this.wrap.dom.innerHTML = '';
25081 //this.wrap.remove();
25086 onFirstFocus : function(){
25088 this.assignDocWin();
25091 this.activated = true;
25094 if(Roo.isGecko){ // prevent silly gecko errors
25096 var s = this.win.getSelection();
25097 if(!s.focusNode || s.focusNode.nodeType != 3){
25098 var r = s.getRangeAt(0);
25099 r.selectNodeContents((this.doc.body || this.doc.documentElement));
25104 this.execCmd('useCSS', true);
25105 this.execCmd('styleWithCSS', false);
25108 this.owner.fireEvent('activate', this);
25112 adjustFont: function(btn){
25113 var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
25114 //if(Roo.isSafari){ // safari
25117 var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
25118 if(Roo.isSafari){ // safari
25119 var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
25120 v = (v < 10) ? 10 : v;
25121 v = (v > 48) ? 48 : v;
25122 v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
25127 v = Math.max(1, v+adjust);
25129 this.execCmd('FontSize', v );
25132 onEditorEvent : function(e){
25133 this.owner.fireEvent('editorevent', this, e);
25134 // this.updateToolbar();
25135 this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
25138 insertTag : function(tg)
25140 // could be a bit smarter... -> wrap the current selected tRoo..
25141 if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
25143 range = this.createRange(this.getSelection());
25144 var wrappingNode = this.doc.createElement(tg.toLowerCase());
25145 wrappingNode.appendChild(range.extractContents());
25146 range.insertNode(wrappingNode);
25153 this.execCmd("formatblock", tg);
25157 insertText : function(txt)
25161 var range = this.createRange();
25162 range.deleteContents();
25163 //alert(Sender.getAttribute('label'));
25165 range.insertNode(this.doc.createTextNode(txt));
25171 * Executes a Midas editor command on the editor document and performs necessary focus and
25172 * toolbar updates. <b>This should only be called after the editor is initialized.</b>
25173 * @param {String} cmd The Midas command
25174 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
25176 relayCmd : function(cmd, value){
25178 this.execCmd(cmd, value);
25179 this.owner.fireEvent('editorevent', this);
25180 //this.updateToolbar();
25181 this.owner.deferFocus();
25185 * Executes a Midas editor command directly on the editor document.
25186 * For visual commands, you should use {@link #relayCmd} instead.
25187 * <b>This should only be called after the editor is initialized.</b>
25188 * @param {String} cmd The Midas command
25189 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
25191 execCmd : function(cmd, value){
25192 this.doc.execCommand(cmd, false, value === undefined ? null : value);
25199 * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
25201 * @param {String} text | dom node..
25203 insertAtCursor : function(text)
25208 if(!this.activated){
25214 var r = this.doc.selection.createRange();
25225 if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
25229 // from jquery ui (MIT licenced)
25231 var win = this.win;
25233 if (win.getSelection && win.getSelection().getRangeAt) {
25234 range = win.getSelection().getRangeAt(0);
25235 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
25236 range.insertNode(node);
25237 } else if (win.document.selection && win.document.selection.createRange) {
25238 // no firefox support
25239 var txt = typeof(text) == 'string' ? text : text.outerHTML;
25240 win.document.selection.createRange().pasteHTML(txt);
25242 // no firefox support
25243 var txt = typeof(text) == 'string' ? text : text.outerHTML;
25244 this.execCmd('InsertHTML', txt);
25253 mozKeyPress : function(e){
25255 var c = e.getCharCode(), cmd;
25258 c = String.fromCharCode(c).toLowerCase();
25272 this.cleanUpPaste.defer(100, this);
25280 e.preventDefault();
25288 fixKeys : function(){ // load time branching for fastest keydown performance
25290 return function(e){
25291 var k = e.getKey(), r;
25294 r = this.doc.selection.createRange();
25297 r.pasteHTML('    ');
25304 r = this.doc.selection.createRange();
25306 var target = r.parentElement();
25307 if(!target || target.tagName.toLowerCase() != 'li'){
25309 r.pasteHTML('<br />');
25315 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
25316 this.cleanUpPaste.defer(100, this);
25322 }else if(Roo.isOpera){
25323 return function(e){
25324 var k = e.getKey();
25328 this.execCmd('InsertHTML','    ');
25331 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
25332 this.cleanUpPaste.defer(100, this);
25337 }else if(Roo.isSafari){
25338 return function(e){
25339 var k = e.getKey();
25343 this.execCmd('InsertText','\t');
25347 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
25348 this.cleanUpPaste.defer(100, this);
25356 getAllAncestors: function()
25358 var p = this.getSelectedNode();
25361 a.push(p); // push blank onto stack..
25362 p = this.getParentElement();
25366 while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
25370 a.push(this.doc.body);
25374 lastSelNode : false,
25377 getSelection : function()
25379 this.assignDocWin();
25380 return Roo.isIE ? this.doc.selection : this.win.getSelection();
25383 getSelectedNode: function()
25385 // this may only work on Gecko!!!
25387 // should we cache this!!!!
25392 var range = this.createRange(this.getSelection()).cloneRange();
25395 var parent = range.parentElement();
25397 var testRange = range.duplicate();
25398 testRange.moveToElementText(parent);
25399 if (testRange.inRange(range)) {
25402 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
25405 parent = parent.parentElement;
25410 // is ancestor a text element.
25411 var ac = range.commonAncestorContainer;
25412 if (ac.nodeType == 3) {
25413 ac = ac.parentNode;
25416 var ar = ac.childNodes;
25419 var other_nodes = [];
25420 var has_other_nodes = false;
25421 for (var i=0;i<ar.length;i++) {
25422 if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ?
25425 // fullly contained node.
25427 if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
25432 // probably selected..
25433 if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
25434 other_nodes.push(ar[i]);
25438 if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0)) {
25443 has_other_nodes = true;
25445 if (!nodes.length && other_nodes.length) {
25446 nodes= other_nodes;
25448 if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
25454 createRange: function(sel)
25456 // this has strange effects when using with
25457 // top toolbar - not sure if it's a great idea.
25458 //this.editor.contentWindow.focus();
25459 if (typeof sel != "undefined") {
25461 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
25463 return this.doc.createRange();
25466 return this.doc.createRange();
25469 getParentElement: function()
25472 this.assignDocWin();
25473 var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
25475 var range = this.createRange(sel);
25478 var p = range.commonAncestorContainer;
25479 while (p.nodeType == 3) { // text node
25490 * Range intersection.. the hard stuff...
25494 * [ -- selected range --- ]
25498 * if end is before start or hits it. fail.
25499 * if start is after end or hits it fail.
25501 * if either hits (but other is outside. - then it's not
25507 // @see http://www.thismuchiknow.co.uk/?p=64.
25508 rangeIntersectsNode : function(range, node)
25510 var nodeRange = node.ownerDocument.createRange();
25512 nodeRange.selectNode(node);
25514 nodeRange.selectNodeContents(node);
25517 var rangeStartRange = range.cloneRange();
25518 rangeStartRange.collapse(true);
25520 var rangeEndRange = range.cloneRange();
25521 rangeEndRange.collapse(false);
25523 var nodeStartRange = nodeRange.cloneRange();
25524 nodeStartRange.collapse(true);
25526 var nodeEndRange = nodeRange.cloneRange();
25527 nodeEndRange.collapse(false);
25529 return rangeStartRange.compareBoundaryPoints(
25530 Range.START_TO_START, nodeEndRange) == -1 &&
25531 rangeEndRange.compareBoundaryPoints(
25532 Range.START_TO_START, nodeStartRange) == 1;
25536 rangeCompareNode : function(range, node)
25538 var nodeRange = node.ownerDocument.createRange();
25540 nodeRange.selectNode(node);
25542 nodeRange.selectNodeContents(node);
25546 range.collapse(true);
25548 nodeRange.collapse(true);
25550 var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
25551 var ee = range.compareBoundaryPoints( Range.END_TO_END, nodeRange);
25553 //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
25555 var nodeIsBefore = ss == 1;
25556 var nodeIsAfter = ee == -1;
25558 if (nodeIsBefore && nodeIsAfter)
25560 if (!nodeIsBefore && nodeIsAfter)
25561 return 1; //right trailed.
25563 if (nodeIsBefore && !nodeIsAfter)
25564 return 2; // left trailed.
25569 // private? - in a new class?
25570 cleanUpPaste : function()
25572 // cleans up the whole document..
25573 Roo.log('cleanuppaste');
25575 this.cleanUpChildren(this.doc.body);
25576 var clean = this.cleanWordChars(this.doc.body.innerHTML);
25577 if (clean != this.doc.body.innerHTML) {
25578 this.doc.body.innerHTML = clean;
25583 cleanWordChars : function(input) {// change the chars to hex code
25584 var he = Roo.HtmlEditorCore;
25586 var output = input;
25587 Roo.each(he.swapCodes, function(sw) {
25588 var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
25590 output = output.replace(swapper, sw[1]);
25597 cleanUpChildren : function (n)
25599 if (!n.childNodes.length) {
25602 for (var i = n.childNodes.length-1; i > -1 ; i--) {
25603 this.cleanUpChild(n.childNodes[i]);
25610 cleanUpChild : function (node)
25613 //console.log(node);
25614 if (node.nodeName == "#text") {
25615 // clean up silly Windows -- stuff?
25618 if (node.nodeName == "#comment") {
25619 node.parentNode.removeChild(node);
25620 // clean up silly Windows -- stuff?
25623 var lcname = node.tagName.toLowerCase();
25624 // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
25625 // whitelist of tags..
25627 if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
25629 node.parentNode.removeChild(node);
25634 var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
25636 // remove <a name=....> as rendering on yahoo mailer is borked with this.
25637 // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
25639 //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
25640 // remove_keep_children = true;
25643 if (remove_keep_children) {
25644 this.cleanUpChildren(node);
25645 // inserts everything just before this node...
25646 while (node.childNodes.length) {
25647 var cn = node.childNodes[0];
25648 node.removeChild(cn);
25649 node.parentNode.insertBefore(cn, node);
25651 node.parentNode.removeChild(node);
25655 if (!node.attributes || !node.attributes.length) {
25656 this.cleanUpChildren(node);
25660 function cleanAttr(n,v)
25663 if (v.match(/^\./) || v.match(/^\//)) {
25666 if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
25669 if (v.match(/^#/)) {
25672 // Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
25673 node.removeAttribute(n);
25677 var cwhite = this.cwhite;
25678 var cblack = this.cblack;
25680 function cleanStyle(n,v)
25682 if (v.match(/expression/)) { //XSS?? should we even bother..
25683 node.removeAttribute(n);
25687 var parts = v.split(/;/);
25690 Roo.each(parts, function(p) {
25691 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
25695 var l = p.split(':').shift().replace(/\s+/g,'');
25696 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
25698 if ( cwhite.length && cblack.indexOf(l) > -1) {
25699 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
25700 //node.removeAttribute(n);
25704 // only allow 'c whitelisted system attributes'
25705 if ( cwhite.length && cwhite.indexOf(l) < 0) {
25706 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
25707 //node.removeAttribute(n);
25717 if (clean.length) {
25718 node.setAttribute(n, clean.join(';'));
25720 node.removeAttribute(n);
25726 for (var i = node.attributes.length-1; i > -1 ; i--) {
25727 var a = node.attributes[i];
25730 if (a.name.toLowerCase().substr(0,2)=='on') {
25731 node.removeAttribute(a.name);
25734 if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
25735 node.removeAttribute(a.name);
25738 if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
25739 cleanAttr(a.name,a.value); // fixme..
25742 if (a.name == 'style') {
25743 cleanStyle(a.name,a.value);
25746 /// clean up MS crap..
25747 // tecnically this should be a list of valid class'es..
25750 if (a.name == 'class') {
25751 if (a.value.match(/^Mso/)) {
25752 node.className = '';
25755 if (a.value.match(/body/)) {
25756 node.className = '';
25767 this.cleanUpChildren(node);
25772 * Clean up MS wordisms...
25774 cleanWord : function(node)
25777 var cleanWordChildren = function()
25779 if (!node.childNodes.length) {
25782 for (var i = node.childNodes.length-1; i > -1 ; i--) {
25783 _t.cleanWord(node.childNodes[i]);
25789 this.cleanWord(this.doc.body);
25792 if (node.nodeName == "#text") {
25793 // clean up silly Windows -- stuff?
25796 if (node.nodeName == "#comment") {
25797 node.parentNode.removeChild(node);
25798 // clean up silly Windows -- stuff?
25802 if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
25803 node.parentNode.removeChild(node);
25807 // remove - but keep children..
25808 if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
25809 while (node.childNodes.length) {
25810 var cn = node.childNodes[0];
25811 node.removeChild(cn);
25812 node.parentNode.insertBefore(cn, node);
25814 node.parentNode.removeChild(node);
25815 cleanWordChildren();
25819 if (node.className.length) {
25821 var cn = node.className.split(/\W+/);
25823 Roo.each(cn, function(cls) {
25824 if (cls.match(/Mso[a-zA-Z]+/)) {
25829 node.className = cna.length ? cna.join(' ') : '';
25831 node.removeAttribute("class");
25835 if (node.hasAttribute("lang")) {
25836 node.removeAttribute("lang");
25839 if (node.hasAttribute("style")) {
25841 var styles = node.getAttribute("style").split(";");
25843 Roo.each(styles, function(s) {
25844 if (!s.match(/:/)) {
25847 var kv = s.split(":");
25848 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
25851 // what ever is left... we allow.
25854 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
25855 if (!nstyle.length) {
25856 node.removeAttribute('style');
25860 cleanWordChildren();
25864 domToHTML : function(currentElement, depth, nopadtext) {
25866 depth = depth || 0;
25867 nopadtext = nopadtext || false;
25869 if (!currentElement) {
25870 return this.domToHTML(this.doc.body);
25873 //Roo.log(currentElement);
25875 var allText = false;
25876 var nodeName = currentElement.nodeName;
25877 var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
25879 if (nodeName == '#text') {
25880 return currentElement.nodeValue;
25885 if (nodeName != 'BODY') {
25888 // Prints the node tagName, such as <A>, <IMG>, etc
25891 for(i = 0; i < currentElement.attributes.length;i++) {
25893 var aname = currentElement.attributes.item(i).name;
25894 if (!currentElement.attributes.item(i).value.length) {
25897 attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
25900 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
25909 if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
25912 if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
25917 // Traverse the tree
25919 var currentElementChild = currentElement.childNodes.item(i);
25920 var allText = true;
25921 var innerHTML = '';
25923 while (currentElementChild) {
25924 // Formatting code (indent the tree so it looks nice on the screen)
25925 var nopad = nopadtext;
25926 if (lastnode == 'SPAN') {
25930 if (currentElementChild.nodeName == '#text') {
25931 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
25932 if (!nopad && toadd.length > 80) {
25933 innerHTML += "\n" + (new Array( depth + 1 )).join( " " );
25935 innerHTML += toadd;
25938 currentElementChild = currentElement.childNodes.item(i);
25944 innerHTML += nopad ? '' : "\n" + (new Array( depth + 1 )).join( " " );
25946 // Recursively traverse the tree structure of the child node
25947 innerHTML += this.domToHTML(currentElementChild, depth+1, nopadtext);
25948 lastnode = currentElementChild.nodeName;
25950 currentElementChild=currentElement.childNodes.item(i);
25956 // The remaining code is mostly for formatting the tree
25957 ret+= nopadtext ? '' : "\n" + (new Array( depth )).join( " " );
25962 ret+= "</"+tagName+">";
25968 applyBlacklists : function()
25970 var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white : [];
25971 var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black : [];
25975 Roo.each(Roo.HtmlEditorCore.white, function(tag) {
25976 if (b.indexOf(tag) > -1) {
25979 this.white.push(tag);
25983 Roo.each(w, function(tag) {
25984 if (b.indexOf(tag) > -1) {
25987 if (this.white.indexOf(tag) > -1) {
25990 this.white.push(tag);
25995 Roo.each(Roo.HtmlEditorCore.black, function(tag) {
25996 if (w.indexOf(tag) > -1) {
25999 this.black.push(tag);
26003 Roo.each(b, function(tag) {
26004 if (w.indexOf(tag) > -1) {
26007 if (this.black.indexOf(tag) > -1) {
26010 this.black.push(tag);
26015 w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite : [];
26016 b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack : [];
26020 Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
26021 if (b.indexOf(tag) > -1) {
26024 this.cwhite.push(tag);
26028 Roo.each(w, function(tag) {
26029 if (b.indexOf(tag) > -1) {
26032 if (this.cwhite.indexOf(tag) > -1) {
26035 this.cwhite.push(tag);
26040 Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
26041 if (w.indexOf(tag) > -1) {
26044 this.cblack.push(tag);
26048 Roo.each(b, function(tag) {
26049 if (w.indexOf(tag) > -1) {
26052 if (this.cblack.indexOf(tag) > -1) {
26055 this.cblack.push(tag);
26060 // hide stuff that is not compatible
26074 * @event specialkey
26078 * @cfg {String} fieldClass @hide
26081 * @cfg {String} focusClass @hide
26084 * @cfg {String} autoCreate @hide
26087 * @cfg {String} inputType @hide
26090 * @cfg {String} invalidClass @hide
26093 * @cfg {String} invalidText @hide
26096 * @cfg {String} msgFx @hide
26099 * @cfg {String} validateOnBlur @hide
26103 Roo.HtmlEditorCore.white = [
26104 'area', 'br', 'img', 'input', 'hr', 'wbr',
26106 'address', 'blockquote', 'center', 'dd', 'dir', 'div',
26107 'dl', 'dt', 'h1', 'h2', 'h3', 'h4',
26108 'h5', 'h6', 'hr', 'isindex', 'listing', 'marquee',
26109 'menu', 'multicol', 'ol', 'p', 'plaintext', 'pre',
26110 'table', 'ul', 'xmp',
26112 'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th',
26115 'dir', 'menu', 'ol', 'ul', 'dl',
26121 Roo.HtmlEditorCore.black = [
26122 // 'embed', 'object', // enable - backend responsiblity to clean thiese
26124 'base', 'basefont', 'bgsound', 'blink', 'body',
26125 'frame', 'frameset', 'head', 'html', 'ilayer',
26126 'iframe', 'layer', 'link', 'meta', 'object',
26127 'script', 'style' ,'title', 'xml' // clean later..
26129 Roo.HtmlEditorCore.clean = [
26130 'script', 'style', 'title', 'xml'
26132 Roo.HtmlEditorCore.remove = [
26137 Roo.HtmlEditorCore.ablack = [
26141 Roo.HtmlEditorCore.aclean = [
26142 'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc'
26146 Roo.HtmlEditorCore.pwhite= [
26147 'http', 'https', 'mailto'
26150 // white listed style attributes.
26151 Roo.HtmlEditorCore.cwhite= [
26152 // 'text-align', /// default is to allow most things..
26158 // black listed style attributes.
26159 Roo.HtmlEditorCore.cblack= [
26160 // 'font-size' -- this can be set by the project
26164 Roo.HtmlEditorCore.swapCodes =[
26175 //<script type="text/javascript">
26178 * Ext JS Library 1.1.1
26179 * Copyright(c) 2006-2007, Ext JS, LLC.
26185 Roo.form.HtmlEditor = function(config){
26189 Roo.form.HtmlEditor.superclass.constructor.call(this, config);
26191 if (!this.toolbars) {
26192 this.toolbars = [];
26194 this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
26200 * @class Roo.form.HtmlEditor
26201 * @extends Roo.form.Field
26202 * Provides a lightweight HTML Editor component.
26204 * This has been tested on Fireforx / Chrome.. IE may not be so great..
26206 * <br><br><b>Note: The focus/blur and validation marking functionality inherited from Ext.form.Field is NOT
26207 * supported by this editor.</b><br/><br/>
26208 * An Editor is a sensitive component that can't be used in all spots standard fields can be used. Putting an Editor within
26209 * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
26211 Roo.extend(Roo.form.HtmlEditor, Roo.form.Field, {
26213 * @cfg {Boolean} clearUp
26217 * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
26222 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
26227 * @cfg {Number} height (in pixels)
26231 * @cfg {Number} width (in pixels)
26236 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
26239 stylesheets: false,
26243 * @cfg {Array} blacklist of css styles style attributes (blacklist overrides whitelist)
26248 * @cfg {Array} whitelist of css styles style attributes (blacklist overrides whitelist)
26254 * @cfg {Array} blacklist of html tags - in addition to standard blacklist.
26259 * @cfg {Array} whitelist of html tags - in addition to statndard whitelist
26267 // private properties
26268 validationEvent : false,
26270 initialized : false,
26273 onFocus : Roo.emptyFn,
26275 hideMode:'offsets',
26277 actionMode : 'container', // defaults to hiding it...
26279 defaultAutoCreate : { // modified by initCompnoent..
26281 style:"width:500px;height:300px;",
26282 autocomplete: "off"
26286 initComponent : function(){
26289 * @event initialize
26290 * Fires when the editor is fully initialized (including the iframe)
26291 * @param {HtmlEditor} this
26296 * Fires when the editor is first receives the focus. Any insertion must wait
26297 * until after this event.
26298 * @param {HtmlEditor} this
26302 * @event beforesync
26303 * Fires before the textarea is updated with content from the editor iframe. Return false
26304 * to cancel the sync.
26305 * @param {HtmlEditor} this
26306 * @param {String} html
26310 * @event beforepush
26311 * Fires before the iframe editor is updated with content from the textarea. Return false
26312 * to cancel the push.
26313 * @param {HtmlEditor} this
26314 * @param {String} html
26319 * Fires when the textarea is updated with content from the editor iframe.
26320 * @param {HtmlEditor} this
26321 * @param {String} html
26326 * Fires when the iframe editor is updated with content from the textarea.
26327 * @param {HtmlEditor} this
26328 * @param {String} html
26332 * @event editmodechange
26333 * Fires when the editor switches edit modes
26334 * @param {HtmlEditor} this
26335 * @param {Boolean} sourceEdit True if source edit, false if standard editing.
26337 editmodechange: true,
26339 * @event editorevent
26340 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
26341 * @param {HtmlEditor} this
26345 * @event firstfocus
26346 * Fires when on first focus - needed by toolbars..
26347 * @param {HtmlEditor} this
26352 * Auto save the htmlEditor value as a file into Events
26353 * @param {HtmlEditor} this
26357 * @event savedpreview
26358 * preview the saved version of htmlEditor
26359 * @param {HtmlEditor} this
26363 this.defaultAutoCreate = {
26365 style:'width: ' + this.width + 'px;height: ' + this.height + 'px;',
26366 autocomplete: "off"
26371 * Protected method that will not generally be called directly. It
26372 * is called when the editor creates its toolbar. Override this method if you need to
26373 * add custom toolbar buttons.
26374 * @param {HtmlEditor} editor
26376 createToolbar : function(editor){
26377 Roo.log("create toolbars");
26378 if (!editor.toolbars || !editor.toolbars.length) {
26379 editor.toolbars = [ new Roo.form.HtmlEditor.ToolbarStandard() ]; // can be empty?
26382 for (var i =0 ; i < editor.toolbars.length;i++) {
26383 editor.toolbars[i] = Roo.factory(
26384 typeof(editor.toolbars[i]) == 'string' ?
26385 { xtype: editor.toolbars[i]} : editor.toolbars[i],
26386 Roo.form.HtmlEditor);
26387 editor.toolbars[i].init(editor);
26395 onRender : function(ct, position)
26398 Roo.form.HtmlEditor.superclass.onRender.call(this, ct, position);
26400 this.wrap = this.el.wrap({
26401 cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
26404 this.editorcore.onRender(ct, position);
26406 if (this.resizable) {
26407 this.resizeEl = new Roo.Resizable(this.wrap, {
26411 minHeight : this.height,
26412 height: this.height,
26413 handles : this.resizable,
26416 resize : function(r, w, h) {
26417 _t.onResize(w,h); // -something
26423 this.createToolbar(this);
26427 this.setSize(this.wrap.getSize());
26429 if (this.resizeEl) {
26430 this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
26431 // should trigger onReize..
26434 // if(this.autosave && this.w){
26435 // this.autoSaveFn = setInterval(this.autosave, 1000);
26440 onResize : function(w, h)
26442 //Roo.log('resize: ' +w + ',' + h );
26443 Roo.form.HtmlEditor.superclass.onResize.apply(this, arguments);
26448 if(typeof w == 'number'){
26449 var aw = w - this.wrap.getFrameWidth('lr');
26450 this.el.setWidth(this.adjustWidth('textarea', aw));
26453 if(typeof h == 'number'){
26455 for (var i =0; i < this.toolbars.length;i++) {
26456 // fixme - ask toolbars for heights?
26457 tbh += this.toolbars[i].tb.el.getHeight();
26458 if (this.toolbars[i].footer) {
26459 tbh += this.toolbars[i].footer.el.getHeight();
26466 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
26467 ah -= 5; // knock a few pixes off for look..
26468 this.el.setHeight(this.adjustWidth('textarea', ah));
26472 Roo.log('onResize:' + [w,h,ew,eh].join(',') );
26473 this.editorcore.onResize(ew,eh);
26478 * Toggles the editor between standard and source edit mode.
26479 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
26481 toggleSourceEdit : function(sourceEditMode)
26483 this.editorcore.toggleSourceEdit(sourceEditMode);
26485 if(this.editorcore.sourceEditMode){
26486 Roo.log('editor - showing textarea');
26489 // Roo.log(this.syncValue());
26490 this.editorcore.syncValue();
26491 this.el.removeClass('x-hidden');
26492 this.el.dom.removeAttribute('tabIndex');
26495 Roo.log('editor - hiding textarea');
26497 // Roo.log(this.pushValue());
26498 this.editorcore.pushValue();
26500 this.el.addClass('x-hidden');
26501 this.el.dom.setAttribute('tabIndex', -1);
26502 //this.deferFocus();
26505 this.setSize(this.wrap.getSize());
26506 this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
26509 // private (for BoxComponent)
26510 adjustSize : Roo.BoxComponent.prototype.adjustSize,
26512 // private (for BoxComponent)
26513 getResizeEl : function(){
26517 // private (for BoxComponent)
26518 getPositionEl : function(){
26523 initEvents : function(){
26524 this.originalValue = this.getValue();
26528 * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
26531 markInvalid : Roo.emptyFn,
26533 * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
26536 clearInvalid : Roo.emptyFn,
26538 setValue : function(v){
26539 Roo.form.HtmlEditor.superclass.setValue.call(this, v);
26540 this.editorcore.pushValue();
26545 deferFocus : function(){
26546 this.focus.defer(10, this);
26550 focus : function(){
26551 this.editorcore.focus();
26557 onDestroy : function(){
26563 for (var i =0; i < this.toolbars.length;i++) {
26564 // fixme - ask toolbars for heights?
26565 this.toolbars[i].onDestroy();
26568 this.wrap.dom.innerHTML = '';
26569 this.wrap.remove();
26574 onFirstFocus : function(){
26575 //Roo.log("onFirstFocus");
26576 this.editorcore.onFirstFocus();
26577 for (var i =0; i < this.toolbars.length;i++) {
26578 this.toolbars[i].onFirstFocus();
26584 syncValue : function()
26586 this.editorcore.syncValue();
26589 pushValue : function()
26591 this.editorcore.pushValue();
26595 // hide stuff that is not compatible
26609 * @event specialkey
26613 * @cfg {String} fieldClass @hide
26616 * @cfg {String} focusClass @hide
26619 * @cfg {String} autoCreate @hide
26622 * @cfg {String} inputType @hide
26625 * @cfg {String} invalidClass @hide
26628 * @cfg {String} invalidText @hide
26631 * @cfg {String} msgFx @hide
26634 * @cfg {String} validateOnBlur @hide
26638 // <script type="text/javascript">
26641 * Ext JS Library 1.1.1
26642 * Copyright(c) 2006-2007, Ext JS, LLC.
26648 * @class Roo.form.HtmlEditorToolbar1
26653 new Roo.form.HtmlEditor({
26656 new Roo.form.HtmlEditorToolbar1({
26657 disable : { fonts: 1 , format: 1, ..., ... , ...],
26663 * @cfg {Object} disable List of elements to disable..
26664 * @cfg {Array} btns List of additional buttons.
26668 * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
26671 Roo.form.HtmlEditor.ToolbarStandard = function(config)
26674 Roo.apply(this, config);
26676 // default disabled, based on 'good practice'..
26677 this.disable = this.disable || {};
26678 Roo.applyIf(this.disable, {
26681 specialElements : true
26685 //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
26686 // dont call parent... till later.
26689 Roo.apply(Roo.form.HtmlEditor.ToolbarStandard.prototype, {
26696 editorcore : false,
26698 * @cfg {Object} disable List of toolbar elements to disable
26705 * @cfg {String} createLinkText The default text for the create link prompt
26707 createLinkText : 'Please enter the URL for the link:',
26709 * @cfg {String} defaultLinkValue The default value for the create link prompt (defaults to http:/ /)
26711 defaultLinkValue : 'http:/'+'/',
26715 * @cfg {Array} fontFamilies An array of available font families
26733 // "á" , ?? a acute?
26738 "°" // , // degrees
26740 // "é" , // e ecute
26741 // "ú" , // u ecute?
26744 specialElements : [
26746 text: "Insert Table",
26749 ihtml : '<table><tr><td>Cell</td></tr></table>'
26753 text: "Insert Image",
26756 ihtml : '<img src="about:blank"/>'
26765 "form", "input:text", "input:hidden", "input:checkbox", "input:radio", "input:password",
26766 "input:submit", "input:button", "select", "textarea", "label" ],
26769 ["h1"],["h2"],["h3"],["h4"],["h5"],["h6"],
26771 ["abbr"],[ "acronym"],[ "address"],[ "cite"],[ "samp"],[ "var"],
26779 * @cfg {String} defaultFont default font to use.
26781 defaultFont: 'tahoma',
26783 fontSelect : false,
26786 formatCombo : false,
26788 init : function(editor)
26790 this.editor = editor;
26791 this.editorcore = editor.editorcore ? editor.editorcore : editor;
26792 var editorcore = this.editorcore;
26796 var fid = editorcore.frameId;
26798 function btn(id, toggle, handler){
26799 var xid = fid + '-'+ id ;
26803 cls : 'x-btn-icon x-edit-'+id,
26804 enableToggle:toggle !== false,
26805 scope: _t, // was editor...
26806 handler:handler||_t.relayBtnCmd,
26807 clickEvent:'mousedown',
26808 tooltip: etb.buttonTips[id] || undefined, ///tips ???
26815 var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
26817 // stop form submits
26818 tb.el.on('click', function(e){
26819 e.preventDefault(); // what does this do?
26822 if(!this.disable.font) { // && !Roo.isSafari){
26823 /* why no safari for fonts
26824 editor.fontSelect = tb.el.createChild({
26827 cls:'x-font-select',
26828 html: this.createFontOptions()
26831 editor.fontSelect.on('change', function(){
26832 var font = editor.fontSelect.dom.value;
26833 editor.relayCmd('fontname', font);
26834 editor.deferFocus();
26838 editor.fontSelect.dom,
26844 if(!this.disable.formats){
26845 this.formatCombo = new Roo.form.ComboBox({
26846 store: new Roo.data.SimpleStore({
26849 data : this.formats // from states.js
26853 //autoCreate : {tag: "div", size: "20"},
26854 displayField:'tag',
26858 triggerAction: 'all',
26859 emptyText:'Add tag',
26860 selectOnFocus:true,
26863 'select': function(c, r, i) {
26864 editorcore.insertTag(r.get('tag'));
26870 tb.addField(this.formatCombo);
26874 if(!this.disable.format){
26881 if(!this.disable.fontSize){
26886 btn('increasefontsize', false, editorcore.adjustFont),
26887 btn('decreasefontsize', false, editorcore.adjustFont)
26892 if(!this.disable.colors){
26895 id:editorcore.frameId +'-forecolor',
26896 cls:'x-btn-icon x-edit-forecolor',
26897 clickEvent:'mousedown',
26898 tooltip: this.buttonTips['forecolor'] || undefined,
26900 menu : new Roo.menu.ColorMenu({
26901 allowReselect: true,
26902 focus: Roo.emptyFn,
26905 selectHandler: function(cp, color){
26906 editorcore.execCmd('forecolor', Roo.isSafari || Roo.isIE ? '#'+color : color);
26907 editor.deferFocus();
26910 clickEvent:'mousedown'
26913 id:editorcore.frameId +'backcolor',
26914 cls:'x-btn-icon x-edit-backcolor',
26915 clickEvent:'mousedown',
26916 tooltip: this.buttonTips['backcolor'] || undefined,
26918 menu : new Roo.menu.ColorMenu({
26919 focus: Roo.emptyFn,
26922 allowReselect: true,
26923 selectHandler: function(cp, color){
26925 editorcore.execCmd('useCSS', false);
26926 editorcore.execCmd('hilitecolor', color);
26927 editorcore.execCmd('useCSS', true);
26928 editor.deferFocus();
26930 editorcore.execCmd(Roo.isOpera ? 'hilitecolor' : 'backcolor',
26931 Roo.isSafari || Roo.isIE ? '#'+color : color);
26932 editor.deferFocus();
26936 clickEvent:'mousedown'
26941 // now add all the items...
26944 if(!this.disable.alignments){
26947 btn('justifyleft'),
26948 btn('justifycenter'),
26949 btn('justifyright')
26953 //if(!Roo.isSafari){
26954 if(!this.disable.links){
26957 btn('createlink', false, this.createLink) /// MOVE TO HERE?!!?!?!?!
26961 if(!this.disable.lists){
26964 btn('insertorderedlist'),
26965 btn('insertunorderedlist')
26968 if(!this.disable.sourceEdit){
26971 btn('sourceedit', true, function(btn){
26973 this.toggleSourceEdit(btn.pressed);
26980 // special menu.. - needs to be tidied up..
26981 if (!this.disable.special) {
26984 cls: 'x-edit-none',
26990 for (var i =0; i < this.specialChars.length; i++) {
26991 smenu.menu.items.push({
26993 html: this.specialChars[i],
26994 handler: function(a,b) {
26995 editorcore.insertAtCursor(String.fromCharCode(a.html.replace('&#','').replace(';', '')));
26996 //editor.insertAtCursor(a.html);
27010 if (!this.disable.cleanStyles) {
27012 cls: 'x-btn-icon x-btn-clear',
27018 for (var i =0; i < this.cleanStyles.length; i++) {
27019 cmenu.menu.items.push({
27020 actiontype : this.cleanStyles[i],
27021 html: 'Remove ' + this.cleanStyles[i],
27022 handler: function(a,b) {
27025 var c = Roo.get(editorcore.doc.body);
27026 c.select('[style]').each(function(s) {
27027 s.dom.style.removeProperty(a.actiontype);
27029 editorcore.syncValue();
27034 cmenu.menu.items.push({
27035 actiontype : 'word',
27036 html: 'Remove MS Word Formating',
27037 handler: function(a,b) {
27038 editorcore.cleanWord();
27039 editorcore.syncValue();
27044 cmenu.menu.items.push({
27045 actiontype : 'all',
27046 html: 'Remove All Styles',
27047 handler: function(a,b) {
27049 var c = Roo.get(editorcore.doc.body);
27050 c.select('[style]').each(function(s) {
27051 s.dom.removeAttribute('style');
27053 editorcore.syncValue();
27057 cmenu.menu.items.push({
27058 actiontype : 'word',
27059 html: 'Tidy HTML Source',
27060 handler: function(a,b) {
27061 editorcore.doc.body.innerHTML = editorcore.domToHTML();
27062 editorcore.syncValue();
27071 if (!this.disable.specialElements) {
27074 cls: 'x-edit-none',
27079 for (var i =0; i < this.specialElements.length; i++) {
27080 semenu.menu.items.push(
27082 handler: function(a,b) {
27083 editor.insertAtCursor(this.ihtml);
27085 }, this.specialElements[i])
27097 for(var i =0; i< this.btns.length;i++) {
27098 var b = Roo.factory(this.btns[i],Roo.form);
27099 b.cls = 'x-edit-none';
27100 b.scope = editorcore;
27108 // disable everything...
27110 this.tb.items.each(function(item){
27111 if(item.id != editorcore.frameId+ '-sourceedit'){
27115 this.rendered = true;
27117 // the all the btns;
27118 editor.on('editorevent', this.updateToolbar, this);
27119 // other toolbars need to implement this..
27120 //editor.on('editmodechange', this.updateToolbar, this);
27124 relayBtnCmd : function(btn) {
27125 this.editorcore.relayCmd(btn.cmd);
27127 // private used internally
27128 createLink : function(){
27129 Roo.log("create link?");
27130 var url = prompt(this.createLinkText, this.defaultLinkValue);
27131 if(url && url != 'http:/'+'/'){
27132 this.editorcore.relayCmd('createlink', url);
27138 * Protected method that will not generally be called directly. It triggers
27139 * a toolbar update by reading the markup state of the current selection in the editor.
27141 updateToolbar: function(){
27143 if(!this.editorcore.activated){
27144 this.editor.onFirstFocus();
27148 var btns = this.tb.items.map,
27149 doc = this.editorcore.doc,
27150 frameId = this.editorcore.frameId;
27152 if(!this.disable.font && !Roo.isSafari){
27154 var name = (doc.queryCommandValue('FontName')||this.editor.defaultFont).toLowerCase();
27155 if(name != this.fontSelect.dom.value){
27156 this.fontSelect.dom.value = name;
27160 if(!this.disable.format){
27161 btns[frameId + '-bold'].toggle(doc.queryCommandState('bold'));
27162 btns[frameId + '-italic'].toggle(doc.queryCommandState('italic'));
27163 btns[frameId + '-underline'].toggle(doc.queryCommandState('underline'));
27165 if(!this.disable.alignments){
27166 btns[frameId + '-justifyleft'].toggle(doc.queryCommandState('justifyleft'));
27167 btns[frameId + '-justifycenter'].toggle(doc.queryCommandState('justifycenter'));
27168 btns[frameId + '-justifyright'].toggle(doc.queryCommandState('justifyright'));
27170 if(!Roo.isSafari && !this.disable.lists){
27171 btns[frameId + '-insertorderedlist'].toggle(doc.queryCommandState('insertorderedlist'));
27172 btns[frameId + '-insertunorderedlist'].toggle(doc.queryCommandState('insertunorderedlist'));
27175 var ans = this.editorcore.getAllAncestors();
27176 if (this.formatCombo) {
27179 var store = this.formatCombo.store;
27180 this.formatCombo.setValue("");
27181 for (var i =0; i < ans.length;i++) {
27182 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
27184 this.formatCombo.setValue(ans[i].tagName.toLowerCase());
27192 // hides menus... - so this cant be on a menu...
27193 Roo.menu.MenuMgr.hideAll();
27195 //this.editorsyncValue();
27199 createFontOptions : function(){
27200 var buf = [], fs = this.fontFamilies, ff, lc;
27204 for(var i = 0, len = fs.length; i< len; i++){
27206 lc = ff.toLowerCase();
27208 '<option value="',lc,'" style="font-family:',ff,';"',
27209 (this.defaultFont == lc ? ' selected="true">' : '>'),
27214 return buf.join('');
27217 toggleSourceEdit : function(sourceEditMode){
27219 Roo.log("toolbar toogle");
27220 if(sourceEditMode === undefined){
27221 sourceEditMode = !this.sourceEditMode;
27223 this.sourceEditMode = sourceEditMode === true;
27224 var btn = this.tb.items.get(this.editorcore.frameId +'-sourceedit');
27225 // just toggle the button?
27226 if(btn.pressed !== this.sourceEditMode){
27227 btn.toggle(this.sourceEditMode);
27231 if(sourceEditMode){
27232 Roo.log("disabling buttons");
27233 this.tb.items.each(function(item){
27234 if(item.cmd != 'sourceedit'){
27240 Roo.log("enabling buttons");
27241 if(this.editorcore.initialized){
27242 this.tb.items.each(function(item){
27248 Roo.log("calling toggole on editor");
27249 // tell the editor that it's been pressed..
27250 this.editor.toggleSourceEdit(sourceEditMode);
27254 * Object collection of toolbar tooltips for the buttons in the editor. The key
27255 * is the command id associated with that button and the value is a valid QuickTips object.
27260 title: 'Bold (Ctrl+B)',
27261 text: 'Make the selected text bold.',
27262 cls: 'x-html-editor-tip'
27265 title: 'Italic (Ctrl+I)',
27266 text: 'Make the selected text italic.',
27267 cls: 'x-html-editor-tip'
27275 title: 'Bold (Ctrl+B)',
27276 text: 'Make the selected text bold.',
27277 cls: 'x-html-editor-tip'
27280 title: 'Italic (Ctrl+I)',
27281 text: 'Make the selected text italic.',
27282 cls: 'x-html-editor-tip'
27285 title: 'Underline (Ctrl+U)',
27286 text: 'Underline the selected text.',
27287 cls: 'x-html-editor-tip'
27289 increasefontsize : {
27290 title: 'Grow Text',
27291 text: 'Increase the font size.',
27292 cls: 'x-html-editor-tip'
27294 decreasefontsize : {
27295 title: 'Shrink Text',
27296 text: 'Decrease the font size.',
27297 cls: 'x-html-editor-tip'
27300 title: 'Text Highlight Color',
27301 text: 'Change the background color of the selected text.',
27302 cls: 'x-html-editor-tip'
27305 title: 'Font Color',
27306 text: 'Change the color of the selected text.',
27307 cls: 'x-html-editor-tip'
27310 title: 'Align Text Left',
27311 text: 'Align text to the left.',
27312 cls: 'x-html-editor-tip'
27315 title: 'Center Text',
27316 text: 'Center text in the editor.',
27317 cls: 'x-html-editor-tip'
27320 title: 'Align Text Right',
27321 text: 'Align text to the right.',
27322 cls: 'x-html-editor-tip'
27324 insertunorderedlist : {
27325 title: 'Bullet List',
27326 text: 'Start a bulleted list.',
27327 cls: 'x-html-editor-tip'
27329 insertorderedlist : {
27330 title: 'Numbered List',
27331 text: 'Start a numbered list.',
27332 cls: 'x-html-editor-tip'
27335 title: 'Hyperlink',
27336 text: 'Make the selected text a hyperlink.',
27337 cls: 'x-html-editor-tip'
27340 title: 'Source Edit',
27341 text: 'Switch to source editing mode.',
27342 cls: 'x-html-editor-tip'
27346 onDestroy : function(){
27349 this.tb.items.each(function(item){
27351 item.menu.removeAll();
27353 item.menu.el.destroy();
27361 onFirstFocus: function() {
27362 this.tb.items.each(function(item){
27371 // <script type="text/javascript">
27374 * Ext JS Library 1.1.1
27375 * Copyright(c) 2006-2007, Ext JS, LLC.
27382 * @class Roo.form.HtmlEditor.ToolbarContext
27387 new Roo.form.HtmlEditor({
27390 { xtype: 'ToolbarStandard', styles : {} }
27391 { xtype: 'ToolbarContext', disable : {} }
27397 * @config : {Object} disable List of elements to disable.. (not done yet.)
27398 * @config : {Object} styles Map of styles available.
27402 Roo.form.HtmlEditor.ToolbarContext = function(config)
27405 Roo.apply(this, config);
27406 //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
27407 // dont call parent... till later.
27408 this.styles = this.styles || {};
27413 Roo.form.HtmlEditor.ToolbarContext.types = {
27425 opts : [ [""],[ "left"],[ "right"],[ "center"],[ "top"]],
27491 opts : [[""],[ "left"],[ "center"],[ "right"],[ "justify"],[ "char"]],
27496 opts : [[""],[ "top"],[ "middle"],[ "bottom"],[ "baseline"]],
27506 style : 'fontFamily',
27507 displayField: 'display',
27508 optname : 'font-family',
27557 // should we really allow this??
27558 // should this just be
27569 style : 'fontFamily',
27570 displayField: 'display',
27571 optname : 'font-family',
27578 style : 'fontFamily',
27579 displayField: 'display',
27580 optname : 'font-family',
27587 style : 'fontFamily',
27588 displayField: 'display',
27589 optname : 'font-family',
27600 // this should be configurable.. - you can either set it up using stores, or modify options somehwere..
27601 Roo.form.HtmlEditor.ToolbarContext.stores = false;
27603 Roo.form.HtmlEditor.ToolbarContext.options = {
27605 [ 'Helvetica,Arial,sans-serif', 'Helvetica'],
27606 [ 'Courier New', 'Courier New'],
27607 [ 'Tahoma', 'Tahoma'],
27608 [ 'Times New Roman,serif', 'Times'],
27609 [ 'Verdana','Verdana' ]
27613 // fixme - these need to be configurable..
27616 Roo.form.HtmlEditor.ToolbarContext.types
27619 Roo.apply(Roo.form.HtmlEditor.ToolbarContext.prototype, {
27626 editorcore : false,
27628 * @cfg {Object} disable List of toolbar elements to disable
27633 * @cfg {Object} styles List of styles
27634 * eg. { '*' : [ 'headline' ] , 'TD' : [ 'underline', 'double-underline' ] }
27636 * These must be defined in the page, so they get rendered correctly..
27647 init : function(editor)
27649 this.editor = editor;
27650 this.editorcore = editor.editorcore ? editor.editorcore : editor;
27651 var editorcore = this.editorcore;
27653 var fid = editorcore.frameId;
27655 function btn(id, toggle, handler){
27656 var xid = fid + '-'+ id ;
27660 cls : 'x-btn-icon x-edit-'+id,
27661 enableToggle:toggle !== false,
27662 scope: editorcore, // was editor...
27663 handler:handler||editorcore.relayBtnCmd,
27664 clickEvent:'mousedown',
27665 tooltip: etb.buttonTips[id] || undefined, ///tips ???
27669 // create a new element.
27670 var wdiv = editor.wrap.createChild({
27672 }, editor.wrap.dom.firstChild.nextSibling, true);
27674 // can we do this more than once??
27676 // stop form submits
27679 // disable everything...
27680 var ty= Roo.form.HtmlEditor.ToolbarContext.types;
27681 this.toolbars = {};
27683 for (var i in ty) {
27685 this.toolbars[i] = this.buildToolbar(ty[i],i);
27687 this.tb = this.toolbars.BODY;
27689 this.buildFooter();
27690 this.footer.show();
27691 editor.on('hide', function( ) { this.footer.hide() }, this);
27692 editor.on('show', function( ) { this.footer.show() }, this);
27695 this.rendered = true;
27697 // the all the btns;
27698 editor.on('editorevent', this.updateToolbar, this);
27699 // other toolbars need to implement this..
27700 //editor.on('editmodechange', this.updateToolbar, this);
27706 * Protected method that will not generally be called directly. It triggers
27707 * a toolbar update by reading the markup state of the current selection in the editor.
27709 updateToolbar: function(editor,ev,sel){
27712 // capture mouse up - this is handy for selecting images..
27713 // perhaps should go somewhere else...
27714 if(!this.editorcore.activated){
27715 this.editor.onFirstFocus();
27719 // http://developer.yahoo.com/yui/docs/simple-editor.js.html
27720 // selectNode - might want to handle IE?
27722 (ev.type == 'mouseup' || ev.type == 'click' ) &&
27723 ev.target && ev.target.tagName == 'IMG') {
27724 // they have click on an image...
27725 // let's see if we can change the selection...
27728 var nodeRange = sel.ownerDocument.createRange();
27730 nodeRange.selectNode(sel);
27732 nodeRange.selectNodeContents(sel);
27734 //nodeRange.collapse(true);
27735 var s = this.editorcore.win.getSelection();
27736 s.removeAllRanges();
27737 s.addRange(nodeRange);
27741 var updateFooter = sel ? false : true;
27744 var ans = this.editorcore.getAllAncestors();
27747 var ty= Roo.form.HtmlEditor.ToolbarContext.types;
27750 sel = ans.length ? (ans[0] ? ans[0] : ans[1]) : this.editorcore.doc.body;
27751 sel = sel ? sel : this.editorcore.doc.body;
27752 sel = sel.tagName.length ? sel : this.editorcore.doc.body;
27755 // pick a menu that exists..
27756 var tn = sel.tagName.toUpperCase();
27757 //sel = typeof(ty[tn]) != 'undefined' ? sel : this.editor.doc.body;
27759 tn = sel.tagName.toUpperCase();
27761 var lastSel = this.tb.selectedNode
27763 this.tb.selectedNode = sel;
27765 // if current menu does not match..
27766 if ((this.tb.name != tn) || (lastSel != this.tb.selectedNode)) {
27769 ///console.log("show: " + tn);
27770 this.tb = typeof(ty[tn]) != 'undefined' ? this.toolbars[tn] : this.toolbars['*'];
27773 this.tb.items.first().el.innerHTML = tn + ': ';
27776 // update attributes
27777 if (this.tb.fields) {
27778 this.tb.fields.each(function(e) {
27780 e.setValue(sel.style[e.stylename]);
27783 e.setValue(sel.getAttribute(e.attrname));
27787 var hasStyles = false;
27788 for(var i in this.styles) {
27795 var st = this.tb.fields.item(0);
27797 st.store.removeAll();
27800 var cn = sel.className.split(/\s+/);
27803 if (this.styles['*']) {
27805 Roo.each(this.styles['*'], function(v) {
27806 avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );
27809 if (this.styles[tn]) {
27810 Roo.each(this.styles[tn], function(v) {
27811 avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );
27815 st.store.loadData(avs);
27819 // flag our selected Node.
27820 this.tb.selectedNode = sel;
27823 Roo.menu.MenuMgr.hideAll();
27827 if (!updateFooter) {
27828 //this.footDisp.dom.innerHTML = '';
27831 // update the footer
27835 this.footerEls = ans.reverse();
27836 Roo.each(this.footerEls, function(a,i) {
27837 if (!a) { return; }
27838 html += html.length ? ' > ' : '';
27840 html += '<span class="x-ed-loc-' + i + '">' + a.tagName + '</span>';
27845 var sz = this.footDisp.up('td').getSize();
27846 this.footDisp.dom.style.width = (sz.width -10) + 'px';
27847 this.footDisp.dom.style.marginLeft = '5px';
27849 this.footDisp.dom.style.overflow = 'hidden';
27851 this.footDisp.dom.innerHTML = html;
27853 //this.editorsyncValue();
27860 onDestroy : function(){
27863 this.tb.items.each(function(item){
27865 item.menu.removeAll();
27867 item.menu.el.destroy();
27875 onFirstFocus: function() {
27876 // need to do this for all the toolbars..
27877 this.tb.items.each(function(item){
27881 buildToolbar: function(tlist, nm)
27883 var editor = this.editor;
27884 var editorcore = this.editorcore;
27885 // create a new element.
27886 var wdiv = editor.wrap.createChild({
27888 }, editor.wrap.dom.firstChild.nextSibling, true);
27891 var tb = new Roo.Toolbar(wdiv);
27894 tb.add(nm+ ": ");
27897 for(var i in this.styles) {
27902 if (styles && styles.length) {
27904 // this needs a multi-select checkbox...
27905 tb.addField( new Roo.form.ComboBox({
27906 store: new Roo.data.SimpleStore({
27908 fields: ['val', 'selected'],
27911 name : '-roo-edit-className',
27912 attrname : 'className',
27913 displayField: 'val',
27917 triggerAction: 'all',
27918 emptyText:'Select Style',
27919 selectOnFocus:true,
27922 'select': function(c, r, i) {
27923 // initial support only for on class per el..
27924 tb.selectedNode.className = r ? r.get('val') : '';
27925 editorcore.syncValue();
27932 var tbc = Roo.form.HtmlEditor.ToolbarContext;
27933 var tbops = tbc.options;
27935 for (var i in tlist) {
27937 var item = tlist[i];
27938 tb.add(item.title + ": ");
27941 //optname == used so you can configure the options available..
27942 var opts = item.opts ? item.opts : false;
27943 if (item.optname) {
27944 opts = tbops[item.optname];
27949 // opts == pulldown..
27950 tb.addField( new Roo.form.ComboBox({
27951 store: typeof(tbc.stores[i]) != 'undefined' ? Roo.factory(tbc.stores[i],Roo.data) : new Roo.data.SimpleStore({
27953 fields: ['val', 'display'],
27956 name : '-roo-edit-' + i,
27958 stylename : item.style ? item.style : false,
27959 displayField: item.displayField ? item.displayField : 'val',
27960 valueField : 'val',
27962 mode: typeof(tbc.stores[i]) != 'undefined' ? 'remote' : 'local',
27964 triggerAction: 'all',
27965 emptyText:'Select',
27966 selectOnFocus:true,
27967 width: item.width ? item.width : 130,
27969 'select': function(c, r, i) {
27971 tb.selectedNode.style[c.stylename] = r.get('val');
27974 tb.selectedNode.setAttribute(c.attrname, r.get('val'));
27983 tb.addField( new Roo.form.TextField({
27986 //allowBlank:false,
27991 tb.addField( new Roo.form.TextField({
27992 name: '-roo-edit-' + i,
27999 'change' : function(f, nv, ov) {
28000 tb.selectedNode.setAttribute(f.attrname, nv);
28009 text: 'Remove Tag',
28012 click : function ()
28015 // undo does not work.
28017 var sn = tb.selectedNode;
28019 var pn = sn.parentNode;
28021 var stn = sn.childNodes[0];
28022 var en = sn.childNodes[sn.childNodes.length - 1 ];
28023 while (sn.childNodes.length) {
28024 var node = sn.childNodes[0];
28025 sn.removeChild(node);
28027 pn.insertBefore(node, sn);
28030 pn.removeChild(sn);
28031 var range = editorcore.createRange();
28033 range.setStart(stn,0);
28034 range.setEnd(en,0); //????
28035 //range.selectNode(sel);
28038 var selection = editorcore.getSelection();
28039 selection.removeAllRanges();
28040 selection.addRange(range);
28044 //_this.updateToolbar(null, null, pn);
28045 _this.updateToolbar(null, null, null);
28046 _this.footDisp.dom.innerHTML = '';
28056 tb.el.on('click', function(e){
28057 e.preventDefault(); // what does this do?
28059 tb.el.setVisibilityMode( Roo.Element.DISPLAY);
28062 // dont need to disable them... as they will get hidden
28067 buildFooter : function()
28070 var fel = this.editor.wrap.createChild();
28071 this.footer = new Roo.Toolbar(fel);
28072 // toolbar has scrolly on left / right?
28073 var footDisp= new Roo.Toolbar.Fill();
28079 handler : function() {
28080 _t.footDisp.scrollTo('left',0,true)
28084 this.footer.add( footDisp );
28089 handler : function() {
28091 _t.footDisp.select('span').last().scrollIntoView(_t.footDisp,true);
28095 var fel = Roo.get(footDisp.el);
28096 fel.addClass('x-editor-context');
28097 this.footDispWrap = fel;
28098 this.footDispWrap.overflow = 'hidden';
28100 this.footDisp = fel.createChild();
28101 this.footDispWrap.on('click', this.onContextClick, this)
28105 onContextClick : function (ev,dom)
28107 ev.preventDefault();
28108 var cn = dom.className;
28110 if (!cn.match(/x-ed-loc-/)) {
28113 var n = cn.split('-').pop();
28114 var ans = this.footerEls;
28118 var range = this.editorcore.createRange();
28120 range.selectNodeContents(sel);
28121 //range.selectNode(sel);
28124 var selection = this.editorcore.getSelection();
28125 selection.removeAllRanges();
28126 selection.addRange(range);
28130 this.updateToolbar(null, null, sel);
28147 * Ext JS Library 1.1.1
28148 * Copyright(c) 2006-2007, Ext JS, LLC.
28150 * Originally Released Under LGPL - original licence link has changed is not relivant.
28153 * <script type="text/javascript">
28157 * @class Roo.form.BasicForm
28158 * @extends Roo.util.Observable
28159 * Supplies the functionality to do "actions" on forms and initialize Roo.form.Field types on existing markup.
28161 * @param {String/HTMLElement/Roo.Element} el The form element or its id
28162 * @param {Object} config Configuration options
28164 Roo.form.BasicForm = function(el, config){
28165 this.allItems = [];
28166 this.childForms = [];
28167 Roo.apply(this, config);
28169 * The Roo.form.Field items in this form.
28170 * @type MixedCollection
28174 this.items = new Roo.util.MixedCollection(false, function(o){
28175 return o.id || (o.id = Roo.id());
28179 * @event beforeaction
28180 * Fires before any action is performed. Return false to cancel the action.
28181 * @param {Form} this
28182 * @param {Action} action The action to be performed
28184 beforeaction: true,
28186 * @event actionfailed
28187 * Fires when an action fails.
28188 * @param {Form} this
28189 * @param {Action} action The action that failed
28191 actionfailed : true,
28193 * @event actioncomplete
28194 * Fires when an action is completed.
28195 * @param {Form} this
28196 * @param {Action} action The action that completed
28198 actioncomplete : true
28203 Roo.form.BasicForm.superclass.constructor.call(this);
28206 Roo.extend(Roo.form.BasicForm, Roo.util.Observable, {
28208 * @cfg {String} method
28209 * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
28212 * @cfg {DataReader} reader
28213 * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when executing "load" actions.
28214 * This is optional as there is built-in support for processing JSON.
28217 * @cfg {DataReader} errorReader
28218 * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when reading validation errors on "submit" actions.
28219 * This is completely optional as there is built-in support for processing JSON.
28222 * @cfg {String} url
28223 * The URL to use for form actions if one isn't supplied in the action options.
28226 * @cfg {Boolean} fileUpload
28227 * Set to true if this form is a file upload.
28231 * @cfg {Object} baseParams
28232 * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
28237 * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
28242 activeAction : null,
28245 * @cfg {Boolean} trackResetOnLoad If set to true, form.reset() resets to the last loaded
28246 * or setValues() data instead of when the form was first created.
28248 trackResetOnLoad : false,
28252 * childForms - used for multi-tab forms
28255 childForms : false,
28258 * allItems - full list of fields.
28264 * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
28265 * element by passing it or its id or mask the form itself by passing in true.
28268 waitMsgTarget : false,
28271 initEl : function(el){
28272 this.el = Roo.get(el);
28273 this.id = this.el.id || Roo.id();
28274 this.el.on('submit', this.onSubmit, this);
28275 this.el.addClass('x-form');
28279 onSubmit : function(e){
28284 * Returns true if client-side validation on the form is successful.
28287 isValid : function(){
28289 this.items.each(function(f){
28298 * Returns true if any fields in this form have changed since their original load.
28301 isDirty : function(){
28303 this.items.each(function(f){
28313 * Performs a predefined action (submit or load) or custom actions you define on this form.
28314 * @param {String} actionName The name of the action type
28315 * @param {Object} options (optional) The options to pass to the action. All of the config options listed
28316 * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
28317 * accept other config options):
28319 Property Type Description
28320 ---------------- --------------- ----------------------------------------------------------------------------------
28321 url String The url for the action (defaults to the form's url)
28322 method String The form method to use (defaults to the form's method, or POST if not defined)
28323 params String/Object The params to pass (defaults to the form's baseParams, or none if not defined)
28324 clientValidation Boolean Applies to submit only. Pass true to call form.isValid() prior to posting to
28325 validate the form on the client (defaults to false)
28327 * @return {BasicForm} this
28329 doAction : function(action, options){
28330 if(typeof action == 'string'){
28331 action = new Roo.form.Action.ACTION_TYPES[action](this, options);
28333 if(this.fireEvent('beforeaction', this, action) !== false){
28334 this.beforeAction(action);
28335 action.run.defer(100, action);
28341 * Shortcut to do a submit action.
28342 * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
28343 * @return {BasicForm} this
28345 submit : function(options){
28346 this.doAction('submit', options);
28351 * Shortcut to do a load action.
28352 * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
28353 * @return {BasicForm} this
28355 load : function(options){
28356 this.doAction('load', options);
28361 * Persists the values in this form into the passed Roo.data.Record object in a beginEdit/endEdit block.
28362 * @param {Record} record The record to edit
28363 * @return {BasicForm} this
28365 updateRecord : function(record){
28366 record.beginEdit();
28367 var fs = record.fields;
28368 fs.each(function(f){
28369 var field = this.findField(f.name);
28371 record.set(f.name, field.getValue());
28379 * Loads an Roo.data.Record into this form.
28380 * @param {Record} record The record to load
28381 * @return {BasicForm} this
28383 loadRecord : function(record){
28384 this.setValues(record.data);
28389 beforeAction : function(action){
28390 var o = action.options;
28393 if(this.waitMsgTarget === true){
28394 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
28395 }else if(this.waitMsgTarget){
28396 this.waitMsgTarget = Roo.get(this.waitMsgTarget);
28397 this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
28399 Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
28405 afterAction : function(action, success){
28406 this.activeAction = null;
28407 var o = action.options;
28409 if(this.waitMsgTarget === true){
28411 }else if(this.waitMsgTarget){
28412 this.waitMsgTarget.unmask();
28414 Roo.MessageBox.updateProgress(1);
28415 Roo.MessageBox.hide();
28422 Roo.callback(o.success, o.scope, [this, action]);
28423 this.fireEvent('actioncomplete', this, action);
28427 // failure condition..
28428 // we have a scenario where updates need confirming.
28429 // eg. if a locking scenario exists..
28430 // we look for { errors : { needs_confirm : true }} in the response.
28432 (typeof(action.result) != 'undefined') &&
28433 (typeof(action.result.errors) != 'undefined') &&
28434 (typeof(action.result.errors.needs_confirm) != 'undefined')
28437 Roo.MessageBox.confirm(
28438 "Change requires confirmation",
28439 action.result.errorMsg,
28444 _t.doAction('submit', { params : { _submit_confirmed : 1 } } );
28454 Roo.callback(o.failure, o.scope, [this, action]);
28455 // show an error message if no failed handler is set..
28456 if (!this.hasListener('actionfailed')) {
28457 Roo.MessageBox.alert("Error",
28458 (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
28459 action.result.errorMsg :
28460 "Saving Failed, please check your entries or try again"
28464 this.fireEvent('actionfailed', this, action);
28470 * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
28471 * @param {String} id The value to search for
28474 findField : function(id){
28475 var field = this.items.get(id);
28477 this.items.each(function(f){
28478 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
28484 return field || null;
28488 * Add a secondary form to this one,
28489 * Used to provide tabbed forms. One form is primary, with hidden values
28490 * which mirror the elements from the other forms.
28492 * @param {Roo.form.Form} form to add.
28495 addForm : function(form)
28498 if (this.childForms.indexOf(form) > -1) {
28502 this.childForms.push(form);
28504 Roo.each(form.allItems, function (fe) {
28506 n = typeof(fe.getName) == 'undefined' ? fe.name : fe.getName();
28507 if (this.findField(n)) { // already added..
28510 var add = new Roo.form.Hidden({
28513 add.render(this.el);
28520 * Mark fields in this form invalid in bulk.
28521 * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
28522 * @return {BasicForm} this
28524 markInvalid : function(errors){
28525 if(errors instanceof Array){
28526 for(var i = 0, len = errors.length; i < len; i++){
28527 var fieldError = errors[i];
28528 var f = this.findField(fieldError.id);
28530 f.markInvalid(fieldError.msg);
28536 if(typeof errors[id] != 'function' && (field = this.findField(id))){
28537 field.markInvalid(errors[id]);
28541 Roo.each(this.childForms || [], function (f) {
28542 f.markInvalid(errors);
28549 * Set values for fields in this form in bulk.
28550 * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
28551 * @return {BasicForm} this
28553 setValues : function(values){
28554 if(values instanceof Array){ // array of objects
28555 for(var i = 0, len = values.length; i < len; i++){
28557 var f = this.findField(v.id);
28559 f.setValue(v.value);
28560 if(this.trackResetOnLoad){
28561 f.originalValue = f.getValue();
28565 }else{ // object hash
28568 if(typeof values[id] != 'function' && (field = this.findField(id))){
28570 if (field.setFromData &&
28571 field.valueField &&
28572 field.displayField &&
28573 // combos' with local stores can
28574 // be queried via setValue()
28575 // to set their value..
28576 (field.store && !field.store.isLocal)
28580 sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
28581 sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
28582 field.setFromData(sd);
28585 field.setValue(values[id]);
28589 if(this.trackResetOnLoad){
28590 field.originalValue = field.getValue();
28596 Roo.each(this.childForms || [], function (f) {
28597 f.setValues(values);
28604 * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
28605 * they are returned as an array.
28606 * @param {Boolean} asString
28609 getValues : function(asString){
28610 if (this.childForms) {
28611 // copy values from the child forms
28612 Roo.each(this.childForms, function (f) {
28613 this.setValues(f.getValues());
28619 var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
28620 if(asString === true){
28623 return Roo.urlDecode(fs);
28627 * Returns the fields in this form as an object with key/value pairs.
28628 * This differs from getValues as it calls getValue on each child item, rather than using dom data.
28631 getFieldValues : function(with_hidden)
28633 if (this.childForms) {
28634 // copy values from the child forms
28635 // should this call getFieldValues - probably not as we do not currently copy
28636 // hidden fields when we generate..
28637 Roo.each(this.childForms, function (f) {
28638 this.setValues(f.getValues());
28643 this.items.each(function(f){
28644 if (!f.getName()) {
28647 var v = f.getValue();
28648 if (f.inputType =='radio') {
28649 if (typeof(ret[f.getName()]) == 'undefined') {
28650 ret[f.getName()] = ''; // empty..
28653 if (!f.el.dom.checked) {
28657 v = f.el.dom.value;
28661 // not sure if this supported any more..
28662 if ((typeof(v) == 'object') && f.getRawValue) {
28663 v = f.getRawValue() ; // dates..
28665 // combo boxes where name != hiddenName...
28666 if (f.name != f.getName()) {
28667 ret[f.name] = f.getRawValue();
28669 ret[f.getName()] = v;
28676 * Clears all invalid messages in this form.
28677 * @return {BasicForm} this
28679 clearInvalid : function(){
28680 this.items.each(function(f){
28684 Roo.each(this.childForms || [], function (f) {
28693 * Resets this form.
28694 * @return {BasicForm} this
28696 reset : function(){
28697 this.items.each(function(f){
28701 Roo.each(this.childForms || [], function (f) {
28710 * Add Roo.form components to this form.
28711 * @param {Field} field1
28712 * @param {Field} field2 (optional)
28713 * @param {Field} etc (optional)
28714 * @return {BasicForm} this
28717 this.items.addAll(Array.prototype.slice.call(arguments, 0));
28723 * Removes a field from the items collection (does NOT remove its markup).
28724 * @param {Field} field
28725 * @return {BasicForm} this
28727 remove : function(field){
28728 this.items.remove(field);
28733 * Looks at the fields in this form, checks them for an id attribute,
28734 * and calls applyTo on the existing dom element with that id.
28735 * @return {BasicForm} this
28737 render : function(){
28738 this.items.each(function(f){
28739 if(f.isFormField && !f.rendered && document.getElementById(f.id)){ // if the element exists
28747 * Calls {@link Ext#apply} for all fields in this form with the passed object.
28748 * @param {Object} values
28749 * @return {BasicForm} this
28751 applyToFields : function(o){
28752 this.items.each(function(f){
28759 * Calls {@link Ext#applyIf} for all field in this form with the passed object.
28760 * @param {Object} values
28761 * @return {BasicForm} this
28763 applyIfToFields : function(o){
28764 this.items.each(function(f){
28772 Roo.BasicForm = Roo.form.BasicForm;/*
28774 * Ext JS Library 1.1.1
28775 * Copyright(c) 2006-2007, Ext JS, LLC.
28777 * Originally Released Under LGPL - original licence link has changed is not relivant.
28780 * <script type="text/javascript">
28784 * @class Roo.form.Form
28785 * @extends Roo.form.BasicForm
28786 * Adds the ability to dynamically render forms with JavaScript to {@link Roo.form.BasicForm}.
28788 * @param {Object} config Configuration options
28790 Roo.form.Form = function(config){
28792 if (config.items) {
28793 xitems = config.items;
28794 delete config.items;
28798 Roo.form.Form.superclass.constructor.call(this, null, config);
28799 this.url = this.url || this.action;
28801 this.root = new Roo.form.Layout(Roo.applyIf({
28805 this.active = this.root;
28807 * Array of all the buttons that have been added to this form via {@link addButton}
28811 this.allItems = [];
28814 * @event clientvalidation
28815 * If the monitorValid config option is true, this event fires repetitively to notify of valid state
28816 * @param {Form} this
28817 * @param {Boolean} valid true if the form has passed client-side validation
28819 clientvalidation: true,
28822 * Fires when the form is rendered
28823 * @param {Roo.form.Form} form
28828 if (this.progressUrl) {
28829 // push a hidden field onto the list of fields..
28833 name : 'UPLOAD_IDENTIFIER'
28838 Roo.each(xitems, this.addxtype, this);
28844 Roo.extend(Roo.form.Form, Roo.form.BasicForm, {
28846 * @cfg {Number} labelWidth The width of labels. This property cascades to child containers.
28849 * @cfg {String} itemCls A css class to apply to the x-form-item of fields. This property cascades to child containers.
28852 * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "center")
28854 buttonAlign:'center',
28857 * @cfg {Number} minButtonWidth Minimum width of all buttons in pixels (defaults to 75)
28862 * @cfg {String} labelAlign Valid values are "left," "top" and "right" (defaults to "left").
28863 * This property cascades to child containers if not set.
28868 * @cfg {Boolean} monitorValid If true the form monitors its valid state <b>client-side</b> and
28869 * fires a looping event with that state. This is required to bind buttons to the valid
28870 * state using the config value formBind:true on the button.
28872 monitorValid : false,
28875 * @cfg {Number} monitorPoll The milliseconds to poll valid state, ignored if monitorValid is not true (defaults to 200)
28880 * @cfg {String} progressUrl - Url to return progress data
28883 progressUrl : false,
28886 * Opens a new {@link Roo.form.Column} container in the layout stack. If fields are passed after the config, the
28887 * fields are added and the column is closed. If no fields are passed the column remains open
28888 * until end() is called.
28889 * @param {Object} config The config to pass to the column
28890 * @param {Field} field1 (optional)
28891 * @param {Field} field2 (optional)
28892 * @param {Field} etc (optional)
28893 * @return Column The column container object
28895 column : function(c){
28896 var col = new Roo.form.Column(c);
28898 if(arguments.length > 1){ // duplicate code required because of Opera
28899 this.add.apply(this, Array.prototype.slice.call(arguments, 1));
28906 * Opens a new {@link Roo.form.FieldSet} container in the layout stack. If fields are passed after the config, the
28907 * fields are added and the fieldset is closed. If no fields are passed the fieldset remains open
28908 * until end() is called.
28909 * @param {Object} config The config to pass to the fieldset
28910 * @param {Field} field1 (optional)
28911 * @param {Field} field2 (optional)
28912 * @param {Field} etc (optional)
28913 * @return FieldSet The fieldset container object
28915 fieldset : function(c){
28916 var fs = new Roo.form.FieldSet(c);
28918 if(arguments.length > 1){ // duplicate code required because of Opera
28919 this.add.apply(this, Array.prototype.slice.call(arguments, 1));
28926 * Opens a new {@link Roo.form.Layout} container in the layout stack. If fields are passed after the config, the
28927 * fields are added and the container is closed. If no fields are passed the container remains open
28928 * until end() is called.
28929 * @param {Object} config The config to pass to the Layout
28930 * @param {Field} field1 (optional)
28931 * @param {Field} field2 (optional)
28932 * @param {Field} etc (optional)
28933 * @return Layout The container object
28935 container : function(c){
28936 var l = new Roo.form.Layout(c);
28938 if(arguments.length > 1){ // duplicate code required because of Opera
28939 this.add.apply(this, Array.prototype.slice.call(arguments, 1));
28946 * Opens the passed container in the layout stack. The container can be any {@link Roo.form.Layout} or subclass.
28947 * @param {Object} container A Roo.form.Layout or subclass of Layout
28948 * @return {Form} this
28950 start : function(c){
28951 // cascade label info
28952 Roo.applyIf(c, {'labelAlign': this.active.labelAlign, 'labelWidth': this.active.labelWidth, 'itemCls': this.active.itemCls});
28953 this.active.stack.push(c);
28954 c.ownerCt = this.active;
28960 * Closes the current open container
28961 * @return {Form} this
28964 if(this.active == this.root){
28967 this.active = this.active.ownerCt;
28972 * Add Roo.form components to the current open container (e.g. column, fieldset, etc.). Fields added via this method
28973 * can also be passed with an additional property of fieldLabel, which if supplied, will provide the text to display
28974 * as the label of the field.
28975 * @param {Field} field1
28976 * @param {Field} field2 (optional)
28977 * @param {Field} etc. (optional)
28978 * @return {Form} this
28981 this.active.stack.push.apply(this.active.stack, arguments);
28982 this.allItems.push.apply(this.allItems,arguments);
28984 for(var i = 0, a = arguments, len = a.length; i < len; i++) {
28985 if(a[i].isFormField){
28990 Roo.form.Form.superclass.add.apply(this, r);
29000 * Find any element that has been added to a form, using it's ID or name
29001 * This can include framesets, columns etc. along with regular fields..
29002 * @param {String} id - id or name to find.
29004 * @return {Element} e - or false if nothing found.
29006 findbyId : function(id)
29012 Roo.each(this.allItems, function(f){
29013 if (f.id == id || f.name == id ){
29024 * Render this form into the passed container. This should only be called once!
29025 * @param {String/HTMLElement/Element} container The element this component should be rendered into
29026 * @return {Form} this
29028 render : function(ct)
29034 var o = this.autoCreate || {
29036 method : this.method || 'POST',
29037 id : this.id || Roo.id()
29039 this.initEl(ct.createChild(o));
29041 this.root.render(this.el);
29045 this.items.each(function(f){
29046 f.render('x-form-el-'+f.id);
29049 if(this.buttons.length > 0){
29050 // tables are required to maintain order and for correct IE layout
29051 var tb = this.el.createChild({cls:'x-form-btns-ct', cn: {
29052 cls:"x-form-btns x-form-btns-"+this.buttonAlign,
29053 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
29055 var tr = tb.getElementsByTagName('tr')[0];
29056 for(var i = 0, len = this.buttons.length; i < len; i++) {
29057 var b = this.buttons[i];
29058 var td = document.createElement('td');
29059 td.className = 'x-form-btn-td';
29060 b.render(tr.appendChild(td));
29063 if(this.monitorValid){ // initialize after render
29064 this.startMonitoring();
29066 this.fireEvent('rendered', this);
29071 * Adds a button to the footer of the form - this <b>must</b> be called before the form is rendered.
29072 * @param {String/Object} config A string becomes the button text, an object can either be a Button config
29073 * object or a valid Roo.DomHelper element config
29074 * @param {Function} handler The function called when the button is clicked
29075 * @param {Object} scope (optional) The scope of the handler function
29076 * @return {Roo.Button}
29078 addButton : function(config, handler, scope){
29082 minWidth: this.minButtonWidth,
29085 if(typeof config == "string"){
29088 Roo.apply(bc, config);
29090 var btn = new Roo.Button(null, bc);
29091 this.buttons.push(btn);
29096 * Adds a series of form elements (using the xtype property as the factory method.
29097 * Valid xtypes are: TextField, TextArea .... Button, Layout, FieldSet, Column, (and 'end' to close a block)
29098 * @param {Object} config
29101 addxtype : function()
29103 var ar = Array.prototype.slice.call(arguments, 0);
29105 for(var i = 0; i < ar.length; i++) {
29107 continue; // skip -- if this happends something invalid got sent, we
29108 // should ignore it, as basically that interface element will not show up
29109 // and that should be pretty obvious!!
29112 if (Roo.form[ar[i].xtype]) {
29114 var fe = Roo.factory(ar[i], Roo.form);
29120 fe.store.form = this;
29125 this.allItems.push(fe);
29126 if (fe.items && fe.addxtype) {
29127 fe.addxtype.apply(fe, fe.items);
29137 // console.log('adding ' + ar[i].xtype);
29139 if (ar[i].xtype == 'Button') {
29140 //console.log('adding button');
29141 //console.log(ar[i]);
29142 this.addButton(ar[i]);
29143 this.allItems.push(fe);
29147 if (ar[i].xtype == 'end') { // so we can add fieldsets... / layout etc.
29148 alert('end is not supported on xtype any more, use items');
29150 // //console.log('adding end');
29158 * Starts monitoring of the valid state of this form. Usually this is done by passing the config
29159 * option "monitorValid"
29161 startMonitoring : function(){
29164 Roo.TaskMgr.start({
29165 run : this.bindHandler,
29166 interval : this.monitorPoll || 200,
29173 * Stops monitoring of the valid state of this form
29175 stopMonitoring : function(){
29176 this.bound = false;
29180 bindHandler : function(){
29182 return false; // stops binding
29185 this.items.each(function(f){
29186 if(!f.isValid(true)){
29191 for(var i = 0, len = this.buttons.length; i < len; i++){
29192 var btn = this.buttons[i];
29193 if(btn.formBind === true && btn.disabled === valid){
29194 btn.setDisabled(!valid);
29197 this.fireEvent('clientvalidation', this, valid);
29211 Roo.Form = Roo.form.Form;
29214 * Ext JS Library 1.1.1
29215 * Copyright(c) 2006-2007, Ext JS, LLC.
29217 * Originally Released Under LGPL - original licence link has changed is not relivant.
29220 * <script type="text/javascript">
29223 // as we use this in bootstrap.
29224 Roo.namespace('Roo.form');
29226 * @class Roo.form.Action
29227 * Internal Class used to handle form actions
29229 * @param {Roo.form.BasicForm} el The form element or its id
29230 * @param {Object} config Configuration options
29235 // define the action interface
29236 Roo.form.Action = function(form, options){
29238 this.options = options || {};
29241 * Client Validation Failed
29244 Roo.form.Action.CLIENT_INVALID = 'client';
29246 * Server Validation Failed
29249 Roo.form.Action.SERVER_INVALID = 'server';
29251 * Connect to Server Failed
29254 Roo.form.Action.CONNECT_FAILURE = 'connect';
29256 * Reading Data from Server Failed
29259 Roo.form.Action.LOAD_FAILURE = 'load';
29261 Roo.form.Action.prototype = {
29263 failureType : undefined,
29264 response : undefined,
29265 result : undefined,
29267 // interface method
29268 run : function(options){
29272 // interface method
29273 success : function(response){
29277 // interface method
29278 handleResponse : function(response){
29282 // default connection failure
29283 failure : function(response){
29285 this.response = response;
29286 this.failureType = Roo.form.Action.CONNECT_FAILURE;
29287 this.form.afterAction(this, false);
29290 processResponse : function(response){
29291 this.response = response;
29292 if(!response.responseText){
29295 this.result = this.handleResponse(response);
29296 return this.result;
29299 // utility functions used internally
29300 getUrl : function(appendParams){
29301 var url = this.options.url || this.form.url || this.form.el.dom.action;
29303 var p = this.getParams();
29305 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
29311 getMethod : function(){
29312 return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
29315 getParams : function(){
29316 var bp = this.form.baseParams;
29317 var p = this.options.params;
29319 if(typeof p == "object"){
29320 p = Roo.urlEncode(Roo.applyIf(p, bp));
29321 }else if(typeof p == 'string' && bp){
29322 p += '&' + Roo.urlEncode(bp);
29325 p = Roo.urlEncode(bp);
29330 createCallback : function(){
29332 success: this.success,
29333 failure: this.failure,
29335 timeout: (this.form.timeout*1000),
29336 upload: this.form.fileUpload ? this.success : undefined
29341 Roo.form.Action.Submit = function(form, options){
29342 Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
29345 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
29348 haveProgress : false,
29349 uploadComplete : false,
29351 // uploadProgress indicator.
29352 uploadProgress : function()
29354 if (!this.form.progressUrl) {
29358 if (!this.haveProgress) {
29359 Roo.MessageBox.progress("Uploading", "Uploading");
29361 if (this.uploadComplete) {
29362 Roo.MessageBox.hide();
29366 this.haveProgress = true;
29368 var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
29370 var c = new Roo.data.Connection();
29372 url : this.form.progressUrl,
29377 success : function(req){
29378 //console.log(data);
29382 rdata = Roo.decode(req.responseText)
29384 Roo.log("Invalid data from server..");
29388 if (!rdata || !rdata.success) {
29390 Roo.MessageBox.alert(Roo.encode(rdata));
29393 var data = rdata.data;
29395 if (this.uploadComplete) {
29396 Roo.MessageBox.hide();
29401 Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
29402 Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
29405 this.uploadProgress.defer(2000,this);
29408 failure: function(data) {
29409 Roo.log('progress url failed ');
29420 // run get Values on the form, so it syncs any secondary forms.
29421 this.form.getValues();
29423 var o = this.options;
29424 var method = this.getMethod();
29425 var isPost = method == 'POST';
29426 if(o.clientValidation === false || this.form.isValid()){
29428 if (this.form.progressUrl) {
29429 this.form.findField('UPLOAD_IDENTIFIER').setValue(
29430 (new Date() * 1) + '' + Math.random());
29435 Roo.Ajax.request(Roo.apply(this.createCallback(), {
29436 form:this.form.el.dom,
29437 url:this.getUrl(!isPost),
29439 params:isPost ? this.getParams() : null,
29440 isUpload: this.form.fileUpload
29443 this.uploadProgress();
29445 }else if (o.clientValidation !== false){ // client validation failed
29446 this.failureType = Roo.form.Action.CLIENT_INVALID;
29447 this.form.afterAction(this, false);
29451 success : function(response)
29453 this.uploadComplete= true;
29454 if (this.haveProgress) {
29455 Roo.MessageBox.hide();
29459 var result = this.processResponse(response);
29460 if(result === true || result.success){
29461 this.form.afterAction(this, true);
29465 this.form.markInvalid(result.errors);
29466 this.failureType = Roo.form.Action.SERVER_INVALID;
29468 this.form.afterAction(this, false);
29470 failure : function(response)
29472 this.uploadComplete= true;
29473 if (this.haveProgress) {
29474 Roo.MessageBox.hide();
29477 this.response = response;
29478 this.failureType = Roo.form.Action.CONNECT_FAILURE;
29479 this.form.afterAction(this, false);
29482 handleResponse : function(response){
29483 if(this.form.errorReader){
29484 var rs = this.form.errorReader.read(response);
29487 for(var i = 0, len = rs.records.length; i < len; i++) {
29488 var r = rs.records[i];
29489 errors[i] = r.data;
29492 if(errors.length < 1){
29496 success : rs.success,
29502 ret = Roo.decode(response.responseText);
29506 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
29516 Roo.form.Action.Load = function(form, options){
29517 Roo.form.Action.Load.superclass.constructor.call(this, form, options);
29518 this.reader = this.form.reader;
29521 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
29526 Roo.Ajax.request(Roo.apply(
29527 this.createCallback(), {
29528 method:this.getMethod(),
29529 url:this.getUrl(false),
29530 params:this.getParams()
29534 success : function(response){
29536 var result = this.processResponse(response);
29537 if(result === true || !result.success || !result.data){
29538 this.failureType = Roo.form.Action.LOAD_FAILURE;
29539 this.form.afterAction(this, false);
29542 this.form.clearInvalid();
29543 this.form.setValues(result.data);
29544 this.form.afterAction(this, true);
29547 handleResponse : function(response){
29548 if(this.form.reader){
29549 var rs = this.form.reader.read(response);
29550 var data = rs.records && rs.records[0] ? rs.records[0].data : null;
29552 success : rs.success,
29556 return Roo.decode(response.responseText);
29560 Roo.form.Action.ACTION_TYPES = {
29561 'load' : Roo.form.Action.Load,
29562 'submit' : Roo.form.Action.Submit
29565 * Ext JS Library 1.1.1
29566 * Copyright(c) 2006-2007, Ext JS, LLC.
29568 * Originally Released Under LGPL - original licence link has changed is not relivant.
29571 * <script type="text/javascript">
29575 * @class Roo.form.Layout
29576 * @extends Roo.Component
29577 * Creates a container for layout and rendering of fields in an {@link Roo.form.Form}.
29579 * @param {Object} config Configuration options
29581 Roo.form.Layout = function(config){
29583 if (config.items) {
29584 xitems = config.items;
29585 delete config.items;
29587 Roo.form.Layout.superclass.constructor.call(this, config);
29589 Roo.each(xitems, this.addxtype, this);
29593 Roo.extend(Roo.form.Layout, Roo.Component, {
29595 * @cfg {String/Object} autoCreate
29596 * A DomHelper element spec used to autocreate the layout (defaults to {tag: 'div', cls: 'x-form-ct'})
29599 * @cfg {String/Object/Function} style
29600 * A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
29601 * a function which returns such a specification.
29604 * @cfg {String} labelAlign
29605 * Valid values are "left," "top" and "right" (defaults to "left")
29608 * @cfg {Number} labelWidth
29609 * Fixed width in pixels of all field labels (defaults to undefined)
29612 * @cfg {Boolean} clear
29613 * True to add a clearing element at the end of this layout, equivalent to CSS clear: both (defaults to true)
29617 * @cfg {String} labelSeparator
29618 * The separator to use after field labels (defaults to ':')
29620 labelSeparator : ':',
29622 * @cfg {Boolean} hideLabels
29623 * True to suppress the display of field labels in this layout (defaults to false)
29625 hideLabels : false,
29628 defaultAutoCreate : {tag: 'div', cls: 'x-form-ct'},
29633 onRender : function(ct, position){
29634 if(this.el){ // from markup
29635 this.el = Roo.get(this.el);
29636 }else { // generate
29637 var cfg = this.getAutoCreate();
29638 this.el = ct.createChild(cfg, position);
29641 this.el.applyStyles(this.style);
29643 if(this.labelAlign){
29644 this.el.addClass('x-form-label-'+this.labelAlign);
29646 if(this.hideLabels){
29647 this.labelStyle = "display:none";
29648 this.elementStyle = "padding-left:0;";
29650 if(typeof this.labelWidth == 'number'){
29651 this.labelStyle = "width:"+this.labelWidth+"px;";
29652 this.elementStyle = "padding-left:"+((this.labelWidth+(typeof this.labelPad == 'number' ? this.labelPad : 5))+'px')+";";
29654 if(this.labelAlign == 'top'){
29655 this.labelStyle = "width:auto;";
29656 this.elementStyle = "padding-left:0;";
29659 var stack = this.stack;
29660 var slen = stack.length;
29662 if(!this.fieldTpl){
29663 var t = new Roo.Template(
29664 '<div class="x-form-item {5}">',
29665 '<label for="{0}" style="{2}">{1}{4}</label>',
29666 '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
29668 '</div><div class="x-form-clear-left"></div>'
29670 t.disableFormats = true;
29672 Roo.form.Layout.prototype.fieldTpl = t;
29674 for(var i = 0; i < slen; i++) {
29675 if(stack[i].isFormField){
29676 this.renderField(stack[i]);
29678 this.renderComponent(stack[i]);
29683 this.el.createChild({cls:'x-form-clear'});
29688 renderField : function(f){
29689 f.fieldEl = Roo.get(this.fieldTpl.append(this.el, [
29692 f.labelStyle||this.labelStyle||'', //2
29693 this.elementStyle||'', //3
29694 typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator, //4
29695 f.itemCls||this.itemCls||'' //5
29696 ], true).getPrevSibling());
29700 renderComponent : function(c){
29701 c.render(c.isLayout ? this.el : this.el.createChild());
29704 * Adds a object form elements (using the xtype property as the factory method.)
29705 * Valid xtypes are: TextField, TextArea .... Button, Layout, FieldSet, Column
29706 * @param {Object} config
29708 addxtype : function(o)
29710 // create the lement.
29711 o.form = this.form;
29712 var fe = Roo.factory(o, Roo.form);
29713 this.form.allItems.push(fe);
29714 this.stack.push(fe);
29716 if (fe.isFormField) {
29717 this.form.items.add(fe);
29725 * @class Roo.form.Column
29726 * @extends Roo.form.Layout
29727 * Creates a column container for layout and rendering of fields in an {@link Roo.form.Form}.
29729 * @param {Object} config Configuration options
29731 Roo.form.Column = function(config){
29732 Roo.form.Column.superclass.constructor.call(this, config);
29735 Roo.extend(Roo.form.Column, Roo.form.Layout, {
29737 * @cfg {Number/String} width
29738 * The fixed width of the column in pixels or CSS value (defaults to "auto")
29741 * @cfg {String/Object} autoCreate
29742 * A DomHelper element spec used to autocreate the column (defaults to {tag: 'div', cls: 'x-form-ct x-form-column'})
29746 defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-column'},
29749 onRender : function(ct, position){
29750 Roo.form.Column.superclass.onRender.call(this, ct, position);
29752 this.el.setWidth(this.width);
29759 * @class Roo.form.Row
29760 * @extends Roo.form.Layout
29761 * Creates a row container for layout and rendering of fields in an {@link Roo.form.Form}.
29763 * @param {Object} config Configuration options
29767 Roo.form.Row = function(config){
29768 Roo.form.Row.superclass.constructor.call(this, config);
29771 Roo.extend(Roo.form.Row, Roo.form.Layout, {
29773 * @cfg {Number/String} width
29774 * The fixed width of the column in pixels or CSS value (defaults to "auto")
29777 * @cfg {Number/String} height
29778 * The fixed height of the column in pixels or CSS value (defaults to "auto")
29780 defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-row'},
29784 onRender : function(ct, position){
29785 //console.log('row render');
29787 var t = new Roo.Template(
29788 '<div class="x-form-item {5}" style="float:left;width:{6}px">',
29789 '<label for="{0}" style="{2}">{1}{4}</label>',
29790 '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
29794 t.disableFormats = true;
29796 Roo.form.Layout.prototype.rowTpl = t;
29798 this.fieldTpl = this.rowTpl;
29800 //console.log('lw' + this.labelWidth +', la:' + this.labelAlign);
29801 var labelWidth = 100;
29803 if ((this.labelAlign != 'top')) {
29804 if (typeof this.labelWidth == 'number') {
29805 labelWidth = this.labelWidth
29807 this.padWidth = 20 + labelWidth;
29811 Roo.form.Column.superclass.onRender.call(this, ct, position);
29813 this.el.setWidth(this.width);
29816 this.el.setHeight(this.height);
29821 renderField : function(f){
29822 f.fieldEl = this.fieldTpl.append(this.el, [
29823 f.id, f.fieldLabel,
29824 f.labelStyle||this.labelStyle||'',
29825 this.elementStyle||'',
29826 typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator,
29827 f.itemCls||this.itemCls||'',
29828 f.width ? f.width + this.padWidth : 160 + this.padWidth
29835 * @class Roo.form.FieldSet
29836 * @extends Roo.form.Layout
29837 * Creates a fieldset container for layout and rendering of fields in an {@link Roo.form.Form}.
29839 * @param {Object} config Configuration options
29841 Roo.form.FieldSet = function(config){
29842 Roo.form.FieldSet.superclass.constructor.call(this, config);
29845 Roo.extend(Roo.form.FieldSet, Roo.form.Layout, {
29847 * @cfg {String} legend
29848 * The text to display as the legend for the FieldSet (defaults to '')
29851 * @cfg {String/Object} autoCreate
29852 * A DomHelper element spec used to autocreate the fieldset (defaults to {tag: 'fieldset', cn: {tag:'legend'}})
29856 defaultAutoCreate : {tag: 'fieldset', cn: {tag:'legend'}},
29859 onRender : function(ct, position){
29860 Roo.form.FieldSet.superclass.onRender.call(this, ct, position);
29862 this.setLegend(this.legend);
29867 setLegend : function(text){
29869 this.el.child('legend').update(text);
29874 * Ext JS Library 1.1.1
29875 * Copyright(c) 2006-2007, Ext JS, LLC.
29877 * Originally Released Under LGPL - original licence link has changed is not relivant.
29880 * <script type="text/javascript">
29883 * @class Roo.form.VTypes
29884 * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
29887 Roo.form.VTypes = function(){
29888 // closure these in so they are only created once.
29889 var alpha = /^[a-zA-Z_]+$/;
29890 var alphanum = /^[a-zA-Z0-9_]+$/;
29891 var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,4}$/;
29892 var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
29894 // All these messages and functions are configurable
29897 * The function used to validate email addresses
29898 * @param {String} value The email address
29900 'email' : function(v){
29901 return email.test(v);
29904 * The error text to display when the email validation function returns false
29907 'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
29909 * The keystroke filter mask to be applied on email input
29912 'emailMask' : /[a-z0-9_\.\-@]/i,
29915 * The function used to validate URLs
29916 * @param {String} value The URL
29918 'url' : function(v){
29919 return url.test(v);
29922 * The error text to display when the url validation function returns false
29925 'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
29928 * The function used to validate alpha values
29929 * @param {String} value The value
29931 'alpha' : function(v){
29932 return alpha.test(v);
29935 * The error text to display when the alpha validation function returns false
29938 'alphaText' : 'This field should only contain letters and _',
29940 * The keystroke filter mask to be applied on alpha input
29943 'alphaMask' : /[a-z_]/i,
29946 * The function used to validate alphanumeric values
29947 * @param {String} value The value
29949 'alphanum' : function(v){
29950 return alphanum.test(v);
29953 * The error text to display when the alphanumeric validation function returns false
29956 'alphanumText' : 'This field should only contain letters, numbers and _',
29958 * The keystroke filter mask to be applied on alphanumeric input
29961 'alphanumMask' : /[a-z0-9_]/i
29963 }();//<script type="text/javascript">
29966 * @class Roo.form.FCKeditor
29967 * @extends Roo.form.TextArea
29968 * Wrapper around the FCKEditor http://www.fckeditor.net
29970 * Creates a new FCKeditor
29971 * @param {Object} config Configuration options
29973 Roo.form.FCKeditor = function(config){
29974 Roo.form.FCKeditor.superclass.constructor.call(this, config);
29977 * @event editorinit
29978 * Fired when the editor is initialized - you can add extra handlers here..
29979 * @param {FCKeditor} this
29980 * @param {Object} the FCK object.
29987 Roo.form.FCKeditor.editors = { };
29988 Roo.extend(Roo.form.FCKeditor, Roo.form.TextArea,
29990 //defaultAutoCreate : {
29991 // tag : "textarea",style : "width:100px;height:60px;" ,autocomplete : "off"
29995 * @cfg {Object} fck options - see fck manual for details.
30000 * @cfg {Object} fck toolbar set (Basic or Default)
30002 toolbarSet : 'Basic',
30004 * @cfg {Object} fck BasePath
30006 basePath : '/fckeditor/',
30014 onRender : function(ct, position)
30017 this.defaultAutoCreate = {
30019 style:"width:300px;height:60px;",
30020 autocomplete: "off"
30023 Roo.form.FCKeditor.superclass.onRender.call(this, ct, position);
30026 this.textSizeEl = Roo.DomHelper.append(document.body, {tag: "pre", cls: "x-form-grow-sizer"});
30027 if(this.preventScrollbars){
30028 this.el.setStyle("overflow", "hidden");
30030 this.el.setHeight(this.growMin);
30033 //console.log('onrender' + this.getId() );
30034 Roo.form.FCKeditor.editors[this.getId()] = this;
30037 this.replaceTextarea() ;
30041 getEditor : function() {
30042 return this.fckEditor;
30045 * Sets a data value into the field and validates it. To set the value directly without validation see {@link #setRawValue}.
30046 * @param {Mixed} value The value to set
30050 setValue : function(value)
30052 //console.log('setValue: ' + value);
30054 if(typeof(value) == 'undefined') { // not sure why this is happending...
30057 Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
30059 //if(!this.el || !this.getEditor()) {
30060 // this.value = value;
30061 //this.setValue.defer(100,this,[value]);
30065 if(!this.getEditor()) {
30069 this.getEditor().SetData(value);
30076 * Returns the normalized data value (undefined or emptyText will be returned as ''). To return the raw value see {@link #getRawValue}.
30077 * @return {Mixed} value The field value
30079 getValue : function()
30082 if (this.frame && this.frame.dom.style.display == 'none') {
30083 return Roo.form.FCKeditor.superclass.getValue.call(this);
30086 if(!this.el || !this.getEditor()) {
30088 // this.getValue.defer(100,this);
30093 var value=this.getEditor().GetData();
30094 Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
30095 return Roo.form.FCKeditor.superclass.getValue.call(this);
30101 * Returns the raw data value which may or may not be a valid, defined value. To return a normalized value see {@link #getValue}.
30102 * @return {Mixed} value The field value
30104 getRawValue : function()
30106 if (this.frame && this.frame.dom.style.display == 'none') {
30107 return Roo.form.FCKeditor.superclass.getRawValue.call(this);
30110 if(!this.el || !this.getEditor()) {
30111 //this.getRawValue.defer(100,this);
30118 var value=this.getEditor().GetData();
30119 Roo.form.FCKeditor.superclass.setRawValue.apply(this,[value]);
30120 return Roo.form.FCKeditor.superclass.getRawValue.call(this);
30124 setSize : function(w,h) {
30128 //if (this.frame && this.frame.dom.style.display == 'none') {
30129 // Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
30132 //if(!this.el || !this.getEditor()) {
30133 // this.setSize.defer(100,this, [w,h]);
30139 Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
30141 this.frame.dom.setAttribute('width', w);
30142 this.frame.dom.setAttribute('height', h);
30143 this.frame.setSize(w,h);
30147 toggleSourceEdit : function(value) {
30151 this.el.dom.style.display = value ? '' : 'none';
30152 this.frame.dom.style.display = value ? 'none' : '';
30157 focus: function(tag)
30159 if (this.frame.dom.style.display == 'none') {
30160 return Roo.form.FCKeditor.superclass.focus.call(this);
30162 if(!this.el || !this.getEditor()) {
30163 this.focus.defer(100,this, [tag]);
30170 var tgs = this.getEditor().EditorDocument.getElementsByTagName(tag);
30171 this.getEditor().Focus();
30173 if (!this.getEditor().Selection.GetSelection()) {
30174 this.focus.defer(100,this, [tag]);
30179 var r = this.getEditor().EditorDocument.createRange();
30180 r.setStart(tgs[0],0);
30181 r.setEnd(tgs[0],0);
30182 this.getEditor().Selection.GetSelection().removeAllRanges();
30183 this.getEditor().Selection.GetSelection().addRange(r);
30184 this.getEditor().Focus();
30191 replaceTextarea : function()
30193 if ( document.getElementById( this.getId() + '___Frame' ) )
30195 //if ( !this.checkBrowser || this._isCompatibleBrowser() )
30197 // We must check the elements firstly using the Id and then the name.
30198 var oTextarea = document.getElementById( this.getId() );
30200 var colElementsByName = document.getElementsByName( this.getId() ) ;
30202 oTextarea.style.display = 'none' ;
30204 if ( oTextarea.tabIndex ) {
30205 this.TabIndex = oTextarea.tabIndex ;
30208 this._insertHtmlBefore( this._getConfigHtml(), oTextarea ) ;
30209 this._insertHtmlBefore( this._getIFrameHtml(), oTextarea ) ;
30210 this.frame = Roo.get(this.getId() + '___Frame')
30213 _getConfigHtml : function()
30217 for ( var o in this.fckconfig ) {
30218 sConfig += sConfig.length > 0 ? '&' : '';
30219 sConfig += encodeURIComponent( o ) + '=' + encodeURIComponent( this.fckconfig[o] ) ;
30222 return '<input type="hidden" id="' + this.getId() + '___Config" value="' + sConfig + '" style="display:none" />' ;
30226 _getIFrameHtml : function()
30228 var sFile = 'fckeditor.html' ;
30229 /* no idea what this is about..
30232 if ( (/fcksource=true/i).test( window.top.location.search ) )
30233 sFile = 'fckeditor.original.html' ;
30238 var sLink = this.basePath + 'editor/' + sFile + '?InstanceName=' + encodeURIComponent( this.getId() ) ;
30239 sLink += this.toolbarSet ? ( '&Toolbar=' + this.toolbarSet) : '';
30242 var html = '<iframe id="' + this.getId() +
30243 '___Frame" src="' + sLink +
30244 '" width="' + this.width +
30245 '" height="' + this.height + '"' +
30246 (this.tabIndex ? ' tabindex="' + this.tabIndex + '"' :'' ) +
30247 ' frameborder="0" scrolling="no"></iframe>' ;
30252 _insertHtmlBefore : function( html, element )
30254 if ( element.insertAdjacentHTML ) {
30256 element.insertAdjacentHTML( 'beforeBegin', html ) ;
30258 var oRange = document.createRange() ;
30259 oRange.setStartBefore( element ) ;
30260 var oFragment = oRange.createContextualFragment( html );
30261 element.parentNode.insertBefore( oFragment, element ) ;
30274 //Roo.reg('fckeditor', Roo.form.FCKeditor);
30276 function FCKeditor_OnComplete(editorInstance){
30277 var f = Roo.form.FCKeditor.editors[editorInstance.Name];
30278 f.fckEditor = editorInstance;
30279 //console.log("loaded");
30280 f.fireEvent('editorinit', f, editorInstance);
30300 //<script type="text/javascript">
30302 * @class Roo.form.GridField
30303 * @extends Roo.form.Field
30304 * Embed a grid (or editable grid into a form)
30307 * This embeds a grid in a form, the value of the field should be the json encoded array of rows
30309 * xgrid.store = Roo.data.Store
30310 * xgrid.store.proxy = Roo.data.MemoryProxy (data = [] )
30311 * xgrid.store.reader = Roo.data.JsonReader
30315 * Creates a new GridField
30316 * @param {Object} config Configuration options
30318 Roo.form.GridField = function(config){
30319 Roo.form.GridField.superclass.constructor.call(this, config);
30323 Roo.extend(Roo.form.GridField, Roo.form.Field, {
30325 * @cfg {Number} width - used to restrict width of grid..
30329 * @cfg {Number} height - used to restrict height of grid..
30333 * @cfg {Object} xgrid (xtype'd description of grid) { xtype : 'Grid', dataSource: .... }
30339 * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
30340 * {tag: "input", type: "checkbox", autocomplete: "off"})
30342 // defaultAutoCreate : { tag: 'div' },
30343 defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'off'},
30345 * @cfg {String} addTitle Text to include for adding a title.
30349 onResize : function(){
30350 Roo.form.Field.superclass.onResize.apply(this, arguments);
30353 initEvents : function(){
30354 // Roo.form.Checkbox.superclass.initEvents.call(this);
30355 // has no events...
30360 getResizeEl : function(){
30364 getPositionEl : function(){
30369 onRender : function(ct, position){
30371 this.style = this.style || 'overflow: hidden; border:1px solid #c3daf9;';
30372 var style = this.style;
30375 Roo.form.GridField.superclass.onRender.call(this, ct, position);
30376 this.wrap = this.el.wrap({cls: ''}); // not sure why ive done thsi...
30377 this.viewEl = this.wrap.createChild({ tag: 'div' });
30379 this.viewEl.applyStyles(style);
30382 this.viewEl.setWidth(this.width);
30385 this.viewEl.setHeight(this.height);
30387 //if(this.inputValue !== undefined){
30388 //this.setValue(this.value);
30391 this.grid = new Roo.grid[this.xgrid.xtype](this.viewEl, this.xgrid);
30394 this.grid.render();
30395 this.grid.getDataSource().on('remove', this.refreshValue, this);
30396 this.grid.getDataSource().on('update', this.refreshValue, this);
30397 this.grid.on('afteredit', this.refreshValue, this);
30403 * Sets the value of the item.
30404 * @param {String} either an object or a string..
30406 setValue : function(v){
30408 v = v || []; // empty set..
30409 // this does not seem smart - it really only affects memoryproxy grids..
30410 if (this.grid && this.grid.getDataSource() && typeof(v) != 'undefined') {
30411 var ds = this.grid.getDataSource();
30412 // assumes a json reader..
30414 data[ds.reader.meta.root ] = typeof(v) == 'string' ? Roo.decode(v) : v;
30415 ds.loadData( data);
30417 // clear selection so it does not get stale.
30418 if (this.grid.sm) {
30419 this.grid.sm.clearSelections();
30422 Roo.form.GridField.superclass.setValue.call(this, v);
30423 this.refreshValue();
30424 // should load data in the grid really....
30428 refreshValue: function() {
30430 this.grid.getDataSource().each(function(r) {
30433 this.el.dom.value = Roo.encode(val);
30441 * Ext JS Library 1.1.1
30442 * Copyright(c) 2006-2007, Ext JS, LLC.
30444 * Originally Released Under LGPL - original licence link has changed is not relivant.
30447 * <script type="text/javascript">
30450 * @class Roo.form.DisplayField
30451 * @extends Roo.form.Field
30452 * A generic Field to display non-editable data.
30454 * Creates a new Display Field item.
30455 * @param {Object} config Configuration options
30457 Roo.form.DisplayField = function(config){
30458 Roo.form.DisplayField.superclass.constructor.call(this, config);
30462 Roo.extend(Roo.form.DisplayField, Roo.form.TextField, {
30463 inputType: 'hidden',
30469 * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
30471 focusClass : undefined,
30473 * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
30475 fieldClass: 'x-form-field',
30478 * @cfg {Function} valueRenderer The renderer for the field (so you can reformat output). should return raw HTML
30480 valueRenderer: undefined,
30484 * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
30485 * {tag: "input", type: "checkbox", autocomplete: "off"})
30488 // defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'off'},
30490 onResize : function(){
30491 Roo.form.DisplayField.superclass.onResize.apply(this, arguments);
30495 initEvents : function(){
30496 // Roo.form.Checkbox.superclass.initEvents.call(this);
30497 // has no events...
30502 getResizeEl : function(){
30506 getPositionEl : function(){
30511 onRender : function(ct, position){
30513 Roo.form.DisplayField.superclass.onRender.call(this, ct, position);
30514 //if(this.inputValue !== undefined){
30515 this.wrap = this.el.wrap();
30517 this.viewEl = this.wrap.createChild({ tag: 'div', cls: 'x-form-displayfield'});
30519 if (this.bodyStyle) {
30520 this.viewEl.applyStyles(this.bodyStyle);
30522 //this.viewEl.setStyle('padding', '2px');
30524 this.setValue(this.value);
30529 initValue : Roo.emptyFn,
30534 onClick : function(){
30539 * Sets the checked state of the checkbox.
30540 * @param {Boolean/String} checked True, 'true', '1', or 'on' to check the checkbox, any other value will uncheck it.
30542 setValue : function(v){
30544 var html = this.valueRenderer ? this.valueRenderer(v) : String.format('{0}', v);
30545 // this might be called before we have a dom element..
30546 if (!this.viewEl) {
30549 this.viewEl.dom.innerHTML = html;
30550 Roo.form.DisplayField.superclass.setValue.call(this, v);
30560 * @class Roo.form.DayPicker
30561 * @extends Roo.form.Field
30562 * A Day picker show [M] [T] [W] ....
30564 * Creates a new Day Picker
30565 * @param {Object} config Configuration options
30567 Roo.form.DayPicker= function(config){
30568 Roo.form.DayPicker.superclass.constructor.call(this, config);
30572 Roo.extend(Roo.form.DayPicker, Roo.form.Field, {
30574 * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
30576 focusClass : undefined,
30578 * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
30580 fieldClass: "x-form-field",
30583 * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
30584 * {tag: "input", type: "checkbox", autocomplete: "off"})
30586 defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "off"},
30589 actionMode : 'viewEl',
30593 inputType : 'hidden',
30596 inputElement: false, // real input element?
30597 basedOn: false, // ????
30599 isFormField: true, // not sure where this is needed!!!!
30601 onResize : function(){
30602 Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
30603 if(!this.boxLabel){
30604 this.el.alignTo(this.wrap, 'c-c');
30608 initEvents : function(){
30609 Roo.form.Checkbox.superclass.initEvents.call(this);
30610 this.el.on("click", this.onClick, this);
30611 this.el.on("change", this.onClick, this);
30615 getResizeEl : function(){
30619 getPositionEl : function(){
30625 onRender : function(ct, position){
30626 Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
30628 this.wrap = this.el.wrap({cls: 'x-form-daypick-item '});
30630 var r1 = '<table><tr>';
30631 var r2 = '<tr class="x-form-daypick-icons">';
30632 for (var i=0; i < 7; i++) {
30633 r1+= '<td><div>' + Date.dayNames[i].substring(0,3) + '</div></td>';
30634 r2+= '<td><img class="x-menu-item-icon" src="' + Roo.BLANK_IMAGE_URL +'"></td>';
30637 var viewEl = this.wrap.createChild( r1 + '</tr>' + r2 + '</tr></table>');
30638 viewEl.select('img').on('click', this.onClick, this);
30639 this.viewEl = viewEl;
30642 // this will not work on Chrome!!!
30643 this.el.on('DOMAttrModified', this.setFromHidden, this); //ff
30644 this.el.on('propertychange', this.setFromHidden, this); //ie
30652 initValue : Roo.emptyFn,
30655 * Returns the checked state of the checkbox.
30656 * @return {Boolean} True if checked, else false
30658 getValue : function(){
30659 return this.el.dom.value;
30664 onClick : function(e){
30665 //this.setChecked(!this.checked);
30666 Roo.get(e.target).toggleClass('x-menu-item-checked');
30667 this.refreshValue();
30668 //if(this.el.dom.checked != this.checked){
30669 // this.setValue(this.el.dom.checked);
30674 refreshValue : function()
30677 this.viewEl.select('img',true).each(function(e,i,n) {
30678 val += e.is(".x-menu-item-checked") ? String(n) : '';
30680 this.setValue(val, true);
30684 * Sets the checked state of the checkbox.
30685 * On is always based on a string comparison between inputValue and the param.
30686 * @param {Boolean/String} value - the value to set
30687 * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
30689 setValue : function(v,suppressEvent){
30690 if (!this.el.dom) {
30693 var old = this.el.dom.value ;
30694 this.el.dom.value = v;
30695 if (suppressEvent) {
30699 // update display..
30700 this.viewEl.select('img',true).each(function(e,i,n) {
30702 var on = e.is(".x-menu-item-checked");
30703 var newv = v.indexOf(String(n)) > -1;
30705 e.toggleClass('x-menu-item-checked');
30711 this.fireEvent('change', this, v, old);
30716 // handle setting of hidden value by some other method!!?!?
30717 setFromHidden: function()
30722 //console.log("SET FROM HIDDEN");
30723 //alert('setFrom hidden');
30724 this.setValue(this.el.dom.value);
30727 onDestroy : function()
30730 Roo.get(this.viewEl).remove();
30733 Roo.form.DayPicker.superclass.onDestroy.call(this);
30737 * RooJS Library 1.1.1
30738 * Copyright(c) 2008-2011 Alan Knowles
30745 * @class Roo.form.ComboCheck
30746 * @extends Roo.form.ComboBox
30747 * A combobox for multiple select items.
30749 * FIXME - could do with a reset button..
30752 * Create a new ComboCheck
30753 * @param {Object} config Configuration options
30755 Roo.form.ComboCheck = function(config){
30756 Roo.form.ComboCheck.superclass.constructor.call(this, config);
30757 // should verify some data...
30759 // hiddenName = required..
30760 // displayField = required
30761 // valudField == required
30762 var req= [ 'hiddenName', 'displayField', 'valueField' ];
30764 Roo.each(req, function(e) {
30765 if ((typeof(_t[e]) == 'undefined' ) || !_t[e].length) {
30766 throw "Roo.form.ComboCheck : missing value for: " + e;
30773 Roo.extend(Roo.form.ComboCheck, Roo.form.ComboBox, {
30778 selectedClass: 'x-menu-item-checked',
30781 onRender : function(ct, position){
30787 var cls = 'x-combo-list';
30790 this.tpl = new Roo.Template({
30791 html : '<div class="'+cls+'-item x-menu-check-item">' +
30792 '<img class="x-menu-item-icon" style="margin: 0px;" src="' + Roo.BLANK_IMAGE_URL + '">' +
30793 '<span>{' + this.displayField + '}</span>' +
30800 Roo.form.ComboCheck.superclass.onRender.call(this, ct, position);
30801 this.view.singleSelect = false;
30802 this.view.multiSelect = true;
30803 this.view.toggleSelect = true;
30804 this.pageTb.add(new Roo.Toolbar.Fill(), {
30807 handler: function()
30814 onViewOver : function(e, t){
30820 onViewClick : function(doFocus,index){
30824 select: function () {
30825 //Roo.log("SELECT CALLED");
30828 selectByValue : function(xv, scrollIntoView){
30829 var ar = this.getValueArray();
30832 Roo.each(ar, function(v) {
30833 if(v === undefined || v === null){
30836 var r = this.findRecord(this.valueField, v);
30838 sels.push(this.store.indexOf(r))
30842 this.view.select(sels);
30848 onSelect : function(record, index){
30849 // Roo.log("onselect Called");
30850 // this is only called by the clear button now..
30851 this.view.clearSelections();
30852 this.setValue('[]');
30853 if (this.value != this.valueBefore) {
30854 this.fireEvent('change', this, this.value, this.valueBefore);
30855 this.valueBefore = this.value;
30858 getValueArray : function()
30863 //Roo.log(this.value);
30864 if (typeof(this.value) == 'undefined') {
30867 var ar = Roo.decode(this.value);
30868 return ar instanceof Array ? ar : []; //?? valid?
30871 Roo.log(e + "\nRoo.form.ComboCheck:getValueArray invalid data:" + this.getValue());
30876 expand : function ()
30879 Roo.form.ComboCheck.superclass.expand.call(this);
30880 this.valueBefore = typeof(this.value) == 'undefined' ? '' : this.value;
30881 //this.valueBefore = typeof(this.valueBefore) == 'undefined' ? '' : this.valueBefore;
30886 collapse : function(){
30887 Roo.form.ComboCheck.superclass.collapse.call(this);
30888 var sl = this.view.getSelectedIndexes();
30889 var st = this.store;
30893 Roo.each(sl, function(i) {
30895 nv.push(r.get(this.valueField));
30897 this.setValue(Roo.encode(nv));
30898 if (this.value != this.valueBefore) {
30900 this.fireEvent('change', this, this.value, this.valueBefore);
30901 this.valueBefore = this.value;
30906 setValue : function(v){
30910 var vals = this.getValueArray();
30912 Roo.each(vals, function(k) {
30913 var r = this.findRecord(this.valueField, k);
30915 tv.push(r.data[this.displayField]);
30916 }else if(this.valueNotFoundText !== undefined){
30917 tv.push( this.valueNotFoundText );
30922 Roo.form.ComboBox.superclass.setValue.call(this, tv.join(', '));
30923 this.hiddenField.value = v;
30929 * Ext JS Library 1.1.1
30930 * Copyright(c) 2006-2007, Ext JS, LLC.
30932 * Originally Released Under LGPL - original licence link has changed is not relivant.
30935 * <script type="text/javascript">
30939 * @class Roo.form.Signature
30940 * @extends Roo.form.Field
30944 * @param {Object} config Configuration options
30947 Roo.form.Signature = function(config){
30948 Roo.form.Signature.superclass.constructor.call(this, config);
30950 this.addEvents({// not in used??
30953 * Fires when the 'confirm' icon is pressed (add a listener to enable add button)
30954 * @param {Roo.form.Signature} combo This combo box
30959 * Fires when the 'edit' icon is pressed (add a listener to enable add button)
30960 * @param {Roo.form.ComboBox} combo This combo box
30961 * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
30967 Roo.extend(Roo.form.Signature, Roo.form.Field, {
30969 * @cfg {Object} labels Label to use when rendering a form.
30973 * confirm : "Confirm"
30978 confirm : "Confirm"
30981 * @cfg {Number} width The signature panel width (defaults to 300)
30985 * @cfg {Number} height The signature panel height (defaults to 100)
30989 * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to false)
30991 allowBlank : false,
30994 // {Object} signPanel The signature SVG panel element (defaults to {})
30996 // {Boolean} isMouseDown False to validate that the mouse down event (defaults to false)
30997 isMouseDown : false,
30998 // {Boolean} isConfirmed validate the signature is confirmed or not for submitting form (defaults to false)
30999 isConfirmed : false,
31000 // {String} signatureTmp SVG mapping string (defaults to empty string)
31004 defaultAutoCreate : { // modified by initCompnoent..
31010 onRender : function(ct, position){
31012 Roo.form.Signature.superclass.onRender.call(this, ct, position);
31014 this.wrap = this.el.wrap({
31015 cls:'x-form-signature-wrap', style : 'width: ' + this.width + 'px', cn:{cls:'x-form-signature'}
31018 this.createToolbar(this);
31019 this.signPanel = this.wrap.createChild({
31021 style: 'width: ' + this.width + 'px; height: ' + this.height + 'px; border: 0;'
31025 this.svgID = Roo.id();
31026 this.svgEl = this.signPanel.createChild({
31027 xmlns : 'http://www.w3.org/2000/svg',
31029 id : this.svgID + "-svg",
31031 height: this.height,
31032 viewBox: '0 0 '+this.width+' '+this.height,
31036 id: this.svgID + "-svg-r",
31038 height: this.height,
31043 id: this.svgID + "-svg-l",
31045 y1: (this.height*0.8), // start set the line in 80% of height
31046 x2: this.width, // end
31047 y2: (this.height*0.8), // end set the line in 80% of height
31049 'stroke-width': "1",
31050 'stroke-dasharray': "3",
31051 'shape-rendering': "crispEdges",
31052 'pointer-events': "none"
31056 id: this.svgID + "-svg-p",
31058 'stroke-width': "3",
31060 'pointer-events': 'none'
31065 this.svgBox = this.svgEl.dom.getScreenCTM();
31067 createSVG : function(){
31068 var svg = this.signPanel;
31069 var r = svg.select('#'+ this.svgID + '-svg-r', true).first().dom;
31072 r.addEventListener('mousedown', function(e) { return t.down(e); }, false);
31073 r.addEventListener('mousemove', function(e) { return t.move(e); }, false);
31074 r.addEventListener('mouseup', function(e) { return t.up(e); }, false);
31075 r.addEventListener('mouseout', function(e) { return t.up(e); }, false);
31076 r.addEventListener('touchstart', function(e) { return t.down(e); }, false);
31077 r.addEventListener('touchmove', function(e) { return t.move(e); }, false);
31078 r.addEventListener('touchend', function(e) { return t.up(e); }, false);
31081 isTouchEvent : function(e){
31082 return e.type.match(/^touch/);
31084 getCoords : function (e) {
31085 var pt = this.svgEl.dom.createSVGPoint();
31088 if (this.isTouchEvent(e)) {
31089 pt.x = e.targetTouches[0].clientX
31090 pt.y = e.targetTouches[0].clientY;
31092 var a = this.svgEl.dom.getScreenCTM();
31093 var b = a.inverse();
31094 var mx = pt.matrixTransform(b);
31095 return mx.x + ',' + mx.y;
31097 //mouse event headler
31098 down : function (e) {
31099 this.signatureTmp += 'M' + this.getCoords(e) + ' ';
31100 this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr('d', this.signatureTmp);
31102 this.isMouseDown = true;
31104 e.preventDefault();
31106 move : function (e) {
31107 if (this.isMouseDown) {
31108 this.signatureTmp += 'L' + this.getCoords(e) + ' ';
31109 this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', this.signatureTmp);
31112 e.preventDefault();
31114 up : function (e) {
31115 this.isMouseDown = false;
31116 var sp = this.signatureTmp.split(' ');
31119 if(!sp[sp.length-2].match(/^L/)){
31123 this.signatureTmp = sp.join(" ");
31126 if(this.getValue() != this.signatureTmp){
31127 this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
31128 this.isConfirmed = false;
31130 e.preventDefault();
31134 * Protected method that will not generally be called directly. It
31135 * is called when the editor creates its toolbar. Override this method if you need to
31136 * add custom toolbar buttons.
31137 * @param {HtmlEditor} editor
31139 createToolbar : function(editor){
31140 function btn(id, toggle, handler){
31141 var xid = fid + '-'+ id ;
31145 cls : 'x-btn-icon x-edit-'+id,
31146 enableToggle:toggle !== false,
31147 scope: editor, // was editor...
31148 handler:handler||editor.relayBtnCmd,
31149 clickEvent:'mousedown',
31150 tooltip: etb.buttonTips[id] || undefined, ///tips ???
31156 var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
31160 cls : ' x-signature-btn x-signature-'+id,
31161 scope: editor, // was editor...
31162 handler: this.reset,
31163 clickEvent:'mousedown',
31164 text: this.labels.clear
31171 cls : ' x-signature-btn x-signature-'+id,
31172 scope: editor, // was editor...
31173 handler: this.confirmHandler,
31174 clickEvent:'mousedown',
31175 text: this.labels.confirm
31182 * when user is clicked confirm then show this image.....
31184 * @return {String} Image Data URI
31186 getImageDataURI : function(){
31187 var svg = this.svgEl.dom.parentNode.innerHTML;
31188 var src = 'data:image/svg+xml;base64,'+window.btoa(svg);
31193 * @return {Boolean} this.isConfirmed
31195 getConfirmed : function(){
31196 return this.isConfirmed;
31200 * @return {Number} this.width
31202 getWidth : function(){
31207 * @return {Number} this.height
31209 getHeight : function(){
31210 return this.height;
31213 getSignature : function(){
31214 return this.signatureTmp;
31217 reset : function(){
31218 this.signatureTmp = '';
31219 this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
31220 this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', '');
31221 this.isConfirmed = false;
31222 Roo.form.Signature.superclass.reset.call(this);
31224 setSignature : function(s){
31225 this.signatureTmp = s;
31226 this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
31227 this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', s);
31229 this.isConfirmed = false;
31230 Roo.form.Signature.superclass.reset.call(this);
31233 // Roo.log(this.signPanel.dom.contentWindow.up())
31236 setConfirmed : function(){
31240 // Roo.log(Roo.get(this.signPanel.dom.contentWindow.r).attr('fill', '#cfc'));
31243 confirmHandler : function(){
31244 if(!this.getSignature()){
31248 this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#cfc');
31249 this.setValue(this.getSignature());
31250 this.isConfirmed = true;
31252 this.fireEvent('confirm', this);
31255 // Subclasses should provide the validation implementation by overriding this
31256 validateValue : function(value){
31257 if(this.allowBlank){
31261 if(this.isConfirmed){
31268 * Ext JS Library 1.1.1
31269 * Copyright(c) 2006-2007, Ext JS, LLC.
31271 * Originally Released Under LGPL - original licence link has changed is not relivant.
31274 * <script type="text/javascript">
31279 * @class Roo.form.ComboBox
31280 * @extends Roo.form.TriggerField
31281 * A combobox control with support for autocomplete, remote-loading, paging and many other features.
31283 * Create a new ComboBox.
31284 * @param {Object} config Configuration options
31286 Roo.form.Select = function(config){
31287 Roo.form.Select.superclass.constructor.call(this, config);
31291 Roo.extend(Roo.form.Select , Roo.form.ComboBox, {
31293 * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
31296 * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
31297 * rendering into an Roo.Editor, defaults to false)
31300 * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
31301 * {tag: "input", type: "text", size: "24", autocomplete: "off"})
31304 * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
31307 * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
31308 * the dropdown list (defaults to undefined, with no header element)
31312 * @cfg {String/Roo.Template} tpl The template to use to render the output
31316 defaultAutoCreate : {tag: "select" },
31318 * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
31320 listWidth: undefined,
31322 * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
31323 * mode = 'remote' or 'text' if mode = 'local')
31325 displayField: undefined,
31327 * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
31328 * mode = 'remote' or 'value' if mode = 'local').
31329 * Note: use of a valueField requires the user make a selection
31330 * in order for a value to be mapped.
31332 valueField: undefined,
31336 * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
31337 * field's data value (defaults to the underlying DOM element's name)
31339 hiddenName: undefined,
31341 * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
31345 * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
31347 selectedClass: 'x-combo-selected',
31349 * @cfg {String} triggerClass An additional CSS class used to style the trigger button. The trigger will always get the
31350 * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
31351 * which displays a downward arrow icon).
31353 triggerClass : 'x-form-arrow-trigger',
31355 * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
31359 * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
31360 * anchor positions (defaults to 'tl-bl')
31362 listAlign: 'tl-bl?',
31364 * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
31368 * @cfg {String} triggerAction The action to execute when the trigger field is activated. Use 'all' to run the
31369 * query specified by the allQuery config option (defaults to 'query')
31371 triggerAction: 'query',
31373 * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
31374 * (defaults to 4, does not apply if editable = false)
31378 * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
31379 * delay (typeAheadDelay) if it matches a known value (defaults to false)
31383 * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
31384 * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
31388 * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
31389 * filter queries will execute with page start and limit parameters. Only applies when mode = 'remote' (defaults to 0)
31393 * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus. Only applies
31394 * when editable = true (defaults to false)
31396 selectOnFocus:false,
31398 * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
31400 queryParam: 'query',
31402 * @cfg {String} loadingText The text to display in the dropdown list while data is loading. Only applies
31403 * when mode = 'remote' (defaults to 'Loading...')
31405 loadingText: 'Loading...',
31407 * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
31411 * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
31415 * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
31416 * traditional select (defaults to true)
31420 * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
31424 * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
31428 * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
31429 * listWidth has a higher value)
31433 * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
31434 * allow the user to set arbitrary text into the field (defaults to false)
31436 forceSelection:false,
31438 * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
31439 * if typeAhead = true (defaults to 250)
31441 typeAheadDelay : 250,
31443 * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
31444 * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
31446 valueNotFoundText : undefined,
31449 * @cfg {String} defaultValue The value displayed after loading the store.
31454 * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
31456 blockFocus : false,
31459 * @cfg {Boolean} disableClear Disable showing of clear button.
31461 disableClear : false,
31463 * @cfg {Boolean} alwaysQuery Disable caching of results, and always send query
31465 alwaysQuery : false,
31471 // element that contains real text value.. (when hidden is used..)
31474 onRender : function(ct, position){
31475 Roo.form.Field.prototype.onRender.call(this, ct, position);
31478 this.store.on('beforeload', this.onBeforeLoad, this);
31479 this.store.on('load', this.onLoad, this);
31480 this.store.on('loadexception', this.onLoadException, this);
31481 this.store.load({});
31489 initEvents : function(){
31490 //Roo.form.ComboBox.superclass.initEvents.call(this);
31494 onDestroy : function(){
31497 this.store.un('beforeload', this.onBeforeLoad, this);
31498 this.store.un('load', this.onLoad, this);
31499 this.store.un('loadexception', this.onLoadException, this);
31501 //Roo.form.ComboBox.superclass.onDestroy.call(this);
31505 fireKey : function(e){
31506 if(e.isNavKeyPress() && !this.list.isVisible()){
31507 this.fireEvent("specialkey", this, e);
31512 onResize: function(w, h){
31520 * Allow or prevent the user from directly editing the field text. If false is passed,
31521 * the user will only be able to select from the items defined in the dropdown list. This method
31522 * is the runtime equivalent of setting the 'editable' config option at config time.
31523 * @param {Boolean} value True to allow the user to directly edit the field text
31525 setEditable : function(value){
31530 onBeforeLoad : function(){
31532 Roo.log("Select before load");
31535 this.innerList.update(this.loadingText ?
31536 '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
31537 //this.restrictHeight();
31538 this.selectedIndex = -1;
31542 onLoad : function(){
31545 var dom = this.el.dom;
31546 dom.innerHTML = '';
31547 var od = dom.ownerDocument;
31549 if (this.emptyText) {
31550 var op = od.createElement('option');
31551 op.setAttribute('value', '');
31552 op.innerHTML = String.format('{0}', this.emptyText);
31553 dom.appendChild(op);
31555 if(this.store.getCount() > 0){
31557 var vf = this.valueField;
31558 var df = this.displayField;
31559 this.store.data.each(function(r) {
31560 // which colmsn to use... testing - cdoe / title..
31561 var op = od.createElement('option');
31562 op.setAttribute('value', r.data[vf]);
31563 op.innerHTML = String.format('{0}', r.data[df]);
31564 dom.appendChild(op);
31566 if (typeof(this.defaultValue != 'undefined')) {
31567 this.setValue(this.defaultValue);
31572 //this.onEmptyResults();
31577 onLoadException : function()
31579 dom.innerHTML = '';
31581 Roo.log("Select on load exception");
31585 Roo.log(this.store.reader.jsonData);
31586 if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
31587 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
31593 onTypeAhead : function(){
31598 onSelect : function(record, index){
31599 Roo.log('on select?');
31601 if(this.fireEvent('beforeselect', this, record, index) !== false){
31602 this.setFromData(index > -1 ? record.data : false);
31604 this.fireEvent('select', this, record, index);
31609 * Returns the currently selected field value or empty string if no value is set.
31610 * @return {String} value The selected value
31612 getValue : function(){
31613 var dom = this.el.dom;
31614 this.value = dom.options[dom.selectedIndex].value;
31620 * Clears any text/value currently set in the field
31622 clearValue : function(){
31624 this.el.dom.selectedIndex = this.emptyText ? 0 : -1;
31629 * Sets the specified value into the field. If the value finds a match, the corresponding record text
31630 * will be displayed in the field. If the value does not match the data value of an existing item,
31631 * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
31632 * Otherwise the field will be blank (although the value will still be set).
31633 * @param {String} value The value to match
31635 setValue : function(v){
31636 var d = this.el.dom;
31637 for (var i =0; i < d.options.length;i++) {
31638 if (v == d.options[i].value) {
31639 d.selectedIndex = i;
31647 * @property {Object} the last set data for the element
31652 * Sets the value of the field based on a object which is related to the record format for the store.
31653 * @param {Object} value the value to set as. or false on reset?
31655 setFromData : function(o){
31656 Roo.log('setfrom data?');
31662 reset : function(){
31666 findRecord : function(prop, value){
31671 if(this.store.getCount() > 0){
31672 this.store.each(function(r){
31673 if(r.data[prop] == value){
31683 getName: function()
31685 // returns hidden if it's set..
31686 if (!this.rendered) {return ''};
31687 return !this.hiddenName && this.el.dom.name ? this.el.dom.name : (this.hiddenName || '');
31695 onEmptyResults : function(){
31696 Roo.log('empty results');
31701 * Returns true if the dropdown list is expanded, else false.
31703 isExpanded : function(){
31708 * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
31709 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
31710 * @param {String} value The data value of the item to select
31711 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
31712 * selected item if it is not currently in view (defaults to true)
31713 * @return {Boolean} True if the value matched an item in the list, else false
31715 selectByValue : function(v, scrollIntoView){
31716 Roo.log('select By Value');
31719 if(v !== undefined && v !== null){
31720 var r = this.findRecord(this.valueField || this.displayField, v);
31722 this.select(this.store.indexOf(r), scrollIntoView);
31730 * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
31731 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
31732 * @param {Number} index The zero-based index of the list item to select
31733 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
31734 * selected item if it is not currently in view (defaults to true)
31736 select : function(index, scrollIntoView){
31737 Roo.log('select ');
31740 this.selectedIndex = index;
31741 this.view.select(index);
31742 if(scrollIntoView !== false){
31743 var el = this.view.getNode(index);
31745 this.innerList.scrollChildIntoView(el, false);
31753 validateBlur : function(){
31760 initQuery : function(){
31761 this.doQuery(this.getRawValue());
31765 doForce : function(){
31766 if(this.el.dom.value.length > 0){
31767 this.el.dom.value =
31768 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
31774 * Execute a query to filter the dropdown list. Fires the beforequery event prior to performing the
31775 * query allowing the query action to be canceled if needed.
31776 * @param {String} query The SQL query to execute
31777 * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
31778 * in the field than the minimum specified by the minChars config option. It also clears any filter previously
31779 * saved in the current store (defaults to false)
31781 doQuery : function(q, forceAll){
31783 Roo.log('doQuery?');
31784 if(q === undefined || q === null){
31789 forceAll: forceAll,
31793 if(this.fireEvent('beforequery', qe)===false || qe.cancel){
31797 forceAll = qe.forceAll;
31798 if(forceAll === true || (q.length >= this.minChars)){
31799 if(this.lastQuery != q || this.alwaysQuery){
31800 this.lastQuery = q;
31801 if(this.mode == 'local'){
31802 this.selectedIndex = -1;
31804 this.store.clearFilter();
31806 this.store.filter(this.displayField, q);
31810 this.store.baseParams[this.queryParam] = q;
31812 params: this.getParams(q)
31817 this.selectedIndex = -1;
31824 getParams : function(q){
31826 //p[this.queryParam] = q;
31829 p.limit = this.pageSize;
31835 * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
31837 collapse : function(){
31842 collapseIf : function(e){
31847 * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
31849 expand : function(){
31857 * @cfg {Boolean} grow
31861 * @cfg {Number} growMin
31865 * @cfg {Number} growMax
31873 setWidth : function()
31877 getResizeEl : function(){
31880 });//<script type="text/javasscript">
31884 * @class Roo.DDView
31885 * A DnD enabled version of Roo.View.
31886 * @param {Element/String} container The Element in which to create the View.
31887 * @param {String} tpl The template string used to create the markup for each element of the View
31888 * @param {Object} config The configuration properties. These include all the config options of
31889 * {@link Roo.View} plus some specific to this class.<br>
31891 * Drag/drop is implemented by adding {@link Roo.data.Record}s to the target DDView. If copying is
31892 * not being performed, the original {@link Roo.data.Record} is removed from the source DDView.<br>
31894 * The following extra CSS rules are needed to provide insertion point highlighting:<pre><code>
31895 .x-view-drag-insert-above {
31896 border-top:1px dotted #3366cc;
31898 .x-view-drag-insert-below {
31899 border-bottom:1px dotted #3366cc;
31905 Roo.DDView = function(container, tpl, config) {
31906 Roo.DDView.superclass.constructor.apply(this, arguments);
31907 this.getEl().setStyle("outline", "0px none");
31908 this.getEl().unselectable();
31909 if (this.dragGroup) {
31910 this.setDraggable(this.dragGroup.split(","));
31912 if (this.dropGroup) {
31913 this.setDroppable(this.dropGroup.split(","));
31915 if (this.deletable) {
31916 this.setDeletable();
31918 this.isDirtyFlag = false;
31924 Roo.extend(Roo.DDView, Roo.View, {
31925 /** @cfg {String/Array} dragGroup The ddgroup name(s) for the View's DragZone. */
31926 /** @cfg {String/Array} dropGroup The ddgroup name(s) for the View's DropZone. */
31927 /** @cfg {Boolean} copy Causes drag operations to copy nodes rather than move. */
31928 /** @cfg {Boolean} allowCopy Causes ctrl/drag operations to copy nodes rather than move. */
31932 reset: Roo.emptyFn,
31934 clearInvalid: Roo.form.Field.prototype.clearInvalid,
31936 validate: function() {
31940 destroy: function() {
31941 this.purgeListeners();
31942 this.getEl.removeAllListeners();
31943 this.getEl().remove();
31944 if (this.dragZone) {
31945 if (this.dragZone.destroy) {
31946 this.dragZone.destroy();
31949 if (this.dropZone) {
31950 if (this.dropZone.destroy) {
31951 this.dropZone.destroy();
31956 /** Allows this class to be an Roo.form.Field so it can be found using {@link Roo.form.BasicForm#findField}. */
31957 getName: function() {
31961 /** Loads the View from a JSON string representing the Records to put into the Store. */
31962 setValue: function(v) {
31964 throw "DDView.setValue(). DDView must be constructed with a valid Store";
31967 data[this.store.reader.meta.root] = v ? [].concat(v) : [];
31968 this.store.proxy = new Roo.data.MemoryProxy(data);
31972 /** @return {String} a parenthesised list of the ids of the Records in the View. */
31973 getValue: function() {
31975 this.store.each(function(rec) {
31976 result += rec.id + ',';
31978 return result.substr(0, result.length - 1) + ')';
31981 getIds: function() {
31982 var i = 0, result = new Array(this.store.getCount());
31983 this.store.each(function(rec) {
31984 result[i++] = rec.id;
31989 isDirty: function() {
31990 return this.isDirtyFlag;
31994 * Part of the Roo.dd.DropZone interface. If no target node is found, the
31995 * whole Element becomes the target, and this causes the drop gesture to append.
31997 getTargetFromEvent : function(e) {
31998 var target = e.getTarget();
31999 while ((target !== null) && (target.parentNode != this.el.dom)) {
32000 target = target.parentNode;
32003 target = this.el.dom.lastChild || this.el.dom;
32009 * Create the drag data which consists of an object which has the property "ddel" as
32010 * the drag proxy element.
32012 getDragData : function(e) {
32013 var target = this.findItemFromChild(e.getTarget());
32015 this.handleSelection(e);
32016 var selNodes = this.getSelectedNodes();
32019 copy: this.copy || (this.allowCopy && e.ctrlKey),
32023 var selectedIndices = this.getSelectedIndexes();
32024 for (var i = 0; i < selectedIndices.length; i++) {
32025 dragData.records.push(this.store.getAt(selectedIndices[i]));
32027 if (selNodes.length == 1) {
32028 dragData.ddel = target.cloneNode(true); // the div element
32030 var div = document.createElement('div'); // create the multi element drag "ghost"
32031 div.className = 'multi-proxy';
32032 for (var i = 0, len = selNodes.length; i < len; i++) {
32033 div.appendChild(selNodes[i].cloneNode(true));
32035 dragData.ddel = div;
32037 //console.log(dragData)
32038 //console.log(dragData.ddel.innerHTML)
32041 //console.log('nodragData')
32045 /** Specify to which ddGroup items in this DDView may be dragged. */
32046 setDraggable: function(ddGroup) {
32047 if (ddGroup instanceof Array) {
32048 Roo.each(ddGroup, this.setDraggable, this);
32051 if (this.dragZone) {
32052 this.dragZone.addToGroup(ddGroup);
32054 this.dragZone = new Roo.dd.DragZone(this.getEl(), {
32055 containerScroll: true,
32059 // Draggability implies selection. DragZone's mousedown selects the element.
32060 if (!this.multiSelect) { this.singleSelect = true; }
32062 // Wire the DragZone's handlers up to methods in *this*
32063 this.dragZone.getDragData = this.getDragData.createDelegate(this);
32067 /** Specify from which ddGroup this DDView accepts drops. */
32068 setDroppable: function(ddGroup) {
32069 if (ddGroup instanceof Array) {
32070 Roo.each(ddGroup, this.setDroppable, this);
32073 if (this.dropZone) {
32074 this.dropZone.addToGroup(ddGroup);
32076 this.dropZone = new Roo.dd.DropZone(this.getEl(), {
32077 containerScroll: true,
32081 // Wire the DropZone's handlers up to methods in *this*
32082 this.dropZone.getTargetFromEvent = this.getTargetFromEvent.createDelegate(this);
32083 this.dropZone.onNodeEnter = this.onNodeEnter.createDelegate(this);
32084 this.dropZone.onNodeOver = this.onNodeOver.createDelegate(this);
32085 this.dropZone.onNodeOut = this.onNodeOut.createDelegate(this);
32086 this.dropZone.onNodeDrop = this.onNodeDrop.createDelegate(this);
32090 /** Decide whether to drop above or below a View node. */
32091 getDropPoint : function(e, n, dd){
32092 if (n == this.el.dom) { return "above"; }
32093 var t = Roo.lib.Dom.getY(n), b = t + n.offsetHeight;
32094 var c = t + (b - t) / 2;
32095 var y = Roo.lib.Event.getPageY(e);
32103 onNodeEnter : function(n, dd, e, data){
32107 onNodeOver : function(n, dd, e, data){
32108 var pt = this.getDropPoint(e, n, dd);
32109 // set the insert point style on the target node
32110 var dragElClass = this.dropNotAllowed;
32113 if (pt == "above"){
32114 dragElClass = n.previousSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-above";
32115 targetElClass = "x-view-drag-insert-above";
32117 dragElClass = n.nextSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-below";
32118 targetElClass = "x-view-drag-insert-below";
32120 if (this.lastInsertClass != targetElClass){
32121 Roo.fly(n).replaceClass(this.lastInsertClass, targetElClass);
32122 this.lastInsertClass = targetElClass;
32125 return dragElClass;
32128 onNodeOut : function(n, dd, e, data){
32129 this.removeDropIndicators(n);
32132 onNodeDrop : function(n, dd, e, data){
32133 if (this.fireEvent("drop", this, n, dd, e, data) === false) {
32136 var pt = this.getDropPoint(e, n, dd);
32137 var insertAt = (n == this.el.dom) ? this.nodes.length : n.nodeIndex;
32138 if (pt == "below") { insertAt++; }
32139 for (var i = 0; i < data.records.length; i++) {
32140 var r = data.records[i];
32141 var dup = this.store.getById(r.id);
32142 if (dup && (dd != this.dragZone)) {
32143 Roo.fly(this.getNode(this.store.indexOf(dup))).frame("red", 1);
32146 this.store.insert(insertAt++, r.copy());
32148 data.source.isDirtyFlag = true;
32150 this.store.insert(insertAt++, r);
32152 this.isDirtyFlag = true;
32155 this.dragZone.cachedTarget = null;
32159 removeDropIndicators : function(n){
32161 Roo.fly(n).removeClass([
32162 "x-view-drag-insert-above",
32163 "x-view-drag-insert-below"]);
32164 this.lastInsertClass = "_noclass";
32169 * Utility method. Add a delete option to the DDView's context menu.
32170 * @param {String} imageUrl The URL of the "delete" icon image.
32172 setDeletable: function(imageUrl) {
32173 if (!this.singleSelect && !this.multiSelect) {
32174 this.singleSelect = true;
32176 var c = this.getContextMenu();
32177 this.contextMenu.on("itemclick", function(item) {
32180 this.remove(this.getSelectedIndexes());
32184 this.contextMenu.add({
32191 /** Return the context menu for this DDView. */
32192 getContextMenu: function() {
32193 if (!this.contextMenu) {
32194 // Create the View's context menu
32195 this.contextMenu = new Roo.menu.Menu({
32196 id: this.id + "-contextmenu"
32198 this.el.on("contextmenu", this.showContextMenu, this);
32200 return this.contextMenu;
32203 disableContextMenu: function() {
32204 if (this.contextMenu) {
32205 this.el.un("contextmenu", this.showContextMenu, this);
32209 showContextMenu: function(e, item) {
32210 item = this.findItemFromChild(e.getTarget());
32213 this.select(this.getNode(item), this.multiSelect && e.ctrlKey, true);
32214 this.contextMenu.showAt(e.getXY());
32219 * Remove {@link Roo.data.Record}s at the specified indices.
32220 * @param {Array/Number} selectedIndices The index (or Array of indices) of Records to remove.
32222 remove: function(selectedIndices) {
32223 selectedIndices = [].concat(selectedIndices);
32224 for (var i = 0; i < selectedIndices.length; i++) {
32225 var rec = this.store.getAt(selectedIndices[i]);
32226 this.store.remove(rec);
32231 * Double click fires the event, but also, if this is draggable, and there is only one other
32232 * related DropZone, it transfers the selected node.
32234 onDblClick : function(e){
32235 var item = this.findItemFromChild(e.getTarget());
32237 if (this.fireEvent("dblclick", this, this.indexOf(item), item, e) === false) {
32240 if (this.dragGroup) {
32241 var targets = Roo.dd.DragDropMgr.getRelated(this.dragZone, true);
32242 while (targets.indexOf(this.dropZone) > -1) {
32243 targets.remove(this.dropZone);
32245 if (targets.length == 1) {
32246 this.dragZone.cachedTarget = null;
32247 var el = Roo.get(targets[0].getEl());
32248 var box = el.getBox(true);
32249 targets[0].onNodeDrop(el.dom, {
32251 xy: [box.x, box.y + box.height - 1]
32252 }, null, this.getDragData(e));
32258 handleSelection: function(e) {
32259 this.dragZone.cachedTarget = null;
32260 var item = this.findItemFromChild(e.getTarget());
32262 this.clearSelections(true);
32265 if (item && (this.multiSelect || this.singleSelect)){
32266 if(this.multiSelect && e.shiftKey && (!e.ctrlKey) && this.lastSelection){
32267 this.select(this.getNodes(this.indexOf(this.lastSelection), item.nodeIndex), false);
32268 }else if (this.isSelected(this.getNode(item)) && e.ctrlKey){
32269 this.unselect(item);
32271 this.select(item, this.multiSelect && e.ctrlKey);
32272 this.lastSelection = item;
32277 onItemClick : function(item, index, e){
32278 if(this.fireEvent("beforeclick", this, index, item, e) === false){
32284 unselect : function(nodeInfo, suppressEvent){
32285 var node = this.getNode(nodeInfo);
32286 if(node && this.isSelected(node)){
32287 if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
32288 Roo.fly(node).removeClass(this.selectedClass);
32289 this.selections.remove(node);
32290 if(!suppressEvent){
32291 this.fireEvent("selectionchange", this, this.selections);
32299 * Ext JS Library 1.1.1
32300 * Copyright(c) 2006-2007, Ext JS, LLC.
32302 * Originally Released Under LGPL - original licence link has changed is not relivant.
32305 * <script type="text/javascript">
32309 * @class Roo.LayoutManager
32310 * @extends Roo.util.Observable
32311 * Base class for layout managers.
32313 Roo.LayoutManager = function(container, config){
32314 Roo.LayoutManager.superclass.constructor.call(this);
32315 this.el = Roo.get(container);
32316 // ie scrollbar fix
32317 if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
32318 document.body.scroll = "no";
32319 }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
32320 this.el.position('relative');
32322 this.id = this.el.id;
32323 this.el.addClass("x-layout-container");
32324 /** false to disable window resize monitoring @type Boolean */
32325 this.monitorWindowResize = true;
32330 * Fires when a layout is performed.
32331 * @param {Roo.LayoutManager} this
32335 * @event regionresized
32336 * Fires when the user resizes a region.
32337 * @param {Roo.LayoutRegion} region The resized region
32338 * @param {Number} newSize The new size (width for east/west, height for north/south)
32340 "regionresized" : true,
32342 * @event regioncollapsed
32343 * Fires when a region is collapsed.
32344 * @param {Roo.LayoutRegion} region The collapsed region
32346 "regioncollapsed" : true,
32348 * @event regionexpanded
32349 * Fires when a region is expanded.
32350 * @param {Roo.LayoutRegion} region The expanded region
32352 "regionexpanded" : true
32354 this.updating = false;
32355 Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
32358 Roo.extend(Roo.LayoutManager, Roo.util.Observable, {
32360 * Returns true if this layout is currently being updated
32361 * @return {Boolean}
32363 isUpdating : function(){
32364 return this.updating;
32368 * Suspend the LayoutManager from doing auto-layouts while
32369 * making multiple add or remove calls
32371 beginUpdate : function(){
32372 this.updating = true;
32376 * Restore auto-layouts and optionally disable the manager from performing a layout
32377 * @param {Boolean} noLayout true to disable a layout update
32379 endUpdate : function(noLayout){
32380 this.updating = false;
32386 layout: function(){
32390 onRegionResized : function(region, newSize){
32391 this.fireEvent("regionresized", region, newSize);
32395 onRegionCollapsed : function(region){
32396 this.fireEvent("regioncollapsed", region);
32399 onRegionExpanded : function(region){
32400 this.fireEvent("regionexpanded", region);
32404 * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
32405 * performs box-model adjustments.
32406 * @return {Object} The size as an object {width: (the width), height: (the height)}
32408 getViewSize : function(){
32410 if(this.el.dom != document.body){
32411 size = this.el.getSize();
32413 size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
32415 size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
32416 size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
32421 * Returns the Element this layout is bound to.
32422 * @return {Roo.Element}
32424 getEl : function(){
32429 * Returns the specified region.
32430 * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
32431 * @return {Roo.LayoutRegion}
32433 getRegion : function(target){
32434 return this.regions[target.toLowerCase()];
32437 onWindowResize : function(){
32438 if(this.monitorWindowResize){
32444 * Ext JS Library 1.1.1
32445 * Copyright(c) 2006-2007, Ext JS, LLC.
32447 * Originally Released Under LGPL - original licence link has changed is not relivant.
32450 * <script type="text/javascript">
32453 * @class Roo.BorderLayout
32454 * @extends Roo.LayoutManager
32455 * This class represents a common layout manager used in desktop applications. For screenshots and more details,
32456 * please see: <br><br>
32457 * <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>
32458 * <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>
32461 var layout = new Roo.BorderLayout(document.body, {
32495 preferredTabWidth: 150
32500 var CP = Roo.ContentPanel;
32502 layout.beginUpdate();
32503 layout.add("north", new CP("north", "North"));
32504 layout.add("south", new CP("south", {title: "South", closable: true}));
32505 layout.add("west", new CP("west", {title: "West"}));
32506 layout.add("east", new CP("autoTabs", {title: "Auto Tabs", closable: true}));
32507 layout.add("center", new CP("center1", {title: "Close Me", closable: true}));
32508 layout.add("center", new CP("center2", {title: "Center Panel", closable: false}));
32509 layout.getRegion("center").showPanel("center1");
32510 layout.endUpdate();
32513 <b>The container the layout is rendered into can be either the body element or any other element.
32514 If it is not the body element, the container needs to either be an absolute positioned element,
32515 or you will need to add "position:relative" to the css of the container. You will also need to specify
32516 the container size if it is not the body element.</b>
32519 * Create a new BorderLayout
32520 * @param {String/HTMLElement/Element} container The container this layout is bound to
32521 * @param {Object} config Configuration options
32523 Roo.BorderLayout = function(container, config){
32524 config = config || {};
32525 Roo.BorderLayout.superclass.constructor.call(this, container, config);
32526 this.factory = config.factory || Roo.BorderLayout.RegionFactory;
32527 for(var i = 0, len = this.factory.validRegions.length; i < len; i++) {
32528 var target = this.factory.validRegions[i];
32529 if(config[target]){
32530 this.addRegion(target, config[target]);
32535 Roo.extend(Roo.BorderLayout, Roo.LayoutManager, {
32537 * Creates and adds a new region if it doesn't already exist.
32538 * @param {String} target The target region key (north, south, east, west or center).
32539 * @param {Object} config The regions config object
32540 * @return {BorderLayoutRegion} The new region
32542 addRegion : function(target, config){
32543 if(!this.regions[target]){
32544 var r = this.factory.create(target, this, config);
32545 this.bindRegion(target, r);
32547 return this.regions[target];
32551 bindRegion : function(name, r){
32552 this.regions[name] = r;
32553 r.on("visibilitychange", this.layout, this);
32554 r.on("paneladded", this.layout, this);
32555 r.on("panelremoved", this.layout, this);
32556 r.on("invalidated", this.layout, this);
32557 r.on("resized", this.onRegionResized, this);
32558 r.on("collapsed", this.onRegionCollapsed, this);
32559 r.on("expanded", this.onRegionExpanded, this);
32563 * Performs a layout update.
32565 layout : function(){
32566 if(this.updating) return;
32567 var size = this.getViewSize();
32568 var w = size.width;
32569 var h = size.height;
32574 //var x = 0, y = 0;
32576 var rs = this.regions;
32577 var north = rs["north"];
32578 var south = rs["south"];
32579 var west = rs["west"];
32580 var east = rs["east"];
32581 var center = rs["center"];
32582 //if(this.hideOnLayout){ // not supported anymore
32583 //c.el.setStyle("display", "none");
32585 if(north && north.isVisible()){
32586 var b = north.getBox();
32587 var m = north.getMargins();
32588 b.width = w - (m.left+m.right);
32591 centerY = b.height + b.y + m.bottom;
32592 centerH -= centerY;
32593 north.updateBox(this.safeBox(b));
32595 if(south && south.isVisible()){
32596 var b = south.getBox();
32597 var m = south.getMargins();
32598 b.width = w - (m.left+m.right);
32600 var totalHeight = (b.height + m.top + m.bottom);
32601 b.y = h - totalHeight + m.top;
32602 centerH -= totalHeight;
32603 south.updateBox(this.safeBox(b));
32605 if(west && west.isVisible()){
32606 var b = west.getBox();
32607 var m = west.getMargins();
32608 b.height = centerH - (m.top+m.bottom);
32610 b.y = centerY + m.top;
32611 var totalWidth = (b.width + m.left + m.right);
32612 centerX += totalWidth;
32613 centerW -= totalWidth;
32614 west.updateBox(this.safeBox(b));
32616 if(east && east.isVisible()){
32617 var b = east.getBox();
32618 var m = east.getMargins();
32619 b.height = centerH - (m.top+m.bottom);
32620 var totalWidth = (b.width + m.left + m.right);
32621 b.x = w - totalWidth + m.left;
32622 b.y = centerY + m.top;
32623 centerW -= totalWidth;
32624 east.updateBox(this.safeBox(b));
32627 var m = center.getMargins();
32629 x: centerX + m.left,
32630 y: centerY + m.top,
32631 width: centerW - (m.left+m.right),
32632 height: centerH - (m.top+m.bottom)
32634 //if(this.hideOnLayout){
32635 //center.el.setStyle("display", "block");
32637 center.updateBox(this.safeBox(centerBox));
32640 this.fireEvent("layout", this);
32644 safeBox : function(box){
32645 box.width = Math.max(0, box.width);
32646 box.height = Math.max(0, box.height);
32651 * Adds a ContentPanel (or subclass) to this layout.
32652 * @param {String} target The target region key (north, south, east, west or center).
32653 * @param {Roo.ContentPanel} panel The panel to add
32654 * @return {Roo.ContentPanel} The added panel
32656 add : function(target, panel){
32658 target = target.toLowerCase();
32659 return this.regions[target].add(panel);
32663 * Remove a ContentPanel (or subclass) to this layout.
32664 * @param {String} target The target region key (north, south, east, west or center).
32665 * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
32666 * @return {Roo.ContentPanel} The removed panel
32668 remove : function(target, panel){
32669 target = target.toLowerCase();
32670 return this.regions[target].remove(panel);
32674 * Searches all regions for a panel with the specified id
32675 * @param {String} panelId
32676 * @return {Roo.ContentPanel} The panel or null if it wasn't found
32678 findPanel : function(panelId){
32679 var rs = this.regions;
32680 for(var target in rs){
32681 if(typeof rs[target] != "function"){
32682 var p = rs[target].getPanel(panelId);
32692 * Searches all regions for a panel with the specified id and activates (shows) it.
32693 * @param {String/ContentPanel} panelId The panels id or the panel itself
32694 * @return {Roo.ContentPanel} The shown panel or null
32696 showPanel : function(panelId) {
32697 var rs = this.regions;
32698 for(var target in rs){
32699 var r = rs[target];
32700 if(typeof r != "function"){
32701 if(r.hasPanel(panelId)){
32702 return r.showPanel(panelId);
32710 * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
32711 * @param {Roo.state.Provider} provider (optional) An alternate state provider
32713 restoreState : function(provider){
32715 provider = Roo.state.Manager;
32717 var sm = new Roo.LayoutStateManager();
32718 sm.init(this, provider);
32722 * Adds a batch of multiple ContentPanels dynamically by passing a special regions config object. This config
32723 * object should contain properties for each region to add ContentPanels to, and each property's value should be
32724 * a valid ContentPanel config object. Example:
32726 // Create the main layout
32727 var layout = new Roo.BorderLayout('main-ct', {
32738 // Create and add multiple ContentPanels at once via configs
32741 id: 'source-files',
32743 title:'Ext Source Files',
32756 * @param {Object} regions An object containing ContentPanel configs by region name
32758 batchAdd : function(regions){
32759 this.beginUpdate();
32760 for(var rname in regions){
32761 var lr = this.regions[rname];
32763 this.addTypedPanels(lr, regions[rname]);
32770 addTypedPanels : function(lr, ps){
32771 if(typeof ps == 'string'){
32772 lr.add(new Roo.ContentPanel(ps));
32774 else if(ps instanceof Array){
32775 for(var i =0, len = ps.length; i < len; i++){
32776 this.addTypedPanels(lr, ps[i]);
32779 else if(!ps.events){ // raw config?
32781 delete ps.el; // prevent conflict
32782 lr.add(new Roo.ContentPanel(el || Roo.id(), ps));
32784 else { // panel object assumed!
32789 * Adds a xtype elements to the layout.
32793 xtype : 'ContentPanel',
32800 xtype : 'NestedLayoutPanel',
32806 items : [ ... list of content panels or nested layout panels.. ]
32810 * @param {Object} cfg Xtype definition of item to add.
32812 addxtype : function(cfg)
32814 // basically accepts a pannel...
32815 // can accept a layout region..!?!?
32816 //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
32818 if (!cfg.xtype.match(/Panel$/)) {
32823 if (typeof(cfg.region) == 'undefined') {
32824 Roo.log("Failed to add Panel, region was not set");
32828 var region = cfg.region;
32834 xitems = cfg.items;
32841 case 'ContentPanel': // ContentPanel (el, cfg)
32842 case 'ScrollPanel': // ContentPanel (el, cfg)
32844 if(cfg.autoCreate) {
32845 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
32847 var el = this.el.createChild();
32848 ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
32851 this.add(region, ret);
32855 case 'TreePanel': // our new panel!
32856 cfg.el = this.el.createChild();
32857 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
32858 this.add(region, ret);
32861 case 'NestedLayoutPanel':
32862 // create a new Layout (which is a Border Layout...
32863 var el = this.el.createChild();
32864 var clayout = cfg.layout;
32866 clayout.items = clayout.items || [];
32867 // replace this exitems with the clayout ones..
32868 xitems = clayout.items;
32871 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
32872 cfg.background = false;
32874 var layout = new Roo.BorderLayout(el, clayout);
32876 ret = new Roo[cfg.xtype](layout, cfg); // new panel!!!!!
32877 //console.log('adding nested layout panel ' + cfg.toSource());
32878 this.add(region, ret);
32879 nb = {}; /// find first...
32884 // needs grid and region
32886 //var el = this.getRegion(region).el.createChild();
32887 var el = this.el.createChild();
32888 // create the grid first...
32890 var grid = new Roo.grid[cfg.grid.xtype](el, cfg.grid);
32892 if (region == 'center' && this.active ) {
32893 cfg.background = false;
32895 ret = new Roo[cfg.xtype](grid, cfg); // new panel!!!!!
32897 this.add(region, ret);
32898 if (cfg.background) {
32899 ret.on('activate', function(gp) {
32900 if (!gp.grid.rendered) {
32915 if (typeof(Roo[cfg.xtype]) != 'undefined') {
32917 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
32918 this.add(region, ret);
32921 alert("Can not add '" + cfg.xtype + "' to BorderLayout");
32925 // GridPanel (grid, cfg)
32928 this.beginUpdate();
32932 Roo.each(xitems, function(i) {
32933 region = nb && i.region ? i.region : false;
32935 var add = ret.addxtype(i);
32938 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
32939 if (!i.background) {
32940 abn[region] = nb[region] ;
32947 // make the last non-background panel active..
32948 //if (nb) { Roo.log(abn); }
32951 for(var r in abn) {
32952 region = this.getRegion(r);
32954 // tried using nb[r], but it does not work..
32956 region.showPanel(abn[r]);
32967 * Shortcut for creating a new BorderLayout object and adding one or more ContentPanels to it in a single step, handling
32968 * the beginUpdate and endUpdate calls internally. The key to this method is the <b>panels</b> property that can be
32969 * provided with each region config, which allows you to add ContentPanel configs in addition to the region configs
32970 * during creation. The following code is equivalent to the constructor-based example at the beginning of this class:
32973 var CP = Roo.ContentPanel;
32975 var layout = Roo.BorderLayout.create({
32979 panels: [new CP("north", "North")]
32988 panels: [new CP("west", {title: "West"})]
32997 panels: [new CP("autoTabs", {title: "Auto Tabs", closable: true})]
33006 panels: [new CP("south", {title: "South", closable: true})]
33013 preferredTabWidth: 150,
33015 new CP("center1", {title: "Close Me", closable: true}),
33016 new CP("center2", {title: "Center Panel", closable: false})
33021 layout.getRegion("center").showPanel("center1");
33026 Roo.BorderLayout.create = function(config, targetEl){
33027 var layout = new Roo.BorderLayout(targetEl || document.body, config);
33028 layout.beginUpdate();
33029 var regions = Roo.BorderLayout.RegionFactory.validRegions;
33030 for(var j = 0, jlen = regions.length; j < jlen; j++){
33031 var lr = regions[j];
33032 if(layout.regions[lr] && config[lr].panels){
33033 var r = layout.regions[lr];
33034 var ps = config[lr].panels;
33035 layout.addTypedPanels(r, ps);
33038 layout.endUpdate();
33043 Roo.BorderLayout.RegionFactory = {
33045 validRegions : ["north","south","east","west","center"],
33048 create : function(target, mgr, config){
33049 target = target.toLowerCase();
33050 if(config.lightweight || config.basic){
33051 return new Roo.BasicLayoutRegion(mgr, config, target);
33055 return new Roo.NorthLayoutRegion(mgr, config);
33057 return new Roo.SouthLayoutRegion(mgr, config);
33059 return new Roo.EastLayoutRegion(mgr, config);
33061 return new Roo.WestLayoutRegion(mgr, config);
33063 return new Roo.CenterLayoutRegion(mgr, config);
33065 throw 'Layout region "'+target+'" not supported.';
33069 * Ext JS Library 1.1.1
33070 * Copyright(c) 2006-2007, Ext JS, LLC.
33072 * Originally Released Under LGPL - original licence link has changed is not relivant.
33075 * <script type="text/javascript">
33079 * @class Roo.BasicLayoutRegion
33080 * @extends Roo.util.Observable
33081 * This class represents a lightweight region in a layout manager. This region does not move dom nodes
33082 * and does not have a titlebar, tabs or any other features. All it does is size and position
33083 * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
33085 Roo.BasicLayoutRegion = function(mgr, config, pos, skipConfig){
33087 this.position = pos;
33090 * @scope Roo.BasicLayoutRegion
33094 * @event beforeremove
33095 * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
33096 * @param {Roo.LayoutRegion} this
33097 * @param {Roo.ContentPanel} panel The panel
33098 * @param {Object} e The cancel event object
33100 "beforeremove" : true,
33102 * @event invalidated
33103 * Fires when the layout for this region is changed.
33104 * @param {Roo.LayoutRegion} this
33106 "invalidated" : true,
33108 * @event visibilitychange
33109 * Fires when this region is shown or hidden
33110 * @param {Roo.LayoutRegion} this
33111 * @param {Boolean} visibility true or false
33113 "visibilitychange" : true,
33115 * @event paneladded
33116 * Fires when a panel is added.
33117 * @param {Roo.LayoutRegion} this
33118 * @param {Roo.ContentPanel} panel The panel
33120 "paneladded" : true,
33122 * @event panelremoved
33123 * Fires when a panel is removed.
33124 * @param {Roo.LayoutRegion} this
33125 * @param {Roo.ContentPanel} panel The panel
33127 "panelremoved" : true,
33130 * Fires when this region is collapsed.
33131 * @param {Roo.LayoutRegion} this
33133 "collapsed" : true,
33136 * Fires when this region is expanded.
33137 * @param {Roo.LayoutRegion} this
33142 * Fires when this region is slid into view.
33143 * @param {Roo.LayoutRegion} this
33145 "slideshow" : true,
33148 * Fires when this region slides out of view.
33149 * @param {Roo.LayoutRegion} this
33151 "slidehide" : true,
33153 * @event panelactivated
33154 * Fires when a panel is activated.
33155 * @param {Roo.LayoutRegion} this
33156 * @param {Roo.ContentPanel} panel The activated panel
33158 "panelactivated" : true,
33161 * Fires when the user resizes this region.
33162 * @param {Roo.LayoutRegion} this
33163 * @param {Number} newSize The new size (width for east/west, height for north/south)
33167 /** A collection of panels in this region. @type Roo.util.MixedCollection */
33168 this.panels = new Roo.util.MixedCollection();
33169 this.panels.getKey = this.getPanelId.createDelegate(this);
33171 this.activePanel = null;
33172 // ensure listeners are added...
33174 if (config.listeners || config.events) {
33175 Roo.BasicLayoutRegion.superclass.constructor.call(this, {
33176 listeners : config.listeners || {},
33177 events : config.events || {}
33181 if(skipConfig !== true){
33182 this.applyConfig(config);
33186 Roo.extend(Roo.BasicLayoutRegion, Roo.util.Observable, {
33187 getPanelId : function(p){
33191 applyConfig : function(config){
33192 this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
33193 this.config = config;
33198 * Resizes the region to the specified size. For vertical regions (west, east) this adjusts
33199 * the width, for horizontal (north, south) the height.
33200 * @param {Number} newSize The new width or height
33202 resizeTo : function(newSize){
33203 var el = this.el ? this.el :
33204 (this.activePanel ? this.activePanel.getEl() : null);
33206 switch(this.position){
33209 el.setWidth(newSize);
33210 this.fireEvent("resized", this, newSize);
33214 el.setHeight(newSize);
33215 this.fireEvent("resized", this, newSize);
33221 getBox : function(){
33222 return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
33225 getMargins : function(){
33226 return this.margins;
33229 updateBox : function(box){
33231 var el = this.activePanel.getEl();
33232 el.dom.style.left = box.x + "px";
33233 el.dom.style.top = box.y + "px";
33234 this.activePanel.setSize(box.width, box.height);
33238 * Returns the container element for this region.
33239 * @return {Roo.Element}
33241 getEl : function(){
33242 return this.activePanel;
33246 * Returns true if this region is currently visible.
33247 * @return {Boolean}
33249 isVisible : function(){
33250 return this.activePanel ? true : false;
33253 setActivePanel : function(panel){
33254 panel = this.getPanel(panel);
33255 if(this.activePanel && this.activePanel != panel){
33256 this.activePanel.setActiveState(false);
33257 this.activePanel.getEl().setLeftTop(-10000,-10000);
33259 this.activePanel = panel;
33260 panel.setActiveState(true);
33262 panel.setSize(this.box.width, this.box.height);
33264 this.fireEvent("panelactivated", this, panel);
33265 this.fireEvent("invalidated");
33269 * Show the specified panel.
33270 * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
33271 * @return {Roo.ContentPanel} The shown panel or null
33273 showPanel : function(panel){
33274 if(panel = this.getPanel(panel)){
33275 this.setActivePanel(panel);
33281 * Get the active panel for this region.
33282 * @return {Roo.ContentPanel} The active panel or null
33284 getActivePanel : function(){
33285 return this.activePanel;
33289 * Add the passed ContentPanel(s)
33290 * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
33291 * @return {Roo.ContentPanel} The panel added (if only one was added)
33293 add : function(panel){
33294 if(arguments.length > 1){
33295 for(var i = 0, len = arguments.length; i < len; i++) {
33296 this.add(arguments[i]);
33300 if(this.hasPanel(panel)){
33301 this.showPanel(panel);
33304 var el = panel.getEl();
33305 if(el.dom.parentNode != this.mgr.el.dom){
33306 this.mgr.el.dom.appendChild(el.dom);
33308 if(panel.setRegion){
33309 panel.setRegion(this);
33311 this.panels.add(panel);
33312 el.setStyle("position", "absolute");
33313 if(!panel.background){
33314 this.setActivePanel(panel);
33315 if(this.config.initialSize && this.panels.getCount()==1){
33316 this.resizeTo(this.config.initialSize);
33319 this.fireEvent("paneladded", this, panel);
33324 * Returns true if the panel is in this region.
33325 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
33326 * @return {Boolean}
33328 hasPanel : function(panel){
33329 if(typeof panel == "object"){ // must be panel obj
33330 panel = panel.getId();
33332 return this.getPanel(panel) ? true : false;
33336 * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
33337 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
33338 * @param {Boolean} preservePanel Overrides the config preservePanel option
33339 * @return {Roo.ContentPanel} The panel that was removed
33341 remove : function(panel, preservePanel){
33342 panel = this.getPanel(panel);
33347 this.fireEvent("beforeremove", this, panel, e);
33348 if(e.cancel === true){
33351 var panelId = panel.getId();
33352 this.panels.removeKey(panelId);
33357 * Returns the panel specified or null if it's not in this region.
33358 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
33359 * @return {Roo.ContentPanel}
33361 getPanel : function(id){
33362 if(typeof id == "object"){ // must be panel obj
33365 return this.panels.get(id);
33369 * Returns this regions position (north/south/east/west/center).
33372 getPosition: function(){
33373 return this.position;
33377 * Ext JS Library 1.1.1
33378 * Copyright(c) 2006-2007, Ext JS, LLC.
33380 * Originally Released Under LGPL - original licence link has changed is not relivant.
33383 * <script type="text/javascript">
33387 * @class Roo.LayoutRegion
33388 * @extends Roo.BasicLayoutRegion
33389 * This class represents a region in a layout manager.
33390 * @cfg {Boolean} collapsible False to disable collapsing (defaults to true)
33391 * @cfg {Boolean} collapsed True to set the initial display to collapsed (defaults to false)
33392 * @cfg {Boolean} floatable False to disable floating (defaults to true)
33393 * @cfg {Object} margins Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
33394 * @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})
33395 * @cfg {String} tabPosition "top" or "bottom" (defaults to "bottom")
33396 * @cfg {String} collapsedTitle Optional string message to display in the collapsed block of a north or south region
33397 * @cfg {Boolean} alwaysShowTabs True to always display tabs even when there is only 1 panel (defaults to false)
33398 * @cfg {Boolean} autoScroll True to enable overflow scrolling (defaults to false)
33399 * @cfg {Boolean} titlebar True to display a title bar (defaults to true)
33400 * @cfg {String} title The title for the region (overrides panel titles)
33401 * @cfg {Boolean} animate True to animate expand/collapse (defaults to false)
33402 * @cfg {Boolean} autoHide False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
33403 * @cfg {Boolean} preservePanels True to preserve removed panels so they can be readded later (defaults to false)
33404 * @cfg {Boolean} closeOnTab True to place the close icon on the tabs instead of the region titlebar (defaults to false)
33405 * @cfg {Boolean} hideTabs True to hide the tab strip (defaults to false)
33406 * @cfg {Boolean} resizeTabs True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
33407 * the space available, similar to FireFox 1.5 tabs (defaults to false)
33408 * @cfg {Number} minTabWidth The minimum tab width (defaults to 40)
33409 * @cfg {Number} preferredTabWidth The preferred tab width (defaults to 150)
33410 * @cfg {Boolean} showPin True to show a pin button
33411 * @cfg {Boolean} hidden True to start the region hidden (defaults to false)
33412 * @cfg {Boolean} hideWhenEmpty True to hide the region when it has no panels
33413 * @cfg {Boolean} disableTabTips True to disable tab tooltips
33414 * @cfg {Number} width For East/West panels
33415 * @cfg {Number} height For North/South panels
33416 * @cfg {Boolean} split To show the splitter
33417 * @cfg {Boolean} toolbar xtype configuration for a toolbar - shows on right of tabbar
33419 Roo.LayoutRegion = function(mgr, config, pos){
33420 Roo.LayoutRegion.superclass.constructor.call(this, mgr, config, pos, true);
33421 var dh = Roo.DomHelper;
33422 /** This region's container element
33423 * @type Roo.Element */
33424 this.el = dh.append(mgr.el.dom, {tag: "div", cls: "x-layout-panel x-layout-panel-" + this.position}, true);
33425 /** This region's title element
33426 * @type Roo.Element */
33428 this.titleEl = dh.append(this.el.dom, {tag: "div", unselectable: "on", cls: "x-unselectable x-layout-panel-hd x-layout-title-"+this.position, children:[
33429 {tag: "span", cls: "x-unselectable x-layout-panel-hd-text", unselectable: "on", html: " "},
33430 {tag: "div", cls: "x-unselectable x-layout-panel-hd-tools", unselectable: "on"}
33432 this.titleEl.enableDisplayMode();
33433 /** This region's title text element
33434 * @type HTMLElement */
33435 this.titleTextEl = this.titleEl.dom.firstChild;
33436 this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
33437 this.closeBtn = this.createTool(this.tools.dom, "x-layout-close");
33438 this.closeBtn.enableDisplayMode();
33439 this.closeBtn.on("click", this.closeClicked, this);
33440 this.closeBtn.hide();
33442 this.createBody(config);
33443 this.visible = true;
33444 this.collapsed = false;
33446 if(config.hideWhenEmpty){
33448 this.on("paneladded", this.validateVisibility, this);
33449 this.on("panelremoved", this.validateVisibility, this);
33451 this.applyConfig(config);
33454 Roo.extend(Roo.LayoutRegion, Roo.BasicLayoutRegion, {
33456 createBody : function(){
33457 /** This region's body element
33458 * @type Roo.Element */
33459 this.bodyEl = this.el.createChild({tag: "div", cls: "x-layout-panel-body"});
33462 applyConfig : function(c){
33463 if(c.collapsible && this.position != "center" && !this.collapsedEl){
33464 var dh = Roo.DomHelper;
33465 if(c.titlebar !== false){
33466 this.collapseBtn = this.createTool(this.tools.dom, "x-layout-collapse-"+this.position);
33467 this.collapseBtn.on("click", this.collapse, this);
33468 this.collapseBtn.enableDisplayMode();
33470 if(c.showPin === true || this.showPin){
33471 this.stickBtn = this.createTool(this.tools.dom, "x-layout-stick");
33472 this.stickBtn.enableDisplayMode();
33473 this.stickBtn.on("click", this.expand, this);
33474 this.stickBtn.hide();
33477 /** This region's collapsed element
33478 * @type Roo.Element */
33479 this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
33480 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
33482 if(c.floatable !== false){
33483 this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
33484 this.collapsedEl.on("click", this.collapseClick, this);
33487 if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
33488 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
33489 id: "message", unselectable: "on", style:{"float":"left"}});
33490 this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
33492 this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
33493 this.expandBtn.on("click", this.expand, this);
33495 if(this.collapseBtn){
33496 this.collapseBtn.setVisible(c.collapsible == true);
33498 this.cmargins = c.cmargins || this.cmargins ||
33499 (this.position == "west" || this.position == "east" ?
33500 {top: 0, left: 2, right:2, bottom: 0} :
33501 {top: 2, left: 0, right:0, bottom: 2});
33502 this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
33503 this.bottomTabs = c.tabPosition != "top";
33504 this.autoScroll = c.autoScroll || false;
33505 if(this.autoScroll){
33506 this.bodyEl.setStyle("overflow", "auto");
33508 this.bodyEl.setStyle("overflow", "hidden");
33510 //if(c.titlebar !== false){
33511 if((!c.titlebar && !c.title) || c.titlebar === false){
33512 this.titleEl.hide();
33514 this.titleEl.show();
33516 this.titleTextEl.innerHTML = c.title;
33520 this.duration = c.duration || .30;
33521 this.slideDuration = c.slideDuration || .45;
33524 this.collapse(true);
33531 * Returns true if this region is currently visible.
33532 * @return {Boolean}
33534 isVisible : function(){
33535 return this.visible;
33539 * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
33540 * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&#160;")
33542 setCollapsedTitle : function(title){
33543 title = title || " ";
33544 if(this.collapsedTitleTextEl){
33545 this.collapsedTitleTextEl.innerHTML = title;
33549 getBox : function(){
33551 if(!this.collapsed){
33552 b = this.el.getBox(false, true);
33554 b = this.collapsedEl.getBox(false, true);
33559 getMargins : function(){
33560 return this.collapsed ? this.cmargins : this.margins;
33563 highlight : function(){
33564 this.el.addClass("x-layout-panel-dragover");
33567 unhighlight : function(){
33568 this.el.removeClass("x-layout-panel-dragover");
33571 updateBox : function(box){
33573 if(!this.collapsed){
33574 this.el.dom.style.left = box.x + "px";
33575 this.el.dom.style.top = box.y + "px";
33576 this.updateBody(box.width, box.height);
33578 this.collapsedEl.dom.style.left = box.x + "px";
33579 this.collapsedEl.dom.style.top = box.y + "px";
33580 this.collapsedEl.setSize(box.width, box.height);
33583 this.tabs.autoSizeTabs();
33587 updateBody : function(w, h){
33589 this.el.setWidth(w);
33590 w -= this.el.getBorderWidth("rl");
33591 if(this.config.adjustments){
33592 w += this.config.adjustments[0];
33596 this.el.setHeight(h);
33597 h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
33598 h -= this.el.getBorderWidth("tb");
33599 if(this.config.adjustments){
33600 h += this.config.adjustments[1];
33602 this.bodyEl.setHeight(h);
33604 h = this.tabs.syncHeight(h);
33607 if(this.panelSize){
33608 w = w !== null ? w : this.panelSize.width;
33609 h = h !== null ? h : this.panelSize.height;
33611 if(this.activePanel){
33612 var el = this.activePanel.getEl();
33613 w = w !== null ? w : el.getWidth();
33614 h = h !== null ? h : el.getHeight();
33615 this.panelSize = {width: w, height: h};
33616 this.activePanel.setSize(w, h);
33618 if(Roo.isIE && this.tabs){
33619 this.tabs.el.repaint();
33624 * Returns the container element for this region.
33625 * @return {Roo.Element}
33627 getEl : function(){
33632 * Hides this region.
33635 if(!this.collapsed){
33636 this.el.dom.style.left = "-2000px";
33639 this.collapsedEl.dom.style.left = "-2000px";
33640 this.collapsedEl.hide();
33642 this.visible = false;
33643 this.fireEvent("visibilitychange", this, false);
33647 * Shows this region if it was previously hidden.
33650 if(!this.collapsed){
33653 this.collapsedEl.show();
33655 this.visible = true;
33656 this.fireEvent("visibilitychange", this, true);
33659 closeClicked : function(){
33660 if(this.activePanel){
33661 this.remove(this.activePanel);
33665 collapseClick : function(e){
33667 e.stopPropagation();
33670 e.stopPropagation();
33676 * Collapses this region.
33677 * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
33679 collapse : function(skipAnim){
33680 if(this.collapsed) return;
33681 this.collapsed = true;
33683 this.split.el.hide();
33685 if(this.config.animate && skipAnim !== true){
33686 this.fireEvent("invalidated", this);
33687 this.animateCollapse();
33689 this.el.setLocation(-20000,-20000);
33691 this.collapsedEl.show();
33692 this.fireEvent("collapsed", this);
33693 this.fireEvent("invalidated", this);
33697 animateCollapse : function(){
33702 * Expands this region if it was previously collapsed.
33703 * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
33704 * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
33706 expand : function(e, skipAnim){
33707 if(e) e.stopPropagation();
33708 if(!this.collapsed || this.el.hasActiveFx()) return;
33710 this.afterSlideIn();
33713 this.collapsed = false;
33714 if(this.config.animate && skipAnim !== true){
33715 this.animateExpand();
33719 this.split.el.show();
33721 this.collapsedEl.setLocation(-2000,-2000);
33722 this.collapsedEl.hide();
33723 this.fireEvent("invalidated", this);
33724 this.fireEvent("expanded", this);
33728 animateExpand : function(){
33732 initTabs : function()
33734 this.bodyEl.setStyle("overflow", "hidden");
33735 var ts = new Roo.TabPanel(
33738 tabPosition: this.bottomTabs ? 'bottom' : 'top',
33739 disableTooltips: this.config.disableTabTips,
33740 toolbar : this.config.toolbar
33743 if(this.config.hideTabs){
33744 ts.stripWrap.setDisplayed(false);
33747 ts.resizeTabs = this.config.resizeTabs === true;
33748 ts.minTabWidth = this.config.minTabWidth || 40;
33749 ts.maxTabWidth = this.config.maxTabWidth || 250;
33750 ts.preferredTabWidth = this.config.preferredTabWidth || 150;
33751 ts.monitorResize = false;
33752 ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
33753 ts.bodyEl.addClass('x-layout-tabs-body');
33754 this.panels.each(this.initPanelAsTab, this);
33757 initPanelAsTab : function(panel){
33758 var ti = this.tabs.addTab(panel.getEl().id, panel.getTitle(), null,
33759 this.config.closeOnTab && panel.isClosable());
33760 if(panel.tabTip !== undefined){
33761 ti.setTooltip(panel.tabTip);
33763 ti.on("activate", function(){
33764 this.setActivePanel(panel);
33766 if(this.config.closeOnTab){
33767 ti.on("beforeclose", function(t, e){
33769 this.remove(panel);
33775 updatePanelTitle : function(panel, title){
33776 if(this.activePanel == panel){
33777 this.updateTitle(title);
33780 var ti = this.tabs.getTab(panel.getEl().id);
33782 if(panel.tabTip !== undefined){
33783 ti.setTooltip(panel.tabTip);
33788 updateTitle : function(title){
33789 if(this.titleTextEl && !this.config.title){
33790 this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : " ");
33794 setActivePanel : function(panel){
33795 panel = this.getPanel(panel);
33796 if(this.activePanel && this.activePanel != panel){
33797 this.activePanel.setActiveState(false);
33799 this.activePanel = panel;
33800 panel.setActiveState(true);
33801 if(this.panelSize){
33802 panel.setSize(this.panelSize.width, this.panelSize.height);
33805 this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
33807 this.updateTitle(panel.getTitle());
33809 this.fireEvent("invalidated", this);
33811 this.fireEvent("panelactivated", this, panel);
33815 * Shows the specified panel.
33816 * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
33817 * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
33819 showPanel : function(panel){
33820 if(panel = this.getPanel(panel)){
33822 var tab = this.tabs.getTab(panel.getEl().id);
33823 if(tab.isHidden()){
33824 this.tabs.unhideTab(tab.id);
33828 this.setActivePanel(panel);
33835 * Get the active panel for this region.
33836 * @return {Roo.ContentPanel} The active panel or null
33838 getActivePanel : function(){
33839 return this.activePanel;
33842 validateVisibility : function(){
33843 if(this.panels.getCount() < 1){
33844 this.updateTitle(" ");
33845 this.closeBtn.hide();
33848 if(!this.isVisible()){
33855 * Adds the passed ContentPanel(s) to this region.
33856 * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
33857 * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
33859 add : function(panel){
33860 if(arguments.length > 1){
33861 for(var i = 0, len = arguments.length; i < len; i++) {
33862 this.add(arguments[i]);
33866 if(this.hasPanel(panel)){
33867 this.showPanel(panel);
33870 panel.setRegion(this);
33871 this.panels.add(panel);
33872 if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
33873 this.bodyEl.dom.appendChild(panel.getEl().dom);
33874 if(panel.background !== true){
33875 this.setActivePanel(panel);
33877 this.fireEvent("paneladded", this, panel);
33883 this.initPanelAsTab(panel);
33885 if(panel.background !== true){
33886 this.tabs.activate(panel.getEl().id);
33888 this.fireEvent("paneladded", this, panel);
33893 * Hides the tab for the specified panel.
33894 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
33896 hidePanel : function(panel){
33897 if(this.tabs && (panel = this.getPanel(panel))){
33898 this.tabs.hideTab(panel.getEl().id);
33903 * Unhides the tab for a previously hidden panel.
33904 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
33906 unhidePanel : function(panel){
33907 if(this.tabs && (panel = this.getPanel(panel))){
33908 this.tabs.unhideTab(panel.getEl().id);
33912 clearPanels : function(){
33913 while(this.panels.getCount() > 0){
33914 this.remove(this.panels.first());
33919 * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
33920 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
33921 * @param {Boolean} preservePanel Overrides the config preservePanel option
33922 * @return {Roo.ContentPanel} The panel that was removed
33924 remove : function(panel, preservePanel){
33925 panel = this.getPanel(panel);
33930 this.fireEvent("beforeremove", this, panel, e);
33931 if(e.cancel === true){
33934 preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
33935 var panelId = panel.getId();
33936 this.panels.removeKey(panelId);
33938 document.body.appendChild(panel.getEl().dom);
33941 this.tabs.removeTab(panel.getEl().id);
33942 }else if (!preservePanel){
33943 this.bodyEl.dom.removeChild(panel.getEl().dom);
33945 if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
33946 var p = this.panels.first();
33947 var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
33948 tempEl.appendChild(p.getEl().dom);
33949 this.bodyEl.update("");
33950 this.bodyEl.dom.appendChild(p.getEl().dom);
33952 this.updateTitle(p.getTitle());
33954 this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
33955 this.setActivePanel(p);
33957 panel.setRegion(null);
33958 if(this.activePanel == panel){
33959 this.activePanel = null;
33961 if(this.config.autoDestroy !== false && preservePanel !== true){
33962 try{panel.destroy();}catch(e){}
33964 this.fireEvent("panelremoved", this, panel);
33969 * Returns the TabPanel component used by this region
33970 * @return {Roo.TabPanel}
33972 getTabs : function(){
33976 createTool : function(parentEl, className){
33977 var btn = Roo.DomHelper.append(parentEl, {tag: "div", cls: "x-layout-tools-button",
33978 children: [{tag: "div", cls: "x-layout-tools-button-inner " + className, html: " "}]}, true);
33979 btn.addClassOnOver("x-layout-tools-button-over");
33984 * Ext JS Library 1.1.1
33985 * Copyright(c) 2006-2007, Ext JS, LLC.
33987 * Originally Released Under LGPL - original licence link has changed is not relivant.
33990 * <script type="text/javascript">
33996 * @class Roo.SplitLayoutRegion
33997 * @extends Roo.LayoutRegion
33998 * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
34000 Roo.SplitLayoutRegion = function(mgr, config, pos, cursor){
34001 this.cursor = cursor;
34002 Roo.SplitLayoutRegion.superclass.constructor.call(this, mgr, config, pos);
34005 Roo.extend(Roo.SplitLayoutRegion, Roo.LayoutRegion, {
34006 splitTip : "Drag to resize.",
34007 collapsibleSplitTip : "Drag to resize. Double click to hide.",
34008 useSplitTips : false,
34010 applyConfig : function(config){
34011 Roo.SplitLayoutRegion.superclass.applyConfig.call(this, config);
34014 var splitEl = Roo.DomHelper.append(this.mgr.el.dom,
34015 {tag: "div", id: this.el.id + "-split", cls: "x-layout-split x-layout-split-"+this.position, html: " "});
34016 /** The SplitBar for this region
34017 * @type Roo.SplitBar */
34018 this.split = new Roo.SplitBar(splitEl, this.el, this.orientation);
34019 this.split.on("moved", this.onSplitMove, this);
34020 this.split.useShim = config.useShim === true;
34021 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
34022 if(this.useSplitTips){
34023 this.split.el.dom.title = config.collapsible ? this.collapsibleSplitTip : this.splitTip;
34025 if(config.collapsible){
34026 this.split.el.on("dblclick", this.collapse, this);
34029 if(typeof config.minSize != "undefined"){
34030 this.split.minSize = config.minSize;
34032 if(typeof config.maxSize != "undefined"){
34033 this.split.maxSize = config.maxSize;
34035 if(config.hideWhenEmpty || config.hidden || config.collapsed){
34036 this.hideSplitter();
34041 getHMaxSize : function(){
34042 var cmax = this.config.maxSize || 10000;
34043 var center = this.mgr.getRegion("center");
34044 return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
34047 getVMaxSize : function(){
34048 var cmax = this.config.maxSize || 10000;
34049 var center = this.mgr.getRegion("center");
34050 return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
34053 onSplitMove : function(split, newSize){
34054 this.fireEvent("resized", this, newSize);
34058 * Returns the {@link Roo.SplitBar} for this region.
34059 * @return {Roo.SplitBar}
34061 getSplitBar : function(){
34066 this.hideSplitter();
34067 Roo.SplitLayoutRegion.superclass.hide.call(this);
34070 hideSplitter : function(){
34072 this.split.el.setLocation(-2000,-2000);
34073 this.split.el.hide();
34079 this.split.el.show();
34081 Roo.SplitLayoutRegion.superclass.show.call(this);
34084 beforeSlide: function(){
34085 if(Roo.isGecko){// firefox overflow auto bug workaround
34086 this.bodyEl.clip();
34087 if(this.tabs) this.tabs.bodyEl.clip();
34088 if(this.activePanel){
34089 this.activePanel.getEl().clip();
34091 if(this.activePanel.beforeSlide){
34092 this.activePanel.beforeSlide();
34098 afterSlide : function(){
34099 if(Roo.isGecko){// firefox overflow auto bug workaround
34100 this.bodyEl.unclip();
34101 if(this.tabs) this.tabs.bodyEl.unclip();
34102 if(this.activePanel){
34103 this.activePanel.getEl().unclip();
34104 if(this.activePanel.afterSlide){
34105 this.activePanel.afterSlide();
34111 initAutoHide : function(){
34112 if(this.autoHide !== false){
34113 if(!this.autoHideHd){
34114 var st = new Roo.util.DelayedTask(this.slideIn, this);
34115 this.autoHideHd = {
34116 "mouseout": function(e){
34117 if(!e.within(this.el, true)){
34121 "mouseover" : function(e){
34127 this.el.on(this.autoHideHd);
34131 clearAutoHide : function(){
34132 if(this.autoHide !== false){
34133 this.el.un("mouseout", this.autoHideHd.mouseout);
34134 this.el.un("mouseover", this.autoHideHd.mouseover);
34138 clearMonitor : function(){
34139 Roo.get(document).un("click", this.slideInIf, this);
34142 // these names are backwards but not changed for compat
34143 slideOut : function(){
34144 if(this.isSlid || this.el.hasActiveFx()){
34147 this.isSlid = true;
34148 if(this.collapseBtn){
34149 this.collapseBtn.hide();
34151 this.closeBtnState = this.closeBtn.getStyle('display');
34152 this.closeBtn.hide();
34154 this.stickBtn.show();
34157 this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
34158 this.beforeSlide();
34159 this.el.setStyle("z-index", 10001);
34160 this.el.slideIn(this.getSlideAnchor(), {
34161 callback: function(){
34163 this.initAutoHide();
34164 Roo.get(document).on("click", this.slideInIf, this);
34165 this.fireEvent("slideshow", this);
34172 afterSlideIn : function(){
34173 this.clearAutoHide();
34174 this.isSlid = false;
34175 this.clearMonitor();
34176 this.el.setStyle("z-index", "");
34177 if(this.collapseBtn){
34178 this.collapseBtn.show();
34180 this.closeBtn.setStyle('display', this.closeBtnState);
34182 this.stickBtn.hide();
34184 this.fireEvent("slidehide", this);
34187 slideIn : function(cb){
34188 if(!this.isSlid || this.el.hasActiveFx()){
34192 this.isSlid = false;
34193 this.beforeSlide();
34194 this.el.slideOut(this.getSlideAnchor(), {
34195 callback: function(){
34196 this.el.setLeftTop(-10000, -10000);
34198 this.afterSlideIn();
34206 slideInIf : function(e){
34207 if(!e.within(this.el)){
34212 animateCollapse : function(){
34213 this.beforeSlide();
34214 this.el.setStyle("z-index", 20000);
34215 var anchor = this.getSlideAnchor();
34216 this.el.slideOut(anchor, {
34217 callback : function(){
34218 this.el.setStyle("z-index", "");
34219 this.collapsedEl.slideIn(anchor, {duration:.3});
34221 this.el.setLocation(-10000,-10000);
34223 this.fireEvent("collapsed", this);
34230 animateExpand : function(){
34231 this.beforeSlide();
34232 this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
34233 this.el.setStyle("z-index", 20000);
34234 this.collapsedEl.hide({
34237 this.el.slideIn(this.getSlideAnchor(), {
34238 callback : function(){
34239 this.el.setStyle("z-index", "");
34242 this.split.el.show();
34244 this.fireEvent("invalidated", this);
34245 this.fireEvent("expanded", this);
34273 getAnchor : function(){
34274 return this.anchors[this.position];
34277 getCollapseAnchor : function(){
34278 return this.canchors[this.position];
34281 getSlideAnchor : function(){
34282 return this.sanchors[this.position];
34285 getAlignAdj : function(){
34286 var cm = this.cmargins;
34287 switch(this.position){
34303 getExpandAdj : function(){
34304 var c = this.collapsedEl, cm = this.cmargins;
34305 switch(this.position){
34307 return [-(cm.right+c.getWidth()+cm.left), 0];
34310 return [cm.right+c.getWidth()+cm.left, 0];
34313 return [0, -(cm.top+cm.bottom+c.getHeight())];
34316 return [0, cm.top+cm.bottom+c.getHeight()];
34322 * Ext JS Library 1.1.1
34323 * Copyright(c) 2006-2007, Ext JS, LLC.
34325 * Originally Released Under LGPL - original licence link has changed is not relivant.
34328 * <script type="text/javascript">
34331 * These classes are private internal classes
34333 Roo.CenterLayoutRegion = function(mgr, config){
34334 Roo.LayoutRegion.call(this, mgr, config, "center");
34335 this.visible = true;
34336 this.minWidth = config.minWidth || 20;
34337 this.minHeight = config.minHeight || 20;
34340 Roo.extend(Roo.CenterLayoutRegion, Roo.LayoutRegion, {
34342 // center panel can't be hidden
34346 // center panel can't be hidden
34349 getMinWidth: function(){
34350 return this.minWidth;
34353 getMinHeight: function(){
34354 return this.minHeight;
34359 Roo.NorthLayoutRegion = function(mgr, config){
34360 Roo.LayoutRegion.call(this, mgr, config, "north", "n-resize");
34362 this.split.placement = Roo.SplitBar.TOP;
34363 this.split.orientation = Roo.SplitBar.VERTICAL;
34364 this.split.el.addClass("x-layout-split-v");
34366 var size = config.initialSize || config.height;
34367 if(typeof size != "undefined"){
34368 this.el.setHeight(size);
34371 Roo.extend(Roo.NorthLayoutRegion, Roo.SplitLayoutRegion, {
34372 orientation: Roo.SplitBar.VERTICAL,
34373 getBox : function(){
34374 if(this.collapsed){
34375 return this.collapsedEl.getBox();
34377 var box = this.el.getBox();
34379 box.height += this.split.el.getHeight();
34384 updateBox : function(box){
34385 if(this.split && !this.collapsed){
34386 box.height -= this.split.el.getHeight();
34387 this.split.el.setLeft(box.x);
34388 this.split.el.setTop(box.y+box.height);
34389 this.split.el.setWidth(box.width);
34391 if(this.collapsed){
34392 this.updateBody(box.width, null);
34394 Roo.LayoutRegion.prototype.updateBox.call(this, box);
34398 Roo.SouthLayoutRegion = function(mgr, config){
34399 Roo.SplitLayoutRegion.call(this, mgr, config, "south", "s-resize");
34401 this.split.placement = Roo.SplitBar.BOTTOM;
34402 this.split.orientation = Roo.SplitBar.VERTICAL;
34403 this.split.el.addClass("x-layout-split-v");
34405 var size = config.initialSize || config.height;
34406 if(typeof size != "undefined"){
34407 this.el.setHeight(size);
34410 Roo.extend(Roo.SouthLayoutRegion, Roo.SplitLayoutRegion, {
34411 orientation: Roo.SplitBar.VERTICAL,
34412 getBox : function(){
34413 if(this.collapsed){
34414 return this.collapsedEl.getBox();
34416 var box = this.el.getBox();
34418 var sh = this.split.el.getHeight();
34425 updateBox : function(box){
34426 if(this.split && !this.collapsed){
34427 var sh = this.split.el.getHeight();
34430 this.split.el.setLeft(box.x);
34431 this.split.el.setTop(box.y-sh);
34432 this.split.el.setWidth(box.width);
34434 if(this.collapsed){
34435 this.updateBody(box.width, null);
34437 Roo.LayoutRegion.prototype.updateBox.call(this, box);
34441 Roo.EastLayoutRegion = function(mgr, config){
34442 Roo.SplitLayoutRegion.call(this, mgr, config, "east", "e-resize");
34444 this.split.placement = Roo.SplitBar.RIGHT;
34445 this.split.orientation = Roo.SplitBar.HORIZONTAL;
34446 this.split.el.addClass("x-layout-split-h");
34448 var size = config.initialSize || config.width;
34449 if(typeof size != "undefined"){
34450 this.el.setWidth(size);
34453 Roo.extend(Roo.EastLayoutRegion, Roo.SplitLayoutRegion, {
34454 orientation: Roo.SplitBar.HORIZONTAL,
34455 getBox : function(){
34456 if(this.collapsed){
34457 return this.collapsedEl.getBox();
34459 var box = this.el.getBox();
34461 var sw = this.split.el.getWidth();
34468 updateBox : function(box){
34469 if(this.split && !this.collapsed){
34470 var sw = this.split.el.getWidth();
34472 this.split.el.setLeft(box.x);
34473 this.split.el.setTop(box.y);
34474 this.split.el.setHeight(box.height);
34477 if(this.collapsed){
34478 this.updateBody(null, box.height);
34480 Roo.LayoutRegion.prototype.updateBox.call(this, box);
34484 Roo.WestLayoutRegion = function(mgr, config){
34485 Roo.SplitLayoutRegion.call(this, mgr, config, "west", "w-resize");
34487 this.split.placement = Roo.SplitBar.LEFT;
34488 this.split.orientation = Roo.SplitBar.HORIZONTAL;
34489 this.split.el.addClass("x-layout-split-h");
34491 var size = config.initialSize || config.width;
34492 if(typeof size != "undefined"){
34493 this.el.setWidth(size);
34496 Roo.extend(Roo.WestLayoutRegion, Roo.SplitLayoutRegion, {
34497 orientation: Roo.SplitBar.HORIZONTAL,
34498 getBox : function(){
34499 if(this.collapsed){
34500 return this.collapsedEl.getBox();
34502 var box = this.el.getBox();
34504 box.width += this.split.el.getWidth();
34509 updateBox : function(box){
34510 if(this.split && !this.collapsed){
34511 var sw = this.split.el.getWidth();
34513 this.split.el.setLeft(box.x+box.width);
34514 this.split.el.setTop(box.y);
34515 this.split.el.setHeight(box.height);
34517 if(this.collapsed){
34518 this.updateBody(null, box.height);
34520 Roo.LayoutRegion.prototype.updateBox.call(this, box);
34525 * Ext JS Library 1.1.1
34526 * Copyright(c) 2006-2007, Ext JS, LLC.
34528 * Originally Released Under LGPL - original licence link has changed is not relivant.
34531 * <script type="text/javascript">
34536 * Private internal class for reading and applying state
34538 Roo.LayoutStateManager = function(layout){
34539 // default empty state
34548 Roo.LayoutStateManager.prototype = {
34549 init : function(layout, provider){
34550 this.provider = provider;
34551 var state = provider.get(layout.id+"-layout-state");
34553 var wasUpdating = layout.isUpdating();
34555 layout.beginUpdate();
34557 for(var key in state){
34558 if(typeof state[key] != "function"){
34559 var rstate = state[key];
34560 var r = layout.getRegion(key);
34563 r.resizeTo(rstate.size);
34565 if(rstate.collapsed == true){
34568 r.expand(null, true);
34574 layout.endUpdate();
34576 this.state = state;
34578 this.layout = layout;
34579 layout.on("regionresized", this.onRegionResized, this);
34580 layout.on("regioncollapsed", this.onRegionCollapsed, this);
34581 layout.on("regionexpanded", this.onRegionExpanded, this);
34584 storeState : function(){
34585 this.provider.set(this.layout.id+"-layout-state", this.state);
34588 onRegionResized : function(region, newSize){
34589 this.state[region.getPosition()].size = newSize;
34593 onRegionCollapsed : function(region){
34594 this.state[region.getPosition()].collapsed = true;
34598 onRegionExpanded : function(region){
34599 this.state[region.getPosition()].collapsed = false;
34604 * Ext JS Library 1.1.1
34605 * Copyright(c) 2006-2007, Ext JS, LLC.
34607 * Originally Released Under LGPL - original licence link has changed is not relivant.
34610 * <script type="text/javascript">
34613 * @class Roo.ContentPanel
34614 * @extends Roo.util.Observable
34615 * A basic ContentPanel element.
34616 * @cfg {Boolean} fitToFrame True for this panel to adjust its size to fit when the region resizes (defaults to false)
34617 * @cfg {Boolean} fitContainer When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container (defaults to false)
34618 * @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
34619 * @cfg {Boolean} closable True if the panel can be closed/removed
34620 * @cfg {Boolean} background True if the panel should not be activated when it is added (defaults to false)
34621 * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
34622 * @cfg {Toolbar} toolbar A toolbar for this panel
34623 * @cfg {Boolean} autoScroll True to scroll overflow in this panel (use with {@link #fitToFrame})
34624 * @cfg {String} title The title for this panel
34625 * @cfg {Array} adjustments Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
34626 * @cfg {String} url Calls {@link #setUrl} with this value
34627 * @cfg {String} region (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
34628 * @cfg {String/Object} params When used with {@link #url}, calls {@link #setUrl} with this value
34629 * @cfg {Boolean} loadOnce When used with {@link #url}, calls {@link #setUrl} with this value
34630 * @cfg {String} content Raw content to fill content panel with (uses setContent on construction.)
34633 * Create a new ContentPanel.
34634 * @param {String/HTMLElement/Roo.Element} el The container element for this panel
34635 * @param {String/Object} config A string to set only the title or a config object
34636 * @param {String} content (optional) Set the HTML content for this panel
34637 * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
34639 Roo.ContentPanel = function(el, config, content){
34643 if(el.autoCreate || el.xtype){ // xtype is available if this is called from factory
34647 if (config && config.parentLayout) {
34648 el = config.parentLayout.el.createChild();
34651 if(el.autoCreate){ // xtype is available if this is called from factory
34655 this.el = Roo.get(el);
34656 if(!this.el && config && config.autoCreate){
34657 if(typeof config.autoCreate == "object"){
34658 if(!config.autoCreate.id){
34659 config.autoCreate.id = config.id||el;
34661 this.el = Roo.DomHelper.append(document.body,
34662 config.autoCreate, true);
34664 this.el = Roo.DomHelper.append(document.body,
34665 {tag: "div", cls: "x-layout-inactive-content", id: config.id||el}, true);
34668 this.closable = false;
34669 this.loaded = false;
34670 this.active = false;
34671 if(typeof config == "string"){
34672 this.title = config;
34674 Roo.apply(this, config);
34677 if (this.toolbar && !this.toolbar.el && this.toolbar.xtype) {
34678 this.wrapEl = this.el.wrap();
34679 this.toolbar.container = this.el.insertSibling(false, 'before');
34680 this.toolbar = new Roo.Toolbar(this.toolbar);
34683 // xtype created footer. - not sure if will work as we normally have to render first..
34684 if (this.footer && !this.footer.el && this.footer.xtype) {
34685 if (!this.wrapEl) {
34686 this.wrapEl = this.el.wrap();
34689 this.footer.container = this.wrapEl.createChild();
34691 this.footer = Roo.factory(this.footer, Roo);
34696 this.resizeEl = Roo.get(this.resizeEl, true);
34698 this.resizeEl = this.el;
34700 // handle view.xtype
34708 * Fires when this panel is activated.
34709 * @param {Roo.ContentPanel} this
34713 * @event deactivate
34714 * Fires when this panel is activated.
34715 * @param {Roo.ContentPanel} this
34717 "deactivate" : true,
34721 * Fires when this panel is resized if fitToFrame is true.
34722 * @param {Roo.ContentPanel} this
34723 * @param {Number} width The width after any component adjustments
34724 * @param {Number} height The height after any component adjustments
34730 * Fires when this tab is created
34731 * @param {Roo.ContentPanel} this
34742 if(this.autoScroll){
34743 this.resizeEl.setStyle("overflow", "auto");
34745 // fix randome scrolling
34746 this.el.on('scroll', function() {
34747 Roo.log('fix random scolling');
34748 this.scrollTo('top',0);
34751 content = content || this.content;
34753 this.setContent(content);
34755 if(config && config.url){
34756 this.setUrl(this.url, this.params, this.loadOnce);
34761 Roo.ContentPanel.superclass.constructor.call(this);
34763 if (this.view && typeof(this.view.xtype) != 'undefined') {
34764 this.view.el = this.el.appendChild(document.createElement("div"));
34765 this.view = Roo.factory(this.view);
34766 this.view.render && this.view.render(false, '');
34770 this.fireEvent('render', this);
34773 Roo.extend(Roo.ContentPanel, Roo.util.Observable, {
34775 setRegion : function(region){
34776 this.region = region;
34778 this.el.replaceClass("x-layout-inactive-content", "x-layout-active-content");
34780 this.el.replaceClass("x-layout-active-content", "x-layout-inactive-content");
34785 * Returns the toolbar for this Panel if one was configured.
34786 * @return {Roo.Toolbar}
34788 getToolbar : function(){
34789 return this.toolbar;
34792 setActiveState : function(active){
34793 this.active = active;
34795 this.fireEvent("deactivate", this);
34797 this.fireEvent("activate", this);
34801 * Updates this panel's element
34802 * @param {String} content The new content
34803 * @param {Boolean} loadScripts (optional) true to look for and process scripts
34805 setContent : function(content, loadScripts){
34806 this.el.update(content, loadScripts);
34809 ignoreResize : function(w, h){
34810 if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
34813 this.lastSize = {width: w, height: h};
34818 * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
34819 * @return {Roo.UpdateManager} The UpdateManager
34821 getUpdateManager : function(){
34822 return this.el.getUpdateManager();
34825 * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
34826 * @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:
34829 url: "your-url.php",
34830 params: {param1: "foo", param2: "bar"}, // or a URL encoded string
34831 callback: yourFunction,
34832 scope: yourObject, //(optional scope)
34835 text: "Loading...",
34840 * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
34841 * 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.
34842 * @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}
34843 * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
34844 * @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.
34845 * @return {Roo.ContentPanel} this
34848 var um = this.el.getUpdateManager();
34849 um.update.apply(um, arguments);
34855 * 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.
34856 * @param {String/Function} url The URL to load the content from or a function to call to get the URL
34857 * @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)
34858 * @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)
34859 * @return {Roo.UpdateManager} The UpdateManager
34861 setUrl : function(url, params, loadOnce){
34862 if(this.refreshDelegate){
34863 this.removeListener("activate", this.refreshDelegate);
34865 this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
34866 this.on("activate", this.refreshDelegate);
34867 return this.el.getUpdateManager();
34870 _handleRefresh : function(url, params, loadOnce){
34871 if(!loadOnce || !this.loaded){
34872 var updater = this.el.getUpdateManager();
34873 updater.update(url, params, this._setLoaded.createDelegate(this));
34877 _setLoaded : function(){
34878 this.loaded = true;
34882 * Returns this panel's id
34885 getId : function(){
34890 * Returns this panel's element - used by regiosn to add.
34891 * @return {Roo.Element}
34893 getEl : function(){
34894 return this.wrapEl || this.el;
34897 adjustForComponents : function(width, height)
34899 //Roo.log('adjustForComponents ');
34900 if(this.resizeEl != this.el){
34901 width -= this.el.getFrameWidth('lr');
34902 height -= this.el.getFrameWidth('tb');
34905 var te = this.toolbar.getEl();
34906 height -= te.getHeight();
34907 te.setWidth(width);
34910 var te = this.footer.getEl();
34911 Roo.log("footer:" + te.getHeight());
34913 height -= te.getHeight();
34914 te.setWidth(width);
34918 if(this.adjustments){
34919 width += this.adjustments[0];
34920 height += this.adjustments[1];
34922 return {"width": width, "height": height};
34925 setSize : function(width, height){
34926 if(this.fitToFrame && !this.ignoreResize(width, height)){
34927 if(this.fitContainer && this.resizeEl != this.el){
34928 this.el.setSize(width, height);
34930 var size = this.adjustForComponents(width, height);
34931 this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
34932 this.fireEvent('resize', this, size.width, size.height);
34937 * Returns this panel's title
34940 getTitle : function(){
34945 * Set this panel's title
34946 * @param {String} title
34948 setTitle : function(title){
34949 this.title = title;
34951 this.region.updatePanelTitle(this, title);
34956 * Returns true is this panel was configured to be closable
34957 * @return {Boolean}
34959 isClosable : function(){
34960 return this.closable;
34963 beforeSlide : function(){
34965 this.resizeEl.clip();
34968 afterSlide : function(){
34970 this.resizeEl.unclip();
34974 * Force a content refresh from the URL specified in the {@link #setUrl} method.
34975 * Will fail silently if the {@link #setUrl} method has not been called.
34976 * This does not activate the panel, just updates its content.
34978 refresh : function(){
34979 if(this.refreshDelegate){
34980 this.loaded = false;
34981 this.refreshDelegate();
34986 * Destroys this panel
34988 destroy : function(){
34989 this.el.removeAllListeners();
34990 var tempEl = document.createElement("span");
34991 tempEl.appendChild(this.el.dom);
34992 tempEl.innerHTML = "";
34998 * form - if the content panel contains a form - this is a reference to it.
34999 * @type {Roo.form.Form}
35003 * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
35004 * This contains a reference to it.
35010 * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
35020 * @param {Object} cfg Xtype definition of item to add.
35023 addxtype : function(cfg) {
35025 if (cfg.xtype.match(/^Form$/)) {
35028 //if (this.footer) {
35029 // el = this.footer.container.insertSibling(false, 'before');
35031 el = this.el.createChild();
35034 this.form = new Roo.form.Form(cfg);
35037 if ( this.form.allItems.length) this.form.render(el.dom);
35040 // should only have one of theses..
35041 if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
35042 // views.. should not be just added - used named prop 'view''
35044 cfg.el = this.el.appendChild(document.createElement("div"));
35047 var ret = new Roo.factory(cfg);
35049 ret.render && ret.render(false, ''); // render blank..
35058 * @class Roo.GridPanel
35059 * @extends Roo.ContentPanel
35061 * Create a new GridPanel.
35062 * @param {Roo.grid.Grid} grid The grid for this panel
35063 * @param {String/Object} config A string to set only the panel's title, or a config object
35065 Roo.GridPanel = function(grid, config){
35068 this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
35069 {tag: "div", cls: "x-layout-grid-wrapper x-layout-inactive-content"}, true);
35071 this.wrapper.dom.appendChild(grid.getGridEl().dom);
35073 Roo.GridPanel.superclass.constructor.call(this, this.wrapper, config);
35076 this.toolbar.el.insertBefore(this.wrapper.dom.firstChild);
35078 // xtype created footer. - not sure if will work as we normally have to render first..
35079 if (this.footer && !this.footer.el && this.footer.xtype) {
35081 this.footer.container = this.grid.getView().getFooterPanel(true);
35082 this.footer.dataSource = this.grid.dataSource;
35083 this.footer = Roo.factory(this.footer, Roo);
35087 grid.monitorWindowResize = false; // turn off autosizing
35088 grid.autoHeight = false;
35089 grid.autoWidth = false;
35091 this.grid.getGridEl().replaceClass("x-layout-inactive-content", "x-layout-component-panel");
35094 Roo.extend(Roo.GridPanel, Roo.ContentPanel, {
35095 getId : function(){
35096 return this.grid.id;
35100 * Returns the grid for this panel
35101 * @return {Roo.grid.Grid}
35103 getGrid : function(){
35107 setSize : function(width, height){
35108 if(!this.ignoreResize(width, height)){
35109 var grid = this.grid;
35110 var size = this.adjustForComponents(width, height);
35111 grid.getGridEl().setSize(size.width, size.height);
35116 beforeSlide : function(){
35117 this.grid.getView().scroller.clip();
35120 afterSlide : function(){
35121 this.grid.getView().scroller.unclip();
35124 destroy : function(){
35125 this.grid.destroy();
35127 Roo.GridPanel.superclass.destroy.call(this);
35133 * @class Roo.NestedLayoutPanel
35134 * @extends Roo.ContentPanel
35136 * Create a new NestedLayoutPanel.
35139 * @param {Roo.BorderLayout} layout The layout for this panel
35140 * @param {String/Object} config A string to set only the title or a config object
35142 Roo.NestedLayoutPanel = function(layout, config)
35144 // construct with only one argument..
35145 /* FIXME - implement nicer consturctors
35146 if (layout.layout) {
35148 layout = config.layout;
35149 delete config.layout;
35151 if (layout.xtype && !layout.getEl) {
35152 // then layout needs constructing..
35153 layout = Roo.factory(layout, Roo);
35158 Roo.NestedLayoutPanel.superclass.constructor.call(this, layout.getEl(), config);
35160 layout.monitorWindowResize = false; // turn off autosizing
35161 this.layout = layout;
35162 this.layout.getEl().addClass("x-layout-nested-layout");
35169 Roo.extend(Roo.NestedLayoutPanel, Roo.ContentPanel, {
35171 setSize : function(width, height){
35172 if(!this.ignoreResize(width, height)){
35173 var size = this.adjustForComponents(width, height);
35174 var el = this.layout.getEl();
35175 el.setSize(size.width, size.height);
35176 var touch = el.dom.offsetWidth;
35177 this.layout.layout();
35178 // ie requires a double layout on the first pass
35179 if(Roo.isIE && !this.initialized){
35180 this.initialized = true;
35181 this.layout.layout();
35186 // activate all subpanels if not currently active..
35188 setActiveState : function(active){
35189 this.active = active;
35191 this.fireEvent("deactivate", this);
35195 this.fireEvent("activate", this);
35196 // not sure if this should happen before or after..
35197 if (!this.layout) {
35198 return; // should not happen..
35201 for (var r in this.layout.regions) {
35202 reg = this.layout.getRegion(r);
35203 if (reg.getActivePanel()) {
35204 //reg.showPanel(reg.getActivePanel()); // force it to activate..
35205 reg.setActivePanel(reg.getActivePanel());
35208 if (!reg.panels.length) {
35211 reg.showPanel(reg.getPanel(0));
35220 * Returns the nested BorderLayout for this panel
35221 * @return {Roo.BorderLayout}
35223 getLayout : function(){
35224 return this.layout;
35228 * Adds a xtype elements to the layout of the nested panel
35232 xtype : 'ContentPanel',
35239 xtype : 'NestedLayoutPanel',
35245 items : [ ... list of content panels or nested layout panels.. ]
35249 * @param {Object} cfg Xtype definition of item to add.
35251 addxtype : function(cfg) {
35252 return this.layout.addxtype(cfg);
35257 Roo.ScrollPanel = function(el, config, content){
35258 config = config || {};
35259 config.fitToFrame = true;
35260 Roo.ScrollPanel.superclass.constructor.call(this, el, config, content);
35262 this.el.dom.style.overflow = "hidden";
35263 var wrap = this.el.wrap({cls: "x-scroller x-layout-inactive-content"});
35264 this.el.removeClass("x-layout-inactive-content");
35265 this.el.on("mousewheel", this.onWheel, this);
35267 var up = wrap.createChild({cls: "x-scroller-up", html: " "}, this.el.dom);
35268 var down = wrap.createChild({cls: "x-scroller-down", html: " "});
35269 up.unselectable(); down.unselectable();
35270 up.on("click", this.scrollUp, this);
35271 down.on("click", this.scrollDown, this);
35272 up.addClassOnOver("x-scroller-btn-over");
35273 down.addClassOnOver("x-scroller-btn-over");
35274 up.addClassOnClick("x-scroller-btn-click");
35275 down.addClassOnClick("x-scroller-btn-click");
35276 this.adjustments = [0, -(up.getHeight() + down.getHeight())];
35278 this.resizeEl = this.el;
35279 this.el = wrap; this.up = up; this.down = down;
35282 Roo.extend(Roo.ScrollPanel, Roo.ContentPanel, {
35284 wheelIncrement : 5,
35285 scrollUp : function(){
35286 this.resizeEl.scroll("up", this.increment, {callback: this.afterScroll, scope: this});
35289 scrollDown : function(){
35290 this.resizeEl.scroll("down", this.increment, {callback: this.afterScroll, scope: this});
35293 afterScroll : function(){
35294 var el = this.resizeEl;
35295 var t = el.dom.scrollTop, h = el.dom.scrollHeight, ch = el.dom.clientHeight;
35296 this.up[t == 0 ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
35297 this.down[h - t <= ch ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
35300 setSize : function(){
35301 Roo.ScrollPanel.superclass.setSize.apply(this, arguments);
35302 this.afterScroll();
35305 onWheel : function(e){
35306 var d = e.getWheelDelta();
35307 this.resizeEl.dom.scrollTop -= (d*this.wheelIncrement);
35308 this.afterScroll();
35312 setContent : function(content, loadScripts){
35313 this.resizeEl.update(content, loadScripts);
35327 * @class Roo.TreePanel
35328 * @extends Roo.ContentPanel
35330 * Create a new TreePanel. - defaults to fit/scoll contents.
35331 * @param {String/Object} config A string to set only the panel's title, or a config object
35332 * @cfg {Roo.tree.TreePanel} tree The tree TreePanel, with config etc.
35334 Roo.TreePanel = function(config){
35335 var el = config.el;
35336 var tree = config.tree;
35337 delete config.tree;
35338 delete config.el; // hopefull!
35340 // wrapper for IE7 strict & safari scroll issue
35342 var treeEl = el.createChild();
35343 config.resizeEl = treeEl;
35347 Roo.TreePanel.superclass.constructor.call(this, el, config);
35350 this.tree = new Roo.tree.TreePanel(treeEl , tree);
35351 //console.log(tree);
35352 this.on('activate', function()
35354 if (this.tree.rendered) {
35357 //console.log('render tree');
35358 this.tree.render();
35360 // this should not be needed.. - it's actually the 'el' that resizes?
35361 // actuall it breaks the containerScroll - dragging nodes auto scroll at top
35363 //this.on('resize', function (cp, w, h) {
35364 // this.tree.innerCt.setWidth(w);
35365 // this.tree.innerCt.setHeight(h);
35366 // //this.tree.innerCt.setStyle('overflow-y', 'auto');
35373 Roo.extend(Roo.TreePanel, Roo.ContentPanel, {
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">
35401 * @class Roo.ReaderLayout
35402 * @extends Roo.BorderLayout
35403 * This is a pre-built layout that represents a classic, 5-pane application. It consists of a header, a primary
35404 * center region containing two nested regions (a top one for a list view and one for item preview below),
35405 * and regions on either side that can be used for navigation, application commands, informational displays, etc.
35406 * The setup and configuration work exactly the same as it does for a {@link Roo.BorderLayout} - this class simply
35407 * expedites the setup of the overall layout and regions for this common application style.
35410 var reader = new Roo.ReaderLayout();
35411 var CP = Roo.ContentPanel; // shortcut for adding
35413 reader.beginUpdate();
35414 reader.add("north", new CP("north", "North"));
35415 reader.add("west", new CP("west", {title: "West"}));
35416 reader.add("east", new CP("east", {title: "East"}));
35418 reader.regions.listView.add(new CP("listView", "List"));
35419 reader.regions.preview.add(new CP("preview", "Preview"));
35420 reader.endUpdate();
35423 * Create a new ReaderLayout
35424 * @param {Object} config Configuration options
35425 * @param {String/HTMLElement/Element} container (optional) The container this layout is bound to (defaults to
35426 * document.body if omitted)
35428 Roo.ReaderLayout = function(config, renderTo){
35429 var c = config || {size:{}};
35430 Roo.ReaderLayout.superclass.constructor.call(this, renderTo || document.body, {
35431 north: c.north !== false ? Roo.apply({
35435 }, c.north) : false,
35436 west: c.west !== false ? Roo.apply({
35444 margins:{left:5,right:0,bottom:5,top:5},
35445 cmargins:{left:5,right:5,bottom:5,top:5}
35446 }, c.west) : false,
35447 east: c.east !== false ? Roo.apply({
35455 margins:{left:0,right:5,bottom:5,top:5},
35456 cmargins:{left:5,right:5,bottom:5,top:5}
35457 }, c.east) : false,
35458 center: Roo.apply({
35459 tabPosition: 'top',
35463 margins:{left:c.west!==false ? 0 : 5,right:c.east!==false ? 0 : 5,bottom:5,top:2}
35467 this.el.addClass('x-reader');
35469 this.beginUpdate();
35471 var inner = new Roo.BorderLayout(Roo.get(document.body).createChild(), {
35472 south: c.preview !== false ? Roo.apply({
35479 cmargins:{top:5,left:0, right:0, bottom:0}
35480 }, c.preview) : false,
35481 center: Roo.apply({
35487 this.add('center', new Roo.NestedLayoutPanel(inner,
35488 Roo.apply({title: c.mainTitle || '',tabTip:''},c.innerPanelCfg)));
35492 this.regions.preview = inner.getRegion('south');
35493 this.regions.listView = inner.getRegion('center');
35496 Roo.extend(Roo.ReaderLayout, Roo.BorderLayout);/*
35498 * Ext JS Library 1.1.1
35499 * Copyright(c) 2006-2007, Ext JS, LLC.
35501 * Originally Released Under LGPL - original licence link has changed is not relivant.
35504 * <script type="text/javascript">
35508 * @class Roo.grid.Grid
35509 * @extends Roo.util.Observable
35510 * This class represents the primary interface of a component based grid control.
35511 * <br><br>Usage:<pre><code>
35512 var grid = new Roo.grid.Grid("my-container-id", {
35515 selModel: mySelectionModel,
35516 autoSizeColumns: true,
35517 monitorWindowResize: false,
35518 trackMouseOver: true
35523 * <b>Common Problems:</b><br/>
35524 * - Grid does not resize properly when going smaller: Setting overflow hidden on the container
35525 * element will correct this<br/>
35526 * - If you get el.style[camel]= NaNpx or -2px or something related, be certain you have given your container element
35527 * dimensions. The grid adapts to your container's size, if your container has no size defined then the results
35528 * are unpredictable.<br/>
35529 * - Do not render the grid into an element with display:none. Try using visibility:hidden. Otherwise there is no way for the
35530 * grid to calculate dimensions/offsets.<br/>
35532 * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
35533 * The container MUST have some type of size defined for the grid to fill. The container will be
35534 * automatically set to position relative if it isn't already.
35535 * @param {Object} config A config object that sets properties on this grid.
35537 Roo.grid.Grid = function(container, config){
35538 // initialize the container
35539 this.container = Roo.get(container);
35540 this.container.update("");
35541 this.container.setStyle("overflow", "hidden");
35542 this.container.addClass('x-grid-container');
35544 this.id = this.container.id;
35546 Roo.apply(this, config);
35547 // check and correct shorthanded configs
35549 this.dataSource = this.ds;
35553 this.colModel = this.cm;
35557 this.selModel = this.sm;
35561 if (this.selModel) {
35562 this.selModel = Roo.factory(this.selModel, Roo.grid);
35563 this.sm = this.selModel;
35564 this.sm.xmodule = this.xmodule || false;
35566 if (typeof(this.colModel.config) == 'undefined') {
35567 this.colModel = new Roo.grid.ColumnModel(this.colModel);
35568 this.cm = this.colModel;
35569 this.cm.xmodule = this.xmodule || false;
35571 if (this.dataSource) {
35572 this.dataSource= Roo.factory(this.dataSource, Roo.data);
35573 this.ds = this.dataSource;
35574 this.ds.xmodule = this.xmodule || false;
35581 this.container.setWidth(this.width);
35585 this.container.setHeight(this.height);
35592 * The raw click event for the entire grid.
35593 * @param {Roo.EventObject} e
35598 * The raw dblclick event for the entire grid.
35599 * @param {Roo.EventObject} e
35603 * @event contextmenu
35604 * The raw contextmenu event for the entire grid.
35605 * @param {Roo.EventObject} e
35607 "contextmenu" : true,
35610 * The raw mousedown event for the entire grid.
35611 * @param {Roo.EventObject} e
35613 "mousedown" : true,
35616 * The raw mouseup event for the entire grid.
35617 * @param {Roo.EventObject} e
35622 * The raw mouseover event for the entire grid.
35623 * @param {Roo.EventObject} e
35625 "mouseover" : true,
35628 * The raw mouseout event for the entire grid.
35629 * @param {Roo.EventObject} e
35634 * The raw keypress event for the entire grid.
35635 * @param {Roo.EventObject} e
35640 * The raw keydown event for the entire grid.
35641 * @param {Roo.EventObject} e
35649 * Fires when a cell is clicked
35650 * @param {Grid} this
35651 * @param {Number} rowIndex
35652 * @param {Number} columnIndex
35653 * @param {Roo.EventObject} e
35655 "cellclick" : true,
35657 * @event celldblclick
35658 * Fires when a cell is double clicked
35659 * @param {Grid} this
35660 * @param {Number} rowIndex
35661 * @param {Number} columnIndex
35662 * @param {Roo.EventObject} e
35664 "celldblclick" : true,
35667 * Fires when a row is clicked
35668 * @param {Grid} this
35669 * @param {Number} rowIndex
35670 * @param {Roo.EventObject} e
35674 * @event rowdblclick
35675 * Fires when a row is double clicked
35676 * @param {Grid} this
35677 * @param {Number} rowIndex
35678 * @param {Roo.EventObject} e
35680 "rowdblclick" : true,
35682 * @event headerclick
35683 * Fires when a header is clicked
35684 * @param {Grid} this
35685 * @param {Number} columnIndex
35686 * @param {Roo.EventObject} e
35688 "headerclick" : true,
35690 * @event headerdblclick
35691 * Fires when a header cell is double clicked
35692 * @param {Grid} this
35693 * @param {Number} columnIndex
35694 * @param {Roo.EventObject} e
35696 "headerdblclick" : true,
35698 * @event rowcontextmenu
35699 * Fires when a row is right clicked
35700 * @param {Grid} this
35701 * @param {Number} rowIndex
35702 * @param {Roo.EventObject} e
35704 "rowcontextmenu" : true,
35706 * @event cellcontextmenu
35707 * Fires when a cell is right clicked
35708 * @param {Grid} this
35709 * @param {Number} rowIndex
35710 * @param {Number} cellIndex
35711 * @param {Roo.EventObject} e
35713 "cellcontextmenu" : true,
35715 * @event headercontextmenu
35716 * Fires when a header is right clicked
35717 * @param {Grid} this
35718 * @param {Number} columnIndex
35719 * @param {Roo.EventObject} e
35721 "headercontextmenu" : true,
35723 * @event bodyscroll
35724 * Fires when the body element is scrolled
35725 * @param {Number} scrollLeft
35726 * @param {Number} scrollTop
35728 "bodyscroll" : true,
35730 * @event columnresize
35731 * Fires when the user resizes a column
35732 * @param {Number} columnIndex
35733 * @param {Number} newSize
35735 "columnresize" : true,
35737 * @event columnmove
35738 * Fires when the user moves a column
35739 * @param {Number} oldIndex
35740 * @param {Number} newIndex
35742 "columnmove" : true,
35745 * Fires when row(s) start being dragged
35746 * @param {Grid} this
35747 * @param {Roo.GridDD} dd The drag drop object
35748 * @param {event} e The raw browser event
35750 "startdrag" : true,
35753 * Fires when a drag operation is complete
35754 * @param {Grid} this
35755 * @param {Roo.GridDD} dd The drag drop object
35756 * @param {event} e The raw browser event
35761 * Fires when dragged row(s) are dropped on a valid DD target
35762 * @param {Grid} this
35763 * @param {Roo.GridDD} dd The drag drop object
35764 * @param {String} targetId The target drag drop object
35765 * @param {event} e The raw browser event
35770 * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
35771 * @param {Grid} this
35772 * @param {Roo.GridDD} dd The drag drop object
35773 * @param {String} targetId The target drag drop object
35774 * @param {event} e The raw browser event
35779 * Fires when the dragged row(s) first cross another DD target while being dragged
35780 * @param {Grid} this
35781 * @param {Roo.GridDD} dd The drag drop object
35782 * @param {String} targetId The target drag drop object
35783 * @param {event} e The raw browser event
35785 "dragenter" : true,
35788 * Fires when the dragged row(s) leave another DD target while being dragged
35789 * @param {Grid} this
35790 * @param {Roo.GridDD} dd The drag drop object
35791 * @param {String} targetId The target drag drop object
35792 * @param {event} e The raw browser event
35797 * Fires when a row is rendered, so you can change add a style to it.
35798 * @param {GridView} gridview The grid view
35799 * @param {Object} rowcfg contains record rowIndex and rowClass - set rowClass to add a style.
35805 * Fires when the grid is rendered
35806 * @param {Grid} grid
35811 Roo.grid.Grid.superclass.constructor.call(this);
35813 Roo.extend(Roo.grid.Grid, Roo.util.Observable, {
35816 * @cfg {String} ddGroup - drag drop group.
35820 * @cfg {Number} minColumnWidth The minimum width a column can be resized to. Default is 25.
35822 minColumnWidth : 25,
35825 * @cfg {Boolean} autoSizeColumns True to automatically resize the columns to fit their content
35826 * <b>on initial render.</b> It is more efficient to explicitly size the columns
35827 * through the ColumnModel's {@link Roo.grid.ColumnModel#width} config option. Default is false.
35829 autoSizeColumns : false,
35832 * @cfg {Boolean} autoSizeHeaders True to measure headers with column data when auto sizing columns. Default is true.
35834 autoSizeHeaders : true,
35837 * @cfg {Boolean} monitorWindowResize True to autoSize the grid when the window resizes. Default is true.
35839 monitorWindowResize : true,
35842 * @cfg {Boolean} maxRowsToMeasure If autoSizeColumns is on, maxRowsToMeasure can be used to limit the number of
35843 * rows measured to get a columns size. Default is 0 (all rows).
35845 maxRowsToMeasure : 0,
35848 * @cfg {Boolean} trackMouseOver True to highlight rows when the mouse is over. Default is true.
35850 trackMouseOver : true,
35853 * @cfg {Boolean} enableDrag True to enable drag of rows. Default is false. (double check if this is needed?)
35857 * @cfg {Boolean} enableDragDrop True to enable drag and drop of rows. Default is false.
35859 enableDragDrop : false,
35862 * @cfg {Boolean} enableColumnMove True to enable drag and drop reorder of columns. Default is true.
35864 enableColumnMove : true,
35867 * @cfg {Boolean} enableColumnHide True to enable hiding of columns with the header context menu. Default is true.
35869 enableColumnHide : true,
35872 * @cfg {Boolean} enableRowHeightSync True to manually sync row heights across locked and not locked rows. Default is false.
35874 enableRowHeightSync : false,
35877 * @cfg {Boolean} stripeRows True to stripe the rows. Default is true.
35882 * @cfg {Boolean} autoHeight True to fit the height of the grid container to the height of the data. Default is false.
35884 autoHeight : false,
35887 * @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.
35889 autoExpandColumn : false,
35892 * @cfg {Number} autoExpandMin The minimum width the autoExpandColumn can have (if enabled).
35895 autoExpandMin : 50,
35898 * @cfg {Number} autoExpandMax The maximum width the autoExpandColumn can have (if enabled). Default is 1000.
35900 autoExpandMax : 1000,
35903 * @cfg {Object} view The {@link Roo.grid.GridView} used by the grid. This can be set before a call to render().
35908 * @cfg {Object} loadMask An {@link Roo.LoadMask} config or true to mask the grid while loading. Default is false.
35912 * @cfg {Roo.dd.DropTarget} dropTarget An {@link Roo.dd.DropTarget} config
35922 * @cfg {Boolean} autoWidth True to set the grid's width to the default total width of the grid's columns instead
35923 * of a fixed width. Default is false.
35926 * @cfg {Number} maxHeight Sets the maximum height of the grid - ignored if autoHeight is not on.
35929 * Called once after all setup has been completed and the grid is ready to be rendered.
35930 * @return {Roo.grid.Grid} this
35932 render : function()
35934 var c = this.container;
35935 // try to detect autoHeight/width mode
35936 if((!c.dom.offsetHeight || c.dom.offsetHeight < 20) || c.getStyle("height") == "auto"){
35937 this.autoHeight = true;
35939 var view = this.getView();
35942 c.on("click", this.onClick, this);
35943 c.on("dblclick", this.onDblClick, this);
35944 c.on("contextmenu", this.onContextMenu, this);
35945 c.on("keydown", this.onKeyDown, this);
35947 c.on("touchstart", this.onTouchStart, this);
35950 this.relayEvents(c, ["mousedown","mouseup","mouseover","mouseout","keypress"]);
35952 this.getSelectionModel().init(this);
35957 this.loadMask = new Roo.LoadMask(this.container,
35958 Roo.apply({store:this.dataSource}, this.loadMask));
35962 if (this.toolbar && this.toolbar.xtype) {
35963 this.toolbar.container = this.getView().getHeaderPanel(true);
35964 this.toolbar = new Roo.Toolbar(this.toolbar);
35966 if (this.footer && this.footer.xtype) {
35967 this.footer.dataSource = this.getDataSource();
35968 this.footer.container = this.getView().getFooterPanel(true);
35969 this.footer = Roo.factory(this.footer, Roo);
35971 if (this.dropTarget && this.dropTarget.xtype) {
35972 delete this.dropTarget.xtype;
35973 this.dropTarget = new Roo.dd.DropTarget(this.getView().mainBody, this.dropTarget);
35977 this.rendered = true;
35978 this.fireEvent('render', this);
35983 * Reconfigures the grid to use a different Store and Column Model.
35984 * The View will be bound to the new objects and refreshed.
35985 * @param {Roo.data.Store} dataSource The new {@link Roo.data.Store} object
35986 * @param {Roo.grid.ColumnModel} The new {@link Roo.grid.ColumnModel} object
35988 reconfigure : function(dataSource, colModel){
35990 this.loadMask.destroy();
35991 this.loadMask = new Roo.LoadMask(this.container,
35992 Roo.apply({store:dataSource}, this.loadMask));
35994 this.view.bind(dataSource, colModel);
35995 this.dataSource = dataSource;
35996 this.colModel = colModel;
35997 this.view.refresh(true);
36001 onKeyDown : function(e){
36002 this.fireEvent("keydown", e);
36006 * Destroy this grid.
36007 * @param {Boolean} removeEl True to remove the element
36009 destroy : function(removeEl, keepListeners){
36011 this.loadMask.destroy();
36013 var c = this.container;
36014 c.removeAllListeners();
36015 this.view.destroy();
36016 this.colModel.purgeListeners();
36017 if(!keepListeners){
36018 this.purgeListeners();
36021 if(removeEl === true){
36027 processEvent : function(name, e){
36028 // does this fire select???
36029 Roo.log('grid:processEvent ' + name);
36031 if (name != 'touchstart' ) {
36032 this.fireEvent(name, e);
36035 var t = e.getTarget();
36037 var header = v.findHeaderIndex(t);
36038 if(header !== false){
36039 var ename = name == 'touchstart' ? 'click' : name;
36041 this.fireEvent("header" + ename, this, header, e);
36043 var row = v.findRowIndex(t);
36044 var cell = v.findCellIndex(t);
36045 if (name == 'touchstart') {
36046 // first touch is always a click.
36047 // hopefull this happens after selection is updated.?
36050 if (typeof(this.selModel.getSelectedCell) != 'undefined') {
36051 var cs = this.selModel.getSelectedCell();
36052 if (row == cs[0] && cell == cs[1]){
36056 if (typeof(this.selModel.getSelections) != 'undefined') {
36057 var cs = this.selModel.getSelections();
36058 var ds = this.dataSource;
36059 if (cs.length == 1 && ds.getAt(row) == cs[0]){
36070 this.fireEvent("row" + name, this, row, e);
36071 if(cell !== false){
36072 this.fireEvent("cell" + name, this, row, cell, e);
36079 onClick : function(e){
36080 this.processEvent("click", e);
36083 onTouchStart : function(e){
36084 this.processEvent("touchstart", e);
36088 onContextMenu : function(e, t){
36089 this.processEvent("contextmenu", e);
36093 onDblClick : function(e){
36094 this.processEvent("dblclick", e);
36098 walkCells : function(row, col, step, fn, scope){
36099 var cm = this.colModel, clen = cm.getColumnCount();
36100 var ds = this.dataSource, rlen = ds.getCount(), first = true;
36112 if(fn.call(scope || this, row, col, cm) === true){
36130 if(fn.call(scope || this, row, col, cm) === true){
36142 getSelections : function(){
36143 return this.selModel.getSelections();
36147 * Causes the grid to manually recalculate its dimensions. Generally this is done automatically,
36148 * but if manual update is required this method will initiate it.
36150 autoSize : function(){
36152 this.view.layout();
36153 if(this.view.adjustForScroll){
36154 this.view.adjustForScroll();
36160 * Returns the grid's underlying element.
36161 * @return {Element} The element
36163 getGridEl : function(){
36164 return this.container;
36167 // private for compatibility, overridden by editor grid
36168 stopEditing : function(){},
36171 * Returns the grid's SelectionModel.
36172 * @return {SelectionModel}
36174 getSelectionModel : function(){
36175 if(!this.selModel){
36176 this.selModel = new Roo.grid.RowSelectionModel();
36178 return this.selModel;
36182 * Returns the grid's DataSource.
36183 * @return {DataSource}
36185 getDataSource : function(){
36186 return this.dataSource;
36190 * Returns the grid's ColumnModel.
36191 * @return {ColumnModel}
36193 getColumnModel : function(){
36194 return this.colModel;
36198 * Returns the grid's GridView object.
36199 * @return {GridView}
36201 getView : function(){
36203 this.view = new Roo.grid.GridView(this.viewConfig);
36208 * Called to get grid's drag proxy text, by default returns this.ddText.
36211 getDragDropText : function(){
36212 var count = this.selModel.getCount();
36213 return String.format(this.ddText, count, count == 1 ? '' : 's');
36217 * Configures the text is the drag proxy (defaults to "%0 selected row(s)").
36218 * %0 is replaced with the number of selected rows.
36221 Roo.grid.Grid.prototype.ddText = "{0} selected row{1}";/*
36223 * Ext JS Library 1.1.1
36224 * Copyright(c) 2006-2007, Ext JS, LLC.
36226 * Originally Released Under LGPL - original licence link has changed is not relivant.
36229 * <script type="text/javascript">
36232 Roo.grid.AbstractGridView = function(){
36236 "beforerowremoved" : true,
36237 "beforerowsinserted" : true,
36238 "beforerefresh" : true,
36239 "rowremoved" : true,
36240 "rowsinserted" : true,
36241 "rowupdated" : true,
36244 Roo.grid.AbstractGridView.superclass.constructor.call(this);
36247 Roo.extend(Roo.grid.AbstractGridView, Roo.util.Observable, {
36248 rowClass : "x-grid-row",
36249 cellClass : "x-grid-cell",
36250 tdClass : "x-grid-td",
36251 hdClass : "x-grid-hd",
36252 splitClass : "x-grid-hd-split",
36254 init: function(grid){
36256 var cid = this.grid.getGridEl().id;
36257 this.colSelector = "#" + cid + " ." + this.cellClass + "-";
36258 this.tdSelector = "#" + cid + " ." + this.tdClass + "-";
36259 this.hdSelector = "#" + cid + " ." + this.hdClass + "-";
36260 this.splitSelector = "#" + cid + " ." + this.splitClass + "-";
36263 getColumnRenderers : function(){
36264 var renderers = [];
36265 var cm = this.grid.colModel;
36266 var colCount = cm.getColumnCount();
36267 for(var i = 0; i < colCount; i++){
36268 renderers[i] = cm.getRenderer(i);
36273 getColumnIds : function(){
36275 var cm = this.grid.colModel;
36276 var colCount = cm.getColumnCount();
36277 for(var i = 0; i < colCount; i++){
36278 ids[i] = cm.getColumnId(i);
36283 getDataIndexes : function(){
36284 if(!this.indexMap){
36285 this.indexMap = this.buildIndexMap();
36287 return this.indexMap.colToData;
36290 getColumnIndexByDataIndex : function(dataIndex){
36291 if(!this.indexMap){
36292 this.indexMap = this.buildIndexMap();
36294 return this.indexMap.dataToCol[dataIndex];
36298 * Set a css style for a column dynamically.
36299 * @param {Number} colIndex The index of the column
36300 * @param {String} name The css property name
36301 * @param {String} value The css value
36303 setCSSStyle : function(colIndex, name, value){
36304 var selector = "#" + this.grid.id + " .x-grid-col-" + colIndex;
36305 Roo.util.CSS.updateRule(selector, name, value);
36308 generateRules : function(cm){
36309 var ruleBuf = [], rulesId = this.grid.id + '-cssrules';
36310 Roo.util.CSS.removeStyleSheet(rulesId);
36311 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
36312 var cid = cm.getColumnId(i);
36313 ruleBuf.push(this.colSelector, cid, " {\n", cm.config[i].css, "}\n",
36314 this.tdSelector, cid, " {\n}\n",
36315 this.hdSelector, cid, " {\n}\n",
36316 this.splitSelector, cid, " {\n}\n");
36318 return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
36322 * Ext JS Library 1.1.1
36323 * Copyright(c) 2006-2007, Ext JS, LLC.
36325 * Originally Released Under LGPL - original licence link has changed is not relivant.
36328 * <script type="text/javascript">
36332 // This is a support class used internally by the Grid components
36333 Roo.grid.HeaderDragZone = function(grid, hd, hd2){
36335 this.view = grid.getView();
36336 this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
36337 Roo.grid.HeaderDragZone.superclass.constructor.call(this, hd);
36339 this.setHandleElId(Roo.id(hd));
36340 this.setOuterHandleElId(Roo.id(hd2));
36342 this.scroll = false;
36344 Roo.extend(Roo.grid.HeaderDragZone, Roo.dd.DragZone, {
36346 getDragData : function(e){
36347 var t = Roo.lib.Event.getTarget(e);
36348 var h = this.view.findHeaderCell(t);
36350 return {ddel: h.firstChild, header:h};
36355 onInitDrag : function(e){
36356 this.view.headersDisabled = true;
36357 var clone = this.dragData.ddel.cloneNode(true);
36358 clone.id = Roo.id();
36359 clone.style.width = Math.min(this.dragData.header.offsetWidth,this.maxDragWidth) + "px";
36360 this.proxy.update(clone);
36364 afterValidDrop : function(){
36366 setTimeout(function(){
36367 v.headersDisabled = false;
36371 afterInvalidDrop : function(){
36373 setTimeout(function(){
36374 v.headersDisabled = false;
36380 * Ext JS Library 1.1.1
36381 * Copyright(c) 2006-2007, Ext JS, LLC.
36383 * Originally Released Under LGPL - original licence link has changed is not relivant.
36386 * <script type="text/javascript">
36389 // This is a support class used internally by the Grid components
36390 Roo.grid.HeaderDropZone = function(grid, hd, hd2){
36392 this.view = grid.getView();
36393 // split the proxies so they don't interfere with mouse events
36394 this.proxyTop = Roo.DomHelper.append(document.body, {
36395 cls:"col-move-top", html:" "
36397 this.proxyBottom = Roo.DomHelper.append(document.body, {
36398 cls:"col-move-bottom", html:" "
36400 this.proxyTop.hide = this.proxyBottom.hide = function(){
36401 this.setLeftTop(-100,-100);
36402 this.setStyle("visibility", "hidden");
36404 this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
36405 // temporarily disabled
36406 //Roo.dd.ScrollManager.register(this.view.scroller.dom);
36407 Roo.grid.HeaderDropZone.superclass.constructor.call(this, grid.getGridEl().dom);
36409 Roo.extend(Roo.grid.HeaderDropZone, Roo.dd.DropZone, {
36410 proxyOffsets : [-4, -9],
36411 fly: Roo.Element.fly,
36413 getTargetFromEvent : function(e){
36414 var t = Roo.lib.Event.getTarget(e);
36415 var cindex = this.view.findCellIndex(t);
36416 if(cindex !== false){
36417 return this.view.getHeaderCell(cindex);
36422 nextVisible : function(h){
36423 var v = this.view, cm = this.grid.colModel;
36426 if(!cm.isHidden(v.getCellIndex(h))){
36434 prevVisible : function(h){
36435 var v = this.view, cm = this.grid.colModel;
36438 if(!cm.isHidden(v.getCellIndex(h))){
36446 positionIndicator : function(h, n, e){
36447 var x = Roo.lib.Event.getPageX(e);
36448 var r = Roo.lib.Dom.getRegion(n.firstChild);
36449 var px, pt, py = r.top + this.proxyOffsets[1];
36450 if((r.right - x) <= (r.right-r.left)/2){
36451 px = r.right+this.view.borderWidth;
36457 var oldIndex = this.view.getCellIndex(h);
36458 var newIndex = this.view.getCellIndex(n);
36460 if(this.grid.colModel.isFixed(newIndex)){
36464 var locked = this.grid.colModel.isLocked(newIndex);
36469 if(oldIndex < newIndex){
36472 if(oldIndex == newIndex && (locked == this.grid.colModel.isLocked(oldIndex))){
36475 px += this.proxyOffsets[0];
36476 this.proxyTop.setLeftTop(px, py);
36477 this.proxyTop.show();
36478 if(!this.bottomOffset){
36479 this.bottomOffset = this.view.mainHd.getHeight();
36481 this.proxyBottom.setLeftTop(px, py+this.proxyTop.dom.offsetHeight+this.bottomOffset);
36482 this.proxyBottom.show();
36486 onNodeEnter : function(n, dd, e, data){
36487 if(data.header != n){
36488 this.positionIndicator(data.header, n, e);
36492 onNodeOver : function(n, dd, e, data){
36493 var result = false;
36494 if(data.header != n){
36495 result = this.positionIndicator(data.header, n, e);
36498 this.proxyTop.hide();
36499 this.proxyBottom.hide();
36501 return result ? this.dropAllowed : this.dropNotAllowed;
36504 onNodeOut : function(n, dd, e, data){
36505 this.proxyTop.hide();
36506 this.proxyBottom.hide();
36509 onNodeDrop : function(n, dd, e, data){
36510 var h = data.header;
36512 var cm = this.grid.colModel;
36513 var x = Roo.lib.Event.getPageX(e);
36514 var r = Roo.lib.Dom.getRegion(n.firstChild);
36515 var pt = (r.right - x) <= ((r.right-r.left)/2) ? "after" : "before";
36516 var oldIndex = this.view.getCellIndex(h);
36517 var newIndex = this.view.getCellIndex(n);
36518 var locked = cm.isLocked(newIndex);
36522 if(oldIndex < newIndex){
36525 if(oldIndex == newIndex && (locked == cm.isLocked(oldIndex))){
36528 cm.setLocked(oldIndex, locked, true);
36529 cm.moveColumn(oldIndex, newIndex);
36530 this.grid.fireEvent("columnmove", oldIndex, newIndex);
36538 * Ext JS Library 1.1.1
36539 * Copyright(c) 2006-2007, Ext JS, LLC.
36541 * Originally Released Under LGPL - original licence link has changed is not relivant.
36544 * <script type="text/javascript">
36548 * @class Roo.grid.GridView
36549 * @extends Roo.util.Observable
36552 * @param {Object} config
36554 Roo.grid.GridView = function(config){
36555 Roo.grid.GridView.superclass.constructor.call(this);
36558 Roo.apply(this, config);
36561 Roo.extend(Roo.grid.GridView, Roo.grid.AbstractGridView, {
36563 unselectable : 'unselectable="on"',
36564 unselectableCls : 'x-unselectable',
36567 rowClass : "x-grid-row",
36569 cellClass : "x-grid-col",
36571 tdClass : "x-grid-td",
36573 hdClass : "x-grid-hd",
36575 splitClass : "x-grid-split",
36577 sortClasses : ["sort-asc", "sort-desc"],
36579 enableMoveAnim : false,
36583 dh : Roo.DomHelper,
36585 fly : Roo.Element.fly,
36587 css : Roo.util.CSS,
36593 scrollIncrement : 22,
36595 cellRE: /(?:.*?)x-grid-(?:hd|cell|csplit)-(?:[\d]+)-([\d]+)(?:.*?)/,
36597 findRE: /\s?(?:x-grid-hd|x-grid-col|x-grid-csplit)\s/,
36599 bind : function(ds, cm){
36601 this.ds.un("load", this.onLoad, this);
36602 this.ds.un("datachanged", this.onDataChange, this);
36603 this.ds.un("add", this.onAdd, this);
36604 this.ds.un("remove", this.onRemove, this);
36605 this.ds.un("update", this.onUpdate, this);
36606 this.ds.un("clear", this.onClear, this);
36609 ds.on("load", this.onLoad, this);
36610 ds.on("datachanged", this.onDataChange, this);
36611 ds.on("add", this.onAdd, this);
36612 ds.on("remove", this.onRemove, this);
36613 ds.on("update", this.onUpdate, this);
36614 ds.on("clear", this.onClear, this);
36619 this.cm.un("widthchange", this.onColWidthChange, this);
36620 this.cm.un("headerchange", this.onHeaderChange, this);
36621 this.cm.un("hiddenchange", this.onHiddenChange, this);
36622 this.cm.un("columnmoved", this.onColumnMove, this);
36623 this.cm.un("columnlockchange", this.onColumnLock, this);
36626 this.generateRules(cm);
36627 cm.on("widthchange", this.onColWidthChange, this);
36628 cm.on("headerchange", this.onHeaderChange, this);
36629 cm.on("hiddenchange", this.onHiddenChange, this);
36630 cm.on("columnmoved", this.onColumnMove, this);
36631 cm.on("columnlockchange", this.onColumnLock, this);
36636 init: function(grid){
36637 Roo.grid.GridView.superclass.init.call(this, grid);
36639 this.bind(grid.dataSource, grid.colModel);
36641 grid.on("headerclick", this.handleHeaderClick, this);
36643 if(grid.trackMouseOver){
36644 grid.on("mouseover", this.onRowOver, this);
36645 grid.on("mouseout", this.onRowOut, this);
36647 grid.cancelTextSelection = function(){};
36648 this.gridId = grid.id;
36650 var tpls = this.templates || {};
36653 tpls.master = new Roo.Template(
36654 '<div class="x-grid" hidefocus="true">',
36655 '<a href="#" class="x-grid-focus" tabIndex="-1"></a>',
36656 '<div class="x-grid-topbar"></div>',
36657 '<div class="x-grid-scroller"><div></div></div>',
36658 '<div class="x-grid-locked">',
36659 '<div class="x-grid-header">{lockedHeader}</div>',
36660 '<div class="x-grid-body">{lockedBody}</div>',
36662 '<div class="x-grid-viewport">',
36663 '<div class="x-grid-header">{header}</div>',
36664 '<div class="x-grid-body">{body}</div>',
36666 '<div class="x-grid-bottombar"></div>',
36668 '<div class="x-grid-resize-proxy"> </div>',
36671 tpls.master.disableformats = true;
36675 tpls.header = new Roo.Template(
36676 '<table border="0" cellspacing="0" cellpadding="0">',
36677 '<tbody><tr class="x-grid-hd-row">{cells}</tr></tbody>',
36680 tpls.header.disableformats = true;
36682 tpls.header.compile();
36685 tpls.hcell = new Roo.Template(
36686 '<td class="x-grid-hd x-grid-td-{id} {cellId}"><div title="{title}" class="x-grid-hd-inner x-grid-hd-{id}">',
36687 '<div class="x-grid-hd-text ' + this.unselectableCls + '" ' + this.unselectable +'>{value}<img class="x-grid-sort-icon" src="', Roo.BLANK_IMAGE_URL, '" /></div>',
36690 tpls.hcell.disableFormats = true;
36692 tpls.hcell.compile();
36695 tpls.hsplit = new Roo.Template('<div class="x-grid-split {splitId} x-grid-split-{id}" style="{style} ' +
36696 this.unselectableCls + '" ' + this.unselectable +'> </div>');
36697 tpls.hsplit.disableFormats = true;
36699 tpls.hsplit.compile();
36702 tpls.body = new Roo.Template(
36703 '<table border="0" cellspacing="0" cellpadding="0">',
36704 "<tbody>{rows}</tbody>",
36707 tpls.body.disableFormats = true;
36709 tpls.body.compile();
36712 tpls.row = new Roo.Template('<tr class="x-grid-row {alt}">{cells}</tr>');
36713 tpls.row.disableFormats = true;
36715 tpls.row.compile();
36718 tpls.cell = new Roo.Template(
36719 '<td class="x-grid-col x-grid-td-{id} {cellId} {css}" tabIndex="0">',
36720 '<div class="x-grid-col-{id} x-grid-cell-inner"><div class="x-grid-cell-text ' +
36721 this.unselectableCls + '" ' + this.unselectable +'" {attr}>{value}</div></div>',
36724 tpls.cell.disableFormats = true;
36726 tpls.cell.compile();
36728 this.templates = tpls;
36731 // remap these for backwards compat
36732 onColWidthChange : function(){
36733 this.updateColumns.apply(this, arguments);
36735 onHeaderChange : function(){
36736 this.updateHeaders.apply(this, arguments);
36738 onHiddenChange : function(){
36739 this.handleHiddenChange.apply(this, arguments);
36741 onColumnMove : function(){
36742 this.handleColumnMove.apply(this, arguments);
36744 onColumnLock : function(){
36745 this.handleLockChange.apply(this, arguments);
36748 onDataChange : function(){
36750 this.updateHeaderSortState();
36753 onClear : function(){
36757 onUpdate : function(ds, record){
36758 this.refreshRow(record);
36761 refreshRow : function(record){
36762 var ds = this.ds, index;
36763 if(typeof record == 'number'){
36765 record = ds.getAt(index);
36767 index = ds.indexOf(record);
36769 this.insertRows(ds, index, index, true);
36770 this.onRemove(ds, record, index+1, true);
36771 this.syncRowHeights(index, index);
36773 this.fireEvent("rowupdated", this, index, record);
36776 onAdd : function(ds, records, index){
36777 this.insertRows(ds, index, index + (records.length-1));
36780 onRemove : function(ds, record, index, isUpdate){
36781 if(isUpdate !== true){
36782 this.fireEvent("beforerowremoved", this, index, record);
36784 var bt = this.getBodyTable(), lt = this.getLockedTable();
36785 if(bt.rows[index]){
36786 bt.firstChild.removeChild(bt.rows[index]);
36788 if(lt.rows[index]){
36789 lt.firstChild.removeChild(lt.rows[index]);
36791 if(isUpdate !== true){
36792 this.stripeRows(index);
36793 this.syncRowHeights(index, index);
36795 this.fireEvent("rowremoved", this, index, record);
36799 onLoad : function(){
36800 this.scrollToTop();
36804 * Scrolls the grid to the top
36806 scrollToTop : function(){
36808 this.scroller.dom.scrollTop = 0;
36814 * Gets a panel in the header of the grid that can be used for toolbars etc.
36815 * After modifying the contents of this panel a call to grid.autoSize() may be
36816 * required to register any changes in size.
36817 * @param {Boolean} doShow By default the header is hidden. Pass true to show the panel
36818 * @return Roo.Element
36820 getHeaderPanel : function(doShow){
36822 this.headerPanel.show();
36824 return this.headerPanel;
36828 * Gets a panel in the footer of the grid that can be used for toolbars etc.
36829 * After modifying the contents of this panel a call to grid.autoSize() may be
36830 * required to register any changes in size.
36831 * @param {Boolean} doShow By default the footer is hidden. Pass true to show the panel
36832 * @return Roo.Element
36834 getFooterPanel : function(doShow){
36836 this.footerPanel.show();
36838 return this.footerPanel;
36841 initElements : function(){
36842 var E = Roo.Element;
36843 var el = this.grid.getGridEl().dom.firstChild;
36844 var cs = el.childNodes;
36846 this.el = new E(el);
36848 this.focusEl = new E(el.firstChild);
36849 this.focusEl.swallowEvent("click", true);
36851 this.headerPanel = new E(cs[1]);
36852 this.headerPanel.enableDisplayMode("block");
36854 this.scroller = new E(cs[2]);
36855 this.scrollSizer = new E(this.scroller.dom.firstChild);
36857 this.lockedWrap = new E(cs[3]);
36858 this.lockedHd = new E(this.lockedWrap.dom.firstChild);
36859 this.lockedBody = new E(this.lockedWrap.dom.childNodes[1]);
36861 this.mainWrap = new E(cs[4]);
36862 this.mainHd = new E(this.mainWrap.dom.firstChild);
36863 this.mainBody = new E(this.mainWrap.dom.childNodes[1]);
36865 this.footerPanel = new E(cs[5]);
36866 this.footerPanel.enableDisplayMode("block");
36868 this.resizeProxy = new E(cs[6]);
36870 this.headerSelector = String.format(
36871 '#{0} td.x-grid-hd, #{1} td.x-grid-hd',
36872 this.lockedHd.id, this.mainHd.id
36875 this.splitterSelector = String.format(
36876 '#{0} div.x-grid-split, #{1} div.x-grid-split',
36877 this.idToCssName(this.lockedHd.id), this.idToCssName(this.mainHd.id)
36880 idToCssName : function(s)
36882 return s.replace(/[^a-z0-9]+/ig, '-');
36885 getHeaderCell : function(index){
36886 return Roo.DomQuery.select(this.headerSelector)[index];
36889 getHeaderCellMeasure : function(index){
36890 return this.getHeaderCell(index).firstChild;
36893 getHeaderCellText : function(index){
36894 return this.getHeaderCell(index).firstChild.firstChild;
36897 getLockedTable : function(){
36898 return this.lockedBody.dom.firstChild;
36901 getBodyTable : function(){
36902 return this.mainBody.dom.firstChild;
36905 getLockedRow : function(index){
36906 return this.getLockedTable().rows[index];
36909 getRow : function(index){
36910 return this.getBodyTable().rows[index];
36913 getRowComposite : function(index){
36915 this.rowEl = new Roo.CompositeElementLite();
36917 var els = [], lrow, mrow;
36918 if(lrow = this.getLockedRow(index)){
36921 if(mrow = this.getRow(index)){
36924 this.rowEl.elements = els;
36928 * Gets the 'td' of the cell
36930 * @param {Integer} rowIndex row to select
36931 * @param {Integer} colIndex column to select
36935 getCell : function(rowIndex, colIndex){
36936 var locked = this.cm.getLockedCount();
36938 if(colIndex < locked){
36939 source = this.lockedBody.dom.firstChild;
36941 source = this.mainBody.dom.firstChild;
36942 colIndex -= locked;
36944 return source.rows[rowIndex].childNodes[colIndex];
36947 getCellText : function(rowIndex, colIndex){
36948 return this.getCell(rowIndex, colIndex).firstChild.firstChild;
36951 getCellBox : function(cell){
36952 var b = this.fly(cell).getBox();
36953 if(Roo.isOpera){ // opera fails to report the Y
36954 b.y = cell.offsetTop + this.mainBody.getY();
36959 getCellIndex : function(cell){
36960 var id = String(cell.className).match(this.cellRE);
36962 return parseInt(id[1], 10);
36967 findHeaderIndex : function(n){
36968 var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
36969 return r ? this.getCellIndex(r) : false;
36972 findHeaderCell : function(n){
36973 var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
36974 return r ? r : false;
36977 findRowIndex : function(n){
36981 var r = Roo.fly(n).findParent("tr." + this.rowClass, 6);
36982 return r ? r.rowIndex : false;
36985 findCellIndex : function(node){
36986 var stop = this.el.dom;
36987 while(node && node != stop){
36988 if(this.findRE.test(node.className)){
36989 return this.getCellIndex(node);
36991 node = node.parentNode;
36996 getColumnId : function(index){
36997 return this.cm.getColumnId(index);
37000 getSplitters : function()
37002 if(this.splitterSelector){
37003 return Roo.DomQuery.select(this.splitterSelector);
37009 getSplitter : function(index){
37010 return this.getSplitters()[index];
37013 onRowOver : function(e, t){
37015 if((row = this.findRowIndex(t)) !== false){
37016 this.getRowComposite(row).addClass("x-grid-row-over");
37020 onRowOut : function(e, t){
37022 if((row = this.findRowIndex(t)) !== false && row !== this.findRowIndex(e.getRelatedTarget())){
37023 this.getRowComposite(row).removeClass("x-grid-row-over");
37027 renderHeaders : function(){
37029 var ct = this.templates.hcell, ht = this.templates.header, st = this.templates.hsplit;
37030 var cb = [], lb = [], sb = [], lsb = [], p = {};
37031 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
37032 p.cellId = "x-grid-hd-0-" + i;
37033 p.splitId = "x-grid-csplit-0-" + i;
37034 p.id = cm.getColumnId(i);
37035 p.title = cm.getColumnTooltip(i) || "";
37036 p.value = cm.getColumnHeader(i) || "";
37037 p.style = (this.grid.enableColumnResize === false || !cm.isResizable(i) || cm.isFixed(i)) ? 'cursor:default' : '';
37038 if(!cm.isLocked(i)){
37039 cb[cb.length] = ct.apply(p);
37040 sb[sb.length] = st.apply(p);
37042 lb[lb.length] = ct.apply(p);
37043 lsb[lsb.length] = st.apply(p);
37046 return [ht.apply({cells: lb.join(""), splits:lsb.join("")}),
37047 ht.apply({cells: cb.join(""), splits:sb.join("")})];
37050 updateHeaders : function(){
37051 var html = this.renderHeaders();
37052 this.lockedHd.update(html[0]);
37053 this.mainHd.update(html[1]);
37057 * Focuses the specified row.
37058 * @param {Number} row The row index
37060 focusRow : function(row)
37062 //Roo.log('GridView.focusRow');
37063 var x = this.scroller.dom.scrollLeft;
37064 this.focusCell(row, 0, false);
37065 this.scroller.dom.scrollLeft = x;
37069 * Focuses the specified cell.
37070 * @param {Number} row The row index
37071 * @param {Number} col The column index
37072 * @param {Boolean} hscroll false to disable horizontal scrolling
37074 focusCell : function(row, col, hscroll)
37076 //Roo.log('GridView.focusCell');
37077 var el = this.ensureVisible(row, col, hscroll);
37078 this.focusEl.alignTo(el, "tl-tl");
37080 this.focusEl.focus();
37082 this.focusEl.focus.defer(1, this.focusEl);
37087 * Scrolls the specified cell into view
37088 * @param {Number} row The row index
37089 * @param {Number} col The column index
37090 * @param {Boolean} hscroll false to disable horizontal scrolling
37092 ensureVisible : function(row, col, hscroll)
37094 //Roo.log('GridView.ensureVisible,' + row + ',' + col);
37095 //return null; //disable for testing.
37096 if(typeof row != "number"){
37097 row = row.rowIndex;
37099 if(row < 0 && row >= this.ds.getCount()){
37102 col = (col !== undefined ? col : 0);
37103 var cm = this.grid.colModel;
37104 while(cm.isHidden(col)){
37108 var el = this.getCell(row, col);
37112 var c = this.scroller.dom;
37114 var ctop = parseInt(el.offsetTop, 10);
37115 var cleft = parseInt(el.offsetLeft, 10);
37116 var cbot = ctop + el.offsetHeight;
37117 var cright = cleft + el.offsetWidth;
37119 var ch = c.clientHeight - this.mainHd.dom.offsetHeight;
37120 var stop = parseInt(c.scrollTop, 10);
37121 var sleft = parseInt(c.scrollLeft, 10);
37122 var sbot = stop + ch;
37123 var sright = sleft + c.clientWidth;
37125 Roo.log('GridView.ensureVisible:' +
37127 ' c.clientHeight:' + c.clientHeight +
37128 ' this.mainHd.dom.offsetHeight:' + this.mainHd.dom.offsetHeight +
37136 c.scrollTop = ctop;
37137 //Roo.log("set scrolltop to ctop DISABLE?");
37138 }else if(cbot > sbot){
37139 //Roo.log("set scrolltop to cbot-ch");
37140 c.scrollTop = cbot-ch;
37143 if(hscroll !== false){
37145 c.scrollLeft = cleft;
37146 }else if(cright > sright){
37147 c.scrollLeft = cright-c.clientWidth;
37154 updateColumns : function(){
37155 this.grid.stopEditing();
37156 var cm = this.grid.colModel, colIds = this.getColumnIds();
37157 //var totalWidth = cm.getTotalWidth();
37159 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
37160 //if(cm.isHidden(i)) continue;
37161 var w = cm.getColumnWidth(i);
37162 this.css.updateRule(this.colSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
37163 this.css.updateRule(this.hdSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
37165 this.updateSplitters();
37168 generateRules : function(cm){
37169 var ruleBuf = [], rulesId = this.idToCssName(this.grid.id)+ '-cssrules';
37170 Roo.util.CSS.removeStyleSheet(rulesId);
37171 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
37172 var cid = cm.getColumnId(i);
37174 if(cm.config[i].align){
37175 align = 'text-align:'+cm.config[i].align+';';
37178 if(cm.isHidden(i)){
37179 hidden = 'display:none;';
37181 var width = "width:" + (cm.getColumnWidth(i) - this.borderWidth) + "px;";
37183 this.colSelector, cid, " {\n", cm.config[i].css, align, width, "\n}\n",
37184 this.hdSelector, cid, " {\n", align, width, "}\n",
37185 this.tdSelector, cid, " {\n",hidden,"\n}\n",
37186 this.splitSelector, cid, " {\n", hidden , "\n}\n");
37188 return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
37191 updateSplitters : function(){
37192 var cm = this.cm, s = this.getSplitters();
37193 if(s){ // splitters not created yet
37194 var pos = 0, locked = true;
37195 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
37196 if(cm.isHidden(i)) continue;
37197 var w = cm.getColumnWidth(i); // make sure it's a number
37198 if(!cm.isLocked(i) && locked){
37203 s[i].style.left = (pos-this.splitOffset) + "px";
37208 handleHiddenChange : function(colModel, colIndex, hidden){
37210 this.hideColumn(colIndex);
37212 this.unhideColumn(colIndex);
37216 hideColumn : function(colIndex){
37217 var cid = this.getColumnId(colIndex);
37218 this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "none");
37219 this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "none");
37221 this.updateHeaders();
37223 this.updateSplitters();
37227 unhideColumn : function(colIndex){
37228 var cid = this.getColumnId(colIndex);
37229 this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "");
37230 this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "");
37233 this.updateHeaders();
37235 this.updateSplitters();
37239 insertRows : function(dm, firstRow, lastRow, isUpdate){
37240 if(firstRow == 0 && lastRow == dm.getCount()-1){
37244 this.fireEvent("beforerowsinserted", this, firstRow, lastRow);
37246 var s = this.getScrollState();
37247 var markup = this.renderRows(firstRow, lastRow);
37248 this.bufferRows(markup[0], this.getLockedTable(), firstRow);
37249 this.bufferRows(markup[1], this.getBodyTable(), firstRow);
37250 this.restoreScroll(s);
37252 this.fireEvent("rowsinserted", this, firstRow, lastRow);
37253 this.syncRowHeights(firstRow, lastRow);
37254 this.stripeRows(firstRow);
37260 bufferRows : function(markup, target, index){
37261 var before = null, trows = target.rows, tbody = target.tBodies[0];
37262 if(index < trows.length){
37263 before = trows[index];
37265 var b = document.createElement("div");
37266 b.innerHTML = "<table><tbody>"+markup+"</tbody></table>";
37267 var rows = b.firstChild.rows;
37268 for(var i = 0, len = rows.length; i < len; i++){
37270 tbody.insertBefore(rows[0], before);
37272 tbody.appendChild(rows[0]);
37279 deleteRows : function(dm, firstRow, lastRow){
37280 if(dm.getRowCount()<1){
37281 this.fireEvent("beforerefresh", this);
37282 this.mainBody.update("");
37283 this.lockedBody.update("");
37284 this.fireEvent("refresh", this);
37286 this.fireEvent("beforerowsdeleted", this, firstRow, lastRow);
37287 var bt = this.getBodyTable();
37288 var tbody = bt.firstChild;
37289 var rows = bt.rows;
37290 for(var rowIndex = firstRow; rowIndex <= lastRow; rowIndex++){
37291 tbody.removeChild(rows[firstRow]);
37293 this.stripeRows(firstRow);
37294 this.fireEvent("rowsdeleted", this, firstRow, lastRow);
37298 updateRows : function(dataSource, firstRow, lastRow){
37299 var s = this.getScrollState();
37301 this.restoreScroll(s);
37304 handleSort : function(dataSource, sortColumnIndex, sortDir, noRefresh){
37308 this.updateHeaderSortState();
37311 getScrollState : function(){
37313 var sb = this.scroller.dom;
37314 return {left: sb.scrollLeft, top: sb.scrollTop};
37317 stripeRows : function(startRow){
37318 if(!this.grid.stripeRows || this.ds.getCount() < 1){
37321 startRow = startRow || 0;
37322 var rows = this.getBodyTable().rows;
37323 var lrows = this.getLockedTable().rows;
37324 var cls = ' x-grid-row-alt ';
37325 for(var i = startRow, len = rows.length; i < len; i++){
37326 var row = rows[i], lrow = lrows[i];
37327 var isAlt = ((i+1) % 2 == 0);
37328 var hasAlt = (' '+row.className + ' ').indexOf(cls) != -1;
37329 if(isAlt == hasAlt){
37333 row.className += " x-grid-row-alt";
37335 row.className = row.className.replace("x-grid-row-alt", "");
37338 lrow.className = row.className;
37343 restoreScroll : function(state){
37344 //Roo.log('GridView.restoreScroll');
37345 var sb = this.scroller.dom;
37346 sb.scrollLeft = state.left;
37347 sb.scrollTop = state.top;
37351 syncScroll : function(){
37352 //Roo.log('GridView.syncScroll');
37353 var sb = this.scroller.dom;
37354 var sh = this.mainHd.dom;
37355 var bs = this.mainBody.dom;
37356 var lv = this.lockedBody.dom;
37357 sh.scrollLeft = bs.scrollLeft = sb.scrollLeft;
37358 lv.scrollTop = bs.scrollTop = sb.scrollTop;
37361 handleScroll : function(e){
37363 var sb = this.scroller.dom;
37364 this.grid.fireEvent("bodyscroll", sb.scrollLeft, sb.scrollTop);
37368 handleWheel : function(e){
37369 var d = e.getWheelDelta();
37370 this.scroller.dom.scrollTop -= d*22;
37371 // set this here to prevent jumpy scrolling on large tables
37372 this.lockedBody.dom.scrollTop = this.mainBody.dom.scrollTop = this.scroller.dom.scrollTop;
37376 renderRows : function(startRow, endRow){
37377 // pull in all the crap needed to render rows
37378 var g = this.grid, cm = g.colModel, ds = g.dataSource, stripe = g.stripeRows;
37379 var colCount = cm.getColumnCount();
37381 if(ds.getCount() < 1){
37385 // build a map for all the columns
37387 for(var i = 0; i < colCount; i++){
37388 var name = cm.getDataIndex(i);
37390 name : typeof name == 'undefined' ? ds.fields.get(i).name : name,
37391 renderer : cm.getRenderer(i),
37392 id : cm.getColumnId(i),
37393 locked : cm.isLocked(i)
37397 startRow = startRow || 0;
37398 endRow = typeof endRow == "undefined"? ds.getCount()-1 : endRow;
37400 // records to render
37401 var rs = ds.getRange(startRow, endRow);
37403 return this.doRender(cs, rs, ds, startRow, colCount, stripe);
37406 // As much as I hate to duplicate code, this was branched because FireFox really hates
37407 // [].join("") on strings. The performance difference was substantial enough to
37408 // branch this function
37409 doRender : Roo.isGecko ?
37410 function(cs, rs, ds, startRow, colCount, stripe){
37411 var ts = this.templates, ct = ts.cell, rt = ts.row;
37413 var buf = "", lbuf = "", cb, lcb, c, p = {}, rp = {}, r, rowIndex;
37415 var hasListener = this.grid.hasListener('rowclass');
37417 for(var j = 0, len = rs.length; j < len; j++){
37418 r = rs[j]; cb = ""; lcb = ""; rowIndex = (j+startRow);
37419 for(var i = 0; i < colCount; i++){
37421 p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
37423 p.css = p.attr = "";
37424 p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
37425 if(p.value == undefined || p.value === "") p.value = " ";
37426 if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
37427 p.css += p.css ? ' x-grid-dirty-cell' : 'x-grid-dirty-cell';
37429 var markup = ct.apply(p);
37437 if(stripe && ((rowIndex+1) % 2 == 0)){
37438 alt.push("x-grid-row-alt")
37441 alt.push( " x-grid-dirty-row");
37444 if(this.getRowClass){
37445 alt.push(this.getRowClass(r, rowIndex));
37451 rowIndex : rowIndex,
37454 this.grid.fireEvent('rowclass', this, rowcfg);
37455 alt.push(rowcfg.rowClass);
37457 rp.alt = alt.join(" ");
37458 lbuf+= rt.apply(rp);
37460 buf+= rt.apply(rp);
37462 return [lbuf, buf];
37464 function(cs, rs, ds, startRow, colCount, stripe){
37465 var ts = this.templates, ct = ts.cell, rt = ts.row;
37467 var buf = [], lbuf = [], cb, lcb, c, p = {}, rp = {}, r, rowIndex;
37468 var hasListener = this.grid.hasListener('rowclass');
37471 for(var j = 0, len = rs.length; j < len; j++){
37472 r = rs[j]; cb = []; lcb = []; rowIndex = (j+startRow);
37473 for(var i = 0; i < colCount; i++){
37475 p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
37477 p.css = p.attr = "";
37478 p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
37479 if(p.value == undefined || p.value === "") p.value = " ";
37480 if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
37481 p.css += p.css ? ' x-grid-dirty-cell' : 'x-grid-dirty-cell';
37484 var markup = ct.apply(p);
37486 cb[cb.length] = markup;
37488 lcb[lcb.length] = markup;
37492 if(stripe && ((rowIndex+1) % 2 == 0)){
37493 alt.push( "x-grid-row-alt");
37496 alt.push(" x-grid-dirty-row");
37499 if(this.getRowClass){
37500 alt.push( this.getRowClass(r, rowIndex));
37506 rowIndex : rowIndex,
37509 this.grid.fireEvent('rowclass', this, rowcfg);
37510 alt.push(rowcfg.rowClass);
37512 rp.alt = alt.join(" ");
37513 rp.cells = lcb.join("");
37514 lbuf[lbuf.length] = rt.apply(rp);
37515 rp.cells = cb.join("");
37516 buf[buf.length] = rt.apply(rp);
37518 return [lbuf.join(""), buf.join("")];
37521 renderBody : function(){
37522 var markup = this.renderRows();
37523 var bt = this.templates.body;
37524 return [bt.apply({rows: markup[0]}), bt.apply({rows: markup[1]})];
37528 * Refreshes the grid
37529 * @param {Boolean} headersToo
37531 refresh : function(headersToo){
37532 this.fireEvent("beforerefresh", this);
37533 this.grid.stopEditing();
37534 var result = this.renderBody();
37535 this.lockedBody.update(result[0]);
37536 this.mainBody.update(result[1]);
37537 if(headersToo === true){
37538 this.updateHeaders();
37539 this.updateColumns();
37540 this.updateSplitters();
37541 this.updateHeaderSortState();
37543 this.syncRowHeights();
37545 this.fireEvent("refresh", this);
37548 handleColumnMove : function(cm, oldIndex, newIndex){
37549 this.indexMap = null;
37550 var s = this.getScrollState();
37551 this.refresh(true);
37552 this.restoreScroll(s);
37553 this.afterMove(newIndex);
37556 afterMove : function(colIndex){
37557 if(this.enableMoveAnim && Roo.enableFx){
37558 this.fly(this.getHeaderCell(colIndex).firstChild).highlight(this.hlColor);
37560 // if multisort - fix sortOrder, and reload..
37561 if (this.grid.dataSource.multiSort) {
37562 // the we can call sort again..
37563 var dm = this.grid.dataSource;
37564 var cm = this.grid.colModel;
37566 for(var i = 0; i < cm.config.length; i++ ) {
37568 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined')) {
37569 continue; // dont' bother, it's not in sort list or being set.
37572 so.push(cm.config[i].dataIndex);
37575 dm.load(dm.lastOptions);
37582 updateCell : function(dm, rowIndex, dataIndex){
37583 var colIndex = this.getColumnIndexByDataIndex(dataIndex);
37584 if(typeof colIndex == "undefined"){ // not present in grid
37587 var cm = this.grid.colModel;
37588 var cell = this.getCell(rowIndex, colIndex);
37589 var cellText = this.getCellText(rowIndex, colIndex);
37592 cellId : "x-grid-cell-" + rowIndex + "-" + colIndex,
37593 id : cm.getColumnId(colIndex),
37594 css: colIndex == cm.getColumnCount()-1 ? "x-grid-col-last" : ""
37596 var renderer = cm.getRenderer(colIndex);
37597 var val = renderer(dm.getValueAt(rowIndex, dataIndex), p, rowIndex, colIndex, dm);
37598 if(typeof val == "undefined" || val === "") val = " ";
37599 cellText.innerHTML = val;
37600 cell.className = this.cellClass + " " + this.idToCssName(p.cellId) + " " + p.css;
37601 this.syncRowHeights(rowIndex, rowIndex);
37604 calcColumnWidth : function(colIndex, maxRowsToMeasure){
37606 if(this.grid.autoSizeHeaders){
37607 var h = this.getHeaderCellMeasure(colIndex);
37608 maxWidth = Math.max(maxWidth, h.scrollWidth);
37611 if(this.cm.isLocked(colIndex)){
37612 tb = this.getLockedTable();
37615 tb = this.getBodyTable();
37616 index = colIndex - this.cm.getLockedCount();
37619 var rows = tb.rows;
37620 var stopIndex = Math.min(maxRowsToMeasure || rows.length, rows.length);
37621 for(var i = 0; i < stopIndex; i++){
37622 var cell = rows[i].childNodes[index].firstChild;
37623 maxWidth = Math.max(maxWidth, cell.scrollWidth);
37626 return maxWidth + /*margin for error in IE*/ 5;
37629 * Autofit a column to its content.
37630 * @param {Number} colIndex
37631 * @param {Boolean} forceMinSize true to force the column to go smaller if possible
37633 autoSizeColumn : function(colIndex, forceMinSize, suppressEvent){
37634 if(this.cm.isHidden(colIndex)){
37635 return; // can't calc a hidden column
37638 var cid = this.cm.getColumnId(colIndex);
37639 this.css.updateRule(this.colSelector +this.idToCssName( cid), "width", this.grid.minColumnWidth + "px");
37640 if(this.grid.autoSizeHeaders){
37641 this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", this.grid.minColumnWidth + "px");
37644 var newWidth = this.calcColumnWidth(colIndex);
37645 this.cm.setColumnWidth(colIndex,
37646 Math.max(this.grid.minColumnWidth, newWidth), suppressEvent);
37647 if(!suppressEvent){
37648 this.grid.fireEvent("columnresize", colIndex, newWidth);
37653 * Autofits all columns to their content and then expands to fit any extra space in the grid
37655 autoSizeColumns : function(){
37656 var cm = this.grid.colModel;
37657 var colCount = cm.getColumnCount();
37658 for(var i = 0; i < colCount; i++){
37659 this.autoSizeColumn(i, true, true);
37661 if(cm.getTotalWidth() < this.scroller.dom.clientWidth){
37664 this.updateColumns();
37670 * Autofits all columns to the grid's width proportionate with their current size
37671 * @param {Boolean} reserveScrollSpace Reserve space for a scrollbar
37673 fitColumns : function(reserveScrollSpace){
37674 var cm = this.grid.colModel;
37675 var colCount = cm.getColumnCount();
37679 for (i = 0; i < colCount; i++){
37680 if(!cm.isHidden(i) && !cm.isFixed(i)){
37681 w = cm.getColumnWidth(i);
37687 var avail = Math.min(this.scroller.dom.clientWidth, this.el.getWidth());
37688 if(reserveScrollSpace){
37691 var frac = (avail - cm.getTotalWidth())/width;
37692 while (cols.length){
37695 cm.setColumnWidth(i, Math.floor(w + w*frac), true);
37697 this.updateColumns();
37701 onRowSelect : function(rowIndex){
37702 var row = this.getRowComposite(rowIndex);
37703 row.addClass("x-grid-row-selected");
37706 onRowDeselect : function(rowIndex){
37707 var row = this.getRowComposite(rowIndex);
37708 row.removeClass("x-grid-row-selected");
37711 onCellSelect : function(row, col){
37712 var cell = this.getCell(row, col);
37714 Roo.fly(cell).addClass("x-grid-cell-selected");
37718 onCellDeselect : function(row, col){
37719 var cell = this.getCell(row, col);
37721 Roo.fly(cell).removeClass("x-grid-cell-selected");
37725 updateHeaderSortState : function(){
37727 // sort state can be single { field: xxx, direction : yyy}
37728 // or { xxx=>ASC , yyy : DESC ..... }
37731 if (!this.ds.multiSort) {
37732 var state = this.ds.getSortState();
37736 mstate[state.field] = state.direction;
37737 // FIXME... - this is not used here.. but might be elsewhere..
37738 this.sortState = state;
37741 mstate = this.ds.sortToggle;
37743 //remove existing sort classes..
37745 var sc = this.sortClasses;
37746 var hds = this.el.select(this.headerSelector).removeClass(sc);
37748 for(var f in mstate) {
37750 var sortColumn = this.cm.findColumnIndex(f);
37752 if(sortColumn != -1){
37753 var sortDir = mstate[f];
37754 hds.item(sortColumn).addClass(sc[sortDir == "DESC" ? 1 : 0]);
37763 handleHeaderClick : function(g, index,e){
37765 Roo.log("header click");
37768 // touch events on header are handled by context
37769 this.handleHdCtx(g,index,e);
37774 if(this.headersDisabled){
37777 var dm = g.dataSource, cm = g.colModel;
37778 if(!cm.isSortable(index)){
37783 if (dm.multiSort) {
37784 // update the sortOrder
37786 for(var i = 0; i < cm.config.length; i++ ) {
37788 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined') && (index != i)) {
37789 continue; // dont' bother, it's not in sort list or being set.
37792 so.push(cm.config[i].dataIndex);
37798 dm.sort(cm.getDataIndex(index));
37802 destroy : function(){
37804 this.colMenu.removeAll();
37805 Roo.menu.MenuMgr.unregister(this.colMenu);
37806 this.colMenu.getEl().remove();
37807 delete this.colMenu;
37810 this.hmenu.removeAll();
37811 Roo.menu.MenuMgr.unregister(this.hmenu);
37812 this.hmenu.getEl().remove();
37815 if(this.grid.enableColumnMove){
37816 var dds = Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
37818 for(var dd in dds){
37819 if(!dds[dd].config.isTarget && dds[dd].dragElId){
37820 var elid = dds[dd].dragElId;
37822 Roo.get(elid).remove();
37823 } else if(dds[dd].config.isTarget){
37824 dds[dd].proxyTop.remove();
37825 dds[dd].proxyBottom.remove();
37828 if(Roo.dd.DDM.locationCache[dd]){
37829 delete Roo.dd.DDM.locationCache[dd];
37832 delete Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
37835 Roo.util.CSS.removeStyleSheet(this.idToCssName(this.grid.id) + '-cssrules');
37836 this.bind(null, null);
37837 Roo.EventManager.removeResizeListener(this.onWindowResize, this);
37840 handleLockChange : function(){
37841 this.refresh(true);
37844 onDenyColumnLock : function(){
37848 onDenyColumnHide : function(){
37852 handleHdMenuClick : function(item){
37853 var index = this.hdCtxIndex;
37854 var cm = this.cm, ds = this.ds;
37857 ds.sort(cm.getDataIndex(index), "ASC");
37860 ds.sort(cm.getDataIndex(index), "DESC");
37863 var lc = cm.getLockedCount();
37864 if(cm.getColumnCount(true) <= lc+1){
37865 this.onDenyColumnLock();
37869 cm.setLocked(index, true, true);
37870 cm.moveColumn(index, lc);
37871 this.grid.fireEvent("columnmove", index, lc);
37873 cm.setLocked(index, true);
37877 var lc = cm.getLockedCount();
37878 if((lc-1) != index){
37879 cm.setLocked(index, false, true);
37880 cm.moveColumn(index, lc-1);
37881 this.grid.fireEvent("columnmove", index, lc-1);
37883 cm.setLocked(index, false);
37886 case 'wider': // used to expand cols on touch..
37888 var cw = cm.getColumnWidth(index);
37889 cw += (item.id == 'wider' ? 1 : -1) * 50;
37890 cw = Math.max(0, cw);
37891 cw = Math.min(cw,4000);
37892 cm.setColumnWidth(index, cw);
37896 index = cm.getIndexById(item.id.substr(4));
37898 if(item.checked && cm.getColumnCount(true) <= 1){
37899 this.onDenyColumnHide();
37902 cm.setHidden(index, item.checked);
37908 beforeColMenuShow : function(){
37909 var cm = this.cm, colCount = cm.getColumnCount();
37910 this.colMenu.removeAll();
37911 for(var i = 0; i < colCount; i++){
37912 this.colMenu.add(new Roo.menu.CheckItem({
37913 id: "col-"+cm.getColumnId(i),
37914 text: cm.getColumnHeader(i),
37915 checked: !cm.isHidden(i),
37921 handleHdCtx : function(g, index, e){
37923 var hd = this.getHeaderCell(index);
37924 this.hdCtxIndex = index;
37925 var ms = this.hmenu.items, cm = this.cm;
37926 ms.get("asc").setDisabled(!cm.isSortable(index));
37927 ms.get("desc").setDisabled(!cm.isSortable(index));
37928 if(this.grid.enableColLock !== false){
37929 ms.get("lock").setDisabled(cm.isLocked(index));
37930 ms.get("unlock").setDisabled(!cm.isLocked(index));
37932 this.hmenu.show(hd, "tl-bl");
37935 handleHdOver : function(e){
37936 var hd = this.findHeaderCell(e.getTarget());
37937 if(hd && !this.headersDisabled){
37938 if(this.grid.colModel.isSortable(this.getCellIndex(hd))){
37939 this.fly(hd).addClass("x-grid-hd-over");
37944 handleHdOut : function(e){
37945 var hd = this.findHeaderCell(e.getTarget());
37947 this.fly(hd).removeClass("x-grid-hd-over");
37951 handleSplitDblClick : function(e, t){
37952 var i = this.getCellIndex(t);
37953 if(this.grid.enableColumnResize !== false && this.cm.isResizable(i) && !this.cm.isFixed(i)){
37954 this.autoSizeColumn(i, true);
37959 render : function(){
37962 var colCount = cm.getColumnCount();
37964 if(this.grid.monitorWindowResize === true){
37965 Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
37967 var header = this.renderHeaders();
37968 var body = this.templates.body.apply({rows:""});
37969 var html = this.templates.master.apply({
37972 lockedHeader: header[0],
37976 //this.updateColumns();
37978 this.grid.getGridEl().dom.innerHTML = html;
37980 this.initElements();
37982 // a kludge to fix the random scolling effect in webkit
37983 this.el.on("scroll", function() {
37984 this.el.dom.scrollTop=0; // hopefully not recursive..
37987 this.scroller.on("scroll", this.handleScroll, this);
37988 this.lockedBody.on("mousewheel", this.handleWheel, this);
37989 this.mainBody.on("mousewheel", this.handleWheel, this);
37991 this.mainHd.on("mouseover", this.handleHdOver, this);
37992 this.mainHd.on("mouseout", this.handleHdOut, this);
37993 this.mainHd.on("dblclick", this.handleSplitDblClick, this,
37994 {delegate: "."+this.splitClass});
37996 this.lockedHd.on("mouseover", this.handleHdOver, this);
37997 this.lockedHd.on("mouseout", this.handleHdOut, this);
37998 this.lockedHd.on("dblclick", this.handleSplitDblClick, this,
37999 {delegate: "."+this.splitClass});
38001 if(this.grid.enableColumnResize !== false && Roo.grid.SplitDragZone){
38002 new Roo.grid.SplitDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
38005 this.updateSplitters();
38007 if(this.grid.enableColumnMove && Roo.grid.HeaderDragZone){
38008 new Roo.grid.HeaderDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
38009 new Roo.grid.HeaderDropZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
38012 if(this.grid.enableCtxMenu !== false && Roo.menu.Menu){
38013 this.hmenu = new Roo.menu.Menu({id: this.grid.id + "-hctx"});
38015 {id:"asc", text: this.sortAscText, cls: "xg-hmenu-sort-asc"},
38016 {id:"desc", text: this.sortDescText, cls: "xg-hmenu-sort-desc"}
38018 if(this.grid.enableColLock !== false){
38019 this.hmenu.add('-',
38020 {id:"lock", text: this.lockText, cls: "xg-hmenu-lock"},
38021 {id:"unlock", text: this.unlockText, cls: "xg-hmenu-unlock"}
38025 this.hmenu.add('-',
38026 {id:"wider", text: this.columnsWiderText},
38027 {id:"narrow", text: this.columnsNarrowText }
38033 if(this.grid.enableColumnHide !== false){
38035 this.colMenu = new Roo.menu.Menu({id:this.grid.id + "-hcols-menu"});
38036 this.colMenu.on("beforeshow", this.beforeColMenuShow, this);
38037 this.colMenu.on("itemclick", this.handleHdMenuClick, this);
38039 this.hmenu.add('-',
38040 {id:"columns", text: this.columnsText, menu: this.colMenu}
38043 this.hmenu.on("itemclick", this.handleHdMenuClick, this);
38045 this.grid.on("headercontextmenu", this.handleHdCtx, this);
38048 if((this.grid.enableDragDrop || this.grid.enableDrag) && Roo.grid.GridDragZone){
38049 this.dd = new Roo.grid.GridDragZone(this.grid, {
38050 ddGroup : this.grid.ddGroup || 'GridDD'
38056 for(var i = 0; i < colCount; i++){
38057 if(cm.isHidden(i)){
38058 this.hideColumn(i);
38060 if(cm.config[i].align){
38061 this.css.updateRule(this.colSelector + i, "textAlign", cm.config[i].align);
38062 this.css.updateRule(this.hdSelector + i, "textAlign", cm.config[i].align);
38066 this.updateHeaderSortState();
38068 this.beforeInitialResize();
38071 // two part rendering gives faster view to the user
38072 this.renderPhase2.defer(1, this);
38075 renderPhase2 : function(){
38076 // render the rows now
38078 if(this.grid.autoSizeColumns){
38079 this.autoSizeColumns();
38083 beforeInitialResize : function(){
38087 onColumnSplitterMoved : function(i, w){
38088 this.userResized = true;
38089 var cm = this.grid.colModel;
38090 cm.setColumnWidth(i, w, true);
38091 var cid = cm.getColumnId(i);
38092 this.css.updateRule(this.colSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
38093 this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
38094 this.updateSplitters();
38096 this.grid.fireEvent("columnresize", i, w);
38099 syncRowHeights : function(startIndex, endIndex){
38100 if(this.grid.enableRowHeightSync === true && this.cm.getLockedCount() > 0){
38101 startIndex = startIndex || 0;
38102 var mrows = this.getBodyTable().rows;
38103 var lrows = this.getLockedTable().rows;
38104 var len = mrows.length-1;
38105 endIndex = Math.min(endIndex || len, len);
38106 for(var i = startIndex; i <= endIndex; i++){
38107 var m = mrows[i], l = lrows[i];
38108 var h = Math.max(m.offsetHeight, l.offsetHeight);
38109 m.style.height = l.style.height = h + "px";
38114 layout : function(initialRender, is2ndPass){
38116 var auto = g.autoHeight;
38117 var scrollOffset = 16;
38118 var c = g.getGridEl(), cm = this.cm,
38119 expandCol = g.autoExpandColumn,
38121 //c.beginMeasure();
38123 if(!c.dom.offsetWidth){ // display:none?
38125 this.lockedWrap.show();
38126 this.mainWrap.show();
38131 var hasLock = this.cm.isLocked(0);
38133 var tbh = this.headerPanel.getHeight();
38134 var bbh = this.footerPanel.getHeight();
38137 var ch = this.getBodyTable().offsetHeight + tbh + bbh + this.mainHd.getHeight();
38138 var newHeight = ch + c.getBorderWidth("tb");
38140 newHeight = Math.min(g.maxHeight, newHeight);
38142 c.setHeight(newHeight);
38146 c.setWidth(cm.getTotalWidth()+c.getBorderWidth('lr'));
38149 var s = this.scroller;
38151 var csize = c.getSize(true);
38153 this.el.setSize(csize.width, csize.height);
38155 this.headerPanel.setWidth(csize.width);
38156 this.footerPanel.setWidth(csize.width);
38158 var hdHeight = this.mainHd.getHeight();
38159 var vw = csize.width;
38160 var vh = csize.height - (tbh + bbh);
38164 var bt = this.getBodyTable();
38165 var ltWidth = hasLock ?
38166 Math.max(this.getLockedTable().offsetWidth, this.lockedHd.dom.firstChild.offsetWidth) : 0;
38168 var scrollHeight = bt.offsetHeight;
38169 var scrollWidth = ltWidth + bt.offsetWidth;
38170 var vscroll = false, hscroll = false;
38172 this.scrollSizer.setSize(scrollWidth, scrollHeight+hdHeight);
38174 var lw = this.lockedWrap, mw = this.mainWrap;
38175 var lb = this.lockedBody, mb = this.mainBody;
38177 setTimeout(function(){
38178 var t = s.dom.offsetTop;
38179 var w = s.dom.clientWidth,
38180 h = s.dom.clientHeight;
38183 lw.setSize(ltWidth, h);
38185 mw.setLeftTop(ltWidth, t);
38186 mw.setSize(w-ltWidth, h);
38188 lb.setHeight(h-hdHeight);
38189 mb.setHeight(h-hdHeight);
38191 if(is2ndPass !== true && !gv.userResized && expandCol){
38192 // high speed resize without full column calculation
38194 var ci = cm.getIndexById(expandCol);
38196 ci = cm.findColumnIndex(expandCol);
38198 ci = Math.max(0, ci); // make sure it's got at least the first col.
38199 var expandId = cm.getColumnId(ci);
38200 var tw = cm.getTotalWidth(false);
38201 var currentWidth = cm.getColumnWidth(ci);
38202 var cw = Math.min(Math.max(((w-tw)+currentWidth-2)-/*scrollbar*/(w <= s.dom.offsetWidth ? 0 : 18), g.autoExpandMin), g.autoExpandMax);
38203 if(currentWidth != cw){
38204 cm.setColumnWidth(ci, cw, true);
38205 gv.css.updateRule(gv.colSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
38206 gv.css.updateRule(gv.hdSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
38207 gv.updateSplitters();
38208 gv.layout(false, true);
38220 onWindowResize : function(){
38221 if(!this.grid.monitorWindowResize || this.grid.autoHeight){
38227 appendFooter : function(parentEl){
38231 sortAscText : "Sort Ascending",
38232 sortDescText : "Sort Descending",
38233 lockText : "Lock Column",
38234 unlockText : "Unlock Column",
38235 columnsText : "Columns",
38237 columnsWiderText : "Wider",
38238 columnsNarrowText : "Thinner"
38242 Roo.grid.GridView.ColumnDragZone = function(grid, hd){
38243 Roo.grid.GridView.ColumnDragZone.superclass.constructor.call(this, grid, hd, null);
38244 this.proxy.el.addClass('x-grid3-col-dd');
38247 Roo.extend(Roo.grid.GridView.ColumnDragZone, Roo.grid.HeaderDragZone, {
38248 handleMouseDown : function(e){
38252 callHandleMouseDown : function(e){
38253 Roo.grid.GridView.ColumnDragZone.superclass.handleMouseDown.call(this, e);
38258 * Ext JS Library 1.1.1
38259 * Copyright(c) 2006-2007, Ext JS, LLC.
38261 * Originally Released Under LGPL - original licence link has changed is not relivant.
38264 * <script type="text/javascript">
38268 // This is a support class used internally by the Grid components
38269 Roo.grid.SplitDragZone = function(grid, hd, hd2){
38271 this.view = grid.getView();
38272 this.proxy = this.view.resizeProxy;
38273 Roo.grid.SplitDragZone.superclass.constructor.call(this, hd,
38274 "gridSplitters" + this.grid.getGridEl().id, {
38275 dragElId : Roo.id(this.proxy.dom), resizeFrame:false
38277 this.setHandleElId(Roo.id(hd));
38278 this.setOuterHandleElId(Roo.id(hd2));
38279 this.scroll = false;
38281 Roo.extend(Roo.grid.SplitDragZone, Roo.dd.DDProxy, {
38282 fly: Roo.Element.fly,
38284 b4StartDrag : function(x, y){
38285 this.view.headersDisabled = true;
38286 this.proxy.setHeight(this.view.mainWrap.getHeight());
38287 var w = this.cm.getColumnWidth(this.cellIndex);
38288 var minw = Math.max(w-this.grid.minColumnWidth, 0);
38289 this.resetConstraints();
38290 this.setXConstraint(minw, 1000);
38291 this.setYConstraint(0, 0);
38292 this.minX = x - minw;
38293 this.maxX = x + 1000;
38295 Roo.dd.DDProxy.prototype.b4StartDrag.call(this, x, y);
38299 handleMouseDown : function(e){
38300 ev = Roo.EventObject.setEvent(e);
38301 var t = this.fly(ev.getTarget());
38302 if(t.hasClass("x-grid-split")){
38303 this.cellIndex = this.view.getCellIndex(t.dom);
38304 this.split = t.dom;
38305 this.cm = this.grid.colModel;
38306 if(this.cm.isResizable(this.cellIndex) && !this.cm.isFixed(this.cellIndex)){
38307 Roo.grid.SplitDragZone.superclass.handleMouseDown.apply(this, arguments);
38312 endDrag : function(e){
38313 this.view.headersDisabled = false;
38314 var endX = Math.max(this.minX, Roo.lib.Event.getPageX(e));
38315 var diff = endX - this.startPos;
38316 this.view.onColumnSplitterMoved(this.cellIndex, this.cm.getColumnWidth(this.cellIndex)+diff);
38319 autoOffset : function(){
38320 this.setDelta(0,0);
38324 * Ext JS Library 1.1.1
38325 * Copyright(c) 2006-2007, Ext JS, LLC.
38327 * Originally Released Under LGPL - original licence link has changed is not relivant.
38330 * <script type="text/javascript">
38334 // This is a support class used internally by the Grid components
38335 Roo.grid.GridDragZone = function(grid, config){
38336 this.view = grid.getView();
38337 Roo.grid.GridDragZone.superclass.constructor.call(this, this.view.mainBody.dom, config);
38338 if(this.view.lockedBody){
38339 this.setHandleElId(Roo.id(this.view.mainBody.dom));
38340 this.setOuterHandleElId(Roo.id(this.view.lockedBody.dom));
38342 this.scroll = false;
38344 this.ddel = document.createElement('div');
38345 this.ddel.className = 'x-grid-dd-wrap';
38348 Roo.extend(Roo.grid.GridDragZone, Roo.dd.DragZone, {
38349 ddGroup : "GridDD",
38351 getDragData : function(e){
38352 var t = Roo.lib.Event.getTarget(e);
38353 var rowIndex = this.view.findRowIndex(t);
38354 var sm = this.grid.selModel;
38356 //Roo.log(rowIndex);
38358 if (sm.getSelectedCell) {
38359 // cell selection..
38360 if (!sm.getSelectedCell()) {
38363 if (rowIndex != sm.getSelectedCell()[0]) {
38369 if(rowIndex !== false){
38374 //Roo.log([ sm.getSelectedCell() ? sm.getSelectedCell()[0] : 'NO' , rowIndex ]);
38376 //if(!sm.isSelected(rowIndex) || e.hasModifier()){
38379 if (e.hasModifier()){
38380 sm.handleMouseDown(e, t); // non modifier buttons are handled by row select.
38383 Roo.log("getDragData");
38388 rowIndex: rowIndex,
38389 selections:sm.getSelections ? sm.getSelections() : (
38390 sm.getSelectedCell() ? [ this.grid.ds.getAt(sm.getSelectedCell()[0]) ] : []
38397 onInitDrag : function(e){
38398 var data = this.dragData;
38399 this.ddel.innerHTML = this.grid.getDragDropText();
38400 this.proxy.update(this.ddel);
38401 // fire start drag?
38404 afterRepair : function(){
38405 this.dragging = false;
38408 getRepairXY : function(e, data){
38412 onEndDrag : function(data, e){
38416 onValidDrop : function(dd, e, id){
38421 beforeInvalidDrop : function(e, id){
38426 * Ext JS Library 1.1.1
38427 * Copyright(c) 2006-2007, Ext JS, LLC.
38429 * Originally Released Under LGPL - original licence link has changed is not relivant.
38432 * <script type="text/javascript">
38437 * @class Roo.grid.ColumnModel
38438 * @extends Roo.util.Observable
38439 * This is the default implementation of a ColumnModel used by the Grid. It defines
38440 * the columns in the grid.
38443 var colModel = new Roo.grid.ColumnModel([
38444 {header: "Ticker", width: 60, sortable: true, locked: true},
38445 {header: "Company Name", width: 150, sortable: true},
38446 {header: "Market Cap.", width: 100, sortable: true},
38447 {header: "$ Sales", width: 100, sortable: true, renderer: money},
38448 {header: "Employees", width: 100, sortable: true, resizable: false}
38453 * The config options listed for this class are options which may appear in each
38454 * individual column definition.
38455 * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
38457 * @param {Object} config An Array of column config objects. See this class's
38458 * config objects for details.
38460 Roo.grid.ColumnModel = function(config){
38462 * The config passed into the constructor
38464 this.config = config;
38467 // if no id, create one
38468 // if the column does not have a dataIndex mapping,
38469 // map it to the order it is in the config
38470 for(var i = 0, len = config.length; i < len; i++){
38472 if(typeof c.dataIndex == "undefined"){
38475 if(typeof c.renderer == "string"){
38476 c.renderer = Roo.util.Format[c.renderer];
38478 if(typeof c.id == "undefined"){
38481 if(c.editor && c.editor.xtype){
38482 c.editor = Roo.factory(c.editor, Roo.grid);
38484 if(c.editor && c.editor.isFormField){
38485 c.editor = new Roo.grid.GridEditor(c.editor);
38487 this.lookup[c.id] = c;
38491 * The width of columns which have no width specified (defaults to 100)
38494 this.defaultWidth = 100;
38497 * Default sortable of columns which have no sortable specified (defaults to false)
38500 this.defaultSortable = false;
38504 * @event widthchange
38505 * Fires when the width of a column changes.
38506 * @param {ColumnModel} this
38507 * @param {Number} columnIndex The column index
38508 * @param {Number} newWidth The new width
38510 "widthchange": true,
38512 * @event headerchange
38513 * Fires when the text of a header changes.
38514 * @param {ColumnModel} this
38515 * @param {Number} columnIndex The column index
38516 * @param {Number} newText The new header text
38518 "headerchange": true,
38520 * @event hiddenchange
38521 * Fires when a column is hidden or "unhidden".
38522 * @param {ColumnModel} this
38523 * @param {Number} columnIndex The column index
38524 * @param {Boolean} hidden true if hidden, false otherwise
38526 "hiddenchange": true,
38528 * @event columnmoved
38529 * Fires when a column is moved.
38530 * @param {ColumnModel} this
38531 * @param {Number} oldIndex
38532 * @param {Number} newIndex
38534 "columnmoved" : true,
38536 * @event columlockchange
38537 * Fires when a column's locked state is changed
38538 * @param {ColumnModel} this
38539 * @param {Number} colIndex
38540 * @param {Boolean} locked true if locked
38542 "columnlockchange" : true
38544 Roo.grid.ColumnModel.superclass.constructor.call(this);
38546 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
38548 * @cfg {String} header The header text to display in the Grid view.
38551 * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
38552 * {@link Roo.data.Record} definition from which to draw the column's value. If not
38553 * specified, the column's index is used as an index into the Record's data Array.
38556 * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
38557 * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
38560 * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
38561 * Defaults to the value of the {@link #defaultSortable} property.
38562 * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
38565 * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid. Defaults to false.
38568 * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed. Defaults to false.
38571 * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
38574 * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
38577 * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
38578 * given the cell's data value. See {@link #setRenderer}. If not specified, the
38579 * default renderer uses the raw data value. If an object is returned (bootstrap only)
38580 * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
38583 * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor
38586 * @cfg {String} align (Optional) Set the CSS text-align property of the column. Defaults to undefined.
38589 * @cfg {String} cursor (Optional)
38592 * Returns the id of the column at the specified index.
38593 * @param {Number} index The column index
38594 * @return {String} the id
38596 getColumnId : function(index){
38597 return this.config[index].id;
38601 * Returns the column for a specified id.
38602 * @param {String} id The column id
38603 * @return {Object} the column
38605 getColumnById : function(id){
38606 return this.lookup[id];
38611 * Returns the column for a specified dataIndex.
38612 * @param {String} dataIndex The column dataIndex
38613 * @return {Object|Boolean} the column or false if not found
38615 getColumnByDataIndex: function(dataIndex){
38616 var index = this.findColumnIndex(dataIndex);
38617 return index > -1 ? this.config[index] : false;
38621 * Returns the index for a specified column id.
38622 * @param {String} id The column id
38623 * @return {Number} the index, or -1 if not found
38625 getIndexById : function(id){
38626 for(var i = 0, len = this.config.length; i < len; i++){
38627 if(this.config[i].id == id){
38635 * Returns the index for a specified column dataIndex.
38636 * @param {String} dataIndex The column dataIndex
38637 * @return {Number} the index, or -1 if not found
38640 findColumnIndex : function(dataIndex){
38641 for(var i = 0, len = this.config.length; i < len; i++){
38642 if(this.config[i].dataIndex == dataIndex){
38650 moveColumn : function(oldIndex, newIndex){
38651 var c = this.config[oldIndex];
38652 this.config.splice(oldIndex, 1);
38653 this.config.splice(newIndex, 0, c);
38654 this.dataMap = null;
38655 this.fireEvent("columnmoved", this, oldIndex, newIndex);
38658 isLocked : function(colIndex){
38659 return this.config[colIndex].locked === true;
38662 setLocked : function(colIndex, value, suppressEvent){
38663 if(this.isLocked(colIndex) == value){
38666 this.config[colIndex].locked = value;
38667 if(!suppressEvent){
38668 this.fireEvent("columnlockchange", this, colIndex, value);
38672 getTotalLockedWidth : function(){
38673 var totalWidth = 0;
38674 for(var i = 0; i < this.config.length; i++){
38675 if(this.isLocked(i) && !this.isHidden(i)){
38676 this.totalWidth += this.getColumnWidth(i);
38682 getLockedCount : function(){
38683 for(var i = 0, len = this.config.length; i < len; i++){
38684 if(!this.isLocked(i)){
38691 * Returns the number of columns.
38694 getColumnCount : function(visibleOnly){
38695 if(visibleOnly === true){
38697 for(var i = 0, len = this.config.length; i < len; i++){
38698 if(!this.isHidden(i)){
38704 return this.config.length;
38708 * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
38709 * @param {Function} fn
38710 * @param {Object} scope (optional)
38711 * @return {Array} result
38713 getColumnsBy : function(fn, scope){
38715 for(var i = 0, len = this.config.length; i < len; i++){
38716 var c = this.config[i];
38717 if(fn.call(scope||this, c, i) === true){
38725 * Returns true if the specified column is sortable.
38726 * @param {Number} col The column index
38727 * @return {Boolean}
38729 isSortable : function(col){
38730 if(typeof this.config[col].sortable == "undefined"){
38731 return this.defaultSortable;
38733 return this.config[col].sortable;
38737 * Returns the rendering (formatting) function defined for the column.
38738 * @param {Number} col The column index.
38739 * @return {Function} The function used to render the cell. See {@link #setRenderer}.
38741 getRenderer : function(col){
38742 if(!this.config[col].renderer){
38743 return Roo.grid.ColumnModel.defaultRenderer;
38745 return this.config[col].renderer;
38749 * Sets the rendering (formatting) function for a column.
38750 * @param {Number} col The column index
38751 * @param {Function} fn The function to use to process the cell's raw data
38752 * to return HTML markup for the grid view. The render function is called with
38753 * the following parameters:<ul>
38754 * <li>Data value.</li>
38755 * <li>Cell metadata. An object in which you may set the following attributes:<ul>
38756 * <li>css A CSS style string to apply to the table cell.</li>
38757 * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
38758 * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
38759 * <li>Row index</li>
38760 * <li>Column index</li>
38761 * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
38763 setRenderer : function(col, fn){
38764 this.config[col].renderer = fn;
38768 * Returns the width for the specified column.
38769 * @param {Number} col The column index
38772 getColumnWidth : function(col){
38773 return this.config[col].width * 1 || this.defaultWidth;
38777 * Sets the width for a column.
38778 * @param {Number} col The column index
38779 * @param {Number} width The new width
38781 setColumnWidth : function(col, width, suppressEvent){
38782 this.config[col].width = width;
38783 this.totalWidth = null;
38784 if(!suppressEvent){
38785 this.fireEvent("widthchange", this, col, width);
38790 * Returns the total width of all columns.
38791 * @param {Boolean} includeHidden True to include hidden column widths
38794 getTotalWidth : function(includeHidden){
38795 if(!this.totalWidth){
38796 this.totalWidth = 0;
38797 for(var i = 0, len = this.config.length; i < len; i++){
38798 if(includeHidden || !this.isHidden(i)){
38799 this.totalWidth += this.getColumnWidth(i);
38803 return this.totalWidth;
38807 * Returns the header for the specified column.
38808 * @param {Number} col The column index
38811 getColumnHeader : function(col){
38812 return this.config[col].header;
38816 * Sets the header for a column.
38817 * @param {Number} col The column index
38818 * @param {String} header The new header
38820 setColumnHeader : function(col, header){
38821 this.config[col].header = header;
38822 this.fireEvent("headerchange", this, col, header);
38826 * Returns the tooltip for the specified column.
38827 * @param {Number} col The column index
38830 getColumnTooltip : function(col){
38831 return this.config[col].tooltip;
38834 * Sets the tooltip for a column.
38835 * @param {Number} col The column index
38836 * @param {String} tooltip The new tooltip
38838 setColumnTooltip : function(col, tooltip){
38839 this.config[col].tooltip = tooltip;
38843 * Returns the dataIndex for the specified column.
38844 * @param {Number} col The column index
38847 getDataIndex : function(col){
38848 return this.config[col].dataIndex;
38852 * Sets the dataIndex for a column.
38853 * @param {Number} col The column index
38854 * @param {Number} dataIndex The new dataIndex
38856 setDataIndex : function(col, dataIndex){
38857 this.config[col].dataIndex = dataIndex;
38863 * Returns true if the cell is editable.
38864 * @param {Number} colIndex The column index
38865 * @param {Number} rowIndex The row index
38866 * @return {Boolean}
38868 isCellEditable : function(colIndex, rowIndex){
38869 return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
38873 * Returns the editor defined for the cell/column.
38874 * return false or null to disable editing.
38875 * @param {Number} colIndex The column index
38876 * @param {Number} rowIndex The row index
38879 getCellEditor : function(colIndex, rowIndex){
38880 return this.config[colIndex].editor;
38884 * Sets if a column is editable.
38885 * @param {Number} col The column index
38886 * @param {Boolean} editable True if the column is editable
38888 setEditable : function(col, editable){
38889 this.config[col].editable = editable;
38894 * Returns true if the column is hidden.
38895 * @param {Number} colIndex The column index
38896 * @return {Boolean}
38898 isHidden : function(colIndex){
38899 return this.config[colIndex].hidden;
38904 * Returns true if the column width cannot be changed
38906 isFixed : function(colIndex){
38907 return this.config[colIndex].fixed;
38911 * Returns true if the column can be resized
38912 * @return {Boolean}
38914 isResizable : function(colIndex){
38915 return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
38918 * Sets if a column is hidden.
38919 * @param {Number} colIndex The column index
38920 * @param {Boolean} hidden True if the column is hidden
38922 setHidden : function(colIndex, hidden){
38923 this.config[colIndex].hidden = hidden;
38924 this.totalWidth = null;
38925 this.fireEvent("hiddenchange", this, colIndex, hidden);
38929 * Sets the editor for a column.
38930 * @param {Number} col The column index
38931 * @param {Object} editor The editor object
38933 setEditor : function(col, editor){
38934 this.config[col].editor = editor;
38938 Roo.grid.ColumnModel.defaultRenderer = function(value){
38939 if(typeof value == "string" && value.length < 1){
38945 // Alias for backwards compatibility
38946 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
38949 * Ext JS Library 1.1.1
38950 * Copyright(c) 2006-2007, Ext JS, LLC.
38952 * Originally Released Under LGPL - original licence link has changed is not relivant.
38955 * <script type="text/javascript">
38959 * @class Roo.grid.AbstractSelectionModel
38960 * @extends Roo.util.Observable
38961 * Abstract base class for grid SelectionModels. It provides the interface that should be
38962 * implemented by descendant classes. This class should not be directly instantiated.
38965 Roo.grid.AbstractSelectionModel = function(){
38966 this.locked = false;
38967 Roo.grid.AbstractSelectionModel.superclass.constructor.call(this);
38970 Roo.extend(Roo.grid.AbstractSelectionModel, Roo.util.Observable, {
38971 /** @ignore Called by the grid automatically. Do not call directly. */
38972 init : function(grid){
38978 * Locks the selections.
38981 this.locked = true;
38985 * Unlocks the selections.
38987 unlock : function(){
38988 this.locked = false;
38992 * Returns true if the selections are locked.
38993 * @return {Boolean}
38995 isLocked : function(){
38996 return this.locked;
39000 * Ext JS Library 1.1.1
39001 * Copyright(c) 2006-2007, Ext JS, LLC.
39003 * Originally Released Under LGPL - original licence link has changed is not relivant.
39006 * <script type="text/javascript">
39009 * @extends Roo.grid.AbstractSelectionModel
39010 * @class Roo.grid.RowSelectionModel
39011 * The default SelectionModel used by {@link Roo.grid.Grid}.
39012 * It supports multiple selections and keyboard selection/navigation.
39014 * @param {Object} config
39016 Roo.grid.RowSelectionModel = function(config){
39017 Roo.apply(this, config);
39018 this.selections = new Roo.util.MixedCollection(false, function(o){
39023 this.lastActive = false;
39027 * @event selectionchange
39028 * Fires when the selection changes
39029 * @param {SelectionModel} this
39031 "selectionchange" : true,
39033 * @event afterselectionchange
39034 * Fires after the selection changes (eg. by key press or clicking)
39035 * @param {SelectionModel} this
39037 "afterselectionchange" : true,
39039 * @event beforerowselect
39040 * Fires when a row is selected being selected, return false to cancel.
39041 * @param {SelectionModel} this
39042 * @param {Number} rowIndex The selected index
39043 * @param {Boolean} keepExisting False if other selections will be cleared
39045 "beforerowselect" : true,
39048 * Fires when a row is selected.
39049 * @param {SelectionModel} this
39050 * @param {Number} rowIndex The selected index
39051 * @param {Roo.data.Record} r The record
39053 "rowselect" : true,
39055 * @event rowdeselect
39056 * Fires when a row is deselected.
39057 * @param {SelectionModel} this
39058 * @param {Number} rowIndex The selected index
39060 "rowdeselect" : true
39062 Roo.grid.RowSelectionModel.superclass.constructor.call(this);
39063 this.locked = false;
39066 Roo.extend(Roo.grid.RowSelectionModel, Roo.grid.AbstractSelectionModel, {
39068 * @cfg {Boolean} singleSelect
39069 * True to allow selection of only one row at a time (defaults to false)
39071 singleSelect : false,
39074 initEvents : function(){
39076 if(!this.grid.enableDragDrop && !this.grid.enableDrag){
39077 this.grid.on("mousedown", this.handleMouseDown, this);
39078 }else{ // allow click to work like normal
39079 this.grid.on("rowclick", this.handleDragableRowClick, this);
39082 this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
39083 "up" : function(e){
39085 this.selectPrevious(e.shiftKey);
39086 }else if(this.last !== false && this.lastActive !== false){
39087 var last = this.last;
39088 this.selectRange(this.last, this.lastActive-1);
39089 this.grid.getView().focusRow(this.lastActive);
39090 if(last !== false){
39094 this.selectFirstRow();
39096 this.fireEvent("afterselectionchange", this);
39098 "down" : function(e){
39100 this.selectNext(e.shiftKey);
39101 }else if(this.last !== false && this.lastActive !== false){
39102 var last = this.last;
39103 this.selectRange(this.last, this.lastActive+1);
39104 this.grid.getView().focusRow(this.lastActive);
39105 if(last !== false){
39109 this.selectFirstRow();
39111 this.fireEvent("afterselectionchange", this);
39116 var view = this.grid.view;
39117 view.on("refresh", this.onRefresh, this);
39118 view.on("rowupdated", this.onRowUpdated, this);
39119 view.on("rowremoved", this.onRemove, this);
39123 onRefresh : function(){
39124 var ds = this.grid.dataSource, i, v = this.grid.view;
39125 var s = this.selections;
39126 s.each(function(r){
39127 if((i = ds.indexOfId(r.id)) != -1){
39136 onRemove : function(v, index, r){
39137 this.selections.remove(r);
39141 onRowUpdated : function(v, index, r){
39142 if(this.isSelected(r)){
39143 v.onRowSelect(index);
39149 * @param {Array} records The records to select
39150 * @param {Boolean} keepExisting (optional) True to keep existing selections
39152 selectRecords : function(records, keepExisting){
39154 this.clearSelections();
39156 var ds = this.grid.dataSource;
39157 for(var i = 0, len = records.length; i < len; i++){
39158 this.selectRow(ds.indexOf(records[i]), true);
39163 * Gets the number of selected rows.
39166 getCount : function(){
39167 return this.selections.length;
39171 * Selects the first row in the grid.
39173 selectFirstRow : function(){
39178 * Select the last row.
39179 * @param {Boolean} keepExisting (optional) True to keep existing selections
39181 selectLastRow : function(keepExisting){
39182 this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
39186 * Selects the row immediately following the last selected row.
39187 * @param {Boolean} keepExisting (optional) True to keep existing selections
39189 selectNext : function(keepExisting){
39190 if(this.last !== false && (this.last+1) < this.grid.dataSource.getCount()){
39191 this.selectRow(this.last+1, keepExisting);
39192 this.grid.getView().focusRow(this.last);
39197 * Selects the row that precedes the last selected row.
39198 * @param {Boolean} keepExisting (optional) True to keep existing selections
39200 selectPrevious : function(keepExisting){
39202 this.selectRow(this.last-1, keepExisting);
39203 this.grid.getView().focusRow(this.last);
39208 * Returns the selected records
39209 * @return {Array} Array of selected records
39211 getSelections : function(){
39212 return [].concat(this.selections.items);
39216 * Returns the first selected record.
39219 getSelected : function(){
39220 return this.selections.itemAt(0);
39225 * Clears all selections.
39227 clearSelections : function(fast){
39228 if(this.locked) return;
39230 var ds = this.grid.dataSource;
39231 var s = this.selections;
39232 s.each(function(r){
39233 this.deselectRow(ds.indexOfId(r.id));
39237 this.selections.clear();
39244 * Selects all rows.
39246 selectAll : function(){
39247 if(this.locked) return;
39248 this.selections.clear();
39249 for(var i = 0, len = this.grid.dataSource.getCount(); i < len; i++){
39250 this.selectRow(i, true);
39255 * Returns True if there is a selection.
39256 * @return {Boolean}
39258 hasSelection : function(){
39259 return this.selections.length > 0;
39263 * Returns True if the specified row is selected.
39264 * @param {Number/Record} record The record or index of the record to check
39265 * @return {Boolean}
39267 isSelected : function(index){
39268 var r = typeof index == "number" ? this.grid.dataSource.getAt(index) : index;
39269 return (r && this.selections.key(r.id) ? true : false);
39273 * Returns True if the specified record id is selected.
39274 * @param {String} id The id of record to check
39275 * @return {Boolean}
39277 isIdSelected : function(id){
39278 return (this.selections.key(id) ? true : false);
39282 handleMouseDown : function(e, t){
39283 var view = this.grid.getView(), rowIndex;
39284 if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
39287 if(e.shiftKey && this.last !== false){
39288 var last = this.last;
39289 this.selectRange(last, rowIndex, e.ctrlKey);
39290 this.last = last; // reset the last
39291 view.focusRow(rowIndex);
39293 var isSelected = this.isSelected(rowIndex);
39294 if(e.button !== 0 && isSelected){
39295 view.focusRow(rowIndex);
39296 }else if(e.ctrlKey && isSelected){
39297 this.deselectRow(rowIndex);
39298 }else if(!isSelected){
39299 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
39300 view.focusRow(rowIndex);
39303 this.fireEvent("afterselectionchange", this);
39306 handleDragableRowClick : function(grid, rowIndex, e)
39308 if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
39309 this.selectRow(rowIndex, false);
39310 grid.view.focusRow(rowIndex);
39311 this.fireEvent("afterselectionchange", this);
39316 * Selects multiple rows.
39317 * @param {Array} rows Array of the indexes of the row to select
39318 * @param {Boolean} keepExisting (optional) True to keep existing selections
39320 selectRows : function(rows, keepExisting){
39322 this.clearSelections();
39324 for(var i = 0, len = rows.length; i < len; i++){
39325 this.selectRow(rows[i], true);
39330 * Selects a range of rows. All rows in between startRow and endRow are also selected.
39331 * @param {Number} startRow The index of the first row in the range
39332 * @param {Number} endRow The index of the last row in the range
39333 * @param {Boolean} keepExisting (optional) True to retain existing selections
39335 selectRange : function(startRow, endRow, keepExisting){
39336 if(this.locked) return;
39338 this.clearSelections();
39340 if(startRow <= endRow){
39341 for(var i = startRow; i <= endRow; i++){
39342 this.selectRow(i, true);
39345 for(var i = startRow; i >= endRow; i--){
39346 this.selectRow(i, true);
39352 * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
39353 * @param {Number} startRow The index of the first row in the range
39354 * @param {Number} endRow The index of the last row in the range
39356 deselectRange : function(startRow, endRow, preventViewNotify){
39357 if(this.locked) return;
39358 for(var i = startRow; i <= endRow; i++){
39359 this.deselectRow(i, preventViewNotify);
39365 * @param {Number} row The index of the row to select
39366 * @param {Boolean} keepExisting (optional) True to keep existing selections
39368 selectRow : function(index, keepExisting, preventViewNotify){
39369 if(this.locked || (index < 0 || index >= this.grid.dataSource.getCount())) return;
39370 if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
39371 if(!keepExisting || this.singleSelect){
39372 this.clearSelections();
39374 var r = this.grid.dataSource.getAt(index);
39375 this.selections.add(r);
39376 this.last = this.lastActive = index;
39377 if(!preventViewNotify){
39378 this.grid.getView().onRowSelect(index);
39380 this.fireEvent("rowselect", this, index, r);
39381 this.fireEvent("selectionchange", this);
39387 * @param {Number} row The index of the row to deselect
39389 deselectRow : function(index, preventViewNotify){
39390 if(this.locked) return;
39391 if(this.last == index){
39394 if(this.lastActive == index){
39395 this.lastActive = false;
39397 var r = this.grid.dataSource.getAt(index);
39398 this.selections.remove(r);
39399 if(!preventViewNotify){
39400 this.grid.getView().onRowDeselect(index);
39402 this.fireEvent("rowdeselect", this, index);
39403 this.fireEvent("selectionchange", this);
39407 restoreLast : function(){
39409 this.last = this._last;
39414 acceptsNav : function(row, col, cm){
39415 return !cm.isHidden(col) && cm.isCellEditable(col, row);
39419 onEditorKey : function(field, e){
39420 var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
39425 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
39427 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
39429 }else if(k == e.ENTER && !e.ctrlKey){
39433 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
39435 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
39437 }else if(k == e.ESC){
39441 g.startEditing(newCell[0], newCell[1]);
39446 * Ext JS Library 1.1.1
39447 * Copyright(c) 2006-2007, Ext JS, LLC.
39449 * Originally Released Under LGPL - original licence link has changed is not relivant.
39452 * <script type="text/javascript">
39455 * @class Roo.grid.CellSelectionModel
39456 * @extends Roo.grid.AbstractSelectionModel
39457 * This class provides the basic implementation for cell selection in a grid.
39459 * @param {Object} config The object containing the configuration of this model.
39460 * @cfg {Boolean} enter_is_tab Enter behaves the same as tab. (eg. goes to next cell) default: false
39462 Roo.grid.CellSelectionModel = function(config){
39463 Roo.apply(this, config);
39465 this.selection = null;
39469 * @event beforerowselect
39470 * Fires before a cell is selected.
39471 * @param {SelectionModel} this
39472 * @param {Number} rowIndex The selected row index
39473 * @param {Number} colIndex The selected cell index
39475 "beforecellselect" : true,
39477 * @event cellselect
39478 * Fires when a cell is selected.
39479 * @param {SelectionModel} this
39480 * @param {Number} rowIndex The selected row index
39481 * @param {Number} colIndex The selected cell index
39483 "cellselect" : true,
39485 * @event selectionchange
39486 * Fires when the active selection changes.
39487 * @param {SelectionModel} this
39488 * @param {Object} selection null for no selection or an object (o) with two properties
39490 <li>o.record: the record object for the row the selection is in</li>
39491 <li>o.cell: An array of [rowIndex, columnIndex]</li>
39494 "selectionchange" : true,
39497 * Fires when the tab (or enter) was pressed on the last editable cell
39498 * You can use this to trigger add new row.
39499 * @param {SelectionModel} this
39503 * @event beforeeditnext
39504 * Fires before the next editable sell is made active
39505 * You can use this to skip to another cell or fire the tabend
39506 * if you set cell to false
39507 * @param {Object} eventdata object : { cell : [ row, col ] }
39509 "beforeeditnext" : true
39511 Roo.grid.CellSelectionModel.superclass.constructor.call(this);
39514 Roo.extend(Roo.grid.CellSelectionModel, Roo.grid.AbstractSelectionModel, {
39516 enter_is_tab: false,
39519 initEvents : function(){
39520 this.grid.on("mousedown", this.handleMouseDown, this);
39521 this.grid.getGridEl().on(Roo.isIE ? "keydown" : "keypress", this.handleKeyDown, this);
39522 var view = this.grid.view;
39523 view.on("refresh", this.onViewChange, this);
39524 view.on("rowupdated", this.onRowUpdated, this);
39525 view.on("beforerowremoved", this.clearSelections, this);
39526 view.on("beforerowsinserted", this.clearSelections, this);
39527 if(this.grid.isEditor){
39528 this.grid.on("beforeedit", this.beforeEdit, this);
39533 beforeEdit : function(e){
39534 this.select(e.row, e.column, false, true, e.record);
39538 onRowUpdated : function(v, index, r){
39539 if(this.selection && this.selection.record == r){
39540 v.onCellSelect(index, this.selection.cell[1]);
39545 onViewChange : function(){
39546 this.clearSelections(true);
39550 * Returns the currently selected cell,.
39551 * @return {Array} The selected cell (row, column) or null if none selected.
39553 getSelectedCell : function(){
39554 return this.selection ? this.selection.cell : null;
39558 * Clears all selections.
39559 * @param {Boolean} true to prevent the gridview from being notified about the change.
39561 clearSelections : function(preventNotify){
39562 var s = this.selection;
39564 if(preventNotify !== true){
39565 this.grid.view.onCellDeselect(s.cell[0], s.cell[1]);
39567 this.selection = null;
39568 this.fireEvent("selectionchange", this, null);
39573 * Returns true if there is a selection.
39574 * @return {Boolean}
39576 hasSelection : function(){
39577 return this.selection ? true : false;
39581 handleMouseDown : function(e, t){
39582 var v = this.grid.getView();
39583 if(this.isLocked()){
39586 var row = v.findRowIndex(t);
39587 var cell = v.findCellIndex(t);
39588 if(row !== false && cell !== false){
39589 this.select(row, cell);
39595 * @param {Number} rowIndex
39596 * @param {Number} collIndex
39598 select : function(rowIndex, colIndex, preventViewNotify, preventFocus, /*internal*/ r){
39599 if(this.fireEvent("beforecellselect", this, rowIndex, colIndex) !== false){
39600 this.clearSelections();
39601 r = r || this.grid.dataSource.getAt(rowIndex);
39604 cell : [rowIndex, colIndex]
39606 if(!preventViewNotify){
39607 var v = this.grid.getView();
39608 v.onCellSelect(rowIndex, colIndex);
39609 if(preventFocus !== true){
39610 v.focusCell(rowIndex, colIndex);
39613 this.fireEvent("cellselect", this, rowIndex, colIndex);
39614 this.fireEvent("selectionchange", this, this.selection);
39619 isSelectable : function(rowIndex, colIndex, cm){
39620 return !cm.isHidden(colIndex);
39624 handleKeyDown : function(e){
39625 //Roo.log('Cell Sel Model handleKeyDown');
39626 if(!e.isNavKeyPress()){
39629 var g = this.grid, s = this.selection;
39632 var cell = g.walkCells(0, 0, 1, this.isSelectable, this);
39634 this.select(cell[0], cell[1]);
39639 var walk = function(row, col, step){
39640 return g.walkCells(row, col, step, sm.isSelectable, sm);
39642 var k = e.getKey(), r = s.cell[0], c = s.cell[1];
39649 // handled by onEditorKey
39650 if (g.isEditor && g.editing) {
39654 newCell = walk(r, c-1, -1);
39656 newCell = walk(r, c+1, 1);
39661 newCell = walk(r+1, c, 1);
39665 newCell = walk(r-1, c, -1);
39669 newCell = walk(r, c+1, 1);
39673 newCell = walk(r, c-1, -1);
39678 if(g.isEditor && !g.editing){
39679 g.startEditing(r, c);
39688 this.select(newCell[0], newCell[1]);
39694 acceptsNav : function(row, col, cm){
39695 return !cm.isHidden(col) && cm.isCellEditable(col, row);
39699 * @param {Number} field (not used) - as it's normally used as a listener
39700 * @param {Number} e - event - fake it by using
39702 * var e = Roo.EventObjectImpl.prototype;
39703 * e.keyCode = e.TAB
39707 onEditorKey : function(field, e){
39709 var k = e.getKey(),
39712 ed = g.activeEditor,
39714 ///Roo.log('onEditorKey' + k);
39717 if (this.enter_is_tab && k == e.ENTER) {
39723 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
39725 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
39731 } else if(k == e.ENTER && !e.ctrlKey){
39734 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
39736 } else if(k == e.ESC){
39741 var ecall = { cell : newCell, forward : forward };
39742 this.fireEvent('beforeeditnext', ecall );
39743 newCell = ecall.cell;
39744 forward = ecall.forward;
39748 //Roo.log('next cell after edit');
39749 g.startEditing.defer(100, g, [newCell[0], newCell[1]]);
39750 } else if (forward) {
39751 // tabbed past last
39752 this.fireEvent.defer(100, this, ['tabend',this]);
39757 * Ext JS Library 1.1.1
39758 * Copyright(c) 2006-2007, Ext JS, LLC.
39760 * Originally Released Under LGPL - original licence link has changed is not relivant.
39763 * <script type="text/javascript">
39767 * @class Roo.grid.EditorGrid
39768 * @extends Roo.grid.Grid
39769 * Class for creating and editable grid.
39770 * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
39771 * The container MUST have some type of size defined for the grid to fill. The container will be
39772 * automatically set to position relative if it isn't already.
39773 * @param {Object} dataSource The data model to bind to
39774 * @param {Object} colModel The column model with info about this grid's columns
39776 Roo.grid.EditorGrid = function(container, config){
39777 Roo.grid.EditorGrid.superclass.constructor.call(this, container, config);
39778 this.getGridEl().addClass("xedit-grid");
39780 if(!this.selModel){
39781 this.selModel = new Roo.grid.CellSelectionModel();
39784 this.activeEditor = null;
39788 * @event beforeedit
39789 * Fires before cell editing is triggered. The edit event object has the following properties <br />
39790 * <ul style="padding:5px;padding-left:16px;">
39791 * <li>grid - This grid</li>
39792 * <li>record - The record being edited</li>
39793 * <li>field - The field name being edited</li>
39794 * <li>value - The value for the field being edited.</li>
39795 * <li>row - The grid row index</li>
39796 * <li>column - The grid column index</li>
39797 * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
39799 * @param {Object} e An edit event (see above for description)
39801 "beforeedit" : true,
39804 * Fires after a cell is edited. <br />
39805 * <ul style="padding:5px;padding-left:16px;">
39806 * <li>grid - This grid</li>
39807 * <li>record - The record being edited</li>
39808 * <li>field - The field name being edited</li>
39809 * <li>value - The value being set</li>
39810 * <li>originalValue - The original value for the field, before the edit.</li>
39811 * <li>row - The grid row index</li>
39812 * <li>column - The grid column index</li>
39814 * @param {Object} e An edit event (see above for description)
39816 "afteredit" : true,
39818 * @event validateedit
39819 * Fires after a cell is edited, but before the value is set in the record.
39820 * You can use this to modify the value being set in the field, Return false
39821 * to cancel the change. The edit event object has the following properties <br />
39822 * <ul style="padding:5px;padding-left:16px;">
39823 * <li>editor - This editor</li>
39824 * <li>grid - This grid</li>
39825 * <li>record - The record being edited</li>
39826 * <li>field - The field name being edited</li>
39827 * <li>value - The value being set</li>
39828 * <li>originalValue - The original value for the field, before the edit.</li>
39829 * <li>row - The grid row index</li>
39830 * <li>column - The grid column index</li>
39831 * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
39833 * @param {Object} e An edit event (see above for description)
39835 "validateedit" : true
39837 this.on("bodyscroll", this.stopEditing, this);
39838 this.on(this.clicksToEdit == 1 ? "cellclick" : "celldblclick", this.onCellDblClick, this);
39841 Roo.extend(Roo.grid.EditorGrid, Roo.grid.Grid, {
39843 * @cfg {Number} clicksToEdit
39844 * The number of clicks on a cell required to display the cell's editor (defaults to 2)
39851 trackMouseOver: false, // causes very odd FF errors
39853 onCellDblClick : function(g, row, col){
39854 this.startEditing(row, col);
39857 onEditComplete : function(ed, value, startValue){
39858 this.editing = false;
39859 this.activeEditor = null;
39860 ed.un("specialkey", this.selModel.onEditorKey, this.selModel);
39862 var field = this.colModel.getDataIndex(ed.col);
39867 originalValue: startValue,
39874 var cell = Roo.get(this.view.getCell(ed.row,ed.col))
39877 if(String(value) !== String(startValue)){
39879 if(this.fireEvent("validateedit", e) !== false && !e.cancel){
39880 r.set(field, e.value);
39881 // if we are dealing with a combo box..
39882 // then we also set the 'name' colum to be the displayField
39883 if (ed.field.displayField && ed.field.name) {
39884 r.set(ed.field.name, ed.field.el.dom.value);
39887 delete e.cancel; //?? why!!!
39888 this.fireEvent("afteredit", e);
39891 this.fireEvent("afteredit", e); // always fire it!
39893 this.view.focusCell(ed.row, ed.col);
39897 * Starts editing the specified for the specified row/column
39898 * @param {Number} rowIndex
39899 * @param {Number} colIndex
39901 startEditing : function(row, col){
39902 this.stopEditing();
39903 if(this.colModel.isCellEditable(col, row)){
39904 this.view.ensureVisible(row, col, true);
39906 var r = this.dataSource.getAt(row);
39907 var field = this.colModel.getDataIndex(col);
39908 var cell = Roo.get(this.view.getCell(row,col));
39913 value: r.data[field],
39918 if(this.fireEvent("beforeedit", e) !== false && !e.cancel){
39919 this.editing = true;
39920 var ed = this.colModel.getCellEditor(col, row);
39926 ed.render(ed.parentEl || document.body);
39932 (function(){ // complex but required for focus issues in safari, ie and opera
39936 ed.on("complete", this.onEditComplete, this, {single: true});
39937 ed.on("specialkey", this.selModel.onEditorKey, this.selModel);
39938 this.activeEditor = ed;
39939 var v = r.data[field];
39940 ed.startEdit(this.view.getCell(row, col), v);
39941 // combo's with 'displayField and name set
39942 if (ed.field.displayField && ed.field.name) {
39943 ed.field.el.dom.value = r.data[ed.field.name];
39947 }).defer(50, this);
39953 * Stops any active editing
39955 stopEditing : function(){
39956 if(this.activeEditor){
39957 this.activeEditor.completeEdit();
39959 this.activeEditor = null;
39963 * Called to get grid's drag proxy text, by default returns this.ddText.
39966 getDragDropText : function(){
39967 var count = this.selModel.getSelectedCell() ? 1 : 0;
39968 return String.format(this.ddText, count, count == 1 ? '' : 's');
39973 * Ext JS Library 1.1.1
39974 * Copyright(c) 2006-2007, Ext JS, LLC.
39976 * Originally Released Under LGPL - original licence link has changed is not relivant.
39979 * <script type="text/javascript">
39982 // private - not really -- you end up using it !
39983 // This is a support class used internally by the Grid components
39986 * @class Roo.grid.GridEditor
39987 * @extends Roo.Editor
39988 * Class for creating and editable grid elements.
39989 * @param {Object} config any settings (must include field)
39991 Roo.grid.GridEditor = function(field, config){
39992 if (!config && field.field) {
39994 field = Roo.factory(config.field, Roo.form);
39996 Roo.grid.GridEditor.superclass.constructor.call(this, field, config);
39997 field.monitorTab = false;
40000 Roo.extend(Roo.grid.GridEditor, Roo.Editor, {
40003 * @cfg {Roo.form.Field} field Field to wrap (or xtyped)
40006 alignment: "tl-tl",
40009 cls: "x-small-editor x-grid-editor",
40014 * Ext JS Library 1.1.1
40015 * Copyright(c) 2006-2007, Ext JS, LLC.
40017 * Originally Released Under LGPL - original licence link has changed is not relivant.
40020 * <script type="text/javascript">
40025 Roo.grid.PropertyRecord = Roo.data.Record.create([
40026 {name:'name',type:'string'}, 'value'
40030 Roo.grid.PropertyStore = function(grid, source){
40032 this.store = new Roo.data.Store({
40033 recordType : Roo.grid.PropertyRecord
40035 this.store.on('update', this.onUpdate, this);
40037 this.setSource(source);
40039 Roo.grid.PropertyStore.superclass.constructor.call(this);
40044 Roo.extend(Roo.grid.PropertyStore, Roo.util.Observable, {
40045 setSource : function(o){
40047 this.store.removeAll();
40050 if(this.isEditableValue(o[k])){
40051 data.push(new Roo.grid.PropertyRecord({name: k, value: o[k]}, k));
40054 this.store.loadRecords({records: data}, {}, true);
40057 onUpdate : function(ds, record, type){
40058 if(type == Roo.data.Record.EDIT){
40059 var v = record.data['value'];
40060 var oldValue = record.modified['value'];
40061 if(this.grid.fireEvent('beforepropertychange', this.source, record.id, v, oldValue) !== false){
40062 this.source[record.id] = v;
40064 this.grid.fireEvent('propertychange', this.source, record.id, v, oldValue);
40071 getProperty : function(row){
40072 return this.store.getAt(row);
40075 isEditableValue: function(val){
40076 if(val && val instanceof Date){
40078 }else if(typeof val == 'object' || typeof val == 'function'){
40084 setValue : function(prop, value){
40085 this.source[prop] = value;
40086 this.store.getById(prop).set('value', value);
40089 getSource : function(){
40090 return this.source;
40094 Roo.grid.PropertyColumnModel = function(grid, store){
40097 g.PropertyColumnModel.superclass.constructor.call(this, [
40098 {header: this.nameText, sortable: true, dataIndex:'name', id: 'name'},
40099 {header: this.valueText, resizable:false, dataIndex: 'value', id: 'value'}
40101 this.store = store;
40102 this.bselect = Roo.DomHelper.append(document.body, {
40103 tag: 'select', style:'display:none', cls: 'x-grid-editor', children: [
40104 {tag: 'option', value: 'true', html: 'true'},
40105 {tag: 'option', value: 'false', html: 'false'}
40108 Roo.id(this.bselect);
40111 'date' : new g.GridEditor(new f.DateField({selectOnFocus:true})),
40112 'string' : new g.GridEditor(new f.TextField({selectOnFocus:true})),
40113 'number' : new g.GridEditor(new f.NumberField({selectOnFocus:true, style:'text-align:left;'})),
40114 'int' : new g.GridEditor(new f.NumberField({selectOnFocus:true, allowDecimals:false, style:'text-align:left;'})),
40115 'boolean' : new g.GridEditor(new f.Field({el:this.bselect,selectOnFocus:true}))
40117 this.renderCellDelegate = this.renderCell.createDelegate(this);
40118 this.renderPropDelegate = this.renderProp.createDelegate(this);
40121 Roo.extend(Roo.grid.PropertyColumnModel, Roo.grid.ColumnModel, {
40125 valueText : 'Value',
40127 dateFormat : 'm/j/Y',
40130 renderDate : function(dateVal){
40131 return dateVal.dateFormat(this.dateFormat);
40134 renderBool : function(bVal){
40135 return bVal ? 'true' : 'false';
40138 isCellEditable : function(colIndex, rowIndex){
40139 return colIndex == 1;
40142 getRenderer : function(col){
40144 this.renderCellDelegate : this.renderPropDelegate;
40147 renderProp : function(v){
40148 return this.getPropertyName(v);
40151 renderCell : function(val){
40153 if(val instanceof Date){
40154 rv = this.renderDate(val);
40155 }else if(typeof val == 'boolean'){
40156 rv = this.renderBool(val);
40158 return Roo.util.Format.htmlEncode(rv);
40161 getPropertyName : function(name){
40162 var pn = this.grid.propertyNames;
40163 return pn && pn[name] ? pn[name] : name;
40166 getCellEditor : function(colIndex, rowIndex){
40167 var p = this.store.getProperty(rowIndex);
40168 var n = p.data['name'], val = p.data['value'];
40170 if(typeof(this.grid.customEditors[n]) == 'string'){
40171 return this.editors[this.grid.customEditors[n]];
40173 if(typeof(this.grid.customEditors[n]) != 'undefined'){
40174 return this.grid.customEditors[n];
40176 if(val instanceof Date){
40177 return this.editors['date'];
40178 }else if(typeof val == 'number'){
40179 return this.editors['number'];
40180 }else if(typeof val == 'boolean'){
40181 return this.editors['boolean'];
40183 return this.editors['string'];
40189 * @class Roo.grid.PropertyGrid
40190 * @extends Roo.grid.EditorGrid
40191 * This class represents the interface of a component based property grid control.
40192 * <br><br>Usage:<pre><code>
40193 var grid = new Roo.grid.PropertyGrid("my-container-id", {
40201 * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
40202 * The container MUST have some type of size defined for the grid to fill. The container will be
40203 * automatically set to position relative if it isn't already.
40204 * @param {Object} config A config object that sets properties on this grid.
40206 Roo.grid.PropertyGrid = function(container, config){
40207 config = config || {};
40208 var store = new Roo.grid.PropertyStore(this);
40209 this.store = store;
40210 var cm = new Roo.grid.PropertyColumnModel(this, store);
40211 store.store.sort('name', 'ASC');
40212 Roo.grid.PropertyGrid.superclass.constructor.call(this, container, Roo.apply({
40215 enableColLock:false,
40216 enableColumnMove:false,
40218 trackMouseOver: false,
40221 this.getGridEl().addClass('x-props-grid');
40222 this.lastEditRow = null;
40223 this.on('columnresize', this.onColumnResize, this);
40226 * @event beforepropertychange
40227 * Fires before a property changes (return false to stop?)
40228 * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
40229 * @param {String} id Record Id
40230 * @param {String} newval New Value
40231 * @param {String} oldval Old Value
40233 "beforepropertychange": true,
40235 * @event propertychange
40236 * Fires after a property changes
40237 * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
40238 * @param {String} id Record Id
40239 * @param {String} newval New Value
40240 * @param {String} oldval Old Value
40242 "propertychange": true
40244 this.customEditors = this.customEditors || {};
40246 Roo.extend(Roo.grid.PropertyGrid, Roo.grid.EditorGrid, {
40249 * @cfg {Object} customEditors map of colnames=> custom editors.
40250 * the custom editor can be one of the standard ones (date|string|number|int|boolean), or a
40251 * grid editor eg. Roo.grid.GridEditor(new Roo.form.TextArea({selectOnFocus:true})),
40252 * false disables editing of the field.
40256 * @cfg {Object} propertyNames map of property Names to their displayed value
40259 render : function(){
40260 Roo.grid.PropertyGrid.superclass.render.call(this);
40261 this.autoSize.defer(100, this);
40264 autoSize : function(){
40265 Roo.grid.PropertyGrid.superclass.autoSize.call(this);
40267 this.view.fitColumns();
40271 onColumnResize : function(){
40272 this.colModel.setColumnWidth(1, this.container.getWidth(true)-this.colModel.getColumnWidth(0));
40276 * Sets the data for the Grid
40277 * accepts a Key => Value object of all the elements avaiable.
40278 * @param {Object} data to appear in grid.
40280 setSource : function(source){
40281 this.store.setSource(source);
40285 * Gets all the data from the grid.
40286 * @return {Object} data data stored in grid
40288 getSource : function(){
40289 return this.store.getSource();
40298 * @class Roo.grid.Calendar
40299 * @extends Roo.util.Grid
40300 * This class extends the Grid to provide a calendar widget
40301 * <br><br>Usage:<pre><code>
40302 var grid = new Roo.grid.Calendar("my-container-id", {
40305 selModel: mySelectionModel,
40306 autoSizeColumns: true,
40307 monitorWindowResize: false,
40308 trackMouseOver: true
40309 eventstore : real data store..
40315 * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
40316 * The container MUST have some type of size defined for the grid to fill. The container will be
40317 * automatically set to position relative if it isn't already.
40318 * @param {Object} config A config object that sets properties on this grid.
40320 Roo.grid.Calendar = function(container, config){
40321 // initialize the container
40322 this.container = Roo.get(container);
40323 this.container.update("");
40324 this.container.setStyle("overflow", "hidden");
40325 this.container.addClass('x-grid-container');
40327 this.id = this.container.id;
40329 Roo.apply(this, config);
40330 // check and correct shorthanded configs
40334 for (var r = 0;r < 6;r++) {
40337 for (var c =0;c < 7;c++) {
40341 if (this.eventStore) {
40342 this.eventStore= Roo.factory(this.eventStore, Roo.data);
40343 this.eventStore.on('load',this.onLoad, this);
40344 this.eventStore.on('beforeload',this.clearEvents, this);
40348 this.dataSource = new Roo.data.Store({
40349 proxy: new Roo.data.MemoryProxy(rows),
40350 reader: new Roo.data.ArrayReader({}, [
40351 'weekday0', 'weekday1', 'weekday2', 'weekday3', 'weekday4', 'weekday5', 'weekday6' ])
40354 this.dataSource.load();
40355 this.ds = this.dataSource;
40356 this.ds.xmodule = this.xmodule || false;
40359 var cellRender = function(v,x,r)
40361 return String.format(
40362 '<div class="fc-day fc-widget-content"><div>' +
40363 '<div class="fc-event-container"></div>' +
40364 '<div class="fc-day-number">{0}</div>'+
40366 '<div class="fc-day-content"><div style="position:relative"></div></div>' +
40367 '</div></div>', v);
40372 this.colModel = new Roo.grid.ColumnModel( [
40374 xtype: 'ColumnModel',
40376 dataIndex : 'weekday0',
40378 renderer : cellRender
40381 xtype: 'ColumnModel',
40383 dataIndex : 'weekday1',
40385 renderer : cellRender
40388 xtype: 'ColumnModel',
40390 dataIndex : 'weekday2',
40391 header : 'Tuesday',
40392 renderer : cellRender
40395 xtype: 'ColumnModel',
40397 dataIndex : 'weekday3',
40398 header : 'Wednesday',
40399 renderer : cellRender
40402 xtype: 'ColumnModel',
40404 dataIndex : 'weekday4',
40405 header : 'Thursday',
40406 renderer : cellRender
40409 xtype: 'ColumnModel',
40411 dataIndex : 'weekday5',
40413 renderer : cellRender
40416 xtype: 'ColumnModel',
40418 dataIndex : 'weekday6',
40419 header : 'Saturday',
40420 renderer : cellRender
40423 this.cm = this.colModel;
40424 this.cm.xmodule = this.xmodule || false;
40428 //this.selModel = new Roo.grid.CellSelectionModel();
40429 //this.sm = this.selModel;
40430 //this.selModel.init(this);
40434 this.container.setWidth(this.width);
40438 this.container.setHeight(this.height);
40445 * The raw click event for the entire grid.
40446 * @param {Roo.EventObject} e
40451 * The raw dblclick event for the entire grid.
40452 * @param {Roo.EventObject} e
40456 * @event contextmenu
40457 * The raw contextmenu event for the entire grid.
40458 * @param {Roo.EventObject} e
40460 "contextmenu" : true,
40463 * The raw mousedown event for the entire grid.
40464 * @param {Roo.EventObject} e
40466 "mousedown" : true,
40469 * The raw mouseup event for the entire grid.
40470 * @param {Roo.EventObject} e
40475 * The raw mouseover event for the entire grid.
40476 * @param {Roo.EventObject} e
40478 "mouseover" : true,
40481 * The raw mouseout event for the entire grid.
40482 * @param {Roo.EventObject} e
40487 * The raw keypress event for the entire grid.
40488 * @param {Roo.EventObject} e
40493 * The raw keydown event for the entire grid.
40494 * @param {Roo.EventObject} e
40502 * Fires when a cell is clicked
40503 * @param {Grid} this
40504 * @param {Number} rowIndex
40505 * @param {Number} columnIndex
40506 * @param {Roo.EventObject} e
40508 "cellclick" : true,
40510 * @event celldblclick
40511 * Fires when a cell is double clicked
40512 * @param {Grid} this
40513 * @param {Number} rowIndex
40514 * @param {Number} columnIndex
40515 * @param {Roo.EventObject} e
40517 "celldblclick" : true,
40520 * Fires when a row is clicked
40521 * @param {Grid} this
40522 * @param {Number} rowIndex
40523 * @param {Roo.EventObject} e
40527 * @event rowdblclick
40528 * Fires when a row is double clicked
40529 * @param {Grid} this
40530 * @param {Number} rowIndex
40531 * @param {Roo.EventObject} e
40533 "rowdblclick" : true,
40535 * @event headerclick
40536 * Fires when a header is clicked
40537 * @param {Grid} this
40538 * @param {Number} columnIndex
40539 * @param {Roo.EventObject} e
40541 "headerclick" : true,
40543 * @event headerdblclick
40544 * Fires when a header cell is double clicked
40545 * @param {Grid} this
40546 * @param {Number} columnIndex
40547 * @param {Roo.EventObject} e
40549 "headerdblclick" : true,
40551 * @event rowcontextmenu
40552 * Fires when a row is right clicked
40553 * @param {Grid} this
40554 * @param {Number} rowIndex
40555 * @param {Roo.EventObject} e
40557 "rowcontextmenu" : true,
40559 * @event cellcontextmenu
40560 * Fires when a cell is right clicked
40561 * @param {Grid} this
40562 * @param {Number} rowIndex
40563 * @param {Number} cellIndex
40564 * @param {Roo.EventObject} e
40566 "cellcontextmenu" : true,
40568 * @event headercontextmenu
40569 * Fires when a header is right clicked
40570 * @param {Grid} this
40571 * @param {Number} columnIndex
40572 * @param {Roo.EventObject} e
40574 "headercontextmenu" : true,
40576 * @event bodyscroll
40577 * Fires when the body element is scrolled
40578 * @param {Number} scrollLeft
40579 * @param {Number} scrollTop
40581 "bodyscroll" : true,
40583 * @event columnresize
40584 * Fires when the user resizes a column
40585 * @param {Number} columnIndex
40586 * @param {Number} newSize
40588 "columnresize" : true,
40590 * @event columnmove
40591 * Fires when the user moves a column
40592 * @param {Number} oldIndex
40593 * @param {Number} newIndex
40595 "columnmove" : true,
40598 * Fires when row(s) start being dragged
40599 * @param {Grid} this
40600 * @param {Roo.GridDD} dd The drag drop object
40601 * @param {event} e The raw browser event
40603 "startdrag" : true,
40606 * Fires when a drag operation is complete
40607 * @param {Grid} this
40608 * @param {Roo.GridDD} dd The drag drop object
40609 * @param {event} e The raw browser event
40614 * Fires when dragged row(s) are dropped on a valid DD target
40615 * @param {Grid} this
40616 * @param {Roo.GridDD} dd The drag drop object
40617 * @param {String} targetId The target drag drop object
40618 * @param {event} e The raw browser event
40623 * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
40624 * @param {Grid} this
40625 * @param {Roo.GridDD} dd The drag drop object
40626 * @param {String} targetId The target drag drop object
40627 * @param {event} e The raw browser event
40632 * Fires when the dragged row(s) first cross another DD target while being dragged
40633 * @param {Grid} this
40634 * @param {Roo.GridDD} dd The drag drop object
40635 * @param {String} targetId The target drag drop object
40636 * @param {event} e The raw browser event
40638 "dragenter" : true,
40641 * Fires when the dragged row(s) leave another DD target while being dragged
40642 * @param {Grid} this
40643 * @param {Roo.GridDD} dd The drag drop object
40644 * @param {String} targetId The target drag drop object
40645 * @param {event} e The raw browser event
40650 * Fires when a row is rendered, so you can change add a style to it.
40651 * @param {GridView} gridview The grid view
40652 * @param {Object} rowcfg contains record rowIndex and rowClass - set rowClass to add a style.
40658 * Fires when the grid is rendered
40659 * @param {Grid} grid
40664 * Fires when a date is selected
40665 * @param {DatePicker} this
40666 * @param {Date} date The selected date
40670 * @event monthchange
40671 * Fires when the displayed month changes
40672 * @param {DatePicker} this
40673 * @param {Date} date The selected month
40675 'monthchange': true,
40677 * @event evententer
40678 * Fires when mouse over an event
40679 * @param {Calendar} this
40680 * @param {event} Event
40682 'evententer': true,
40684 * @event eventleave
40685 * Fires when the mouse leaves an
40686 * @param {Calendar} this
40689 'eventleave': true,
40691 * @event eventclick
40692 * Fires when the mouse click an
40693 * @param {Calendar} this
40696 'eventclick': true,
40698 * @event eventrender
40699 * Fires before each cell is rendered, so you can modify the contents, like cls / title / qtip
40700 * @param {Calendar} this
40701 * @param {data} data to be modified
40703 'eventrender': true
40707 Roo.grid.Grid.superclass.constructor.call(this);
40708 this.on('render', function() {
40709 this.view.el.addClass('x-grid-cal');
40711 (function() { this.setDate(new Date()); }).defer(100,this); //default today..
40715 if (!Roo.grid.Calendar.style) {
40716 Roo.grid.Calendar.style = Roo.util.CSS.createStyleSheet({
40719 '.x-grid-cal .x-grid-col' : {
40720 height: 'auto !important',
40721 'vertical-align': 'top'
40723 '.x-grid-cal .fc-event-hori' : {
40734 Roo.extend(Roo.grid.Calendar, Roo.grid.Grid, {
40736 * @cfg {Store} eventStore The store that loads events.
40741 activeDate : false,
40744 monitorWindowResize : false,
40747 resizeColumns : function() {
40748 var col = (this.view.el.getWidth() / 7) - 3;
40749 // loop through cols, and setWidth
40750 for(var i =0 ; i < 7 ; i++){
40751 this.cm.setColumnWidth(i, col);
40754 setDate :function(date) {
40756 Roo.log('setDate?');
40758 this.resizeColumns();
40759 var vd = this.activeDate;
40760 this.activeDate = date;
40761 // if(vd && this.el){
40762 // var t = date.getTime();
40763 // if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
40764 // Roo.log('using add remove');
40766 // this.fireEvent('monthchange', this, date);
40768 // this.cells.removeClass("fc-state-highlight");
40769 // this.cells.each(function(c){
40770 // if(c.dateValue == t){
40771 // c.addClass("fc-state-highlight");
40772 // setTimeout(function(){
40773 // try{c.dom.firstChild.focus();}catch(e){}
40783 var days = date.getDaysInMonth();
40785 var firstOfMonth = date.getFirstDateOfMonth();
40786 var startingPos = firstOfMonth.getDay()-this.startDay;
40788 if(startingPos < this.startDay){
40792 var pm = date.add(Date.MONTH, -1);
40793 var prevStart = pm.getDaysInMonth()-startingPos;
40797 this.cells = this.view.el.select('.x-grid-row .x-grid-col',true);
40799 this.textNodes = this.view.el.query('.x-grid-row .x-grid-col .x-grid-cell-text');
40800 //this.cells.addClassOnOver('fc-state-hover');
40802 var cells = this.cells.elements;
40803 var textEls = this.textNodes;
40805 //Roo.each(cells, function(cell){
40806 // cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
40809 days += startingPos;
40811 // convert everything to numbers so it's fast
40812 var day = 86400000;
40813 var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
40816 //Roo.log(prevStart);
40818 var today = new Date().clearTime().getTime();
40819 var sel = date.clearTime().getTime();
40820 var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
40821 var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
40822 var ddMatch = this.disabledDatesRE;
40823 var ddText = this.disabledDatesText;
40824 var ddays = this.disabledDays ? this.disabledDays.join("") : false;
40825 var ddaysText = this.disabledDaysText;
40826 var format = this.format;
40828 var setCellClass = function(cal, cell){
40830 //Roo.log('set Cell Class');
40832 var t = d.getTime();
40837 cell.dateValue = t;
40839 cell.className += " fc-today";
40840 cell.className += " fc-state-highlight";
40841 cell.title = cal.todayText;
40844 // disable highlight in other month..
40845 cell.className += " fc-state-highlight";
40850 //cell.className = " fc-state-disabled";
40851 cell.title = cal.minText;
40855 //cell.className = " fc-state-disabled";
40856 cell.title = cal.maxText;
40860 if(ddays.indexOf(d.getDay()) != -1){
40861 // cell.title = ddaysText;
40862 // cell.className = " fc-state-disabled";
40865 if(ddMatch && format){
40866 var fvalue = d.dateFormat(format);
40867 if(ddMatch.test(fvalue)){
40868 cell.title = ddText.replace("%0", fvalue);
40869 cell.className = " fc-state-disabled";
40873 if (!cell.initialClassName) {
40874 cell.initialClassName = cell.dom.className;
40877 cell.dom.className = cell.initialClassName + ' ' + cell.className;
40882 for(; i < startingPos; i++) {
40883 cells[i].dayName = (++prevStart);
40884 Roo.log(textEls[i]);
40885 d.setDate(d.getDate()+1);
40887 //cells[i].className = "fc-past fc-other-month";
40888 setCellClass(this, cells[i]);
40893 for(; i < days; i++){
40894 intDay = i - startingPos + 1;
40895 cells[i].dayName = (intDay);
40896 d.setDate(d.getDate()+1);
40898 cells[i].className = ''; // "x-date-active";
40899 setCellClass(this, cells[i]);
40903 for(; i < 42; i++) {
40904 //textEls[i].innerHTML = (++extraDays);
40906 d.setDate(d.getDate()+1);
40907 cells[i].dayName = (++extraDays);
40908 cells[i].className = "fc-future fc-other-month";
40909 setCellClass(this, cells[i]);
40912 //this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
40914 var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
40916 // this will cause all the cells to mis
40919 for (var r = 0;r < 6;r++) {
40920 for (var c =0;c < 7;c++) {
40921 this.ds.getAt(r).set('weekday' + c ,cells[i++].dayName );
40925 this.cells = this.view.el.select('.x-grid-row .x-grid-col',true);
40926 for(i=0;i<cells.length;i++) {
40928 this.cells.elements[i].dayName = cells[i].dayName ;
40929 this.cells.elements[i].className = cells[i].className;
40930 this.cells.elements[i].initialClassName = cells[i].initialClassName ;
40931 this.cells.elements[i].title = cells[i].title ;
40932 this.cells.elements[i].dateValue = cells[i].dateValue ;
40938 //this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
40939 //this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
40941 ////if(totalRows != 6){
40942 //this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
40943 // this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
40946 this.fireEvent('monthchange', this, date);
40951 * Returns the grid's SelectionModel.
40952 * @return {SelectionModel}
40954 getSelectionModel : function(){
40955 if(!this.selModel){
40956 this.selModel = new Roo.grid.CellSelectionModel();
40958 return this.selModel;
40962 this.eventStore.load()
40968 findCell : function(dt) {
40969 dt = dt.clearTime().getTime();
40971 this.cells.each(function(c){
40972 //Roo.log("check " +c.dateValue + '?=' + dt);
40973 if(c.dateValue == dt){
40983 findCells : function(rec) {
40984 var s = rec.data.start_dt.clone().clearTime().getTime();
40986 var e= rec.data.end_dt.clone().clearTime().getTime();
40989 this.cells.each(function(c){
40990 ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
40992 if(c.dateValue > e){
40995 if(c.dateValue < s){
41004 findBestRow: function(cells)
41008 for (var i =0 ; i < cells.length;i++) {
41009 ret = Math.max(cells[i].rows || 0,ret);
41016 addItem : function(rec)
41018 // look for vertical location slot in
41019 var cells = this.findCells(rec);
41021 rec.row = this.findBestRow(cells);
41023 // work out the location.
41027 for(var i =0; i < cells.length; i++) {
41035 if (crow.start.getY() == cells[i].getY()) {
41037 crow.end = cells[i];
41053 for (var i = 0; i < cells.length;i++) {
41054 cells[i].rows = Math.max(cells[i].rows || 0 , rec.row + 1 );
41061 clearEvents: function() {
41063 if (!this.eventStore.getCount()) {
41066 // reset number of rows in cells.
41067 Roo.each(this.cells.elements, function(c){
41071 this.eventStore.each(function(e) {
41072 this.clearEvent(e);
41077 clearEvent : function(ev)
41080 Roo.each(ev.els, function(el) {
41081 el.un('mouseenter' ,this.onEventEnter, this);
41082 el.un('mouseleave' ,this.onEventLeave, this);
41090 renderEvent : function(ev,ctr) {
41092 ctr = this.view.el.select('.fc-event-container',true).first();
41096 this.clearEvent(ev);
41102 var cells = ev.cells;
41103 var rows = ev.rows;
41104 this.fireEvent('eventrender', this, ev);
41106 for(var i =0; i < rows.length; i++) {
41110 cls += ' fc-event-start';
41112 if ((i+1) == rows.length) {
41113 cls += ' fc-event-end';
41116 //Roo.log(ev.data);
41117 // how many rows should it span..
41118 var cg = this.eventTmpl.append(ctr,Roo.apply({
41121 }, ev.data) , true);
41124 cg.on('mouseenter' ,this.onEventEnter, this, ev);
41125 cg.on('mouseleave' ,this.onEventLeave, this, ev);
41126 cg.on('click', this.onEventClick, this, ev);
41130 var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
41131 var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
41134 cg.setXY([sbox.x +2, sbox.y +(ev.row * 20)]);
41135 cg.setWidth(ebox.right - sbox.x -2);
41139 renderEvents: function()
41141 // first make sure there is enough space..
41143 if (!this.eventTmpl) {
41144 this.eventTmpl = new Roo.Template(
41145 '<div class="roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable {fccls} {cls}" style="position: absolute" unselectable="on">' +
41146 '<div class="fc-event-inner">' +
41147 '<span class="fc-event-time">{time}</span>' +
41148 '<span class="fc-event-title" qtip="{qtip}">{title}</span>' +
41150 '<div class="ui-resizable-heandle ui-resizable-e"> </div>' +
41158 this.cells.each(function(c) {
41159 //Roo.log(c.select('.fc-day-content div',true).first());
41160 c.select('.fc-day-content div',true).first().setHeight(Math.max(34, (c.rows || 1) * 20));
41163 var ctr = this.view.el.select('.fc-event-container',true).first();
41166 this.eventStore.each(function(ev){
41168 this.renderEvent(ev);
41172 this.view.layout();
41176 onEventEnter: function (e, el,event,d) {
41177 this.fireEvent('evententer', this, el, event);
41180 onEventLeave: function (e, el,event,d) {
41181 this.fireEvent('eventleave', this, el, event);
41184 onEventClick: function (e, el,event,d) {
41185 this.fireEvent('eventclick', this, el, event);
41188 onMonthChange: function () {
41192 onLoad: function () {
41194 //Roo.log('calendar onload');
41196 if(this.eventStore.getCount() > 0){
41200 this.eventStore.each(function(d){
41205 if (typeof(add.end_dt) == 'undefined') {
41206 Roo.log("Missing End time in calendar data: ");
41210 if (typeof(add.start_dt) == 'undefined') {
41211 Roo.log("Missing Start time in calendar data: ");
41215 add.start_dt = typeof(add.start_dt) == 'string' ? Date.parseDate(add.start_dt,'Y-m-d H:i:s') : add.start_dt,
41216 add.end_dt = typeof(add.end_dt) == 'string' ? Date.parseDate(add.end_dt,'Y-m-d H:i:s') : add.end_dt,
41217 add.id = add.id || d.id;
41218 add.title = add.title || '??';
41226 this.renderEvents();
41236 render : function ()
41240 if (!this.view.el.hasClass('course-timesheet')) {
41241 this.view.el.addClass('course-timesheet');
41243 if (this.tsStyle) {
41248 Roo.log(_this.grid.view.el.getWidth());
41251 this.tsStyle = Roo.util.CSS.createStyleSheet({
41252 '.course-timesheet .x-grid-row' : {
41255 '.x-grid-row td' : {
41256 'vertical-align' : 0
41258 '.course-edit-link' : {
41260 'text-overflow' : 'ellipsis',
41261 'overflow' : 'hidden',
41262 'white-space' : 'nowrap',
41263 'cursor' : 'pointer'
41268 '.de-act-sup-link' : {
41269 'color' : 'purple',
41270 'text-decoration' : 'line-through'
41274 'text-decoration' : 'line-through'
41276 '.course-timesheet .course-highlight' : {
41277 'border-top-style': 'dashed !important',
41278 'border-bottom-bottom': 'dashed !important'
41280 '.course-timesheet .course-item' : {
41281 'font-family' : 'tahoma, arial, helvetica',
41282 'font-size' : '11px',
41283 'overflow' : 'hidden',
41284 'padding-left' : '10px',
41285 'padding-right' : '10px',
41286 'padding-top' : '10px'
41294 monitorWindowResize : false,
41295 cellrenderer : function(v,x,r)
41300 xtype: 'CellSelectionModel',
41307 beforeload : function (_self, options)
41309 options.params = options.params || {};
41310 options.params._month = _this.monthField.getValue();
41311 options.params.limit = 9999;
41312 options.params['sort'] = 'when_dt';
41313 options.params['dir'] = 'ASC';
41314 this.proxy.loadResponse = this.loadResponse;
41316 //this.addColumns();
41318 load : function (_self, records, options)
41320 _this.grid.view.el.select('.course-edit-link', true).on('click', function() {
41321 // if you click on the translation.. you can edit it...
41322 var el = Roo.get(this);
41323 var id = el.dom.getAttribute('data-id');
41324 var d = el.dom.getAttribute('data-date');
41325 var t = el.dom.getAttribute('data-time');
41326 //var id = this.child('span').dom.textContent;
41329 Pman.Dialog.CourseCalendar.show({
41333 productitem_active : id ? 1 : 0
41335 _this.grid.ds.load({});
41340 _this.panel.fireEvent('resize', [ '', '' ]);
41343 loadResponse : function(o, success, response){
41344 // this is overridden on before load..
41346 Roo.log("our code?");
41347 //Roo.log(success);
41348 //Roo.log(response)
41349 delete this.activeRequest;
41351 this.fireEvent("loadexception", this, o, response);
41352 o.request.callback.call(o.request.scope, null, o.request.arg, false);
41357 result = o.reader.read(response);
41359 Roo.log("load exception?");
41360 this.fireEvent("loadexception", this, o, response, e);
41361 o.request.callback.call(o.request.scope, null, o.request.arg, false);
41364 Roo.log("ready...");
41365 // loop through result.records;
41366 // and set this.tdate[date] = [] << array of records..
41368 Roo.each(result.records, function(r){
41370 if(typeof(_this.tdata[r.data.when_dt.format('j')]) == 'undefined'){
41371 _this.tdata[r.data.when_dt.format('j')] = [];
41373 _this.tdata[r.data.when_dt.format('j')].push(r.data);
41376 //Roo.log(_this.tdata);
41378 result.records = [];
41379 result.totalRecords = 6;
41381 // let's generate some duumy records for the rows.
41382 //var st = _this.dateField.getValue();
41384 // work out monday..
41385 //st = st.add(Date.DAY, -1 * st.format('w'));
41387 var date = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
41389 var firstOfMonth = date.getFirstDayOfMonth();
41390 var days = date.getDaysInMonth();
41392 var firstAdded = false;
41393 for (var i = 0; i < result.totalRecords ; i++) {
41394 //var d= st.add(Date.DAY, i);
41397 for(var w = 0 ; w < 7 ; w++){
41398 if(!firstAdded && firstOfMonth != w){
41405 var dd = (d > 0 && d < 10) ? "0"+d : d;
41406 row['weekday'+w] = String.format(
41407 '<span style="font-size: 16px;"><b>{0}</b></span>'+
41408 '<span class="course-edit-link" style="color:blue;" data-id="0" data-date="{1}"> Add New</span>',
41410 date.format('Y-m-')+dd
41413 if(typeof(_this.tdata[d]) != 'undefined'){
41414 Roo.each(_this.tdata[d], function(r){
41418 var desc = (r.productitem_id_descrip) ? r.productitem_id_descrip : '';
41419 if(r.parent_id*1>0){
41420 is_sub = (r.productitem_id_visible*1 < 1) ? 'de-act-sup-link' :'sub-link';
41423 if(r.productitem_id_visible*1 < 1 && r.parent_id*1 < 1){
41424 deactive = 'de-act-link';
41427 row['weekday'+w] += String.format(
41428 '<br /><span class="course-edit-link {3} {4}" qtip="{5}" data-id="{0}">{2} - {1}</span>',
41430 r.product_id_name, //1
41431 r.when_dt.format('h:ia'), //2
41441 // only do this if something added..
41443 result.records.push(_this.grid.dataSource.reader.newRow(row));
41447 // push it twice. (second one with an hour..
41451 this.fireEvent("load", this, o, o.request.arg);
41452 o.request.callback.call(o.request.scope, result, o.request.arg, true);
41454 sortInfo : {field: 'when_dt', direction : 'ASC' },
41456 xtype: 'HttpProxy',
41459 url : baseURL + '/Roo/Shop_course.php'
41462 xtype: 'JsonReader',
41479 'name': 'parent_id',
41483 'name': 'product_id',
41487 'name': 'productitem_id',
41505 click : function (_self, e)
41507 var sd = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
41508 sd.setMonth(sd.getMonth()-1);
41509 _this.monthField.setValue(sd.format('Y-m-d'));
41510 _this.grid.ds.load({});
41516 xtype: 'Separator',
41520 xtype: 'MonthField',
41523 render : function (_self)
41525 _this.monthField = _self;
41526 // _this.monthField.set today
41528 select : function (combo, date)
41530 _this.grid.ds.load({});
41533 value : (function() { return new Date(); })()
41536 xtype: 'Separator',
41542 text : "Blue: in-active, green: in-active sup-event, red: de-active, purple: de-active sup-event"
41552 click : function (_self, e)
41554 var sd = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
41555 sd.setMonth(sd.getMonth()+1);
41556 _this.monthField.setValue(sd.format('Y-m-d'));
41557 _this.grid.ds.load({});
41570 * Ext JS Library 1.1.1
41571 * Copyright(c) 2006-2007, Ext JS, LLC.
41573 * Originally Released Under LGPL - original licence link has changed is not relivant.
41576 * <script type="text/javascript">
41580 * @class Roo.LoadMask
41581 * A simple utility class for generically masking elements while loading data. If the element being masked has
41582 * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
41583 * process and the mask element will be cached for reuse. For all other elements, this mask will replace the
41584 * element's UpdateManager load indicator and will be destroyed after the initial load.
41586 * Create a new LoadMask
41587 * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
41588 * @param {Object} config The config object
41590 Roo.LoadMask = function(el, config){
41591 this.el = Roo.get(el);
41592 Roo.apply(this, config);
41594 this.store.on('beforeload', this.onBeforeLoad, this);
41595 this.store.on('load', this.onLoad, this);
41596 this.store.on('loadexception', this.onLoadException, this);
41597 this.removeMask = false;
41599 var um = this.el.getUpdateManager();
41600 um.showLoadIndicator = false; // disable the default indicator
41601 um.on('beforeupdate', this.onBeforeLoad, this);
41602 um.on('update', this.onLoad, this);
41603 um.on('failure', this.onLoad, this);
41604 this.removeMask = true;
41608 Roo.LoadMask.prototype = {
41610 * @cfg {Boolean} removeMask
41611 * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
41612 * False to persist the mask element reference for multiple uses (e.g., for paged data widgets). Defaults to false.
41615 * @cfg {String} msg
41616 * The text to display in a centered loading message box (defaults to 'Loading...')
41618 msg : 'Loading...',
41620 * @cfg {String} msgCls
41621 * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
41623 msgCls : 'x-mask-loading',
41626 * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
41632 * Disables the mask to prevent it from being displayed
41634 disable : function(){
41635 this.disabled = true;
41639 * Enables the mask so that it can be displayed
41641 enable : function(){
41642 this.disabled = false;
41645 onLoadException : function()
41647 Roo.log(arguments);
41649 if (typeof(arguments[3]) != 'undefined') {
41650 Roo.MessageBox.alert("Error loading",arguments[3]);
41654 if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
41655 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
41664 this.el.unmask(this.removeMask);
41667 onLoad : function()
41669 this.el.unmask(this.removeMask);
41673 onBeforeLoad : function(){
41674 if(!this.disabled){
41675 this.el.mask(this.msg, this.msgCls);
41680 destroy : function(){
41682 this.store.un('beforeload', this.onBeforeLoad, this);
41683 this.store.un('load', this.onLoad, this);
41684 this.store.un('loadexception', this.onLoadException, this);
41686 var um = this.el.getUpdateManager();
41687 um.un('beforeupdate', this.onBeforeLoad, this);
41688 um.un('update', this.onLoad, this);
41689 um.un('failure', this.onLoad, this);
41694 * Ext JS Library 1.1.1
41695 * Copyright(c) 2006-2007, Ext JS, LLC.
41697 * Originally Released Under LGPL - original licence link has changed is not relivant.
41700 * <script type="text/javascript">
41705 * @class Roo.XTemplate
41706 * @extends Roo.Template
41707 * Provides a template that can have nested templates for loops or conditionals. The syntax is:
41709 var t = new Roo.XTemplate(
41710 '<select name="{name}">',
41711 '<tpl for="options"><option value="{value:trim}">{text:ellipsis(10)}</option></tpl>',
41715 // then append, applying the master template values
41718 * Supported features:
41723 {a_variable} - output encoded.
41724 {a_variable.format:("Y-m-d")} - call a method on the variable
41725 {a_variable:raw} - unencoded output
41726 {a_variable:toFixed(1,2)} - Roo.util.Format."toFixed"
41727 {a_variable:this.method_on_template(...)} - call a method on the template object.
41732 <tpl for="a_variable or condition.."></tpl>
41733 <tpl if="a_variable or condition"></tpl>
41734 <tpl exec="some javascript"></tpl>
41735 <tpl name="named_template"></tpl> (experimental)
41737 <tpl for="."></tpl> - just iterate the property..
41738 <tpl for=".."></tpl> - iterates with the parent (probably the template)
41742 Roo.XTemplate = function()
41744 Roo.XTemplate.superclass.constructor.apply(this, arguments);
41751 Roo.extend(Roo.XTemplate, Roo.Template, {
41754 * The various sub templates
41759 * basic tag replacing syntax
41762 * // you can fake an object call by doing this
41766 re : /\{([\w-\.]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
41769 * compile the template
41771 * This is not recursive, so I'm not sure how nested templates are really going to be handled..
41774 compile: function()
41778 s = ['<tpl>', s, '</tpl>'].join('');
41780 var re = /<tpl\b[^>]*>((?:(?=([^<]+))\2|<(?!tpl\b[^>]*>))*?)<\/tpl>/,
41781 nameRe = /^<tpl\b[^>]*?for="(.*?)"/,
41782 ifRe = /^<tpl\b[^>]*?if="(.*?)"/,
41783 execRe = /^<tpl\b[^>]*?exec="(.*?)"/,
41784 namedRe = /^<tpl\b[^>]*?name="(\w+)"/, // named templates..
41789 while(true == !!(m = s.match(re))){
41790 var forMatch = m[0].match(nameRe),
41791 ifMatch = m[0].match(ifRe),
41792 execMatch = m[0].match(execRe),
41793 namedMatch = m[0].match(namedRe),
41798 name = forMatch && forMatch[1] ? forMatch[1] : '';
41801 // if - puts fn into test..
41802 exp = ifMatch && ifMatch[1] ? ifMatch[1] : null;
41804 fn = new Function('values', 'parent', 'with(values){ return '+(Roo.util.Format.htmlDecode(exp))+'; }');
41809 // exec - calls a function... returns empty if true is returned.
41810 exp = execMatch && execMatch[1] ? execMatch[1] : null;
41812 exec = new Function('values', 'parent', 'with(values){ '+(Roo.util.Format.htmlDecode(exp))+'; }');
41820 case '.': name = new Function('values', 'parent', 'with(values){ return values; }'); break;
41821 case '..': name = new Function('values', 'parent', 'with(values){ return parent; }'); break;
41822 default: name = new Function('values', 'parent', 'with(values){ return '+name+'; }');
41825 var uid = namedMatch ? namedMatch[1] : id;
41829 id: namedMatch ? namedMatch[1] : id,
41836 s = s.replace(m[0], '');
41838 s = s.replace(m[0], '{xtpl'+ id + '}');
41843 for(var i = tpls.length-1; i >= 0; --i){
41844 this.compileTpl(tpls[i]);
41845 this.tpls[tpls[i].id] = tpls[i];
41847 this.master = tpls[tpls.length-1];
41851 * same as applyTemplate, except it's done to one of the subTemplates
41852 * when using named templates, you can do:
41854 * var str = pl.applySubTemplate('your-name', values);
41857 * @param {Number} id of the template
41858 * @param {Object} values to apply to template
41859 * @param {Object} parent (normaly the instance of this object)
41861 applySubTemplate : function(id, values, parent)
41865 var t = this.tpls[id];
41869 if(t.test && !t.test.call(this, values, parent)){
41873 Roo.log("Xtemplate.applySubTemplate 'test': Exception thrown");
41874 Roo.log(e.toString());
41880 if(t.exec && t.exec.call(this, values, parent)){
41884 Roo.log("Xtemplate.applySubTemplate 'exec': Exception thrown");
41885 Roo.log(e.toString());
41890 var vs = t.target ? t.target.call(this, values, parent) : values;
41891 parent = t.target ? values : parent;
41892 if(t.target && vs instanceof Array){
41894 for(var i = 0, len = vs.length; i < len; i++){
41895 buf[buf.length] = t.compiled.call(this, vs[i], parent);
41897 return buf.join('');
41899 return t.compiled.call(this, vs, parent);
41901 Roo.log("Xtemplate.applySubTemplate : Exception thrown");
41902 Roo.log(e.toString());
41903 Roo.log(t.compiled);
41908 compileTpl : function(tpl)
41910 var fm = Roo.util.Format;
41911 var useF = this.disableFormats !== true;
41912 var sep = Roo.isGecko ? "+" : ",";
41913 var undef = function(str) {
41914 Roo.log("Property not found :" + str);
41918 var fn = function(m, name, format, args)
41920 //Roo.log(arguments);
41921 args = args ? args.replace(/\\'/g,"'") : args;
41922 //["{TEST:(a,b,c)}", "TEST", "", "a,b,c", 0, "{TEST:(a,b,c)}"]
41923 if (typeof(format) == 'undefined') {
41924 format= 'htmlEncode';
41926 if (format == 'raw' ) {
41930 if(name.substr(0, 4) == 'xtpl'){
41931 return "'"+ sep +'this.applySubTemplate('+name.substr(4)+', values, parent)'+sep+"'";
41934 // build an array of options to determine if value is undefined..
41936 // basically get 'xxxx.yyyy' then do
41937 // (typeof(xxxx) == 'undefined' || typeof(xxx.yyyy) == 'undefined') ?
41938 // (function () { Roo.log("Property not found"); return ''; })() :
41943 Roo.each(name.split('.'), function(st) {
41944 lookfor += (lookfor.length ? '.': '') + st;
41945 udef_ar.push( "(typeof(" + lookfor + ") == 'undefined')" );
41948 var udef_st = '((' + udef_ar.join(" || ") +") ? undef('" + name + "') : "; // .. needs )
41951 if(format && useF){
41953 args = args ? ',' + args : "";
41955 if(format.substr(0, 5) != "this."){
41956 format = "fm." + format + '(';
41958 format = 'this.call("'+ format.substr(5) + '", ';
41962 return "'"+ sep + udef_st + format + name + args + "))"+sep+"'";
41966 // called with xxyx.yuu:(test,test)
41968 return "'"+ sep + udef_st + name + '(' + args + "))"+sep+"'";
41970 // raw.. - :raw modifier..
41971 return "'"+ sep + udef_st + name + ")"+sep+"'";
41975 // branched to use + in gecko and [].join() in others
41977 body = "tpl.compiled = function(values, parent){ with(values) { return '" +
41978 tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
41981 body = ["tpl.compiled = function(values, parent){ with (values) { return ['"];
41982 body.push(tpl.body.replace(/(\r\n|\n)/g,
41983 '\\n').replace(/'/g, "\\'").replace(this.re, fn));
41984 body.push("'].join('');};};");
41985 body = body.join('');
41988 Roo.debug && Roo.log(body.replace(/\\n/,'\n'));
41990 /** eval:var:tpl eval:var:fm eval:var:useF eval:var:undef */
41996 applyTemplate : function(values){
41997 return this.master.compiled.call(this, values, {});
41998 //var s = this.subs;
42001 apply : function(){
42002 return this.applyTemplate.apply(this, arguments);
42007 Roo.XTemplate.from = function(el){
42008 el = Roo.getDom(el);
42009 return new Roo.XTemplate(el.value || el.innerHTML);